From 5b7bfebcd4a0fd3cbe355d9d290e6b5101810b7e Mon Sep 17 00:00:00 2001 From: neglect-yp Date: Thu, 28 Jul 2022 23:58:15 +0900 Subject: [PATCH] feat(spanner/spansql): add support for nullif expressions (#6423) Co-authored-by: rahul2393 --- spanner/spansql/parser.go | 27 +++++++++++++++++++++++++++ spanner/spansql/parser_test.go | 6 ++++++ spanner/spansql/sql.go | 9 +++++++++ spanner/spansql/sql_test.go | 14 ++++++++++++++ spanner/spansql/types.go | 8 ++++++++ 5 files changed, 64 insertions(+) diff --git a/spanner/spansql/parser.go b/spanner/spansql/parser.go index a68e5b1f6f0a..0f6f571c4bde 100644 --- a/spanner/spansql/parser.go +++ b/spanner/spansql/parser.go @@ -3044,6 +3044,9 @@ func (p *parser) parseLit() (Expr, *parseError) { case tok.caseEqual("IFNULL"): p.back() return p.parseIfNullExpr() + case tok.caseEqual("NULLIF"): + p.back() + return p.parseNullIfExpr() } // Handle typed literals. @@ -3210,6 +3213,30 @@ func (p *parser) parseIfNullExpr() (IfNull, *parseError) { return IfNull{Expr: expr, NullResult: nullResult}, nil } +func (p *parser) parseNullIfExpr() (NullIf, *parseError) { + if err := p.expect("NULLIF", "("); err != nil { + return NullIf{}, err + } + + expr, err := p.parseExpr() + if err != nil { + return NullIf{}, err + } + if err := p.expect(","); err != nil { + return NullIf{}, err + } + + exprToMatch, err := p.parseExpr() + if err != nil { + return NullIf{}, err + } + if err := p.expect(")"); err != nil { + return NullIf{}, err + } + + return NullIf{Expr: expr, ExprToMatch: exprToMatch}, nil +} + func (p *parser) parseArrayLit() (Array, *parseError) { // ARRAY keyword is optional. // TODO: If it is present, consume any after it. diff --git a/spanner/spansql/parser_test.go b/spanner/spansql/parser_test.go index 4837df40f22c..bcc27eceae42 100644 --- a/spanner/spansql/parser_test.go +++ b/spanner/spansql/parser_test.go @@ -446,6 +446,12 @@ func TestParseExpr(t *testing.T) { NullResult: True, }, }, + {`NULLIF("a", "b")`, + NullIf{ + Expr: StringLiteral("a"), + ExprToMatch: StringLiteral("b"), + }, + }, // String literal: // Accept double quote and single quote. diff --git a/spanner/spansql/sql.go b/spanner/spansql/sql.go index 23573ee2f5fb..f9587ae8d55b 100644 --- a/spanner/spansql/sql.go +++ b/spanner/spansql/sql.go @@ -753,6 +753,15 @@ func (in IfNull) addSQL(sb *strings.Builder) { sb.WriteString(")") } +func (ni NullIf) SQL() string { return buildSQL(ni) } +func (ni NullIf) addSQL(sb *strings.Builder) { + sb.WriteString("NULLIF(") + ni.Expr.addSQL(sb) + sb.WriteString(", ") + ni.ExprToMatch.addSQL(sb) + sb.WriteString(")") +} + func (b BoolLiteral) SQL() string { return buildSQL(b) } func (b BoolLiteral) addSQL(sb *strings.Builder) { if b { diff --git a/spanner/spansql/sql_test.go b/spanner/spansql/sql_test.go index 8e09e47cd15c..f68f33754ef5 100644 --- a/spanner/spansql/sql_test.go +++ b/spanner/spansql/sql_test.go @@ -685,6 +685,20 @@ func TestSQL(t *testing.T) { `SELECT IFNULL(10, 0)`, reparseQuery, }, + { + Query{ + Select: Select{ + List: []Expr{ + NullIf{ + Expr: IntegerLiteral(10), + ExprToMatch: IntegerLiteral(0), + }, + }, + }, + }, + `SELECT NULLIF(10, 0)`, + reparseQuery, + }, } for _, test := range tests { sql := test.data.SQL() diff --git a/spanner/spansql/types.go b/spanner/spansql/types.go index 7df108c63202..87c681c7d259 100644 --- a/spanner/spansql/types.go +++ b/spanner/spansql/types.go @@ -760,6 +760,14 @@ type IfNull struct { func (IfNull) isBoolExpr() {} // possibly bool func (IfNull) isExpr() {} +type NullIf struct { + Expr Expr + ExprToMatch Expr +} + +func (NullIf) isBoolExpr() {} // possibly bool +func (NullIf) isExpr() {} + type BoolLiteral bool const (