Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions internal/explain/explain.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,8 @@ func Node(sb *strings.Builder, node interface{}, depth int) {
explainUseQuery(sb, n, indent)
case *ast.DescribeQuery:
explainDescribeQuery(sb, n, indent)
case *ast.ExistsQuery:
explainExistsTableQuery(sb, n, indent)

// Types
case *ast.DataType:
Expand Down
28 changes: 27 additions & 1 deletion internal/explain/functions.go
Original file line number Diff line number Diff line change
Expand Up @@ -800,12 +800,38 @@ func explainExistsExpr(sb *strings.Builder, n *ast.ExistsExpr, indent string, de

func explainExtractExpr(sb *strings.Builder, n *ast.ExtractExpr, indent string, depth int) {
// EXTRACT is represented as Function toYear, toMonth, etc.
fnName := "to" + strings.Title(strings.ToLower(n.Field))
// ClickHouse uses specific function names for date/time extraction
fnName := extractFieldToFunction(n.Field)
fmt.Fprintf(sb, "%sFunction %s (children %d)\n", indent, fnName, 1)
fmt.Fprintf(sb, "%s ExpressionList (children %d)\n", indent, 1)
Node(sb, n.From, depth+2)
}

// extractFieldToFunction maps EXTRACT field names to ClickHouse function names
func extractFieldToFunction(field string) string {
switch strings.ToUpper(field) {
case "DAY":
return "toDayOfMonth"
case "MONTH":
return "toMonth"
case "YEAR":
return "toYear"
case "SECOND":
return "toSecond"
case "MINUTE":
return "toMinute"
case "HOUR":
return "toHour"
case "QUARTER":
return "toQuarter"
case "WEEK":
return "toWeek"
default:
// Fallback to generic "to" + TitleCase(field)
return "to" + strings.Title(strings.ToLower(field))
}
}

func explainWindowSpec(sb *strings.Builder, n *ast.WindowSpec, indent string, depth int) {
// Window spec is represented as WindowDefinition
// For simple cases like OVER (), just output WindowDefinition without children
Expand Down
7 changes: 7 additions & 0 deletions internal/explain/select.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,9 @@ func explainOrderByElement(sb *strings.Builder, n *ast.OrderByElement, indent st
if n.FillStep != nil {
children++
}
if n.Collate != "" {
children++
}
fmt.Fprintf(sb, "%sOrderByElement (children %d)\n", indent, children)
Node(sb, n.Expression, depth+1)
if n.FillFrom != nil {
Expand All @@ -186,6 +189,10 @@ func explainOrderByElement(sb *strings.Builder, n *ast.OrderByElement, indent st
if n.FillStep != nil {
Node(sb, n.FillStep, depth+1)
}
if n.Collate != "" {
// COLLATE is output as a string literal
fmt.Fprintf(sb, "%s Literal \\'%s\\'\n", indent, n.Collate)
}
}
}

Expand Down
30 changes: 29 additions & 1 deletion internal/explain/statements.go
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,13 @@ func explainShowQuery(sb *strings.Builder, n *ast.ShowQuery, indent string) {
showType = "Tables"
}

// SHOW CREATE DATABASE has special output format
if n.ShowType == ast.ShowCreateDB && n.From != "" {
fmt.Fprintf(sb, "%sShowCreateDatabaseQuery %s (children 1)\n", indent, n.From)
fmt.Fprintf(sb, "%s Identifier %s\n", indent, n.From)
return
}

// SHOW CREATE TABLE has special output format with database and table identifiers
if n.ShowType == ast.ShowCreate && (n.Database != "" || n.From != "") {
// Format: ShowCreateTableQuery database table (children 2)
Expand All @@ -383,11 +390,19 @@ func explainShowQuery(sb *strings.Builder, n *ast.ShowQuery, indent string) {
return
}

// SHOW TABLES FROM database - include database as child
if n.ShowType == ast.ShowTables && n.From != "" {
fmt.Fprintf(sb, "%sShowTables (children 1)\n", indent)
fmt.Fprintf(sb, "%s Identifier %s\n", indent, n.From)
return
}

fmt.Fprintf(sb, "%sShow%s\n", indent, showType)
}

func explainUseQuery(sb *strings.Builder, n *ast.UseQuery, indent string) {
fmt.Fprintf(sb, "%sUse %s\n", indent, n.Database)
fmt.Fprintf(sb, "%sUseQuery %s (children %d)\n", indent, n.Database, 1)
fmt.Fprintf(sb, "%s Identifier %s\n", indent, n.Database)
}

func explainDescribeQuery(sb *strings.Builder, n *ast.DescribeQuery, indent string) {
Expand Down Expand Up @@ -422,6 +437,19 @@ func explainDescribeQuery(sb *strings.Builder, n *ast.DescribeQuery, indent stri
}
}

func explainExistsTableQuery(sb *strings.Builder, n *ast.ExistsQuery, indent string) {
// EXISTS TABLE/DATABASE/DICTIONARY query
name := n.Table
if n.Database != "" {
name = n.Database + " " + n.Table
}
fmt.Fprintf(sb, "%sExistsTableQuery %s (children %d)\n", indent, name, 2)
if n.Database != "" {
fmt.Fprintf(sb, "%s Identifier %s\n", indent, n.Database)
}
fmt.Fprintf(sb, "%s Identifier %s\n", indent, n.Table)
}

func explainDataType(sb *strings.Builder, n *ast.DataType, indent string, depth int) {
// If type has parameters, expand them as children
if len(n.Parameters) > 0 {
Expand Down
2 changes: 1 addition & 1 deletion parser/testdata/00619_extract/metadata.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"todo": true}
{}
2 changes: 1 addition & 1 deletion parser/testdata/00950_dict_get/metadata.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"todo": true}
{}
2 changes: 1 addition & 1 deletion parser/testdata/01048_exists_query/metadata.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"todo": true}
{}
2 changes: 1 addition & 1 deletion parser/testdata/01161_information_schema/metadata.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"todo": true}
{}
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"todo": true}
{}
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"todo": true}
{}