Add support for CREATE OR REPLACE MATERIALIZED VIEW and TTL GROUP BY - #316
Conversation
Identifies two statement shapes the parser currently rejects that were each validated against a full ClickHouse server (26.7.4.58): CREATE OR REPLACE MATERIALIZED VIEW and the TTL GROUP BY ... SET action. Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
Allow MATERIALIZED after CREATE OR REPLACE (guard + CreateMaterializedView.OrReplace), and parse the TTL GROUP BY <keys> [SET <col> = <agg>] action into new TTLPolicyGroupBy/TTLPolicySetExpr nodes. Both forms validated against a full ClickHouse server (26.7.4.58) before implementation. Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
The plan is complete; the two grammar gaps it tracked are implemented. Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
Model the TTL GROUP BY <keys> [SET <col> = <agg>] action with the existing GroupByClause and UpdateAssignment nodes instead of dedicated TTLPolicyGroupBy/TTLPolicySetExpr types, and drop the now-redundant TTLPolicy.GroupBy field. Net removal of ~130 lines. Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
A TTL GROUP BY action only accepts a plain expression list; the query-level forms (ALL, CUBE/ROLLUP/GROUPING SETS, and WITH CUBE/ROLLUP/TOTALS) are rejected by ClickHouse inside a TTL. Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
ClickHouse rejects query-level GROUP BY modifiers (ALL, CUBE/ROLLUP/ GROUPING SETS, WITH CUBE/ROLLUP/TOTALS) inside a TTL GROUP BY, but ALL and CUBE/ROLLUP(...) read there as ordinary key expressions and can be valid (e.g. a column named all). Parse the keys directly as a plain list so the clause is built from the expected shape and future query-level GROUP BY sugar stays out of the TTL grammar. Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 83181bcdba
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
There was a problem hiding this comment.
Pull request overview
Adds ClickHouse parser support for CREATE OR REPLACE MATERIALIZED VIEW and TTL GROUP BY ... SET actions.
Changes:
- Extends AST, parsing, formatting, and traversal support.
- Adds valid and invalid syntax tests plus regenerated goldens.
- Adds formatting fixtures for both statement forms.
Review findings:
parser/ast.go:2480— Moderate, 2 votes: TTL span calculations omitPolicy.End().parser/parser_table.go:1259— Moderate, 2 votes:GROUP BY ALL SET ...is rejected.parser/parser_table.go:1278— Moderate, 2 votes: Commas before subsequent TTL rules are consumed as assignments.
Reviewed changes
Copilot reviewed 27 out of 27 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Reviewed change |
|---|---|
parser/walk.go |
Traverses TTL grouping and assignments. |
parser/testdata/dml/output/alter_table_modify_ttl_multiple.sql.golden.json |
Updates multiple-TTL AST golden data. |
parser/testdata/ddl/output/create_table_with_ttl_policy.sql.golden.json |
Updates existing TTL policy golden data. |
parser/testdata/ddl/output/create_table_ttl_group_by.sql.golden.json |
Adds TTL GROUP BY ... SET AST golden data. |
parser/testdata/ddl/output/create_or_replace_materialized_view.sql.golden.json |
Adds materialized-view replacement AST golden data. |
parser/testdata/ddl/output/create_mv_with_order_by.sql.golden.json |
Updates materialized-view golden data. |
parser/testdata/ddl/output/create_mv_with_not_op.sql.golden.json |
Updates materialized-view golden data. |
parser/testdata/ddl/output/create_materialized_view_with_refresh.sql.golden.json |
Updates materialized-view golden data. |
parser/testdata/ddl/output/create_materialized_view_with_gcs.sql.golden.json |
Updates materialized-view golden data. |
parser/testdata/ddl/output/create_materialized_view_with_empty_table_schema.sql.golden.json |
Updates materialized-view golden data. |
parser/testdata/ddl/output/create_materialized_view_with_definer.sql.golden.json |
Updates materialized-view golden data. |
parser/testdata/ddl/output/create_materialized_view_with_comment_before_as.sql.golden.json |
Updates materialized-view golden data. |
parser/testdata/ddl/output/create_materialized_view_rmv_engine_with_columns.sql.golden.json |
Updates materialized-view golden data. |
parser/testdata/ddl/output/create_materialized_view_rmv_depends_on_multi.sql.golden.json |
Updates materialized-view golden data. |
parser/testdata/ddl/output/create_materialized_view_basic.sql.golden.json |
Updates materialized-view golden data. |
parser/testdata/ddl/output/bug_001.sql.golden.json |
Updates materialized-view golden data. |
parser/testdata/ddl/format/create_table_ttl_group_by.sql |
Adds TTL formatting fixture. |
parser/testdata/ddl/format/create_or_replace_materialized_view.sql |
Adds replacement-view formatting fixture. |
parser/testdata/ddl/format/beautify/create_table_ttl_group_by.sql |
Adds beautified TTL formatting fixture. |
parser/testdata/ddl/format/beautify/create_or_replace_materialized_view.sql |
Adds beautified replacement-view fixture. |
parser/testdata/ddl/create_table_ttl_group_by.sql |
Adds TTL grouping and assignment fixtures. |
parser/testdata/ddl/create_or_replace_materialized_view.sql |
Adds replacement materialized-view fixture. |
parser/parser_view.go |
Propagates OR REPLACE state. |
parser/parser_test.go |
Adds invalid TTL syntax coverage. |
parser/parser_table.go |
Parses the new DDL and TTL syntax. |
parser/format.go |
Formats replacement views and TTL actions. |
parser/ast.go |
Adds materialized-view and TTL AST fields. |
Suppressed comments (3)
parser/parser_table.go:1259
- This generic comma-separated list greedily consumes commas that belong to the surrounding
parseTTLClause(..., true). For example, inTTL ... GROUP BY id, created + INTERVAL 2 DAY DELETE, the second TTL expression is parsed as another group key and the trailingDELETEremains unexpected. Existing CREATE/ALTER TTL syntax supports multiple comma-separated rules, so the new GROUP BY rule needs a boundary-aware parser rather thanparseColumnExprList.
keys, err := p.parseColumnExprList(p.Pos())
parser/parser_table.go:1245
- This branch is also reachable while parsing a column definition:
parseTableColumncallstryParseTTLClause(..., false)and that reusestryParseTTLPolicy. AGROUP BY ... SETaction is a table TTL operation, so input such asx UInt64 TTL created + INTERVAL 1 DAY GROUP BY id SET x = sum(x)is accepted here even though ClickHouse does not allow row-grouping actions in a column TTL. Pass the table/column context through the TTL parser (or gate this branch for table TTLs) rather than enabling it for both callers.
case p.matchKeyword(KeywordGroup):
parser/parser_table.go:1259
parseColumnExprListconsumes an initialDISTINCTintoHasDistinct, so this TTL-specific path accepts and re-formats... TTL expr GROUP BY DISTINCT id.DISTINCTis a SELECT/list modifier, not a valid TTL GROUP BY key-list modifier (ClickHouse rejects this form); use an expression-list parser that does not consumeDISTINCThere while still allowing ordinary key expressions such asALLorCUBE(...).
keys, err := p.parseColumnExprList(p.Pos())
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
- Do not consume the comma separating TTL expressions as a SET assignment separator: probe for the next assignment and roll back when the comma starts the next TTL rule. - Include the policy span in TTLExpr.End() so StatementEnd/ListEnd cover GROUP BY/SET actions. - Accept bare keyword TTL GROUP BY keys (e.g. GROUP BY ALL SET ...) by falling back to a keyword identifier when an expression cannot start. Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 39740e0784
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
A TTL SET assignment has no trailing clause to bound the right-hand side, unlike ALTER TABLE UPDATE's IN PARTITION, so parse it with full expression precedence (e.g. SET x = max(y) > 0). Also restrict OR REPLACE MATERIALIZED VIEW to the CREATE path: ClickHouse rejects an ATTACH OR REPLACE combination. Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
Keep tryParseTTLPolicy's switch declarative: the GROUP BY case now delegates to parseTTLPolicyGroupBy, which owns the key list, the SET assignment probe, and rule construction. Behavior-neutral. Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 46c2a9c2c1
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
A comma after a TTL GROUP BY key continues the key list, and the engine rejects a trailing TTL action after a comma-separated key (unlike after a complete SET assignment, where the comma starts the next TTL rule). Add the engine-rejected combined form to TestParser_InvalidSyntax so the behavior stays locked in. Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 27 out of 27 changed files in this pull request and generated no new comments.
Suppressed comments (1)
parser/parser_table.go:1259
- The new GROUP BY branch falls through to the shared
tryParseWhereClause, so the parser now accepts and formatsTTL ... GROUP BY id SET x = sum(x) WHERE id > 0. ClickHouse's TTL GROUP BY action ends after the optional SET list; WHERE belongs to DELETE actions. Return this policy immediately so a trailing WHERE remains unconsumed and is rejected by the statement parser.
case p.matchKeyword(KeywordGroup):
groupBy, err := p.parseTTLPolicyGroupBy(pos)
if err != nil {
return nil, err
}
rule = groupBy
ClickHouse rejects a WHERE clause after RECOMPRESS, TO DISK/VOLUME, and GROUP BY; it belongs only to DELETE. Parse it only in the DELETE branch so the other actions leave WHERE unconsumed for the statement parser, and lock the rejected forms in with regression tests. Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
|
Responding to the Copilot review pullrequestreview-4992522436 about Confirmed and fixed in |
|
@marre Thank you! |
Closes #315
Both statement shapes were validated against a real ClickHouse server (26.7.4.58) before and after implementation.
CREATE OR REPLACE MATERIALIZED VIEWparseDDLguard now acceptsMATERIALIZEDafterCREATE OR REPLACE.CreateMaterializedViewgains anOrReplacefield, wired through the parser,FormatSQL, and the visitor.TTL
GROUP BY ... SETactiontryParseTTLPolicyparses aGROUP BYaction without a preceding keyword: the keys are read as a plain expression list (matching ClickHouse, which rejects query-level GROUP BY modifiers such asWITH TOTALS/GROUPING SETSinside a TTL, but treatsGROUP BY ALLandCUBE(...)as ordinary key expressions), followed by optionalSET <col> = <agg expr>[, ...]assignments.GroupByClauseandUpdateAssignmentAST nodes, so no new node types were introduced.TTLPolicyRulegainedGroupBy *GroupByClauseandSet []*UpdateAssignment; the redundantTTLPolicy.GroupByfield was removed.Tests
parser/testdata/ddl/create_or_replace_materialized_view.sql,parser/testdata/ddl/create_table_ttl_group_by.sql(singleSET,SET-less, multi-key/multi-SETvariants), goldens regenerated.TestParser_InvalidSyntaxcovers the engine-rejected TTL forms (WITH TOTALS/ROLLUP/CUBE,GROUPING SETS).make test,golangci-lintv1.53.3 (CI pin),gofmt.