featurebase/sql3/parser/parser_test.go
Seebs c66d392c87 uncomment old LIMIT tests, make them pass
We forward-ported a handful of tests from the previous parser
which relied on LIMIT clauses, but then we didn't support that.
Now that we do, we uncomment most of these tests, and actually
give them the correct data structures to compare with.

We leave two tests commented out. One was using `limit 10, 5` to
express a limit plus offset, and the other is using `not fld = 1`
as a WHERE clause, but we don't support unary-not to negate
other expressions.

In the process, we discover that converting a SELECT with a
LIMIT clause back to a string has a missing space, and fix that.
2023-04-06 11:49:11 -05:00

4930 lines
166 KiB
Go

// Copyright 2021 Molecula Corp. All rights reserved.
package parser_test
import (
"strings"
"testing"
"github.com/davecgh/go-spew/spew"
"github.com/featurebasedb/featurebase/v3/sql3/parser"
"github.com/go-test/deep"
)
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("ErrNoCall", func(t *testing.T) {
AssertParseStatementError(t, `SELECT MIN;`, `1:11: expected call expression, found ';'`)
})
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("AlterDatabase", func(t *testing.T) {
AssertParseStatement(t, `ALTER DATABASE db1 WITH UNITS 4`, &parser.AlterDatabaseStatement{
Alter: pos(0),
Database: pos(6),
Name: &parser.Ident{NamePos: pos(15), Name: "db1"},
With: pos(19),
Option: &parser.UnitsOption{
Units: pos(24),
Expr: &parser.IntegerLit{
ValuePos: pos(30),
Value: "4",
},
},
})
AssertParseStatementError(t, `ALTER`, `1:1: expected DATABASE, TABLE or VIEW`)
AssertParseStatementError(t, `ALTER DATABASE`, `1:14: expected database name, found 'EOF'`)
AssertParseStatementError(t, `ALTER DATABASE db1`, `1:18: expected WITH, found 'EOF'`)
AssertParseStatementError(t, `ALTER DATABASE db1 WITH`, `1:23: expected UNITS, found 'EOF'`)
AssertParseStatementError(t, `ALTER DATABASE db1 WITH UNITS`, `1:29: expected literal, found 'EOF'`)
})
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:1: expected DATABASE, TABLE or VIEW`)
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'`)
})
t.Run("AlterView", func(t *testing.T) {
AssertParseStatementError(t, `ALTER VIEW`, `1:10: expected view name, found 'EOF'`)
AssertParseStatementError(t, `ALTER VIEW vw 23`, `1:15: expected AS, found 23`)
AssertParseStatementError(t, `ALTER VIEW vw AS 23`, `1:18: expected SELECT, found 23`)
})
}
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 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),
ReturnType: &parser.Type{Name: &parser.Ident{NamePos: pos(73), Name: "int"}},
As: pos(77),
Begin: pos(80),
End: pos(86),
})
// 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),
Function: pos(5),
Name: &parser.Ident{NamePos: pos(14), Name: "func"},
})
AssertParseStatement(t, `DROP FUNCTION IF EXISTS func`, &parser.DropFunctionStatement{
Drop: pos(0),
Function: 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("ShowDatabasesAndTables", func(t *testing.T) {
AssertParseStatement(t, `SHOW DATABASES`, &parser.ShowDatabasesStatement{
Show: pos(0),
Databases: pos(5),
})
AssertParseStatement(t, `SHOW TABLES`, &parser.ShowTablesStatement{
Show: pos(0),
Tables: pos(5),
})
AssertParseStatementError(t, `SHOW`, `1:4: expected DATABASES, TABLES, COLUMNS or CREATE, found 'EOF'`)
AssertParseStatementError(t, `SHOW BLAH`, `1:6: expected DATABASES, 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 DATABASES, 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 DATABASES, 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("CreateDatabase", func(t *testing.T) {
AssertParseStatement(t, `CREATE DATABASE db WITH UNITS 4`, &parser.CreateDatabaseStatement{
Create: pos(0),
Database: pos(7),
Name: &parser.Ident{
Name: "db",
NamePos: pos(16),
},
With: pos(19),
Options: []parser.DatabaseOption{
&parser.UnitsOption{
Units: pos(24),
Expr: &parser.IntegerLit{
ValuePos: pos(30),
Value: "4",
},
},
},
})
AssertParseStatement(t, `CREATE DATABASE db WITH COMMENT 'foo' COMMENT 23.5 COMMENT true COMMENT x'foo' COMMENT NULL`, &parser.CreateDatabaseStatement{
Create: pos(0),
Database: pos(7),
Name: &parser.Ident{
Name: "db",
NamePos: pos(16),
},
With: pos(19),
Options: []parser.DatabaseOption{
&parser.CommentOption{
Comment: pos(24),
Expr: &parser.StringLit{
ValuePos: pos(32),
Value: "foo",
},
},
&parser.CommentOption{
Comment: pos(38),
Expr: &parser.FloatLit{
ValuePos: pos(46),
Value: "23.5",
},
},
&parser.CommentOption{
Comment: pos(51),
Expr: &parser.BoolLit{
ValuePos: pos(59),
Value: true,
},
},
&parser.CommentOption{
Comment: pos(64),
Expr: &parser.StringLit{
IsBlob: true,
ValuePos: pos(72),
Value: "foo",
},
},
&parser.CommentOption{
Comment: pos(79),
Expr: &parser.NullLit{
ValuePos: pos(87),
},
},
},
})
AssertParseStatementError(t, `CREATE`, `1:1: expected DATABASE, TABLE, VIEW, FUNCTION or MODEL`)
AssertParseStatementError(t, `CREATE DATABASE`, `1:15: expected database name, found 'EOF'`)
AssertParseStatementError(t, `CREATE DATABASE IF`, `1:18: expected NOT, found 'EOF'`)
AssertParseStatementError(t, `CREATE DATABASE IF NOT`, `1:22: expected EXISTS, found 'EOF'`)
AssertParseStatementError(t, `CREATE DATABASE db (`, `1:20: expected semicolon or EOF, found '('`)
AssertParseStatementError(t, `CREATE DATABASE db extra`, `1:20: expected semicolon or EOF, found extra`)
AssertParseStatementError(t, `CREATE DATABASE db WITH`, `1:20: expected at least one option after WITH`)
AssertParseStatementError(t, `CREATE DATABASE db WITH UNITS`, `1:29: expected literal, found 'EOF'`)
AssertParseStatementError(t, `CREATE DATABASE db WITH COMMENT`, `1:31: expected literal, found 'EOF'`)
})
t.Run("CreateTable", func(t *testing.T) {
AssertParseStatement(t, `CREATE TABLE tbl (col1 TEXT, col2 DECIMAL(2)) KEYPARTITIONS 12 COMMENT 'foo'`, &parser.CreateTableStatement{
Create: pos(0),
Table: pos(7),
Name: &parser.Ident{
Name: "tbl",
NamePos: pos(13),
},
Lparen: pos(17),
Options: []parser.TableOption{
&parser.KeyPartitionsOption{KeyPartitions: pos(46), Expr: &parser.IntegerLit{ValuePos: pos(60), Value: "12"}},
&parser.CommentOption{Comment: pos(63), Expr: &parser.StringLit{ValuePos: pos(71), Value: "foo"}},
},
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("DropDatabase", func(t *testing.T) {
AssertParseStatement(t, `DROP DATABASE db`, &parser.DropDatabaseStatement{
Drop: pos(0),
Database: pos(5),
Name: &parser.Ident{NamePos: pos(14), Name: "db"},
})
AssertParseStatement(t, `DROP DATABASE IF EXISTS db`, &parser.DropDatabaseStatement{
Drop: pos(0),
Database: pos(5),
If: pos(14),
IfExists: pos(17),
Name: &parser.Ident{NamePos: pos(24), Name: "db"},
})
AssertParseStatementError(t, `DROP DATABASE`, `1:13: expected database name, found 'EOF'`)
AssertParseStatementError(t, `DROP DATABASE IF`, `1:16: expected EXISTS, found 'EOF'`)
AssertParseStatementError(t, `DROP DATABASE IF EXISTS`, `1:23: expected database name, 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{
AssertParseStatement(t, `CREATE VIEW vw 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(15),
Select: &parser.SelectStatement{
Select: pos(18),
Columns: []*parser.ResultColumn{
{Expr: &parser.Ident{NamePos: pos(25), Name: "x"}},
{Expr: &parser.Ident{NamePos: pos(28), 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 DATABASE, 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 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.JoinClause{
X: &parser.QualifiedTableName{
Name: &parser.Ident{NamePos: pos(14), Name: "X"},
},
Operator: &parser.JoinOperator{Inner: pos(16), Join: pos(22)},
Y: &parser.QualifiedTableName{
Name: &parser.Ident{NamePos: pos(27), Name: "Y"},
},
Constraint: &parser.OnConstraint{
On: pos(29),
X: &parser.BoolLit{ValuePos: pos(32), Value: true},
},
},
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},
},
},
})
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),
},
},
})*/
AssertParseStatement(t, `SELECT _id FROM tbl`, &parser.SelectStatement{
Select: pos(0),
Columns: []*parser.ResultColumn{
{Expr: &parser.Ident{NamePos: pos(7), Name: "_id"}},
},
From: pos(11),
Source: &parser.QualifiedTableName{
Name: &parser.Ident{NamePos: pos(16), Name: "tbl"},
},
})
AssertParseStatement(t, `SELECT fld FROM tbl`, &parser.SelectStatement{
Select: pos(0),
Columns: []*parser.ResultColumn{
{Expr: &parser.Ident{NamePos: pos(7), Name: "fld"}},
},
From: pos(11),
Source: &parser.QualifiedTableName{
Name: &parser.Ident{NamePos: pos(16), Name: "tbl"},
},
})
AssertParseStatement(t, `SELECT fld1, fld2 FROM tbl`, &parser.SelectStatement{
Select: pos(0),
Columns: []*parser.ResultColumn{
{Expr: &parser.Ident{NamePos: pos(7), Name: "fld1"}},
{Expr: &parser.Ident{NamePos: pos(13), Name: "fld2"}},
},
From: pos(18),
Source: &parser.QualifiedTableName{
Name: &parser.Ident{NamePos: pos(23), Name: "tbl"},
},
})
AssertParseStatement(t, `SELECT COUNT(*) FROM tbl`, &parser.SelectStatement{
Select: pos(0),
Columns: []*parser.ResultColumn{
{
Expr: &parser.Call{
Name: &parser.Ident{NamePos: pos(7), Name: "COUNT"},
Lparen: pos(12),
Star: pos(13),
Rparen: pos(14),
},
},
},
From: pos(16),
Source: &parser.QualifiedTableName{
Name: &parser.Ident{NamePos: pos(21), Name: "tbl"},
},
})
AssertParseStatement(t, `SELECT min(fld) FROM tbl`, &parser.SelectStatement{
Select: pos(0),
Columns: []*parser.ResultColumn{
{
Expr: &parser.Call{
Name: &parser.Ident{NamePos: pos(7), Name: "min"},
Lparen: pos(10),
Args: []parser.Expr{
&parser.Ident{NamePos: pos(11), Name: "fld"},
},
Rparen: pos(14),
},
},
},
From: pos(16),
Source: &parser.QualifiedTableName{
Name: &parser.Ident{NamePos: pos(21), Name: "tbl"},
},
})
AssertParseStatement(t, `SELECT max(fld) FROM tbl`, &parser.SelectStatement{
Select: pos(0),
Columns: []*parser.ResultColumn{
{
Expr: &parser.Call{
Name: &parser.Ident{NamePos: pos(7), Name: "max"},
Lparen: pos(10),
Args: []parser.Expr{
&parser.Ident{NamePos: pos(11), Name: "fld"},
},
Rparen: pos(14),
},
},
},
From: pos(16),
Source: &parser.QualifiedTableName{
Name: &parser.Ident{NamePos: pos(21), Name: "tbl"},
},
})
AssertParseStatement(t, `SELECT sum(fld) FROM tbl`, &parser.SelectStatement{
Select: pos(0),
Columns: []*parser.ResultColumn{
{
Expr: &parser.Call{
Name: &parser.Ident{NamePos: pos(7), Name: "sum"},
Lparen: pos(10),
Args: []parser.Expr{
&parser.Ident{NamePos: pos(11), Name: "fld"},
},
Rparen: pos(14),
},
},
},
From: pos(16),
Source: &parser.QualifiedTableName{
Name: &parser.Ident{NamePos: pos(21), Name: "tbl"},
},
})
AssertParseStatement(t, `SELECT avg(fld) FROM tbl`, &parser.SelectStatement{
Select: pos(0),
Columns: []*parser.ResultColumn{
{
Expr: &parser.Call{
Name: &parser.Ident{NamePos: pos(7), Name: "avg"},
Lparen: pos(10),
Args: []parser.Expr{
&parser.Ident{NamePos: pos(11), Name: "fld"},
},
Rparen: pos(14),
},
},
},
From: pos(16),
Source: &parser.QualifiedTableName{
Name: &parser.Ident{NamePos: pos(21), Name: "tbl"},
},
})
AssertParseStatement(t, `SELECT _id, COUNT(*) FROM tbl`, &parser.SelectStatement{
Select: pos(0),
Columns: []*parser.ResultColumn{
{Expr: &parser.Ident{NamePos: pos(7), Name: "_id"}},
{
Expr: &parser.Call{
Name: &parser.Ident{NamePos: pos(12), Name: "COUNT"},
Lparen: pos(17),
Star: pos(18),
Rparen: pos(19),
},
},
},
From: pos(21),
Source: &parser.QualifiedTableName{
Name: &parser.Ident{NamePos: pos(26), Name: "tbl"},
},
})
AssertParseStatement(t, `SELECT _id FROM tbl1, tbl2`, &parser.SelectStatement{
Select: pos(0),
Columns: []*parser.ResultColumn{
{Expr: &parser.Ident{NamePos: pos(7), Name: "_id"}},
},
From: pos(11),
Source: &parser.JoinClause{
X: &parser.QualifiedTableName{
Name: &parser.Ident{NamePos: pos(16), Name: "tbl1"},
},
Operator: &parser.JoinOperator{
Comma: pos(20),
},
Y: &parser.QualifiedTableName{
Name: &parser.Ident{NamePos: pos(22), Name: "tbl2"},
},
},
})
AssertParseStatement(t, `SELECT _id FROM tbl1 INNER JOIN tbl2`, &parser.SelectStatement{
Select: pos(0),
Columns: []*parser.ResultColumn{
{Expr: &parser.Ident{NamePos: pos(7), Name: "_id"}},
},
From: pos(11),
Source: &parser.JoinClause{
X: &parser.QualifiedTableName{
Name: &parser.Ident{NamePos: pos(16), Name: "tbl1"},
},
Operator: &parser.JoinOperator{
Inner: pos(21),
Join: pos(27),
},
Y: &parser.QualifiedTableName{
Name: &parser.Ident{NamePos: pos(32), Name: "tbl2"},
},
},
})
AssertParseStatement(t, `SELECT * FROM tbl where _id = 1`, &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"},
},
Where: pos(18),
WhereExpr: &parser.BinaryExpr{
X: &parser.Ident{NamePos: pos(24), Name: "_id"},
OpPos: pos(28),
Op: parser.EQ,
Y: &parser.IntegerLit{
ValuePos: pos(30),
Value: "1",
},
},
})
AssertParseStatement(t, `SELECT * FROM tbl where fld = 1`, &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"},
},
Where: pos(18),
WhereExpr: &parser.BinaryExpr{
X: &parser.Ident{NamePos: pos(24), Name: "fld"},
OpPos: pos(28),
Op: parser.EQ,
Y: &parser.IntegerLit{
ValuePos: pos(30),
Value: "1",
},
},
})
AssertParseStatement(t, `SELECT * FROM tbl group by fld`, &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"},
},
Group: pos(18),
GroupBy: pos(24),
GroupByExprs: []parser.Expr{
&parser.Ident{NamePos: pos(27), Name: "fld"},
},
})
AssertParseStatement(t, `SELECT * FROM tbl group by fld1, fld2`, &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"},
},
Group: pos(18),
GroupBy: pos(24),
GroupByExprs: []parser.Expr{
&parser.Ident{NamePos: pos(27), Name: "fld1"},
&parser.Ident{NamePos: pos(33), Name: "fld2"},
},
})
AssertParseStatement(t, `SELECT fld, sum(fld) FROM tbl group by fld`, &parser.SelectStatement{
Select: pos(0),
Columns: []*parser.ResultColumn{
{Expr: &parser.Ident{NamePos: pos(7), Name: "fld"}},
{
Expr: &parser.Call{
Name: &parser.Ident{NamePos: pos(12), Name: "sum"},
Lparen: pos(15),
Args: []parser.Expr{
&parser.Ident{NamePos: pos(16), Name: "fld"},
},
Rparen: pos(19),
},
},
},
From: pos(21),
Source: &parser.QualifiedTableName{
Name: &parser.Ident{NamePos: pos(26), Name: "tbl"},
},
Group: pos(30),
GroupBy: pos(36),
GroupByExprs: []parser.Expr{
&parser.Ident{NamePos: pos(39), Name: "fld"},
},
})
AssertParseStatement(t, `SELECT fld, sum(fld) FROM tbl group by fld having sum > 10`, &parser.SelectStatement{
Select: pos(0),
Columns: []*parser.ResultColumn{
{Expr: &parser.Ident{NamePos: pos(7), Name: "fld"}},
{
Expr: &parser.Call{
Name: &parser.Ident{NamePos: pos(12), Name: "sum"},
Lparen: pos(15),
Args: []parser.Expr{
&parser.Ident{NamePos: pos(16), Name: "fld"},
},
Rparen: pos(19),
},
},
},
From: pos(21),
Source: &parser.QualifiedTableName{
Name: &parser.Ident{NamePos: pos(26), Name: "tbl"},
},
Group: pos(30),
GroupBy: pos(36),
GroupByExprs: []parser.Expr{
&parser.Ident{NamePos: pos(39), Name: "fld"},
},
Having: pos(43),
HavingExpr: &parser.BinaryExpr{
X: &parser.Ident{NamePos: pos(50), Name: "sum"},
OpPos: pos(54),
Op: parser.GT,
Y: &parser.IntegerLit{
ValuePos: pos(56),
Value: "10",
},
},
})
AssertParseStatement(t, `SELECT fld FROM tbl order by fld`, &parser.SelectStatement{
Select: pos(0),
Columns: []*parser.ResultColumn{
{Expr: &parser.Ident{NamePos: pos(7), Name: "fld"}},
},
From: pos(11),
Source: &parser.QualifiedTableName{
Name: &parser.Ident{NamePos: pos(16), Name: "tbl"},
},
Order: pos(20),
OrderBy: pos(26),
OrderingTerms: []*parser.OrderingTerm{
{
X: &parser.Ident{NamePos: pos(29), Name: "fld"},
},
},
})
AssertParseStatement(t, `SELECT fld FROM tbl order by fld1, fld2`, &parser.SelectStatement{
Select: pos(0),
Columns: []*parser.ResultColumn{
{Expr: &parser.Ident{NamePos: pos(7), Name: "fld"}},
},
From: pos(11),
Source: &parser.QualifiedTableName{
Name: &parser.Ident{NamePos: pos(16), Name: "tbl"},
},
Order: pos(20),
OrderBy: pos(26),
OrderingTerms: []*parser.OrderingTerm{
{
X: &parser.Ident{NamePos: pos(29), Name: "fld1"},
},
{
X: &parser.Ident{NamePos: pos(35), Name: "fld2"},
},
},
})
AssertParseStatement(t, `SELECT DISTINCT fld FROM tbl`, &parser.SelectStatement{
Select: pos(0),
Distinct: pos(7),
Columns: []*parser.ResultColumn{
{Expr: &parser.Ident{NamePos: pos(16), Name: "fld"}},
},
From: pos(20),
Source: &parser.QualifiedTableName{
Name: &parser.Ident{NamePos: pos(25), Name: "tbl"},
},
})
AssertParseStatement(t, `SELECT COUNT(*) FROM tbl where fld = 1`, &parser.SelectStatement{
Select: pos(0),
Columns: []*parser.ResultColumn{
{
Expr: &parser.Call{
Name: &parser.Ident{NamePos: pos(7), Name: "COUNT"},
Lparen: pos(12),
Star: pos(13),
Rparen: pos(14),
},
},
},
From: pos(16),
Source: &parser.QualifiedTableName{
Name: &parser.Ident{NamePos: pos(21), Name: "tbl"},
},
Where: pos(25),
WhereExpr: &parser.BinaryExpr{
X: &parser.Ident{NamePos: pos(31), Name: "fld"},
OpPos: pos(35),
Op: parser.EQ,
Y: &parser.IntegerLit{
ValuePos: pos(37),
Value: "1",
},
},
})
AssertParseStatement(t, `SELECT COUNT(*) FROM tbl where fld1 = 1 and fld2 = 2`, &parser.SelectStatement{
Select: pos(0),
Columns: []*parser.ResultColumn{
{
Expr: &parser.Call{
Name: &parser.Ident{NamePos: pos(7), Name: "COUNT"},
Lparen: pos(12),
Star: pos(13),
Rparen: pos(14),
},
},
},
From: pos(16),
Source: &parser.QualifiedTableName{
Name: &parser.Ident{NamePos: pos(21), Name: "tbl"},
},
Where: pos(25),
WhereExpr: &parser.BinaryExpr{
X: &parser.BinaryExpr{
X: &parser.Ident{NamePos: pos(31), Name: "fld1"},
OpPos: pos(36),
Op: parser.EQ,
Y: &parser.IntegerLit{
ValuePos: pos(38),
Value: "1",
},
},
OpPos: pos(40),
Op: parser.AND,
Y: &parser.BinaryExpr{
X: &parser.Ident{NamePos: pos(44), Name: "fld2"},
OpPos: pos(49),
Op: parser.EQ,
Y: &parser.IntegerLit{
ValuePos: pos(51),
Value: "2",
},
},
},
})
AssertParseStatement(t, `SELECT COUNT(*) FROM tbl where fld1 = 1 or fld2 = 2`, &parser.SelectStatement{
Select: pos(0),
Columns: []*parser.ResultColumn{
{
Expr: &parser.Call{
Name: &parser.Ident{NamePos: pos(7), Name: "COUNT"},
Lparen: pos(12),
Star: pos(13),
Rparen: pos(14),
},
},
},
From: pos(16),
Source: &parser.QualifiedTableName{
Name: &parser.Ident{NamePos: pos(21), Name: "tbl"},
},
Where: pos(25),
WhereExpr: &parser.BinaryExpr{
X: &parser.BinaryExpr{
X: &parser.Ident{NamePos: pos(31), Name: "fld1"},
OpPos: pos(36),
Op: parser.EQ,
Y: &parser.IntegerLit{
ValuePos: pos(38),
Value: "1",
},
},
OpPos: pos(40),
Op: parser.OR,
Y: &parser.BinaryExpr{
X: &parser.Ident{NamePos: pos(43), Name: "fld2"},
OpPos: pos(48),
Op: parser.EQ,
Y: &parser.IntegerLit{
ValuePos: pos(50),
Value: "2",
},
},
},
})
AssertParseStatement(t, `SELECT _id FROM tbl where fld between 1 and 3`, &parser.SelectStatement{
Select: pos(0),
Columns: []*parser.ResultColumn{
{Expr: &parser.Ident{NamePos: pos(7), Name: "_id"}},
},
From: pos(11),
Source: &parser.QualifiedTableName{
Name: &parser.Ident{NamePos: pos(16), Name: "tbl"},
},
Where: pos(20),
WhereExpr: &parser.BinaryExpr{
X: &parser.Ident{NamePos: pos(26), Name: "fld"},
OpPos: pos(30),
Op: parser.BETWEEN,
Y: &parser.Range{
X: &parser.IntegerLit{
ValuePos: pos(38),
Value: "1",
},
And: pos(40),
Y: &parser.IntegerLit{
ValuePos: pos(44),
Value: "3",
},
},
},
})
AssertParseStatement(t, `SELECT _id FROM tbl where (fld1 between 1 and 3) and (fld2 = 2)`, &parser.SelectStatement{
Select: pos(0),
Columns: []*parser.ResultColumn{
{Expr: &parser.Ident{NamePos: pos(7), Name: "_id"}},
},
From: pos(11),
Source: &parser.QualifiedTableName{
Name: &parser.Ident{NamePos: pos(16), Name: "tbl"},
},
Where: pos(20),
WhereExpr: &parser.BinaryExpr{
X: &parser.ParenExpr{
Lparen: pos(26),
X: &parser.BinaryExpr{
X: &parser.Ident{NamePos: pos(27), Name: "fld1"},
Op: parser.BETWEEN,
OpPos: pos(32),
Y: &parser.Range{
X: &parser.IntegerLit{
ValuePos: pos(40),
Value: "1",
},
And: pos(42),
Y: &parser.IntegerLit{
ValuePos: pos(46),
Value: "3",
},
},
},
Rparen: pos(47),
},
OpPos: pos(49),
Op: parser.AND,
Y: &parser.ParenExpr{
Lparen: pos(53),
X: &parser.BinaryExpr{
X: &parser.Ident{NamePos: pos(54), Name: "fld2"},
OpPos: pos(59),
Op: parser.EQ,
Y: &parser.IntegerLit{
ValuePos: pos(61),
Value: "2",
},
},
Rparen: pos(62),
},
},
})
AssertParseStatement(t, `SELECT COUNT(*) FROM tbl where fld is not null`, &parser.SelectStatement{
Select: pos(0),
Columns: []*parser.ResultColumn{
{
Expr: &parser.Call{
Name: &parser.Ident{NamePos: pos(7), Name: "COUNT"},
Lparen: pos(12),
Star: pos(13),
Rparen: pos(14),
},
},
},
From: pos(16),
Source: &parser.QualifiedTableName{
Name: &parser.Ident{NamePos: pos(21), Name: "tbl"},
},
Where: pos(25),
WhereExpr: &parser.BinaryExpr{
X: &parser.Ident{NamePos: pos(31), Name: "fld"},
OpPos: pos(35),
Op: parser.ISNOT,
Y: &parser.NullLit{
ValuePos: pos(42),
},
},
})
AssertParseStatement(t, `SELECT fld, COUNT(*) FROM tbl group by fld`, &parser.SelectStatement{
Select: pos(0),
Columns: []*parser.ResultColumn{
{Expr: &parser.Ident{NamePos: pos(7), Name: "fld"}},
{
Expr: &parser.Call{
Name: &parser.Ident{NamePos: pos(12), Name: "COUNT"},
Lparen: pos(17),
Star: pos(18),
Rparen: pos(19),
},
},
},
From: pos(21),
Source: &parser.QualifiedTableName{
Name: &parser.Ident{NamePos: pos(26), Name: "tbl"},
},
Group: pos(30),
GroupBy: pos(36),
GroupByExprs: []parser.Expr{
&parser.Ident{NamePos: pos(39), Name: "fld"},
},
})
AssertParseStatement(t, `SELECT fld1, fld2, COUNT(*) FROM grouper group by fld1, fld2`, &parser.SelectStatement{
Select: pos(0),
Columns: []*parser.ResultColumn{
{Expr: &parser.Ident{NamePos: pos(7), Name: "fld1"}},
{Expr: &parser.Ident{NamePos: pos(13), Name: "fld2"}},
{
Expr: &parser.Call{
Name: &parser.Ident{NamePos: pos(19), Name: "COUNT"},
Lparen: pos(24),
Star: pos(25),
Rparen: pos(26),
},
},
},
From: pos(28),
Source: &parser.QualifiedTableName{
Name: &parser.Ident{NamePos: pos(33), Name: "grouper"},
},
Group: pos(41),
GroupBy: pos(47),
GroupByExprs: []parser.Expr{
&parser.Ident{NamePos: pos(50), Name: "fld1"},
&parser.Ident{NamePos: pos(56), Name: "fld2"},
},
})
AssertParseStatement(t, `SELECT COUNT(DISTINCT fld) FROM tbl`, &parser.SelectStatement{
Select: pos(0),
Columns: []*parser.ResultColumn{
{
Expr: &parser.Call{
Name: &parser.Ident{NamePos: pos(7), Name: "COUNT"},
Lparen: pos(12),
Distinct: pos(13),
Args: []parser.Expr{
&parser.Ident{NamePos: pos(22), Name: "fld"},
},
Rparen: pos(25),
},
},
},
From: pos(27),
Source: &parser.QualifiedTableName{
Name: &parser.Ident{NamePos: pos(32), Name: "tbl"},
},
})
AssertParseStatement(t, `SELECT COUNT(*) FROM tbl1 INNER JOIN tbl2 ON tbl1._id = tbl2.bsi`, &parser.SelectStatement{
Select: pos(0),
Columns: []*parser.ResultColumn{
{
Expr: &parser.Call{
Name: &parser.Ident{NamePos: pos(7), Name: "COUNT"},
Lparen: pos(12),
Star: pos(13),
Rparen: pos(14),
},
},
},
From: pos(16),
Source: &parser.JoinClause{
X: &parser.QualifiedTableName{
Name: &parser.Ident{NamePos: pos(21), Name: "tbl1"},
},
Operator: &parser.JoinOperator{
Inner: pos(26),
Join: pos(32),
},
Y: &parser.QualifiedTableName{
Name: &parser.Ident{NamePos: pos(37), Name: "tbl2"},
},
Constraint: &parser.OnConstraint{
On: pos(42),
X: &parser.BinaryExpr{
X: &parser.QualifiedRef{
Table: &parser.Ident{NamePos: pos(45), Name: "tbl1"},
Dot: pos(49),
Column: &parser.Ident{NamePos: pos(50), Name: "_id"},
ColumnIndex: 0,
},
OpPos: pos(54),
Op: parser.EQ,
Y: &parser.QualifiedRef{
Table: &parser.Ident{NamePos: pos(56), Name: "tbl2"},
Dot: pos(60),
Column: &parser.Ident{NamePos: pos(61), Name: "bsi"},
ColumnIndex: 0,
},
},
},
},
})
AssertParseStatement(t, `SELECT COUNT(*) FROM tbl1 INNER JOIN tbl2 ON tbl1._id = tbl2.bsi where tbl1.fld1 = 1 and tbl2.fld2 = 2`, &parser.SelectStatement{
Select: pos(0),
Columns: []*parser.ResultColumn{
{
Expr: &parser.Call{
Name: &parser.Ident{NamePos: pos(7), Name: "COUNT"},
Lparen: pos(12),
Star: pos(13),
Rparen: pos(14),
},
},
},
From: pos(16),
Source: &parser.JoinClause{
X: &parser.QualifiedTableName{
Name: &parser.Ident{NamePos: pos(21), Name: "tbl1"},
},
Operator: &parser.JoinOperator{
Inner: pos(26),
Join: pos(32),
},
Y: &parser.QualifiedTableName{
Name: &parser.Ident{NamePos: pos(37), Name: "tbl2"},
},
Constraint: &parser.OnConstraint{
On: pos(42),
X: &parser.BinaryExpr{
X: &parser.QualifiedRef{
Table: &parser.Ident{NamePos: pos(45), Name: "tbl1"},
Dot: pos(49),
Column: &parser.Ident{NamePos: pos(50), Name: "_id"},
ColumnIndex: 0,
},
OpPos: pos(54),
Op: parser.EQ,
Y: &parser.QualifiedRef{
Table: &parser.Ident{NamePos: pos(56), Name: "tbl2"},
Dot: pos(60),
Column: &parser.Ident{NamePos: pos(61), Name: "bsi"},
ColumnIndex: 0,
},
},
},
},
Where: pos(65),
WhereExpr: &parser.BinaryExpr{
X: &parser.BinaryExpr{
X: &parser.QualifiedRef{
Table: &parser.Ident{NamePos: pos(71), Name: "tbl1"},
Dot: pos(75),
Column: &parser.Ident{NamePos: pos(76), Name: "fld1"},
ColumnIndex: 0,
},
OpPos: pos(81),
Op: parser.EQ,
Y: &parser.IntegerLit{
ValuePos: pos(83),
Value: "1",
},
},
OpPos: pos(85),
Op: parser.AND,
Y: &parser.BinaryExpr{
X: &parser.QualifiedRef{
Table: &parser.Ident{NamePos: pos(89), Name: "tbl2"},
Dot: pos(93),
Column: &parser.Ident{NamePos: pos(94), Name: "fld2"},
ColumnIndex: 0,
},
OpPos: pos(99),
Op: parser.EQ,
Y: &parser.IntegerLit{
ValuePos: pos(101),
Value: "2",
},
},
},
})
AssertParseStatement(t, `SELECT COUNT(*) FROM grouper g INNER JOIN joiner j ON g._id = j.grouperid`, &parser.SelectStatement{
Select: pos(0),
Columns: []*parser.ResultColumn{
{
Expr: &parser.Call{
Name: &parser.Ident{NamePos: pos(7), Name: "COUNT"},
Lparen: pos(12),
Star: pos(13),
Rparen: pos(14),
},
},
},
From: pos(16),
Source: &parser.JoinClause{
X: &parser.QualifiedTableName{
Name: &parser.Ident{NamePos: pos(21), Name: "grouper"},
Alias: &parser.Ident{NamePos: pos(29), Name: "g"},
},
Operator: &parser.JoinOperator{
Inner: pos(31),
Join: pos(37),
},
Y: &parser.QualifiedTableName{
Name: &parser.Ident{NamePos: pos(42), Name: "joiner"},
Alias: &parser.Ident{NamePos: pos(49), Name: "j"},
},
Constraint: &parser.OnConstraint{
On: pos(51),
X: &parser.BinaryExpr{
X: &parser.QualifiedRef{
Table: &parser.Ident{NamePos: pos(54), Name: "g"},
Dot: pos(55),
Column: &parser.Ident{NamePos: pos(56), Name: "_id"},
ColumnIndex: 0,
},
OpPos: pos(60),
Op: parser.EQ,
Y: &parser.QualifiedRef{
Table: &parser.Ident{NamePos: pos(62), Name: "j"},
Dot: pos(63),
Column: &parser.Ident{NamePos: pos(64), Name: "grouperid"},
ColumnIndex: 0,
},
},
},
},
})
AssertParseStatement(t, `SELECT _id FROM grouper g INNER JOIN joiner j ON g._id = j.grouperid`, &parser.SelectStatement{
Select: pos(0),
Columns: []*parser.ResultColumn{
{Expr: &parser.Ident{NamePos: pos(7), Name: "_id"}},
},
From: pos(11),
Source: &parser.JoinClause{
X: &parser.QualifiedTableName{
Name: &parser.Ident{NamePos: pos(16), Name: "grouper"},
Alias: &parser.Ident{NamePos: pos(24), Name: "g"},
},
Operator: &parser.JoinOperator{
Inner: pos(26),
Join: pos(32),
},
Y: &parser.QualifiedTableName{
Name: &parser.Ident{NamePos: pos(37), Name: "joiner"},
Alias: &parser.Ident{NamePos: pos(44), Name: "j"},
},
Constraint: &parser.OnConstraint{
On: pos(46),
X: &parser.BinaryExpr{
X: &parser.QualifiedRef{
Table: &parser.Ident{NamePos: pos(49), Name: "g"},
Dot: pos(50),
Column: &parser.Ident{NamePos: pos(51), Name: "_id"},
ColumnIndex: 0,
},
OpPos: pos(55),
Op: parser.EQ,
Y: &parser.QualifiedRef{
Table: &parser.Ident{NamePos: pos(57), Name: "j"},
Dot: pos(58),
Column: &parser.Ident{NamePos: pos(59), Name: "grouperid"},
ColumnIndex: 0,
},
},
},
},
})
AssertParseStatement(t, `SELECT _id FROM grouper g INNER JOIN joiner j ON g._id = j.grouperid where g.color = 'red'`, &parser.SelectStatement{
Select: pos(0),
Columns: []*parser.ResultColumn{
{Expr: &parser.Ident{NamePos: pos(7), Name: "_id"}},
},
From: pos(11),
Source: &parser.JoinClause{
X: &parser.QualifiedTableName{
Name: &parser.Ident{NamePos: pos(16), Name: "grouper"},
Alias: &parser.Ident{NamePos: pos(24), Name: "g"},
},
Operator: &parser.JoinOperator{
Inner: pos(26),
Join: pos(32),
},
Y: &parser.QualifiedTableName{
Name: &parser.Ident{NamePos: pos(37), Name: "joiner"},
Alias: &parser.Ident{NamePos: pos(44), Name: "j"},
},
Constraint: &parser.OnConstraint{
On: pos(46),
X: &parser.BinaryExpr{
X: &parser.QualifiedRef{
Table: &parser.Ident{NamePos: pos(49), Name: "g"},
Dot: pos(50),
Column: &parser.Ident{NamePos: pos(51), Name: "_id"},
ColumnIndex: 0,
},
OpPos: pos(55),
Op: parser.EQ,
Y: &parser.QualifiedRef{
Table: &parser.Ident{NamePos: pos(57), Name: "j"},
Dot: pos(58),
Column: &parser.Ident{NamePos: pos(59), Name: "grouperid"},
ColumnIndex: 0,
},
},
},
},
Where: pos(69),
WhereExpr: &parser.BinaryExpr{
X: &parser.QualifiedRef{
Table: &parser.Ident{NamePos: pos(75), Name: "g"},
Dot: pos(76),
Column: &parser.Ident{NamePos: pos(77), Name: "color"},
ColumnIndex: 0,
},
OpPos: pos(83),
Op: parser.EQ,
Y: &parser.StringLit{
ValuePos: pos(85),
Value: "red",
},
},
})
AssertParseStatement(t, `SELECT _id FROM grouper g INNER JOIN joiner j ON g._id = j.grouperid where g.color = 'red' and j.jointype = 2`, &parser.SelectStatement{
Select: pos(0),
Columns: []*parser.ResultColumn{
{Expr: &parser.Ident{NamePos: pos(7), Name: "_id"}},
},
From: pos(11),
Source: &parser.JoinClause{
X: &parser.QualifiedTableName{
Name: &parser.Ident{NamePos: pos(16), Name: "grouper"},
Alias: &parser.Ident{NamePos: pos(24), Name: "g"},
},
Operator: &parser.JoinOperator{
Inner: pos(26),
Join: pos(32),
},
Y: &parser.QualifiedTableName{
Name: &parser.Ident{NamePos: pos(37), Name: "joiner"},
Alias: &parser.Ident{NamePos: pos(44), Name: "j"},
},
Constraint: &parser.OnConstraint{
On: pos(46),
X: &parser.BinaryExpr{
X: &parser.QualifiedRef{
Table: &parser.Ident{NamePos: pos(49), Name: "g"},
Dot: pos(50),
Column: &parser.Ident{NamePos: pos(51), Name: "_id"},
ColumnIndex: 0,
},
OpPos: pos(55),
Op: parser.EQ,
Y: &parser.QualifiedRef{
Table: &parser.Ident{NamePos: pos(57), Name: "j"},
Dot: pos(58),
Column: &parser.Ident{NamePos: pos(59), Name: "grouperid"},
ColumnIndex: 0,
},
},
},
},
Where: pos(69),
WhereExpr: &parser.BinaryExpr{
X: &parser.BinaryExpr{
X: &parser.QualifiedRef{
Table: &parser.Ident{NamePos: pos(75), Name: "g"},
Dot: pos(76),
Column: &parser.Ident{NamePos: pos(77), Name: "color"},
ColumnIndex: 0,
},
OpPos: pos(83),
Op: parser.EQ,
Y: &parser.StringLit{
ValuePos: pos(85),
Value: "red",
},
},
OpPos: pos(91),
Op: parser.AND,
Y: &parser.BinaryExpr{
X: &parser.QualifiedRef{
Table: &parser.Ident{NamePos: pos(95), Name: "j"},
Dot: pos(96),
Column: &parser.Ident{NamePos: pos(97), Name: "jointype"},
ColumnIndex: 0,
},
OpPos: pos(106),
Op: parser.EQ,
Y: &parser.IntegerLit{
ValuePos: pos(108),
Value: "2",
},
},
},
})
AssertParseStatement(t, `SELECT DISTINCT score FROM grouper order by score asc`, &parser.SelectStatement{
Select: pos(0),
Distinct: pos(7),
Columns: []*parser.ResultColumn{
{Expr: &parser.Ident{NamePos: pos(16), Name: "score"}},
},
From: pos(22),
Source: &parser.QualifiedTableName{
Name: &parser.Ident{NamePos: pos(27), Name: "grouper"},
},
Order: pos(35),
OrderBy: pos(41),
OrderingTerms: []*parser.OrderingTerm{
{
X: &parser.Ident{NamePos: pos(44), Name: "score"},
Asc: pos(50),
},
},
})
AssertParseStatement(t, `SELECT DISTINCT score FROM grouper order by score desc`, &parser.SelectStatement{
Select: pos(0),
Distinct: pos(7),
Columns: []*parser.ResultColumn{
{Expr: &parser.Ident{NamePos: pos(16), Name: "score"}},
},
From: pos(22),
Source: &parser.QualifiedTableName{
Name: &parser.Ident{NamePos: pos(27), Name: "grouper"},
},
Order: pos(35),
OrderBy: pos(41),
OrderingTerms: []*parser.OrderingTerm{
{
X: &parser.Ident{NamePos: pos(44), Name: "score"},
Desc: pos(50),
},
},
})
AssertParseStatement(t, `SELECT fld1, fld2, COUNT(*) FROM tbl where fld1 = 1 group by fld1, fld2 limit 1`, &parser.SelectStatement{
Select: pos(0),
Columns: []*parser.ResultColumn{
{Expr: &parser.Ident{NamePos: pos(7), Name: "fld1"}},
{Expr: &parser.Ident{NamePos: pos(13), Name: "fld2"}},
{
Expr: &parser.Call{
Name: &parser.Ident{NamePos: pos(19), Name: "COUNT"},
Lparen: pos(24),
Star: pos(25),
Rparen: pos(26),
},
},
},
From: pos(28),
Source: &parser.QualifiedTableName{Name: &parser.Ident{NamePos: pos(33), Name: "tbl"}},
Where: pos(37),
WhereExpr: &parser.BinaryExpr{
X: &parser.Ident{NamePos: pos(43), Name: "fld1"},
OpPos: pos(48),
Op: parser.EQ,
Y: &parser.IntegerLit{
ValuePos: pos(50),
Value: "1",
},
},
Group: pos(52),
GroupBy: pos(58),
GroupByExprs: []parser.Expr{
&parser.Ident{NamePos: pos(61), Name: "fld1"},
&parser.Ident{NamePos: pos(67), Name: "fld2"},
},
Limit: pos(72),
LimitExpr: &parser.IntegerLit{
ValuePos: pos(78),
Value: "1",
},
})
AssertParseStatement(t, `SELECT DISTINCT score FROM grouper order by score asc limit 5`, &parser.SelectStatement{
Select: pos(0),
Distinct: pos(7),
Columns: []*parser.ResultColumn{
{Expr: &parser.Ident{NamePos: pos(16), Name: "score"}},
},
From: pos(22),
Source: &parser.QualifiedTableName{Name: &parser.Ident{NamePos: pos(27), Name: "grouper"}},
Order: pos(35),
OrderBy: pos(41),
OrderingTerms: []*parser.OrderingTerm{
{
X: &parser.Ident{NamePos: pos(44), Name: "score"},
Asc: pos(50),
},
},
Limit: pos(54),
LimitExpr: &parser.IntegerLit{
ValuePos: pos(60),
Value: "5",
},
})
AssertParseStatement(t, `SELECT DISTINCT score FROM grouper order by score desc limit 5`, &parser.SelectStatement{
Select: pos(0),
Distinct: pos(7),
Columns: []*parser.ResultColumn{
{Expr: &parser.Ident{NamePos: pos(16), Name: "score"}},
},
From: pos(22),
Source: &parser.QualifiedTableName{Name: &parser.Ident{NamePos: pos(27), Name: "grouper"}},
Order: pos(35),
OrderBy: pos(41),
OrderingTerms: []*parser.OrderingTerm{
{
X: &parser.Ident{NamePos: pos(44), Name: "score"},
Desc: pos(50),
},
},
Limit: pos(55),
LimitExpr: &parser.IntegerLit{
ValuePos: pos(61),
Value: "5",
},
})
AssertParseStatement(t, `SELECT fld FROM tbl limit 10`, &parser.SelectStatement{
Select: pos(0),
Columns: []*parser.ResultColumn{
{Expr: &parser.Ident{NamePos: pos(7), Name: "fld"}},
},
From: pos(11),
Source: &parser.QualifiedTableName{Name: &parser.Ident{NamePos: pos(16), Name: "tbl"}},
Limit: pos(20),
LimitExpr: &parser.IntegerLit{
ValuePos: pos(26),
Value: "10",
},
})
// our previous SQL implementation supported "limit 10, 5" to mean a limit of 10 items,
// starting from the 5th item. The new implementation does not support this feature yet.
// AssertParseStatement(t, `SELECT fld FROM tbl limit 10, 5`, nil) // 1:27: expected semicolon or EOF, found 10
// the previous SQL implementation allowed `where not [condition]` but we don't currently.
// AssertParseStatement(t, `SELECT _id FROM tbl where not fld = 1 limit 10`, nil) // 1:31: expected EXISTS, found fld
/*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 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 RIGHT`, `1:23: expected JOIN, found 'EOF'`)
AssertParseStatementError(t, `SELECT * FROM foo RIGHT OUTER`, `1:29: expected JOIN, found 'EOF'`)
AssertParseStatementError(t, `SELECT * FROM foo FULL`, `1:22: expected JOIN, found 'EOF'`)
AssertParseStatementError(t, `SELECT * FROM foo FULL 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, y) = {1, 2}`, &parser.UpdateStatement{
Update: pos(0),
Table: &parser.QualifiedTableName{
Name: &parser.Ident{NamePos: pos(7), Name: "tbl"},
},
Set: pos(11),
Assignments: []*parser.Assignment{
{
Lparen: pos(15),
Rparen: pos(20),
Columns: []*parser.Ident{{NamePos: pos(16), Name: "x"}, {NamePos: pos(19), Name: "y"}},
Eq: pos(22),
Expr: &parser.TupleLiteralExpr{
Lbrace: pos(24),
Rbrace: pos(29),
Members: []parser.Expr{
&parser.IntegerLit{ValuePos: pos(25), Value: "1"},
&parser.IntegerLit{ValuePos: pos(28), 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),
TableName: &parser.QualifiedTableName{
Name: &parser.Ident{NamePos: pos(12), Name: "tbl"},
},
Source: &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),
TableName: &parser.QualifiedTableName{
Name: &parser.Ident{NamePos: pos(12), Name: "tbl"},
},
Source: &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.Error(err)
} else if diff := deep.Equal(stmt, want); diff != nil {
tb.Errorf("mismatch:\n%s", strings.Join(diff, "\n"))
} else {
AssertStatementSanity(tb, stmt, s)
}
}
func init() {
spew.Config.DisableMethods = true
spew.Config.DisablePointerAddresses = true
spew.Config.DisableCapacities = true
}
// 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()
expr, err := parser.NewParser(strings.NewReader(s)).ParseExpr()
if err != nil {
tb.Fatal(err)
} else if diff := deep.Equal(expr, want); diff != nil {
tb.Fatalf("mismatch:\n%s", strings.Join(diff, "\n"))
} else {
AssertExprSanity(tb, expr, s)
}
}
// 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}
}