From ed4acf58ca1624ba80dffa48cd51f7475a4dcfae Mon Sep 17 00:00:00 2001 From: Denis Arh Date: Tue, 16 Jul 2019 09:54:39 +0200 Subject: [PATCH] Add support for IS NULL and IS NOT NULL syntax --- compose/internal/repository/ql/ast_nodes.go | 6 +++++ compose/internal/repository/ql/ast_parser.go | 2 ++ .../internal/repository/ql/ast_parser_test.go | 27 +++++++++++++++++++ compose/internal/repository/ql/squirrel.go | 4 +++ compose/internal/repository/ql/token_codes.go | 1 + .../internal/repository/ql/token_consumers.go | 4 ++- 6 files changed, 43 insertions(+), 1 deletion(-) diff --git a/compose/internal/repository/ql/ast_nodes.go b/compose/internal/repository/ql/ast_nodes.go index a1d47aac8..c54c84623 100644 --- a/compose/internal/repository/ql/ast_nodes.go +++ b/compose/internal/repository/ql/ast_nodes.go @@ -20,6 +20,8 @@ type ( Columns []Column + Null struct{} + String struct { Value string Args []interface{} @@ -51,12 +53,16 @@ type ( Expr ASTNodes Alias string } + Function struct { Name string Arguments ASTSet } ) +func (n Null) Validate() (err error) { return } +func (n Null) String() string { return "NULL" } + func (n String) Validate() (err error) { return } func (n String) String() string { return fmt.Sprintf("%q", n.Value) } diff --git a/compose/internal/repository/ql/ast_parser.go b/compose/internal/repository/ql/ast_parser.go index 81b499cd7..4d177d457 100644 --- a/compose/internal/repository/ql/ast_parser.go +++ b/compose/internal/repository/ql/ast_parser.go @@ -166,6 +166,8 @@ checkToken: list = append(list, ident) goto next } + case NULL: + list = append(list, Null{}) case OPERATOR: if len(list) > 0 { // Merge with previous operator node diff --git a/compose/internal/repository/ql/ast_parser_test.go b/compose/internal/repository/ql/ast_parser_test.go index 2ec478598..71007aaa7 100644 --- a/compose/internal/repository/ql/ast_parser_test.go +++ b/compose/internal/repository/ql/ast_parser_test.go @@ -156,6 +156,33 @@ func TestAstParser_Parser(t *testing.T) { String{Value: "bar%"}, }, }, + { + parser: NewParser().ParseExpression, + in: `foo = NULL`, + tree: ASTNodes{ + Ident{Value: "foo"}, + Operator{Kind: "="}, + Null{}, + }, + }, + { + parser: NewParser().ParseExpression, + in: `foo IS NOT NULL`, + tree: ASTNodes{ + Ident{Value: "foo"}, + Operator{Kind: "IS NOT"}, + Null{}, + }, + }, + { + parser: NewParser().ParseExpression, + in: `foo IS NULL`, + tree: ASTNodes{ + Ident{Value: "foo"}, + Operator{Kind: "IS"}, + Null{}, + }, + }, } for i, test := range tests { diff --git a/compose/internal/repository/ql/squirrel.go b/compose/internal/repository/ql/squirrel.go index 3d7c5a82c..9d4375c8e 100644 --- a/compose/internal/repository/ql/squirrel.go +++ b/compose/internal/repository/ql/squirrel.go @@ -62,6 +62,10 @@ func (n Ident) ToSql() (string, []interface{}, error) { return n.Value, n.Args, nil } +func (n Null) ToSql() (string, []interface{}, error) { + return "NULL", nil, nil +} + func (n Function) ToSql() (string, []interface{}, error) { if paramsSql, args, err := n.Arguments.ToSql(); err != nil { return "", nil, err diff --git a/compose/internal/repository/ql/token_codes.go b/compose/internal/repository/ql/token_codes.go index b1d996cc5..8ea28b1da 100644 --- a/compose/internal/repository/ql/token_codes.go +++ b/compose/internal/repository/ql/token_codes.go @@ -23,4 +23,5 @@ const ( PARENTHESIS_OPEN PARENTHESIS_CLOSE KEYWORD + NULL ) diff --git a/compose/internal/repository/ql/token_consumers.go b/compose/internal/repository/ql/token_consumers.go index c48acc940..bc4d4cb48 100644 --- a/compose/internal/repository/ql/token_consumers.go +++ b/compose/internal/repository/ql/token_consumers.go @@ -85,7 +85,9 @@ func (TokenConsumerIdent) Consume(s RuneReader) Token { lit := strings.ToUpper(buf.String()) switch lit { - case "LIKE", "NOT", "AND", "OR", "XOR": + case "NULL": + return Token{code: NULL} + case "IS", "LIKE", "NOT", "AND", "OR", "XOR": return Token{code: OPERATOR, literal: lit} case "DESC", "ASC", "INTERVAL": return Token{code: KEYWORD, literal: lit}