diff --git a/elm.json b/elm.json index c1dbf3e55..21b374e4f 100644 --- a/elm.json +++ b/elm.json @@ -15,6 +15,7 @@ "elm/html": "1.0.0", "elm/http": "2.0.0", "elm/json": "1.1.3", + "elm/parser": "1.1.0", "elm/random": "1.0.0", "elm/regex": "1.0.0", "elm/svg": "1.0.1", @@ -33,7 +34,6 @@ "dillonkearns/elm-markdown": "6.0.1", "elm/bytes": "1.0.8", "elm/file": "1.0.5", - "elm/parser": "1.1.0", "elm/virtual-dom": "1.0.2", "pablohirafuji/elm-syntax-highlight": "3.4.1", "rtfeldman/elm-hex": "1.0.0" diff --git a/src/DataSources/NewSqlParser/Dsl.elm b/src/DataSources/NewSqlParser/Dsl.elm new file mode 100644 index 000000000..b02dc6652 --- /dev/null +++ b/src/DataSources/NewSqlParser/Dsl.elm @@ -0,0 +1,67 @@ +module DataSources.NewSqlParser.Dsl exposing (CheckInner, ColumnConstraint(..), ForeignKeyInner, ForeignKeyRef, IndexInner, ParseError, ParsedColumn, ParsedConstraint(..), ParsedTable, PrimaryKeyInner, SqlStatement, UniqueInner) + +import Libs.Nel exposing (Nel) + + +type alias SqlStatement = + String + + +type alias ParsedTable = + { schema : Maybe String + , table : String + , columns : List ParsedColumn + , constraints : List ParsedConstraint + } + + +type alias ParsedColumn = + { name : String + , kind : String + , nullable : Bool + , default : Maybe String + , primaryKey : Maybe String + , foreignKey : Maybe ( String, ForeignKeyRef ) + , check : Maybe String + } + + +type alias ForeignKeyRef = + { schema : Maybe String, table : String, column : Maybe String } + + +type ColumnConstraint + = ColumnPrimaryKey + | ColumnForeignKey ForeignKeyRef + + +type ParsedConstraint + = PrimaryKey PrimaryKeyInner + | ForeignKey ForeignKeyInner + | Unique UniqueInner + | Index IndexInner + | Check CheckInner + + +type alias PrimaryKeyInner = + { name : Maybe String, columns : Nel String } + + +type alias ForeignKeyInner = + { name : Maybe String, src : String, ref : ForeignKeyRef } + + +type alias UniqueInner = + { name : String, columns : Nel String } + + +type alias IndexInner = + { name : String, columns : Nel String, definition : String } + + +type alias CheckInner = + { name : String, columns : List String, predicate : String } + + +type alias ParseError = + String diff --git a/src/DataSources/NewSqlParser/FileParser.elm b/src/DataSources/NewSqlParser/FileParser.elm new file mode 100644 index 000000000..efde0ed68 --- /dev/null +++ b/src/DataSources/NewSqlParser/FileParser.elm @@ -0,0 +1,6 @@ +module DataSources.NewSqlParser.FileParser exposing (parse) + + +parse : String -> String +parse file = + file diff --git a/src/DataSources/NewSqlParser/Parsers/Basic.elm b/src/DataSources/NewSqlParser/Parsers/Basic.elm new file mode 100644 index 000000000..ec56e6a70 --- /dev/null +++ b/src/DataSources/NewSqlParser/Parsers/Basic.elm @@ -0,0 +1,166 @@ +module DataSources.NewSqlParser.Parsers.Basic exposing (checkParser, columnNameParser, columnTypeParser, constraintParser, defaultValueParser, foreignKeyParser, foreignKeyRefParser, notNullParser, primaryKeyParser, schemaNameParser, tableNameParser, tableRefParser) + +import DataSources.NewSqlParser.Dsl exposing (ColumnConstraint(..), ForeignKeyRef) +import Libs.Parser exposing (exists, identifierOrQuoted, maybe, symbolInsensitive) +import Parser exposing ((|.), (|=), Nestable(..), Parser, Trailing(..), backtrackable, getChompedString, int, multiComment, oneOf, sequence, spaces, succeed, symbol) + + + +-- reusable (small) building blocks for bigger parsers + + +tableRefParser : Parser ( Maybe String, String ) +tableRefParser = + succeed + (\schemaName tableName -> + case ( schemaName, tableName ) of + ( part1, Nothing ) -> + ( Nothing, part1 ) + + ( part1, Just part2 ) -> + ( Just part1, part2 ) + ) + |= identifierOrQuoted + |= maybe + (succeed identity + |. symbol "." + |= identifierOrQuoted + ) + + +schemaNameParser : Parser String +schemaNameParser = + identifierOrQuoted + + +tableNameParser : Parser String +tableNameParser = + identifierOrQuoted + + +columnNameParser : Parser String +columnNameParser = + identifierOrQuoted + + +columnTypeParser : Parser String +columnTypeParser = + -- cf https://www.postgresql.org/docs/current/datatype.html + oneOf + [ customColumnTypeParser "BIT VARYING" (maybe numbers |> Parser.map (Maybe.withDefault "")) + , customColumnTypeParser "CHARACTER VARYING" (maybe numbers |> Parser.map (Maybe.withDefault "")) + , customColumnTypeParser "DOUBLE PRECISION" nothing + , customColumnTypeParser "INT IDENTITY" (maybe numbers |> Parser.map (Maybe.withDefault "")) + , succeed (\name nums -> name ++ nums) + |= identifierOrQuoted + |. spaces + |= (maybe numbers |> Parser.map (Maybe.withDefault "")) + ] + + +customColumnTypeParser : String -> Parser String -> Parser String +customColumnTypeParser name parser = + succeed (\kind value -> kind ++ value) + |= symbolInsensitive name + |. spaces + |= parser + + +numbers : Parser String +numbers = + Parser.map (\nums -> "(" ++ (nums |> List.map String.fromInt |> String.join ",") ++ ")") + (sequence + { start = "(" + , separator = "," + , end = ")" + , spaces = spaces + , item = int + , trailing = Forbidden + } + ) + + +nothing : Parser String +nothing = + succeed "" + + +notNullParser : Parser Bool +notNullParser = + exists (symbolInsensitive "NOT NULL") |> Parser.map not + + +defaultValueParser : Parser (Maybe String) +defaultValueParser = + oneOf + [ succeed (\value kind -> Just (value ++ (kind |> Maybe.withDefault ""))) + |. symbolInsensitive "DEFAULT" + |. spaces + |= (identifierOrQuoted |> getChompedString) + |= oneOf + [ succeed (\t -> Just ("::" ++ t)) + |. symbol "::" + |= columnTypeParser + , succeed + Nothing + ] + , succeed Nothing + ] + + +primaryKeyParser : Parser (Maybe String) +primaryKeyParser = + maybe (succeed "" |. symbolInsensitive "PRIMARY KEY") + + +checkParser : Parser (Maybe String) +checkParser = + oneOf + [ succeed (\str -> str |> String.dropLeft 1 |> String.dropRight 1 |> Just) + |. symbolInsensitive "CHECK" + |. spaces + |= (multiComment "(" ")" Nestable |> getChompedString) + , succeed Nothing + ] + + +constraintParser : Parser (Maybe ( String, ColumnConstraint )) +constraintParser = + maybe + (succeed (\name constraint -> ( name, constraint )) + |. symbolInsensitive "CONSTRAINT" + |. spaces + |= identifierOrQuoted + |. spaces + |= oneOf + [ succeed ColumnPrimaryKey |. symbolInsensitive "PRIMARY KEY" + , foreignKeyParser |> Parser.map ColumnForeignKey + ] + ) + + +foreignKeyParser : Parser ForeignKeyRef +foreignKeyParser = + succeed identity + |. symbolInsensitive "REFERENCES" + |. spaces + |= foreignKeyRefParser + + +foreignKeyRefParser : Parser ForeignKeyRef +foreignKeyRefParser = + oneOf + [ backtrackable <| + succeed (\schema table column -> ForeignKeyRef (Just schema) table (Just column)) + |= identifierOrQuoted + |. symbol "." + |= identifierOrQuoted + |. symbol "." + |= identifierOrQuoted + , backtrackable <| + succeed (\table column -> ForeignKeyRef Nothing table (Just column)) + |= identifierOrQuoted + |. symbol "." + |= identifierOrQuoted + , succeed (\table -> ForeignKeyRef Nothing table Nothing) |= identifierOrQuoted + ] diff --git a/src/DataSources/NewSqlParser/Parsers/CreateTable.elm b/src/DataSources/NewSqlParser/Parsers/CreateTable.elm new file mode 100644 index 000000000..8e05a226d --- /dev/null +++ b/src/DataSources/NewSqlParser/Parsers/CreateTable.elm @@ -0,0 +1,75 @@ +module DataSources.NewSqlParser.Parsers.CreateTable exposing (columnParser, columnsParser, createTableParser) + +import DataSources.NewSqlParser.Dsl exposing (ColumnConstraint(..), ParsedColumn, ParsedTable) +import DataSources.NewSqlParser.Parsers.Basic exposing (checkParser, columnNameParser, columnTypeParser, constraintParser, defaultValueParser, notNullParser, primaryKeyParser, tableRefParser) +import Libs.Maybe as M +import Libs.Parser exposing (symbolInsensitive) +import Parser exposing ((|.), (|=), Parser, Trailing(..), oneOf, sequence, spaces, succeed) + + + +-- https://www.postgresql.org/docs/current/sql-createtable.html +-- https://dev.mysql.com/doc/refman/8.0/en/create-table.html +-- https://docs.microsoft.com/fr-fr/sql/t-sql/statements/create-table-transact-sql +-- https://docs.oracle.com/en/database/oracle/oracle-database/21/sqlrf/CREATE-TABLE.html +-- https://www.sqlite.org/lang_createtable.html + + +createTableParser : Parser ParsedTable +createTableParser = + succeed (\( schemaName, tableName ) columns -> ParsedTable schemaName tableName columns []) + |. symbolInsensitive "CREATE TABLE" + |. spaces + |. oneOf + [ symbolInsensitive "IF NOT EXISTS" + , succeed "" + ] + |. spaces + |= tableRefParser + |. spaces + |= columnsParser + + +columnsParser : Parser (List ParsedColumn) +columnsParser = + sequence + { start = "(" + , separator = "," + , end = ")" + , spaces = spaces + , item = columnParser + , trailing = Forbidden + } + + +columnParser : Parser ParsedColumn +columnParser = + succeed + (\name kind nullable default primaryKey check constraint -> + let + ( pk, fk ) = + case constraint of + Just ( constraintName, ColumnPrimaryKey ) -> + ( primaryKey |> M.orElse (Just constraintName), Nothing ) + + Just ( constraintName, ColumnForeignKey ref ) -> + ( primaryKey, Just ( constraintName, ref ) ) + + Nothing -> + ( primaryKey, Nothing ) + in + ParsedColumn name kind nullable default pk fk check + ) + |= columnNameParser + |. spaces + |= columnTypeParser + |. spaces + |= notNullParser + |. spaces + |= defaultValueParser + |. spaces + |= primaryKeyParser + |. spaces + |= checkParser + |. spaces + |= constraintParser diff --git a/src/DataSources/NewSqlParser/StatementParser.elm b/src/DataSources/NewSqlParser/StatementParser.elm new file mode 100644 index 000000000..facca6038 --- /dev/null +++ b/src/DataSources/NewSqlParser/StatementParser.elm @@ -0,0 +1,6 @@ +module DataSources.NewSqlParser.StatementParser exposing (parse) + + +parse : String -> String +parse file = + file diff --git a/src/Libs/Parser.elm b/src/Libs/Parser.elm new file mode 100644 index 000000000..2dd513269 --- /dev/null +++ b/src/Libs/Parser.elm @@ -0,0 +1,73 @@ +module Libs.Parser exposing (exists, getWhile, identifier, identifierOrQuoted, isSpace, maybe, notSpace, quotedParser, symbolInsensitive) + +import Parser exposing ((|.), (|=), Parser, chompIf, chompWhile, getChompedString, oneOf, succeed, symbol) + + + +-- generic parsers, could be extracted as a lib + + +identifierOrQuoted : Parser String +identifierOrQuoted = + oneOf + [ quotedParser '`' '`' + , quotedParser '\'' '\'' + , quotedParser '"' '"' + , quotedParser '[' ']' + , identifier + ] + + +identifier : Parser String +identifier = + getWhile Char.isAlphaNum (\c -> notSpace c && c /= '.' && c /= ',' && c /= '(' && c /= ')') + + +quotedParser : Char -> Char -> Parser String +quotedParser first last = + succeed identity + |. chompIf (\c -> c == first) + |= (succeed () + |. chompWhile (\c -> c /= last) + |> getChompedString + ) + |. chompIf (\c -> c == last) + + +getWhile : (Char -> Bool) -> (Char -> Bool) -> Parser String +getWhile start end = + succeed () + |. chompIf start + |. chompWhile end + |> getChompedString + + +symbolInsensitive : String -> Parser String +symbolInsensitive name = + oneOf [ symbol (name |> String.toUpper), symbol (name |> String.toLower) ] |> getChompedString + + +maybe : Parser a -> Parser (Maybe a) +maybe p = + oneOf + [ succeed Just |= p + , succeed Nothing + ] + + +exists : Parser a -> Parser Bool +exists p = + oneOf + [ succeed True |. p + , succeed False + ] + + +isSpace : Char -> Bool +isSpace c = + c == ' ' || c == '\n' || c == '\u{000D}' + + +notSpace : Char -> Bool +notSpace c = + not (isSpace c) diff --git a/tests/DataSources/NewSqlParser/FileParserTest.elm b/tests/DataSources/NewSqlParser/FileParserTest.elm new file mode 100644 index 000000000..056ad786c --- /dev/null +++ b/tests/DataSources/NewSqlParser/FileParserTest.elm @@ -0,0 +1,11 @@ +module DataSources.NewSqlParser.FileParserTest exposing (..) + +import Expect +import Test exposing (Test, describe, test) + + +suite : Test +suite = + describe "FileParser" + [ test "aaa" (\_ -> "aaa" |> Expect.equal "aaa") + ] diff --git a/tests/DataSources/NewSqlParser/Parsers/BasicTest.elm b/tests/DataSources/NewSqlParser/Parsers/BasicTest.elm new file mode 100644 index 000000000..f45dfacd4 --- /dev/null +++ b/tests/DataSources/NewSqlParser/Parsers/BasicTest.elm @@ -0,0 +1,74 @@ +module DataSources.NewSqlParser.Parsers.BasicTest exposing (..) + +import DataSources.NewSqlParser.Dsl exposing (ColumnConstraint(..)) +import DataSources.NewSqlParser.Parsers.Basic exposing (checkParser, columnNameParser, columnTypeParser, constraintParser, defaultValueParser, foreignKeyRefParser, notNullParser, primaryKeyParser, schemaNameParser, tableNameParser, tableRefParser) +import Expect +import Parser +import Test exposing (Test, describe, test) + + +suite : Test +suite = + describe "Basic" + [ describe "tableRefParser" + [ test "no schema" (\_ -> "table" |> Parser.run tableRefParser |> Expect.equal (Ok ( Nothing, "table" ))) + , test "with schema" (\_ -> "schema.table" |> Parser.run tableRefParser |> Expect.equal (Ok ( Just "schema", "table" ))) + , test "with brackets" (\_ -> "schema.[Table]" |> Parser.run tableRefParser |> Expect.equal (Ok ( Just "schema", "Table" ))) + ] + , describe "schemaNameParser" + [ test "basic" (\_ -> "schema" |> Parser.run schemaNameParser |> Expect.equal (Ok "schema")) + ] + , describe "tableNameParser" + [ test "basic" (\_ -> "table" |> Parser.run tableNameParser |> Expect.equal (Ok "table")) + , test "brackets" (\_ -> "[table]" |> Parser.run tableNameParser |> Expect.equal (Ok "table")) + ] + , describe "columnNameParser" + [ test "basic" (\_ -> "column" |> Parser.run columnNameParser |> Expect.equal (Ok "column")) + , test "space" (\_ -> "column " |> Parser.run columnTypeParser |> Expect.equal (Ok "column")) + , test "quoted" (\_ -> "'column'" |> Parser.run columnNameParser |> Expect.equal (Ok "column")) + , test "double quoted" (\_ -> "\"column\"" |> Parser.run columnNameParser |> Expect.equal (Ok "column")) + , test "back quoted" (\_ -> "`column`" |> Parser.run columnNameParser |> Expect.equal (Ok "column")) + , test "brackets" (\_ -> "[column]" |> Parser.run columnNameParser |> Expect.equal (Ok "column")) + , test "quoted with space" (\_ -> "'a column'" |> Parser.run columnNameParser |> Expect.equal (Ok "a column")) + , test "brackets with space" (\_ -> "[a column]" |> Parser.run columnNameParser |> Expect.equal (Ok "a column")) + ] + , describe "columnTypeParser" + [ test "basic" (\_ -> "INT" |> Parser.run columnTypeParser |> Expect.equal (Ok "INT")) + , test "space stop" (\_ -> "INT " |> Parser.run columnTypeParser |> Expect.equal (Ok "INT")) + , test "comma stop" (\_ -> "INT," |> Parser.run columnTypeParser |> Expect.equal (Ok "INT")) + , test "parenthesis stop" (\_ -> "INT)" |> Parser.run columnTypeParser |> Expect.equal (Ok "INT")) + , test "varchar" (\_ -> "VARCHAR(255)" |> Parser.run columnTypeParser |> Expect.equal (Ok "VARCHAR(255)")) + , test "bit varying" (\_ -> "bit varying(8)" |> Parser.run columnTypeParser |> Expect.equal (Ok "bit varying(8)")) + , test "character varying" (\_ -> "character varying ( 255 )" |> Parser.run columnTypeParser |> Expect.equal (Ok "character varying(255)")) + , test "double precision" (\_ -> "double precision" |> Parser.run columnTypeParser |> Expect.equal (Ok "double precision")) + , test "numeric" (\_ -> "numeric(4, 2)" |> Parser.run columnTypeParser |> Expect.equal (Ok "numeric(4,2)")) + ] + , describe "notNullParser" + [ test "not null" (\_ -> "NOT NULL" |> Parser.run notNullParser |> Expect.equal (Ok False)) + , test "nullable" (\_ -> "" |> Parser.run notNullParser |> Expect.equal (Ok True)) + ] + , describe "defaultValueParser" + [ test "no value" (\_ -> "" |> Parser.run defaultValueParser |> Expect.equal (Ok Nothing)) + , test "int value" (\_ -> "DEFAULT 42" |> Parser.run defaultValueParser |> Expect.equal (Ok (Just "42"))) + , test "lowercase" (\_ -> "default 42" |> Parser.run defaultValueParser |> Expect.equal (Ok (Just "42"))) + , test "string value" (\_ -> "DEFAULT 'some value'" |> Parser.run defaultValueParser |> Expect.equal (Ok (Just "'some value'"))) + , test "typed value" (\_ -> "DEFAULT '{}'::bigint[]" |> Parser.run defaultValueParser |> Expect.equal (Ok (Just "'{}'::bigint[]"))) + , test "type with space" (\_ -> "DEFAULT 'aa'::character varying" |> Parser.run defaultValueParser |> Expect.equal (Ok (Just "'aa'::character varying"))) + ] + , describe "primaryKeyParser" + [ test "primary key" (\_ -> "PRIMARY KEY" |> Parser.run primaryKeyParser |> Expect.equal (Ok (Just ""))) + , test "no primary key" (\_ -> "" |> Parser.run primaryKeyParser |> Expect.equal (Ok Nothing)) + ] + , describe "checkParser" + [ test "check" (\_ -> "check(LEN(name) > 4)" |> Parser.run checkParser |> Expect.equal (Ok (Just "LEN(name) > 4"))) + , test "with space" (\_ -> "CHECK (LEN(name) > 4)" |> Parser.run checkParser |> Expect.equal (Ok (Just "LEN(name) > 4"))) + , test "no check" (\_ -> "" |> Parser.run checkParser |> Expect.equal (Ok Nothing)) + ] + , describe "constraintParser" + [ test "primary key" (\_ -> "CONSTRAINT pk PRIMARY KEY" |> Parser.run constraintParser |> Expect.equal (Ok (Just ( "pk", ColumnPrimaryKey )))) + , test "foreign key" (\_ -> "CONSTRAINT fk REFERENCES schema.table.column" |> Parser.run constraintParser |> Expect.equal (Ok (Just ( "fk", ColumnForeignKey { schema = Just "schema", table = "table", column = Just "column" } )))) + ] + , describe "foreignKeyRefParser" + [ test "all" (\_ -> "schema.table.column" |> Parser.run foreignKeyRefParser |> Expect.equal (Ok { schema = Just "schema", table = "table", column = Just "column" })) + ] + ] diff --git a/tests/DataSources/NewSqlParser/Parsers/CreateTableTest.elm b/tests/DataSources/NewSqlParser/Parsers/CreateTableTest.elm new file mode 100644 index 000000000..47691d976 --- /dev/null +++ b/tests/DataSources/NewSqlParser/Parsers/CreateTableTest.elm @@ -0,0 +1,104 @@ +module DataSources.NewSqlParser.Parsers.CreateTableTest exposing (..) + +import DataSources.NewSqlParser.Dsl exposing (ParsedColumn, ParsedConstraint(..), ParsedTable, SqlStatement) +import DataSources.NewSqlParser.Parsers.CreateTable exposing (columnParser, columnsParser, createTableParser) +import Expect +import Libs.Nel exposing (Nel) +import Parser +import Test exposing (Test, describe, skip, test) + + +suite : Test +suite = + describe "CreateTable" + [ testParse "basic" + """CREATE TABLE users ( + id INT, + name VARCHAR + );""" + { schema = Nothing + , table = "users" + , columns = + [ { column | name = "id", kind = "INT" } + , { column | name = "name", kind = "VARCHAR" } + ] + , constraints = [] + } + , testParse "with schema, quotes, not null, primary key and default" + """CREATE TABLE IF NOT EXISTS public.users( + `id` INT NOT NULL PRIMARY KEY, + 'name' character varying(255) DEFAULT 'no name' + );""" + { schema = Just "public" + , table = "users" + , columns = + [ { column | name = "id", kind = "INT", nullable = False, primaryKey = Just "" } + , { column | name = "name", kind = "character varying(255)", default = Just "'no name'" } + ] + , constraints = [] + } + , testParse "WIP" + """CREATE TABLE [Users] ( + [id] int identity(1,1) NOT NULL CONSTRAINT users_pk PRIMARY KEY + , "name" VARCHAR(255) check(LEN(name) > 4) + , bio text default ''::character varying + , profile_id INT CONSTRAINT users_profile_fk REFERENCES public.profiles.id + );""" + { schema = Nothing + , table = "Users" + , columns = + [ { column | name = "id", kind = "int identity(1,1)", nullable = False, primaryKey = Just "users_pk" } + , { column | name = "name", kind = "VARCHAR(255)", check = Just "LEN(name) > 4" } + , { column | name = "bio", kind = "text", default = Just "''::character varying" } + , { column | name = "profile_id", kind = "INT", foreignKey = Just ( "users_profile_fk", { schema = Just "public", table = "profiles", column = Just "id" } ) } + ] + , constraints = [] + } + , skip + (testParse "with constraints" + """CREATE TABLE [Users] ( + [id] int identity(1,1) NOT NULL CONSTRAINT users_pk PRIMARY KEY + , "name" VARCHAR(255) check(LEN(name) > 4) + , bio text default ''::character varying + , profile_id INT CONSTRAINT users_profile_fk REFERENCES public.profiles.id + , foreign key(`profile_id`) references `profiles`(`id`) + , constraint `no_duplicate_name` unique (`name`) + , CONSTRAINT bio_not_null CHECK (bio IS NOT NULL) + );""" + { schema = Nothing + , table = "Users" + , columns = + [ { column | name = "id", kind = "int identity(1,1)", nullable = False, primaryKey = Just "users_pk" } + , { column | name = "name", kind = "VARCHAR(255)", check = Just "LEN(name) > 4" } + , { column | name = "bio", kind = "text" } + , { column | name = "profile_id", kind = "INT", foreignKey = Just ( "users_profile_fk", { schema = Just "public", table = "profiles", column = Just "id" } ) } + ] + , constraints = + [ ForeignKey { name = Nothing, src = "profile_id", ref = { schema = Nothing, table = "profiles", column = Just "id" } } + , Unique { name = "no_duplicate_name", columns = Nel "name" [] } + , Check { name = "bio_not_null", columns = [], predicate = "bio IS NOT NULL" } + ] + } + ) + , describe "columnsParser" + [ test "single" (\_ -> "(id INT)" |> Parser.run columnsParser |> Expect.equal (Ok [ { column | name = "id", kind = "INT" } ])) + , test "multiple" (\_ -> "(id INT, name VARCHAR)" |> Parser.run columnsParser |> Expect.equal (Ok [ { column | name = "id", kind = "INT" }, { column | name = "name", kind = "VARCHAR" } ])) + ] + , describe "columnParser" + [ test "basic" (\_ -> "id INT" |> Parser.run columnParser |> Expect.equal (Ok { column | name = "id", kind = "INT" })) + , test "not null" (\_ -> "id INT NOT NULL" |> Parser.run columnParser |> Expect.equal (Ok { column | name = "id", kind = "INT", nullable = False })) + , test "primary key" (\_ -> "id INT PRIMARY KEY" |> Parser.run columnParser |> Expect.equal (Ok { column | name = "id", kind = "INT", primaryKey = Just "" })) + , test "default" (\_ -> "id INT DEFAULT 1" |> Parser.run columnParser |> Expect.equal (Ok { column | name = "id", kind = "INT", default = Just "1" })) + , test "all" (\_ -> "id INT NOT NULL DEFAULT 1 PRIMARY KEY" |> Parser.run columnParser |> Expect.equal (Ok { column | name = "id", kind = "INT", nullable = False, primaryKey = Just "", default = Just "1" })) + ] + ] + + +testParse : String -> SqlStatement -> ParsedTable -> Test +testParse name sql result = + test name (\_ -> sql |> Parser.run createTableParser |> Expect.equal (Ok result)) + + +column : ParsedColumn +column = + { name = "", kind = "", nullable = True, default = Nothing, primaryKey = Nothing, foreignKey = Nothing, check = Nothing } diff --git a/tests/DataSources/NewSqlParser/StatementParserTest.elm b/tests/DataSources/NewSqlParser/StatementParserTest.elm new file mode 100644 index 000000000..7f51b2d16 --- /dev/null +++ b/tests/DataSources/NewSqlParser/StatementParserTest.elm @@ -0,0 +1,11 @@ +module DataSources.NewSqlParser.StatementParserTest exposing (..) + +import Expect +import Test exposing (Test, describe, test) + + +suite : Test +suite = + describe "StatementParser" + [ test "aaa" (\_ -> "aaa" |> Expect.equal "aaa") + ] diff --git a/tests/Libs/ParserTest.elm b/tests/Libs/ParserTest.elm new file mode 100644 index 000000000..ea10af099 --- /dev/null +++ b/tests/Libs/ParserTest.elm @@ -0,0 +1,17 @@ +module Libs.ParserTest exposing (..) + +import Expect +import Libs.Parser exposing (quotedParser) +import Parser +import Test exposing (Test, describe, test) + + +suite : Test +suite = + describe "Parser" + [ describe "quotedParser" + [ test "brackets" (\_ -> "[text]" |> Parser.run (quotedParser '[' ']') |> Expect.equal (Ok "text")) + , test "backquotes" (\_ -> "`text`" |> Parser.run (quotedParser '`' '`') |> Expect.equal (Ok "text")) + , test "empty" (\_ -> "''" |> Parser.run (quotedParser '\'' '\'') |> Expect.equal (Ok "")) + ] + ]