diff --git a/pkg/ql/ast_nodes.go b/pkg/ql/ast_nodes.go index f9e8bac19..d7a6499d1 100644 --- a/pkg/ql/ast_nodes.go +++ b/pkg/ql/ast_nodes.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/Masterminds/squirrel" + "strings" ) // SelectStatement represents a SQL SELECT statement. @@ -61,6 +62,29 @@ type ( } ) +var ( + operators = map[string]bool{ + `=`: true, + `!=`: true, + `<`: true, + `>`: true, + `>=`: true, + `<=`: true, + `<>`: true, + `+`: true, + `-`: true, + `*`: true, + `/`: true, + `AND`: true, + `OR`: true, + `XOR`: true, + `NOT LIKE`: true, + `LIKE`: true, + `IS`: true, + `IS NOT`: true, + } +) + func (n LNull) Validate() (err error) { return } func (n LNull) String() string { return "NULL" } @@ -79,8 +103,13 @@ func (n LString) String() string { return fmt.Sprintf("%q", n.Value) } func (n LNumber) Validate() (err error) { return } func (n LNumber) String() string { return n.Value } -func (n Operator) Validate() (err error) { return } -func (n Operator) String() string { return n.Kind } +func (n Operator) Validate() (err error) { + if !operators[strings.ToUpper(n.Kind)] { + return fmt.Errorf("unknown operator '%s'", n.Kind) + } + return +} +func (n Operator) String() string { return n.Kind } func (n Keyword) Validate() (err error) { return } func (n Keyword) String() string { return n.Keyword } @@ -94,7 +123,10 @@ func (n Function) String() string { return fmt.Sprintf("%s(%s)", n.Name, func (n Ident) Validate() (err error) { return } func (n Ident) String() string { return n.Value } -func (n Column) Validate() (err error) { return } +func (n Column) Validate() (err error) { + return n.Expr.Validate() +} + func (n Column) String() (out string) { out = n.Expr.String() if n.Alias != "" { @@ -150,7 +182,15 @@ func (nn ASTSet) String() (out string) { return } -func (nn Columns) Validate() (err error) { return } +func (nn Columns) Validate() (err error) { + for _, n := range nn { + if err = n.Validate(); err != nil { + return + } + } + + return +} func (nn Columns) String() (out string) { for i, n := range nn { if i > 0 { diff --git a/pkg/ql/ast_parser_test.go b/pkg/ql/ast_parser_test.go index e85c96e45..6341d51fe 100644 --- a/pkg/ql/ast_parser_test.go +++ b/pkg/ql/ast_parser_test.go @@ -2,6 +2,7 @@ package ql import ( "fmt" + "github.com/stretchr/testify/require" "reflect" "testing" ) @@ -285,7 +286,7 @@ func TestAstParser_ColumnParser(t *testing.T) { var tests = []struct { in string cols Columns - err error + err string }{ { in: `a AS b`, @@ -358,15 +359,43 @@ func TestAstParser_ColumnParser(t *testing.T) { }, }, }, + { + in: `-- - DATE_FORMAT(some_date, '%Y-%m-01')`, + cols: Columns{ + Column{ + Expr: ASTNodes{ + Function{ + Name: "DATE_FORMAT", + Arguments: ASTSet{ + Ident{Value: "some_date"}, + LString{Value: "%Y-%m-01"}, + }, + }, + }, + }, + }, + err: "unknown operator '-- -'", + }, } - p := NewParser() - for i, test := range tests { - if cols, err := p.ParseColumns(test.in); err != test.err { - t.Fatalf("%d. %s: error mismatch:\n expected: %v\n got: %v\n\n", i, test.in, test.err, err) - } else if test.err == nil && !reflect.DeepEqual(test.cols, cols) { - t.Errorf("%d. %s\n\ncols does not match:\n\nexpected: %#v\n got: %#v\n\n", i, test.in, test.cols, cols) - } + for _, test := range tests { + t.Run(test.in, func(t *testing.T) { + var ( + p = NewParser() + req = require.New(t) + ) + + cols, err := p.ParseColumns(test.in) + if test.err == "" { + req.NoError(err) + } else { + req.Error(err, test.err) + } + + if err == nil { + req.Equal(test.cols, cols) + } + }) } } @@ -388,7 +417,6 @@ func TestAstParser_IdentModifier(t *testing.T) { ident.Value = fmt.Sprintf("__wrap_%s_wrap__", ident.Value) return ident, nil } - if tree, err := p.ParseExpression(test.in); err != test.err { t.Fatalf("%d. error mismatch:\n expected: %v\n got: %v\n\n", i, test.err, err) } else if test.err == nil && test.out != tree.String() {