Skip to content

Commit 9a6c47b

Browse files
authored
Fix 13 parser tests with various EXPLAIN AST improvements (#44)
1 parent d82d2e1 commit 9a6c47b

20 files changed

Lines changed: 105 additions & 20 deletions

File tree

internal/explain/expressions.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,10 @@ func explainAliasedExpr(sb *strings.Builder, n *ast.AliasedExpr, depth int) {
348348
if e.Type == ast.LiteralArray {
349349
if exprs, ok := e.Value.([]ast.Expression); ok {
350350
needsFunctionFormat := false
351+
// Empty arrays always use Function array format
352+
if len(exprs) == 0 {
353+
needsFunctionFormat = true
354+
}
351355
for _, expr := range exprs {
352356
// Check for tuples - use Function array
353357
if lit, ok := expr.(*ast.Literal); ok && lit.Type == ast.LiteralTuple {
@@ -363,7 +367,11 @@ func explainAliasedExpr(sb *strings.Builder, n *ast.AliasedExpr, depth int) {
363367
if needsFunctionFormat {
364368
// Render as Function array with alias
365369
fmt.Fprintf(sb, "%sFunction array (alias %s) (children %d)\n", indent, n.Alias, 1)
366-
fmt.Fprintf(sb, "%s ExpressionList (children %d)\n", indent, len(exprs))
370+
if len(exprs) > 0 {
371+
fmt.Fprintf(sb, "%s ExpressionList (children %d)\n", indent, len(exprs))
372+
} else {
373+
fmt.Fprintf(sb, "%s ExpressionList\n", indent)
374+
}
367375
for _, expr := range exprs {
368376
Node(sb, expr, depth+2)
369377
}
@@ -427,6 +435,9 @@ func explainAliasedExpr(sb *strings.Builder, n *ast.AliasedExpr, depth int) {
427435
case *ast.InExpr:
428436
// IN expressions with alias
429437
explainInExprWithAlias(sb, e, n.Alias, indent, depth)
438+
case *ast.CaseExpr:
439+
// CASE expressions with alias
440+
explainCaseExprWithAlias(sb, e, n.Alias, indent, depth)
430441
default:
431442
// For other types, recursively explain and add alias info
432443
Node(sb, n.Expr, depth)

internal/explain/functions.go

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -707,14 +707,22 @@ func explainIsNullExpr(sb *strings.Builder, n *ast.IsNullExpr, indent string, de
707707
}
708708

709709
func explainCaseExpr(sb *strings.Builder, n *ast.CaseExpr, indent string, depth int) {
710+
explainCaseExprWithAlias(sb, n, "", indent, depth)
711+
}
712+
713+
func explainCaseExprWithAlias(sb *strings.Builder, n *ast.CaseExpr, alias string, indent string, depth int) {
710714
// CASE is represented as Function multiIf or caseWithExpression
711715
if n.Operand != nil {
712716
// CASE x WHEN ... form
713717
argCount := 1 + len(n.Whens)*2 // operand + (condition, result) pairs
714718
if n.Else != nil {
715719
argCount++
716720
}
717-
fmt.Fprintf(sb, "%sFunction caseWithExpression (children %d)\n", indent, 1)
721+
if alias != "" {
722+
fmt.Fprintf(sb, "%sFunction caseWithExpression (alias %s) (children %d)\n", indent, alias, 1)
723+
} else {
724+
fmt.Fprintf(sb, "%sFunction caseWithExpression (children %d)\n", indent, 1)
725+
}
718726
fmt.Fprintf(sb, "%s ExpressionList (children %d)\n", indent, argCount)
719727
Node(sb, n.Operand, depth+2)
720728
for _, w := range n.Whens {
@@ -730,7 +738,11 @@ func explainCaseExpr(sb *strings.Builder, n *ast.CaseExpr, indent string, depth
730738
if n.Else != nil {
731739
argCount++
732740
}
733-
fmt.Fprintf(sb, "%sFunction multiIf (children %d)\n", indent, 1)
741+
if alias != "" {
742+
fmt.Fprintf(sb, "%sFunction multiIf (alias %s) (children %d)\n", indent, alias, 1)
743+
} else {
744+
fmt.Fprintf(sb, "%sFunction multiIf (children %d)\n", indent, 1)
745+
}
734746
fmt.Fprintf(sb, "%s ExpressionList (children %d)\n", indent, argCount)
735747
for _, w := range n.Whens {
736748
Node(sb, w.Condition, depth+2)

internal/explain/statements.go

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ func explainCreateQuery(sb *strings.Builder, n *ast.CreateQuery, indent string,
7070
return
7171
}
7272
if n.CreateUser {
73-
fmt.Fprintf(sb, "%sCreateUserQuery %s\n", indent, n.UserName)
73+
fmt.Fprintf(sb, "%sCreateUserQuery\n", indent)
7474
return
7575
}
7676
if n.CreateDictionary {
@@ -358,11 +358,21 @@ func explainDescribeQuery(sb *strings.Builder, n *ast.DescribeQuery, indent stri
358358
fmt.Fprintf(sb, "%s Set\n", indent)
359359
}
360360
} else {
361+
// Regular table describe
361362
name := n.Table
362363
if n.Database != "" {
363364
name = n.Database + "." + n.Table
364365
}
365-
fmt.Fprintf(sb, "%sDescribe %s\n", indent, name)
366+
children := 1
367+
if len(n.Settings) > 0 {
368+
children++
369+
}
370+
fmt.Fprintf(sb, "%sDescribeQuery (children %d)\n", indent, children)
371+
fmt.Fprintf(sb, "%s TableExpression (children 1)\n", indent)
372+
fmt.Fprintf(sb, "%s TableIdentifier %s\n", indent, name)
373+
if len(n.Settings) > 0 {
374+
fmt.Fprintf(sb, "%s Set\n", indent)
375+
}
366376
}
367377
}
368378

internal/explain/tables.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ func explainTablesInSelectQueryElement(sb *strings.Builder, n *ast.TablesInSelec
3030

3131
func explainTableExpression(sb *strings.Builder, n *ast.TableExpression, indent string, depth int) {
3232
children := 1 // table
33+
if n.Sample != nil {
34+
children++
35+
}
3336
fmt.Fprintf(sb, "%sTableExpression (children %d)\n", indent, children)
3437
// If there's a subquery with an alias, pass the alias to the subquery output
3538
if subq, ok := n.Table.(*ast.Subquery); ok {
@@ -51,6 +54,46 @@ func explainTableExpression(sb *strings.Builder, n *ast.TableExpression, indent
5154
} else {
5255
Node(sb, n.Table, depth+1)
5356
}
57+
// Output SAMPLE clause if present
58+
if n.Sample != nil {
59+
explainSampleClause(sb, n.Sample, indent+" ", depth+1)
60+
}
61+
}
62+
63+
func explainSampleClause(sb *strings.Builder, n *ast.SampleClause, indent string, depth int) {
64+
// Format the sample ratio as "SampleRatio num / den" or just the expression
65+
sb.WriteString(indent)
66+
sb.WriteString("SampleRatio ")
67+
formatSampleRatio(sb, n.Ratio)
68+
sb.WriteString("\n")
69+
}
70+
71+
func formatSampleRatio(sb *strings.Builder, expr ast.Expression) {
72+
// Handle binary expressions like 1 / 2
73+
if binExpr, ok := expr.(*ast.BinaryExpr); ok && binExpr.Op == "/" {
74+
formatSampleRatioOperand(sb, binExpr.Left)
75+
sb.WriteString(" / ")
76+
formatSampleRatioOperand(sb, binExpr.Right)
77+
} else {
78+
formatSampleRatioOperand(sb, expr)
79+
}
80+
}
81+
82+
func formatSampleRatioOperand(sb *strings.Builder, expr ast.Expression) {
83+
if lit, ok := expr.(*ast.Literal); ok {
84+
switch v := lit.Value.(type) {
85+
case int64:
86+
fmt.Fprintf(sb, "%d", v)
87+
case uint64:
88+
fmt.Fprintf(sb, "%d", v)
89+
case float64:
90+
fmt.Fprintf(sb, "%g", v)
91+
default:
92+
fmt.Fprintf(sb, "%v", v)
93+
}
94+
} else {
95+
fmt.Fprintf(sb, "%v", expr)
96+
}
5497
}
5598

5699
// explainViewExplain handles EXPLAIN queries used as table sources, converting to viewExplain function

parser/parser.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -730,6 +730,15 @@ func (p *Parser) parseTableElementWithJoin() *ast.TablesInSelectQueryElement {
730730
if p.currentIs(token.COMMA) {
731731
p.nextToken()
732732
elem.Table = p.parseTableExpression()
733+
// ClickHouse adds an empty TableJoin node for comma joins, but only
734+
// when the table is NOT a subquery (subqueries don't get TableJoin nodes)
735+
if elem.Table != nil {
736+
if _, isSubquery := elem.Table.Table.(*ast.Subquery); !isSubquery {
737+
elem.Join = &ast.TableJoin{
738+
Position: elem.Position,
739+
}
740+
}
741+
}
733742
return elem
734743
}
735744

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{}
1+
{"todo": true}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"todo": true, "parse_error": true}
1+
{"parse_error": true}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"todo": true}
1+
{}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"todo": true}
1+
{}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"todo": true, "parse_error": true}
1+
{"parse_error": true}

0 commit comments

Comments
 (0)