// Copyright © 2016 Abcum 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)
	}

}

// 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 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, INFO, BOTH, DIFF, BEFORE, AFTER`",
		},
		{
			sql: `CREATE person RETURN NONE`,
			res: &Query{Statements: []Statement{&CreateStatement{
				What: []Expr{&Ident{"person"}},
				Echo: NONE,
			}}},
		},
		{
			sql: `CREATE person RETURN BOTH`,
			res: &Query{Statements: []Statement{&CreateStatement{
				What: []Expr{&Ident{"person"}},
				Echo: BOTH,
			}}},
		},
		{
			sql: `CREATE person RETURN DIFF`,
			res: &Query{Statements: []Statement{&CreateStatement{
				What: []Expr{&Ident{"person"}},
				Echo: DIFF,
			}}},
		},
		{
			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 RETURN`,
			err: "Found `` but expected `NONE, INFO, BOTH, DIFF, BEFORE, AFTER`",
		},
		{
			sql: `UPDATE person RETURN NONE`,
			res: &Query{Statements: []Statement{&UpdateStatement{
				What: []Expr{&Ident{"person"}},
				Echo: NONE,
			}}},
		},
		{
			sql: `UPDATE person RETURN BOTH`,
			res: &Query{Statements: []Statement{&UpdateStatement{
				What: []Expr{&Ident{"person"}},
				Echo: BOTH,
			}}},
		},
		{
			sql: `UPDATE person RETURN DIFF`,
			res: &Query{Statements: []Statement{&UpdateStatement{
				What: []Expr{&Ident{"person"}},
				Echo: DIFF,
			}}},
		},
		{
			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, INFO, BOTH, DIFF, 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 BOTH`,
			res: &Query{Statements: []Statement{&DeleteStatement{
				What: []Expr{&Ident{"person"}},
				Echo: BOTH,
			}}},
		},
		{
			sql: `DELETE person RETURN DIFF`,
			res: &Query{Statements: []Statement{&DeleteStatement{
				What: []Expr{&Ident{"person"}},
				Echo: DIFF,
			}}},
		},
		{
			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 `table`",
		},
		{
			sql: `RELATE purchase`,
			err: "Found `` but expected `FROM`",
		},
		{
			sql: `RELATE purchase FROM`,
			err: "Found `` but expected `expression`",
		},
		{
			sql: `RELATE purchase FROM person`,
			err: "Found `` but expected `TO, WITH`",
		},
		{
			sql: `RELATE purchase FROM person WITH`,
			err: "Found `` but expected `expression`",
		},
		{
			sql: `RELATE purchase FROM person WITH item`,
			res: &Query{Statements: []Statement{&RelateStatement{
				Type: &Table{"purchase"},
				From: []Expr{&Ident{"person"}},
				With: []Expr{&Ident{"item"}},
				Echo: AFTER,
			}}},
		},
		{
			sql: `RELATE purchase FROM person WITH item UNIQUE`,
			res: &Query{Statements: []Statement{&RelateStatement{
				Type: &Table{"purchase"},
				From: []Expr{&Ident{"person"}},
				With: []Expr{&Ident{"item"}},
				Uniq: true,
				Echo: AFTER,
			}}},
		},
		{
			sql: `RELATE purchase FROM person WITH item SET 123`,
			err: "Found `123` but expected `field name`",
		},
		{
			sql: `RELATE purchase FROM person WITH item SET firstname`,
			err: "Found `` but expected `=, +=, -=`",
		},
		{
			sql: `RELATE purchase FROM person WITH 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 purchase FROM person WITH item RETURN`,
			err: "Found `` but expected `NONE, INFO, BOTH, DIFF, BEFORE, AFTER`",
		},
		{
			sql: `RELATE purchase FROM person WITH item RETURN NONE`,
			res: &Query{Statements: []Statement{&RelateStatement{
				Type: &Table{"purchase"},
				From: []Expr{&Ident{"person"}},
				With: []Expr{&Ident{"item"}},
				Echo: NONE,
			}}},
		},
		{
			sql: `RELATE purchase FROM person WITH item RETURN BOTH`,
			res: &Query{Statements: []Statement{&RelateStatement{
				Type: &Table{"purchase"},
				From: []Expr{&Ident{"person"}},
				With: []Expr{&Ident{"item"}},
				Echo: BOTH,
			}}},
		},
		{
			sql: `RELATE purchase FROM person WITH item RETURN DIFF`,
			res: &Query{Statements: []Statement{&RelateStatement{
				Type: &Table{"purchase"},
				From: []Expr{&Ident{"person"}},
				With: []Expr{&Ident{"item"}},
				Echo: DIFF,
			}}},
		},
		{
			sql: `RELATE purchase FROM person WITH item RETURN BEFORE`,
			res: &Query{Statements: []Statement{&RelateStatement{
				Type: &Table{"purchase"},
				From: []Expr{&Ident{"person"}},
				With: []Expr{&Ident{"item"}},
				Echo: BEFORE,
			}}},
		},
		{
			sql: `RELATE purchase FROM person WITH item RETURN AFTER`,
			str: `RELATE purchase FROM person WITH item`,
			res: &Query{Statements: []Statement{&RelateStatement{
				Type: &Table{"purchase"},
				From: []Expr{&Ident{"person"}},
				With: []Expr{&Ident{"item"}},
				Echo: AFTER,
			}}},
		},
		{
			sql: `RELATE purchase FROM person WITH 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 purchase FROM person WITH item TIMEOUT null`,
			err: "Found `null` but expected `duration`",
		},
		{
			sql: `RELATE purchase FROM person WITH 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, INFO, BOTH, DIFF, 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 INFO`,
			res: &Query{Statements: []Statement{&InsertStatement{
				Data: []interface{}{"one", "two", "tre"},
				Into: &Table{"person"},
				Echo: INFO,
			}}},
		},
		{
			sql: `INSERT ["one","two","tre"] INTO person RETURN BOTH`,
			res: &Query{Statements: []Statement{&InsertStatement{
				Data: []interface{}{"one", "two", "tre"},
				Into: &Table{"person"},
				Echo: BOTH,
			}}},
		},
		{
			sql: `INSERT ["one","two","tre"] INTO person RETURN DIFF`,
			res: &Query{Statements: []Statement{&InsertStatement{
				Data: []interface{}{"one", "two", "tre"},
				Into: &Table{"person"},
				Echo: DIFF,
			}}},
		},
		{
			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, INFO, BOTH, DIFF, 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 INFO`,
			res: &Query{Statements: []Statement{&UpsertStatement{
				Data: []interface{}{"one", "two", "tre"},
				Into: &Table{"person"},
				Echo: INFO,
			}}},
		},
		{
			sql: `UPSERT ["one","two","tre"] INTO person RETURN BOTH`,
			res: &Query{Statements: []Statement{&UpsertStatement{
				Data: []interface{}{"one", "two", "tre"},
				Into: &Table{"person"},
				Echo: BOTH,
			}}},
		},
		{
			sql: `UPSERT ["one","two","tre"] INTO person RETURN DIFF`,
			res: &Query{Statements: []Statement{&UpsertStatement{
				Data: []interface{}{"one", "two", "tre"},
				Into: &Table{"person"},
				Echo: DIFF,
			}}},
		},
		{
			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{
						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)
	}

}