Skip to content

Commit 5f5d3a9

Browse files
committed
Flatten OR/AND operators in EXPLAIN AST output
Add collectBooleanOperands helper function to flatten chained OR and AND expressions into a flat list, matching ClickHouse's EXPLAIN AST format. Previously, "a OR b OR c" would produce nested output like: Function or ExpressionList (2) Function or (2) a b c Now it produces the expected flat format: Function or ExpressionList (3) a b c This change applies the flattening in three places: - explainBinaryExpr (main expression handling) - explainAliasedExpr (aliased expressions) - explainWithElement (WITH clause elements) This fixes 419 explain tests.
1 parent fec9a6b commit 5f5d3a9

420 files changed

Lines changed: 414 additions & 1784 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

internal/explain/expressions.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,18 @@ func explainBinaryExpr(sb *strings.Builder, n *ast.BinaryExpr, indent string, de
264264
return
265265
}
266266

267+
// For OR and AND operators, flatten chained expressions
268+
opLower := strings.ToLower(n.Op)
269+
if opLower == "or" || opLower == "and" {
270+
operands := collectBooleanOperands(n, opLower)
271+
fmt.Fprintf(sb, "%sFunction %s (children %d)\n", indent, fnName, 1)
272+
fmt.Fprintf(sb, "%s ExpressionList (children %d)\n", indent, len(operands))
273+
for _, op := range operands {
274+
Node(sb, op, depth+2)
275+
}
276+
return
277+
}
278+
267279
fmt.Fprintf(sb, "%sFunction %s (children %d)\n", indent, fnName, 1)
268280
fmt.Fprintf(sb, "%s ExpressionList (children %d)\n", indent, 2)
269281
Node(sb, n.Left, depth+2)
@@ -291,6 +303,27 @@ func collectConcatOperands(n *ast.BinaryExpr) []ast.Expression {
291303
return operands
292304
}
293305

306+
// collectBooleanOperands flattens chained OR/AND operations into a list of operands
307+
func collectBooleanOperands(n *ast.BinaryExpr, op string) []ast.Expression {
308+
var operands []ast.Expression
309+
310+
// Recursively collect from left side if it's also the same operator
311+
if left, ok := n.Left.(*ast.BinaryExpr); ok && strings.ToLower(left.Op) == op {
312+
operands = append(operands, collectBooleanOperands(left, op)...)
313+
} else {
314+
operands = append(operands, n.Left)
315+
}
316+
317+
// Recursively collect from right side if it's also the same operator
318+
if right, ok := n.Right.(*ast.BinaryExpr); ok && strings.ToLower(right.Op) == op {
319+
operands = append(operands, collectBooleanOperands(right, op)...)
320+
} else {
321+
operands = append(operands, n.Right)
322+
}
323+
324+
return operands
325+
}
326+
294327
func explainUnaryExpr(sb *strings.Builder, n *ast.UnaryExpr, indent string, depth int) {
295328
// Handle negate of literal numbers - output as negative literal instead of function
296329
if n.Op == "-" {
@@ -410,6 +443,14 @@ func explainAliasedExpr(sb *strings.Builder, n *ast.AliasedExpr, depth int) {
410443
for _, op := range operands {
411444
Node(sb, op, depth+2)
412445
}
446+
} else if opLower := strings.ToLower(e.Op); opLower == "or" || opLower == "and" {
447+
// For OR and AND operators, flatten chained expressions
448+
operands := collectBooleanOperands(e, opLower)
449+
fmt.Fprintf(sb, "%sFunction %s (alias %s) (children %d)\n", indent, fnName, escapeAlias(n.Alias), 1)
450+
fmt.Fprintf(sb, "%s ExpressionList (children %d)\n", indent, len(operands))
451+
for _, op := range operands {
452+
Node(sb, op, depth+2)
453+
}
413454
} else {
414455
fmt.Fprintf(sb, "%sFunction %s (alias %s) (children %d)\n", indent, fnName, escapeAlias(n.Alias), 1)
415456
fmt.Fprintf(sb, "%s ExpressionList (children %d)\n", indent, 2)
@@ -585,6 +626,18 @@ func explainWithElement(sb *strings.Builder, n *ast.WithElement, indent string,
585626
for _, op := range operands {
586627
Node(sb, op, depth+2)
587628
}
629+
} else if opLower := strings.ToLower(e.Op); opLower == "or" || opLower == "and" {
630+
// For OR and AND operators, flatten chained expressions
631+
operands := collectBooleanOperands(e, opLower)
632+
if n.Name != "" {
633+
fmt.Fprintf(sb, "%sFunction %s (alias %s) (children %d)\n", indent, fnName, n.Name, 1)
634+
} else {
635+
fmt.Fprintf(sb, "%sFunction %s (children %d)\n", indent, fnName, 1)
636+
}
637+
fmt.Fprintf(sb, "%s ExpressionList (children %d)\n", indent, len(operands))
638+
for _, op := range operands {
639+
Node(sb, op, depth+2)
640+
}
588641
} else {
589642
if n.Name != "" {
590643
fmt.Fprintf(sb, "%sFunction %s (alias %s) (children %d)\n", indent, fnName, n.Name, 1)
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"explain_todo":{"stmt11":true,"stmt12":true,"stmt5":true,"stmt6":true,"stmt7":true}}
1+
{}
Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1 @@
1-
{
2-
"explain_todo": {
3-
"stmt2": true
4-
}
5-
}
1+
{}
Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1 @@
1-
{
2-
"explain_todo": {
3-
"stmt6": true,
4-
"stmt7": true,
5-
"stmt8": true
6-
}
7-
}
1+
{}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"explain_todo":{"stmt4":true}}
1+
{}
Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1 @@
1-
{
2-
"explain_todo": {
3-
"stmt28": true,
4-
"stmt42": true,
5-
"stmt43": true,
6-
"stmt44": true,
7-
"stmt45": true,
8-
"stmt46": true,
9-
"stmt47": true,
10-
"stmt48": true
11-
}
12-
}
1+
{}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"explain_todo":{"stmt3":true,"stmt4":true}}
1+
{}
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+
{}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"explain_todo":{"stmt3":true}}
1+
{}
Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1 @@
1-
{
2-
"explain_todo": {
3-
"stmt1": true,
4-
"stmt2": true,
5-
"stmt3": true
6-
}
7-
}
1+
{}

0 commit comments

Comments
 (0)