diff --git a/spanner/spansql/parser.go b/spanner/spansql/parser.go index 0f6f571c4bd..8b245aeaf82 100644 --- a/spanner/spansql/parser.go +++ b/spanner/spansql/parser.go @@ -3038,6 +3038,9 @@ func (p *parser) parseLit() (Expr, *parseError) { case tok.caseEqual("CASE"): p.back() return p.parseCaseExpr() + case tok.caseEqual("COALESCE"): + p.back() + return p.parseCoalesceExpr() case tok.caseEqual("IF"): p.back() return p.parseIfExpr() @@ -3157,6 +3160,17 @@ func (p *parser) parseWhenClause() (WhenClause, *parseError) { return WhenClause{Cond: cond, Result: result}, nil } +func (p *parser) parseCoalesceExpr() (Coalesce, *parseError) { + if err := p.expect("COALESCE"); err != nil { + return Coalesce{}, err + } + exprList, err := p.parseParenExprList() + if err != nil { + return Coalesce{}, err + } + return Coalesce{ExprList: exprList}, nil +} + func (p *parser) parseIfExpr() (If, *parseError) { if err := p.expect("IF", "("); err != nil { return If{}, err diff --git a/spanner/spansql/parser_test.go b/spanner/spansql/parser_test.go index bcc27eceae4..69d8d08a4f7 100644 --- a/spanner/spansql/parser_test.go +++ b/spanner/spansql/parser_test.go @@ -433,6 +433,9 @@ func TestParseExpr(t *testing.T) { }, }, }, + {`COALESCE(NULL, "B", "C")`, + Coalesce{ExprList: []Expr{Null, StringLiteral("B"), StringLiteral("C")}}, + }, {`IF(A < B, TRUE, FALSE)`, If{ Expr: ComparisonOp{LHS: ID("A"), Op: Lt, RHS: ID("B")}, diff --git a/spanner/spansql/sql.go b/spanner/spansql/sql.go index f9587ae8d55..2afda836e79 100644 --- a/spanner/spansql/sql.go +++ b/spanner/spansql/sql.go @@ -733,6 +733,18 @@ func (c Case) addSQL(sb *strings.Builder) { sb.WriteString("END") } +func (c Coalesce) SQL() string { return buildSQL(c) } +func (c Coalesce) addSQL(sb *strings.Builder) { + sb.WriteString("COALESCE(") + for i, expr := range c.ExprList { + if i > 0 { + sb.WriteString(", ") + } + expr.addSQL(sb) + } + sb.WriteString(")") +} + func (i If) SQL() string { return buildSQL(i) } func (i If) addSQL(sb *strings.Builder) { sb.WriteString("IF(") diff --git a/spanner/spansql/sql_test.go b/spanner/spansql/sql_test.go index f68f33754ef..8a0ae827ac9 100644 --- a/spanner/spansql/sql_test.go +++ b/spanner/spansql/sql_test.go @@ -699,6 +699,23 @@ func TestSQL(t *testing.T) { `SELECT NULLIF(10, 0)`, reparseQuery, }, + { + Query{ + Select: Select{ + List: []Expr{ + Coalesce{ + ExprList: []Expr{ + StringLiteral("A"), + Null, + StringLiteral("C"), + }, + }, + }, + }, + }, + `SELECT COALESCE("A", NULL, "C")`, + reparseQuery, + }, } for _, test := range tests { sql := test.data.SQL() diff --git a/spanner/spansql/types.go b/spanner/spansql/types.go index 87c681c7d25..2469bccd750 100644 --- a/spanner/spansql/types.go +++ b/spanner/spansql/types.go @@ -743,6 +743,13 @@ type WhenClause struct { Result Expr } +type Coalesce struct { + ExprList []Expr +} + +func (Coalesce) isBoolExpr() {} // possibly bool +func (Coalesce) isExpr() {} + type If struct { Expr Expr TrueResult Expr