syncbase: syncql: make k, v and functions case sensitive
Field specifications are case sensitive except for the k and v.
Change to also require the correct case for k and v.
For example,
select V.Foo.Bar from Customer
^
Did you mean 'v'?
In addition, functions are now case sensitve and also provide a helpful error:
select now() from Customer
^
Did you mean 'Now'?
With this change, only syncQL reserved words are case insensitive.
Change-Id: I51b1f9958cf03a02473390b68c41d9bfced46b44
diff --git a/v23/syncbase/nosql/exec_test/exec_test.go b/v23/syncbase/nosql/exec_test/exec_test.go
index e8b10dc..1ddc151 100644
--- a/v23/syncbase/nosql/exec_test/exec_test.go
+++ b/v23/syncbase/nosql/exec_test/exec_test.go
@@ -762,8 +762,8 @@
},
// Test string functions in where clause.
{
- // Select invoices shipped to Any street -- using LowerCase.
- "select k from Customer where Type(v) like \"%.Invoice\" and LowerCase(v.ShipTo.Street) like \"%any%\"",
+ // Select invoices shipped to Any street -- using Lowercase.
+ "select k from Customer where Type(v) like \"%.Invoice\" and Lowercase(v.ShipTo.Street) like \"%any%\"",
[]string{"k"},
[][]*vdl.Value{
[]*vdl.Value{vdl.ValueOf(customerEntries[5].key)},
@@ -773,8 +773,8 @@
},
},
{
- // Select invoices shipped to Any street -- using UpperCase.
- "select k from Customer where Type(v) like \"%.Invoice\" and UpperCase(v.ShipTo.Street) like \"%ANY%\"",
+ // Select invoices shipped to Any street -- using Uppercase.
+ "select k from Customer where Type(v) like \"%.Invoice\" and Uppercase(v.ShipTo.Street) like \"%ANY%\"",
[]string{"k"},
[][]*vdl.Value{
[]*vdl.Value{vdl.ValueOf(customerEntries[5].key)},
@@ -818,9 +818,9 @@
[]*vdl.Value{vdl.ValueOf(t2015_07_01_01_23_45)},
},
},
- // LowerCase function
+ // Lowercase function
{
- "select LowerCase(v.Name) as name from Customer where Type(v) like \"%.Customer\"",
+ "select Lowercase(v.Name) as name from Customer where Type(v) like \"%.Customer\"",
[]string{"name"},
[][]*vdl.Value{
[]*vdl.Value{vdl.ValueOf("john smith")},
@@ -828,9 +828,9 @@
[]*vdl.Value{vdl.ValueOf("john steed")},
},
},
- // UpperCase function
+ // Uppercase function
{
- "select UpperCase(v.Name) as NAME from Customer where Type(v) like \"%.Customer\"",
+ "select Uppercase(v.Name) as NAME from Customer where Type(v) like \"%.Customer\"",
[]string{"NAME"},
[][]*vdl.Value{
[]*vdl.Value{vdl.ValueOf("JOHN SMITH")},
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 d28c9e2..f750dea 100644
--- a/v23/syncbase/nosql/internal/query/query_checker/query_checker.go
+++ b/v23/syncbase/nosql/internal/query/query_checker/query_checker.go
@@ -57,13 +57,19 @@
for _, selector := range s.Selectors {
switch selector.Type {
case query_parser.TypSelField:
- switch strings.ToLower(selector.Field.Segments[0].Value) {
+ 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.
+ case "K":
+ // Be nice and warn of mistakenly capped 'K'.
+ return syncql.NewErrDidYouMeanLowercaseK(db.GetContext(), selector.Field.Segments[0].Off)
+ case "V":
+ // Be nice and warn of mistakenly capped 'V'.
+ return syncql.NewErrDidYouMeanLowercaseV(db.GetContext(), selector.Field.Segments[0].Off)
default:
return syncql.NewErrInvalidSelectField(db.GetContext(), selector.Field.Segments[0].Off)
}
@@ -169,12 +175,19 @@
case query_parser.TypExpr:
return checkExpression(db, o.Expr)
case query_parser.TypField:
- switch strings.ToLower(o.Column.Segments[0].Value) {
+ switch o.Column.Segments[0].Value {
case "k":
if len(o.Column.Segments) > 1 {
return syncql.NewErrDotNotationDisallowedForKey(db.GetContext(), o.Column.Segments[1].Off)
}
case "v":
+ // Nothing to do.
+ case "K":
+ // Be nice and warn of mistakenly capped 'K'.
+ return syncql.NewErrDidYouMeanLowercaseK(db.GetContext(), o.Column.Segments[0].Off)
+ case "V":
+ // Be nice and warn of mistakenly capped 'V'.
+ return syncql.NewErrDidYouMeanLowercaseV(db.GetContext(), o.Column.Segments[0].Off)
default:
return syncql.NewErrBadFieldInWhere(db.GetContext(), o.Column.Segments[0].Off)
}
@@ -372,11 +385,11 @@
}
func IsKeyField(f *query_parser.Field) bool {
- return strings.ToLower(f.Segments[0].Value) == "k"
+ return f.Segments[0].Value == "k"
}
func IsValueField(f *query_parser.Field) bool {
- return strings.ToLower(f.Segments[0].Value) == "v"
+ return f.Segments[0].Value == "v"
}
func IsExpr(o *query_parser.Operand) bool {
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 95f1469..5188c52 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
@@ -432,17 +432,22 @@
{"select v from Customer where v.ZipCode is not 94303", syncql.NewErrIsIsNotRequireRhsNil(db.GetContext(), 46)},
{"select v from Customer where v.ZipCode is not true", syncql.NewErrIsIsNotRequireRhsNil(db.GetContext(), 46)},
{"select v from Customer where v.ZipCode is not 943.03", syncql.NewErrIsIsNotRequireRhsNil(db.GetContext(), 46)},
- {"select v from Customer where Type(v) = \"Customer\" and y(v.InvoiceDate, \"ABC\") = 2015", syncql.NewErrLocationConversionError(db.GetContext(), 71, errors.New("unknown time zone ABC"))},
+ {"select v from Customer where Type(v) = \"Customer\" and Y(v.InvoiceDate, \"ABC\") = 2015", syncql.NewErrLocationConversionError(db.GetContext(), 71, errors.New("unknown time zone ABC"))},
{"select v from Customer where Type(v) = \"Customer\" and YM(v.InvoiceDate, \"ABC\") = 2015", syncql.NewErrLocationConversionError(db.GetContext(), 72, errors.New("unknown time zone ABC"))},
{"select v from Customer where Type(v) = \"Customer\" and YMD(v.InvoiceDate, \"ABC\") = 2015", syncql.NewErrLocationConversionError(db.GetContext(), 73, errors.New("unknown time zone ABC"))},
{"select v from Customer where Type(v) = \"Customer\" and YMDH(v.InvoiceDate, \"ABC\") = 2015", syncql.NewErrLocationConversionError(db.GetContext(), 74, errors.New("unknown time zone ABC"))},
{"select v from Customer where Type(v) = \"Customer\" and YMDHM(v.InvoiceDate, \"ABC\") = 2015", syncql.NewErrLocationConversionError(db.GetContext(), 75, errors.New("unknown time zone ABC"))},
{"select v from Customer where Type(v) = \"Customer\" and YMDHMS(v.InvoiceDate, \"ABC\") = 2015", syncql.NewErrLocationConversionError(db.GetContext(), 76, errors.New("unknown time zone ABC"))},
{"select v from Customer where Type(v) = \"Customer\" and Now(v.InvoiceDate, \"ABC\") = 2015", syncql.NewErrFunctionArgCount(db.GetContext(), 54, "Now", 0, 2)},
- {"select v from Customer where Type(v) = \"Customer\" and LowerCase(v.Name, 2) = \"smith\"", syncql.NewErrFunctionArgCount(db.GetContext(), 54, "LowerCase", 1, 2)},
- {"select v from Customer where Type(v) = \"Customer\" and UpperCase(v.Name, 2) = \"SMITH\"", syncql.NewErrFunctionArgCount(db.GetContext(), 54, "UpperCase", 1, 2)},
+ {"select v from Customer where Type(v) = \"Customer\" and Lowercase(v.Name, 2) = \"smith\"", syncql.NewErrFunctionArgCount(db.GetContext(), 54, "Lowercase", 1, 2)},
+ {"select v from Customer where Type(v) = \"Customer\" and Uppercase(v.Name, 2) = \"SMITH\"", syncql.NewErrFunctionArgCount(db.GetContext(), 54, "Uppercase", 1, 2)},
{"select Date() from Customer", syncql.NewErrFunctionArgCount(db.GetContext(), 7, "Date", 1, 0)},
{"select Y(v.InvoiceDate, \"Foo\") from Customer where Type(v) = \"Invoice\"", syncql.NewErrLocationConversionError(db.GetContext(), 24, errors.New("unknown time zone Foo"))},
+ {"select K from Customer where Type(v) = \"Invoice\"", syncql.NewErrDidYouMeanLowercaseK(db.GetContext(), 7)},
+ {"select V from Customer where Type(v) = \"Invoice\"", syncql.NewErrDidYouMeanLowercaseV(db.GetContext(), 7)},
+ {"select k from Customer where K = \"001\"", syncql.NewErrDidYouMeanLowercaseK(db.GetContext(), 29)},
+ {"select v from Customer where Type(V) = \"Invoice\"", syncql.NewErrDidYouMeanLowercaseV(db.GetContext(), 34)},
+ {"select K, V from Customer where Type(V) = \"Invoice\" and K = \"001\"", syncql.NewErrDidYouMeanLowercaseK(db.GetContext(), 7)},
}
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 250c089..cb7e40e 100644
--- a/v23/syncbase/nosql/internal/query/query_functions/query_functions.go
+++ b/v23/syncbase/nosql/internal/query/query_functions/query_functions.go
@@ -25,28 +25,35 @@
}
var functions map[string]function
+var lowercaseFunctions map[string]string // map of lowercase(funcName)->funcName
func init() {
functions = make(map[string]function)
- functions["date"] = function{[]query_parser.OperandType{query_parser.TypStr}, query_parser.TypTime, date, singleTimeArgCheck}
- functions["datetime"] = function{[]query_parser.OperandType{query_parser.TypStr}, query_parser.TypTime, dateTime, singleTimeArgCheck}
- functions["y"] = function{[]query_parser.OperandType{query_parser.TypStr, query_parser.TypStr}, query_parser.TypTime, y, timeAndStringArgsCheck}
- functions["ym"] = function{[]query_parser.OperandType{query_parser.TypStr, query_parser.TypStr}, query_parser.TypTime, ym, timeAndStringArgsCheck}
- functions["ymd"] = function{[]query_parser.OperandType{query_parser.TypStr, query_parser.TypStr}, query_parser.TypTime, ymd, timeAndStringArgsCheck}
- functions["ymdh"] = function{[]query_parser.OperandType{query_parser.TypStr, query_parser.TypStr}, query_parser.TypTime, ymdh, timeAndStringArgsCheck}
- functions["ymdhm"] = function{[]query_parser.OperandType{query_parser.TypStr, query_parser.TypStr}, query_parser.TypTime, ymdhm, timeAndStringArgsCheck}
- functions["ymdhms"] = function{[]query_parser.OperandType{query_parser.TypStr, query_parser.TypStr}, query_parser.TypTime, ymdhms, timeAndStringArgsCheck}
- functions["now"] = function{[]query_parser.OperandType{}, query_parser.TypTime, now, nil}
+ functions["Date"] = function{[]query_parser.OperandType{query_parser.TypStr}, query_parser.TypTime, date, singleTimeArgCheck}
+ functions["DateTime"] = function{[]query_parser.OperandType{query_parser.TypStr}, query_parser.TypTime, dateTime, singleTimeArgCheck}
+ functions["Y"] = function{[]query_parser.OperandType{query_parser.TypStr, query_parser.TypStr}, query_parser.TypTime, y, timeAndStringArgsCheck}
+ functions["YM"] = function{[]query_parser.OperandType{query_parser.TypStr, query_parser.TypStr}, query_parser.TypTime, ym, timeAndStringArgsCheck}
+ functions["YMD"] = function{[]query_parser.OperandType{query_parser.TypStr, query_parser.TypStr}, query_parser.TypTime, ymd, timeAndStringArgsCheck}
+ functions["YMDH"] = function{[]query_parser.OperandType{query_parser.TypStr, query_parser.TypStr}, query_parser.TypTime, ymdh, timeAndStringArgsCheck}
+ functions["YMDHM"] = function{[]query_parser.OperandType{query_parser.TypStr, query_parser.TypStr}, query_parser.TypTime, ymdhm, timeAndStringArgsCheck}
+ functions["YMDHMS"] = function{[]query_parser.OperandType{query_parser.TypStr, query_parser.TypStr}, query_parser.TypTime, ymdhms, timeAndStringArgsCheck}
+ functions["Now"] = function{[]query_parser.OperandType{}, query_parser.TypTime, now, nil}
- functions["lowercase"] = function{[]query_parser.OperandType{query_parser.TypStr}, query_parser.TypStr, lowerCase, singleStringArgCheck}
- functions["split"] = function{[]query_parser.OperandType{query_parser.TypStr, query_parser.TypStr}, query_parser.TypObject, split, twoStringArgsCheck}
- functions["type"] = function{[]query_parser.OperandType{query_parser.TypObject}, query_parser.TypStr, typeFunc, singleFieldArgCheck}
- functions["uppercase"] = function{[]query_parser.OperandType{query_parser.TypStr}, query_parser.TypStr, upperCase, singleStringArgCheck}
+ functions["Lowercase"] = function{[]query_parser.OperandType{query_parser.TypStr}, query_parser.TypStr, lowerCase, singleStringArgCheck}
+ functions["Split"] = function{[]query_parser.OperandType{query_parser.TypStr, query_parser.TypStr}, query_parser.TypObject, split, twoStringArgsCheck}
+ functions["Type"] = function{[]query_parser.OperandType{query_parser.TypObject}, query_parser.TypStr, typeFunc, singleFieldArgCheck}
+ functions["Uppercase"] = function{[]query_parser.OperandType{query_parser.TypStr}, query_parser.TypStr, upperCase, singleStringArgCheck}
- functions["complex"] = function{[]query_parser.OperandType{query_parser.TypFloat, query_parser.TypFloat}, query_parser.TypComplex, complexFunc, twoFloatsArgsCheck}
+ functions["Complex"] = function{[]query_parser.OperandType{query_parser.TypFloat, query_parser.TypFloat}, query_parser.TypComplex, complexFunc, twoFloatsArgsCheck}
- functions["len"] = function{[]query_parser.OperandType{query_parser.TypObject}, query_parser.TypInt, lenFunc, nil}
+ functions["Len"] = function{[]query_parser.OperandType{query_parser.TypObject}, query_parser.TypInt, lenFunc, nil}
+
+ // Build lowercaseFuncName->funcName
+ lowercaseFunctions = make(map[string]string)
+ for f := range functions {
+ lowercaseFunctions[strings.ToLower(f)] = f
+ }
}
// Check that function exists and that the number of args passed matches the spec.
@@ -57,8 +64,8 @@
// early). CheckFunction will fill in arg types, return types and may fill in
// Computed and RetValue.
func CheckFunction(db query_db.Database, f *query_parser.Function) error {
- if entry, ok := functions[strings.ToLower(f.Name)]; !ok {
- return syncql.NewErrFunctionNotFound(db.GetContext(), f.Off, f.Name)
+ if entry, err := lookupFuncName(db, f); err != nil {
+ return err
} else {
f.ArgTypes = entry.argTypes
f.RetType = entry.returnType
@@ -101,9 +108,23 @@
}
}
+func lookupFuncName(db query_db.Database, f *query_parser.Function) (*function, error) {
+ if entry, ok := functions[f.Name]; !ok {
+ // No such function, is the case wrong?
+ if correctCase, ok := lowercaseFunctions[strings.ToLower(f.Name)]; !ok {
+ return nil, syncql.NewErrFunctionNotFound(db.GetContext(), f.Off, f.Name)
+ } else {
+ // the case is wrong
+ return nil, syncql.NewErrDidYouMeanFunction(db.GetContext(), f.Off, correctCase)
+ }
+ } else {
+ return &entry, nil
+ }
+}
+
func FuncCheck(db query_db.Database, f *query_parser.Function, args []*query_parser.Operand) error {
- if entry, ok := functions[strings.ToLower(f.Name)]; !ok {
- return syncql.NewErrFunctionNotFound(db.GetContext(), f.Off, f.Name)
+ if entry, err := lookupFuncName(db, f); err != nil {
+ return err
} else {
if entry.checkArgsAddr != nil {
if err := entry.checkArgsAddr(db, f.Off, args); err != nil {
@@ -115,8 +136,8 @@
}
func ExecFunction(db query_db.Database, f *query_parser.Function, args []*query_parser.Operand) (*query_parser.Operand, error) {
- if entry, ok := functions[strings.ToLower(f.Name)]; !ok {
- return nil, syncql.NewErrFunctionNotFound(db.GetContext(), f.Off, f.Name)
+ if entry, err := lookupFuncName(db, f); err != nil {
+ return nil, err
} else {
retValue, err := entry.funcAddr(db, f.Off, args)
if err != nil {
@@ -194,7 +215,7 @@
func singleFieldArgCheck(db query_db.Database, off int64, args []*query_parser.Operand) error {
// single argument must be of type field
// It must begin with a v segment.
- if args[0].Type != query_parser.TypField || len(args[0].Column.Segments) < 1 || strings.ToLower(args[0].Column.Segments[0].Value) != "v" {
+ if args[0].Type != query_parser.TypField || len(args[0].Column.Segments) < 1 || args[0].Column.Segments[0].Value != "v" {
return syncql.NewErrArgMustBeField(db.GetContext(), args[0].Off)
}
return nil
diff --git a/v23/syncbase/nosql/internal/query/query_functions/query_functions_test.go b/v23/syncbase/nosql/internal/query/query_functions/query_functions_test.go
index fc5eae7..549cb9a 100644
--- a/v23/syncbase/nosql/internal/query/query_functions/query_functions_test.go
+++ b/v23/syncbase/nosql/internal/query/query_functions/query_functions_test.go
@@ -13,9 +13,11 @@
"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/query_db"
+ "v.io/syncbase/v23/syncbase/nosql/syncql"
"v.io/v23"
"v.io/v23/context"
"v.io/v23/vdl"
+ "v.io/v23/verror"
_ "v.io/x/ref/runtime/factories/generic"
"v.io/x/ref/test"
)
@@ -49,6 +51,12 @@
result *query_parser.Operand
}
+type functionsErrorTest struct {
+ f *query_parser.Function
+ args []*query_parser.Operand
+ err error
+}
+
var t_2015 time.Time
var t_2015_06 time.Time
var t_2015_06_21 time.Time
@@ -346,10 +354,10 @@
Time: t_2015_06_21_01_23_45,
},
},
- // LowerCase
+ // Lowercase
functionsTest{
&query_parser.Function{
- Name: "LowerCase",
+ Name: "Lowercase",
Args: []*query_parser.Operand{
&query_parser.Operand{
Type: query_parser.TypStr,
@@ -374,10 +382,10 @@
Str: "foobar",
},
},
- // UpperCase
+ // Uppercase
functionsTest{
&query_parser.Function{
- Name: "UpperCase",
+ Name: "Uppercase",
Args: []*query_parser.Operand{
&query_parser.Operand{
Type: query_parser.TypStr,
@@ -561,3 +569,41 @@
}
}
}
+
+func TestErrorFunctions(t *testing.T) {
+ tests := []functionsErrorTest{
+ // date
+ functionsErrorTest{
+ &query_parser.Function{
+ Name: "date",
+ Args: []*query_parser.Operand{
+ &query_parser.Operand{
+ Type: query_parser.TypStr,
+ Str: "2015-06-21 PDT",
+ },
+ },
+ ArgTypes: []query_parser.OperandType{
+ query_parser.TypStr,
+ },
+ RetType: query_parser.TypTime,
+ Computed: false,
+ RetValue: nil,
+ Node: query_parser.Node{Off: 42},
+ },
+ []*query_parser.Operand{
+ &query_parser.Operand{
+ Type: query_parser.TypStr,
+ Str: "2015-06-21 PDT",
+ },
+ },
+ syncql.NewErrDidYouMeanFunction(db.GetContext(), int64(42), "Date"),
+ },
+ }
+
+ for _, test := range tests {
+ _, err := query_functions.ExecFunction(&db, test.f, test.args)
+ if verror.ErrorID(err) != verror.ErrorID(test.err) || err.Error() != test.err.Error() {
+ t.Errorf("function: %v; got %v, want %v", test.f, err, test.err)
+ }
+ }
+}
diff --git a/v23/syncbase/nosql/internal/query/query_parser/doc.go b/v23/syncbase/nosql/internal/query/query_parser/doc.go
index 488ef23..c16c6bf 100644
--- a/v23/syncbase/nosql/internal/query/query_parser/doc.go
+++ b/v23/syncbase/nosql/internal/query/query_parser/doc.go
@@ -23,8 +23,8 @@
// <selector> ::= <column> [AS <string_literal>]
//
// <column> ::=
-// K
-// | V[<period><field>]
+// k
+// | v[<period><field>]
// | <function>
//
// <field> ::= <segment>[{<period><segment>}...]
@@ -53,11 +53,11 @@
//
// <binary_expression> ::=
// <operand> <binary_op> <operand>
-// | V[<period><field>] IS [NOT] NIL
+// | v[<period><field>] IS [NOT] NIL
//
// <operand> ::=
-// K
-// | V[<period><field>]
+// k
+// | v[<period><field>]
// | <literal>
// | <function>
//
diff --git a/v23/syncbase/nosql/internal/query/test/query_test.go b/v23/syncbase/nosql/internal/query/test/query_test.go
index 3c5464d..61c320a 100644
--- a/v23/syncbase/nosql/internal/query/test/query_test.go
+++ b/v23/syncbase/nosql/internal/query/test/query_test.go
@@ -1159,8 +1159,8 @@
},
// Test string functions in where clause.
{
- // Select invoices shipped to Any street -- using LowerCase.
- "select k from Customer where Type(v) like \"%.Invoice\" and LowerCase(v.ShipTo.Street) like \"%any%\"",
+ // Select invoices shipped to Any street -- using Lowercase.
+ "select k from Customer where Type(v) like \"%.Invoice\" and Lowercase(v.ShipTo.Street) like \"%any%\"",
[]string{"k"},
[][]*vdl.Value{
[]*vdl.Value{vdl.ValueOf(custTable.rows[5].key)},
@@ -1170,8 +1170,8 @@
},
},
{
- // Select invoices shipped to Any street -- using UpperCase.
- "select k from Customer where Type(v) like \"%.Invoice\" and UpperCase(v.ShipTo.Street) like \"%ANY%\"",
+ // Select invoices shipped to Any street -- using Uppercase.
+ "select k from Customer where Type(v) like \"%.Invoice\" and Uppercase(v.ShipTo.Street) like \"%ANY%\"",
[]string{"k"},
[][]*vdl.Value{
[]*vdl.Value{vdl.ValueOf(custTable.rows[5].key)},
@@ -1215,9 +1215,9 @@
[]*vdl.Value{vdl.ValueOf(t2015_07_01_01_23_45)},
},
},
- // LowerCase function
+ // Lowercase function
{
- "select LowerCase(v.Name) as name from Customer where Type(v) like \"%.Customer\"",
+ "select Lowercase(v.Name) as name from Customer where Type(v) like \"%.Customer\"",
[]string{"name"},
[][]*vdl.Value{
[]*vdl.Value{vdl.ValueOf("john smith")},
@@ -1225,9 +1225,9 @@
[]*vdl.Value{vdl.ValueOf("john steed")},
},
},
- // UpperCase function
+ // Uppercase function
{
- "select UpperCase(v.Name) as NAME from Customer where Type(v) like \"%.Customer\"",
+ "select Uppercase(v.Name) as NAME from Customer where Type(v) like \"%.Customer\"",
[]string{"NAME"},
[][]*vdl.Value{
[]*vdl.Value{vdl.ValueOf("JOHN SMITH")},
@@ -1325,8 +1325,8 @@
},
// Map in selection using function as key.
{
- "select v.Credit.Report.TransUnionReport.PreviousRatings[UpperCase(\"2015q2\")] from Customer where v.Name = \"Bat Masterson\"",
- []string{"v.Credit.Report.TransUnionReport.PreviousRatings[UpperCase]"},
+ "select v.Credit.Report.TransUnionReport.PreviousRatings[Uppercase(\"2015q2\")] from Customer where v.Name = \"Bat Masterson\"",
+ []string{"v.Credit.Report.TransUnionReport.PreviousRatings[Uppercase]"},
[][]*vdl.Value{
[]*vdl.Value{vdl.ValueOf(int16(40))},
},
@@ -1351,8 +1351,8 @@
},
// Function using a map lookup as arg
{
- "select UpperCase(v.B[true]) from ManyMaps",
- []string{"UpperCase"},
+ "select Uppercase(v.B[true]) from ManyMaps",
+ []string{"Uppercase"},
[][]*vdl.Value{
[]*vdl.Value{vdl.ValueOf("IT WAS THE BEST OF TIMES,")},
},
@@ -1823,7 +1823,7 @@
svPairs(100, 300),
},
{
- "select k, v.Key from BigTable where V.Key = K",
+ "select k, v.Key from BigTable where v.Key = k",
[]string{"k", "v.Key"},
svPairs(100, 300),
},
@@ -1858,8 +1858,8 @@
[][]*vdl.Value{},
},
{
- "select K, V.Key from BigTable where k >= v.Key",
- []string{"K", "V.Key"},
+ "select k, v.Key from BigTable where k >= v.Key",
+ []string{"k", "v.Key"},
svPairs(100, 300),
},
{
@@ -1868,8 +1868,8 @@
svPairs(100, 300),
},
{
- "select K, V.Key from BigTable where k <= v.Key",
- []string{"K", "V.Key"},
+ "select k, v.Key from BigTable where k <= v.Key",
+ []string{"k", "v.Key"},
svPairs(100, 300),
},
{
@@ -2708,6 +2708,94 @@
"select v from Customer where Len(10) = 3",
syncql.NewErrFunctionLenInvalidArg(db.GetContext(), 33),
},
+ {
+ "select K from Customer where Type(v) = \"Invoice\"",
+ syncql.NewErrDidYouMeanLowercaseK(db.GetContext(), 7),
+ },
+ {
+ "select V from Customer where Type(v) = \"Invoice\"",
+ syncql.NewErrDidYouMeanLowercaseV(db.GetContext(), 7),
+ },
+ {
+ "select k from Customer where K = \"001\"",
+ syncql.NewErrDidYouMeanLowercaseK(db.GetContext(), 29),
+ },
+ {
+ "select v from Customer where Type(V) = \"Invoice\"",
+ syncql.NewErrDidYouMeanLowercaseV(db.GetContext(), 34),
+ },
+ {
+ "select K, V from Customer where Type(V) = \"Invoice\" and K = \"001\"",
+ syncql.NewErrDidYouMeanLowercaseK(db.GetContext(), 7),
+ },
+ {
+ "select type(v) from Customer where Type(v) = \"Invoice\"",
+ syncql.NewErrDidYouMeanFunction(db.GetContext(), 7, "Type"),
+ },
+ {
+ "select Type(v) from Customer where type(v) = \"Invoice\"",
+ syncql.NewErrDidYouMeanFunction(db.GetContext(), 35, "Type"),
+ },
+ {
+ "select type(v) from Customer where type(v) = \"Invoice\"",
+ syncql.NewErrDidYouMeanFunction(db.GetContext(), 7, "Type"),
+ },
+ {
+ "select date(\"foo\") from Customer",
+ syncql.NewErrDidYouMeanFunction(db.GetContext(), 7, "Date"),
+ },
+ {
+ "select Datetime(\"foo\") from Customer",
+ syncql.NewErrDidYouMeanFunction(db.GetContext(), 7, "DateTime"),
+ },
+ {
+ "select y(\"foo\") from Customer",
+ syncql.NewErrDidYouMeanFunction(db.GetContext(), 7, "Y"),
+ },
+ {
+ "select ym(\"foo\") from Customer",
+ syncql.NewErrDidYouMeanFunction(db.GetContext(), 7, "YM"),
+ },
+ {
+ "select ymd(\"foo\") from Customer",
+ syncql.NewErrDidYouMeanFunction(db.GetContext(), 7, "YMD"),
+ },
+ {
+ "select ymdh(\"foo\") from Customer",
+ syncql.NewErrDidYouMeanFunction(db.GetContext(), 7, "YMDH"),
+ },
+ {
+ "select ymdhm(\"foo\") from Customer",
+ syncql.NewErrDidYouMeanFunction(db.GetContext(), 7, "YMDHM"),
+ },
+ {
+ "select ymdhms(\"foo\") from Customer",
+ syncql.NewErrDidYouMeanFunction(db.GetContext(), 7, "YMDHMS"),
+ },
+ {
+ "select now() from Customer",
+ syncql.NewErrDidYouMeanFunction(db.GetContext(), 7, "Now"),
+ },
+ {
+ "select LowerCase(\"foo\") from Customer",
+ syncql.NewErrDidYouMeanFunction(db.GetContext(), 7, "Lowercase"),
+ },
+ {
+ "select UPPERCASE(\"foo\") from Customer",
+ syncql.NewErrDidYouMeanFunction(db.GetContext(), 7, "Uppercase"),
+ },
+ {
+ "select spliT(\"foo:bar\", \":\") from Customer",
+ syncql.NewErrDidYouMeanFunction(db.GetContext(), 7, "Split"),
+ },
+ {
+ "select comPLex(1.0, 2.0) from Customer",
+ syncql.NewErrDidYouMeanFunction(db.GetContext(), 7, "Complex"),
+ },
+ {
+ "select len(\"foo\") from Customer",
+ syncql.NewErrDidYouMeanFunction(db.GetContext(), 7, "Len"),
+ },
}
for _, test := range basic {
diff --git a/v23/syncbase/nosql/syncql/syncql.vdl b/v23/syncbase/nosql/syncql/syncql.vdl
index 810c3e0..4fda53f 100644
--- a/v23/syncbase/nosql/syncql/syncql.vdl
+++ b/v23/syncbase/nosql/syncql/syncql.vdl
@@ -118,4 +118,13 @@
UnknownIdentifier(off int64, found string) {
"en": "[{off}]Uknown identifier: {found}.",
}
+ DidYouMeanLowercaseK(off int64) {
+ "en": "[{off}]Did you mean: 'k'?",
+ }
+ DidYouMeanLowercaseV(off int64) {
+ "en": "[{off}]Did you mean: 'v'?",
+ }
+ DidYouMeanFunction(off int64, correctName string) {
+ "en": "[{off}]Did you mean: '{correctName}'?",
+ }
)
diff --git a/v23/syncbase/nosql/syncql/syncql.vdl.go b/v23/syncbase/nosql/syncql/syncql.vdl.go
index 1d4e2b4..0b5babc 100644
--- a/v23/syncbase/nosql/syncql/syncql.vdl.go
+++ b/v23/syncbase/nosql/syncql/syncql.vdl.go
@@ -52,6 +52,9 @@
ErrUnexpected = verror.Register("v.io/syncbase/v23/syncbase/nosql/syncql.Unexpected", verror.NoRetry, "{1:}{2:} [{3}]Unexpected: {4}.")
ErrUnexpectedEndOfStatement = verror.Register("v.io/syncbase/v23/syncbase/nosql/syncql.UnexpectedEndOfStatement", verror.NoRetry, "{1:}{2:} [{3}]No statement found.")
ErrUnknownIdentifier = verror.Register("v.io/syncbase/v23/syncbase/nosql/syncql.UnknownIdentifier", verror.NoRetry, "{1:}{2:} [{3}]Uknown identifier: {4}.")
+ ErrDidYouMeanLowercaseK = verror.Register("v.io/syncbase/v23/syncbase/nosql/syncql.DidYouMeanLowercaseK", verror.NoRetry, "{1:}{2:} [{3}]Did you mean: 'k'?")
+ ErrDidYouMeanLowercaseV = verror.Register("v.io/syncbase/v23/syncbase/nosql/syncql.DidYouMeanLowercaseV", verror.NoRetry, "{1:}{2:} [{3}]Did you mean: 'v'?")
+ ErrDidYouMeanFunction = verror.Register("v.io/syncbase/v23/syncbase/nosql/syncql.DidYouMeanFunction", verror.NoRetry, "{1:}{2:} [{3}]Did you mean: '{4}'?")
)
func init() {
@@ -92,6 +95,9 @@
i18n.Cat().SetWithBase(i18n.LangID("en"), i18n.MsgID(ErrUnexpected.ID), "{1:}{2:} [{3}]Unexpected: {4}.")
i18n.Cat().SetWithBase(i18n.LangID("en"), i18n.MsgID(ErrUnexpectedEndOfStatement.ID), "{1:}{2:} [{3}]No statement found.")
i18n.Cat().SetWithBase(i18n.LangID("en"), i18n.MsgID(ErrUnknownIdentifier.ID), "{1:}{2:} [{3}]Uknown identifier: {4}.")
+ i18n.Cat().SetWithBase(i18n.LangID("en"), i18n.MsgID(ErrDidYouMeanLowercaseK.ID), "{1:}{2:} [{3}]Did you mean: 'k'?")
+ i18n.Cat().SetWithBase(i18n.LangID("en"), i18n.MsgID(ErrDidYouMeanLowercaseV.ID), "{1:}{2:} [{3}]Did you mean: 'v'?")
+ i18n.Cat().SetWithBase(i18n.LangID("en"), i18n.MsgID(ErrDidYouMeanFunction.ID), "{1:}{2:} [{3}]Did you mean: '{4}'?")
}
// NewErrBadFieldInWhere returns an error with the ErrBadFieldInWhere ID.
@@ -278,3 +284,18 @@
func NewErrUnknownIdentifier(ctx *context.T, off int64, found string) error {
return verror.New(ErrUnknownIdentifier, ctx, off, found)
}
+
+// NewErrDidYouMeanLowercaseK returns an error with the ErrDidYouMeanLowercaseK ID.
+func NewErrDidYouMeanLowercaseK(ctx *context.T, off int64) error {
+ return verror.New(ErrDidYouMeanLowercaseK, ctx, off)
+}
+
+// NewErrDidYouMeanLowercaseV returns an error with the ErrDidYouMeanLowercaseV ID.
+func NewErrDidYouMeanLowercaseV(ctx *context.T, off int64) error {
+ return verror.New(ErrDidYouMeanLowercaseV, ctx, off)
+}
+
+// NewErrDidYouMeanFunction returns an error with the ErrDidYouMeanFunction ID.
+func NewErrDidYouMeanFunction(ctx *context.T, off int64, correctName string) error {
+ return verror.New(ErrDidYouMeanFunction, ctx, off, correctName)
+}