// Copyright 2021 Molecula Corp. All rights reserved. package parser_test import ( "strings" "testing" "github.com/go-test/deep" "github.com/molecula/featurebase/v3/sql3/parser" ) func TestParser_ParseNonNullColumnConstraints(t *testing.T) { t.Run("NotNull", func(t *testing.T) { t.Run("ErrNoKey", func(t *testing.T) { AssertParseStatementError(t, `CREATE TABLE tbl (col1 STRING`, `1:29: expected column name, or right paren, found 'EOF'`) }) t.Run("Simple", func(t *testing.T) { AssertParseStatement(t, `CREATE TABLE tbl (col1 STRING)`, &parser.CreateTableStatement{ Create: pos(0), Table: pos(7), Name: &parser.Ident{Name: "tbl", NamePos: pos(13)}, Lparen: pos(17), Columns: []*parser.ColumnDefinition{ { Name: &parser.Ident{Name: "col1", NamePos: pos(18)}, Type: &parser.Type{ Name: &parser.Ident{Name: "STRING", NamePos: pos(23)}, }, }, }, Rparen: pos(29), }) }) }) } func TestParser_ParseMinMaxColumnConstraints(t *testing.T) { t.Run("Min", func(t *testing.T) { t.Run("ErrNoKey", func(t *testing.T) { AssertParseStatementError(t, `CREATE TABLE tbl (col1 INT MIN`, `1:30: expected expression, found 'EOF'`) }) t.Run("Simple", func(t *testing.T) { AssertParseStatement(t, `CREATE TABLE tbl (col1 INT MIN 0)`, &parser.CreateTableStatement{ Create: pos(0), Table: pos(7), Name: &parser.Ident{Name: "tbl", NamePos: pos(13)}, Lparen: pos(17), Columns: []*parser.ColumnDefinition{ { Name: &parser.Ident{Name: "col1", NamePos: pos(18)}, Type: &parser.Type{ Name: &parser.Ident{Name: "INT", NamePos: pos(23)}, }, Constraints: []parser.Constraint{ &parser.MinConstraint{ Min: pos(27), Expr: &parser.IntegerLit{ ValuePos: pos(31), Value: "0", }, }, }, }, }, Rparen: pos(32), }) }) t.Run("SimpleBoth", func(t *testing.T) { AssertParseStatement(t, `CREATE TABLE tbl (col1 INT MIN 0 MAX 1024)`, &parser.CreateTableStatement{ Create: pos(0), Table: pos(7), Name: &parser.Ident{Name: "tbl", NamePos: pos(13)}, Lparen: pos(17), Columns: []*parser.ColumnDefinition{ { Name: &parser.Ident{Name: "col1", NamePos: pos(18)}, Type: &parser.Type{ Name: &parser.Ident{Name: "INT", NamePos: pos(23)}, }, Constraints: []parser.Constraint{ &parser.MinConstraint{ Min: pos(27), Expr: &parser.IntegerLit{ ValuePos: pos(31), Value: "0", }, }, &parser.MaxConstraint{ Max: pos(33), Expr: &parser.IntegerLit{ ValuePos: pos(37), Value: "1024", }, }, }, }, }, Rparen: pos(41), }) }) t.Run("NegativeIntegers", func(t *testing.T) { AssertParseStatement(t, `CREATE TABLE tbl (col1 INT MIN -400 MAX -100)`, &parser.CreateTableStatement{ Create: pos(0), Table: pos(7), Name: &parser.Ident{Name: "tbl", NamePos: pos(13)}, Lparen: pos(17), Columns: []*parser.ColumnDefinition{ { Name: &parser.Ident{Name: "col1", NamePos: pos(18)}, Type: &parser.Type{ Name: &parser.Ident{Name: "INT", NamePos: pos(23)}, }, Constraints: []parser.Constraint{ &parser.MinConstraint{ Min: pos(27), Expr: &parser.UnaryExpr{ OpPos: pos(31), Op: parser.MINUS, X: &parser.IntegerLit{ ValuePos: pos(32), Value: `400`, }, }, }, &parser.MaxConstraint{ Max: pos(36), Expr: &parser.UnaryExpr{ OpPos: pos(40), Op: parser.MINUS, X: &parser.IntegerLit{ ValuePos: pos(41), Value: `100`, }, }, }, }, }, }, Rparen: pos(44), }) }) t.Run("PositiveDecimals", func(t *testing.T) { AssertParseStatement(t, `CREATE TABLE tbl (col1 DECIMAL MIN 2.34 MAX 12.87)`, &parser.CreateTableStatement{ Create: pos(0), Table: pos(7), Name: &parser.Ident{Name: "tbl", NamePos: pos(13)}, Lparen: pos(17), Columns: []*parser.ColumnDefinition{ { Name: &parser.Ident{Name: "col1", NamePos: pos(18)}, Type: &parser.Type{ Name: &parser.Ident{Name: "DECIMAL", NamePos: pos(23)}, }, Constraints: []parser.Constraint{ &parser.MinConstraint{ Min: pos(31), Expr: &parser.FloatLit{ ValuePos: pos(35), Value: "2.34", }, }, &parser.MaxConstraint{ Max: pos(40), Expr: &parser.FloatLit{ ValuePos: pos(44), Value: "12.87", }, }, }, }, }, Rparen: pos(49), }) }) t.Run("NegativeDecimals", func(t *testing.T) { AssertParseStatement(t, `CREATE TABLE tbl (col1 DECIMAL MIN -12.34 MAX -2.87)`, &parser.CreateTableStatement{ Create: pos(0), Table: pos(7), Name: &parser.Ident{Name: "tbl", NamePos: pos(13)}, Lparen: pos(17), Columns: []*parser.ColumnDefinition{ { Name: &parser.Ident{Name: "col1", NamePos: pos(18)}, Type: &parser.Type{ Name: &parser.Ident{Name: "DECIMAL", NamePos: pos(23)}, }, Constraints: []parser.Constraint{ &parser.MinConstraint{ Min: pos(31), Expr: &parser.UnaryExpr{ OpPos: pos(35), Op: parser.MINUS, X: &parser.FloatLit{ ValuePos: pos(36), Value: `12.34`, }, }, }, &parser.MaxConstraint{ Max: pos(42), Expr: &parser.UnaryExpr{ OpPos: pos(46), Op: parser.MINUS, X: &parser.FloatLit{ ValuePos: pos(47), Value: `2.87`, }, }, }, }, }, }, Rparen: pos(51), }) }) }) } func TestParser_ParseTimeUnitConstraints(t *testing.T) { t.Run("TimeUnit", func(t *testing.T) { t.Run("ErrNoKey", func(t *testing.T) { AssertParseStatementError(t, `CREATE TABLE tbl (col1 STRING TIMEUNIT`, `1:38: expected literal, found 'EOF'`) }) t.Run("Simple", func(t *testing.T) { AssertParseStatement(t, `CREATE TABLE tbl (col1 STRING TIMEUNIT 's')`, &parser.CreateTableStatement{ Create: pos(0), Table: pos(7), Name: &parser.Ident{Name: "tbl", NamePos: pos(13)}, Lparen: pos(17), Columns: []*parser.ColumnDefinition{ { Name: &parser.Ident{Name: "col1", NamePos: pos(18)}, Type: &parser.Type{ Name: &parser.Ident{Name: "STRING", NamePos: pos(23)}, }, Constraints: []parser.Constraint{ &parser.TimeUnitConstraint{ TimeUnit: pos(30), Expr: &parser.StringLit{ ValuePos: pos(39), Value: "s", }, }, }, }, }, Rparen: pos(42), }) }) }) } func TestParser_ParseTimeQuantumConstraints(t *testing.T) { t.Run("TimeQuantum", func(t *testing.T) { t.Run("ErrNoKey", func(t *testing.T) { AssertParseStatementError(t, `CREATE TABLE tbl (col1 STRING TIMEQUANTUM`, `1:41: expected literal, found 'EOF'`) }) t.Run("Simple", func(t *testing.T) { AssertParseStatement(t, `CREATE TABLE tbl (col1 STRING TIMEQUANTUM 'YMD')`, &parser.CreateTableStatement{ Create: pos(0), Table: pos(7), Name: &parser.Ident{Name: "tbl", NamePos: pos(13)}, Lparen: pos(17), Columns: []*parser.ColumnDefinition{ { Name: &parser.Ident{Name: "col1", NamePos: pos(18)}, Type: &parser.Type{ Name: &parser.Ident{Name: "STRING", NamePos: pos(23)}, }, Constraints: []parser.Constraint{ &parser.TimeQuantumConstraint{ TimeQuantum: pos(30), Expr: &parser.StringLit{ ValuePos: pos(42), Value: "YMD", }, }, }, }, }, Rparen: pos(47), }) }) }) } func TestParser_ParseCacheTypeConstraints(t *testing.T) { t.Run("CacheType", func(t *testing.T) { t.Run("ErrNoKey", func(t *testing.T) { AssertParseStatementError(t, `CREATE TABLE tbl (col1 INT CACHETYPE`, `1:36: expected RANKED or LRU, found 'EOF'`) }) t.Run("Simple", func(t *testing.T) { AssertParseStatement(t, `CREATE TABLE tbl (col1 INT CACHETYPE RANKED SIZE 1024)`, &parser.CreateTableStatement{ Create: pos(0), Table: pos(7), Name: &parser.Ident{Name: "tbl", NamePos: pos(13)}, Lparen: pos(17), Columns: []*parser.ColumnDefinition{ { Name: &parser.Ident{Name: "col1", NamePos: pos(18)}, Type: &parser.Type{ Name: &parser.Ident{Name: "INT", NamePos: pos(23)}, }, Constraints: []parser.Constraint{ &parser.CacheTypeConstraint{ CacheType: pos(27), CacheTypeValue: "ranked", Size: pos(44), SizeExpr: &parser.IntegerLit{ ValuePos: pos(49), Value: "1024", }, }, }, }, }, Rparen: pos(53), }) }) t.Run("Simple2", func(t *testing.T) { AssertParseStatement(t, `CREATE TABLE tbl (col1 INT CACHETYPE LRU SIZE 1024)`, &parser.CreateTableStatement{ Create: pos(0), Table: pos(7), Name: &parser.Ident{Name: "tbl", NamePos: pos(13)}, Lparen: pos(17), Columns: []*parser.ColumnDefinition{ { Name: &parser.Ident{Name: "col1", NamePos: pos(18)}, Type: &parser.Type{ Name: &parser.Ident{Name: "INT", NamePos: pos(23)}, }, Constraints: []parser.Constraint{ &parser.CacheTypeConstraint{ CacheType: pos(27), CacheTypeValue: "lru", Size: pos(41), SizeExpr: &parser.IntegerLit{ ValuePos: pos(46), Value: "1024", }, }, }, }, }, Rparen: pos(50), }) }) }) } func TestParser_ParseAlterStatement(t *testing.T) { t.Run("AlterTable", func(t *testing.T) { /*AssertParseStatement(t, `ALTER TABLE tbl RENAME TO new_tbl`, &parser.AlterTableStatement{ Alter: pos(0), Table: pos(6), Name: &parser.Ident{NamePos: pos(12), Name: "tbl"}, Rename: pos(16), RenameTo: pos(23), NewName: &parser.Ident{NamePos: pos(26), Name: "new_tbl"}, })*/ AssertParseStatement(t, `ALTER TABLE tbl RENAME COLUMN col TO new_col`, &parser.AlterTableStatement{ Alter: pos(0), Table: pos(6), Name: &parser.Ident{NamePos: pos(12), Name: "tbl"}, Rename: pos(16), RenameColumn: pos(23), OldColumnName: &parser.Ident{NamePos: pos(30), Name: "col"}, To: pos(34), NewColumnName: &parser.Ident{NamePos: pos(37), Name: "new_col"}, }) AssertParseStatement(t, `ALTER TABLE tbl RENAME col TO new_col`, &parser.AlterTableStatement{ Alter: pos(0), Table: pos(6), Name: &parser.Ident{NamePos: pos(12), Name: "tbl"}, Rename: pos(16), OldColumnName: &parser.Ident{NamePos: pos(23), Name: "col"}, To: pos(27), NewColumnName: &parser.Ident{NamePos: pos(30), Name: "new_col"}, }) /*AssertParseStatement(t, `ALTER TABLE tbl ADD COLUMN col TEXT PRIMARY KEY`, &parser.AlterTableStatement{ Alter: pos(0), Table: pos(6), Name: &parser.Ident{NamePos: pos(12), Name: "tbl"}, Add: pos(16), AddColumn: pos(20), ColumnDef: &parser.ColumnDefinition{ Name: &parser.Ident{Name: "col", NamePos: pos(27)}, Type: &parser.Type{ Name: &parser.Ident{Name: "TEXT", NamePos: pos(31)}, }, Constraints: []sql.Constraint{ &parser.PrimaryKeyConstraint{ Primary: pos(36), Key: pos(44), }, }, }, })*/ AssertParseStatement(t, `ALTER TABLE tbl ADD col TEXT`, &parser.AlterTableStatement{ Alter: pos(0), Table: pos(6), Name: &parser.Ident{NamePos: pos(12), Name: "tbl"}, Add: pos(16), ColumnDef: &parser.ColumnDefinition{ Name: &parser.Ident{Name: "col", NamePos: pos(20)}, Type: &parser.Type{ Name: &parser.Ident{Name: "TEXT", NamePos: pos(24)}, }, }, }) AssertParseStatement(t, `ALTER TABLE tbl DROP col`, &parser.AlterTableStatement{ Alter: pos(0), Table: pos(6), Name: &parser.Ident{NamePos: pos(12), Name: "tbl"}, Drop: pos(16), DropColumnName: &parser.Ident{NamePos: pos(21), Name: "col"}, }) AssertParseStatement(t, `ALTER TABLE tbl DROP COLUMN col`, &parser.AlterTableStatement{ Alter: pos(0), Table: pos(6), Name: &parser.Ident{NamePos: pos(12), Name: "tbl"}, Drop: pos(16), DropColumn: pos(21), DropColumnName: &parser.Ident{NamePos: pos(28), Name: "col"}, }) AssertParseStatementError(t, `ALTER`, `1:5: expected TABLE, found 'EOF'`) AssertParseStatementError(t, `ALTER TABLE`, `1:11: expected table name, found 'EOF'`) AssertParseStatementError(t, `ALTER TABLE tbl`, `1:15: expected ADD, DROP or RENAME, found 'EOF'`) AssertParseStatementError(t, `ALTER TABLE tbl RENAME`, `1:22: expected COLUMN keyword or column name, found 'EOF'`) //AssertParseStatementError(t, `ALTER TABLE tbl RENAME TO`, `1:25: expected new table name, found 'EOF'`) AssertParseStatementError(t, `ALTER TABLE tbl RENAME COLUMN`, `1:29: expected column name, found 'EOF'`) AssertParseStatementError(t, `ALTER TABLE tbl RENAME COLUMN col`, `1:33: expected TO, found 'EOF'`) AssertParseStatementError(t, `ALTER TABLE tbl RENAME COLUMN col TO`, `1:36: expected new column name, found 'EOF'`) AssertParseStatementError(t, `ALTER TABLE tbl ADD`, `1:19: expected COLUMN keyword or column name, found 'EOF'`) AssertParseStatementError(t, `ALTER TABLE tbl ADD COLUMN`, `1:26: expected column name, found 'EOF'`) }) } func TestParser_ParseFunctionStatement(t *testing.T) { t.Run("CreateFunction", func(t *testing.T) { AssertParseStatement(t, `CREATE FUNCTION IF NOT EXISTS func (@param1 int, @param2 string) returns @scalar int as begin end`, &parser.CreateFunctionStatement{ Create: pos(0), Function: pos(7), If: pos(16), IfNot: pos(19), IfNotExists: pos(23), Name: &parser.Ident{NamePos: pos(30), Name: "func"}, Lparen: pos(35), Parameters: []*parser.ParameterDefinition{ { Name: &parser.Variable{Name: "@param1", NamePos: pos(36)}, Type: &parser.Type{Name: &parser.Ident{NamePos: pos(44), Name: "int"}}, }, { Name: &parser.Variable{Name: "@param2", NamePos: pos(49)}, Type: &parser.Type{Name: &parser.Ident{NamePos: pos(57), Name: "string"}}, }, }, Rparen: pos(63), Returns: pos(65), ReturnDef: &parser.ParameterDefinition{ Name: &parser.Variable{Name: "@scalar", NamePos: pos(73)}, Type: &parser.Type{Name: &parser.Ident{NamePos: pos(81), Name: "int"}}, }, As: pos(85), Begin: pos(88), End: pos(94), }) // AssertParseStatement(t, `CREATE TRIGGER IF NOT EXISTS trig BEFORE INSERT ON tbl BEGIN DELETE FROM new; END`, &parser.CreateFunctionStatement{ // Create: pos(0), // Function: pos(7), // If: pos(15), // IfNot: pos(18), // IfNotExists: pos(22), // Name: &parser.Ident{NamePos: pos(29), Name: "trig"}, // Before: pos(34), // Insert: pos(41), // On: pos(48), // Table: &parser.Ident{NamePos: pos(51), Name: "tbl"}, // Begin: pos(55), // Body: []parser.Statement{ // &parser.DeleteStatement{ // Delete: pos(61), // From: pos(68), // Table: &parser.QualifiedTableName{ // Name: &parser.Ident{NamePos: pos(73), Name: "new"}, // }, // }, // }, // End: pos(78), // }) // AssertParseStatement(t, `CREATE TRIGGER trig INSTEAD OF UPDATE ON tbl BEGIN SELECT *; END`, &parser.CreateFunctionStatement{ // Create: pos(0), // Function: pos(7), // Name: &parser.Ident{NamePos: pos(15), Name: "trig"}, // Instead: pos(20), // InsteadOf: pos(28), // Update: pos(31), // On: pos(38), // Table: &parser.Ident{NamePos: pos(41), Name: "tbl"}, // Begin: pos(45), // Body: []parser.Statement{ // &parser.SelectStatement{ // Select: pos(51), // Columns: []*parser.ResultColumn{{Star: pos(58)}}, // }, // }, // End: pos(61), // }) // AssertParseStatement(t, `CREATE TRIGGER trig INSTEAD OF UPDATE OF x, y ON tbl FOR EACH ROW WHEN true BEGIN SELECT *; END`, &parser.CreateFunctionStatement{ // Create: pos(0), // Function: pos(7), // Name: &parser.Ident{NamePos: pos(15), Name: "trig"}, // Instead: pos(20), // InsteadOf: pos(28), // Update: pos(31), // UpdateOf: pos(38), // UpdateOfColumns: []*parser.Ident{ // {NamePos: pos(41), Name: "x"}, // {NamePos: pos(44), Name: "y"}, // }, // On: pos(46), // Table: &parser.Ident{NamePos: pos(49), Name: "tbl"}, // For: pos(53), // ForEach: pos(57), // ForEachRow: pos(62), // When: pos(66), // WhenExpr: &parser.BoolLit{ValuePos: pos(71), Value: true}, // Begin: pos(76), // Body: []parser.Statement{ // &parser.SelectStatement{ // Select: pos(82), // Columns: []*parser.ResultColumn{{Star: pos(89)}}, // }, // }, // End: pos(92), // }) // AssertParseStatement(t, `CREATE TRIGGER trig AFTER UPDATE ON tbl BEGIN WITH cte (x) AS (SELECT y) SELECT *; END`, &parser.CreateFunctionStatement{ // Create: pos(0), // Function: pos(7), // Name: &parser.Ident{NamePos: pos(15), Name: "trig"}, // After: pos(20), // Update: pos(26), // On: pos(33), // Table: &parser.Ident{NamePos: pos(36), Name: "tbl"}, // Begin: pos(40), // Body: []parser.Statement{ // &parser.SelectStatement{ // WithClause: &parser.WithClause{ // With: pos(46), // CTEs: []*parser.CTE{ // { // TableName: &parser.Ident{NamePos: pos(51), Name: "cte"}, // ColumnsLparen: pos(55), // Columns: []*parser.Ident{ // {NamePos: pos(56), Name: "x"}, // }, // ColumnsRparen: pos(57), // As: pos(59), // SelectLparen: pos(62), // Select: &parser.SelectStatement{ // Select: pos(63), // Columns: []*parser.ResultColumn{ // {Expr: &parser.Ident{NamePos: pos(70), Name: "y"}}, // }, // }, // SelectRparen: pos(71), // }, // }, // }, // Select: pos(73), // Columns: []*parser.ResultColumn{{Star: pos(80)}}, // }, // }, // End: pos(83), // }) AssertParseStatementError(t, `CREATE FUNCTION`, `1:15: expected function name, found 'EOF'`) AssertParseStatementError(t, `CREATE FUNCTION IF`, `1:18: expected NOT, found 'EOF'`) AssertParseStatementError(t, `CREATE FUNCTION IF NOT`, `1:22: expected EXISTS, found 'EOF'`) // AssertParseStatementError(t, `CREATE FUNCTION trig INSTEAD`, `1:27: expected OF, found 'EOF'`) // AssertParseStatementError(t, `CREATE FUNCTION trig AFTER`, `1:25: expected DELETE, INSERT, or UPDATE, found 'EOF'`) // AssertParseStatementError(t, `CREATE FUNCTION trig UPDATE OF`, `1:29: expected column name, found 'EOF'`) // AssertParseStatementError(t, `CREATE TRIGGER trig UPDATE OF x,`, `1:32: expected column name, found 'EOF'`) // AssertParseStatementError(t, `CREATE TRIGGER trig AFTER INSERT`, `1:32: expected ON, found 'EOF'`) // AssertParseStatementError(t, `CREATE TRIGGER trig AFTER INSERT ON `, `1:36: expected table name, found 'EOF'`) // AssertParseStatementError(t, `CREATE TRIGGER trig AFTER INSERT ON tbl FOR`, `1:43: expected EACH, found 'EOF'`) // AssertParseStatementError(t, `CREATE TRIGGER trig AFTER INSERT ON tbl FOR EACH`, `1:48: expected ROW, found 'EOF'`) // AssertParseStatementError(t, `CREATE TRIGGER trig AFTER INSERT ON tbl WHEN`, `1:44: expected expression, found 'EOF'`) // AssertParseStatementError(t, `CREATE TRIGGER trig AFTER INSERT ON tbl`, `1:39: expected BEGIN, found 'EOF'`) // AssertParseStatementError(t, `CREATE TRIGGER trig AFTER INSERT ON tbl BEGIN`, `1:45: expected statement, found 'EOF'`) // AssertParseStatementError(t, `CREATE TRIGGER trig AFTER INSERT ON tbl BEGIN SELECT`, `1:52: expected expression, found 'EOF'`) // AssertParseStatementError(t, `CREATE TRIGGER trig AFTER INSERT ON tbl BEGIN SELECT *`, `1:54: expected semicolon, found 'EOF'`) // AssertParseStatementError(t, `CREATE TRIGGER trig AFTER INSERT ON tbl BEGIN SELECT *;`, `1:55: expected statement, found 'EOF'`) }) t.Run("DropFunction", func(t *testing.T) { AssertParseStatement(t, `DROP FUNCTION func`, &parser.DropFunctionStatement{ Drop: pos(0), Trigger: pos(5), Name: &parser.Ident{NamePos: pos(14), Name: "func"}, }) AssertParseStatement(t, `DROP FUNCTION IF EXISTS func`, &parser.DropFunctionStatement{ Drop: pos(0), Trigger: pos(5), If: pos(14), IfExists: pos(17), Name: &parser.Ident{NamePos: pos(24), Name: "func"}, }) AssertParseStatementError(t, `DROP FUNCTION`, `1:13: expected function name, found 'EOF'`) AssertParseStatementError(t, `DROP FUNCTION IF`, `1:16: expected EXISTS, found 'EOF'`) AssertParseStatementError(t, `DROP FUNCTION IF EXISTS`, `1:23: expected function name, found 'EOF'`) }) } func TestParser_ParseStatement(t *testing.T) { t.Run("ErrNoStatement", func(t *testing.T) { AssertParseStatementError(t, `123`, `1:1: expected statement, found 123`) }) t.Run("ShowTables", func(t *testing.T) { AssertParseStatement(t, `SHOW TABLES`, &parser.ShowTablesStatement{ Show: pos(0), Tables: pos(5), }) AssertParseStatementError(t, `SHOW`, `1:4: expected TABLES, COLUMNS or CREATE, found 'EOF'`) AssertParseStatementError(t, `SHOW BLAH`, `1:6: expected TABLES, COLUMNS or CREATE, found BLAH`) }) t.Run("ShowColumns", func(t *testing.T) { AssertParseStatement(t, `SHOW COLUMNS FROM FOO`, &parser.ShowColumnsStatement{ Show: pos(0), Columns: pos(5), From: pos(13), TableName: &parser.Ident{ Name: "FOO", NamePos: pos(18), }, }) AssertParseStatementError(t, `SHOW`, `1:4: expected TABLES, COLUMNS or CREATE, found 'EOF'`) AssertParseStatementError(t, `SHOW COLUMNS`, `1:12: expected FROM, found 'EOF'`) AssertParseStatementError(t, `SHOW COLUMNS FOO`, `1:14: expected FROM, found FOO`) AssertParseStatementError(t, `SHOW COLUMNS FROM`, `1:17: expected table name, found 'EOF'`) AssertParseStatementError(t, `SHOW COLUMNS FROM 12`, `1:19: expected table name, found 12`) }) t.Run("ShowCreateTable", func(t *testing.T) { AssertParseStatement(t, `SHOW CREATE TABLE FOO`, &parser.ShowCreateTableStatement{ Show: pos(0), Create: pos(5), Table: pos(12), TableName: &parser.Ident{ Name: "FOO", NamePos: pos(18), }, }) AssertParseStatementError(t, `SHOW`, `1:4: expected TABLES, COLUMNS or CREATE, found 'EOF'`) AssertParseStatementError(t, `SHOW CREATE`, `1:11: expected TABLES, found 'EOF'`) AssertParseStatementError(t, `SHOW CREATE TABLE`, `1:17: expected table name, found 'EOF'`) AssertParseStatementError(t, `SHOW CREATE TABLE 12`, `1:19: expected table name, found 12`) }) t.Run("Explain", func(t *testing.T) { /*t.Run("", func(t *testing.T) { AssertParseStatement(t, `EXPLAIN BEGIN`, &parser.ExplainStatement{ Explain: pos(0), Stmt: &parser.BeginStatement{ Begin: pos(8), }, }) })*/ /*t.Run("QueryPlan", func(t *testing.T) { AssertParseStatement(t, `EXPLAIN QUERY PLAN BEGIN`, &parser.ExplainStatement{ Explain: pos(0), Query: pos(8), QueryPlan: pos(14), Stmt: &parser.BeginStatement{ Begin: pos(19), }, }) })*/ /*t.Run("ErrNoPlan", func(t *testing.T) { AssertParseStatementError(t, `EXPLAIN QUERY`, `1:13: expected PLAN, found 'EOF'`) })*/ /* t.Run("ErrStmt", func(t *testing.T) { AssertParseStatementError(t, `EXPLAIN CREATE`, `1:9: expected TABLE, VIEW, INDEX, TRIGGER`) })*/ }) /*t.Run("Begin", func(t *testing.T) { t.Run("", func(t *testing.T) { AssertParseStatement(t, `BEGIN`, &parser.BeginStatement{ Begin: pos(0), }) }) t.Run("Transaction", func(t *testing.T) { AssertParseStatement(t, `BEGIN TRANSACTION`, &parser.BeginStatement{ Begin: pos(0), Transaction: pos(6), }) }) t.Run("DeferredTransaction", func(t *testing.T) { AssertParseStatement(t, `BEGIN DEFERRED TRANSACTION`, &parser.BeginStatement{ Begin: pos(0), Deferred: pos(6), Transaction: pos(15), }) }) t.Run("Immediate", func(t *testing.T) { AssertParseStatement(t, `BEGIN IMMEDIATE;`, &parser.BeginStatement{ Begin: pos(0), Immediate: pos(6), }) }) t.Run("Exclusive", func(t *testing.T) { AssertParseStatement(t, `BEGIN EXCLUSIVE`, &parser.BeginStatement{ Begin: pos(0), Exclusive: pos(6), }) }) t.Run("ErrOverrun", func(t *testing.T) { AssertParseStatementError(t, `BEGIN COMMIT`, `1:7: expected semicolon or EOF, found 'COMMIT'`) }) })*/ /*t.Run("Commit", func(t *testing.T) { t.Run("", func(t *testing.T) { AssertParseStatement(t, `COMMIT`, &parser.CommitStatement{ Commit: pos(0), }) }) t.Run("Transaction", func(t *testing.T) { AssertParseStatement(t, `COMMIT TRANSACTION`, &parser.CommitStatement{ Commit: pos(0), Transaction: pos(7), }) }) })*/ /*t.Run("End", func(t *testing.T) { t.Run("", func(t *testing.T) { AssertParseStatement(t, `END`, &parser.CommitStatement{ End: pos(0), }) }) t.Run("Transaction", func(t *testing.T) { AssertParseStatement(t, `END TRANSACTION`, &parser.CommitStatement{ End: pos(0), Transaction: pos(4), }) }) })*/ /*t.Run("Rollback", func(t *testing.T) { t.Run("", func(t *testing.T) { AssertParseStatement(t, `ROLLBACK`, &parser.RollbackStatement{ Rollback: pos(0), }) }) t.Run("Transaction", func(t *testing.T) { AssertParseStatement(t, `ROLLBACK TRANSACTION`, &parser.RollbackStatement{ Rollback: pos(0), Transaction: pos(9), }) }) t.Run("To", func(t *testing.T) { AssertParseStatement(t, `ROLLBACK TO svpt`, &parser.RollbackStatement{ Rollback: pos(0), To: pos(9), SavepointName: &parser.Ident{ Name: "svpt", NamePos: pos(12), }, }) }) t.Run("TransactionToSavepoint", func(t *testing.T) { AssertParseStatement(t, `ROLLBACK TRANSACTION TO SAVEPOINT "svpt"`, &parser.RollbackStatement{ Rollback: pos(0), Transaction: pos(9), To: pos(21), Savepoint: pos(24), SavepointName: &parser.Ident{ Name: "svpt", NamePos: pos(34), Quoted: true, }, }) }) t.Run("ErrSavepointName", func(t *testing.T) { AssertParseStatementError(t, `ROLLBACK TO SAVEPOINT 123`, `1:23: expected savepoint name, found 123`) }) })*/ /*t.Run("Savepoint", func(t *testing.T) { t.Run("Ident", func(t *testing.T) { AssertParseStatement(t, `SAVEPOINT svpt`, &parser.SavepointStatement{ Savepoint: pos(0), Name: &parser.Ident{ Name: "svpt", NamePos: pos(10), }, }) }) t.Run("String", func(t *testing.T) { AssertParseStatement(t, `SAVEPOINT "svpt"`, &parser.SavepointStatement{ Savepoint: pos(0), Name: &parser.Ident{ Name: "svpt", NamePos: pos(10), Quoted: true, }, }) }) t.Run("ErrSavepointName", func(t *testing.T) { AssertParseStatementError(t, `SAVEPOINT 123`, `1:11: expected savepoint name, found 123`) }) })*/ /*t.Run("Release", func(t *testing.T) { t.Run("Ident", func(t *testing.T) { AssertParseStatement(t, `RELEASE svpt`, &parser.ReleaseStatement{ Release: pos(0), Name: &parser.Ident{ Name: "svpt", NamePos: pos(8), }, }) }) t.Run("String", func(t *testing.T) { AssertParseStatement(t, `RELEASE "svpt"`, &parser.ReleaseStatement{ Release: pos(0), Name: &parser.Ident{ Name: "svpt", NamePos: pos(8), Quoted: true, }, }) }) t.Run("SavepointIdent", func(t *testing.T) { AssertParseStatement(t, `RELEASE SAVEPOINT svpt`, &parser.ReleaseStatement{ Release: pos(0), Savepoint: pos(8), Name: &parser.Ident{ Name: "svpt", NamePos: pos(18), }, }) }) t.Run("ErrSavepointName", func(t *testing.T) { AssertParseStatementError(t, `RELEASE 123`, `1:9: expected savepoint name, found 123`) }) })*/ t.Run("CreateTable", func(t *testing.T) { AssertParseStatement(t, `CREATE TABLE tbl (col1 TEXT, col2 DECIMAL(2))`, &parser.CreateTableStatement{ Create: pos(0), Table: pos(7), Name: &parser.Ident{ Name: "tbl", NamePos: pos(13), }, Lparen: pos(17), Columns: []*parser.ColumnDefinition{ { Name: &parser.Ident{NamePos: pos(18), Name: "col1"}, Type: &parser.Type{ Name: &parser.Ident{NamePos: pos(23), Name: "TEXT"}, }, }, { Name: &parser.Ident{NamePos: pos(29), Name: "col2"}, Type: &parser.Type{ Name: &parser.Ident{NamePos: pos(34), Name: "DECIMAL"}, Lparen: pos(41), Scale: &parser.IntegerLit{ValuePos: pos(42), Value: "2"}, Rparen: pos(43), }, }, }, Rparen: pos(44), }) AssertParseStatementError(t, `CREATE TABLE`, `1:12: expected table name, found 'EOF'`) AssertParseStatementError(t, `CREATE TABLE tbl `, `1:17: expected left paren, found 'EOF'`) AssertParseStatementError(t, `CREATE TABLE tbl (`, `1:18: expected column name, or right paren, found 'EOF'`) AssertParseStatementError(t, `CREATE TABLE tbl (col1 TEXT`, `1:27: expected column name, or right paren, found 'EOF'`) AssertParseStatement(t, `CREATE TABLE IF NOT EXISTS tbl (col1 TEXT)`, &parser.CreateTableStatement{ Create: pos(0), Table: pos(7), If: pos(13), IfNot: pos(16), IfNotExists: pos(20), Name: &parser.Ident{ Name: "tbl", NamePos: pos(27), }, Lparen: pos(31), Columns: []*parser.ColumnDefinition{ { Name: &parser.Ident{ NamePos: pos(32), Name: "col1", }, Type: &parser.Type{ Name: &parser.Ident{ NamePos: pos(37), Name: "TEXT", }, }, }, }, Rparen: pos(41), }) AssertParseStatementError(t, `CREATE TABLE IF`, `1:15: expected NOT, found 'EOF'`) AssertParseStatementError(t, `CREATE TABLE IF NOT`, `1:19: expected EXISTS, found 'EOF'`) AssertParseStatementError(t, `CREATE TABLE tbl (col1`, `1:22: expected type name, found 'EOF'`) AssertParseStatementError(t, `CREATE TABLE tbl (col1 DECIMAL(`, `1:31: expected scale, found 'EOF'`) AssertParseStatementError(t, `CREATE TABLE tbl (col1 DECIMAL(12,`, `1:34: expected right paren, found ','`) AssertParseStatementError(t, `CREATE TABLE tbl (col1 DECIMAL(1,2`, `1:33: expected right paren, found ','`) AssertParseStatementError(t, `CREATE TABLE tbl (col1 DECIMAL(1`, `1:32: expected right paren, found 'EOF'`) /*AssertParseStatementError(t, `CREATE TABLE tbl (col1 TEXT CONSTRAINT`, `1:38: expected constraint name, found 'EOF'`)*/ /*AssertParseStatement(t, `CREATE TABLE tbl AS SELECT foo`, &parser.CreateTableStatement{ Create: pos(0), Table: pos(7), Name: &parser.Ident{ Name: "tbl", NamePos: pos(13), }, As: pos(17), Select: &parser.SelectStatement{ Select: pos(20), Columns: []*sql.ResultColumn{ {Expr: &parser.Ident{NamePos: pos(27), Name: "foo"}}, }, }, })*/ /*AssertParseStatement(t, `CREATE TABLE tbl AS WITH cte (x) AS (SELECT y) SELECT foo`, &parser.CreateTableStatement{ Create: pos(0), Table: pos(7), Name: &parser.Ident{ Name: "tbl", NamePos: pos(13), }, As: pos(17), Select: &parser.SelectStatement{ WithClause: &parser.WithClause{ With: pos(20), CTEs: []*sql.CTE{ { TableName: &parser.Ident{NamePos: pos(25), Name: "cte"}, ColumnsLparen: pos(29), Columns: []*sql.Ident{ {NamePos: pos(30), Name: "x"}, }, ColumnsRparen: pos(31), As: pos(33), SelectLparen: pos(36), Select: &parser.SelectStatement{ Select: pos(37), Columns: []*sql.ResultColumn{ {Expr: &parser.Ident{NamePos: pos(44), Name: "y"}}, }, }, SelectRparen: pos(45), }, }, }, Select: pos(47), Columns: []*sql.ResultColumn{ {Expr: &parser.Ident{NamePos: pos(54), Name: "foo"}}, }, }, })*/ /*AssertParseStatementError(t, `CREATE TABLE tbl AS`, `1:19: expected SELECT or VALUES, found 'EOF'`)*/ /*AssertParseStatementError(t, `CREATE TABLE tbl AS WITH`, `1:24: expected table name, found 'EOF'`)*/ t.Run("ColumnConstraint", func(t *testing.T) { /*t.Run("PrimaryKey", func(t *testing.T) { t.Run("Simple", func(t *testing.T) { AssertParseStatement(t, `CREATE TABLE tbl (col1 TEXT PRIMARY KEY)`, &parser.CreateTableStatement{ Create: pos(0), Table: pos(7), Name: &parser.Ident{Name: "tbl", NamePos: pos(13)}, Lparen: pos(17), Columns: []*sql.ColumnDefinition{ { Name: &parser.Ident{Name: "col1", NamePos: pos(18)}, Type: &parser.Type{ Name: &parser.Ident{Name: "TEXT", NamePos: pos(23)}, }, Constraints: []sql.Constraint{ &parser.PrimaryKeyConstraint{ Primary: pos(28), Key: pos(36), }, }, }, }, Rparen: pos(39), }) }) t.Run("Full", func(t *testing.T) { stmt := ParseStatementOrFail(t, `CREATE TABLE tbl (col1 TEXT CONSTRAINT cons1 PRIMARY KEY AUTOINCREMENT)`).(*sql.CreateTableStatement) if diff := deep.Equal(stmt.Columns[0].Constraints[0], &parser.PrimaryKeyConstraint{ Constraint: pos(28), Name: &parser.Ident{Name: "cons1", NamePos: pos(39)}, Primary: pos(45), Key: pos(53), Autoincrement: pos(57), }); diff != nil { t.Fatal(diff) } }) t.Run("ErrNoKey", func(t *testing.T) { AssertParseStatementError(t, `CREATE TABLE tbl (col1 TEXT PRIMARY`, `1:35: expected KEY, found 'EOF'`) }) })*/ /*t.Run("Unique", func(t *testing.T) { t.Run("Simple", func(t *testing.T) { AssertParseStatement(t, `CREATE TABLE tbl (col1 TEXT CONSTRAINT con1 UNIQUE)`, &parser.CreateTableStatement{ Create: pos(0), Table: pos(7), Name: &parser.Ident{Name: "tbl", NamePos: pos(13)}, Lparen: pos(17), Columns: []*sql.ColumnDefinition{ { Name: &parser.Ident{Name: "col1", NamePos: pos(18)}, Type: &parser.Type{ Name: &parser.Ident{Name: "TEXT", NamePos: pos(23)}, }, Constraints: []sql.Constraint{ &parser.UniqueConstraint{ Constraint: pos(28), Name: &parser.Ident{Name: "con1", NamePos: pos(39)}, Unique: pos(44), }, }, }, }, Rparen: pos(50), }) }) })*/ /*t.Run("Check", func(t *testing.T) { AssertParseStatement(t, `CREATE TABLE tbl (col1 TEXT CHECK (col1 > 1))`, &parser.CreateTableStatement{ Create: pos(0), Table: pos(7), Name: &parser.Ident{Name: "tbl", NamePos: pos(13)}, Lparen: pos(17), Columns: []*sql.ColumnDefinition{ { Name: &parser.Ident{Name: "col1", NamePos: pos(18)}, Type: &parser.Type{ Name: &parser.Ident{Name: "TEXT", NamePos: pos(23)}, }, Constraints: []sql.Constraint{ &parser.CheckConstraint{ Check: pos(28), Lparen: pos(34), Expr: &parser.BinaryExpr{ X: &parser.Ident{Name: "col1", NamePos: pos(35)}, Op: sql.GT, OpPos: pos(40), Y: &parser.NumberLit{Value: "1", ValuePos: pos(42)}, }, Rparen: pos(43), }, }, }, }, Rparen: pos(44), }) })*/ /*t.Run("Default", func(t *testing.T) { t.Run("Expr", func(t *testing.T) { AssertParseStatement(t, `CREATE TABLE tbl (col1 TEXT DEFAULT (1))`, &parser.CreateTableStatement{ Create: pos(0), Table: pos(7), Name: &parser.Ident{Name: "tbl", NamePos: pos(13)}, Lparen: pos(17), Columns: []*sql.ColumnDefinition{ { Name: &parser.Ident{Name: "col1", NamePos: pos(18)}, Type: &parser.Type{ Name: &parser.Ident{Name: "TEXT", NamePos: pos(23)}, }, Constraints: []sql.Constraint{ &parser.DefaultConstraint{ Default: pos(28), Lparen: pos(36), Expr: &parser.NumberLit{Value: "1", ValuePos: pos(37)}, Rparen: pos(38), }, }, }, }, Rparen: pos(39), }) }) t.Run("String", func(t *testing.T) { stmt := ParseStatementOrFail(t, `CREATE TABLE tbl (col1 TEXT DEFAULT 'foo')`).(*sql.CreateTableStatement) if diff := deepEqual(stmt.Columns[0].Constraints[0], &parser.DefaultConstraint{ Default: pos(28), Expr: &parser.StringLit{Value: "foo", ValuePos: pos(36)}, }); diff != "" { t.Fatal(diff) } }) t.Run("Blob", func(t *testing.T) { stmt := ParseStatementOrFail(t, `CREATE TABLE tbl (col1 TEXT DEFAULT x'0F0F')`).(*sql.CreateTableStatement) if diff := deepEqual(stmt.Columns[0].Constraints[0], &parser.DefaultConstraint{ Default: pos(28), Expr: &parser.BlobLit{Value: "0F0F", ValuePos: pos(36)}, }); diff != "" { t.Fatal(diff) } }) t.Run("Number", func(t *testing.T) { stmt := ParseStatementOrFail(t, `CREATE TABLE tbl (col1 TEXT DEFAULT 1)`).(*sql.CreateTableStatement) if diff := deepEqual(stmt.Columns[0].Constraints[0], &parser.DefaultConstraint{ Default: pos(28), Expr: &parser.NumberLit{Value: "1", ValuePos: pos(36)}, }); diff != "" { t.Fatal(diff) } }) t.Run("Null", func(t *testing.T) { stmt := ParseStatementOrFail(t, `CREATE TABLE tbl (col1 TEXT DEFAULT NULL)`).(*sql.CreateTableStatement) if diff := deepEqual(stmt.Columns[0].Constraints[0], &parser.DefaultConstraint{ Default: pos(28), Expr: &parser.NullLit{Pos: pos(36)}, }); diff != "" { t.Fatal(diff) } }) t.Run("Bool", func(t *testing.T) { stmt := ParseStatementOrFail(t, `CREATE TABLE tbl (col1 TEXT DEFAULT true)`).(*sql.CreateTableStatement) if diff := deepEqual(stmt.Columns[0].Constraints[0], &parser.DefaultConstraint{ Default: pos(28), Expr: &parser.BoolLit{Value: true, ValuePos: pos(36)}, }); diff != "" { t.Fatal(diff) } }) AssertParseStatementError(t, `CREATE TABLE tbl (col1 TEXT DEFAULT +`, `1:37: expected signed number, found 'EOF'`) AssertParseStatementError(t, `CREATE TABLE tbl (col1 TEXT DEFAULT -`, `1:37: expected signed number, found 'EOF'`) AssertParseStatementError(t, `CREATE TABLE tbl (col1 TEXT DEFAULT `, `1:36: expected literal value or left paren, found 'EOF'`) AssertParseStatementError(t, `CREATE TABLE tbl (col1 TEXT DEFAULT (TABLE`, `1:38: expected expression, found 'TABLE'`) AssertParseStatementError(t, `CREATE TABLE tbl (col1 TEXT DEFAULT (true`, `1:41: expected right paren, found 'EOF'`) })*/ /*t.Run("ForeignKey", func(t *testing.T) { t.Run("Simple", func(t *testing.T) { AssertParseStatement(t, `CREATE TABLE tbl (col1 TEXT REFERENCES foo (col2))`, &parser.CreateTableStatement{ Create: pos(0), Table: pos(7), Name: &parser.Ident{Name: "tbl", NamePos: pos(13)}, Lparen: pos(17), Columns: []*sql.ColumnDefinition{ { Name: &parser.Ident{Name: "col1", NamePos: pos(18)}, Type: &parser.Type{ Name: &parser.Ident{Name: "TEXT", NamePos: pos(23)}, }, Constraints: []sql.Constraint{ &parser.ForeignKeyConstraint{ References: pos(28), ForeignTable: &parser.Ident{Name: "foo", NamePos: pos(39)}, ForeignLparen: pos(43), ForeignColumns: []*sql.Ident{ {Name: "col2", NamePos: pos(44)}, }, ForeignRparen: pos(48), }, }, }, }, Rparen: pos(49), }) }) t.Run("OnDeleteSetNull", func(t *testing.T) { stmt := ParseStatementOrFail(t, `CREATE TABLE tbl (col1 TEXT REFERENCES foo (col2) ON DELETE SET NULL)`).(*sql.CreateTableStatement) if diff := deepEqual(stmt.Columns[0].Constraints[0], &parser.ForeignKeyConstraint{ References: pos(28), ForeignTable: &parser.Ident{Name: "foo", NamePos: pos(39)}, ForeignLparen: pos(43), ForeignColumns: []*sql.Ident{ {Name: "col2", NamePos: pos(44)}, }, ForeignRparen: pos(48), Args: []*sql.ForeignKeyArg{ { On: pos(50), OnDelete: pos(53), Set: pos(60), SetNull: pos(64), }, }, }); diff != "" { t.Fatal(diff) } }) t.Run("OnDeleteSetDefault", func(t *testing.T) { stmt := ParseStatementOrFail(t, `CREATE TABLE tbl (col1 TEXT REFERENCES foo (col2) ON DELETE SET DEFAULT)`).(*sql.CreateTableStatement) if diff := deepEqual(stmt.Columns[0].Constraints[0], &parser.ForeignKeyConstraint{ References: pos(28), ForeignTable: &parser.Ident{Name: "foo", NamePos: pos(39)}, ForeignLparen: pos(43), ForeignColumns: []*sql.Ident{ {Name: "col2", NamePos: pos(44)}, }, ForeignRparen: pos(48), Args: []*sql.ForeignKeyArg{ { On: pos(50), OnDelete: pos(53), Set: pos(60), SetDefault: pos(64), }, }, }); diff != "" { t.Fatal(diff) } }) t.Run("OnDeleteSetDefault", func(t *testing.T) { stmt := ParseStatementOrFail(t, `CREATE TABLE tbl (col1 TEXT REFERENCES foo (col2) ON DELETE CASCADE)`).(*sql.CreateTableStatement) if diff := deepEqual(stmt.Columns[0].Constraints[0], &parser.ForeignKeyConstraint{ References: pos(28), ForeignTable: &parser.Ident{Name: "foo", NamePos: pos(39)}, ForeignLparen: pos(43), ForeignColumns: []*sql.Ident{ {Name: "col2", NamePos: pos(44)}, }, ForeignRparen: pos(48), Args: []*sql.ForeignKeyArg{ { On: pos(50), OnDelete: pos(53), Cascade: pos(60), }, }, }); diff != "" { t.Fatal(diff) } }) t.Run("OnDeleteSetRestrict", func(t *testing.T) { stmt := ParseStatementOrFail(t, `CREATE TABLE tbl (col1 TEXT REFERENCES foo (col2) ON DELETE RESTRICT)`).(*sql.CreateTableStatement) if diff := deepEqual(stmt.Columns[0].Constraints[0], &parser.ForeignKeyConstraint{ References: pos(28), ForeignTable: &parser.Ident{Name: "foo", NamePos: pos(39)}, ForeignLparen: pos(43), ForeignColumns: []*sql.Ident{ {Name: "col2", NamePos: pos(44)}, }, ForeignRparen: pos(48), Args: []*sql.ForeignKeyArg{ { On: pos(50), OnDelete: pos(53), Restrict: pos(60), }, }, }); diff != "" { t.Fatal(diff) } }) t.Run("OnDeleteSetNoAction", func(t *testing.T) { stmt := ParseStatementOrFail(t, `CREATE TABLE tbl (col1 TEXT REFERENCES foo (col2) ON DELETE NO ACTION)`).(*sql.CreateTableStatement) if diff := deepEqual(stmt.Columns[0].Constraints[0], &parser.ForeignKeyConstraint{ References: pos(28), ForeignTable: &parser.Ident{Name: "foo", NamePos: pos(39)}, ForeignLparen: pos(43), ForeignColumns: []*sql.Ident{ {Name: "col2", NamePos: pos(44)}, }, ForeignRparen: pos(48), Args: []*sql.ForeignKeyArg{ { On: pos(50), OnDelete: pos(53), No: pos(60), NoAction: pos(63), }, }, }); diff != "" { t.Fatal(diff) } }) t.Run("Multiple", func(t *testing.T) { stmt := ParseStatementOrFail(t, `CREATE TABLE tbl (col1 TEXT REFERENCES foo (col2) ON DELETE CASCADE ON UPDATE RESTRICT)`).(*sql.CreateTableStatement) if diff := deepEqual(stmt.Columns[0].Constraints[0], &parser.ForeignKeyConstraint{ References: pos(28), ForeignTable: &parser.Ident{Name: "foo", NamePos: pos(39)}, ForeignLparen: pos(43), ForeignColumns: []*sql.Ident{ {Name: "col2", NamePos: pos(44)}, }, ForeignRparen: pos(48), Args: []*sql.ForeignKeyArg{ { On: pos(50), OnDelete: pos(53), Cascade: pos(60), }, { On: pos(68), OnUpdate: pos(71), Restrict: pos(78), }, }, }); diff != "" { t.Fatal(diff) } }) t.Run("Deferrable", func(t *testing.T) { stmt := ParseStatementOrFail(t, `CREATE TABLE tbl (col1 TEXT REFERENCES foo (col2) DEFERRABLE)`).(*sql.CreateTableStatement) if diff := deepEqual(stmt.Columns[0].Constraints[0], &parser.ForeignKeyConstraint{ References: pos(28), ForeignTable: &parser.Ident{Name: "foo", NamePos: pos(39)}, ForeignLparen: pos(43), ForeignColumns: []*sql.Ident{ {Name: "col2", NamePos: pos(44)}, }, ForeignRparen: pos(48), Deferrable: pos(50), }); diff != "" { t.Fatal(diff) } }) t.Run("NotDeferrable", func(t *testing.T) { stmt := ParseStatementOrFail(t, `CREATE TABLE tbl (col1 TEXT REFERENCES foo (col2) NOT DEFERRABLE)`).(*sql.CreateTableStatement) if diff := deepEqual(stmt.Columns[0].Constraints[0], &parser.ForeignKeyConstraint{ References: pos(28), ForeignTable: &parser.Ident{Name: "foo", NamePos: pos(39)}, ForeignLparen: pos(43), ForeignColumns: []*sql.Ident{ {Name: "col2", NamePos: pos(44)}, }, ForeignRparen: pos(48), Not: pos(50), NotDeferrable: pos(54), }); diff != "" { t.Fatal(diff) } }) t.Run("InitiallyDeferred", func(t *testing.T) { stmt := ParseStatementOrFail(t, `CREATE TABLE tbl (col1 TEXT REFERENCES foo (col2) DEFERRABLE INITIALLY DEFERRED)`).(*sql.CreateTableStatement) if diff := deepEqual(stmt.Columns[0].Constraints[0], &parser.ForeignKeyConstraint{ References: pos(28), ForeignTable: &parser.Ident{Name: "foo", NamePos: pos(39)}, ForeignLparen: pos(43), ForeignColumns: []*sql.Ident{ {Name: "col2", NamePos: pos(44)}, }, ForeignRparen: pos(48), Deferrable: pos(50), Initially: pos(61), InitiallyDeferred: pos(71), }); diff != "" { t.Fatal(diff) } }) t.Run("InitiallyImmediate", func(t *testing.T) { stmt := ParseStatementOrFail(t, `CREATE TABLE tbl (col1 TEXT REFERENCES foo (col2) DEFERRABLE INITIALLY IMMEDIATE)`).(*sql.CreateTableStatement) if diff := deepEqual(stmt.Columns[0].Constraints[0], &parser.ForeignKeyConstraint{ References: pos(28), ForeignTable: &parser.Ident{Name: "foo", NamePos: pos(39)}, ForeignLparen: pos(43), ForeignColumns: []*sql.Ident{ {Name: "col2", NamePos: pos(44)}, }, ForeignRparen: pos(48), Deferrable: pos(50), Initially: pos(61), InitiallyImmediate: pos(71), }); diff != "" { t.Fatal(diff) } }) })*/ }) t.Run("TableConstraint", func(t *testing.T) { /* !!Not supporting PRIMARY KEY, UNIQUE, CHECK or REFERENCES table constraints!! t.Run("PrimaryKey", func(t *testing.T) { AssertParseStatement(t, `CREATE TABLE tbl (col1 TEXT, PRIMARY KEY (col1, col2))`, &parser.CreateTableStatement{ Create: pos(0), Table: pos(7), Name: &parser.Ident{Name: "tbl", NamePos: pos(13)}, Lparen: pos(17), Columns: []*sql.ColumnDefinition{ { Name: &parser.Ident{Name: "col1", NamePos: pos(18)}, Type: &parser.Type{ Name: &parser.Ident{Name: "TEXT", NamePos: pos(23)}, }, }, }, Constraints: []sql.Constraint{ &parser.PrimaryKeyConstraint{ Primary: pos(29), Key: pos(37), Lparen: pos(41), Columns: []*sql.Ident{ {Name: "col1", NamePos: pos(42)}, {Name: "col2", NamePos: pos(48)}, }, Rparen: pos(52), }, }, Rparen: pos(53), }) AssertParseStatementError(t, `CREATE TABLE tbl (col1 TEXT, PRIMARY`, `1:36: expected KEY, found 'EOF'`) AssertParseStatementError(t, `CREATE TABLE tbl (col1 TEXT, PRIMARY KEY`, `1:40: expected left paren, found 'EOF'`) AssertParseStatementError(t, `CREATE TABLE tbl (col1 TEXT, PRIMARY KEY (col1)`, `1:47: expected right paren, found 'EOF'`) AssertParseStatementError(t, `CREATE TABLE tbl (col1 TEXT, PRIMARY KEY (1`, `1:43: expected column name, found 1`) AssertParseStatementError(t, `CREATE TABLE tbl (col1 TEXT, PRIMARY KEY (foo x`, `1:47: expected comma or right paren, found x`) })*/ /*t.Run("Unique", func(t *testing.T) { AssertParseStatement(t, `CREATE TABLE tbl (col1 TEXT, CONSTRAINT con1 UNIQUE (col1, col2))`, &parser.CreateTableStatement{ Create: pos(0), Table: pos(7), Name: &parser.Ident{Name: "tbl", NamePos: pos(13)}, Lparen: pos(17), Columns: []*sql.ColumnDefinition{ { Name: &parser.Ident{Name: "col1", NamePos: pos(18)}, Type: &parser.Type{ Name: &parser.Ident{Name: "TEXT", NamePos: pos(23)}, }, }, }, Constraints: []sql.Constraint{ &parser.UniqueConstraint{ Constraint: pos(29), Name: &parser.Ident{Name: "con1", NamePos: pos(40)}, Unique: pos(45), Lparen: pos(52), Columns: []*sql.Ident{ {Name: "col1", NamePos: pos(53)}, {Name: "col2", NamePos: pos(59)}, }, Rparen: pos(63), }, }, Rparen: pos(64), }) AssertParseStatementError(t, `CREATE TABLE tbl (col1 TEXT, UNIQUE`, `1:35: expected left paren, found 'EOF'`) AssertParseStatementError(t, `CREATE TABLE tbl (col1 TEXT, UNIQUE (1`, `1:38: expected column name, found 1`) AssertParseStatementError(t, `CREATE TABLE tbl (col1 TEXT, UNIQUE (x y`, `1:40: expected comma or right paren, found y`) })*/ /*(t.Run("Check", func(t *testing.T) { AssertParseStatement(t, `CREATE TABLE tbl (col1 TEXT, CHECK(foo = bar))`, &parser.CreateTableStatement{ Create: pos(0), Table: pos(7), Name: &parser.Ident{Name: "tbl", NamePos: pos(13)}, Lparen: pos(17), Columns: []*sql.ColumnDefinition{ { Name: &parser.Ident{Name: "col1", NamePos: pos(18)}, Type: &parser.Type{ Name: &parser.Ident{Name: "TEXT", NamePos: pos(23)}, }, }, }, Constraints: []sql.Constraint{ &parser.CheckConstraint{ Check: pos(29), Lparen: pos(34), Expr: &parser.BinaryExpr{ X: &parser.Ident{Name: "foo", NamePos: pos(35)}, Op: sql.EQ, OpPos: pos(39), Y: &parser.Ident{Name: "bar", NamePos: pos(41)}, }, Rparen: pos(44), }, }, Rparen: pos(45), }) AssertParseStatementError(t, `CREATE TABLE tbl (col1 TEXT, CHECK`, `1:34: expected left paren, found 'EOF'`) AssertParseStatementError(t, `CREATE TABLE tbl (col1 TEXT, CHECK (TABLE`, `1:37: expected expression, found 'TABLE'`) AssertParseStatementError(t, `CREATE TABLE tbl (col1 TEXT, CHECK (true`, `1:40: expected right paren, found 'EOF'`) })*/ /*t.Run("ForeignKey", func(t *testing.T) { AssertParseStatement(t, `CREATE TABLE tbl (col1 TEXT, FOREIGN KEY (col1, col2) REFERENCES tbl2 (x, y))`, &parser.CreateTableStatement{ Create: pos(0), Table: pos(7), Name: &parser.Ident{Name: "tbl", NamePos: pos(13)}, Lparen: pos(17), Columns: []*sql.ColumnDefinition{ { Name: &parser.Ident{Name: "col1", NamePos: pos(18)}, Type: &parser.Type{ Name: &parser.Ident{Name: "TEXT", NamePos: pos(23)}, }, }, }, Constraints: []sql.Constraint{ &parser.ForeignKeyConstraint{ Foreign: pos(29), ForeignKey: pos(37), Lparen: pos(41), Columns: []*sql.Ident{ {Name: "col1", NamePos: pos(42)}, {Name: "col2", NamePos: pos(48)}, }, Rparen: pos(52), References: pos(54), ForeignTable: &parser.Ident{Name: "tbl2", NamePos: pos(65)}, ForeignLparen: pos(70), ForeignColumns: []*sql.Ident{ {Name: "x", NamePos: pos(71)}, {Name: "y", NamePos: pos(74)}, }, ForeignRparen: pos(75), }, }, Rparen: pos(76), }) AssertParseStatementError(t, `CREATE TABLE tbl (col1 TEXT, FOREIGN`, `1:36: expected KEY, found 'EOF'`) AssertParseStatementError(t, `CREATE TABLE tbl (col1 TEXT, FOREIGN KEY`, `1:40: expected left paren, found 'EOF'`) AssertParseStatementError(t, `CREATE TABLE tbl (col1 TEXT, FOREIGN KEY (`, `1:42: expected column name, found 'EOF'`) AssertParseStatementError(t, `CREATE TABLE tbl (col1 TEXT, FOREIGN KEY (x`, `1:43: expected comma or right paren, found 'EOF'`) AssertParseStatementError(t, `CREATE TABLE tbl (col1 TEXT, FOREIGN KEY (x)`, `1:44: expected REFERENCES, found 'EOF'`) AssertParseStatementError(t, `CREATE TABLE tbl (col1 TEXT, FOREIGN KEY (x) REFERENCES`, `1:55: expected foreign table name, found 'EOF'`) AssertParseStatementError(t, `CREATE TABLE tbl (col1 TEXT, FOREIGN KEY (x) REFERENCES tbl`, `1:59: expected left paren, found 'EOF'`) AssertParseStatementError(t, `CREATE TABLE tbl (col1 TEXT, FOREIGN KEY (x) REFERENCES tbl (`, `1:61: expected foreign column name, found 'EOF'`) AssertParseStatementError(t, `CREATE TABLE tbl (col1 TEXT, FOREIGN KEY (x) REFERENCES tbl (x`, `1:62: expected comma or right paren, found 'EOF'`) AssertParseStatementError(t, `CREATE TABLE tbl (col1 TEXT, FOREIGN KEY (x) REFERENCES tbl (x) ON`, `1:66: expected UPDATE or DELETE, found 'EOF'`) AssertParseStatementError(t, `CREATE TABLE tbl (col1 TEXT, FOREIGN KEY (x) REFERENCES tbl (x) ON UPDATE SET`, `1:77: expected NULL or DEFAULT, found 'EOF'`) AssertParseStatementError(t, `CREATE TABLE tbl (col1 TEXT, FOREIGN KEY (x) REFERENCES tbl (x) ON UPDATE NO`, `1:76: expected ACTION, found 'EOF'`) AssertParseStatementError(t, `CREATE TABLE tbl (col1 TEXT, FOREIGN KEY (x) REFERENCES tbl (x) ON UPDATE TABLE`, `1:75: expected SET NULL, SET DEFAULT, CASCADE, RESTRICT, or NO ACTION, found 'TABLE'`) AssertParseStatementError(t, `CREATE TABLE tbl (col1 TEXT, FOREIGN KEY (x) REFERENCES tbl (x) ON UPDATE CASCADE NOT`, `1:85: expected DEFERRABLE, found 'EOF'`) })*/ }) }) t.Run("DropTable", func(t *testing.T) { AssertParseStatement(t, `DROP TABLE vw`, &parser.DropTableStatement{ Drop: pos(0), Table: pos(5), Name: &parser.Ident{NamePos: pos(11), Name: "vw"}, }) AssertParseStatement(t, `DROP TABLE IF EXISTS vw`, &parser.DropTableStatement{ Drop: pos(0), Table: pos(5), If: pos(11), IfExists: pos(14), Name: &parser.Ident{NamePos: pos(21), Name: "vw"}, }) AssertParseStatementError(t, `DROP TABLE`, `1:10: expected table name, found 'EOF'`) AssertParseStatementError(t, `DROP TABLE IF`, `1:13: expected EXISTS, found 'EOF'`) AssertParseStatementError(t, `DROP TABLE IF EXISTS`, `1:20: expected table name, found 'EOF'`) }) t.Run("CreateView", func(t *testing.T) { AssertParseStatement(t, `CREATE VIEW vw (col1, col2) AS SELECT x, y`, &parser.CreateViewStatement{ Create: pos(0), View: pos(7), Name: &parser.Ident{NamePos: pos(12), Name: "vw"}, Lparen: pos(15), Columns: []*parser.Ident{ {NamePos: pos(16), Name: "col1"}, {NamePos: pos(22), Name: "col2"}, }, Rparen: pos(26), As: pos(28), Select: &parser.SelectStatement{ Select: pos(31), Columns: []*parser.ResultColumn{ {Expr: &parser.Ident{NamePos: pos(38), Name: "x"}}, {Expr: &parser.Ident{NamePos: pos(41), Name: "y"}}, }, }, }) AssertParseStatement(t, `CREATE VIEW vw AS SELECT x`, &parser.CreateViewStatement{ Create: pos(0), View: pos(7), Name: &parser.Ident{NamePos: pos(12), Name: "vw"}, As: pos(15), Select: &parser.SelectStatement{ Select: pos(18), Columns: []*parser.ResultColumn{ {Expr: &parser.Ident{NamePos: pos(25), Name: "x"}}, }, }, }) AssertParseStatement(t, `CREATE VIEW IF NOT EXISTS vw AS SELECT x`, &parser.CreateViewStatement{ Create: pos(0), View: pos(7), If: pos(12), IfNot: pos(15), IfNotExists: pos(19), Name: &parser.Ident{NamePos: pos(26), Name: "vw"}, As: pos(29), Select: &parser.SelectStatement{ Select: pos(32), Columns: []*parser.ResultColumn{ {Expr: &parser.Ident{NamePos: pos(39), Name: "x"}}, }, }, }) AssertParseStatementError(t, `CREATE VIEW`, `1:11: expected view name, found 'EOF'`) AssertParseStatementError(t, `CREATE VIEW IF`, `1:14: expected NOT, found 'EOF'`) AssertParseStatementError(t, `CREATE VIEW IF NOT`, `1:18: expected EXISTS, found 'EOF'`) AssertParseStatementError(t, `CREATE VIEW vw`, `1:14: expected AS, found 'EOF'`) AssertParseStatementError(t, `CREATE VIEW vw (`, `1:16: expected column name, found 'EOF'`) AssertParseStatementError(t, `CREATE VIEW vw (x`, `1:17: expected comma or right paren, found 'EOF'`) AssertParseStatementError(t, `CREATE VIEW vw AS`, `1:17: expected SELECT, found 'EOF'`) AssertParseStatementError(t, `CREATE VIEW vw AS SELECT`, `1:24: expected expression, found 'EOF'`) }) t.Run("DropView", func(t *testing.T) { AssertParseStatement(t, `DROP VIEW vw`, &parser.DropViewStatement{ Drop: pos(0), View: pos(5), Name: &parser.Ident{NamePos: pos(10), Name: "vw"}, }) AssertParseStatement(t, `DROP VIEW IF EXISTS vw`, &parser.DropViewStatement{ Drop: pos(0), View: pos(5), If: pos(10), IfExists: pos(13), Name: &parser.Ident{NamePos: pos(20), Name: "vw"}, }) AssertParseStatementError(t, `DROP`, `1:1: expected TABLE, VIEW or FUNCTION`) AssertParseStatementError(t, `DROP VIEW`, `1:9: expected view name, found 'EOF'`) AssertParseStatementError(t, `DROP VIEW IF`, `1:12: expected EXISTS, found 'EOF'`) AssertParseStatementError(t, `DROP VIEW IF EXISTS`, `1:19: expected view name, found 'EOF'`) }) /*t.Run("CreateIndex", func(t *testing.T) { AssertParseStatement(t, `CREATE INDEX idx ON tbl (x ASC, y DESC, z)`, &parser.CreateIndexStatement{ Create: pos(0), Index: pos(7), Name: &parser.Ident{NamePos: pos(13), Name: "idx"}, On: pos(17), Table: &parser.Ident{NamePos: pos(20), Name: "tbl"}, Lparen: pos(24), Columns: []*sql.IndexedColumn{ {X: &parser.Ident{NamePos: pos(25), Name: "x"}, Asc: pos(27)}, {X: &parser.Ident{NamePos: pos(32), Name: "y"}, Desc: pos(34)}, {X: &parser.Ident{NamePos: pos(40), Name: "z"}}, }, Rparen: pos(41), }) AssertParseStatement(t, `CREATE UNIQUE INDEX idx ON tbl (x)`, &parser.CreateIndexStatement{ Create: pos(0), Unique: pos(7), Index: pos(14), Name: &parser.Ident{NamePos: pos(20), Name: "idx"}, On: pos(24), Table: &parser.Ident{NamePos: pos(27), Name: "tbl"}, Lparen: pos(31), Columns: []*sql.IndexedColumn{ {X: &parser.Ident{NamePos: pos(32), Name: "x"}}, }, Rparen: pos(33), }) AssertParseStatement(t, `CREATE INDEX idx ON tbl (x) WHERE true`, &parser.CreateIndexStatement{ Create: pos(0), Index: pos(7), Name: &parser.Ident{NamePos: pos(13), Name: "idx"}, On: pos(17), Table: &parser.Ident{NamePos: pos(20), Name: "tbl"}, Lparen: pos(24), Columns: []*sql.IndexedColumn{ {X: &parser.Ident{NamePos: pos(25), Name: "x"}}, }, Rparen: pos(26), Where: pos(28), WhereExpr: &parser.BoolLit{ValuePos: pos(34), Value: true}, }) AssertParseStatement(t, `CREATE INDEX IF NOT EXISTS idx ON tbl (x)`, &parser.CreateIndexStatement{ Create: pos(0), Index: pos(7), If: pos(13), IfNot: pos(16), IfNotExists: pos(20), Name: &parser.Ident{NamePos: pos(27), Name: "idx"}, On: pos(31), Table: &parser.Ident{NamePos: pos(34), Name: "tbl"}, Lparen: pos(38), Columns: []*sql.IndexedColumn{ {X: &parser.Ident{NamePos: pos(39), Name: "x"}}, }, Rparen: pos(40), }) AssertParseStatementError(t, `CREATE UNIQUE`, `1:13: expected INDEX, found 'EOF'`) AssertParseStatementError(t, `CREATE INDEX`, `1:12: expected index name, found 'EOF'`) AssertParseStatementError(t, `CREATE INDEX IF`, `1:15: expected NOT, found 'EOF'`) AssertParseStatementError(t, `CREATE INDEX IF NOT`, `1:19: expected EXISTS, found 'EOF'`) AssertParseStatementError(t, `CREATE INDEX idx`, `1:16: expected ON, found 'EOF'`) AssertParseStatementError(t, `CREATE INDEX idx ON`, `1:19: expected table name, found 'EOF'`) AssertParseStatementError(t, `CREATE INDEX idx ON tbl`, `1:23: expected left paren, found 'EOF'`) AssertParseStatementError(t, `CREATE INDEX idx ON tbl (`, `1:25: expected expression, found 'EOF'`) AssertParseStatementError(t, `CREATE INDEX idx ON tbl (x`, `1:26: expected comma or right paren, found 'EOF'`) AssertParseStatementError(t, `CREATE INDEX idx ON tbl (x) WHERE`, `1:33: expected expression, found 'EOF'`) })*/ /*t.Run("DropIndex", func(t *testing.T) { AssertParseStatement(t, `DROP INDEX idx`, &parser.DropIndexStatement{ Drop: pos(0), Index: pos(5), Name: &parser.Ident{NamePos: pos(11), Name: "idx"}, }) AssertParseStatement(t, `DROP INDEX IF EXISTS idx`, &parser.DropIndexStatement{ Drop: pos(0), Index: pos(5), If: pos(11), IfExists: pos(14), Name: &parser.Ident{NamePos: pos(21), Name: "idx"}, }) AssertParseStatementError(t, `DROP INDEX`, `1:10: expected index name, found 'EOF'`) AssertParseStatementError(t, `DROP INDEX IF`, `1:13: expected EXISTS, found 'EOF'`) AssertParseStatementError(t, `DROP INDEX IF EXISTS`, `1:20: expected index name, found 'EOF'`) })*/ t.Run("Select", func(t *testing.T) { AssertParseStatement(t, `SELECT * FROM tbl`, &parser.SelectStatement{ Select: pos(0), Columns: []*parser.ResultColumn{ {Star: pos(7)}, }, From: pos(9), Source: &parser.QualifiedTableName{ Name: &parser.Ident{NamePos: pos(14), Name: "tbl"}, }, }) AssertParseStatement(t, `SELECT DISTINCT * FROM tbl`, &parser.SelectStatement{ Select: pos(0), Distinct: pos(7), Columns: []*parser.ResultColumn{ {Star: pos(16)}, }, From: pos(18), Source: &parser.QualifiedTableName{ Name: &parser.Ident{NamePos: pos(23), Name: "tbl"}, }, }) AssertParseStatement(t, `SELECT foo AS FOO, bar baz, tbl.* FROM tbl`, &parser.SelectStatement{ Select: pos(0), Columns: []*parser.ResultColumn{ { Expr: &parser.Ident{NamePos: pos(7), Name: "foo"}, As: pos(11), Alias: &parser.Ident{NamePos: pos(14), Name: "FOO"}, }, { Expr: &parser.Ident{NamePos: pos(19), Name: "bar"}, Alias: &parser.Ident{NamePos: pos(23), Name: "baz"}, }, { Expr: &parser.QualifiedRef{ Table: &parser.Ident{NamePos: pos(28), Name: "tbl"}, Dot: pos(31), Star: pos(32), }, }, }, From: pos(34), Source: &parser.QualifiedTableName{ Name: &parser.Ident{NamePos: pos(39), Name: "tbl"}, }, }) AssertParseStatement(t, `SELECT * FROM tbl tbl2`, &parser.SelectStatement{ Select: pos(0), Columns: []*parser.ResultColumn{ {Star: pos(7)}, }, From: pos(9), Source: &parser.QualifiedTableName{ Name: &parser.Ident{NamePos: pos(14), Name: "tbl"}, Alias: &parser.Ident{NamePos: pos(18), Name: "tbl2"}, }, }) AssertParseStatement(t, `SELECT * FROM tbl AS tbl2`, &parser.SelectStatement{ Select: pos(0), Columns: []*parser.ResultColumn{ {Star: pos(7)}, }, From: pos(9), Source: &parser.QualifiedTableName{ Name: &parser.Ident{NamePos: pos(14), Name: "tbl"}, As: pos(18), Alias: &parser.Ident{NamePos: pos(21), Name: "tbl2"}, }, }) /*AssertParseStatement(t, `SELECT * FROM tbl INDEXED BY idx`, &parser.SelectStatement{ Select: pos(0), Columns: []*sql.ResultColumn{ {Star: pos(7)}, }, From: pos(9), Source: &parser.QualifiedTableName{ Name: &parser.Ident{NamePos: pos(14), Name: "tbl"}, Indexed: pos(18), IndexedBy: pos(26), Index: &parser.Ident{NamePos: pos(29), Name: "idx"}, }, })*/ /*AssertParseStatement(t, `SELECT * FROM tbl NOT INDEXED`, &parser.SelectStatement{ Select: pos(0), Columns: []*sql.ResultColumn{ {Star: pos(7)}, }, From: pos(9), Source: &parser.QualifiedTableName{ Name: &parser.Ident{NamePos: pos(14), Name: "tbl"}, Not: pos(18), NotIndexed: pos(22), }, })*/ AssertParseStatement(t, `SELECT * FROM (SELECT *) AS tbl`, &parser.SelectStatement{ Select: pos(0), Columns: []*parser.ResultColumn{ {Star: pos(7)}, }, From: pos(9), Source: &parser.ParenSource{ Lparen: pos(14), X: &parser.SelectStatement{ Select: pos(15), Columns: []*parser.ResultColumn{ {Star: pos(22)}, }, }, Rparen: pos(23), As: pos(25), Alias: &parser.Ident{NamePos: pos(28), Name: "tbl"}, }, }) AssertParseStatement(t, `SELECT * FROM foo, bar`, &parser.SelectStatement{ Select: pos(0), Columns: []*parser.ResultColumn{ {Star: pos(7)}, }, From: pos(9), Source: &parser.JoinClause{ X: &parser.QualifiedTableName{ Name: &parser.Ident{NamePos: pos(14), Name: "foo"}, }, Operator: &parser.JoinOperator{Comma: pos(17)}, Y: &parser.QualifiedTableName{ Name: &parser.Ident{NamePos: pos(19), Name: "bar"}, }, }, }) AssertParseStatement(t, `SELECT * FROM foo JOIN bar`, &parser.SelectStatement{ Select: pos(0), Columns: []*parser.ResultColumn{ {Star: pos(7)}, }, From: pos(9), Source: &parser.JoinClause{ X: &parser.QualifiedTableName{ Name: &parser.Ident{NamePos: pos(14), Name: "foo"}, }, Operator: &parser.JoinOperator{Join: pos(18)}, Y: &parser.QualifiedTableName{ Name: &parser.Ident{NamePos: pos(23), Name: "bar"}, }, }, }) AssertParseStatement(t, `SELECT * FROM foo NATURAL JOIN bar`, &parser.SelectStatement{ Select: pos(0), Columns: []*parser.ResultColumn{ {Star: pos(7)}, }, From: pos(9), Source: &parser.JoinClause{ X: &parser.QualifiedTableName{ Name: &parser.Ident{NamePos: pos(14), Name: "foo"}, }, Operator: &parser.JoinOperator{Natural: pos(18), Join: pos(26)}, Y: &parser.QualifiedTableName{ Name: &parser.Ident{NamePos: pos(31), Name: "bar"}, }, }, }) AssertParseStatement(t, `SELECT * FROM foo INNER JOIN bar ON true`, &parser.SelectStatement{ Select: pos(0), Columns: []*parser.ResultColumn{ {Star: pos(7)}, }, From: pos(9), Source: &parser.JoinClause{ X: &parser.QualifiedTableName{ Name: &parser.Ident{NamePos: pos(14), Name: "foo"}, }, Operator: &parser.JoinOperator{Inner: pos(18), Join: pos(24)}, Y: &parser.QualifiedTableName{ Name: &parser.Ident{NamePos: pos(29), Name: "bar"}, }, Constraint: &parser.OnConstraint{ On: pos(33), X: &parser.BoolLit{ValuePos: pos(36), Value: true}, }, }, }) AssertParseStatement(t, `SELECT * FROM foo LEFT JOIN bar USING (x, y)`, &parser.SelectStatement{ Select: pos(0), Columns: []*parser.ResultColumn{ {Star: pos(7)}, }, From: pos(9), Source: &parser.JoinClause{ X: &parser.QualifiedTableName{ Name: &parser.Ident{NamePos: pos(14), Name: "foo"}, }, Operator: &parser.JoinOperator{Left: pos(18), Join: pos(23)}, Y: &parser.QualifiedTableName{ Name: &parser.Ident{NamePos: pos(28), Name: "bar"}, }, Constraint: &parser.UsingConstraint{ Using: pos(32), Lparen: pos(38), Columns: []*parser.Ident{ {NamePos: pos(39), Name: "x"}, {NamePos: pos(42), Name: "y"}, }, Rparen: pos(43), }, }, }) AssertParseStatement(t, `SELECT * FROM X INNER JOIN Y ON true INNER JOIN Z ON false`, &parser.SelectStatement{ Select: pos(0), Columns: []*parser.ResultColumn{ {Star: pos(7)}, }, From: pos(9), Source: &parser.JoinClause{ X: &parser.QualifiedTableName{ Name: &parser.Ident{NamePos: pos(14), Name: "X"}, }, Operator: &parser.JoinOperator{Inner: pos(16), Join: pos(22)}, Y: &parser.JoinClause{ X: &parser.QualifiedTableName{ Name: &parser.Ident{NamePos: pos(27), Name: "Y"}, }, Operator: &parser.JoinOperator{Inner: pos(37), Join: pos(43)}, Y: &parser.QualifiedTableName{ Name: &parser.Ident{NamePos: pos(48), Name: "Z"}, }, Constraint: &parser.OnConstraint{ On: pos(50), X: &parser.BoolLit{ValuePos: pos(53), Value: false}, }, }, Constraint: &parser.OnConstraint{ On: pos(29), X: &parser.BoolLit{ValuePos: pos(32), Value: true}, }, }, }) AssertParseStatement(t, `SELECT * FROM foo LEFT OUTER JOIN bar`, &parser.SelectStatement{ Select: pos(0), Columns: []*parser.ResultColumn{ {Star: pos(7)}, }, From: pos(9), Source: &parser.JoinClause{ X: &parser.QualifiedTableName{ Name: &parser.Ident{NamePos: pos(14), Name: "foo"}, }, Operator: &parser.JoinOperator{Left: pos(18), Outer: pos(23), Join: pos(29)}, Y: &parser.QualifiedTableName{ Name: &parser.Ident{NamePos: pos(34), Name: "bar"}, }, }, }) AssertParseStatement(t, `SELECT * FROM foo CROSS JOIN bar`, &parser.SelectStatement{ Select: pos(0), Columns: []*parser.ResultColumn{ {Star: pos(7)}, }, From: pos(9), Source: &parser.JoinClause{ X: &parser.QualifiedTableName{ Name: &parser.Ident{NamePos: pos(14), Name: "foo"}, }, Operator: &parser.JoinOperator{Cross: pos(18), Join: pos(24)}, Y: &parser.QualifiedTableName{ Name: &parser.Ident{NamePos: pos(29), Name: "bar"}, }, }, }) /*AssertParseStatement(t, `WITH cte (foo, bar) AS (SELECT baz), xxx AS (SELECT yyy) SELECT bat`, &parser.SelectStatement{ WithClause: &parser.WithClause{ With: pos(0), CTEs: []*sql.CTE{ { TableName: &parser.Ident{NamePos: pos(5), Name: "cte"}, ColumnsLparen: pos(9), Columns: []*sql.Ident{ {NamePos: pos(10), Name: "foo"}, {NamePos: pos(15), Name: "bar"}, }, ColumnsRparen: pos(18), As: pos(20), SelectLparen: pos(23), Select: &parser.SelectStatement{ Select: pos(24), Columns: []*sql.ResultColumn{ {Expr: &parser.Ident{NamePos: pos(31), Name: "baz"}}, }, }, SelectRparen: pos(34), }, { TableName: &parser.Ident{NamePos: pos(37), Name: "xxx"}, As: pos(41), SelectLparen: pos(44), Select: &parser.SelectStatement{ Select: pos(45), Columns: []*sql.ResultColumn{ {Expr: &parser.Ident{NamePos: pos(52), Name: "yyy"}}, }, }, SelectRparen: pos(55), }, }, }, Select: pos(57), Columns: []*sql.ResultColumn{ {Expr: &parser.Ident{NamePos: pos(64), Name: "bat"}}, }, })*/ /*AssertParseStatement(t, `WITH RECURSIVE cte AS (SELECT foo) SELECT bar`, &parser.SelectStatement{ WithClause: &parser.WithClause{ With: pos(0), Recursive: pos(5), CTEs: []*sql.CTE{ { TableName: &parser.Ident{NamePos: pos(15), Name: "cte"}, As: pos(19), SelectLparen: pos(22), Select: &parser.SelectStatement{ Select: pos(23), Columns: []*sql.ResultColumn{ {Expr: &parser.Ident{NamePos: pos(30), Name: "foo"}}, }, }, SelectRparen: pos(33), }, }, }, Select: pos(35), Columns: []*sql.ResultColumn{ {Expr: &parser.Ident{NamePos: pos(42), Name: "bar"}}, }, })*/ AssertParseStatement(t, `SELECT * WHERE true`, &parser.SelectStatement{ Select: pos(0), Columns: []*parser.ResultColumn{{Star: pos(7)}}, Where: pos(9), WhereExpr: &parser.BoolLit{ValuePos: pos(15), Value: true}, }) AssertParseStatement(t, `SELECT * GROUP BY foo, bar`, &parser.SelectStatement{ Select: pos(0), Columns: []*parser.ResultColumn{{Star: pos(7)}}, Group: pos(9), GroupBy: pos(15), GroupByExprs: []parser.Expr{ &parser.Ident{NamePos: pos(18), Name: "foo"}, &parser.Ident{NamePos: pos(23), Name: "bar"}, }, }) AssertParseStatement(t, `SELECT * GROUP BY foo HAVING true`, &parser.SelectStatement{ Select: pos(0), Columns: []*parser.ResultColumn{{Star: pos(7)}}, Group: pos(9), GroupBy: pos(15), GroupByExprs: []parser.Expr{ &parser.Ident{NamePos: pos(18), Name: "foo"}, }, Having: pos(22), HavingExpr: &parser.BoolLit{ValuePos: pos(29), Value: true}, }) AssertParseStatement(t, `SELECT * WINDOW win1 AS (), win2 AS ()`, &parser.SelectStatement{ Select: pos(0), Columns: []*parser.ResultColumn{{Star: pos(7)}}, Window: pos(9), Windows: []*parser.Window{ { Name: &parser.Ident{NamePos: pos(16), Name: "win1"}, As: pos(21), Definition: &parser.WindowDefinition{ Lparen: pos(24), Rparen: pos(25), }, }, { Name: &parser.Ident{NamePos: pos(28), Name: "win2"}, As: pos(33), Definition: &parser.WindowDefinition{ Lparen: pos(36), Rparen: pos(37), }, }, }, }) AssertParseStatement(t, `SELECT * ORDER BY foo ASC, bar DESC`, &parser.SelectStatement{ Select: pos(0), Columns: []*parser.ResultColumn{ {Star: pos(7)}, }, Order: pos(9), OrderBy: pos(15), OrderingTerms: []*parser.OrderingTerm{ {X: &parser.Ident{NamePos: pos(18), Name: "foo"}, Asc: pos(22)}, {X: &parser.Ident{NamePos: pos(27), Name: "bar"}, Desc: pos(31)}, }, }) AssertParseStatement(t, `SELECT * UNION SELECT * ORDER BY foo`, &parser.SelectStatement{ Select: pos(0), Columns: []*parser.ResultColumn{ {Star: pos(7)}, }, Union: pos(9), Compound: &parser.SelectStatement{ Select: pos(15), Columns: []*parser.ResultColumn{ {Star: pos(22)}, }, }, Order: pos(24), OrderBy: pos(30), OrderingTerms: []*parser.OrderingTerm{ {X: &parser.Ident{NamePos: pos(33), Name: "foo"}}, }, }) AssertParseStatement(t, `SELECT * UNION ALL SELECT *`, &parser.SelectStatement{ Select: pos(0), Columns: []*parser.ResultColumn{ {Star: pos(7)}, }, Union: pos(9), UnionAll: pos(15), Compound: &parser.SelectStatement{ Select: pos(19), Columns: []*parser.ResultColumn{ {Star: pos(26)}, }, }, }) AssertParseStatement(t, `SELECT * INTERSECT SELECT *`, &parser.SelectStatement{ Select: pos(0), Columns: []*parser.ResultColumn{ {Star: pos(7)}, }, Intersect: pos(9), Compound: &parser.SelectStatement{ Select: pos(19), Columns: []*parser.ResultColumn{ {Star: pos(26)}, }, }, }) AssertParseStatement(t, `SELECT * EXCEPT SELECT *`, &parser.SelectStatement{ Select: pos(0), Columns: []*parser.ResultColumn{ {Star: pos(7)}, }, Except: pos(9), Compound: &parser.SelectStatement{ Select: pos(16), Columns: []*parser.ResultColumn{ {Star: pos(23)}, }, }, }) /*AssertParseStatement(t, `VALUES (1, 2), (3, 4)`, &parser.SelectStatement{ Values: pos(0), ValueLists: []*sql.ExprList{ { Lparen: pos(7), Exprs: []sql.Expr{ &parser.NumberLit{ValuePos: pos(8), Value: "1"}, &parser.NumberLit{ValuePos: pos(11), Value: "2"}, }, Rparen: pos(12), }, { Lparen: pos(15), Exprs: []sql.Expr{ &parser.NumberLit{ValuePos: pos(16), Value: "3"}, &parser.NumberLit{ValuePos: pos(19), Value: "4"}, }, Rparen: pos(20), }, }, })*/ /*AssertParseStatementError(t, `WITH `, `1:5: expected table name, found 'EOF'`) AssertParseStatementError(t, `WITH cte`, `1:8: expected AS, found 'EOF'`) AssertParseStatementError(t, `WITH cte (`, `1:10: expected column name, found 'EOF'`) AssertParseStatementError(t, `WITH cte (foo`, `1:13: expected comma or right paren, found 'EOF'`) AssertParseStatementError(t, `WITH cte (foo)`, `1:14: expected AS, found 'EOF'`) AssertParseStatementError(t, `WITH cte AS`, `1:11: expected left paren, found 'EOF'`) AssertParseStatementError(t, `WITH cte AS (`, `1:13: expected SELECT or VALUES, found 'EOF'`) AssertParseStatementError(t, `WITH cte AS (SELECT foo`, `1:23: expected right paren, found 'EOF'`) AssertParseStatementError(t, `WITH cte AS (SELECT foo)`, `1:24: expected SELECT, VALUES, INSERT, REPLACE, UPDATE, or DELETE, found 'EOF'`)*/ AssertParseStatementError(t, `SELECT `, `1:7: expected expression, found 'EOF'`) AssertParseStatementError(t, `SELECT 1+`, `1:9: expected expression, found 'EOF'`) AssertParseStatementError(t, `SELECT foo,`, `1:11: expected expression, found 'EOF'`) AssertParseStatementError(t, `SELECT foo AS`, `1:13: expected column alias, found 'EOF'`) AssertParseStatementError(t, `SELECT foo.* AS`, `1:14: expected semicolon or EOF, found 'AS'`) AssertParseStatementError(t, `SELECT foo FROM`, `1:15: expected table name or left paren, found 'EOF'`) /*AssertParseStatementError(t, `SELECT foo FROM foo INDEXED`, `1:27: expected BY, found 'EOF'`) AssertParseStatementError(t, `SELECT foo FROM foo INDEXED BY`, `1:30: expected index name, found 'EOF'`)*/ /*AssertParseStatementError(t, `SELECT foo FROM foo NOT`, `1:23: expected INDEXED, found 'EOF'`)*/ AssertParseStatementError(t, `SELECT * FROM foo INNER`, `1:23: expected JOIN, found 'EOF'`) AssertParseStatementError(t, `SELECT * FROM foo CROSS`, `1:23: expected JOIN, found 'EOF'`) AssertParseStatementError(t, `SELECT * FROM foo NATURAL`, `1:25: expected JOIN, found 'EOF'`) AssertParseStatementError(t, `SELECT * FROM foo LEFT`, `1:22: expected JOIN, found 'EOF'`) AssertParseStatementError(t, `SELECT * FROM foo LEFT OUTER`, `1:28: expected JOIN, found 'EOF'`) AssertParseStatementError(t, `SELECT * FROM foo,`, `1:18: expected table name or left paren, found 'EOF'`) AssertParseStatementError(t, `SELECT * FROM foo JOIN bar ON`, `1:29: expected expression, found 'EOF'`) AssertParseStatementError(t, `SELECT * FROM foo JOIN bar USING`, `1:32: expected left paren, found 'EOF'`) AssertParseStatementError(t, `SELECT * FROM foo JOIN bar USING (`, `1:34: expected column name, found 'EOF'`) AssertParseStatementError(t, `SELECT * FROM foo JOIN bar USING (x`, `1:35: expected comma or right paren, found 'EOF'`) AssertParseStatementError(t, `SELECT * FROM foo JOIN bar USING (x,`, `1:36: expected column name, found 'EOF'`) AssertParseStatementError(t, `SELECT * FROM (`, `1:15: expected table name or left paren, found 'EOF'`) AssertParseStatementError(t, `SELECT * FROM ((`, `1:16: expected table name or left paren, found 'EOF'`) AssertParseStatementError(t, `SELECT * FROM (SELECT`, `1:21: expected expression, found 'EOF'`) AssertParseStatementError(t, `SELECT * FROM (tbl`, `1:18: expected right paren, found 'EOF'`) AssertParseStatementError(t, `SELECT * FROM (SELECT *) AS`, `1:27: expected table alias, found 'EOF'`) AssertParseStatementError(t, `SELECT * FROM foo AS`, `1:20: expected table alias, found 'EOF'`) AssertParseStatementError(t, `SELECT foo WHERE`, `1:16: expected expression, found 'EOF'`) AssertParseStatementError(t, `SELECT * GROUP`, `1:14: expected BY, found 'EOF'`) AssertParseStatementError(t, `SELECT * GROUP BY`, `1:17: expected expression, found 'EOF'`) AssertParseStatementError(t, `SELECT * GROUP BY foo bar`, `1:23: expected semicolon or EOF, found bar`) AssertParseStatementError(t, `SELECT * GROUP BY foo HAVING`, `1:28: expected expression, found 'EOF'`) AssertParseStatementError(t, `SELECT * WINDOW`, `1:15: expected window name, found 'EOF'`) AssertParseStatementError(t, `SELECT * WINDOW win1`, `1:20: expected AS, found 'EOF'`) AssertParseStatementError(t, `SELECT * WINDOW win1 AS`, `1:23: expected left paren, found 'EOF'`) AssertParseStatementError(t, `SELECT * WINDOW win1 AS (`, `1:25: expected right paren, found 'EOF'`) AssertParseStatementError(t, `SELECT * WINDOW win1 AS () win2`, `1:28: expected semicolon or EOF, found win2`) AssertParseStatementError(t, `SELECT * ORDER`, `1:14: expected BY, found 'EOF'`) AssertParseStatementError(t, `SELECT * ORDER BY`, `1:17: expected expression, found 'EOF'`) AssertParseStatementError(t, `SELECT * ORDER BY 1,`, `1:20: expected expression, found 'EOF'`) //AssertParseStatementError(t, `SELECT * LIMIT`, `1:14: expected expression, found 'EOF'`) //AssertParseStatementError(t, `SELECT * LIMIT 1,`, `1:17: expected expression, found 'EOF'`) //AssertParseStatementError(t, `SELECT * LIMIT 1 OFFSET`, `1:23: expected expression, found 'EOF'`) /*AssertParseStatementError(t, `VALUES`, `1:6: expected left paren, found 'EOF'`) AssertParseStatementError(t, `VALUES (`, `1:8: expected expression, found 'EOF'`) AssertParseStatementError(t, `VALUES (1`, `1:9: expected comma or right paren, found 'EOF'`) AssertParseStatementError(t, `VALUES (1,`, `1:10: expected expression, found 'EOF'`)*/ //AssertParseStatementError(t, `SELECT * UNION`, `1:14: expected SELECT or VALUES, found 'EOF'`) }) t.Run("Insert", func(t *testing.T) { AssertParseStatement(t, `INSERT INTO tbl (x, y) VALUES (1, 2)`, &parser.InsertStatement{ Insert: pos(0), Into: pos(7), Table: &parser.Ident{NamePos: pos(12), Name: "tbl"}, ColumnsLparen: pos(16), Columns: []*parser.Ident{ {NamePos: pos(17), Name: "x"}, {NamePos: pos(20), Name: "y"}, }, ColumnsRparen: pos(21), Values: pos(23), TupleList: []*parser.ExprList{ { Lparen: pos(30), Exprs: []parser.Expr{ &parser.IntegerLit{ValuePos: pos(31), Value: "1"}, &parser.IntegerLit{ValuePos: pos(34), Value: "2"}, }, Rparen: pos(35), }, }, }) // Ensure we can parse multiple tuple values in an INSERT INTO statement. AssertParseStatement(t, `INSERT INTO tbl (x, y) VALUES (1, 2), (3, 4)`, &parser.InsertStatement{ Insert: pos(0), Into: pos(7), Table: &parser.Ident{NamePos: pos(12), Name: "tbl"}, ColumnsLparen: pos(16), Columns: []*parser.Ident{ {NamePos: pos(17), Name: "x"}, {NamePos: pos(20), Name: "y"}, }, ColumnsRparen: pos(21), Values: pos(23), TupleList: []*parser.ExprList{ { Lparen: pos(30), Exprs: []parser.Expr{ &parser.IntegerLit{ValuePos: pos(31), Value: "1"}, &parser.IntegerLit{ValuePos: pos(34), Value: "2"}, }, Rparen: pos(35), }, { Lparen: pos(38), Exprs: []parser.Expr{ &parser.IntegerLit{ValuePos: pos(39), Value: "3"}, &parser.IntegerLit{ValuePos: pos(42), Value: "4"}, }, Rparen: pos(43), }, }, }) /*AssertParseStatement(t, `REPLACE INTO tbl (x, y) VALUES (1, 2), (3, 4)`, &parser.InsertStatement{ Replace: pos(0), Into: pos(8), Table: &parser.Ident{NamePos: pos(13), Name: "tbl"}, ColumnsLparen: pos(17), Columns: []*parser.Ident{ {NamePos: pos(18), Name: "x"}, {NamePos: pos(21), Name: "y"}, }, ColumnsRparen: pos(22), Values: pos(24), ValueLists: []*parser.ExprList{ { Lparen: pos(31), Exprs: []parser.Expr{ &parser.IntegerLit{ValuePos: pos(32), Value: "1"}, &parser.IntegerLit{ValuePos: pos(35), Value: "2"}, }, Rparen: pos(36), }, { Lparen: pos(39), Exprs: []parser.Expr{ &parser.IntegerLit{ValuePos: pos(40), Value: "3"}, &parser.IntegerLit{ValuePos: pos(43), Value: "4"}, }, Rparen: pos(44), }, }, })*/ /*AssertParseStatement(t, `INSERT OR REPLACE INTO tbl (x) VALUES (1)`, &parser.InsertStatement{ Insert: pos(0), InsertOr: pos(7), InsertOrReplace: pos(10), Into: pos(18), Table: &parser.Ident{NamePos: pos(23), Name: "tbl"}, ColumnsLparen: pos(27), Columns: []*parser.Ident{ {NamePos: pos(28), Name: "x"}, }, ColumnsRparen: pos(29), Values: pos(31), ValueLists: []*parser.ExprList{{ Lparen: pos(38), Exprs: []parser.Expr{ &parser.IntegerLit{ValuePos: pos(39), Value: "1"}, }, Rparen: pos(40), }}, })*/ /*AssertParseStatement(t, `INSERT OR ROLLBACK INTO tbl (x) VALUES (1)`, &parser.InsertStatement{ Insert: pos(0), InsertOr: pos(7), InsertOrRollback: pos(10), Into: pos(19), Table: &parser.Ident{NamePos: pos(24), Name: "tbl"}, ColumnsLparen: pos(28), Columns: []*parser.Ident{ {NamePos: pos(29), Name: "x"}, }, ColumnsRparen: pos(30), Values: pos(32), ValueLists: []*parser.ExprList{{ Lparen: pos(39), Exprs: []parser.Expr{ &parser.IntegerLit{ValuePos: pos(40), Value: "1"}, }, Rparen: pos(41), }}, })*/ /*AssertParseStatement(t, `INSERT OR ABORT INTO tbl (x) VALUES (1)`, &parser.InsertStatement{ Insert: pos(0), InsertOr: pos(7), InsertOrAbort: pos(10), Into: pos(16), Table: &parser.Ident{NamePos: pos(21), Name: "tbl"}, ColumnsLparen: pos(25), Columns: []*parser.Ident{ {NamePos: pos(26), Name: "x"}, }, ColumnsRparen: pos(27), Values: pos(29), ValueLists: []*parser.ExprList{{ Lparen: pos(36), Exprs: []parser.Expr{ &parser.IntegerLit{ValuePos: pos(37), Value: "1"}, }, Rparen: pos(38), }}, })*/ /*AssertParseStatement(t, `INSERT OR FAIL INTO tbl VALUES (1)`, &parser.InsertStatement{ Insert: pos(0), InsertOr: pos(7), InsertOrFail: pos(10), Into: pos(15), Table: &parser.Ident{NamePos: pos(20), Name: "tbl"}, Values: pos(24), ValueLists: []*parser.ExprList{{ Lparen: pos(31), Exprs: []parser.Expr{ &parser.IntegerLit{ValuePos: pos(32), Value: "1"}, }, Rparen: pos(33), }}, })*/ /*AssertParseStatement(t, `INSERT OR IGNORE INTO tbl AS tbl2 VALUES (1)`, &parser.InsertStatement{ Insert: pos(0), InsertOr: pos(7), InsertOrIgnore: pos(10), Into: pos(17), Table: &parser.Ident{NamePos: pos(22), Name: "tbl"}, As: pos(26), Alias: &parser.Ident{NamePos: pos(29), Name: "tbl2"}, Values: pos(34), ValueLists: []*parser.ExprList{{ Lparen: pos(41), Exprs: []parser.Expr{ &parser.IntegerLit{ValuePos: pos(42), Value: "1"}, }, Rparen: pos(43), }}, })*/ /*AssertParseStatement(t, `WITH cte (foo) AS (SELECT bar) INSERT INTO tbl VALUES (1)`, &parser.InsertStatement{ WithClause: &parser.WithClause{ With: pos(0), CTEs: []*sql.CTE{{ TableName: &parser.Ident{NamePos: pos(5), Name: "cte"}, ColumnsLparen: pos(9), Columns: []*sql.Ident{ {NamePos: pos(10), Name: "foo"}, }, ColumnsRparen: pos(13), As: pos(15), SelectLparen: pos(18), Select: &parser.SelectStatement{ Select: pos(19), Columns: []*sql.ResultColumn{ {Expr: &parser.Ident{NamePos: pos(26), Name: "bar"}}, }, }, SelectRparen: pos(29), }}, }, Insert: pos(31), Into: pos(38), Table: &parser.Ident{NamePos: pos(43), Name: "tbl"}, Values: pos(47), ValueLists: []*sql.ExprList{{ Lparen: pos(54), Exprs: []sql.Expr{ &parser.NumberLit{ValuePos: pos(55), Value: "1"}, }, Rparen: pos(56), }}, })*/ /*AssertParseStatement(t, `WITH cte (foo) AS (SELECT bar) INSERT INTO tbl VALUES (1)`, &parser.InsertStatement{ WithClause: &parser.WithClause{ With: pos(0), CTEs: []*sql.CTE{{ TableName: &parser.Ident{NamePos: pos(5), Name: "cte"}, ColumnsLparen: pos(9), Columns: []*sql.Ident{ {NamePos: pos(10), Name: "foo"}, }, ColumnsRparen: pos(13), As: pos(15), SelectLparen: pos(18), Select: &parser.SelectStatement{ Select: pos(19), Columns: []*sql.ResultColumn{ {Expr: &parser.Ident{NamePos: pos(26), Name: "bar"}}, }, }, SelectRparen: pos(29), }}, }, Insert: pos(31), Into: pos(38), Table: &parser.Ident{NamePos: pos(43), Name: "tbl"}, Values: pos(47), ValueLists: []*sql.ExprList{{ Lparen: pos(54), Exprs: []sql.Expr{ &parser.NumberLit{ValuePos: pos(55), Value: "1"}, }, Rparen: pos(56), }}, })*/ /*AssertParseStatement(t, `INSERT INTO tbl (x) SELECT y`, &parser.InsertStatement{ Insert: pos(0), Into: pos(7), Table: &parser.Ident{NamePos: pos(12), Name: "tbl"}, ColumnsLparen: pos(16), Columns: []*parser.Ident{ {NamePos: pos(17), Name: "x"}, }, ColumnsRparen: pos(18), Select: &parser.SelectStatement{ Select: pos(20), Columns: []*parser.ResultColumn{ {Expr: &parser.Ident{NamePos: pos(27), Name: "y"}}, }, }, })*/ /*AssertParseStatement(t, `INSERT INTO tbl (x) DEFAULT VALUES`, &parser.InsertStatement{ Insert: pos(0), Into: pos(7), Table: &parser.Ident{NamePos: pos(12), Name: "tbl"}, ColumnsLparen: pos(16), Columns: []*parser.Ident{ {NamePos: pos(17), Name: "x"}, }, ColumnsRparen: pos(18), Default: pos(20), DefaultValues: pos(28), })*/ /*AssertParseStatement(t, `INSERT INTO tbl (x) VALUES (1) ON CONFLICT (y ASC, z DESC) DO NOTHING`, &parser.InsertStatement{ Insert: pos(0), Into: pos(7), Table: &parser.Ident{NamePos: pos(12), Name: "tbl"}, ColumnsLparen: pos(16), Columns: []*parser.Ident{ {NamePos: pos(17), Name: "x"}, }, ColumnsRparen: pos(18), Values: pos(20), ValueLists: []*parser.ExprList{{ Lparen: pos(27), Exprs: []parser.Expr{ &parser.IntegerLit{ValuePos: pos(28), Value: "1"}, }, Rparen: pos(29), }}, UpsertClause: &parser.UpsertClause{ On: pos(31), OnConflict: pos(34), Lparen: pos(43), Columns: []*parser.IndexedColumn{ {X: &parser.Ident{NamePos: pos(44), Name: "y"}, Asc: pos(46)}, {X: &parser.Ident{NamePos: pos(51), Name: "z"}, Desc: pos(53)}, }, Rparen: pos(57), Do: pos(59), DoNothing: pos(62), }, })*/ /*AssertParseStatement(t, `INSERT INTO tbl (x) VALUES (1) ON CONFLICT (y) WHERE true DO UPDATE SET foo = 1, (bar, baz) = 2 WHERE false`, &parser.InsertStatement{ Insert: pos(0), Into: pos(7), Table: &parser.Ident{NamePos: pos(12), Name: "tbl"}, ColumnsLparen: pos(16), Columns: []*parser.Ident{ {NamePos: pos(17), Name: "x"}, }, ColumnsRparen: pos(18), Values: pos(20), ValueLists: []*parser.ExprList{{ Lparen: pos(27), Exprs: []parser.Expr{ &parser.IntegerLit{ValuePos: pos(28), Value: "1"}, }, Rparen: pos(29), }}, UpsertClause: &parser.UpsertClause{ On: pos(31), OnConflict: pos(34), Lparen: pos(43), Columns: []*parser.IndexedColumn{ {X: &parser.Ident{NamePos: pos(44), Name: "y"}}, }, Rparen: pos(45), Where: pos(47), WhereExpr: &parser.BoolLit{ValuePos: pos(53), Value: true}, Do: pos(58), DoUpdate: pos(61), DoUpdateSet: pos(68), Assignments: []*parser.Assignment{ { Columns: []*parser.Ident{ {NamePos: pos(72), Name: "foo"}, }, Eq: pos(76), Expr: &parser.IntegerLit{ValuePos: pos(78), Value: "1"}, }, { Lparen: pos(81), Columns: []*parser.Ident{ {NamePos: pos(82), Name: "bar"}, {NamePos: pos(87), Name: "baz"}, }, Rparen: pos(90), Eq: pos(92), Expr: &parser.IntegerLit{ValuePos: pos(94), Value: "2"}, }, }, UpdateWhere: pos(96), UpdateWhereExpr: &parser.BoolLit{ValuePos: pos(102), Value: false}, }, })*/ AssertParseStatementError(t, `INSERT`, `1:6: expected INTO, found 'EOF'`) //AssertParseStatementError(t, `INSERT OR`, `1:9: expected ROLLBACK, REPLACE, ABORT, FAIL, or IGNORE, found 'EOF'`) AssertParseStatementError(t, `INSERT INTO`, `1:11: expected table name, found 'EOF'`) AssertParseStatementError(t, `INSERT INTO tbl AS`, `1:18: expected alias, found 'EOF'`) AssertParseStatementError(t, `INSERT INTO tbl `, `1:16: expected VALUES, found 'EOF'`) AssertParseStatementError(t, `INSERT INTO tbl (`, `1:17: expected column name, found 'EOF'`) AssertParseStatementError(t, `INSERT INTO tbl (x`, `1:18: expected comma or right paren, found 'EOF'`) AssertParseStatementError(t, `INSERT INTO tbl (x)`, `1:19: expected VALUES, found 'EOF'`) AssertParseStatementError(t, `INSERT INTO tbl (x) VALUES`, `1:26: expected left paren, found 'EOF'`) AssertParseStatementError(t, `INSERT INTO tbl (x) VALUES (`, `1:28: expected expression, found 'EOF'`) AssertParseStatementError(t, `INSERT INTO tbl (x) VALUES (1`, `1:29: expected comma or right paren, found 'EOF'`) /*AssertParseStatementError(t, `INSERT INTO tbl (x) SELECT`, `1:26: expected expression, found 'EOF'`) AssertParseStatementError(t, `INSERT INTO tbl (x) DEFAULT`, `1:27: expected VALUES, found 'EOF'`) AssertParseStatementError(t, `INSERT INTO tbl (x) VALUES (1) ON`, `1:33: expected CONFLICT, found 'EOF'`) AssertParseStatementError(t, `INSERT INTO tbl (x) VALUES (1) ON CONFLICT (`, `1:44: expected expression, found 'EOF'`) AssertParseStatementError(t, `INSERT INTO tbl (x) VALUES (1) ON CONFLICT (x`, `1:45: expected comma or right paren, found 'EOF'`) AssertParseStatementError(t, `INSERT INTO tbl (x) VALUES (1) ON CONFLICT (x) WHERE`, `1:52: expected expression, found 'EOF'`) AssertParseStatementError(t, `INSERT INTO tbl (x) VALUES (1) ON CONFLICT (x)`, `1:46: expected DO, found 'EOF'`) AssertParseStatementError(t, `INSERT INTO tbl (x) VALUES (1) ON CONFLICT (x) DO`, `1:49: expected NOTHING or UPDATE SET, found 'EOF'`) AssertParseStatementError(t, `INSERT INTO tbl (x) VALUES (1) ON CONFLICT (x) DO UPDATE`, `1:56: expected SET, found 'EOF'`) AssertParseStatementError(t, `INSERT INTO tbl (x) VALUES (1) ON CONFLICT (x) DO UPDATE SET foo`, `1:64: expected =, found 'EOF'`) AssertParseStatementError(t, `INSERT INTO tbl (x) VALUES (1) ON CONFLICT (x) DO UPDATE SET foo =`, `1:66: expected expression, found 'EOF'`) AssertParseStatementError(t, `INSERT INTO tbl (x) VALUES (1) ON CONFLICT (x) DO UPDATE SET foo = 1 WHERE`, `1:74: expected expression, found 'EOF'`) AssertParseStatementError(t, `INSERT INTO tbl (x) VALUES (1) ON CONFLICT (x) DO UPDATE SET (`, `1:62: expected column name, found 'EOF'`) AssertParseStatementError(t, `INSERT INTO tbl (x) VALUES (1) ON CONFLICT (x) DO UPDATE SET (foo`, `1:65: expected comma or right paren, found 'EOF'`)*/ }) t.Run("Update", func(t *testing.T) { AssertParseStatement(t, `UPDATE tbl SET x = 1, y = 2`, &parser.UpdateStatement{ Update: pos(0), Table: &parser.QualifiedTableName{ Name: &parser.Ident{NamePos: pos(7), Name: "tbl"}, }, Set: pos(11), Assignments: []*parser.Assignment{ { Columns: []*parser.Ident{{NamePos: pos(15), Name: "x"}}, Eq: pos(17), Expr: &parser.IntegerLit{ValuePos: pos(19), Value: "1"}, }, { Columns: []*parser.Ident{{NamePos: pos(22), Name: "y"}}, Eq: pos(24), Expr: &parser.IntegerLit{ValuePos: pos(26), Value: "2"}, }, }, }) AssertParseStatement(t, `UPDATE tbl SET x = 1 WHERE y = 2`, &parser.UpdateStatement{ Update: pos(0), Table: &parser.QualifiedTableName{ Name: &parser.Ident{NamePos: pos(7), Name: "tbl"}, }, Set: pos(11), Assignments: []*parser.Assignment{{ Columns: []*parser.Ident{{NamePos: pos(15), Name: "x"}}, Eq: pos(17), Expr: &parser.IntegerLit{ValuePos: pos(19), Value: "1"}, }}, Where: pos(21), WhereExpr: &parser.BinaryExpr{ X: &parser.Ident{NamePos: pos(27), Name: "y"}, OpPos: pos(29), Op: parser.EQ, Y: &parser.IntegerLit{ValuePos: pos(31), Value: "2"}, }, }) AssertParseStatement(t, `UPDATE OR ROLLBACK tbl SET x = 1`, &parser.UpdateStatement{ Update: pos(0), UpdateOr: pos(7), UpdateOrRollback: pos(10), Table: &parser.QualifiedTableName{ Name: &parser.Ident{NamePos: pos(19), Name: "tbl"}, }, Set: pos(23), Assignments: []*parser.Assignment{{ Columns: []*parser.Ident{{NamePos: pos(27), Name: "x"}}, Eq: pos(29), Expr: &parser.IntegerLit{ValuePos: pos(31), Value: "1"}, }}, }) AssertParseStatement(t, `UPDATE OR ABORT tbl SET x = 1`, &parser.UpdateStatement{ Update: pos(0), UpdateOr: pos(7), UpdateOrAbort: pos(10), Table: &parser.QualifiedTableName{ Name: &parser.Ident{NamePos: pos(16), Name: "tbl"}, }, Set: pos(20), Assignments: []*parser.Assignment{{ Columns: []*parser.Ident{{NamePos: pos(24), Name: "x"}}, Eq: pos(26), Expr: &parser.IntegerLit{ValuePos: pos(28), Value: "1"}, }}, }) AssertParseStatement(t, `UPDATE OR REPLACE tbl SET x = 1`, &parser.UpdateStatement{ Update: pos(0), UpdateOr: pos(7), UpdateOrReplace: pos(10), Table: &parser.QualifiedTableName{ Name: &parser.Ident{NamePos: pos(18), Name: "tbl"}, }, Set: pos(22), Assignments: []*parser.Assignment{{ Columns: []*parser.Ident{{NamePos: pos(26), Name: "x"}}, Eq: pos(28), Expr: &parser.IntegerLit{ValuePos: pos(30), Value: "1"}, }}, }) AssertParseStatement(t, `UPDATE OR FAIL tbl SET x = 1`, &parser.UpdateStatement{ Update: pos(0), UpdateOr: pos(7), UpdateOrFail: pos(10), Table: &parser.QualifiedTableName{ Name: &parser.Ident{NamePos: pos(15), Name: "tbl"}, }, Set: pos(19), Assignments: []*parser.Assignment{{ Columns: []*parser.Ident{{NamePos: pos(23), Name: "x"}}, Eq: pos(25), Expr: &parser.IntegerLit{ValuePos: pos(27), Value: "1"}, }}, }) AssertParseStatement(t, `UPDATE OR IGNORE tbl SET x = 1`, &parser.UpdateStatement{ Update: pos(0), UpdateOr: pos(7), UpdateOrIgnore: pos(10), Table: &parser.QualifiedTableName{ Name: &parser.Ident{NamePos: pos(17), Name: "tbl"}, }, Set: pos(21), Assignments: []*parser.Assignment{{ Columns: []*parser.Ident{{NamePos: pos(25), Name: "x"}}, Eq: pos(27), Expr: &parser.IntegerLit{ValuePos: pos(29), Value: "1"}, }}, }) /*AssertParseStatement(t, `WITH cte (x) AS (SELECT y) UPDATE tbl SET x = 1`, &parser.UpdateStatement{ WithClause: &parser.WithClause{ With: pos(0), CTEs: []*sql.CTE{ { TableName: &parser.Ident{NamePos: pos(5), Name: "cte"}, ColumnsLparen: pos(9), Columns: []*sql.Ident{ {NamePos: pos(10), Name: "x"}, }, ColumnsRparen: pos(11), As: pos(13), SelectLparen: pos(16), Select: &parser.SelectStatement{ Select: pos(17), Columns: []*sql.ResultColumn{ {Expr: &parser.Ident{NamePos: pos(24), Name: "y"}}, }, }, SelectRparen: pos(25), }, }, }, Update: pos(27), Table: &parser.QualifiedTableName{ Name: &parser.Ident{NamePos: pos(34), Name: "tbl"}, }, Set: pos(38), Assignments: []*sql.Assignment{{ Columns: []*sql.Ident{{NamePos: pos(42), Name: "x"}}, Eq: pos(44), Expr: &parser.NumberLit{ValuePos: pos(46), Value: "1"}, }}, })*/ AssertParseStatementError(t, `UPDATE`, `1:6: expected table name, found 'EOF'`) AssertParseStatementError(t, `UPDATE OR`, `1:9: expected ROLLBACK, REPLACE, ABORT, FAIL, or IGNORE, found 'EOF'`) AssertParseStatementError(t, `UPDATE tbl`, `1:10: expected SET, found 'EOF'`) AssertParseStatementError(t, `UPDATE tbl SET`, `1:14: expected column name or column list, found 'EOF'`) AssertParseStatementError(t, `UPDATE tbl SET x = `, `1:19: expected expression, found 'EOF'`) AssertParseStatementError(t, `UPDATE tbl SET x = 1 WHERE`, `1:26: expected expression, found 'EOF'`) AssertParseStatementError(t, `UPDATE tbl SET x = 1 WHERE y =`, `1:30: expected expression, found 'EOF'`) }) t.Run("Delete", func(t *testing.T) { AssertParseStatement(t, `DELETE FROM tbl`, &parser.DeleteStatement{ Delete: pos(0), From: pos(7), Table: &parser.QualifiedTableName{ Name: &parser.Ident{NamePos: pos(12), Name: "tbl"}, }, }) AssertParseStatement(t, `DELETE FROM tbl WHERE x = 1`, &parser.DeleteStatement{ Delete: pos(0), From: pos(7), Table: &parser.QualifiedTableName{ Name: &parser.Ident{NamePos: pos(12), Name: "tbl"}, }, Where: pos(16), WhereExpr: &parser.BinaryExpr{ X: &parser.Ident{NamePos: pos(22), Name: "x"}, OpPos: pos(24), Op: parser.EQ, Y: &parser.IntegerLit{ValuePos: pos(26), Value: "1"}, }, }) /*AssertParseStatement(t, `WITH cte (x) AS (SELECT y) DELETE FROM tbl`, &parser.DeleteStatement{ WithClause: &parser.WithClause{ With: pos(0), CTEs: []*sql.CTE{ { TableName: &parser.Ident{NamePos: pos(5), Name: "cte"}, ColumnsLparen: pos(9), Columns: []*sql.Ident{ {NamePos: pos(10), Name: "x"}, }, ColumnsRparen: pos(11), As: pos(13), SelectLparen: pos(16), Select: &parser.SelectStatement{ Select: pos(17), Columns: []*sql.ResultColumn{ {Expr: &parser.Ident{NamePos: pos(24), Name: "y"}}, }, }, SelectRparen: pos(25), }, }, }, Delete: pos(27), From: pos(34), Table: &parser.QualifiedTableName{ Name: &parser.Ident{NamePos: pos(39), Name: "tbl"}, }, })*/ /*AssertParseStatement(t, `DELETE FROM tbl ORDER BY x, y LIMIT 1 OFFSET 2`, &parser.DeleteStatement{ Delete: pos(0), From: pos(7), Table: &parser.QualifiedTableName{ Name: &parser.Ident{NamePos: pos(12), Name: "tbl"}, }, Order: pos(16), OrderBy: pos(22), OrderingTerms: []*parser.OrderingTerm{ {X: &parser.Ident{NamePos: pos(25), Name: "x"}}, {X: &parser.Ident{NamePos: pos(28), Name: "y"}}, }, Limit: pos(30), LimitExpr: &parser.IntegerLit{ValuePos: pos(36), Value: "1"}, Offset: pos(38), OffsetExpr: &parser.IntegerLit{ValuePos: pos(45), Value: "2"}, })*/ /*AssertParseStatement(t, `DELETE FROM tbl LIMIT 1`, &parser.DeleteStatement{ Delete: pos(0), From: pos(7), Table: &parser.QualifiedTableName{ Name: &parser.Ident{NamePos: pos(12), Name: "tbl"}, }, Limit: pos(16), LimitExpr: &parser.IntegerLit{ValuePos: pos(22), Value: "1"}, })*/ /*AssertParseStatement(t, `DELETE FROM tbl LIMIT 1, 2`, &parser.DeleteStatement{ Delete: pos(0), From: pos(7), Table: &parser.QualifiedTableName{ Name: &parser.Ident{NamePos: pos(12), Name: "tbl"}, }, Limit: pos(16), LimitExpr: &parser.IntegerLit{ValuePos: pos(22), Value: "1"}, OffsetComma: pos(23), OffsetExpr: &parser.IntegerLit{ValuePos: pos(25), Value: "2"}, })*/ AssertParseStatementError(t, `DELETE`, `1:6: expected FROM, found 'EOF'`) AssertParseStatementError(t, `DELETE FROM`, `1:11: expected table name, found 'EOF'`) AssertParseStatementError(t, `DELETE FROM tbl WHERE`, `1:21: expected expression, found 'EOF'`) AssertParseStatementError(t, `DELETE FROM tbl ORDER `, `1:22: expected BY, found 'EOF'`) AssertParseStatementError(t, `DELETE FROM tbl ORDER BY`, `1:24: expected expression, found 'EOF'`) //AssertParseStatementError(t, `DELETE FROM tbl ORDER BY x`, `1:26: expected LIMIT, found 'EOF'`) //AssertParseStatementError(t, `DELETE FROM tbl LIMIT`, `1:21: expected expression, found 'EOF'`) //AssertParseStatementError(t, `DELETE FROM tbl LIMIT 1,`, `1:24: expected expression, found 'EOF'`) //AssertParseStatementError(t, `DELETE FROM tbl LIMIT 1 OFFSET`, `1:30: expected expression, found 'EOF'`) }) /*t.Run("Analyze", func(t *testing.T) { AssertParseStatement(t, `ANALYZE tbl`, &parser.AnalyzeStatement{ Analyze: pos(0), Name: &parser.Ident{NamePos: pos(8), Name: "tbl"}, }) AssertParseStatementError(t, `ANALYZE`, `1:7: expected table or index name, found 'EOF'`) })*/ } func TestParser_ParseExpr(t *testing.T) { t.Run("Ident", func(t *testing.T) { AssertParseExpr(t, `fooBAR_123'`, &parser.Ident{NamePos: pos(0), Name: `fooBAR_123`}) }) t.Run("StringLit", func(t *testing.T) { AssertParseExpr(t, `'foo bar'`, &parser.StringLit{ValuePos: pos(0), Value: `foo bar`}) }) t.Run("Integer", func(t *testing.T) { AssertParseExpr(t, `123`, &parser.IntegerLit{ValuePos: pos(0), Value: `123`}) }) t.Run("Float", func(t *testing.T) { AssertParseExpr(t, `123.456`, &parser.FloatLit{ValuePos: pos(0), Value: `123.456`}) }) t.Run("Null", func(t *testing.T) { AssertParseExpr(t, `NULL`, &parser.NullLit{ValuePos: pos(0)}) }) t.Run("Bool", func(t *testing.T) { AssertParseExpr(t, `true`, &parser.BoolLit{ValuePos: pos(0), Value: true}) AssertParseExpr(t, `false`, &parser.BoolLit{ValuePos: pos(0), Value: false}) }) t.Run("UnaryExpr", func(t *testing.T) { AssertParseExpr(t, `-123`, &parser.UnaryExpr{OpPos: pos(0), Op: parser.MINUS, X: &parser.IntegerLit{ValuePos: pos(1), Value: `123`}}) AssertParseExprError(t, `-`, `1:1: expected expression, found 'EOF'`) }) t.Run("QualifiedRef", func(t *testing.T) { AssertParseExpr(t, `tbl.col`, &parser.QualifiedRef{ Table: &parser.Ident{NamePos: pos(0), Name: "tbl"}, Dot: pos(3), Column: &parser.Ident{NamePos: pos(4), Name: "col"}, }) AssertParseExpr(t, `"tbl"."col"`, &parser.QualifiedRef{ Table: &parser.Ident{NamePos: pos(0), Name: "tbl", Quoted: true}, Dot: pos(5), Column: &parser.Ident{NamePos: pos(6), Name: "col", Quoted: true}, }) AssertParseExprError(t, `tbl.`, `1:4: expected column name, found 'EOF'`) }) t.Run("Exists", func(t *testing.T) { AssertParseExpr(t, `EXISTS (SELECT *)`, &parser.Exists{ Exists: pos(0), Lparen: pos(7), Select: &parser.SelectStatement{ Select: pos(8), Columns: []*parser.ResultColumn{ {Star: pos(15)}, }, }, Rparen: pos(16), }) AssertParseExpr(t, `NOT EXISTS (SELECT *)`, &parser.Exists{ Not: pos(0), Exists: pos(4), Lparen: pos(11), Select: &parser.SelectStatement{ Select: pos(12), Columns: []*parser.ResultColumn{ {Star: pos(19)}, }, }, Rparen: pos(20), }) AssertParseExprError(t, `NOT`, `1:3: expected EXISTS, found 'EOF'`) AssertParseExprError(t, `EXISTS`, `1:6: expected left paren, found 'EOF'`) //AssertParseExprError(t, `EXISTS (`, `1:8: expected SELECT or VALUES, found 'EOF'`) //AssertParseExprError(t, `EXISTS (SELECT`, `1:14: expected expression, found 'EOF'`) //AssertParseExprError(t, `EXISTS (SELECT *`, `1:16: expected right paren, found 'EOF'`) }) t.Run("BinaryExpr", func(t *testing.T) { AssertParseExpr(t, `1 + 2'`, &parser.BinaryExpr{ X: &parser.IntegerLit{ValuePos: pos(0), Value: "1"}, OpPos: pos(2), Op: parser.PLUS, Y: &parser.IntegerLit{ValuePos: pos(4), Value: "2"}, }) AssertParseExpr(t, `1 - 2'`, &parser.BinaryExpr{ X: &parser.IntegerLit{ValuePos: pos(0), Value: "1"}, OpPos: pos(2), Op: parser.MINUS, Y: &parser.IntegerLit{ValuePos: pos(4), Value: "2"}, }) AssertParseExpr(t, `1 * 2'`, &parser.BinaryExpr{ X: &parser.IntegerLit{ValuePos: pos(0), Value: "1"}, OpPos: pos(2), Op: parser.STAR, Y: &parser.IntegerLit{ValuePos: pos(4), Value: "2"}, }) AssertParseExpr(t, `1 / 2'`, &parser.BinaryExpr{ X: &parser.IntegerLit{ValuePos: pos(0), Value: "1"}, OpPos: pos(2), Op: parser.SLASH, Y: &parser.IntegerLit{ValuePos: pos(4), Value: "2"}, }) AssertParseExpr(t, `1 % 2'`, &parser.BinaryExpr{ X: &parser.IntegerLit{ValuePos: pos(0), Value: "1"}, OpPos: pos(2), Op: parser.REM, Y: &parser.IntegerLit{ValuePos: pos(4), Value: "2"}, }) AssertParseExpr(t, `1 || 2'`, &parser.BinaryExpr{ X: &parser.IntegerLit{ValuePos: pos(0), Value: "1"}, OpPos: pos(2), Op: parser.CONCAT, Y: &parser.IntegerLit{ValuePos: pos(5), Value: "2"}, }) AssertParseExpr(t, `1 << 2'`, &parser.BinaryExpr{ X: &parser.IntegerLit{ValuePos: pos(0), Value: "1"}, OpPos: pos(2), Op: parser.LSHIFT, Y: &parser.IntegerLit{ValuePos: pos(5), Value: "2"}, }) AssertParseExpr(t, `1 >> 2'`, &parser.BinaryExpr{ X: &parser.IntegerLit{ValuePos: pos(0), Value: "1"}, OpPos: pos(2), Op: parser.RSHIFT, Y: &parser.IntegerLit{ValuePos: pos(5), Value: "2"}, }) AssertParseExpr(t, `1 & 2'`, &parser.BinaryExpr{ X: &parser.IntegerLit{ValuePos: pos(0), Value: "1"}, OpPos: pos(2), Op: parser.BITAND, Y: &parser.IntegerLit{ValuePos: pos(4), Value: "2"}, }) AssertParseExpr(t, `1 | 2'`, &parser.BinaryExpr{ X: &parser.IntegerLit{ValuePos: pos(0), Value: "1"}, OpPos: pos(2), Op: parser.BITOR, Y: &parser.IntegerLit{ValuePos: pos(4), Value: "2"}, }) AssertParseExpr(t, `1 < 2'`, &parser.BinaryExpr{ X: &parser.IntegerLit{ValuePos: pos(0), Value: "1"}, OpPos: pos(2), Op: parser.LT, Y: &parser.IntegerLit{ValuePos: pos(4), Value: "2"}, }) AssertParseExpr(t, `1 <= 2'`, &parser.BinaryExpr{ X: &parser.IntegerLit{ValuePos: pos(0), Value: "1"}, OpPos: pos(2), Op: parser.LE, Y: &parser.IntegerLit{ValuePos: pos(5), Value: "2"}, }) AssertParseExpr(t, `1 > 2'`, &parser.BinaryExpr{ X: &parser.IntegerLit{ValuePos: pos(0), Value: "1"}, OpPos: pos(2), Op: parser.GT, Y: &parser.IntegerLit{ValuePos: pos(4), Value: "2"}, }) AssertParseExpr(t, `1 >= 2'`, &parser.BinaryExpr{ X: &parser.IntegerLit{ValuePos: pos(0), Value: "1"}, OpPos: pos(2), Op: parser.GE, Y: &parser.IntegerLit{ValuePos: pos(5), Value: "2"}, }) AssertParseExpr(t, `1 = 2'`, &parser.BinaryExpr{ X: &parser.IntegerLit{ValuePos: pos(0), Value: "1"}, OpPos: pos(2), Op: parser.EQ, Y: &parser.IntegerLit{ValuePos: pos(4), Value: "2"}, }) AssertParseExpr(t, `1 != 2'`, &parser.BinaryExpr{ X: &parser.IntegerLit{ValuePos: pos(0), Value: "1"}, OpPos: pos(2), Op: parser.NE, Y: &parser.IntegerLit{ValuePos: pos(5), Value: "2"}, }) AssertParseExpr(t, `(1 + 2)'`, &parser.ParenExpr{ Lparen: pos(0), X: &parser.BinaryExpr{ X: &parser.IntegerLit{ValuePos: pos(1), Value: "1"}, OpPos: pos(3), Op: parser.PLUS, Y: &parser.IntegerLit{ValuePos: pos(5), Value: "2"}, }, Rparen: pos(6), }) AssertParseExprError(t, `(`, `1:1: expected expression, found 'EOF'`) AssertParseExpr(t, `1 IS 2'`, &parser.BinaryExpr{ X: &parser.IntegerLit{ValuePos: pos(0), Value: "1"}, OpPos: pos(2), Op: parser.IS, Y: &parser.IntegerLit{ValuePos: pos(5), Value: "2"}, }) AssertParseExpr(t, `1 IS NOT 2'`, &parser.BinaryExpr{ X: &parser.IntegerLit{ValuePos: pos(0), Value: "1"}, OpPos: pos(2), Op: parser.ISNOT, Y: &parser.IntegerLit{ValuePos: pos(9), Value: "2"}, }) AssertParseExpr(t, `1 LIKE 2'`, &parser.BinaryExpr{ X: &parser.IntegerLit{ValuePos: pos(0), Value: "1"}, OpPos: pos(2), Op: parser.LIKE, Y: &parser.IntegerLit{ValuePos: pos(7), Value: "2"}, }) AssertParseExpr(t, `1 NOT LIKE 2'`, &parser.BinaryExpr{ X: &parser.IntegerLit{ValuePos: pos(0), Value: "1"}, OpPos: pos(2), Op: parser.NOTLIKE, Y: &parser.IntegerLit{ValuePos: pos(11), Value: "2"}, }) AssertParseExpr(t, `1 GLOB 2'`, &parser.BinaryExpr{ X: &parser.IntegerLit{ValuePos: pos(0), Value: "1"}, OpPos: pos(2), Op: parser.GLOB, Y: &parser.IntegerLit{ValuePos: pos(7), Value: "2"}, }) AssertParseExpr(t, `1 NOT GLOB 2'`, &parser.BinaryExpr{ X: &parser.IntegerLit{ValuePos: pos(0), Value: "1"}, OpPos: pos(2), Op: parser.NOTGLOB, Y: &parser.IntegerLit{ValuePos: pos(11), Value: "2"}, }) AssertParseExpr(t, `1 REGEXP 2'`, &parser.BinaryExpr{ X: &parser.IntegerLit{ValuePos: pos(0), Value: "1"}, OpPos: pos(2), Op: parser.REGEXP, Y: &parser.IntegerLit{ValuePos: pos(9), Value: "2"}, }) AssertParseExpr(t, `1 NOT REGEXP 2'`, &parser.BinaryExpr{ X: &parser.IntegerLit{ValuePos: pos(0), Value: "1"}, OpPos: pos(2), Op: parser.NOTREGEXP, Y: &parser.IntegerLit{ValuePos: pos(13), Value: "2"}, }) AssertParseExpr(t, `1 MATCH 2'`, &parser.BinaryExpr{ X: &parser.IntegerLit{ValuePos: pos(0), Value: "1"}, OpPos: pos(2), Op: parser.MATCH, Y: &parser.IntegerLit{ValuePos: pos(8), Value: "2"}, }) AssertParseExpr(t, `1 NOT MATCH 2'`, &parser.BinaryExpr{ X: &parser.IntegerLit{ValuePos: pos(0), Value: "1"}, OpPos: pos(2), Op: parser.NOTMATCH, Y: &parser.IntegerLit{ValuePos: pos(12), Value: "2"}, }) AssertParseExprError(t, `1 NOT TABLE`, `1:7: expected IN, LIKE, GLOB, REGEXP, MATCH, or BETWEEN, found 'TABLE'`) AssertParseExpr(t, `1 IN (2, 3)'`, &parser.BinaryExpr{ X: &parser.IntegerLit{ValuePos: pos(0), Value: "1"}, OpPos: pos(2), Op: parser.IN, Y: &parser.ExprList{ Lparen: pos(5), Exprs: []parser.Expr{ &parser.IntegerLit{ValuePos: pos(6), Value: "2"}, &parser.IntegerLit{ValuePos: pos(9), Value: "3"}, }, Rparen: pos(10), }, }) AssertParseExpr(t, `1 NOT IN (2, 3)'`, &parser.BinaryExpr{ X: &parser.IntegerLit{ValuePos: pos(0), Value: "1"}, OpPos: pos(2), Op: parser.NOTIN, Y: &parser.ExprList{ Lparen: pos(9), Exprs: []parser.Expr{ &parser.IntegerLit{ValuePos: pos(10), Value: "2"}, &parser.IntegerLit{ValuePos: pos(13), Value: "3"}, }, Rparen: pos(14), }, }) AssertParseExprError(t, `1 IN 2`, `1:6: expected left paren, found 2`) AssertParseExprError(t, `1 IN (`, `1:6: expected expression, found 'EOF'`) AssertParseExprError(t, `1 IN (2 3`, `1:9: expected comma or right paren, found 3`) AssertParseExpr(t, `1 BETWEEN 2 AND 3'`, &parser.BinaryExpr{ X: &parser.IntegerLit{ValuePos: pos(0), Value: "1"}, OpPos: pos(2), Op: parser.BETWEEN, Y: &parser.Range{ X: &parser.IntegerLit{ValuePos: pos(10), Value: "2"}, And: pos(12), Y: &parser.IntegerLit{ValuePos: pos(16), Value: "3"}, }, }) AssertParseExpr(t, `1 NOT BETWEEN 2 AND 3'`, &parser.BinaryExpr{ X: &parser.IntegerLit{ValuePos: pos(0), Value: "1"}, OpPos: pos(2), Op: parser.NOTBETWEEN, Y: &parser.Range{ X: &parser.IntegerLit{ValuePos: pos(14), Value: "2"}, And: pos(16), Y: &parser.IntegerLit{ValuePos: pos(20), Value: "3"}, }, }) AssertParseExprError(t, `1 BETWEEN`, `1:9: expected expression, found 'EOF'`) AssertParseExprError(t, `1 BETWEEN 2`, `1:11: expected range expression, found 'EOF'`) AssertParseExprError(t, `1 BETWEEN 2 + 3`, `1:15: expected range expression, found 'EOF'`) AssertParseExprError(t, `1 + `, `1:4: expected expression, found 'EOF'`) }) t.Run("Call", func(t *testing.T) { AssertParseExpr(t, `sum()`, &parser.Call{ Name: &parser.Ident{NamePos: pos(0), Name: "sum"}, Lparen: pos(3), Rparen: pos(4), }) AssertParseExpr(t, `sum(*)`, &parser.Call{ Name: &parser.Ident{NamePos: pos(0), Name: "sum"}, Lparen: pos(3), Star: pos(4), Rparen: pos(5), }) AssertParseExpr(t, `sum(foo, 123)`, &parser.Call{ Name: &parser.Ident{NamePos: pos(0), Name: "sum"}, Lparen: pos(3), Args: []parser.Expr{ &parser.Ident{NamePos: pos(4), Name: "foo"}, &parser.IntegerLit{ValuePos: pos(9), Value: "123"}, }, Rparen: pos(12), }) AssertParseExpr(t, `sum(distinct 'foo')`, &parser.Call{ Name: &parser.Ident{NamePos: pos(0), Name: "sum"}, Lparen: pos(3), Distinct: pos(4), Args: []parser.Expr{ &parser.StringLit{ValuePos: pos(13), Value: "foo"}, }, Rparen: pos(18), }) AssertParseExpr(t, `sum() filter (where true)`, &parser.Call{ Name: &parser.Ident{NamePos: pos(0), Name: "sum"}, Lparen: pos(3), Rparen: pos(4), Filter: &parser.FilterClause{ Filter: pos(6), Lparen: pos(13), Where: pos(14), X: &parser.BoolLit{ValuePos: pos(20), Value: true}, Rparen: pos(24), }, }) AssertParseExpr(t, `sum() over win1`, &parser.Call{ Name: &parser.Ident{NamePos: pos(0), Name: "sum"}, Lparen: pos(3), Rparen: pos(4), Over: &parser.OverClause{ Over: pos(6), Name: &parser.Ident{NamePos: pos(11), Name: "win1"}, }, }) AssertParseExpr(t, `sum() over (win1 partition by foo, bar order by baz ASC NULLS FIRST, biz)`, &parser.Call{ Name: &parser.Ident{NamePos: pos(0), Name: "sum"}, Lparen: pos(3), Rparen: pos(4), Over: &parser.OverClause{ Over: pos(6), Definition: &parser.WindowDefinition{ Lparen: pos(11), Base: &parser.Ident{NamePos: pos(12), Name: "win1"}, Partition: pos(17), PartitionBy: pos(27), Partitions: []parser.Expr{ &parser.Ident{NamePos: pos(30), Name: "foo"}, &parser.Ident{NamePos: pos(35), Name: "bar"}, }, Order: pos(39), OrderBy: pos(45), OrderingTerms: []*parser.OrderingTerm{ { X: &parser.Ident{NamePos: pos(48), Name: "baz"}, Asc: pos(52), Nulls: pos(56), NullsFirst: pos(62), }, { X: &parser.Ident{NamePos: pos(69), Name: "biz"}, }, }, Rparen: pos(72), }, }, }) AssertParseExpr(t, `sum() over (order by baz DESC NULLS LAST)`, &parser.Call{ Name: &parser.Ident{NamePos: pos(0), Name: "sum"}, Lparen: pos(3), Rparen: pos(4), Over: &parser.OverClause{ Over: pos(6), Definition: &parser.WindowDefinition{ Lparen: pos(11), Order: pos(12), OrderBy: pos(18), OrderingTerms: []*parser.OrderingTerm{ { X: &parser.Ident{NamePos: pos(21), Name: "baz"}, Desc: pos(25), Nulls: pos(30), NullsLast: pos(36), }, }, Rparen: pos(40), }, }, }) AssertParseExpr(t, `sum() over (range foo preceding)`, &parser.Call{ Name: &parser.Ident{NamePos: pos(0), Name: "sum"}, Lparen: pos(3), Rparen: pos(4), Over: &parser.OverClause{ Over: pos(6), Definition: &parser.WindowDefinition{ Lparen: pos(11), Frame: &parser.FrameSpec{ Range: pos(12), X: &parser.Ident{NamePos: pos(18), Name: "foo"}, PrecedingX: pos(22), }, Rparen: pos(31), }, }, }) AssertParseExpr(t, `sum() over (rows between foo following and bar preceding)`, &parser.Call{ Name: &parser.Ident{NamePos: pos(0), Name: "sum"}, Lparen: pos(3), Rparen: pos(4), Over: &parser.OverClause{ Over: pos(6), Definition: &parser.WindowDefinition{ Lparen: pos(11), Frame: &parser.FrameSpec{ Rows: pos(12), Between: pos(17), X: &parser.Ident{NamePos: pos(25), Name: "foo"}, FollowingX: pos(29), And: pos(39), Y: &parser.Ident{NamePos: pos(43), Name: "bar"}, PrecedingY: pos(47), }, Rparen: pos(56), }, }, }) AssertParseExpr(t, `sum() over (rows between foo following and bar following)`, &parser.Call{ Name: &parser.Ident{NamePos: pos(0), Name: "sum"}, Lparen: pos(3), Rparen: pos(4), Over: &parser.OverClause{ Over: pos(6), Definition: &parser.WindowDefinition{ Lparen: pos(11), Frame: &parser.FrameSpec{ Rows: pos(12), Between: pos(17), X: &parser.Ident{NamePos: pos(25), Name: "foo"}, FollowingX: pos(29), And: pos(39), Y: &parser.Ident{NamePos: pos(43), Name: "bar"}, FollowingY: pos(47), }, Rparen: pos(56), }, }, }) AssertParseExpr(t, `sum() over (groups between unbounded preceding and unbounded following)`, &parser.Call{ Name: &parser.Ident{NamePos: pos(0), Name: "sum"}, Lparen: pos(3), Rparen: pos(4), Over: &parser.OverClause{ Over: pos(6), Definition: &parser.WindowDefinition{ Lparen: pos(11), Frame: &parser.FrameSpec{ Groups: pos(12), Between: pos(19), UnboundedX: pos(27), PrecedingX: pos(37), And: pos(47), UnboundedY: pos(51), FollowingY: pos(61), }, Rparen: pos(70), }, }, }) AssertParseExpr(t, `sum() over (groups between current row and current row)`, &parser.Call{ Name: &parser.Ident{NamePos: pos(0), Name: "sum"}, Lparen: pos(3), Rparen: pos(4), Over: &parser.OverClause{ Over: pos(6), Definition: &parser.WindowDefinition{ Lparen: pos(11), Frame: &parser.FrameSpec{ Groups: pos(12), Between: pos(19), CurrentX: pos(27), CurrentRowX: pos(35), And: pos(39), CurrentY: pos(43), CurrentRowY: pos(51), }, Rparen: pos(54), }, }, }) AssertParseExpr(t, `sum() over (groups current row exclude no others)`, &parser.Call{ Name: &parser.Ident{NamePos: pos(0), Name: "sum"}, Lparen: pos(3), Rparen: pos(4), Over: &parser.OverClause{ Over: pos(6), Definition: &parser.WindowDefinition{ Lparen: pos(11), Frame: &parser.FrameSpec{ Groups: pos(12), CurrentX: pos(19), CurrentRowX: pos(27), Exclude: pos(31), ExcludeNo: pos(39), ExcludeNoOthers: pos(42), }, Rparen: pos(48), }, }, }) AssertParseExpr(t, `sum() over (groups current row exclude current row)`, &parser.Call{ Name: &parser.Ident{NamePos: pos(0), Name: "sum"}, Lparen: pos(3), Rparen: pos(4), Over: &parser.OverClause{ Over: pos(6), Definition: &parser.WindowDefinition{ Lparen: pos(11), Frame: &parser.FrameSpec{ Groups: pos(12), CurrentX: pos(19), CurrentRowX: pos(27), Exclude: pos(31), ExcludeCurrent: pos(39), ExcludeCurrentRow: pos(47), }, Rparen: pos(50), }, }, }) AssertParseExpr(t, `sum() over (groups current row exclude group)`, &parser.Call{ Name: &parser.Ident{NamePos: pos(0), Name: "sum"}, Lparen: pos(3), Rparen: pos(4), Over: &parser.OverClause{ Over: pos(6), Definition: &parser.WindowDefinition{ Lparen: pos(11), Frame: &parser.FrameSpec{ Groups: pos(12), CurrentX: pos(19), CurrentRowX: pos(27), Exclude: pos(31), ExcludeGroup: pos(39), }, Rparen: pos(44), }, }, }) AssertParseExpr(t, `sum() over (groups current row exclude ties)`, &parser.Call{ Name: &parser.Ident{NamePos: pos(0), Name: "sum"}, Lparen: pos(3), Rparen: pos(4), Over: &parser.OverClause{ Over: pos(6), Definition: &parser.WindowDefinition{ Lparen: pos(11), Frame: &parser.FrameSpec{ Groups: pos(12), CurrentX: pos(19), CurrentRowX: pos(27), Exclude: pos(31), ExcludeTies: pos(39), }, Rparen: pos(43), }, }, }) AssertParseExprError(t, `sum(`, `1:4: expected expression, found 'EOF'`) AssertParseExprError(t, `sum(*`, `1:5: expected right paren, found 'EOF'`) AssertParseExprError(t, `sum(foo foo`, `1:9: expected comma or right paren, found foo`) AssertParseExprError(t, `sum() filter`, `1:12: expected left paren, found 'EOF'`) AssertParseExprError(t, `sum() filter (`, `1:14: expected WHERE, found 'EOF'`) AssertParseExprError(t, `sum() filter (where`, `1:19: expected expression, found 'EOF'`) AssertParseExprError(t, `sum() filter (where true`, `1:24: expected right paren, found 'EOF'`) AssertParseExprError(t, `sum() over`, `1:10: expected left paren, found 'EOF'`) AssertParseExprError(t, `sum() over (partition`, `1:21: expected BY, found 'EOF'`) AssertParseExprError(t, `sum() over (partition by`, `1:24: expected expression, found 'EOF'`) AssertParseExprError(t, `sum() over (partition by foo foo`, `1:30: expected right paren, found foo`) AssertParseExprError(t, `sum() over (order`, `1:17: expected BY, found 'EOF'`) AssertParseExprError(t, `sum() over (order by`, `1:20: expected expression, found 'EOF'`) AssertParseExprError(t, `sum() over (order by foo foo`, `1:26: expected right paren, found foo`) AssertParseExprError(t, `sum() over (order by foo nulls foo`, `1:32: expected FIRST or LAST, found foo`) AssertParseExprError(t, `sum() over (range between`, `1:25: expected expression, found 'EOF'`) AssertParseExprError(t, `sum() over (range between unbounded`, `1:35: expected PRECEDING, found 'EOF'`) AssertParseExprError(t, `sum() over (range between current`, `1:33: expected ROW, found 'EOF'`) AssertParseExprError(t, `sum() over (range between foo`, `1:29: expected PRECEDING or FOLLOWING, found 'EOF'`) AssertParseExprError(t, `sum() over (range between foo following`, `1:39: expected AND, found 'EOF'`) AssertParseExprError(t, `sum() over (range between foo following and`, `1:43: expected expression, found 'EOF'`) AssertParseExprError(t, `sum() over (range between foo following and unbounded`, `1:53: expected FOLLOWING, found 'EOF'`) AssertParseExprError(t, `sum() over (range between foo following and current`, `1:51: expected ROW, found 'EOF'`) AssertParseExprError(t, `sum() over (range between foo following and foo`, `1:47: expected PRECEDING or FOLLOWING, found 'EOF'`) AssertParseExprError(t, `sum() over (range current row exclude`, `1:37: expected NO OTHERS, CURRENT ROW, GROUP, or TIES, found 'EOF'`) AssertParseExprError(t, `sum() over (range current row exclude no`, `1:40: expected OTHERS, found 'EOF'`) AssertParseExprError(t, `sum() over (range current row exclude current`, `1:45: expected ROW, found 'EOF'`) AssertParseExprError(t, `sum() over (range foo following`, `1:23: expected PRECEDING, found 'FOLLOWING'`) }) t.Run("Cast", func(t *testing.T) { AssertParseExpr(t, `CAST (1 AS INTEGER)`, &parser.CastExpr{ Cast: pos(0), Lparen: pos(5), X: &parser.IntegerLit{ValuePos: pos(6), Value: "1"}, As: pos(8), Type: &parser.Type{Name: &parser.Ident{NamePos: pos(11), Name: "INTEGER"}}, Rparen: pos(18), }) AssertParseExprError(t, `CAST`, `1:4: expected left paren, found 'EOF'`) AssertParseExprError(t, `CAST (`, `1:6: expected expression, found 'EOF'`) AssertParseExprError(t, `CAST (1`, `1:7: expected AS, found 'EOF'`) AssertParseExprError(t, `CAST (1 AS`, `1:10: expected type name, found 'EOF'`) AssertParseExprError(t, `CAST (1 AS INTEGER`, `1:18: expected right paren, found 'EOF'`) }) t.Run("Case", func(t *testing.T) { AssertParseExpr(t, `CASE 1 WHEN 2 THEN 3 WHEN 4 THEN 5 ELSE 6 END`, &parser.CaseExpr{ Case: pos(0), Operand: &parser.IntegerLit{ValuePos: pos(5), Value: "1"}, Blocks: []*parser.CaseBlock{ { When: pos(7), Condition: &parser.IntegerLit{ValuePos: pos(12), Value: "2"}, Then: pos(14), Body: &parser.IntegerLit{ValuePos: pos(19), Value: "3"}, }, { When: pos(21), Condition: &parser.IntegerLit{ValuePos: pos(26), Value: "4"}, Then: pos(28), Body: &parser.IntegerLit{ValuePos: pos(33), Value: "5"}, }, }, Else: pos(35), ElseExpr: &parser.IntegerLit{ValuePos: pos(40), Value: "6"}, End: pos(42), }) AssertParseExpr(t, `CASE WHEN 1 THEN 2 END`, &parser.CaseExpr{ Case: pos(0), Blocks: []*parser.CaseBlock{ { When: pos(5), Condition: &parser.IntegerLit{ValuePos: pos(10), Value: "1"}, Then: pos(12), Body: &parser.IntegerLit{ValuePos: pos(17), Value: "2"}, }, }, End: pos(19), }) AssertParseExprError(t, `CASE`, `1:4: expected expression, found 'EOF'`) AssertParseExprError(t, `CASE 1`, `1:6: expected WHEN, found 'EOF'`) AssertParseExprError(t, `CASE WHEN`, `1:9: expected expression, found 'EOF'`) AssertParseExprError(t, `CASE WHEN 1`, `1:11: expected THEN, found 'EOF'`) AssertParseExprError(t, `CASE WHEN 1 THEN`, `1:16: expected expression, found 'EOF'`) AssertParseExprError(t, `CASE WHEN 1 THEN 2`, `1:18: expected WHEN, ELSE or END, found 'EOF'`) AssertParseExprError(t, `CASE WHEN 1 THEN 2 ELSE`, `1:23: expected expression, found 'EOF'`) AssertParseExprError(t, `CASE WHEN 1 THEN 2 ELSE 3`, `1:25: expected END, found 'EOF'`) }) /*t.Run("Raise", func(t *testing.T) { AssertParseExpr(t, `RAISE(IGNORE)`, &parser.Raise{ Raise: pos(0), Lparen: pos(5), Ignore: pos(6), Rparen: pos(12), }) AssertParseExpr(t, `RAISE(ROLLBACK, 'bad error')`, &parser.Raise{ Raise: pos(0), Lparen: pos(5), Rollback: pos(6), Comma: pos(14), Error: &parser.StringLit{ValuePos: pos(16), Value: "bad error"}, Rparen: pos(27), }) AssertParseExpr(t, `RAISE(ABORT, 'error')`, &parser.Raise{ Raise: pos(0), Lparen: pos(5), Abort: pos(6), Comma: pos(11), Error: &parser.StringLit{ValuePos: pos(13), Value: "error"}, Rparen: pos(20), }) AssertParseExpr(t, `RAISE(FAIL, 'error')`, &parser.Raise{ Raise: pos(0), Lparen: pos(5), Fail: pos(6), Comma: pos(10), Error: &parser.StringLit{ValuePos: pos(12), Value: "error"}, Rparen: pos(19), }) AssertParseExprError(t, `RAISE`, `1:5: expected left paren, found 'EOF'`) AssertParseExprError(t, `RAISE(`, `1:6: expected IGNORE, ROLLBACK, ABORT, or FAIL, found 'EOF'`) AssertParseExprError(t, `RAISE(IGNORE`, `1:12: expected right paren, found 'EOF'`) AssertParseExprError(t, `RAISE(ROLLBACK`, `1:14: expected comma, found 'EOF'`) AssertParseExprError(t, `RAISE(ROLLBACK,`, `1:15: expected error message, found 'EOF'`) })*/ } func TestError_Error(t *testing.T) { err := &parser.Error{Msg: "test"} if got, want := err.Error(), `test`; got != want { t.Fatalf("Error()=%s, want %s", got, want) } } // ParseStatementOrFail parses a statement from s. Fail on error. func ParseStatementOrFail(tb testing.TB, s string) parser.Statement { tb.Helper() stmt, err := parser.NewParser(strings.NewReader(s)).ParseStatement() if err != nil { tb.Fatal(err) } return stmt } // AssertParseStatement asserts the value of the first parse of s. func AssertParseStatement(tb testing.TB, s string, want parser.Statement) { tb.Helper() stmt, err := parser.NewParser(strings.NewReader(s)).ParseStatement() if err != nil { tb.Fatal(err) } else if diff := deep.Equal(stmt, want); diff != nil { tb.Fatalf("mismatch:\n%s", strings.Join(diff, "\n")) } } // AssertParseStatementError asserts s parses to a given error string. func AssertParseStatementError(tb testing.TB, s string, want string) { tb.Helper() _, err := parser.NewParser(strings.NewReader(s)).ParseStatement() if err == nil || err.Error() != want { tb.Fatalf("ParseStatement()=%q, want %q", err, want) } } // AssertParseExpr asserts the value of the first parse of s. func AssertParseExpr(tb testing.TB, s string, want parser.Expr) { tb.Helper() stmt, err := parser.NewParser(strings.NewReader(s)).ParseExpr() if err != nil { tb.Fatal(err) } else if diff := deep.Equal(stmt, want); diff != nil { tb.Fatalf("mismatch:\n%s", strings.Join(diff, "\n")) } } // AssertParseExprError asserts s parses to a given error string. func AssertParseExprError(tb testing.TB, s string, want string) { tb.Helper() _, err := parser.NewParser(strings.NewReader(s)).ParseExpr() if err == nil || err.Error() != want { tb.Fatalf("ParseExpr()=%q, want %q", err, want) } } // pos is a helper function for generating positions based on offset for one-line parsing. func pos(offset int) parser.Pos { return parser.Pos{Offset: offset, Line: 1, Column: offset + 1} }