syncbase: syncQL: switch from operating on interface{} values to *vdl.Value values.
Change-Id: Ib4373e9efc38e849c696561ce00238feac571369
diff --git a/v23/syncbase/nosql/internal/query/demo/db_objects.vdl b/v23/syncbase/nosql/internal/query/demo/db_objects.vdl
new file mode 100644
index 0000000..a6338c0
--- /dev/null
+++ b/v23/syncbase/nosql/internal/query/demo/db_objects.vdl
@@ -0,0 +1,93 @@
+// 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 main
+
+type AddressInfo struct {
+ Street string
+ City string
+ State string
+ Zip string
+}
+
+type CreditAgency enum {
+ Equifax
+ Experian
+ TransUnion
+}
+
+type ExperianRating enum {
+ Good
+ Bad
+}
+
+type EquifaxCreditReport struct {
+ Rating byte
+}
+
+type ExperianCreditReport struct {
+ Rating ExperianRating
+}
+
+type TransUnionCreditReport struct {
+ Rating int16
+}
+
+type AgencyReport union {
+ EquifaxReport EquifaxCreditReport
+ ExperianReport ExperianCreditReport
+ TransUnionReport TransUnionCreditReport
+}
+
+type CreditReport struct {
+ Agency CreditAgency
+ Report AgencyReport
+}
+
+type Customer struct {
+ Name string
+ Id int64
+ Active bool
+ Address AddressInfo
+ Credit CreditReport
+}
+
+type Invoice struct {
+ CustId int64
+ InvoiceNum int64
+ Amount int64
+ ShipTo AddressInfo
+}
+
+type Numbers struct {
+ B byte
+ Ui16 uint16
+ Ui32 uint32
+ Ui64 uint64
+ I16 int16
+ I32 int32
+ I64 int64
+ F32 float32
+ F64 float64
+ C64 complex64
+ C128 complex128
+}
+
+type FooType struct {
+ Bar BarType
+}
+
+type BarType struct {
+ Baz BazType
+}
+
+type TitleOrValueType union {
+ Title string
+ Value int64
+}
+
+type BazType struct {
+ Name string
+ TitleOrValue TitleOrValueType
+}
diff --git a/v23/syncbase/nosql/internal/query/demo/db_objects.vdl.go b/v23/syncbase/nosql/internal/query/demo/db_objects.vdl.go
new file mode 100644
index 0000000..dd96a5c
--- /dev/null
+++ b/v23/syncbase/nosql/internal/query/demo/db_objects.vdl.go
@@ -0,0 +1,335 @@
+// 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.
+
+// This file was auto-generated by the vanadium vdl tool.
+// Source: db_objects.vdl
+
+package main
+
+import (
+ // VDL system imports
+ "fmt"
+ "v.io/v23/vdl"
+)
+
+type AddressInfo struct {
+ Street string
+ City string
+ State string
+ Zip string
+}
+
+func (AddressInfo) __VDLReflect(struct {
+ Name string `vdl:"v.io/syncbase/v23/syncbase/nosql/internal/query/demo.AddressInfo"`
+}) {
+}
+
+type CreditAgency int
+
+const (
+ CreditAgencyEquifax CreditAgency = iota
+ CreditAgencyExperian
+ CreditAgencyTransUnion
+)
+
+// CreditAgencyAll holds all labels for CreditAgency.
+var CreditAgencyAll = [...]CreditAgency{CreditAgencyEquifax, CreditAgencyExperian, CreditAgencyTransUnion}
+
+// CreditAgencyFromString creates a CreditAgency from a string label.
+func CreditAgencyFromString(label string) (x CreditAgency, err error) {
+ err = x.Set(label)
+ return
+}
+
+// Set assigns label to x.
+func (x *CreditAgency) Set(label string) error {
+ switch label {
+ case "Equifax", "equifax":
+ *x = CreditAgencyEquifax
+ return nil
+ case "Experian", "experian":
+ *x = CreditAgencyExperian
+ return nil
+ case "TransUnion", "transunion":
+ *x = CreditAgencyTransUnion
+ return nil
+ }
+ *x = -1
+ return fmt.Errorf("unknown label %q in main.CreditAgency", label)
+}
+
+// String returns the string label of x.
+func (x CreditAgency) String() string {
+ switch x {
+ case CreditAgencyEquifax:
+ return "Equifax"
+ case CreditAgencyExperian:
+ return "Experian"
+ case CreditAgencyTransUnion:
+ return "TransUnion"
+ }
+ return ""
+}
+
+func (CreditAgency) __VDLReflect(struct {
+ Name string `vdl:"v.io/syncbase/v23/syncbase/nosql/internal/query/demo.CreditAgency"`
+ Enum struct{ Equifax, Experian, TransUnion string }
+}) {
+}
+
+type ExperianRating int
+
+const (
+ ExperianRatingGood ExperianRating = iota
+ ExperianRatingBad
+)
+
+// ExperianRatingAll holds all labels for ExperianRating.
+var ExperianRatingAll = [...]ExperianRating{ExperianRatingGood, ExperianRatingBad}
+
+// ExperianRatingFromString creates a ExperianRating from a string label.
+func ExperianRatingFromString(label string) (x ExperianRating, err error) {
+ err = x.Set(label)
+ return
+}
+
+// Set assigns label to x.
+func (x *ExperianRating) Set(label string) error {
+ switch label {
+ case "Good", "good":
+ *x = ExperianRatingGood
+ return nil
+ case "Bad", "bad":
+ *x = ExperianRatingBad
+ return nil
+ }
+ *x = -1
+ return fmt.Errorf("unknown label %q in main.ExperianRating", label)
+}
+
+// String returns the string label of x.
+func (x ExperianRating) String() string {
+ switch x {
+ case ExperianRatingGood:
+ return "Good"
+ case ExperianRatingBad:
+ return "Bad"
+ }
+ return ""
+}
+
+func (ExperianRating) __VDLReflect(struct {
+ Name string `vdl:"v.io/syncbase/v23/syncbase/nosql/internal/query/demo.ExperianRating"`
+ Enum struct{ Good, Bad string }
+}) {
+}
+
+type EquifaxCreditReport struct {
+ Rating byte
+}
+
+func (EquifaxCreditReport) __VDLReflect(struct {
+ Name string `vdl:"v.io/syncbase/v23/syncbase/nosql/internal/query/demo.EquifaxCreditReport"`
+}) {
+}
+
+type ExperianCreditReport struct {
+ Rating ExperianRating
+}
+
+func (ExperianCreditReport) __VDLReflect(struct {
+ Name string `vdl:"v.io/syncbase/v23/syncbase/nosql/internal/query/demo.ExperianCreditReport"`
+}) {
+}
+
+type TransUnionCreditReport struct {
+ Rating int16
+}
+
+func (TransUnionCreditReport) __VDLReflect(struct {
+ Name string `vdl:"v.io/syncbase/v23/syncbase/nosql/internal/query/demo.TransUnionCreditReport"`
+}) {
+}
+
+type (
+ // AgencyReport represents any single field of the AgencyReport union type.
+ AgencyReport interface {
+ // Index returns the field index.
+ Index() int
+ // Interface returns the field value as an interface.
+ Interface() interface{}
+ // Name returns the field name.
+ Name() string
+ // __VDLReflect describes the AgencyReport union type.
+ __VDLReflect(__AgencyReportReflect)
+ }
+ // AgencyReportEquifaxReport represents field EquifaxReport of the AgencyReport union type.
+ AgencyReportEquifaxReport struct{ Value EquifaxCreditReport }
+ // AgencyReportExperianReport represents field ExperianReport of the AgencyReport union type.
+ AgencyReportExperianReport struct{ Value ExperianCreditReport }
+ // AgencyReportTransUnionReport represents field TransUnionReport of the AgencyReport union type.
+ AgencyReportTransUnionReport struct{ Value TransUnionCreditReport }
+ // __AgencyReportReflect describes the AgencyReport union type.
+ __AgencyReportReflect struct {
+ Name string `vdl:"v.io/syncbase/v23/syncbase/nosql/internal/query/demo.AgencyReport"`
+ Type AgencyReport
+ Union struct {
+ EquifaxReport AgencyReportEquifaxReport
+ ExperianReport AgencyReportExperianReport
+ TransUnionReport AgencyReportTransUnionReport
+ }
+ }
+)
+
+func (x AgencyReportEquifaxReport) Index() int { return 0 }
+func (x AgencyReportEquifaxReport) Interface() interface{} { return x.Value }
+func (x AgencyReportEquifaxReport) Name() string { return "EquifaxReport" }
+func (x AgencyReportEquifaxReport) __VDLReflect(__AgencyReportReflect) {}
+
+func (x AgencyReportExperianReport) Index() int { return 1 }
+func (x AgencyReportExperianReport) Interface() interface{} { return x.Value }
+func (x AgencyReportExperianReport) Name() string { return "ExperianReport" }
+func (x AgencyReportExperianReport) __VDLReflect(__AgencyReportReflect) {}
+
+func (x AgencyReportTransUnionReport) Index() int { return 2 }
+func (x AgencyReportTransUnionReport) Interface() interface{} { return x.Value }
+func (x AgencyReportTransUnionReport) Name() string { return "TransUnionReport" }
+func (x AgencyReportTransUnionReport) __VDLReflect(__AgencyReportReflect) {}
+
+type CreditReport struct {
+ Agency CreditAgency
+ Report AgencyReport
+}
+
+func (CreditReport) __VDLReflect(struct {
+ Name string `vdl:"v.io/syncbase/v23/syncbase/nosql/internal/query/demo.CreditReport"`
+}) {
+}
+
+type Customer struct {
+ Name string
+ Id int64
+ Active bool
+ Address AddressInfo
+ Credit CreditReport
+}
+
+func (Customer) __VDLReflect(struct {
+ Name string `vdl:"v.io/syncbase/v23/syncbase/nosql/internal/query/demo.Customer"`
+}) {
+}
+
+type Invoice struct {
+ CustId int64
+ InvoiceNum int64
+ Amount int64
+ ShipTo AddressInfo
+}
+
+func (Invoice) __VDLReflect(struct {
+ Name string `vdl:"v.io/syncbase/v23/syncbase/nosql/internal/query/demo.Invoice"`
+}) {
+}
+
+type Numbers struct {
+ B byte
+ Ui16 uint16
+ Ui32 uint32
+ Ui64 uint64
+ I16 int16
+ I32 int32
+ I64 int64
+ F32 float32
+ F64 float64
+ C64 complex64
+ C128 complex128
+}
+
+func (Numbers) __VDLReflect(struct {
+ Name string `vdl:"v.io/syncbase/v23/syncbase/nosql/internal/query/demo.Numbers"`
+}) {
+}
+
+type FooType struct {
+ Bar BarType
+}
+
+func (FooType) __VDLReflect(struct {
+ Name string `vdl:"v.io/syncbase/v23/syncbase/nosql/internal/query/demo.FooType"`
+}) {
+}
+
+type BarType struct {
+ Baz BazType
+}
+
+func (BarType) __VDLReflect(struct {
+ Name string `vdl:"v.io/syncbase/v23/syncbase/nosql/internal/query/demo.BarType"`
+}) {
+}
+
+type (
+ // TitleOrValueType represents any single field of the TitleOrValueType union type.
+ TitleOrValueType interface {
+ // Index returns the field index.
+ Index() int
+ // Interface returns the field value as an interface.
+ Interface() interface{}
+ // Name returns the field name.
+ Name() string
+ // __VDLReflect describes the TitleOrValueType union type.
+ __VDLReflect(__TitleOrValueTypeReflect)
+ }
+ // TitleOrValueTypeTitle represents field Title of the TitleOrValueType union type.
+ TitleOrValueTypeTitle struct{ Value string }
+ // TitleOrValueTypeValue represents field Value of the TitleOrValueType union type.
+ TitleOrValueTypeValue struct{ Value int64 }
+ // __TitleOrValueTypeReflect describes the TitleOrValueType union type.
+ __TitleOrValueTypeReflect struct {
+ Name string `vdl:"v.io/syncbase/v23/syncbase/nosql/internal/query/demo.TitleOrValueType"`
+ Type TitleOrValueType
+ Union struct {
+ Title TitleOrValueTypeTitle
+ Value TitleOrValueTypeValue
+ }
+ }
+)
+
+func (x TitleOrValueTypeTitle) Index() int { return 0 }
+func (x TitleOrValueTypeTitle) Interface() interface{} { return x.Value }
+func (x TitleOrValueTypeTitle) Name() string { return "Title" }
+func (x TitleOrValueTypeTitle) __VDLReflect(__TitleOrValueTypeReflect) {}
+
+func (x TitleOrValueTypeValue) Index() int { return 1 }
+func (x TitleOrValueTypeValue) Interface() interface{} { return x.Value }
+func (x TitleOrValueTypeValue) Name() string { return "Value" }
+func (x TitleOrValueTypeValue) __VDLReflect(__TitleOrValueTypeReflect) {}
+
+type BazType struct {
+ Name string
+ TitleOrValue TitleOrValueType
+}
+
+func (BazType) __VDLReflect(struct {
+ Name string `vdl:"v.io/syncbase/v23/syncbase/nosql/internal/query/demo.BazType"`
+}) {
+}
+
+func init() {
+ vdl.Register((*AddressInfo)(nil))
+ vdl.Register((*CreditAgency)(nil))
+ vdl.Register((*ExperianRating)(nil))
+ vdl.Register((*EquifaxCreditReport)(nil))
+ vdl.Register((*ExperianCreditReport)(nil))
+ vdl.Register((*TransUnionCreditReport)(nil))
+ vdl.Register((*AgencyReport)(nil))
+ vdl.Register((*CreditReport)(nil))
+ vdl.Register((*Customer)(nil))
+ vdl.Register((*Invoice)(nil))
+ vdl.Register((*Numbers)(nil))
+ vdl.Register((*FooType)(nil))
+ vdl.Register((*BarType)(nil))
+ vdl.Register((*TitleOrValueType)(nil))
+ vdl.Register((*BazType)(nil))
+}
diff --git a/v23/syncbase/nosql/internal/query/demo/demo.go b/v23/syncbase/nosql/internal/query/demo/demo.go
index 963a00d..81274e0 100644
--- a/v23/syncbase/nosql/internal/query/demo/demo.go
+++ b/v23/syncbase/nosql/internal/query/demo/demo.go
@@ -8,18 +8,23 @@
"bufio"
"errors"
"fmt"
- "math/big"
"os"
"strings"
"v.io/syncbase/v23/syncbase/nosql/internal/query"
"v.io/syncbase/v23/syncbase/nosql/internal/query/query_db"
+ "v.io/v23/vdl"
)
type demoDB struct {
tables []table
}
+type kv struct {
+ key string
+ value *vdl.Value
+}
+
type table struct {
name string
rows []kv
@@ -58,7 +63,7 @@
return false
}
-func (kvs *keyValueStreamImpl) KeyValue() (string, interface{}) {
+func (kvs *keyValueStreamImpl) KeyValue() (string, *vdl.Value) {
return kvs.table.rows[kvs.cursor].key, kvs.table.rows[kvs.cursor].value
}
@@ -87,47 +92,6 @@
}
-type AddressInfo struct {
- Street string
- City string
- State string
- Zip string
-}
-
-type Customer struct {
- Name string
- ID int64
- Active bool
- Rating rune
- Address AddressInfo
-}
-
-type Invoice struct {
- CustID int64
- InvoiceNum int64
- Amount int64
- ShipTo AddressInfo
-}
-
-type Numbers struct {
- B byte
- UI16 uint16
- UI32 uint32
- UI64 uint64
- I16 int16
- I32 int32
- I64 int64
- BI big.Int
- F32 float32
- F64 float64
- BR big.Rat
-}
-
-type kv struct {
- key string
- value interface{}
-}
-
func createDB() query_db.Database {
var db demoDB
var custTable table
@@ -135,39 +99,39 @@
custTable.rows = []kv{
kv{
"001",
- Customer{"John Smith", 1, true, 'A', AddressInfo{"1 Main St.", "Palo Alto", "CA", "94303"}},
+ vdl.ValueOf(Customer{"John Smith", 1, true, AddressInfo{"1 Main St.", "Palo Alto", "CA", "94303"}, CreditReport{Agency: CreditAgencyEquifax, Report: AgencyReportEquifaxReport{EquifaxCreditReport{'A'}}}}),
},
kv{
"001001",
- Invoice{1, 1000, 42, AddressInfo{"1 Main St.", "Palo Alto", "CA", "94303"}},
+ vdl.ValueOf(Invoice{1, 1000, 42, AddressInfo{"1 Main St.", "Palo Alto", "CA", "94303"}}),
},
kv{
"001002",
- Invoice{1, 1003, 7, AddressInfo{"2 Main St.", "Palo Alto", "CA", "94303"}},
+ vdl.ValueOf(Invoice{1, 1003, 7, AddressInfo{"2 Main St.", "Palo Alto", "CA", "94303"}}),
},
kv{
"001003",
- Invoice{1, 1005, 88, AddressInfo{"3 Main St.", "Palo Alto", "CA", "94303"}},
+ vdl.ValueOf(Invoice{1, 1005, 88, AddressInfo{"3 Main St.", "Palo Alto", "CA", "94303"}}),
},
kv{
"002",
- Customer{"Bat Masterson", 2, true, 'B', AddressInfo{"777 Any St.", "Collins", "IA", "50055"}},
+ vdl.ValueOf(Customer{"Bat Masterson", 2, true, AddressInfo{"777 Any St.", "Collins", "IA", "50055"}, CreditReport{Agency: CreditAgencyTransUnion, Report: AgencyReportTransUnionReport{TransUnionCreditReport{80}}}}),
},
kv{
"002001",
- Invoice{2, 1001, 166, AddressInfo{"777 Any St.", "collins", "IA", "50055"}},
+ vdl.ValueOf(Invoice{2, 1001, 166, AddressInfo{"777 Any St.", "collins", "IA", "50055"}}),
},
kv{
"002002",
- Invoice{2, 1002, 243, AddressInfo{"888 Any St.", "collins", "IA", "50055"}},
+ vdl.ValueOf(Invoice{2, 1002, 243, AddressInfo{"888 Any St.", "collins", "IA", "50055"}}),
},
kv{
"002003",
- Invoice{2, 1004, 787, AddressInfo{"999 Any St.", "collins", "IA", "50055"}},
+ vdl.ValueOf(Invoice{2, 1004, 787, AddressInfo{"999 Any St.", "collins", "IA", "50055"}}),
},
kv{
"002004",
- Invoice{2, 1006, 88, AddressInfo{"101010 Any St.", "collins", "IA", "50055"}},
+ vdl.ValueOf(Invoice{2, 1006, 88, AddressInfo{"101010 Any St.", "collins", "IA", "50055"}}),
},
}
db.tables = append(db.tables, custTable)
@@ -177,14 +141,19 @@
numTable.rows = []kv{
kv{
"001",
- Numbers{byte(12), uint16(1234), uint32(5678), uint64(999888777666), int16(9876), int32(876543), int64(128), *big.NewInt(1234567890), float32(3.14159), float64(2.71828182846), *big.NewRat(123, 1)},
+ vdl.ValueOf(Numbers{byte(12), uint16(1234), uint32(5678), uint64(999888777666), int16(9876), int32(876543), int64(128), float32(3.14159), float64(2.71828182846), complex64(123.0 + 7.0i), complex128(456.789 + 10.1112i)}),
},
kv{
"002",
- Numbers{byte(9), uint16(99), uint32(999), uint64(9999999), int16(9), int32(99), int64(88), *big.NewInt(9999), float32(1.41421356237), float64(1.73205080757), *big.NewRat(999999, 1)},
+ vdl.ValueOf(Numbers{byte(9), uint16(99), uint32(999), uint64(9999999), int16(9), int32(99), int64(88), float32(1.41421356237), float64(1.73205080757), complex64(9.87 + 7.65i), complex128(4.32 + 1.0i)}),
+ },
+ kv{
+ "003",
+ vdl.ValueOf(Numbers{byte(210), uint16(210), uint32(210), uint64(210), int16(210), int32(210), int64(210), float32(210.0), float64(210.0), complex64(210.0 + 0.0i), complex128(210.0 + 0.0i)}),
},
}
db.tables = append(db.tables, numTable)
+
return db
}
@@ -194,17 +163,7 @@
for _, t := range db.tables {
fmt.Printf("table: %s\n", t.name)
for _, row := range t.rows {
- fmt.Printf("key: %s, value: ", row.key)
- switch v := row.value.(type) {
- case Customer:
- fmt.Printf("type:Customer Name:%v,ID:%v,Active:%v,Rating:%v,Address{%v}\n", v.Name, v.ID, v.Active, v.Rating, dumpAddress(&v.Address))
- case Invoice:
- fmt.Printf("type:Invoice CustID:%v,InvoiceNum:%v,Amount:%v,ShipTo:{%v}\n", v.CustID, v.InvoiceNum, v.Amount, dumpAddress(&v.ShipTo))
- case Numbers:
- fmt.Printf("type:Numbers B:%v,UI16:%v,UI32:%v,UI64:%v,I16:%v,I32:%v,I64:%v,BI:%v,F32:%v,F64:%v,BR:%v\n", v.B, v.UI16, v.UI32, v.UI64, v.I16, v.I32, v.I64, v.BI, v.F32, v.F64, v.BR)
- default:
- fmt.Printf("%v\n", row.value)
- }
+ fmt.Printf("key: %s, value: %v", row.key, row.value)
}
}
}
diff --git a/v23/syncbase/nosql/internal/query/eval.go b/v23/syncbase/nosql/internal/query/eval.go
index e923c60..ce9d7f0 100644
--- a/v23/syncbase/nosql/internal/query/eval.go
+++ b/v23/syncbase/nosql/internal/query/eval.go
@@ -13,9 +13,10 @@
"v.io/syncbase/v23/syncbase/nosql/internal/query/query_checker"
"v.io/syncbase/v23/syncbase/nosql/internal/query/query_parser"
+ "v.io/v23/vdl"
)
-func Eval(k string, v interface{}, e *query_parser.Expression) bool {
+func Eval(k string, v *vdl.Value, e *query_parser.Expression) bool {
if query_checker.IsLogicalOperator(e.Operator) {
return evalLogicalOperators(k, v, e)
} else {
@@ -23,7 +24,7 @@
}
}
-func evalLogicalOperators(k string, v interface{}, e *query_parser.Expression) bool {
+func evalLogicalOperators(k string, v *vdl.Value, e *query_parser.Expression) bool {
switch e.Operator.Type {
case query_parser.And:
return Eval(k, v, e.Operand1.Expr) && Eval(k, v, e.Operand2.Expr)
@@ -35,7 +36,7 @@
}
}
-func evalComparisonOperators(k string, v interface{}, e *query_parser.Expression) bool {
+func evalComparisonOperators(k string, v *vdl.Value, e *query_parser.Expression) bool {
lhsValue := resolveOperand(k, v, e.Operand1)
// Check for an is nil epression (i.e., v[.<field>...] is nil).
// These expressions evaluate to true if the field cannot be resolved.
@@ -68,6 +69,8 @@
return compareBigRats(lhsValue, rhsValue, e.Operator)
case query_parser.TypBool:
return compareBools(lhsValue, rhsValue, e.Operator)
+ case query_parser.TypComplex:
+ return compareComplex(lhsValue, rhsValue, e.Operator)
case query_parser.TypFloat:
return compareFloats(lhsValue, rhsValue, e.Operator)
case query_parser.TypInt:
@@ -83,6 +86,7 @@
}
func coerceValues(lhsValue, rhsValue *query_parser.Operand) (*query_parser.Operand, *query_parser.Operand, error) {
+ // TODO(jkline): explore using vdl for coercions ( https://v.io/designdocs/vdl-spec.html#conversions ).
var err error
// If either operand is a string, convert the other to a string.
if lhsValue.Type == query_parser.TypStr || rhsValue.Type == query_parser.TypStr {
@@ -92,6 +96,23 @@
if rhsValue, err = convertValueToString(rhsValue); err != nil {
return nil, nil, err
}
+ return lhsValue, rhsValue, nil
+ }
+ // If either operand is Complex, promote numerics to Complex.
+ // Comparing complex to string is handled above.
+ if lhsValue.Type == query_parser.TypComplex || rhsValue.Type == query_parser.TypComplex {
+ // If both complex, just return them.
+ if lhsValue.Type == query_parser.TypComplex && rhsValue.Type == query_parser.TypComplex {
+ return lhsValue, rhsValue, nil
+ }
+ var err error
+ if lhsValue, err = convertValueToComplex(lhsValue); err != nil {
+ return nil, nil, err
+ }
+ if rhsValue, err = convertValueToComplex(rhsValue); err != nil {
+ return nil, nil, err
+ }
+ return lhsValue, rhsValue, nil
}
// If either operand is a big rat, convert both to a big rat.
// Also, if one operand is a float and the other is a big int,
@@ -103,6 +124,7 @@
if rhsValue, err = convertValueToBigRat(rhsValue); err != nil {
return nil, nil, err
}
+ return lhsValue, rhsValue, nil
}
// If either operand is a float, convert the other to a float.
if lhsValue.Type == query_parser.TypFloat || rhsValue.Type == query_parser.TypFloat {
@@ -112,6 +134,7 @@
if rhsValue, err = convertValueToFloat(rhsValue); err != nil {
return nil, nil, err
}
+ return lhsValue, rhsValue, nil
}
// If either operand is a big int, convert both to a big int.
// Also, if one operand is a uint64 and the other is an int64, convert both to big ints.
@@ -122,6 +145,7 @@
if rhsValue, err = convertValueToBigInt(rhsValue); err != nil {
return nil, nil, err
}
+ return lhsValue, rhsValue, nil
}
// If either operand is an int64, convert the other to int64.
if lhsValue.Type == query_parser.TypInt || rhsValue.Type == query_parser.TypInt {
@@ -131,6 +155,7 @@
if rhsValue, err = convertValueToInt(rhsValue); err != nil {
return nil, nil, err
}
+ return lhsValue, rhsValue, nil
}
// If either operand is an uint64, convert the other to uint64.
if lhsValue.Type == query_parser.TypUint || rhsValue.Type == query_parser.TypUint {
@@ -140,6 +165,7 @@
if rhsValue, err = convertValueToUint(rhsValue); err != nil {
return nil, nil, err
}
+ return lhsValue, rhsValue, nil
}
// Must be the same at this point.
if lhsValue.Type != rhsValue.Type {
@@ -160,6 +186,8 @@
c.Str = o.BigRat.String()
case query_parser.TypBool:
c.Str = strconv.FormatBool(o.Bool)
+ case query_parser.TypComplex:
+ c.Str = fmt.Sprintf("%g", o.Complex)
case query_parser.TypFloat:
c.Str = strconv.FormatFloat(o.Float, 'f', -1, 64)
case query_parser.TypInt:
@@ -181,6 +209,24 @@
return &c, nil
}
+func convertValueToComplex(o *query_parser.Operand) (*query_parser.Operand, error) {
+ var c query_parser.Operand
+ c.Type = query_parser.TypComplex
+ switch o.Type {
+ case query_parser.TypComplex:
+ return o, nil
+ case query_parser.TypFloat:
+ c.Complex = complex(o.Float, 0.0i)
+ case query_parser.TypInt:
+ c.Complex = complex(float64(o.Int), 0.0i)
+ case query_parser.TypUint:
+ c.Complex = complex(float64(o.Uint), 0.0i)
+ default:
+ return nil, errors.New("Cannot convert operand to Complex for comparison.")
+ }
+ return &c, nil
+}
+
func convertValueToBigRat(o *query_parser.Operand) (*query_parser.Operand, error) {
// operand cannot be string literal.
var c query_parser.Operand
@@ -346,6 +392,18 @@
}
}
+func compareComplex(lhsValue, rhsValue *query_parser.Operand, oper *query_parser.BinaryOperator) bool {
+ switch oper.Type {
+ case query_parser.Equal:
+ return lhsValue.Complex == rhsValue.Complex
+ case query_parser.NotEqual:
+ return lhsValue.Complex != rhsValue.Complex
+ default:
+ // Complex values are not ordered. All other operands return false.
+ return false
+ }
+}
+
func compareFloats(lhsValue, rhsValue *query_parser.Operand, oper *query_parser.BinaryOperator) bool {
switch oper.Type {
case query_parser.Equal:
@@ -447,12 +505,12 @@
}
}
-func resolveOperand(k string, v interface{}, o *query_parser.Operand) *query_parser.Operand {
+func resolveOperand(k string, v *vdl.Value, o *query_parser.Operand) *query_parser.Operand {
if o.Type != query_parser.TypField {
return o
}
value, hasAltStr, altStr := ResolveField(k, v, o.Column)
- if value == nil {
+ if value.IsNil() {
return nil
}
@@ -460,61 +518,34 @@
var newOp query_parser.Operand
newOp.Off = o.Off
- switch value := value.(type) {
- case bool:
+ switch value.Kind() {
+ case vdl.Bool:
newOp.Type = query_parser.TypBool
- newOp.Bool = value
- case int:
+ newOp.Bool = value.Bool()
+ case vdl.Byte:
newOp.Type = query_parser.TypInt
- newOp.Int = int64(value)
- case int8:
- newOp.Type = query_parser.TypInt
- newOp.Int = int64(value)
- case int16:
- newOp.Type = query_parser.TypInt
- newOp.Int = int64(value)
- case int32: // rune
- newOp.Type = query_parser.TypInt
- newOp.Int = int64(value)
- case int64:
- newOp.Type = query_parser.TypInt
- newOp.Int = value
- case uint:
- newOp.Type = query_parser.TypBigInt
- var b big.Int
- b.SetUint64(uint64(value))
- newOp.BigInt = &b
- case uint8: // byte
- newOp.Type = query_parser.TypInt
- newOp.Int = int64(value)
- case uint16:
- newOp.Type = query_parser.TypInt
- newOp.Int = int64(value)
- case uint32:
- newOp.Type = query_parser.TypInt
- newOp.Int = int64(value)
- case uint64:
- newOp.Type = query_parser.TypBigInt
- var b big.Int
- b.SetUint64(value)
- newOp.BigInt = &b
- case float32:
- newOp.Type = query_parser.TypFloat
- newOp.Float = float64(value)
- case float64:
- newOp.Type = query_parser.TypFloat
- newOp.Float = value
- case string:
+ newOp.Int = int64(value.Byte())
+ case vdl.Enum:
newOp.Type = query_parser.TypStr
- newOp.Str = value
+ newOp.Str = value.EnumLabel()
+ newOp.HasAltStr = false
+ case vdl.Int16, vdl.Int32, vdl.Int64:
+ newOp.Type = query_parser.TypInt
+ newOp.Int = value.Int()
+ case vdl.Uint16, vdl.Uint32, vdl.Uint64:
+ newOp.Type = query_parser.TypInt
+ newOp.Int = int64(value.Uint())
+ case vdl.Float32, vdl.Float64:
+ newOp.Type = query_parser.TypFloat
+ newOp.Float = value.Float()
+ case vdl.String:
+ newOp.Type = query_parser.TypStr
+ newOp.Str = value.RawString()
newOp.HasAltStr = hasAltStr
newOp.AltStr = altStr
- case *big.Int:
- newOp.Type = query_parser.TypBigInt
- newOp.BigInt = value
- case *big.Rat:
- newOp.Type = query_parser.TypBigRat
- newOp.BigRat = value
+ case vdl.Complex64, vdl.Complex128:
+ newOp.Type = query_parser.TypComplex
+ newOp.Complex = value.Complex()
default: // OpObject for structs, arrays, maps, ...
newOp.Type = query_parser.TypObject
newOp.Object = value
@@ -526,37 +557,38 @@
// to a string being returned, and alternate string is returned. In this case,
// <string-value>, true, <alt-string> is returned. In all other cases,
// <value>,false,"" is returned.
-func ResolveField(k string, v interface{}, f *query_parser.Field) (interface{}, bool, string) {
+func ResolveField(k string, v *vdl.Value, f *query_parser.Field) (*vdl.Value, bool, string) {
if query_checker.IsKeyField(f) {
- return k, false, ""
+ return vdl.StringValue(k), false, ""
}
+ t := v.Type()
if query_checker.IsTypeField(f) {
- if v == nil {
- return nil, false, ""
- } else {
- // Types evaluate to two strings, Str and AltStr.
- // This is because types match on full path or just the name.
- name := reflect.ValueOf(v).Type().Name()
- return reflect.ValueOf(v).Type().PkgPath() + "." + name, true, name
- }
+ // Types evaluate to two strings, Str and AltStr.
+ // This is because types match on full path or just the name.
+ pkg, name := vdl.SplitIdent(t.Name())
+ return vdl.StringValue(pkg + "." + name), true, name
}
+
object := v
segments := f.Segments
- // The first segment will always be v itself, skip it.
+ // The first segment will always be v (itself), skip it.
for i := 1; i < len(segments); i++ {
// object must be a struct in order to look for the next segment.
- if reflect.ValueOf(object).Kind() != reflect.Struct {
- return nil, false, "" // field does not exist
+ if object.Kind() == vdl.Struct {
+ if object = object.StructFieldByName(segments[i].Value); object == nil {
+ return vdl.ValueOf(nil), false, "" // field does not exist
+ }
+ } else if object.Kind() == vdl.Union {
+ unionType := object.Type()
+ idx, tempValue := object.UnionField()
+ if segments[i].Value == unionType.Field(idx).Name {
+ object = tempValue
+ } else {
+ return vdl.ValueOf(nil), false, "" // union field does not exist or is not set
+ }
+ } else {
+ return vdl.ValueOf(nil), false, "" // can only traverse into structs and unions
}
- // Look up the segment in object.
- _, ok := reflect.ValueOf(object).Type().FieldByName(segments[i].Value)
- if !ok {
- return nil, false, "" // field does not exist
- }
- if !reflect.ValueOf(object).FieldByName(segments[i].Value).CanInterface() {
- return nil, false, ""
- }
- object = reflect.ValueOf(object).FieldByName(segments[i].Value).Interface()
}
return object, false, ""
}
diff --git a/v23/syncbase/nosql/internal/query/query.go b/v23/syncbase/nosql/internal/query/query.go
index a6c1c99..5c76cd2 100644
--- a/v23/syncbase/nosql/internal/query/query.go
+++ b/v23/syncbase/nosql/internal/query/query.go
@@ -11,6 +11,7 @@
"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"
+ "v.io/v23/vdl"
)
type QueryError struct {
@@ -20,7 +21,7 @@
type ResultStream interface {
Advance() bool
- Result() []interface{}
+ Result() []*vdl.Value
Err() *QueryError
Cancel()
}
@@ -59,8 +60,8 @@
// Given a key, a value and a SelectClause, return the projection.
// This function is only called if Eval returned true on the WhereClause expression.
-func ComposeProjection(k string, v interface{}, s *query_parser.SelectClause) []interface{} {
- var projection []interface{}
+func ComposeProjection(k string, v *vdl.Value, s *query_parser.SelectClause) []*vdl.Value {
+ var projection []*vdl.Value
for _, f := range s.Columns {
// If field not found, nil is returned (as per specification).
c, _, _ := ResolveField(k, v, &f)
@@ -85,12 +86,13 @@
}
// For testing purposes, given a SelectStatement, k and v;
-// return nil if row not selected, else return the projection (type []interface{}).
+// return nil if row not selected, else return the projection (type []*vdl.Value).
// Note: limit and offset clauses are ignored for this function as they make no sense
// for a single row.
-func ExecSelectSingleRow(k string, v interface{}, s *query_parser.SelectStatement) interface{} {
+func ExecSelectSingleRow(k string, v *vdl.Value, s *query_parser.SelectStatement) []*vdl.Value {
if !Eval(k, v, s.Where.Expr) {
- return nil
+ rs := []*vdl.Value{}
+ return rs
}
return ComposeProjection(k, v, s.Select)
}
@@ -101,7 +103,7 @@
skippedCount int64 // skipped so far (needed for offset clause)
keyValueStream query_db.KeyValueStream
k string
- v interface{}
+ v *vdl.Value
err *QueryError
}
@@ -142,7 +144,7 @@
return false
}
-func (rs *resultStreamImpl) Result() []interface{} {
+func (rs *resultStreamImpl) Result() []*vdl.Value {
return ComposeProjection(rs.k, rs.v, rs.selectStatement.Select)
}
diff --git a/v23/syncbase/nosql/internal/query/query_db/query_db.go b/v23/syncbase/nosql/internal/query/query_db/query_db.go
index 10e5ec0..8724fd2 100644
--- a/v23/syncbase/nosql/internal/query/query_db/query_db.go
+++ b/v23/syncbase/nosql/internal/query/query_db/query_db.go
@@ -4,6 +4,10 @@
package query_db
+import (
+ "v.io/v23/vdl"
+)
+
type Database interface {
GetTable(name string) (Table, error)
}
@@ -29,7 +33,7 @@
// KeyValue returns the element that was staged by Advance.
// KeyValue may panic if Advance returned false or was not
// called at all. KeyValue does not block.
- KeyValue() (string, interface{})
+ KeyValue() (string, *vdl.Value)
// Err returns a non-nil error iff the stream encountered
// any errors. Err does not block.
diff --git a/v23/syncbase/nosql/internal/query/query_parser/query_parser.go b/v23/syncbase/nosql/internal/query/query_parser/query_parser.go
index 502fe43..2e45daf 100644
--- a/v23/syncbase/nosql/internal/query/query_parser/query_parser.go
+++ b/v23/syncbase/nosql/internal/query/query_parser/query_parser.go
@@ -14,6 +14,7 @@
"unicode/utf8"
"v.io/syncbase/v23/syncbase/nosql/internal/query/query_db"
+ "v.io/v23/vdl"
)
type TokenType int
@@ -101,6 +102,7 @@
TypBigInt OperandType = 1 + iota // Only as a result of Resolve/Coerce Operand
TypBigRat // Only as a result of Resolve/Coerce Operand
TypBool
+ TypComplex
TypExpr
TypField
TypFloat
@@ -117,6 +119,7 @@
BigInt *big.Int
BigRat *big.Rat
Bool bool
+ Complex complex128
Column *Field
Float float64
Function *Function
@@ -129,7 +132,7 @@
Uint uint64
CompRegex *regexp.Regexp
Expr *Expression
- Object interface{}
+ Object *vdl.Value
Node
}
@@ -903,6 +906,9 @@
case TypBigRat:
val += "(BigRat)"
val += o.BigRat.String()
+ case TypComplex:
+ val += "(Complex)"
+ val += fmt.Sprintf("%g", o.Complex)
case TypField:
val += "(field)"
val += o.Column.String()
diff --git a/v23/syncbase/nosql/internal/query/query_test.go b/v23/syncbase/nosql/internal/query/query_test.go
deleted file mode 100644
index a703e17..0000000
--- a/v23/syncbase/nosql/internal/query/query_test.go
+++ /dev/null
@@ -1,1179 +0,0 @@
-// 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 init() {
- 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 query.EvalWithKeyResult
-}
-
-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 values where v.InvoiceNum is nil
- // Since InvoiceNum does not exist for Invoice,
- // this will return just customers.
- "select v from Customer where v.InvoiceNum is nil",
- []interface{}{
- []interface{}{customerRows[0].value},
- []interface{}{customerRows[4].value},
- },
- },
- {
- // Select values where v.InvoiceNum is nil
- // or v.Name is nil This will select all customers
- // with the former and all invoices with the latter.
- // Hence, all records are returned.
- "select v from Customer where v.InvoiceNum is nil or v.Name is nil",
- []interface{}{
- []interface{}{customerRows[0].value},
- []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 values where v.InvoiceNum is nil
- // and v.Name is nil. Expect nothing returned.
- "select v from Customer where v.InvoiceNum is nil and v.Name is nil",
- []interface{}{},
- },
- {
- // Select values where v.InvoiceNum is not nil
- // This will return just invoices.
- "select v from Customer where v.InvoiceNum is not nil",
- []interface{}{
- []interface{}{customerRows[1].value},
- []interface{}{customerRows[2].value},
- []interface{}{customerRows[3].value},
- []interface{}{customerRows[5].value},
- []interface{}{customerRows[6].value},
- []interface{}{customerRows[7].value},
- []interface{}{customerRows[8].value},
- },
- },
- {
- // Select values where v.InvoiceNum is not nil
- // or v.Name is not nil. All records are returned.
- "select v from Customer where v.InvoiceNum is not nil or v.Name is not nil",
- []interface{}{
- []interface{}{customerRows[0].value},
- []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 values where v.InvoiceNum is nil and v.Name is not nil.
- // All customers are returned.
- "select v from Customer where v.InvoiceNum is nil and v.Name is not nil",
- []interface{}{
- []interface{}{customerRows[0].value},
- []interface{}{customerRows[4].value},
- },
- },
- {
- // Select values where v.InvoiceNum is not nil
- // and v.Name is not nil. Expect nothing returned.
- "select v from Customer where v.InvoiceNum is not nil and v.Name is not nil",
- []interface{}{},
- },
- {
- // 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 records where v.Foo.FooBarBaz.Baz is 84 and v.InvoiceNum is not nil.
- "select v from Customer where v.Foo.FooBarBaz.Baz = 84 and v.InvoiceNum is not nil",
- []interface{}{},
- },
- {
- // Select records where v.Foo.FooBarBaz.Baz is 84 and v.InvoiceNum is nil.
- "select v from Customer where v.Foo.FooBarBaz.Baz = 84 and v.InvoiceNum is nil",
- []interface{}{
- []interface{}{customerRows[4].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},
- },
- },
- {
- // Select records where v.Foo.FooBarBaz.Baz is 84 or (type is Invoice and v.InvoiceNum is not nil).
- // Limit 3 Offset 2
- "select v from Customer where v.Foo.FooBarBaz.Baz = 84 or (t = \"Invoice\" and v.InvoiceNum is not nil) limit 3 offset 2",
- []interface{}{
- []interface{}{customerRows[3].value},
- []interface{}{customerRows[4].value},
- []interface{}{customerRows[5].value},
- },
- },
- {
- // Select records where v.Foo.FooBarBaz.Baz is 84 or (type is Invoice and v.InvoiceNum is nil).
- // Limit 3 Offset 2
- "select v from Customer where v.Foo.FooBarBaz.Baz = 84 or (t = \"Invoice\" and v.InvoiceNum is nil) limit 3 offset 2",
- []interface{}{},
- },
- }
-
- 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", test.query, 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",
- query.INCLUDE,
- },
- {
- // Row will be rejected using only the key.
- "select k, v from Customer where k like \"abc\"",
- "abcd",
- query.EXCLUDE,
- },
- {
- // Need value to determine if row should be selected.
- "select k, v from Customer where k = \"abc\" or v.zip = \"94303\"",
- "abcd",
- query.FETCH_VALUE,
- },
- {
- // 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",
- query.FETCH_VALUE,
- },
- {
- // 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",
- query.EXCLUDE,
- },
- }
-
- 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.EvalWhereUsingOnlyKey(&sel, test.key)
- 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", test.query, 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", test.query, 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", test.query, 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", test.query, 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)
- }
- }
-}
diff --git a/v23/syncbase/nosql/internal/query/test/db_objects.vdl b/v23/syncbase/nosql/internal/query/test/db_objects.vdl
new file mode 100644
index 0000000..a444f0d
--- /dev/null
+++ b/v23/syncbase/nosql/internal/query/test/db_objects.vdl
@@ -0,0 +1,93 @@
+// 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 test
+
+type AddressInfo struct {
+ Street string
+ City string
+ State string
+ Zip string
+}
+
+type CreditAgency enum {
+ Equifax
+ Experian
+ TransUnion
+}
+
+type ExperianRating enum {
+ Good
+ Bad
+}
+
+type EquifaxCreditReport struct {
+ Rating byte
+}
+
+type ExperianCreditReport struct {
+ Rating ExperianRating
+}
+
+type TransUnionCreditReport struct {
+ Rating int16
+}
+
+type AgencyReport union {
+ EquifaxReport EquifaxCreditReport
+ ExperianReport ExperianCreditReport
+ TransUnionReport TransUnionCreditReport
+}
+
+type CreditReport struct {
+ Agency CreditAgency
+ Report AgencyReport
+}
+
+type Customer struct {
+ Name string
+ Id int64
+ Active bool
+ Address AddressInfo
+ Credit CreditReport
+}
+
+type Invoice struct {
+ CustId int64
+ InvoiceNum int64
+ Amount int64
+ ShipTo AddressInfo
+}
+
+type Numbers struct {
+ B byte
+ Ui16 uint16
+ Ui32 uint32
+ Ui64 uint64
+ I16 int16
+ I32 int32
+ I64 int64
+ F32 float32
+ F64 float64
+ C64 complex64
+ C128 complex128
+}
+
+type FooType struct {
+ Bar BarType
+}
+
+type BarType struct {
+ Baz BazType
+}
+
+type TitleOrValueType union {
+ Title string
+ Value int64
+}
+
+type BazType struct {
+ Name string
+ TitleOrValue TitleOrValueType
+}
diff --git a/v23/syncbase/nosql/internal/query/test/db_objects.vdl.go b/v23/syncbase/nosql/internal/query/test/db_objects.vdl.go
new file mode 100644
index 0000000..e05dedf
--- /dev/null
+++ b/v23/syncbase/nosql/internal/query/test/db_objects.vdl.go
@@ -0,0 +1,335 @@
+// 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.
+
+// This file was auto-generated by the vanadium vdl tool.
+// Source: db_objects.vdl
+
+package test
+
+import (
+ // VDL system imports
+ "fmt"
+ "v.io/v23/vdl"
+)
+
+type AddressInfo struct {
+ Street string
+ City string
+ State string
+ Zip string
+}
+
+func (AddressInfo) __VDLReflect(struct {
+ Name string `vdl:"v.io/syncbase/v23/syncbase/nosql/internal/query/test.AddressInfo"`
+}) {
+}
+
+type CreditAgency int
+
+const (
+ CreditAgencyEquifax CreditAgency = iota
+ CreditAgencyExperian
+ CreditAgencyTransUnion
+)
+
+// CreditAgencyAll holds all labels for CreditAgency.
+var CreditAgencyAll = [...]CreditAgency{CreditAgencyEquifax, CreditAgencyExperian, CreditAgencyTransUnion}
+
+// CreditAgencyFromString creates a CreditAgency from a string label.
+func CreditAgencyFromString(label string) (x CreditAgency, err error) {
+ err = x.Set(label)
+ return
+}
+
+// Set assigns label to x.
+func (x *CreditAgency) Set(label string) error {
+ switch label {
+ case "Equifax", "equifax":
+ *x = CreditAgencyEquifax
+ return nil
+ case "Experian", "experian":
+ *x = CreditAgencyExperian
+ return nil
+ case "TransUnion", "transunion":
+ *x = CreditAgencyTransUnion
+ return nil
+ }
+ *x = -1
+ return fmt.Errorf("unknown label %q in test.CreditAgency", label)
+}
+
+// String returns the string label of x.
+func (x CreditAgency) String() string {
+ switch x {
+ case CreditAgencyEquifax:
+ return "Equifax"
+ case CreditAgencyExperian:
+ return "Experian"
+ case CreditAgencyTransUnion:
+ return "TransUnion"
+ }
+ return ""
+}
+
+func (CreditAgency) __VDLReflect(struct {
+ Name string `vdl:"v.io/syncbase/v23/syncbase/nosql/internal/query/test.CreditAgency"`
+ Enum struct{ Equifax, Experian, TransUnion string }
+}) {
+}
+
+type ExperianRating int
+
+const (
+ ExperianRatingGood ExperianRating = iota
+ ExperianRatingBad
+)
+
+// ExperianRatingAll holds all labels for ExperianRating.
+var ExperianRatingAll = [...]ExperianRating{ExperianRatingGood, ExperianRatingBad}
+
+// ExperianRatingFromString creates a ExperianRating from a string label.
+func ExperianRatingFromString(label string) (x ExperianRating, err error) {
+ err = x.Set(label)
+ return
+}
+
+// Set assigns label to x.
+func (x *ExperianRating) Set(label string) error {
+ switch label {
+ case "Good", "good":
+ *x = ExperianRatingGood
+ return nil
+ case "Bad", "bad":
+ *x = ExperianRatingBad
+ return nil
+ }
+ *x = -1
+ return fmt.Errorf("unknown label %q in test.ExperianRating", label)
+}
+
+// String returns the string label of x.
+func (x ExperianRating) String() string {
+ switch x {
+ case ExperianRatingGood:
+ return "Good"
+ case ExperianRatingBad:
+ return "Bad"
+ }
+ return ""
+}
+
+func (ExperianRating) __VDLReflect(struct {
+ Name string `vdl:"v.io/syncbase/v23/syncbase/nosql/internal/query/test.ExperianRating"`
+ Enum struct{ Good, Bad string }
+}) {
+}
+
+type EquifaxCreditReport struct {
+ Rating byte
+}
+
+func (EquifaxCreditReport) __VDLReflect(struct {
+ Name string `vdl:"v.io/syncbase/v23/syncbase/nosql/internal/query/test.EquifaxCreditReport"`
+}) {
+}
+
+type ExperianCreditReport struct {
+ Rating ExperianRating
+}
+
+func (ExperianCreditReport) __VDLReflect(struct {
+ Name string `vdl:"v.io/syncbase/v23/syncbase/nosql/internal/query/test.ExperianCreditReport"`
+}) {
+}
+
+type TransUnionCreditReport struct {
+ Rating int16
+}
+
+func (TransUnionCreditReport) __VDLReflect(struct {
+ Name string `vdl:"v.io/syncbase/v23/syncbase/nosql/internal/query/test.TransUnionCreditReport"`
+}) {
+}
+
+type (
+ // AgencyReport represents any single field of the AgencyReport union type.
+ AgencyReport interface {
+ // Index returns the field index.
+ Index() int
+ // Interface returns the field value as an interface.
+ Interface() interface{}
+ // Name returns the field name.
+ Name() string
+ // __VDLReflect describes the AgencyReport union type.
+ __VDLReflect(__AgencyReportReflect)
+ }
+ // AgencyReportEquifaxReport represents field EquifaxReport of the AgencyReport union type.
+ AgencyReportEquifaxReport struct{ Value EquifaxCreditReport }
+ // AgencyReportExperianReport represents field ExperianReport of the AgencyReport union type.
+ AgencyReportExperianReport struct{ Value ExperianCreditReport }
+ // AgencyReportTransUnionReport represents field TransUnionReport of the AgencyReport union type.
+ AgencyReportTransUnionReport struct{ Value TransUnionCreditReport }
+ // __AgencyReportReflect describes the AgencyReport union type.
+ __AgencyReportReflect struct {
+ Name string `vdl:"v.io/syncbase/v23/syncbase/nosql/internal/query/test.AgencyReport"`
+ Type AgencyReport
+ Union struct {
+ EquifaxReport AgencyReportEquifaxReport
+ ExperianReport AgencyReportExperianReport
+ TransUnionReport AgencyReportTransUnionReport
+ }
+ }
+)
+
+func (x AgencyReportEquifaxReport) Index() int { return 0 }
+func (x AgencyReportEquifaxReport) Interface() interface{} { return x.Value }
+func (x AgencyReportEquifaxReport) Name() string { return "EquifaxReport" }
+func (x AgencyReportEquifaxReport) __VDLReflect(__AgencyReportReflect) {}
+
+func (x AgencyReportExperianReport) Index() int { return 1 }
+func (x AgencyReportExperianReport) Interface() interface{} { return x.Value }
+func (x AgencyReportExperianReport) Name() string { return "ExperianReport" }
+func (x AgencyReportExperianReport) __VDLReflect(__AgencyReportReflect) {}
+
+func (x AgencyReportTransUnionReport) Index() int { return 2 }
+func (x AgencyReportTransUnionReport) Interface() interface{} { return x.Value }
+func (x AgencyReportTransUnionReport) Name() string { return "TransUnionReport" }
+func (x AgencyReportTransUnionReport) __VDLReflect(__AgencyReportReflect) {}
+
+type CreditReport struct {
+ Agency CreditAgency
+ Report AgencyReport
+}
+
+func (CreditReport) __VDLReflect(struct {
+ Name string `vdl:"v.io/syncbase/v23/syncbase/nosql/internal/query/test.CreditReport"`
+}) {
+}
+
+type Customer struct {
+ Name string
+ Id int64
+ Active bool
+ Address AddressInfo
+ Credit CreditReport
+}
+
+func (Customer) __VDLReflect(struct {
+ Name string `vdl:"v.io/syncbase/v23/syncbase/nosql/internal/query/test.Customer"`
+}) {
+}
+
+type Invoice struct {
+ CustId int64
+ InvoiceNum int64
+ Amount int64
+ ShipTo AddressInfo
+}
+
+func (Invoice) __VDLReflect(struct {
+ Name string `vdl:"v.io/syncbase/v23/syncbase/nosql/internal/query/test.Invoice"`
+}) {
+}
+
+type Numbers struct {
+ B byte
+ Ui16 uint16
+ Ui32 uint32
+ Ui64 uint64
+ I16 int16
+ I32 int32
+ I64 int64
+ F32 float32
+ F64 float64
+ C64 complex64
+ C128 complex128
+}
+
+func (Numbers) __VDLReflect(struct {
+ Name string `vdl:"v.io/syncbase/v23/syncbase/nosql/internal/query/test.Numbers"`
+}) {
+}
+
+type FooType struct {
+ Bar BarType
+}
+
+func (FooType) __VDLReflect(struct {
+ Name string `vdl:"v.io/syncbase/v23/syncbase/nosql/internal/query/test.FooType"`
+}) {
+}
+
+type BarType struct {
+ Baz BazType
+}
+
+func (BarType) __VDLReflect(struct {
+ Name string `vdl:"v.io/syncbase/v23/syncbase/nosql/internal/query/test.BarType"`
+}) {
+}
+
+type (
+ // TitleOrValueType represents any single field of the TitleOrValueType union type.
+ TitleOrValueType interface {
+ // Index returns the field index.
+ Index() int
+ // Interface returns the field value as an interface.
+ Interface() interface{}
+ // Name returns the field name.
+ Name() string
+ // __VDLReflect describes the TitleOrValueType union type.
+ __VDLReflect(__TitleOrValueTypeReflect)
+ }
+ // TitleOrValueTypeTitle represents field Title of the TitleOrValueType union type.
+ TitleOrValueTypeTitle struct{ Value string }
+ // TitleOrValueTypeValue represents field Value of the TitleOrValueType union type.
+ TitleOrValueTypeValue struct{ Value int64 }
+ // __TitleOrValueTypeReflect describes the TitleOrValueType union type.
+ __TitleOrValueTypeReflect struct {
+ Name string `vdl:"v.io/syncbase/v23/syncbase/nosql/internal/query/test.TitleOrValueType"`
+ Type TitleOrValueType
+ Union struct {
+ Title TitleOrValueTypeTitle
+ Value TitleOrValueTypeValue
+ }
+ }
+)
+
+func (x TitleOrValueTypeTitle) Index() int { return 0 }
+func (x TitleOrValueTypeTitle) Interface() interface{} { return x.Value }
+func (x TitleOrValueTypeTitle) Name() string { return "Title" }
+func (x TitleOrValueTypeTitle) __VDLReflect(__TitleOrValueTypeReflect) {}
+
+func (x TitleOrValueTypeValue) Index() int { return 1 }
+func (x TitleOrValueTypeValue) Interface() interface{} { return x.Value }
+func (x TitleOrValueTypeValue) Name() string { return "Value" }
+func (x TitleOrValueTypeValue) __VDLReflect(__TitleOrValueTypeReflect) {}
+
+type BazType struct {
+ Name string
+ TitleOrValue TitleOrValueType
+}
+
+func (BazType) __VDLReflect(struct {
+ Name string `vdl:"v.io/syncbase/v23/syncbase/nosql/internal/query/test.BazType"`
+}) {
+}
+
+func init() {
+ vdl.Register((*AddressInfo)(nil))
+ vdl.Register((*CreditAgency)(nil))
+ vdl.Register((*ExperianRating)(nil))
+ vdl.Register((*EquifaxCreditReport)(nil))
+ vdl.Register((*ExperianCreditReport)(nil))
+ vdl.Register((*TransUnionCreditReport)(nil))
+ vdl.Register((*AgencyReport)(nil))
+ vdl.Register((*CreditReport)(nil))
+ vdl.Register((*Customer)(nil))
+ vdl.Register((*Invoice)(nil))
+ vdl.Register((*Numbers)(nil))
+ vdl.Register((*FooType)(nil))
+ vdl.Register((*BarType)(nil))
+ vdl.Register((*TitleOrValueType)(nil))
+ vdl.Register((*BazType)(nil))
+}
diff --git a/v23/syncbase/nosql/internal/query/test/doc.go b/v23/syncbase/nosql/internal/query/test/doc.go
new file mode 100644
index 0000000..89b03b0
--- /dev/null
+++ b/v23/syncbase/nosql/internal/query/test/doc.go
@@ -0,0 +1,6 @@
+// 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 test contains tests for the query package.
+package test
diff --git a/v23/syncbase/nosql/internal/query/test/query_test.go b/v23/syncbase/nosql/internal/query/test/query_test.go
new file mode 100644
index 0000000..f61b8b1
--- /dev/null
+++ b/v23/syncbase/nosql/internal/query/test/query_test.go
@@ -0,0 +1,1261 @@
+// 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 test
+
+import (
+ "errors"
+ "fmt"
+ "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"
+ "v.io/v23/vdl"
+)
+
+type mockDB struct {
+ tables []table
+}
+
+type table struct {
+ name string
+ rows []kv
+}
+
+type keyValueStreamImpl struct {
+ table table
+ cursor int
+ prefixes []string
+ prefixCursor int
+}
+
+func (kvs *keyValueStreamImpl) Advance() bool {
+ for true {
+ kvs.cursor++ // initialized to -1
+ if kvs.cursor >= len(kvs.table.rows) {
+ return false
+ }
+ for kvs.prefixCursor < len(kvs.prefixes) {
+ // does it match any prefix
+ if kvs.prefixes[kvs.prefixCursor] == "" || strings.HasPrefix(kvs.table.rows[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.table.rows[kvs.cursor].key {
+ kvs.prefixCursor++
+ if kvs.prefixCursor >= len(kvs.prefixes) {
+ return false
+ }
+ } else {
+ break
+ }
+ }
+ }
+ return false
+}
+
+func (kvs *keyValueStreamImpl) KeyValue() (string, *vdl.Value) {
+ return kvs.table.rows[kvs.cursor].key, kvs.table.rows[kvs.cursor].value
+}
+
+func (kvs *keyValueStreamImpl) Err() error {
+ return nil
+}
+
+func (kvs *keyValueStreamImpl) Cancel() {
+}
+
+func (t table) Scan(prefixes []string) (query_db.KeyValueStream, error) {
+ var keyValueStreamImpl keyValueStreamImpl
+ keyValueStreamImpl.table = t
+ keyValueStreamImpl.cursor = -1
+ keyValueStreamImpl.prefixes = prefixes
+ return &keyValueStreamImpl, nil
+}
+
+func (db mockDB) GetTable(table string) (query_db.Table, error) {
+ for _, t := range db.tables {
+ if t.name == table {
+ return t, nil
+ }
+ }
+ return nil, errors.New(fmt.Sprintf("No such table: %s.", table))
+
+}
+
+var db mockDB
+var custTable table
+var numTable table
+var fooTable table
+
+type kv struct {
+ key string
+ value *vdl.Value
+}
+
+func init() {
+ custTable.name = "Customer"
+ custTable.rows = []kv{
+ kv{
+ "001",
+ vdl.ValueOf(Customer{"John Smith", 1, true, AddressInfo{"1 Main St.", "Palo Alto", "CA", "94303"}, CreditReport{Agency: CreditAgencyEquifax, Report: AgencyReportEquifaxReport{EquifaxCreditReport{'A'}}}}),
+ },
+ kv{
+ "001001",
+ vdl.ValueOf(Invoice{1, 1000, 42, AddressInfo{"1 Main St.", "Palo Alto", "CA", "94303"}}),
+ },
+ kv{
+ "001002",
+ vdl.ValueOf(Invoice{1, 1003, 7, AddressInfo{"2 Main St.", "Palo Alto", "CA", "94303"}}),
+ },
+ kv{
+ "001003",
+ vdl.ValueOf(Invoice{1, 1005, 88, AddressInfo{"3 Main St.", "Palo Alto", "CA", "94303"}}),
+ },
+ kv{
+ "002",
+ vdl.ValueOf(Customer{"Bat Masterson", 2, true, AddressInfo{"777 Any St.", "Collins", "IA", "50055"}, CreditReport{Agency: CreditAgencyTransUnion, Report: AgencyReportTransUnionReport{TransUnionCreditReport{80}}}}),
+ },
+ kv{
+ "002001",
+ vdl.ValueOf(Invoice{2, 1001, 166, AddressInfo{"777 Any St.", "collins", "IA", "50055"}}),
+ },
+ kv{
+ "002002",
+ vdl.ValueOf(Invoice{2, 1002, 243, AddressInfo{"888 Any St.", "collins", "IA", "50055"}}),
+ },
+ kv{
+ "002003",
+ vdl.ValueOf(Invoice{2, 1004, 787, AddressInfo{"999 Any St.", "collins", "IA", "50055"}}),
+ },
+ kv{
+ "002004",
+ vdl.ValueOf(Invoice{2, 1006, 88, AddressInfo{"101010 Any St.", "collins", "IA", "50055"}}),
+ },
+ }
+ db.tables = append(db.tables, custTable)
+
+ numTable.name = "Numbers"
+ numTable.rows = []kv{
+ kv{
+ "001",
+ vdl.ValueOf(Numbers{byte(12), uint16(1234), uint32(5678), uint64(999888777666), int16(9876), int32(876543), int64(128), float32(3.14159), float64(2.71828182846), complex64(123.0 + 7.0i), complex128(456.789 + 10.1112i)}),
+ },
+ kv{
+ "002",
+ vdl.ValueOf(Numbers{byte(9), uint16(99), uint32(999), uint64(9999999), int16(9), int32(99), int64(88), float32(1.41421356237), float64(1.73205080757), complex64(9.87 + 7.65i), complex128(4.32 + 1.0i)}),
+ },
+ kv{
+ "003",
+ vdl.ValueOf(Numbers{byte(210), uint16(210), uint32(210), uint64(210), int16(210), int32(210), int64(210), float32(210.0), float64(210.0), complex64(210.0 + 0.0i), complex128(210.0 + 0.0i)}),
+ },
+ }
+ db.tables = append(db.tables, numTable)
+
+ fooTable.name = "Foo"
+ fooTable.rows = []kv{
+ kv{
+ "001",
+ vdl.ValueOf(FooType{BarType{BazType{"FooBarBaz", TitleOrValueTypeTitle{"Vice President"}}}}),
+ },
+ kv{
+ "002",
+ vdl.ValueOf(FooType{BarType{BazType{"BazBarFoo", TitleOrValueTypeValue{42}}}}),
+ },
+ }
+ db.tables = append(db.tables, fooTable)
+}
+
+type keyPrefixesTest struct {
+ query string
+ keyPrefixes []string
+ err *query.QueryError
+}
+
+type evalWhereUsingOnlyKeyTest struct {
+ query string
+ key string
+ result query.EvalWithKeyResult
+}
+
+type evalTest struct {
+ query string
+ k string
+ v *vdl.Value
+ result bool
+}
+
+type projectionTest struct {
+ query string
+ k string
+ v *vdl.Value
+ result []*vdl.Value
+}
+
+type execSelectTest struct {
+ query string
+ r [][]*vdl.Value
+}
+
+type execSelectSingleRowTest struct {
+ query string
+ k string
+ v *vdl.Value
+ result []*vdl.Value
+}
+
+type execSelectErrorTest struct {
+ query string
+ err *query.QueryError
+}
+
+type execResolveFieldTest struct {
+ k string
+ v *vdl.Value
+ f query_parser.Field
+ r *vdl.Value
+}
+
+func TestQueryExec(t *testing.T) {
+ basic := []execSelectTest{
+ {
+ // Select values for all customer records.
+ "select v from Customer where t = \"Customer\"",
+ [][]*vdl.Value{
+ []*vdl.Value{custTable.rows[0].value},
+ []*vdl.Value{custTable.rows[4].value},
+ },
+ },
+ {
+ // Select values where v.InvoiceNum is nil
+ // Since InvoiceNum does not exist for Invoice,
+ // this will return just customers.
+ "select v from Customer where v.InvoiceNum is nil",
+ [][]*vdl.Value{
+ []*vdl.Value{custTable.rows[0].value},
+ []*vdl.Value{custTable.rows[4].value},
+ },
+ },
+ {
+ // Select values where v.InvoiceNum is nil
+ // or v.Name is nil This will select all customers
+ // with the former and all invoices with the latter.
+ // Hence, all records are returned.
+ "select v from Customer where v.InvoiceNum is nil or v.Name is nil",
+ [][]*vdl.Value{
+ []*vdl.Value{custTable.rows[0].value},
+ []*vdl.Value{custTable.rows[1].value},
+ []*vdl.Value{custTable.rows[2].value},
+ []*vdl.Value{custTable.rows[3].value},
+ []*vdl.Value{custTable.rows[4].value},
+ []*vdl.Value{custTable.rows[5].value},
+ []*vdl.Value{custTable.rows[6].value},
+ []*vdl.Value{custTable.rows[7].value},
+ []*vdl.Value{custTable.rows[8].value},
+ },
+ },
+ {
+ // Select values where v.InvoiceNum is nil
+ // and v.Name is nil. Expect nothing returned.
+ "select v from Customer where v.InvoiceNum is nil and v.Name is nil",
+ [][]*vdl.Value{},
+ },
+ {
+ // Select values where v.InvoiceNum is not nil
+ // This will return just invoices.
+ "select v from Customer where v.InvoiceNum is not nil",
+ [][]*vdl.Value{
+ []*vdl.Value{custTable.rows[1].value},
+ []*vdl.Value{custTable.rows[2].value},
+ []*vdl.Value{custTable.rows[3].value},
+ []*vdl.Value{custTable.rows[5].value},
+ []*vdl.Value{custTable.rows[6].value},
+ []*vdl.Value{custTable.rows[7].value},
+ []*vdl.Value{custTable.rows[8].value},
+ },
+ },
+ {
+ // Select values where v.InvoiceNum is not nil
+ // or v.Name is not nil. All records are returned.
+ "select v from Customer where v.InvoiceNum is not nil or v.Name is not nil",
+ [][]*vdl.Value{
+ []*vdl.Value{custTable.rows[0].value},
+ []*vdl.Value{custTable.rows[1].value},
+ []*vdl.Value{custTable.rows[2].value},
+ []*vdl.Value{custTable.rows[3].value},
+ []*vdl.Value{custTable.rows[4].value},
+ []*vdl.Value{custTable.rows[5].value},
+ []*vdl.Value{custTable.rows[6].value},
+ []*vdl.Value{custTable.rows[7].value},
+ []*vdl.Value{custTable.rows[8].value},
+ },
+ },
+ {
+ // Select values where v.InvoiceNum is nil and v.Name is not nil.
+ // All customers are returned.
+ "select v from Customer where v.InvoiceNum is nil and v.Name is not nil",
+ [][]*vdl.Value{
+ []*vdl.Value{custTable.rows[0].value},
+ []*vdl.Value{custTable.rows[4].value},
+ },
+ },
+ {
+ // Select values where v.InvoiceNum is not nil
+ // and v.Name is not nil. Expect nothing returned.
+ "select v from Customer where v.InvoiceNum is not nil and v.Name is not nil",
+ [][]*vdl.Value{},
+ },
+ {
+ // Select keys & values for all customer records.
+ "select k, v from Customer where t = \"Customer\"",
+ [][]*vdl.Value{
+ []*vdl.Value{vdl.ValueOf(custTable.rows[0].key), custTable.rows[0].value},
+ []*vdl.Value{vdl.ValueOf(custTable.rows[4].key), custTable.rows[4].value},
+ },
+ },
+ {
+ // Select keys & names for all customer records.
+ "select k, v.Name from Customer where t = \"Customer\"",
+ [][]*vdl.Value{
+ []*vdl.Value{vdl.ValueOf(custTable.rows[0].key), vdl.ValueOf("John Smith")},
+ []*vdl.Value{vdl.ValueOf(custTable.rows[4].key), vdl.ValueOf("Bat Masterson")},
+ },
+ },
+ {
+ // Select both customer and invoice records.
+ // Customer records have Id.
+ // Invoice records have CustId.
+ "select v.Id, v.CustId from Customer",
+ [][]*vdl.Value{
+ []*vdl.Value{vdl.ValueOf(int64(1)), vdl.ValueOf(nil)},
+ []*vdl.Value{vdl.ValueOf(nil), vdl.ValueOf(int64(1))},
+ []*vdl.Value{vdl.ValueOf(nil), vdl.ValueOf(int64(1))},
+ []*vdl.Value{vdl.ValueOf(nil), vdl.ValueOf(int64(1))},
+ []*vdl.Value{vdl.ValueOf(int64(2)), vdl.ValueOf(nil)},
+ []*vdl.Value{vdl.ValueOf(nil), vdl.ValueOf(int64(2))},
+ []*vdl.Value{vdl.ValueOf(nil), vdl.ValueOf(int64(2))},
+ []*vdl.Value{vdl.ValueOf(nil), vdl.ValueOf(int64(2))},
+ []*vdl.Value{vdl.ValueOf(nil), vdl.ValueOf(int64(2))},
+ },
+ },
+ {
+ // Select keys & values fo all invoice records.
+ "select k, v from Customer where t = \"Invoice\"",
+ [][]*vdl.Value{
+ []*vdl.Value{vdl.ValueOf(custTable.rows[1].key), custTable.rows[1].value},
+ []*vdl.Value{vdl.ValueOf(custTable.rows[2].key), custTable.rows[2].value},
+ []*vdl.Value{vdl.ValueOf(custTable.rows[3].key), custTable.rows[3].value},
+ []*vdl.Value{vdl.ValueOf(custTable.rows[5].key), custTable.rows[5].value},
+ []*vdl.Value{vdl.ValueOf(custTable.rows[6].key), custTable.rows[6].value},
+ []*vdl.Value{vdl.ValueOf(custTable.rows[7].key), custTable.rows[7].value},
+ []*vdl.Value{vdl.ValueOf(custTable.rows[8].key), custTable.rows[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",
+ [][]*vdl.Value{
+ []*vdl.Value{vdl.ValueOf(custTable.rows[3].key), vdl.ValueOf(int64(1)), vdl.ValueOf(int64(1005)), vdl.ValueOf(int64(88))},
+ []*vdl.Value{vdl.ValueOf(custTable.rows[8].key), vdl.ValueOf(int64(2)), vdl.ValueOf(int64(1006)), vdl.ValueOf(int64(88))},
+ },
+ },
+ {
+ // Select keys & values for all records with a key prefix of "001".
+ "select k, v from Customer where k like \"001%\"",
+ [][]*vdl.Value{
+ []*vdl.Value{vdl.ValueOf(custTable.rows[0].key), custTable.rows[0].value},
+ []*vdl.Value{vdl.ValueOf(custTable.rows[1].key), custTable.rows[1].value},
+ []*vdl.Value{vdl.ValueOf(custTable.rows[2].key), custTable.rows[2].value},
+ []*vdl.Value{vdl.ValueOf(custTable.rows[3].key), custTable.rows[3].value},
+ },
+ },
+ {
+ // Select keys & values for all records with a key prefix of "001".
+ "select k, v from Customer where k like \"002%\"",
+ [][]*vdl.Value{
+ []*vdl.Value{vdl.ValueOf(custTable.rows[4].key), custTable.rows[4].value},
+ []*vdl.Value{vdl.ValueOf(custTable.rows[5].key), custTable.rows[5].value},
+ []*vdl.Value{vdl.ValueOf(custTable.rows[6].key), custTable.rows[6].value},
+ []*vdl.Value{vdl.ValueOf(custTable.rows[7].key), custTable.rows[7].value},
+ []*vdl.Value{vdl.ValueOf(custTable.rows[8].key), custTable.rows[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%\"",
+ [][]*vdl.Value{
+ []*vdl.Value{vdl.ValueOf(custTable.rows[0].key), custTable.rows[0].value},
+ []*vdl.Value{vdl.ValueOf(custTable.rows[1].key), custTable.rows[1].value},
+ []*vdl.Value{vdl.ValueOf(custTable.rows[2].key), custTable.rows[2].value},
+ []*vdl.Value{vdl.ValueOf(custTable.rows[3].key), custTable.rows[3].value},
+ []*vdl.Value{vdl.ValueOf(custTable.rows[4].key), custTable.rows[4].value},
+ []*vdl.Value{vdl.ValueOf(custTable.rows[5].key), custTable.rows[5].value},
+ []*vdl.Value{vdl.ValueOf(custTable.rows[6].key), custTable.rows[6].value},
+ []*vdl.Value{vdl.ValueOf(custTable.rows[7].key), custTable.rows[7].value},
+ []*vdl.Value{vdl.ValueOf(custTable.rows[8].key), custTable.rows[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%\"",
+ [][]*vdl.Value{
+ []*vdl.Value{vdl.ValueOf(custTable.rows[0].key), custTable.rows[0].value},
+ []*vdl.Value{vdl.ValueOf(custTable.rows[1].key), custTable.rows[1].value},
+ []*vdl.Value{vdl.ValueOf(custTable.rows[2].key), custTable.rows[2].value},
+ []*vdl.Value{vdl.ValueOf(custTable.rows[3].key), custTable.rows[3].value},
+ []*vdl.Value{vdl.ValueOf(custTable.rows[4].key), custTable.rows[4].value},
+ []*vdl.Value{vdl.ValueOf(custTable.rows[5].key), custTable.rows[5].value},
+ []*vdl.Value{vdl.ValueOf(custTable.rows[6].key), custTable.rows[6].value},
+ []*vdl.Value{vdl.ValueOf(custTable.rows[7].key), custTable.rows[7].value},
+ []*vdl.Value{vdl.ValueOf(custTable.rows[8].key), custTable.rows[8].value},
+ },
+ },
+ {
+ // Let's play with whitespace and mixed case.
+ " sElEcT k, v from \n Customer WhErE k lIkE \"002%\" oR k LiKe \"001%\"",
+ [][]*vdl.Value{
+ []*vdl.Value{vdl.ValueOf(custTable.rows[0].key), custTable.rows[0].value},
+ []*vdl.Value{vdl.ValueOf(custTable.rows[1].key), custTable.rows[1].value},
+ []*vdl.Value{vdl.ValueOf(custTable.rows[2].key), custTable.rows[2].value},
+ []*vdl.Value{vdl.ValueOf(custTable.rows[3].key), custTable.rows[3].value},
+ []*vdl.Value{vdl.ValueOf(custTable.rows[4].key), custTable.rows[4].value},
+ []*vdl.Value{vdl.ValueOf(custTable.rows[5].key), custTable.rows[5].value},
+ []*vdl.Value{vdl.ValueOf(custTable.rows[6].key), custTable.rows[6].value},
+ []*vdl.Value{vdl.ValueOf(custTable.rows[7].key), custTable.rows[7].value},
+ []*vdl.Value{vdl.ValueOf(custTable.rows[8].key), custTable.rows[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 \"%\"",
+ [][]*vdl.Value{
+ []*vdl.Value{vdl.ValueOf(custTable.rows[0].key), custTable.rows[0].value},
+ []*vdl.Value{vdl.ValueOf(custTable.rows[1].key), custTable.rows[1].value},
+ []*vdl.Value{vdl.ValueOf(custTable.rows[2].key), custTable.rows[2].value},
+ []*vdl.Value{vdl.ValueOf(custTable.rows[3].key), custTable.rows[3].value},
+ []*vdl.Value{vdl.ValueOf(custTable.rows[4].key), custTable.rows[4].value},
+ []*vdl.Value{vdl.ValueOf(custTable.rows[5].key), custTable.rows[5].value},
+ []*vdl.Value{vdl.ValueOf(custTable.rows[6].key), custTable.rows[6].value},
+ []*vdl.Value{vdl.ValueOf(custTable.rows[7].key), custTable.rows[7].value},
+ []*vdl.Value{vdl.ValueOf(custTable.rows[8].key), custTable.rows[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\"",
+ [][]*vdl.Value{
+ []*vdl.Value{vdl.ValueOf(int64(2)), vdl.ValueOf("Bat Masterson")},
+ },
+ },
+ {
+ // Select records where v.Address.City is "Collins" or type is Invoice.
+ "select v from Customer where v.Address.City = \"Collins\" or t = \"Invoice\"",
+ [][]*vdl.Value{
+ []*vdl.Value{custTable.rows[1].value},
+ []*vdl.Value{custTable.rows[2].value},
+ []*vdl.Value{custTable.rows[3].value},
+ []*vdl.Value{custTable.rows[4].value},
+ []*vdl.Value{custTable.rows[5].value},
+ []*vdl.Value{custTable.rows[6].value},
+ []*vdl.Value{custTable.rows[7].value},
+ []*vdl.Value{custTable.rows[8].value},
+ },
+ },
+ {
+ // Select records where v.Address.City is "Collins" and v.InvoiceNum is not nil.
+ "select v from Customer where v.Address.City = \"Collins\" and v.InvoiceNum is not nil",
+ [][]*vdl.Value{},
+ },
+ {
+ // Select records where v.Address.City is "Collins" and v.InvoiceNum is nil.
+ "select v from Customer where v.Address.City = \"Collins\" and v.InvoiceNum is nil",
+ [][]*vdl.Value{
+ []*vdl.Value{custTable.rows[4].value},
+ },
+ },
+ {
+ // Select customer name for customer Id (i.e., key) "001".
+ "select v.Name from Customer where t = \"Customer\" and k = \"001\"",
+ [][]*vdl.Value{
+ []*vdl.Value{vdl.ValueOf("John Smith")},
+ },
+ },
+ {
+ // Select v where v.Credit.Report.EquifaxReport.Rating = 'A'
+ "select v from Customer where v.Credit.Report.EquifaxReport.Rating = 'A'",
+ [][]*vdl.Value{
+ []*vdl.Value{custTable.rows[0].value},
+ },
+ },
+ {
+ // Select v where v.AgencyRating = "Bad"
+ "select v from Customer where v.Credit.Report.EquifaxReport.Rating < 'A' or v.Credit.Report.ExperianReport.Rating = \"Bad\" or v.Credit.Report.TransUnionReport.Rating < 90",
+ [][]*vdl.Value{
+ []*vdl.Value{custTable.rows[4].value},
+ },
+ },
+ {
+ // Select records where v.Bar.Baz.Name = "FooBarBaz"
+ "select v from Foo where v.Bar.Baz.Name = \"FooBarBaz\"",
+ [][]*vdl.Value{
+ []*vdl.Value{fooTable.rows[0].value},
+ },
+ },
+ {
+ // Select records where v.Bar.Baz.TitleOrValue.Value = 42
+ "select v from Foo where v.Bar.Baz.TitleOrValue.Value = 42",
+ [][]*vdl.Value{
+ []*vdl.Value{fooTable.rows[1].value},
+ },
+ },
+ {
+ // Select records where v.Bar.Baz.TitleOrValue.Title = "Vice President"
+ "select v from Foo where v.Bar.Baz.TitleOrValue.Title = \"Vice President\"",
+ [][]*vdl.Value{
+ []*vdl.Value{fooTable.rows[0].value},
+ },
+ },
+ {
+ // Select records where v.Address.City = "Collins" or type is Invoice.
+ // Limit 3
+ "select v from Customer where v.Address.City = \"Collins\" or t = \"Invoice\" limit 3",
+ [][]*vdl.Value{
+ []*vdl.Value{custTable.rows[1].value},
+ []*vdl.Value{custTable.rows[2].value},
+ []*vdl.Value{custTable.rows[3].value},
+ },
+ },
+ {
+ // Select records where v.Address.City = "Collins" or type is Invoice.
+ // Offset 5
+ "select v from Customer where v.Address.City = \"Collins\" or t = \"Invoice\" offset 5",
+ [][]*vdl.Value{
+ []*vdl.Value{custTable.rows[6].value},
+ []*vdl.Value{custTable.rows[7].value},
+ []*vdl.Value{custTable.rows[8].value},
+ },
+ },
+ {
+ // Select records where v.Address.City = "Collins" is "Mountain View".
+ "select v from Customer where v.Address.City = \"Mountain View\"",
+ [][]*vdl.Value{},
+ },
+ {
+ // Select records where v.Address.City = "Collins" or type is Invoice.
+ // Offset 8
+ "select v from Customer where v.Address.City = \"Collins\" or t = \"Invoice\" offset 8",
+ [][]*vdl.Value{},
+ },
+ {
+ // Select records where v.Address.City = "Collins" or type is Invoice.
+ // Offset 23
+ "select v from Customer where v.Address.City = \"Collins\" or t = \"Invoice\" offset 23",
+ [][]*vdl.Value{},
+ },
+ {
+ // Select records where v.Address.City = "Collins" is 84 or type is Invoice.
+ // Limit 3 Offset 2
+ "select v from Customer where v.Address.City = \"Collins\" or t = \"Invoice\" limit 3 offset 2",
+ [][]*vdl.Value{
+ []*vdl.Value{custTable.rows[3].value},
+ []*vdl.Value{custTable.rows[4].value},
+ []*vdl.Value{custTable.rows[5].value},
+ },
+ },
+ {
+ // Select records where v.Address.City = "Collins" or (type is Invoice and v.InvoiceNum is not nil).
+ // Limit 3 Offset 2
+ "select v from Customer where v.Address.City = \"Collins\" or (t = \"Invoice\" and v.InvoiceNum is not nil) limit 3 offset 2",
+ [][]*vdl.Value{
+ []*vdl.Value{custTable.rows[3].value},
+ []*vdl.Value{custTable.rows[4].value},
+ []*vdl.Value{custTable.rows[5].value},
+ },
+ },
+ {
+ // Select records where v.Address.City = "Collins" or (type is Invoice and v.InvoiceNum is nil).
+ // Limit 3 Offset 2
+ "select v from Customer where v.Address.City = \"Collins\" or (t = \"Invoice\" and v.InvoiceNum is nil) limit 3 offset 2",
+ [][]*vdl.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 := [][]*vdl.Value{}
+ 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", test.query, 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",
+ query.INCLUDE,
+ },
+ {
+ // Row will be rejected using only the key.
+ "select k, v from Customer where k like \"abc\"",
+ "abcd",
+ query.EXCLUDE,
+ },
+ {
+ // Need value to determine if row should be selected.
+ "select k, v from Customer where k = \"abc\" or v.zip = \"94303\"",
+ "abcd",
+ query.FETCH_VALUE,
+ },
+ {
+ // 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",
+ query.FETCH_VALUE,
+ },
+ {
+ // 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",
+ query.EXCLUDE,
+ },
+ }
+
+ 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.EvalWhereUsingOnlyKey(&sel, test.key)
+ 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", test.query, 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\"",
+ custTable.rows[0].key, custTable.rows[0].value, true,
+ },
+ {
+ "select k, v from Customer where t = \"Customer\"",
+ numTable.rows[0].key, custTable.rows[0].value, true,
+ },
+ {
+ "select k, v from Customer where v.Name = \"John Smith\"",
+ numTable.rows[0].key, custTable.rows[0].value, true,
+ },
+ {
+ "select k, v from Customer where v.Name = v.Name",
+ numTable.rows[0].key, custTable.rows[0].value, true,
+ },
+ {
+ "select k, v from Customer where v = v",
+ numTable.rows[0].key, custTable.rows[0].value, true,
+ },
+ {
+ "select k, v from Customer where v > v",
+ numTable.rows[0].key, custTable.rows[0].value, false,
+ },
+ {
+ "select k, v from Customer where v < v",
+ numTable.rows[0].key, custTable.rows[0].value, false,
+ },
+ {
+ "select k, v from Customer where v >= v",
+ numTable.rows[0].key, custTable.rows[0].value, false,
+ },
+ {
+ "select k, v from Customer where v <= v",
+ numTable.rows[0].key, custTable.rows[0].value, false,
+ },
+ {
+ "select k, v from Customer where v.Credit.Report.EquifaxReport.Rating = 'A'",
+ numTable.rows[0].key, custTable.rows[0].value, true,
+ },
+ {
+ "select k, v from Customer where v.Credit.Report.EquifaxReport.Rating <> 'A'",
+ numTable.rows[0].key, custTable.rows[0].value, false,
+ },
+ {
+ "select k, v from Customer where v.Credit.Report.EquifaxReport.Rating >= 'B'",
+ numTable.rows[0].key, custTable.rows[0].value, false,
+ },
+ {
+ "select k, v from Customer where v.Credit.Report.EquifaxReport.Rating <= 'B'",
+ numTable.rows[0].key, custTable.rows[0].value, true,
+ },
+ {
+ "select k, v from Customer where v.Credit.Report.TransUnionReport.Rating = 80",
+ numTable.rows[0].key, custTable.rows[4].value, true,
+ },
+ {
+ "select k, v from Customer where v.Credit.Report.TransUnionReport.Rating <> 80",
+ numTable.rows[0].key, custTable.rows[4].value, false,
+ },
+ {
+ "select k, v from Customer where v.Cr4dit.Report.TransUnionReport.Rating <= 70",
+ numTable.rows[0].key, custTable.rows[0].value, false,
+ },
+ {
+ "select k, v from Customer where v.Credit.Report.TransUnionReport.Rating >= 70",
+ numTable.rows[0].key, custTable.rows[4].value, true,
+ },
+ {
+ "select k, v from Customer where v.Active = true",
+ numTable.rows[0].key, custTable.rows[0].value, true,
+ },
+ {
+ "select k, v from Customer where v.Active = false",
+ numTable.rows[0].key, custTable.rows[0].value, false,
+ },
+ {
+ "select k, v from Numbers where 12 = v.B",
+ numTable.rows[0].key, numTable.rows[0].value, true,
+ },
+ {
+ "select k, v from Numbers where 11 < v.B",
+ numTable.rows[0].key, numTable.rows[0].value, true,
+ },
+ {
+ "select k, v from Numbers where v.B > 10",
+ numTable.rows[0].key, numTable.rows[0].value, true,
+ },
+ {
+ "select k, v from Numbers where v.B >= 14",
+ numTable.rows[0].key, numTable.rows[0].value, false,
+ },
+ {
+ "select k, v from Numbers where v.B >= 11.0",
+ numTable.rows[0].key, numTable.rows[0].value, true,
+ },
+ {
+ "select k, v from Numbers where v.Ui64 = 999888777666",
+ numTable.rows[0].key, numTable.rows[0].value, true,
+ },
+ {
+ "select k, v from Numbers where v.Ui64 < 999888777666",
+ numTable.rows[0].key, numTable.rows[0].value, false,
+ },
+ {
+ "select k, v from Numbers where v.B < v.Ui64",
+ numTable.rows[0].key, numTable.rows[0].value, true,
+ },
+ {
+ "select k, v from Numbers where v.C64 = \"(123+7i)\"",
+ numTable.rows[0].key, numTable.rows[0].value, true,
+ },
+ {
+ "select k, v from Numbers where v.C128 = \"(456.789+10.1112i)\"",
+ numTable.rows[0].key, numTable.rows[0].value, true,
+ },
+ {
+ "select k, v from Numbers where v.Ui16 = 1234",
+ numTable.rows[0].key, numTable.rows[0].value, true,
+ },
+ {
+ "select k, v from Numbers where v.Ui32 = 5678",
+ numTable.rows[0].key, numTable.rows[0].value, true,
+ },
+ {
+ "select k, v from Numbers where v.I16 = 9876",
+ numTable.rows[0].key, numTable.rows[0].value, true,
+ },
+ {
+ "select k, v from Numbers where v.I32 = 876543",
+ numTable.rows[0].key, numTable.rows[0].value, true,
+ },
+ {
+ // Deeply nested.
+ "select v from Foo where v.Bar.Baz.Name = \"FooBarBaz\"",
+ numTable.rows[0].key, fooTable.rows[0].value, true,
+ },
+ {
+ // Convert int64 to string
+ "select v from Customer where v.CustId = \"1\"",
+ numTable.rows[0].key, custTable.rows[1].value, true,
+ },
+ {
+ // Convert bool to string
+ "select v from Customer where v.Active = \"true\"",
+ numTable.rows[0].key, custTable.rows[0].value, true,
+ },
+ {
+ // Bool can't convert to other types.
+ "select v from Customer where v.Active = 1",
+ numTable.rows[0].key, custTable.rows[0].value, false,
+ },
+ {
+ // Test that numeric types can compare to a float64
+ "select v from Numbers where v.F64 = v.B and v.F64 = v.Ui16 and v.F64 = v.Ui32 and v.F64 = v.Ui64 and v.F64 = v.I16 and v.F64 = v.I32 and v.F64 = v.I64 and v.F64 = v.F32 and v.F64 = v.C64 and v.F64 = v.C128",
+ numTable.rows[2].key, numTable.rows[2].value, true,
+ },
+ {
+ // Test that all numeric types can compare to an int32
+ "select v from Numbers where v.I32 = v.B and v.I32 = v.Ui16 and v.I32 = v.Ui32 and v.I32 = v.Ui64 and v.I32 = v.I16 and v.I32 = v.F64 and v.I32 = v.I64 and v.I32 = v.F32 and v.I32 = v.C64 and v.I32 = v.C128",
+ numTable.rows[2].key, numTable.rows[2].value, true,
+ },
+ {
+ // Test that all numeric types can compare to an int16
+ "select v from Numbers where v.I16 = v.B and v.I16 = v.Ui16 and v.I16 = v.Ui32 and v.I16 = v.Ui64 and v.I16 = v.F64 and v.I16 = v.I32 and v.I16 = v.I64 and v.I16 = v.F32 and v.I16 = v.C64 and v.I16 = v.C128",
+ numTable.rows[2].key, numTable.rows[2].value, true,
+ },
+ {
+ // Test that all numeric types can compare to an uint64
+ "select v from Numbers where v.Ui64 = v.B and v.Ui64 = v.Ui16 and v.Ui64 = v.Ui32 and v.Ui64 = v.F64 and v.Ui64 = v.I16 and v.Ui64 = v.I32 and v.Ui64 = v.I64 and v.Ui64 = v.F32 and v.Ui64 = v.C64 and v.Ui64 = v.C128",
+ numTable.rows[2].key, numTable.rows[2].value, true,
+ },
+ {
+ // Test that all numeric types can compare to an uint32
+ "select v from Numbers where v.Ui32 = v.B and v.Ui32 = v.Ui16 and v.Ui32 = v.F64 and v.Ui32 = v.Ui64 and v.Ui32 = v.I16 and v.Ui32 = v.I32 and v.Ui32 = v.I64 and v.Ui32 = v.F32 and v.Ui32 = v.C64 and v.Ui32 = v.C128",
+ numTable.rows[2].key, numTable.rows[2].value, true,
+ },
+ {
+ // Test that all numeric types can compare to an uint16
+ "select v from Numbers where v.Ui16 = v.B and v.Ui16 = v.F64 and v.Ui16 = v.Ui32 and v.Ui16 = v.Ui64 and v.Ui16 = v.I16 and v.Ui16 = v.I32 and v.Ui16 = v.I64 and v.Ui16 = v.F32 and v.Ui16 = v.C64 and v.Ui16 = v.C128",
+ numTable.rows[2].key, numTable.rows[2].value, true,
+ },
+ {
+ // Test C64 = C128
+ "select v from Numbers where v.C64 = v.C128",
+ numTable.rows[2].key, numTable.rows[2].value, true,
+ },
+ {
+ // Test C64 <> C128
+ "select v from Numbers where v.C64 <> v.C128",
+ numTable.rows[0].key, numTable.rows[0].value, true,
+ },
+ {
+ // Complex integers can only compare to themselves and other numerics.
+ // Compare to integer
+ "select v from Numbers where v.C128 <> false",
+ numTable.rows[0].key, numTable.rows[0].value, false,
+ },
+ {
+ "select v from Numbers where v.C128 = \"(456.789+10.1112i)\"",
+ numTable.rows[0].key, numTable.rows[0].value, 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", test.query, reflect.TypeOf(*s))
+ }
+ }
+ }
+ }
+}
+
+func TestProjection(t *testing.T) {
+ basic := []projectionTest{
+ {
+ "select k, v from Customer where t = \"Customer\"",
+ "123456", custTable.rows[0].value,
+ []*vdl.Value{
+ vdl.ValueOf("123456"),
+ custTable.rows[0].value,
+ },
+ },
+ {
+ "select k, v, v.Name, v.Id, v.Active, v.Credit.Agency, v.Credit.Report.EquifaxReport.Rating, v.Address.Street, v.Address.City, v.Address.State, v.Address.Zip from Customer where t = \"Customer\"",
+ custTable.rows[0].key, custTable.rows[0].value,
+ []*vdl.Value{
+ vdl.ValueOf(custTable.rows[0].key),
+ custTable.rows[0].value,
+ vdl.ValueOf("John Smith"),
+ vdl.ValueOf(int64(1)),
+ vdl.ValueOf(true),
+ vdl.ValueOf(CreditAgencyEquifax),
+ vdl.ValueOf(byte('A')),
+ vdl.ValueOf("1 Main St."),
+ vdl.ValueOf("Palo Alto"),
+ vdl.ValueOf("CA"),
+ vdl.ValueOf("94303"),
+ },
+ },
+ }
+
+ 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", test.query, reflect.TypeOf(*s))
+ }
+ }
+ }
+ }
+}
+
+func TestExecSelectSingleRow(t *testing.T) {
+ basic := []execSelectSingleRowTest{
+ {
+ "select k, v from Customer where t = \"Customer\"",
+ "123456", custTable.rows[0].value,
+ []*vdl.Value{
+ vdl.ValueOf("123456"),
+ custTable.rows[0].value,
+ },
+ },
+ {
+ "select k, v from Customer where t = \"Customer\" and k like \"123%\"",
+ "123456", custTable.rows[0].value,
+ []*vdl.Value{
+ vdl.ValueOf("123456"),
+ custTable.rows[0].value,
+ },
+ },
+ {
+ "select k, v from Customer where t = \"Invoice\" and k like \"123%\"",
+ "123456", custTable.rows[0].value,
+ []*vdl.Value{},
+ },
+ {
+ "select k, v from Customer where t = \"Customer\" and k like \"456%\"",
+ "123456", custTable.rows[0].value,
+ []*vdl.Value{},
+ },
+ {
+ "select v from Customer where v.Name = \"John Smith\"",
+ "123456", custTable.rows[0].value,
+ []*vdl.Value{
+ custTable.rows[0].value,
+ },
+ },
+ {
+ "select v from Customer where v.Name = \"John Doe\"",
+ "123456", custTable.rows[0].value,
+ []*vdl.Value{},
+ },
+ {
+ "select k, v, v.Name, v.Id, v.Active, v.Credit.Report.EquifaxReport.Rating, v.Credit.Report.ExperianReport.Rating, v.Credit.Report.TransUnionReport.Rating, v.Address.Street, v.Address.City, v.Address.State, v.Address.Zip from Customer where t = \"Customer\"",
+ custTable.rows[0].key, custTable.rows[0].value,
+ []*vdl.Value{
+ vdl.ValueOf(custTable.rows[0].key),
+ custTable.rows[0].value,
+ vdl.ValueOf("John Smith"),
+ vdl.ValueOf(int64(1)),
+ vdl.ValueOf(true),
+ vdl.ValueOf(byte('A')),
+ vdl.ValueOf(nil),
+ vdl.ValueOf(nil),
+ vdl.ValueOf("1 Main St."),
+ vdl.ValueOf("Palo Alto"),
+ vdl.ValueOf("CA"),
+ vdl.ValueOf("94303"),
+ },
+ },
+ }
+
+ 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", test.query, 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)
+ }
+ }
+}
+
+func TestResolveField(t *testing.T) {
+ basic := []execResolveFieldTest{
+ {
+ custTable.rows[0].key,
+ custTable.rows[0].value,
+ query_parser.Field{
+ Segments: []query_parser.Segment{
+ query_parser.Segment{
+ Value: "t",
+ Node: query_parser.Node{Off: 7},
+ },
+ },
+ Node: query_parser.Node{Off: 7},
+ },
+ vdl.ValueOf("v.io/syncbase/v23/syncbase/nosql/internal/query/test.Customer"),
+ },
+ {
+ custTable.rows[0].key,
+ custTable.rows[0].value,
+ query_parser.Field{
+ Segments: []query_parser.Segment{
+ query_parser.Segment{
+ Value: "k",
+ Node: query_parser.Node{Off: 7},
+ },
+ },
+ Node: query_parser.Node{Off: 7},
+ },
+ vdl.ValueOf("001"),
+ },
+ {
+ custTable.rows[0].key,
+ custTable.rows[0].value,
+ query_parser.Field{
+ Segments: []query_parser.Segment{
+ query_parser.Segment{
+ Value: "v",
+ Node: query_parser.Node{Off: 7},
+ },
+ query_parser.Segment{
+ Value: "Address",
+ Node: query_parser.Node{Off: 7},
+ },
+ query_parser.Segment{
+ Value: "City",
+ Node: query_parser.Node{Off: 7},
+ },
+ },
+ Node: query_parser.Node{Off: 7},
+ },
+ vdl.ValueOf("Palo Alto"),
+ },
+ }
+
+ for _, test := range basic {
+ r, _, _ := query.ResolveField(test.k, test.v, &test.f)
+ if !reflect.DeepEqual(r, test.r) {
+ t.Errorf("got %v(%s), want %v(%s)", r, r.Type(), test.r, test.r.Type())
+ }
+ }
+}