Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion internal/explain/expressions.go
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,10 @@ func explainAliasedExpr(sb *strings.Builder, n *ast.AliasedExpr, depth int) {
if e.Type == ast.LiteralArray {
if exprs, ok := e.Value.([]ast.Expression); ok {
needsFunctionFormat := false
// Empty arrays always use Function array format
if len(exprs) == 0 {
needsFunctionFormat = true
}
for _, expr := range exprs {
// Check for tuples - use Function array
if lit, ok := expr.(*ast.Literal); ok && lit.Type == ast.LiteralTuple {
Expand All @@ -363,7 +367,11 @@ func explainAliasedExpr(sb *strings.Builder, n *ast.AliasedExpr, depth int) {
if needsFunctionFormat {
// Render as Function array with alias
fmt.Fprintf(sb, "%sFunction array (alias %s) (children %d)\n", indent, n.Alias, 1)
fmt.Fprintf(sb, "%s ExpressionList (children %d)\n", indent, len(exprs))
if len(exprs) > 0 {
fmt.Fprintf(sb, "%s ExpressionList (children %d)\n", indent, len(exprs))
} else {
fmt.Fprintf(sb, "%s ExpressionList\n", indent)
}
for _, expr := range exprs {
Node(sb, expr, depth+2)
}
Expand Down Expand Up @@ -427,6 +435,9 @@ func explainAliasedExpr(sb *strings.Builder, n *ast.AliasedExpr, depth int) {
case *ast.InExpr:
// IN expressions with alias
explainInExprWithAlias(sb, e, n.Alias, indent, depth)
case *ast.CaseExpr:
// CASE expressions with alias
explainCaseExprWithAlias(sb, e, n.Alias, indent, depth)
default:
// For other types, recursively explain and add alias info
Node(sb, n.Expr, depth)
Expand Down
16 changes: 14 additions & 2 deletions internal/explain/functions.go
Original file line number Diff line number Diff line change
Expand Up @@ -707,14 +707,22 @@ func explainIsNullExpr(sb *strings.Builder, n *ast.IsNullExpr, indent string, de
}

func explainCaseExpr(sb *strings.Builder, n *ast.CaseExpr, indent string, depth int) {
explainCaseExprWithAlias(sb, n, "", indent, depth)
}

func explainCaseExprWithAlias(sb *strings.Builder, n *ast.CaseExpr, alias string, indent string, depth int) {
// CASE is represented as Function multiIf or caseWithExpression
if n.Operand != nil {
// CASE x WHEN ... form
argCount := 1 + len(n.Whens)*2 // operand + (condition, result) pairs
if n.Else != nil {
argCount++
}
fmt.Fprintf(sb, "%sFunction caseWithExpression (children %d)\n", indent, 1)
if alias != "" {
fmt.Fprintf(sb, "%sFunction caseWithExpression (alias %s) (children %d)\n", indent, alias, 1)
} else {
fmt.Fprintf(sb, "%sFunction caseWithExpression (children %d)\n", indent, 1)
}
fmt.Fprintf(sb, "%s ExpressionList (children %d)\n", indent, argCount)
Node(sb, n.Operand, depth+2)
for _, w := range n.Whens {
Expand All @@ -730,7 +738,11 @@ func explainCaseExpr(sb *strings.Builder, n *ast.CaseExpr, indent string, depth
if n.Else != nil {
argCount++
}
fmt.Fprintf(sb, "%sFunction multiIf (children %d)\n", indent, 1)
if alias != "" {
fmt.Fprintf(sb, "%sFunction multiIf (alias %s) (children %d)\n", indent, alias, 1)
} else {
fmt.Fprintf(sb, "%sFunction multiIf (children %d)\n", indent, 1)
}
fmt.Fprintf(sb, "%s ExpressionList (children %d)\n", indent, argCount)
for _, w := range n.Whens {
Node(sb, w.Condition, depth+2)
Expand Down
14 changes: 12 additions & 2 deletions internal/explain/statements.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ func explainCreateQuery(sb *strings.Builder, n *ast.CreateQuery, indent string,
return
}
if n.CreateUser {
fmt.Fprintf(sb, "%sCreateUserQuery %s\n", indent, n.UserName)
fmt.Fprintf(sb, "%sCreateUserQuery\n", indent)
return
}
if n.CreateDictionary {
Expand Down Expand Up @@ -358,11 +358,21 @@ func explainDescribeQuery(sb *strings.Builder, n *ast.DescribeQuery, indent stri
fmt.Fprintf(sb, "%s Set\n", indent)
}
} else {
// Regular table describe
name := n.Table
if n.Database != "" {
name = n.Database + "." + n.Table
}
fmt.Fprintf(sb, "%sDescribe %s\n", indent, name)
children := 1
if len(n.Settings) > 0 {
children++
}
fmt.Fprintf(sb, "%sDescribeQuery (children %d)\n", indent, children)
fmt.Fprintf(sb, "%s TableExpression (children 1)\n", indent)
fmt.Fprintf(sb, "%s TableIdentifier %s\n", indent, name)
if len(n.Settings) > 0 {
fmt.Fprintf(sb, "%s Set\n", indent)
}
}
}

Expand Down
43 changes: 43 additions & 0 deletions internal/explain/tables.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ func explainTablesInSelectQueryElement(sb *strings.Builder, n *ast.TablesInSelec

func explainTableExpression(sb *strings.Builder, n *ast.TableExpression, indent string, depth int) {
children := 1 // table
if n.Sample != nil {
children++
}
fmt.Fprintf(sb, "%sTableExpression (children %d)\n", indent, children)
// If there's a subquery with an alias, pass the alias to the subquery output
if subq, ok := n.Table.(*ast.Subquery); ok {
Expand All @@ -51,6 +54,46 @@ func explainTableExpression(sb *strings.Builder, n *ast.TableExpression, indent
} else {
Node(sb, n.Table, depth+1)
}
// Output SAMPLE clause if present
if n.Sample != nil {
explainSampleClause(sb, n.Sample, indent+" ", depth+1)
}
}

func explainSampleClause(sb *strings.Builder, n *ast.SampleClause, indent string, depth int) {
// Format the sample ratio as "SampleRatio num / den" or just the expression
sb.WriteString(indent)
sb.WriteString("SampleRatio ")
formatSampleRatio(sb, n.Ratio)
sb.WriteString("\n")
}

func formatSampleRatio(sb *strings.Builder, expr ast.Expression) {
// Handle binary expressions like 1 / 2
if binExpr, ok := expr.(*ast.BinaryExpr); ok && binExpr.Op == "/" {
formatSampleRatioOperand(sb, binExpr.Left)
sb.WriteString(" / ")
formatSampleRatioOperand(sb, binExpr.Right)
} else {
formatSampleRatioOperand(sb, expr)
}
}

func formatSampleRatioOperand(sb *strings.Builder, expr ast.Expression) {
if lit, ok := expr.(*ast.Literal); ok {
switch v := lit.Value.(type) {
case int64:
fmt.Fprintf(sb, "%d", v)
case uint64:
fmt.Fprintf(sb, "%d", v)
case float64:
fmt.Fprintf(sb, "%g", v)
default:
fmt.Fprintf(sb, "%v", v)
}
} else {
fmt.Fprintf(sb, "%v", expr)
}
}

// explainViewExplain handles EXPLAIN queries used as table sources, converting to viewExplain function
Expand Down
9 changes: 9 additions & 0 deletions parser/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -730,6 +730,15 @@ func (p *Parser) parseTableElementWithJoin() *ast.TablesInSelectQueryElement {
if p.currentIs(token.COMMA) {
p.nextToken()
elem.Table = p.parseTableExpression()
// ClickHouse adds an empty TableJoin node for comma joins, but only
// when the table is NOT a subquery (subqueries don't get TableJoin nodes)
if elem.Table != nil {
if _, isSubquery := elem.Table.Table.(*ast.Subquery); !isSubquery {
elem.Join = &ast.TableJoin{
Position: elem.Position,
}
}
}
return elem
}

Expand Down
2 changes: 1 addition & 1 deletion parser/testdata/01116_cross_count_asterisks/metadata.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{}
{"todo": true}
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"todo": true, "parse_error": true}
{"parse_error": true}
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"todo": true}
{}
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"todo": true}
{}
2 changes: 1 addition & 1 deletion parser/testdata/02128_apply_lambda_parsing/metadata.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"todo": true, "parse_error": true}
{"parse_error": true}
2 changes: 1 addition & 1 deletion parser/testdata/02265_cross_join_empty_list/metadata.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"todo": true}
{}
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"todo": true}
{}
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"todo": true}
{"todo": false}
2 changes: 1 addition & 1 deletion parser/testdata/02469_fix_aliases_parser/metadata.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"todo": true, "parse_error": true}
{"parse_error": true}
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"todo": true, "parse_error": true}
{"parse_error": true}
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"todo": true, "parse_error": true}
{"parse_error": true}
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"todo": true, "parse_error": true}
{"parse_error": true}
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"todo": true}
{}
2 changes: 1 addition & 1 deletion parser/testdata/02896_illegal_sampling/metadata.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"todo": true}
{}
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"todo": true, "parse_error": true}
{}