syncbase: syncQL: implement functions in select clause.
Functions were previously implemented but were only allowed
in expressions in the where clause. This follow on change allows
functions in the select clause.
As such, select clause "selectors" may be fields or functions.
For example:
select v.CustNum, UpperCase(v.Name) from Customer where t= "Customer"
Note: This change renames ColumnEntry to "Selector" (requested by kash
in a post submit comment to a previous change).
Change-Id: I74267b05c082e613e49a2bfa3ac98fff645db36d
diff --git a/v23/syncbase/nosql/internal/query/eval.go b/v23/syncbase/nosql/internal/query/eval.go
index 9612b0d..e3ef7f3 100644
--- a/v23/syncbase/nosql/internal/query/eval.go
+++ b/v23/syncbase/nosql/internal/query/eval.go
@@ -14,6 +14,7 @@
"v.io/syncbase/v23/syncbase/nosql/internal/query/query_db"
"v.io/syncbase/v23/syncbase/nosql/internal/query/query_functions"
"v.io/syncbase/v23/syncbase/nosql/internal/query/query_parser"
+ "v.io/syncbase/v23/syncbase/nosql/syncql"
"v.io/v23/vdl"
)
@@ -363,26 +364,35 @@
}
}
+func resolveArgsAndExecFunction(db query_db.Database, k string, v *vdl.Value, f *query_parser.Function) (*query_parser.Operand, error) {
+ // Resolve the function's arguments
+ callingArgs := []*query_parser.Operand{}
+ for _, arg := range f.Args {
+ resolvedArg := resolveOperand(db, k, v, arg)
+ if resolvedArg == nil {
+ return nil, syncql.NewErrFunctionArgBad(db.GetContext(), arg.Off, f.Name, arg.String())
+ }
+ callingArgs = append(callingArgs, resolvedArg)
+ }
+ // Exec the function
+ retValue, err := query_functions.ExecFunction(db, f, callingArgs)
+ if err != nil {
+ return nil, err
+ }
+ return retValue, nil
+}
+
func resolveOperand(db query_db.Database, k string, v *vdl.Value, o *query_parser.Operand) *query_parser.Operand {
if o.Type == query_parser.TypFunction {
// Note: if the function was computed at check time, the operand is replaced
// in the parse tree with the return value. As such, thre is no need to check
// the computed field.
- // Resolve the functions arguments
- callingArgs := []*query_parser.Operand{}
- for _, arg := range o.Function.Args {
- resolvedArg := resolveOperand(db, k, v, arg)
- if resolvedArg == nil {
- return nil
- }
- callingArgs = append(callingArgs, resolvedArg)
- }
- // Exec the function
- retValue, err := query_functions.ExecFunction(db, o.Function, callingArgs)
- if err != nil {
+ if retValue, err := resolveArgsAndExecFunction(db, k, v, o.Function); err == nil {
+ return retValue
+ } else {
+ // Per spec, function errors resolve to nil
return nil
}
- return retValue
}
if o.Type != query_parser.TypField {
return o
diff --git a/v23/syncbase/nosql/internal/query/query.go b/v23/syncbase/nosql/internal/query/query.go
index 6845d7c..a6cd34c 100644
--- a/v23/syncbase/nosql/internal/query/query.go
+++ b/v23/syncbase/nosql/internal/query/query.go
@@ -9,6 +9,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_functions"
"v.io/syncbase/v23/syncbase/nosql/internal/query/query_parser"
"v.io/syncbase/v23/syncbase/nosql/syncql"
"v.io/v23/vdl"
@@ -39,12 +40,27 @@
// 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 *vdl.Value, s *query_parser.SelectClause) []*vdl.Value {
+func ComposeProjection(db query_db.Database, 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.Column)
- projection = append(projection, c)
+ for _, selector := range s.Selectors {
+ switch selector.Type {
+ case query_parser.TypSelField:
+ // If field not found, nil is returned (as per specification).
+ f, _, _ := ResolveField(k, v, selector.Field)
+ projection = append(projection, f)
+ case query_parser.TypSelFunc:
+ if selector.Function.Computed {
+ projection = append(projection, query_functions.ConvertFunctionRetValueToVdlValue(selector.Function.RetValue))
+ } else {
+ // need to exec function
+ // If error executing function, return nil (as per specification).
+ retValue, err := resolveArgsAndExecFunction(db, k, v, selector.Function)
+ if err != nil {
+ retValue = nil
+ }
+ projection = append(projection, query_functions.ConvertFunctionRetValueToVdlValue(retValue))
+ }
+ }
}
return projection
}
@@ -73,7 +89,7 @@
rs := []*vdl.Value{}
return rs
}
- return ComposeProjection(k, v, s.Select)
+ return ComposeProjection(db, k, v, s.Select)
}
type resultStreamImpl struct {
@@ -125,7 +141,7 @@
}
func (rs *resultStreamImpl) Result() []*vdl.Value {
- return ComposeProjection(rs.k, rs.v, rs.selectStatement.Select)
+ return ComposeProjection(rs.db, rs.k, rs.v, rs.selectStatement.Select)
}
func (rs *resultStreamImpl) Err() error {
@@ -138,16 +154,20 @@
func getColumnHeadings(s *query_parser.SelectStatement) []string {
columnHeaders := []string{}
- for _, column := range s.Select.Columns {
+ for _, selector := range s.Select.Selectors {
columnName := ""
- if column.As != nil {
- columnName = column.As.AltName.Value
+ if selector.As != nil {
+ columnName = selector.As.AltName.Value
} else {
- field := column.Column
- sep := ""
- for _, segment := range field.Segments {
- columnName = columnName + sep + segment.Value
- sep = "."
+ switch selector.Type {
+ case query_parser.TypSelField:
+ sep := ""
+ for _, segment := range selector.Field.Segments {
+ columnName = columnName + sep + segment.Value
+ sep = "."
+ }
+ case query_parser.TypSelFunc:
+ columnName = selector.Function.Name
}
}
columnHeaders = append(columnHeaders, columnName)
diff --git a/v23/syncbase/nosql/internal/query/query_checker/query_checker.go b/v23/syncbase/nosql/internal/query/query_checker/query_checker.go
index 5d32f3b..01dac70 100644
--- a/v23/syncbase/nosql/internal/query/query_checker/query_checker.go
+++ b/v23/syncbase/nosql/internal/query/query_checker/query_checker.go
@@ -46,16 +46,24 @@
// Check select clause. Fields can be 'k' and v[{.<ident>}...]
func checkSelectClause(db query_db.Database, s *query_parser.SelectClause) error {
- for _, c := range s.Columns {
- switch c.Column.Segments[0].Value {
- case "k":
- if len(c.Column.Segments) > 1 {
- return syncql.NewErrDotNotationDisallowedForKey(db.GetContext(), c.Column.Segments[1].Off)
+ for _, selector := range s.Selectors {
+ switch selector.Type {
+ case query_parser.TypSelField:
+ switch selector.Field.Segments[0].Value {
+ case "k":
+ if len(selector.Field.Segments) > 1 {
+ return syncql.NewErrDotNotationDisallowedForKey(db.GetContext(), selector.Field.Segments[1].Off)
+ }
+ case "v":
+ // Nothing to check.
+ default:
+ return syncql.NewErrInvalidSelectField(db.GetContext(), selector.Field.Segments[0].Off)
}
- case "v":
- // Nothing to check.
- default:
- return syncql.NewErrInvalidSelectField(db.GetContext(), c.Column.Segments[0].Off)
+ case query_parser.TypSelFunc:
+ err := query_functions.CheckFunction(db, selector.Function)
+ if err != nil {
+ return err
+ }
}
}
return nil
diff --git a/v23/syncbase/nosql/internal/query/query_checker/query_checker_test.go b/v23/syncbase/nosql/internal/query/query_checker/query_checker_test.go
index 4568324..f97857b 100644
--- a/v23/syncbase/nosql/internal/query/query_checker/query_checker_test.go
+++ b/v23/syncbase/nosql/internal/query/query_checker/query_checker_test.go
@@ -105,6 +105,8 @@
{"select v from Customer where v.ZipCode is not nil"},
{"select v from Customer where v.ZipCode IS NOT NIL"},
{"select v from Customer where Now() < 10"},
+ {"select Now() from Customer"},
+ {"select Date(\"2015-06-01 PST\"), DateTime(\"2015-06-01 12:34:56 PST\"), Y(YM(YMD(YMDH(YMDHM(YMDHMS(Now(), \"America/Los_Angeles\"), \"America/Los_Angeles\"), \"America/Los_Angeles\"), \"America/Los_Angeles\"), \"America/Los_Angeles\"), \"America/Los_Angeles\") from Customer"},
}
for _, test := range basic {
@@ -298,6 +300,8 @@
{"select v from Customer where t = \"Customer\" and Now(v.InvoiceDate, \"ABC\") = 2015", syncql.NewErrFunctionArgCount(db.GetContext(), 48, "Now", 0, 2)},
{"select v from Customer where t = \"Customer\" and LowerCase(v.Name, 2) = \"smith\"", syncql.NewErrFunctionArgCount(db.GetContext(), 48, "LowerCase", 1, 2)},
{"select v from Customer where t = \"Customer\" and UpperCase(v.Name, 2) = \"SMITH\"", syncql.NewErrFunctionArgCount(db.GetContext(), 48, "UpperCase", 1, 2)},
+ {"select Date() from Customer", syncql.NewErrFunctionArgCount(db.GetContext(), 7, "Date", 1, 0)},
+ {"select Y(v.InvoiceDate, \"Foo\") from Customer where t = \"Invoice\"", syncql.NewErrFunctionReturnedError(db.GetContext(), 24, "Y", errors.New("unknown time zone Foo"))},
}
for _, test := range basic {
diff --git a/v23/syncbase/nosql/internal/query/query_functions/query_functions.go b/v23/syncbase/nosql/internal/query/query_functions/query_functions.go
index e163601..ca83efb 100644
--- a/v23/syncbase/nosql/internal/query/query_functions/query_functions.go
+++ b/v23/syncbase/nosql/internal/query/query_functions/query_functions.go
@@ -11,6 +11,7 @@
"v.io/syncbase/v23/syncbase/nosql/internal/query/query_db"
"v.io/syncbase/v23/syncbase/nosql/internal/query/query_parser"
"v.io/syncbase/v23/syncbase/nosql/syncql"
+ "v.io/v23/vdl"
)
type queryFunc func(int64, []*query_parser.Operand) (*query_parser.Operand, error)
@@ -119,6 +120,32 @@
}
}
+func ConvertFunctionRetValueToVdlValue(o *query_parser.Operand) *vdl.Value {
+ switch o.Type {
+ case query_parser.TypBool:
+ return vdl.ValueOf(o.Bool)
+ case query_parser.TypComplex:
+ return vdl.ValueOf(o.Complex)
+ case query_parser.TypFloat:
+ return vdl.ValueOf(o.Float)
+ case query_parser.TypInt:
+ return vdl.ValueOf(o.Int)
+ case query_parser.TypStr:
+ return vdl.ValueOf(o.Str)
+ case query_parser.TypTime:
+ return vdl.ValueOf(o.Time)
+ case query_parser.TypObject:
+ return vdl.ValueOf(o.Object)
+ case query_parser.TypUint:
+ return vdl.ValueOf(o.Uint)
+ default:
+ // Other types can't be converted and *shouldn't* be returned
+ // from a function. This case will result in a nil for this
+ // column in the row.
+ return nil
+ }
+}
+
func makeStrOp(off int64, s string) *query_parser.Operand {
var o query_parser.Operand
o.Off = off
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 88b0373..08600a7 100644
--- a/v23/syncbase/nosql/internal/query/query_parser/query_parser.go
+++ b/v23/syncbase/nosql/internal/query/query_parser/query_parser.go
@@ -144,12 +144,22 @@
Node
}
-// ColumnEntry is only used in SelectClause.
+type SelectorType int
+
+const (
+ TypSelField SelectorType = 1 + iota
+ TypSelFunc
+)
+
+// Selector: entries in the select clause.
+// Entries can be functions for fields.
// The AS name, if present, will ONLY be used in the
// returned column header.
-type ColumnEntry struct {
- Column Field
- As *AsClause // If not nil, used in returned column header.
+type Selector struct {
+ Type SelectorType
+ Field *Field
+ Function *Function
+ As *AsClause // If not nil, used in returned column header.
Node
}
@@ -164,7 +174,7 @@
}
type SelectClause struct {
- Columns []ColumnEntry
+ Selectors []Selector
Node
}
@@ -325,9 +335,9 @@
// Parse the select clause (fields). Return *SelectClause, next token (or error).
func parseSelectClause(db query_db.Database, s *scanner.Scanner, token *Token) (*SelectClause, *Token, error) {
- // must be at least one column or it is an error
- // columns may be in dot notation
- // columns are separated by commas
+ // must be at least one selector or it is an error
+ // field seclectors may be in dot notation
+ // selectors are separated by commas
var selectClause SelectClause
selectClause.Off = token.Off
token = scanToken(s) // eat the select
@@ -335,15 +345,15 @@
return nil, nil, syncql.NewErrUnexpectedEndOfStatement(db.GetContext(), token.Off)
}
var err error
- // scan first column
- if token, err = parseColumn(db, s, &selectClause, token); err != nil {
+ // scan first selector
+ if token, err = parseSelector(db, s, &selectClause, token); err != nil {
return nil, nil, err
}
- // More columns?
+ // More selectors?
for token.Tok == TokCOMMA {
token = scanToken(s)
- if token, err = parseColumn(db, s, &selectClause, token); err != nil {
+ if token, err = parseSelector(db, s, &selectClause, token); err != nil {
return nil, nil, err
}
}
@@ -351,31 +361,47 @@
return &selectClause, token, nil
}
-// Parse a column (field). Return SelectClause and next token (or error).
-func parseColumn(db query_db.Database, s *scanner.Scanner, selectClause *SelectClause, token *Token) (*Token, error) {
+// Parse a selector. Return next token (or error).
+func parseSelector(db query_db.Database, s *scanner.Scanner, selectClause *SelectClause, token *Token) (*Token, error) {
if token.Tok != TokIDENT {
return nil, syncql.NewErrExpectedIdentifier(db.GetContext(), token.Off, token.Value)
}
- var columnEntry ColumnEntry
- columnEntry.Off = token.Off
- columnEntry.Column.Off = token.Off
+ var selector Selector
+ selector.Off = token.Off
+ selector.Type = TypSelField
+ var field Field
+ selector.Field = &field
+ selector.Field.Off = token.Off
+ selector.Field = &field
var segment Segment
segment.Value = token.Value
segment.Off = token.Off
- columnEntry.Column.Segments = append(columnEntry.Column.Segments, segment)
+ selector.Field.Segments = append(selector.Field.Segments, segment)
token = scanToken(s)
- for token.Tok != TokEOF && token.Tok == TokPERIOD {
- token = scanToken(s)
- if token.Tok != TokIDENT {
- return nil, syncql.NewErrExpectedIdentifier(db.GetContext(), token.Off, token.Value)
+ // It might be a function.
+ if token.Tok == TokLEFTPAREN {
+ // switch selector to a function
+ selector.Type = TypSelFunc
+ var err error
+ if selector.Function, token, err = parseFunction(db, s, segment.Value, segment.Off, token); err != nil {
+ return nil, err
}
- var segment Segment
- segment.Value = token.Value
- segment.Off = token.Off
- columnEntry.Column.Segments = append(columnEntry.Column.Segments, segment)
- token = scanToken(s)
+ selector.Field = nil
+
+ } else {
+ for token.Tok != TokEOF && token.Tok == TokPERIOD {
+ token = scanToken(s)
+ if token.Tok != TokIDENT {
+ return nil, syncql.NewErrExpectedIdentifier(db.GetContext(), token.Off, token.Value)
+ }
+ var segment Segment
+ segment.Value = token.Value
+ segment.Off = token.Off
+ selector.Field.Segments = append(selector.Field.Segments, segment)
+ token = scanToken(s)
+ }
}
// Check for AS
@@ -388,11 +414,11 @@
}
asClause.AltName.Value = token.Value
asClause.AltName.Off = token.Off
- columnEntry.As = &asClause
+ selector.As = &asClause
token = scanToken(s)
}
- selectClause.Columns = append(selectClause.Columns, columnEntry)
+ selectClause.Selectors = append(selectClause.Selectors, selector)
return token, nil
}
@@ -554,6 +580,37 @@
return &expression, token, nil
}
+func parseFunction(db query_db.Database, s *scanner.Scanner, funcName string, funcOffset int64, token *Token) (*Function, *Token, error) {
+ var function Function
+ function.Name = funcName
+ function.Off = funcOffset
+ token = scanToken(s) // eat left paren
+ for token.Tok != TokRIGHTPAREN {
+ if token.Tok == TokEOF {
+ return nil, nil, syncql.NewErrExpected(db.GetContext(), token.Off, ")")
+ }
+ var arg *Operand
+ var err error
+ arg, token, err = parseOperand(db, s, token)
+ if err != nil {
+ return nil, nil, err
+ }
+ function.Args = append(function.Args, arg)
+ // A comma or right paren is expected, but a right paren cannot come after a comma.
+ if token.Tok == TokCOMMA {
+ token = scanToken(s)
+ if token.Tok == TokRIGHTPAREN {
+ // right paren cannot come after a comma
+ return nil, nil, syncql.NewErrExpectedOperand(db.GetContext(), token.Off, token.Value)
+ }
+ } else if token.Tok != TokRIGHTPAREN {
+ return nil, nil, syncql.NewErrUnexpected(db.GetContext(), token.Off, token.Value)
+ }
+ }
+ token = scanToken(s) // eat right paren
+ return &function, token, nil
+}
+
// Parse an operand (field or literal) and return it and the next Token (or error)
func parseOperand(db query_db.Database, s *scanner.Scanner, token *Token) (*Operand, *Token, error) {
if token.Tok == TokEOF {
@@ -582,34 +639,10 @@
operand.Type = TypNil
} else if token.Tok == TokLEFTPAREN {
operand.Type = TypFunction
- var function Function
- function.Name = segment.Value
- function.Off = segment.Off
- token = scanToken(s)
- for token.Tok != TokRIGHTPAREN {
- if token.Tok == TokEOF {
- return nil, nil, syncql.NewErrExpected(db.GetContext(), token.Off, ")")
- }
- var arg *Operand
- var err error
- arg, token, err = parseOperand(db, s, token)
- if err != nil {
- return nil, nil, err
- }
- function.Args = append(function.Args, arg)
- // A comma or right paren is expected, but a right paren cannot come after a comma.
- if token.Tok == TokCOMMA {
- token = scanToken(s)
- if token.Tok == TokRIGHTPAREN {
- // right paren cannot come after a comma
- return nil, nil, syncql.NewErrExpectedOperand(db.GetContext(), token.Off, token.Value)
- }
- } else if token.Tok != TokRIGHTPAREN {
- return nil, nil, syncql.NewErrUnexpected(db.GetContext(), token.Off, token.Value)
- }
+ var err error
+ if operand.Function, token, err = parseFunction(db, s, segment.Value, segment.Off, token); err != nil {
+ return nil, nil, err
}
- token = scanToken(s) // eat right paren
- operand.Function = &function
} else { // This is a field (column) operand.
// If the next token is a period, collect the rest of the segments in the column.
for token.Tok != TokEOF && token.Tok == TokPERIOD {
@@ -872,19 +905,24 @@
func (sel SelectClause) String() string {
val := fmt.Sprintf(" Off(%d):SELECT Columns(", sel.Off)
sep := ""
- for _, column := range sel.Columns {
- val += sep + column.String()
+ for _, selector := range sel.Selectors {
+ val += sep + selector.String()
sep = ","
}
val += ")"
return val
}
-func (c ColumnEntry) String() string {
- val := fmt.Sprintf(" Off(%d):", c.Off)
- val += c.Column.String()
- if c.As != nil {
- val += c.As.String()
+func (s Selector) String() string {
+ val := fmt.Sprintf(" Off(%d):", s.Off)
+ switch s.Type {
+ case TypSelField:
+ val += s.Field.String()
+ case TypSelFunc:
+ val += s.Function.String()
+ }
+ if s.As != nil {
+ val += s.As.String()
}
return val
}
diff --git a/v23/syncbase/nosql/internal/query/query_parser/query_parser_test.go b/v23/syncbase/nosql/internal/query/query_parser/query_parser_test.go
index aa61f39..a1e8961 100644
--- a/v23/syncbase/nosql/internal/query/query_parser/query_parser_test.go
+++ b/v23/syncbase/nosql/internal/query/query_parser/query_parser_test.go
@@ -55,9 +55,10 @@
"select v from Customer",
query_parser.SelectStatement{
Select: &query_parser.SelectClause{
- Columns: []query_parser.ColumnEntry{
- query_parser.ColumnEntry{
- Column: query_parser.Field{
+ Selectors: []query_parser.Selector{
+ query_parser.Selector{
+ Type: query_parser.TypSelField,
+ Field: &query_parser.Field{
Segments: []query_parser.Segment{
query_parser.Segment{
Value: "v",
@@ -85,9 +86,10 @@
"select k as Key, v as Value from Customer",
query_parser.SelectStatement{
Select: &query_parser.SelectClause{
- Columns: []query_parser.ColumnEntry{
- query_parser.ColumnEntry{
- Column: query_parser.Field{
+ Selectors: []query_parser.Selector{
+ query_parser.Selector{
+ Type: query_parser.TypSelField,
+ Field: &query_parser.Field{
Segments: []query_parser.Segment{
query_parser.Segment{
Value: "k",
@@ -105,8 +107,9 @@
},
Node: query_parser.Node{Off: 7},
},
- query_parser.ColumnEntry{
- Column: query_parser.Field{
+ query_parser.Selector{
+ Type: query_parser.TypSelField,
+ Field: &query_parser.Field{
Segments: []query_parser.Segment{
query_parser.Segment{
Value: "v",
@@ -141,9 +144,10 @@
" select v from Customer",
query_parser.SelectStatement{
Select: &query_parser.SelectClause{
- Columns: []query_parser.ColumnEntry{
- query_parser.ColumnEntry{
- Column: query_parser.Field{
+ Selectors: []query_parser.Selector{
+ query_parser.Selector{
+ Type: query_parser.TypSelField,
+ Field: &query_parser.Field{
Segments: []query_parser.Segment{
query_parser.Segment{
Value: "v",
@@ -172,9 +176,10 @@
"select v from Customer limit 100 offset 200",
query_parser.SelectStatement{
Select: &query_parser.SelectClause{
- Columns: []query_parser.ColumnEntry{
- query_parser.ColumnEntry{
- Column: query_parser.Field{
+ Selectors: []query_parser.Selector{
+ query_parser.Selector{
+ Type: query_parser.TypSelField,
+ Field: &query_parser.Field{
Segments: []query_parser.Segment{
query_parser.Segment{
Value: "v",
@@ -217,9 +222,10 @@
"select v from Customer offset 400 limit 10",
query_parser.SelectStatement{
Select: &query_parser.SelectClause{
- Columns: []query_parser.ColumnEntry{
- query_parser.ColumnEntry{
- Column: query_parser.Field{
+ Selectors: []query_parser.Selector{
+ query_parser.Selector{
+ Type: query_parser.TypSelField,
+ Field: &query_parser.Field{
Segments: []query_parser.Segment{
query_parser.Segment{
Value: "v",
@@ -262,9 +268,10 @@
"select v from Customer limit 100 offset 200 limit 1 offset 2",
query_parser.SelectStatement{
Select: &query_parser.SelectClause{
- Columns: []query_parser.ColumnEntry{
- query_parser.ColumnEntry{
- Column: query_parser.Field{
+ Selectors: []query_parser.Selector{
+ query_parser.Selector{
+ Type: query_parser.TypSelField,
+ Field: &query_parser.Field{
Segments: []query_parser.Segment{
query_parser.Segment{
Value: "v",
@@ -307,9 +314,10 @@
"select foo.x, bar.y from Customer",
query_parser.SelectStatement{
Select: &query_parser.SelectClause{
- Columns: []query_parser.ColumnEntry{
- query_parser.ColumnEntry{
- Column: query_parser.Field{
+ Selectors: []query_parser.Selector{
+ query_parser.Selector{
+ Type: query_parser.TypSelField,
+ Field: &query_parser.Field{
Segments: []query_parser.Segment{
query_parser.Segment{
Value: "foo",
@@ -324,8 +332,9 @@
},
Node: query_parser.Node{Off: 7},
},
- query_parser.ColumnEntry{
- Column: query_parser.Field{
+ query_parser.Selector{
+ Type: query_parser.TypSelField,
+ Field: &query_parser.Field{
Segments: []query_parser.Segment{
query_parser.Segment{
Value: "bar",
@@ -358,9 +367,10 @@
"select select from from where where equal 42",
query_parser.SelectStatement{
Select: &query_parser.SelectClause{
- Columns: []query_parser.ColumnEntry{
- query_parser.ColumnEntry{
- Column: query_parser.Field{
+ Selectors: []query_parser.Selector{
+ query_parser.Selector{
+ Type: query_parser.TypSelField,
+ Field: &query_parser.Field{
Segments: []query_parser.Segment{
query_parser.Segment{
Value: "select",
@@ -417,9 +427,10 @@
"select v from Customer where v.Value equal true",
query_parser.SelectStatement{
Select: &query_parser.SelectClause{
- Columns: []query_parser.ColumnEntry{
- query_parser.ColumnEntry{
- Column: query_parser.Field{
+ Selectors: []query_parser.Selector{
+ query_parser.Selector{
+ Type: query_parser.TypSelField,
+ Field: &query_parser.Field{
Segments: []query_parser.Segment{
query_parser.Segment{
Value: "v",
@@ -480,9 +491,10 @@
"select v from Customer where v.ZipCode is nil",
query_parser.SelectStatement{
Select: &query_parser.SelectClause{
- Columns: []query_parser.ColumnEntry{
- query_parser.ColumnEntry{
- Column: query_parser.Field{
+ Selectors: []query_parser.Selector{
+ query_parser.Selector{
+ Type: query_parser.TypSelField,
+ Field: &query_parser.Field{
Segments: []query_parser.Segment{
query_parser.Segment{
Value: "v",
@@ -542,9 +554,10 @@
"select v from Customer where v.ZipCode is not nil",
query_parser.SelectStatement{
Select: &query_parser.SelectClause{
- Columns: []query_parser.ColumnEntry{
- query_parser.ColumnEntry{
- Column: query_parser.Field{
+ Selectors: []query_parser.Selector{
+ query_parser.Selector{
+ Type: query_parser.TypSelField,
+ Field: &query_parser.Field{
Segments: []query_parser.Segment{
query_parser.Segment{
Value: "v",
@@ -604,9 +617,10 @@
"select v from Customer where v.Value = false",
query_parser.SelectStatement{
Select: &query_parser.SelectClause{
- Columns: []query_parser.ColumnEntry{
- query_parser.ColumnEntry{
- Column: query_parser.Field{
+ Selectors: []query_parser.Selector{
+ query_parser.Selector{
+ Type: query_parser.TypSelField,
+ Field: &query_parser.Field{
Segments: []query_parser.Segment{
query_parser.Segment{
Value: "v",
@@ -667,9 +681,10 @@
"select v from Customer where v.Value equal -42",
query_parser.SelectStatement{
Select: &query_parser.SelectClause{
- Columns: []query_parser.ColumnEntry{
- query_parser.ColumnEntry{
- Column: query_parser.Field{
+ Selectors: []query_parser.Selector{
+ query_parser.Selector{
+ Type: query_parser.TypSelField,
+ Field: &query_parser.Field{
Segments: []query_parser.Segment{
query_parser.Segment{
Value: "v",
@@ -730,9 +745,10 @@
"select v from Customer where v.Value equal -18.888",
query_parser.SelectStatement{
Select: &query_parser.SelectClause{
- Columns: []query_parser.ColumnEntry{
- query_parser.ColumnEntry{
- Column: query_parser.Field{
+ Selectors: []query_parser.Selector{
+ query_parser.Selector{
+ Type: query_parser.TypSelField,
+ Field: &query_parser.Field{
Segments: []query_parser.Segment{
query_parser.Segment{
Value: "v",
@@ -793,9 +809,10 @@
"select x from y where b = 'c'",
query_parser.SelectStatement{
Select: &query_parser.SelectClause{
- Columns: []query_parser.ColumnEntry{
- query_parser.ColumnEntry{
- Column: query_parser.Field{
+ Selectors: []query_parser.Selector{
+ query_parser.Selector{
+ Type: query_parser.TypSelField,
+ Field: &query_parser.Field{
Segments: []query_parser.Segment{
query_parser.Segment{
Value: "x",
@@ -852,9 +869,10 @@
"select x from y where b = 'c' limit 10 offset 20",
query_parser.SelectStatement{
Select: &query_parser.SelectClause{
- Columns: []query_parser.ColumnEntry{
- query_parser.ColumnEntry{
- Column: query_parser.Field{
+ Selectors: []query_parser.Selector{
+ query_parser.Selector{
+ Type: query_parser.TypSelField,
+ Field: &query_parser.Field{
Segments: []query_parser.Segment{
query_parser.Segment{
Value: "x",
@@ -925,9 +943,10 @@
"select x from y where b = 'c' limit 10",
query_parser.SelectStatement{
Select: &query_parser.SelectClause{
- Columns: []query_parser.ColumnEntry{
- query_parser.ColumnEntry{
- Column: query_parser.Field{
+ Selectors: []query_parser.Selector{
+ query_parser.Selector{
+ Type: query_parser.TypSelField,
+ Field: &query_parser.Field{
Segments: []query_parser.Segment{
query_parser.Segment{
Value: "x",
@@ -991,9 +1010,10 @@
"select x from y where b = 'c' offset 10",
query_parser.SelectStatement{
Select: &query_parser.SelectClause{
- Columns: []query_parser.ColumnEntry{
- query_parser.ColumnEntry{
- Column: query_parser.Field{
+ Selectors: []query_parser.Selector{
+ query_parser.Selector{
+ Type: query_parser.TypSelField,
+ Field: &query_parser.Field{
Segments: []query_parser.Segment{
query_parser.Segment{
Value: "x",
@@ -1057,9 +1077,10 @@
"select foo.bar, tom.dick.harry from Customer where a.b.c = \"baz\" and d.e.f like \"%foobarbaz\"",
query_parser.SelectStatement{
Select: &query_parser.SelectClause{
- Columns: []query_parser.ColumnEntry{
- query_parser.ColumnEntry{
- Column: query_parser.Field{
+ Selectors: []query_parser.Selector{
+ query_parser.Selector{
+ Type: query_parser.TypSelField,
+ Field: &query_parser.Field{
Segments: []query_parser.Segment{
query_parser.Segment{
Value: "foo",
@@ -1074,8 +1095,9 @@
},
Node: query_parser.Node{Off: 7},
},
- query_parser.ColumnEntry{
- Column: query_parser.Field{
+ query_parser.Selector{
+ Type: query_parser.TypSelField,
+ Field: &query_parser.Field{
Segments: []query_parser.Segment{
query_parser.Segment{
Value: "tom",
@@ -1196,9 +1218,10 @@
"select foo, bar from Customer where CustRecord.CustID=123 or CustRecord.Name like \"f%\"",
query_parser.SelectStatement{
Select: &query_parser.SelectClause{
- Columns: []query_parser.ColumnEntry{
- query_parser.ColumnEntry{
- Column: query_parser.Field{
+ Selectors: []query_parser.Selector{
+ query_parser.Selector{
+ Type: query_parser.TypSelField,
+ Field: &query_parser.Field{
Segments: []query_parser.Segment{
query_parser.Segment{
Value: "foo",
@@ -1209,8 +1232,9 @@
},
Node: query_parser.Node{Off: 7},
},
- query_parser.ColumnEntry{
- Column: query_parser.Field{
+ query_parser.Selector{
+ Type: query_parser.TypSelField,
+ Field: &query_parser.Field{
Segments: []query_parser.Segment{
query_parser.Segment{
Value: "bar",
@@ -1315,9 +1339,10 @@
"select foo from Customer where A=123 or B=456 and C=789",
query_parser.SelectStatement{
Select: &query_parser.SelectClause{
- Columns: []query_parser.ColumnEntry{
- query_parser.ColumnEntry{
- Column: query_parser.Field{
+ Selectors: []query_parser.Selector{
+ query_parser.Selector{
+ Type: query_parser.TypSelField,
+ Field: &query_parser.Field{
Segments: []query_parser.Segment{
query_parser.Segment{
Value: "foo",
@@ -1454,9 +1479,10 @@
"select foo from Customer where (A=123 or B=456) and C=789",
query_parser.SelectStatement{
Select: &query_parser.SelectClause{
- Columns: []query_parser.ColumnEntry{
- query_parser.ColumnEntry{
- Column: query_parser.Field{
+ Selectors: []query_parser.Selector{
+ query_parser.Selector{
+ Type: query_parser.TypSelField,
+ Field: &query_parser.Field{
Segments: []query_parser.Segment{
query_parser.Segment{
Value: "foo",
@@ -1593,9 +1619,10 @@
"select foo from Customer where (A<=123 or B>456) and C>=789",
query_parser.SelectStatement{
Select: &query_parser.SelectClause{
- Columns: []query_parser.ColumnEntry{
- query_parser.ColumnEntry{
- Column: query_parser.Field{
+ Selectors: []query_parser.Selector{
+ query_parser.Selector{
+ Type: query_parser.TypSelField,
+ Field: &query_parser.Field{
Segments: []query_parser.Segment{
query_parser.Segment{
Value: "foo",
@@ -1732,9 +1759,10 @@
"select foo from Customer where A=123 or (B=456 and C=789)",
query_parser.SelectStatement{
Select: &query_parser.SelectClause{
- Columns: []query_parser.ColumnEntry{
- query_parser.ColumnEntry{
- Column: query_parser.Field{
+ Selectors: []query_parser.Selector{
+ query_parser.Selector{
+ Type: query_parser.TypSelField,
+ Field: &query_parser.Field{
Segments: []query_parser.Segment{
query_parser.Segment{
Value: "foo",
@@ -1871,9 +1899,10 @@
"select foo from Customer where (A=123) or ((B=456) and (C=789))",
query_parser.SelectStatement{
Select: &query_parser.SelectClause{
- Columns: []query_parser.ColumnEntry{
- query_parser.ColumnEntry{
- Column: query_parser.Field{
+ Selectors: []query_parser.Selector{
+ query_parser.Selector{
+ Type: query_parser.TypSelField,
+ Field: &query_parser.Field{
Segments: []query_parser.Segment{
query_parser.Segment{
Value: "foo",
@@ -2010,9 +2039,10 @@
"select foo from Customer where A<>123 or B not equal 456 and C not like \"abc%\"",
query_parser.SelectStatement{
Select: &query_parser.SelectClause{
- Columns: []query_parser.ColumnEntry{
- query_parser.ColumnEntry{
- Column: query_parser.Field{
+ Selectors: []query_parser.Selector{
+ query_parser.Selector{
+ Type: query_parser.TypSelField,
+ Field: &query_parser.Field{
Segments: []query_parser.Segment{
query_parser.Segment{
Value: "foo",
@@ -2149,9 +2179,10 @@
"select k, v from Customer where k = \"\\\"",
query_parser.SelectStatement{
Select: &query_parser.SelectClause{
- Columns: []query_parser.ColumnEntry{
- query_parser.ColumnEntry{
- Column: query_parser.Field{
+ Selectors: []query_parser.Selector{
+ query_parser.Selector{
+ Type: query_parser.TypSelField,
+ Field: &query_parser.Field{
Segments: []query_parser.Segment{
query_parser.Segment{
Value: "k",
@@ -2162,8 +2193,9 @@
},
Node: query_parser.Node{Off: 7},
},
- query_parser.ColumnEntry{
- Column: query_parser.Field{
+ query_parser.Selector{
+ Type: query_parser.TypSelField,
+ Field: &query_parser.Field{
Segments: []query_parser.Segment{
query_parser.Segment{
Value: "v",
@@ -2220,9 +2252,10 @@
"select v from Customer where Now() < Time(\"2015/07/22\") and Foo(10,20.1,v.Bar) = true",
query_parser.SelectStatement{
Select: &query_parser.SelectClause{
- Columns: []query_parser.ColumnEntry{
- query_parser.ColumnEntry{
- Column: query_parser.Field{
+ Selectors: []query_parser.Selector{
+ query_parser.Selector{
+ Type: query_parser.TypSelField,
+ Field: &query_parser.Field{
Segments: []query_parser.Segment{
query_parser.Segment{
Value: "v",
@@ -2345,6 +2378,75 @@
},
nil,
},
+ {
+ "select Now() from Customer",
+ query_parser.SelectStatement{
+ Select: &query_parser.SelectClause{
+ Selectors: []query_parser.Selector{
+ query_parser.Selector{
+ Type: query_parser.TypSelFunc,
+ Function: &query_parser.Function{
+ Name: "Now",
+ Args: nil,
+ Node: query_parser.Node{Off: 7},
+ },
+ Node: query_parser.Node{Off: 7},
+ },
+ },
+ },
+ From: &query_parser.FromClause{
+ Table: query_parser.TableEntry{
+ Name: "Customer",
+ Node: query_parser.Node{Off: 18},
+ },
+ Node: query_parser.Node{Off: 13},
+ },
+ Node: query_parser.Node{Off: 0},
+ },
+ nil,
+ },
+ {
+ "select Now(), Date(\"2015-06-01 PST\") from Customer",
+ query_parser.SelectStatement{
+ Select: &query_parser.SelectClause{
+ Selectors: []query_parser.Selector{
+ query_parser.Selector{
+ Type: query_parser.TypSelFunc,
+ Function: &query_parser.Function{
+ Name: "Now",
+ Args: nil,
+ Node: query_parser.Node{Off: 7},
+ },
+ Node: query_parser.Node{Off: 7},
+ },
+ query_parser.Selector{
+ Type: query_parser.TypSelFunc,
+ Function: &query_parser.Function{
+ Name: "Date",
+ Args: []*query_parser.Operand{
+ &query_parser.Operand{
+ Type: query_parser.TypStr,
+ Str: "2015-06-01 PST",
+ Node: query_parser.Node{Off: 19},
+ },
+ },
+ Node: query_parser.Node{Off: 14},
+ },
+ Node: query_parser.Node{Off: 14},
+ },
+ },
+ },
+ From: &query_parser.FromClause{
+ Table: query_parser.TableEntry{
+ Name: "Customer",
+ Node: query_parser.Node{Off: 42},
+ },
+ Node: query_parser.Node{Off: 37},
+ },
+ Node: query_parser.Node{Off: 0},
+ },
+ nil,
+ },
}
for _, test := range basic {
diff --git a/v23/syncbase/nosql/internal/query/test/query_test.go b/v23/syncbase/nosql/internal/query/test/query_test.go
index 0cb5f56..fd113bf 100644
--- a/v23/syncbase/nosql/internal/query/test/query_test.go
+++ b/v23/syncbase/nosql/internal/query/test/query_test.go
@@ -111,6 +111,18 @@
value *vdl.Value
}
+var t2015 time.Time
+
+var t2015_04 time.Time
+var t2015_04_12 time.Time
+var t2015_04_12_22 time.Time
+var t2015_04_12_22_16 time.Time
+var t2015_04_12_22_16_06 time.Time
+
+var t2015_07 time.Time
+var t2015_07_01 time.Time
+var t2015_07_01_01_23_45 time.Time
+
func init() {
var shutdown v23.Shutdown
db.ctx, shutdown = test.V23Init()
@@ -124,6 +136,19 @@
t20150412221606, _ := time.Parse("Jan 2 2006 15:04:05 -0700 MST", "Apr 12 2015 22:16:06 -0700 PDT")
t20150413141707, _ := time.Parse("Jan 2 2006 15:04:05 -0700 MST", "Apr 13 2015 14:17:07 -0700 PDT")
+ t2015, _ = time.Parse("2006 MST", "2015 PST")
+
+ t2015_04, _ = time.Parse("Jan 2006 MST", "Apr 2015 PDT")
+ t2015_07, _ = time.Parse("Jan 2006 MST", "Jul 2015 PDT")
+
+ t2015_04_12, _ = time.Parse("Jan 2 2006 MST", "Apr 12 2015 PDT")
+ t2015_07_01, _ = time.Parse("Jan 2 2006 MST", "Jul 01 2015 PDT")
+
+ t2015_04_12_22, _ = time.Parse("Jan 2 2006 15 MST", "Apr 12 2015 22 PDT")
+ t2015_04_12_22_16, _ = time.Parse("Jan 2 2006 15:04 MST", "Apr 12 2015 22:16 PDT")
+ t2015_04_12_22_16_06, _ = time.Parse("Jan 2 2006 15:04:05 MST", "Apr 12 2015 22:16:06 PDT")
+ t2015_07_01_01_23_45, _ = time.Parse("Jan 2 2006 15:04:05 MST", "Jul 01 2015 01:23:45 PDT")
+
custTable.name = "Customer"
custTable.rows = []kv{
kv{
@@ -228,6 +253,11 @@
r [][]*vdl.Value
}
+type preExecFunctionTest struct {
+ query string
+ h []string
+}
+
type execSelectSingleRowTest struct {
query string
k string
@@ -679,7 +709,7 @@
// Note: this wouldn't work for March as daylight saving occurs March 8
// and causes comparisons for those days to be off 1 hour.
// It would work to use UTC -- see next test.
- "select k from Customer where YM(v.InvoiceDate, \"America/Los_Angeles\") = YM(Date(\"2015-04-01 PST\"), \"America/Los_Angeles\")",
+ "select k from Customer where YM(v.InvoiceDate, \"America/Los_Angeles\") = YM(Date(\"2015-04-01 PDT\"), \"America/Los_Angeles\")",
[]string{"k"},
[][]*vdl.Value{
[]*vdl.Value{vdl.ValueOf(custTable.rows[7].key)},
@@ -743,7 +773,7 @@
[]*vdl.Value{vdl.ValueOf(custTable.rows[6].key)},
},
},
- // Test string functions.
+ // Test string functions in where clause.
{
// Select invoices shipped to Any street -- using LowerCase.
"select k from Customer where t = \"Invoice\" and LowerCase(v.ShipTo.Street) like \"%any%\"",
@@ -766,6 +796,137 @@
[]*vdl.Value{vdl.ValueOf(custTable.rows[8].key)},
},
},
+ // Select clause functions.
+ // Date function
+ {
+ "select Date(\"2015-07-01 PDT\") from Customer",
+ []string{"Date"},
+ [][]*vdl.Value{
+ []*vdl.Value{vdl.ValueOf(t2015_07_01)},
+ []*vdl.Value{vdl.ValueOf(t2015_07_01)},
+ []*vdl.Value{vdl.ValueOf(t2015_07_01)},
+ []*vdl.Value{vdl.ValueOf(t2015_07_01)},
+ []*vdl.Value{vdl.ValueOf(t2015_07_01)},
+ []*vdl.Value{vdl.ValueOf(t2015_07_01)},
+ []*vdl.Value{vdl.ValueOf(t2015_07_01)},
+ []*vdl.Value{vdl.ValueOf(t2015_07_01)},
+ []*vdl.Value{vdl.ValueOf(t2015_07_01)},
+ },
+ },
+ // DateTime function
+ {
+ "select DateTime(\"2015-07-01 01:23:45 PDT\") from Customer",
+ []string{"DateTime"},
+ [][]*vdl.Value{
+ []*vdl.Value{vdl.ValueOf(t2015_07_01_01_23_45)},
+ []*vdl.Value{vdl.ValueOf(t2015_07_01_01_23_45)},
+ []*vdl.Value{vdl.ValueOf(t2015_07_01_01_23_45)},
+ []*vdl.Value{vdl.ValueOf(t2015_07_01_01_23_45)},
+ []*vdl.Value{vdl.ValueOf(t2015_07_01_01_23_45)},
+ []*vdl.Value{vdl.ValueOf(t2015_07_01_01_23_45)},
+ []*vdl.Value{vdl.ValueOf(t2015_07_01_01_23_45)},
+ []*vdl.Value{vdl.ValueOf(t2015_07_01_01_23_45)},
+ []*vdl.Value{vdl.ValueOf(t2015_07_01_01_23_45)},
+ },
+ },
+ // LowerCase function
+ {
+ "select LowerCase(v.Name) as name from Customer where t = \"Customer\"",
+ []string{"name"},
+ [][]*vdl.Value{
+ []*vdl.Value{vdl.ValueOf("john smith")},
+ []*vdl.Value{vdl.ValueOf("bat masterson")},
+ },
+ },
+ // UpperCase function
+ {
+ "select UpperCase(v.Name) as NAME from Customer where t = \"Customer\"",
+ []string{"NAME"},
+ [][]*vdl.Value{
+ []*vdl.Value{vdl.ValueOf("JOHN SMITH")},
+ []*vdl.Value{vdl.ValueOf("BAT MASTERSON")},
+ },
+ },
+ // YMDHMS function
+ {
+ "select k, YMDHMS(v.InvoiceDate, \"America/Los_Angeles\") from Customer where t = \"Invoice\" and k = \"002003\"",
+ []string{
+ "k",
+ "YMDHMS",
+ },
+ [][]*vdl.Value{
+ []*vdl.Value{vdl.ValueOf("002003"), vdl.ValueOf(t2015_04_12_22_16_06)},
+ },
+ },
+ // YMDHM function
+ {
+ "select k, YMDHM(v.InvoiceDate, \"America/Los_Angeles\") from Customer where t = \"Invoice\" and k = \"002003\"",
+ []string{
+ "k",
+ "YMDHM",
+ },
+ [][]*vdl.Value{
+ []*vdl.Value{vdl.ValueOf("002003"), vdl.ValueOf(t2015_04_12_22_16)},
+ },
+ },
+ // YMDH function
+ {
+ "select k, YMDH(v.InvoiceDate, \"America/Los_Angeles\") from Customer where t = \"Invoice\" and k = \"002003\"",
+ []string{
+ "k",
+ "YMDH",
+ },
+ [][]*vdl.Value{
+ []*vdl.Value{vdl.ValueOf("002003"), vdl.ValueOf(t2015_04_12_22)},
+ },
+ },
+ // YMD function
+ {
+ "select k, YMD(v.InvoiceDate, \"America/Los_Angeles\") from Customer where t = \"Invoice\" and k = \"002003\"",
+ []string{
+ "k",
+ "YMD",
+ },
+ [][]*vdl.Value{
+ []*vdl.Value{vdl.ValueOf("002003"), vdl.ValueOf(t2015_04_12)},
+ },
+ },
+ // YM function
+ {
+ "select k, YM(v.InvoiceDate, \"America/Los_Angeles\") from Customer where t = \"Invoice\" and k = \"002003\"",
+ []string{
+ "k",
+ "YM",
+ },
+ [][]*vdl.Value{
+ []*vdl.Value{vdl.ValueOf("002003"), vdl.ValueOf(t2015_04)},
+ },
+ },
+ // Y function
+ {
+ "select k, Y(v.InvoiceDate, \"America/Los_Angeles\") from Customer where t = \"Invoice\" and k = \"001001\"",
+ []string{
+ "k",
+ "Y",
+ },
+ [][]*vdl.Value{
+ []*vdl.Value{vdl.ValueOf("001001"), vdl.ValueOf(t2015)},
+ },
+ },
+ // Nested functions
+ {
+ "select Y(YM(YMD(YMDH(YMDHM(YMDHMS(v.InvoiceDate, \"America/Los_Angeles\"), \"America/Los_Angeles\"), \"America/Los_Angeles\"), \"America/Los_Angeles\"), \"America/Los_Angeles\"), \"America/Los_Angeles\") from Customer where t = \"Invoice\" and k = \"001001\"",
+ []string{"Y"},
+ [][]*vdl.Value{
+ []*vdl.Value{vdl.ValueOf(t2015)},
+ },
+ },
+ // Bad arg to function. Expression is false.
+ {
+ "select v from Customer where t = \"Invoice\" and YMD(v.InvoiceDate, v.Foo) = v.InvoiceDate",
+ []string{"v"},
+ [][]*vdl.Value{},
+ },
}
for _, test := range basic {
@@ -788,6 +949,40 @@
}
}
+// Use Now to verify it is "pre" executed such that all the rows
+// have the same time.
+func TestPreExecFunctions(t *testing.T) {
+ basic := []preExecFunctionTest{
+ {
+ "select Now() from Customer",
+ []string{
+ "Now",
+ },
+ },
+ }
+
+ for _, test := range basic {
+ h, rs, err := query.Exec(db, test.query)
+ if err != nil {
+ t.Errorf("query: %s; got %v, want nil", test.query, err)
+ } else {
+ // Check that all results are identical.
+ // Collect results.
+ var last []*vdl.Value
+ for rs.Advance() {
+ result := rs.Result()
+ if last != nil && !reflect.DeepEqual(last, result) {
+ t.Errorf("query: %s; got %v, want %v", test.query, result, last)
+ }
+ last = result
+ }
+ if !reflect.DeepEqual(test.h, h) {
+ t.Errorf("query: %s; got %v, want %v", test.query, h, test.h)
+ }
+ }
+ }
+}
+
func TestKeyPrefixes(t *testing.T) {
basic := []keyPrefixesTest{
{
@@ -1257,7 +1452,7 @@
if semErr == nil {
switch sel := (*s).(type) {
case query_parser.SelectStatement:
- result := query.ComposeProjection(test.k, test.v, sel.Select)
+ result := query.ComposeProjection(db, 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)
}
diff --git a/v23/syncbase/nosql/syncql/syncql.vdl b/v23/syncbase/nosql/syncql/syncql.vdl
index eb76a44..e8a3053 100644
--- a/v23/syncbase/nosql/syncql/syncql.vdl
+++ b/v23/syncbase/nosql/syncql/syncql.vdl
@@ -48,6 +48,9 @@
FunctionArgCount(off int64, name string, expected int64, found int64) {
"en": "[{off}]Function '{name}' expects {expected} args, found: {found}.",
}
+ FunctionArgBad(off int64, funcName, argName string) {
+ "en": "[{off}]Function '{funcName}' arg '{argName}' could not be resolved.",
+ }
FunctionNotFound(off int64, name string) {
"en": "[{off}]Function '{name}' not found.",
}
diff --git a/v23/syncbase/nosql/syncql/syncql.vdl.go b/v23/syncbase/nosql/syncql/syncql.vdl.go
index f6a4280..d227fbb 100644
--- a/v23/syncbase/nosql/syncql/syncql.vdl.go
+++ b/v23/syncbase/nosql/syncql/syncql.vdl.go
@@ -29,6 +29,7 @@
ErrExpectedOperand = verror.Register("v.io/syncbase/v23/syncbase/nosql/syncql.ExpectedOperand", verror.NoRetry, "{1:}{2:} [{3}]Expected operand, found {4}.")
ErrExpectedOperator = verror.Register("v.io/syncbase/v23/syncbase/nosql/syncql.ExpectedOperator", verror.NoRetry, "{1:}{2:} [{3}]Expected operator, found {4}.")
ErrFunctionArgCount = verror.Register("v.io/syncbase/v23/syncbase/nosql/syncql.FunctionArgCount", verror.NoRetry, "{1:}{2:} [{3}]Function '{4}' expects {5} args, found: {6}.")
+ ErrFunctionArgBad = verror.Register("v.io/syncbase/v23/syncbase/nosql/syncql.FunctionArgBad", verror.NoRetry, "{1:}{2:} [{3}]Function '{4}' arg '{5}' could not be resolved.")
ErrFunctionNotFound = verror.Register("v.io/syncbase/v23/syncbase/nosql/syncql.FunctionNotFound", verror.NoRetry, "{1:}{2:} [{3}]Function '{4}' not found.")
ErrFunctionReturnedError = verror.Register("v.io/syncbase/v23/syncbase/nosql/syncql.FunctionReturnedError", verror.NoRetry, "{1:}{2:} [{3}]Function '{4}' returned error: {5}.")
ErrIsIsNotRequireLhsValue = verror.Register("v.io/syncbase/v23/syncbase/nosql/syncql.IsIsNotRequireLhsValue", verror.NoRetry, "{1:}{2:} [{3}]'Is/is not' expressions require left operand to be a value operand.")
@@ -64,6 +65,7 @@
i18n.Cat().SetWithBase(i18n.LangID("en"), i18n.MsgID(ErrExpectedOperand.ID), "{1:}{2:} [{3}]Expected operand, found {4}.")
i18n.Cat().SetWithBase(i18n.LangID("en"), i18n.MsgID(ErrExpectedOperator.ID), "{1:}{2:} [{3}]Expected operator, found {4}.")
i18n.Cat().SetWithBase(i18n.LangID("en"), i18n.MsgID(ErrFunctionArgCount.ID), "{1:}{2:} [{3}]Function '{4}' expects {5} args, found: {6}.")
+ i18n.Cat().SetWithBase(i18n.LangID("en"), i18n.MsgID(ErrFunctionArgBad.ID), "{1:}{2:} [{3}]Function '{4}' arg '{5}' could not be resolved.")
i18n.Cat().SetWithBase(i18n.LangID("en"), i18n.MsgID(ErrFunctionNotFound.ID), "{1:}{2:} [{3}]Function '{4}' not found.")
i18n.Cat().SetWithBase(i18n.LangID("en"), i18n.MsgID(ErrFunctionReturnedError.ID), "{1:}{2:} [{3}]Function '{4}' returned error: {5}.")
i18n.Cat().SetWithBase(i18n.LangID("en"), i18n.MsgID(ErrIsIsNotRequireLhsValue.ID), "{1:}{2:} [{3}]'Is/is not' expressions require left operand to be a value operand.")
@@ -154,6 +156,11 @@
return verror.New(ErrFunctionArgCount, ctx, off, name, expected, found)
}
+// NewErrFunctionArgBad returns an error with the ErrFunctionArgBad ID.
+func NewErrFunctionArgBad(ctx *context.T, off int64, funcName string, argName string) error {
+ return verror.New(ErrFunctionArgBad, ctx, off, funcName, argName)
+}
+
// NewErrFunctionNotFound returns an error with the ErrFunctionNotFound ID.
func NewErrFunctionNotFound(ctx *context.T, off int64, name string) error {
return verror.New(ErrFunctionNotFound, ctx, off, name)