Skip to content

Commit 30ddd15

Browse files
committed
Add EXPLAIN settings and explicit type support
Add HasSettings and ExplicitType fields to ExplainQuery AST to properly handle EXPLAIN statements with settings (e.g., `explain header = 1`) and to correctly distinguish when the EXPLAIN type was explicitly specified. - When EXPLAIN has settings like `header = 1`, output includes a Set child - When EXPLAIN type is not explicitly specified, omit type from output (e.g., `Explain EXPLAIN` instead of `Explain EXPLAIN PLAN`) This enables 61 previously failing explain tests to pass, including the target test 01508_explain_header.
1 parent 761ca2a commit 30ddd15

61 files changed

Lines changed: 88 additions & 630 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

ast/ast.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -559,9 +559,11 @@ const (
559559

560560
// ExplainQuery represents an EXPLAIN statement.
561561
type ExplainQuery struct {
562-
Position token.Position `json:"-"`
563-
ExplainType ExplainType `json:"explain_type"`
564-
Statement Statement `json:"statement"`
562+
Position token.Position `json:"-"`
563+
ExplainType ExplainType `json:"explain_type"`
564+
Statement Statement `json:"statement"`
565+
HasSettings bool `json:"has_settings,omitempty"`
566+
ExplicitType bool `json:"explicit_type,omitempty"` // true if type was explicitly specified
565567
}
566568

567569
func (e *ExplainQuery) Pos() token.Position { return e.Position }

internal/explain/statements.go

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -384,22 +384,36 @@ func explainSystemQuery(sb *strings.Builder, indent string) {
384384
}
385385

386386
func explainExplainQuery(sb *strings.Builder, n *ast.ExplainQuery, indent string, depth int) {
387+
// Determine the type string - only show if explicitly specified
388+
typeStr := ""
389+
if n.ExplicitType {
390+
typeStr = " " + string(n.ExplainType)
391+
}
392+
387393
// EXPLAIN CURRENT TRANSACTION has no children
388394
if n.ExplainType == ast.ExplainCurrentTransaction {
389395
// At top level (depth 0), ClickHouse outputs "Explain EXPLAIN <TYPE>"
390396
if depth == 0 {
391-
fmt.Fprintf(sb, "%sExplain EXPLAIN %s\n", indent, n.ExplainType)
397+
fmt.Fprintf(sb, "%sExplain EXPLAIN%s\n", indent, typeStr)
392398
} else {
393-
fmt.Fprintf(sb, "%sExplain %s\n", indent, n.ExplainType)
399+
fmt.Fprintf(sb, "%sExplain%s\n", indent, typeStr)
394400
}
395401
return
396402
}
403+
// Count children: settings (if present) + statement
404+
children := 1
405+
if n.HasSettings {
406+
children++
407+
}
397408
// At top level (depth 0), ClickHouse outputs "Explain EXPLAIN <TYPE>"
398409
// Nested in subqueries, it outputs "Explain <TYPE>"
399410
if depth == 0 {
400-
fmt.Fprintf(sb, "%sExplain EXPLAIN %s (children %d)\n", indent, n.ExplainType, 1)
411+
fmt.Fprintf(sb, "%sExplain EXPLAIN%s (children %d)\n", indent, typeStr, children)
401412
} else {
402-
fmt.Fprintf(sb, "%sExplain %s (children %d)\n", indent, n.ExplainType, 1)
413+
fmt.Fprintf(sb, "%sExplain%s (children %d)\n", indent, typeStr, children)
414+
}
415+
if n.HasSettings {
416+
fmt.Fprintf(sb, "%s Set\n", indent)
403417
}
404418
Node(sb, n.Statement, depth+1)
405419
}

parser/parser.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2895,18 +2895,23 @@ func (p *Parser) parseExplain() *ast.ExplainQuery {
28952895
switch strings.ToUpper(p.current.Value) {
28962896
case "AST":
28972897
explain.ExplainType = ast.ExplainAST
2898+
explain.ExplicitType = true
28982899
p.nextToken()
28992900
case "SYNTAX":
29002901
explain.ExplainType = ast.ExplainSyntax
2902+
explain.ExplicitType = true
29012903
p.nextToken()
29022904
case "PLAN":
29032905
explain.ExplainType = ast.ExplainPlan
2906+
explain.ExplicitType = true
29042907
p.nextToken()
29052908
case "PIPELINE":
29062909
explain.ExplainType = ast.ExplainPipeline
2910+
explain.ExplicitType = true
29072911
p.nextToken()
29082912
case "ESTIMATE":
29092913
explain.ExplainType = ast.ExplainEstimate
2914+
explain.ExplicitType = true
29102915
p.nextToken()
29112916
case "CURRENT":
29122917
// EXPLAIN CURRENT TRANSACTION
@@ -2915,6 +2920,7 @@ func (p *Parser) parseExplain() *ast.ExplainQuery {
29152920
p.nextToken()
29162921
}
29172922
explain.ExplainType = ast.ExplainCurrentTransaction
2923+
explain.ExplicitType = true
29182924
return explain // No statement follows CURRENT TRANSACTION
29192925
default:
29202926
explain.ExplainType = ast.ExplainPlan
@@ -2926,6 +2932,7 @@ func (p *Parser) parseExplain() *ast.ExplainQuery {
29262932
// Options can be identifiers or keywords like OPTIMIZE followed by =
29272933
for p.peekIs(token.EQ) && !p.currentIs(token.SELECT) && !p.currentIs(token.WITH) {
29282934
// This is an option (name = value)
2935+
explain.HasSettings = true
29292936
p.nextToken() // skip option name
29302937
p.nextToken() // skip =
29312938
p.parseExpression(LOWEST) // skip value
Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1 @@
1-
{
2-
"explain_todo": {
3-
"stmt2": true
4-
}
5-
}
1+
{}
Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1 @@
1-
{
2-
"explain_todo": {
3-
"stmt15": true,
4-
"stmt16": true,
5-
"stmt18": true,
6-
"stmt19": true,
7-
"stmt4": true,
8-
"stmt5": true,
9-
"stmt7": true,
10-
"stmt8": true
11-
}
12-
}
1+
{"explain_todo":{"stmt16":true,"stmt19":true,"stmt5":true,"stmt8":true}}
Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1 @@
1-
{
2-
"explain_todo": {
3-
"stmt22": true,
4-
"stmt23": true,
5-
"stmt3": true,
6-
"stmt32": true,
7-
"stmt33": true,
8-
"stmt34": true,
9-
"stmt36": true,
10-
"stmt37": true,
11-
"stmt38": true,
12-
"stmt4": true,
13-
"stmt42": true,
14-
"stmt5": true,
15-
"stmt51": true
16-
}
17-
}
1+
{"explain_todo":{"stmt22":true,"stmt23":true,"stmt3":true,"stmt4":true,"stmt42":true,"stmt5":true,"stmt51":true}}
Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1 @@
1-
{
2-
"explain_todo": {
3-
"stmt11": true,
4-
"stmt23": true,
5-
"stmt24": true,
6-
"stmt5": true
7-
}
8-
}
1+
{"explain_todo":{"stmt11":true,"stmt5":true}}
Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1 @@
1-
{
2-
"explain_todo": {
3-
"stmt2": true,
4-
"stmt3": true
5-
}
6-
}
1+
{}
Lines changed: 1 addition & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1 @@
1-
{
2-
"explain_todo": {
3-
"stmt10": true,
4-
"stmt11": true,
5-
"stmt12": true,
6-
"stmt14": true,
7-
"stmt15": true,
8-
"stmt16": true,
9-
"stmt17": true,
10-
"stmt18": true,
11-
"stmt19": true,
12-
"stmt20": true,
13-
"stmt21": true,
14-
"stmt5": true,
15-
"stmt6": true,
16-
"stmt7": true,
17-
"stmt8": true,
18-
"stmt9": true
19-
}
20-
}
1+
{"explain_todo":{"stmt11":true,"stmt12":true,"stmt16":true,"stmt17":true,"stmt20":true,"stmt21":true,"stmt7":true,"stmt8":true}}
Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1 @@
1-
{
2-
"explain_todo": {
3-
"stmt11": true,
4-
"stmt12": true,
5-
"stmt13": true,
6-
"stmt14": true
7-
}
8-
}
1+
{}

0 commit comments

Comments
 (0)