From 443aba5e666e0a292e309318b3a0539f36b3947f Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 30 Dec 2025 20:42:33 +0000 Subject: [PATCH 01/18] Add CREATE/ALTER/DROP/SHOW CREATE ROW POLICY support Implement parsing and explain output for ROW POLICY statements: - CREATE ROW POLICY / CREATE POLICY - ALTER ROW POLICY / ALTER POLICY - DROP ROW POLICY / DROP POLICY - SHOW CREATE ROW POLICY / SHOW CREATE POLICY The explain output matches ClickHouse's format: - CREATE/ALTER -> "CREATE ROW POLICY or ALTER ROW POLICY query" - DROP -> "DROP ROW POLICY query" - SHOW CREATE -> "SHOW CREATE ROW POLICY query" This fixes 57 of 60 failing statements in 01295_create_row_policy test. The remaining 3 failures are for ROLE statements (not ROW POLICY). --- ast/ast.go | 29 ++++ internal/explain/explain.go | 6 + parser/parser.go | 125 +++++++++++++++++- .../metadata.json | 6 +- .../01295_create_row_policy/metadata.json | 59 +-------- .../metadata.json | 42 +----- .../metadata.json | 8 +- .../01702_system_query_log/metadata.json | 6 - .../01710_projection_row_policy/metadata.json | 7 +- .../testdata/01710_projections/metadata.json | 3 - .../metadata.json | 8 +- .../metadata.json | 20 +-- .../metadata.json | 20 +-- .../metadata.json | 11 +- .../metadata.json | 8 +- .../metadata.json | 7 +- .../metadata.json | 8 +- .../metadata.json | 11 +- .../metadata.json | 2 +- .../metadata.json | 6 +- .../metadata.json | 8 +- .../02864_statistics_bug_67742/metadata.json | 9 +- .../02864_statistics_bug_69589/metadata.json | 3 +- .../metadata.json | 10 +- .../metadata.json | 2 - .../02864_statistics_predicates/metadata.json | 6 +- .../02864_statistics_usage/metadata.json | 4 - .../02911_row_policy_on_cluster/metadata.json | 8 +- .../metadata.json | 5 +- .../metadata.json | 2 - .../metadata.json | 6 +- .../03580_improve_prewhere/metadata.json | 1 - .../metadata.json | 5 +- .../03625_auto_statistics/metadata.json | 1 - .../03625_auto_statistics_alter/metadata.json | 9 +- .../metadata.json | 9 +- .../03625_auto_statistics_rmt/metadata.json | 1 - .../03641_analyzer_issue_85834/metadata.json | 6 +- .../03707_statistics_cache/metadata.json | 15 +-- .../03743_fix_estimator_crash/metadata.json | 2 - 40 files changed, 187 insertions(+), 317 deletions(-) diff --git a/ast/ast.go b/ast/ast.go index 3304569abd..ddfd7e40bf 100644 --- a/ast/ast.go +++ b/ast/ast.go @@ -901,6 +901,35 @@ func (s *ShowCreateSettingsProfileQuery) Pos() token.Position { return s.Positio func (s *ShowCreateSettingsProfileQuery) End() token.Position { return s.Position } func (s *ShowCreateSettingsProfileQuery) statementNode() {} +// CreateRowPolicyQuery represents a CREATE ROW POLICY or ALTER ROW POLICY statement. +type CreateRowPolicyQuery struct { + Position token.Position `json:"-"` + IsAlter bool `json:"is_alter,omitempty"` +} + +func (c *CreateRowPolicyQuery) Pos() token.Position { return c.Position } +func (c *CreateRowPolicyQuery) End() token.Position { return c.Position } +func (c *CreateRowPolicyQuery) statementNode() {} + +// DropRowPolicyQuery represents a DROP ROW POLICY statement. +type DropRowPolicyQuery struct { + Position token.Position `json:"-"` + IfExists bool `json:"if_exists,omitempty"` +} + +func (d *DropRowPolicyQuery) Pos() token.Position { return d.Position } +func (d *DropRowPolicyQuery) End() token.Position { return d.Position } +func (d *DropRowPolicyQuery) statementNode() {} + +// ShowCreateRowPolicyQuery represents a SHOW CREATE ROW POLICY statement. +type ShowCreateRowPolicyQuery struct { + Position token.Position `json:"-"` +} + +func (s *ShowCreateRowPolicyQuery) Pos() token.Position { return s.Position } +func (s *ShowCreateRowPolicyQuery) End() token.Position { return s.Position } +func (s *ShowCreateRowPolicyQuery) statementNode() {} + // CreateIndexQuery represents a CREATE INDEX statement. type CreateIndexQuery struct { Position token.Position `json:"-"` diff --git a/internal/explain/explain.go b/internal/explain/explain.go index d2fd2c982d..d416a35ef1 100644 --- a/internal/explain/explain.go +++ b/internal/explain/explain.go @@ -143,6 +143,12 @@ func Node(sb *strings.Builder, node interface{}, depth int) { } else { fmt.Fprintf(sb, "%sSHOW CREATE SETTINGS PROFILE query\n", indent) } + case *ast.CreateRowPolicyQuery: + fmt.Fprintf(sb, "%sCREATE ROW POLICY or ALTER ROW POLICY query\n", indent) + case *ast.DropRowPolicyQuery: + fmt.Fprintf(sb, "%sDROP ROW POLICY query\n", indent) + case *ast.ShowCreateRowPolicyQuery: + fmt.Fprintf(sb, "%sSHOW CREATE ROW POLICY query\n", indent) case *ast.ShowGrantsQuery: fmt.Fprintf(sb, "%sShowGrantsQuery\n", indent) case *ast.GrantQuery: diff --git a/parser/parser.go b/parser/parser.go index ca37f8a0b5..a5ce2ba836 100644 --- a/parser/parser.go +++ b/parser/parser.go @@ -131,6 +131,10 @@ func (p *Parser) parseStatement() ast.Statement { if p.peek.Token == token.IDENT && strings.ToUpper(p.peek.Value) == "PROFILE" { return p.parseDropSettingsProfile() } + // Check for DROP ROW POLICY or DROP POLICY + if p.peek.Token == token.IDENT && (strings.ToUpper(p.peek.Value) == "ROW" || strings.ToUpper(p.peek.Value) == "POLICY") { + return p.parseDropRowPolicy() + } return p.parseDrop() case token.ALTER: // Check for ALTER USER @@ -145,6 +149,10 @@ func (p *Parser) parseStatement() ast.Statement { if p.peek.Token == token.IDENT && strings.ToUpper(p.peek.Value) == "PROFILE" { return p.parseAlterSettingsProfile() } + // Check for ALTER ROW POLICY or ALTER POLICY + if p.peek.Token == token.IDENT && (strings.ToUpper(p.peek.Value) == "ROW" || strings.ToUpper(p.peek.Value) == "POLICY") { + return p.parseAlterRowPolicy() + } return p.parseAlter() case token.TRUNCATE: return p.parseTruncate() @@ -1352,7 +1360,13 @@ func (p *Parser) parseCreate() ast.Statement { case "PROFILE": // CREATE PROFILE (without SETTINGS keyword) return p.parseCreateSettingsProfile(pos) - case "RESOURCE", "WORKLOAD", "POLICY", "ROLE", "QUOTA": + case "ROW": + // CREATE ROW POLICY + return p.parseCreateRowPolicy(pos) + case "POLICY": + // CREATE POLICY (without ROW keyword) + return p.parseCreateRowPolicy(pos) + case "RESOURCE", "WORKLOAD", "ROLE", "QUOTA": // Skip these statements - just consume tokens until semicolon p.parseCreateGeneric(create) default: @@ -2064,6 +2078,112 @@ func (p *Parser) parseShowCreateSettingsProfile(pos token.Position) *ast.ShowCre return query } +func (p *Parser) parseCreateRowPolicy(pos token.Position) *ast.CreateRowPolicyQuery { + query := &ast.CreateRowPolicyQuery{ + Position: pos, + } + + // Skip ROW if present (CREATE ROW POLICY vs CREATE POLICY) + if p.currentIs(token.IDENT) && strings.ToUpper(p.current.Value) == "ROW" { + p.nextToken() + } + + // Skip POLICY + if p.currentIs(token.IDENT) && strings.ToUpper(p.current.Value) == "POLICY" { + p.nextToken() + } + + // Skip the rest of the statement (policy names, ON table, etc.) + for !p.currentIs(token.EOF) && !p.currentIs(token.SEMICOLON) { + p.nextToken() + } + + return query +} + +func (p *Parser) parseDropRowPolicy() *ast.DropRowPolicyQuery { + query := &ast.DropRowPolicyQuery{ + Position: p.current.Pos, + } + + p.nextToken() // skip DROP + + // Skip ROW if present (DROP ROW POLICY vs DROP POLICY) + if p.currentIs(token.IDENT) && strings.ToUpper(p.current.Value) == "ROW" { + p.nextToken() + } + + // Skip POLICY + if p.currentIs(token.IDENT) && strings.ToUpper(p.current.Value) == "POLICY" { + p.nextToken() + } + + // Handle IF EXISTS + if p.currentIs(token.IF) { + p.nextToken() + if p.currentIs(token.EXISTS) { + query.IfExists = true + p.nextToken() + } + } + + // Skip the rest of the statement (policy names, ON table, etc.) + for !p.currentIs(token.EOF) && !p.currentIs(token.SEMICOLON) { + p.nextToken() + } + + return query +} + +func (p *Parser) parseAlterRowPolicy() *ast.CreateRowPolicyQuery { + query := &ast.CreateRowPolicyQuery{ + Position: p.current.Pos, + IsAlter: true, + } + + p.nextToken() // skip ALTER + + // Skip ROW if present (ALTER ROW POLICY vs ALTER POLICY) + if p.currentIs(token.IDENT) && strings.ToUpper(p.current.Value) == "ROW" { + p.nextToken() + } + + // Skip POLICY + if p.currentIs(token.IDENT) && strings.ToUpper(p.current.Value) == "POLICY" { + p.nextToken() + } + + // Skip the rest of the statement (policy names, ON table, etc.) + for !p.currentIs(token.EOF) && !p.currentIs(token.SEMICOLON) { + p.nextToken() + } + + return query +} + +func (p *Parser) parseShowCreateRowPolicy(pos token.Position) *ast.ShowCreateRowPolicyQuery { + query := &ast.ShowCreateRowPolicyQuery{ + Position: pos, + } + + // Skip ROW if present (SHOW CREATE ROW POLICY vs SHOW CREATE POLICY) + if p.currentIs(token.IDENT) && strings.ToUpper(p.current.Value) == "ROW" { + p.nextToken() + } + + // Skip POLICY + if p.currentIs(token.IDENT) && strings.ToUpper(p.current.Value) == "POLICY" { + p.nextToken() + } + + // Skip the rest of the statement (policy names, ON table, etc.) + for !p.currentIs(token.EOF) && !p.currentIs(token.SEMICOLON) { + p.nextToken() + } + + return query +} + func (p *Parser) parseCreateDictionary(create *ast.CreateQuery) { // Handle IF NOT EXISTS if p.currentIs(token.IF) { @@ -3717,6 +3837,9 @@ func (p *Parser) parseShow() ast.Statement { } else if p.currentIs(token.SETTINGS) || (p.currentIs(token.IDENT) && strings.ToUpper(p.current.Value) == "PROFILE") { // SHOW CREATE SETTINGS PROFILE or SHOW CREATE PROFILE return p.parseShowCreateSettingsProfile(pos) + } else if p.currentIs(token.IDENT) && (strings.ToUpper(p.current.Value) == "ROW" || strings.ToUpper(p.current.Value) == "POLICY") { + // SHOW CREATE ROW POLICY or SHOW CREATE POLICY + return p.parseShowCreateRowPolicy(pos) } else if p.currentIs(token.IDENT) && strings.ToUpper(p.current.Value) == "DICTIONARY" { show.ShowType = ast.ShowCreateDictionary p.nextToken() diff --git a/parser/testdata/00910_buffer_prewhere_different_types/metadata.json b/parser/testdata/00910_buffer_prewhere_different_types/metadata.json index ab9202e88e..0967ef424b 100644 --- a/parser/testdata/00910_buffer_prewhere_different_types/metadata.json +++ b/parser/testdata/00910_buffer_prewhere_different_types/metadata.json @@ -1,5 +1 @@ -{ - "explain_todo": { - "stmt11": true - } -} +{} diff --git a/parser/testdata/01295_create_row_policy/metadata.json b/parser/testdata/01295_create_row_policy/metadata.json index 9577879dfa..07b15c5528 100644 --- a/parser/testdata/01295_create_row_policy/metadata.json +++ b/parser/testdata/01295_create_row_policy/metadata.json @@ -1,64 +1,7 @@ { "explain_todo": { - "stmt1": true, - "stmt11": true, - "stmt12": true, - "stmt13": true, - "stmt14": true, - "stmt16": true, - "stmt17": true, - "stmt18": true, - "stmt19": true, - "stmt2": true, - "stmt21": true, - "stmt22": true, - "stmt23": true, - "stmt24": true, - "stmt25": true, - "stmt26": true, - "stmt27": true, - "stmt28": true, - "stmt29": true, - "stmt3": true, "stmt31": true, - "stmt33": true, - "stmt34": true, - "stmt35": true, - "stmt36": true, - "stmt37": true, - "stmt38": true, - "stmt39": true, - "stmt4": true, - "stmt40": true, - "stmt41": true, - "stmt42": true, - "stmt43": true, - "stmt44": true, - "stmt45": true, - "stmt46": true, - "stmt47": true, - "stmt48": true, - "stmt49": true, - "stmt50": true, - "stmt51": true, - "stmt53": true, - "stmt54": true, - "stmt55": true, - "stmt56": true, - "stmt57": true, - "stmt58": true, - "stmt59": true, "stmt6": true, - "stmt60": true, - "stmt61": true, - "stmt62": true, - "stmt63": true, - "stmt65": true, - "stmt66": true, - "stmt67": true, - "stmt69": true, - "stmt70": true, - "stmt8": true, - "stmt9": true + "stmt70": true } } diff --git a/parser/testdata/01296_create_row_policy_in_current_database/metadata.json b/parser/testdata/01296_create_row_policy_in_current_database/metadata.json index 1e76857ca4..0967ef424b 100644 --- a/parser/testdata/01296_create_row_policy_in_current_database/metadata.json +++ b/parser/testdata/01296_create_row_policy_in_current_database/metadata.json @@ -1,41 +1 @@ -{ - "explain_todo": { - "stmt1": true, - "stmt10": true, - "stmt11": true, - "stmt12": true, - "stmt13": true, - "stmt14": true, - "stmt15": true, - "stmt17": true, - "stmt19": true, - "stmt2": true, - "stmt20": true, - "stmt21": true, - "stmt22": true, - "stmt23": true, - "stmt24": true, - "stmt25": true, - "stmt26": true, - "stmt27": true, - "stmt28": true, - "stmt29": true, - "stmt30": true, - "stmt31": true, - "stmt32": true, - "stmt33": true, - "stmt34": true, - "stmt35": true, - "stmt36": true, - "stmt37": true, - "stmt38": true, - "stmt39": true, - "stmt40": true, - "stmt41": true, - "stmt42": true, - "stmt43": true, - "stmt44": true, - "stmt8": true, - "stmt9": true - } -} +{} diff --git a/parser/testdata/01308_row_policy_and_trivial_count_query/metadata.json b/parser/testdata/01308_row_policy_and_trivial_count_query/metadata.json index 522dceb54a..0967ef424b 100644 --- a/parser/testdata/01308_row_policy_and_trivial_count_query/metadata.json +++ b/parser/testdata/01308_row_policy_and_trivial_count_query/metadata.json @@ -1,7 +1 @@ -{ - "explain_todo": { - "stmt6": true, - "stmt7": true, - "stmt9": true - } -} +{} diff --git a/parser/testdata/01702_system_query_log/metadata.json b/parser/testdata/01702_system_query_log/metadata.json index e7f39f194f..c201dfd43b 100644 --- a/parser/testdata/01702_system_query_log/metadata.json +++ b/parser/testdata/01702_system_query_log/metadata.json @@ -3,8 +3,6 @@ "stmt14": true, "stmt15": true, "stmt16": true, - "stmt17": true, - "stmt18": true, "stmt19": true, "stmt24": true, "stmt26": true, @@ -38,16 +36,12 @@ "stmt57": true, "stmt58": true, "stmt59": true, - "stmt6": true, "stmt60": true, "stmt68": true, "stmt69": true, - "stmt7": true, "stmt74": true, "stmt8": true, "stmt83": true, - "stmt84": true, - "stmt85": true, "stmt86": true } } diff --git a/parser/testdata/01710_projection_row_policy/metadata.json b/parser/testdata/01710_projection_row_policy/metadata.json index 943b275814..0967ef424b 100644 --- a/parser/testdata/01710_projection_row_policy/metadata.json +++ b/parser/testdata/01710_projection_row_policy/metadata.json @@ -1,6 +1 @@ -{ - "explain_todo": { - "stmt4": true, - "stmt6": true - } -} +{} diff --git a/parser/testdata/01710_projections/metadata.json b/parser/testdata/01710_projections/metadata.json index cac2f57748..75fec6a18f 100644 --- a/parser/testdata/01710_projections/metadata.json +++ b/parser/testdata/01710_projections/metadata.json @@ -1,8 +1,5 @@ { "explain_todo": { - "stmt10": true, - "stmt11": true, - "stmt13": true, "stmt2": true, "stmt22": true } diff --git a/parser/testdata/01851_fix_row_policy_empty_result/metadata.json b/parser/testdata/01851_fix_row_policy_empty_result/metadata.json index 97c64968a0..0967ef424b 100644 --- a/parser/testdata/01851_fix_row_policy_empty_result/metadata.json +++ b/parser/testdata/01851_fix_row_policy_empty_result/metadata.json @@ -1,7 +1 @@ -{ - "explain_todo": { - "stmt4": true, - "stmt5": true, - "stmt7": true - } -} +{} diff --git a/parser/testdata/02131_row_policies_combination/metadata.json b/parser/testdata/02131_row_policies_combination/metadata.json index ccc4061606..0967ef424b 100644 --- a/parser/testdata/02131_row_policies_combination/metadata.json +++ b/parser/testdata/02131_row_policies_combination/metadata.json @@ -1,19 +1 @@ -{ - "explain_todo": { - "stmt11": true, - "stmt14": true, - "stmt17": true, - "stmt26": true, - "stmt29": true, - "stmt32": true, - "stmt35": true, - "stmt38": true, - "stmt4": true, - "stmt41": true, - "stmt44": true, - "stmt5": true, - "stmt6": true, - "stmt7": true, - "stmt8": true - } -} +{} diff --git a/parser/testdata/02131_used_row_policies_in_query_log/metadata.json b/parser/testdata/02131_used_row_policies_in_query_log/metadata.json index 2e0bbb28aa..0967ef424b 100644 --- a/parser/testdata/02131_used_row_policies_in_query_log/metadata.json +++ b/parser/testdata/02131_used_row_policies_in_query_log/metadata.json @@ -1,19 +1 @@ -{ - "explain_todo": { - "stmt11": true, - "stmt14": true, - "stmt17": true, - "stmt20": true, - "stmt23": true, - "stmt26": true, - "stmt29": true, - "stmt32": true, - "stmt35": true, - "stmt38": true, - "stmt4": true, - "stmt5": true, - "stmt6": true, - "stmt7": true, - "stmt8": true - } -} +{} diff --git a/parser/testdata/02319_no_columns_in_row_level_filter/metadata.json b/parser/testdata/02319_no_columns_in_row_level_filter/metadata.json index a8d8626b95..0967ef424b 100644 --- a/parser/testdata/02319_no_columns_in_row_level_filter/metadata.json +++ b/parser/testdata/02319_no_columns_in_row_level_filter/metadata.json @@ -1,10 +1 @@ -{ - "explain_todo": { - "stmt1": true, - "stmt11": true, - "stmt2": true, - "stmt20": true, - "stmt21": true, - "stmt6": true - } -} +{} diff --git a/parser/testdata/02416_row_policy_always_false_index/metadata.json b/parser/testdata/02416_row_policy_always_false_index/metadata.json index 09a911427f..0967ef424b 100644 --- a/parser/testdata/02416_row_policy_always_false_index/metadata.json +++ b/parser/testdata/02416_row_policy_always_false_index/metadata.json @@ -1,7 +1 @@ -{ - "explain_todo": { - "stmt4": true, - "stmt5": true, - "stmt8": true - } -} +{} diff --git a/parser/testdata/02460_prewhere_row_level_policy/metadata.json b/parser/testdata/02460_prewhere_row_level_policy/metadata.json index f5dd12602b..0967ef424b 100644 --- a/parser/testdata/02460_prewhere_row_level_policy/metadata.json +++ b/parser/testdata/02460_prewhere_row_level_policy/metadata.json @@ -1,6 +1 @@ -{ - "explain_todo": { - "stmt2": true, - "stmt5": true - } -} +{} diff --git a/parser/testdata/02481_default_value_used_in_row_level_filter/metadata.json b/parser/testdata/02481_default_value_used_in_row_level_filter/metadata.json index 2a5c198ffd..0967ef424b 100644 --- a/parser/testdata/02481_default_value_used_in_row_level_filter/metadata.json +++ b/parser/testdata/02481_default_value_used_in_row_level_filter/metadata.json @@ -1,7 +1 @@ -{ - "explain_todo": { - "stmt10": true, - "stmt6": true, - "stmt7": true - } -} +{} diff --git a/parser/testdata/02559_multiple_read_steps_in_prewhere/metadata.json b/parser/testdata/02559_multiple_read_steps_in_prewhere/metadata.json index 26453bacf9..0967ef424b 100644 --- a/parser/testdata/02559_multiple_read_steps_in_prewhere/metadata.json +++ b/parser/testdata/02559_multiple_read_steps_in_prewhere/metadata.json @@ -1,10 +1 @@ -{ - "explain_todo": { - "stmt24": true, - "stmt26": true, - "stmt28": true, - "stmt29": true, - "stmt4": true, - "stmt5": true - } -} +{} diff --git a/parser/testdata/02679_explain_merge_tree_prewhere_row_policy/metadata.json b/parser/testdata/02679_explain_merge_tree_prewhere_row_policy/metadata.json index 7ee9a1f9ac..0967ef424b 100644 --- a/parser/testdata/02679_explain_merge_tree_prewhere_row_policy/metadata.json +++ b/parser/testdata/02679_explain_merge_tree_prewhere_row_policy/metadata.json @@ -1 +1 @@ -{"explain_todo":{"stmt4":true,"stmt5":true,"stmt8":true}} +{} diff --git a/parser/testdata/02720_row_policy_column_with_dots/metadata.json b/parser/testdata/02720_row_policy_column_with_dots/metadata.json index b65b07d7a6..0967ef424b 100644 --- a/parser/testdata/02720_row_policy_column_with_dots/metadata.json +++ b/parser/testdata/02720_row_policy_column_with_dots/metadata.json @@ -1,5 +1 @@ -{ - "explain_todo": { - "stmt4": true - } -} +{} diff --git a/parser/testdata/02833_multiprewhere_extra_column/metadata.json b/parser/testdata/02833_multiprewhere_extra_column/metadata.json index 1c966db2fb..0967ef424b 100644 --- a/parser/testdata/02833_multiprewhere_extra_column/metadata.json +++ b/parser/testdata/02833_multiprewhere_extra_column/metadata.json @@ -1,7 +1 @@ -{ - "explain_todo": { - "stmt11": true, - "stmt2": true, - "stmt4": true - } -} +{} diff --git a/parser/testdata/02864_statistics_bug_67742/metadata.json b/parser/testdata/02864_statistics_bug_67742/metadata.json index bdef5dcec2..0967ef424b 100644 --- a/parser/testdata/02864_statistics_bug_67742/metadata.json +++ b/parser/testdata/02864_statistics_bug_67742/metadata.json @@ -1,8 +1 @@ -{ - "explain_todo": { - "stmt10": true, - "stmt15": true, - "stmt20": true, - "stmt5": true - } -} +{} diff --git a/parser/testdata/02864_statistics_bug_69589/metadata.json b/parser/testdata/02864_statistics_bug_69589/metadata.json index 6bf8d5b80a..3a06a4a1ac 100644 --- a/parser/testdata/02864_statistics_bug_69589/metadata.json +++ b/parser/testdata/02864_statistics_bug_69589/metadata.json @@ -1,6 +1,5 @@ { "explain_todo": { - "stmt5": true, - "stmt7": true + "stmt5": true } } diff --git a/parser/testdata/02864_statistics_create_materialize_drop/metadata.json b/parser/testdata/02864_statistics_create_materialize_drop/metadata.json index 90acd3216d..0967ef424b 100644 --- a/parser/testdata/02864_statistics_create_materialize_drop/metadata.json +++ b/parser/testdata/02864_statistics_create_materialize_drop/metadata.json @@ -1,9 +1 @@ -{ - "explain_todo": { - "stmt11": true, - "stmt12": true, - "stmt13": true, - "stmt15": true, - "stmt7": true - } -} +{} diff --git a/parser/testdata/02864_statistics_delayed_materialization_in_merge/metadata.json b/parser/testdata/02864_statistics_delayed_materialization_in_merge/metadata.json index 11ed0c7932..734bbd9a95 100644 --- a/parser/testdata/02864_statistics_delayed_materialization_in_merge/metadata.json +++ b/parser/testdata/02864_statistics_delayed_materialization_in_merge/metadata.json @@ -1,9 +1,7 @@ { "explain_todo": { "stmt12": true, - "stmt16": true, "stmt18": true, - "stmt6": true, "stmt9": true } } diff --git a/parser/testdata/02864_statistics_predicates/metadata.json b/parser/testdata/02864_statistics_predicates/metadata.json index b65b07d7a6..0967ef424b 100644 --- a/parser/testdata/02864_statistics_predicates/metadata.json +++ b/parser/testdata/02864_statistics_predicates/metadata.json @@ -1,5 +1 @@ -{ - "explain_todo": { - "stmt4": true - } -} +{} diff --git a/parser/testdata/02864_statistics_usage/metadata.json b/parser/testdata/02864_statistics_usage/metadata.json index 912e3621fc..a39db17e43 100644 --- a/parser/testdata/02864_statistics_usage/metadata.json +++ b/parser/testdata/02864_statistics_usage/metadata.json @@ -1,13 +1,9 @@ { "explain_todo": { - "stmt10": true, "stmt12": true, - "stmt14": true, - "stmt15": true, "stmt19": true, "stmt22": true, "stmt25": true, - "stmt6": true, "stmt9": true } } diff --git a/parser/testdata/02911_row_policy_on_cluster/metadata.json b/parser/testdata/02911_row_policy_on_cluster/metadata.json index da4a3c9f9e..0967ef424b 100644 --- a/parser/testdata/02911_row_policy_on_cluster/metadata.json +++ b/parser/testdata/02911_row_policy_on_cluster/metadata.json @@ -1,7 +1 @@ -{ - "explain_todo": { - "stmt1": true, - "stmt4": true, - "stmt5": true - } -} +{} diff --git a/parser/testdata/03006_parallel_replicas_prewhere/metadata.json b/parser/testdata/03006_parallel_replicas_prewhere/metadata.json index f8b119c922..1295a45747 100644 --- a/parser/testdata/03006_parallel_replicas_prewhere/metadata.json +++ b/parser/testdata/03006_parallel_replicas_prewhere/metadata.json @@ -1,8 +1,5 @@ { "explain_todo": { - "stmt1": true, - "stmt10": true, - "stmt3": true, - "stmt4": true + "stmt3": true } } diff --git a/parser/testdata/03279_join_choose_build_table_statistics/metadata.json b/parser/testdata/03279_join_choose_build_table_statistics/metadata.json index be4d02dac9..9d32d2ea78 100644 --- a/parser/testdata/03279_join_choose_build_table_statistics/metadata.json +++ b/parser/testdata/03279_join_choose_build_table_statistics/metadata.json @@ -1,7 +1,5 @@ { "explain_todo": { - "stmt15": true, - "stmt16": true, "stmt20": true, "stmt21": true, "stmt22": true, diff --git a/parser/testdata/03547_analyzer_correlated_columns_check_bug/metadata.json b/parser/testdata/03547_analyzer_correlated_columns_check_bug/metadata.json index 3a06a4a1ac..0967ef424b 100644 --- a/parser/testdata/03547_analyzer_correlated_columns_check_bug/metadata.json +++ b/parser/testdata/03547_analyzer_correlated_columns_check_bug/metadata.json @@ -1,5 +1 @@ -{ - "explain_todo": { - "stmt5": true - } -} +{} diff --git a/parser/testdata/03580_improve_prewhere/metadata.json b/parser/testdata/03580_improve_prewhere/metadata.json index 5f9c937a5b..bf70691c76 100644 --- a/parser/testdata/03580_improve_prewhere/metadata.json +++ b/parser/testdata/03580_improve_prewhere/metadata.json @@ -1,6 +1,5 @@ { "explain_todo": { - "stmt11": true, "stmt13": true, "stmt14": true, "stmt15": true diff --git a/parser/testdata/03591_optimize_prewhere_row_policy/metadata.json b/parser/testdata/03591_optimize_prewhere_row_policy/metadata.json index 6b6a2f2262..52f07a6b85 100644 --- a/parser/testdata/03591_optimize_prewhere_row_policy/metadata.json +++ b/parser/testdata/03591_optimize_prewhere_row_policy/metadata.json @@ -1,11 +1,8 @@ { "explain_todo": { - "stmt10": true, "stmt12": true, "stmt13": true, "stmt15": true, - "stmt16": true, - "stmt17": true, - "stmt5": true + "stmt16": true } } diff --git a/parser/testdata/03625_auto_statistics/metadata.json b/parser/testdata/03625_auto_statistics/metadata.json index 65dec676c2..3a352d68aa 100644 --- a/parser/testdata/03625_auto_statistics/metadata.json +++ b/parser/testdata/03625_auto_statistics/metadata.json @@ -1,7 +1,6 @@ { "explain_todo": { "stmt10": true, - "stmt3": true, "stmt4": true } } diff --git a/parser/testdata/03625_auto_statistics_alter/metadata.json b/parser/testdata/03625_auto_statistics_alter/metadata.json index 152e73dbe1..0967ef424b 100644 --- a/parser/testdata/03625_auto_statistics_alter/metadata.json +++ b/parser/testdata/03625_auto_statistics_alter/metadata.json @@ -1,8 +1 @@ -{ - "explain_todo": { - "stmt16": true, - "stmt19": true, - "stmt4": true, - "stmt9": true - } -} +{} diff --git a/parser/testdata/03625_auto_statistics_alter_rmt/metadata.json b/parser/testdata/03625_auto_statistics_alter_rmt/metadata.json index 152e73dbe1..0967ef424b 100644 --- a/parser/testdata/03625_auto_statistics_alter_rmt/metadata.json +++ b/parser/testdata/03625_auto_statistics_alter_rmt/metadata.json @@ -1,8 +1 @@ -{ - "explain_todo": { - "stmt16": true, - "stmt19": true, - "stmt4": true, - "stmt9": true - } -} +{} diff --git a/parser/testdata/03625_auto_statistics_rmt/metadata.json b/parser/testdata/03625_auto_statistics_rmt/metadata.json index 7d2781a8cf..15d79beb93 100644 --- a/parser/testdata/03625_auto_statistics_rmt/metadata.json +++ b/parser/testdata/03625_auto_statistics_rmt/metadata.json @@ -1,7 +1,6 @@ { "explain_todo": { "stmt11": true, - "stmt4": true, "stmt5": true } } diff --git a/parser/testdata/03641_analyzer_issue_85834/metadata.json b/parser/testdata/03641_analyzer_issue_85834/metadata.json index 3a06a4a1ac..0967ef424b 100644 --- a/parser/testdata/03641_analyzer_issue_85834/metadata.json +++ b/parser/testdata/03641_analyzer_issue_85834/metadata.json @@ -1,5 +1 @@ -{ - "explain_todo": { - "stmt5": true - } -} +{} diff --git a/parser/testdata/03707_statistics_cache/metadata.json b/parser/testdata/03707_statistics_cache/metadata.json index f31d890a35..0967ef424b 100644 --- a/parser/testdata/03707_statistics_cache/metadata.json +++ b/parser/testdata/03707_statistics_cache/metadata.json @@ -1,14 +1 @@ -{ - "explain_todo": { - "stmt11": true, - "stmt12": true, - "stmt16": true, - "stmt17": true, - "stmt24": true, - "stmt25": true, - "stmt35": true, - "stmt36": true, - "stmt37": true, - "stmt38": true - } -} +{} diff --git a/parser/testdata/03743_fix_estimator_crash/metadata.json b/parser/testdata/03743_fix_estimator_crash/metadata.json index ad61064d66..dbdbb76d4f 100644 --- a/parser/testdata/03743_fix_estimator_crash/metadata.json +++ b/parser/testdata/03743_fix_estimator_crash/metadata.json @@ -1,7 +1,5 @@ { "explain_todo": { - "stmt3": true, - "stmt5": true, "stmt6": true } } From 9a0b0ad33215da8da0518029729991a2003bde1c Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 30 Dec 2025 20:48:24 +0000 Subject: [PATCH 02/18] Fix window frame keywords being parsed as implicit aliases The PARTITION BY clause in window functions was incorrectly consuming window frame keywords like ROWS, RANGE, GROUPS as implicit aliases. This broke window function parsing for queries like: SELECT abs(number) over (partition by x rows unbounded preceding) Added window frame keywords to the exclusion list in parseImplicitAlias: - ROWS, RANGE, GROUPS (frame type) - UNBOUNDED, PRECEDING, FOLLOWING, CURRENT (frame bounds) --- parser/expression.go | 5 +++++ parser/testdata/01571_window_functions/metadata.json | 1 - parser/testdata/01591_window_functions/metadata.json | 2 -- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/parser/expression.go b/parser/expression.go index 49d007721f..688f5765c3 100644 --- a/parser/expression.go +++ b/parser/expression.go @@ -180,6 +180,11 @@ func (p *Parser) parseImplicitAlias(expr ast.Expression) ast.Expression { if upper == "INTERSECT" { return expr } + // Don't consume window frame keywords as implicit aliases + switch upper { + case "ROWS", "RANGE", "GROUPS", "UNBOUNDED", "PRECEDING", "FOLLOWING", "CURRENT": + return expr + } alias := p.current.Value p.nextToken() diff --git a/parser/testdata/01571_window_functions/metadata.json b/parser/testdata/01571_window_functions/metadata.json index 875fd7e8b1..1295a45747 100644 --- a/parser/testdata/01571_window_functions/metadata.json +++ b/parser/testdata/01571_window_functions/metadata.json @@ -1,6 +1,5 @@ { "explain_todo": { - "stmt12": true, "stmt3": true } } diff --git a/parser/testdata/01591_window_functions/metadata.json b/parser/testdata/01591_window_functions/metadata.json index 0abe7de429..1e881517d7 100644 --- a/parser/testdata/01591_window_functions/metadata.json +++ b/parser/testdata/01591_window_functions/metadata.json @@ -20,7 +20,6 @@ "stmt38": true, "stmt39": true, "stmt41": true, - "stmt45": true, "stmt50": true, "stmt51": true, "stmt52": true, @@ -39,7 +38,6 @@ "stmt77": true, "stmt78": true, "stmt79": true, - "stmt8": true, "stmt80": true, "stmt81": true, "stmt82": true, From 0994664d12b823de8e849523347ab31863045058 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 30 Dec 2025 20:54:15 +0000 Subject: [PATCH 03/18] Add window function support to keyword-as-function parsing When keywords like ANY, ALL, etc. are used as function names (e.g., any(number)), they were being parsed by parseKeywordAsFunction which didn't handle OVER clause for window functions. Added support for: - DISTINCT inside keyword functions - IGNORE NULLS / RESPECT NULLS modifiers - FILTER clause for aggregate functions - OVER clause for window functions This fixes parsing of queries like: SELECT any(number) OVER () FROM numbers(2); --- parser/expression.go | 52 ++++++++++++++++--- .../01591_window_functions/metadata.json | 1 - .../metadata.json | 6 +-- .../metadata.json | 9 +--- .../metadata.json | 6 +-- 5 files changed, 48 insertions(+), 26 deletions(-) diff --git a/parser/expression.go b/parser/expression.go index 688f5765c3..39f6df6e87 100644 --- a/parser/expression.go +++ b/parser/expression.go @@ -1897,22 +1897,60 @@ func (p *Parser) parseKeywordAsFunction() ast.Expression { return nil } - var args []ast.Expression + fn := &ast.FunctionCall{ + Position: pos, + Name: name, + } + + // Handle DISTINCT + if p.currentIs(token.DISTINCT) { + fn.Distinct = true + p.nextToken() + } + // Handle view() and similar functions that take a subquery as argument if name == "view" && (p.currentIs(token.SELECT) || p.currentIs(token.WITH)) { subquery := p.parseSelectWithUnion() - args = []ast.Expression{&ast.Subquery{Position: pos, Query: subquery}} + fn.Arguments = []ast.Expression{&ast.Subquery{Position: pos, Query: subquery}} } else if !p.currentIs(token.RPAREN) { - args = p.parseExpressionList() + fn.Arguments = p.parseExpressionList() } p.expect(token.RPAREN) - return &ast.FunctionCall{ - Position: pos, - Name: name, - Arguments: args, + // Handle IGNORE NULLS / RESPECT NULLS (window function modifiers) + for p.currentIs(token.IDENT) { + upper := strings.ToUpper(p.current.Value) + if upper == "IGNORE" || upper == "RESPECT" { + p.nextToken() + if p.currentIs(token.NULLS) { + p.nextToken() + } + } else { + break + } + } + + // Handle FILTER clause for aggregate functions: func() FILTER(WHERE condition) + if p.currentIs(token.IDENT) && strings.ToUpper(p.current.Value) == "FILTER" { + p.nextToken() // skip FILTER + if p.currentIs(token.LPAREN) { + p.nextToken() // skip ( + if p.currentIs(token.WHERE) { + p.nextToken() // skip WHERE + p.parseExpression(LOWEST) + } + p.expect(token.RPAREN) + } + } + + // Handle OVER clause for window functions + if p.currentIs(token.OVER) { + p.nextToken() + fn.Over = p.parseWindowSpec() } + + return fn } func (p *Parser) parseKeywordAsIdentifier() ast.Expression { diff --git a/parser/testdata/01591_window_functions/metadata.json b/parser/testdata/01591_window_functions/metadata.json index 1e881517d7..7fdde4e3aa 100644 --- a/parser/testdata/01591_window_functions/metadata.json +++ b/parser/testdata/01591_window_functions/metadata.json @@ -11,7 +11,6 @@ "stmt110": true, "stmt111": true, "stmt112": true, - "stmt28": true, "stmt31": true, "stmt34": true, "stmt35": true, diff --git a/parser/testdata/02900_window_function_with_sparse_column/metadata.json b/parser/testdata/02900_window_function_with_sparse_column/metadata.json index b563327205..0967ef424b 100644 --- a/parser/testdata/02900_window_function_with_sparse_column/metadata.json +++ b/parser/testdata/02900_window_function_with_sparse_column/metadata.json @@ -1,5 +1 @@ -{ - "explain_todo": { - "stmt7": true - } -} +{} diff --git a/parser/testdata/02922_respect_nulls_extensive/metadata.json b/parser/testdata/02922_respect_nulls_extensive/metadata.json index 3268fa43a8..d689cec729 100644 --- a/parser/testdata/02922_respect_nulls_extensive/metadata.json +++ b/parser/testdata/02922_respect_nulls_extensive/metadata.json @@ -1,13 +1,6 @@ { "explain_todo": { - "stmt36": true, - "stmt38": true, "stmt40": true, - "stmt41": true, - "stmt42": true, - "stmt44": true, - "stmt46": true, - "stmt47": true, - "stmt6": true + "stmt41": true } } diff --git a/parser/testdata/02941_any_RESPECT_NULL_sparse_column/metadata.json b/parser/testdata/02941_any_RESPECT_NULL_sparse_column/metadata.json index b65b07d7a6..0967ef424b 100644 --- a/parser/testdata/02941_any_RESPECT_NULL_sparse_column/metadata.json +++ b/parser/testdata/02941_any_RESPECT_NULL_sparse_column/metadata.json @@ -1,5 +1 @@ -{ - "explain_todo": { - "stmt4": true - } -} +{} From ea9dbc0f97e773ff35bad7051344bf39c6126ed7 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 30 Dec 2025 21:00:29 +0000 Subject: [PATCH 04/18] Add CREATE/ALTER/DROP/SHOW CREATE ROLE support Implement parsing and explain output for ROLE statements: - CREATE ROLE - ALTER ROLE - DROP ROLE - SHOW CREATE ROLE The explain output matches ClickHouse's format: - CREATE/ALTER -> "CreateRoleQuery" - DROP -> "DROP ROLE query" - SHOW CREATE (single) -> "SHOW CREATE ROLE query" - SHOW CREATE (multiple) -> "SHOW CREATE ROLES query" This fixes all 56 failing statements in 01293_create_role test and also fixes related ROLE statements in other test files. --- ast/ast.go | 30 ++++ internal/explain/explain.go | 11 ++ parser/parser.go | 129 +++++++++++++++++- .../testdata/01292_create_user/metadata.json | 5 +- .../testdata/01293_create_role/metadata.json | 61 +-------- .../metadata.json | 8 +- .../01295_create_row_policy/metadata.json | 8 +- .../01702_system_query_log/metadata.json | 3 - .../01999_grant_with_replace/metadata.json | 11 +- 9 files changed, 174 insertions(+), 92 deletions(-) diff --git a/ast/ast.go b/ast/ast.go index ddfd7e40bf..0370260222 100644 --- a/ast/ast.go +++ b/ast/ast.go @@ -930,6 +930,36 @@ func (s *ShowCreateRowPolicyQuery) Pos() token.Position { return s.Position } func (s *ShowCreateRowPolicyQuery) End() token.Position { return s.Position } func (s *ShowCreateRowPolicyQuery) statementNode() {} +// CreateRoleQuery represents a CREATE ROLE or ALTER ROLE statement. +type CreateRoleQuery struct { + Position token.Position `json:"-"` + IsAlter bool `json:"is_alter,omitempty"` +} + +func (c *CreateRoleQuery) Pos() token.Position { return c.Position } +func (c *CreateRoleQuery) End() token.Position { return c.Position } +func (c *CreateRoleQuery) statementNode() {} + +// DropRoleQuery represents a DROP ROLE statement. +type DropRoleQuery struct { + Position token.Position `json:"-"` + IfExists bool `json:"if_exists,omitempty"` +} + +func (d *DropRoleQuery) Pos() token.Position { return d.Position } +func (d *DropRoleQuery) End() token.Position { return d.Position } +func (d *DropRoleQuery) statementNode() {} + +// ShowCreateRoleQuery represents a SHOW CREATE ROLE statement. +type ShowCreateRoleQuery struct { + Position token.Position `json:"-"` + RoleCount int `json:"role_count,omitempty"` // Number of roles specified +} + +func (s *ShowCreateRoleQuery) Pos() token.Position { return s.Position } +func (s *ShowCreateRoleQuery) End() token.Position { return s.Position } +func (s *ShowCreateRoleQuery) statementNode() {} + // CreateIndexQuery represents a CREATE INDEX statement. type CreateIndexQuery struct { Position token.Position `json:"-"` diff --git a/internal/explain/explain.go b/internal/explain/explain.go index d416a35ef1..2f947f0a80 100644 --- a/internal/explain/explain.go +++ b/internal/explain/explain.go @@ -149,6 +149,17 @@ func Node(sb *strings.Builder, node interface{}, depth int) { fmt.Fprintf(sb, "%sDROP ROW POLICY query\n", indent) case *ast.ShowCreateRowPolicyQuery: fmt.Fprintf(sb, "%sSHOW CREATE ROW POLICY query\n", indent) + case *ast.CreateRoleQuery: + fmt.Fprintf(sb, "%sCreateRoleQuery\n", indent) + case *ast.DropRoleQuery: + fmt.Fprintf(sb, "%sDROP ROLE query\n", indent) + case *ast.ShowCreateRoleQuery: + // Use ROLES (plural) when multiple roles are specified + if n.RoleCount > 1 { + fmt.Fprintf(sb, "%sSHOW CREATE ROLES query\n", indent) + } else { + fmt.Fprintf(sb, "%sSHOW CREATE ROLE query\n", indent) + } case *ast.ShowGrantsQuery: fmt.Fprintf(sb, "%sShowGrantsQuery\n", indent) case *ast.GrantQuery: diff --git a/parser/parser.go b/parser/parser.go index a5ce2ba836..0c5aa03321 100644 --- a/parser/parser.go +++ b/parser/parser.go @@ -135,6 +135,10 @@ func (p *Parser) parseStatement() ast.Statement { if p.peek.Token == token.IDENT && (strings.ToUpper(p.peek.Value) == "ROW" || strings.ToUpper(p.peek.Value) == "POLICY") { return p.parseDropRowPolicy() } + // Check for DROP ROLE + if p.peek.Token == token.IDENT && strings.ToUpper(p.peek.Value) == "ROLE" { + return p.parseDropRole() + } return p.parseDrop() case token.ALTER: // Check for ALTER USER @@ -153,6 +157,10 @@ func (p *Parser) parseStatement() ast.Statement { if p.peek.Token == token.IDENT && (strings.ToUpper(p.peek.Value) == "ROW" || strings.ToUpper(p.peek.Value) == "POLICY") { return p.parseAlterRowPolicy() } + // Check for ALTER ROLE + if p.peek.Token == token.IDENT && strings.ToUpper(p.peek.Value) == "ROLE" { + return p.parseAlterRole() + } return p.parseAlter() case token.TRUNCATE: return p.parseTruncate() @@ -1366,7 +1374,10 @@ func (p *Parser) parseCreate() ast.Statement { case "POLICY": // CREATE POLICY (without ROW keyword) return p.parseCreateRowPolicy(pos) - case "RESOURCE", "WORKLOAD", "ROLE", "QUOTA": + case "ROLE": + // CREATE ROLE + return p.parseCreateRole(pos) + case "RESOURCE", "WORKLOAD", "QUOTA": // Skip these statements - just consume tokens until semicolon p.parseCreateGeneric(create) default: @@ -2184,6 +2195,119 @@ func (p *Parser) parseShowCreateRowPolicy(pos token.Position) *ast.ShowCreateRow return query } +func (p *Parser) parseCreateRole(pos token.Position) *ast.CreateRoleQuery { + query := &ast.CreateRoleQuery{ + Position: pos, + } + + // Skip ROLE + if p.currentIs(token.IDENT) && strings.ToUpper(p.current.Value) == "ROLE" { + p.nextToken() + } + + // Skip the rest of the statement (role names, SETTINGS, etc.) + for !p.currentIs(token.EOF) && !p.currentIs(token.SEMICOLON) { + p.nextToken() + } + + return query +} + +func (p *Parser) parseDropRole() *ast.DropRoleQuery { + query := &ast.DropRoleQuery{ + Position: p.current.Pos, + } + + p.nextToken() // skip DROP + + // Skip ROLE + if p.currentIs(token.IDENT) && strings.ToUpper(p.current.Value) == "ROLE" { + p.nextToken() + } + + // Handle IF EXISTS + if p.currentIs(token.IF) { + p.nextToken() + if p.currentIs(token.EXISTS) { + query.IfExists = true + p.nextToken() + } + } + + // Skip the rest of the statement (role names, etc.) + for !p.currentIs(token.EOF) && !p.currentIs(token.SEMICOLON) { + p.nextToken() + } + + return query +} + +func (p *Parser) parseAlterRole() *ast.CreateRoleQuery { + query := &ast.CreateRoleQuery{ + Position: p.current.Pos, + IsAlter: true, + } + + p.nextToken() // skip ALTER + + // Skip ROLE + if p.currentIs(token.IDENT) && strings.ToUpper(p.current.Value) == "ROLE" { + p.nextToken() + } + + // Skip the rest of the statement (role names, SETTINGS, RENAME TO, etc.) + for !p.currentIs(token.EOF) && !p.currentIs(token.SEMICOLON) { + p.nextToken() + } + + return query +} + +func (p *Parser) parseShowCreateRole(pos token.Position) *ast.ShowCreateRoleQuery { + query := &ast.ShowCreateRoleQuery{ + Position: pos, + RoleCount: 1, // Default to 1 role + } + + // Skip ROLE + if p.currentIs(token.IDENT) && strings.ToUpper(p.current.Value) == "ROLE" { + p.nextToken() + } + + // Count role names (separated by commas) + // Skip first role name + for p.currentIs(token.IDENT) || p.currentIs(token.STRING) || p.current.Token.IsKeyword() { + p.nextToken() + // Handle role@host syntax + if p.currentIs(token.IDENT) && strings.HasPrefix(p.current.Value, "@") { + p.nextToken() + } + break + } + + // Count additional roles + for p.currentIs(token.COMMA) { + query.RoleCount++ + p.nextToken() + // Skip role name + for p.currentIs(token.IDENT) || p.currentIs(token.STRING) || p.current.Token.IsKeyword() { + p.nextToken() + // Handle role@host syntax + if p.currentIs(token.IDENT) && strings.HasPrefix(p.current.Value, "@") { + p.nextToken() + } + break + } + } + + // Skip the rest of the statement + for !p.currentIs(token.EOF) && !p.currentIs(token.SEMICOLON) { + p.nextToken() + } + + return query +} + func (p *Parser) parseCreateDictionary(create *ast.CreateQuery) { // Handle IF NOT EXISTS if p.currentIs(token.IF) { @@ -3840,6 +3964,9 @@ func (p *Parser) parseShow() ast.Statement { } else if p.currentIs(token.IDENT) && (strings.ToUpper(p.current.Value) == "ROW" || strings.ToUpper(p.current.Value) == "POLICY") { // SHOW CREATE ROW POLICY or SHOW CREATE POLICY return p.parseShowCreateRowPolicy(pos) + } else if p.currentIs(token.IDENT) && strings.ToUpper(p.current.Value) == "ROLE" { + // SHOW CREATE ROLE + return p.parseShowCreateRole(pos) } else if p.currentIs(token.IDENT) && strings.ToUpper(p.current.Value) == "DICTIONARY" { show.ShowType = ast.ShowCreateDictionary p.nextToken() diff --git a/parser/testdata/01292_create_user/metadata.json b/parser/testdata/01292_create_user/metadata.json index 53603a8efe..1202e9052e 100644 --- a/parser/testdata/01292_create_user/metadata.json +++ b/parser/testdata/01292_create_user/metadata.json @@ -1,6 +1,5 @@ { "explain_todo": { - "stmt141": true, "stmt157": true, "stmt158": true, "stmt159": true, @@ -14,7 +13,6 @@ "stmt205": true, "stmt207": true, "stmt208": true, - "stmt219": true, "stmt22": true, "stmt221": true, "stmt23": true, @@ -29,7 +27,6 @@ "stmt40": true, "stmt41": true, "stmt42": true, - "stmt43": true, - "stmt6": true + "stmt43": true } } diff --git a/parser/testdata/01293_create_role/metadata.json b/parser/testdata/01293_create_role/metadata.json index 2ec9c7cc2c..0967ef424b 100644 --- a/parser/testdata/01293_create_role/metadata.json +++ b/parser/testdata/01293_create_role/metadata.json @@ -1,60 +1 @@ -{ - "explain_todo": { - "stmt1": true, - "stmt11": true, - "stmt12": true, - "stmt13": true, - "stmt14": true, - "stmt16": true, - "stmt17": true, - "stmt18": true, - "stmt19": true, - "stmt2": true, - "stmt20": true, - "stmt21": true, - "stmt22": true, - "stmt24": true, - "stmt25": true, - "stmt26": true, - "stmt27": true, - "stmt28": true, - "stmt29": true, - "stmt3": true, - "stmt30": true, - "stmt31": true, - "stmt32": true, - "stmt33": true, - "stmt34": true, - "stmt35": true, - "stmt36": true, - "stmt37": true, - "stmt38": true, - "stmt39": true, - "stmt40": true, - "stmt41": true, - "stmt42": true, - "stmt43": true, - "stmt44": true, - "stmt45": true, - "stmt46": true, - "stmt47": true, - "stmt48": true, - "stmt5": true, - "stmt50": true, - "stmt51": true, - "stmt52": true, - "stmt53": true, - "stmt54": true, - "stmt56": true, - "stmt58": true, - "stmt6": true, - "stmt60": true, - "stmt61": true, - "stmt62": true, - "stmt63": true, - "stmt64": true, - "stmt66": true, - "stmt8": true, - "stmt9": true - } -} +{} diff --git a/parser/testdata/01294_create_settings_profile/metadata.json b/parser/testdata/01294_create_settings_profile/metadata.json index cf94ac4194..0967ef424b 100644 --- a/parser/testdata/01294_create_settings_profile/metadata.json +++ b/parser/testdata/01294_create_settings_profile/metadata.json @@ -1,7 +1 @@ -{ - "explain_todo": { - "stmt108": true, - "stmt4": true, - "stmt47": true - } -} +{} diff --git a/parser/testdata/01295_create_row_policy/metadata.json b/parser/testdata/01295_create_row_policy/metadata.json index 07b15c5528..0967ef424b 100644 --- a/parser/testdata/01295_create_row_policy/metadata.json +++ b/parser/testdata/01295_create_row_policy/metadata.json @@ -1,7 +1 @@ -{ - "explain_todo": { - "stmt31": true, - "stmt6": true, - "stmt70": true - } -} +{} diff --git a/parser/testdata/01702_system_query_log/metadata.json b/parser/testdata/01702_system_query_log/metadata.json index c201dfd43b..e5ed7f989d 100644 --- a/parser/testdata/01702_system_query_log/metadata.json +++ b/parser/testdata/01702_system_query_log/metadata.json @@ -2,7 +2,6 @@ "explain_todo": { "stmt14": true, "stmt15": true, - "stmt16": true, "stmt19": true, "stmt24": true, "stmt26": true, @@ -25,7 +24,6 @@ "stmt45": true, "stmt46": true, "stmt49": true, - "stmt5": true, "stmt50": true, "stmt51": true, "stmt52": true, @@ -41,7 +39,6 @@ "stmt69": true, "stmt74": true, "stmt8": true, - "stmt83": true, "stmt86": true } } diff --git a/parser/testdata/01999_grant_with_replace/metadata.json b/parser/testdata/01999_grant_with_replace/metadata.json index f1cf49e8d2..0967ef424b 100644 --- a/parser/testdata/01999_grant_with_replace/metadata.json +++ b/parser/testdata/01999_grant_with_replace/metadata.json @@ -1,10 +1 @@ -{ - "explain_todo": { - "stmt26": true, - "stmt27": true, - "stmt47": true, - "stmt48": true, - "stmt52": true, - "stmt53": true - } -} +{} From 9785146af935359bf89eaeec30563bd7bf96e16b Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 30 Dec 2025 21:05:28 +0000 Subject: [PATCH 05/18] Add JSON subcolumn type accessor syntax support ClickHouse supports a special syntax for JSON path type casting: - json.field.:`TypeName` (backtick-quoted type) - json.field.:TypeName (unquoted type) These are parsed as part of identifier paths and represented as parts like ":`String`" or ":`Array(Nullable(Int64))`". This fixes ~100 failing statements across multiple JSON-related test files. --- parser/expression.go | 9 +++ .../01825_new_type_json_10/metadata.json | 7 +-- .../metadata.json | 36 +----------- .../metadata.json | 56 +------------------ .../metadata.json | 6 +- .../metadata.json | 6 +- .../metadata.json | 6 +- .../metadata.json | 6 +- .../metadata.json | 7 +-- .../metadata.json | 3 +- .../metadata.json | 6 +- .../metadata.json | 6 +- 12 files changed, 20 insertions(+), 134 deletions(-) diff --git a/parser/expression.go b/parser/expression.go index 39f6df6e87..f97de34c48 100644 --- a/parser/expression.go +++ b/parser/expression.go @@ -475,6 +475,15 @@ func (p *Parser) parseIdentifierOrFunction() ast.Expression { } else { break } + } else if p.currentIs(token.COLON) { + // JSON subcolumn type accessor: json.field.:`TypeName` or json.field.:TypeName + p.nextToken() // skip : + typePart := ":" + if p.currentIs(token.IDENT) || p.current.Token.IsKeyword() || p.currentIs(token.STRING) { + typePart += "`" + p.current.Value + "`" + p.nextToken() + } + parts = append(parts, typePart) } else if p.currentIs(token.IDENT) || p.current.Token.IsKeyword() { // Keywords can be used as column/field names (e.g., l_t.key, t.index) parts = append(parts, p.current.Value) diff --git a/parser/testdata/01825_new_type_json_10/metadata.json b/parser/testdata/01825_new_type_json_10/metadata.json index 92efb02376..0967ef424b 100644 --- a/parser/testdata/01825_new_type_json_10/metadata.json +++ b/parser/testdata/01825_new_type_json_10/metadata.json @@ -1,6 +1 @@ -{ - "explain_todo": { - "stmt6": true, - "stmt8": true - } -} +{} diff --git a/parser/testdata/03207_json_read_subcolumns_1_memory/metadata.json b/parser/testdata/03207_json_read_subcolumns_1_memory/metadata.json index a991583cbb..f04990226e 100644 --- a/parser/testdata/03207_json_read_subcolumns_1_memory/metadata.json +++ b/parser/testdata/03207_json_read_subcolumns_1_memory/metadata.json @@ -1,40 +1,6 @@ { "explain_todo": { "stmt14": true, - "stmt17": true, - "stmt18": true, - "stmt20": true, - "stmt21": true, - "stmt23": true, - "stmt24": true, - "stmt28": true, - "stmt29": true, - "stmt31": true, - "stmt32": true, - "stmt34": true, - "stmt35": true, - "stmt37": true, - "stmt38": true, - "stmt40": true, - "stmt41": true, - "stmt43": true, - "stmt44": true, - "stmt46": true, - "stmt47": true, - "stmt49": true, - "stmt50": true, - "stmt52": true, - "stmt53": true, - "stmt55": true, - "stmt56": true, - "stmt58": true, - "stmt59": true, - "stmt6": true, - "stmt61": true, - "stmt62": true, - "stmt66": true, - "stmt67": true, - "stmt69": true, - "stmt70": true + "stmt6": true } } diff --git a/parser/testdata/03207_json_read_subcolumns_2_memory/metadata.json b/parser/testdata/03207_json_read_subcolumns_2_memory/metadata.json index 24f9aa9d97..f04990226e 100644 --- a/parser/testdata/03207_json_read_subcolumns_2_memory/metadata.json +++ b/parser/testdata/03207_json_read_subcolumns_2_memory/metadata.json @@ -1,60 +1,6 @@ { "explain_todo": { - "stmt100": true, - "stmt101": true, - "stmt103": true, - "stmt104": true, - "stmt105": true, "stmt14": true, - "stmt17": true, - "stmt18": true, - "stmt19": true, - "stmt20": true, - "stmt22": true, - "stmt24": true, - "stmt25": true, - "stmt27": true, - "stmt28": true, - "stmt29": true, - "stmt36": true, - "stmt39": true, - "stmt40": true, - "stmt41": true, - "stmt42": true, - "stmt45": true, - "stmt46": true, - "stmt47": true, - "stmt48": true, - "stmt50": true, - "stmt52": true, - "stmt53": true, - "stmt55": true, - "stmt56": true, - "stmt57": true, - "stmt59": true, - "stmt6": true, - "stmt61": true, - "stmt62": true, - "stmt64": true, - "stmt65": true, - "stmt66": true, - "stmt68": true, - "stmt70": true, - "stmt71": true, - "stmt73": true, - "stmt74": true, - "stmt75": true, - "stmt77": true, - "stmt79": true, - "stmt80": true, - "stmt82": true, - "stmt83": true, - "stmt84": true, - "stmt86": true, - "stmt88": true, - "stmt89": true, - "stmt91": true, - "stmt92": true, - "stmt93": true + "stmt6": true } } diff --git a/parser/testdata/03227_dynamic_subcolumns_enumerate_streams/metadata.json b/parser/testdata/03227_dynamic_subcolumns_enumerate_streams/metadata.json index dbdbb76d4f..0967ef424b 100644 --- a/parser/testdata/03227_dynamic_subcolumns_enumerate_streams/metadata.json +++ b/parser/testdata/03227_dynamic_subcolumns_enumerate_streams/metadata.json @@ -1,5 +1 @@ -{ - "explain_todo": { - "stmt6": true - } -} +{} diff --git a/parser/testdata/03228_dynamic_subcolumns_from_subquery/metadata.json b/parser/testdata/03228_dynamic_subcolumns_from_subquery/metadata.json index b563327205..0967ef424b 100644 --- a/parser/testdata/03228_dynamic_subcolumns_from_subquery/metadata.json +++ b/parser/testdata/03228_dynamic_subcolumns_from_subquery/metadata.json @@ -1,5 +1 @@ -{ - "explain_todo": { - "stmt7": true - } -} +{} diff --git a/parser/testdata/03257_json_escape_file_names/metadata.json b/parser/testdata/03257_json_escape_file_names/metadata.json index b563327205..0967ef424b 100644 --- a/parser/testdata/03257_json_escape_file_names/metadata.json +++ b/parser/testdata/03257_json_escape_file_names/metadata.json @@ -1,5 +1 @@ -{ - "explain_todo": { - "stmt7": true - } -} +{} diff --git a/parser/testdata/03363_read_json_and_subcolumns_from_view/metadata.json b/parser/testdata/03363_read_json_and_subcolumns_from_view/metadata.json index ab9202e88e..0967ef424b 100644 --- a/parser/testdata/03363_read_json_and_subcolumns_from_view/metadata.json +++ b/parser/testdata/03363_read_json_and_subcolumns_from_view/metadata.json @@ -1,5 +1 @@ -{ - "explain_todo": { - "stmt11": true - } -} +{} diff --git a/parser/testdata/03525_json_infer_array_of_dynamic_from_array_of_different_types/metadata.json b/parser/testdata/03525_json_infer_array_of_dynamic_from_array_of_different_types/metadata.json index 92e84e943a..0967ef424b 100644 --- a/parser/testdata/03525_json_infer_array_of_dynamic_from_array_of_different_types/metadata.json +++ b/parser/testdata/03525_json_infer_array_of_dynamic_from_array_of_different_types/metadata.json @@ -1,6 +1 @@ -{ - "explain_todo": { - "stmt8": true, - "stmt9": true - } -} +{} diff --git a/parser/testdata/03620_json_advanced_shared_data_seek_bug/metadata.json b/parser/testdata/03620_json_advanced_shared_data_seek_bug/metadata.json index bc5c6edb66..1295a45747 100644 --- a/parser/testdata/03620_json_advanced_shared_data_seek_bug/metadata.json +++ b/parser/testdata/03620_json_advanced_shared_data_seek_bug/metadata.json @@ -1,6 +1,5 @@ { "explain_todo": { - "stmt3": true, - "stmt5": true + "stmt3": true } } diff --git a/parser/testdata/03712_json_advanced_shared_data_bug/metadata.json b/parser/testdata/03712_json_advanced_shared_data_bug/metadata.json index 3a06a4a1ac..0967ef424b 100644 --- a/parser/testdata/03712_json_advanced_shared_data_bug/metadata.json +++ b/parser/testdata/03712_json_advanced_shared_data_bug/metadata.json @@ -1,5 +1 @@ -{ - "explain_todo": { - "stmt5": true - } -} +{} diff --git a/parser/testdata/03722_json_compact_part_substreams_cache_bug/metadata.json b/parser/testdata/03722_json_compact_part_substreams_cache_bug/metadata.json index 3a06a4a1ac..0967ef424b 100644 --- a/parser/testdata/03722_json_compact_part_substreams_cache_bug/metadata.json +++ b/parser/testdata/03722_json_compact_part_substreams_cache_bug/metadata.json @@ -1,5 +1 @@ -{ - "explain_todo": { - "stmt5": true - } -} +{} From ea15fe33bdcd9d45de5c86a513678c528957e11c Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 30 Dec 2025 21:09:22 +0000 Subject: [PATCH 06/18] Add frame parsing to WINDOW clause definitions The WINDOW clause parser (parseWindowDefinitions) was missing support for frame specifications (ROWS/RANGE/GROUPS). This meant named window definitions like: WINDOW w1 AS (ROWS UNBOUNDED PRECEDING) would fail to parse correctly. Added frame parsing after ORDER BY in parseWindowDefinitions, matching the logic in parseWindowSpec(). --- parser/parser.go | 8 ++++++++ .../01591_window_functions/metadata.json | 17 ----------------- .../02020_exponential_smoothing/metadata.json | 8 +------- .../03210_lag_lead_inframe_types/metadata.json | 6 +----- 4 files changed, 10 insertions(+), 29 deletions(-) diff --git a/parser/parser.go b/parser/parser.go index 0c5aa03321..146c98f2c2 100644 --- a/parser/parser.go +++ b/parser/parser.go @@ -4551,6 +4551,14 @@ func (p *Parser) parseWindowDefinitions() []*ast.WindowDefinition { } } + // Parse frame specification (ROWS/RANGE/GROUPS) + if p.currentIs(token.IDENT) { + frameType := strings.ToUpper(p.current.Value) + if frameType == "ROWS" || frameType == "RANGE" || frameType == "GROUPS" { + spec.Frame = p.parseWindowFrame() + } + } + p.expect(token.RPAREN) def.Spec = spec defs = append(defs, def) diff --git a/parser/testdata/01591_window_functions/metadata.json b/parser/testdata/01591_window_functions/metadata.json index 7fdde4e3aa..5f4992df96 100644 --- a/parser/testdata/01591_window_functions/metadata.json +++ b/parser/testdata/01591_window_functions/metadata.json @@ -11,23 +11,6 @@ "stmt110": true, "stmt111": true, "stmt112": true, - "stmt31": true, - "stmt34": true, - "stmt35": true, - "stmt36": true, - "stmt37": true, - "stmt38": true, - "stmt39": true, - "stmt41": true, - "stmt50": true, - "stmt51": true, - "stmt52": true, - "stmt53": true, - "stmt54": true, - "stmt55": true, - "stmt56": true, - "stmt57": true, - "stmt69": true, "stmt70": true, "stmt71": true, "stmt72": true, diff --git a/parser/testdata/02020_exponential_smoothing/metadata.json b/parser/testdata/02020_exponential_smoothing/metadata.json index 5b9df19a80..0967ef424b 100644 --- a/parser/testdata/02020_exponential_smoothing/metadata.json +++ b/parser/testdata/02020_exponential_smoothing/metadata.json @@ -1,7 +1 @@ -{ - "explain_todo": { - "stmt32": true, - "stmt33": true, - "stmt35": true - } -} +{} diff --git a/parser/testdata/03210_lag_lead_inframe_types/metadata.json b/parser/testdata/03210_lag_lead_inframe_types/metadata.json index 3a06a4a1ac..0967ef424b 100644 --- a/parser/testdata/03210_lag_lead_inframe_types/metadata.json +++ b/parser/testdata/03210_lag_lead_inframe_types/metadata.json @@ -1,5 +1 @@ -{ - "explain_todo": { - "stmt5": true - } -} +{} From 3f861b605ef1fe502773099b29770531b1e1e364 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 30 Dec 2025 21:20:42 +0000 Subject: [PATCH 07/18] Add underscore digit separator support for hex literals Enable hex literals to use underscores as digit separators, matching ClickHouse's support for numbers like 0xbad_cafe. Fixed the readNumberOrIdent function which is the code path used when parsing numbers starting with digits. --- lexer/lexer.go | 6 ++- .../metadata.json | 1 - .../02523_array_shuffle/metadata.json | 49 +------------------ 3 files changed, 5 insertions(+), 51 deletions(-) diff --git a/lexer/lexer.go b/lexer/lexer.go index 4f4be5c3ff..76361f779e 100644 --- a/lexer/lexer.go +++ b/lexer/lexer.go @@ -630,9 +630,10 @@ func (l *Lexer) readNumber() Item { l.readChar() if l.ch == 'x' || l.ch == 'X' { // Hex literal (may include P notation for floats: 0x1p4, 0x1.2p-3) + // Also allows underscores as digit separators: 0xbad_cafe sb.WriteRune(l.ch) l.readChar() - for isHexDigit(l.ch) { + for isHexDigit(l.ch) || l.ch == '_' { sb.WriteRune(l.ch) l.readChar() } @@ -834,7 +835,8 @@ func (l *Lexer) readNumberOrIdent() Item { if val == "0" && (l.ch == 'x' || l.ch == 'X') { sb.WriteRune(l.ch) l.readChar() - for isHexDigit(l.ch) { + // Also allow underscores as digit separators: 0xbad_cafe + for isHexDigit(l.ch) || l.ch == '_' { sb.WriteRune(l.ch) l.readChar() } diff --git a/parser/testdata/02354_numeric_literals_with_underscores/metadata.json b/parser/testdata/02354_numeric_literals_with_underscores/metadata.json index 19830977ac..0f293987f1 100644 --- a/parser/testdata/02354_numeric_literals_with_underscores/metadata.json +++ b/parser/testdata/02354_numeric_literals_with_underscores/metadata.json @@ -1,6 +1,5 @@ { "explain_todo": { - "stmt4": true, "stmt5": true, "stmt6": true } diff --git a/parser/testdata/02523_array_shuffle/metadata.json b/parser/testdata/02523_array_shuffle/metadata.json index 53e54ead63..03cf9045c9 100644 --- a/parser/testdata/02523_array_shuffle/metadata.json +++ b/parser/testdata/02523_array_shuffle/metadata.json @@ -1,58 +1,11 @@ { "explain_todo": { - "stmt10": true, - "stmt11": true, - "stmt12": true, - "stmt13": true, "stmt16": true, "stmt17": true, "stmt18": true, - "stmt19": true, - "stmt2": true, - "stmt20": true, - "stmt21": true, - "stmt22": true, - "stmt25": true, - "stmt28": true, - "stmt29": true, - "stmt30": true, - "stmt31": true, - "stmt32": true, - "stmt33": true, - "stmt34": true, - "stmt35": true, - "stmt36": true, "stmt39": true, - "stmt4": true, "stmt40": true, - "stmt41": true, - "stmt42": true, - "stmt43": true, - "stmt44": true, - "stmt46": true, - "stmt47": true, - "stmt48": true, - "stmt49": true, - "stmt5": true, - "stmt50": true, - "stmt51": true, - "stmt52": true, - "stmt53": true, - "stmt54": true, - "stmt55": true, - "stmt56": true, - "stmt57": true, - "stmt58": true, - "stmt59": true, - "stmt6": true, "stmt60": true, - "stmt61": true, - "stmt62": true, - "stmt63": true, - "stmt64": true, - "stmt65": true, - "stmt7": true, - "stmt8": true, - "stmt9": true + "stmt61": true } } From 8309f9e5142947c9e0b3d06c4606c3e4db066fba Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 30 Dec 2025 21:27:49 +0000 Subject: [PATCH 08/18] Add CREATE/DROP RESOURCE and WORKLOAD statement support Implement parsing and explain output for ClickHouse resource and workload management statements including CREATE RESOURCE, DROP RESOURCE, CREATE WORKLOAD, and DROP WORKLOAD. --- ast/ast.go | 39 ++++++ internal/explain/explain.go | 18 +++ parser/parser.go | 113 +++++++++++++++++- .../metadata.json | 7 +- .../metadata.json | 58 +-------- 5 files changed, 171 insertions(+), 64 deletions(-) diff --git a/ast/ast.go b/ast/ast.go index 0370260222..3cace3b68e 100644 --- a/ast/ast.go +++ b/ast/ast.go @@ -960,6 +960,45 @@ func (s *ShowCreateRoleQuery) Pos() token.Position { return s.Position } func (s *ShowCreateRoleQuery) End() token.Position { return s.Position } func (s *ShowCreateRoleQuery) statementNode() {} +// CreateResourceQuery represents a CREATE RESOURCE statement. +type CreateResourceQuery struct { + Position token.Position `json:"-"` + Name string `json:"name"` +} + +func (c *CreateResourceQuery) Pos() token.Position { return c.Position } +func (c *CreateResourceQuery) End() token.Position { return c.Position } +func (c *CreateResourceQuery) statementNode() {} + +// DropResourceQuery represents a DROP RESOURCE statement. +type DropResourceQuery struct { + Position token.Position `json:"-"` +} + +func (d *DropResourceQuery) Pos() token.Position { return d.Position } +func (d *DropResourceQuery) End() token.Position { return d.Position } +func (d *DropResourceQuery) statementNode() {} + +// CreateWorkloadQuery represents a CREATE WORKLOAD statement. +type CreateWorkloadQuery struct { + Position token.Position `json:"-"` + Name string `json:"name"` + Parent string `json:"parent,omitempty"` // Parent workload name (after IN) +} + +func (c *CreateWorkloadQuery) Pos() token.Position { return c.Position } +func (c *CreateWorkloadQuery) End() token.Position { return c.Position } +func (c *CreateWorkloadQuery) statementNode() {} + +// DropWorkloadQuery represents a DROP WORKLOAD statement. +type DropWorkloadQuery struct { + Position token.Position `json:"-"` +} + +func (d *DropWorkloadQuery) Pos() token.Position { return d.Position } +func (d *DropWorkloadQuery) End() token.Position { return d.Position } +func (d *DropWorkloadQuery) statementNode() {} + // CreateIndexQuery represents a CREATE INDEX statement. type CreateIndexQuery struct { Position token.Position `json:"-"` diff --git a/internal/explain/explain.go b/internal/explain/explain.go index 2f947f0a80..f8e212eb2d 100644 --- a/internal/explain/explain.go +++ b/internal/explain/explain.go @@ -160,6 +160,24 @@ func Node(sb *strings.Builder, node interface{}, depth int) { } else { fmt.Fprintf(sb, "%sSHOW CREATE ROLE query\n", indent) } + case *ast.CreateResourceQuery: + fmt.Fprintf(sb, "%sCreateResourceQuery %s (children 1)\n", indent, n.Name) + childIndent := indent + " " + explainIdentifier(sb, &ast.Identifier{Parts: []string{n.Name}}, childIndent) + case *ast.DropResourceQuery: + fmt.Fprintf(sb, "%sDropResourceQuery\n", indent) + case *ast.CreateWorkloadQuery: + childIndent := indent + " " + if n.Parent != "" { + fmt.Fprintf(sb, "%sCreateWorkloadQuery %s (children 2)\n", indent, n.Name) + explainIdentifier(sb, &ast.Identifier{Parts: []string{n.Name}}, childIndent) + explainIdentifier(sb, &ast.Identifier{Parts: []string{n.Parent}}, childIndent) + } else { + fmt.Fprintf(sb, "%sCreateWorkloadQuery %s (children 1)\n", indent, n.Name) + explainIdentifier(sb, &ast.Identifier{Parts: []string{n.Name}}, childIndent) + } + case *ast.DropWorkloadQuery: + fmt.Fprintf(sb, "%sDropWorkloadQuery\n", indent) case *ast.ShowGrantsQuery: fmt.Fprintf(sb, "%sShowGrantsQuery\n", indent) case *ast.GrantQuery: diff --git a/parser/parser.go b/parser/parser.go index 146c98f2c2..7fa4d8fc79 100644 --- a/parser/parser.go +++ b/parser/parser.go @@ -139,6 +139,14 @@ func (p *Parser) parseStatement() ast.Statement { if p.peek.Token == token.IDENT && strings.ToUpper(p.peek.Value) == "ROLE" { return p.parseDropRole() } + // Check for DROP RESOURCE + if p.peek.Token == token.IDENT && strings.ToUpper(p.peek.Value) == "RESOURCE" { + return p.parseDropResource() + } + // Check for DROP WORKLOAD + if p.peek.Token == token.IDENT && strings.ToUpper(p.peek.Value) == "WORKLOAD" { + return p.parseDropWorkload() + } return p.parseDrop() case token.ALTER: // Check for ALTER USER @@ -1377,7 +1385,13 @@ func (p *Parser) parseCreate() ast.Statement { case "ROLE": // CREATE ROLE return p.parseCreateRole(pos) - case "RESOURCE", "WORKLOAD", "QUOTA": + case "RESOURCE": + // CREATE RESOURCE + return p.parseCreateResource(pos) + case "WORKLOAD": + // CREATE WORKLOAD + return p.parseCreateWorkload(pos) + case "QUOTA": // Skip these statements - just consume tokens until semicolon p.parseCreateGeneric(create) default: @@ -2308,6 +2322,103 @@ func (p *Parser) parseShowCreateRole(pos token.Position) *ast.ShowCreateRoleQuer return query } +func (p *Parser) parseCreateResource(pos token.Position) *ast.CreateResourceQuery { + query := &ast.CreateResourceQuery{ + Position: pos, + } + + // Skip RESOURCE + if p.currentIs(token.IDENT) && strings.ToUpper(p.current.Value) == "RESOURCE" { + p.nextToken() + } + + // Get resource name + if p.currentIs(token.IDENT) || p.current.Token.IsKeyword() { + query.Name = p.current.Value + p.nextToken() + } + + // Skip the rest of the statement (resource definition, etc.) + for !p.currentIs(token.EOF) && !p.currentIs(token.SEMICOLON) { + p.nextToken() + } + + return query +} + +func (p *Parser) parseDropResource() *ast.DropResourceQuery { + query := &ast.DropResourceQuery{ + Position: p.current.Pos, + } + + p.nextToken() // skip DROP + + // Skip RESOURCE + if p.currentIs(token.IDENT) && strings.ToUpper(p.current.Value) == "RESOURCE" { + p.nextToken() + } + + // Skip the rest of the statement (IF EXISTS, name, etc.) + for !p.currentIs(token.EOF) && !p.currentIs(token.SEMICOLON) { + p.nextToken() + } + + return query +} + +func (p *Parser) parseCreateWorkload(pos token.Position) *ast.CreateWorkloadQuery { + query := &ast.CreateWorkloadQuery{ + Position: pos, + } + + // Skip WORKLOAD + if p.currentIs(token.IDENT) && strings.ToUpper(p.current.Value) == "WORKLOAD" { + p.nextToken() + } + + // Get workload name + if p.currentIs(token.IDENT) || p.current.Token.IsKeyword() { + query.Name = p.current.Value + p.nextToken() + } + + // Check for IN (parent workload) + if p.currentIs(token.IN) { + p.nextToken() + if p.currentIs(token.IDENT) || p.current.Token.IsKeyword() { + query.Parent = p.current.Value + p.nextToken() + } + } + + // Skip the rest of the statement (SETTINGS, etc.) + for !p.currentIs(token.EOF) && !p.currentIs(token.SEMICOLON) { + p.nextToken() + } + + return query +} + +func (p *Parser) parseDropWorkload() *ast.DropWorkloadQuery { + query := &ast.DropWorkloadQuery{ + Position: p.current.Pos, + } + + p.nextToken() // skip DROP + + // Skip WORKLOAD + if p.currentIs(token.IDENT) && strings.ToUpper(p.current.Value) == "WORKLOAD" { + p.nextToken() + } + + // Skip the rest of the statement (IF EXISTS, name, etc.) + for !p.currentIs(token.EOF) && !p.currentIs(token.SEMICOLON) { + p.nextToken() + } + + return query +} + func (p *Parser) parseCreateDictionary(create *ast.CreateQuery) { // Handle IF NOT EXISTS if p.currentIs(token.IF) { diff --git a/parser/testdata/03232_workload_create_and_drop/metadata.json b/parser/testdata/03232_workload_create_and_drop/metadata.json index 289fd4cfda..1295a45747 100644 --- a/parser/testdata/03232_workload_create_and_drop/metadata.json +++ b/parser/testdata/03232_workload_create_and_drop/metadata.json @@ -1,10 +1,5 @@ { "explain_todo": { - "stmt1": true, - "stmt3": true, - "stmt4": true, - "stmt6": true, - "stmt7": true, - "stmt9": true + "stmt3": true } } diff --git a/parser/testdata/03232_workloads_and_resources/metadata.json b/parser/testdata/03232_workloads_and_resources/metadata.json index 61b5428b47..0967ef424b 100644 --- a/parser/testdata/03232_workloads_and_resources/metadata.json +++ b/parser/testdata/03232_workloads_and_resources/metadata.json @@ -1,57 +1 @@ -{ - "explain_todo": { - "stmt1": true, - "stmt10": true, - "stmt11": true, - "stmt12": true, - "stmt13": true, - "stmt14": true, - "stmt15": true, - "stmt16": true, - "stmt17": true, - "stmt18": true, - "stmt19": true, - "stmt2": true, - "stmt20": true, - "stmt21": true, - "stmt22": true, - "stmt23": true, - "stmt24": true, - "stmt25": true, - "stmt26": true, - "stmt27": true, - "stmt28": true, - "stmt29": true, - "stmt30": true, - "stmt31": true, - "stmt32": true, - "stmt33": true, - "stmt34": true, - "stmt35": true, - "stmt36": true, - "stmt37": true, - "stmt38": true, - "stmt39": true, - "stmt4": true, - "stmt40": true, - "stmt41": true, - "stmt42": true, - "stmt43": true, - "stmt44": true, - "stmt45": true, - "stmt46": true, - "stmt47": true, - "stmt48": true, - "stmt49": true, - "stmt5": true, - "stmt50": true, - "stmt51": true, - "stmt52": true, - "stmt53": true, - "stmt54": true, - "stmt6": true, - "stmt7": true, - "stmt8": true, - "stmt9": true - } -} +{} From 37a38121a8ea9ab473fa1073fb0f72dd783b3db7 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 30 Dec 2025 21:32:57 +0000 Subject: [PATCH 09/18] Allow ARRAY keyword to be used as identifier (column name) When ARRAY is not followed by '(', treat it as an identifier rather than requiring it to be a constructor. This allows column names like 'array' to be referenced correctly. --- parser/expression.go | 8 ++- .../metadata.json | 58 +------------------ .../testdata/00262_alter_alias/metadata.json | 5 +- .../metadata.json | 6 +- .../metadata.json | 13 +---- .../02346_text_index_creation/metadata.json | 3 +- .../metadata.json | 3 +- .../metadata.json | 3 +- .../metadata.json | 3 +- .../metadata.json | 21 +------ .../metadata.json | 1 - .../03707_function_array_remove/metadata.json | 1 - .../metadata.json | 3 +- 13 files changed, 16 insertions(+), 112 deletions(-) diff --git a/parser/expression.go b/parser/expression.go index f97de34c48..f0d7ee4205 100644 --- a/parser/expression.go +++ b/parser/expression.go @@ -303,8 +303,12 @@ func (p *Parser) parsePrefixExpression() ast.Expression { case token.COLUMNS: return p.parseColumnsMatcher() case token.ARRAY: - // array(1,2,3) constructor - return p.parseArrayConstructor() + // array(1,2,3) constructor or array as identifier (column name) + if p.peekIs(token.LPAREN) { + return p.parseArrayConstructor() + } + // array used as identifier (column/variable name) + return p.parseKeywordAsIdentifier() case token.IF: // IF function return p.parseIfFunction() diff --git a/parser/testdata/00261_storage_aliases_and_array_join/metadata.json b/parser/testdata/00261_storage_aliases_and_array_join/metadata.json index 3d76cc4621..0967ef424b 100644 --- a/parser/testdata/00261_storage_aliases_and_array_join/metadata.json +++ b/parser/testdata/00261_storage_aliases_and_array_join/metadata.json @@ -1,57 +1 @@ -{ - "explain_todo": { - "stmt17": true, - "stmt18": true, - "stmt20": true, - "stmt21": true, - "stmt22": true, - "stmt23": true, - "stmt24": true, - "stmt25": true, - "stmt26": true, - "stmt28": true, - "stmt29": true, - "stmt3": true, - "stmt30": true, - "stmt31": true, - "stmt32": true, - "stmt33": true, - "stmt34": true, - "stmt36": true, - "stmt37": true, - "stmt38": true, - "stmt39": true, - "stmt40": true, - "stmt41": true, - "stmt42": true, - "stmt44": true, - "stmt45": true, - "stmt46": true, - "stmt47": true, - "stmt48": true, - "stmt49": true, - "stmt50": true, - "stmt52": true, - "stmt53": true, - "stmt54": true, - "stmt55": true, - "stmt56": true, - "stmt57": true, - "stmt58": true, - "stmt60": true, - "stmt61": true, - "stmt62": true, - "stmt63": true, - "stmt64": true, - "stmt65": true, - "stmt66": true, - "stmt68": true, - "stmt70": true, - "stmt72": true, - "stmt74": true, - "stmt76": true, - "stmt78": true, - "stmt80": true, - "stmt81": true - } -} +{} diff --git a/parser/testdata/00262_alter_alias/metadata.json b/parser/testdata/00262_alter_alias/metadata.json index 38d5317aa9..a56c7cdb0b 100644 --- a/parser/testdata/00262_alter_alias/metadata.json +++ b/parser/testdata/00262_alter_alias/metadata.json @@ -1,9 +1,6 @@ { "explain_todo": { "stmt10": true, - "stmt12": true, - "stmt5": true, - "stmt7": true, - "stmt9": true + "stmt12": true } } diff --git a/parser/testdata/01054_cache_dictionary_overflow_cell/metadata.json b/parser/testdata/01054_cache_dictionary_overflow_cell/metadata.json index b09bea8db0..0967ef424b 100644 --- a/parser/testdata/01054_cache_dictionary_overflow_cell/metadata.json +++ b/parser/testdata/01054_cache_dictionary_overflow_cell/metadata.json @@ -1,5 +1 @@ -{ - "explain_todo": { - "stmt25": true - } -} +{} diff --git a/parser/testdata/02022_array_full_text_bloom_filter_index/metadata.json b/parser/testdata/02022_array_full_text_bloom_filter_index/metadata.json index be6b941946..0967ef424b 100644 --- a/parser/testdata/02022_array_full_text_bloom_filter_index/metadata.json +++ b/parser/testdata/02022_array_full_text_bloom_filter_index/metadata.json @@ -1,12 +1 @@ -{ - "explain_todo": { - "stmt13": true, - "stmt14": true, - "stmt15": true, - "stmt3": true, - "stmt4": true, - "stmt7": true, - "stmt8": true, - "stmt9": true - } -} +{} diff --git a/parser/testdata/02346_text_index_creation/metadata.json b/parser/testdata/02346_text_index_creation/metadata.json index de70defc32..5fe61c8fd1 100644 --- a/parser/testdata/02346_text_index_creation/metadata.json +++ b/parser/testdata/02346_text_index_creation/metadata.json @@ -1,7 +1,6 @@ { "explain_todo": { "stmt102": true, - "stmt103": true, - "stmt29": true + "stmt103": true } } diff --git a/parser/testdata/02346_text_index_dictionary_cache/metadata.json b/parser/testdata/02346_text_index_dictionary_cache/metadata.json index 4ec8630990..c45b7602ba 100644 --- a/parser/testdata/02346_text_index_dictionary_cache/metadata.json +++ b/parser/testdata/02346_text_index_dictionary_cache/metadata.json @@ -1,6 +1,5 @@ { "explain_todo": { - "stmt12": true, - "stmt9": true + "stmt12": true } } diff --git a/parser/testdata/02346_text_index_header_cache/metadata.json b/parser/testdata/02346_text_index_header_cache/metadata.json index eae7356f40..54590bc988 100644 --- a/parser/testdata/02346_text_index_header_cache/metadata.json +++ b/parser/testdata/02346_text_index_header_cache/metadata.json @@ -1,7 +1,6 @@ { "explain_todo": { "stmt11": true, - "stmt13": true, - "stmt9": true + "stmt13": true } } diff --git a/parser/testdata/02346_text_index_postings_cache/metadata.json b/parser/testdata/02346_text_index_postings_cache/metadata.json index b6a0ad05da..d4d1d99f95 100644 --- a/parser/testdata/02346_text_index_postings_cache/metadata.json +++ b/parser/testdata/02346_text_index_postings_cache/metadata.json @@ -1,6 +1,5 @@ { "explain_todo": { - "stmt14": true, - "stmt9": true + "stmt14": true } } diff --git a/parser/testdata/02943_use_full_text_skip_index_with_has_any/metadata.json b/parser/testdata/02943_use_full_text_skip_index_with_has_any/metadata.json index a24b44850e..0967ef424b 100644 --- a/parser/testdata/02943_use_full_text_skip_index_with_has_any/metadata.json +++ b/parser/testdata/02943_use_full_text_skip_index_with_has_any/metadata.json @@ -1,20 +1 @@ -{ - "explain_todo": { - "stmt11": true, - "stmt13": true, - "stmt15": true, - "stmt17": true, - "stmt19": true, - "stmt21": true, - "stmt23": true, - "stmt25": true, - "stmt27": true, - "stmt29": true, - "stmt3": true, - "stmt31": true, - "stmt33": true, - "stmt4": true, - "stmt7": true, - "stmt9": true - } -} +{} diff --git a/parser/testdata/03702_text_index_hint_basics/metadata.json b/parser/testdata/03702_text_index_hint_basics/metadata.json index 17d05faf5c..596438587e 100644 --- a/parser/testdata/03702_text_index_hint_basics/metadata.json +++ b/parser/testdata/03702_text_index_hint_basics/metadata.json @@ -2,7 +2,6 @@ "explain_todo": { "stmt11": true, "stmt13": true, - "stmt16": true, "stmt19": true, "stmt21": true, "stmt27": true, diff --git a/parser/testdata/03707_function_array_remove/metadata.json b/parser/testdata/03707_function_array_remove/metadata.json index d071b2f49d..7ee47c55de 100644 --- a/parser/testdata/03707_function_array_remove/metadata.json +++ b/parser/testdata/03707_function_array_remove/metadata.json @@ -1,6 +1,5 @@ { "explain_todo": { - "stmt33": true, "stmt45": true, "stmt46": true } diff --git a/parser/testdata/03742_lazy_materialization_of_array_after_alter_add_column/metadata.json b/parser/testdata/03742_lazy_materialization_of_array_after_alter_add_column/metadata.json index ef382ce51e..b65b07d7a6 100644 --- a/parser/testdata/03742_lazy_materialization_of_array_after_alter_add_column/metadata.json +++ b/parser/testdata/03742_lazy_materialization_of_array_after_alter_add_column/metadata.json @@ -1,6 +1,5 @@ { "explain_todo": { - "stmt4": true, - "stmt5": true + "stmt4": true } } From f287c18d1362eb1f10d15dc5ec2eaeafd0c64f33 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 30 Dec 2025 21:46:23 +0000 Subject: [PATCH 10/18] Add projection support in CREATE TABLE column definitions - Add Projections field to CreateQuery AST - Update parser to parse PROJECTION definitions in column lists - Update explain to output projections as third child of Columns definition - Handle multi-column ORDER BY in projections as Function tuple --- ast/ast.go | 3 +- internal/explain/statements.go | 29 +++++++++++--- parser/parser.go | 39 +++++++++---------- .../metadata.json | 5 +-- .../metadata.json | 3 +- 5 files changed, 47 insertions(+), 32 deletions(-) diff --git a/ast/ast.go b/ast/ast.go index 3cace3b68e..98006926c3 100644 --- a/ast/ast.go +++ b/ast/ast.go @@ -253,6 +253,7 @@ type CreateQuery struct { Populate bool `json:"populate,omitempty"` // POPULATE for materialized views Columns []*ColumnDeclaration `json:"columns,omitempty"` Indexes []*IndexDefinition `json:"indexes,omitempty"` + Projections []*Projection `json:"projections,omitempty"` Constraints []*Constraint `json:"constraints,omitempty"` Engine *EngineClause `json:"engine,omitempty"` OrderBy []Expression `json:"order_by,omitempty"` @@ -542,7 +543,7 @@ type ProjectionSelectQuery struct { Position token.Position `json:"-"` Columns []Expression `json:"columns"` GroupBy []Expression `json:"group_by,omitempty"` - OrderBy *Identifier `json:"order_by,omitempty"` // Single column for ORDER BY + OrderBy []Expression `json:"order_by,omitempty"` // ORDER BY columns } // Assignment represents a column assignment in UPDATE. diff --git a/internal/explain/statements.go b/internal/explain/statements.go index 68644bb4a1..7870829d2b 100644 --- a/internal/explain/statements.go +++ b/internal/explain/statements.go @@ -137,7 +137,7 @@ func explainCreateQuery(sb *strings.Builder, n *ast.CreateQuery, indent string, if hasDatabase { children++ // additional identifier for database } - if len(n.Columns) > 0 || len(n.Indexes) > 0 || len(n.Constraints) > 0 { + if len(n.Columns) > 0 || len(n.Indexes) > 0 || len(n.Projections) > 0 || len(n.Constraints) > 0 { children++ } if n.Engine != nil || len(n.OrderBy) > 0 || len(n.PrimaryKey) > 0 || n.PartitionBy != nil { @@ -162,7 +162,7 @@ func explainCreateQuery(sb *strings.Builder, n *ast.CreateQuery, indent string, fmt.Fprintf(sb, "%sCreateQuery %s (children %d)\n", indent, name, children) fmt.Fprintf(sb, "%s Identifier %s\n", indent, name) } - if len(n.Columns) > 0 || len(n.Indexes) > 0 || len(n.Constraints) > 0 { + if len(n.Columns) > 0 || len(n.Indexes) > 0 || len(n.Projections) > 0 || len(n.Constraints) > 0 { childrenCount := 0 if len(n.Columns) > 0 { childrenCount++ @@ -170,6 +170,9 @@ func explainCreateQuery(sb *strings.Builder, n *ast.CreateQuery, indent string, if len(n.Indexes) > 0 { childrenCount++ } + if len(n.Projections) > 0 { + childrenCount++ + } if len(n.Constraints) > 0 { childrenCount++ } @@ -196,6 +199,12 @@ func explainCreateQuery(sb *strings.Builder, n *ast.CreateQuery, indent string, Index(sb, idx, depth+3) } } + if len(n.Projections) > 0 { + fmt.Fprintf(sb, "%s ExpressionList (children %d)\n", indent, len(n.Projections)) + for _, proj := range n.Projections { + explainProjection(sb, proj, indent+" ", depth+3) + } + } // Output constraints wrapped in Constraint nodes if len(n.Constraints) > 0 { fmt.Fprintf(sb, "%s ExpressionList (children %d)\n", indent, len(n.Constraints)) @@ -949,7 +958,7 @@ func explainProjectionSelectQuery(sb *strings.Builder, q *ast.ProjectionSelectQu if len(q.Columns) > 0 { children++ } - if q.OrderBy != nil { + if len(q.OrderBy) > 0 { children++ } if len(q.GroupBy) > 0 { @@ -962,8 +971,18 @@ func explainProjectionSelectQuery(sb *strings.Builder, q *ast.ProjectionSelectQu Node(sb, col, depth+2) } } - if q.OrderBy != nil { - fmt.Fprintf(sb, "%s Identifier %s\n", indent, q.OrderBy.Parts[len(q.OrderBy.Parts)-1]) + if len(q.OrderBy) > 0 { + if len(q.OrderBy) == 1 { + // Single column: just output as Identifier + Node(sb, q.OrderBy[0], depth+1) + } else { + // Multiple columns: wrap in Function tuple + fmt.Fprintf(sb, "%s Function tuple (children 1)\n", indent) + fmt.Fprintf(sb, "%s ExpressionList (children %d)\n", indent, len(q.OrderBy)) + for _, col := range q.OrderBy { + Node(sb, col, depth+3) + } + } } if len(q.GroupBy) > 0 { fmt.Fprintf(sb, "%s ExpressionList (children %d)\n", indent, len(q.GroupBy)) diff --git a/parser/parser.go b/parser/parser.go index 7fa4d8fc79..dcb69af998 100644 --- a/parser/parser.go +++ b/parser/parser.go @@ -1512,21 +1512,11 @@ func (p *Parser) parseCreateTable(create *ast.CreateQuery) { create.Indexes = append(create.Indexes, idx) } } else if p.currentIs(token.IDENT) && strings.ToUpper(p.current.Value) == "PROJECTION" { - // Skip PROJECTION definitions: PROJECTION name (SELECT ...) + // Parse PROJECTION definitions: PROJECTION name (SELECT ...) p.nextToken() // skip PROJECTION - p.parseIdentifierName() // projection name - // Skip the (SELECT ...) part - if p.currentIs(token.LPAREN) { - depth := 1 - p.nextToken() - for depth > 0 && !p.currentIs(token.EOF) { - if p.currentIs(token.LPAREN) { - depth++ - } else if p.currentIs(token.RPAREN) { - depth-- - } - p.nextToken() - } + proj := p.parseProjection() + if proj != nil { + create.Projections = append(create.Projections, proj) } } else if p.currentIs(token.CONSTRAINT) { // Parse CONSTRAINT name CHECK (expression) @@ -4961,13 +4951,22 @@ func (p *Parser) parseProjection() *ast.Projection { if p.currentIs(token.BY) { p.nextToken() // BY } - // For projection ORDER BY, we just need the column name - if p.currentIs(token.IDENT) { - proj.Select.OrderBy = &ast.Identifier{ - Position: p.current.Pos, - Parts: []string{p.current.Value}, + // Parse ORDER BY columns (comma-separated identifiers) + for !p.currentIs(token.EOF) && !p.currentIs(token.RPAREN) { + if p.currentIs(token.IDENT) || p.current.Token.IsKeyword() { + proj.Select.OrderBy = append(proj.Select.OrderBy, &ast.Identifier{ + Position: p.current.Pos, + Parts: []string{p.current.Value}, + }) + p.nextToken() + } else { + break + } + if p.currentIs(token.COMMA) { + p.nextToken() + } else { + break } - p.nextToken() } } diff --git a/parser/testdata/02934_merge_tree_max_projections/metadata.json b/parser/testdata/02934_merge_tree_max_projections/metadata.json index 7512d3c446..ca4130f74d 100644 --- a/parser/testdata/02934_merge_tree_max_projections/metadata.json +++ b/parser/testdata/02934_merge_tree_max_projections/metadata.json @@ -2,9 +2,6 @@ "explain_todo": { "stmt11": true, "stmt12": true, - "stmt13": true, - "stmt5": true, - "stmt6": true, - "stmt8": true + "stmt13": true } } diff --git a/parser/testdata/03560_parallel_replicas_projection/metadata.json b/parser/testdata/03560_parallel_replicas_projection/metadata.json index bf2df32156..da14dca034 100644 --- a/parser/testdata/03560_parallel_replicas_projection/metadata.json +++ b/parser/testdata/03560_parallel_replicas_projection/metadata.json @@ -7,7 +7,6 @@ "stmt3": true, "stmt31": true, "stmt38": true, - "stmt41": true, - "stmt5": true + "stmt41": true } } From 063e557cbc6d6a6cceb27b27dcfdece0c3b988e9 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 30 Dec 2025 21:57:04 +0000 Subject: [PATCH 11/18] Fix projection indentation in EXPLAIN output - Remove extra space before "Projection" in explainProjection format string - Adjust indent passed to explainProjectionSelectQuery from +2 to +1 spaces - Add space to indent when calling explainProjection from AlterCommand This fixes indentation for projections in both CREATE TABLE and ALTER TABLE ADD PROJECTION statements to match ClickHouse's EXPLAIN AST output. --- internal/explain/statements.go | 6 +++--- parser/testdata/01576_alias_column_rewrite/metadata.json | 3 +-- .../01701_clear_projection_and_part_remove/metadata.json | 1 - .../metadata.json | 1 - .../metadata.json | 1 - .../metadata.json | 6 +----- .../metadata.json | 3 +-- parser/testdata/01710_force_use_projection/metadata.json | 6 +----- .../metadata.json | 6 +----- .../01710_order_by_projections_complete/metadata.json | 1 - .../01710_projection_aggregation_in_order/metadata.json | 7 +------ parser/testdata/01710_projection_array_join/metadata.json | 6 +----- .../testdata/01710_projection_detach_part/metadata.json | 1 - .../01710_projection_drop_if_exists/metadata.json | 6 +----- .../01710_projection_external_aggregate/metadata.json | 6 +----- parser/testdata/01710_projection_fetch_long/metadata.json | 2 -- parser/testdata/01710_projection_in_index/metadata.json | 6 +----- parser/testdata/01710_projection_in_set/metadata.json | 6 +----- parser/testdata/01710_projection_mutation/metadata.json | 1 - .../metadata.json | 6 +----- .../metadata.json | 6 +----- parser/testdata/01710_projection_part_check/metadata.json | 8 +------- .../01710_projection_with_alter_conversions/metadata.json | 1 - .../metadata.json | 8 +------- parser/testdata/01710_projection_with_joins/metadata.json | 7 +------ parser/testdata/01710_projections/metadata.json | 3 +-- .../01710_projections_and_duplicate_columms/metadata.json | 6 +----- .../01710_projections_group_by_no_key/metadata.json | 6 +----- .../01710_projections_order_by_complete/metadata.json | 1 - .../01710_query_log_with_projection_info/metadata.json | 6 +----- .../metadata.json | 6 +----- parser/testdata/02343_aggregation_pipeline/metadata.json | 1 - .../02478_projection_with_group_by_alter/metadata.json | 1 - .../testdata/02515_projections_with_totals/metadata.json | 6 +----- parser/testdata/02540_duplicate_primary_key/metadata.json | 6 +----- .../testdata/02540_duplicate_primary_key2/metadata.json | 6 +----- .../metadata.json | 6 +----- .../metadata.json | 1 - .../02725_agg_projection_respect_PK/metadata.json | 1 - .../02784_projections_read_in_order_bug/metadata.json | 6 +----- parser/testdata/02792_drop_projection_lwd/metadata.json | 1 - .../02832_alter_delete_indexes_projections/metadata.json | 1 - .../02906_force_optimize_projection_name/metadata.json | 6 +----- parser/testdata/02915_analyzer_fuzz_2/metadata.json | 6 +----- .../02920_alter_column_of_projections/metadata.json | 1 - .../02934_merge_tree_max_projections/metadata.json | 8 +------- .../02965_projection_with_partition_pruning/metadata.json | 6 +----- parser/testdata/02968_projection_merge/metadata.json | 4 ---- .../02996_analyzer_prewhere_projection/metadata.json | 6 +----- parser/testdata/03002_analyzer_prewhere/metadata.json | 6 +----- .../03047_on_fly_mutations_projections/metadata.json | 1 - .../03128_argMin_combinator_projection/metadata.json | 2 -- .../03161_lightweight_delete_projection/metadata.json | 2 -- .../testdata/03174_projection_deduplicate/metadata.json | 1 - .../metadata.json | 6 ------ .../metadata.json | 1 - parser/testdata/03221_merge_profile_events/metadata.json | 1 - .../metadata.json | 3 +-- parser/testdata/03230_system_projections/metadata.json | 1 - .../03254_project_lwd_respects_row_exists/metadata.json | 2 -- .../testdata/03290_force_normal_projection/metadata.json | 6 +----- .../metadata.json | 7 +------ .../metadata.json | 7 +------ .../testdata/03340_projections_formatting/metadata.json | 5 ----- .../metadata.json | 5 +---- .../metadata.json | 3 +-- .../metadata.json | 1 - parser/testdata/03443_part_starting_offset/metadata.json | 6 +----- .../testdata/03460_normal_projection_index/metadata.json | 4 +--- .../metadata.json | 6 +----- .../metadata.json | 3 +-- .../metadata.json | 6 +----- .../metadata.json | 1 - .../metadata.json | 6 +----- .../03550_projection_with_part_offset_ttl/metadata.json | 6 +----- parser/testdata/03571_lwd_and_projections/metadata.json | 1 - .../metadata.json | 6 +----- .../03593_backup_with_broken_projection/metadata.json | 1 - .../metadata.json | 2 +- .../metadata.json | 1 - 80 files changed, 50 insertions(+), 271 deletions(-) diff --git a/internal/explain/statements.go b/internal/explain/statements.go index 7870829d2b..b8041a6aea 100644 --- a/internal/explain/statements.go +++ b/internal/explain/statements.go @@ -925,7 +925,7 @@ func explainAlterCommand(sb *strings.Builder, cmd *ast.AlterCommand, indent stri } case ast.AlterAddProjection: if cmd.Projection != nil { - explainProjection(sb, cmd.Projection, indent, depth+1) + explainProjection(sb, cmd.Projection, indent+" ", depth+1) } case ast.AlterDropProjection, ast.AlterMaterializeProjection, ast.AlterClearProjection: if cmd.ProjectionName != "" { @@ -947,9 +947,9 @@ func explainProjection(sb *strings.Builder, p *ast.Projection, indent string, de if p.Select != nil { children++ } - fmt.Fprintf(sb, "%s Projection (children %d)\n", indent, children) + fmt.Fprintf(sb, "%sProjection (children %d)\n", indent, children) if p.Select != nil { - explainProjectionSelectQuery(sb, p.Select, indent+" ", depth+1) + explainProjectionSelectQuery(sb, p.Select, indent+" ", depth+1) } } diff --git a/parser/testdata/01576_alias_column_rewrite/metadata.json b/parser/testdata/01576_alias_column_rewrite/metadata.json index b8121d05e2..2581609646 100644 --- a/parser/testdata/01576_alias_column_rewrite/metadata.json +++ b/parser/testdata/01576_alias_column_rewrite/metadata.json @@ -1,7 +1,6 @@ { "explain_todo": { "stmt23": true, - "stmt42": true, - "stmt51": true + "stmt42": true } } diff --git a/parser/testdata/01701_clear_projection_and_part_remove/metadata.json b/parser/testdata/01701_clear_projection_and_part_remove/metadata.json index b04956cf7e..f6d9f2395b 100644 --- a/parser/testdata/01701_clear_projection_and_part_remove/metadata.json +++ b/parser/testdata/01701_clear_projection_and_part_remove/metadata.json @@ -1,6 +1,5 @@ { "explain_todo": { - "stmt2": true, "stmt7": true, "stmt8": true } diff --git a/parser/testdata/01705_normalize_create_alter_function_names/metadata.json b/parser/testdata/01705_normalize_create_alter_function_names/metadata.json index bc141058a4..1295a45747 100644 --- a/parser/testdata/01705_normalize_create_alter_function_names/metadata.json +++ b/parser/testdata/01705_normalize_create_alter_function_names/metadata.json @@ -1,6 +1,5 @@ { "explain_todo": { - "stmt2": true, "stmt3": true } } diff --git a/parser/testdata/01710_aggregate_projection_with_grouping_set/metadata.json b/parser/testdata/01710_aggregate_projection_with_grouping_set/metadata.json index 9a8cc69c0b..b65b07d7a6 100644 --- a/parser/testdata/01710_aggregate_projection_with_grouping_set/metadata.json +++ b/parser/testdata/01710_aggregate_projection_with_grouping_set/metadata.json @@ -1,6 +1,5 @@ { "explain_todo": { - "stmt2": true, "stmt4": true } } diff --git a/parser/testdata/01710_aggregate_projection_with_monotonic_key_expr/metadata.json b/parser/testdata/01710_aggregate_projection_with_monotonic_key_expr/metadata.json index b65b07d7a6..0967ef424b 100644 --- a/parser/testdata/01710_aggregate_projection_with_monotonic_key_expr/metadata.json +++ b/parser/testdata/01710_aggregate_projection_with_monotonic_key_expr/metadata.json @@ -1,5 +1 @@ -{ - "explain_todo": { - "stmt4": true - } -} +{} diff --git a/parser/testdata/01710_aggregate_projection_with_normalized_states/metadata.json b/parser/testdata/01710_aggregate_projection_with_normalized_states/metadata.json index bc141058a4..ef58f80315 100644 --- a/parser/testdata/01710_aggregate_projection_with_normalized_states/metadata.json +++ b/parser/testdata/01710_aggregate_projection_with_normalized_states/metadata.json @@ -1,6 +1,5 @@ { "explain_todo": { - "stmt2": true, - "stmt3": true + "stmt2": true } } diff --git a/parser/testdata/01710_force_use_projection/metadata.json b/parser/testdata/01710_force_use_projection/metadata.json index ef58f80315..0967ef424b 100644 --- a/parser/testdata/01710_force_use_projection/metadata.json +++ b/parser/testdata/01710_force_use_projection/metadata.json @@ -1,5 +1 @@ -{ - "explain_todo": { - "stmt2": true - } -} +{} diff --git a/parser/testdata/01710_normal_projection_with_query_plan_optimization/metadata.json b/parser/testdata/01710_normal_projection_with_query_plan_optimization/metadata.json index ef58f80315..0967ef424b 100644 --- a/parser/testdata/01710_normal_projection_with_query_plan_optimization/metadata.json +++ b/parser/testdata/01710_normal_projection_with_query_plan_optimization/metadata.json @@ -1,5 +1 @@ -{ - "explain_todo": { - "stmt2": true - } -} +{} diff --git a/parser/testdata/01710_order_by_projections_complete/metadata.json b/parser/testdata/01710_order_by_projections_complete/metadata.json index bc141058a4..1295a45747 100644 --- a/parser/testdata/01710_order_by_projections_complete/metadata.json +++ b/parser/testdata/01710_order_by_projections_complete/metadata.json @@ -1,6 +1,5 @@ { "explain_todo": { - "stmt2": true, "stmt3": true } } diff --git a/parser/testdata/01710_projection_aggregation_in_order/metadata.json b/parser/testdata/01710_projection_aggregation_in_order/metadata.json index 076821553a..0967ef424b 100644 --- a/parser/testdata/01710_projection_aggregation_in_order/metadata.json +++ b/parser/testdata/01710_projection_aggregation_in_order/metadata.json @@ -1,6 +1 @@ -{ - "explain_todo": { - "stmt14": true, - "stmt2": true - } -} +{} diff --git a/parser/testdata/01710_projection_array_join/metadata.json b/parser/testdata/01710_projection_array_join/metadata.json index 1295a45747..0967ef424b 100644 --- a/parser/testdata/01710_projection_array_join/metadata.json +++ b/parser/testdata/01710_projection_array_join/metadata.json @@ -1,5 +1 @@ -{ - "explain_todo": { - "stmt3": true - } -} +{} diff --git a/parser/testdata/01710_projection_detach_part/metadata.json b/parser/testdata/01710_projection_detach_part/metadata.json index bc5c6edb66..3a06a4a1ac 100644 --- a/parser/testdata/01710_projection_detach_part/metadata.json +++ b/parser/testdata/01710_projection_detach_part/metadata.json @@ -1,6 +1,5 @@ { "explain_todo": { - "stmt3": true, "stmt5": true } } diff --git a/parser/testdata/01710_projection_drop_if_exists/metadata.json b/parser/testdata/01710_projection_drop_if_exists/metadata.json index ef58f80315..0967ef424b 100644 --- a/parser/testdata/01710_projection_drop_if_exists/metadata.json +++ b/parser/testdata/01710_projection_drop_if_exists/metadata.json @@ -1,5 +1 @@ -{ - "explain_todo": { - "stmt2": true - } -} +{} diff --git a/parser/testdata/01710_projection_external_aggregate/metadata.json b/parser/testdata/01710_projection_external_aggregate/metadata.json index ef58f80315..0967ef424b 100644 --- a/parser/testdata/01710_projection_external_aggregate/metadata.json +++ b/parser/testdata/01710_projection_external_aggregate/metadata.json @@ -1,5 +1 @@ -{ - "explain_todo": { - "stmt2": true - } -} +{} diff --git a/parser/testdata/01710_projection_fetch_long/metadata.json b/parser/testdata/01710_projection_fetch_long/metadata.json index a5ba65a3dc..3821cdf8c3 100644 --- a/parser/testdata/01710_projection_fetch_long/metadata.json +++ b/parser/testdata/01710_projection_fetch_long/metadata.json @@ -4,8 +4,6 @@ "stmt19": true, "stmt20": true, "stmt24": true, - "stmt3": true, - "stmt4": true, "stmt6": true, "stmt9": true } diff --git a/parser/testdata/01710_projection_in_index/metadata.json b/parser/testdata/01710_projection_in_index/metadata.json index 1295a45747..0967ef424b 100644 --- a/parser/testdata/01710_projection_in_index/metadata.json +++ b/parser/testdata/01710_projection_in_index/metadata.json @@ -1,5 +1 @@ -{ - "explain_todo": { - "stmt3": true - } -} +{} diff --git a/parser/testdata/01710_projection_in_set/metadata.json b/parser/testdata/01710_projection_in_set/metadata.json index ef58f80315..0967ef424b 100644 --- a/parser/testdata/01710_projection_in_set/metadata.json +++ b/parser/testdata/01710_projection_in_set/metadata.json @@ -1,5 +1 @@ -{ - "explain_todo": { - "stmt2": true - } -} +{} diff --git a/parser/testdata/01710_projection_mutation/metadata.json b/parser/testdata/01710_projection_mutation/metadata.json index 9a8cc69c0b..b65b07d7a6 100644 --- a/parser/testdata/01710_projection_mutation/metadata.json +++ b/parser/testdata/01710_projection_mutation/metadata.json @@ -1,6 +1,5 @@ { "explain_todo": { - "stmt2": true, "stmt4": true } } diff --git a/parser/testdata/01710_projection_optimize_aggregators_of_group_by_keys/metadata.json b/parser/testdata/01710_projection_optimize_aggregators_of_group_by_keys/metadata.json index ef58f80315..0967ef424b 100644 --- a/parser/testdata/01710_projection_optimize_aggregators_of_group_by_keys/metadata.json +++ b/parser/testdata/01710_projection_optimize_aggregators_of_group_by_keys/metadata.json @@ -1,5 +1 @@ -{ - "explain_todo": { - "stmt2": true - } -} +{} diff --git a/parser/testdata/01710_projection_optimize_group_by_function_keys/metadata.json b/parser/testdata/01710_projection_optimize_group_by_function_keys/metadata.json index ef58f80315..0967ef424b 100644 --- a/parser/testdata/01710_projection_optimize_group_by_function_keys/metadata.json +++ b/parser/testdata/01710_projection_optimize_group_by_function_keys/metadata.json @@ -1,5 +1 @@ -{ - "explain_todo": { - "stmt2": true - } -} +{} diff --git a/parser/testdata/01710_projection_part_check/metadata.json b/parser/testdata/01710_projection_part_check/metadata.json index 110d4dee5c..0967ef424b 100644 --- a/parser/testdata/01710_projection_part_check/metadata.json +++ b/parser/testdata/01710_projection_part_check/metadata.json @@ -1,7 +1 @@ -{ - "explain_todo": { - "stmt12": true, - "stmt2": true, - "stmt7": true - } -} +{} diff --git a/parser/testdata/01710_projection_with_alter_conversions/metadata.json b/parser/testdata/01710_projection_with_alter_conversions/metadata.json index 9a8cc69c0b..b65b07d7a6 100644 --- a/parser/testdata/01710_projection_with_alter_conversions/metadata.json +++ b/parser/testdata/01710_projection_with_alter_conversions/metadata.json @@ -1,6 +1,5 @@ { "explain_todo": { - "stmt2": true, "stmt4": true } } diff --git a/parser/testdata/01710_projection_with_ast_rewrite_settings/metadata.json b/parser/testdata/01710_projection_with_ast_rewrite_settings/metadata.json index 110d4dee5c..0967ef424b 100644 --- a/parser/testdata/01710_projection_with_ast_rewrite_settings/metadata.json +++ b/parser/testdata/01710_projection_with_ast_rewrite_settings/metadata.json @@ -1,7 +1 @@ -{ - "explain_todo": { - "stmt12": true, - "stmt2": true, - "stmt7": true - } -} +{} diff --git a/parser/testdata/01710_projection_with_joins/metadata.json b/parser/testdata/01710_projection_with_joins/metadata.json index ae11f9e277..0967ef424b 100644 --- a/parser/testdata/01710_projection_with_joins/metadata.json +++ b/parser/testdata/01710_projection_with_joins/metadata.json @@ -1,6 +1 @@ -{ - "explain_todo": { - "stmt13": true, - "stmt2": true - } -} +{} diff --git a/parser/testdata/01710_projections/metadata.json b/parser/testdata/01710_projections/metadata.json index 75fec6a18f..ef58f80315 100644 --- a/parser/testdata/01710_projections/metadata.json +++ b/parser/testdata/01710_projections/metadata.json @@ -1,6 +1,5 @@ { "explain_todo": { - "stmt2": true, - "stmt22": true + "stmt2": true } } diff --git a/parser/testdata/01710_projections_and_duplicate_columms/metadata.json b/parser/testdata/01710_projections_and_duplicate_columms/metadata.json index 1295a45747..0967ef424b 100644 --- a/parser/testdata/01710_projections_and_duplicate_columms/metadata.json +++ b/parser/testdata/01710_projections_and_duplicate_columms/metadata.json @@ -1,5 +1 @@ -{ - "explain_todo": { - "stmt3": true - } -} +{} diff --git a/parser/testdata/01710_projections_group_by_no_key/metadata.json b/parser/testdata/01710_projections_group_by_no_key/metadata.json index ef58f80315..0967ef424b 100644 --- a/parser/testdata/01710_projections_group_by_no_key/metadata.json +++ b/parser/testdata/01710_projections_group_by_no_key/metadata.json @@ -1,5 +1 @@ -{ - "explain_todo": { - "stmt2": true - } -} +{} diff --git a/parser/testdata/01710_projections_order_by_complete/metadata.json b/parser/testdata/01710_projections_order_by_complete/metadata.json index bc141058a4..1295a45747 100644 --- a/parser/testdata/01710_projections_order_by_complete/metadata.json +++ b/parser/testdata/01710_projections_order_by_complete/metadata.json @@ -1,6 +1,5 @@ { "explain_todo": { - "stmt2": true, "stmt3": true } } diff --git a/parser/testdata/01710_query_log_with_projection_info/metadata.json b/parser/testdata/01710_query_log_with_projection_info/metadata.json index 3a06a4a1ac..0967ef424b 100644 --- a/parser/testdata/01710_query_log_with_projection_info/metadata.json +++ b/parser/testdata/01710_query_log_with_projection_info/metadata.json @@ -1,5 +1 @@ -{ - "explain_todo": { - "stmt5": true - } -} +{} diff --git a/parser/testdata/02302_projections_GROUP_BY_ORDERY_BY_optimize_aggregation_in_order/metadata.json b/parser/testdata/02302_projections_GROUP_BY_ORDERY_BY_optimize_aggregation_in_order/metadata.json index ef58f80315..0967ef424b 100644 --- a/parser/testdata/02302_projections_GROUP_BY_ORDERY_BY_optimize_aggregation_in_order/metadata.json +++ b/parser/testdata/02302_projections_GROUP_BY_ORDERY_BY_optimize_aggregation_in_order/metadata.json @@ -1,5 +1 @@ -{ - "explain_todo": { - "stmt2": true - } -} +{} diff --git a/parser/testdata/02343_aggregation_pipeline/metadata.json b/parser/testdata/02343_aggregation_pipeline/metadata.json index 6fe28e776c..c3cc276ac4 100644 --- a/parser/testdata/02343_aggregation_pipeline/metadata.json +++ b/parser/testdata/02343_aggregation_pipeline/metadata.json @@ -1,6 +1,5 @@ { "explain_todo": { - "stmt24": true, "stmt30": true, "stmt32": true } diff --git a/parser/testdata/02478_projection_with_group_by_alter/metadata.json b/parser/testdata/02478_projection_with_group_by_alter/metadata.json index 25eef6c509..abf21b100d 100644 --- a/parser/testdata/02478_projection_with_group_by_alter/metadata.json +++ b/parser/testdata/02478_projection_with_group_by_alter/metadata.json @@ -2,7 +2,6 @@ "explain_todo": { "stmt11": true, "stmt15": true, - "stmt2": true, "stmt7": true } } diff --git a/parser/testdata/02515_projections_with_totals/metadata.json b/parser/testdata/02515_projections_with_totals/metadata.json index ef58f80315..0967ef424b 100644 --- a/parser/testdata/02515_projections_with_totals/metadata.json +++ b/parser/testdata/02515_projections_with_totals/metadata.json @@ -1,5 +1 @@ -{ - "explain_todo": { - "stmt2": true - } -} +{} diff --git a/parser/testdata/02540_duplicate_primary_key/metadata.json b/parser/testdata/02540_duplicate_primary_key/metadata.json index 1295a45747..0967ef424b 100644 --- a/parser/testdata/02540_duplicate_primary_key/metadata.json +++ b/parser/testdata/02540_duplicate_primary_key/metadata.json @@ -1,5 +1 @@ -{ - "explain_todo": { - "stmt3": true - } -} +{} diff --git a/parser/testdata/02540_duplicate_primary_key2/metadata.json b/parser/testdata/02540_duplicate_primary_key2/metadata.json index 1295a45747..0967ef424b 100644 --- a/parser/testdata/02540_duplicate_primary_key2/metadata.json +++ b/parser/testdata/02540_duplicate_primary_key2/metadata.json @@ -1,5 +1 @@ -{ - "explain_todo": { - "stmt3": true - } -} +{} diff --git a/parser/testdata/02668_column_block_number_with_projections/metadata.json b/parser/testdata/02668_column_block_number_with_projections/metadata.json index ef58f80315..0967ef424b 100644 --- a/parser/testdata/02668_column_block_number_with_projections/metadata.json +++ b/parser/testdata/02668_column_block_number_with_projections/metadata.json @@ -1,5 +1 @@ -{ - "explain_todo": { - "stmt2": true - } -} +{} diff --git a/parser/testdata/02691_drop_column_with_projections_replicated/metadata.json b/parser/testdata/02691_drop_column_with_projections_replicated/metadata.json index 9a8cc69c0b..b65b07d7a6 100644 --- a/parser/testdata/02691_drop_column_with_projections_replicated/metadata.json +++ b/parser/testdata/02691_drop_column_with_projections_replicated/metadata.json @@ -1,6 +1,5 @@ { "explain_todo": { - "stmt2": true, "stmt4": true } } diff --git a/parser/testdata/02725_agg_projection_respect_PK/metadata.json b/parser/testdata/02725_agg_projection_respect_PK/metadata.json index 68d51a3364..ff0eba6904 100644 --- a/parser/testdata/02725_agg_projection_respect_PK/metadata.json +++ b/parser/testdata/02725_agg_projection_respect_PK/metadata.json @@ -1,6 +1,5 @@ { "explain_todo": { - "stmt2": true, "stmt6": true, "stmt7": true } diff --git a/parser/testdata/02784_projections_read_in_order_bug/metadata.json b/parser/testdata/02784_projections_read_in_order_bug/metadata.json index ef58f80315..0967ef424b 100644 --- a/parser/testdata/02784_projections_read_in_order_bug/metadata.json +++ b/parser/testdata/02784_projections_read_in_order_bug/metadata.json @@ -1,5 +1 @@ -{ - "explain_todo": { - "stmt2": true - } -} +{} diff --git a/parser/testdata/02792_drop_projection_lwd/metadata.json b/parser/testdata/02792_drop_projection_lwd/metadata.json index c0508292e0..3c17446e87 100644 --- a/parser/testdata/02792_drop_projection_lwd/metadata.json +++ b/parser/testdata/02792_drop_projection_lwd/metadata.json @@ -1,6 +1,5 @@ { "explain_todo": { - "stmt3": true, "stmt5": true, "stmt6": true, "stmt8": true diff --git a/parser/testdata/02832_alter_delete_indexes_projections/metadata.json b/parser/testdata/02832_alter_delete_indexes_projections/metadata.json index d21b63e86b..f04990226e 100644 --- a/parser/testdata/02832_alter_delete_indexes_projections/metadata.json +++ b/parser/testdata/02832_alter_delete_indexes_projections/metadata.json @@ -1,6 +1,5 @@ { "explain_todo": { - "stmt10": true, "stmt14": true, "stmt6": true } diff --git a/parser/testdata/02906_force_optimize_projection_name/metadata.json b/parser/testdata/02906_force_optimize_projection_name/metadata.json index ef58f80315..0967ef424b 100644 --- a/parser/testdata/02906_force_optimize_projection_name/metadata.json +++ b/parser/testdata/02906_force_optimize_projection_name/metadata.json @@ -1,5 +1 @@ -{ - "explain_todo": { - "stmt2": true - } -} +{} diff --git a/parser/testdata/02915_analyzer_fuzz_2/metadata.json b/parser/testdata/02915_analyzer_fuzz_2/metadata.json index ef58f80315..0967ef424b 100644 --- a/parser/testdata/02915_analyzer_fuzz_2/metadata.json +++ b/parser/testdata/02915_analyzer_fuzz_2/metadata.json @@ -1,5 +1 @@ -{ - "explain_todo": { - "stmt2": true - } -} +{} diff --git a/parser/testdata/02920_alter_column_of_projections/metadata.json b/parser/testdata/02920_alter_column_of_projections/metadata.json index 8e8ba24aa6..6b88c8032f 100644 --- a/parser/testdata/02920_alter_column_of_projections/metadata.json +++ b/parser/testdata/02920_alter_column_of_projections/metadata.json @@ -2,7 +2,6 @@ "explain_todo": { "stmt10": true, "stmt12": true, - "stmt2": true, "stmt6": true } } diff --git a/parser/testdata/02934_merge_tree_max_projections/metadata.json b/parser/testdata/02934_merge_tree_max_projections/metadata.json index ca4130f74d..0967ef424b 100644 --- a/parser/testdata/02934_merge_tree_max_projections/metadata.json +++ b/parser/testdata/02934_merge_tree_max_projections/metadata.json @@ -1,7 +1 @@ -{ - "explain_todo": { - "stmt11": true, - "stmt12": true, - "stmt13": true - } -} +{} diff --git a/parser/testdata/02965_projection_with_partition_pruning/metadata.json b/parser/testdata/02965_projection_with_partition_pruning/metadata.json index ef58f80315..0967ef424b 100644 --- a/parser/testdata/02965_projection_with_partition_pruning/metadata.json +++ b/parser/testdata/02965_projection_with_partition_pruning/metadata.json @@ -1,5 +1 @@ -{ - "explain_todo": { - "stmt2": true - } -} +{} diff --git a/parser/testdata/02968_projection_merge/metadata.json b/parser/testdata/02968_projection_merge/metadata.json index 83d4828c9d..7bf4b04abe 100644 --- a/parser/testdata/02968_projection_merge/metadata.json +++ b/parser/testdata/02968_projection_merge/metadata.json @@ -1,9 +1,5 @@ { "explain_todo": { - "stmt12": true, - "stmt21": true, - "stmt3": true, - "stmt30": true, "stmt33": true } } diff --git a/parser/testdata/02996_analyzer_prewhere_projection/metadata.json b/parser/testdata/02996_analyzer_prewhere_projection/metadata.json index ef58f80315..0967ef424b 100644 --- a/parser/testdata/02996_analyzer_prewhere_projection/metadata.json +++ b/parser/testdata/02996_analyzer_prewhere_projection/metadata.json @@ -1,5 +1 @@ -{ - "explain_todo": { - "stmt2": true - } -} +{} diff --git a/parser/testdata/03002_analyzer_prewhere/metadata.json b/parser/testdata/03002_analyzer_prewhere/metadata.json index ef58f80315..0967ef424b 100644 --- a/parser/testdata/03002_analyzer_prewhere/metadata.json +++ b/parser/testdata/03002_analyzer_prewhere/metadata.json @@ -1,5 +1 @@ -{ - "explain_todo": { - "stmt2": true - } -} +{} diff --git a/parser/testdata/03047_on_fly_mutations_projections/metadata.json b/parser/testdata/03047_on_fly_mutations_projections/metadata.json index c5d9509926..25122ac4f4 100644 --- a/parser/testdata/03047_on_fly_mutations_projections/metadata.json +++ b/parser/testdata/03047_on_fly_mutations_projections/metadata.json @@ -1,6 +1,5 @@ { "explain_todo": { - "stmt4": true, "stmt5": true, "stmt9": true } diff --git a/parser/testdata/03128_argMin_combinator_projection/metadata.json b/parser/testdata/03128_argMin_combinator_projection/metadata.json index e0f1f11696..b563327205 100644 --- a/parser/testdata/03128_argMin_combinator_projection/metadata.json +++ b/parser/testdata/03128_argMin_combinator_projection/metadata.json @@ -1,7 +1,5 @@ { "explain_todo": { - "stmt3": true, - "stmt6": true, "stmt7": true } } diff --git a/parser/testdata/03161_lightweight_delete_projection/metadata.json b/parser/testdata/03161_lightweight_delete_projection/metadata.json index 75c34b7372..d0b0d74418 100644 --- a/parser/testdata/03161_lightweight_delete_projection/metadata.json +++ b/parser/testdata/03161_lightweight_delete_projection/metadata.json @@ -2,11 +2,9 @@ "explain_todo": { "stmt11": true, "stmt18": true, - "stmt24": true, "stmt28": true, "stmt31": true, "stmt38": true, - "stmt4": true, "stmt8": true } } diff --git a/parser/testdata/03174_projection_deduplicate/metadata.json b/parser/testdata/03174_projection_deduplicate/metadata.json index 0c77050385..d02612666a 100644 --- a/parser/testdata/03174_projection_deduplicate/metadata.json +++ b/parser/testdata/03174_projection_deduplicate/metadata.json @@ -1,6 +1,5 @@ { "explain_todo": { - "stmt2": true, "stmt5": true, "stmt8": true } diff --git a/parser/testdata/03206_projection_merge_special_mergetree/metadata.json b/parser/testdata/03206_projection_merge_special_mergetree/metadata.json index 85fd03c532..9dfc64b8e0 100644 --- a/parser/testdata/03206_projection_merge_special_mergetree/metadata.json +++ b/parser/testdata/03206_projection_merge_special_mergetree/metadata.json @@ -1,15 +1,9 @@ { "explain_todo": { "stmt10": true, - "stmt12": true, - "stmt13": true, - "stmt14": true, "stmt19": true, - "stmt2": true, - "stmt21": true, "stmt23": true, "stmt4": true, - "stmt6": true, "stmt8": true } } diff --git a/parser/testdata/03206_projection_merge_special_mergetree_ignore/metadata.json b/parser/testdata/03206_projection_merge_special_mergetree_ignore/metadata.json index f5dd12602b..3a06a4a1ac 100644 --- a/parser/testdata/03206_projection_merge_special_mergetree_ignore/metadata.json +++ b/parser/testdata/03206_projection_merge_special_mergetree_ignore/metadata.json @@ -1,6 +1,5 @@ { "explain_todo": { - "stmt2": true, "stmt5": true } } diff --git a/parser/testdata/03221_merge_profile_events/metadata.json b/parser/testdata/03221_merge_profile_events/metadata.json index 7bab4f9f10..e1d516782d 100644 --- a/parser/testdata/03221_merge_profile_events/metadata.json +++ b/parser/testdata/03221_merge_profile_events/metadata.json @@ -1,7 +1,6 @@ { "explain_todo": { "stmt15": true, - "stmt18": true, "stmt23": true, "stmt7": true } diff --git a/parser/testdata/03230_show_create_query_identifier_quoting_style/metadata.json b/parser/testdata/03230_show_create_query_identifier_quoting_style/metadata.json index cd7763da2a..bb3f5293b9 100644 --- a/parser/testdata/03230_show_create_query_identifier_quoting_style/metadata.json +++ b/parser/testdata/03230_show_create_query_identifier_quoting_style/metadata.json @@ -48,7 +48,6 @@ "stmt73": true, "stmt74": true, "stmt75": true, - "stmt76": true, - "stmt8": true + "stmt76": true } } diff --git a/parser/testdata/03230_system_projections/metadata.json b/parser/testdata/03230_system_projections/metadata.json index 7b4ddafa53..b65b07d7a6 100644 --- a/parser/testdata/03230_system_projections/metadata.json +++ b/parser/testdata/03230_system_projections/metadata.json @@ -1,6 +1,5 @@ { "explain_todo": { - "stmt3": true, "stmt4": true } } diff --git a/parser/testdata/03254_project_lwd_respects_row_exists/metadata.json b/parser/testdata/03254_project_lwd_respects_row_exists/metadata.json index 0c58ba5f88..9022255763 100644 --- a/parser/testdata/03254_project_lwd_respects_row_exists/metadata.json +++ b/parser/testdata/03254_project_lwd_respects_row_exists/metadata.json @@ -1,8 +1,6 @@ { "explain_todo": { - "stmt2": true, "stmt4": true, - "stmt7": true, "stmt9": true } } diff --git a/parser/testdata/03290_force_normal_projection/metadata.json b/parser/testdata/03290_force_normal_projection/metadata.json index ef58f80315..0967ef424b 100644 --- a/parser/testdata/03290_force_normal_projection/metadata.json +++ b/parser/testdata/03290_force_normal_projection/metadata.json @@ -1,5 +1 @@ -{ - "explain_todo": { - "stmt2": true - } -} +{} diff --git a/parser/testdata/03310_aggregate_projection_count_nullable/metadata.json b/parser/testdata/03310_aggregate_projection_count_nullable/metadata.json index 7fb4a26677..0967ef424b 100644 --- a/parser/testdata/03310_aggregate_projection_count_nullable/metadata.json +++ b/parser/testdata/03310_aggregate_projection_count_nullable/metadata.json @@ -1,6 +1 @@ -{ - "explain_todo": { - "stmt12": true, - "stmt2": true - } -} +{} diff --git a/parser/testdata/03325_alter_modify_projection_primary_key_column/metadata.json b/parser/testdata/03325_alter_modify_projection_primary_key_column/metadata.json index c6e7b4d56b..0967ef424b 100644 --- a/parser/testdata/03325_alter_modify_projection_primary_key_column/metadata.json +++ b/parser/testdata/03325_alter_modify_projection_primary_key_column/metadata.json @@ -1,6 +1 @@ -{ - "explain_todo": { - "stmt2": true, - "stmt8": true - } -} +{} diff --git a/parser/testdata/03340_projections_formatting/metadata.json b/parser/testdata/03340_projections_formatting/metadata.json index 546438e0f8..9685022035 100644 --- a/parser/testdata/03340_projections_formatting/metadata.json +++ b/parser/testdata/03340_projections_formatting/metadata.json @@ -1,18 +1,13 @@ { "explain_todo": { "stmt1": true, - "stmt10": true, "stmt11": true, - "stmt13": true, "stmt14": true, - "stmt16": true, "stmt17": true, - "stmt19": true, "stmt2": true, "stmt20": true, "stmt4": true, "stmt5": true, - "stmt7": true, "stmt8": true } } diff --git a/parser/testdata/03401_normal_projection_with_part_offset/metadata.json b/parser/testdata/03401_normal_projection_with_part_offset/metadata.json index 34aef141f9..f4c74e32be 100644 --- a/parser/testdata/03401_normal_projection_with_part_offset/metadata.json +++ b/parser/testdata/03401_normal_projection_with_part_offset/metadata.json @@ -1,8 +1,5 @@ { "explain_todo": { - "stmt10": true, - "stmt16": true, - "stmt2": true, - "stmt20": true + "stmt10": true } } diff --git a/parser/testdata/03401_normal_projection_with_part_offset_no_sorting/metadata.json b/parser/testdata/03401_normal_projection_with_part_offset_no_sorting/metadata.json index 0f7f289811..f4c74e32be 100644 --- a/parser/testdata/03401_normal_projection_with_part_offset_no_sorting/metadata.json +++ b/parser/testdata/03401_normal_projection_with_part_offset_no_sorting/metadata.json @@ -1,6 +1,5 @@ { "explain_todo": { - "stmt10": true, - "stmt2": true + "stmt10": true } } diff --git a/parser/testdata/03402_adding_projection_to_temporary_table/metadata.json b/parser/testdata/03402_adding_projection_to_temporary_table/metadata.json index bc141058a4..1295a45747 100644 --- a/parser/testdata/03402_adding_projection_to_temporary_table/metadata.json +++ b/parser/testdata/03402_adding_projection_to_temporary_table/metadata.json @@ -1,6 +1,5 @@ { "explain_todo": { - "stmt2": true, "stmt3": true } } diff --git a/parser/testdata/03443_part_starting_offset/metadata.json b/parser/testdata/03443_part_starting_offset/metadata.json index ef58f80315..0967ef424b 100644 --- a/parser/testdata/03443_part_starting_offset/metadata.json +++ b/parser/testdata/03443_part_starting_offset/metadata.json @@ -1,5 +1 @@ -{ - "explain_todo": { - "stmt2": true - } -} +{} diff --git a/parser/testdata/03460_normal_projection_index/metadata.json b/parser/testdata/03460_normal_projection_index/metadata.json index 55684d89b2..16c95357c2 100644 --- a/parser/testdata/03460_normal_projection_index/metadata.json +++ b/parser/testdata/03460_normal_projection_index/metadata.json @@ -4,12 +4,10 @@ "stmt15": true, "stmt17": true, "stmt19": true, - "stmt24": true, "stmt32": true, "stmt34": true, "stmt36": true, "stmt45": true, - "stmt47": true, - "stmt5": true + "stmt47": true } } diff --git a/parser/testdata/03460_normal_projection_index_bug_race_conditions/metadata.json b/parser/testdata/03460_normal_projection_index_bug_race_conditions/metadata.json index 3a06a4a1ac..0967ef424b 100644 --- a/parser/testdata/03460_normal_projection_index_bug_race_conditions/metadata.json +++ b/parser/testdata/03460_normal_projection_index_bug_race_conditions/metadata.json @@ -1,5 +1 @@ -{ - "explain_todo": { - "stmt5": true - } -} +{} diff --git a/parser/testdata/03460_projection_part_filtering_and_introspection/metadata.json b/parser/testdata/03460_projection_part_filtering_and_introspection/metadata.json index c1da806dad..a0dba08bb1 100644 --- a/parser/testdata/03460_projection_part_filtering_and_introspection/metadata.json +++ b/parser/testdata/03460_projection_part_filtering_and_introspection/metadata.json @@ -3,7 +3,6 @@ "stmt12": true, "stmt13": true, "stmt14": true, - "stmt15": true, - "stmt2": true + "stmt15": true } } diff --git a/parser/testdata/03460_query_condition_cache_with_projections/metadata.json b/parser/testdata/03460_query_condition_cache_with_projections/metadata.json index 3a06a4a1ac..0967ef424b 100644 --- a/parser/testdata/03460_query_condition_cache_with_projections/metadata.json +++ b/parser/testdata/03460_query_condition_cache_with_projections/metadata.json @@ -1,5 +1 @@ -{ - "explain_todo": { - "stmt5": true - } -} +{} diff --git a/parser/testdata/03513_lazy_materialization_projections_fix/metadata.json b/parser/testdata/03513_lazy_materialization_projections_fix/metadata.json index 88720bfeb4..d61214279f 100644 --- a/parser/testdata/03513_lazy_materialization_projections_fix/metadata.json +++ b/parser/testdata/03513_lazy_materialization_projections_fix/metadata.json @@ -5,7 +5,6 @@ "stmt18": true, "stmt23": true, "stmt24": true, - "stmt6": true, "stmt9": true } } diff --git a/parser/testdata/03546_merge_tree_projection_shared_snapshot/metadata.json b/parser/testdata/03546_merge_tree_projection_shared_snapshot/metadata.json index ef58f80315..0967ef424b 100644 --- a/parser/testdata/03546_merge_tree_projection_shared_snapshot/metadata.json +++ b/parser/testdata/03546_merge_tree_projection_shared_snapshot/metadata.json @@ -1,5 +1 @@ -{ - "explain_todo": { - "stmt2": true - } -} +{} diff --git a/parser/testdata/03550_projection_with_part_offset_ttl/metadata.json b/parser/testdata/03550_projection_with_part_offset_ttl/metadata.json index ef58f80315..0967ef424b 100644 --- a/parser/testdata/03550_projection_with_part_offset_ttl/metadata.json +++ b/parser/testdata/03550_projection_with_part_offset_ttl/metadata.json @@ -1,5 +1 @@ -{ - "explain_todo": { - "stmt2": true - } -} +{} diff --git a/parser/testdata/03571_lwd_and_projections/metadata.json b/parser/testdata/03571_lwd_and_projections/metadata.json index 323c7c4c53..dbdbb76d4f 100644 --- a/parser/testdata/03571_lwd_and_projections/metadata.json +++ b/parser/testdata/03571_lwd_and_projections/metadata.json @@ -1,6 +1,5 @@ { "explain_todo": { - "stmt2": true, "stmt6": true } } diff --git a/parser/testdata/03593_allow_projection_with_parent_part_offset/metadata.json b/parser/testdata/03593_allow_projection_with_parent_part_offset/metadata.json index ef58f80315..0967ef424b 100644 --- a/parser/testdata/03593_allow_projection_with_parent_part_offset/metadata.json +++ b/parser/testdata/03593_allow_projection_with_parent_part_offset/metadata.json @@ -1,5 +1 @@ -{ - "explain_todo": { - "stmt2": true - } -} +{} diff --git a/parser/testdata/03593_backup_with_broken_projection/metadata.json b/parser/testdata/03593_backup_with_broken_projection/metadata.json index d5b3714b30..a4f8773303 100644 --- a/parser/testdata/03593_backup_with_broken_projection/metadata.json +++ b/parser/testdata/03593_backup_with_broken_projection/metadata.json @@ -1,6 +1,5 @@ { "explain_todo": { - "stmt1": true, "stmt3": true, "stmt4": true, "stmt5": true diff --git a/parser/testdata/03701_distinct_but_no_group_by_projection_table_use_check/metadata.json b/parser/testdata/03701_distinct_but_no_group_by_projection_table_use_check/metadata.json index ab7874eb0c..0967ef424b 100644 --- a/parser/testdata/03701_distinct_but_no_group_by_projection_table_use_check/metadata.json +++ b/parser/testdata/03701_distinct_but_no_group_by_projection_table_use_check/metadata.json @@ -1 +1 @@ -{"explain_todo":{"stmt19":true,"stmt3":true}} +{} diff --git a/parser/testdata/03707_empty_parts_with_non_empty_projections_merge/metadata.json b/parser/testdata/03707_empty_parts_with_non_empty_projections_merge/metadata.json index 0081ae918c..bc5c6edb66 100644 --- a/parser/testdata/03707_empty_parts_with_non_empty_projections_merge/metadata.json +++ b/parser/testdata/03707_empty_parts_with_non_empty_projections_merge/metadata.json @@ -1,6 +1,5 @@ { "explain_todo": { - "stmt2": true, "stmt3": true, "stmt5": true } From 1079b4ec5240352dc570c29767921d4c67b52375 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 30 Dec 2025 22:02:23 +0000 Subject: [PATCH 12/18] Add ViewTargets support for MATERIALIZED VIEW with TO clause - Add hasStorageChild check for counting ViewTargets as a child - Output ViewTargets when MATERIALIZED VIEW has TO clause but no storage - This matches ClickHouse's EXPLAIN AST format for MATERIALIZED VIEW --- internal/explain/statements.go | 11 ++++++++++- .../00508_materialized_view_to/metadata.json | 6 +----- .../metadata.json | 6 +++++- .../00975_recursive_materialized_view/metadata.json | 6 +----- .../metadata.json | 2 +- .../metadata.json | 2 +- .../metadata.json | 1 - .../metadata.json | 6 +----- .../metadata.json | 1 - .../metadata.json | 2 -- .../metadata.json | 6 +----- .../metadata.json | 6 +----- parser/testdata/01268_mv_scalars/metadata.json | 6 +----- .../testdata/01277_buffer_column_order/metadata.json | 7 +------ .../metadata.json | 6 +----- .../metadata.json | 2 +- .../metadata.json | 6 +----- .../metadata.json | 8 +------- parser/testdata/01947_mv_subquery/metadata.json | 2 -- parser/testdata/02125_query_views_log/metadata.json | 7 +------ parser/testdata/02137_mv_into_join/metadata.json | 7 +------ .../02139_MV_with_scalar_subquery/metadata.json | 7 +------ .../testdata/02174_cte_scalar_cache_mv/metadata.json | 8 +------- .../02184_default_table_engine/metadata.json | 1 - .../02187_insert_values_with_mv/metadata.json | 10 +--------- .../02231_buffer_aggregate_states_leak/metadata.json | 6 +----- .../02345_implicit_transaction/metadata.json | 3 +-- parser/testdata/02420_final_setting/metadata.json | 3 +-- .../02420_final_setting_analyzer/metadata.json | 3 +-- .../metadata.json | 6 +----- .../metadata.json | 2 +- .../metadata.json | 6 +----- .../metadata.json | 6 +----- .../metadata.json | 6 +----- .../testdata/02697_alter_dependencies/metadata.json | 1 - .../02912_ingestion_mv_deduplication/metadata.json | 1 - .../metadata.json | 1 - .../metadata.json | 6 +----- parser/testdata/02933_ephemeral_mv/metadata.json | 6 +----- .../metadata.json | 12 +----------- .../metadata.json | 7 +------ .../02999_scalar_subqueries_bug_2/metadata.json | 6 +----- parser/testdata/03002_modify_query_cte/metadata.json | 1 - .../metadata.json | 4 +--- .../03008_deduplication_wrong_mv/metadata.json | 6 +----- .../metadata.json | 3 +-- .../03094_grouparraysorted_memory/metadata.json | 6 +----- .../03094_virtual_column_table_name/metadata.json | 1 - .../metadata.json | 1 - .../03150_dynamic_type_mv_insert/metadata.json | 6 +----- .../03155_in_nested_subselects/metadata.json | 6 +----- .../testdata/03161_create_table_as_mv/metadata.json | 3 +-- .../metadata.json | 3 +-- .../metadata.json | 2 -- .../metadata.json | 1 - .../metadata.json | 3 +-- .../03285_default_engine_with_settings/metadata.json | 7 +------ parser/testdata/03301_subcolumns_in_mv/metadata.json | 3 +-- .../metadata.json | 2 +- .../metadata.json | 6 +----- .../03357_analyzer_insert_view/metadata.json | 6 +----- .../03357_storage_join_mv_context/metadata.json | 6 +----- .../metadata.json | 7 +------ .../metadata.json | 2 +- .../metadata.json | 6 +----- .../03633_mv_squash_parallel_inserts/metadata.json | 6 +----- .../03710_pr_insert_into_mv_with_join/metadata.json | 2 +- parser/testdata/03710_pr_join_with_mv/metadata.json | 1 - .../metadata.json | 1 - 69 files changed, 67 insertions(+), 247 deletions(-) diff --git a/internal/explain/statements.go b/internal/explain/statements.go index b8041a6aea..6ea3a2130d 100644 --- a/internal/explain/statements.go +++ b/internal/explain/statements.go @@ -140,9 +140,14 @@ func explainCreateQuery(sb *strings.Builder, n *ast.CreateQuery, indent string, if len(n.Columns) > 0 || len(n.Indexes) > 0 || len(n.Projections) > 0 || len(n.Constraints) > 0 { children++ } - if n.Engine != nil || len(n.OrderBy) > 0 || len(n.PrimaryKey) > 0 || n.PartitionBy != nil { + hasStorageChild := n.Engine != nil || len(n.OrderBy) > 0 || len(n.PrimaryKey) > 0 || n.PartitionBy != nil || n.SampleBy != nil || n.TTL != nil || len(n.Settings) > 0 + if hasStorageChild { children++ } + // For materialized views with TO clause but no storage, count ViewTargets as a child + if n.Materialized && n.To != "" && !hasStorageChild { + children++ // ViewTargets + } if n.AsSelect != nil { children++ } @@ -381,6 +386,10 @@ func explainCreateQuery(sb *strings.Builder, n *ast.CreateQuery, indent string, if len(n.Settings) > 0 { fmt.Fprintf(sb, "%s Set\n", storageIndent) } + } else if n.Materialized && n.To != "" { + // For materialized views with TO clause but no storage definition, + // output just ViewTargets without children + fmt.Fprintf(sb, "%s ViewTargets\n", indent) } // For non-materialized views, output AsSelect after storage if n.AsSelect != nil && !n.Materialized { diff --git a/parser/testdata/00508_materialized_view_to/metadata.json b/parser/testdata/00508_materialized_view_to/metadata.json index 1295a45747..0967ef424b 100644 --- a/parser/testdata/00508_materialized_view_to/metadata.json +++ b/parser/testdata/00508_materialized_view_to/metadata.json @@ -1,5 +1 @@ -{ - "explain_todo": { - "stmt3": true - } -} +{} diff --git a/parser/testdata/00943_mv_rename_without_inner_table/metadata.json b/parser/testdata/00943_mv_rename_without_inner_table/metadata.json index e582b9b3fe..f4c74e32be 100644 --- a/parser/testdata/00943_mv_rename_without_inner_table/metadata.json +++ b/parser/testdata/00943_mv_rename_without_inner_table/metadata.json @@ -1 +1,5 @@ -{"explain_todo":{"stmt10":true,"stmt7":true}} +{ + "explain_todo": { + "stmt10": true + } +} diff --git a/parser/testdata/00975_recursive_materialized_view/metadata.json b/parser/testdata/00975_recursive_materialized_view/metadata.json index b563327205..0967ef424b 100644 --- a/parser/testdata/00975_recursive_materialized_view/metadata.json +++ b/parser/testdata/00975_recursive_materialized_view/metadata.json @@ -1,5 +1 @@ -{ - "explain_todo": { - "stmt7": true - } -} +{} diff --git a/parser/testdata/00982_low_cardinality_setting_in_mv/metadata.json b/parser/testdata/00982_low_cardinality_setting_in_mv/metadata.json index 3a2014fe8c..0967ef424b 100644 --- a/parser/testdata/00982_low_cardinality_setting_in_mv/metadata.json +++ b/parser/testdata/00982_low_cardinality_setting_in_mv/metadata.json @@ -1 +1 @@ -{"explain_todo":{"stmt6":true}} +{} diff --git a/parser/testdata/00986_materialized_view_stack_overflow/metadata.json b/parser/testdata/00986_materialized_view_stack_overflow/metadata.json index 2d26779ee7..0967ef424b 100644 --- a/parser/testdata/00986_materialized_view_stack_overflow/metadata.json +++ b/parser/testdata/00986_materialized_view_stack_overflow/metadata.json @@ -1 +1 @@ -{"explain_todo":{"stmt7":true,"stmt8":true}} +{} diff --git a/parser/testdata/01019_alter_materialized_view_query/metadata.json b/parser/testdata/01019_alter_materialized_view_query/metadata.json index 08a237ec55..7ad5569408 100644 --- a/parser/testdata/01019_alter_materialized_view_query/metadata.json +++ b/parser/testdata/01019_alter_materialized_view_query/metadata.json @@ -1,6 +1,5 @@ { "explain_todo": { - "stmt6": true, "stmt9": true } } diff --git a/parser/testdata/01019_materialized_view_select_extra_columns/metadata.json b/parser/testdata/01019_materialized_view_select_extra_columns/metadata.json index b563327205..0967ef424b 100644 --- a/parser/testdata/01019_materialized_view_select_extra_columns/metadata.json +++ b/parser/testdata/01019_materialized_view_select_extra_columns/metadata.json @@ -1,5 +1 @@ -{ - "explain_todo": { - "stmt7": true - } -} +{} diff --git a/parser/testdata/01023_materialized_view_query_context/metadata.json b/parser/testdata/01023_materialized_view_query_context/metadata.json index 5e06643b76..dbdbb76d4f 100644 --- a/parser/testdata/01023_materialized_view_query_context/metadata.json +++ b/parser/testdata/01023_materialized_view_query_context/metadata.json @@ -1,6 +1,5 @@ { "explain_todo": { - "stmt12": true, "stmt6": true } } diff --git a/parser/testdata/01064_incremental_streaming_from_2_src_with_feedback/metadata.json b/parser/testdata/01064_incremental_streaming_from_2_src_with_feedback/metadata.json index 2250064190..99a7322526 100644 --- a/parser/testdata/01064_incremental_streaming_from_2_src_with_feedback/metadata.json +++ b/parser/testdata/01064_incremental_streaming_from_2_src_with_feedback/metadata.json @@ -1,7 +1,5 @@ { "explain_todo": { - "stmt14": true, - "stmt16": true, "stmt17": true, "stmt18": true, "stmt19": true diff --git a/parser/testdata/01069_materialized_view_alter_target_table/metadata.json b/parser/testdata/01069_materialized_view_alter_target_table/metadata.json index dbdbb76d4f..0967ef424b 100644 --- a/parser/testdata/01069_materialized_view_alter_target_table/metadata.json +++ b/parser/testdata/01069_materialized_view_alter_target_table/metadata.json @@ -1,5 +1 @@ -{ - "explain_todo": { - "stmt6": true - } -} +{} diff --git a/parser/testdata/01069_materialized_view_alter_target_table_with_default_expression/metadata.json b/parser/testdata/01069_materialized_view_alter_target_table_with_default_expression/metadata.json index dbdbb76d4f..0967ef424b 100644 --- a/parser/testdata/01069_materialized_view_alter_target_table_with_default_expression/metadata.json +++ b/parser/testdata/01069_materialized_view_alter_target_table_with_default_expression/metadata.json @@ -1,5 +1 @@ -{ - "explain_todo": { - "stmt6": true - } -} +{} diff --git a/parser/testdata/01268_mv_scalars/metadata.json b/parser/testdata/01268_mv_scalars/metadata.json index 8c6a18d871..0967ef424b 100644 --- a/parser/testdata/01268_mv_scalars/metadata.json +++ b/parser/testdata/01268_mv_scalars/metadata.json @@ -1,5 +1 @@ -{ - "explain_todo": { - "stmt19": true - } -} +{} diff --git a/parser/testdata/01277_buffer_column_order/metadata.json b/parser/testdata/01277_buffer_column_order/metadata.json index 470d47b502..0967ef424b 100644 --- a/parser/testdata/01277_buffer_column_order/metadata.json +++ b/parser/testdata/01277_buffer_column_order/metadata.json @@ -1,6 +1 @@ -{ - "explain_todo": { - "stmt10": true, - "stmt9": true - } -} +{} diff --git a/parser/testdata/01361_buffer_table_flush_with_materialized_view/metadata.json b/parser/testdata/01361_buffer_table_flush_with_materialized_view/metadata.json index b563327205..0967ef424b 100644 --- a/parser/testdata/01361_buffer_table_flush_with_materialized_view/metadata.json +++ b/parser/testdata/01361_buffer_table_flush_with_materialized_view/metadata.json @@ -1,5 +1 @@ -{ - "explain_todo": { - "stmt7": true - } -} +{} diff --git a/parser/testdata/01527_materialized_view_stack_overflow/metadata.json b/parser/testdata/01527_materialized_view_stack_overflow/metadata.json index 731f2dc7ca..0967ef424b 100644 --- a/parser/testdata/01527_materialized_view_stack_overflow/metadata.json +++ b/parser/testdata/01527_materialized_view_stack_overflow/metadata.json @@ -1 +1 @@ -{"explain_todo":{"stmt13":true,"stmt14":true,"stmt5":true,"stmt6":true}} +{} diff --git a/parser/testdata/01836_date_time_keep_default_timezone_on_operations_den_crane/metadata.json b/parser/testdata/01836_date_time_keep_default_timezone_on_operations_den_crane/metadata.json index c45b7602ba..0967ef424b 100644 --- a/parser/testdata/01836_date_time_keep_default_timezone_on_operations_den_crane/metadata.json +++ b/parser/testdata/01836_date_time_keep_default_timezone_on_operations_den_crane/metadata.json @@ -1,5 +1 @@ -{ - "explain_todo": { - "stmt12": true - } -} +{} diff --git a/parser/testdata/01927_query_views_log_current_database/metadata.json b/parser/testdata/01927_query_views_log_current_database/metadata.json index b67140f418..0967ef424b 100644 --- a/parser/testdata/01927_query_views_log_current_database/metadata.json +++ b/parser/testdata/01927_query_views_log_current_database/metadata.json @@ -1,7 +1 @@ -{ - "explain_todo": { - "stmt10": true, - "stmt11": true, - "stmt9": true - } -} +{} diff --git a/parser/testdata/01947_mv_subquery/metadata.json b/parser/testdata/01947_mv_subquery/metadata.json index f57f70af25..8eb3175658 100644 --- a/parser/testdata/01947_mv_subquery/metadata.json +++ b/parser/testdata/01947_mv_subquery/metadata.json @@ -1,8 +1,6 @@ { "explain_todo": { - "stmt15": true, "stmt17": true, - "stmt6": true, "stmt8": true } } diff --git a/parser/testdata/02125_query_views_log/metadata.json b/parser/testdata/02125_query_views_log/metadata.json index 92e84e943a..0967ef424b 100644 --- a/parser/testdata/02125_query_views_log/metadata.json +++ b/parser/testdata/02125_query_views_log/metadata.json @@ -1,6 +1 @@ -{ - "explain_todo": { - "stmt8": true, - "stmt9": true - } -} +{} diff --git a/parser/testdata/02137_mv_into_join/metadata.json b/parser/testdata/02137_mv_into_join/metadata.json index ef382ce51e..0967ef424b 100644 --- a/parser/testdata/02137_mv_into_join/metadata.json +++ b/parser/testdata/02137_mv_into_join/metadata.json @@ -1,6 +1 @@ -{ - "explain_todo": { - "stmt4": true, - "stmt5": true - } -} +{} diff --git a/parser/testdata/02139_MV_with_scalar_subquery/metadata.json b/parser/testdata/02139_MV_with_scalar_subquery/metadata.json index ef382ce51e..0967ef424b 100644 --- a/parser/testdata/02139_MV_with_scalar_subquery/metadata.json +++ b/parser/testdata/02139_MV_with_scalar_subquery/metadata.json @@ -1,6 +1 @@ -{ - "explain_todo": { - "stmt4": true, - "stmt5": true - } -} +{} diff --git a/parser/testdata/02174_cte_scalar_cache_mv/metadata.json b/parser/testdata/02174_cte_scalar_cache_mv/metadata.json index 188e60b5be..0967ef424b 100644 --- a/parser/testdata/02174_cte_scalar_cache_mv/metadata.json +++ b/parser/testdata/02174_cte_scalar_cache_mv/metadata.json @@ -1,7 +1 @@ -{ - "explain_todo": { - "stmt19": true, - "stmt33": true, - "stmt4": true - } -} +{} diff --git a/parser/testdata/02184_default_table_engine/metadata.json b/parser/testdata/02184_default_table_engine/metadata.json index 93ef183eaa..62efe4ca41 100644 --- a/parser/testdata/02184_default_table_engine/metadata.json +++ b/parser/testdata/02184_default_table_engine/metadata.json @@ -1,6 +1,5 @@ { "explain_todo": { - "stmt106": true, "stmt107": true, "stmt114": true, "stmt26": true, diff --git a/parser/testdata/02187_insert_values_with_mv/metadata.json b/parser/testdata/02187_insert_values_with_mv/metadata.json index 6294db7a1a..0967ef424b 100644 --- a/parser/testdata/02187_insert_values_with_mv/metadata.json +++ b/parser/testdata/02187_insert_values_with_mv/metadata.json @@ -1,9 +1 @@ -{ - "explain_todo": { - "stmt3": true, - "stmt4": true, - "stmt5": true, - "stmt6": true, - "stmt7": true - } -} +{} diff --git a/parser/testdata/02231_buffer_aggregate_states_leak/metadata.json b/parser/testdata/02231_buffer_aggregate_states_leak/metadata.json index 342b3ff5b4..0967ef424b 100644 --- a/parser/testdata/02231_buffer_aggregate_states_leak/metadata.json +++ b/parser/testdata/02231_buffer_aggregate_states_leak/metadata.json @@ -1,5 +1 @@ -{ - "explain_todo": { - "stmt8": true - } -} +{} diff --git a/parser/testdata/02345_implicit_transaction/metadata.json b/parser/testdata/02345_implicit_transaction/metadata.json index c494997b76..c775d35209 100644 --- a/parser/testdata/02345_implicit_transaction/metadata.json +++ b/parser/testdata/02345_implicit_transaction/metadata.json @@ -5,7 +5,6 @@ "stmt32": true, "stmt35": true, "stmt38": true, - "stmt41": true, - "stmt6": true + "stmt41": true } } diff --git a/parser/testdata/02420_final_setting/metadata.json b/parser/testdata/02420_final_setting/metadata.json index d3a95234cd..e9d6e46171 100644 --- a/parser/testdata/02420_final_setting/metadata.json +++ b/parser/testdata/02420_final_setting/metadata.json @@ -1,6 +1,5 @@ { "explain_todo": { - "stmt1": true, - "stmt23": true + "stmt1": true } } diff --git a/parser/testdata/02420_final_setting_analyzer/metadata.json b/parser/testdata/02420_final_setting_analyzer/metadata.json index 182f4d0e03..ef58f80315 100644 --- a/parser/testdata/02420_final_setting_analyzer/metadata.json +++ b/parser/testdata/02420_final_setting_analyzer/metadata.json @@ -1,6 +1,5 @@ { "explain_todo": { - "stmt2": true, - "stmt24": true + "stmt2": true } } diff --git a/parser/testdata/02459_materialized_view_default_value/metadata.json b/parser/testdata/02459_materialized_view_default_value/metadata.json index dbdbb76d4f..0967ef424b 100644 --- a/parser/testdata/02459_materialized_view_default_value/metadata.json +++ b/parser/testdata/02459_materialized_view_default_value/metadata.json @@ -1,5 +1 @@ -{ - "explain_todo": { - "stmt6": true - } -} +{} diff --git a/parser/testdata/02552_check_referential_table_dependencies/metadata.json b/parser/testdata/02552_check_referential_table_dependencies/metadata.json index 9bcf41ab2b..0967ef424b 100644 --- a/parser/testdata/02552_check_referential_table_dependencies/metadata.json +++ b/parser/testdata/02552_check_referential_table_dependencies/metadata.json @@ -1 +1 @@ -{"explain_todo":{"stmt15":true,"stmt6":true}} +{} diff --git a/parser/testdata/02572_materialized_views_ignore_errors/metadata.json b/parser/testdata/02572_materialized_views_ignore_errors/metadata.json index 7ad5569408..0967ef424b 100644 --- a/parser/testdata/02572_materialized_views_ignore_errors/metadata.json +++ b/parser/testdata/02572_materialized_views_ignore_errors/metadata.json @@ -1,5 +1 @@ -{ - "explain_todo": { - "stmt9": true - } -} +{} diff --git a/parser/testdata/02572_system_logs_materialized_views_ignore_errors/metadata.json b/parser/testdata/02572_system_logs_materialized_views_ignore_errors/metadata.json index dbdbb76d4f..0967ef424b 100644 --- a/parser/testdata/02572_system_logs_materialized_views_ignore_errors/metadata.json +++ b/parser/testdata/02572_system_logs_materialized_views_ignore_errors/metadata.json @@ -1,5 +1 @@ -{ - "explain_todo": { - "stmt6": true - } -} +{} diff --git a/parser/testdata/02696_ignore_inacc_tables_mat_view_atttach/metadata.json b/parser/testdata/02696_ignore_inacc_tables_mat_view_atttach/metadata.json index b65b07d7a6..0967ef424b 100644 --- a/parser/testdata/02696_ignore_inacc_tables_mat_view_atttach/metadata.json +++ b/parser/testdata/02696_ignore_inacc_tables_mat_view_atttach/metadata.json @@ -1,5 +1 @@ -{ - "explain_todo": { - "stmt4": true - } -} +{} diff --git a/parser/testdata/02697_alter_dependencies/metadata.json b/parser/testdata/02697_alter_dependencies/metadata.json index 7b4ddafa53..b65b07d7a6 100644 --- a/parser/testdata/02697_alter_dependencies/metadata.json +++ b/parser/testdata/02697_alter_dependencies/metadata.json @@ -1,6 +1,5 @@ { "explain_todo": { - "stmt3": true, "stmt4": true } } diff --git a/parser/testdata/02912_ingestion_mv_deduplication/metadata.json b/parser/testdata/02912_ingestion_mv_deduplication/metadata.json index 8ab516340a..c5ac9fc15b 100644 --- a/parser/testdata/02912_ingestion_mv_deduplication/metadata.json +++ b/parser/testdata/02912_ingestion_mv_deduplication/metadata.json @@ -2,7 +2,6 @@ "explain_todo": { "stmt17": true, "stmt29": true, - "stmt42": true, "stmt5": true } } diff --git a/parser/testdata/02931_alter_materialized_view_query_inconsistent/metadata.json b/parser/testdata/02931_alter_materialized_view_query_inconsistent/metadata.json index 92efb02376..342b3ff5b4 100644 --- a/parser/testdata/02931_alter_materialized_view_query_inconsistent/metadata.json +++ b/parser/testdata/02931_alter_materialized_view_query_inconsistent/metadata.json @@ -1,6 +1,5 @@ { "explain_todo": { - "stmt6": true, "stmt8": true } } diff --git a/parser/testdata/02932_materialized_view_with_dropped_target_table_no_exception/metadata.json b/parser/testdata/02932_materialized_view_with_dropped_target_table_no_exception/metadata.json index 342b3ff5b4..0967ef424b 100644 --- a/parser/testdata/02932_materialized_view_with_dropped_target_table_no_exception/metadata.json +++ b/parser/testdata/02932_materialized_view_with_dropped_target_table_no_exception/metadata.json @@ -1,5 +1 @@ -{ - "explain_todo": { - "stmt8": true - } -} +{} diff --git a/parser/testdata/02933_ephemeral_mv/metadata.json b/parser/testdata/02933_ephemeral_mv/metadata.json index 1295a45747..0967ef424b 100644 --- a/parser/testdata/02933_ephemeral_mv/metadata.json +++ b/parser/testdata/02933_ephemeral_mv/metadata.json @@ -1,5 +1 @@ -{ - "explain_todo": { - "stmt3": true - } -} +{} diff --git a/parser/testdata/02972_insert_deduplication_token_hierarchical_inserts/metadata.json b/parser/testdata/02972_insert_deduplication_token_hierarchical_inserts/metadata.json index dfb47f61ad..0967ef424b 100644 --- a/parser/testdata/02972_insert_deduplication_token_hierarchical_inserts/metadata.json +++ b/parser/testdata/02972_insert_deduplication_token_hierarchical_inserts/metadata.json @@ -1,11 +1 @@ -{ - "explain_todo": { - "stmt13": true, - "stmt17": true, - "stmt19": true, - "stmt23": true, - "stmt27": true, - "stmt31": true, - "stmt9": true - } -} +{} diff --git a/parser/testdata/02972_insert_deduplication_token_hierarchical_inserts_views/metadata.json b/parser/testdata/02972_insert_deduplication_token_hierarchical_inserts_views/metadata.json index a56c7cdb0b..0967ef424b 100644 --- a/parser/testdata/02972_insert_deduplication_token_hierarchical_inserts_views/metadata.json +++ b/parser/testdata/02972_insert_deduplication_token_hierarchical_inserts_views/metadata.json @@ -1,6 +1 @@ -{ - "explain_todo": { - "stmt10": true, - "stmt12": true - } -} +{} diff --git a/parser/testdata/02999_scalar_subqueries_bug_2/metadata.json b/parser/testdata/02999_scalar_subqueries_bug_2/metadata.json index f4c74e32be..0967ef424b 100644 --- a/parser/testdata/02999_scalar_subqueries_bug_2/metadata.json +++ b/parser/testdata/02999_scalar_subqueries_bug_2/metadata.json @@ -1,5 +1 @@ -{ - "explain_todo": { - "stmt10": true - } -} +{} diff --git a/parser/testdata/03002_modify_query_cte/metadata.json b/parser/testdata/03002_modify_query_cte/metadata.json index 9a8cc69c0b..b65b07d7a6 100644 --- a/parser/testdata/03002_modify_query_cte/metadata.json +++ b/parser/testdata/03002_modify_query_cte/metadata.json @@ -1,6 +1,5 @@ { "explain_todo": { - "stmt2": true, "stmt4": true } } diff --git a/parser/testdata/03008_deduplication_cases_from_docs/metadata.json b/parser/testdata/03008_deduplication_cases_from_docs/metadata.json index 5febba85e8..1334c74e46 100644 --- a/parser/testdata/03008_deduplication_cases_from_docs/metadata.json +++ b/parser/testdata/03008_deduplication_cases_from_docs/metadata.json @@ -1,8 +1,6 @@ { "explain_todo": { "stmt27": true, - "stmt68": true, - "stmt8": true, - "stmt9": true + "stmt68": true } } diff --git a/parser/testdata/03008_deduplication_wrong_mv/metadata.json b/parser/testdata/03008_deduplication_wrong_mv/metadata.json index dbdbb76d4f..0967ef424b 100644 --- a/parser/testdata/03008_deduplication_wrong_mv/metadata.json +++ b/parser/testdata/03008_deduplication_wrong_mv/metadata.json @@ -1,5 +1 @@ -{ - "explain_todo": { - "stmt6": true - } -} +{} diff --git a/parser/testdata/03030_system_flush_distributed_settings/metadata.json b/parser/testdata/03030_system_flush_distributed_settings/metadata.json index 16b983e029..a9472b2bf5 100644 --- a/parser/testdata/03030_system_flush_distributed_settings/metadata.json +++ b/parser/testdata/03030_system_flush_distributed_settings/metadata.json @@ -3,7 +3,6 @@ "stmt10": true, "stmt15": true, "stmt16": true, - "stmt7": true, - "stmt9": true + "stmt7": true } } diff --git a/parser/testdata/03094_grouparraysorted_memory/metadata.json b/parser/testdata/03094_grouparraysorted_memory/metadata.json index 1295a45747..0967ef424b 100644 --- a/parser/testdata/03094_grouparraysorted_memory/metadata.json +++ b/parser/testdata/03094_grouparraysorted_memory/metadata.json @@ -1,5 +1 @@ -{ - "explain_todo": { - "stmt3": true - } -} +{} diff --git a/parser/testdata/03094_virtual_column_table_name/metadata.json b/parser/testdata/03094_virtual_column_table_name/metadata.json index 14c1d02e42..d05a7b54b6 100644 --- a/parser/testdata/03094_virtual_column_table_name/metadata.json +++ b/parser/testdata/03094_virtual_column_table_name/metadata.json @@ -1,6 +1,5 @@ { "explain_todo": { - "stmt59": true, "stmt60": true } } diff --git a/parser/testdata/03149_analyzer_join_projection_name_2/metadata.json b/parser/testdata/03149_analyzer_join_projection_name_2/metadata.json index 470d47b502..7ad5569408 100644 --- a/parser/testdata/03149_analyzer_join_projection_name_2/metadata.json +++ b/parser/testdata/03149_analyzer_join_projection_name_2/metadata.json @@ -1,6 +1,5 @@ { "explain_todo": { - "stmt10": true, "stmt9": true } } diff --git a/parser/testdata/03150_dynamic_type_mv_insert/metadata.json b/parser/testdata/03150_dynamic_type_mv_insert/metadata.json index 342b3ff5b4..0967ef424b 100644 --- a/parser/testdata/03150_dynamic_type_mv_insert/metadata.json +++ b/parser/testdata/03150_dynamic_type_mv_insert/metadata.json @@ -1,5 +1 @@ -{ - "explain_todo": { - "stmt8": true - } -} +{} diff --git a/parser/testdata/03155_in_nested_subselects/metadata.json b/parser/testdata/03155_in_nested_subselects/metadata.json index dbdbb76d4f..0967ef424b 100644 --- a/parser/testdata/03155_in_nested_subselects/metadata.json +++ b/parser/testdata/03155_in_nested_subselects/metadata.json @@ -1,5 +1 @@ -{ - "explain_todo": { - "stmt6": true - } -} +{} diff --git a/parser/testdata/03161_create_table_as_mv/metadata.json b/parser/testdata/03161_create_table_as_mv/metadata.json index 470d47b502..f4c74e32be 100644 --- a/parser/testdata/03161_create_table_as_mv/metadata.json +++ b/parser/testdata/03161_create_table_as_mv/metadata.json @@ -1,6 +1,5 @@ { "explain_todo": { - "stmt10": true, - "stmt9": true + "stmt10": true } } diff --git a/parser/testdata/03168_attach_as_replicated_materialized_view/metadata.json b/parser/testdata/03168_attach_as_replicated_materialized_view/metadata.json index 684b7e3513..ec09c7e10e 100644 --- a/parser/testdata/03168_attach_as_replicated_materialized_view/metadata.json +++ b/parser/testdata/03168_attach_as_replicated_materialized_view/metadata.json @@ -1,7 +1,6 @@ { "explain_todo": { "stmt11": true, - "stmt12": true, - "stmt3": true + "stmt12": true } } diff --git a/parser/testdata/03230_show_create_query_identifier_quoting_style/metadata.json b/parser/testdata/03230_show_create_query_identifier_quoting_style/metadata.json index bb3f5293b9..5cd92645f7 100644 --- a/parser/testdata/03230_show_create_query_identifier_quoting_style/metadata.json +++ b/parser/testdata/03230_show_create_query_identifier_quoting_style/metadata.json @@ -1,7 +1,5 @@ { "explain_todo": { - "stmt10": true, - "stmt12": true, "stmt19": true, "stmt21": true, "stmt24": true, diff --git a/parser/testdata/03243_check_for_nullable_nothing_in_alter/metadata.json b/parser/testdata/03243_check_for_nullable_nothing_in_alter/metadata.json index f6d9f2395b..342b3ff5b4 100644 --- a/parser/testdata/03243_check_for_nullable_nothing_in_alter/metadata.json +++ b/parser/testdata/03243_check_for_nullable_nothing_in_alter/metadata.json @@ -1,6 +1,5 @@ { "explain_todo": { - "stmt7": true, "stmt8": true } } diff --git a/parser/testdata/03254_normalize_aggregate_states_with_named_tuple_args/metadata.json b/parser/testdata/03254_normalize_aggregate_states_with_named_tuple_args/metadata.json index 60106a3b25..1295a45747 100644 --- a/parser/testdata/03254_normalize_aggregate_states_with_named_tuple_args/metadata.json +++ b/parser/testdata/03254_normalize_aggregate_states_with_named_tuple_args/metadata.json @@ -1,6 +1,5 @@ { "explain_todo": { - "stmt3": true, - "stmt9": true + "stmt3": true } } diff --git a/parser/testdata/03285_default_engine_with_settings/metadata.json b/parser/testdata/03285_default_engine_with_settings/metadata.json index 92e84e943a..0967ef424b 100644 --- a/parser/testdata/03285_default_engine_with_settings/metadata.json +++ b/parser/testdata/03285_default_engine_with_settings/metadata.json @@ -1,6 +1 @@ -{ - "explain_todo": { - "stmt8": true, - "stmt9": true - } -} +{} diff --git a/parser/testdata/03301_subcolumns_in_mv/metadata.json b/parser/testdata/03301_subcolumns_in_mv/metadata.json index 6bf8d5b80a..3a06a4a1ac 100644 --- a/parser/testdata/03301_subcolumns_in_mv/metadata.json +++ b/parser/testdata/03301_subcolumns_in_mv/metadata.json @@ -1,6 +1,5 @@ { "explain_todo": { - "stmt5": true, - "stmt7": true + "stmt5": true } } diff --git a/parser/testdata/03310_materialized_view_with_bad_select/metadata.json b/parser/testdata/03310_materialized_view_with_bad_select/metadata.json index a643c4949b..0967ef424b 100644 --- a/parser/testdata/03310_materialized_view_with_bad_select/metadata.json +++ b/parser/testdata/03310_materialized_view_with_bad_select/metadata.json @@ -1 +1 @@ -{"explain_todo":{"stmt14":true,"stmt16":true,"stmt20":true,"stmt7":true}} +{} diff --git a/parser/testdata/03322_materialized_view_ignore_errors_url/metadata.json b/parser/testdata/03322_materialized_view_ignore_errors_url/metadata.json index dbdbb76d4f..0967ef424b 100644 --- a/parser/testdata/03322_materialized_view_ignore_errors_url/metadata.json +++ b/parser/testdata/03322_materialized_view_ignore_errors_url/metadata.json @@ -1,5 +1 @@ -{ - "explain_todo": { - "stmt6": true - } -} +{} diff --git a/parser/testdata/03357_analyzer_insert_view/metadata.json b/parser/testdata/03357_analyzer_insert_view/metadata.json index 7ad5569408..0967ef424b 100644 --- a/parser/testdata/03357_analyzer_insert_view/metadata.json +++ b/parser/testdata/03357_analyzer_insert_view/metadata.json @@ -1,5 +1 @@ -{ - "explain_todo": { - "stmt9": true - } -} +{} diff --git a/parser/testdata/03357_storage_join_mv_context/metadata.json b/parser/testdata/03357_storage_join_mv_context/metadata.json index 1295a45747..0967ef424b 100644 --- a/parser/testdata/03357_storage_join_mv_context/metadata.json +++ b/parser/testdata/03357_storage_join_mv_context/metadata.json @@ -1,5 +1 @@ -{ - "explain_todo": { - "stmt3": true - } -} +{} diff --git a/parser/testdata/03530_insert_into_distributed_different_types_sparseness/metadata.json b/parser/testdata/03530_insert_into_distributed_different_types_sparseness/metadata.json index e7d28e3b9f..0967ef424b 100644 --- a/parser/testdata/03530_insert_into_distributed_different_types_sparseness/metadata.json +++ b/parser/testdata/03530_insert_into_distributed_different_types_sparseness/metadata.json @@ -1,6 +1 @@ -{ - "explain_todo": { - "stmt15": true, - "stmt18": true - } -} +{} diff --git a/parser/testdata/03531_insert_removing_sparse_transform/metadata.json b/parser/testdata/03531_insert_removing_sparse_transform/metadata.json index 74ae21c6ea..0967ef424b 100644 --- a/parser/testdata/03531_insert_removing_sparse_transform/metadata.json +++ b/parser/testdata/03531_insert_removing_sparse_transform/metadata.json @@ -1 +1 @@ -{"explain_todo":{"stmt11":true,"stmt14":true}} +{} diff --git a/parser/testdata/03561_materialized_subcolumns_materialized_view/metadata.json b/parser/testdata/03561_materialized_subcolumns_materialized_view/metadata.json index b65b07d7a6..0967ef424b 100644 --- a/parser/testdata/03561_materialized_subcolumns_materialized_view/metadata.json +++ b/parser/testdata/03561_materialized_subcolumns_materialized_view/metadata.json @@ -1,5 +1 @@ -{ - "explain_todo": { - "stmt4": true - } -} +{} diff --git a/parser/testdata/03633_mv_squash_parallel_inserts/metadata.json b/parser/testdata/03633_mv_squash_parallel_inserts/metadata.json index 7ad5569408..0967ef424b 100644 --- a/parser/testdata/03633_mv_squash_parallel_inserts/metadata.json +++ b/parser/testdata/03633_mv_squash_parallel_inserts/metadata.json @@ -1,5 +1 @@ -{ - "explain_todo": { - "stmt9": true - } -} +{} diff --git a/parser/testdata/03710_pr_insert_into_mv_with_join/metadata.json b/parser/testdata/03710_pr_insert_into_mv_with_join/metadata.json index a133290734..0967ef424b 100644 --- a/parser/testdata/03710_pr_insert_into_mv_with_join/metadata.json +++ b/parser/testdata/03710_pr_insert_into_mv_with_join/metadata.json @@ -1 +1 @@ -{"explain_todo":{"stmt8":true}} +{} diff --git a/parser/testdata/03710_pr_join_with_mv/metadata.json b/parser/testdata/03710_pr_join_with_mv/metadata.json index 08d08853f0..b6a815cb66 100644 --- a/parser/testdata/03710_pr_join_with_mv/metadata.json +++ b/parser/testdata/03710_pr_join_with_mv/metadata.json @@ -1,6 +1,5 @@ { "explain_todo": { - "stmt10": true, "stmt16": true, "stmt20": true } diff --git a/parser/testdata/03740_alter_modify_query_dict_name_in_cse/metadata.json b/parser/testdata/03740_alter_modify_query_dict_name_in_cse/metadata.json index 92e84e943a..7ad5569408 100644 --- a/parser/testdata/03740_alter_modify_query_dict_name_in_cse/metadata.json +++ b/parser/testdata/03740_alter_modify_query_dict_name_in_cse/metadata.json @@ -1,6 +1,5 @@ { "explain_todo": { - "stmt8": true, "stmt9": true } } From 2f9eff0f6487e4c9e49d3597cca78c3ecf67deae Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 30 Dec 2025 22:12:35 +0000 Subject: [PATCH 13/18] Add SHOW CREATE VIEW and SETTINGS support - Fix SHOW CREATE VIEW parsing to use token.VIEW instead of IDENT check - Add HasSettings field to ShowQuery AST for SETTINGS clause - Parse SETTINGS clause in SHOW queries - Update EXPLAIN output for ShowCreateTableQuery, ShowCreateViewQuery, and ShowCreateDictionaryQuery to include Set when SETTINGS is present --- ast/ast.go | 17 ++-- internal/explain/statements.go | 78 ++++++++++++++++--- parser/parser.go | 12 ++- .../metadata.json | 6 +- .../metadata.json | 52 +------------ .../metadata.json | 5 +- 6 files changed, 92 insertions(+), 78 deletions(-) diff --git a/ast/ast.go b/ast/ast.go index 98006926c3..33d085a46c 100644 --- a/ast/ast.go +++ b/ast/ast.go @@ -658,14 +658,15 @@ func (d *DescribeQuery) statementNode() {} // ShowQuery represents a SHOW statement. type ShowQuery struct { - Position token.Position `json:"-"` - ShowType ShowType `json:"show_type"` - Database string `json:"database,omitempty"` - From string `json:"from,omitempty"` - Like string `json:"like,omitempty"` - Where Expression `json:"where,omitempty"` - Limit Expression `json:"limit,omitempty"` - Format string `json:"format,omitempty"` + Position token.Position `json:"-"` + ShowType ShowType `json:"show_type"` + Database string `json:"database,omitempty"` + From string `json:"from,omitempty"` + Like string `json:"like,omitempty"` + Where Expression `json:"where,omitempty"` + Limit Expression `json:"limit,omitempty"` + Format string `json:"format,omitempty"` + HasSettings bool `json:"has_settings,omitempty"` // Whether SETTINGS clause was specified } func (s *ShowQuery) Pos() token.Position { return s.Position } diff --git a/internal/explain/statements.go b/internal/explain/statements.go index 6ea3a2130d..035813b967 100644 --- a/internal/explain/statements.go +++ b/internal/explain/statements.go @@ -572,15 +572,36 @@ func explainShowQuery(sb *strings.Builder, n *ast.ShowQuery, indent string) { // SHOW CREATE DICTIONARY has special output format if n.ShowType == ast.ShowCreateDictionary && (n.Database != "" || n.From != "") { if n.Database != "" && n.From != "" { - fmt.Fprintf(sb, "%sShowCreateDictionaryQuery %s %s (children 2)\n", indent, n.Database, n.From) + children := 2 + if n.HasSettings { + children++ + } + fmt.Fprintf(sb, "%sShowCreateDictionaryQuery %s %s (children %d)\n", indent, n.Database, n.From, children) fmt.Fprintf(sb, "%s Identifier %s\n", indent, n.Database) fmt.Fprintf(sb, "%s Identifier %s\n", indent, n.From) + if n.HasSettings { + fmt.Fprintf(sb, "%s Set\n", indent) + } } else if n.From != "" { - fmt.Fprintf(sb, "%sShowCreateDictionaryQuery %s (children 1)\n", indent, n.From) + children := 1 + if n.HasSettings { + children++ + } + fmt.Fprintf(sb, "%sShowCreateDictionaryQuery %s (children %d)\n", indent, n.From, children) fmt.Fprintf(sb, "%s Identifier %s\n", indent, n.From) + if n.HasSettings { + fmt.Fprintf(sb, "%s Set\n", indent) + } } else if n.Database != "" { - fmt.Fprintf(sb, "%sShowCreateDictionaryQuery %s (children 1)\n", indent, n.Database) + children := 1 + if n.HasSettings { + children++ + } + fmt.Fprintf(sb, "%sShowCreateDictionaryQuery %s (children %d)\n", indent, n.Database, children) fmt.Fprintf(sb, "%s Identifier %s\n", indent, n.Database) + if n.HasSettings { + fmt.Fprintf(sb, "%s Set\n", indent) + } } return } @@ -588,15 +609,36 @@ func explainShowQuery(sb *strings.Builder, n *ast.ShowQuery, indent string) { // SHOW CREATE VIEW has special output format if n.ShowType == ast.ShowCreateView && (n.Database != "" || n.From != "") { if n.Database != "" && n.From != "" { - fmt.Fprintf(sb, "%sShowCreateViewQuery %s %s (children 2)\n", indent, n.Database, n.From) + children := 2 + if n.HasSettings { + children++ + } + fmt.Fprintf(sb, "%sShowCreateViewQuery %s %s (children %d)\n", indent, n.Database, n.From, children) fmt.Fprintf(sb, "%s Identifier %s\n", indent, n.Database) fmt.Fprintf(sb, "%s Identifier %s\n", indent, n.From) + if n.HasSettings { + fmt.Fprintf(sb, "%s Set\n", indent) + } } else if n.From != "" { - fmt.Fprintf(sb, "%sShowCreateViewQuery %s (children 1)\n", indent, n.From) + children := 1 + if n.HasSettings { + children++ + } + fmt.Fprintf(sb, "%sShowCreateViewQuery %s (children %d)\n", indent, n.From, children) fmt.Fprintf(sb, "%s Identifier %s\n", indent, n.From) + if n.HasSettings { + fmt.Fprintf(sb, "%s Set\n", indent) + } } else if n.Database != "" { - fmt.Fprintf(sb, "%sShowCreateViewQuery %s (children 1)\n", indent, n.Database) + children := 1 + if n.HasSettings { + children++ + } + fmt.Fprintf(sb, "%sShowCreateViewQuery %s (children %d)\n", indent, n.Database, children) fmt.Fprintf(sb, "%s Identifier %s\n", indent, n.Database) + if n.HasSettings { + fmt.Fprintf(sb, "%s Set\n", indent) + } } return } @@ -608,7 +650,10 @@ func explainShowQuery(sb *strings.Builder, n *ast.ShowQuery, indent string) { if n.Database != "" && n.From != "" { children := 2 if n.Format != "" { - children = 3 + children++ + } + if n.HasSettings { + children++ } fmt.Fprintf(sb, "%sShowCreateTableQuery %s %s (children %d)\n", indent, n.Database, n.From, children) fmt.Fprintf(sb, "%s Identifier %s\n", indent, n.Database) @@ -616,26 +661,41 @@ func explainShowQuery(sb *strings.Builder, n *ast.ShowQuery, indent string) { if n.Format != "" { fmt.Fprintf(sb, "%s Identifier %s\n", indent, n.Format) } + if n.HasSettings { + fmt.Fprintf(sb, "%s Set\n", indent) + } } else if n.From != "" { children := 1 if n.Format != "" { - children = 2 + children++ + } + if n.HasSettings { + children++ } fmt.Fprintf(sb, "%sShowCreateTableQuery %s (children %d)\n", indent, name, children) fmt.Fprintf(sb, "%s Identifier %s\n", indent, name) if n.Format != "" { fmt.Fprintf(sb, "%s Identifier %s\n", indent, n.Format) } + if n.HasSettings { + fmt.Fprintf(sb, "%s Set\n", indent) + } } else if n.Database != "" { children := 1 if n.Format != "" { - children = 2 + children++ + } + if n.HasSettings { + children++ } fmt.Fprintf(sb, "%sShowCreateTableQuery %s (children %d)\n", indent, n.Database, children) fmt.Fprintf(sb, "%s Identifier %s\n", indent, n.Database) if n.Format != "" { fmt.Fprintf(sb, "%s Identifier %s\n", indent, n.Format) } + if n.HasSettings { + fmt.Fprintf(sb, "%s Set\n", indent) + } } else { fmt.Fprintf(sb, "%sShow%s\n", indent, showType) } diff --git a/parser/parser.go b/parser/parser.go index dcb69af998..31db538316 100644 --- a/parser/parser.go +++ b/parser/parser.go @@ -4071,7 +4071,7 @@ func (p *Parser) parseShow() ast.Statement { } else if p.currentIs(token.IDENT) && strings.ToUpper(p.current.Value) == "DICTIONARY" { show.ShowType = ast.ShowCreateDictionary p.nextToken() - } else if p.currentIs(token.IDENT) && strings.ToUpper(p.current.Value) == "VIEW" { + } else if p.currentIs(token.VIEW) { show.ShowType = ast.ShowCreateView p.nextToken() } else if p.currentIs(token.USER) { @@ -4170,6 +4170,16 @@ func (p *Parser) parseShow() ast.Statement { } } + // Parse SETTINGS clause + if p.currentIs(token.SETTINGS) { + show.HasSettings = true + // Skip SETTINGS and all settings key-value pairs + p.nextToken() + for !p.currentIs(token.EOF) && !p.currentIs(token.SEMICOLON) && !p.currentIs(token.FORMAT) { + p.nextToken() + } + } + return show } diff --git a/parser/testdata/01083_expressions_in_engine_arguments/metadata.json b/parser/testdata/01083_expressions_in_engine_arguments/metadata.json index 85cc99e9fa..0967ef424b 100644 --- a/parser/testdata/01083_expressions_in_engine_arguments/metadata.json +++ b/parser/testdata/01083_expressions_in_engine_arguments/metadata.json @@ -1,5 +1 @@ -{ - "explain_todo": { - "stmt34": true - } -} +{} diff --git a/parser/testdata/03230_show_create_query_identifier_quoting_style/metadata.json b/parser/testdata/03230_show_create_query_identifier_quoting_style/metadata.json index 5cd92645f7..0967ef424b 100644 --- a/parser/testdata/03230_show_create_query_identifier_quoting_style/metadata.json +++ b/parser/testdata/03230_show_create_query_identifier_quoting_style/metadata.json @@ -1,51 +1 @@ -{ - "explain_todo": { - "stmt19": true, - "stmt21": true, - "stmt24": true, - "stmt25": true, - "stmt26": true, - "stmt27": true, - "stmt28": true, - "stmt30": true, - "stmt31": true, - "stmt32": true, - "stmt33": true, - "stmt34": true, - "stmt36": true, - "stmt37": true, - "stmt38": true, - "stmt39": true, - "stmt40": true, - "stmt42": true, - "stmt43": true, - "stmt44": true, - "stmt45": true, - "stmt46": true, - "stmt48": true, - "stmt49": true, - "stmt50": true, - "stmt51": true, - "stmt52": true, - "stmt54": true, - "stmt55": true, - "stmt56": true, - "stmt57": true, - "stmt58": true, - "stmt60": true, - "stmt61": true, - "stmt62": true, - "stmt63": true, - "stmt64": true, - "stmt66": true, - "stmt67": true, - "stmt68": true, - "stmt69": true, - "stmt70": true, - "stmt72": true, - "stmt73": true, - "stmt74": true, - "stmt75": true, - "stmt76": true - } -} +{} diff --git a/parser/testdata/03234_enable_secure_identifiers/metadata.json b/parser/testdata/03234_enable_secure_identifiers/metadata.json index 70b9b4fa17..e1d0c546fa 100644 --- a/parser/testdata/03234_enable_secure_identifiers/metadata.json +++ b/parser/testdata/03234_enable_secure_identifiers/metadata.json @@ -1,9 +1,6 @@ { "explain_todo": { "stmt11": true, - "stmt12": true, - "stmt14": true, - "stmt15": true, - "stmt18": true + "stmt14": true } } From f48af3aa932f84d5cacd46f4989af134677e9547 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 30 Dec 2025 22:24:58 +0000 Subject: [PATCH 14/18] Fix SYSTEM query EXPLAIN output and parsing - Add table/database as children in SYSTEM query explain output - Exclude FLUSH LOGS command from showing table name as child - Add INDEX, INSERT, PRIMARY, KEY tokens to isSystemCommandKeyword - Add QUEUE to system command identifiers list --- internal/explain/statements.go | 26 ++++++++++++++++++- parser/parser.go | 8 +++--- .../metadata.json | 6 +---- 3 files changed, 31 insertions(+), 9 deletions(-) diff --git a/internal/explain/statements.go b/internal/explain/statements.go index 035813b967..837993ce0e 100644 --- a/internal/explain/statements.go +++ b/internal/explain/statements.go @@ -516,7 +516,31 @@ func explainSetQuery(sb *strings.Builder, indent string) { } func explainSystemQuery(sb *strings.Builder, n *ast.SystemQuery, indent string) { - fmt.Fprintf(sb, "%sSYSTEM query\n", indent) + // Some commands like FLUSH LOGS don't show the log name as a child + // For other commands, table/database names are shown as children + isFlushLogs := strings.HasPrefix(strings.ToUpper(n.Command), "FLUSH LOGS") + + // Count children - database and table are children if present and not FLUSH LOGS + children := 0 + if !isFlushLogs { + if n.Database != "" { + children++ + } + if n.Table != "" { + children++ + } + } + if children > 0 { + fmt.Fprintf(sb, "%sSYSTEM query (children %d)\n", indent, children) + if n.Database != "" { + fmt.Fprintf(sb, "%s Identifier %s\n", indent, n.Database) + } + if n.Table != "" { + fmt.Fprintf(sb, "%s Identifier %s\n", indent, n.Table) + } + } else { + fmt.Fprintf(sb, "%sSYSTEM query\n", indent) + } } func explainExplainQuery(sb *strings.Builder, n *ast.ExplainQuery, indent string, depth int) { diff --git a/parser/parser.go b/parser/parser.go index 31db538316..ec272a913e 100644 --- a/parser/parser.go +++ b/parser/parser.go @@ -4359,13 +4359,15 @@ func (p *Parser) parseSystem() *ast.SystemQuery { // isSystemCommandKeyword returns true if current token is a keyword that can be part of SYSTEM command func (p *Parser) isSystemCommandKeyword() bool { switch p.current.Token { - case token.TTL, token.SYNC, token.DROP, token.FORMAT, token.FOR: + case token.TTL, token.SYNC, token.DROP, token.FORMAT, token.FOR, token.INDEX, token.INSERT, + token.PRIMARY, token.KEY: return true } - // Handle SCHEMA, CACHE as identifiers since they're not keyword tokens + // Handle SCHEMA, CACHE, QUEUE and other identifiers that are part of SYSTEM commands if p.currentIs(token.IDENT) { upper := strings.ToUpper(p.current.Value) - if upper == "SCHEMA" || upper == "CACHE" { + switch upper { + case "SCHEMA", "CACHE", "QUEUE": return true } } diff --git a/parser/testdata/02354_read_in_order_prewhere/metadata.json b/parser/testdata/02354_read_in_order_prewhere/metadata.json index 1295a45747..0967ef424b 100644 --- a/parser/testdata/02354_read_in_order_prewhere/metadata.json +++ b/parser/testdata/02354_read_in_order_prewhere/metadata.json @@ -1,5 +1 @@ -{ - "explain_todo": { - "stmt3": true - } -} +{} From 8a04ae37126a88edb0dcecde29003b80e165519f Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 30 Dec 2025 22:35:07 +0000 Subject: [PATCH 15/18] Fix INSERT column list parsing to handle keyword column names The parseInsert function was only checking for token.IDENT when parsing column lists in INSERT statements. Column names like 'key' and 'value' are keywords with their own tokens, so they were being skipped. Changed to use parseIdentifierName() which handles both IDENT tokens and keywords that can be used as identifiers. --- parser/parser.go | 9 ++-- .../metadata.json | 8 +--- .../metadata.json | 6 +-- .../metadata.json | 7 +-- .../00671_max_intersections/metadata.json | 7 +-- .../testdata/00927_asof_joins/metadata.json | 9 +--- .../metadata.json | 9 +--- .../00950_test_gorilla_codec/metadata.json | 9 +--- .../metadata.json | 3 +- .../metadata.json | 8 +--- .../metadata.json | 8 +--- .../01323_add_scalars_in_time/metadata.json | 6 +-- .../01763_long_ttl_group_by/metadata.json | 11 +---- .../metadata.json | 46 +------------------ .../01911_logical_error_minus/metadata.json | 13 +----- .../02313_test_fpc_codec/metadata.json | 13 +----- .../testdata/02508_bad_graphite/metadata.json | 6 +-- .../metadata.json | 1 - .../02725_any_join_single_row/metadata.json | 12 +---- .../metadata.json | 6 +-- .../metadata.json | 2 - .../metadata.json | 1 - .../metadata.json | 6 +-- .../02949_ttl_group_by_bug/metadata.json | 6 +-- .../metadata.json | 6 +-- .../metadata.json | 1 - .../metadata.json | 6 +-- .../metadata.json | 2 - .../metadata.json | 19 +------- .../metadata.json | 46 +------------------ 30 files changed, 29 insertions(+), 263 deletions(-) diff --git a/parser/parser.go b/parser/parser.go index ec272a913e..7d36f70dae 100644 --- a/parser/parser.go +++ b/parser/parser.go @@ -1170,12 +1170,13 @@ func (p *Parser) parseInsert() *ast.InsertQuery { if p.currentIs(token.LPAREN) { p.nextToken() for !p.currentIs(token.RPAREN) && !p.currentIs(token.EOF) { - if p.currentIs(token.IDENT) { + pos := p.current.Pos + colName := p.parseIdentifierName() + if colName != "" { ins.Columns = append(ins.Columns, &ast.Identifier{ - Position: p.current.Pos, - Parts: []string{p.current.Value}, + Position: pos, + Parts: []string{colName}, }) - p.nextToken() } if p.currentIs(token.COMMA) { p.nextToken() diff --git a/parser/testdata/00531_aggregate_over_nullable/metadata.json b/parser/testdata/00531_aggregate_over_nullable/metadata.json index 19830977ac..0967ef424b 100644 --- a/parser/testdata/00531_aggregate_over_nullable/metadata.json +++ b/parser/testdata/00531_aggregate_over_nullable/metadata.json @@ -1,7 +1 @@ -{ - "explain_todo": { - "stmt4": true, - "stmt5": true, - "stmt6": true - } -} +{} diff --git a/parser/testdata/00605_intersections_aggregate_functions/metadata.json b/parser/testdata/00605_intersections_aggregate_functions/metadata.json index 1295a45747..0967ef424b 100644 --- a/parser/testdata/00605_intersections_aggregate_functions/metadata.json +++ b/parser/testdata/00605_intersections_aggregate_functions/metadata.json @@ -1,5 +1 @@ -{ - "explain_todo": { - "stmt3": true - } -} +{} diff --git a/parser/testdata/00615_nullable_alter_optimize/metadata.json b/parser/testdata/00615_nullable_alter_optimize/metadata.json index 8b0256d0ad..0967ef424b 100644 --- a/parser/testdata/00615_nullable_alter_optimize/metadata.json +++ b/parser/testdata/00615_nullable_alter_optimize/metadata.json @@ -1,6 +1 @@ -{ - "explain_todo": { - "stmt4": true, - "stmt7": true - } -} +{} diff --git a/parser/testdata/00671_max_intersections/metadata.json b/parser/testdata/00671_max_intersections/metadata.json index 0f293987f1..0967ef424b 100644 --- a/parser/testdata/00671_max_intersections/metadata.json +++ b/parser/testdata/00671_max_intersections/metadata.json @@ -1,6 +1 @@ -{ - "explain_todo": { - "stmt5": true, - "stmt6": true - } -} +{} diff --git a/parser/testdata/00927_asof_joins/metadata.json b/parser/testdata/00927_asof_joins/metadata.json index 6c28a962d3..0967ef424b 100644 --- a/parser/testdata/00927_asof_joins/metadata.json +++ b/parser/testdata/00927_asof_joins/metadata.json @@ -1,8 +1 @@ -{ - "explain_todo": { - "stmt4": true, - "stmt5": true, - "stmt7": true, - "stmt8": true - } -} +{} diff --git a/parser/testdata/00950_test_double_delta_codec/metadata.json b/parser/testdata/00950_test_double_delta_codec/metadata.json index 39429336f8..0967ef424b 100644 --- a/parser/testdata/00950_test_double_delta_codec/metadata.json +++ b/parser/testdata/00950_test_double_delta_codec/metadata.json @@ -1,8 +1 @@ -{ - "explain_todo": { - "stmt3": true, - "stmt4": true, - "stmt5": true, - "stmt6": true - } -} +{} diff --git a/parser/testdata/00950_test_gorilla_codec/metadata.json b/parser/testdata/00950_test_gorilla_codec/metadata.json index 2dda3f42fa..0967ef424b 100644 --- a/parser/testdata/00950_test_gorilla_codec/metadata.json +++ b/parser/testdata/00950_test_gorilla_codec/metadata.json @@ -1,8 +1 @@ -{ - "explain_todo": { - "stmt4": true, - "stmt5": true, - "stmt6": true, - "stmt7": true - } -} +{} diff --git a/parser/testdata/01213_alter_rename_with_default_zookeeper_long/metadata.json b/parser/testdata/01213_alter_rename_with_default_zookeeper_long/metadata.json index 5543f9b5fe..62b81668c3 100644 --- a/parser/testdata/01213_alter_rename_with_default_zookeeper_long/metadata.json +++ b/parser/testdata/01213_alter_rename_with_default_zookeeper_long/metadata.json @@ -1,6 +1,5 @@ { "explain_todo": { - "stmt13": true, - "stmt3": true + "stmt13": true } } diff --git a/parser/testdata/01275_alter_rename_column_default_expr/metadata.json b/parser/testdata/01275_alter_rename_column_default_expr/metadata.json index d46a48e54e..0967ef424b 100644 --- a/parser/testdata/01275_alter_rename_column_default_expr/metadata.json +++ b/parser/testdata/01275_alter_rename_column_default_expr/metadata.json @@ -1,7 +1 @@ -{ - "explain_todo": { - "stmt15": true, - "stmt3": true, - "stmt9": true - } -} +{} diff --git a/parser/testdata/01276_alter_rename_column_materialized_expr/metadata.json b/parser/testdata/01276_alter_rename_column_materialized_expr/metadata.json index 0f2fdcdca0..0967ef424b 100644 --- a/parser/testdata/01276_alter_rename_column_materialized_expr/metadata.json +++ b/parser/testdata/01276_alter_rename_column_materialized_expr/metadata.json @@ -1,7 +1 @@ -{ - "explain_todo": { - "stmt10": true, - "stmt18": true, - "stmt3": true - } -} +{} diff --git a/parser/testdata/01323_add_scalars_in_time/metadata.json b/parser/testdata/01323_add_scalars_in_time/metadata.json index 7ad5569408..0967ef424b 100644 --- a/parser/testdata/01323_add_scalars_in_time/metadata.json +++ b/parser/testdata/01323_add_scalars_in_time/metadata.json @@ -1,5 +1 @@ -{ - "explain_todo": { - "stmt9": true - } -} +{} diff --git a/parser/testdata/01763_long_ttl_group_by/metadata.json b/parser/testdata/01763_long_ttl_group_by/metadata.json index 0dd8a71a4b..0967ef424b 100644 --- a/parser/testdata/01763_long_ttl_group_by/metadata.json +++ b/parser/testdata/01763_long_ttl_group_by/metadata.json @@ -1,10 +1 @@ -{ - "explain_todo": { - "stmt3": true, - "stmt4": true, - "stmt5": true, - "stmt6": true, - "stmt7": true, - "stmt8": true - } -} +{} diff --git a/parser/testdata/01781_merge_tree_deduplication/metadata.json b/parser/testdata/01781_merge_tree_deduplication/metadata.json index 4f432c9521..6ed51d7100 100644 --- a/parser/testdata/01781_merge_tree_deduplication/metadata.json +++ b/parser/testdata/01781_merge_tree_deduplication/metadata.json @@ -1,51 +1,7 @@ { "explain_todo": { - "stmt10": true, - "stmt11": true, - "stmt12": true, - "stmt15": true, - "stmt16": true, - "stmt17": true, - "stmt18": true, - "stmt21": true, - "stmt22": true, - "stmt23": true, - "stmt24": true, - "stmt25": true, - "stmt26": true, - "stmt27": true, - "stmt28": true, "stmt3": true, "stmt31": true, - "stmt32": true, - "stmt34": true, - "stmt35": true, - "stmt4": true, - "stmt40": true, - "stmt41": true, - "stmt45": true, - "stmt47": true, - "stmt48": true, - "stmt52": true, - "stmt53": true, - "stmt54": true, - "stmt55": true, - "stmt59": true, - "stmt6": true, - "stmt60": true, - "stmt66": true, - "stmt69": true, - "stmt70": true, - "stmt71": true, - "stmt72": true, - "stmt73": true, - "stmt81": true, - "stmt82": true, - "stmt86": true, - "stmt87": true, - "stmt88": true, - "stmt9": true, - "stmt91": true, - "stmt92": true + "stmt34": true } } diff --git a/parser/testdata/01911_logical_error_minus/metadata.json b/parser/testdata/01911_logical_error_minus/metadata.json index d7e82c70c9..0967ef424b 100644 --- a/parser/testdata/01911_logical_error_minus/metadata.json +++ b/parser/testdata/01911_logical_error_minus/metadata.json @@ -1,12 +1 @@ -{ - "explain_todo": { - "stmt12": true, - "stmt13": true, - "stmt14": true, - "stmt15": true, - "stmt4": true, - "stmt5": true, - "stmt6": true, - "stmt7": true - } -} +{} diff --git a/parser/testdata/02313_test_fpc_codec/metadata.json b/parser/testdata/02313_test_fpc_codec/metadata.json index 48a7016a67..0967ef424b 100644 --- a/parser/testdata/02313_test_fpc_codec/metadata.json +++ b/parser/testdata/02313_test_fpc_codec/metadata.json @@ -1,12 +1 @@ -{ - "explain_todo": { - "stmt14": true, - "stmt15": true, - "stmt16": true, - "stmt17": true, - "stmt4": true, - "stmt5": true, - "stmt6": true, - "stmt7": true - } -} +{} diff --git a/parser/testdata/02508_bad_graphite/metadata.json b/parser/testdata/02508_bad_graphite/metadata.json index 1295a45747..0967ef424b 100644 --- a/parser/testdata/02508_bad_graphite/metadata.json +++ b/parser/testdata/02508_bad_graphite/metadata.json @@ -1,5 +1 @@ -{ - "explain_todo": { - "stmt3": true - } -} +{} diff --git a/parser/testdata/02564_read_in_order_final_desc/metadata.json b/parser/testdata/02564_read_in_order_final_desc/metadata.json index f3773105ca..ff0eba6904 100644 --- a/parser/testdata/02564_read_in_order_final_desc/metadata.json +++ b/parser/testdata/02564_read_in_order_final_desc/metadata.json @@ -1,6 +1,5 @@ { "explain_todo": { - "stmt4": true, "stmt6": true, "stmt7": true } diff --git a/parser/testdata/02725_any_join_single_row/metadata.json b/parser/testdata/02725_any_join_single_row/metadata.json index 3018712d54..0967ef424b 100644 --- a/parser/testdata/02725_any_join_single_row/metadata.json +++ b/parser/testdata/02725_any_join_single_row/metadata.json @@ -1,11 +1 @@ -{ - "explain_todo": { - "stmt10": true, - "stmt13": true, - "stmt14": true, - "stmt15": true, - "stmt17": true, - "stmt6": true, - "stmt9": true - } -} +{} diff --git a/parser/testdata/02812_pointwise_array_operations/metadata.json b/parser/testdata/02812_pointwise_array_operations/metadata.json index c45b7602ba..0967ef424b 100644 --- a/parser/testdata/02812_pointwise_array_operations/metadata.json +++ b/parser/testdata/02812_pointwise_array_operations/metadata.json @@ -1,5 +1 @@ -{ - "explain_todo": { - "stmt12": true - } -} +{} diff --git a/parser/testdata/02842_vertical_merge_after_add_drop_column/metadata.json b/parser/testdata/02842_vertical_merge_after_add_drop_column/metadata.json index c55f31987e..dbdbb76d4f 100644 --- a/parser/testdata/02842_vertical_merge_after_add_drop_column/metadata.json +++ b/parser/testdata/02842_vertical_merge_after_add_drop_column/metadata.json @@ -1,7 +1,5 @@ { "explain_todo": { - "stmt3": true, - "stmt4": true, "stmt6": true } } diff --git a/parser/testdata/02883_array_scalar_mult_div_modulo/metadata.json b/parser/testdata/02883_array_scalar_mult_div_modulo/metadata.json index c6081c2b46..daf05a4474 100644 --- a/parser/testdata/02883_array_scalar_mult_div_modulo/metadata.json +++ b/parser/testdata/02883_array_scalar_mult_div_modulo/metadata.json @@ -2,7 +2,6 @@ "explain_todo": { "stmt10": true, "stmt11": true, - "stmt20": true, "stmt8": true, "stmt9": true } diff --git a/parser/testdata/02885_create_distributed_table_without_as/metadata.json b/parser/testdata/02885_create_distributed_table_without_as/metadata.json index dbdbb76d4f..0967ef424b 100644 --- a/parser/testdata/02885_create_distributed_table_without_as/metadata.json +++ b/parser/testdata/02885_create_distributed_table_without_as/metadata.json @@ -1,5 +1 @@ -{ - "explain_todo": { - "stmt6": true - } -} +{} diff --git a/parser/testdata/02949_ttl_group_by_bug/metadata.json b/parser/testdata/02949_ttl_group_by_bug/metadata.json index b65b07d7a6..0967ef424b 100644 --- a/parser/testdata/02949_ttl_group_by_bug/metadata.json +++ b/parser/testdata/02949_ttl_group_by_bug/metadata.json @@ -1,5 +1 @@ -{ - "explain_todo": { - "stmt4": true - } -} +{} diff --git a/parser/testdata/03045_unknown_identifier_alias_substitution/metadata.json b/parser/testdata/03045_unknown_identifier_alias_substitution/metadata.json index 3a06a4a1ac..0967ef424b 100644 --- a/parser/testdata/03045_unknown_identifier_alias_substitution/metadata.json +++ b/parser/testdata/03045_unknown_identifier_alias_substitution/metadata.json @@ -1,5 +1 @@ -{ - "explain_todo": { - "stmt5": true - } -} +{} diff --git a/parser/testdata/03100_lwu_45_query_condition_cache/metadata.json b/parser/testdata/03100_lwu_45_query_condition_cache/metadata.json index 05747ff9e9..661bded8e9 100644 --- a/parser/testdata/03100_lwu_45_query_condition_cache/metadata.json +++ b/parser/testdata/03100_lwu_45_query_condition_cache/metadata.json @@ -1,6 +1,5 @@ { "explain_todo": { - "stmt6": true, "stmt7": true, "stmt8": true, "stmt9": true diff --git a/parser/testdata/03170_part_offset_as_table_column/metadata.json b/parser/testdata/03170_part_offset_as_table_column/metadata.json index ef58f80315..0967ef424b 100644 --- a/parser/testdata/03170_part_offset_as_table_column/metadata.json +++ b/parser/testdata/03170_part_offset_as_table_column/metadata.json @@ -1,5 +1 @@ -{ - "explain_todo": { - "stmt2": true - } -} +{} diff --git a/parser/testdata/03254_timeseries_instant_value_aggregate_functions/metadata.json b/parser/testdata/03254_timeseries_instant_value_aggregate_functions/metadata.json index af33896a08..7ad5569408 100644 --- a/parser/testdata/03254_timeseries_instant_value_aggregate_functions/metadata.json +++ b/parser/testdata/03254_timeseries_instant_value_aggregate_functions/metadata.json @@ -1,7 +1,5 @@ { "explain_todo": { - "stmt4": true, - "stmt7": true, "stmt9": true } } diff --git a/parser/testdata/03566_one_row_summing_merge_tree/metadata.json b/parser/testdata/03566_one_row_summing_merge_tree/metadata.json index 1c6a038e0a..0967ef424b 100644 --- a/parser/testdata/03566_one_row_summing_merge_tree/metadata.json +++ b/parser/testdata/03566_one_row_summing_merge_tree/metadata.json @@ -1,18 +1 @@ -{ - "explain_todo": { - "stmt28": true, - "stmt29": true, - "stmt30": true, - "stmt31": true, - "stmt32": true, - "stmt33": true, - "stmt34": true, - "stmt39": true, - "stmt40": true, - "stmt41": true, - "stmt42": true, - "stmt43": true, - "stmt44": true, - "stmt45": true - } -} +{} diff --git a/parser/testdata/03711_merge_tree_deduplication_with_disk_not_support_writing_with_append/metadata.json b/parser/testdata/03711_merge_tree_deduplication_with_disk_not_support_writing_with_append/metadata.json index 153dd70274..8dc308e86d 100644 --- a/parser/testdata/03711_merge_tree_deduplication_with_disk_not_support_writing_with_append/metadata.json +++ b/parser/testdata/03711_merge_tree_deduplication_with_disk_not_support_writing_with_append/metadata.json @@ -1,51 +1,7 @@ { "explain_todo": { - "stmt10": true, - "stmt11": true, - "stmt12": true, - "stmt13": true, - "stmt16": true, - "stmt17": true, - "stmt18": true, - "stmt19": true, - "stmt22": true, - "stmt23": true, - "stmt24": true, - "stmt25": true, - "stmt26": true, - "stmt27": true, - "stmt28": true, - "stmt29": true, "stmt32": true, - "stmt33": true, "stmt35": true, - "stmt36": true, - "stmt4": true, - "stmt41": true, - "stmt42": true, - "stmt46": true, - "stmt48": true, - "stmt49": true, - "stmt5": true, - "stmt53": true, - "stmt54": true, - "stmt55": true, - "stmt56": true, - "stmt60": true, - "stmt61": true, - "stmt67": true, - "stmt7": true, - "stmt70": true, - "stmt71": true, - "stmt72": true, - "stmt73": true, - "stmt74": true, - "stmt82": true, - "stmt83": true, - "stmt87": true, - "stmt88": true, - "stmt89": true, - "stmt92": true, - "stmt93": true + "stmt4": true } } From 0955c9a53c799c2b33d00923c4e07020494ce11e Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 30 Dec 2025 22:46:14 +0000 Subject: [PATCH 16/18] Fix negative literal handling in :: cast expressions When parsing expressions like -0::Int16, ClickHouse keeps the minus sign with the literal so it becomes CAST('-0', 'Int16'). Previously we parsed this as negate(CAST(0, Int16)). Changes: - Modified parseUnaryMinus to detect -number::type pattern and keep them together as a signed literal - Added Negative field to ast.Literal to track explicitly negative literals (needed for -0 which equals 0 numerically) - Updated formatExprAsString to handle Negative flag and output the original signed representation --- ast/ast.go | 1 + internal/explain/format.go | 24 +++++++++ internal/explain/functions.go | 24 +++++++++ parser/expression.go | 43 +++++++++++++++- .../01852_cast_operator_3/metadata.json | 11 +--- .../02277_full_sort_join_misc/metadata.json | 3 +- .../testdata/02560_count_digits/metadata.json | 9 +--- .../02676_to_decimal_string/metadata.json | 2 +- .../02685_decimal256_various/metadata.json | 2 - .../testdata/02708_dotProduct/metadata.json | 4 -- .../02752_space_function/metadata.json | 9 +--- parser/testdata/02887_byteswap/metadata.json | 11 ---- .../metadata.json | 2 - .../metadata.json | 51 +------------------ .../03376_iceberg_truncate/metadata.json | 2 +- 15 files changed, 97 insertions(+), 101 deletions(-) diff --git a/ast/ast.go b/ast/ast.go index 33d085a46c..b298f3d10c 100644 --- a/ast/ast.go +++ b/ast/ast.go @@ -1059,6 +1059,7 @@ type Literal struct { Position token.Position `json:"-"` Type LiteralType `json:"type"` Value interface{} `json:"value"` + Negative bool `json:"negative,omitempty"` // True if literal was explicitly negative (for -0) } func (l *Literal) Pos() token.Position { return l.Position } diff --git a/internal/explain/format.go b/internal/explain/format.go index ae5db9aa24..842b0171c2 100644 --- a/internal/explain/format.go +++ b/internal/explain/format.go @@ -354,10 +354,34 @@ func UnaryOperatorToFunction(op string) string { func formatExprAsString(expr ast.Expression) string { switch e := expr.(type) { case *ast.Literal: + // Handle explicitly negative literals (like -0 in -0::Int16) + prefix := "" + if e.Negative { + prefix = "-" + } switch e.Type { case ast.LiteralInteger: + // For explicitly negative literals, show the absolute value with prefix + if e.Negative { + switch v := e.Value.(type) { + case int64: + if v <= 0 { + return fmt.Sprintf("-%d", -v) + } + case uint64: + return fmt.Sprintf("-%d", v) + } + } return fmt.Sprintf("%d", e.Value) case ast.LiteralFloat: + if e.Negative { + switch v := e.Value.(type) { + case float64: + if v <= 0 { + return fmt.Sprintf("%s%v", prefix, -v) + } + } + } return fmt.Sprintf("%v", e.Value) case ast.LiteralString: return e.Value.(string) diff --git a/internal/explain/functions.go b/internal/explain/functions.go index 05a4d45914..26c4f74c93 100644 --- a/internal/explain/functions.go +++ b/internal/explain/functions.go @@ -143,6 +143,9 @@ func explainCastExprWithAlias(sb *strings.Builder, n *ast.CastExpr, alias string exprStr := formatExprAsString(lit) fmt.Fprintf(sb, "%s Literal \\'%s\\'\n", indent, exprStr) } + } else if negatedLit := extractNegatedLiteral(n.Expr); negatedLit != "" { + // Handle negated literal like -0::Int16 -> CAST('-0', 'Int16') + fmt.Fprintf(sb, "%s Literal \\'%s\\'\n", indent, negatedLit) } else { // Complex expression - use normal AST node Node(sb, n.Expr, depth+2) @@ -333,6 +336,27 @@ func exprToLiteral(expr ast.Expression) *ast.Literal { return nil } +// extractNegatedLiteral checks if expr is a negated literal (like -0, -12) +// and returns its string representation (like "-0", "-12") for :: cast expressions. +// Returns empty string if not a negated literal. +func extractNegatedLiteral(expr ast.Expression) string { + unary, ok := expr.(*ast.UnaryExpr) + if !ok || unary.Op != "-" { + return "" + } + lit, ok := unary.Operand.(*ast.Literal) + if !ok { + return "" + } + switch lit.Type { + case ast.LiteralInteger: + return "-" + formatExprAsString(lit) + case ast.LiteralFloat: + return "-" + formatExprAsString(lit) + } + return "" +} + func explainInExpr(sb *strings.Builder, n *ast.InExpr, indent string, depth int) { // IN is represented as Function in fnName := "in" diff --git a/parser/expression.go b/parser/expression.go index f0d7ee4205..684d6d1ad3 100644 --- a/parser/expression.go +++ b/parser/expression.go @@ -835,11 +835,50 @@ func (p *Parser) parseSpecialNumber() ast.Expression { } func (p *Parser) parseUnaryMinus() ast.Expression { + pos := p.current.Pos + p.nextToken() // skip minus + + // For negative number literals followed by ::, keep them together as a signed literal + // This matches ClickHouse's behavior where -0::Int16 becomes CAST('-0', 'Int16') + if p.currentIs(token.NUMBER) && p.peekIs(token.COLONCOLON) { + // Parse the number and create a "signed" literal + // We'll store the negative sign in the raw value + numVal := "-" + p.current.Value + lit := &ast.Literal{ + Position: pos, + Type: ast.LiteralInteger, + Negative: true, // Mark as explicitly negative for proper formatting + } + // Check if it's a float + if strings.Contains(numVal, ".") || strings.ContainsAny(numVal, "eE") { + f, _ := strconv.ParseFloat(numVal, 64) + lit.Type = ast.LiteralFloat + lit.Value = f + } else { + i, _ := strconv.ParseInt(numVal, 10, 64) + lit.Value = i + } + p.nextToken() // move past number + // Apply postfix operators like :: using the expression parsing loop + left := ast.Expression(lit) + for !p.currentIs(token.EOF) && LOWEST < p.precedenceForCurrent() { + startPos := p.current.Pos + left = p.parseInfixExpression(left) + if left == nil { + return nil + } + if p.current.Pos == startPos { + break + } + } + return left + } + + // Standard unary minus handling expr := &ast.UnaryExpr{ - Position: p.current.Pos, + Position: pos, Op: "-", } - p.nextToken() expr.Operand = p.parseExpression(UNARY) return expr } diff --git a/parser/testdata/01852_cast_operator_3/metadata.json b/parser/testdata/01852_cast_operator_3/metadata.json index 1aae0cd1d0..0967ef424b 100644 --- a/parser/testdata/01852_cast_operator_3/metadata.json +++ b/parser/testdata/01852_cast_operator_3/metadata.json @@ -1,10 +1 @@ -{ - "explain_todo": { - "stmt1": true, - "stmt2": true, - "stmt3": true, - "stmt4": true, - "stmt5": true, - "stmt6": true - } -} +{} diff --git a/parser/testdata/02277_full_sort_join_misc/metadata.json b/parser/testdata/02277_full_sort_join_misc/metadata.json index 6bf8d5b80a..3a06a4a1ac 100644 --- a/parser/testdata/02277_full_sort_join_misc/metadata.json +++ b/parser/testdata/02277_full_sort_join_misc/metadata.json @@ -1,6 +1,5 @@ { "explain_todo": { - "stmt5": true, - "stmt7": true + "stmt5": true } } diff --git a/parser/testdata/02560_count_digits/metadata.json b/parser/testdata/02560_count_digits/metadata.json index 718397ed0a..d4d1d99f95 100644 --- a/parser/testdata/02560_count_digits/metadata.json +++ b/parser/testdata/02560_count_digits/metadata.json @@ -1,12 +1,5 @@ { "explain_todo": { - "stmt12": true, - "stmt13": true, - "stmt14": true, - "stmt15": true, - "stmt16": true, - "stmt17": true, - "stmt18": true, - "stmt19": true + "stmt14": true } } diff --git a/parser/testdata/02676_to_decimal_string/metadata.json b/parser/testdata/02676_to_decimal_string/metadata.json index cf1345bd3f..0967ef424b 100644 --- a/parser/testdata/02676_to_decimal_string/metadata.json +++ b/parser/testdata/02676_to_decimal_string/metadata.json @@ -1 +1 @@ -{"explain_todo":{"stmt11":true,"stmt12":true,"stmt13":true}} +{} diff --git a/parser/testdata/02685_decimal256_various/metadata.json b/parser/testdata/02685_decimal256_various/metadata.json index 4a1e0a00ce..611db97131 100644 --- a/parser/testdata/02685_decimal256_various/metadata.json +++ b/parser/testdata/02685_decimal256_various/metadata.json @@ -2,8 +2,6 @@ "explain_todo": { "stmt10": true, "stmt27": true, - "stmt28": true, - "stmt29": true, "stmt30": true, "stmt31": true, "stmt32": true, diff --git a/parser/testdata/02708_dotProduct/metadata.json b/parser/testdata/02708_dotProduct/metadata.json index 04c97fdb02..435ccb1955 100644 --- a/parser/testdata/02708_dotProduct/metadata.json +++ b/parser/testdata/02708_dotProduct/metadata.json @@ -12,10 +12,6 @@ "stmt19": true, "stmt20": true, "stmt21": true, - "stmt27": true, - "stmt28": true, - "stmt29": true, - "stmt30": true, "stmt34": true, "stmt35": true, "stmt36": true, diff --git a/parser/testdata/02752_space_function/metadata.json b/parser/testdata/02752_space_function/metadata.json index a0dba08bb1..0967ef424b 100644 --- a/parser/testdata/02752_space_function/metadata.json +++ b/parser/testdata/02752_space_function/metadata.json @@ -1,8 +1 @@ -{ - "explain_todo": { - "stmt12": true, - "stmt13": true, - "stmt14": true, - "stmt15": true - } -} +{} diff --git a/parser/testdata/02887_byteswap/metadata.json b/parser/testdata/02887_byteswap/metadata.json index 90a70852d2..aa054347dc 100644 --- a/parser/testdata/02887_byteswap/metadata.json +++ b/parser/testdata/02887_byteswap/metadata.json @@ -1,16 +1,5 @@ { "explain_todo": { - "stmt16": true, - "stmt17": true, - "stmt18": true, - "stmt19": true, - "stmt20": true, - "stmt21": true, - "stmt22": true, - "stmt23": true, - "stmt24": true, - "stmt25": true, - "stmt26": true, "stmt27": true, "stmt29": true, "stmt31": true diff --git a/parser/testdata/03011_definitive_guide_to_cast/metadata.json b/parser/testdata/03011_definitive_guide_to_cast/metadata.json index 13ca773cd0..6733b1ee91 100644 --- a/parser/testdata/03011_definitive_guide_to_cast/metadata.json +++ b/parser/testdata/03011_definitive_guide_to_cast/metadata.json @@ -1,10 +1,8 @@ { "explain_todo": { "stmt36": true, - "stmt50": true, "stmt52": true, "stmt53": true, - "stmt56": true, "stmt84": true, "stmt86": true, "stmt88": true diff --git a/parser/testdata/03365_time_time64_conversions/metadata.json b/parser/testdata/03365_time_time64_conversions/metadata.json index 4a805c2cbd..0967ef424b 100644 --- a/parser/testdata/03365_time_time64_conversions/metadata.json +++ b/parser/testdata/03365_time_time64_conversions/metadata.json @@ -1,50 +1 @@ -{ - "explain_todo": { - "stmt100": true, - "stmt101": true, - "stmt130": true, - "stmt131": true, - "stmt132": true, - "stmt137": true, - "stmt138": true, - "stmt139": true, - "stmt140": true, - "stmt146": true, - "stmt147": true, - "stmt148": true, - "stmt149": true, - "stmt154": true, - "stmt155": true, - "stmt156": true, - "stmt157": true, - "stmt162": true, - "stmt163": true, - "stmt164": true, - "stmt165": true, - "stmt48": true, - "stmt49": true, - "stmt50": true, - "stmt55": true, - "stmt56": true, - "stmt57": true, - "stmt58": true, - "stmt64": true, - "stmt65": true, - "stmt66": true, - "stmt67": true, - "stmt73": true, - "stmt74": true, - "stmt75": true, - "stmt76": true, - "stmt82": true, - "stmt83": true, - "stmt84": true, - "stmt85": true, - "stmt90": true, - "stmt91": true, - "stmt92": true, - "stmt93": true, - "stmt98": true, - "stmt99": true - } -} +{} diff --git a/parser/testdata/03376_iceberg_truncate/metadata.json b/parser/testdata/03376_iceberg_truncate/metadata.json index a60fff857b..0967ef424b 100644 --- a/parser/testdata/03376_iceberg_truncate/metadata.json +++ b/parser/testdata/03376_iceberg_truncate/metadata.json @@ -1 +1 @@ -{"explain_todo":{"stmt17":true,"stmt18":true,"stmt19":true,"stmt20":true,"stmt40":true}} +{} From 21f020a13d0b3023750177dd912e8529ae053444 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 30 Dec 2025 22:52:03 +0000 Subject: [PATCH 17/18] Add ColumnsListMatcher support for COLUMNS(id, col) syntax Previously the parser only handled COLUMNS('pattern') as a regex matcher. Now it also handles COLUMNS(col1, col2) as a list matcher, which is output as ColumnsListMatcher in EXPLAIN AST. Changes: - Added Columns field to ColumnsMatcher AST type - Added Qualifier field for qualified matchers like table.COLUMNS(...) - Updated parseColumnsMatcher to parse column lists - Added explainColumnsMatcher function for proper EXPLAIN output --- ast/ast.go | 12 ++++--- internal/explain/explain.go | 2 +- internal/explain/expressions.go | 35 +++++++++++++++++++ parser/expression.go | 16 ++++++++- .../metadata.json | 6 ---- 5 files changed, 59 insertions(+), 12 deletions(-) diff --git a/ast/ast.go b/ast/ast.go index b298f3d10c..aed7f31d39 100644 --- a/ast/ast.go +++ b/ast/ast.go @@ -1137,11 +1137,15 @@ type ReplaceExpr struct { func (r *ReplaceExpr) Pos() token.Position { return r.Position } func (r *ReplaceExpr) End() token.Position { return r.Position } -// ColumnsMatcher represents COLUMNS('pattern') expression. +// ColumnsMatcher represents COLUMNS('pattern') or COLUMNS(col1, col2) expression. +// When Pattern is set, it's a regex matcher (ColumnsRegexpMatcher in explain). +// When Columns is set, it's a list matcher (ColumnsListMatcher in explain). type ColumnsMatcher struct { - Position token.Position `json:"-"` - Pattern string `json:"pattern"` - Except []string `json:"except,omitempty"` + Position token.Position `json:"-"` + Pattern string `json:"pattern,omitempty"` + Columns []Expression `json:"columns,omitempty"` // For COLUMNS(id, name) syntax + Except []string `json:"except,omitempty"` + Qualifier string `json:"qualifier,omitempty"` // For qualified matchers like table.COLUMNS(...) } func (c *ColumnsMatcher) Pos() token.Position { return c.Position } diff --git a/internal/explain/explain.go b/internal/explain/explain.go index f8e212eb2d..44a3355f60 100644 --- a/internal/explain/explain.go +++ b/internal/explain/explain.go @@ -74,7 +74,7 @@ func Node(sb *strings.Builder, node interface{}, depth int) { case *ast.Asterisk: explainAsterisk(sb, n, indent, depth) case *ast.ColumnsMatcher: - fmt.Fprintf(sb, "%sColumnsRegexpMatcher\n", indent) + explainColumnsMatcher(sb, n, indent, depth) // Functions case *ast.FunctionCall: diff --git a/internal/explain/expressions.go b/internal/explain/expressions.go index f493e6f2ca..0934f27834 100644 --- a/internal/explain/expressions.go +++ b/internal/explain/expressions.go @@ -585,6 +585,41 @@ func explainColumnsTransformers(sb *strings.Builder, n *ast.Asterisk, indent str } } +func explainColumnsMatcher(sb *strings.Builder, n *ast.ColumnsMatcher, indent string, depth int) { + // Determine the matcher type based on whether it's a pattern or a list + if len(n.Columns) > 0 { + // ColumnsListMatcher for COLUMNS(col1, col2, ...) + typeName := "ColumnsListMatcher" + if n.Qualifier != "" { + typeName = "QualifiedColumnsListMatcher" + } + if n.Qualifier != "" { + // QualifiedColumnsListMatcher has qualifier as a child + fmt.Fprintf(sb, "%s%s (children %d)\n", indent, typeName, 2) + fmt.Fprintf(sb, "%s Identifier %s\n", indent, n.Qualifier) + } else { + fmt.Fprintf(sb, "%s%s (children %d)\n", indent, typeName, 1) + } + // Output the columns as ExpressionList + fmt.Fprintf(sb, "%s ExpressionList (children %d)\n", indent, len(n.Columns)) + for _, col := range n.Columns { + Node(sb, col, depth+2) + } + } else { + // ColumnsRegexpMatcher for COLUMNS('pattern') + typeName := "ColumnsRegexpMatcher" + if n.Qualifier != "" { + typeName = "QualifiedColumnsRegexpMatcher" + } + if n.Qualifier != "" { + fmt.Fprintf(sb, "%s%s (children %d)\n", indent, typeName, 1) + fmt.Fprintf(sb, "%s Identifier %s\n", indent, n.Qualifier) + } else { + fmt.Fprintf(sb, "%s%s\n", indent, typeName) + } + } +} + func explainWithElement(sb *strings.Builder, n *ast.WithElement, indent string, depth int) { // For WITH elements, we need to show the underlying expression with the name as alias // When name is empty, don't show the alias part diff --git a/parser/expression.go b/parser/expression.go index 684d6d1ad3..e620f9bd18 100644 --- a/parser/expression.go +++ b/parser/expression.go @@ -1868,10 +1868,24 @@ func (p *Parser) parseColumnsMatcher() ast.Expression { return nil } - // Parse the pattern (string) + // Parse the arguments - either a string pattern or a list of identifiers if p.currentIs(token.STRING) { + // String pattern: COLUMNS('pattern') matcher.Pattern = p.current.Value p.nextToken() + } else { + // Column list: COLUMNS(col1, col2, ...) + for !p.currentIs(token.RPAREN) && !p.currentIs(token.EOF) { + col := p.parseExpression(LOWEST) + if col != nil { + matcher.Columns = append(matcher.Columns, col) + } + if p.currentIs(token.COMMA) { + p.nextToken() + } else { + break + } + } } p.expect(token.RPAREN) diff --git a/parser/testdata/02339_analyzer_matcher_basic/metadata.json b/parser/testdata/02339_analyzer_matcher_basic/metadata.json index af5fe5c15a..59d3de61c0 100644 --- a/parser/testdata/02339_analyzer_matcher_basic/metadata.json +++ b/parser/testdata/02339_analyzer_matcher_basic/metadata.json @@ -4,10 +4,6 @@ "stmt103": true, "stmt105": true, "stmt106": true, - "stmt18": true, - "stmt19": true, - "stmt21": true, - "stmt22": true, "stmt30": true, "stmt31": true, "stmt33": true, @@ -20,7 +16,6 @@ "stmt52": true, "stmt54": true, "stmt55": true, - "stmt6": true, "stmt60": true, "stmt61": true, "stmt63": true, @@ -28,7 +23,6 @@ "stmt66": true, "stmt67": true, "stmt69": true, - "stmt7": true, "stmt70": true, "stmt79": true, "stmt80": true, From 5cf0e28806cefd1069e35ae931ce996e3fc085d7 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 30 Dec 2025 22:57:05 +0000 Subject: [PATCH 18/18] Add DROP access control statement support in EXPLAIN output Added proper parsing and EXPLAIN output for: - DROP ROLE - DROP QUOTA - DROP POLICY - DROP ROW POLICY - DROP SETTINGS PROFILE These statements now output their expected EXPLAIN format like "DROP ROLE query" instead of being treated as table drops. --- ast/ast.go | 31 +++++++++-------- internal/explain/statements.go | 30 +++++++++++++++++ parser/parser.go | 33 +++++++++++++++++-- .../01702_system_query_log/metadata.json | 4 +-- 4 files changed, 80 insertions(+), 18 deletions(-) diff --git a/ast/ast.go b/ast/ast.go index aed7f31d39..b2249e485c 100644 --- a/ast/ast.go +++ b/ast/ast.go @@ -468,19 +468,24 @@ func (t *TTLClause) End() token.Position { return t.Position } // DropQuery represents a DROP statement. type DropQuery struct { - Position token.Position `json:"-"` - IfExists bool `json:"if_exists,omitempty"` - Database string `json:"database,omitempty"` - Table string `json:"table,omitempty"` - Tables []*TableIdentifier `json:"tables,omitempty"` // For DROP TABLE t1, t2, t3 - View string `json:"view,omitempty"` - User string `json:"user,omitempty"` - Function string `json:"function,omitempty"` // For DROP FUNCTION - Dictionary string `json:"-"` // For DROP DICTIONARY (format only, not in AST JSON) - Temporary bool `json:"temporary,omitempty"` - OnCluster string `json:"on_cluster,omitempty"` - DropDatabase bool `json:"drop_database,omitempty"` - Sync bool `json:"sync,omitempty"` + Position token.Position `json:"-"` + IfExists bool `json:"if_exists,omitempty"` + Database string `json:"database,omitempty"` + Table string `json:"table,omitempty"` + Tables []*TableIdentifier `json:"tables,omitempty"` // For DROP TABLE t1, t2, t3 + View string `json:"view,omitempty"` + User string `json:"user,omitempty"` + Function string `json:"function,omitempty"` // For DROP FUNCTION + Dictionary string `json:"-"` // For DROP DICTIONARY (format only, not in AST JSON) + Role string `json:"role,omitempty"` // For DROP ROLE + Quota string `json:"quota,omitempty"` // For DROP QUOTA + Policy string `json:"policy,omitempty"` // For DROP POLICY + RowPolicy string `json:"row_policy,omitempty"` // For DROP ROW POLICY + SettingsProfile string `json:"settings_profile,omitempty"` // For DROP SETTINGS PROFILE + Temporary bool `json:"temporary,omitempty"` + OnCluster string `json:"on_cluster,omitempty"` + DropDatabase bool `json:"drop_database,omitempty"` + Sync bool `json:"sync,omitempty"` } func (d *DropQuery) Pos() token.Position { return d.Position } diff --git a/internal/explain/statements.go b/internal/explain/statements.go index 837993ce0e..e551403daf 100644 --- a/internal/explain/statements.go +++ b/internal/explain/statements.go @@ -415,6 +415,36 @@ func explainDropQuery(sb *strings.Builder, n *ast.DropQuery, indent string, dept return } + // DROP ROLE + if n.Role != "" { + fmt.Fprintf(sb, "%sDROP ROLE query\n", indent) + return + } + + // DROP QUOTA + if n.Quota != "" { + fmt.Fprintf(sb, "%sDROP QUOTA query\n", indent) + return + } + + // DROP POLICY + if n.Policy != "" { + fmt.Fprintf(sb, "%sDROP POLICY query\n", indent) + return + } + + // DROP ROW POLICY + if n.RowPolicy != "" { + fmt.Fprintf(sb, "%sDROP ROW POLICY query\n", indent) + return + } + + // DROP SETTINGS PROFILE + if n.SettingsProfile != "" { + fmt.Fprintf(sb, "%sDROP SETTINGS PROFILE query\n", indent) + return + } + // Handle multiple tables: DROP TABLE t1, t2, t3 if len(n.Tables) > 1 { fmt.Fprintf(sb, "%sDropQuery (children %d)\n", indent, 1) diff --git a/parser/parser.go b/parser/parser.go index 7d36f70dae..ad01c16557 100644 --- a/parser/parser.go +++ b/parser/parser.go @@ -3268,6 +3268,7 @@ func (p *Parser) parseDrop() *ast.DropQuery { if p.currentIs(token.IDENT) && strings.ToUpper(p.current.Value) == "PROFILE" { p.nextToken() } + drop.SettingsProfile = "_pending_" default: // Handle multi-word DROP types: ROW POLICY, NAMED COLLECTION, DICTIONARY if p.currentIs(token.IDENT) { @@ -3276,8 +3277,26 @@ func (p *Parser) parseDrop() *ast.DropQuery { case "DICTIONARY": dropDictionary = true p.nextToken() - case "ROW", "NAMED", "POLICY", "QUOTA", "ROLE": - // Skip the DROP type tokens + case "ROW": + // DROP ROW POLICY + p.nextToken() // skip ROW + if p.currentIs(token.IDENT) && strings.ToUpper(p.current.Value) == "POLICY" { + p.nextToken() // skip POLICY + } + // Mark as row policy drop - name will be set later + drop.RowPolicy = "_pending_" + case "POLICY": + // DROP POLICY + p.nextToken() + drop.Policy = "_pending_" + case "QUOTA": + p.nextToken() + drop.Quota = "_pending_" + case "ROLE": + p.nextToken() + drop.Role = "_pending_" + case "NAMED": + // DROP NAMED COLLECTION - skip tokens for p.currentIs(token.IDENT) || p.current.Token.IsKeyword() { if p.currentIs(token.IF) { break // Hit IF EXISTS @@ -3326,6 +3345,16 @@ func (p *Parser) parseDrop() *ast.DropQuery { } } else if dropFunction { drop.Function = tableName + } else if drop.Role == "_pending_" { + drop.Role = tableName + } else if drop.Quota == "_pending_" { + drop.Quota = tableName + } else if drop.Policy == "_pending_" { + drop.Policy = tableName + } else if drop.RowPolicy == "_pending_" { + drop.RowPolicy = tableName + } else if drop.SettingsProfile == "_pending_" { + drop.SettingsProfile = tableName } else if dropDictionary { drop.Dictionary = tableName // Also set Table/Tables for backward compatibility with AST JSON diff --git a/parser/testdata/01702_system_query_log/metadata.json b/parser/testdata/01702_system_query_log/metadata.json index e5ed7f989d..9528cef5c8 100644 --- a/parser/testdata/01702_system_query_log/metadata.json +++ b/parser/testdata/01702_system_query_log/metadata.json @@ -37,8 +37,6 @@ "stmt60": true, "stmt68": true, "stmt69": true, - "stmt74": true, - "stmt8": true, - "stmt86": true + "stmt74": true } }