From aaad34a7da845e1d0984755f24140c417fbf0ab9 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 26 Dec 2025 07:55:53 +0000 Subject: [PATCH 1/3] Fix 4 parser tests with EXPLAIN AST improvements - Fix EXTRACT function mapping to use ClickHouse's exact function names (e.g., toDayOfMonth instead of toDay for EXTRACT(DAY FROM ...)) - Fix UseQuery explain format to output UseQuery with children count and Identifier child - Add ExistsTableQuery explain handler for EXISTS TABLE/DATABASE/DICTIONARY - Fix ShowTables FROM database to include database identifier as child Tests enabled: - 00619_extract - 00950_dict_get - 01048_exists_query - 01161_information_schema --- internal/explain/explain.go | 2 ++ internal/explain/functions.go | 28 ++++++++++++++++++- internal/explain/statements.go | 23 ++++++++++++++- parser/testdata/00619_extract/metadata.json | 2 +- parser/testdata/00950_dict_get/metadata.json | 2 +- .../testdata/01048_exists_query/metadata.json | 2 +- .../01161_information_schema/metadata.json | 2 +- 7 files changed, 55 insertions(+), 6 deletions(-) diff --git a/internal/explain/explain.go b/internal/explain/explain.go index ce2bc8985..49afa0c09 100644 --- a/internal/explain/explain.go +++ b/internal/explain/explain.go @@ -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: diff --git a/internal/explain/functions.go b/internal/explain/functions.go index 028126a4b..cd5aa6364 100644 --- a/internal/explain/functions.go +++ b/internal/explain/functions.go @@ -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 diff --git a/internal/explain/statements.go b/internal/explain/statements.go index 83c8bf627..be3de1eb9 100644 --- a/internal/explain/statements.go +++ b/internal/explain/statements.go @@ -383,11 +383,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) { @@ -422,6 +430,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 { diff --git a/parser/testdata/00619_extract/metadata.json b/parser/testdata/00619_extract/metadata.json index ef120d978..0967ef424 100644 --- a/parser/testdata/00619_extract/metadata.json +++ b/parser/testdata/00619_extract/metadata.json @@ -1 +1 @@ -{"todo": true} +{} diff --git a/parser/testdata/00950_dict_get/metadata.json b/parser/testdata/00950_dict_get/metadata.json index ef120d978..0967ef424 100644 --- a/parser/testdata/00950_dict_get/metadata.json +++ b/parser/testdata/00950_dict_get/metadata.json @@ -1 +1 @@ -{"todo": true} +{} diff --git a/parser/testdata/01048_exists_query/metadata.json b/parser/testdata/01048_exists_query/metadata.json index ef120d978..0967ef424 100644 --- a/parser/testdata/01048_exists_query/metadata.json +++ b/parser/testdata/01048_exists_query/metadata.json @@ -1 +1 @@ -{"todo": true} +{} diff --git a/parser/testdata/01161_information_schema/metadata.json b/parser/testdata/01161_information_schema/metadata.json index ef120d978..0967ef424 100644 --- a/parser/testdata/01161_information_schema/metadata.json +++ b/parser/testdata/01161_information_schema/metadata.json @@ -1 +1 @@ -{"todo": true} +{} From bd78043c1467df43fb8f8ff6bcae66fa35a8a4c3 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 26 Dec 2025 07:59:12 +0000 Subject: [PATCH 2/3] Fix SHOW CREATE DATABASE explain output format Add special handling for ShowCreateDatabaseQuery in explainShowQuery to produce the expected output format with database identifier. --- internal/explain/statements.go | 7 +++++++ .../02206_information_schema_show_database/metadata.json | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/internal/explain/statements.go b/internal/explain/statements.go index be3de1eb9..06de66ffc 100644 --- a/internal/explain/statements.go +++ b/internal/explain/statements.go @@ -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) diff --git a/parser/testdata/02206_information_schema_show_database/metadata.json b/parser/testdata/02206_information_schema_show_database/metadata.json index ef120d978..0967ef424 100644 --- a/parser/testdata/02206_information_schema_show_database/metadata.json +++ b/parser/testdata/02206_information_schema_show_database/metadata.json @@ -1 +1 @@ -{"todo": true} +{} From b3f71176229a7252f987322c205af4e6b345ee3b Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 26 Dec 2025 08:17:05 +0000 Subject: [PATCH 3/3] Add COLLATE support to ORDER BY explain output OrderByElement now outputs the COLLATE clause as a child Literal when a collation is specified. --- internal/explain/select.go | 7 +++++++ .../01354_order_by_tuple_collate_const/metadata.json | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/internal/explain/select.go b/internal/explain/select.go index c90ad34e5..85fc9632e 100644 --- a/internal/explain/select.go +++ b/internal/explain/select.go @@ -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 { @@ -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) + } } } diff --git a/parser/testdata/01354_order_by_tuple_collate_const/metadata.json b/parser/testdata/01354_order_by_tuple_collate_const/metadata.json index ef120d978..0967ef424 100644 --- a/parser/testdata/01354_order_by_tuple_collate_const/metadata.json +++ b/parser/testdata/01354_order_by_tuple_collate_const/metadata.json @@ -1 +1 @@ -{"todo": true} +{}