Skip to content

Commit aed1662

Browse files
authored
Remove incorrect UNION ALL simplification in EXPLAIN output (#81)
1 parent af1415f commit aed1662

File tree

3 files changed

+4
-97
lines changed

3 files changed

+4
-97
lines changed

.github/workflows/test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,4 @@ jobs:
2121
run: go build ./...
2222

2323
- name: Test
24-
run: go test -v ./...
24+
run: go test ./...

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)