blob: ecf9d92aa1755ab88500e5cac01878eabdd57c6b [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_test
import (
"errors"
"fmt"
"math/big"
"reflect"
"strings"
"testing"
"v.io/syncbase/v23/syncbase/nosql/internal/query"
"v.io/syncbase/v23/syncbase/nosql/internal/query/query_checker"
"v.io/syncbase/v23/syncbase/nosql/internal/query/query_db"
"v.io/syncbase/v23/syncbase/nosql/internal/query/query_parser"
)
type MockDB struct {
}
type CustomerTable struct {
}
type KeyValueStreamImpl struct {
cursor int
prefixes []string
prefixCursor int
customerRows []CustomerKV
}
func (kvs *KeyValueStreamImpl) Advance() bool {
for true {
kvs.cursor++ // initialized to -1
if kvs.cursor >= len(kvs.customerRows) {
return false
}
for kvs.prefixCursor < len(kvs.prefixes) {
// does it match any prefix
if kvs.prefixes[kvs.prefixCursor] == "" || strings.HasPrefix(kvs.customerRows[kvs.cursor].key, kvs.prefixes[kvs.prefixCursor]) {
return true
}
// Keys and prefixes are both sorted low to high, so we can increment
// prefixCursor if the prefix is < the key.
if kvs.prefixes[kvs.prefixCursor] < kvs.customerRows[kvs.cursor].key {
kvs.prefixCursor++
if kvs.prefixCursor >= len(kvs.prefixes) {
return false
}
} else {
break
}
}
}
return false
}
func (kvs *KeyValueStreamImpl) KeyValue() (string, interface{}) {
return kvs.customerRows[kvs.cursor].key, kvs.customerRows[kvs.cursor].value
}
func (kvs *KeyValueStreamImpl) Err() error {
return nil
}
func (kvs *KeyValueStreamImpl) Cancel() {
}
func (customerTable CustomerTable) Scan(prefixes []string) (query_db.KeyValueStream, error) {
var keyValueStreamImpl KeyValueStreamImpl
keyValueStreamImpl.cursor = -1
keyValueStreamImpl.prefixes = prefixes
keyValueStreamImpl.customerRows = customerRows
return &keyValueStreamImpl, nil
}
func (db MockDB) GetTable(table string) (query_db.Table, error) {
if table == "Customer" {
var customerTable CustomerTable
return customerTable, nil
}
return nil, errors.New(fmt.Sprintf("No such table: %s.", table))
}
type Nest2 struct {
Foo string
Bar bool
Baz int64
}
type Nest1 struct {
FooBarBaz Nest2
}
type Customer struct {
Name string
ID int64
Active bool
Rating rune
Street string
City string
State string
Zip string
GratuituousBigInt *big.Int
GratuituousBigRat *big.Rat
GratuituousByte byte
GratuituousUint16 uint16
GratuituousUint32 uint32
GratuituousUint64 uint64
GratuituousInt16 int16
GratuituousInt32 int32
Foo Nest1
}
type Invoice struct {
CustID int64
InvoiceNum int64
Amount int64
}
var db MockDB
var sampleRow Customer
var sampleRow123 Customer
type CustomerKV struct {
key string
value interface{}
}
var customerRows []CustomerKV
func TestCreate(t *testing.T) {
sampleRow = Customer{"John Smith", 123456, true, 'A', "1 Main St.", "Palo Alto", "CA", "94303", big.NewInt(1234567890), big.NewRat(123, 1), byte(12), uint16(1234), uint32(5678), uint64(999888777666), int16(9876), int32(876543), Nest1{Nest2{"foo", true, 42}}}
sampleRow123 = Customer{"John Smith", 123, true, 123, "1 Main St.", "Palo Alto", "CA", "94303", big.NewInt(123), big.NewRat(123, 1), byte(123), uint16(123), uint32(123), uint64(123), int16(123), int32(123), Nest1{Nest2{"foo", true, 123}}}
sampleRow = Customer{"John Smith", 123456, true, 'A', "1 Main St.", "Palo Alto", "CA", "94303", big.NewInt(1234567890), big.NewRat(123, 1), byte(12), uint16(1234), uint32(5678), uint64(999888777666), int16(9876), int32(876543), Nest1{Nest2{"foo", true, 42}}}
customerRows = []CustomerKV{
CustomerKV{
"001",
Customer{"John Smith", 1, true, 'A', "1 Main St.", "Palo Alto", "CA", "94303", big.NewInt(1234567890), big.NewRat(123, 1), byte(12), uint16(1234), uint32(5678), uint64(999888777666), int16(9876), int32(876543), Nest1{Nest2{"foo", true, 42}}},
},
CustomerKV{
"001001",
Invoice{1, 1000, 42},
},
CustomerKV{
"001002",
Invoice{1, 1003, 7},
},
CustomerKV{
"001003",
Invoice{1, 1005, 88},
},
CustomerKV{
"002",
Customer{"Bat Masterson", 2, true, 'B', "777 Any St.", "collins", "IA", "50055", big.NewInt(9999), big.NewRat(999999, 1), byte(9), uint16(99), uint32(999), uint64(9999999), int16(9), int32(99), Nest1{Nest2{"bar", false, 84}}},
},
CustomerKV{
"002001",
Invoice{2, 1001, 166},
},
CustomerKV{
"002002",
Invoice{2, 1002, 243},
},
CustomerKV{
"002003",
Invoice{2, 1004, 787},
},
CustomerKV{
"002004",
Invoice{2, 1006, 88},
},
}
}
type keyPrefixesTest struct {
query string
keyPrefixes []string
err *query.QueryError
}
type evalWhereUsingOnlyKeyTest struct {
query string
key string
result bool
err error
}
type evalTest struct {
query string
k string
v interface{}
result bool
}
type projectionTest struct {
query string
k string
v interface{}
result []interface{}
}
type execSelectTest struct {
query string
r []interface{}
}
type execSelectSingleRowTest struct {
query string
k string
v interface{}
result interface{}
}
type execSelectErrorTest struct {
query string
err *query.QueryError
}
func TestQueryExec(t *testing.T) {
basic := []execSelectTest{
{
// Select values for all customer records.
"select v from Customer where t = \"Customer\"",
[]interface{}{
[]interface{}{customerRows[0].value},
[]interface{}{customerRows[4].value},
},
},
{
// Select keys & values for all customer records.
"select k, v from Customer where t = \"Customer\"",
[]interface{}{
[]interface{}{customerRows[0].key, customerRows[0].value},
[]interface{}{customerRows[4].key, customerRows[4].value},
},
},
{
// Select keys & names for all customer records.
"select k, v.Name from Customer where t = \"Customer\"",
[]interface{}{
[]interface{}{customerRows[0].key, "John Smith"},
[]interface{}{customerRows[4].key, "Bat Masterson"},
},
},
{
// Select both customer and invoice records.
// Customer records have ID.
// Invoice records have CustID.
"select v.ID, v.CustID from Customer",
[]interface{}{
[]interface{}{int64(1), nil},
[]interface{}{nil, int64(1)},
[]interface{}{nil, int64(1)},
[]interface{}{nil, int64(1)},
[]interface{}{int64(2), nil},
[]interface{}{nil, int64(2)},
[]interface{}{nil, int64(2)},
[]interface{}{nil, int64(2)},
[]interface{}{nil, int64(2)},
},
},
{
// Select keys & values fo all invoice records.
"select k, v from Customer where t = \"Invoice\"",
[]interface{}{
[]interface{}{customerRows[1].key, customerRows[1].value},
[]interface{}{customerRows[2].key, customerRows[2].value},
[]interface{}{customerRows[3].key, customerRows[3].value},
[]interface{}{customerRows[5].key, customerRows[5].value},
[]interface{}{customerRows[6].key, customerRows[6].value},
[]interface{}{customerRows[7].key, customerRows[7].value},
[]interface{}{customerRows[8].key, customerRows[8].value},
},
},
{
// Select key, cust id, invoice number and amount for $88 invoices.
"select k, v.CustID, v.InvoiceNum, v.Amount from Customer where t = \"Invoice\" and v.Amount = 88",
[]interface{}{
[]interface{}{customerRows[3].key, int64(1), int64(1005), int64(88)},
[]interface{}{customerRows[8].key, int64(2), int64(1006), int64(88)},
},
},
{
// Select keys & values for all records with a key prefix of "001".
"select k, v from Customer where k like \"001%\"",
[]interface{}{
[]interface{}{customerRows[0].key, customerRows[0].value},
[]interface{}{customerRows[1].key, customerRows[1].value},
[]interface{}{customerRows[2].key, customerRows[2].value},
[]interface{}{customerRows[3].key, customerRows[3].value},
},
},
{
// Select keys & values for all records with a key prefix of "001".
"select k, v from Customer where k like \"002%\"",
[]interface{}{
[]interface{}{customerRows[4].key, customerRows[4].value},
[]interface{}{customerRows[5].key, customerRows[5].value},
[]interface{}{customerRows[6].key, customerRows[6].value},
[]interface{}{customerRows[7].key, customerRows[7].value},
[]interface{}{customerRows[8].key, customerRows[8].value},
},
},
{
// Select keys & values for all records with a key prefix of "001".
// or a key prefix of "002".
"select k, v from Customer where k like \"001%\" or k like \"002%\"",
[]interface{}{
[]interface{}{customerRows[0].key, customerRows[0].value},
[]interface{}{customerRows[1].key, customerRows[1].value},
[]interface{}{customerRows[2].key, customerRows[2].value},
[]interface{}{customerRows[3].key, customerRows[3].value},
[]interface{}{customerRows[4].key, customerRows[4].value},
[]interface{}{customerRows[5].key, customerRows[5].value},
[]interface{}{customerRows[6].key, customerRows[6].value},
[]interface{}{customerRows[7].key, customerRows[7].value},
[]interface{}{customerRows[8].key, customerRows[8].value},
},
},
{
// Select keys & values for all records with a key prefix of "001".
// or a key prefix of "002".
"select k, v from Customer where k like \"002%\" or k like \"001%\"",
[]interface{}{
[]interface{}{customerRows[0].key, customerRows[0].value},
[]interface{}{customerRows[1].key, customerRows[1].value},
[]interface{}{customerRows[2].key, customerRows[2].value},
[]interface{}{customerRows[3].key, customerRows[3].value},
[]interface{}{customerRows[4].key, customerRows[4].value},
[]interface{}{customerRows[5].key, customerRows[5].value},
[]interface{}{customerRows[6].key, customerRows[6].value},
[]interface{}{customerRows[7].key, customerRows[7].value},
[]interface{}{customerRows[8].key, customerRows[8].value},
},
},
{
// Let's play with whitespace and mixed case.
" sElEcT k, v from \n Customer WhErE k lIkE \"002%\" oR k LiKe \"001%\"",
[]interface{}{
[]interface{}{customerRows[0].key, customerRows[0].value},
[]interface{}{customerRows[1].key, customerRows[1].value},
[]interface{}{customerRows[2].key, customerRows[2].value},
[]interface{}{customerRows[3].key, customerRows[3].value},
[]interface{}{customerRows[4].key, customerRows[4].value},
[]interface{}{customerRows[5].key, customerRows[5].value},
[]interface{}{customerRows[6].key, customerRows[6].value},
[]interface{}{customerRows[7].key, customerRows[7].value},
[]interface{}{customerRows[8].key, customerRows[8].value},
},
},
{
// Add in a like clause that accepts all strings.
" sElEcT k, v from \n Customer WhErE k lIkE \"002%\" oR k LiKe \"001%\" or k lIkE \"%\"",
[]interface{}{
[]interface{}{customerRows[0].key, customerRows[0].value},
[]interface{}{customerRows[1].key, customerRows[1].value},
[]interface{}{customerRows[2].key, customerRows[2].value},
[]interface{}{customerRows[3].key, customerRows[3].value},
[]interface{}{customerRows[4].key, customerRows[4].value},
[]interface{}{customerRows[5].key, customerRows[5].value},
[]interface{}{customerRows[6].key, customerRows[6].value},
[]interface{}{customerRows[7].key, customerRows[7].value},
[]interface{}{customerRows[8].key, customerRows[8].value},
},
},
{
// Select id, name for customers whose last name is Masterson.
"select v.ID, v.Name from Customer where t = \"Customer\" and v.Name like \"%Masterson\"",
[]interface{}{
[]interface{}{int64(2), "Bat Masterson"},
},
},
{
// Select records where v.Foo.FooBarBaz.Baz is 84 or type is Invoice.
"select v from Customer where v.Foo.FooBarBaz.Baz = 84 or t = \"Invoice\"",
[]interface{}{
[]interface{}{customerRows[1].value},
[]interface{}{customerRows[2].value},
[]interface{}{customerRows[3].value},
[]interface{}{customerRows[4].value},
[]interface{}{customerRows[5].value},
[]interface{}{customerRows[6].value},
[]interface{}{customerRows[7].value},
[]interface{}{customerRows[8].value},
},
},
{
// Select customer name for customer ID (i.e., key) "001".
"select v.Name from Customer where t = \"Customer\" and k = \"001\"",
[]interface{}{
[]interface{}{"John Smith"},
},
},
{
// Select records where v.Foo.FooBarBaz.Bar is true.
"select v from Customer where v.Foo.FooBarBaz.Bar = true",
[]interface{}{
[]interface{}{customerRows[0].value},
},
},
{
// Select records where v.Foo.FooBarBaz.Baz is 84 or type is Invoice.
// Limit 3
"select v from Customer where v.Foo.FooBarBaz.Baz = 84 or t = \"Invoice\" limit 3",
[]interface{}{
[]interface{}{customerRows[1].value},
[]interface{}{customerRows[2].value},
[]interface{}{customerRows[3].value},
},
},
{
// Select records where v.Foo.FooBarBaz.Baz is 84 or type is Invoice.
// Offset 5
"select v from Customer where v.Foo.FooBarBaz.Baz = 84 or t = \"Invoice\" offset 5",
[]interface{}{
[]interface{}{customerRows[6].value},
[]interface{}{customerRows[7].value},
[]interface{}{customerRows[8].value},
},
},
{
// Select records where v.Foo.FooBarBaz.Baz is 199.
"select v from Customer where v.Foo.FooBarBaz.Baz = 199",
[]interface{}{},
},
{
// Select records where v.Foo.FooBarBaz.Baz is 84 or type is Invoice.
// Offset 8
"select v from Customer where v.Foo.FooBarBaz.Baz = 84 or t = \"Invoice\" offset 8",
[]interface{}{},
},
{
// Select records where v.Foo.FooBarBaz.Baz is 84 or type is Invoice.
// Offset 23
"select v from Customer where v.Foo.FooBarBaz.Baz = 84 or t = \"Invoice\" offset 23",
[]interface{}{},
},
{
// Select records where v.Foo.FooBarBaz.Baz is 84 or type is Invoice.
// Limit 3 Offset 2
"select v from Customer where v.Foo.FooBarBaz.Baz = 84 or t = \"Invoice\" limit 3 offset 2",
[]interface{}{
[]interface{}{customerRows[3].value},
[]interface{}{customerRows[4].value},
[]interface{}{customerRows[5].value},
},
},
}
for _, test := range basic {
rs, err := query.Exec(db, test.query)
if err != nil {
t.Errorf("query: %s; got %v, want nil", test.query, err)
} else {
// Collect results.
r := []interface{}{}
for rs.Advance() {
r = append(r, rs.Result())
}
if !reflect.DeepEqual(test.r, r) {
t.Errorf("query: %s; got %v, want %v", test.query, r, test.r)
}
}
}
}
func TestKeyPrefixes(t *testing.T) {
basic := []keyPrefixesTest{
{
// Need all keys (single prefix of "").
"select k, v from Customer",
[]string{""},
nil,
},
{
// All selected rows will have key prefix of "abc".
"select k, v from Customer where t = \"Foo.Bar\" and k like \"abc%\"",
[]string{"abc"},
nil,
},
{
// Need all keys (single prefix of "").
"select k, v from Customer where t = \"Foo.Bar\" or k like \"abc%\"",
[]string{""},
nil,
},
{
// Need all keys (single prefix of "").
"select k, v from Customer where k like \"abc%\" or v.zip = \"94303\"",
[]string{""},
nil,
},
{
// All selected rows will have key prefix of "foo".
"select k, v from Customer where t = \"Foo.Bar\" and k like \"foo_bar\"",
[]string{"foo"},
nil,
},
{
// All selected rows will have key prefix of "baz" or "foo".
"select k, v from Customer where k like \"foo_bar\" or k = \"baz\"",
[]string{"baz", "foo"},
nil,
},
{
// All selected rows will have key prefix of "fo".
"select k, v from Customer where k like \"foo_bar\" or k = \"fo\"",
[]string{"fo"},
nil,
},
{
// All selected rows will have key prefix of "foo".
"select k, v from Customer where k like \"foo%bar\"",
[]string{"foo"},
nil,
},
{
// All selected rows will have key prefix of "foo\bar".
"select k, v from Customer where k like \"foo\\\\bar\"",
[]string{"foo\\bar"},
nil,
},
{
// All selected rows will have key prefix of "foo%bar".
"select k, v from Customer where k like \"foo\\%bar\"",
[]string{"foo%bar"},
nil,
},
{
// All selected rows will have key prefix of "foo\%bar".
"select k, v from Customer where k like \"foo\\\\\\%bar\"",
[]string{"foo\\%bar"},
nil,
},
{
// Need all keys (single prefix of "").
"select k, v from Customer where k like \"%foo\"",
[]string{""},
nil,
},
{
// Need all keys (single prefix of "").
"select k, v from Customer where k like \"_foo\"",
[]string{""},
nil,
},
{
// All selected rows will have key prefix of "foo_bar".
"select k, v from Customer where k like \"foo\\_bar\"",
[]string{"foo_bar"},
nil,
},
{
// All selected rows will have key prefix of "foobar%".
"select k, v from Customer where k like \"foobar\\%\"",
[]string{"foobar%"},
nil,
},
{
// All selected rows will have key prefix of "foobar_".
"select k, v from Customer where k like \"foobar\\_\"",
[]string{"foobar_"},
nil,
},
{
// All selected rows will have key prefix of "\%_".
"select k, v from Customer where k like \"\\\\\\%\\_\"",
[]string{"\\%_"},
nil,
},
{
// All selected rows will have key prefix of "%_abc\".
"select k, v from Customer where k = \"%_abc\\\"",
[]string{"%_abc\\"},
nil,
},
}
for _, test := range basic {
s, synErr := query_parser.Parse(test.query)
if synErr != nil {
t.Errorf("query: %s; got %v, want nil", test.query, synErr)
}
if synErr == nil {
semErr := query_checker.Check(db, s)
if semErr != nil {
t.Errorf("query: %s; got %v, want nil", test.query, semErr)
}
if semErr == nil {
switch sel := (*s).(type) {
case query_parser.SelectStatement:
keyPrefixes := query.CompileKeyPrefixes(sel.Where)
if !reflect.DeepEqual(test.keyPrefixes, keyPrefixes) {
t.Errorf("query: %s;\nGOT %v\nWANT %v", test.query, keyPrefixes, test.keyPrefixes)
}
default:
t.Errorf("query: %s; got %v, want query_parser.SelectStatement", reflect.TypeOf(*s))
}
}
}
}
}
func TestEvalWhereUsingOnlyKey(t *testing.T) {
basic := []evalWhereUsingOnlyKeyTest{
{
// Row will be selected using only the key.
"select k, v from Customer where k like \"abc%\"",
"abcdef",
true,
nil,
},
{
// Row will be rejected using only the key.
"select k, v from Customer where k like \"abc\"",
"abcd",
false,
nil,
},
{
// Need value to determine if row should be selected.
"select k, v from Customer where k = \"abc\" or v.zip = \"94303\"",
"abcd",
false,
errors.New("Value required for answer."),
},
{
// Need value (i.e., its type) to determine if row should be selected.
"select k, v from Customer where k = \"xyz\" or t = \"foo.Bar\"",
"wxyz",
false,
errors.New("Value required for answer."),
},
{
// Although value is in where clause, it is not needed to reject row.
"select k, v from Customer where k = \"abcd\" and v.zip = \"94303\"",
"abcde",
false,
nil,
},
}
for _, test := range basic {
s, synErr := query_parser.Parse(test.query)
if synErr != nil {
t.Errorf("query: %s; got %v, want nil", test.query, synErr)
}
if synErr == nil {
semErr := query_checker.Check(db, s)
if semErr != nil {
t.Errorf("query: %s; got %v, want nil", test.query, semErr)
}
if semErr == nil {
switch sel := (*s).(type) {
case query_parser.SelectStatement:
result, err := query.EvalWhereUsingOnlyKey(&sel, test.key)
if result != test.result {
t.Errorf("query: %s; got %v, want %v", test.query, result, test.result)
}
if (err == nil && test.err != nil) || (err != nil && test.err == nil) {
t.Errorf("query: %s; got %v, want %v", test.query, err, test.err)
}
default:
t.Errorf("query: %s; got %v, want query_parser.SelectStatement", reflect.TypeOf(*s))
}
}
}
}
}
func TestEval(t *testing.T) {
basic := []evalTest{
{
"select k, v from Customer where t = \"v.io/syncbase/v23/syncbase/nosql/internal/query_test.Customer\"",
"123456", sampleRow, true,
},
{
"select k, v from Customer where t = \"Customer\"",
"123456", sampleRow, true,
},
{
"select k, v from Customer where v.Name = \"John Smith\"",
"123456", sampleRow, true,
},
{
"select k, v from Customer where v.Name = v.Name",
"123456", sampleRow, true,
},
{
"select k, v from Customer where v = v",
"123456", sampleRow, true,
},
{
"select k, v from Customer where v > v",
"123456", sampleRow, false,
},
{
"select k, v from Customer where v < v",
"123456", sampleRow, false,
},
{
"select k, v from Customer where v >= v",
"123456", sampleRow, false,
},
{
"select k, v from Customer where v <= v",
"123456", sampleRow, false,
},
{
"select k, v from Customer where v.Rating = 'A'",
"123456", sampleRow, true,
},
{
"select k, v from Customer where v.Rating <> 'A'",
"123456", sampleRow, false,
},
{
"select k, v from Customer where v.Rating >= 'B'",
"123456", sampleRow, false,
},
{
"select k, v from Customer where v.Rating <= 'B'",
"123456", sampleRow, true,
},
{
"select k, v from Customer where v.Active = true",
"123456", sampleRow, true,
},
{
"select k, v from Customer where v.Active = false",
"123456", sampleRow, false,
},
{
"select k, v from Customer where v.GratuituousBigInt > 100",
"123456", sampleRow, true,
},
{
"select k, v from Customer where v.GratuituousBigInt = 1234567890",
"123456", sampleRow, true,
},
{
"select k, v from Customer where 9876543210 < v.GratuituousBigInt",
"123456", sampleRow, false,
},
{
"select k, v from Customer where 12 = v.GratuituousByte",
"123456", sampleRow, true,
},
{
"select k, v from Customer where 11 < v.GratuituousByte",
"123456", sampleRow, true,
},
{
"select k, v from Customer where v.GratuituousByte > 10",
"123456", sampleRow, true,
},
{
"select k, v from Customer where v.GratuituousByte >= 14",
"123456", sampleRow, false,
},
{
"select k, v from Customer where v.GratuituousByte >= 11.0",
"123456", sampleRow, true,
},
{
"select k, v from Customer where v.GratuituousUint64 = 999888777666",
"123456", sampleRow, true,
},
{
"select k, v from Customer where v.GratuituousUint64 < 999888777666",
"123456", sampleRow, false,
},
{
"select k, v from Customer where v.GratuituousByte < v.GratuituousUint64",
"123456", sampleRow, true,
},
{
"select k, v from Customer where v.GratuituousBigRat = 123",
"123456", sampleRow, true,
},
{
"select k, v from Customer where v.GratuituousUint16 = 1234",
"123456", sampleRow, true,
},
{
"select k, v from Customer where v.GratuituousUint32 = 5678",
"123456", sampleRow, true,
},
{
"select k, v from Customer where v.GratuituousInt16 = 9876",
"123456", sampleRow, true,
},
{
"select k, v from Customer where v.GratuituousInt32 = 876543",
"123456", sampleRow, true,
},
{
// Deeply nested string.
"select v from Customer where v.Foo.FooBarBaz.Foo = \"foo\"",
"123456", sampleRow, true,
},
{
// Deeply nested bool.
"select v from Customer where v.Foo.FooBarBaz.Bar = true",
"123456", sampleRow, true,
},
{
// Deeply nested int64.
"select v from Customer where v.Foo.FooBarBaz.Baz = 42",
"123456", sampleRow, true,
},
{
// Convert int64 to string
"select v from Customer where v.Foo.FooBarBaz.Baz = \"42\"",
"123456", sampleRow, true,
},
{
// Convert bool to string
"select v from Customer where v.Foo.FooBarBaz.Bar = \"true\"",
"123456", sampleRow, true,
},
{
// Bool can't convert to other types.
"select v from Customer where v.Foo.FooBarBaz.Bar = 1",
"123456", sampleRow, false,
},
{
// Test that all numeric types can compare to a big.Rat
"select v from Customer where v.GratuituousBigRat = v.Foo.FooBarBaz.Baz and v.GratuituousBigRat = v.ID and v.GratuituousBigRat = v.Rating and v.GratuituousBigRat = v.GratuituousBigInt and v.GratuituousBigRat = v.GratuituousByte and v.GratuituousBigRat = v.GratuituousUint16 and v.GratuituousBigRat = v.GratuituousUint32 and v.GratuituousBigRat = v.GratuituousUint64 and v.GratuituousBigRat = v.GratuituousInt16 and v.GratuituousBigRat = v.GratuituousInt32",
"123", sampleRow123, true,
},
{
// Test that all numeric types can compare to a big.Int
"select v from Customer where v.GratuituousBigInt = v.Foo.FooBarBaz.Baz and v.GratuituousBigInt = v.ID and v.GratuituousBigRat = v.Rating and v.GratuituousBigInt = v.GratuituousBigInt and v.GratuituousBigInt = v.GratuituousByte and v.GratuituousBigInt = v.GratuituousUint16 and v.GratuituousBigInt = v.GratuituousUint32 and v.GratuituousBigInt = v.GratuituousUint64 and v.GratuituousBigInt = v.GratuituousInt16 and v.GratuituousBigInt = v.GratuituousInt32",
"123", sampleRow123, true,
},
{
// Test that all numeric types can compare to an int32
"select v from Customer where v.GratuituousInt32 = v.Foo.FooBarBaz.Baz and v.GratuituousInt32 = v.ID and v.GratuituousBigRat = v.Rating and v.GratuituousInt32 = v.GratuituousInt32 and v.GratuituousInt32 = v.GratuituousByte and v.GratuituousInt32 = v.GratuituousUint16 and v.GratuituousInt32 = v.GratuituousUint32 and v.GratuituousInt32 = v.GratuituousUint64 and v.GratuituousInt32 = v.GratuituousInt16 and v.GratuituousInt32 = v.GratuituousBigInt",
"123", sampleRow123, true,
},
{
// Test that all numeric types can compare to an int16
"select v from Customer where v.GratuituousInt16 = v.Foo.FooBarBaz.Baz and v.GratuituousInt16 = v.ID and v.GratuituousBigRat = v.Rating and v.GratuituousInt16 = v.GratuituousInt16 and v.GratuituousInt16 = v.GratuituousByte and v.GratuituousInt16 = v.GratuituousUint16 and v.GratuituousInt16 = v.GratuituousUint32 and v.GratuituousInt16 = v.GratuituousUint64 and v.GratuituousInt16 = v.GratuituousInt32 and v.GratuituousInt16 = v.GratuituousBigInt",
"123", sampleRow123, true,
},
{
// Test that all numeric types can compare to an uint64
"select v from Customer where v.GratuituousUint64 = v.Foo.FooBarBaz.Baz and v.GratuituousUint64 = v.ID and v.GratuituousBigRat = v.Rating and v.GratuituousUint64 = v.GratuituousUint64 and v.GratuituousUint64 = v.GratuituousByte and v.GratuituousUint64 = v.GratuituousUint16 and v.GratuituousUint64 = v.GratuituousUint32 and v.GratuituousUint64 = v.GratuituousUint16 and v.GratuituousUint64 = v.GratuituousInt32 and v.GratuituousUint64 = v.GratuituousBigInt",
"123", sampleRow123, true,
},
{
// Test that all numeric types can compare to an uint32
"select v from Customer where v.GratuituousUint32 = v.Foo.FooBarBaz.Baz and v.GratuituousUint32 = v.ID and v.GratuituousBigRat = v.Rating and v.GratuituousUint32 = v.GratuituousUint32 and v.GratuituousUint32 = v.GratuituousByte and v.GratuituousUint32 = v.GratuituousUint16 and v.GratuituousUint32 = v.GratuituousUint64 and v.GratuituousUint32 = v.GratuituousUint16 and v.GratuituousUint32 = v.GratuituousInt32 and v.GratuituousUint32 = v.GratuituousBigInt",
"123", sampleRow123, true,
},
{
// Test that all numeric types can compare to an uint16
"select v from Customer where v.GratuituousUint16 = v.Foo.FooBarBaz.Baz and v.GratuituousUint16 = v.ID and v.GratuituousBigRat = v.Rating and v.GratuituousUint16 = v.GratuituousUint16 and v.GratuituousUint16 = v.GratuituousByte and v.GratuituousUint32 = v.GratuituousUint16 and v.GratuituousUint16 = v.GratuituousUint64 and v.GratuituousUint16 = v.GratuituousUint16 and v.GratuituousUint16 = v.GratuituousInt32 and v.GratuituousUint16 = v.GratuituousBigInt",
"123", sampleRow123, true,
},
}
for _, test := range basic {
s, synErr := query_parser.Parse(test.query)
if synErr != nil {
t.Errorf("query: %s; got %v, want nil", test.query, synErr)
}
if synErr == nil {
semErr := query_checker.Check(db, s)
if semErr != nil {
t.Errorf("query: %s; got %v, want nil", test.query, semErr)
}
if semErr == nil {
switch sel := (*s).(type) {
case query_parser.SelectStatement:
result := query.Eval(test.k, test.v, sel.Where.Expr)
if result != test.result {
t.Errorf("query: %s; got %v, want %v", test.query, result, test.result)
}
default:
t.Errorf("query: %s; got %v, want query_parser.SelectStatement", reflect.TypeOf(*s))
}
}
}
}
}
func TestProjection(t *testing.T) {
basic := []projectionTest{
{
"select k, v from Customer where t = \"Customer\"",
"123456", sampleRow,
[]interface{}{
"123456",
sampleRow,
},
},
{
"select k, v, v.Name, v.ID, v.Active, v.Rating, v.Street, v.City, v.State, v.Zip, v.GratuituousBigInt, v.GratuituousBigRat, v.GratuituousByte, v.GratuituousUint16, v.GratuituousUint32, v.GratuituousUint64, v.GratuituousInt16, v.GratuituousInt32, v.Foo, v.Foo.FooBarBaz, v.Foo.FooBarBaz.Foo, v.Foo.FooBarBaz.Bar, v.Foo.FooBarBaz.Baz from Customer where t = \"Customer\"",
"123456", sampleRow,
[]interface{}{
"123456",
sampleRow,
"John Smith",
int64(123456),
true,
'A',
"1 Main St.",
"Palo Alto",
"CA",
"94303",
big.NewInt(1234567890),
big.NewRat(123, 1),
byte(12),
uint16(1234),
uint32(5678),
uint64(999888777666),
int16(9876),
int32(876543),
Nest1{Nest2{"foo", true, int64(42)}},
Nest2{"foo", true, int64(42)},
"foo",
true,
int64(42),
},
},
}
for _, test := range basic {
s, synErr := query_parser.Parse(test.query)
if synErr != nil {
t.Errorf("query: %s; got %v, want nil", test.query, synErr)
}
if synErr == nil {
semErr := query_checker.Check(db, s)
if semErr != nil {
t.Errorf("query: %s; got %v, want nil", test.query, semErr)
}
if semErr == nil {
switch sel := (*s).(type) {
case query_parser.SelectStatement:
result := query.ComposeProjection(test.k, test.v, sel.Select)
if !reflect.DeepEqual(result, test.result) {
t.Errorf("query: %s; got %v, want %v", test.query, result, test.result)
}
default:
t.Errorf("query: %s; got %v, want query_parser.SelectStatement", reflect.TypeOf(*s))
}
}
}
}
}
func TestExecSelectSingleRow(t *testing.T) {
basic := []execSelectSingleRowTest{
{
"select k, v from Customer where t = \"Customer\"",
"123456", sampleRow,
[]interface{}{
"123456",
sampleRow,
},
},
{
"select k, v from Customer where t = \"Customer\" and k like \"123%\"",
"123456", sampleRow,
[]interface{}{
"123456",
sampleRow,
},
},
{
"select k, v from Customer where t = \"Invoice\" and k like \"123%\"",
"123456", sampleRow,
nil,
},
{
"select k, v from Customer where t = \"Customer\" and k like \"456%\"",
"123456", sampleRow,
nil,
},
{
"select v from Customer where v.Name = \"John Smith\"",
"123456", sampleRow,
[]interface{}{
sampleRow,
},
},
{
"select v from Customer where v.Name = \"John Doe\"",
"123456", sampleRow,
nil,
},
{
"select k, v, v.Name, v.ID, v.Active, v.Rating, v.Street, v.City, v.State, v.Zip, v.GratuituousBigInt, v.GratuituousBigRat, v.GratuituousByte, v.GratuituousUint16, v.GratuituousUint32, v.GratuituousUint64, v.GratuituousInt16, v.GratuituousInt32, v.Foo, v.Foo.FooBarBaz, v.Foo.FooBarBaz.Foo, v.Foo.FooBarBaz.Bar, v.Foo.FooBarBaz.Baz from Customer where t = \"Customer\"",
"123456", sampleRow,
[]interface{}{
"123456",
sampleRow,
"John Smith",
int64(123456),
true,
'A',
"1 Main St.",
"Palo Alto",
"CA",
"94303",
big.NewInt(1234567890),
big.NewRat(123, 1),
byte(12),
uint16(1234),
uint32(5678),
uint64(999888777666),
int16(9876),
int32(876543),
Nest1{Nest2{"foo", true, int64(42)}},
Nest2{"foo", true, int64(42)},
"foo",
true,
int64(42),
},
},
}
for _, test := range basic {
s, synErr := query_parser.Parse(test.query)
if synErr != nil {
t.Errorf("query: %s; got %v, want nil", test.query, synErr)
}
if synErr == nil {
semErr := query_checker.Check(db, s)
if semErr != nil {
t.Errorf("query: %s; got %v, want nil", test.query, semErr)
}
if semErr == nil {
switch sel := (*s).(type) {
case query_parser.SelectStatement:
result := query.ExecSelectSingleRow(test.k, test.v, &sel)
if !reflect.DeepEqual(result, test.result) {
t.Errorf("query: %s; got %v, want %v", test.query, result, test.result)
}
default:
t.Errorf("query: %s; got %v, want query_parser.SelectStatement", reflect.TypeOf(*s))
}
}
}
}
}
// TODO(jkline): More negative tests here (even though they are tested elsewhere)?
func TestExecErrors(t *testing.T) {
basic := []execSelectErrorTest{
{
"select a from Customer",
query.Error(7, "Select field must be 'k' or 'v[{.<ident>}...]'."),
},
{
"select v from Unknown",
// The following error text is dependent on implementation of Database.
query.Error(14, "No such table: Unknown."),
},
{
"select v from Customer offset -1",
// The following error text is dependent on implementation of Database.
query.Error(30, "Expected positive integer literal., found '-'."),
},
}
for _, test := range basic {
_, err := query.Exec(db, test.query)
if !reflect.DeepEqual(err, test.err) {
t.Errorf("query: %s; got %v, want %v", test.query, err, test.err)
}
}
}