Skip to content

Commit 88afb1c

Browse files
committed
Remove incorrect UNION ALL simplification in EXPLAIN output
The simplifyUnionSelects function was incorrectly collapsing UNION ALL queries when selects had identical expressions but different aliases. ClickHouse's EXPLAIN AST output keeps all SELECT queries in a UNION, so this optimization was incorrect. Fixes test 00592_union_all_different_aliases.
1 parent af1415f commit 88afb1c

2 files changed

Lines changed: 3 additions & 96 deletions

File tree

internal/explain/select.go

Lines changed: 2 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -248,98 +248,9 @@ func countSelectUnionChildren(n *ast.SelectWithUnionQuery) int {
248248
return count
249249
}
250250

251-
// simplifyUnionSelects implements ClickHouse's UNION ALL optimization:
252-
// When all SELECT queries in a UNION have identical expressions (ignoring aliases)
253-
// but different aliases, only the first SELECT is returned.
254-
// This only applies when ALL columns in ALL SELECTs have explicit aliases.
255-
// If aliases are the same across all SELECTs, or if any column lacks an alias, all are kept.
251+
// simplifyUnionSelects returns all SELECT statements in a UNION.
252+
// ClickHouse does not simplify UNION ALL queries in EXPLAIN AST output.
256253
func simplifyUnionSelects(selects []ast.Statement) []ast.Statement {
257-
if len(selects) <= 1 {
258-
return selects
259-
}
260-
261-
// Check if all are simple SelectQuery with only literal columns
262-
var queries []*ast.SelectQuery
263-
for _, sel := range selects {
264-
sq, ok := sel.(*ast.SelectQuery)
265-
if !ok {
266-
// Not a simple SelectQuery, can't simplify
267-
return selects
268-
}
269-
// Only handle simple SELECT with just columns, no FROM/WHERE/etc.
270-
if sq.From != nil || sq.Where != nil || sq.GroupBy != nil ||
271-
sq.Having != nil || sq.OrderBy != nil || len(sq.With) > 0 {
272-
return selects
273-
}
274-
queries = append(queries, sq)
275-
}
276-
277-
// Check if all have the same number of columns
278-
numCols := len(queries[0].Columns)
279-
for _, q := range queries[1:] {
280-
if len(q.Columns) != numCols {
281-
return selects
282-
}
283-
}
284-
285-
// Check if columns are all literals with aliases
286-
// and compare expressions (without aliases) and aliases separately
287-
allSameAliases := true
288-
allSameExprs := true
289-
allHaveAliases := true
290-
291-
for colIdx := 0; colIdx < numCols; colIdx++ {
292-
firstAlias := ""
293-
firstExpr := ""
294-
295-
for i, q := range queries {
296-
col := q.Columns[colIdx]
297-
alias := ""
298-
exprStr := ""
299-
hasAlias := false
300-
301-
switch c := col.(type) {
302-
case *ast.AliasedExpr:
303-
alias = c.Alias
304-
hasAlias = c.Alias != ""
305-
// Get string representation of the expression
306-
if lit, ok := c.Expr.(*ast.Literal); ok {
307-
exprStr = fmt.Sprintf("%v", lit.Value)
308-
} else {
309-
// Non-literal expression, can't simplify
310-
return selects
311-
}
312-
case *ast.Literal:
313-
exprStr = fmt.Sprintf("%v", c.Value)
314-
hasAlias = false
315-
default:
316-
// Not a simple literal or aliased literal
317-
return selects
318-
}
319-
320-
if !hasAlias {
321-
allHaveAliases = false
322-
}
323-
324-
if i == 0 {
325-
firstAlias = alias
326-
firstExpr = exprStr
327-
} else {
328-
if alias != firstAlias {
329-
allSameAliases = false
330-
}
331-
if exprStr != firstExpr {
332-
allSameExprs = false
333-
}
334-
}
335-
}
336-
}
337-
338-
// If expressions are the same, all have aliases, but aliases differ, return only first SELECT
339-
if allSameExprs && allHaveAliases && !allSameAliases {
340-
return selects[:1]
341-
}
342-
343254
return selects
344255
}
345256

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1 @@
1-
{
2-
"explain_todo": {
3-
"stmt1": true
4-
}
5-
}
1+
{}

0 commit comments

Comments
 (0)