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/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/internal/explain/statements.go b/internal/explain/statements.go index 83c8bf627..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) @@ -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) { @@ -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 { 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} +{} 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} +{} 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} +{}