Skip to content

Commit f5cb67b

Browse files
ngalaikorahul2393
andauthored
feat(spanner/spansql): support EXISTS in query parsing (#12439)
Co-authored-by: rahul2393 <[email protected]>
1 parent 770daf7 commit f5cb67b

File tree

5 files changed

+91
-1
lines changed

5 files changed

+91
-1
lines changed

spanner/spansql/parser.go

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4310,6 +4310,7 @@ ascending order of precedence:
43104310
parseExpr
43114311
orParser
43124312
andParser
4313+
parseExistsOp
43134314
parseIsOp
43144315
parseInOp
43154316
parseComparisonOp
@@ -4418,7 +4419,7 @@ func newBinArithParser(opStr string, op ArithOperator, nextPrec func(*parser) (E
44184419

44194420
func (p *parser) parseLogicalNot() (Expr, *parseError) {
44204421
if !p.eat("NOT") {
4421-
return p.parseIsOp()
4422+
return p.parseExistsOp()
44224423
}
44234424
be, err := p.parseBoolExpr()
44244425
if err != nil {
@@ -4427,6 +4428,27 @@ func (p *parser) parseLogicalNot() (Expr, *parseError) {
44274428
return LogicalOp{Op: Not, RHS: be}, nil
44284429
}
44294430

4431+
func (p *parser) parseExistsOp() (Expr, *parseError) {
4432+
debugf("parseExistsOp: %v", p)
4433+
4434+
if !p.eat("EXISTS") {
4435+
return p.parseIsOp()
4436+
}
4437+
4438+
if err := p.expect("("); err != nil {
4439+
return nil, err
4440+
}
4441+
subq, err := p.parseQuery()
4442+
if err != nil {
4443+
return nil, err
4444+
}
4445+
if err := p.expect(")"); err != nil {
4446+
return nil, err
4447+
}
4448+
4449+
return ExistsOp{Subquery: subq}, nil
4450+
}
4451+
44304452
func (p *parser) parseIsOp() (Expr, *parseError) {
44314453
debugf("parseIsOp: %v", p)
44324454

spanner/spansql/parser_test.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,31 @@ func TestParseQuery(t *testing.T) {
340340
},
341341
},
342342
},
343+
{
344+
`SELECT * FROM A WHERE EXISTS (SELECT * FROM B)`,
345+
Query{
346+
Select: Select{
347+
List: []Expr{Star},
348+
From: []SelectFrom{
349+
SelectFromTable{
350+
Table: "A",
351+
},
352+
},
353+
Where: ExistsOp{
354+
Subquery: Query{
355+
Select: Select{
356+
List: []Expr{Star},
357+
From: []SelectFrom{
358+
SelectFromTable{
359+
Table: "B",
360+
},
361+
},
362+
},
363+
},
364+
},
365+
},
366+
},
367+
},
343368
}
344369
for _, test := range tests {
345370
got, err := ParseQuery(test.in)

spanner/spansql/sql.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1048,6 +1048,13 @@ func (co ComparisonOp) addSQL(sb *strings.Builder) {
10481048
}
10491049
}
10501050

1051+
func (eo ExistsOp) SQL() string { return buildSQL(eo) }
1052+
func (eo ExistsOp) addSQL(sb *strings.Builder) {
1053+
sb.WriteString("EXISTS (")
1054+
eo.Subquery.addSQL(sb)
1055+
sb.WriteString(")")
1056+
}
1057+
10511058
func (io InOp) SQL() string { return buildSQL(io) }
10521059
func (io InOp) addSQL(sb *strings.Builder) {
10531060
io.LHS.addSQL(sb)

spanner/spansql/sql_test.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1216,6 +1216,35 @@ func TestSQL(t *testing.T) {
12161216
`SELECT A, B AS banana FROM Table WHERE C < "whelp" AND D IS NOT NULL ORDER BY OCol DESC LIMIT 1000`,
12171217
reparseQuery,
12181218
},
1219+
{
1220+
Query{
1221+
Select: Select{
1222+
List: []Expr{Star},
1223+
From: []SelectFrom{
1224+
SelectFromTable{
1225+
Table: "A",
1226+
},
1227+
},
1228+
Where: LogicalOp{
1229+
Op: Not,
1230+
RHS: ExistsOp{
1231+
Subquery: Query{
1232+
Select: Select{
1233+
List: []Expr{Star},
1234+
From: []SelectFrom{
1235+
SelectFromTable{
1236+
Table: "B",
1237+
},
1238+
},
1239+
},
1240+
},
1241+
},
1242+
},
1243+
},
1244+
},
1245+
`SELECT * FROM A WHERE NOT EXISTS (SELECT * FROM B)`,
1246+
reparseQuery,
1247+
},
12191248
{
12201249
Query{
12211250
Select: Select{

spanner/spansql/types.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -808,6 +808,13 @@ const (
808808
NotBetween
809809
)
810810

811+
type ExistsOp struct {
812+
Subquery Query
813+
}
814+
815+
func (ExistsOp) isBoolExpr() {} // usually
816+
func (ExistsOp) isExpr() {}
817+
811818
type InOp struct {
812819
LHS Expr
813820
Neg bool

0 commit comments

Comments
 (0)