Skip to content

Commit 588e73b

Browse files
committed
Add INTERSECT keyword and fix EXCEPT explain formatting
- Add INTERSECT as a proper keyword token (was being parsed as identifier which caused it to be treated as an alias for function calls) - Update parser to use token.INTERSECT instead of identifier check - Fix explainSelectIntersectExceptQuery to wrap first operand in SelectWithUnionQuery when EXCEPT is present (matches ClickHouse behavior) - Update metadata.json files to reflect newly passing explain tests This fixes 12 INTERSECT/EXCEPT explain tests. Remaining failures require parser changes to handle INTERSECT/EXCEPT operator precedence properly.
1 parent c0d59a4 commit 588e73b

15 files changed

Lines changed: 34 additions & 73 deletions

File tree

internal/explain/select.go

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,26 @@ import (
99

1010
func explainSelectIntersectExceptQuery(sb *strings.Builder, n *ast.SelectIntersectExceptQuery, indent string, depth int) {
1111
fmt.Fprintf(sb, "%sSelectIntersectExceptQuery (children %d)\n", indent, len(n.Selects))
12-
for _, sel := range n.Selects {
13-
Node(sb, sel, depth+1)
12+
13+
// ClickHouse wraps first operand in SelectWithUnionQuery when EXCEPT is present
14+
hasExcept := false
15+
for _, op := range n.Operators {
16+
if op == "EXCEPT" {
17+
hasExcept = true
18+
break
19+
}
20+
}
21+
22+
childIndent := strings.Repeat(" ", depth+1)
23+
for i, sel := range n.Selects {
24+
if hasExcept && i == 0 {
25+
// Wrap first operand in SelectWithUnionQuery -> ExpressionList format
26+
fmt.Fprintf(sb, "%sSelectWithUnionQuery (children 1)\n", childIndent)
27+
fmt.Fprintf(sb, "%s ExpressionList (children 1)\n", childIndent)
28+
Node(sb, sel, depth+3)
29+
} else {
30+
Node(sb, sel, depth+1)
31+
}
1432
}
1533
}
1634

parser/parser.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -314,8 +314,7 @@ func (p *Parser) parseSelectWithUnion() *ast.SelectWithUnionQuery {
314314
}
315315

316316
// Parse UNION/INTERSECT ALL/EXCEPT ALL clauses
317-
for p.currentIs(token.UNION) || p.currentIs(token.EXCEPT) ||
318-
(p.currentIs(token.IDENT) && strings.ToUpper(p.current.Value) == "INTERSECT") {
317+
for p.currentIs(token.UNION) || p.currentIs(token.EXCEPT) || p.currentIs(token.INTERSECT) {
319318
var setOp string
320319
if p.currentIs(token.UNION) {
321320
setOp = "UNION"
@@ -366,8 +365,7 @@ func (p *Parser) parseSelectWithUnion() *ast.SelectWithUnionQuery {
366365
// Only INTERSECT ALL and EXCEPT ALL are flattened (no wrapper).
367366
// INTERSECT DISTINCT, INTERSECT, EXCEPT DISTINCT, and EXCEPT all use the wrapper.
368367
func (p *Parser) isIntersectExceptWithWrapper() bool {
369-
if !p.currentIs(token.EXCEPT) &&
370-
!(p.currentIs(token.IDENT) && strings.ToUpper(p.current.Value) == "INTERSECT") {
368+
if !p.currentIs(token.EXCEPT) && !p.currentIs(token.INTERSECT) {
371369
return false
372370
}
373371
// INTERSECT ALL and EXCEPT ALL are flattened (no wrapper)
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"explain_todo":{"stmt17":true,"stmt9":true}}
1+
{}
Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,8 @@
11
{
22
"explain_todo": {
3-
"stmt10": true,
4-
"stmt11": true,
53
"stmt12": true,
64
"stmt13": true,
75
"stmt14": true,
8-
"stmt2": true,
9-
"stmt3": true,
10-
"stmt4": true,
11-
"stmt5": true,
12-
"stmt6": true,
13-
"stmt7": true,
14-
"stmt8": true,
15-
"stmt9": true
6+
"stmt2": true
167
}
178
}

parser/testdata/02004_intersect_except_distinct_operators/metadata.json

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,34 +7,22 @@
77
"stmt14": true,
88
"stmt15": true,
99
"stmt16": true,
10-
"stmt17": true,
1110
"stmt18": true,
12-
"stmt20": true,
1311
"stmt21": true,
1412
"stmt22": true,
1513
"stmt23": true,
16-
"stmt24": true,
17-
"stmt25": true,
1814
"stmt26": true,
1915
"stmt27": true,
2016
"stmt28": true,
21-
"stmt30": true,
2217
"stmt31": true,
2318
"stmt32": true,
2419
"stmt33": true,
2520
"stmt34": true,
2621
"stmt35": true,
2722
"stmt36": true,
28-
"stmt37": true,
2923
"stmt38": true,
3024
"stmt39": true,
31-
"stmt41": true,
3225
"stmt42": true,
33-
"stmt45": true,
34-
"stmt5": true,
35-
"stmt6": true,
36-
"stmt7": true,
37-
"stmt8": true,
38-
"stmt9": true
26+
"stmt45": true
3927
}
4028
}

parser/testdata/02004_intersect_except_operators/metadata.json

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,35 +5,23 @@
55
"stmt12": true,
66
"stmt13": true,
77
"stmt14": true,
8-
"stmt15": true,
98
"stmt16": true,
10-
"stmt18": true,
119
"stmt19": true,
1210
"stmt20": true,
1311
"stmt21": true,
14-
"stmt22": true,
15-
"stmt23": true,
1612
"stmt24": true,
1713
"stmt25": true,
1814
"stmt26": true,
19-
"stmt28": true,
2015
"stmt29": true,
21-
"stmt3": true,
2216
"stmt30": true,
2317
"stmt31": true,
2418
"stmt32": true,
2519
"stmt33": true,
2620
"stmt34": true,
27-
"stmt35": true,
2821
"stmt36": true,
2922
"stmt37": true,
30-
"stmt39": true,
31-
"stmt4": true,
3223
"stmt40": true,
3324
"stmt43": true,
34-
"stmt5": true,
35-
"stmt6": true,
36-
"stmt7": true,
3725
"stmt8": true,
3826
"stmt9": true
3927
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"explain_todo":{"stmt2":true,"stmt5":true}}
1+
{}
Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1 @@
1-
{
2-
"explain_todo": {
3-
"stmt60": true,
4-
"stmt68": true,
5-
"stmt76": true
6-
}
7-
}
1+
{}
Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
{
22
"explain_todo": {
3-
"stmt3": true,
4-
"stmt6": true,
5-
"stmt7": true
3+
"stmt3": true
64
}
75
}
Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1 @@
1-
{
2-
"explain_todo": {
3-
"stmt20": true,
4-
"stmt21": true
5-
}
6-
}
1+
{}

0 commit comments

Comments
 (0)