surrealpatch/sql/sql_test.go
2021-12-14 08:13:19 +00:00

3313 lines
83 KiB
Go

// Copyright © 2016 SurrealDB Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package sql
import (
"fmt"
"testing"
"time"
. "github.com/smartystreets/goconvey/convey"
)
type tester struct {
skip bool
sql string
err string
str string
res Statement
}
func testerr(err error) string {
if err != nil {
return err.Error()
}
return ""
}
type Selfer interface{}
func testsql(t *testing.T, test tester) {
if test.skip {
Convey(" ❗️ "+test.sql, t, nil)
return
}
s, e := Parse(test.sql)
Convey(test.sql, t, func() {
if test.err != "" {
Convey(testerr(e), func() {
So(testerr(e), ShouldResemble, test.err)
})
}
if test.err == "" {
So(e, ShouldBeNil)
So(s, ShouldResemble, test.res)
if test.str != "" {
So(fmt.Sprint(test.res.(*Query).Statements[0]), ShouldEqual, test.str)
} else {
So(fmt.Sprint(test.res.(*Query).Statements[0]), ShouldEqual, test.sql)
}
}
})
}
func TestMain(t *testing.T) {
var tests = []tester{
{
sql: `USE`,
err: "Found `` but expected `NAMESPACE, DATABASE, NS, DB`",
},
{
sql: `USE NAMESPACE`,
err: "Found `` but expected `IDENT, STRING, NUMBER, DOUBLE, DATE, TIME`",
},
{
sql: `USE NAMESPACE ''`,
err: "Found `` but expected `namespace name`",
},
{
sql: `USE NAMESPACE name`,
res: &Query{Statements: []Statement{&UseStatement{
NS: "name",
}}},
},
{
sql: `USE NAMESPACE 1`,
res: &Query{Statements: []Statement{&UseStatement{
NS: "1",
}}},
},
{
sql: `USE NAMESPACE 1.3000`,
res: &Query{Statements: []Statement{&UseStatement{
NS: "1.3000",
}}},
},
{
sql: `USE NAMESPACE 123.123.123.123`,
res: &Query{Statements: []Statement{&UseStatement{
NS: "123.123.123.123",
}}},
},
{
sql: `USE NAMESPACE {"some":"thing"}`,
err: "Found `{\"some\":\"thing\"}` but expected `IDENT, STRING, NUMBER, DOUBLE, DATE, TIME`",
},
{
sql: `USE NAMESPACE name something`,
err: "Found `something` but expected `;`",
},
{
sql: `USE DATABASE`,
err: "Found `` but expected `IDENT, STRING, NUMBER, DOUBLE, DATE, TIME`",
},
{
sql: `USE DATABASE ''`,
err: "Found `` but expected `database name`",
},
{
sql: `USE DATABASE name`,
res: &Query{Statements: []Statement{&UseStatement{
DB: "name",
}}},
},
{
sql: `USE DATABASE 1`,
res: &Query{Statements: []Statement{&UseStatement{
DB: "1",
}}},
},
{
sql: `USE DATABASE 1.3000`,
res: &Query{Statements: []Statement{&UseStatement{
DB: "1.3000",
}}},
},
{
sql: `USE DATABASE 123.123.123.123`,
res: &Query{Statements: []Statement{&UseStatement{
DB: "123.123.123.123",
}}},
},
{
sql: `USE DATABASE {}`,
err: "Found `{}` but expected `IDENT, STRING, NUMBER, DOUBLE, DATE, TIME`",
},
{
sql: `USE DATABASE name something`,
err: "Found `something` but expected `;`",
},
{
sql: "USE NS `*` DB `*`",
str: "USE NAMESPACE `*` DATABASE `*`",
res: &Query{Statements: []Statement{&UseStatement{
NS: "*",
DB: "*",
}}},
},
{
sql: "USE NAMESPACE `*` DATABASE `*`",
res: &Query{Statements: []Statement{&UseStatement{
NS: "*",
DB: "*",
}}},
},
}
for _, test := range tests {
testsql(t, test)
}
}
func Test_Parse_IDs(t *testing.T) {
Convey("All IDs should parse correctly", t, func() {
So(ParseThing("test:tester"), ShouldResemble, &Thing{
TB: "test",
ID: string("tester"),
})
So(ParseThing("test:123456"), ShouldResemble, &Thing{
TB: "test",
ID: float64(123456),
})
So(ParseThing("test:abcdef"), ShouldResemble, &Thing{
TB: "test",
ID: string("abcdef"),
})
So(ParseThing("test:a1b2c3"), ShouldResemble, &Thing{
TB: "test",
ID: string("a1b2c3"),
})
So(ParseThing("test:1a2b3c"), ShouldResemble, &Thing{
TB: "test",
ID: string("1a2b3c"),
})
So(ParseThing("test:4m9sms"), ShouldResemble, &Thing{
TB: "test",
ID: string("4m9sms"),
})
So(ParseThing("test:4m98m85ms"), ShouldResemble, &Thing{
TB: "test",
ID: string("4m98m85ms"),
})
So(ParseThing("test:00s5w36sm"), ShouldResemble, &Thing{
TB: "test",
ID: string("00s5w36sm"),
})
})
}
// Ensure the parser can parse a multi-statement query.
func Test_Parse_General(t *testing.T) {
s := `SELECT a FROM b`
q, err := Parse(s)
if err != nil {
t.Fatalf("unexpected error: %s", err)
} else if len(q.Statements) != 1 {
t.Fatalf("unexpected statement count: %d", len(q.Statements))
}
}
// Ensure the parser can parse a multi-statement query.
func Test_Parse_General_Single(t *testing.T) {
s := `SELECT a FROM b`
q, err := Parse(s)
if err != nil {
t.Fatalf("unexpected error: %s", err)
} else if len(q.Statements) != 1 {
t.Fatalf("unexpected statement count: %d", len(q.Statements))
}
}
// Ensure the parser can parse a multi-statement query.
func Test_Parse_General_Multi(t *testing.T) {
s := `SELECT a FROM b; SELECT c FROM d`
q, err := Parse(s)
if err != nil {
t.Fatalf("unexpected error: %s", err)
} else if len(q.Statements) != 2 {
t.Fatalf("unexpected statement count: %d", len(q.Statements))
}
}
func Test_Parse_Queries_Malformed(t *testing.T) {
var tests = []tester{
{
sql: ``,
err: "Your SQL query is empty",
},
{
sql: "SELECT ` FROM person",
err: "Found ` FROM person` but expected `expression`",
},
{
sql: `SELECT ' FROM person`,
err: "Found ` FROM person` but expected `expression`",
},
{
sql: `SELECT " FROM person`,
err: "Found ` FROM person` but expected `expression`",
},
{
sql: `SELECT "\" FROM person`,
err: "Found `\" FROM person` but expected `expression`",
},
{
sql: `!`,
err: "Found `!` but expected `USE, INFO, BEGIN, CANCEL, COMMIT, IF, LET, RETURN, LIVE, KILL, SELECT, CREATE, UPDATE, DELETE, RELATE, INSERT, UPSERT, DEFINE, REMOVE, OPTION`",
},
}
for _, test := range tests {
testsql(t, test)
}
}
func Test_Parse_Queries_Info(t *testing.T) {
var tests = []tester{
{
sql: `INFO`,
err: "Found `` but expected `FOR`",
},
{
sql: `INFO FOR`,
err: "Found `` but expected `ALL, NAMESPACE, DATABASE, SCOPE, TABLE, NS, DB`",
},
{
sql: `INFO FOR ALL`,
res: &Query{Statements: []Statement{&InfoStatement{
Kind: ALL,
}}},
},
{
sql: `INFO FOR NAMESPACE`,
res: &Query{Statements: []Statement{&InfoStatement{
Kind: NAMESPACE,
}}},
},
{
sql: `INFO FOR NS`,
str: `INFO FOR NAMESPACE`,
res: &Query{Statements: []Statement{&InfoStatement{
Kind: NS,
}}},
},
{
sql: `INFO FOR DATABASE`,
res: &Query{Statements: []Statement{&InfoStatement{
Kind: DATABASE,
}}},
},
{
sql: `INFO FOR DB`,
str: `INFO FOR DATABASE`,
res: &Query{Statements: []Statement{&InfoStatement{
Kind: DB,
}}},
},
{
sql: `INFO FOR SCOPE`,
err: "Found `` but expected `name`",
},
{
sql: `INFO FOR SCOPE test`,
res: &Query{Statements: []Statement{&InfoStatement{
Kind: SCOPE,
What: &Ident{"test"},
}}},
},
{
sql: `INFO FOR TABLE`,
err: "Found `` but expected `name`",
},
{
sql: `INFO FOR TABLE test`,
res: &Query{Statements: []Statement{&InfoStatement{
Kind: TABLE,
What: &Ident{"test"},
}}},
},
{
sql: `INFO FOR TABLE test something`,
err: "Found `something` but expected `;`",
},
}
for _, test := range tests {
testsql(t, test)
}
}
func Test_Parse_Queries_Let(t *testing.T) {
var tests = []tester{
{
sql: `LET`,
err: "Found `` but expected `name`",
},
{
sql: `LET name`,
err: "Found `` but expected `=`",
},
{
sql: `LET name =`,
err: "Found `=` but expected `expression`",
},
{
sql: `LET name = true`,
res: &Query{Statements: []Statement{&LetStatement{
RW: false,
Name: &Ident{"name"},
What: true,
}}},
},
{
sql: `LET name = false`,
res: &Query{Statements: []Statement{&LetStatement{
RW: false,
Name: &Ident{"name"},
What: false,
}}},
},
{
sql: `LET name = "test"`,
res: &Query{Statements: []Statement{&LetStatement{
RW: false,
Name: &Ident{"name"},
What: &Value{"test"},
}}},
},
{
sql: `LET name = 1`,
res: &Query{Statements: []Statement{&LetStatement{
RW: false,
Name: &Ident{"name"},
What: float64(1),
}}},
},
{
sql: `LET name = 1.0`,
str: `LET name = 1`,
res: &Query{Statements: []Statement{&LetStatement{
RW: false,
Name: &Ident{"name"},
What: float64(1),
}}},
},
{
sql: `LET name = 1.1`,
res: &Query{Statements: []Statement{&LetStatement{
RW: false,
Name: &Ident{"name"},
What: float64(1.1),
}}},
},
{
sql: `LET name = thing:test`,
res: &Query{Statements: []Statement{&LetStatement{
RW: false,
Name: &Ident{"name"},
What: &Thing{TB: "thing", ID: "test"},
}}},
},
{
sql: `LET name = thing:test`,
res: &Query{Statements: []Statement{&LetStatement{
RW: false,
Name: &Ident{"name"},
What: &Thing{TB: "thing", ID: "test"},
}}},
},
{
sql: `LET name = @thing:test`,
str: `LET name = thing:test`,
res: &Query{Statements: []Statement{&LetStatement{
RW: false,
Name: &Ident{"name"},
What: &Thing{TB: "thing", ID: "test"},
}}},
},
{
sql: `LET name = {"key": "val"}`,
str: `LET name = {"key":"val"}`,
res: &Query{Statements: []Statement{&LetStatement{
RW: false,
Name: &Ident{"name"},
What: map[string]interface{}{"key": "val"},
}}},
},
{
sql: `LET name = ["key", "val"]`,
str: `LET name = ["key","val"]`,
res: &Query{Statements: []Statement{&LetStatement{
RW: false,
Name: &Ident{"name"},
What: []interface{}{"key", "val"},
}}},
},
{
sql: `LET name = $test`,
res: &Query{Statements: []Statement{&LetStatement{
RW: false,
Name: &Ident{"name"},
What: &Param{"test"},
}}},
},
{
sql: `LET name = (CREATE person)`,
res: &Query{Statements: []Statement{&LetStatement{
RW: true,
Name: &Ident{"name"},
What: &SubExpression{Expr: &CreateStatement{
What: Exprs{&Ident{"person"}},
Echo: AFTER,
}},
}}},
},
{
sql: `LET name = "test" something`,
err: "Found `something` but expected `;`",
},
}
for _, test := range tests {
testsql(t, test)
}
}
func Test_Parse_Queries_Return(t *testing.T) {
var tests = []tester{
{
sql: `RETURN`,
err: "Found `` but expected `expression`",
},
{
sql: `RETURN true`,
res: &Query{Statements: []Statement{&ReturnStatement{
RW: false,
What: Exprs{true},
}}},
},
{
sql: `RETURN true`,
res: &Query{Statements: []Statement{&ReturnStatement{
RW: false,
What: Exprs{true},
}}},
},
{
sql: `RETURN false`,
res: &Query{Statements: []Statement{&ReturnStatement{
RW: false,
What: Exprs{false},
}}},
},
{
sql: `RETURN "test"`,
res: &Query{Statements: []Statement{&ReturnStatement{
RW: false,
What: Exprs{&Value{"test"}},
}}},
},
{
sql: `RETURN 1`,
res: &Query{Statements: []Statement{&ReturnStatement{
RW: false,
What: Exprs{float64(1)},
}}},
},
{
sql: `RETURN 1.0`,
str: `RETURN 1`,
res: &Query{Statements: []Statement{&ReturnStatement{
RW: false,
What: Exprs{float64(1)},
}}},
},
{
sql: `RETURN 1.1`,
res: &Query{Statements: []Statement{&ReturnStatement{
RW: false,
What: Exprs{float64(1.1)},
}}},
},
{
sql: `RETURN @thing:test`,
str: `RETURN thing:test`,
res: &Query{Statements: []Statement{&ReturnStatement{
RW: false,
What: Exprs{&Thing{TB: "thing", ID: "test"}},
}}},
},
{
sql: `RETURN {"key": "val"}`,
str: `RETURN {"key":"val"}`,
res: &Query{Statements: []Statement{&ReturnStatement{
RW: false,
What: Exprs{map[string]interface{}{"key": "val"}},
}}},
},
{
sql: `RETURN ["key", "val"]`,
str: `RETURN ["key","val"]`,
res: &Query{Statements: []Statement{&ReturnStatement{
RW: false,
What: Exprs{[]interface{}{"key", "val"}},
}}},
},
{
sql: `RETURN $test`,
res: &Query{Statements: []Statement{&ReturnStatement{
RW: false,
What: Exprs{&Param{"test"}},
}}},
},
{
sql: `RETURN (CREATE person)`,
res: &Query{Statements: []Statement{&ReturnStatement{
RW: true,
What: Exprs{&SubExpression{Expr: &CreateStatement{
What: Exprs{&Ident{"person"}},
Echo: AFTER,
}}},
}}},
},
{
sql: `RETURN $test something`,
err: "Found `something` but expected `;`",
},
}
for _, test := range tests {
testsql(t, test)
}
}
func Test_Parse_Queries_Select(t *testing.T) {
date, _ := time.Parse("2006-01-02", "1987-06-22")
nano, _ := time.Parse(time.RFC3339, "1987-06-22T08:30:30.511Z")
var tests = []tester{
{
sql: `SELECT`,
err: "Found `` but expected `expression`",
},
{
sql: `SELECT FROM`,
err: "Found `FROM` but expected `expression`",
},
{
sql: `SELECT *`,
err: "Found `` but expected `FROM`",
},
{
sql: `SELECT * FROM`,
err: "Found `` but expected `expression`",
},
{
sql: `SELECT * FROM per!son`,
err: "Found `!` but expected `;`",
},
{
sql: `SELECT * FROM person`,
res: &Query{Statements: []Statement{&SelectStatement{
RW: false,
Expr: []*Field{{Expr: &All{}, Field: "*"}},
What: []Expr{&Ident{"person"}},
}}},
},
{
sql: `SELECT * FROM @`,
err: "Found `@` but expected `expression`",
},
{
sql: `SELECT * FROM @person`,
err: "Found `@person` but expected `expression`",
},
{
sql: `SELECT * FROM @person:`,
err: "Found `@person:` but expected `expression`",
},
{
sql: `SELECT * FROM @person WHERE`,
err: "Found `@person` but expected `expression`",
},
{
sql: "SELECT * FROM 111",
res: &Query{Statements: []Statement{&SelectStatement{
RW: false,
Expr: []*Field{{Expr: &All{}, Field: "*"}},
What: []Expr{float64(111)},
}}},
},
{
sql: "SELECT * FROM `111`",
str: "SELECT * FROM 111",
res: &Query{Statements: []Statement{&SelectStatement{
RW: false,
Expr: []*Field{{Expr: &All{}, Field: "*"}},
What: []Expr{&Ident{"111"}},
}}},
},
{
sql: "SELECT * FROM `2006-01-02`",
res: &Query{Statements: []Statement{&SelectStatement{
RW: false,
Expr: []*Field{{Expr: &All{}, Field: "*"}},
What: []Expr{&Ident{"2006-01-02"}},
}}},
},
{
sql: "SELECT * FROM `2006-01-02T15:04:05+07:00`",
res: &Query{Statements: []Statement{&SelectStatement{
RW: false,
Expr: []*Field{{Expr: &All{}, Field: "*"}},
What: []Expr{&Ident{"2006-01-02T15:04:05+07:00"}},
}}},
},
{
sql: "SELECT * FROM `2006-01-02T15:04:05.999999999+07:00`",
res: &Query{Statements: []Statement{&SelectStatement{
RW: false,
Expr: []*Field{{Expr: &All{}, Field: "*"}},
What: []Expr{&Ident{"2006-01-02T15:04:05.999999999+07:00"}},
}}},
},
{
sql: `SELECT * FROM person`,
res: &Query{Statements: []Statement{&SelectStatement{
RW: false,
Expr: []*Field{{Expr: &All{}, Field: "*"}},
What: []Expr{&Ident{"person"}},
}}},
},
{
sql: `SELECT * FROM person, tweet`,
res: &Query{Statements: []Statement{&SelectStatement{
RW: false,
Expr: []*Field{{Expr: &All{}, Field: "*"}},
What: []Expr{&Ident{"person"}, &Ident{"tweet"}},
}}},
},
{
sql: `SELECT * FROM person:1a`,
res: &Query{Statements: []Statement{&SelectStatement{
RW: false,
Expr: []*Field{{Expr: &All{}, Field: "*"}},
What: []Expr{&Thing{TB: "person", ID: "1a"}},
}}},
},
{
sql: `SELECT * FROM person:⟨1a⟩`,
str: `SELECT * FROM person:1a`,
res: &Query{Statements: []Statement{&SelectStatement{
RW: false,
Expr: []*Field{{Expr: &All{}, Field: "*"}},
What: []Expr{&Thing{TB: "person", ID: "1a"}},
}}},
},
{
sql: `SELECT * FROM person:123456`,
res: &Query{Statements: []Statement{&SelectStatement{
RW: false,
Expr: []*Field{{Expr: &All{}, Field: "*"}},
What: []Expr{&Thing{TB: "person", ID: float64(123456)}},
}}},
},
{
sql: `SELECT * FROM person:⟨123456⟩`,
str: `SELECT * FROM person:123456`,
res: &Query{Statements: []Statement{&SelectStatement{
RW: false,
Expr: []*Field{{Expr: &All{}, Field: "*"}},
What: []Expr{&Thing{TB: "person", ID: float64(123456)}},
}}},
},
{
sql: `SELECT * FROM person:123.456`,
res: &Query{Statements: []Statement{&SelectStatement{
RW: false,
Expr: []*Field{{Expr: &All{}, Field: "*"}},
What: []Expr{&Thing{TB: "person", ID: float64(123.456)}},
}}},
},
{
sql: `SELECT * FROM person:⟨123.456⟩`,
str: `SELECT * FROM person:123.456`,
res: &Query{Statements: []Statement{&SelectStatement{
RW: false,
Expr: []*Field{{Expr: &All{}, Field: "*"}},
What: []Expr{&Thing{TB: "person", ID: float64(123.456)}},
}}},
},
{
sql: `SELECT * FROM person:123.456.789.012`,
res: &Query{Statements: []Statement{&SelectStatement{
RW: false,
Expr: []*Field{{Expr: &All{}, Field: "*"}},
What: []Expr{&Thing{TB: "person", ID: "123.456.789.012"}},
}}},
},
{
sql: `SELECT * FROM person:⟨123.456.789.012⟩`,
str: `SELECT * FROM person:123.456.789.012`,
res: &Query{Statements: []Statement{&SelectStatement{
RW: false,
Expr: []*Field{{Expr: &All{}, Field: "*"}},
What: []Expr{&Thing{TB: "person", ID: "123.456.789.012"}},
}}},
},
{
sql: `SELECT * FROM person:⟨1987-06-22⟩`,
str: `SELECT * FROM person:⟨1987-06-22T00:00:00Z⟩`,
res: &Query{Statements: []Statement{&SelectStatement{
RW: false,
Expr: []*Field{{Expr: &All{}, Field: "*"}},
What: []Expr{&Thing{TB: "person", ID: date}},
}}},
},
{
sql: `SELECT * FROM person:⟨1987-06-22T08:30:30.511Z⟩`,
res: &Query{Statements: []Statement{&SelectStatement{
RW: false,
Expr: []*Field{{Expr: &All{}, Field: "*"}},
What: []Expr{&Thing{TB: "person", ID: nano}},
}}},
},
{
sql: `SELECT * FROM person:⟨A250C5A3-948F-4657-88AD-FF5F27B5B24E⟩`,
res: &Query{Statements: []Statement{&SelectStatement{
RW: false,
Expr: []*Field{{Expr: &All{}, Field: "*"}},
What: []Expr{&Thing{TB: "person", ID: "A250C5A3-948F-4657-88AD-FF5F27B5B24E"}},
}}},
},
{
sql: `SELECT * FROM person:⟨8250C5A3-948F-4657-88AD-FF5F27B5B24E⟩`,
res: &Query{Statements: []Statement{&SelectStatement{
RW: false,
Expr: []*Field{{Expr: &All{}, Field: "*"}},
What: []Expr{&Thing{TB: "person", ID: "8250C5A3-948F-4657-88AD-FF5F27B5B24E"}},
}}},
},
{
sql: `SELECT * FROM person:⟨Tobie Morgan Hitchcock⟩`,
res: &Query{Statements: []Statement{&SelectStatement{
RW: false,
Expr: []*Field{{Expr: &All{}, Field: "*"}},
What: []Expr{&Thing{TB: "person", ID: "Tobie Morgan Hitchcock"}},
}}},
},
{
sql: `SELECT * FROM ⟨email addresses⟩:⟨tobie@abcum.com⟩`,
res: &Query{Statements: []Statement{&SelectStatement{
RW: false,
Expr: []*Field{{Expr: &All{}, Field: "*"}},
What: []Expr{&Thing{TB: "email addresses", ID: "tobie@abcum.com"}},
}}},
},
{
sql: `SELECT * FROM ⟨email addresses⟩:⟨tobie+spam@abcum.com⟩`,
res: &Query{Statements: []Statement{&SelectStatement{
RW: false,
Expr: []*Field{{Expr: &All{}, Field: "*"}},
What: []Expr{&Thing{TB: "email addresses", ID: "tobie+spam@abcum.com"}},
}}},
},
{
sql: `SELECT *, temp AS test FROM person`,
res: &Query{Statements: []Statement{&SelectStatement{
RW: false,
Expr: []*Field{
{Expr: &All{}, Field: "*"},
{Expr: &Ident{"temp"}, Field: "test", Alias: "test"},
},
What: []Expr{&Ident{"person"}},
}}},
},
{
sql: "SELECT `email addresses` AS emails FROM person",
res: &Query{Statements: []Statement{&SelectStatement{
RW: false,
Expr: []*Field{
{Expr: &Ident{"email addresses"}, Field: "emails", Alias: "emails"},
},
What: []Expr{&Ident{"person"}},
}}},
},
{
sql: "SELECT emails AS `email addresses` FROM person",
res: &Query{Statements: []Statement{&SelectStatement{
RW: false,
Expr: []*Field{
{Expr: &Ident{"emails"}, Field: "email addresses", Alias: "email addresses"},
},
What: []Expr{&Ident{"person"}},
}}},
},
{
sql: `SELECT * FROM (CREATE person)`,
res: &Query{Statements: []Statement{&SelectStatement{
RW: true,
Expr: []*Field{
{Expr: &All{}, Field: "*"},
},
What: Exprs{&SubExpression{Expr: &CreateStatement{
What: Exprs{&Ident{"person"}},
Echo: AFTER,
}}},
}}},
},
{
sql: "SELECT * FROM person WHERE id = \"\x0A\"",
res: &Query{Statements: []Statement{&SelectStatement{
RW: false,
Expr: []*Field{{Expr: &All{}, Field: "*"}},
What: []Expr{&Ident{"person"}},
Cond: &BinaryExpression{LHS: &Ident{"id"}, Op: EQ, RHS: &Value{"\n"}},
}}},
},
{
sql: "SELECT * FROM person WHERE id = \"\x0D\"",
res: &Query{Statements: []Statement{&SelectStatement{
RW: false,
Expr: []*Field{{Expr: &All{}, Field: "*"}},
What: []Expr{&Ident{"person"}},
Cond: &BinaryExpression{LHS: &Ident{"id"}, Op: EQ, RHS: &Value{"\r"}},
}}},
},
{
sql: "SELECT * FROM person WHERE id = \"\b\n\r\t\"",
res: &Query{Statements: []Statement{&SelectStatement{
RW: false,
Expr: []*Field{{Expr: &All{}, Field: "*"}},
What: []Expr{&Ident{"person"}},
Cond: &BinaryExpression{LHS: &Ident{"id"}, Op: EQ, RHS: &Value{"\b\n\r\t"}},
}}},
},
{
sql: `SELECT * FROM person WHERE`,
err: "Found `` but expected `expression`",
},
{
sql: `SELECT * FROM person WHERE id`,
res: &Query{Statements: []Statement{&SelectStatement{
RW: false,
Expr: []*Field{{Expr: &All{}, Field: "*"}},
What: []Expr{&Ident{"person"}},
Cond: &Ident{"id"},
}}},
},
{
sql: `SELECT * FROM person WHERE id = `,
err: "Found `` but expected `expression`",
},
{
sql: `SELECT * FROM person WHERE id = 1`,
res: &Query{Statements: []Statement{&SelectStatement{
RW: false,
Expr: []*Field{{Expr: &All{}, Field: "*"}},
What: []Expr{&Ident{"person"}},
Cond: &BinaryExpression{LHS: &Ident{"id"}, Op: EQ, RHS: float64(1)},
}}},
},
{
sql: `SELECT * FROM person WHERE old = EMPTY`,
res: &Query{Statements: []Statement{&SelectStatement{
RW: false,
Expr: []*Field{{Expr: &All{}, Field: "*"}},
What: []Expr{&Ident{"person"}},
Cond: &BinaryExpression{LHS: &Ident{"old"}, Op: EQ, RHS: &Empty{}},
}}},
},
{
sql: `SELECT * FROM person WHERE old != EMPTY`,
res: &Query{Statements: []Statement{&SelectStatement{
RW: false,
Expr: []*Field{{Expr: &All{}, Field: "*"}},
What: []Expr{&Ident{"person"}},
Cond: &BinaryExpression{LHS: &Ident{"old"}, Op: NEQ, RHS: &Empty{}},
}}},
},
{
sql: `SELECT * FROM person WHERE old = MISSING`,
str: `SELECT * FROM person WHERE old = VOID`,
res: &Query{Statements: []Statement{&SelectStatement{
RW: false,
Expr: []*Field{{Expr: &All{}, Field: "*"}},
What: []Expr{&Ident{"person"}},
Cond: &BinaryExpression{LHS: &Ident{"old"}, Op: EQ, RHS: &Void{}},
}}},
},
{
sql: `SELECT * FROM person WHERE old != MISSING`,
str: `SELECT * FROM person WHERE old != VOID`,
res: &Query{Statements: []Statement{&SelectStatement{
RW: false,
Expr: []*Field{{Expr: &All{}, Field: "*"}},
What: []Expr{&Ident{"person"}},
Cond: &BinaryExpression{LHS: &Ident{"old"}, Op: NEQ, RHS: &Void{}},
}}},
},
{
sql: `SELECT * FROM person WHERE old IS EMPTY`,
str: `SELECT * FROM person WHERE old = EMPTY`,
res: &Query{Statements: []Statement{&SelectStatement{
RW: false,
Expr: []*Field{{Expr: &All{}, Field: "*"}},
What: []Expr{&Ident{"person"}},
Cond: &BinaryExpression{LHS: &Ident{"old"}, Op: EQ, RHS: &Empty{}},
}}},
},
{
sql: `SELECT * FROM person WHERE old IS NOT EMPTY`,
str: `SELECT * FROM person WHERE old != EMPTY`,
res: &Query{Statements: []Statement{&SelectStatement{
RW: false,
Expr: []*Field{{Expr: &All{}, Field: "*"}},
What: []Expr{&Ident{"person"}},
Cond: &BinaryExpression{LHS: &Ident{"old"}, Op: NEQ, RHS: &Empty{}},
}}},
},
{
sql: `SELECT * FROM person WHERE old IS MISSING`,
str: `SELECT * FROM person WHERE old = VOID`,
res: &Query{Statements: []Statement{&SelectStatement{
RW: false,
Expr: []*Field{{Expr: &All{}, Field: "*"}},
What: []Expr{&Ident{"person"}},
Cond: &BinaryExpression{LHS: &Ident{"old"}, Op: EQ, RHS: &Void{}},
}}},
},
{
sql: `SELECT * FROM person WHERE old IS NOT MISSING`,
str: `SELECT * FROM person WHERE old != VOID`,
res: &Query{Statements: []Statement{&SelectStatement{
RW: false,
Expr: []*Field{{Expr: &All{}, Field: "*"}},
What: []Expr{&Ident{"person"}},
Cond: &BinaryExpression{LHS: &Ident{"old"}, Op: NEQ, RHS: &Void{}},
}}},
},
{
sql: `SELECT * FROM person WHERE old = true`,
res: &Query{Statements: []Statement{&SelectStatement{
RW: false,
Expr: []*Field{{Expr: &All{}, Field: "*"}},
What: []Expr{&Ident{"person"}},
Cond: &BinaryExpression{LHS: &Ident{"old"}, Op: EQ, RHS: true},
}}},
},
{
sql: `SELECT * FROM person WHERE old = false`,
res: &Query{Statements: []Statement{&SelectStatement{
RW: false,
Expr: []*Field{{Expr: &All{}, Field: "*"}},
What: []Expr{&Ident{"person"}},
Cond: &BinaryExpression{LHS: &Ident{"old"}, Op: EQ, RHS: false},
}}},
},
{
sql: `SELECT * FROM person WHERE id != NULL AND id > 13.9 AND id < 31 AND id >= 15 AND id <= 29.9`,
res: &Query{Statements: []Statement{&SelectStatement{
RW: false,
Expr: []*Field{{Expr: &All{}, Field: "*"}},
What: []Expr{&Ident{"person"}},
Cond: &BinaryExpression{
LHS: &BinaryExpression{
LHS: &Ident{"id"},
Op: NEQ,
RHS: &Null{},
},
Op: AND,
RHS: &BinaryExpression{
LHS: &BinaryExpression{
LHS: &Ident{"id"},
Op: GT,
RHS: float64(13.9),
},
Op: AND,
RHS: &BinaryExpression{
LHS: &BinaryExpression{
LHS: &Ident{"id"},
Op: LT,
RHS: float64(31),
},
Op: AND,
RHS: &BinaryExpression{
LHS: &BinaryExpression{
LHS: &Ident{"id"},
Op: GTE,
RHS: float64(15),
},
Op: AND,
RHS: &BinaryExpression{
LHS: &Ident{"id"},
Op: LTE,
RHS: float64(29.9),
},
},
},
},
},
}}},
},
{
sql: `SELECT * FROM person WHERE 2 * 3 + 4 = 10`,
res: &Query{Statements: []Statement{&SelectStatement{
RW: false,
Expr: []*Field{{Expr: &All{}, Field: "*"}},
What: []Expr{&Ident{"person"}},
Cond: &BinaryExpression{
LHS: &BinaryExpression{
LHS: float64(2),
Op: MUL,
RHS: &BinaryExpression{
LHS: float64(3),
Op: ADD,
RHS: float64(4),
},
},
Op: EQ,
RHS: float64(10),
},
}}},
},
{
sql: `SELECT * FROM person WHERE 2 + 3 * 4 = 14`,
res: &Query{Statements: []Statement{&SelectStatement{
RW: false,
Expr: []*Field{{Expr: &All{}, Field: "*"}},
What: []Expr{&Ident{"person"}},
Cond: &BinaryExpression{
LHS: &BinaryExpression{
LHS: float64(2),
Op: ADD,
RHS: &BinaryExpression{
LHS: float64(3),
Op: MUL,
RHS: float64(4),
},
},
Op: EQ,
RHS: float64(14),
},
}}},
},
{
sql: `SELECT * FROM person WHERE 2 * 3 + 3 * 4 = 18`,
res: &Query{Statements: []Statement{&SelectStatement{
RW: false,
Expr: []*Field{{Expr: &All{}, Field: "*"}},
What: []Expr{&Ident{"person"}},
Cond: &BinaryExpression{
LHS: &BinaryExpression{
LHS: float64(2),
Op: MUL,
RHS: &BinaryExpression{
LHS: float64(3),
Op: ADD,
RHS: &BinaryExpression{
LHS: float64(3),
Op: MUL,
RHS: float64(4),
},
},
},
Op: EQ,
RHS: float64(18),
},
}}},
},
{
sql: `SELECT * FROM person WHERE 2 + 3 + 3 * 4 = 17`,
res: &Query{Statements: []Statement{&SelectStatement{
RW: false,
Expr: []*Field{{Expr: &All{}, Field: "*"}},
What: []Expr{&Ident{"person"}},
Cond: &BinaryExpression{
LHS: &BinaryExpression{
LHS: float64(2),
Op: ADD,
RHS: &BinaryExpression{
LHS: float64(3),
Op: ADD,
RHS: &BinaryExpression{
LHS: float64(3),
Op: MUL,
RHS: float64(4),
},
},
},
Op: EQ,
RHS: float64(17),
},
}}},
},
{
sql: `SELECT * FROM person WHERE (2 + 3 + 3) * 4 = 32`,
res: &Query{Statements: []Statement{&SelectStatement{
RW: false,
Expr: []*Field{{Expr: &All{}, Field: "*"}},
What: []Expr{&Ident{"person"}},
Cond: &BinaryExpression{
LHS: &BinaryExpression{
LHS: &SubExpression{
Expr: &BinaryExpression{
LHS: float64(2),
Op: ADD,
RHS: &BinaryExpression{
LHS: float64(3),
Op: ADD,
RHS: float64(3),
},
},
},
Op: MUL,
RHS: float64(4),
},
Op: EQ,
RHS: float64(32),
},
}}},
},
{
sql: `SELECT * FROM person WHERE test IN ["London","Paris"]`,
str: `SELECT * FROM person WHERE test ∈ ["London","Paris"]`,
res: &Query{Statements: []Statement{&SelectStatement{
RW: false,
Expr: []*Field{{Expr: &All{}, Field: "*"}},
What: []Expr{&Ident{"person"}},
Cond: &BinaryExpression{LHS: &Ident{"test"}, Op: INS, RHS: []interface{}{"London", "Paris"}},
}}},
},
{
sql: `SELECT * FROM person WHERE test IS IN ["London","Paris"]`,
str: `SELECT * FROM person WHERE test ∈ ["London","Paris"]`,
res: &Query{Statements: []Statement{&SelectStatement{
RW: false,
Expr: []*Field{{Expr: &All{}, Field: "*"}},
What: []Expr{&Ident{"person"}},
Cond: &BinaryExpression{LHS: &Ident{"test"}, Op: INS, RHS: []interface{}{"London", "Paris"}},
}}},
},
{
sql: `SELECT * FROM person WHERE test IS NOT IN ["London","Paris"]`,
str: `SELECT * FROM person WHERE test ∉ ["London","Paris"]`,
res: &Query{Statements: []Statement{&SelectStatement{
RW: false,
Expr: []*Field{{Expr: &All{}, Field: "*"}},
What: []Expr{&Ident{"person"}},
Cond: &BinaryExpression{LHS: &Ident{"test"}, Op: NIS, RHS: []interface{}{"London", "Paris"}},
}}},
},
{
sql: `SELECT * FROM person WHERE ["London","Paris"] CONTAINS test`,
str: `SELECT * FROM person WHERE ["London","Paris"] ∋ test`,
res: &Query{Statements: []Statement{&SelectStatement{
RW: false,
Expr: []*Field{{Expr: &All{}, Field: "*"}},
What: []Expr{&Ident{"person"}},
Cond: &BinaryExpression{LHS: []interface{}{"London", "Paris"}, Op: SIN, RHS: &Ident{"test"}},
}}},
},
{
sql: `SELECT * FROM person WHERE ["London","Paris"] CONTAINS NOT test`,
str: `SELECT * FROM person WHERE ["London","Paris"] ∌ test`,
res: &Query{Statements: []Statement{&SelectStatement{
RW: false,
Expr: []*Field{{Expr: &All{}, Field: "*"}},
What: []Expr{&Ident{"person"}},
Cond: &BinaryExpression{LHS: []interface{}{"London", "Paris"}, Op: SNI, RHS: &Ident{"test"}},
}}},
},
{
sql: `SELECT * FROM person WHERE test = {`,
err: "Found `{` but expected `expression`",
},
{
sql: `SELECT * FROM person WHERE test = {"name":"London"}`,
res: &Query{Statements: []Statement{&SelectStatement{
RW: false,
Expr: []*Field{{Expr: &All{}, Field: "*"}},
What: []Expr{&Ident{"person"}},
Cond: &BinaryExpression{LHS: &Ident{"test"}, Op: EQ, RHS: map[string]interface{}{"name": "London"}},
}}},
},
{
sql: `SELECT * FROM person WHERE test = {"name":{"f":"first","l":"last"}}`,
res: &Query{Statements: []Statement{&SelectStatement{
RW: false,
Expr: []*Field{{Expr: &All{}, Field: "*"}},
What: []Expr{&Ident{"person"}},
Cond: &BinaryExpression{LHS: &Ident{"test"}, Op: EQ, RHS: map[string]interface{}{"name": map[string]interface{}{"f": "first", "l": "last"}}},
}}},
},
{
sql: `SELECT * FROM person TIMEOUT 1s`,
res: &Query{Statements: []Statement{&SelectStatement{
RW: false,
Expr: []*Field{{Expr: &All{}, Field: "*"}},
What: []Expr{&Ident{"person"}},
Timeout: 1 * time.Second,
}}},
},
{
sql: `SELECT * FROM person TIMEOUT null`,
err: "Found `null` but expected `duration`",
},
}
/*bday1, _ := time.Parse("2006-01-02", "1987-06-22")
bday2, _ := time.Parse(time.RFC3339, "1987-06-22T08:00:00Z")
bday3, _ := time.Parse(time.RFC3339, "1987-06-22T08:30:00.193943735Z")
bday4, _ := time.Parse(time.RFC3339, "2016-03-14T11:19:31.193943735Z")
tests = append(tests, tester{
sql: `SELECT * FROM person WHERE bday >= "1987-06-22" AND bday >= "1987-06-22T08:00:00Z" AND bday >= "1987-06-22T08:30:00.193943735Z" AND bday <= "2016-03-14T11:19:31.193943735Z"`,
res: &Query{Statements: []Statement{&SelectStatement{
RW: true,
Expr: []*Field{{Expr: &All{}, Field: "*"}},
What: []Expr{&Ident{"person"}},
Cond: &BinaryExpression{
LHS: &BinaryExpression{
LHS: &BinaryExpression{
LHS: &BinaryExpression{
LHS: &Ident{"bday"},
Op: GTE,
RHS: bday1,
},
Op: AND,
RHS: &BinaryExpression{
LHS: &Ident{"bday"},
Op: GTE,
RHS: bday2,
},
},
Op: AND,
RHS: &BinaryExpression{
LHS: &Ident{"bday"},
Op: GTE,
RHS: bday3,
},
},
Op: AND,
RHS: &BinaryExpression{
LHS: &Ident{"bday"},
Op: LTE,
RHS: bday4,
},
},
}}},
})*/
for _, test := range tests {
testsql(t, test)
}
}
func Test_Parse_Queries_Create(t *testing.T) {
var tests = []tester{
{
sql: `CREATE`,
err: "Found `` but expected `expression`",
},
{
sql: `CREATE person`,
res: &Query{Statements: []Statement{&CreateStatement{
What: []Expr{&Ident{"person"}},
Echo: AFTER,
}}},
},
{
sql: `CREATE person SET 123`,
err: "Found `123` but expected `field name`",
},
{
sql: `CREATE person SET firstname`,
err: "Found `` but expected `=, +=, -=`",
},
{
sql: `CREATE person SET firstname = VOID`,
res: &Query{Statements: []Statement{&CreateStatement{
What: []Expr{&Ident{"person"}},
Data: &DataExpression{Data: []*ItemExpression{{LHS: &Ident{"firstname"}, Op: EQ, RHS: &Void{}}}},
Echo: AFTER,
}}},
},
{
sql: `CREATE person SET firstname = EMPTY`,
res: &Query{Statements: []Statement{&CreateStatement{
What: []Expr{&Ident{"person"}},
Data: &DataExpression{Data: []*ItemExpression{{LHS: &Ident{"firstname"}, Op: EQ, RHS: &Empty{}}}},
Echo: AFTER,
}}},
},
{
sql: `CREATE person SET firstname = "Tobie" something`,
err: "Found `something` but expected `;`",
},
{
sql: `CREATE person SET firstname = "Tobie"`,
res: &Query{Statements: []Statement{&CreateStatement{
What: []Expr{&Ident{"person"}},
Data: &DataExpression{Data: []*ItemExpression{{LHS: &Ident{"firstname"}, Op: EQ, RHS: &Value{"Tobie"}}}},
Echo: AFTER,
}}},
},
{
sql: `CREATE person MERGE something`,
err: "Found `something` but expected `json`",
},
{
sql: `CREATE person MERGE {"firstname"::"Tobie"}`,
err: "Found `{\"firstname\"::\"Tobie\"}` but expected `json`",
},
{
sql: `CREATE person MERGE {"firstname":"Tobie"} something`,
err: "Found `something` but expected `;`",
},
{
sql: `CREATE person MERGE {"firstname":"Tobie"}`,
res: &Query{Statements: []Statement{&CreateStatement{
What: []Expr{&Ident{"person"}},
Data: &MergeExpression{Data: map[string]interface{}{"firstname": "Tobie"}},
Echo: AFTER,
}}},
},
{
sql: `CREATE person CONTENT {"test":"{{{"}`,
res: &Query{Statements: []Statement{&CreateStatement{
What: []Expr{&Ident{"person"}},
Data: &ContentExpression{Data: map[string]interface{}{"test": "{{{"}},
Echo: AFTER,
}}},
},
{
sql: `CREATE person CONTENT something`,
err: "Found `something` but expected `json`",
},
{
sql: `CREATE person CONTENT {"firstname"::"Tobie"}`,
err: "Found `{\"firstname\"::\"Tobie\"}` but expected `json`",
},
{
sql: `CREATE person CONTENT {"firstname":"Tobie"} something`,
err: "Found `something` but expected `;`",
},
{
sql: `CREATE person CONTENT {"firstname":"Tobie"}`,
res: &Query{Statements: []Statement{&CreateStatement{
What: []Expr{&Ident{"person"}},
Data: &ContentExpression{Data: map[string]interface{}{"firstname": "Tobie"}},
Echo: AFTER,
}}},
},
{
sql: `CREATE person RETURN`,
err: "Found `` but expected `NONE, BEFORE, AFTER`",
},
{
sql: `CREATE person RETURN NONE`,
res: &Query{Statements: []Statement{&CreateStatement{
What: []Expr{&Ident{"person"}},
Echo: NONE,
}}},
},
{
sql: `CREATE person RETURN BEFORE`,
res: &Query{Statements: []Statement{&CreateStatement{
What: []Expr{&Ident{"person"}},
Echo: BEFORE,
}}},
},
{
sql: `CREATE person RETURN AFTER`,
str: `CREATE person`,
res: &Query{Statements: []Statement{&CreateStatement{
What: []Expr{&Ident{"person"}},
Echo: AFTER,
}}},
},
{
sql: `CREATE person TIMEOUT 1s`,
res: &Query{Statements: []Statement{&CreateStatement{
What: []Expr{&Ident{"person"}},
Echo: AFTER,
Timeout: 1 * time.Second,
}}},
},
{
sql: `CREATE person TIMEOUT null`,
err: "Found `null` but expected `duration`",
},
{
sql: `CREATE person something`,
err: "Found `something` but expected `;`",
},
}
for _, test := range tests {
testsql(t, test)
}
}
func Test_Parse_Queries_Update(t *testing.T) {
var tests = []tester{
{
sql: `UPDATE`,
err: "Found `` but expected `expression`",
},
{
sql: `UPDATE person`,
res: &Query{Statements: []Statement{&UpdateStatement{
What: []Expr{&Ident{"person"}},
Echo: AFTER,
}}},
},
{
sql: `UPDATE person SET 123`,
err: "Found `123` but expected `field name`",
},
{
sql: `UPDATE person SET firstname`,
err: "Found `` but expected `=, +=, -=`",
},
{
sql: `UPDATE person SET firstname = VOID`,
res: &Query{Statements: []Statement{&UpdateStatement{
What: []Expr{&Ident{"person"}},
Data: &DataExpression{Data: []*ItemExpression{{LHS: &Ident{"firstname"}, Op: EQ, RHS: &Void{}}}},
Echo: AFTER,
}}},
},
{
sql: `UPDATE person SET firstname = EMPTY`,
res: &Query{Statements: []Statement{&UpdateStatement{
What: []Expr{&Ident{"person"}},
Data: &DataExpression{Data: []*ItemExpression{{LHS: &Ident{"firstname"}, Op: EQ, RHS: &Empty{}}}},
Echo: AFTER,
}}},
},
{
sql: `UPDATE person SET firstname = "Tobie" something`,
err: "Found `something` but expected `;`",
},
{
sql: `UPDATE person SET firstname = "Tobie"`,
res: &Query{Statements: []Statement{&UpdateStatement{
What: []Expr{&Ident{"person"}},
Data: &DataExpression{Data: []*ItemExpression{{LHS: &Ident{"firstname"}, Op: EQ, RHS: &Value{"Tobie"}}}},
Echo: AFTER,
}}},
},
{
sql: `UPDATE person DIFF something`,
err: "Found `something` but expected `json`",
},
{
sql: `UPDATE person DIFF {} something`,
err: "Found `{}` but expected `json`",
},
{
sql: `UPDATE person DIFF [] something`,
err: "Found `something` but expected `;`",
},
{
sql: `UPDATE person DIFF []`,
res: &Query{Statements: []Statement{&UpdateStatement{
What: []Expr{&Ident{"person"}},
Data: &DiffExpression{Data: []interface{}{}},
Echo: AFTER,
}}},
},
{
sql: `UPDATE person MERGE something`,
err: "Found `something` but expected `json`",
},
{
sql: `UPDATE person MERGE {"firstname"::"Tobie"}`,
err: "Found `{\"firstname\"::\"Tobie\"}` but expected `json`",
},
{
sql: `UPDATE person MERGE {"firstname":"Tobie"} something`,
err: "Found `something` but expected `;`",
},
{
sql: `UPDATE person MERGE {"firstname":"Tobie"}`,
res: &Query{Statements: []Statement{&UpdateStatement{
What: []Expr{&Ident{"person"}},
Data: &MergeExpression{Data: map[string]interface{}{"firstname": "Tobie"}},
Echo: AFTER,
}}},
},
{
sql: `UPDATE person CONTENT something`,
err: "Found `something` but expected `json`",
},
{
sql: `UPDATE person CONTENT {"firstname"::"Tobie"}`,
err: "Found `{\"firstname\"::\"Tobie\"}` but expected `json`",
},
{
sql: `UPDATE person CONTENT {"firstname":"Tobie"} something`,
err: "Found `something` but expected `;`",
},
{
sql: `UPDATE person CONTENT {"firstname":"Tobie"}`,
res: &Query{Statements: []Statement{&UpdateStatement{
What: []Expr{&Ident{"person"}},
Data: &ContentExpression{Data: map[string]interface{}{"firstname": "Tobie"}},
Echo: AFTER,
}}},
},
{
sql: `UPDATE person CONTENT {"test":"{{{"}`,
res: &Query{Statements: []Statement{&UpdateStatement{
What: []Expr{&Ident{"person"}},
Data: &ContentExpression{Data: map[string]interface{}{"test": "{{{"}},
Echo: AFTER,
}}},
},
{
sql: `UPDATE person RETURN`,
err: "Found `` but expected `NONE, BEFORE, AFTER`",
},
{
sql: `UPDATE person RETURN NONE`,
res: &Query{Statements: []Statement{&UpdateStatement{
What: []Expr{&Ident{"person"}},
Echo: NONE,
}}},
},
{
sql: `UPDATE person RETURN BEFORE`,
res: &Query{Statements: []Statement{&UpdateStatement{
What: []Expr{&Ident{"person"}},
Echo: BEFORE,
}}},
},
{
sql: `UPDATE person RETURN AFTER`,
str: `UPDATE person`,
res: &Query{Statements: []Statement{&UpdateStatement{
What: []Expr{&Ident{"person"}},
Echo: AFTER,
}}},
},
{
sql: `UPDATE person TIMEOUT 1s`,
res: &Query{Statements: []Statement{&UpdateStatement{
What: []Expr{&Ident{"person"}},
Echo: AFTER,
Timeout: 1 * time.Second,
}}},
},
{
sql: `UPDATE person TIMEOUT null`,
err: "Found `null` but expected `duration`",
},
{
sql: `UPDATE person something`,
err: "Found `something` but expected `;`",
},
}
for _, test := range tests {
testsql(t, test)
}
}
func Test_Parse_Queries_Delete(t *testing.T) {
var tests = []tester{
{
sql: `DELETE`,
err: "Found `` but expected `expression`",
},
{
sql: `DELETE FROM`,
err: "Found `` but expected `expression`",
},
{
sql: `DELETE person`,
res: &Query{Statements: []Statement{&DeleteStatement{
What: []Expr{&Ident{"person"}},
Echo: NONE,
}}},
},
{
sql: `DELETE person RETURN`,
err: "Found `` but expected `NONE, BEFORE, AFTER`",
},
{
sql: `DELETE person RETURN NONE`,
str: `DELETE person`,
res: &Query{Statements: []Statement{&DeleteStatement{
What: []Expr{&Ident{"person"}},
Echo: NONE,
}}},
},
{
sql: `DELETE person RETURN BEFORE`,
res: &Query{Statements: []Statement{&DeleteStatement{
What: []Expr{&Ident{"person"}},
Echo: BEFORE,
}}},
},
{
sql: `DELETE person RETURN AFTER`,
res: &Query{Statements: []Statement{&DeleteStatement{
What: []Expr{&Ident{"person"}},
Echo: AFTER,
}}},
},
{
sql: `DELETE person TIMEOUT 1s`,
res: &Query{Statements: []Statement{&DeleteStatement{
What: []Expr{&Ident{"person"}},
Echo: NONE,
Timeout: 1 * time.Second,
}}},
},
{
sql: `DELETE person TIMEOUT null`,
err: "Found `null` but expected `duration`",
},
{
sql: `DELETE person something`,
err: "Found `something` but expected `;`",
},
}
for _, test := range tests {
testsql(t, test)
}
}
func Test_Parse_Queries_Relate(t *testing.T) {
var tests = []tester{
{
sql: `RELATE`,
err: "Found `` but expected `expression`",
},
{
sql: `RELATE person`,
err: "Found `` but expected `->, <-`",
},
{
sql: `RELATE person ->`,
err: "Found `` but expected `table`",
},
{
sql: `RELATE person -> purchase`,
err: "Found `` but expected `->`",
},
{
sql: `RELATE item <- purchase`,
err: "Found `` but expected `<-`",
},
{
sql: `RELATE person -> purchase -> `,
err: "Found `` but expected `expression`",
},
{
sql: `RELATE item <- purchase <- `,
err: "Found `` but expected `expression`",
},
{
sql: `RELATE person -> purchase -> item`,
res: &Query{Statements: []Statement{&RelateStatement{
Type: &Table{"purchase"},
From: []Expr{&Ident{"person"}},
With: []Expr{&Ident{"item"}},
Echo: AFTER,
}}},
},
{
sql: `RELATE item <- purchase <- person`,
str: `RELATE person -> purchase -> item`,
res: &Query{Statements: []Statement{&RelateStatement{
Type: &Table{"purchase"},
From: []Expr{&Ident{"person"}},
With: []Expr{&Ident{"item"}},
Echo: AFTER,
}}},
},
{
sql: `RELATE person -> purchase -> item UNIQUE`,
res: &Query{Statements: []Statement{&RelateStatement{
Type: &Table{"purchase"},
From: []Expr{&Ident{"person"}},
With: []Expr{&Ident{"item"}},
Uniq: true,
Echo: AFTER,
}}},
},
{
sql: `RELATE item <- purchase <- person UNIQUE`,
str: `RELATE person -> purchase -> item UNIQUE`,
res: &Query{Statements: []Statement{&RelateStatement{
Type: &Table{"purchase"},
From: []Expr{&Ident{"person"}},
With: []Expr{&Ident{"item"}},
Uniq: true,
Echo: AFTER,
}}},
},
{
sql: `RELATE person -> purchase -> item SET 123`,
err: "Found `123` but expected `field name`",
},
{
sql: `RELATE person -> purchase -> item SET firstname`,
err: "Found `` but expected `=, +=, -=`",
},
{
sql: `RELATE person -> purchase -> item SET public = true`,
res: &Query{Statements: []Statement{&RelateStatement{
Type: &Table{"purchase"},
From: []Expr{&Ident{"person"}},
With: []Expr{&Ident{"item"}},
Data: &DataExpression{Data: []*ItemExpression{{LHS: &Ident{"public"}, Op: EQ, RHS: true}}},
Echo: AFTER,
}}},
},
{
sql: `RELATE person -> purchase -> item RETURN`,
err: "Found `` but expected `NONE, BEFORE, AFTER`",
},
{
sql: `RELATE person -> purchase -> item RETURN NONE`,
res: &Query{Statements: []Statement{&RelateStatement{
Type: &Table{"purchase"},
From: []Expr{&Ident{"person"}},
With: []Expr{&Ident{"item"}},
Echo: NONE,
}}},
},
{
sql: `RELATE person -> purchase -> item RETURN BEFORE`,
res: &Query{Statements: []Statement{&RelateStatement{
Type: &Table{"purchase"},
From: []Expr{&Ident{"person"}},
With: []Expr{&Ident{"item"}},
Echo: BEFORE,
}}},
},
{
sql: `RELATE person -> purchase -> item RETURN AFTER`,
str: `RELATE person -> purchase -> item`,
res: &Query{Statements: []Statement{&RelateStatement{
Type: &Table{"purchase"},
From: []Expr{&Ident{"person"}},
With: []Expr{&Ident{"item"}},
Echo: AFTER,
}}},
},
{
sql: `RELATE person -> purchase -> item TIMEOUT 1s`,
res: &Query{Statements: []Statement{&RelateStatement{
Type: &Table{"purchase"},
From: []Expr{&Ident{"person"}},
With: []Expr{&Ident{"item"}},
Echo: AFTER,
Timeout: 1 * time.Second,
}}},
},
{
sql: `RELATE person -> purchase -> item TIMEOUT null`,
err: "Found `null` but expected `duration`",
},
{
sql: `RELATE person -> purchase -> item something`,
err: "Found `something` but expected `;`",
},
}
for _, test := range tests {
testsql(t, test)
}
}
func Test_Parse_Queries_Insert(t *testing.T) {
var tests = []tester{
{
sql: `INSERT`,
err: "Found `` but expected `expression`",
},
{
sql: `INSERT ["one","two","tre"]`,
err: "Found `` but expected `INTO`",
},
{
sql: `INSERT ["one","two","tre"] INTO`,
err: "Found `` but expected `table`",
},
{
sql: `INSERT ["one","two","tre"] INTO person`,
res: &Query{Statements: []Statement{&InsertStatement{
Data: []interface{}{"one", "two", "tre"},
Into: &Table{"person"},
Echo: AFTER,
}}},
},
{
sql: `INSERT ["one","two","tre"] INTO person RETURN`,
err: "Found `` but expected `NONE, BEFORE, AFTER`",
},
{
sql: `INSERT ["one","two","tre"] INTO person RETURN NONE`,
res: &Query{Statements: []Statement{&InsertStatement{
Data: []interface{}{"one", "two", "tre"},
Into: &Table{"person"},
Echo: NONE,
}}},
},
{
sql: `INSERT ["one","two","tre"] INTO person RETURN BEFORE`,
res: &Query{Statements: []Statement{&InsertStatement{
Data: []interface{}{"one", "two", "tre"},
Into: &Table{"person"},
Echo: BEFORE,
}}},
},
{
sql: `INSERT ["one","two","tre"] INTO person RETURN AFTER`,
str: `INSERT ["one","two","tre"] INTO person`,
res: &Query{Statements: []Statement{&InsertStatement{
Data: []interface{}{"one", "two", "tre"},
Into: &Table{"person"},
Echo: AFTER,
}}},
},
{
sql: `INSERT ["one","two","tre"] INTO person TIMEOUT 1s`,
res: &Query{Statements: []Statement{&InsertStatement{
Data: []interface{}{"one", "two", "tre"},
Into: &Table{"person"},
Echo: AFTER,
Timeout: 1 * time.Second,
}}},
},
{
sql: `INSERT ["one","two","tre"] INTO person TIMEOUT null`,
err: "Found `null` but expected `duration`",
},
{
sql: `INSERT ["one","two","tre"] INTO person something`,
err: "Found `something` but expected `;`",
},
}
for _, test := range tests {
testsql(t, test)
}
}
func Test_Parse_Queries_Upsert(t *testing.T) {
var tests = []tester{
{
sql: `UPSERT`,
err: "Found `` but expected `expression`",
},
{
sql: `UPSERT ["one","two","tre"]`,
err: "Found `` but expected `INTO`",
},
{
sql: `UPSERT ["one","two","tre"] INTO`,
err: "Found `` but expected `table`",
},
{
sql: `UPSERT ["one","two","tre"] INTO person`,
res: &Query{Statements: []Statement{&UpsertStatement{
Data: []interface{}{"one", "two", "tre"},
Into: &Table{"person"},
Echo: AFTER,
}}},
},
{
sql: `UPSERT ["one","two","tre"] INTO person RETURN`,
err: "Found `` but expected `NONE, BEFORE, AFTER`",
},
{
sql: `UPSERT ["one","two","tre"] INTO person RETURN NONE`,
res: &Query{Statements: []Statement{&UpsertStatement{
Data: []interface{}{"one", "two", "tre"},
Into: &Table{"person"},
Echo: NONE,
}}},
},
{
sql: `UPSERT ["one","two","tre"] INTO person RETURN BEFORE`,
res: &Query{Statements: []Statement{&UpsertStatement{
Data: []interface{}{"one", "two", "tre"},
Into: &Table{"person"},
Echo: BEFORE,
}}},
},
{
sql: `UPSERT ["one","two","tre"] INTO person RETURN AFTER`,
str: `UPSERT ["one","two","tre"] INTO person`,
res: &Query{Statements: []Statement{&UpsertStatement{
Data: []interface{}{"one", "two", "tre"},
Into: &Table{"person"},
Echo: AFTER,
}}},
},
{
sql: `UPSERT ["one","two","tre"] INTO person TIMEOUT 1s`,
res: &Query{Statements: []Statement{&UpsertStatement{
Data: []interface{}{"one", "two", "tre"},
Into: &Table{"person"},
Echo: AFTER,
Timeout: 1 * time.Second,
}}},
},
{
sql: `UPSERT ["one","two","tre"] INTO person TIMEOUT null`,
err: "Found `null` but expected `duration`",
},
{
sql: `UPSERT ["one","two","tre"] INTO person something`,
err: "Found `something` but expected `;`",
},
}
for _, test := range tests {
testsql(t, test)
}
}
func Test_Parse_Queries_Live(t *testing.T) {
var tests = []tester{
{
sql: `LIVE`,
err: "Found `` but expected `SELECT`",
},
{
sql: `LIVE SELECT`,
err: "Found `` but expected `expression`",
},
{
sql: `LIVE SELECT *`,
err: "Found `` but expected `FROM`",
},
{
sql: `LIVE SELECT * FROM`,
err: "Found `` but expected `expression`",
},
{
sql: `LIVE SELECT * FROM person`,
res: &Query{Statements: []Statement{&LiveStatement{
Expr: []*Field{{Expr: &All{}, Field: "*"}},
What: Exprs{&Ident{"person"}},
}}},
},
{
sql: `LIVE SELECT * FROM person WHERE`,
err: "Found `` but expected `expression`",
},
{
sql: `LIVE SELECT * FROM person WHERE public = true`,
res: &Query{Statements: []Statement{&LiveStatement{
Expr: []*Field{{Expr: &All{}, Field: "*"}},
What: Exprs{&Ident{"person"}},
Cond: &BinaryExpression{
LHS: &Ident{"public"},
Op: EQ,
RHS: true,
},
}}},
},
{
sql: `LIVE SELECT * FROM person WHERE public = true something`,
err: "Found `something` but expected `;`",
},
}
for _, test := range tests {
testsql(t, test)
}
}
func Test_Parse_Queries_Kill(t *testing.T) {
var tests = []tester{
{
sql: `KILL`,
err: "Found `` but expected `expression`",
},
{
sql: `KILL identifier`,
res: &Query{Statements: []Statement{&KillStatement{
What: Exprs{&Ident{"identifier"}},
}}},
},
{
sql: `KILL "identifier"`,
res: &Query{Statements: []Statement{&KillStatement{
What: Exprs{&Value{"identifier"}},
}}},
},
{
sql: `KILL "identifier" something`,
err: "Found `something` but expected `;`",
},
}
for _, test := range tests {
testsql(t, test)
}
}
func Test_Parse_Queries_Define(t *testing.T) {
var tests = []tester{
{
sql: `DEFINE`,
err: "Found `` but expected `NAMESPACE, DATABASE, LOGIN, TOKEN, SCOPE, TABLE, EVENT, FIELD, INDEX`",
},
// ----------------------------------------------------------------------
{
sql: `DEFINE NAMESPACE`,
err: "Found `` but expected `name`",
},
{
sql: `DEFINE NAMESPACE 111`,
err: "Found `111` but expected `name`",
},
{
sql: `DEFINE NAMESPACE 111.111`,
err: "Found `111.111` but expected `name`",
},
{
sql: `DEFINE NAMESPACE test`,
res: &Query{Statements: []Statement{&DefineNamespaceStatement{
Name: &Ident{"test"},
}}},
},
{
sql: `DEFINE NAMESPACE test something`,
err: "Found `something` but expected `;`",
},
// ----------------------------------------------------------------------
{
sql: `DEFINE DATABASE`,
err: "Found `` but expected `name`",
},
{
sql: `DEFINE DATABASE 111`,
err: "Found `111` but expected `name`",
},
{
sql: `DEFINE DATABASE 111.111`,
err: "Found `111.111` but expected `name`",
},
{
sql: `DEFINE DATABASE test`,
res: &Query{Statements: []Statement{&DefineDatabaseStatement{
Name: &Ident{"test"},
}}},
},
{
sql: `DEFINE DATABASE test something`,
err: "Found `something` but expected `;`",
},
// ----------------------------------------------------------------------
{
sql: `DEFINE LOGIN`,
err: "Found `` but expected `name`",
},
{
sql: `DEFINE LOGIN 111`,
err: "Found `111` but expected `name`",
},
{
sql: `DEFINE LOGIN 111.111`,
err: "Found `111.111` but expected `name`",
},
{
sql: `DEFINE LOGIN test`,
err: "Found `` but expected `ON`",
},
{
sql: `DEFINE LOGIN test ON`,
err: "Found `` but expected `NAMESPACE, DATABASE`",
},
{
sql: `DEFINE LOGIN test ON something`,
err: "Found `something` but expected `NAMESPACE, DATABASE`",
},
{
sql: `DEFINE LOGIN test ON NAMESPACE`,
err: "Found `` but expected `PASSWORD, PASSHASH`",
},
{
sql: `DEFINE LOGIN test ON NAMESPACE PASSWORD`,
err: "Found `` but expected `string`",
},
{
sql: `DEFINE LOGIN test ON NAMESPACE PASSWORD "123456"`,
str: `DEFINE LOGIN test ON NAMESPACE PASSHASH "123456"`,
res: &Query{Statements: []Statement{&DefineLoginStatement{
Kind: NAMESPACE,
User: &Ident{"test"},
Pass: []byte("123456"),
}}},
},
{
sql: `DEFINE LOGIN test ON DATABASE PASSWORD "123456"`,
str: `DEFINE LOGIN test ON DATABASE PASSHASH "123456"`,
res: &Query{Statements: []Statement{&DefineLoginStatement{
Kind: DATABASE,
User: &Ident{"test"},
Pass: []byte("123456"),
}}},
},
{
sql: `DEFINE LOGIN test ON NAMESPACE PASSWORD "123456" something`,
err: "Found `something` but expected `;`",
},
// ----------------------------------------------------------------------
{
sql: `DEFINE TOKEN`,
err: "Found `` but expected `name`",
},
{
sql: `DEFINE TOKEN 111`,
err: "Found `111` but expected `name`",
},
{
sql: `DEFINE TOKEN 111.111`,
err: "Found `111.111` but expected `name`",
},
{
sql: `DEFINE TOKEN test`,
err: "Found `` but expected `ON`",
},
{
sql: `DEFINE TOKEN test ON`,
err: "Found `` but expected `NAMESPACE, DATABASE, SCOPE`",
},
{
sql: `DEFINE TOKEN test ON something`,
err: "Found `something` but expected `NAMESPACE, DATABASE, SCOPE`",
},
{
sql: `DEFINE TOKEN test ON NAMESPACE`,
err: "Found `` but expected `TYPE`",
},
{
sql: `DEFINE TOKEN test ON NAMESPACE TYPE 100`,
err: "Found `100` but expected `ES256, ES384, ES512, HS256, HS384, HS512, PS256, PS384, PS512, RS256, RS384, RS512`",
},
{
sql: `DEFINE TOKEN test ON NAMESPACE TYPE XX512`,
err: "Found `XX512` but expected `ES256, ES384, ES512, HS256, HS384, HS512, PS256, PS384, PS512, RS256, RS384, RS512`",
},
{
sql: `DEFINE TOKEN test ON NAMESPACE TYPE HS512`,
err: "Found `` but expected `VALUE`",
},
{
sql: `DEFINE TOKEN test ON NAMESPACE TYPE HS512`,
err: "Found `` but expected `VALUE`",
},
{
sql: `DEFINE TOKEN test ON NAMESPACE TYPE HS512 VALUE`,
err: "Found `` but expected `string`",
},
{
sql: `DEFINE TOKEN test ON NAMESPACE TYPE HS512 VALUE "secret"`,
str: `DEFINE TOKEN test ON NAMESPACE TYPE HS512 VALUE "secret"`,
res: &Query{Statements: []Statement{&DefineTokenStatement{
Kind: NAMESPACE,
Name: &Ident{"test"},
What: &Ident{""},
Type: "HS512",
Code: []byte("secret"),
}}},
},
{
sql: `DEFINE TOKEN test ON DATABASE TYPE HS512 VALUE "secret"`,
str: `DEFINE TOKEN test ON DATABASE TYPE HS512 VALUE "secret"`,
res: &Query{Statements: []Statement{&DefineTokenStatement{
Kind: DATABASE,
Name: &Ident{"test"},
What: &Ident{""},
Type: "HS512",
Code: []byte("secret"),
}}},
},
{
sql: `DEFINE TOKEN test ON SCOPE test TYPE HS512 VALUE "secret"`,
str: `DEFINE TOKEN test ON SCOPE test TYPE HS512 VALUE "secret"`,
res: &Query{Statements: []Statement{&DefineTokenStatement{
Kind: SCOPE,
Name: &Ident{"test"},
What: &Ident{"test"},
Type: "HS512",
Code: []byte("secret"),
}}},
},
{
sql: `DEFINE TOKEN test ON SCOPE test TYPE HS512 VALUE "secret" something`,
err: "Found `something` but expected `;`",
},
// ----------------------------------------------------------------------
{
sql: `DEFINE SCOPE`,
err: "Found `` but expected `name`",
},
{
sql: `DEFINE SCOPE 111`,
err: "Found `111` but expected `name`",
},
{
sql: `DEFINE SCOPE 111.111`,
err: "Found `111.111` but expected `name`",
},
{
sql: `DEFINE SCOPE test`,
res: &Query{Statements: []Statement{&DefineScopeStatement{
Name: &Ident{"test"},
}}},
},
{
sql: `DEFINE SCOPE test SESSION null`,
err: "Found `null` but expected `duration`",
},
{
sql: `DEFINE SCOPE test SESSION 1h`,
str: `DEFINE SCOPE test SESSION 1h0m0s`,
res: &Query{Statements: []Statement{&DefineScopeStatement{
Name: &Ident{"test"},
Time: 1 * time.Hour,
}}},
},
{
sql: `DEFINE SCOPE test SESSION 1d`,
str: `DEFINE SCOPE test SESSION 24h0m0s`,
res: &Query{Statements: []Statement{&DefineScopeStatement{
Name: &Ident{"test"},
Time: 24 * time.Hour,
}}},
},
{
sql: `DEFINE SCOPE test SESSION 1w`,
str: `DEFINE SCOPE test SESSION 168h0m0s`,
res: &Query{Statements: []Statement{&DefineScopeStatement{
Name: &Ident{"test"},
Time: 168 * time.Hour,
}}},
},
{
sql: `DEFINE SCOPE test SIGNUP AS NONE`,
err: "Found `NONE` but expected `expression`",
},
{
sql: `DEFINE SCOPE test SIGNUP AS (CREATE person)`,
res: &Query{Statements: []Statement{&DefineScopeStatement{
Name: &Ident{"test"},
Signup: &SubExpression{
Expr: &CreateStatement{
What: Exprs{&Ident{"person"}},
Echo: AFTER,
},
},
}}},
},
{
sql: `DEFINE SCOPE test SIGNIN AS NONE`,
err: "Found `NONE` but expected `expression`",
},
{
sql: `DEFINE SCOPE test SIGNIN AS (SELECT * FROM person)`,
res: &Query{Statements: []Statement{&DefineScopeStatement{
Name: &Ident{"test"},
Signin: &SubExpression{
Expr: &SelectStatement{
Expr: []*Field{{Expr: &All{}, Field: "*"}},
What: []Expr{&Ident{"person"}},
},
},
}}},
},
{
sql: `DEFINE SCOPE test CONNECT AS NONE`,
err: "Found `NONE` but expected `expression`",
},
{
sql: `DEFINE SCOPE test CONNECT AS (SELECT * FROM $id)`,
res: &Query{Statements: []Statement{&DefineScopeStatement{
Name: &Ident{"test"},
Connect: &SubExpression{
Expr: &SelectStatement{
RW: false,
Expr: []*Field{{Expr: &All{}, Field: "*"}},
What: []Expr{&Param{"id"}},
},
},
}}},
},
{
sql: `DEFINE SCOPE test SIGNUP AS (CREATE tester) SIGNIN AS (SELECT * FROM tester) CONNECT AS (SELECT * FROM $id)`,
res: &Query{Statements: []Statement{&DefineScopeStatement{
Name: &Ident{"test"},
Signup: &SubExpression{
Expr: &CreateStatement{
What: []Expr{&Ident{"tester"}},
Echo: AFTER,
},
},
Signin: &SubExpression{
Expr: &SelectStatement{
RW: false,
Expr: []*Field{{Expr: &All{}, Field: "*"}},
What: []Expr{&Ident{"tester"}},
},
},
Connect: &SubExpression{
Expr: &SelectStatement{
RW: false,
Expr: []*Field{{Expr: &All{}, Field: "*"}},
What: []Expr{&Param{"id"}},
},
},
}}},
},
{
sql: `DEFINE SCOPE test something`,
err: "Found `something` but expected `;`",
},
// ----------------------------------------------------------------------
{
sql: `DEFINE TABLE`,
err: "Found `` but expected `table`",
},
{
sql: `DEFINE TABLE 111`,
err: "Found `111` but expected `table`",
},
{
sql: `DEFINE TABLE 111.111`,
err: "Found `111.111` but expected `table`",
},
{
sql: `DEFINE TABLE person`,
res: &Query{Statements: []Statement{&DefineTableStatement{
What: Tables{&Table{"person"}},
}}},
},
{
sql: `DEFINE TABLE person something`,
err: "Found `something` but expected `;`",
},
{
sql: `DEFINE TABLE person DROP`,
str: `DEFINE TABLE person DROP`,
res: &Query{Statements: []Statement{&DefineTableStatement{
What: Tables{&Table{"person"}},
Drop: true,
}}},
},
{
sql: `DEFINE TABLE person SCHEMALESS`,
str: `DEFINE TABLE person`,
res: &Query{Statements: []Statement{&DefineTableStatement{
What: Tables{&Table{"person"}},
Full: false,
}}},
},
{
sql: `DEFINE TABLE person SCHEMAFULL`,
res: &Query{Statements: []Statement{&DefineTableStatement{
What: Tables{&Table{"person"}},
Full: true,
}}},
},
{
sql: `DEFINE TABLE person PERMISSIONS SOME`,
err: "Found `SOME` but expected `FOR, NONE, FULL, WHERE`",
},
{
sql: `DEFINE TABLE person PERMISSIONS NONE`,
res: &Query{Statements: []Statement{&DefineTableStatement{
What: Tables{&Table{"person"}},
Perms: &PermExpression{
Select: false,
Create: false,
Update: false,
Delete: false,
},
}}},
},
{
sql: `DEFINE TABLE person PERMISSIONS FULL`,
res: &Query{Statements: []Statement{&DefineTableStatement{
What: Tables{&Table{"person"}},
Perms: &PermExpression{
Select: true,
Create: true,
Update: true,
Delete: true,
},
}}},
},
{
sql: `DEFINE TABLE person PERMISSIONS WHERE public = true`,
res: &Query{Statements: []Statement{&DefineTableStatement{
What: Tables{&Table{"person"}},
Perms: &PermExpression{
Select: &BinaryExpression{LHS: &Ident{"public"}, Op: EQ, RHS: true},
Create: &BinaryExpression{LHS: &Ident{"public"}, Op: EQ, RHS: true},
Update: &BinaryExpression{LHS: &Ident{"public"}, Op: EQ, RHS: true},
Delete: &BinaryExpression{LHS: &Ident{"public"}, Op: EQ, RHS: true},
},
}}},
},
{
sql: `DEFINE TABLE person PERMISSIONS FOR select FULL FOR insert, upsert NONE`,
err: "Found `insert` but expected `SELECT, CREATE, UPDATE, DELETE`",
},
{
sql: `DEFINE TABLE person PERMISSIONS FOR select FULL FOR create, update, delete SOME`,
err: "Found `SOME` but expected `FULL, NONE, WHERE`",
},
{
sql: `DEFINE TABLE person PERMISSIONS FOR select FULL FOR create, update, delete NONE`,
res: &Query{Statements: []Statement{&DefineTableStatement{
What: Tables{&Table{"person"}},
Perms: &PermExpression{
Select: true,
Create: false,
Update: false,
Delete: false,
},
}}},
},
{
sql: `DEFINE TABLE person PERMISSIONS FOR select, create, update WHERE public = true FOR delete NONE`,
res: &Query{Statements: []Statement{&DefineTableStatement{
What: Tables{&Table{"person"}},
Perms: &PermExpression{
Select: &BinaryExpression{LHS: &Ident{"public"}, Op: EQ, RHS: true},
Create: &BinaryExpression{LHS: &Ident{"public"}, Op: EQ, RHS: true},
Update: &BinaryExpression{LHS: &Ident{"public"}, Op: EQ, RHS: true},
Delete: false,
},
}}},
},
{
sql: `DEFINE TABLE person AS SELECT nationality, math.midhinge(age) AS mid FROM users GROUP BY nationality`,
err: "Found 'mid' but field is not an aggregate function, and is not present in GROUP expression",
},
{
sql: `DEFINE TABLE person AS SELECT nationality, count(*) AS total FROM users WHERE public = true GROUP BY nationality`,
res: &Query{Statements: []Statement{&DefineTableStatement{
What: Tables{&Table{"person"}},
Lock: true,
Expr: Fields{
&Field{
Expr: &Ident{"nationality"},
Field: "nationality",
},
&Field{
Expr: &FuncExpression{
Name: "count",
Args: Exprs{&All{}},
Aggr: true,
},
Field: "total",
Alias: "total",
},
},
From: Tables{
&Table{TB: "users"},
},
Cond: &BinaryExpression{
LHS: &Ident{"public"},
Op: EQ,
RHS: true,
},
Group: Groups{
&Group{
Expr: &Ident{"nationality"},
},
},
}}},
},
{
sql: `DEFINE TABLE person SCHEMALESS something`,
err: "Found `something` but expected `;`",
},
{
sql: `DEFINE TABLE person SCHEMAFULL something`,
err: "Found `something` but expected `;`",
},
// ----------------------------------------------------------------------
{
sql: `DEFINE EVENT`,
err: "Found `` but expected `name`",
},
{
sql: `DEFINE EVENT temp`,
err: "Found `` but expected `ON`",
},
{
sql: `DEFINE EVENT temp ON`,
err: "Found `` but expected `table`",
},
{
sql: `DEFINE EVENT temp ON person`,
err: "Found `` but expected `WHEN`",
},
{
sql: `DEFINE EVENT temp ON person WHEN`,
err: "Found `` but expected `expression`",
},
{
sql: `DEFINE EVENT temp ON person WHEN $before.price < $after.price`,
err: "Found `` but expected `THEN`",
},
{
sql: `DEFINE EVENT temp ON person WHEN $before.price < $after.price THEN`,
err: "Found `` but expected `(`",
},
{
sql: `DEFINE EVENT temp ON person WHEN $before.price < $after.price THEN (UPDATE $this SET increased = true)`,
res: &Query{Statements: []Statement{&DefineEventStatement{
Name: &Ident{"temp"},
What: Tables{&Table{"person"}},
When: &BinaryExpression{
LHS: &Param{"before.price"},
Op: LT,
RHS: &Param{"after.price"},
},
Then: &MultExpression{
Expr: []Expr{
&UpdateStatement{
What: Exprs{&Param{"this"}},
Data: &DataExpression{[]*ItemExpression{
{
LHS: &Ident{"increased"},
Op: EQ,
RHS: true,
},
}},
Echo: AFTER,
},
},
},
}}},
},
{
sql: `DEFINE EVENT temp ON person WHEN $before.price < $after.price THEN (UPDATE $this SET increased = true; UPDATE $this SET finished = true)`,
res: &Query{Statements: []Statement{&DefineEventStatement{
Name: &Ident{"temp"},
What: Tables{&Table{"person"}},
When: &BinaryExpression{
LHS: &Param{"before.price"},
Op: LT,
RHS: &Param{"after.price"},
},
Then: &MultExpression{
Expr: []Expr{
&UpdateStatement{
What: Exprs{&Param{"this"}},
Data: &DataExpression{[]*ItemExpression{
{
LHS: &Ident{"increased"},
Op: EQ,
RHS: true,
},
}},
Echo: AFTER,
},
&UpdateStatement{
What: Exprs{&Param{"this"}},
Data: &DataExpression{[]*ItemExpression{
{
LHS: &Ident{"finished"},
Op: EQ,
RHS: true,
},
}},
Echo: AFTER,
},
},
},
}}},
},
{
sql: `DEFINE EVENT temp ON person WHEN $before.price < $after.price THEN (UPDATE $this SET increased = true) something`,
err: "Found `something` but expected `;`",
},
// ----------------------------------------------------------------------
{
sql: `DEFINE FIELD`,
err: "Found `` but expected `name, or expression`",
},
{
sql: `DEFINE FIELD temp`,
err: "Found `` but expected `ON`",
},
{
sql: `DEFINE FIELD temp ON`,
err: "Found `` but expected `table`",
},
{
sql: `DEFINE FIELD temp ON person`,
res: &Query{Statements: []Statement{&DefineFieldStatement{
Name: &Ident{"temp"},
What: Tables{&Table{"person"}},
}}},
},
{
sql: `DEFINE FIELD temp ON person TYPE`,
err: "Found `` but expected `array, boolean, circle, datetime, number, object, point, polygon, record, string`",
},
{
sql: `DEFINE FIELD temp ON person TYPE something`,
err: "Found `something` but expected `array, boolean, circle, datetime, number, object, point, polygon, record, string`",
},
{
sql: `DEFINE FIELD temp ON person TYPE array`,
res: &Query{Statements: []Statement{&DefineFieldStatement{
Name: &Ident{"temp"},
What: Tables{&Table{"person"}},
Type: "array",
}}},
},
{
sql: `DEFINE FIELD temp ON person TYPE object`,
res: &Query{Statements: []Statement{&DefineFieldStatement{
Name: &Ident{"temp"},
What: Tables{&Table{"person"}},
Type: "object",
}}},
},
{
sql: `DEFINE FIELD temp ON person TYPE string`,
res: &Query{Statements: []Statement{&DefineFieldStatement{
Name: &Ident{"temp"},
What: Tables{&Table{"person"}},
Type: "string",
}}},
},
{
sql: `DEFINE FIELD temp ON person TYPE number`,
res: &Query{Statements: []Statement{&DefineFieldStatement{
Name: &Ident{"temp"},
What: Tables{&Table{"person"}},
Type: "number",
}}},
},
{
sql: `DEFINE FIELD temp ON person TYPE record`,
res: &Query{Statements: []Statement{&DefineFieldStatement{
Name: &Ident{"temp"},
What: Tables{&Table{"person"}},
Type: "record",
}}},
},
{
sql: `DEFINE FIELD temp ON person TYPE record (item)`,
res: &Query{Statements: []Statement{&DefineFieldStatement{
Name: &Ident{"temp"},
What: Tables{&Table{"person"}},
Type: "record",
Kind: "item",
}}},
},
{
sql: `DEFINE FIELD temp ON person VALUE`,
err: "Found `` but expected `expression`",
},
{
sql: `DEFINE FIELD temp ON person VALUE string.uppercase($value)`,
res: &Query{Statements: []Statement{&DefineFieldStatement{
Name: &Ident{"temp"},
What: Tables{&Table{"person"}},
Value: &FuncExpression{Name: "string.uppercase", Args: Exprs{&Param{"value"}}},
}}},
},
{
sql: `DEFINE FIELD temp ON person ASSERT`,
err: "Found `` but expected `expression`",
},
{
sql: `DEFINE FIELD temp ON person ASSERT $value > 0 AND $value < 100`,
res: &Query{Statements: []Statement{&DefineFieldStatement{
Name: &Ident{"temp"},
What: Tables{&Table{"person"}},
Assert: &BinaryExpression{
LHS: &BinaryExpression{
LHS: &Param{"value"},
Op: GT,
RHS: 0.0,
},
Op: AND,
RHS: &BinaryExpression{
LHS: &Param{"value"},
Op: LT,
RHS: 100.0,
},
},
}}},
},
{
sql: `DEFINE FIELD temp ON person PERMISSIONS SOME`,
err: "Found `SOME` but expected `FOR, NONE, FULL, WHERE`",
},
{
sql: `DEFINE FIELD temp ON person PERMISSIONS NONE`,
res: &Query{Statements: []Statement{&DefineFieldStatement{
Name: &Ident{"temp"},
What: Tables{&Table{"person"}},
Perms: &PermExpression{
Select: false,
Create: false,
Update: false,
Delete: false,
},
}}},
},
{
sql: `DEFINE FIELD temp ON person PERMISSIONS FULL`,
res: &Query{Statements: []Statement{&DefineFieldStatement{
Name: &Ident{"temp"},
What: Tables{&Table{"person"}},
Perms: &PermExpression{
Select: true,
Create: true,
Update: true,
Delete: true,
},
}}},
},
{
sql: `DEFINE FIELD temp ON person PERMISSIONS WHERE public = true`,
res: &Query{Statements: []Statement{&DefineFieldStatement{
Name: &Ident{"temp"},
What: Tables{&Table{"person"}},
Perms: &PermExpression{
Select: &BinaryExpression{LHS: &Ident{"public"}, Op: EQ, RHS: true},
Create: &BinaryExpression{LHS: &Ident{"public"}, Op: EQ, RHS: true},
Update: &BinaryExpression{LHS: &Ident{"public"}, Op: EQ, RHS: true},
Delete: &BinaryExpression{LHS: &Ident{"public"}, Op: EQ, RHS: true},
},
}}},
},
{
sql: `DEFINE FIELD temp ON person PERMISSIONS FOR select FULL FOR insert, upsert NONE`,
err: "Found `insert` but expected `SELECT, CREATE, UPDATE, DELETE`",
},
{
sql: `DEFINE FIELD temp ON person PERMISSIONS FOR select FULL FOR create, update, delete SOME`,
err: "Found `SOME` but expected `FULL, NONE, WHERE`",
},
{
sql: `DEFINE FIELD temp ON person PERMISSIONS FOR select FULL FOR create, update, delete NONE`,
res: &Query{Statements: []Statement{&DefineFieldStatement{
Name: &Ident{"temp"},
What: Tables{&Table{"person"}},
Perms: &PermExpression{
Select: true,
Create: false,
Update: false,
Delete: false,
},
}}},
},
{
sql: `DEFINE FIELD temp ON person PERMISSIONS FOR select, create, update WHERE public = true FOR delete NONE`,
res: &Query{Statements: []Statement{&DefineFieldStatement{
Name: &Ident{"temp"},
What: Tables{&Table{"person"}},
Perms: &PermExpression{
Select: &BinaryExpression{LHS: &Ident{"public"}, Op: EQ, RHS: true},
Create: &BinaryExpression{LHS: &Ident{"public"}, Op: EQ, RHS: true},
Update: &BinaryExpression{LHS: &Ident{"public"}, Op: EQ, RHS: true},
Delete: false,
},
}}},
},
{
sql: `DEFINE FIELD temp ON person something`,
err: "Found `something` but expected `;`",
},
// ----------------------------------------------------------------------
{
sql: `DEFINE INDEX`,
err: "Found `` but expected `name`",
},
{
sql: `DEFINE INDEX temp`,
err: "Found `` but expected `ON`",
},
{
sql: `DEFINE INDEX temp ON`,
err: "Found `` but expected `table`",
},
{
sql: `DEFINE INDEX temp ON person`,
err: "Found `` but expected `COLUMNS`",
},
{
sql: `DEFINE INDEX temp ON person COLUMNS`,
err: "Found `` but expected `name, or expression`",
},
{
sql: `DEFINE INDEX temp ON person COLUMNS firstname, lastname`,
res: &Query{Statements: []Statement{&DefineIndexStatement{
Name: &Ident{"temp"},
What: Tables{&Table{"person"}},
Cols: Idents{&Ident{"firstname"}, &Ident{"lastname"}},
Uniq: false,
}}},
},
{
sql: `DEFINE INDEX temp ON person COLUMNS firstname, lastname UNIQUE`,
res: &Query{Statements: []Statement{&DefineIndexStatement{
Name: &Ident{"temp"},
What: Tables{&Table{"person"}},
Cols: Idents{&Ident{"firstname"}, &Ident{"lastname"}},
Uniq: true,
}}},
},
{
sql: `DEFINE INDEX temp ON person COLUMNS firstname, lastname something UNIQUE`,
err: "Found `something` but expected `;`",
},
{
sql: `DEFINE INDEX temp ON person COLUMNS firstname, lastname UNIQUE something`,
err: "Found `something` but expected `;`",
},
}
for _, test := range tests {
testsql(t, test)
}
}
func Test_Parse_Queries_Remove(t *testing.T) {
var tests = []tester{
{
sql: `REMOVE`,
err: "Found `` but expected `NAMESPACE, DATABASE, LOGIN, TOKEN, SCOPE, TABLE, EVENT, FIELD, INDEX`",
},
// ----------------------------------------------------------------------
{
sql: `REMOVE NAMESPACE`,
err: "Found `` but expected `name`",
},
{
sql: `REMOVE NAMESPACE 111`,
err: "Found `111` but expected `name`",
},
{
sql: `REMOVE NAMESPACE 111.111`,
err: "Found `111.111` but expected `name`",
},
{
sql: `REMOVE NAMESPACE test`,
res: &Query{Statements: []Statement{&RemoveNamespaceStatement{
Name: &Ident{"test"},
}}},
},
{
sql: `REMOVE NAMESPACE test something`,
err: "Found `something` but expected `;`",
},
// ----------------------------------------------------------------------
{
sql: `REMOVE DATABASE`,
err: "Found `` but expected `name`",
},
{
sql: `REMOVE DATABASE 111`,
err: "Found `111` but expected `name`",
},
{
sql: `REMOVE DATABASE 111.111`,
err: "Found `111.111` but expected `name`",
},
{
sql: `REMOVE DATABASE test`,
res: &Query{Statements: []Statement{&RemoveDatabaseStatement{
Name: &Ident{"test"},
}}},
},
{
sql: `REMOVE DATABASE test something`,
err: "Found `something` but expected `;`",
},
// ----------------------------------------------------------------------
{
sql: `REMOVE LOGIN`,
err: "Found `` but expected `name`",
},
{
sql: `REMOVE LOGIN 111`,
err: "Found `111` but expected `name`",
},
{
sql: `REMOVE LOGIN 111.111`,
err: "Found `111.111` but expected `name`",
},
{
sql: `REMOVE LOGIN test`,
err: "Found `` but expected `ON`",
},
{
sql: `REMOVE LOGIN test ON`,
err: "Found `` but expected `NAMESPACE, DATABASE`",
},
{
sql: `REMOVE LOGIN test ON something`,
err: "Found `something` but expected `NAMESPACE, DATABASE`",
},
{
sql: `REMOVE LOGIN test ON NAMESPACE`,
res: &Query{Statements: []Statement{&RemoveLoginStatement{
Kind: NAMESPACE,
User: &Ident{"test"},
}}},
},
{
sql: `REMOVE LOGIN test ON DATABASE`,
res: &Query{Statements: []Statement{&RemoveLoginStatement{
Kind: DATABASE,
User: &Ident{"test"},
}}},
},
{
sql: `REMOVE LOGIN test ON DATABASE something`,
err: "Found `something` but expected `;`",
},
// ----------------------------------------------------------------------
{
sql: `REMOVE TOKEN`,
err: "Found `` but expected `name`",
},
{
sql: `REMOVE TOKEN 111`,
err: "Found `111` but expected `name`",
},
{
sql: `REMOVE TOKEN 111.111`,
err: "Found `111.111` but expected `name`",
},
{
sql: `REMOVE TOKEN test`,
err: "Found `` but expected `ON`",
},
{
sql: `REMOVE TOKEN test ON`,
err: "Found `` but expected `NAMESPACE, DATABASE, SCOPE`",
},
{
sql: `REMOVE TOKEN test ON something`,
err: "Found `something` but expected `NAMESPACE, DATABASE, SCOPE`",
},
{
sql: `REMOVE TOKEN test ON NAMESPACE`,
res: &Query{Statements: []Statement{&RemoveTokenStatement{
Kind: NAMESPACE,
Name: &Ident{"test"},
What: &Ident{""},
}}},
},
{
sql: `REMOVE TOKEN test ON DATABASE`,
res: &Query{Statements: []Statement{&RemoveTokenStatement{
Kind: DATABASE,
Name: &Ident{"test"},
What: &Ident{""},
}}},
},
{
sql: `REMOVE TOKEN test ON SCOPE`,
err: "Found `` but expected `name`",
},
{
sql: `REMOVE TOKEN test ON SCOPE test`,
res: &Query{Statements: []Statement{&RemoveTokenStatement{
Kind: SCOPE,
Name: &Ident{"test"},
What: &Ident{"test"},
}}},
},
{
sql: `REMOVE TOKEN test ON DATABASE something`,
err: "Found `something` but expected `;`",
},
// ----------------------------------------------------------------------
{
sql: `REMOVE SCOPE`,
err: "Found `` but expected `name`",
},
{
sql: `REMOVE SCOPE 111`,
err: "Found `111` but expected `name`",
},
{
sql: `REMOVE SCOPE 111.111`,
err: "Found `111.111` but expected `name`",
},
{
sql: `REMOVE SCOPE test`,
res: &Query{Statements: []Statement{&RemoveScopeStatement{
Name: &Ident{"test"},
}}},
},
{
sql: `REMOVE SCOPE test something`,
err: "Found `something` but expected `;`",
},
// ----------------------------------------------------------------------
{
sql: `REMOVE TABLE`,
err: "Found `` but expected `table`",
},
{
sql: `REMOVE TABLE 111`,
err: "Found `111` but expected `table`",
},
{
sql: `REMOVE TABLE 111.111`,
err: "Found `111.111` but expected `table`",
},
{
sql: `REMOVE TABLE person`,
res: &Query{Statements: []Statement{&RemoveTableStatement{
What: Tables{&Table{"person"}},
}}},
},
{
sql: `REMOVE TABLE person something`,
err: "Found `something` but expected `;`",
},
// ----------------------------------------------------------------------
{
sql: `REMOVE EVENT`,
err: "Found `` but expected `name`",
},
{
sql: `REMOVE EVENT temp`,
err: "Found `` but expected `ON`",
},
{
sql: `REMOVE EVENT temp ON`,
err: "Found `` but expected `table`",
},
{
sql: `REMOVE EVENT temp ON person`,
res: &Query{Statements: []Statement{&RemoveEventStatement{
Name: &Ident{"temp"},
What: Tables{&Table{"person"}},
}}},
},
{
sql: `REMOVE EVENT temp ON person something`,
err: "Found `something` but expected `;`",
},
// ----------------------------------------------------------------------
{
sql: `REMOVE FIELD`,
err: "Found `` but expected `name, or expression`",
},
{
sql: `REMOVE FIELD temp`,
err: "Found `` but expected `ON`",
},
{
sql: `REMOVE FIELD temp ON`,
err: "Found `` but expected `table`",
},
{
sql: `REMOVE FIELD temp ON person`,
res: &Query{Statements: []Statement{&RemoveFieldStatement{
Name: &Ident{"temp"},
What: Tables{&Table{"person"}},
}}},
},
{
sql: `REMOVE FIELD temp ON person something`,
err: "Found `something` but expected `;`",
},
// ----------------------------------------------------------------------
{
sql: `REMOVE INDEX`,
err: "Found `` but expected `name`",
},
{
sql: `REMOVE INDEX temp`,
err: "Found `` but expected `ON`",
},
{
sql: `REMOVE INDEX temp ON`,
err: "Found `` but expected `table`",
},
{
sql: `REMOVE INDEX temp ON person`,
res: &Query{Statements: []Statement{&RemoveIndexStatement{
Name: &Ident{"temp"},
What: Tables{&Table{"person"}},
}}},
},
{
sql: `REMOVE INDEX temp ON person something`,
err: "Found `something` but expected `;`",
},
}
for _, test := range tests {
testsql(t, test)
}
}
func Test_Parse_Queries_Begin(t *testing.T) {
var tests = []tester{
{
sql: `BEGIN`,
str: `BEGIN TRANSACTION`,
res: &Query{Statements: []Statement{&BeginStatement{}}},
},
{
sql: `BEGIN something`,
err: "Found `something` but expected `;`",
},
{
sql: `BEGIN TRANSACTION`,
res: &Query{Statements: []Statement{&BeginStatement{}}},
},
{
sql: `BEGIN TRANSACTION something`,
err: "Found `something` but expected `;`",
},
}
for _, test := range tests {
testsql(t, test)
}
}
func Test_Parse_Queries_Cancel(t *testing.T) {
var tests = []tester{
{
sql: `CANCEL`,
str: `CANCEL TRANSACTION`,
res: &Query{Statements: []Statement{&CancelStatement{}}},
},
{
sql: `CANCEL something`,
err: "Found `something` but expected `;`",
},
{
sql: `CANCEL TRANSACTION`,
res: &Query{Statements: []Statement{&CancelStatement{}}},
},
{
sql: `CANCEL TRANSACTION something`,
err: "Found `something` but expected `;`",
},
}
for _, test := range tests {
testsql(t, test)
}
}
func Test_Parse_Queries_Commit(t *testing.T) {
var tests = []tester{
{
sql: `COMMIT`,
str: `COMMIT TRANSACTION`,
res: &Query{Statements: []Statement{&CommitStatement{}}},
},
{
sql: `COMMIT something`,
err: "Found `something` but expected `;`",
},
{
sql: `COMMIT TRANSACTION`,
res: &Query{Statements: []Statement{&CommitStatement{}}},
},
{
sql: `COMMIT TRANSACTION something`,
err: "Found `something` but expected `;`",
},
}
for _, test := range tests {
testsql(t, test)
}
}