Skip to content

Commit e0490f2

Browse files
committed
Add EXPLAIN options capture and viewExplain wrapper transformation
- Add OptionsString field to ExplainQuery to capture EXPLAIN options - Update parser to capture options like "actions = 1" as strings - Update explainViewExplain to output proper SELECT * FROM viewExplain() structure - This matches ClickHouse's internal transformation of EXPLAIN as table source Updates metadata for many passing explain tests.
1 parent d0cdb97 commit e0490f2

File tree

73 files changed

+109
-604
lines changed

Some content is hidden

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

73 files changed

+109
-604
lines changed

ast/ast.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -700,11 +700,12 @@ const (
700700

701701
// ExplainQuery represents an EXPLAIN statement.
702702
type ExplainQuery struct {
703-
Position token.Position `json:"-"`
704-
ExplainType ExplainType `json:"explain_type"`
705-
Statement Statement `json:"statement"`
706-
HasSettings bool `json:"has_settings,omitempty"`
707-
ExplicitType bool `json:"explicit_type,omitempty"` // true if type was explicitly specified
703+
Position token.Position `json:"-"`
704+
ExplainType ExplainType `json:"explain_type"`
705+
Statement Statement `json:"statement"`
706+
HasSettings bool `json:"has_settings,omitempty"`
707+
ExplicitType bool `json:"explicit_type,omitempty"` // true if type was explicitly specified
708+
OptionsString string `json:"options_string,omitempty"` // Formatted options like "actions = 1"
708709
}
709710

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

internal/explain/tables.go

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -133,22 +133,30 @@ func abs(x float64) float64 {
133133
}
134134

135135
// explainViewExplain handles EXPLAIN queries used as table sources, converting to viewExplain function
136+
// ClickHouse internally transforms EXPLAIN to SELECT * FROM viewExplain(...)
136137
func explainViewExplain(sb *strings.Builder, n *ast.ExplainQuery, alias string, indent string, depth int) {
137-
// When EXPLAIN is used as a table source, it becomes viewExplain function
138-
// Arguments: 'EXPLAIN', 'options', subquery
139-
fmt.Fprintf(sb, "%sFunction viewExplain (children %d)\n", indent, 1)
140-
fmt.Fprintf(sb, "%s ExpressionList (children %d)\n", indent, 3)
138+
// When EXPLAIN is used as a table source, it becomes wrapped in SELECT * FROM viewExplain(...)
139+
// Structure: Subquery -> SelectWithUnionQuery -> ExpressionList -> SelectQuery -> Asterisk, TablesInSelectQuery -> viewExplain
140+
fmt.Fprintf(sb, "%sSubquery (children %d)\n", indent, 1)
141+
fmt.Fprintf(sb, "%s SelectWithUnionQuery (children %d)\n", indent, 1)
142+
fmt.Fprintf(sb, "%s ExpressionList (children %d)\n", indent, 1)
143+
fmt.Fprintf(sb, "%s SelectQuery (children %d)\n", indent, 2)
144+
fmt.Fprintf(sb, "%s ExpressionList (children %d)\n", indent, 1)
145+
fmt.Fprintf(sb, "%s Asterisk\n", indent)
146+
fmt.Fprintf(sb, "%s TablesInSelectQuery (children %d)\n", indent, 1)
147+
fmt.Fprintf(sb, "%s TablesInSelectQueryElement (children %d)\n", indent, 1)
148+
fmt.Fprintf(sb, "%s TableExpression (children %d)\n", indent, 1)
149+
// Now output the viewExplain function
150+
fmt.Fprintf(sb, "%s Function viewExplain (children %d)\n", indent, 1)
151+
fmt.Fprintf(sb, "%s ExpressionList (children %d)\n", indent, 3)
141152
// First argument: 'EXPLAIN' literal
142-
fmt.Fprintf(sb, "%s Literal \\'EXPLAIN\\'\n", indent)
143-
// Second argument: options string (empty for now since we don't track detailed options)
144-
options := string(n.ExplainType)
145-
if options == "PLAN" {
146-
options = ""
147-
}
148-
fmt.Fprintf(sb, "%s Literal \\'%s\\'\n", indent, options)
153+
fmt.Fprintf(sb, "%s Literal \\'EXPLAIN\\'\n", indent)
154+
// Second argument: options string (e.g., "actions = 1")
155+
options := n.OptionsString
156+
fmt.Fprintf(sb, "%s Literal \\'%s\\'\n", indent, options)
149157
// Third argument: the subquery being explained
150-
fmt.Fprintf(sb, "%s Subquery (children %d)\n", indent, 1)
151-
Node(sb, n.Statement, depth+3)
158+
fmt.Fprintf(sb, "%s Subquery (children %d)\n", indent, 1)
159+
Node(sb, n.Statement, depth+10)
152160
}
153161

154162
func explainTableIdentifierWithAlias(sb *strings.Builder, n *ast.TableIdentifier, alias string, indent string) {

parser/parser.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4312,17 +4312,27 @@ func (p *Parser) parseExplain() *ast.ExplainQuery {
43124312
// Parse EXPLAIN options (e.g., header = 1, optimize = 0)
43134313
// These come before the actual statement
43144314
// Options can be identifiers or keywords like OPTIMIZE followed by =
4315+
var optionParts []string
43154316
for p.peekIs(token.EQ) && !p.currentIs(token.SELECT) && !p.currentIs(token.WITH) {
43164317
// This is an option (name = value)
43174318
explain.HasSettings = true
4319+
optionName := p.current.Value
43184320
p.nextToken() // skip option name
43194321
p.nextToken() // skip =
4320-
p.parseExpression(LOWEST) // skip value
4322+
// Get the value
4323+
optionValue := p.current.Value
4324+
if p.currentIs(token.NUMBER) || p.currentIs(token.STRING) || p.currentIs(token.IDENT) {
4325+
optionParts = append(optionParts, optionName+" = "+optionValue)
4326+
}
4327+
p.parseExpression(LOWEST) // skip value expression (may consume more tokens)
43214328
// Skip comma if present
43224329
if p.currentIs(token.COMMA) {
43234330
p.nextToken()
43244331
}
43254332
}
4333+
if len(optionParts) > 0 {
4334+
explain.OptionsString = strings.Join(optionParts, ", ")
4335+
}
43264336

43274337
// Parse the statement being explained
43284338
explain.Statement = p.parseStatement()
Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1 @@
1-
{
2-
"explain_todo": {
3-
"stmt5": true,
4-
"stmt6": true
5-
}
6-
}
1+
{}
Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1 @@
1-
{
2-
"explain_todo": {
3-
"stmt13": true,
4-
"stmt14": true,
5-
"stmt16": true,
6-
"stmt17": true
7-
}
8-
}
1+
{}
Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1 @@
1-
{
2-
"explain_todo": {
3-
"stmt11": true,
4-
"stmt6": true
5-
}
6-
}
1+
{}
Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,5 @@
11
{
22
"explain_todo": {
3-
"stmt12": true,
4-
"stmt13": true,
5-
"stmt14": true,
6-
"stmt15": true,
7-
"stmt16": true,
8-
"stmt17": true,
9-
"stmt18": true,
10-
"stmt19": true,
113
"stmt5": true
124
}
135
}
Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1 @@
1-
{
2-
"explain_todo": {
3-
"stmt3": true,
4-
"stmt5": true
5-
}
6-
}
1+
{}
Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1 @@
1-
{
2-
"explain_todo": {
3-
"stmt5": true
4-
}
5-
}
1+
{}
Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1 @@
1-
{
2-
"explain_todo": {
3-
"stmt10": true,
4-
"stmt11": true,
5-
"stmt12": true,
6-
"stmt13": true,
7-
"stmt14": true,
8-
"stmt15": true,
9-
"stmt16": true,
10-
"stmt17": true,
11-
"stmt20": true,
12-
"stmt21": true,
13-
"stmt22": true,
14-
"stmt23": true,
15-
"stmt8": true,
16-
"stmt9": true
17-
}
18-
}
1+
{}

0 commit comments

Comments
 (0)