diff --git a/pql/decimal.go b/pql/decimal.go index 189656e8f..c091cb89f 100644 --- a/pql/decimal.go +++ b/pql/decimal.go @@ -3,6 +3,7 @@ package pql import ( + "fmt" "math" "math/big" "strconv" @@ -344,6 +345,33 @@ const ( stateMantissa = "mantissa" ) +// FromInt64 converts an int64 into a Decimal. +func FromInt64(i int64, scale int64) Decimal { + us := i * Pow10(scale) + return NewDecimal(us, scale) +} + +// FromFloat64 converts a float into a Decimal. +func FromFloat64(f float64) Decimal { + scale := decimalPlaces(fmt.Sprintf("%v", f)) + us := int64(f * math.Pow(10, float64(scale))) + return NewDecimal(us, int64(scale)) +} + +func decimalPlaces(v string) int { + i := strings.IndexByte(v, '.') + if i > -1 { + return len(v) - i - 1 + } + return 0 +} + +// FromFloat64WithScale converts a float into a Decimal. +func FromFloat64WithScale(f float64, scale int) (Decimal, error) { + us := int64(f * math.Pow(10, float64(scale))) + return NewDecimal(us, int64(scale)), nil +} + // ParseDecimal parses a string into a Decimal. func ParseDecimal(s string) (Decimal, error) { var sign bool diff --git a/sql3/parser/ast.go b/sql3/parser/ast.go index fd4d9cc0e..30431aee2 100644 --- a/sql3/parser/ast.go +++ b/sql3/parser/ast.go @@ -33,14 +33,14 @@ func (*ColumnDefinition) node() {} func (*CommitStatement) node() {} func (*CreateIndexStatement) node() {} func (*CreateTableStatement) node() {} -func (*CreateTriggerStatement) node() {} +func (*CreateFunctionStatement) node() {} func (*CreateViewStatement) node() {} func (*DateLit) node() {} func (*DefaultConstraint) node() {} func (*DeleteStatement) node() {} func (*DropIndexStatement) node() {} func (*DropTableStatement) node() {} -func (*DropTriggerStatement) node() {} +func (*DropFunctionStatement) node() {} func (*DropViewStatement) node() {} func (*Exists) node() {} func (*ExplainStatement) node() {} @@ -51,7 +51,7 @@ func (*ForeignKeyArg) node() {} func (*ForeignKeyConstraint) node() {} func (*FrameSpec) node() {} func (*Ident) node() {} -func (*VariableRef) node() {} +func (*Variable) node() {} func (*IndexedColumn) node() {} func (*InsertStatement) node() {} func (*JoinClause) node() {} @@ -98,29 +98,29 @@ type Statement interface { stmt() } -func (*AlterTableStatement) stmt() {} -func (*AnalyzeStatement) stmt() {} -func (*BeginStatement) stmt() {} -func (*BulkInsertStatement) stmt() {} -func (*ShowTablesStatement) stmt() {} -func (*ShowColumnsStatement) stmt() {} -func (*CommitStatement) stmt() {} -func (*CreateIndexStatement) stmt() {} -func (*CreateTableStatement) stmt() {} -func (*CreateTriggerStatement) stmt() {} -func (*CreateViewStatement) stmt() {} -func (*DeleteStatement) stmt() {} -func (*DropIndexStatement) stmt() {} -func (*DropTableStatement) stmt() {} -func (*DropTriggerStatement) stmt() {} -func (*DropViewStatement) stmt() {} -func (*ExplainStatement) stmt() {} -func (*InsertStatement) stmt() {} -func (*ReleaseStatement) stmt() {} -func (*RollbackStatement) stmt() {} -func (*SavepointStatement) stmt() {} -func (*SelectStatement) stmt() {} -func (*UpdateStatement) stmt() {} +func (*AlterTableStatement) stmt() {} +func (*AnalyzeStatement) stmt() {} +func (*BeginStatement) stmt() {} +func (*BulkInsertStatement) stmt() {} +func (*ShowTablesStatement) stmt() {} +func (*ShowColumnsStatement) stmt() {} +func (*CommitStatement) stmt() {} +func (*CreateIndexStatement) stmt() {} +func (*CreateTableStatement) stmt() {} +func (*CreateFunctionStatement) stmt() {} +func (*CreateViewStatement) stmt() {} +func (*DeleteStatement) stmt() {} +func (*DropIndexStatement) stmt() {} +func (*DropTableStatement) stmt() {} +func (*DropFunctionStatement) stmt() {} +func (*DropViewStatement) stmt() {} +func (*ExplainStatement) stmt() {} +func (*InsertStatement) stmt() {} +func (*ReleaseStatement) stmt() {} +func (*RollbackStatement) stmt() {} +func (*SavepointStatement) stmt() {} +func (*SelectStatement) stmt() {} +func (*UpdateStatement) stmt() {} // CloneStatement returns a deep copy stmt. func CloneStatement(stmt Statement) Statement { @@ -141,7 +141,7 @@ func CloneStatement(stmt Statement) Statement { return stmt.Clone() case *CreateTableStatement: return stmt.Clone() - case *CreateTriggerStatement: + case *CreateFunctionStatement: return stmt.Clone() case *CreateViewStatement: return stmt.Clone() @@ -151,7 +151,7 @@ func CloneStatement(stmt Statement) Statement { return stmt.Clone() case *DropTableStatement: return stmt.Clone() - case *DropTriggerStatement: + case *DropFunctionStatement: return stmt.Clone() case *DropViewStatement: return stmt.Clone() @@ -218,7 +218,7 @@ func (*DateLit) expr() {} func (*Exists) expr() {} func (*ExprList) expr() {} func (*Ident) expr() {} -func (*VariableRef) expr() {} +func (*Variable) expr() {} func (*NullLit) expr() {} func (*IntegerLit) expr() {} func (*FloatLit) expr() {} @@ -268,7 +268,7 @@ func CloneExpr(expr Expr) Expr { return expr.Clone() case *UnaryExpr: return expr.Clone() - case *VariableRef: + case *Variable: return expr.Clone() default: @@ -1436,7 +1436,10 @@ func cloneIdents(a []*Ident) []*Ident { // String returns the string representation of the expression. func (i *Ident) String() string { - return `"` + strings.Replace(i.Name, `"`, `""`, -1) + `"` + if i.Quoted { + return `"` + strings.Replace(i.Name, `"`, `""`, -1) + `"` + } + return i.Name } // IdentName returns the name of ident. Returns a blank string if ident is nil. @@ -1447,25 +1450,25 @@ func IdentName(ident *Ident) string { return ident.Name } -type VariableRef struct { +type Variable struct { NamePos Pos // variable position Name string // variable name VariableIndex int VarDataType ExprDataType } -func (expr *VariableRef) IsLiteral() bool { return false } +func (expr *Variable) IsLiteral() bool { return false } -func (expr *VariableRef) DataType() ExprDataType { +func (expr *Variable) DataType() ExprDataType { return expr.VarDataType } -func (expr *VariableRef) Pos() Pos { +func (expr *Variable) Pos() Pos { return expr.NamePos } // Clone returns a deep copy of i. -func (i *VariableRef) Clone() *VariableRef { +func (i *Variable) Clone() *Variable { if i == nil { return nil } @@ -1474,11 +1477,11 @@ func (i *VariableRef) Clone() *VariableRef { } // String returns the string representation of the expression. -func (i *VariableRef) String() string { - return `"` + i.Name + `"` +func (i *Variable) String() string { + return i.Name } -func (i *VariableRef) VarName() string { +func (i *Variable) VarName() string { return i.Name[1:] } @@ -2632,98 +2635,68 @@ func (s *DropIndexStatement) String() string { return buf.String() } -type CreateTriggerStatement struct { +type ParameterDefinition struct { + Name *Variable // parameter name + Type *Type // data type +} + +type CreateFunctionStatement struct { Create Pos // position of CREATE keyword - Trigger Pos // position of TRIGGER keyword + Function Pos // position of FUNCTION keyword If Pos // position of IF keyword IfNot Pos // position of NOT keyword after IF IfNotExists Pos // position of EXISTS keyword after IF NOT Name *Ident // index name - Before Pos // position of BEFORE keyword - After Pos // position of AFTER keyword - Instead Pos // position of INSTEAD keyword - InsteadOf Pos // position of OF keyword after INSTEAD + Lparen Pos // position of parameter LParen + Parameters []*ParameterDefinition // parameters + Rparen Pos // position of parameter RParen - Delete Pos // position of DELETE keyword - Insert Pos // position of INSERT keyword - Update Pos // position of UPDATE keyword - UpdateOf Pos // position of OF keyword after UPDATE - UpdateOfColumns []*Ident // columns list for UPDATE OF - On Pos // position of ON keyword - Table *Ident // table name + Returns Pos // position of RETURNS keyword + ReturnDef *ParameterDefinition // return def - For Pos // position of FOR keyword - ForEach Pos // position of EACH keyword after FOR - ForEachRow Pos // position of ROW keyword after FOR EACH - - When Pos // position of WHEN keyword - WhenExpr Expr // conditional expression + As Pos // position of AS keyword Begin Pos // position of BEGIN keyword - Body []Statement // trigger body + Body []Statement // function body End Pos // position of END keyword } // Clone returns a deep copy of s. -func (s *CreateTriggerStatement) Clone() *CreateTriggerStatement { +func (s *CreateFunctionStatement) Clone() *CreateFunctionStatement { if s == nil { return nil } other := *s other.Name = s.Name.Clone() - other.UpdateOfColumns = cloneIdents(s.UpdateOfColumns) - other.Table = s.Table.Clone() - other.WhenExpr = CloneExpr(s.WhenExpr) other.Body = cloneStatements(s.Body) return &other } // String returns the string representation of the statement. -func (s *CreateTriggerStatement) String() string { +func (s *CreateFunctionStatement) String() string { var buf bytes.Buffer - buf.WriteString("CREATE TRIGGER") + buf.WriteString("CREATE FUNCTION") if s.IfNotExists.IsValid() { buf.WriteString(" IF NOT EXISTS") } fmt.Fprintf(&buf, " %s", s.Name.String()) - if s.Before.IsValid() { - buf.WriteString(" BEFORE") - } else if s.After.IsValid() { - buf.WriteString(" AFTER") - } else if s.InsteadOf.IsValid() { - buf.WriteString(" INSTEAD OF") - } - - if s.Delete.IsValid() { - buf.WriteString(" DELETE") - } else if s.Insert.IsValid() { - buf.WriteString(" INSERT") - } else if s.Update.IsValid() { - buf.WriteString(" UPDATE") - if s.UpdateOf.IsValid() { - buf.WriteString(" OF ") - for i, col := range s.UpdateOfColumns { - if i != 0 { - buf.WriteString(", ") - } - buf.WriteString(col.String()) + if len(s.Parameters) > 0 { + buf.WriteString(" (") + for idx, p := range s.Parameters { + if idx > 0 { + buf.WriteString(", ") } + fmt.Fprintf(&buf, "%s %s", p.Name.Name, p.Type.Name) } + buf.WriteString(")") } - fmt.Fprintf(&buf, " ON %s", s.Table.String()) + buf.WriteString(" RETURNS ") + fmt.Fprintf(&buf, "%s %s", s.ReturnDef.Name, s.ReturnDef.Type.Name) - if s.ForEachRow.IsValid() { - buf.WriteString(" FOR EACH ROW") - } - - if s.WhenExpr != nil { - fmt.Fprintf(&buf, " WHEN %s", s.WhenExpr.String()) - } - - buf.WriteString(" BEGIN") + buf.WriteString(" AS BEGIN") for i := range s.Body { fmt.Fprintf(&buf, " %s;", s.Body[i].String()) } @@ -2732,7 +2705,7 @@ func (s *CreateTriggerStatement) String() string { return buf.String() } -type DropTriggerStatement struct { +type DropFunctionStatement struct { Drop Pos // position of DROP keyword Trigger Pos // position of TRIGGER keyword If Pos // position of IF keyword @@ -2741,7 +2714,7 @@ type DropTriggerStatement struct { } // Clone returns a deep copy of s. -func (s *DropTriggerStatement) Clone() *DropTriggerStatement { +func (s *DropFunctionStatement) Clone() *DropFunctionStatement { if s == nil { return nil } @@ -2750,7 +2723,7 @@ func (s *DropTriggerStatement) Clone() *DropTriggerStatement { return &other } -func (s *DropTriggerStatement) String() string { +func (s *DropFunctionStatement) String() string { var buf bytes.Buffer buf.WriteString("DROP TRIGGER") if s.IfExists.IsValid() { diff --git a/sql3/parser/ast_test.go b/sql3/parser/ast_test.go index 67eb82f88..78f1357c5 100644 --- a/sql3/parser/ast_test.go +++ b/sql3/parser/ast_test.go @@ -60,7 +60,7 @@ func TestAlterTableStatement_String(t *testing.T) { Name: &parser.Ident{Name: "foo"}, OldColumnName: &parser.Ident{Name: "col1"}, NewColumnName: &parser.Ident{Name: "col2"}, - }, `ALTER TABLE "foo" RENAME COLUMN "col1" TO "col2"`) + }, `ALTER TABLE foo RENAME COLUMN col1 TO col2`) AssertStatementStringer(t, &parser.AlterTableStatement{ Name: &parser.Ident{Name: "foo"}, @@ -68,11 +68,11 @@ func TestAlterTableStatement_String(t *testing.T) { Name: &parser.Ident{Name: "bar"}, Type: &parser.Type{Name: &parser.Ident{Name: "INTEGER"}}, }, - }, `ALTER TABLE "foo" ADD COLUMN "bar" INTEGER`) + }, `ALTER TABLE foo ADD COLUMN bar INTEGER`) AssertStatementStringer(t, &parser.AlterTableStatement{ Name: &parser.Ident{Name: "foo"}, DropColumnName: &parser.Ident{Name: "bar"}, - }, `ALTER TABLE "foo" DROP COLUMN "bar"`) + }, `ALTER TABLE foo DROP COLUMN bar`) } /*func TestAnalyzeStatement_String(t *testing.T) { @@ -130,7 +130,7 @@ func TestCreateTableStatement_String(t *testing.T) { Type: &parser.Type{Name: &parser.Ident{Name: "STRING"}}, }, }, - }, `CREATE TABLE IF NOT EXISTS "foo" ("bar" INTEGER, "baz" STRING)`) + }, `CREATE TABLE IF NOT EXISTS foo (bar INTEGER, baz STRING)`) AssertStatementStringer(t, &parser.CreateTableStatement{ Name: &parser.Ident{Name: "foo"}, @@ -212,15 +212,15 @@ func TestCreateTableStatement_String(t *testing.T) { }, }, }, - }, `CREATE TABLE IF NOT EXISTS "foo" (`+ - `"boolcol" BOOL, `+ - `"decimalcol" DECIMAL MIN 100.25 MAX 1000.75, `+ - `"idcol" ID CACHETYPE RANKED SIZE 10000, `+ - `"idsetcol" IDSET CACHETYPE RANKED SIZE 10000, `+ - `"intcol" INTEGER MIN 100 MAX 1000, `+ - `"stringcol" STRING CACHETYPE RANKED SIZE 10000, `+ - `"stringsetcol" STRINGSET CACHETYPE RANKED SIZE 10000, `+ - `"timestampcol" TIMESTAMP TIMEUNIT 's' EPOCH '2021-01-01T00:00:00Z'`+ + }, `CREATE TABLE IF NOT EXISTS foo (`+ + `boolcol BOOL, `+ + `decimalcol DECIMAL MIN 100.25 MAX 1000.75, `+ + `idcol ID CACHETYPE RANKED SIZE 10000, `+ + `idsetcol IDSET CACHETYPE RANKED SIZE 10000, `+ + `intcol INTEGER MIN 100 MAX 1000, `+ + `stringcol STRING CACHETYPE RANKED SIZE 10000, `+ + `stringsetcol STRINGSET CACHETYPE RANKED SIZE 10000, `+ + `timestampcol TIMESTAMP TIMEUNIT 's' EPOCH '2021-01-01T00:00:00Z'`+ `)`) } @@ -361,51 +361,35 @@ func OLDTestCreateTableStatement_String(t *testing.T) { }, `CREATE TABLE "foo" AS SELECT *`) } -func TestCreateTriggerStatement_String(t *testing.T) { - t.Skip("CREATE TRIGGER is currently disabled in the parser") - AssertStatementStringer(t, &parser.CreateTriggerStatement{ - Name: &parser.Ident{Name: "trig"}, - Insert: pos(0), - Table: &parser.Ident{Name: "tbl"}, - Body: []parser.Statement{ - &parser.DeleteStatement{Table: &parser.QualifiedTableName{Name: &parser.Ident{Name: "tbl2"}}}, +func TestCreateFunctionStatement_String(t *testing.T) { + AssertStatementStringer(t, &parser.CreateFunctionStatement{ + Name: &parser.Ident{Name: "func"}, + Parameters: []*parser.ParameterDefinition{ + { + Name: &parser.Variable{Name: "@param1"}, + Type: &parser.Type{Name: &parser.Ident{Name: "int"}}, + }, }, - }, `CREATE TRIGGER "trig" INSERT ON "tbl" BEGIN DELETE FROM "tbl2"; END`) - - AssertStatementStringer(t, &parser.CreateTriggerStatement{ - Name: &parser.Ident{Name: "trig"}, - Before: pos(0), - Delete: pos(0), - ForEachRow: pos(0), - Table: &parser.Ident{Name: "tbl"}, - Body: []parser.Statement{ - &parser.DeleteStatement{Table: &parser.QualifiedTableName{Name: &parser.Ident{Name: "x"}}}, + ReturnDef: &parser.ParameterDefinition{ + Name: &parser.Variable{Name: "@scalar"}, + Type: &parser.Type{Name: &parser.Ident{Name: "int"}}, }, - }, `CREATE TRIGGER "trig" BEFORE DELETE ON "tbl" FOR EACH ROW BEGIN DELETE FROM "x"; END`) + }, `CREATE FUNCTION func (@param1 int) RETURNS @scalar int AS BEGIN END`) - AssertStatementStringer(t, &parser.CreateTriggerStatement{ + AssertStatementStringer(t, &parser.CreateFunctionStatement{ IfNotExists: pos(0), - Name: &parser.Ident{Name: "trig"}, - After: pos(0), - Update: pos(0), - Table: &parser.Ident{Name: "tbl"}, - WhenExpr: &parser.BoolLit{Value: true}, - Body: []parser.Statement{ - &parser.DeleteStatement{Table: &parser.QualifiedTableName{Name: &parser.Ident{Name: "x"}}}, + Name: &parser.Ident{Name: "func"}, + Parameters: []*parser.ParameterDefinition{ + { + Name: &parser.Variable{Name: "@param1"}, + Type: &parser.Type{Name: &parser.Ident{Name: "int"}}, + }, }, - }, `CREATE TRIGGER IF NOT EXISTS "trig" AFTER UPDATE ON "tbl" WHEN TRUE BEGIN DELETE FROM "x"; END`) - - AssertStatementStringer(t, &parser.CreateTriggerStatement{ - Name: &parser.Ident{Name: "trig"}, - InsteadOf: pos(0), - Update: pos(0), - UpdateOf: pos(0), - UpdateOfColumns: []*parser.Ident{{Name: "x"}, {Name: "y"}}, - Table: &parser.Ident{Name: "tbl"}, - Body: []parser.Statement{ - &parser.DeleteStatement{Table: &parser.QualifiedTableName{Name: &parser.Ident{Name: "x"}}}, + ReturnDef: &parser.ParameterDefinition{ + Name: &parser.Variable{Name: "@scalar"}, + Type: &parser.Type{Name: &parser.Ident{Name: "int"}}, }, - }, `CREATE TRIGGER "trig" INSTEAD OF UPDATE OF "x", "y" ON "tbl" BEGIN DELETE FROM "x"; END`) + }, `CREATE FUNCTION IF NOT EXISTS func (@param1 int) RETURNS @scalar int AS BEGIN END`) } func TestCreateViewStatement_String(t *testing.T) { @@ -433,7 +417,7 @@ func TestCreateViewStatement_String(t *testing.T) { func TestDeleteStatement_String(t *testing.T) { AssertStatementStringer(t, &parser.DeleteStatement{ Table: &parser.QualifiedTableName{Name: &parser.Ident{Name: "tbl"}, Alias: &parser.Ident{Name: "tbl2"}}, - }, `DELETE FROM "tbl" AS "tbl2"`) + }, `DELETE FROM tbl AS tbl2`) // AssertStatementStringer(t, &sql.DeleteStatement{ // Table: &sql.QualifiedTableName{Name: &sql.Ident{Name: "tbl"}, Index: &sql.Ident{Name: "idx"}}, @@ -483,21 +467,21 @@ func TestDropIndexStatement_String(t *testing.T) { func TestDropTableStatement_String(t *testing.T) { AssertStatementStringer(t, &parser.DropTableStatement{ Name: &parser.Ident{Name: "tbl"}, - }, `DROP TABLE "tbl"`) + }, `DROP TABLE tbl`) AssertStatementStringer(t, &parser.DropTableStatement{ IfExists: pos(0), Name: &parser.Ident{Name: "tbl"}, - }, `DROP TABLE IF EXISTS "tbl"`) + }, `DROP TABLE IF EXISTS tbl`) } func TestDropTriggerStatement_String(t *testing.T) { t.Skip("DROP TRIGGER is currently disabled in the parser") - AssertStatementStringer(t, &parser.DropTriggerStatement{ + AssertStatementStringer(t, &parser.DropFunctionStatement{ Name: &parser.Ident{Name: "trig"}, }, `DROP TRIGGER "trig"`) - AssertStatementStringer(t, &parser.DropTriggerStatement{ + AssertStatementStringer(t, &parser.DropFunctionStatement{ IfExists: pos(0), Name: &parser.Ident{Name: "trig"}, }, `DROP TRIGGER IF EXISTS "trig"`) @@ -597,7 +581,7 @@ func TestInsertStatement_String(t *testing.T) { Exprs: []parser.Expr{&parser.NullLit{}, &parser.NullLit{}}, }, }, - }, `INSERT INTO "tbl" ("x", "y") VALUES (NULL, NULL)`) + }, `INSERT INTO tbl (x, y) VALUES (NULL, NULL)`) AssertStatementStringer(t, &parser.InsertStatement{ Table: &parser.Ident{Name: "tbl"}, @@ -613,7 +597,7 @@ func TestInsertStatement_String(t *testing.T) { Exprs: []parser.Expr{&parser.IntegerLit{Value: "3"}, &parser.IntegerLit{Value: "4"}}, }, }, - }, `INSERT INTO "tbl" ("x", "y") VALUES (1, 2), (3, 4)`) + }, `INSERT INTO tbl (x, y) VALUES (1, 2), (3, 4)`) // AssertStatementStringer(t, &sql.InsertStatement{ // WithClause: &sql.WithClause{ @@ -687,14 +671,14 @@ func TestSelectStatement_String(t *testing.T) { {Expr: &parser.Ident{Name: "x"}, Alias: &parser.Ident{Name: "y"}}, {Expr: &parser.Ident{Name: "z"}}, }, - }, `SELECT "x" AS "y", "z"`) + }, `SELECT x AS y, z`) AssertStatementStringer(t, &parser.SelectStatement{ Distinct: pos(0), Columns: []*parser.ResultColumn{ {Expr: &parser.Ident{Name: "x"}}, }, - }, `SELECT DISTINCT "x"`) + }, `SELECT DISTINCT x`) // AssertStatementStringer(t, &sql.SelectStatement{ // All: pos(0), @@ -709,7 +693,7 @@ func TestSelectStatement_String(t *testing.T) { WhereExpr: &parser.BoolLit{Value: true}, GroupByExprs: []parser.Expr{&parser.Ident{Name: "x"}, &parser.Ident{Name: "y"}}, HavingExpr: &parser.Ident{Name: "z"}, - }, `SELECT * FROM "tbl" WHERE TRUE GROUP BY "x", "y" HAVING "z"`) + }, `SELECT * FROM tbl WHERE TRUE GROUP BY x, y HAVING z`) AssertStatementStringer(t, &parser.SelectStatement{ Columns: []*parser.ResultColumn{{Star: pos(0)}}, @@ -717,7 +701,7 @@ func TestSelectStatement_String(t *testing.T) { X: &parser.SelectStatement{Columns: []*parser.ResultColumn{{Star: pos(0)}}}, Alias: &parser.Ident{Name: "tbl"}, }, - }, `SELECT * FROM (SELECT *) AS "tbl"`) + }, `SELECT * FROM (SELECT *) AS tbl`) AssertStatementStringer(t, &parser.SelectStatement{ Columns: []*parser.ResultColumn{{Star: pos(0)}}, @@ -753,7 +737,7 @@ func TestSelectStatement_String(t *testing.T) { }, }, }, - }, `SELECT * FROM "tbl" WINDOW "win1" AS ("base" PARTITION BY "x", "y" ORDER BY "x" ASC NULLS FIRST, "y" DESC NULLS LAST RANGE UNBOUNDED PRECEDING), "win2" AS ("base2")`) + }, `SELECT * FROM tbl WINDOW win1 AS (base PARTITION BY x, y ORDER BY x ASC NULLS FIRST, y DESC NULLS LAST RANGE UNBOUNDED PRECEDING), win2 AS (base2)`) // AssertStatementStringer(t, &sql.SelectStatement{ // WithClause: &sql.WithClause{ @@ -813,7 +797,7 @@ func TestSelectStatement_String(t *testing.T) { {X: &parser.Ident{Name: "x"}}, {X: &parser.Ident{Name: "y"}}, }, - }, `SELECT * ORDER BY "x", "y"`) + }, `SELECT * ORDER BY x, y`) AssertStatementStringer(t, &parser.SelectStatement{ Columns: []*parser.ResultColumn{{Star: pos(0)}}, @@ -822,7 +806,7 @@ func TestSelectStatement_String(t *testing.T) { Operator: &parser.JoinOperator{Comma: pos(0)}, Y: &parser.QualifiedTableName{Name: &parser.Ident{Name: "y"}}, }, - }, `SELECT * FROM "x", "y"`) + }, `SELECT * FROM x, y`) AssertStatementStringer(t, &parser.SelectStatement{ Columns: []*parser.ResultColumn{{Star: pos(0)}}, @@ -832,7 +816,7 @@ func TestSelectStatement_String(t *testing.T) { Y: &parser.QualifiedTableName{Name: &parser.Ident{Name: "y"}}, Constraint: &parser.OnConstraint{X: &parser.BoolLit{Value: true}}, }, - }, `SELECT * FROM "x" JOIN "y" ON TRUE`) + }, `SELECT * FROM x JOIN y ON TRUE`) AssertStatementStringer(t, &parser.SelectStatement{ Columns: []*parser.ResultColumn{{Star: pos(0)}}, @@ -844,7 +828,7 @@ func TestSelectStatement_String(t *testing.T) { Columns: []*parser.Ident{{Name: "a"}, {Name: "b"}}, }, }, - }, `SELECT * FROM "x" NATURAL INNER JOIN "y" USING ("a", "b")`) + }, `SELECT * FROM x NATURAL INNER JOIN y USING (a, b)`) AssertStatementStringer(t, &parser.SelectStatement{ Columns: []*parser.ResultColumn{{Star: pos(0)}}, @@ -853,7 +837,7 @@ func TestSelectStatement_String(t *testing.T) { Operator: &parser.JoinOperator{Left: pos(0)}, Y: &parser.QualifiedTableName{Name: &parser.Ident{Name: "y"}}, }, - }, `SELECT * FROM "x" LEFT JOIN "y"`) + }, `SELECT * FROM x LEFT JOIN y`) AssertStatementStringer(t, &parser.SelectStatement{ Columns: []*parser.ResultColumn{{Star: pos(0)}}, @@ -862,7 +846,7 @@ func TestSelectStatement_String(t *testing.T) { Operator: &parser.JoinOperator{Left: pos(0), Outer: pos(0)}, Y: &parser.QualifiedTableName{Name: &parser.Ident{Name: "y"}}, }, - }, `SELECT * FROM "x" LEFT OUTER JOIN "y"`) + }, `SELECT * FROM x LEFT OUTER JOIN y`) AssertStatementStringer(t, &parser.SelectStatement{ Columns: []*parser.ResultColumn{{Star: pos(0)}}, @@ -871,7 +855,7 @@ func TestSelectStatement_String(t *testing.T) { Operator: &parser.JoinOperator{Cross: pos(0)}, Y: &parser.QualifiedTableName{Name: &parser.Ident{Name: "y"}}, }, - }, `SELECT * FROM "x" CROSS JOIN "y"`) + }, `SELECT * FROM x CROSS JOIN y`) } func TestUpdateStatement_String(t *testing.T) { @@ -882,7 +866,7 @@ func TestUpdateStatement_String(t *testing.T) { {Columns: []*parser.Ident{{Name: "y"}}, Expr: &parser.IntegerLit{Value: "200"}}, }, WhereExpr: &parser.BoolLit{Value: true}, - }, `UPDATE "tbl" SET "x" = 100, "y" = 200 WHERE TRUE`) + }, `UPDATE tbl SET x = 100, y = 200 WHERE TRUE`) AssertStatementStringer(t, &parser.UpdateStatement{ UpdateOrRollback: pos(0), @@ -890,7 +874,7 @@ func TestUpdateStatement_String(t *testing.T) { Assignments: []*parser.Assignment{ {Columns: []*parser.Ident{{Name: "x"}}, Expr: &parser.IntegerLit{Value: "100"}}, }, - }, `UPDATE OR ROLLBACK "tbl" SET "x" = 100`) + }, `UPDATE OR ROLLBACK tbl SET x = 100`) AssertStatementStringer(t, &parser.UpdateStatement{ UpdateOrAbort: pos(0), @@ -898,7 +882,7 @@ func TestUpdateStatement_String(t *testing.T) { Assignments: []*parser.Assignment{ {Columns: []*parser.Ident{{Name: "x"}}, Expr: &parser.IntegerLit{Value: "100"}}, }, - }, `UPDATE OR ABORT "tbl" SET "x" = 100`) + }, `UPDATE OR ABORT tbl SET x = 100`) AssertStatementStringer(t, &parser.UpdateStatement{ UpdateOrReplace: pos(0), @@ -906,7 +890,7 @@ func TestUpdateStatement_String(t *testing.T) { Assignments: []*parser.Assignment{ {Columns: []*parser.Ident{{Name: "x"}}, Expr: &parser.IntegerLit{Value: "100"}}, }, - }, `UPDATE OR REPLACE "tbl" SET "x" = 100`) + }, `UPDATE OR REPLACE tbl SET x = 100`) AssertStatementStringer(t, &parser.UpdateStatement{ UpdateOrFail: pos(0), @@ -914,7 +898,7 @@ func TestUpdateStatement_String(t *testing.T) { Assignments: []*parser.Assignment{ {Columns: []*parser.Ident{{Name: "x"}}, Expr: &parser.IntegerLit{Value: "100"}}, }, - }, `UPDATE OR FAIL "tbl" SET "x" = 100`) + }, `UPDATE OR FAIL tbl SET x = 100`) AssertStatementStringer(t, &parser.UpdateStatement{ UpdateOrIgnore: pos(0), @@ -922,7 +906,7 @@ func TestUpdateStatement_String(t *testing.T) { Assignments: []*parser.Assignment{ {Columns: []*parser.Ident{{Name: "x"}}, Expr: &parser.IntegerLit{Value: "100"}}, }, - }, `UPDATE OR IGNORE "tbl" SET "x" = 100`) + }, `UPDATE OR IGNORE tbl SET x = 100`) // AssertStatementStringer(t, &sql.UpdateStatement{ // WithClause: &sql.WithClause{ @@ -941,8 +925,8 @@ func TestUpdateStatement_String(t *testing.T) { } func TestIdent_String(t *testing.T) { - AssertExprStringer(t, &parser.Ident{Name: "foo"}, `"foo"`) - AssertExprStringer(t, &parser.Ident{Name: "foo \" bar"}, `"foo "" bar"`) + AssertExprStringer(t, &parser.Ident{Name: "foo", Quoted: true}, `"foo"`) + AssertExprStringer(t, &parser.Ident{Name: "foo \" bar", Quoted: true}, `"foo "" bar"`) } func TestStringLit_String(t *testing.T) { @@ -1021,7 +1005,7 @@ func TestCaseExpr_String(t *testing.T) { {Condition: &parser.IntegerLit{Value: "2"}, Body: &parser.BoolLit{Value: false}}, }, ElseExpr: &parser.NullLit{}, - }, `CASE "foo" WHEN 1 THEN TRUE WHEN 2 THEN FALSE ELSE NULL END`) + }, `CASE foo WHEN 1 THEN TRUE WHEN 2 THEN FALSE ELSE NULL END`) AssertExprStringer(t, &parser.CaseExpr{ Blocks: []*parser.CaseBlock{ @@ -1036,8 +1020,8 @@ func TestExprList_String(t *testing.T) { } func TestQualifiedRef_String(t *testing.T) { - AssertExprStringer(t, &parser.QualifiedRef{Table: &parser.Ident{Name: "tbl"}, Column: &parser.Ident{Name: "col"}}, `"tbl"."col"`) - AssertExprStringer(t, &parser.QualifiedRef{Table: &parser.Ident{Name: "tbl"}, Star: pos(0)}, `"tbl".*`) + AssertExprStringer(t, &parser.QualifiedRef{Table: &parser.Ident{Name: "tbl"}, Column: &parser.Ident{Name: "col"}}, `tbl.col`) + AssertExprStringer(t, &parser.QualifiedRef{Table: &parser.Ident{Name: "tbl"}, Star: pos(0)}, `tbl.*`) } func TestCall_String(t *testing.T) { @@ -1065,7 +1049,7 @@ func TestCall_String(t *testing.T) { Over: &parser.OverClause{ Name: &parser.Ident{Name: "win"}, }, - }, `foo() OVER "win"`) + }, `foo() OVER win`) t.Run("FrameSpec", func(t *testing.T) { AssertExprStringer(t, &parser.Call{ diff --git a/sql3/parser/astdatatype.go b/sql3/parser/astdatatype.go index f2491f9bd..30bba88f7 100644 --- a/sql3/parser/astdatatype.go +++ b/sql3/parser/astdatatype.go @@ -2,11 +2,7 @@ package parser import ( "fmt" - "math" - "strconv" "strings" - - "github.com/featurebasedb/featurebase/v3/pql" ) // TODO(pok) make all these lower case @@ -360,25 +356,6 @@ func (*DataTypeTimestamp) TypeInfo() map[string]interface{} { return nil } -func StringToDecimal(v string) (pql.Decimal, error) { - fvalue, err := strconv.ParseFloat(v, 64) - if err != nil { - return pql.NewDecimal(0, 0), err - } - return FloatToDecimal(fvalue), nil -} - -func FloatToDecimal(v float64) pql.Decimal { - scale := NumDecimalPlaces(fmt.Sprintf("%v", v)) - unscaledValue := int64(v * math.Pow(10, float64(scale))) - return pql.NewDecimal(unscaledValue, int64(scale)) -} - -func FloatToDecimalWithScale(v float64, s int64) pql.Decimal { - unscaledValue := int64(v * math.Pow(10, float64(s))) - return pql.NewDecimal(unscaledValue, int64(s)) -} - func NumDecimalPlaces(v string) int { i := strings.IndexByte(v, '.') if i > -1 { diff --git a/sql3/parser/parser.go b/sql3/parser/parser.go index 054384d47..fe4bb287c 100644 --- a/sql3/parser/parser.go +++ b/sql3/parser/parser.go @@ -300,9 +300,9 @@ func (p *Parser) parseCreateStatement() (Statement, error) { /* case VIEW: return p.parseCreateViewStatement(pos) case INDEX, UNIQUE: - return p.parseCreateIndexStatement(pos) - case TRIGGER: - return p.parseCreateTriggerStatement(pos)*/ + return p.parseCreateIndexStatement(pos)*/ + case FUNCTION: + return p.parseCreateFunctionStatement(pos) default: return nil, p.errorExpected(pos, tok, "TABLE") } @@ -318,9 +318,9 @@ func (p *Parser) parseDropStatement() (Statement, error) { /* case VIEW: return p.parseDropViewStatement(pos) case INDEX: - return p.parseDropIndexStatement(pos) - case TRIGGER: - return p.parseDropTriggerStatement(pos)*/ + return p.parseDropIndexStatement(pos)*/ + case FUNCTION: + return p.parseDropFunctionStatement(pos) default: return nil, p.errorExpected(pos, tok, "TABLE") } @@ -1173,12 +1173,43 @@ func (p *Parser) parseDropTableStatement(dropPos Pos) (_ *DropTableStatement, er return &stmt, nil }*/ -/*func (p *Parser) parseCreateTriggerStatement(createPos Pos) (_ *CreateTriggerStatement, err error) { - assert(p.peek() == TRIGGER) +func (p *Parser) parseParameterDefinitions() (_ []*ParameterDefinition, err error) { + var params []*ParameterDefinition + for { + switch { + case p.peek() == VARIABLE: + col, err := p.parseParameterDefinition() + params = append(params, col) + if err != nil { + return params, err + } + if p.peek() == COMMA { + p.scan() + } + case p.peek() == RP: + return params, nil + default: + return params, p.errorExpected(p.pos, p.tok, "parameter name, or right paren") + } + } +} - var stmt CreateTriggerStatement +func (p *Parser) parseParameterDefinition() (_ *ParameterDefinition, err error) { + var param ParameterDefinition + if param.Name, err = p.parseVariable("parameter name"); err != nil { + return ¶m, err + } else if param.Type, err = p.parseType(); err != nil { + return ¶m, err + } + return ¶m, nil +} + +func (p *Parser) parseCreateFunctionStatement(createPos Pos) (_ *CreateFunctionStatement, err error) { + assert(p.peek() == FUNCTION) + + var stmt CreateFunctionStatement stmt.Create = createPos - stmt.Trigger, _, _ = p.scan() + stmt.Function, _, _ = p.scan() // Parse optional "IF NOT EXISTS". if p.peek() == IF { @@ -1195,135 +1226,83 @@ func (p *Parser) parseDropTableStatement(dropPos Pos) (_ *DropTableStatement, er stmt.IfNotExists, _, _ = p.scan() } - if stmt.Name, err = p.parseIdent("index name"); err != nil { + if stmt.Name, err = p.parseIdent("function name"); err != nil { return &stmt, err } - // Parse BEFORE, AFTER, or INSTEAD OF - switch p.peek() { - case BEFORE: - stmt.Before, _, _ = p.scan() - case AFTER: - stmt.After, _, _ = p.scan() - case INSTEAD: - stmt.Instead, _, _ = p.scan() - if p.peek() != OF { - return &stmt, p.errorExpected(p.pos, p.tok, "OF") - } - stmt.InsteadOf, _, _ = p.scan() - } + // parameters + if p.peek() == LP { + stmt.Lparen, _, _ = p.scan() - // Parse DELETE, INSERT, UPDATE, or UPDATE OF [columns] - switch p.peek() { - case DELETE: - stmt.Delete, _, _ = p.scan() - case INSERT: - stmt.Insert, _, _ = p.scan() - case UPDATE: - stmt.Update, _, _ = p.scan() - if p.peek() == OF { - stmt.UpdateOf, _, _ = p.scan() - for { - col, err := p.parseIdent("column name") - if err != nil { - return &stmt, err - } - stmt.UpdateOfColumns = append(stmt.UpdateOfColumns, col) - - if p.peek() != COMMA { - break - } - p.scan() - } - } - default: - return &stmt, p.errorExpected(p.pos, p.tok, "DELETE, INSERT, or UPDATE") - } - - // Parse "ON table-name". - if p.peek() != ON { - return &stmt, p.errorExpected(p.pos, p.tok, "ON") - } - stmt.On, _, _ = p.scan() - if stmt.Table, err = p.parseIdent("table name"); err != nil { - return &stmt, err - } - - // Parse optional "FOR EACH ROW". - if p.peek() == FOR { - stmt.For, _, _ = p.scan() - if p.peek() != EACH { - return &stmt, p.errorExpected(p.pos, p.tok, "EACH") - } - stmt.ForEach, _, _ = p.scan() - if p.peek() != ROW { - return &stmt, p.errorExpected(p.pos, p.tok, "ROW") - } - stmt.ForEachRow, _, _ = p.scan() - } - - // Parse optional "WHEN expr". - if p.peek() == WHEN { - stmt.When, _, _ = p.scan() - if stmt.WhenExpr, err = p.ParseExpr(); err != nil { + stmt.Parameters, err = p.parseParameterDefinitions() + if err != nil { return &stmt, err } + + if p.peek() != RP { + return &stmt, p.errorExpected(p.pos, p.tok, ")") + } + stmt.Rparen, _, _ = p.scan() } - // Parse trigger body. + if p.peek() != RETURNS { + return &stmt, p.errorExpected(p.pos, p.tok, "RETURNS") + } + stmt.Returns, _, _ = p.scan() + stmt.ReturnDef, err = p.parseParameterDefinition() + if err != nil { + return &stmt, err + } + + if p.peek() != AS { + return &stmt, p.errorExpected(p.pos, p.tok, "AS") + } + stmt.As, _, _ = p.scan() + if p.peek() != BEGIN { return &stmt, p.errorExpected(p.pos, p.tok, "BEGIN") } stmt.Begin, _, _ = p.scan() for { - s, err := p.parseTriggerBodyStatement() + s, err := p.parseFunctionBodyStatement() if err != nil { return &stmt, err } - stmt.Body = append(stmt.Body, s) + if s != nil { + stmt.Body = append(stmt.Body, s) + } if p.peek() == END { break } } + + if p.peek() != END { + return &stmt, p.errorExpected(p.pos, p.tok, "END") + } stmt.End, _, _ = p.scan() return &stmt, nil -}*/ +} -/*func (p *Parser) parseTriggerBodyStatement() (stmt Statement, err error) { +func (p *Parser) parseFunctionBodyStatement() (stmt Statement, err error) { switch p.peek() { - case SELECT, VALUES: - stmt, err = p.parseSelectStatement(false, nil) - case INSERT, REPLACE: - stmt, err = p.parseInsertStatement(nil) - case UPDATE: - stmt, err = p.parseUpdateStatement(nil) - case DELETE: - stmt, err = p.parseDeleteStatement(nil) - //case WITH: - // stmt, err = p.parseWithStatement() + case END: + break default: return nil, p.errorExpected(p.pos, p.tok, "statement") } if err != nil { return stmt, err } - - // Ensure trailing semicolon exists. - if p.peek() != SEMI { - return stmt, p.errorExpected(p.pos, p.tok, "semicolon") - } - p.scan() - return stmt, nil -}*/ +} -/*func (p *Parser) parseDropTriggerStatement(dropPos Pos) (_ *DropTriggerStatement, err error) { - assert(p.peek() == TRIGGER) +func (p *Parser) parseDropFunctionStatement(dropPos Pos) (_ *DropFunctionStatement, err error) { + assert(p.peek() == FUNCTION) - var stmt DropTriggerStatement + var stmt DropFunctionStatement stmt.Drop = dropPos stmt.Trigger, _, _ = p.scan() @@ -1336,12 +1315,12 @@ func (p *Parser) parseDropTableStatement(dropPos Pos) (_ *DropTableStatement, er stmt.IfExists, _, _ = p.scan() } - if stmt.Name, err = p.parseIdent("trigger name"); err != nil { + if stmt.Name, err = p.parseIdent("function name"); err != nil { return &stmt, err } return &stmt, nil -}*/ +} func (p *Parser) parseIdent(desc string) (*Ident, error) { pos, tok, lit := p.scan() @@ -1353,6 +1332,19 @@ func (p *Parser) parseIdent(desc string) (*Ident, error) { } } +func (p *Parser) parseVariable(desc string) (*Variable, error) { + pos, tok, lit := p.scan() + switch tok { + case VARIABLE: + return &Variable{ + Name: lit, + NamePos: pos, + }, nil + default: + return nil, p.errorExpected(pos, tok, desc) + } +} + func (p *Parser) parseType() (_ *Type, err error) { var typ Type if typ.Name, err = p.parseIdent("type name"); err != nil { @@ -2567,7 +2559,7 @@ func (p *Parser) parseOperand() (expr Expr, err error) { } return ident, nil case VARIABLE: - return &VariableRef{Name: lit, NamePos: pos}, nil + return &Variable{Name: lit, NamePos: pos}, nil case MIN, MAX: ident := &Ident{Name: lit, NamePos: pos, Quoted: tok == QIDENT} return p.parseCall(ident) diff --git a/sql3/parser/parser_test.go b/sql3/parser/parser_test.go index cee879e3e..cb2388cef 100644 --- a/sql3/parser/parser_test.go +++ b/sql3/parser/parser_test.go @@ -351,13 +351,13 @@ func TestParser_ParseCacheTypeConstraints(t *testing.T) { func TestParser_ParseAlterStatement(t *testing.T) { t.Run("AlterTable", func(t *testing.T) { - /*AssertParseStatement(t, `ALTER TABLE tbl RENAME TO new_tbl`, &sql.AlterTableStatement{ + /*AssertParseStatement(t, `ALTER TABLE tbl RENAME TO new_tbl`, &parser.AlterTableStatement{ Alter: pos(0), Table: pos(6), - Name: &sql.Ident{NamePos: pos(12), Name: "tbl"}, + Name: &parser.Ident{NamePos: pos(12), Name: "tbl"}, Rename: pos(16), RenameTo: pos(23), - NewName: &sql.Ident{NamePos: pos(26), Name: "new_tbl"}, + 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), @@ -378,19 +378,19 @@ func TestParser_ParseAlterStatement(t *testing.T) { To: pos(27), NewColumnName: &parser.Ident{NamePos: pos(30), Name: "new_col"}, }) - /*AssertParseStatement(t, `ALTER TABLE tbl ADD COLUMN col TEXT PRIMARY KEY`, &sql.AlterTableStatement{ + /*AssertParseStatement(t, `ALTER TABLE tbl ADD COLUMN col TEXT PRIMARY KEY`, &parser.AlterTableStatement{ Alter: pos(0), Table: pos(6), - Name: &sql.Ident{NamePos: pos(12), Name: "tbl"}, + Name: &parser.Ident{NamePos: pos(12), Name: "tbl"}, Add: pos(16), AddColumn: pos(20), - ColumnDef: &sql.ColumnDefinition{ - Name: &sql.Ident{Name: "col", NamePos: pos(27)}, - Type: &sql.Type{ - Name: &sql.Ident{Name: "TEXT", NamePos: pos(31)}, + 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{ - &sql.PrimaryKeyConstraint{ + &parser.PrimaryKeyConstraint{ Primary: pos(36), Key: pos(44), }, @@ -440,6 +440,183 @@ func TestParser_ParseAlterStatement(t *testing.T) { }) } +func TestParser_ParseFunctionStatement(t *testing.T) { + t.Run("CreateFunction", func(t *testing.T) { + AssertParseStatement(t, `CREATE FUNCTION IF NOT EXISTS func (@param1 int, @param2 string) returns @scalar int as begin end`, &parser.CreateFunctionStatement{ + Create: pos(0), + Function: pos(7), + If: pos(16), + IfNot: pos(19), + IfNotExists: pos(23), + Name: &parser.Ident{NamePos: pos(30), Name: "func"}, + Lparen: pos(35), + Parameters: []*parser.ParameterDefinition{ + { + Name: &parser.Variable{Name: "@param1", NamePos: pos(36)}, + Type: &parser.Type{Name: &parser.Ident{NamePos: pos(44), Name: "int"}}, + }, + { + Name: &parser.Variable{Name: "@param2", NamePos: pos(49)}, + Type: &parser.Type{Name: &parser.Ident{NamePos: pos(57), Name: "string"}}, + }, + }, + Rparen: pos(63), + Returns: pos(65), + ReturnDef: &parser.ParameterDefinition{ + Name: &parser.Variable{Name: "@scalar", NamePos: pos(73)}, + Type: &parser.Type{Name: &parser.Ident{NamePos: pos(81), Name: "int"}}, + }, + As: pos(85), + Begin: pos(88), + End: pos(94), + }) + // AssertParseStatement(t, `CREATE TRIGGER IF NOT EXISTS trig BEFORE INSERT ON tbl BEGIN DELETE FROM new; END`, &parser.CreateFunctionStatement{ + // Create: pos(0), + // Function: pos(7), + // If: pos(15), + // IfNot: pos(18), + // IfNotExists: pos(22), + // Name: &parser.Ident{NamePos: pos(29), Name: "trig"}, + // Before: pos(34), + // Insert: pos(41), + // On: pos(48), + // Table: &parser.Ident{NamePos: pos(51), Name: "tbl"}, + // Begin: pos(55), + // Body: []parser.Statement{ + // &parser.DeleteStatement{ + // Delete: pos(61), + // From: pos(68), + // Table: &parser.QualifiedTableName{ + // Name: &parser.Ident{NamePos: pos(73), Name: "new"}, + // }, + // }, + // }, + // End: pos(78), + // }) + // AssertParseStatement(t, `CREATE TRIGGER trig INSTEAD OF UPDATE ON tbl BEGIN SELECT *; END`, &parser.CreateFunctionStatement{ + // Create: pos(0), + // Function: pos(7), + // Name: &parser.Ident{NamePos: pos(15), Name: "trig"}, + // Instead: pos(20), + // InsteadOf: pos(28), + // Update: pos(31), + // On: pos(38), + // Table: &parser.Ident{NamePos: pos(41), Name: "tbl"}, + // Begin: pos(45), + // Body: []parser.Statement{ + // &parser.SelectStatement{ + // Select: pos(51), + // Columns: []*parser.ResultColumn{{Star: pos(58)}}, + // }, + // }, + // End: pos(61), + // }) + // AssertParseStatement(t, `CREATE TRIGGER trig INSTEAD OF UPDATE OF x, y ON tbl FOR EACH ROW WHEN true BEGIN SELECT *; END`, &parser.CreateFunctionStatement{ + // Create: pos(0), + // Function: pos(7), + // Name: &parser.Ident{NamePos: pos(15), Name: "trig"}, + // Instead: pos(20), + // InsteadOf: pos(28), + // Update: pos(31), + // UpdateOf: pos(38), + // UpdateOfColumns: []*parser.Ident{ + // {NamePos: pos(41), Name: "x"}, + // {NamePos: pos(44), Name: "y"}, + // }, + // On: pos(46), + // Table: &parser.Ident{NamePos: pos(49), Name: "tbl"}, + // For: pos(53), + // ForEach: pos(57), + // ForEachRow: pos(62), + // When: pos(66), + // WhenExpr: &parser.BoolLit{ValuePos: pos(71), Value: true}, + // Begin: pos(76), + // Body: []parser.Statement{ + // &parser.SelectStatement{ + // Select: pos(82), + // Columns: []*parser.ResultColumn{{Star: pos(89)}}, + // }, + // }, + // End: pos(92), + // }) + // AssertParseStatement(t, `CREATE TRIGGER trig AFTER UPDATE ON tbl BEGIN WITH cte (x) AS (SELECT y) SELECT *; END`, &parser.CreateFunctionStatement{ + // Create: pos(0), + // Function: pos(7), + // Name: &parser.Ident{NamePos: pos(15), Name: "trig"}, + // After: pos(20), + // Update: pos(26), + // On: pos(33), + // Table: &parser.Ident{NamePos: pos(36), Name: "tbl"}, + // Begin: pos(40), + // Body: []parser.Statement{ + // &parser.SelectStatement{ + // WithClause: &parser.WithClause{ + // With: pos(46), + // CTEs: []*parser.CTE{ + // { + // TableName: &parser.Ident{NamePos: pos(51), Name: "cte"}, + // ColumnsLparen: pos(55), + // Columns: []*parser.Ident{ + // {NamePos: pos(56), Name: "x"}, + // }, + // ColumnsRparen: pos(57), + // As: pos(59), + // SelectLparen: pos(62), + // Select: &parser.SelectStatement{ + // Select: pos(63), + // Columns: []*parser.ResultColumn{ + // {Expr: &parser.Ident{NamePos: pos(70), Name: "y"}}, + // }, + // }, + // SelectRparen: pos(71), + // }, + // }, + // }, + // Select: pos(73), + // Columns: []*parser.ResultColumn{{Star: pos(80)}}, + // }, + // }, + // End: pos(83), + // }) + + AssertParseStatementError(t, `CREATE FUNCTION`, `1:15: expected function name, found 'EOF'`) + AssertParseStatementError(t, `CREATE FUNCTION IF`, `1:18: expected NOT, found 'EOF'`) + AssertParseStatementError(t, `CREATE FUNCTION IF NOT`, `1:22: expected EXISTS, found 'EOF'`) + // AssertParseStatementError(t, `CREATE FUNCTION trig INSTEAD`, `1:27: expected OF, found 'EOF'`) + // AssertParseStatementError(t, `CREATE FUNCTION trig AFTER`, `1:25: expected DELETE, INSERT, or UPDATE, found 'EOF'`) + // AssertParseStatementError(t, `CREATE FUNCTION trig UPDATE OF`, `1:29: expected column name, found 'EOF'`) + // AssertParseStatementError(t, `CREATE TRIGGER trig UPDATE OF x,`, `1:32: expected column name, found 'EOF'`) + // AssertParseStatementError(t, `CREATE TRIGGER trig AFTER INSERT`, `1:32: expected ON, found 'EOF'`) + // AssertParseStatementError(t, `CREATE TRIGGER trig AFTER INSERT ON `, `1:36: expected table name, found 'EOF'`) + // AssertParseStatementError(t, `CREATE TRIGGER trig AFTER INSERT ON tbl FOR`, `1:43: expected EACH, found 'EOF'`) + // AssertParseStatementError(t, `CREATE TRIGGER trig AFTER INSERT ON tbl FOR EACH`, `1:48: expected ROW, found 'EOF'`) + // AssertParseStatementError(t, `CREATE TRIGGER trig AFTER INSERT ON tbl WHEN`, `1:44: expected expression, found 'EOF'`) + // AssertParseStatementError(t, `CREATE TRIGGER trig AFTER INSERT ON tbl`, `1:39: expected BEGIN, found 'EOF'`) + // AssertParseStatementError(t, `CREATE TRIGGER trig AFTER INSERT ON tbl BEGIN`, `1:45: expected statement, found 'EOF'`) + // AssertParseStatementError(t, `CREATE TRIGGER trig AFTER INSERT ON tbl BEGIN SELECT`, `1:52: expected expression, found 'EOF'`) + // AssertParseStatementError(t, `CREATE TRIGGER trig AFTER INSERT ON tbl BEGIN SELECT *`, `1:54: expected semicolon, found 'EOF'`) + // AssertParseStatementError(t, `CREATE TRIGGER trig AFTER INSERT ON tbl BEGIN SELECT *;`, `1:55: expected statement, found 'EOF'`) + }) + + t.Run("DropFunction", func(t *testing.T) { + AssertParseStatement(t, `DROP FUNCTION func`, &parser.DropFunctionStatement{ + Drop: pos(0), + Trigger: pos(5), + Name: &parser.Ident{NamePos: pos(14), Name: "func"}, + }) + AssertParseStatement(t, `DROP FUNCTION IF EXISTS func`, &parser.DropFunctionStatement{ + Drop: pos(0), + Trigger: pos(5), + If: pos(14), + IfExists: pos(17), + Name: &parser.Ident{NamePos: pos(24), Name: "func"}, + }) + AssertParseStatementError(t, `DROP FUNCTION`, `1:13: expected function name, found 'EOF'`) + AssertParseStatementError(t, `DROP FUNCTION IF`, `1:16: expected EXISTS, found 'EOF'`) + AssertParseStatementError(t, `DROP FUNCTION IF EXISTS`, `1:23: expected function name, found 'EOF'`) + }) +} + func TestParser_ParseStatement(t *testing.T) { t.Run("ErrNoStatement", func(t *testing.T) { AssertParseStatementError(t, `123`, `1:1: expected statement, found 123`) @@ -473,19 +650,19 @@ func TestParser_ParseStatement(t *testing.T) { t.Run("Explain", func(t *testing.T) { /*t.Run("", func(t *testing.T) { - AssertParseStatement(t, `EXPLAIN BEGIN`, &sql.ExplainStatement{ + AssertParseStatement(t, `EXPLAIN BEGIN`, &parser.ExplainStatement{ Explain: pos(0), - Stmt: &sql.BeginStatement{ + Stmt: &parser.BeginStatement{ Begin: pos(8), }, }) })*/ /*t.Run("QueryPlan", func(t *testing.T) { - AssertParseStatement(t, `EXPLAIN QUERY PLAN BEGIN`, &sql.ExplainStatement{ + AssertParseStatement(t, `EXPLAIN QUERY PLAN BEGIN`, &parser.ExplainStatement{ Explain: pos(0), Query: pos(8), QueryPlan: pos(14), - Stmt: &sql.BeginStatement{ + Stmt: &parser.BeginStatement{ Begin: pos(19), }, }) @@ -500,31 +677,31 @@ func TestParser_ParseStatement(t *testing.T) { /*t.Run("Begin", func(t *testing.T) { t.Run("", func(t *testing.T) { - AssertParseStatement(t, `BEGIN`, &sql.BeginStatement{ + AssertParseStatement(t, `BEGIN`, &parser.BeginStatement{ Begin: pos(0), }) }) t.Run("Transaction", func(t *testing.T) { - AssertParseStatement(t, `BEGIN TRANSACTION`, &sql.BeginStatement{ + AssertParseStatement(t, `BEGIN TRANSACTION`, &parser.BeginStatement{ Begin: pos(0), Transaction: pos(6), }) }) t.Run("DeferredTransaction", func(t *testing.T) { - AssertParseStatement(t, `BEGIN DEFERRED TRANSACTION`, &sql.BeginStatement{ + 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;`, &sql.BeginStatement{ + AssertParseStatement(t, `BEGIN IMMEDIATE;`, &parser.BeginStatement{ Begin: pos(0), Immediate: pos(6), }) }) t.Run("Exclusive", func(t *testing.T) { - AssertParseStatement(t, `BEGIN EXCLUSIVE`, &sql.BeginStatement{ + AssertParseStatement(t, `BEGIN EXCLUSIVE`, &parser.BeginStatement{ Begin: pos(0), Exclusive: pos(6), }) @@ -536,12 +713,12 @@ func TestParser_ParseStatement(t *testing.T) { /*t.Run("Commit", func(t *testing.T) { t.Run("", func(t *testing.T) { - AssertParseStatement(t, `COMMIT`, &sql.CommitStatement{ + AssertParseStatement(t, `COMMIT`, &parser.CommitStatement{ Commit: pos(0), }) }) t.Run("Transaction", func(t *testing.T) { - AssertParseStatement(t, `COMMIT TRANSACTION`, &sql.CommitStatement{ + AssertParseStatement(t, `COMMIT TRANSACTION`, &parser.CommitStatement{ Commit: pos(0), Transaction: pos(7), }) @@ -550,12 +727,12 @@ func TestParser_ParseStatement(t *testing.T) { /*t.Run("End", func(t *testing.T) { t.Run("", func(t *testing.T) { - AssertParseStatement(t, `END`, &sql.CommitStatement{ + AssertParseStatement(t, `END`, &parser.CommitStatement{ End: pos(0), }) }) t.Run("Transaction", func(t *testing.T) { - AssertParseStatement(t, `END TRANSACTION`, &sql.CommitStatement{ + AssertParseStatement(t, `END TRANSACTION`, &parser.CommitStatement{ End: pos(0), Transaction: pos(4), }) @@ -564,33 +741,33 @@ func TestParser_ParseStatement(t *testing.T) { /*t.Run("Rollback", func(t *testing.T) { t.Run("", func(t *testing.T) { - AssertParseStatement(t, `ROLLBACK`, &sql.RollbackStatement{ + AssertParseStatement(t, `ROLLBACK`, &parser.RollbackStatement{ Rollback: pos(0), }) }) t.Run("Transaction", func(t *testing.T) { - AssertParseStatement(t, `ROLLBACK TRANSACTION`, &sql.RollbackStatement{ + AssertParseStatement(t, `ROLLBACK TRANSACTION`, &parser.RollbackStatement{ Rollback: pos(0), Transaction: pos(9), }) }) t.Run("To", func(t *testing.T) { - AssertParseStatement(t, `ROLLBACK TO svpt`, &sql.RollbackStatement{ + AssertParseStatement(t, `ROLLBACK TO svpt`, &parser.RollbackStatement{ Rollback: pos(0), To: pos(9), - SavepointName: &sql.Ident{ + SavepointName: &parser.Ident{ Name: "svpt", NamePos: pos(12), }, }) }) t.Run("TransactionToSavepoint", func(t *testing.T) { - AssertParseStatement(t, `ROLLBACK TRANSACTION TO SAVEPOINT "svpt"`, &sql.RollbackStatement{ + AssertParseStatement(t, `ROLLBACK TRANSACTION TO SAVEPOINT "svpt"`, &parser.RollbackStatement{ Rollback: pos(0), Transaction: pos(9), To: pos(21), Savepoint: pos(24), - SavepointName: &sql.Ident{ + SavepointName: &parser.Ident{ Name: "svpt", NamePos: pos(34), Quoted: true, @@ -604,18 +781,18 @@ func TestParser_ParseStatement(t *testing.T) { /*t.Run("Savepoint", func(t *testing.T) { t.Run("Ident", func(t *testing.T) { - AssertParseStatement(t, `SAVEPOINT svpt`, &sql.SavepointStatement{ + AssertParseStatement(t, `SAVEPOINT svpt`, &parser.SavepointStatement{ Savepoint: pos(0), - Name: &sql.Ident{ + Name: &parser.Ident{ Name: "svpt", NamePos: pos(10), }, }) }) t.Run("String", func(t *testing.T) { - AssertParseStatement(t, `SAVEPOINT "svpt"`, &sql.SavepointStatement{ + AssertParseStatement(t, `SAVEPOINT "svpt"`, &parser.SavepointStatement{ Savepoint: pos(0), - Name: &sql.Ident{ + Name: &parser.Ident{ Name: "svpt", NamePos: pos(10), Quoted: true, @@ -629,18 +806,18 @@ func TestParser_ParseStatement(t *testing.T) { /*t.Run("Release", func(t *testing.T) { t.Run("Ident", func(t *testing.T) { - AssertParseStatement(t, `RELEASE svpt`, &sql.ReleaseStatement{ + AssertParseStatement(t, `RELEASE svpt`, &parser.ReleaseStatement{ Release: pos(0), - Name: &sql.Ident{ + Name: &parser.Ident{ Name: "svpt", NamePos: pos(8), }, }) }) t.Run("String", func(t *testing.T) { - AssertParseStatement(t, `RELEASE "svpt"`, &sql.ReleaseStatement{ + AssertParseStatement(t, `RELEASE "svpt"`, &parser.ReleaseStatement{ Release: pos(0), - Name: &sql.Ident{ + Name: &parser.Ident{ Name: "svpt", NamePos: pos(8), Quoted: true, @@ -648,10 +825,10 @@ func TestParser_ParseStatement(t *testing.T) { }) }) t.Run("SavepointIdent", func(t *testing.T) { - AssertParseStatement(t, `RELEASE SAVEPOINT svpt`, &sql.ReleaseStatement{ + AssertParseStatement(t, `RELEASE SAVEPOINT svpt`, &parser.ReleaseStatement{ Release: pos(0), Savepoint: pos(8), - Name: &sql.Ident{ + Name: &parser.Ident{ Name: "svpt", NamePos: pos(18), }, @@ -732,35 +909,35 @@ func TestParser_ParseStatement(t *testing.T) { 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`, &sql.CreateTableStatement{ + /*AssertParseStatement(t, `CREATE TABLE tbl AS SELECT foo`, &parser.CreateTableStatement{ Create: pos(0), Table: pos(7), - Name: &sql.Ident{ + Name: &parser.Ident{ Name: "tbl", NamePos: pos(13), }, As: pos(17), - Select: &sql.SelectStatement{ + Select: &parser.SelectStatement{ Select: pos(20), Columns: []*sql.ResultColumn{ - {Expr: &sql.Ident{NamePos: pos(27), Name: "foo"}}, + {Expr: &parser.Ident{NamePos: pos(27), Name: "foo"}}, }, }, })*/ - /*AssertParseStatement(t, `CREATE TABLE tbl AS WITH cte (x) AS (SELECT y) SELECT foo`, &sql.CreateTableStatement{ + /*AssertParseStatement(t, `CREATE TABLE tbl AS WITH cte (x) AS (SELECT y) SELECT foo`, &parser.CreateTableStatement{ Create: pos(0), Table: pos(7), - Name: &sql.Ident{ + Name: &parser.Ident{ Name: "tbl", NamePos: pos(13), }, As: pos(17), - Select: &sql.SelectStatement{ - WithClause: &sql.WithClause{ + Select: &parser.SelectStatement{ + WithClause: &parser.WithClause{ With: pos(20), CTEs: []*sql.CTE{ { - TableName: &sql.Ident{NamePos: pos(25), Name: "cte"}, + TableName: &parser.Ident{NamePos: pos(25), Name: "cte"}, ColumnsLparen: pos(29), Columns: []*sql.Ident{ {NamePos: pos(30), Name: "x"}, @@ -768,10 +945,10 @@ func TestParser_ParseStatement(t *testing.T) { ColumnsRparen: pos(31), As: pos(33), SelectLparen: pos(36), - Select: &sql.SelectStatement{ + Select: &parser.SelectStatement{ Select: pos(37), Columns: []*sql.ResultColumn{ - {Expr: &sql.Ident{NamePos: pos(44), Name: "y"}}, + {Expr: &parser.Ident{NamePos: pos(44), Name: "y"}}, }, }, SelectRparen: pos(45), @@ -780,7 +957,7 @@ func TestParser_ParseStatement(t *testing.T) { }, Select: pos(47), Columns: []*sql.ResultColumn{ - {Expr: &sql.Ident{NamePos: pos(54), Name: "foo"}}, + {Expr: &parser.Ident{NamePos: pos(54), Name: "foo"}}, }, }, })*/ @@ -790,19 +967,19 @@ func TestParser_ParseStatement(t *testing.T) { 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)`, &sql.CreateTableStatement{ + AssertParseStatement(t, `CREATE TABLE tbl (col1 TEXT PRIMARY KEY)`, &parser.CreateTableStatement{ Create: pos(0), Table: pos(7), - Name: &sql.Ident{Name: "tbl", NamePos: pos(13)}, + Name: &parser.Ident{Name: "tbl", NamePos: pos(13)}, Lparen: pos(17), Columns: []*sql.ColumnDefinition{ { - Name: &sql.Ident{Name: "col1", NamePos: pos(18)}, - Type: &sql.Type{ - Name: &sql.Ident{Name: "TEXT", NamePos: pos(23)}, + Name: &parser.Ident{Name: "col1", NamePos: pos(18)}, + Type: &parser.Type{ + Name: &parser.Ident{Name: "TEXT", NamePos: pos(23)}, }, Constraints: []sql.Constraint{ - &sql.PrimaryKeyConstraint{ + &parser.PrimaryKeyConstraint{ Primary: pos(28), Key: pos(36), }, @@ -814,9 +991,9 @@ func TestParser_ParseStatement(t *testing.T) { }) 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], &sql.PrimaryKeyConstraint{ + if diff := deep.Equal(stmt.Columns[0].Constraints[0], &parser.PrimaryKeyConstraint{ Constraint: pos(28), - Name: &sql.Ident{Name: "cons1", NamePos: pos(39)}, + Name: &parser.Ident{Name: "cons1", NamePos: pos(39)}, Primary: pos(45), Key: pos(53), Autoincrement: pos(57), @@ -831,21 +1008,21 @@ func TestParser_ParseStatement(t *testing.T) { /*t.Run("Unique", func(t *testing.T) { t.Run("Simple", func(t *testing.T) { - AssertParseStatement(t, `CREATE TABLE tbl (col1 TEXT CONSTRAINT con1 UNIQUE)`, &sql.CreateTableStatement{ + AssertParseStatement(t, `CREATE TABLE tbl (col1 TEXT CONSTRAINT con1 UNIQUE)`, &parser.CreateTableStatement{ Create: pos(0), Table: pos(7), - Name: &sql.Ident{Name: "tbl", NamePos: pos(13)}, + Name: &parser.Ident{Name: "tbl", NamePos: pos(13)}, Lparen: pos(17), Columns: []*sql.ColumnDefinition{ { - Name: &sql.Ident{Name: "col1", NamePos: pos(18)}, - Type: &sql.Type{ - Name: &sql.Ident{Name: "TEXT", NamePos: pos(23)}, + Name: &parser.Ident{Name: "col1", NamePos: pos(18)}, + Type: &parser.Type{ + Name: &parser.Ident{Name: "TEXT", NamePos: pos(23)}, }, Constraints: []sql.Constraint{ - &sql.UniqueConstraint{ + &parser.UniqueConstraint{ Constraint: pos(28), - Name: &sql.Ident{Name: "con1", NamePos: pos(39)}, + Name: &parser.Ident{Name: "con1", NamePos: pos(39)}, Unique: pos(44), }, }, @@ -856,25 +1033,25 @@ func TestParser_ParseStatement(t *testing.T) { }) })*/ /*t.Run("Check", func(t *testing.T) { - AssertParseStatement(t, `CREATE TABLE tbl (col1 TEXT CHECK (col1 > 1))`, &sql.CreateTableStatement{ + AssertParseStatement(t, `CREATE TABLE tbl (col1 TEXT CHECK (col1 > 1))`, &parser.CreateTableStatement{ Create: pos(0), Table: pos(7), - Name: &sql.Ident{Name: "tbl", NamePos: pos(13)}, + Name: &parser.Ident{Name: "tbl", NamePos: pos(13)}, Lparen: pos(17), Columns: []*sql.ColumnDefinition{ { - Name: &sql.Ident{Name: "col1", NamePos: pos(18)}, - Type: &sql.Type{ - Name: &sql.Ident{Name: "TEXT", NamePos: pos(23)}, + Name: &parser.Ident{Name: "col1", NamePos: pos(18)}, + Type: &parser.Type{ + Name: &parser.Ident{Name: "TEXT", NamePos: pos(23)}, }, Constraints: []sql.Constraint{ - &sql.CheckConstraint{ + &parser.CheckConstraint{ Check: pos(28), Lparen: pos(34), - Expr: &sql.BinaryExpr{ - X: &sql.Ident{Name: "col1", NamePos: pos(35)}, + Expr: &parser.BinaryExpr{ + X: &parser.Ident{Name: "col1", NamePos: pos(35)}, Op: sql.GT, OpPos: pos(40), - Y: &sql.NumberLit{Value: "1", ValuePos: pos(42)}, + Y: &parser.NumberLit{Value: "1", ValuePos: pos(42)}, }, Rparen: pos(43), }, @@ -886,22 +1063,22 @@ func TestParser_ParseStatement(t *testing.T) { })*/ /*t.Run("Default", func(t *testing.T) { t.Run("Expr", func(t *testing.T) { - AssertParseStatement(t, `CREATE TABLE tbl (col1 TEXT DEFAULT (1))`, &sql.CreateTableStatement{ + AssertParseStatement(t, `CREATE TABLE tbl (col1 TEXT DEFAULT (1))`, &parser.CreateTableStatement{ Create: pos(0), Table: pos(7), - Name: &sql.Ident{Name: "tbl", NamePos: pos(13)}, + Name: &parser.Ident{Name: "tbl", NamePos: pos(13)}, Lparen: pos(17), Columns: []*sql.ColumnDefinition{ { - Name: &sql.Ident{Name: "col1", NamePos: pos(18)}, - Type: &sql.Type{ - Name: &sql.Ident{Name: "TEXT", NamePos: pos(23)}, + Name: &parser.Ident{Name: "col1", NamePos: pos(18)}, + Type: &parser.Type{ + Name: &parser.Ident{Name: "TEXT", NamePos: pos(23)}, }, Constraints: []sql.Constraint{ - &sql.DefaultConstraint{ + &parser.DefaultConstraint{ Default: pos(28), Lparen: pos(36), - Expr: &sql.NumberLit{Value: "1", ValuePos: pos(37)}, + Expr: &parser.NumberLit{Value: "1", ValuePos: pos(37)}, Rparen: pos(38), }, }, @@ -912,45 +1089,45 @@ func TestParser_ParseStatement(t *testing.T) { }) 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], &sql.DefaultConstraint{ + if diff := deepEqual(stmt.Columns[0].Constraints[0], &parser.DefaultConstraint{ Default: pos(28), - Expr: &sql.StringLit{Value: "foo", ValuePos: pos(36)}, + 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], &sql.DefaultConstraint{ + if diff := deepEqual(stmt.Columns[0].Constraints[0], &parser.DefaultConstraint{ Default: pos(28), - Expr: &sql.BlobLit{Value: "0F0F", ValuePos: pos(36)}, + 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], &sql.DefaultConstraint{ + if diff := deepEqual(stmt.Columns[0].Constraints[0], &parser.DefaultConstraint{ Default: pos(28), - Expr: &sql.NumberLit{Value: "1", ValuePos: pos(36)}, + 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], &sql.DefaultConstraint{ + if diff := deepEqual(stmt.Columns[0].Constraints[0], &parser.DefaultConstraint{ Default: pos(28), - Expr: &sql.NullLit{Pos: pos(36)}, + 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], &sql.DefaultConstraint{ + if diff := deepEqual(stmt.Columns[0].Constraints[0], &parser.DefaultConstraint{ Default: pos(28), - Expr: &sql.BoolLit{Value: true, ValuePos: pos(36)}, + Expr: &parser.BoolLit{Value: true, ValuePos: pos(36)}, }); diff != "" { t.Fatal(diff) } @@ -964,21 +1141,21 @@ func TestParser_ParseStatement(t *testing.T) { })*/ /*t.Run("ForeignKey", func(t *testing.T) { t.Run("Simple", func(t *testing.T) { - AssertParseStatement(t, `CREATE TABLE tbl (col1 TEXT REFERENCES foo (col2))`, &sql.CreateTableStatement{ + AssertParseStatement(t, `CREATE TABLE tbl (col1 TEXT REFERENCES foo (col2))`, &parser.CreateTableStatement{ Create: pos(0), Table: pos(7), - Name: &sql.Ident{Name: "tbl", NamePos: pos(13)}, + Name: &parser.Ident{Name: "tbl", NamePos: pos(13)}, Lparen: pos(17), Columns: []*sql.ColumnDefinition{ { - Name: &sql.Ident{Name: "col1", NamePos: pos(18)}, - Type: &sql.Type{ - Name: &sql.Ident{Name: "TEXT", NamePos: pos(23)}, + Name: &parser.Ident{Name: "col1", NamePos: pos(18)}, + Type: &parser.Type{ + Name: &parser.Ident{Name: "TEXT", NamePos: pos(23)}, }, Constraints: []sql.Constraint{ - &sql.ForeignKeyConstraint{ + &parser.ForeignKeyConstraint{ References: pos(28), - ForeignTable: &sql.Ident{Name: "foo", NamePos: pos(39)}, + ForeignTable: &parser.Ident{Name: "foo", NamePos: pos(39)}, ForeignLparen: pos(43), ForeignColumns: []*sql.Ident{ {Name: "col2", NamePos: pos(44)}, @@ -993,9 +1170,9 @@ func TestParser_ParseStatement(t *testing.T) { }) 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], &sql.ForeignKeyConstraint{ + if diff := deepEqual(stmt.Columns[0].Constraints[0], &parser.ForeignKeyConstraint{ References: pos(28), - ForeignTable: &sql.Ident{Name: "foo", NamePos: pos(39)}, + ForeignTable: &parser.Ident{Name: "foo", NamePos: pos(39)}, ForeignLparen: pos(43), ForeignColumns: []*sql.Ident{ {Name: "col2", NamePos: pos(44)}, @@ -1015,9 +1192,9 @@ func TestParser_ParseStatement(t *testing.T) { }) 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], &sql.ForeignKeyConstraint{ + if diff := deepEqual(stmt.Columns[0].Constraints[0], &parser.ForeignKeyConstraint{ References: pos(28), - ForeignTable: &sql.Ident{Name: "foo", NamePos: pos(39)}, + ForeignTable: &parser.Ident{Name: "foo", NamePos: pos(39)}, ForeignLparen: pos(43), ForeignColumns: []*sql.Ident{ {Name: "col2", NamePos: pos(44)}, @@ -1037,9 +1214,9 @@ func TestParser_ParseStatement(t *testing.T) { }) 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], &sql.ForeignKeyConstraint{ + if diff := deepEqual(stmt.Columns[0].Constraints[0], &parser.ForeignKeyConstraint{ References: pos(28), - ForeignTable: &sql.Ident{Name: "foo", NamePos: pos(39)}, + ForeignTable: &parser.Ident{Name: "foo", NamePos: pos(39)}, ForeignLparen: pos(43), ForeignColumns: []*sql.Ident{ {Name: "col2", NamePos: pos(44)}, @@ -1058,9 +1235,9 @@ func TestParser_ParseStatement(t *testing.T) { }) 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], &sql.ForeignKeyConstraint{ + if diff := deepEqual(stmt.Columns[0].Constraints[0], &parser.ForeignKeyConstraint{ References: pos(28), - ForeignTable: &sql.Ident{Name: "foo", NamePos: pos(39)}, + ForeignTable: &parser.Ident{Name: "foo", NamePos: pos(39)}, ForeignLparen: pos(43), ForeignColumns: []*sql.Ident{ {Name: "col2", NamePos: pos(44)}, @@ -1079,9 +1256,9 @@ func TestParser_ParseStatement(t *testing.T) { }) 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], &sql.ForeignKeyConstraint{ + if diff := deepEqual(stmt.Columns[0].Constraints[0], &parser.ForeignKeyConstraint{ References: pos(28), - ForeignTable: &sql.Ident{Name: "foo", NamePos: pos(39)}, + ForeignTable: &parser.Ident{Name: "foo", NamePos: pos(39)}, ForeignLparen: pos(43), ForeignColumns: []*sql.Ident{ {Name: "col2", NamePos: pos(44)}, @@ -1101,9 +1278,9 @@ func TestParser_ParseStatement(t *testing.T) { }) 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], &sql.ForeignKeyConstraint{ + if diff := deepEqual(stmt.Columns[0].Constraints[0], &parser.ForeignKeyConstraint{ References: pos(28), - ForeignTable: &sql.Ident{Name: "foo", NamePos: pos(39)}, + ForeignTable: &parser.Ident{Name: "foo", NamePos: pos(39)}, ForeignLparen: pos(43), ForeignColumns: []*sql.Ident{ {Name: "col2", NamePos: pos(44)}, @@ -1127,9 +1304,9 @@ func TestParser_ParseStatement(t *testing.T) { }) 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], &sql.ForeignKeyConstraint{ + if diff := deepEqual(stmt.Columns[0].Constraints[0], &parser.ForeignKeyConstraint{ References: pos(28), - ForeignTable: &sql.Ident{Name: "foo", NamePos: pos(39)}, + ForeignTable: &parser.Ident{Name: "foo", NamePos: pos(39)}, ForeignLparen: pos(43), ForeignColumns: []*sql.Ident{ {Name: "col2", NamePos: pos(44)}, @@ -1142,9 +1319,9 @@ func TestParser_ParseStatement(t *testing.T) { }) 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], &sql.ForeignKeyConstraint{ + if diff := deepEqual(stmt.Columns[0].Constraints[0], &parser.ForeignKeyConstraint{ References: pos(28), - ForeignTable: &sql.Ident{Name: "foo", NamePos: pos(39)}, + ForeignTable: &parser.Ident{Name: "foo", NamePos: pos(39)}, ForeignLparen: pos(43), ForeignColumns: []*sql.Ident{ {Name: "col2", NamePos: pos(44)}, @@ -1158,9 +1335,9 @@ func TestParser_ParseStatement(t *testing.T) { }) 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], &sql.ForeignKeyConstraint{ + if diff := deepEqual(stmt.Columns[0].Constraints[0], &parser.ForeignKeyConstraint{ References: pos(28), - ForeignTable: &sql.Ident{Name: "foo", NamePos: pos(39)}, + ForeignTable: &parser.Ident{Name: "foo", NamePos: pos(39)}, ForeignLparen: pos(43), ForeignColumns: []*sql.Ident{ {Name: "col2", NamePos: pos(44)}, @@ -1175,9 +1352,9 @@ func TestParser_ParseStatement(t *testing.T) { }) 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], &sql.ForeignKeyConstraint{ + if diff := deepEqual(stmt.Columns[0].Constraints[0], &parser.ForeignKeyConstraint{ References: pos(28), - ForeignTable: &sql.Ident{Name: "foo", NamePos: pos(39)}, + ForeignTable: &parser.Ident{Name: "foo", NamePos: pos(39)}, ForeignLparen: pos(43), ForeignColumns: []*sql.Ident{ {Name: "col2", NamePos: pos(44)}, @@ -1199,21 +1376,21 @@ func TestParser_ParseStatement(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))`, &sql.CreateTableStatement{ + AssertParseStatement(t, `CREATE TABLE tbl (col1 TEXT, PRIMARY KEY (col1, col2))`, &parser.CreateTableStatement{ Create: pos(0), Table: pos(7), - Name: &sql.Ident{Name: "tbl", NamePos: pos(13)}, + Name: &parser.Ident{Name: "tbl", NamePos: pos(13)}, Lparen: pos(17), Columns: []*sql.ColumnDefinition{ { - Name: &sql.Ident{Name: "col1", NamePos: pos(18)}, - Type: &sql.Type{ - Name: &sql.Ident{Name: "TEXT", NamePos: pos(23)}, + Name: &parser.Ident{Name: "col1", NamePos: pos(18)}, + Type: &parser.Type{ + Name: &parser.Ident{Name: "TEXT", NamePos: pos(23)}, }, }, }, Constraints: []sql.Constraint{ - &sql.PrimaryKeyConstraint{ + &parser.PrimaryKeyConstraint{ Primary: pos(29), Key: pos(37), Lparen: pos(41), @@ -1234,23 +1411,23 @@ func TestParser_ParseStatement(t *testing.T) { 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))`, &sql.CreateTableStatement{ + AssertParseStatement(t, `CREATE TABLE tbl (col1 TEXT, CONSTRAINT con1 UNIQUE (col1, col2))`, &parser.CreateTableStatement{ Create: pos(0), Table: pos(7), - Name: &sql.Ident{Name: "tbl", NamePos: pos(13)}, + Name: &parser.Ident{Name: "tbl", NamePos: pos(13)}, Lparen: pos(17), Columns: []*sql.ColumnDefinition{ { - Name: &sql.Ident{Name: "col1", NamePos: pos(18)}, - Type: &sql.Type{ - Name: &sql.Ident{Name: "TEXT", NamePos: pos(23)}, + Name: &parser.Ident{Name: "col1", NamePos: pos(18)}, + Type: &parser.Type{ + Name: &parser.Ident{Name: "TEXT", NamePos: pos(23)}, }, }, }, Constraints: []sql.Constraint{ - &sql.UniqueConstraint{ + &parser.UniqueConstraint{ Constraint: pos(29), - Name: &sql.Ident{Name: "con1", NamePos: pos(40)}, + Name: &parser.Ident{Name: "con1", NamePos: pos(40)}, Unique: pos(45), Lparen: pos(52), Columns: []*sql.Ident{ @@ -1267,27 +1444,27 @@ func TestParser_ParseStatement(t *testing.T) { 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))`, &sql.CreateTableStatement{ + AssertParseStatement(t, `CREATE TABLE tbl (col1 TEXT, CHECK(foo = bar))`, &parser.CreateTableStatement{ Create: pos(0), Table: pos(7), - Name: &sql.Ident{Name: "tbl", NamePos: pos(13)}, + Name: &parser.Ident{Name: "tbl", NamePos: pos(13)}, Lparen: pos(17), Columns: []*sql.ColumnDefinition{ { - Name: &sql.Ident{Name: "col1", NamePos: pos(18)}, - Type: &sql.Type{ - Name: &sql.Ident{Name: "TEXT", NamePos: pos(23)}, + Name: &parser.Ident{Name: "col1", NamePos: pos(18)}, + Type: &parser.Type{ + Name: &parser.Ident{Name: "TEXT", NamePos: pos(23)}, }, }, }, Constraints: []sql.Constraint{ - &sql.CheckConstraint{ + &parser.CheckConstraint{ Check: pos(29), Lparen: pos(34), - Expr: &sql.BinaryExpr{ - X: &sql.Ident{Name: "foo", NamePos: pos(35)}, + Expr: &parser.BinaryExpr{ + X: &parser.Ident{Name: "foo", NamePos: pos(35)}, Op: sql.EQ, OpPos: pos(39), - Y: &sql.Ident{Name: "bar", NamePos: pos(41)}, + Y: &parser.Ident{Name: "bar", NamePos: pos(41)}, }, Rparen: pos(44), }, @@ -1300,21 +1477,21 @@ func TestParser_ParseStatement(t *testing.T) { 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))`, &sql.CreateTableStatement{ + AssertParseStatement(t, `CREATE TABLE tbl (col1 TEXT, FOREIGN KEY (col1, col2) REFERENCES tbl2 (x, y))`, &parser.CreateTableStatement{ Create: pos(0), Table: pos(7), - Name: &sql.Ident{Name: "tbl", NamePos: pos(13)}, + Name: &parser.Ident{Name: "tbl", NamePos: pos(13)}, Lparen: pos(17), Columns: []*sql.ColumnDefinition{ { - Name: &sql.Ident{Name: "col1", NamePos: pos(18)}, - Type: &sql.Type{ - Name: &sql.Ident{Name: "TEXT", NamePos: pos(23)}, + Name: &parser.Ident{Name: "col1", NamePos: pos(18)}, + Type: &parser.Type{ + Name: &parser.Ident{Name: "TEXT", NamePos: pos(23)}, }, }, }, Constraints: []sql.Constraint{ - &sql.ForeignKeyConstraint{ + &parser.ForeignKeyConstraint{ Foreign: pos(29), ForeignKey: pos(37), Lparen: pos(41), @@ -1324,7 +1501,7 @@ func TestParser_ParseStatement(t *testing.T) { }, Rparen: pos(52), References: pos(54), - ForeignTable: &sql.Ident{Name: "tbl2", NamePos: pos(65)}, + ForeignTable: &parser.Ident{Name: "tbl2", NamePos: pos(65)}, ForeignLparen: pos(70), ForeignColumns: []*sql.Ident{ {Name: "x", NamePos: pos(71)}, @@ -1373,10 +1550,10 @@ func TestParser_ParseStatement(t *testing.T) { }) /*t.Run("CreateView", func(t *testing.T) { - AssertParseStatement(t, `CREATE VIEW vw (col1, col2) AS SELECT x, y`, &sql.CreateViewStatement{ + AssertParseStatement(t, `CREATE VIEW vw (col1, col2) AS SELECT x, y`, &parser.CreateViewStatement{ Create: pos(0), View: pos(7), - Name: &sql.Ident{NamePos: pos(12), Name: "vw"}, + Name: &parser.Ident{NamePos: pos(12), Name: "vw"}, Lparen: pos(15), Columns: []*sql.Ident{ {NamePos: pos(16), Name: "col1"}, @@ -1384,38 +1561,38 @@ func TestParser_ParseStatement(t *testing.T) { }, Rparen: pos(26), As: pos(28), - Select: &sql.SelectStatement{ + Select: &parser.SelectStatement{ Select: pos(31), Columns: []*sql.ResultColumn{ - {Expr: &sql.Ident{NamePos: pos(38), Name: "x"}}, - {Expr: &sql.Ident{NamePos: pos(41), Name: "y"}}, + {Expr: &parser.Ident{NamePos: pos(38), Name: "x"}}, + {Expr: &parser.Ident{NamePos: pos(41), Name: "y"}}, }, }, }) - AssertParseStatement(t, `CREATE VIEW vw AS SELECT x`, &sql.CreateViewStatement{ + AssertParseStatement(t, `CREATE VIEW vw AS SELECT x`, &parser.CreateViewStatement{ Create: pos(0), View: pos(7), - Name: &sql.Ident{NamePos: pos(12), Name: "vw"}, + Name: &parser.Ident{NamePos: pos(12), Name: "vw"}, As: pos(15), - Select: &sql.SelectStatement{ + Select: &parser.SelectStatement{ Select: pos(18), Columns: []*sql.ResultColumn{ - {Expr: &sql.Ident{NamePos: pos(25), Name: "x"}}, + {Expr: &parser.Ident{NamePos: pos(25), Name: "x"}}, }, }, }) - AssertParseStatement(t, `CREATE VIEW IF NOT EXISTS vw AS SELECT x`, &sql.CreateViewStatement{ + 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: &sql.Ident{NamePos: pos(26), Name: "vw"}, + Name: &parser.Ident{NamePos: pos(26), Name: "vw"}, As: pos(29), - Select: &sql.SelectStatement{ + Select: &parser.SelectStatement{ Select: pos(32), Columns: []*sql.ResultColumn{ - {Expr: &sql.Ident{NamePos: pos(39), Name: "x"}}, + {Expr: &parser.Ident{NamePos: pos(39), Name: "x"}}, }, }, }) @@ -1430,17 +1607,17 @@ func TestParser_ParseStatement(t *testing.T) { })*/ /*t.Run("DropView", func(t *testing.T) { - AssertParseStatement(t, `DROP VIEW vw`, &sql.DropViewStatement{ + AssertParseStatement(t, `DROP VIEW vw`, &parser.DropViewStatement{ Drop: pos(0), View: pos(5), - Name: &sql.Ident{NamePos: pos(10), Name: "vw"}, + Name: &parser.Ident{NamePos: pos(10), Name: "vw"}, }) - AssertParseStatement(t, `DROP VIEW IF EXISTS vw`, &sql.DropViewStatement{ + AssertParseStatement(t, `DROP VIEW IF EXISTS vw`, &parser.DropViewStatement{ Drop: pos(0), View: pos(5), If: pos(10), IfExists: pos(13), - Name: &sql.Ident{NamePos: pos(20), Name: "vw"}, + Name: &parser.Ident{NamePos: pos(20), Name: "vw"}, }) AssertParseStatementError(t, `DROP`, `1:1: expected TABLE, VIEW, INDEX, or TRIGGER`) AssertParseStatementError(t, `DROP VIEW`, `1:9: expected view name, found 'EOF'`) @@ -1449,59 +1626,59 @@ func TestParser_ParseStatement(t *testing.T) { })*/ /*t.Run("CreateIndex", func(t *testing.T) { - AssertParseStatement(t, `CREATE INDEX idx ON tbl (x ASC, y DESC, z)`, &sql.CreateIndexStatement{ + AssertParseStatement(t, `CREATE INDEX idx ON tbl (x ASC, y DESC, z)`, &parser.CreateIndexStatement{ Create: pos(0), Index: pos(7), - Name: &sql.Ident{NamePos: pos(13), Name: "idx"}, + Name: &parser.Ident{NamePos: pos(13), Name: "idx"}, On: pos(17), - Table: &sql.Ident{NamePos: pos(20), Name: "tbl"}, + Table: &parser.Ident{NamePos: pos(20), Name: "tbl"}, Lparen: pos(24), Columns: []*sql.IndexedColumn{ - {X: &sql.Ident{NamePos: pos(25), Name: "x"}, Asc: pos(27)}, - {X: &sql.Ident{NamePos: pos(32), Name: "y"}, Desc: pos(34)}, - {X: &sql.Ident{NamePos: pos(40), Name: "z"}}, + {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)`, &sql.CreateIndexStatement{ + AssertParseStatement(t, `CREATE UNIQUE INDEX idx ON tbl (x)`, &parser.CreateIndexStatement{ Create: pos(0), Unique: pos(7), Index: pos(14), - Name: &sql.Ident{NamePos: pos(20), Name: "idx"}, + Name: &parser.Ident{NamePos: pos(20), Name: "idx"}, On: pos(24), - Table: &sql.Ident{NamePos: pos(27), Name: "tbl"}, + Table: &parser.Ident{NamePos: pos(27), Name: "tbl"}, Lparen: pos(31), Columns: []*sql.IndexedColumn{ - {X: &sql.Ident{NamePos: pos(32), Name: "x"}}, + {X: &parser.Ident{NamePos: pos(32), Name: "x"}}, }, Rparen: pos(33), }) - AssertParseStatement(t, `CREATE INDEX idx ON tbl (x) WHERE true`, &sql.CreateIndexStatement{ + AssertParseStatement(t, `CREATE INDEX idx ON tbl (x) WHERE true`, &parser.CreateIndexStatement{ Create: pos(0), Index: pos(7), - Name: &sql.Ident{NamePos: pos(13), Name: "idx"}, + Name: &parser.Ident{NamePos: pos(13), Name: "idx"}, On: pos(17), - Table: &sql.Ident{NamePos: pos(20), Name: "tbl"}, + Table: &parser.Ident{NamePos: pos(20), Name: "tbl"}, Lparen: pos(24), Columns: []*sql.IndexedColumn{ - {X: &sql.Ident{NamePos: pos(25), Name: "x"}}, + {X: &parser.Ident{NamePos: pos(25), Name: "x"}}, }, Rparen: pos(26), Where: pos(28), - WhereExpr: &sql.BoolLit{ValuePos: pos(34), Value: true}, + WhereExpr: &parser.BoolLit{ValuePos: pos(34), Value: true}, }) - AssertParseStatement(t, `CREATE INDEX IF NOT EXISTS idx ON tbl (x)`, &sql.CreateIndexStatement{ + 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: &sql.Ident{NamePos: pos(27), Name: "idx"}, + Name: &parser.Ident{NamePos: pos(27), Name: "idx"}, On: pos(31), - Table: &sql.Ident{NamePos: pos(34), Name: "tbl"}, + Table: &parser.Ident{NamePos: pos(34), Name: "tbl"}, Lparen: pos(38), Columns: []*sql.IndexedColumn{ - {X: &sql.Ident{NamePos: pos(39), Name: "x"}}, + {X: &parser.Ident{NamePos: pos(39), Name: "x"}}, }, Rparen: pos(40), }) @@ -1518,201 +1695,23 @@ func TestParser_ParseStatement(t *testing.T) { })*/ /*t.Run("DropIndex", func(t *testing.T) { - AssertParseStatement(t, `DROP INDEX idx`, &sql.DropIndexStatement{ + AssertParseStatement(t, `DROP INDEX idx`, &parser.DropIndexStatement{ Drop: pos(0), Index: pos(5), - Name: &sql.Ident{NamePos: pos(11), Name: "idx"}, + Name: &parser.Ident{NamePos: pos(11), Name: "idx"}, }) - AssertParseStatement(t, `DROP INDEX IF EXISTS idx`, &sql.DropIndexStatement{ + AssertParseStatement(t, `DROP INDEX IF EXISTS idx`, &parser.DropIndexStatement{ Drop: pos(0), Index: pos(5), If: pos(11), IfExists: pos(14), - Name: &sql.Ident{NamePos: pos(21), Name: "idx"}, + 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("CreateTrigger", func(t *testing.T) { - AssertParseStatement(t, `CREATE TRIGGER trig DELETE ON tbl BEGIN INSERT INTO new DEFAULT VALUES; UPDATE new SET x = 1; END`, &sql.CreateTriggerStatement{ - Create: pos(0), - Trigger: pos(7), - Name: &sql.Ident{NamePos: pos(15), Name: "trig"}, - Delete: pos(20), - On: pos(27), - Table: &sql.Ident{NamePos: pos(30), Name: "tbl"}, - Begin: pos(34), - Body: []sql.Statement{ - &sql.InsertStatement{ - Insert: pos(40), - Into: pos(47), - Table: &sql.Ident{NamePos: pos(52), Name: "new"}, - Default: pos(56), - DefaultValues: pos(64), - }, - &sql.UpdateStatement{ - Update: pos(72), - Table: &sql.QualifiedTableName{ - Name: &sql.Ident{NamePos: pos(79), Name: "new"}, - }, - Set: pos(83), - Assignments: []*sql.Assignment{{ - Columns: []*sql.Ident{{NamePos: pos(87), Name: "x"}}, - Eq: pos(89), - Expr: &sql.NumberLit{ValuePos: pos(91), Value: "1"}, - }}, - }, - }, - End: pos(94), - }) - AssertParseStatement(t, `CREATE TRIGGER IF NOT EXISTS trig BEFORE INSERT ON tbl BEGIN DELETE FROM new; END`, &sql.CreateTriggerStatement{ - Create: pos(0), - Trigger: pos(7), - If: pos(15), - IfNot: pos(18), - IfNotExists: pos(22), - Name: &sql.Ident{NamePos: pos(29), Name: "trig"}, - Before: pos(34), - Insert: pos(41), - On: pos(48), - Table: &sql.Ident{NamePos: pos(51), Name: "tbl"}, - Begin: pos(55), - Body: []sql.Statement{ - &sql.DeleteStatement{ - Delete: pos(61), - From: pos(68), - Table: &sql.QualifiedTableName{ - Name: &sql.Ident{NamePos: pos(73), Name: "new"}, - }, - }, - }, - End: pos(78), - }) - AssertParseStatement(t, `CREATE TRIGGER trig INSTEAD OF UPDATE ON tbl BEGIN SELECT *; END`, &sql.CreateTriggerStatement{ - Create: pos(0), - Trigger: pos(7), - Name: &sql.Ident{NamePos: pos(15), Name: "trig"}, - Instead: pos(20), - InsteadOf: pos(28), - Update: pos(31), - On: pos(38), - Table: &sql.Ident{NamePos: pos(41), Name: "tbl"}, - Begin: pos(45), - Body: []sql.Statement{ - &sql.SelectStatement{ - Select: pos(51), - Columns: []*sql.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`, &sql.CreateTriggerStatement{ - Create: pos(0), - Trigger: pos(7), - Name: &sql.Ident{NamePos: pos(15), Name: "trig"}, - Instead: pos(20), - InsteadOf: pos(28), - Update: pos(31), - UpdateOf: pos(38), - UpdateOfColumns: []*sql.Ident{ - {NamePos: pos(41), Name: "x"}, - {NamePos: pos(44), Name: "y"}, - }, - On: pos(46), - Table: &sql.Ident{NamePos: pos(49), Name: "tbl"}, - For: pos(53), - ForEach: pos(57), - ForEachRow: pos(62), - When: pos(66), - WhenExpr: &sql.BoolLit{ValuePos: pos(71), Value: true}, - Begin: pos(76), - Body: []sql.Statement{ - &sql.SelectStatement{ - Select: pos(82), - Columns: []*sql.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`, &sql.CreateTriggerStatement{ - Create: pos(0), - Trigger: pos(7), - Name: &sql.Ident{NamePos: pos(15), Name: "trig"}, - After: pos(20), - Update: pos(26), - On: pos(33), - Table: &sql.Ident{NamePos: pos(36), Name: "tbl"}, - Begin: pos(40), - Body: []sql.Statement{ - &sql.SelectStatement{ - WithClause: &sql.WithClause{ - With: pos(46), - CTEs: []*sql.CTE{ - { - TableName: &sql.Ident{NamePos: pos(51), Name: "cte"}, - ColumnsLparen: pos(55), - Columns: []*sql.Ident{ - {NamePos: pos(56), Name: "x"}, - }, - ColumnsRparen: pos(57), - As: pos(59), - SelectLparen: pos(62), - Select: &sql.SelectStatement{ - Select: pos(63), - Columns: []*sql.ResultColumn{ - {Expr: &sql.Ident{NamePos: pos(70), Name: "y"}}, - }, - }, - SelectRparen: pos(71), - }, - }, - }, - Select: pos(73), - Columns: []*sql.ResultColumn{{Star: pos(80)}}, - }, - }, - End: pos(83), - }) - - AssertParseStatementError(t, `CREATE TRIGGER`, `1:14: expected index name, found 'EOF'`) - AssertParseStatementError(t, `CREATE TRIGGER IF`, `1:17: expected NOT, found 'EOF'`) - AssertParseStatementError(t, `CREATE TRIGGER IF NOT`, `1:21: expected EXISTS, found 'EOF'`) - AssertParseStatementError(t, `CREATE TRIGGER trig INSTEAD`, `1:27: expected OF, found 'EOF'`) - AssertParseStatementError(t, `CREATE TRIGGER trig AFTER`, `1:25: expected DELETE, INSERT, or UPDATE, found 'EOF'`) - AssertParseStatementError(t, `CREATE TRIGGER 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("DropTrigger", func(t *testing.T) { - AssertParseStatement(t, `DROP TRIGGER trig`, &sql.DropTriggerStatement{ - Drop: pos(0), - Trigger: pos(5), - Name: &sql.Ident{NamePos: pos(13), Name: "trig"}, - }) - AssertParseStatement(t, `DROP TRIGGER IF EXISTS trig`, &sql.DropTriggerStatement{ - Drop: pos(0), - Trigger: pos(5), - If: pos(13), - IfExists: pos(16), - Name: &sql.Ident{NamePos: pos(23), Name: "trig"}, - }) - AssertParseStatementError(t, `DROP TRIGGER`, `1:12: expected trigger name, found 'EOF'`) - AssertParseStatementError(t, `DROP TRIGGER IF`, `1:15: expected EXISTS, found 'EOF'`) - AssertParseStatementError(t, `DROP TRIGGER IF EXISTS`, `1:22: expected trigger name, found 'EOF'`) - })*/ - t.Run("Select", func(t *testing.T) { AssertParseStatement(t, `SELECT * FROM tbl`, &parser.SelectStatement{ Select: pos(0), @@ -1785,27 +1784,27 @@ func TestParser_ParseStatement(t *testing.T) { Alias: &parser.Ident{NamePos: pos(21), Name: "tbl2"}, }, }) - /*AssertParseStatement(t, `SELECT * FROM tbl INDEXED BY idx`, &sql.SelectStatement{ + /*AssertParseStatement(t, `SELECT * FROM tbl INDEXED BY idx`, &parser.SelectStatement{ Select: pos(0), Columns: []*sql.ResultColumn{ {Star: pos(7)}, }, From: pos(9), - Source: &sql.QualifiedTableName{ - Name: &sql.Ident{NamePos: pos(14), Name: "tbl"}, + Source: &parser.QualifiedTableName{ + Name: &parser.Ident{NamePos: pos(14), Name: "tbl"}, Indexed: pos(18), IndexedBy: pos(26), - Index: &sql.Ident{NamePos: pos(29), Name: "idx"}, + Index: &parser.Ident{NamePos: pos(29), Name: "idx"}, }, })*/ - /*AssertParseStatement(t, `SELECT * FROM tbl NOT INDEXED`, &sql.SelectStatement{ + /*AssertParseStatement(t, `SELECT * FROM tbl NOT INDEXED`, &parser.SelectStatement{ Select: pos(0), Columns: []*sql.ResultColumn{ {Star: pos(7)}, }, From: pos(9), - Source: &sql.QualifiedTableName{ - Name: &sql.Ident{NamePos: pos(14), Name: "tbl"}, + Source: &parser.QualifiedTableName{ + Name: &parser.Ident{NamePos: pos(14), Name: "tbl"}, Not: pos(18), NotIndexed: pos(22), }, @@ -1987,12 +1986,12 @@ func TestParser_ParseStatement(t *testing.T) { }, }) - /*AssertParseStatement(t, `WITH cte (foo, bar) AS (SELECT baz), xxx AS (SELECT yyy) SELECT bat`, &sql.SelectStatement{ - WithClause: &sql.WithClause{ + /*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: &sql.Ident{NamePos: pos(5), Name: "cte"}, + TableName: &parser.Ident{NamePos: pos(5), Name: "cte"}, ColumnsLparen: pos(9), Columns: []*sql.Ident{ {NamePos: pos(10), Name: "foo"}, @@ -2001,22 +2000,22 @@ func TestParser_ParseStatement(t *testing.T) { ColumnsRparen: pos(18), As: pos(20), SelectLparen: pos(23), - Select: &sql.SelectStatement{ + Select: &parser.SelectStatement{ Select: pos(24), Columns: []*sql.ResultColumn{ - {Expr: &sql.Ident{NamePos: pos(31), Name: "baz"}}, + {Expr: &parser.Ident{NamePos: pos(31), Name: "baz"}}, }, }, SelectRparen: pos(34), }, { - TableName: &sql.Ident{NamePos: pos(37), Name: "xxx"}, + TableName: &parser.Ident{NamePos: pos(37), Name: "xxx"}, As: pos(41), SelectLparen: pos(44), - Select: &sql.SelectStatement{ + Select: &parser.SelectStatement{ Select: pos(45), Columns: []*sql.ResultColumn{ - {Expr: &sql.Ident{NamePos: pos(52), Name: "yyy"}}, + {Expr: &parser.Ident{NamePos: pos(52), Name: "yyy"}}, }, }, SelectRparen: pos(55), @@ -2025,22 +2024,22 @@ func TestParser_ParseStatement(t *testing.T) { }, Select: pos(57), Columns: []*sql.ResultColumn{ - {Expr: &sql.Ident{NamePos: pos(64), Name: "bat"}}, + {Expr: &parser.Ident{NamePos: pos(64), Name: "bat"}}, }, })*/ - /*AssertParseStatement(t, `WITH RECURSIVE cte AS (SELECT foo) SELECT bar`, &sql.SelectStatement{ - WithClause: &sql.WithClause{ + /*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: &sql.Ident{NamePos: pos(15), Name: "cte"}, + TableName: &parser.Ident{NamePos: pos(15), Name: "cte"}, As: pos(19), SelectLparen: pos(22), - Select: &sql.SelectStatement{ + Select: &parser.SelectStatement{ Select: pos(23), Columns: []*sql.ResultColumn{ - {Expr: &sql.Ident{NamePos: pos(30), Name: "foo"}}, + {Expr: &parser.Ident{NamePos: pos(30), Name: "foo"}}, }, }, SelectRparen: pos(33), @@ -2049,7 +2048,7 @@ func TestParser_ParseStatement(t *testing.T) { }, Select: pos(35), Columns: []*sql.ResultColumn{ - {Expr: &sql.Ident{NamePos: pos(42), Name: "bar"}}, + {Expr: &parser.Ident{NamePos: pos(42), Name: "bar"}}, }, })*/ @@ -2177,22 +2176,22 @@ func TestParser_ParseStatement(t *testing.T) { }, }) - /*AssertParseStatement(t, `VALUES (1, 2), (3, 4)`, &sql.SelectStatement{ + /*AssertParseStatement(t, `VALUES (1, 2), (3, 4)`, &parser.SelectStatement{ Values: pos(0), ValueLists: []*sql.ExprList{ { Lparen: pos(7), Exprs: []sql.Expr{ - &sql.NumberLit{ValuePos: pos(8), Value: "1"}, - &sql.NumberLit{ValuePos: pos(11), Value: "2"}, + &parser.NumberLit{ValuePos: pos(8), Value: "1"}, + &parser.NumberLit{ValuePos: pos(11), Value: "2"}, }, Rparen: pos(12), }, { Lparen: pos(15), Exprs: []sql.Expr{ - &sql.NumberLit{ValuePos: pos(16), Value: "3"}, - &sql.NumberLit{ValuePos: pos(19), Value: "4"}, + &parser.NumberLit{ValuePos: pos(16), Value: "3"}, + &parser.NumberLit{ValuePos: pos(19), Value: "4"}, }, Rparen: pos(20), }, @@ -2436,11 +2435,11 @@ func TestParser_ParseStatement(t *testing.T) { }}, })*/ - /*AssertParseStatement(t, `WITH cte (foo) AS (SELECT bar) INSERT INTO tbl VALUES (1)`, &sql.InsertStatement{ - WithClause: &sql.WithClause{ + /*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: &sql.Ident{NamePos: pos(5), Name: "cte"}, + TableName: &parser.Ident{NamePos: pos(5), Name: "cte"}, ColumnsLparen: pos(9), Columns: []*sql.Ident{ {NamePos: pos(10), Name: "foo"}, @@ -2448,10 +2447,10 @@ func TestParser_ParseStatement(t *testing.T) { ColumnsRparen: pos(13), As: pos(15), SelectLparen: pos(18), - Select: &sql.SelectStatement{ + Select: &parser.SelectStatement{ Select: pos(19), Columns: []*sql.ResultColumn{ - {Expr: &sql.Ident{NamePos: pos(26), Name: "bar"}}, + {Expr: &parser.Ident{NamePos: pos(26), Name: "bar"}}, }, }, SelectRparen: pos(29), @@ -2459,21 +2458,21 @@ func TestParser_ParseStatement(t *testing.T) { }, Insert: pos(31), Into: pos(38), - Table: &sql.Ident{NamePos: pos(43), Name: "tbl"}, + Table: &parser.Ident{NamePos: pos(43), Name: "tbl"}, Values: pos(47), ValueLists: []*sql.ExprList{{ Lparen: pos(54), Exprs: []sql.Expr{ - &sql.NumberLit{ValuePos: pos(55), Value: "1"}, + &parser.NumberLit{ValuePos: pos(55), Value: "1"}, }, Rparen: pos(56), }}, })*/ - /*AssertParseStatement(t, `WITH cte (foo) AS (SELECT bar) INSERT INTO tbl VALUES (1)`, &sql.InsertStatement{ - WithClause: &sql.WithClause{ + /*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: &sql.Ident{NamePos: pos(5), Name: "cte"}, + TableName: &parser.Ident{NamePos: pos(5), Name: "cte"}, ColumnsLparen: pos(9), Columns: []*sql.Ident{ {NamePos: pos(10), Name: "foo"}, @@ -2481,10 +2480,10 @@ func TestParser_ParseStatement(t *testing.T) { ColumnsRparen: pos(13), As: pos(15), SelectLparen: pos(18), - Select: &sql.SelectStatement{ + Select: &parser.SelectStatement{ Select: pos(19), Columns: []*sql.ResultColumn{ - {Expr: &sql.Ident{NamePos: pos(26), Name: "bar"}}, + {Expr: &parser.Ident{NamePos: pos(26), Name: "bar"}}, }, }, SelectRparen: pos(29), @@ -2492,12 +2491,12 @@ func TestParser_ParseStatement(t *testing.T) { }, Insert: pos(31), Into: pos(38), - Table: &sql.Ident{NamePos: pos(43), Name: "tbl"}, + Table: &parser.Ident{NamePos: pos(43), Name: "tbl"}, Values: pos(47), ValueLists: []*sql.ExprList{{ Lparen: pos(54), Exprs: []sql.Expr{ - &sql.NumberLit{ValuePos: pos(55), Value: "1"}, + &parser.NumberLit{ValuePos: pos(55), Value: "1"}, }, Rparen: pos(56), }}, @@ -2752,12 +2751,12 @@ func TestParser_ParseStatement(t *testing.T) { Expr: &parser.IntegerLit{ValuePos: pos(29), Value: "1"}, }}, }) - /*AssertParseStatement(t, `WITH cte (x) AS (SELECT y) UPDATE tbl SET x = 1`, &sql.UpdateStatement{ - WithClause: &sql.WithClause{ + /*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: &sql.Ident{NamePos: pos(5), Name: "cte"}, + TableName: &parser.Ident{NamePos: pos(5), Name: "cte"}, ColumnsLparen: pos(9), Columns: []*sql.Ident{ {NamePos: pos(10), Name: "x"}, @@ -2765,10 +2764,10 @@ func TestParser_ParseStatement(t *testing.T) { ColumnsRparen: pos(11), As: pos(13), SelectLparen: pos(16), - Select: &sql.SelectStatement{ + Select: &parser.SelectStatement{ Select: pos(17), Columns: []*sql.ResultColumn{ - {Expr: &sql.Ident{NamePos: pos(24), Name: "y"}}, + {Expr: &parser.Ident{NamePos: pos(24), Name: "y"}}, }, }, SelectRparen: pos(25), @@ -2776,14 +2775,14 @@ func TestParser_ParseStatement(t *testing.T) { }, }, Update: pos(27), - Table: &sql.QualifiedTableName{ - Name: &sql.Ident{NamePos: pos(34), Name: "tbl"}, + 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: &sql.NumberLit{ValuePos: pos(46), Value: "1"}, + Expr: &parser.NumberLit{ValuePos: pos(46), Value: "1"}, }}, })*/ @@ -2817,12 +2816,12 @@ func TestParser_ParseStatement(t *testing.T) { Y: &parser.IntegerLit{ValuePos: pos(26), Value: "1"}, }, }) - /*AssertParseStatement(t, `WITH cte (x) AS (SELECT y) DELETE FROM tbl`, &sql.DeleteStatement{ - WithClause: &sql.WithClause{ + /*AssertParseStatement(t, `WITH cte (x) AS (SELECT y) DELETE FROM tbl`, &parser.DeleteStatement{ + WithClause: &parser.WithClause{ With: pos(0), CTEs: []*sql.CTE{ { - TableName: &sql.Ident{NamePos: pos(5), Name: "cte"}, + TableName: &parser.Ident{NamePos: pos(5), Name: "cte"}, ColumnsLparen: pos(9), Columns: []*sql.Ident{ {NamePos: pos(10), Name: "x"}, @@ -2830,10 +2829,10 @@ func TestParser_ParseStatement(t *testing.T) { ColumnsRparen: pos(11), As: pos(13), SelectLparen: pos(16), - Select: &sql.SelectStatement{ + Select: &parser.SelectStatement{ Select: pos(17), Columns: []*sql.ResultColumn{ - {Expr: &sql.Ident{NamePos: pos(24), Name: "y"}}, + {Expr: &parser.Ident{NamePos: pos(24), Name: "y"}}, }, }, SelectRparen: pos(25), @@ -2842,8 +2841,8 @@ func TestParser_ParseStatement(t *testing.T) { }, Delete: pos(27), From: pos(34), - Table: &sql.QualifiedTableName{ - Name: &sql.Ident{NamePos: pos(39), Name: "tbl"}, + 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{ @@ -3540,34 +3539,34 @@ func TestParser_ParseExpr(t *testing.T) { /*t.Run("Raise", func(t *testing.T) { - AssertParseExpr(t, `RAISE(IGNORE)`, &sql.Raise{ + AssertParseExpr(t, `RAISE(IGNORE)`, &parser.Raise{ Raise: pos(0), Lparen: pos(5), Ignore: pos(6), Rparen: pos(12), }) - AssertParseExpr(t, `RAISE(ROLLBACK, 'bad error')`, &sql.Raise{ + AssertParseExpr(t, `RAISE(ROLLBACK, 'bad error')`, &parser.Raise{ Raise: pos(0), Lparen: pos(5), Rollback: pos(6), Comma: pos(14), - Error: &sql.StringLit{ValuePos: pos(16), Value: "bad error"}, + Error: &parser.StringLit{ValuePos: pos(16), Value: "bad error"}, Rparen: pos(27), }) - AssertParseExpr(t, `RAISE(ABORT, 'error')`, &sql.Raise{ + AssertParseExpr(t, `RAISE(ABORT, 'error')`, &parser.Raise{ Raise: pos(0), Lparen: pos(5), Abort: pos(6), Comma: pos(11), - Error: &sql.StringLit{ValuePos: pos(13), Value: "error"}, + Error: &parser.StringLit{ValuePos: pos(13), Value: "error"}, Rparen: pos(20), }) - AssertParseExpr(t, `RAISE(FAIL, 'error')`, &sql.Raise{ + AssertParseExpr(t, `RAISE(FAIL, 'error')`, &parser.Raise{ Raise: pos(0), Lparen: pos(5), Fail: pos(6), Comma: pos(10), - Error: &sql.StringLit{ValuePos: pos(12), Value: "error"}, + Error: &parser.StringLit{ValuePos: pos(12), Value: "error"}, Rparen: pos(19), }) AssertParseExprError(t, `RAISE`, `1:5: expected left paren, found 'EOF'`) diff --git a/sql3/parser/token.go b/sql3/parser/token.go index 35b64c949..d3b11494b 100644 --- a/sql3/parser/token.go +++ b/sql3/parser/token.go @@ -205,6 +205,8 @@ const ( RENAME REPLACE RESTRICT + RETURNS + RETURN ROLLBACK ROW ROWS @@ -426,6 +428,8 @@ var tokens = [...]string{ RENAME: "RENAME", REPLACE: "REPLACE", RESTRICT: "RESTRICT", + RETURNS: "RETURNS", + RETURN: "RETURN", ROLLBACK: "ROLLBACK", ROW: "ROW", ROWS: "ROWS", diff --git a/sql3/parser/walk.go b/sql3/parser/walk.go index 8433961c1..1607fd10c 100644 --- a/sql3/parser/walk.go +++ b/sql3/parser/walk.go @@ -142,7 +142,7 @@ func walk(v Visitor, node Node) (_ Node, err error) { return node, err } - case *DropTriggerStatement: + case *DropFunctionStatement: if err := walkIdent(v, &n.Name); err != nil { return node, err } @@ -161,19 +161,13 @@ func walk(v Visitor, node Node) (_ Node, err error) { return node, err } - case *CreateTriggerStatement: + case *CreateFunctionStatement: if err := walkIdent(v, &n.Name); err != nil { return node, err } - if err := walkIdentList(v, n.UpdateOfColumns); err != nil { - return node, err - } - if err := walkIdent(v, &n.Table); err != nil { - return node, err - } - if err := walkExpr(v, &n.WhenExpr); err != nil { - return node, err - } + // if err := walkIdentList(v, n.UpdateOfColumns); err != nil { + // return node, err + // } for i := range n.Body { if body, err := walk(v, n.Body[i]); err != nil { return node, err diff --git a/sql3/planner/expression.go b/sql3/planner/expression.go index a5e472123..0a66de567 100644 --- a/sql3/planner/expression.go +++ b/sql3/planner/expression.go @@ -1808,13 +1808,7 @@ func newFloatLiteralPlanExpression(value string) *floatLiteralPlanExpression { } func (n *floatLiteralPlanExpression) Evaluate(currentRow []interface{}) (interface{}, error) { - scale := parser.NumDecimalPlaces(n.value) - fvalue, err := strconv.ParseFloat(n.value, 64) - if err != nil { - return nil, err - } - unscaledValue := int64(fvalue * math.Pow(10, float64(scale))) - return pql.NewDecimal(unscaledValue, int64(scale)), nil + return pql.ParseDecimal(n.value) } func (n *floatLiteralPlanExpression) Type() parser.ExprDataType { @@ -2081,14 +2075,11 @@ func (n *castPlanExpression) Evaluate(currentRow []interface{}) (interface{}, er return i, nil case *parser.DataTypeDecimal: - fvalue, err := strconv.ParseFloat(nl, 64) + castValue, err := pql.ParseDecimal(nl) if err != nil { //TODO(pok) need to push location into here return nil, sql3.NewErrInvalidCast(0, 0, nl, n.targetType.TypeDescription()) } - scale := parser.NumDecimalPlaces(nl) - unscaledValue := int64(fvalue * math.Pow(10, float64(scale))) - castValue := pql.NewDecimal(unscaledValue, int64(scale)) if tt.Scale < castValue.Scale { return nil, sql3.NewErrInvalidCast(0, 0, nl, n.targetType.TypeDescription()) } @@ -2500,7 +2491,7 @@ func (p *ExecutionPlanner) compileExpr(expr parser.Expr) (_ types.PlanExpression case *parser.ParenExpr: return p.compileExpr(expr.X) - case *parser.VariableRef: + case *parser.Variable: ref := newVariableRefPlanExpression(expr.Name, expr.VariableIndex, expr.DataType()) return ref, nil diff --git a/sql3/planner/expressionagg.go b/sql3/planner/expressionagg.go index 744a6e2f0..8d827df10 100644 --- a/sql3/planner/expressionagg.go +++ b/sql3/planner/expressionagg.go @@ -5,7 +5,6 @@ package planner import ( "context" "fmt" - "reflect" "github.com/featurebasedb/featurebase/v3/pql" "github.com/featurebasedb/featurebase/v3/sql3" @@ -346,19 +345,17 @@ func (n *sumPlanExpression) WithChildren(children ...types.PlanExpression) (type return newSumPlanExpression(children[0], n.returnDataType), nil } -// aggregator for AVG +// aggregator for AVG() type aggregateAvg struct { - sum float64 + sum interface{} rows int64 expr types.PlanExpression } func NewAggAvgBuffer(child types.PlanExpression) *aggregateAvg { - const ( - sum = float64(0) - rows = int64(0) - ) - return &aggregateAvg{sum, rows, child} + return &aggregateAvg{ + expr: child, + } } func (a *aggregateAvg) Update(ctx context.Context, row types.Row) error { @@ -370,24 +367,87 @@ func (a *aggregateAvg) Update(ctx context.Context, row types.Row) error { if v == nil { return nil } - a.sum += v.(float64) + + aggExpr, ok := a.expr.(*avgPlanExpression) + if !ok { + return sql3.NewErrInternalf("unexpected aggregate expression type '%T'", a.expr) + } + + // we're going to do the sum in the return type + switch returnType := aggExpr.returnDataType.(type) { + case *parser.DataTypeDecimal: + + // get the current agg value + var ok bool + var aggVal pql.Decimal + if a.sum == nil { + aggVal = pql.NewDecimal(0, returnType.Scale) + } else { + aggVal, ok = a.sum.(pql.Decimal) + if !ok { + return sql3.NewErrInternalf("unexpected type conversion '%T'", v) + } + } + + switch dataType := aggExpr.arg.Type().(type) { + case *parser.DataTypeDecimal: + thisVal, ok := v.(pql.Decimal) + if !ok { + return sql3.NewErrInternalf("unexpected type conversion '%T'", v) + } + + a.sum = pql.AddDecimal(thisVal, aggVal) + + case *parser.DataTypeInt: + thisIVal, ok := v.(int64) + if !ok { + return sql3.NewErrInternalf("unexpected type conversion '%T'", v) + } + + thisVal := pql.FromInt64(thisIVal, returnType.Scale) + a.sum = pql.AddDecimal(thisVal, aggVal) + + default: + return sql3.NewErrInternalf("unhandled aggregate expression datatype '%T'", dataType) + } + default: + return sql3.NewErrInternalf("unhandled aggregate expression datatype '%T'", returnType) + } a.rows += 1 - //return nil - return sql3.NewErrInternalf("implement me") + return nil } func (a *aggregateAvg) Eval(ctx context.Context) (interface{}, error) { - // This case is triggered when no rows exist. - if a.sum == 0 && a.rows == 0 { + // bail if we have no aggregate at all + if a.sum == nil && a.rows == 0 { return nil, nil } - if a.rows == 0 { - return float64(0), nil + aggExpr, ok := a.expr.(*avgPlanExpression) + if !ok { + return nil, sql3.NewErrInternalf("unexpected aggregate expression type '%T'", a.expr) + } + + switch returnType := aggExpr.returnDataType.(type) { + case *parser.DataTypeDecimal: + + // if no rows, average is 0 + if a.rows == 0 { + return pql.NewDecimal(0, returnType.Scale), nil + } + count := pql.FromInt64(a.rows, returnType.Scale) + + sum, ok := a.sum.(pql.Decimal) + if !ok { + return nil, sql3.NewErrInternalf("unexpected type conversion '%T'", a.sum) + } + return pql.DivideDecimal(sum, count), nil + + default: + return nil, sql3.NewErrInternalf("unhandled aggregate expression datatype '%T'", returnType) } - return a.sum / float64(a.rows), nil } // avgPlanExpression handles AVG() @@ -458,37 +518,76 @@ func (n *avgPlanExpression) WithChildren(children ...types.PlanExpression) (type return newAvgPlanExpression(children[0], n.returnDataType), nil } -// aggregator for MIN -type aggreagateMin struct { +// aggregator for MIN() +type aggregateMin struct { val interface{} expr types.PlanExpression } -func NewAggMinBuffer(child types.PlanExpression) *aggreagateMin { - return &aggreagateMin{nil, child} +func NewAggMinBuffer(child types.PlanExpression) *aggregateMin { + return &aggregateMin{nil, child} } -func (m *aggreagateMin) Update(ctx context.Context, row types.Row) error { +func (m *aggregateMin) Update(ctx context.Context, row types.Row) error { v, err := m.expr.Evaluate(row) if err != nil { return err } - if reflect.TypeOf(v) == nil { + // skip if nil + if v == nil { return nil } + // if we have no min, then set the value if m.val == nil { m.val = v return nil } - //return nil - return sql3.NewErrInternalf("implement me") + aggExpr, ok := m.expr.(*minPlanExpression) + if !ok { + return sql3.NewErrInternalf("unexpected aggregate expression type '%T'", m.expr) + } + switch dataType := aggExpr.arg.Type().(type) { + case *parser.DataTypeDecimal: + thisVal, ok := v.(pql.Decimal) + if !ok { + return sql3.NewErrInternalf("unexpected type conversion '%T'", v) + } + + aggVal, ok := m.val.(pql.Decimal) + if !ok { + return sql3.NewErrInternalf("unexpected type conversion '%T'", v) + } + + if thisVal.LessThan(aggVal) { + m.val = thisVal + } + + case *parser.DataTypeInt: + thisVal, ok := v.(int64) + if !ok { + return sql3.NewErrInternalf("unexpected type conversion '%T'", v) + } + + aggVal, ok := m.val.(int64) + if !ok { + return sql3.NewErrInternalf("unexpected type conversion '%T'", v) + } + + if thisVal < aggVal { + m.val = thisVal + } + + default: + return sql3.NewErrInternalf("unhandled aggregate expression datatype '%T'", dataType) + } + return nil } -func (m *aggreagateMin) Eval(ctx context.Context) (interface{}, error) { +func (m *aggregateMin) Eval(ctx context.Context) (interface{}, error) { return m.val, nil } @@ -560,7 +659,7 @@ func (n *minPlanExpression) WithChildren(children ...types.PlanExpression) (type return newMinPlanExpression(children[0], n.returnDataType), nil } -// aggregator for MAX +// aggregator for MAX() type aggregateMax struct { val interface{} expr types.PlanExpression @@ -576,18 +675,58 @@ func (m *aggregateMax) Update(ctx context.Context, row types.Row) error { return err } - if reflect.TypeOf(v) == nil { + // skip if nil + if v == nil { return nil } + // if we have no min, then set the value if m.val == nil { m.val = v return nil } - //return nil - return sql3.NewErrInternalf("implement me") + aggExpr, ok := m.expr.(*maxPlanExpression) + if !ok { + return sql3.NewErrInternalf("unexpected aggregate expression type '%T'", m.expr) + } + switch dataType := aggExpr.arg.Type().(type) { + case *parser.DataTypeDecimal: + thisVal, ok := v.(pql.Decimal) + if !ok { + return sql3.NewErrInternalf("unexpected type conversion '%T'", v) + } + + aggVal, ok := m.val.(pql.Decimal) + if !ok { + return sql3.NewErrInternalf("unexpected type conversion '%T'", v) + } + + if thisVal.GreaterThan(aggVal) { + m.val = thisVal + } + + case *parser.DataTypeInt: + thisVal, ok := v.(int64) + if !ok { + return sql3.NewErrInternalf("unexpected type conversion '%T'", v) + } + + aggVal, ok := m.val.(int64) + if !ok { + return sql3.NewErrInternalf("unexpected type conversion '%T'", v) + } + + if thisVal > aggVal { + m.val = thisVal + } + + default: + return sql3.NewErrInternalf("unhandled aggregate expression datatype '%T'", dataType) + } + + return nil } func (m *aggregateMax) Eval(ctx context.Context) (interface{}, error) { @@ -736,7 +875,7 @@ func (n *percentilePlanExpression) WithChildren(children ...types.PlanExpression return newPercentilePlanExpression(children[0], children[1], n.returnDataType), nil } -// aggregator for last +// aggregator for LAST() type aggregateLast struct { val interface{} expr types.PlanExpression @@ -764,7 +903,7 @@ func (l *aggregateLast) Eval(ctx context.Context) (interface{}, error) { return l.val, nil } -// aggregator for first +// aggregator for FIRST() type aggregateFirst struct { val interface{} expr types.PlanExpression diff --git a/sql3/planner/expressionanalyzer.go b/sql3/planner/expressionanalyzer.go index a47711ee1..387aee5a2 100644 --- a/sql3/planner/expressionanalyzer.go +++ b/sql3/planner/expressionanalyzer.go @@ -89,7 +89,7 @@ func (p *ExecutionPlanner) analyzeExpression(expr parser.Expr, scope parser.Stat return nil, sql3.NewErrInternalf("unhandled scope type '%T'", sc) } - case *parser.VariableRef: + case *parser.Variable: switch sc := scope.(type) { case *parser.BulkInsertStatement: // get the name of the variable without the @ diff --git a/sql3/planner/opbulkinsert.go b/sql3/planner/opbulkinsert.go index aa5f00d67..ac295d564 100644 --- a/sql3/planner/opbulkinsert.go +++ b/sql3/planner/opbulkinsert.go @@ -310,7 +310,7 @@ func (i *bulkInsertSourceCSVRowIter) Next(ctx context.Context) (types.Row, error result[idx] = bval case *parser.DataTypeDecimal: - dval, err := parser.StringToDecimal(evalValue) + dval, err := pql.ParseDecimal(evalValue) if err != nil { return nil, sql3.NewErrTypeConversionOnMap(0, 0, evalValue, mapColumn.colType.TypeName()) } @@ -658,7 +658,7 @@ func (i *bulkInsertSourceNDJsonRowIter) Next(ctx context.Context) (types.Row, er case *parser.DataTypeDecimal: switch v := evalValue.(type) { case float64: - result[idx] = parser.FloatToDecimal(v) + result[idx] = pql.FromFloat64(v) case []interface{}: return nil, sql3.NewErrTypeConversionOnMap(0, 0, v, mapColumn.colType.TypeName()) diff --git a/sql3/planner/opquery.go b/sql3/planner/opquery.go index 4a07fdf31..8a7165c82 100644 --- a/sql3/planner/opquery.go +++ b/sql3/planner/opquery.go @@ -123,7 +123,6 @@ func newQueryIterator(requests pilosa.ExecutionRequestsAPI, query *PlanOpQuery, func (i *queryIterator) Next(ctx context.Context) (types.Row, error) { if i.hasStarted == nil { - i.hasStarted = &struct{}{} requestId, ok := fbcontext.RequestID(ctx) if !ok { @@ -134,6 +133,7 @@ func (i *queryIterator) Next(ctx context.Context) (types.Row, error) { userId, _ = fbcontext.UserID(ctx) i.requests.AddRequest(requestId, userId, time.Now(), i.query.sql) + i.hasStarted = &struct{}{} } row, err := i.child.Next(ctx) diff --git a/sql3/planner/optop.go b/sql3/planner/optop.go index 3c5fcbd38..a87e0355a 100644 --- a/sql3/planner/optop.go +++ b/sql3/planner/optop.go @@ -30,8 +30,11 @@ func (p *PlanOpTop) Schema() types.Schema { } func (p *PlanOpTop) Iterator(ctx context.Context, row types.Row) (types.RowIterator, error) { - // TODO (pok) actually implement top - return p.ChildOp.Iterator(ctx, row) + iter, err := p.ChildOp.Iterator(ctx, row) + if err != nil { + return nil, err + } + return newTopIter(p.expr, iter), nil } func (p *PlanOpTop) Children() []types.PlanOperator { @@ -75,3 +78,47 @@ func (p *PlanOpTop) Warnings() []string { w = append(w, p.ChildOp.Warnings()...) return w } + +type topIter struct { + child types.RowIterator + expr types.PlanExpression + + rowCount int64 + topValue int64 + + hasStarted *struct{} +} + +func newTopIter(expr types.PlanExpression, child types.RowIterator) *topIter { + return &topIter{ + child: child, + expr: expr, + } +} + +func (i *topIter) Next(ctx context.Context) (types.Row, error) { + if i.hasStarted == nil { + topEval, err := i.expr.Evaluate(nil) + if err != nil { + return nil, err + } + var ok bool + i.topValue, ok = topEval.(int64) + if !ok { + return nil, sql3.NewErrInternalf("unexpected top expression result type %T", topEval) + } + i.hasStarted = &struct{}{} + + } + + if i.rowCount >= i.topValue { + return nil, types.ErrNoMoreRows + } + + row, err := i.child.Next(ctx) + if err != nil { + return nil, err + } + i.rowCount++ + return row, nil +} diff --git a/sql3/sql_complex_test.go b/sql3/sql_complex_test.go index 7fea27c57..3b12e6054 100644 --- a/sql3/sql_complex_test.go +++ b/sql3/sql_complex_test.go @@ -19,7 +19,6 @@ import ( pilosa "github.com/molecula/featurebase/v3" "github.com/molecula/featurebase/v3/dax" "github.com/molecula/featurebase/v3/pql" - "github.com/molecula/featurebase/v3/sql3/parser" sql_test "github.com/molecula/featurebase/v3/sql3/test" "github.com/molecula/featurebase/v3/test" "github.com/stretchr/testify/assert" @@ -27,14 +26,14 @@ import ( func TestPlanner_Misc(t *testing.T) { - d, err := parser.StringToDecimal("12.345678") + d, err := pql.ParseDecimal("12.345678") if err != nil { t.Fatal(err) } assert.True(t, d.EqualTo(pql.NewDecimal(12345678, 6))) - d = parser.FloatToDecimalWithScale(12.345678, 6) + d, err = pql.FromFloat64WithScale(12.345678, 6) if err != nil { t.Fatal(err) } @@ -136,6 +135,30 @@ func TestPlanner_Show(t *testing.T) { } }) + t.Run("SystemTablesExecRequestsAgg", func(t *testing.T) { + _, columns, err := sql_test.MustQueryRows(t, c.GetNode(0).Server, `select + count(request_id) as request_count, + min(elapsed_time) as min_duration, + max(elapsed_time) as max_duration, + avg(elapsed_time) as avg_duration + from + fb_exec_requests + where + status = 'complete';`) + if err != nil { + t.Fatal(err) + } + + if diff := cmp.Diff([]*pilosa.WireQueryField{ + wireQueryFieldInt("request_count"), + wireQueryFieldInt("min_duration"), + wireQueryFieldInt("max_duration"), + wireQueryFieldDecimal("avg_duration", 4), + }, columns); diff != "" { + t.Fatal(diff) + } + }) + t.Run("ShowTables", func(t *testing.T) { results, columns, err := sql_test.MustQueryRows(t, c.GetNode(0).Server, `SHOW TABLES`) if err != nil {