Skip to content

Add support for CREATE OR REPLACE MATERIALIZED VIEW and TTL GROUP BY - #316

Merged
git-hulk merged 11 commits into
AfterShip:masterfrom
marre:feat/lagr-parser-gaps
Aug 21, 2026
Merged

Add support for CREATE OR REPLACE MATERIALIZED VIEW and TTL GROUP BY#316
git-hulk merged 11 commits into
AfterShip:masterfrom
marre:feat/lagr-parser-gaps

Conversation

@marre

@marre marre commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Closes #315

Both statement shapes were validated against a real ClickHouse server (26.7.4.58) before and after implementation.

CREATE OR REPLACE MATERIALIZED VIEW

  • parseDDL guard now accepts MATERIALIZED after CREATE OR REPLACE.
  • CreateMaterializedView gains an OrReplace field, wired through the parser, FormatSQL, and the visitor.

TTL GROUP BY ... SET action

  • tryParseTTLPolicy parses a GROUP BY action without a preceding keyword: the keys are read as a plain expression list (matching ClickHouse, which rejects query-level GROUP BY modifiers such as WITH TOTALS/GROUPING SETS inside a TTL, but treats GROUP BY ALL and CUBE(...) as ordinary key expressions), followed by optional SET <col> = <agg expr>[, ...] assignments.
  • Reuses the existing GroupByClause and UpdateAssignment AST nodes, so no new node types were introduced.
  • TTLPolicyRule gained GroupBy *GroupByClause and Set []*UpdateAssignment; the redundant TTLPolicy.GroupBy field was removed.

Tests

  • New fixtures: parser/testdata/ddl/create_or_replace_materialized_view.sql, parser/testdata/ddl/create_table_ttl_group_by.sql (single SET, SET-less, multi-key/multi-SET variants), goldens regenerated.
  • TestParser_InvalidSyntax covers the engine-rejected TTL forms (WITH TOTALS/ROLLUP/CUBE, GROUPING SETS).
  • Verified: make test, golangci-lint v1.53.3 (CI pin), gofmt.

marre and others added 6 commits August 21, 2026 09:03
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>
Copilot AI lite review requested due to automatic review settings August 21, 2026 08:53

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment thread parser/parser_table.go Outdated

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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:2480Moderate, 2 votes: TTL span calculations omit Policy.End().
  • parser/parser_table.go:1259Moderate, 2 votes: GROUP BY ALL SET ... is rejected.
  • parser/parser_table.go:1278Moderate, 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, in TTL ... GROUP BY id, created + INTERVAL 2 DAY DELETE, the second TTL expression is parsed as another group key and the trailing DELETE remains unexpected. Existing CREATE/ALTER TTL syntax supports multiple comma-separated rules, so the new GROUP BY rule needs a boundary-aware parser rather than parseColumnExprList.
		keys, err := p.parseColumnExprList(p.Pos())

parser/parser_table.go:1245

  • This branch is also reachable while parsing a column definition: parseTableColumn calls tryParseTTLClause(..., false) and that reuses tryParseTTLPolicy. A GROUP BY ... SET action is a table TTL operation, so input such as x 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

  • parseColumnExprList consumes an initial DISTINCT into HasDistinct, so this TTL-specific path accepts and re-formats ... TTL expr GROUP BY DISTINCT id. DISTINCT is 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 consume DISTINCT here while still allowing ordinary key expressions such as ALL or CUBE(...).
		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.

Comment thread parser/ast.go
Comment thread parser/parser_table.go Outdated
Comment thread parser/parser_table.go Outdated
- 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>
@git-hulk
git-hulk self-requested a review August 21, 2026 09:24

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment thread parser/parser_table.go Outdated
Comment thread parser/parser_table.go Outdated
marre and others added 2 commits August 21, 2026 11:34
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>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment thread parser/parser_table.go
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>
@marre
marre requested a balanced review from Copilot August 21, 2026 10:49

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 formats TTL ... 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>
@marre

marre commented Aug 21, 2026

Copy link
Copy Markdown
Contributor Author

Responding to the Copilot review pullrequestreview-4992522436 about TTL ... GROUP BY id SET x = sum(x) WHERE id > 0:

Confirmed and fixed in fa6ab50. Verified on a full ClickHouse server (26.7.4.58) that WHERE is rejected after GROUP BY and RECOMPRESS as well as TO DISK/VOLUME — it belongs only to the DELETE action. tryParseTTLPolicy now parses WHERE only in the DELETE branch and leaves it unconsumed for the statement parser otherwise, so all four ... WHERE forms (GROUP BY SET, GROUP BY, RECOMPRESS, TO VOLUME) are rejected, while DELETE WHERE and every existing fixture still round-trip. Regression tests added to TestParser_InvalidSyntax.

@git-hulk git-hulk left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@git-hulk
git-hulk merged commit 8ee6ab2 into AfterShip:master Aug 21, 2026
1 check passed
@git-hulk

Copy link
Copy Markdown
Member

@marre Thank you!

@marre
marre deleted the feat/lagr-parser-gaps branch August 21, 2026 11:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Parser rejects CREATE OR REPLACE MATERIALIZED VIEW and TTL GROUP BY actions that ClickHouse accepts

3 participants