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
22 changes: 18 additions & 4 deletions internal/explain/expressions.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,15 @@ func explainLiteral(sb *strings.Builder, n *ast.Literal, indent string, depth in
fmt.Fprintf(sb, "%s ExpressionList\n", indent)
return
}
// Single-element tuples (from trailing comma syntax like (1,)) always render as Function tuple
if len(exprs) == 1 {
fmt.Fprintf(sb, "%sFunction tuple (children %d)\n", indent, 1)
fmt.Fprintf(sb, "%s ExpressionList (children %d)\n", indent, len(exprs))
for _, e := range exprs {
Node(sb, e, depth+2)
}
return
}
hasComplexExpr := false
for _, e := range exprs {
// Simple literals (numbers, strings, etc.) are OK
Expand Down Expand Up @@ -454,11 +463,16 @@ func explainAliasedExpr(sb *strings.Builder, n *ast.AliasedExpr, depth int) {
explainCastExpr(sb, e, indent, depth)
}
case *ast.ArrayAccess:
// Array access - ClickHouse doesn't show aliases on arrayElement in EXPLAIN AST
explainArrayAccess(sb, e, indent, depth)
// Array access - show alias only when array is not a literal
// ClickHouse hides alias when array access is on a literal
if _, isLit := e.Array.(*ast.Literal); isLit {
explainArrayAccess(sb, e, indent, depth)
} else {
explainArrayAccessWithAlias(sb, e, n.Alias, indent, depth)
}
case *ast.TupleAccess:
// Tuple access - ClickHouse doesn't show aliases on tupleElement in EXPLAIN AST
explainTupleAccess(sb, e, indent, depth)
// Tuple access with alias
explainTupleAccessWithAlias(sb, e, n.Alias, indent, depth)
case *ast.InExpr:
// IN expressions with alias
explainInExprWithAlias(sb, e, n.Alias, indent, depth)
Expand Down
73 changes: 56 additions & 17 deletions internal/explain/functions.go
Original file line number Diff line number Diff line change
Expand Up @@ -395,19 +395,20 @@ func explainInExpr(sb *strings.Builder, n *ast.InExpr, indent string, depth int)
argCount += len(n.List)
}
} else {
// Check if all items are tuples
allTuples := true
// Check if all items are string literals (large list case - no wrapper)
allStringLiterals := true
for _, item := range n.List {
if lit, ok := item.(*ast.Literal); !ok || lit.Type != ast.LiteralTuple {
allTuples = false
if lit, ok := item.(*ast.Literal); !ok || lit.Type != ast.LiteralString {
allStringLiterals = false
break
}
}
if allTuples {
// All tuples get wrapped in a single Function tuple
argCount++
} else {
if allStringLiterals {
// Large string list - separate children
argCount += len(n.List)
} else {
// Non-string items get wrapped in a single Function tuple
argCount++
}
}
}
Expand Down Expand Up @@ -455,8 +456,26 @@ func explainInExpr(sb *strings.Builder, n *ast.InExpr, indent string, depth int)
explainTupleInInList(sb, item.(*ast.Literal), indent+" ", depth+4)
}
} else {
// Check if all items are string literals (large list case)
allStringLiterals := true
for _, item := range n.List {
Node(sb, item, depth+2)
if lit, ok := item.(*ast.Literal); !ok || lit.Type != ast.LiteralString {
allStringLiterals = false
break
}
}
if allStringLiterals {
// Large string list - output as separate children (no tuple wrapper)
for _, item := range n.List {
Node(sb, item, depth+2)
}
} else {
// Wrap non-literal/non-tuple list items in Function tuple
fmt.Fprintf(sb, "%s Function tuple (children %d)\n", indent, 1)
fmt.Fprintf(sb, "%s ExpressionList (children %d)\n", indent, len(n.List))
for _, item := range n.List {
Node(sb, item, depth+4)
}
}
}
}
Expand Down Expand Up @@ -548,18 +567,20 @@ func explainInExprWithAlias(sb *strings.Builder, n *ast.InExpr, alias string, in
argCount += len(n.List)
}
} else {
// Check if all items are tuples
allTuples := true
// Check if all items are string literals (large list case - no wrapper)
allStringLiterals := true
for _, item := range n.List {
if lit, ok := item.(*ast.Literal); !ok || lit.Type != ast.LiteralTuple {
allTuples = false
if lit, ok := item.(*ast.Literal); !ok || lit.Type != ast.LiteralString {
allStringLiterals = false
break
}
}
if allTuples {
argCount++
} else {
if allStringLiterals {
// Large string list - separate children
argCount += len(n.List)
} else {
// Non-string items get wrapped in a single Function tuple
argCount++
}
}
}
Expand Down Expand Up @@ -600,8 +621,26 @@ func explainInExprWithAlias(sb *strings.Builder, n *ast.InExpr, alias string, in
explainTupleInInList(sb, item.(*ast.Literal), indent+" ", depth+4)
}
} else {
// Check if all items are string literals (large list case)
allStringLiterals := true
for _, item := range n.List {
Node(sb, item, depth+2)
if lit, ok := item.(*ast.Literal); !ok || lit.Type != ast.LiteralString {
allStringLiterals = false
break
}
}
if allStringLiterals {
// Large string list - output as separate children (no tuple wrapper)
for _, item := range n.List {
Node(sb, item, depth+2)
}
} else {
// Wrap non-literal/non-tuple list items in Function tuple
fmt.Fprintf(sb, "%s Function tuple (children %d)\n", indent, 1)
fmt.Fprintf(sb, "%s ExpressionList (children %d)\n", indent, len(n.List))
for _, item := range n.List {
Node(sb, item, depth+4)
}
}
}
}
Expand Down
4 changes: 4 additions & 0 deletions parser/expression.go
Original file line number Diff line number Diff line change
Expand Up @@ -895,6 +895,10 @@ func (p *Parser) parseGroupedOrTuple() ast.Expression {
elements := []ast.Expression{first}
for p.currentIs(token.COMMA) {
p.nextToken()
// Handle trailing comma: (1,) should create tuple with single element
if p.currentIs(token.RPAREN) {
break
}
elements = append(elements, p.parseExpression(LOWEST))
}
p.expect(token.RPAREN)
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"todo":true,"todo_format":true}
{"todo_format":true}
2 changes: 1 addition & 1 deletion parser/testdata/00172_constexprs_in_set/metadata.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"todo":true,"todo_format":true}
{"todo_format":true}
2 changes: 1 addition & 1 deletion parser/testdata/00604_show_create_database/metadata.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"explain":false,"todo":true,"todo_format":true}
{"todo_format":true,"explain":false}
2 changes: 1 addition & 1 deletion parser/testdata/00606_quantiles_and_nans/metadata.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"explain":false,"todo":true,"todo_format":true}
{"todo_format":true,"explain":false}
2 changes: 1 addition & 1 deletion parser/testdata/00840_top_k_weighted/metadata.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"todo":true,"todo_format":true}
{"todo_format":true}
2 changes: 1 addition & 1 deletion parser/testdata/01021_tuple_parser/metadata.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"todo":true,"todo_format":true}
{"todo_format":true}
2 changes: 1 addition & 1 deletion parser/testdata/01277_large_tuples/metadata.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"explain":false,"todo":true,"todo_format":true}
{"todo_format":true,"explain":false}
2 changes: 1 addition & 1 deletion parser/testdata/01407_lambda_arrayJoin/metadata.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"todo":true,"todo_format":true}
{"todo_format":true}
2 changes: 1 addition & 1 deletion parser/testdata/01813_quantileBfloat16_nans/metadata.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"explain":false,"todo":true,"todo_format":true}
{"todo_format":true,"explain":false}
2 changes: 1 addition & 1 deletion parser/testdata/02163_operators/metadata.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"explain":false,"todo":true,"todo_format":true}
{"todo_format":true,"explain":false}
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"explain":false,"todo":true,"todo_format":true}
{"todo_format":true,"explain":false}
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"explain":false,"todo":true,"todo_format":true}
{"todo_format":true,"explain":false}
2 changes: 1 addition & 1 deletion parser/testdata/02513_date_string_comparison/metadata.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"explain":false,"todo":true,"todo_format":true}
{"todo_format":true,"explain":false}
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"todo":true,"todo_format":true}
{"todo_format":true}
2 changes: 1 addition & 1 deletion parser/testdata/02922_respect_nulls_Nullable/metadata.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"explain":false,"todo":true,"todo_format":true}
{"todo_format":true,"explain":false}
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"explain":false,"todo":true,"todo_format":true}
{"todo_format":true,"explain":false}
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"explain":false,"todo":true,"todo_format":true}
{"todo_format":true,"explain":false}
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"explain":false,"todo":true,"todo_format":true}
{"todo_format":true,"explain":false}
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"explain":false,"todo":true,"todo_format":true}
{"todo_format":true,"explain":false}
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"todo":true,"todo_format":true}
{"todo_format":true}