Skip to content

Commit 1ad9fac

Browse files
committed
Handle tuples with subqueries in WITH clause EXPLAIN output
Add isSimpleLiteralOrNestedLiteral helper to properly detect when a tuple in a WITH clause contains complex expressions (like subqueries) vs just nested literals. Tuples with subqueries render as Function tuple, while tuples of nested literals stay as Literal Tuple. Fixes tests in 01461_query_start_time_microseconds and 01651_bugs_from_15889.
1 parent 1b7808a commit 1ad9fac

3 files changed

Lines changed: 57 additions & 19 deletions

File tree

internal/explain/expressions.go

Lines changed: 55 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,31 @@ func isSimpleLiteralOrNegation(e ast.Expression) bool {
211211
return false
212212
}
213213

214+
// isSimpleLiteralOrNestedLiteral checks if an expression is a literal (including nested tuples/arrays of literals)
215+
// Returns false for complex expressions like subqueries, function calls, identifiers, etc.
216+
func isSimpleLiteralOrNestedLiteral(e ast.Expression) bool {
217+
if lit, ok := e.(*ast.Literal); ok {
218+
// For nested arrays/tuples, recursively check if all elements are also literals
219+
if lit.Type == ast.LiteralArray || lit.Type == ast.LiteralTuple {
220+
if exprs, ok := lit.Value.([]ast.Expression); ok {
221+
for _, elem := range exprs {
222+
if !isSimpleLiteralOrNestedLiteral(elem) {
223+
return false
224+
}
225+
}
226+
}
227+
}
228+
return true
229+
}
230+
// Unary minus of a literal integer/float is also simple (negative number)
231+
if unary, ok := e.(*ast.UnaryExpr); ok && unary.Op == "-" {
232+
if lit, ok := unary.Operand.(*ast.Literal); ok {
233+
return lit.Type == ast.LiteralInteger || lit.Type == ast.LiteralFloat
234+
}
235+
}
236+
return false
237+
}
238+
214239
// containsOnlyArraysOrTuples checks if a slice of expressions contains
215240
// only array or tuple literals (including empty arrays).
216241
// Returns true if the slice is empty or contains only arrays/tuples.
@@ -952,16 +977,39 @@ func explainWithElement(sb *strings.Builder, n *ast.WithElement, indent string,
952977
// When name is empty, don't show the alias part
953978
switch e := n.Query.(type) {
954979
case *ast.Literal:
955-
// Empty tuples should be rendered as Function tuple, not Literal
980+
// Tuples containing complex expressions (subqueries, function calls, etc) should be rendered as Function tuple
981+
// But tuples of simple literals (including nested tuples of literals) stay as Literal
956982
if e.Type == ast.LiteralTuple {
957-
if exprs, ok := e.Value.([]ast.Expression); ok && len(exprs) == 0 {
958-
if n.Name != "" {
959-
fmt.Fprintf(sb, "%sFunction tuple (alias %s) (children %d)\n", indent, n.Name, 1)
983+
if exprs, ok := e.Value.([]ast.Expression); ok {
984+
needsFunctionFormat := false
985+
// Empty tuples always use Function tuple format
986+
if len(exprs) == 0 {
987+
needsFunctionFormat = true
960988
} else {
961-
fmt.Fprintf(sb, "%sFunction tuple (children %d)\n", indent, 1)
989+
for _, expr := range exprs {
990+
// Check if any element is a truly complex expression (not just a literal)
991+
if !isSimpleLiteralOrNestedLiteral(expr) {
992+
needsFunctionFormat = true
993+
break
994+
}
995+
}
996+
}
997+
if needsFunctionFormat {
998+
if n.Name != "" {
999+
fmt.Fprintf(sb, "%sFunction tuple (alias %s) (children %d)\n", indent, n.Name, 1)
1000+
} else {
1001+
fmt.Fprintf(sb, "%sFunction tuple (children %d)\n", indent, 1)
1002+
}
1003+
if len(exprs) > 0 {
1004+
fmt.Fprintf(sb, "%s ExpressionList (children %d)\n", indent, len(exprs))
1005+
} else {
1006+
fmt.Fprintf(sb, "%s ExpressionList\n", indent)
1007+
}
1008+
for _, expr := range exprs {
1009+
Node(sb, expr, depth+2)
1010+
}
1011+
return
9621012
}
963-
fmt.Fprintf(sb, "%s ExpressionList\n", indent)
964-
return
9651013
}
9661014
}
9671015
// Arrays containing non-literal expressions should be rendered as Function array
Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1 @@
1-
{
2-
"explain_todo": {
3-
"stmt4": true,
4-
"stmt8": true
5-
}
6-
}
1+
{}
Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1 @@
1-
{
2-
"explain_todo": {
3-
"stmt21": true,
4-
"stmt22": true
5-
}
6-
}
1+
{}

0 commit comments

Comments
 (0)