blob: 045a0c57f2e5881ff7bada6d1faef77af174b48e [file] [log] [blame]
// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package query_checker_test
import (
"errors"
"fmt"
"reflect"
"testing"
"v.io/v23"
"v.io/v23/context"
ds "v.io/v23/query/engine/datasource"
"v.io/v23/query/engine/internal/query_checker"
"v.io/v23/query/engine/internal/query_parser"
"v.io/v23/query/syncql"
"v.io/v23/vdl"
"v.io/v23/verror"
_ "v.io/x/ref/runtime/factories/roaming"
"v.io/x/ref/test"
)
type mockDB struct {
ctx *context.T
}
func (db *mockDB) GetContext() *context.T {
return db.ctx
}
type customerTable struct {
}
type invoiceTable struct {
}
func init() {
var shutdown v23.Shutdown
db.ctx, shutdown = test.V23Init()
defer shutdown()
}
func (t invoiceTable) GetIndexFields() []ds.Index {
return []ds.Index{}
}
func (t invoiceTable) Scan(indexRanges ...ds.IndexRanges) (ds.KeyValueStream, error) {
return nil, errors.New("unimplemented")
}
func (t invoiceTable) Delete(k string) (bool, error) {
return false, errors.New("unimplemented")
}
func (t customerTable) GetIndexFields() []ds.Index {
return []ds.Index{}
}
func (t customerTable) Scan(indexRanges ...ds.IndexRanges) (ds.KeyValueStream, error) {
return nil, errors.New("unimplemented")
}
func (t customerTable) Delete(k string) (bool, error) {
return false, errors.New("unimplemented")
}
func (db *mockDB) GetTable(table string, writeAccessReq bool) (ds.Table, error) {
if table == "Customer" {
var t customerTable
return t, nil
} else if table == "Invoice" {
var t invoiceTable
return t, nil
}
return nil, errors.New(fmt.Sprintf("No such table: %s", table))
}
var db mockDB
type checkTest struct {
query string
}
type keyRangesTest struct {
query string
indexRanges *ds.IndexRanges
}
type indexRangesTest struct {
query string
fieldNames []string
indexRanges []*ds.IndexRanges
}
type regularExpressionsTest struct {
query string
regex string
matches []string
nonMatches []string
}
type checkErrorTest struct {
query string
err error
}
func TestQueryChecker(t *testing.T) {
basic := []checkTest{
// Select
{"select k, v from Customer"},
{"select k, v.name from Customer"},
{"select k, v.name from Customer limit 200"},
{"select k, v.name from Customer offset 100"},
{"select k, v.name from Customer where k = \"foo\""},
{"select v.z from Customer where k = v.y"},
{"select v.z from Customer where k <> v.y"},
{"select v.z from Customer where k < v.y"},
{"select v.z from Customer where k <= v.y"},
{"select v.z from Customer where k > v.y"},
{"select v.z from Customer where k >= v.y"},
{"select v from Customer where k is nil"},
{"select v from Customer where k is not nil"},
{"select k, v.name from Customer where \"foo\" = k"},
{"select v.z from Customer where v.y = k"},
{"select v.z from Customer where v.y <> k"},
{"select v.z from Customer where v.y < k"},
{"select v.z from Customer where v.y <= k"},
{"select v.z from Customer where v.y > k"},
{"select v.z from Customer where \"abc%\" = k"},
{"select v from Customer where k is nil"},
{"select v from Customer where k is not nil"},
{"select v from Customer where Type(v) = \"Foo.Bar\""},
{"select v from Customer where Type(v) <> \"Foo.Bar\""},
{"select v from Customer where Type(v) < \"Foo.Bar\""},
{"select v from Customer where Type(v) <= \"Foo.Bar\""},
{"select v from Customer where Type(v) > \"Foo.Bar\""},
{"select v from Customer where Type(v) >= \"Foo.Bar\""},
{"select v from Customer where Type(v) like \"%.Foo.Bar\""},
{"select v from Customer where Type(v) not like \"%.Foo.Bar\""},
{"select v from Customer where Type(v) is nil"},
{"select v from Customer where Type(v) is not nil"},
{"select v from Customer where \"Foo.Bar\" = Type(v)"},
{"select v from Customer where \"Foo.Bar\" <> Type(v)"},
{"select v from Customer where \"Foo.Bar\" < Type(v)"},
{"select v from Customer where \"Foo.Bar\" > Type(v)"},
{"select v from Customer where \"Foo.Bar\" <= Type(v)"},
{"select v from Customer where \"Foo.Bar\" >= Type(v)"},
{"select v.z from Customer where Type(v) = 2"},
{"select v.z from Customer where Type(v) <> \"foo\""},
{"select v.z from Customer where Type(v) < \"foo\""},
{"select v.z from Customer where Type(v) <= \"foo\""},
{"select v.z from Customer where Type(v) > \"foo\""},
{"select v.z from Customer where Type(v) >= \"foo\""},
{"select v.z from Customer where \"foo\" = Type(v)"},
{"select k, v from Customer where Type(v) = \"Foo.Bar\" and k like \"abc%\" limit 100 offset 200"},
{"select v from Customer where Type(v) is nil"},
{"select v from Customer where Type(v) is not nil"},
{"select v.z from Customer where k not like \"foo\""},
{"select v.z from Customer where k not like \"foo%\""},
{"select v from Customer where v.A = true"},
{"select v from Customer where v.A <> true"},
{"select v from Customer where false = v.A"},
{"select v from Customer where false = false"},
{"select v from Customer where true = true"},
{"select v from Customer where false = true"},
{"select v from Customer where true = false"},
{"select v from Customer where false <> true"},
{"select v from Customer where v.ZipCode is nil"},
{"select v from Customer where v.ZipCode Is Nil"},
{"select v from Customer where v.ZipCode is not nil"},
{"select v from Customer where v.ZipCode IS NOT NIL"},
{"select v from Customer where Now() < 10"},
{"select Now() from Customer"},
{"select Time(\"2006-01-02 MST\", \"2015-06-01 PST\"), Time(\"2006-01-02 15:04:05 MST\", \"2015-06-01 12:34:56 PST\"), Year(Now(), \"America/Los_Angeles\") from Customer"},
// Delete
{"delete from Customer"},
{"delete from Customer where k = v.name"},
{"delete from Customer limit 200"},
{"delete from Customer where k = \"foo\""},
{"delete from Customer where k = v.y"},
{"delete from Customer where k <> v.y"},
{"delete from Customer where k < v.y"},
{"delete from Customer where k <= v.y"},
{"delete from Customer where k > v.y"},
{"delete from Customer where k >= v.y"},
{"delete from Customer where k is nil"},
{"delete from Customer where k is not nil"},
{"delete from Customer where \"foo\" = k"},
{"delete from Customer where v.y = k"},
{"delete from Customer where v.y <> k"},
{"delete from Customer where v.y < k"},
{"delete from Customer where v.y <= k"},
{"delete from Customer where v.y > k"},
{"delete from Customer where \"abc%\" = k"},
{"delete from Customer where k is nil"},
{"delete from Customer where k is not nil"},
{"delete from Customer where Type(v) = \"Foo.Bar\""},
{"delete from Customer where Type(v) <> \"Foo.Bar\""},
{"delete from Customer where Type(v) < \"Foo.Bar\""},
{"delete from Customer where Type(v) <= \"Foo.Bar\""},
{"delete from Customer where Type(v) > \"Foo.Bar\""},
{"delete from Customer where Type(v) >= \"Foo.Bar\""},
{"delete from Customer where Type(v) like \"%.Foo.Bar\""},
{"delete from Customer where Type(v) not like \"%.Foo.Bar\""},
{"delete from Customer where Type(v) is nil"},
{"delete from Customer where Type(v) is not nil"},
{"delete from Customer where \"Foo.Bar\" = Type(v)"},
{"delete from Customer where \"Foo.Bar\" <> Type(v)"},
{"delete from Customer where \"Foo.Bar\" < Type(v)"},
{"delete from Customer where \"Foo.Bar\" > Type(v)"},
{"delete from Customer where \"Foo.Bar\" <= Type(v)"},
{"delete from Customer where \"Foo.Bar\" >= Type(v)"},
{"delete from Customer where Type(v) = 2"},
{"delete from Customer where Type(v) <> \"foo\""},
{"delete from Customer where Type(v) < \"foo\""},
{"delete from Customer where Type(v) <= \"foo\""},
{"delete from Customer where Type(v) > \"foo\""},
{"delete from Customer where Type(v) >= \"foo\""},
{"delete from Customer where \"foo\" = Type(v)"},
{"delete from Customer where Type(v) = \"Foo.Bar\" and k like \"abc%\" limit 100"},
{"delete from Customer where Type(v) is nil"},
{"delete from Customer where Type(v) is not nil"},
{"delete from Customer where k not like \"foo\""},
{"delete from Customer where k not like \"foo%\""},
{"delete from Customer where v.A = true"},
{"delete from Customer where v.A <> true"},
{"delete from Customer where false = v.A"},
{"delete from Customer where false = false"},
{"delete from Customer where true = true"},
{"delete from Customer where false = true"},
{"delete from Customer where true = false"},
{"delete from Customer where false <> true"},
{"delete from Customer where v.ZipCode is nil"},
{"delete from Customer where v.ZipCode Is Nil"},
{"delete from Customer where v.ZipCode is not nil"},
{"delete from Customer where v.ZipCode IS NOT NIL"},
{"delete from Customer where Now() < 10"},
{"delete from Customer where Now() > 0"},
{"delete from Customer where Time(\"2006-01-02 MST\", \"2015-06-01 PST\") > 0 and Time(\"2006-01-02 15:04:05 MST\", \"2015-06-01 12:34:56 PST\") > 1 and Year(Now(), \"America/Los_Angeles\") > 3"},
}
for _, test := range basic {
s, syntaxErr := query_parser.Parse(&db, test.query)
if syntaxErr != nil {
t.Errorf("query: %s; unexpected error: got %v, want nil", test.query, syntaxErr)
}
err := query_checker.Check(&db, s)
if err != nil {
t.Errorf("query: %s; got %v, want: nil", test.query, err)
}
}
}
func appendZeroByte(start string) string {
limit := []byte(start)
limit = append(limit, 0)
return string(limit)
}
func TestKeyRanges(t *testing.T) {
basic := []keyRangesTest{
// Select
{
"select k, v from Customer",
&ds.IndexRanges{
FieldName: "k",
Kind: vdl.String,
NilAllowed: true,
StringRanges: &ds.StringFieldRanges{
ds.StringFieldRange{Start: "", Limit: ""},
},
},
},
{
"select k, v from Customer where k = \"abc\" or k = \"def\"",
&ds.IndexRanges{
FieldName: "k",
Kind: vdl.String,
NilAllowed: false,
StringRanges: &ds.StringFieldRanges{
ds.StringFieldRange{Start: "abc", Limit: appendZeroByte("abc")},
ds.StringFieldRange{Start: "def", Limit: appendZeroByte("def")},
},
},
},
{
"select k, v from Customer where \"abc\" = k or \"def\" = k",
&ds.IndexRanges{
FieldName: "k",
Kind: vdl.String,
NilAllowed: false,
StringRanges: &ds.StringFieldRanges{
ds.StringFieldRange{Start: "abc", Limit: appendZeroByte("abc")},
ds.StringFieldRange{Start: "def", Limit: appendZeroByte("def")},
},
},
},
{
"select k, v from Customer where k >= \"foo\" and k < \"goo\"",
&ds.IndexRanges{
FieldName: "k",
Kind: vdl.String,
NilAllowed: false,
StringRanges: &ds.StringFieldRanges{
ds.StringFieldRange{Start: "foo", Limit: "goo"},
},
},
},
{
"select k, v from Customer where \"foo\" <= k and \"goo\" >= k",
&ds.IndexRanges{
FieldName: "k",
Kind: vdl.String,
NilAllowed: false,
StringRanges: &ds.StringFieldRanges{
ds.StringFieldRange{Start: "foo", Limit: appendZeroByte("goo")},
},
},
},
{
"select k, v from Customer where k <> \"foo\"",
&ds.IndexRanges{
FieldName: "k",
Kind: vdl.String,
NilAllowed: false,
StringRanges: &ds.StringFieldRanges{
ds.StringFieldRange{Start: "", Limit: "foo"},
ds.StringFieldRange{Start: appendZeroByte("foo"), Limit: ""},
},
},
},
{
"select k, v from Customer where k <> \"foo\" and k > \"bar\"",
&ds.IndexRanges{
FieldName: "k",
Kind: vdl.String,
NilAllowed: false,
StringRanges: &ds.StringFieldRanges{
ds.StringFieldRange{Start: appendZeroByte("bar"), Limit: "foo"},
ds.StringFieldRange{Start: appendZeroByte("foo"), Limit: ""},
},
},
},
{
"select k, v from Customer where k <> \"foo\" or k > \"bar\"",
&ds.IndexRanges{
FieldName: "k",
Kind: vdl.String,
NilAllowed: false,
StringRanges: &ds.StringFieldRanges{
ds.StringFieldRange{Start: "", Limit: ""},
},
},
},
{
"select k, v from Customer where k <> \"bar\" or k > \"foo\"",
&ds.IndexRanges{
FieldName: "k",
Kind: vdl.String,
NilAllowed: false,
StringRanges: &ds.StringFieldRanges{
ds.StringFieldRange{Start: "", Limit: "bar"},
ds.StringFieldRange{Start: appendZeroByte("bar"), Limit: ""},
},
},
},
{
"select v from Customer where Type(v) = \"Foo.Bar\" and k >= \"100\" and k < \"200\" and v.foo > 50 and v.bar <= 1000 and v.baz <> -20.7",
&ds.IndexRanges{
FieldName: "k",
Kind: vdl.String,
NilAllowed: false,
StringRanges: &ds.StringFieldRanges{
ds.StringFieldRange{Start: "100", Limit: "200"},
},
},
},
{
"select k, v from Customer where k = \"abc\" and k = \"def\"",
&ds.IndexRanges{
FieldName: "k",
Kind: vdl.String,
NilAllowed: false,
StringRanges: &ds.StringFieldRanges{},
},
},
{
"select k, v from Customer where Type(v) = \"Foo.Bar\" and k like \"abc%\" limit 100 offset 200",
&ds.IndexRanges{
FieldName: "k",
Kind: vdl.String,
NilAllowed: false,
StringRanges: &ds.StringFieldRanges{
ds.StringFieldRange{Start: "abc", Limit: "abd"},
},
},
},
{
"select k, v from \n Customer where k like \"002%\" or k like \"001%\" or k like \"%\"",
&ds.IndexRanges{
FieldName: "k",
Kind: vdl.String,
NilAllowed: false,
StringRanges: &ds.StringFieldRanges{
ds.StringFieldRange{Start: "", Limit: ""},
},
},
},
{
"select k, v from Customer where k = \"Foo.Bar\" and k like \"abc%\" limit 100 offset 200",
&ds.IndexRanges{
FieldName: "k",
Kind: vdl.String,
NilAllowed: false,
StringRanges: &ds.StringFieldRanges{},
},
},
{
"select k, v from Customer where k like \"foo%\" and k like \"bar%\"",
&ds.IndexRanges{
FieldName: "k",
Kind: vdl.String,
NilAllowed: false,
StringRanges: &ds.StringFieldRanges{},
},
},
{
"select k, v from Customer where k like \"foo%\" or k like \"bar%\"",
&ds.IndexRanges{
FieldName: "k",
Kind: vdl.String,
NilAllowed: false,
StringRanges: &ds.StringFieldRanges{
ds.StringFieldRange{Start: "bar", Limit: "bas"},
ds.StringFieldRange{Start: "foo", Limit: "fop"},
},
},
},
{
// Note: 'like "Foo"' is optimized to '= "Foo"
"select k, v from Customer where k = \"Foo.Bar\" or k like \"Foo\" or k like \"abc%\" limit 100 offset 200",
&ds.IndexRanges{
FieldName: "k",
Kind: vdl.String,
NilAllowed: false,
StringRanges: &ds.StringFieldRanges{
ds.StringFieldRange{Start: "Foo", Limit: appendZeroByte("Foo")},
ds.StringFieldRange{Start: "Foo.Bar", Limit: appendZeroByte("Foo.Bar")},
ds.StringFieldRange{Start: "abc", Limit: "abd"},
},
},
},
{
"select k, v from Customer where k like \"Foo@%Bar\" or k like \"abc%\" limit 100 offset 200 escape '@'",
&ds.IndexRanges{
FieldName: "k",
Kind: vdl.String,
NilAllowed: false,
StringRanges: &ds.StringFieldRanges{
ds.StringFieldRange{Start: "Foo%Bar", Limit: appendZeroByte("Foo%Bar")},
ds.StringFieldRange{Start: "abc", Limit: "abd"},
},
},
},
{
"select k, v from Customer where k like \"Foo%Bar\" or k like \"abc%\" limit 100 offset 200",
&ds.IndexRanges{
FieldName: "k",
Kind: vdl.String,
NilAllowed: false,
StringRanges: &ds.StringFieldRanges{
ds.StringFieldRange{Start: "Foo", Limit: "Fop"},
ds.StringFieldRange{Start: "abc", Limit: "abd"},
},
},
},
{
"select k, v from Customer where k like \"Foo#%Bar\" or k like \"abc%\" escape '#' limit 100 offset 200",
&ds.IndexRanges{
FieldName: "k",
Kind: vdl.String,
NilAllowed: false,
StringRanges: &ds.StringFieldRanges{
ds.StringFieldRange{Start: "Foo%Bar", Limit: appendZeroByte("Foo%Bar")},
ds.StringFieldRange{Start: "abc", Limit: "abd"},
},
},
},
{
"select k, v from Customer where k not like \"002%\"",
&ds.IndexRanges{
FieldName: "k",
Kind: vdl.String,
NilAllowed: false,
StringRanges: &ds.StringFieldRanges{
ds.StringFieldRange{Start: "", Limit: "002"},
ds.StringFieldRange{Start: "003", Limit: ""},
},
},
},
// Delete
{
"delete from Customer",
&ds.IndexRanges{
FieldName: "k",
Kind: vdl.String,
NilAllowed: true,
StringRanges: &ds.StringFieldRanges{
ds.StringFieldRange{Start: "", Limit: ""},
},
},
},
{
"delete from Customer where k = \"abc\" or k = \"def\"",
&ds.IndexRanges{
FieldName: "k",
Kind: vdl.String,
NilAllowed: false,
StringRanges: &ds.StringFieldRanges{
ds.StringFieldRange{Start: "abc", Limit: appendZeroByte("abc")},
ds.StringFieldRange{Start: "def", Limit: appendZeroByte("def")},
},
},
},
{
"delete from Customer where \"abc\" = k or \"def\" = k",
&ds.IndexRanges{
FieldName: "k",
Kind: vdl.String,
NilAllowed: false,
StringRanges: &ds.StringFieldRanges{
ds.StringFieldRange{Start: "abc", Limit: appendZeroByte("abc")},
ds.StringFieldRange{Start: "def", Limit: appendZeroByte("def")},
},
},
},
{
"delete from Customer where k >= \"foo\" and k < \"goo\"",
&ds.IndexRanges{
FieldName: "k",
Kind: vdl.String,
NilAllowed: false,
StringRanges: &ds.StringFieldRanges{
ds.StringFieldRange{Start: "foo", Limit: "goo"},
},
},
},
{
"delete from Customer where \"foo\" <= k and \"goo\" >= k",
&ds.IndexRanges{
FieldName: "k",
Kind: vdl.String,
NilAllowed: false,
StringRanges: &ds.StringFieldRanges{
ds.StringFieldRange{Start: "foo", Limit: appendZeroByte("goo")},
},
},
},
{
"delete from Customer where k <> \"foo\"",
&ds.IndexRanges{
FieldName: "k",
Kind: vdl.String,
NilAllowed: false,
StringRanges: &ds.StringFieldRanges{
ds.StringFieldRange{Start: "", Limit: "foo"},
ds.StringFieldRange{Start: appendZeroByte("foo"), Limit: ""},
},
},
},
{
"delete from Customer where k <> \"foo\" and k > \"bar\"",
&ds.IndexRanges{
FieldName: "k",
Kind: vdl.String,
NilAllowed: false,
StringRanges: &ds.StringFieldRanges{
ds.StringFieldRange{Start: appendZeroByte("bar"), Limit: "foo"},
ds.StringFieldRange{Start: appendZeroByte("foo"), Limit: ""},
},
},
},
{
"delete from Customer where k <> \"foo\" or k > \"bar\"",
&ds.IndexRanges{
FieldName: "k",
Kind: vdl.String,
NilAllowed: false,
StringRanges: &ds.StringFieldRanges{
ds.StringFieldRange{Start: "", Limit: ""},
},
},
},
{
"delete from Customer where k <> \"bar\" or k > \"foo\"",
&ds.IndexRanges{
FieldName: "k",
Kind: vdl.String,
NilAllowed: false,
StringRanges: &ds.StringFieldRanges{
ds.StringFieldRange{Start: "", Limit: "bar"},
ds.StringFieldRange{Start: appendZeroByte("bar"), Limit: ""},
},
},
},
{
"select v from Customer where Type(v) = \"Foo.Bar\" and k >= \"100\" and k < \"200\" and v.foo > 50 and v.bar <= 1000 and v.baz <> -20.7",
&ds.IndexRanges{
FieldName: "k",
Kind: vdl.String,
NilAllowed: false,
StringRanges: &ds.StringFieldRanges{
ds.StringFieldRange{Start: "100", Limit: "200"},
},
},
},
{
"delete from Customer where k = \"abc\" and k = \"def\"",
&ds.IndexRanges{
FieldName: "k",
Kind: vdl.String,
NilAllowed: false,
StringRanges: &ds.StringFieldRanges{},
},
},
{
"delete from Customer where Type(v) = \"Foo.Bar\" and k like \"abc%\" limit 100",
&ds.IndexRanges{
FieldName: "k",
Kind: vdl.String,
NilAllowed: false,
StringRanges: &ds.StringFieldRanges{
ds.StringFieldRange{Start: "abc", Limit: "abd"},
},
},
},
{
"select k, v from \n Customer where k like \"002%\" or k like \"001%\" or k like \"%\"",
&ds.IndexRanges{
FieldName: "k",
Kind: vdl.String,
NilAllowed: false,
StringRanges: &ds.StringFieldRanges{
ds.StringFieldRange{Start: "", Limit: ""},
},
},
},
{
"delete from Customer where k = \"Foo.Bar\" and k like \"abc%\" limit 100",
&ds.IndexRanges{
FieldName: "k",
Kind: vdl.String,
NilAllowed: false,
StringRanges: &ds.StringFieldRanges{},
},
},
{
"delete from Customer where k like \"foo%\" and k like \"bar%\"",
&ds.IndexRanges{
FieldName: "k",
Kind: vdl.String,
NilAllowed: false,
StringRanges: &ds.StringFieldRanges{},
},
},
{
"delete from Customer where k like \"foo%\" or k like \"bar%\"",
&ds.IndexRanges{
FieldName: "k",
Kind: vdl.String,
NilAllowed: false,
StringRanges: &ds.StringFieldRanges{
ds.StringFieldRange{Start: "bar", Limit: "bas"},
ds.StringFieldRange{Start: "foo", Limit: "fop"},
},
},
},
{
// Note: 'like "Foo"' is optimized to '= "Foo"
"delete from Customer where k = \"Foo.Bar\" or k like \"Foo\" or k like \"abc%\" limit 100",
&ds.IndexRanges{
FieldName: "k",
Kind: vdl.String,
NilAllowed: false,
StringRanges: &ds.StringFieldRanges{
ds.StringFieldRange{Start: "Foo", Limit: appendZeroByte("Foo")},
ds.StringFieldRange{Start: "Foo.Bar", Limit: appendZeroByte("Foo.Bar")},
ds.StringFieldRange{Start: "abc", Limit: "abd"},
},
},
},
{
"delete from Customer where k like \"Foo@%Bar\" or k like \"abc%\" limit 100 escape '@'",
&ds.IndexRanges{
FieldName: "k",
Kind: vdl.String,
NilAllowed: false,
StringRanges: &ds.StringFieldRanges{
ds.StringFieldRange{Start: "Foo%Bar", Limit: appendZeroByte("Foo%Bar")},
ds.StringFieldRange{Start: "abc", Limit: "abd"},
},
},
},
{
"delete from Customer where k like \"Foo%Bar\" or k like \"abc%\" limit 100",
&ds.IndexRanges{
FieldName: "k",
Kind: vdl.String,
NilAllowed: false,
StringRanges: &ds.StringFieldRanges{
ds.StringFieldRange{Start: "Foo", Limit: "Fop"},
ds.StringFieldRange{Start: "abc", Limit: "abd"},
},
},
},
{
"delete from Customer where k like \"Foo#%Bar\" or k like \"abc%\" escape '#' limit 100",
&ds.IndexRanges{
FieldName: "k",
Kind: vdl.String,
NilAllowed: false,
StringRanges: &ds.StringFieldRanges{
ds.StringFieldRange{Start: "Foo%Bar", Limit: appendZeroByte("Foo%Bar")},
ds.StringFieldRange{Start: "abc", Limit: "abd"},
},
},
},
{
"delete from Customer where k not like \"002%\"",
&ds.IndexRanges{
FieldName: "k",
Kind: vdl.String,
NilAllowed: false,
StringRanges: &ds.StringFieldRanges{
ds.StringFieldRange{Start: "", Limit: "002"},
ds.StringFieldRange{Start: "003", Limit: ""},
},
},
},
}
for _, test := range basic {
s, syntaxErr := query_parser.Parse(&db, test.query)
if syntaxErr != nil {
t.Errorf("query: %s; unexpected error: got %v, want nil", test.query, syntaxErr)
}
err := query_checker.Check(&db, s)
if err != nil {
t.Errorf("query: %s; got %v, want: nil", test.query, err)
}
switch sel := (*s).(type) {
case query_parser.SelectStatement:
indexRanges := query_checker.CompileIndexRanges(&query_parser.Field{Segments: []query_parser.Segment{query_parser.Segment{Value: "k"}}}, vdl.String, sel.Where)
if !reflect.DeepEqual(test.indexRanges, indexRanges) {
t.Errorf("query: %s;\nGOT %s\nWANT %s", test.query, indexRanges.String(), test.indexRanges.String())
}
case query_parser.DeleteStatement:
indexRanges := query_checker.CompileIndexRanges(&query_parser.Field{Segments: []query_parser.Segment{query_parser.Segment{Value: "k"}}}, vdl.String, sel.Where)
if !reflect.DeepEqual(test.indexRanges, indexRanges) {
t.Errorf("query: %s;\nGOT %s\nWANT %s", test.query, indexRanges.String(), test.indexRanges.String())
}
default:
t.Errorf("query: %s;\nGOT %v\nWANT select or delete statement", test.query, *s)
}
}
}
func TestIndexRanges(t *testing.T) {
basic := []indexRangesTest{
// Select
{
"select k, v from Customer",
[]string{"v.InterfaceName"},
[]*ds.IndexRanges{
&ds.IndexRanges{
FieldName: "v.InterfaceName",
Kind: vdl.String,
NilAllowed: true,
StringRanges: &ds.StringFieldRanges{
ds.StringFieldRange{Start: "", Limit: ""},
},
},
},
},
{
"select k, v from Customer where v.Foo = 12",
[]string{"v.InterfaceName"},
[]*ds.IndexRanges{
&ds.IndexRanges{
FieldName: "v.InterfaceName",
Kind: vdl.String,
NilAllowed: true,
StringRanges: &ds.StringFieldRanges{
ds.StringFieldRange{Start: "", Limit: ""},
},
},
},
},
{
"select k, v from Customer where v.InterfaceName = \"FooBar\"",
[]string{"v.InterfaceName"},
[]*ds.IndexRanges{
&ds.IndexRanges{
FieldName: "v.InterfaceName",
Kind: vdl.String,
NilAllowed: false,
StringRanges: &ds.StringFieldRanges{
ds.StringFieldRange{Start: "FooBar", Limit: appendZeroByte("FooBar")},
},
},
},
},
{
"select k, v from Customer where v.InterfaceName = \"Foo\" or v.InterfaceName = \"Bar\"",
[]string{"v.InterfaceName"},
[]*ds.IndexRanges{
&ds.IndexRanges{
FieldName: "v.InterfaceName",
Kind: vdl.String,
NilAllowed: false,
StringRanges: &ds.StringFieldRanges{
ds.StringFieldRange{Start: "Bar", Limit: appendZeroByte("Bar")},
ds.StringFieldRange{Start: "Foo", Limit: appendZeroByte("Foo")},
},
},
},
},
{
"select k, v from Customer where v.InterfaceName = \"Foo\" and v.InterfaceName = \"Bar\"",
[]string{"v.InterfaceName"},
[]*ds.IndexRanges{
&ds.IndexRanges{
FieldName: "v.InterfaceName",
Kind: vdl.String,
NilAllowed: false,
StringRanges: &ds.StringFieldRanges{},
},
},
},
{
"select k, v from Customer where v.InterfaceName like \"Foo%\"",
[]string{"v.InterfaceName"},
[]*ds.IndexRanges{
&ds.IndexRanges{
FieldName: "v.InterfaceName",
Kind: vdl.String,
NilAllowed: false,
StringRanges: &ds.StringFieldRanges{
ds.StringFieldRange{Start: "Foo", Limit: "Fop"},
},
},
},
},
{
"select k, v from Customer where v.InterfaceName like \"Foo%\" and v.Address like \"Bar%\"",
[]string{"v.InterfaceName", "v.Address"},
[]*ds.IndexRanges{
&ds.IndexRanges{
FieldName: "v.InterfaceName",
Kind: vdl.String,
NilAllowed: false,
StringRanges: &ds.StringFieldRanges{
ds.StringFieldRange{Start: "Foo", Limit: "Fop"},
},
},
&ds.IndexRanges{
FieldName: "v.Address",
Kind: vdl.String,
NilAllowed: false,
StringRanges: &ds.StringFieldRanges{
ds.StringFieldRange{Start: "Bar", Limit: "Bas"},
},
},
},
},
{
"select k, v from Customer where \"abc\" = v.Foo or \"def\" = v.Foo",
[]string{"v.Foo"},
[]*ds.IndexRanges{
&ds.IndexRanges{
FieldName: "v.Foo",
Kind: vdl.String,
NilAllowed: false,
StringRanges: &ds.StringFieldRanges{
ds.StringFieldRange{Start: "abc", Limit: appendZeroByte("abc")},
ds.StringFieldRange{Start: "def", Limit: appendZeroByte("def")},
},
},
},
},
{
"select k, v from Customer where v.Foo >= \"foo\" and v.Foo < \"goo\"",
[]string{"v.Foo"},
[]*ds.IndexRanges{
&ds.IndexRanges{
FieldName: "v.Foo",
Kind: vdl.String,
NilAllowed: false,
StringRanges: &ds.StringFieldRanges{
ds.StringFieldRange{Start: "foo", Limit: "goo"},
},
},
},
},
{
"select k, v from Customer where \"foo\" <= v.Foo and \"goo\" >= v.Foo",
[]string{"v.Foo"},
[]*ds.IndexRanges{
&ds.IndexRanges{
FieldName: "v.Foo",
Kind: vdl.String,
NilAllowed: false,
StringRanges: &ds.StringFieldRanges{
ds.StringFieldRange{Start: "foo", Limit: appendZeroByte("goo")},
},
},
},
},
{
"select k, v from Customer where v.Foo <> \"foo\"",
[]string{"v.Foo"},
[]*ds.IndexRanges{
&ds.IndexRanges{
FieldName: "v.Foo",
Kind: vdl.String,
NilAllowed: false,
StringRanges: &ds.StringFieldRanges{
ds.StringFieldRange{Start: "", Limit: "foo"},
ds.StringFieldRange{Start: appendZeroByte("foo"), Limit: ""},
},
},
},
},
{
"select k, v from Customer where v.Foo <> \"foo\" and v.Foo > \"bar\"",
[]string{"v.Foo"},
[]*ds.IndexRanges{
&ds.IndexRanges{
FieldName: "v.Foo",
Kind: vdl.String,
NilAllowed: false,
StringRanges: &ds.StringFieldRanges{
ds.StringFieldRange{Start: appendZeroByte("bar"), Limit: "foo"},
ds.StringFieldRange{Start: appendZeroByte("foo"), Limit: ""},
},
},
},
},
{
"select k, v from Customer where v.Foo <> \"foo\" or v.Foo > \"bar\"",
[]string{"v.Foo"},
[]*ds.IndexRanges{
&ds.IndexRanges{
FieldName: "v.Foo",
Kind: vdl.String,
NilAllowed: false,
StringRanges: &ds.StringFieldRanges{
ds.StringFieldRange{Start: "", Limit: ""},
},
},
},
},
{
"select k, v from Customer where v.Foo <> \"bar\" or v.Foo > \"foo\"",
[]string{"v.Foo"},
[]*ds.IndexRanges{
&ds.IndexRanges{
FieldName: "v.Foo",
Kind: vdl.String,
NilAllowed: false,
StringRanges: &ds.StringFieldRanges{
ds.StringFieldRange{Start: "", Limit: "bar"},
ds.StringFieldRange{Start: appendZeroByte("bar"), Limit: ""},
},
},
},
},
{
"select v from Customer where Type(v) = \"Foo.Bar\" and v.Foo >= \"100\" and v.Foo < \"200\" and v.foo > 50 and v.bar <= 1000 and v.baz <> -20.7",
[]string{"v.Foo"},
[]*ds.IndexRanges{
&ds.IndexRanges{
FieldName: "v.Foo",
Kind: vdl.String,
NilAllowed: false,
StringRanges: &ds.StringFieldRanges{
ds.StringFieldRange{Start: "100", Limit: "200"},
},
},
},
},
{
"select k, v from Customer where v.Foo = \"abc\" and v.Foo = \"def\"",
[]string{"v.Foo"},
[]*ds.IndexRanges{
&ds.IndexRanges{
FieldName: "v.Foo",
Kind: vdl.String,
NilAllowed: false,
StringRanges: &ds.StringFieldRanges{},
},
},
},
{
"select k, v from Customer where Type(v) = \"Foo.Bar\" and v.Foo like \"abc%\" limit 100 offset 200",
[]string{"v.Foo"},
[]*ds.IndexRanges{
&ds.IndexRanges{
FieldName: "v.Foo",
Kind: vdl.String,
NilAllowed: false,
StringRanges: &ds.StringFieldRanges{
ds.StringFieldRange{Start: "abc", Limit: "abd"},
},
},
},
},
{
"select k, v from \n Customer where v.Foo like \"002%\" or v.Foo like \"001%\" or v.Foo like \"%\"",
[]string{"v.Foo"},
[]*ds.IndexRanges{
&ds.IndexRanges{
FieldName: "v.Foo",
Kind: vdl.String,
NilAllowed: false,
StringRanges: &ds.StringFieldRanges{
ds.StringFieldRange{Start: "", Limit: ""},
},
},
},
},
{
"select k, v from Customer where v.Foo = \"Foo.Bar\" and v.Foo like \"abc%\" limit 100 offset 200",
[]string{"v.Foo"},
[]*ds.IndexRanges{
&ds.IndexRanges{
FieldName: "v.Foo",
Kind: vdl.String,
NilAllowed: false,
StringRanges: &ds.StringFieldRanges{},
},
},
},
{
"select k, v from Customer where v.Foo like \"foo%\" and v.Foo like \"bar%\"",
[]string{"v.Foo"},
[]*ds.IndexRanges{
&ds.IndexRanges{
FieldName: "v.Foo",
Kind: vdl.String,
NilAllowed: false,
StringRanges: &ds.StringFieldRanges{},
},
},
},
{
"select k, v from Customer where v.Foo like \"foo%\" or v.Foo like \"bar%\"",
[]string{"v.Foo"},
[]*ds.IndexRanges{
&ds.IndexRanges{
FieldName: "v.Foo",
Kind: vdl.String,
NilAllowed: false,
StringRanges: &ds.StringFieldRanges{
ds.StringFieldRange{Start: "bar", Limit: "bas"},
ds.StringFieldRange{Start: "foo", Limit: "fop"},
},
},
},
},
{
// Note: 'like "Foo"' is optimized to '= "Foo"
"select k, v from Customer where v.Foo = \"Foo.Bar\" or v.Foo like \"Foo\" or v.Foo like \"abc%\" limit 100 offset 200",
[]string{"v.Foo"},
[]*ds.IndexRanges{
&ds.IndexRanges{
FieldName: "v.Foo",
Kind: vdl.String,
NilAllowed: false,
StringRanges: &ds.StringFieldRanges{
ds.StringFieldRange{Start: "Foo", Limit: appendZeroByte("Foo")},
ds.StringFieldRange{Start: "Foo.Bar", Limit: appendZeroByte("Foo.Bar")},
ds.StringFieldRange{Start: "abc", Limit: "abd"},
},
},
},
},
{
"select k, v from Customer where v.Foo like \"Foo@%Bar\" or v.Foo like \"abc%\" limit 100 offset 200 escape '@'",
[]string{"v.Foo"},
[]*ds.IndexRanges{
&ds.IndexRanges{
FieldName: "v.Foo",
Kind: vdl.String,
NilAllowed: false,
StringRanges: &ds.StringFieldRanges{
ds.StringFieldRange{Start: "Foo%Bar", Limit: appendZeroByte("Foo%Bar")},
ds.StringFieldRange{Start: "abc", Limit: "abd"},
},
},
},
},
{
"select k, v from Customer where v.Foo like \"Foo%Bar\" or v.Foo like \"abc%\" limit 100 offset 200",
[]string{"v.Foo"},
[]*ds.IndexRanges{
&ds.IndexRanges{
FieldName: "v.Foo",
Kind: vdl.String,
NilAllowed: false,
StringRanges: &ds.StringFieldRanges{
ds.StringFieldRange{Start: "Foo", Limit: "Fop"},
ds.StringFieldRange{Start: "abc", Limit: "abd"},
},
},
},
},
{
"select k, v from Customer where v.Foo like \"Foo#%Bar\" or v.Foo like \"abc%\" escape '#' limit 100 offset 200",
[]string{"v.Foo"},
[]*ds.IndexRanges{
&ds.IndexRanges{
FieldName: "v.Foo",
Kind: vdl.String,
NilAllowed: false,
StringRanges: &ds.StringFieldRanges{
ds.StringFieldRange{Start: "Foo%Bar", Limit: appendZeroByte("Foo%Bar")},
ds.StringFieldRange{Start: "abc", Limit: "abd"},
},
},
},
},
{
"select k, v from Customer where v.Foo not like \"002%\"",
[]string{"v.Foo"},
[]*ds.IndexRanges{
&ds.IndexRanges{
FieldName: "v.Foo",
Kind: vdl.String,
NilAllowed: false,
StringRanges: &ds.StringFieldRanges{
ds.StringFieldRange{Start: "", Limit: "002"},
ds.StringFieldRange{Start: "003", Limit: ""},
},
},
},
},
// delete
{
"delete from Customer",
[]string{"v.InterfaceName"},
[]*ds.IndexRanges{
&ds.IndexRanges{
FieldName: "v.InterfaceName",
Kind: vdl.String,
NilAllowed: true,
StringRanges: &ds.StringFieldRanges{
ds.StringFieldRange{Start: "", Limit: ""},
},
},
},
},
{
"delete from Customer where v.Foo = 12",
[]string{"v.InterfaceName"},
[]*ds.IndexRanges{
&ds.IndexRanges{
FieldName: "v.InterfaceName",
Kind: vdl.String,
NilAllowed: true,
StringRanges: &ds.StringFieldRanges{
ds.StringFieldRange{Start: "", Limit: ""},
},
},
},
},
{
"delete from Customer where v.InterfaceName = \"FooBar\"",
[]string{"v.InterfaceName"},
[]*ds.IndexRanges{
&ds.IndexRanges{
FieldName: "v.InterfaceName",
Kind: vdl.String,
NilAllowed: false,
StringRanges: &ds.StringFieldRanges{
ds.StringFieldRange{Start: "FooBar", Limit: appendZeroByte("FooBar")},
},
},
},
},
{
"delete from Customer where v.InterfaceName = \"Foo\" or v.InterfaceName = \"Bar\"",
[]string{"v.InterfaceName"},
[]*ds.IndexRanges{
&ds.IndexRanges{
FieldName: "v.InterfaceName",
Kind: vdl.String,
NilAllowed: false,
StringRanges: &ds.StringFieldRanges{
ds.StringFieldRange{Start: "Bar", Limit: appendZeroByte("Bar")},
ds.StringFieldRange{Start: "Foo", Limit: appendZeroByte("Foo")},
},
},
},
},
{
"delete from Customer where v.InterfaceName = \"Foo\" and v.InterfaceName = \"Bar\"",
[]string{"v.InterfaceName"},
[]*ds.IndexRanges{
&ds.IndexRanges{
FieldName: "v.InterfaceName",
Kind: vdl.String,
NilAllowed: false,
StringRanges: &ds.StringFieldRanges{},
},
},
},
{
"delete from Customer where v.InterfaceName like \"Foo%\"",
[]string{"v.InterfaceName"},
[]*ds.IndexRanges{
&ds.IndexRanges{
FieldName: "v.InterfaceName",
Kind: vdl.String,
NilAllowed: false,
StringRanges: &ds.StringFieldRanges{
ds.StringFieldRange{Start: "Foo", Limit: "Fop"},
},
},
},
},
{
"delete from Customer where v.InterfaceName like \"Foo%\" and v.Address like \"Bar%\"",
[]string{"v.InterfaceName", "v.Address"},
[]*ds.IndexRanges{
&ds.IndexRanges{
FieldName: "v.InterfaceName",
Kind: vdl.String,
NilAllowed: false,
StringRanges: &ds.StringFieldRanges{
ds.StringFieldRange{Start: "Foo", Limit: "Fop"},
},
},
&ds.IndexRanges{
FieldName: "v.Address",
Kind: vdl.String,
NilAllowed: false,
StringRanges: &ds.StringFieldRanges{
ds.StringFieldRange{Start: "Bar", Limit: "Bas"},
},
},
},
},
{
"delete from Customer where \"abc\" = v.Foo or \"def\" = v.Foo",
[]string{"v.Foo"},
[]*ds.IndexRanges{
&ds.IndexRanges{
FieldName: "v.Foo",
Kind: vdl.String,
NilAllowed: false,
StringRanges: &ds.StringFieldRanges{
ds.StringFieldRange{Start: "abc", Limit: appendZeroByte("abc")},
ds.StringFieldRange{Start: "def", Limit: appendZeroByte("def")},
},
},
},
},
{
"delete from Customer where v.Foo >= \"foo\" and v.Foo < \"goo\"",
[]string{"v.Foo"},
[]*ds.IndexRanges{
&ds.IndexRanges{
FieldName: "v.Foo",
Kind: vdl.String,
NilAllowed: false,
StringRanges: &ds.StringFieldRanges{
ds.StringFieldRange{Start: "foo", Limit: "goo"},
},
},
},
},
{
"delete from Customer where \"foo\" <= v.Foo and \"goo\" >= v.Foo",
[]string{"v.Foo"},
[]*ds.IndexRanges{
&ds.IndexRanges{
FieldName: "v.Foo",
Kind: vdl.String,
NilAllowed: false,
StringRanges: &ds.StringFieldRanges{
ds.StringFieldRange{Start: "foo", Limit: appendZeroByte("goo")},
},
},
},
},
{
"delete from Customer where v.Foo <> \"foo\"",
[]string{"v.Foo"},
[]*ds.IndexRanges{
&ds.IndexRanges{
FieldName: "v.Foo",
Kind: vdl.String,
NilAllowed: false,
StringRanges: &ds.StringFieldRanges{
ds.StringFieldRange{Start: "", Limit: "foo"},
ds.StringFieldRange{Start: appendZeroByte("foo"), Limit: ""},
},
},
},
},
{
"delete from Customer where v.Foo <> \"foo\" and v.Foo > \"bar\"",
[]string{"v.Foo"},
[]*ds.IndexRanges{
&ds.IndexRanges{
FieldName: "v.Foo",
Kind: vdl.String,
NilAllowed: false,
StringRanges: &ds.StringFieldRanges{
ds.StringFieldRange{Start: appendZeroByte("bar"), Limit: "foo"},
ds.StringFieldRange{Start: appendZeroByte("foo"), Limit: ""},
},
},
},
},
{
"delete from Customer where v.Foo <> \"foo\" or v.Foo > \"bar\"",
[]string{"v.Foo"},
[]*ds.IndexRanges{
&ds.IndexRanges{
FieldName: "v.Foo",
Kind: vdl.String,
NilAllowed: false,
StringRanges: &ds.StringFieldRanges{
ds.StringFieldRange{Start: "", Limit: ""},
},
},
},
},
{
"delete from Customer where v.Foo <> \"bar\" or v.Foo > \"foo\"",
[]string{"v.Foo"},
[]*ds.IndexRanges{
&ds.IndexRanges{
FieldName: "v.Foo",
Kind: vdl.String,
NilAllowed: false,
StringRanges: &ds.StringFieldRanges{
ds.StringFieldRange{Start: "", Limit: "bar"},
ds.StringFieldRange{Start: appendZeroByte("bar"), Limit: ""},
},
},
},
},
{
"select v from Customer where Type(v) = \"Foo.Bar\" and v.Foo >= \"100\" and v.Foo < \"200\" and v.foo > 50 and v.bar <= 1000 and v.baz <> -20.7",
[]string{"v.Foo"},
[]*ds.IndexRanges{
&ds.IndexRanges{
FieldName: "v.Foo",
Kind: vdl.String,
NilAllowed: false,
StringRanges: &ds.StringFieldRanges{
ds.StringFieldRange{Start: "100", Limit: "200"},
},
},
},
},
{
"delete from Customer where v.Foo = \"abc\" and v.Foo = \"def\"",
[]string{"v.Foo"},
[]*ds.IndexRanges{
&ds.IndexRanges{
FieldName: "v.Foo",
Kind: vdl.String,
NilAllowed: false,
StringRanges: &ds.StringFieldRanges{},
},
},
},
{
"delete from Customer where Type(v) = \"Foo.Bar\" and v.Foo like \"abc%\" limit 100",
[]string{"v.Foo"},
[]*ds.IndexRanges{
&ds.IndexRanges{
FieldName: "v.Foo",
Kind: vdl.String,
NilAllowed: false,
StringRanges: &ds.StringFieldRanges{
ds.StringFieldRange{Start: "abc", Limit: "abd"},
},
},
},
},
{
"select k, v from \n Customer where v.Foo like \"002%\" or v.Foo like \"001%\" or v.Foo like \"%\"",
[]string{"v.Foo"},
[]*ds.IndexRanges{
&ds.IndexRanges{
FieldName: "v.Foo",
Kind: vdl.String,
NilAllowed: false,
StringRanges: &ds.StringFieldRanges{
ds.StringFieldRange{Start: "", Limit: ""},
},
},
},
},
{
"delete from Customer where v.Foo = \"Foo.Bar\" and v.Foo like \"abc%\" limit 100",
[]string{"v.Foo"},
[]*ds.IndexRanges{
&ds.IndexRanges{
FieldName: "v.Foo",
Kind: vdl.String,
NilAllowed: false,
StringRanges: &ds.StringFieldRanges{},
},
},
},
{
"delete from Customer where v.Foo like \"foo%\" and v.Foo like \"bar%\"",
[]string{"v.Foo"},
[]*ds.IndexRanges{
&ds.IndexRanges{
FieldName: "v.Foo",
Kind: vdl.String,
NilAllowed: false,
StringRanges: &ds.StringFieldRanges{},
},
},
},
{
"delete from Customer where v.Foo like \"foo%\" or v.Foo like \"bar%\"",
[]string{"v.Foo"},
[]*ds.IndexRanges{
&ds.IndexRanges{
FieldName: "v.Foo",
Kind: vdl.String,
NilAllowed: false,
StringRanges: &ds.StringFieldRanges{
ds.StringFieldRange{Start: "bar", Limit: "bas"},
ds.StringFieldRange{Start: "foo", Limit: "fop"},
},
},
},
},
{
// Note: 'like "Foo"' is optimized to '= "Foo"
"delete from Customer where v.Foo = \"Foo.Bar\" or v.Foo like \"Foo\" or v.Foo like \"abc%\" limit 100",
[]string{"v.Foo"},
[]*ds.IndexRanges{
&ds.IndexRanges{
FieldName: "v.Foo",
Kind: vdl.String,
NilAllowed: false,
StringRanges: &ds.StringFieldRanges{
ds.StringFieldRange{Start: "Foo", Limit: appendZeroByte("Foo")},
ds.StringFieldRange{Start: "Foo.Bar", Limit: appendZeroByte("Foo.Bar")},
ds.StringFieldRange{Start: "abc", Limit: "abd"},
},
},
},
},
{
"delete from Customer where v.Foo like \"Foo@%Bar\" or v.Foo like \"abc%\" limit 100 escape '@'",
[]string{"v.Foo"},
[]*ds.IndexRanges{
&ds.IndexRanges{
FieldName: "v.Foo",
Kind: vdl.String,
NilAllowed: false,
StringRanges: &ds.StringFieldRanges{
ds.StringFieldRange{Start: "Foo%Bar", Limit: appendZeroByte("Foo%Bar")},
ds.StringFieldRange{Start: "abc", Limit: "abd"},
},
},
},
},
{
"delete from Customer where v.Foo like \"Foo%Bar\" or v.Foo like \"abc%\" limit 100",
[]string{"v.Foo"},
[]*ds.IndexRanges{
&ds.IndexRanges{
FieldName: "v.Foo",
Kind: vdl.String,
NilAllowed: false,
StringRanges: &ds.StringFieldRanges{
ds.StringFieldRange{Start: "Foo", Limit: "Fop"},
ds.StringFieldRange{Start: "abc", Limit: "abd"},
},
},
},
},
{
"delete from Customer where v.Foo like \"Foo#%Bar\" or v.Foo like \"abc%\" escape '#' limit 100",
[]string{"v.Foo"},
[]*ds.IndexRanges{
&ds.IndexRanges{
FieldName: "v.Foo",
Kind: vdl.String,
NilAllowed: false,
StringRanges: &ds.StringFieldRanges{
ds.StringFieldRange{Start: "Foo%Bar", Limit: appendZeroByte("Foo%Bar")},
ds.StringFieldRange{Start: "abc", Limit: "abd"},
},
},
},
},
{
"delete from Customer where v.Foo not like \"002%\"",
[]string{"v.Foo"},
[]*ds.IndexRanges{
&ds.IndexRanges{
FieldName: "v.Foo",
Kind: vdl.String,
NilAllowed: false,
StringRanges: &ds.StringFieldRanges{
ds.StringFieldRange{Start: "", Limit: "002"},
ds.StringFieldRange{Start: "003", Limit: ""},
},
},
},
},
}
for _, test := range basic {
s, syntaxErr := query_parser.Parse(&db, test.query)
if syntaxErr != nil {
t.Errorf("query: %s; unexpected error: got %v, want nil", test.query, syntaxErr)
}
err := query_checker.Check(&db, s)
if err != nil {
t.Errorf("query: %s; got %v, want: nil", test.query, err)
}
switch sel := (*s).(type) {
case query_parser.SelectStatement:
for i, fieldName := range test.fieldNames {
idxField, err := query_parser.ParseIndexField(&db, fieldName, "Services")
if err != nil {
t.Errorf("query: %s;\nGOT %v\nWANT nil", test.query, err)
}
indexRanges := query_checker.CompileIndexRanges(idxField, vdl.String, sel.Where)
if !reflect.DeepEqual(test.indexRanges[i], indexRanges) {
t.Errorf("query: %s;\nGOT %v\nWANT %v", test.query, indexRanges, test.indexRanges[i])
}
}
case query_parser.DeleteStatement:
for i, fieldName := range test.fieldNames {
idxField, err := query_parser.ParseIndexField(&db, fieldName, "Services")
if err != nil {
t.Errorf("query: %s;\nGOT %v\nWANT nil", test.query, err)
}
indexRanges := query_checker.CompileIndexRanges(idxField, vdl.String, sel.Where)
if !reflect.DeepEqual(test.indexRanges[i], indexRanges) {
t.Errorf("query: %s;\nGOT %v\nWANT %v", test.query, indexRanges, test.indexRanges[i])
}
}
default:
t.Errorf("query: %s;\nGOT %v\nWANT select or delete statement", test.query, *s)
}
}
}
func TestRegularExpressions(t *testing.T) {
basic := []regularExpressionsTest{
// Select
{
"select v from Customer where v like \"abc%\"",
"^abc.*?$",
[]string{"abc", "abcd", "abcabc"},
[]string{"xabcd"},
},
{
"select v from Customer where v like \"abc_\"",
"^abc.$",
[]string{"abcd", "abc1"},
[]string{"abc", "xabcd", "abcde"},
},
{
"select v from Customer where v like \"*%*_%\" escape '*'",
"^%_.*?$",
[]string{"%_", "%_abc"},
[]string{"%a", "abc%_abc"},
},
{
"select v from Customer where v like \"abc^_\" escape '^'",
"^abc_$",
[]string{"abc_"},
[]string{"abc", "xabcd", "abcde"},
},
{
"select v from Customer where v like \"abc^%\" escape '^'",
"^abc%$",
[]string{"abc%"},
[]string{"abc", "xabcd", "abcde"},
},
{
"select v from Customer where v like \"abc_efg\"",
"^abc.efg$",
[]string{"abcdefg"},
[]string{"abc", "xabcd", "abcde", "abcdefgh"},
},
{
"select v from Customer where v like \"abc%def\"",
"^abc.*?def$",
[]string{"abcdefdef", "abcdef", "abcdefghidef"},
[]string{"abcdefg", "abcdefde"},
},
{
"select v from Customer where v like \"[0-9]*abc%def\"",
"^\\[0-9\\]\\*abc.*?def$",
[]string{"[0-9]*abcdefdef", "[0-9]*abcdef", "[0-9]*abcdefghidef"},
[]string{"0abcdefg", "9abcdefde", "[0-9]abcdefg", "[0-9]abcdefg", "[0-9]abcdefg"},
},
// Delete
{
"delete from Customer where v like \"abc%\"",
"^abc.*?$",
[]string{"abc", "abcd", "abcabc"},
[]string{"xabcd"},
},
{
"delete from Customer where v like \"abc_\"",
"^abc.$",
[]string{"abcd", "abc1"},
[]string{"abc", "xabcd", "abcde"},
},
{
"delete from Customer where v like \"*%*_%\" escape '*'",
"^%_.*?$",
[]string{"%_", "%_abc"},
[]string{"%a", "abc%_abc"},
},
{
"delete from Customer where v like \"abc^_\" escape '^'",
"^abc_$",
[]string{"abc_"},
[]string{"abc", "xabcd", "abcde"},
},
{
"delete from Customer where v like \"abc^%\" escape '^'",
"^abc%$",
[]string{"abc%"},
[]string{"abc", "xabcd", "abcde"},
},
{
"delete from Customer where v like \"abc_efg\"",
"^abc.efg$",
[]string{"abcdefg"},
[]string{"abc", "xabcd", "abcde", "abcdefgh"},
},
{
"delete from Customer where v like \"abc%def\"",
"^abc.*?def$",
[]string{"abcdefdef", "abcdef", "abcdefghidef"},
[]string{"abcdefg", "abcdefde"},
},
{
"delete from Customer where v like \"[0-9]*abc%def\"",
"^\\[0-9\\]\\*abc.*?def$",
[]string{"[0-9]*abcdefdef", "[0-9]*abcdef", "[0-9]*abcdefghidef"},
[]string{"0abcdefg", "9abcdefde", "[0-9]abcdefg", "[0-9]abcdefg", "[0-9]abcdefg"},
},
}
for _, test := range basic {
s, syntaxErr := query_parser.Parse(&db, test.query)
if syntaxErr != nil {
t.Errorf("query: %s; unexpected error: got %v, want nil", test.query, syntaxErr)
}
err := query_checker.Check(&db, s)
if err != nil {
t.Errorf("query: %s; got %v, want: nil", test.query, err)
}
switch sel := (*s).(type) {
case query_parser.SelectStatement:
// We know there is exactly one like expression and operand2 contains
// a regex and compiled regex.
if sel.Where.Expr.Operand2.Regex != test.regex {
t.Errorf("query: %s;\nGOT %s\nWANT %s", test.query, sel.Where.Expr.Operand2.Regex, test.regex)
}
regexp := sel.Where.Expr.Operand2.CompRegex
// Make sure all matches actually match
for _, m := range test.matches {
if !regexp.MatchString(m) {
t.Errorf("query: %s;Expected match: %s; \nGOT false\nWANT true", test.query, m)
}
}
// Make sure all nonMatches actually don't match
for _, n := range test.nonMatches {
if regexp.MatchString(n) {
t.Errorf("query: %s;Expected nonMatch: %s; \nGOT true\nWANT false", test.query, n)
}
}
case query_parser.DeleteStatement:
// We know there is exactly one like expression and operand2 contains
// a regex and compiled regex.
if sel.Where.Expr.Operand2.Regex != test.regex {
t.Errorf("query: %s;\nGOT %s\nWANT %s", test.query, sel.Where.Expr.Operand2.Regex, test.regex)
}
regexp := sel.Where.Expr.Operand2.CompRegex
// Make sure all matches actually match
for _, m := range test.matches {
if !regexp.MatchString(m) {
t.Errorf("query: %s;Expected match: %s; \nGOT false\nWANT true", test.query, m)
}
}
// Make sure all nonMatches actually don't match
for _, n := range test.nonMatches {
if regexp.MatchString(n) {
t.Errorf("query: %s;Expected nonMatch: %s; \nGOT true\nWANT false", test.query, n)
}
}
}
}
}
func TestQueryCheckerErrors(t *testing.T) {
basic := []checkErrorTest{
// Select
{"select a from Customer", syncql.NewErrInvalidSelectField(db.GetContext(), 7)},
{"select v from Bob", syncql.NewErrTableCantAccess(db.GetContext(), 14, "Bob", errors.New("No such table: Bob"))},
{"select k.a from Customer", syncql.NewErrDotNotationDisallowedForKey(db.GetContext(), 9)},
{"select k from Customer where t.a = \"Foo.Bar\"", syncql.NewErrBadFieldInWhere(db.GetContext(), 29)},
{"select v from Customer where a=1", syncql.NewErrBadFieldInWhere(db.GetContext(), 29)},
{"select v from Customer limit 0", syncql.NewErrLimitMustBeGt0(db.GetContext(), 29)},
{"select v.z from Customer where v.x like v.y", syncql.NewErrLikeExpressionsRequireRhsString(db.GetContext(), 40)},
{"select v.z from Customer where k like \"a^bc%\" escape '^'", syncql.NewErrInvalidEscapeSequence(db.GetContext(), 38)},
{"select v from Customer where v.A > false", syncql.NewErrBoolInvalidExpression(db.GetContext(), 33)},
{"select v from Customer where true <= v.A", syncql.NewErrBoolInvalidExpression(db.GetContext(), 34)},
{"select v from Customer where Foo(\"2015/07/22\", true, 3.14157) = true", syncql.NewErrFunctionNotFound(db.GetContext(), 29, "Foo")},
{"select v from Customer where nil is v.ZipCode", syncql.NewErrIsIsNotRequireLhsValue(db.GetContext(), 29)},
{"select v from Customer where v.ZipCode is \"94303\"", syncql.NewErrIsIsNotRequireRhsNil(db.GetContext(), 42)},
{"select v from Customer where v.ZipCode is 94303", syncql.NewErrIsIsNotRequireRhsNil(db.GetContext(), 42)},
{"select v from Customer where v.ZipCode is true", syncql.NewErrIsIsNotRequireRhsNil(db.GetContext(), 42)},
{"select v from Customer where v.ZipCode is 943.03", syncql.NewErrIsIsNotRequireRhsNil(db.GetContext(), 42)},
{"select v from Customer where nil is not v.ZipCode", syncql.NewErrIsIsNotRequireLhsValue(db.GetContext(), 29)},
{"select v from Customer where v.ZipCode is not \"94303\"", syncql.NewErrIsIsNotRequireRhsNil(db.GetContext(), 46)},
{"select v from Customer where v.ZipCode is not 94303", syncql.NewErrIsIsNotRequireRhsNil(db.GetContext(), 46)},
{"select v from Customer where v.ZipCode is not true", syncql.NewErrIsIsNotRequireRhsNil(db.GetContext(), 46)},
{"select v from Customer where v.ZipCode is not 943.03", syncql.NewErrIsIsNotRequireRhsNil(db.GetContext(), 46)},
{"select v from Customer where Type(v) = \"Customer\" and Year(v.InvoiceDate, \"ABC\") = 2015", syncql.NewErrLocationConversionError(db.GetContext(), 74, errors.New("unknown time zone ABC"))},
{"select v from Customer where Type(v) = \"Customer\" and Month(v.InvoiceDate, \"ABC\") = 2015", syncql.NewErrLocationConversionError(db.GetContext(), 75, errors.New("unknown time zone ABC"))},
{"select v from Customer where Type(v) = \"Customer\" and Day(v.InvoiceDate, \"ABC\") = 2015", syncql.NewErrLocationConversionError(db.GetContext(), 73, errors.New("unknown time zone ABC"))},
{"select v from Customer where Type(v) = \"Customer\" and Hour(v.InvoiceDate, \"ABC\") = 2015", syncql.NewErrLocationConversionError(db.GetContext(), 74, errors.New("unknown time zone ABC"))},
{"select v from Customer where Type(v) = \"Customer\" and Minute(v.InvoiceDate, \"ABC\") = 2015", syncql.NewErrLocationConversionError(db.GetContext(), 76, errors.New("unknown time zone ABC"))},
{"select v from Customer where Type(v) = \"Customer\" and Second(v.InvoiceDate, \"ABC\") = 2015", syncql.NewErrLocationConversionError(db.GetContext(), 76, errors.New("unknown time zone ABC"))},
{"select v from Customer where Type(v) = \"Customer\" and Now(v.InvoiceDate, \"ABC\") = 2015", syncql.NewErrFunctionArgCount(db.GetContext(), 54, "Now", 0, 2)},
{"select v from Customer where Type(v) = \"Customer\" and Lowercase(v.Name, 2) = \"smith\"", syncql.NewErrFunctionArgCount(db.GetContext(), 54, "Lowercase", 1, 2)},
{"select v from Customer where Type(v) = \"Customer\" and Uppercase(v.Name, 2) = \"SMITH\"", syncql.NewErrFunctionArgCount(db.GetContext(), 54, "Uppercase", 1, 2)},
{"select Time() from Customer", syncql.NewErrFunctionArgCount(db.GetContext(), 7, "Time", 2, 0)},
{"select Year(v.InvoiceDate, \"Foo\") from Customer where Type(v) = \"Invoice\"",
syncql.NewErrLocationConversionError(db.GetContext(), 27, errors.New("unknown time zone Foo"))},
{"select K from Customer where Type(v) = \"Invoice\"", syncql.NewErrDidYouMeanLowercaseK(db.GetContext(), 7)},
{"select V from Customer where Type(v) = \"Invoice\"", syncql.NewErrDidYouMeanLowercaseV(db.GetContext(), 7)},
{"select k from Customer where K = \"001\"", syncql.NewErrDidYouMeanLowercaseK(db.GetContext(), 29)},
{"select v from Customer where Type(V) = \"Invoice\"", syncql.NewErrDidYouMeanLowercaseV(db.GetContext(), 34)},
{"select K, V from Customer where Type(V) = \"Invoice\" and K = \"001\"", syncql.NewErrDidYouMeanLowercaseK(db.GetContext(), 7)},
// Delete
{"delete from Bob", syncql.NewErrTableCantAccess(db.GetContext(), 12, "Bob", errors.New("No such table: Bob"))},
{"delete from Customer where k.a = \"a\"", syncql.NewErrDotNotationDisallowedForKey(db.GetContext(), 29)},
{"delete from Customer where t.a = \"Foo.Bar\"", syncql.NewErrBadFieldInWhere(db.GetContext(), 27)},
{"delete from Customer where a=1", syncql.NewErrBadFieldInWhere(db.GetContext(), 27)},
{"delete from Customer limit 0", syncql.NewErrLimitMustBeGt0(db.GetContext(), 27)},
{"delete from Customer where v.x like v.y", syncql.NewErrLikeExpressionsRequireRhsString(db.GetContext(), 36)},
{"delete from Customer where k like \"a^bc%\" escape '^'", syncql.NewErrInvalidEscapeSequence(db.GetContext(), 34)},
{"delete from Customer where v.A > false", syncql.NewErrBoolInvalidExpression(db.GetContext(), 31)},
{"delete from Customer where true <= v.A", syncql.NewErrBoolInvalidExpression(db.GetContext(), 32)},
{"delete from Customer where Foo(\"2015/07/22\", true, 3.14157) = true", syncql.NewErrFunctionNotFound(db.GetContext(), 27, "Foo")},
{"delete from Customer where nil is v.ZipCode", syncql.NewErrIsIsNotRequireLhsValue(db.GetContext(), 27)},
{"delete from Customer where v.ZipCode is \"94303\"", syncql.NewErrIsIsNotRequireRhsNil(db.GetContext(), 40)},
{"delete from Customer where v.ZipCode is 94303", syncql.NewErrIsIsNotRequireRhsNil(db.GetContext(), 40)},
{"delete from Customer where v.ZipCode is true", syncql.NewErrIsIsNotRequireRhsNil(db.GetContext(), 40)},
{"delete from Customer where v.ZipCode is 943.03", syncql.NewErrIsIsNotRequireRhsNil(db.GetContext(), 40)},
{"delete from Customer where nil is not v.ZipCode", syncql.NewErrIsIsNotRequireLhsValue(db.GetContext(), 27)},
{"delete from Customer where v.ZipCode is not \"94303\"", syncql.NewErrIsIsNotRequireRhsNil(db.GetContext(), 44)},
{"delete from Customer where v.ZipCode is not 94303", syncql.NewErrIsIsNotRequireRhsNil(db.GetContext(), 44)},
{"delete from Customer where v.ZipCode is not true", syncql.NewErrIsIsNotRequireRhsNil(db.GetContext(), 44)},
{"delete from Customer where v.ZipCode is not 943.03", syncql.NewErrIsIsNotRequireRhsNil(db.GetContext(), 44)},
{"delete from Customer where Type(v) = \"Customer\" and Year(v.InvoiceDate, \"ABC\") = 2015", syncql.NewErrLocationConversionError(db.GetContext(), 72, errors.New("unknown time zone ABC"))},
{"delete from Customer where Type(v) = \"Customer\" and Month(v.InvoiceDate, \"ABC\") = 2015", syncql.NewErrLocationConversionError(db.GetContext(), 73, errors.New("unknown time zone ABC"))},
{"delete from Customer where Type(v) = \"Customer\" and Day(v.InvoiceDate, \"ABC\") = 2015", syncql.NewErrLocationConversionError(db.GetContext(), 71, errors.New("unknown time zone ABC"))},
{"delete from Customer where Type(v) = \"Customer\" and Hour(v.InvoiceDate, \"ABC\") = 2015", syncql.NewErrLocationConversionError(db.GetContext(), 72, errors.New("unknown time zone ABC"))},
{"delete from Customer where Type(v) = \"Customer\" and Minute(v.InvoiceDate, \"ABC\") = 2015", syncql.NewErrLocationConversionError(db.GetContext(), 74, errors.New("unknown time zone ABC"))},
{"delete from Customer where Type(v) = \"Customer\" and Second(v.InvoiceDate, \"ABC\") = 2015", syncql.NewErrLocationConversionError(db.GetContext(), 74, errors.New("unknown time zone ABC"))},
{"delete from Customer where Type(v) = \"Customer\" and Now(v.InvoiceDate, \"ABC\") = 2015", syncql.NewErrFunctionArgCount(db.GetContext(), 52, "Now", 0, 2)},
{"delete from Customer where Type(v) = \"Customer\" and Lowercase(v.Name, 2) = \"smith\"", syncql.NewErrFunctionArgCount(db.GetContext(), 52, "Lowercase", 1, 2)},
{"delete from Customer where Type(v) = \"Customer\" and Uppercase(v.Name, 2) = \"SMITH\"", syncql.NewErrFunctionArgCount(db.GetContext(), 52, "Uppercase", 1, 2)},
{"delete from Customer where Time() > 0", syncql.NewErrFunctionArgCount(db.GetContext(), 27, "Time", 2, 0)},
{"delete from Customer where Year(v.InvoiceDate, \"Foo\") > 1 and Type(v) = \"Invoice\"",
syncql.NewErrLocationConversionError(db.GetContext(), 47, errors.New("unknown time zone Foo"))},
{"delete from Customer where K = \"1\" and Type(v) = \"Invoice\"", syncql.NewErrDidYouMeanLowercaseK(db.GetContext(), 27)},
{"delete from Customer where V = \"abc\" and Type(v) = \"Invoice\"", syncql.NewErrDidYouMeanLowercaseV(db.GetContext(), 27)},
{"delete from Customer where K = \"001\"", syncql.NewErrDidYouMeanLowercaseK(db.GetContext(), 27)},
{"delete from Customer where Type(V) = \"Invoice\"", syncql.NewErrDidYouMeanLowercaseV(db.GetContext(), 32)},
{"delete from Customer where Type(V) = \"Invoice\" and K = \"001\"", syncql.NewErrDidYouMeanLowercaseV(db.GetContext(), 32)},
}
for _, test := range basic {
s, syntaxErr := query_parser.Parse(&db, test.query)
if syntaxErr != nil {
t.Errorf("query: %s; unexpected error: got %v, want nil", test.query, syntaxErr)
} else {
err := query_checker.Check(&db, s)
// Test both that the ID and offset are equal.
testErrOff, _ := syncql.SplitError(test.err)
errOff, _ := syncql.SplitError(err)
if verror.ErrorID(test.err) != verror.ErrorID(err) || testErrOff != errOff {
t.Errorf("query: %s; got %v, want %v", test.query, err, test.err)
}
}
}
}