Reject CREATE TABLE ... CLONE AS SELECT / table function by tuanpach · Pull Request #105004 · ClickHouse/ClickHouse · GitHub
Skip to content

Reject CREATE TABLE ... CLONE AS SELECT / table function#105004

Open
tuanpach wants to merge 13 commits into
ClickHouse:masterfrom
tuanpach:fix-clone-as-select
Open

Reject CREATE TABLE ... CLONE AS SELECT / table function#105004
tuanpach wants to merge 13 commits into
ClickHouse:masterfrom
tuanpach:fix-clone-as-select

Conversation

@tuanpach

Copy link
Copy Markdown
Member

The CREATE TABLE ... CLONE AS <source> clause is intended to clone a real on-disk source table — the partition-attach step performed after table creation needs actual partitions to copy. The parser, however, also accepted CREATE TABLE t CLONE AS SELECT ... and CREATE TABLE t CLONE AS <table_function>. Neither shape has partitions to attach, so the CLONE keyword was silently ignored and the table was created as a plain CREATE ... AS SELECT with an inferred schema and ORDER BY tuple().

This PR rejects both shapes early in getTablePropertiesAndNormalizeCreateQuery with BAD_ARGUMENTS, so the user gets a clear error instead of silent misbehavior. It also fixes a related defect in ASTCreateQuery::formatQueryImpl: the if (select) branch did not call add_clone_if_needed, so an AST format round-trip silently dropped CLONE from CLONE AS SELECT. The randomized-settings injection in clickhouse-test round-trips queries through this formatter, which is how the gap surfaced.

Fixes #104994

Changelog category (leave one):

  • Bug Fix (user-visible misbehavior in an official stable release)

Changelog entry (a user-readable short description of the changes that goes into CHANGELOG.md):

Fix CREATE TABLE ... CLONE AS SELECT ... and CREATE TABLE ... CLONE AS <table_function> silently ignoring the CLONE keyword and creating a plain CREATE ... AS SELECT instead. These shapes are now rejected with BAD_ARGUMENTS.

Documentation entry for user-facing changes

  • Documentation is unchanged: this is a bug fix that brings behavior in line with the existing CLONE AS documentation, which only describes the CLONE AS <table> form.

@clickhouse-gh

clickhouse-gh Bot commented May 15, 2026

Copy link
Copy Markdown
Contributor

@clickhouse-gh clickhouse-gh Bot added the pr-bugfix Pull request with bugfix, not backported by default label May 15, 2026
Comment thread src/Interpreters/InterpreterCreateQuery.cpp Outdated
The parser accepted CREATE TABLE t CLONE AS SELECT ... and CREATE TABLE t
CLONE AS <table_function>, but neither shape has partitions to attach, so
the CLONE keyword was silently ignored and the table was created as a
plain CREATE ... AS SELECT. Reject these shapes in
getTablePropertiesAndNormalizeCreateQuery with BAD_ARGUMENTS. The check
is gated to fresh user CREATE only — pre-fix the AS-table-function shape
was persisted into .sql metadata, and rejecting on ATTACH would break
loading of any such legacy table on upgrade.

Also fix ASTCreateQuery::formatQueryImpl: the `if (select)` branch did
not emit CLONE, so an AST round-trip silently dropped the keyword. The
randomized-settings injection in clickhouse-test round-trips queries
through this formatter, which is how the gap surfaced.

Fixes ClickHouse#104994
@tuanpach tuanpach force-pushed the fix-clone-as-select branch from ae90c08 to 5fb381c Compare May 15, 2026 14:11
@alexey-milovidov

Copy link
Copy Markdown
Member

This was fixed by #105146. Let's update the branch.

@SmitaRKulkarni SmitaRKulkarni self-assigned this Jun 9, 2026

@SmitaRKulkarni SmitaRKulkarni 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

Comment thread src/Interpreters/InterpreterCreateQuery.cpp Outdated
The guard added in `getTablePropertiesAndNormalizeCreateQuery` only fired
for a fresh `CREATE` (`mode <= CREATE`). A user-supplied full `ATTACH`
definition — one that carries an explicit column list (and an engine for
the `SELECT` form) — is not the short-attach path: `attach_short_syntax`
is false, so it goes through the create-like path with `mode == ATTACH`
and bypassed the guard. `isCreateQueryWithImmediateInsertSelect` returns
false for `attach` and the clone partition-attach path requires
`as_table_saved`, so `CLONE AS SELECT` / `CLONE AS table_function` were
silently ignored for such `ATTACH` queries, exactly as they were for
`CREATE` before this fix.

Extend the guard to also reject `is_clone_as && as_table.empty()` for a
user-supplied full `ATTACH` (`mode == ATTACH && !attach_short_syntax`),
while leaving short `ATTACH` (`ATTACH TABLE t;`) and `SECONDARY_CREATE`
(`DatabaseReplicated` internal queries, `RESTORE`) permissive so
previously-validated metadata that persisted these forms still loads.
Server startup is unaffected: it loads tables via `createTableFromAST`,
which never calls this function.

Adds full-`ATTACH` regression cases to
`04238_clone_as_select_rejected`.

Addresses review feedback on ClickHouse#105004

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@alexey-milovidov

Copy link
Copy Markdown
Member

Addressed the AI review's "Request changes" verdict (full ATTACH case) in commit 8d1f677.

The guard in getTablePropertiesAndNormalizeCreateQuery previously fired only for a fresh CREATE (mode <= CREATE). A user-supplied full ATTACH definition (explicit column list, plus an engine for the SELECT form) is not the short-attach path — attach_short_syntax is false, so it goes through the create-like path with mode == ATTACH and bypassed the guard, silently dropping CLONE.

The guard now also rejects is_clone_as && as_table.empty() for a full user ATTACH (mode == ATTACH && !attach_short_syntax), while leaving short ATTACH (ATTACH TABLE t;) and SECONDARY_CREATE (DatabaseReplicated / RESTORE) permissive so previously-validated metadata still loads. Server startup is unaffected: tables are loaded via createTableFromAST, which never calls this function.

Added regression cases to 04238_clone_as_select_rejected for full-ATTACH ... CLONE AS SELECT and full-ATTACH ... CLONE AS <table function>; both now return BAD_ARGUMENTS. Verified locally with an ASan build.

@alexey-milovidov

Copy link
Copy Markdown
Member

The only red check is AST fuzzer (amd_debug)Logical error: 'Inconsistent AST formatting: the original AST: (STID: 1941-1bfa). It is a pre-existing, unrelated fuzzer flake, not a regression from this PR.

The failing query is a fuzzer-mutated CREATE TABLE dist_t0__fuzz_47 (a Decimal(22, 3), b Array(Array(Array(Array(Array(intDiv(less(1048575, '6ba7b810-…'), LowCardinality(UInt8)))))))) ENGINE = Distributed(…). It contains no CLONE and no SELECT, so none of this PR's code (the getTablePropertiesAndNormalizeCreateQuery guard, or the ASTCreateQuery::formatQueryImpl CLONE-emitting branch) is exercised. The divergence is entirely in the fuzzer's nonsense nested type: the token 1048575 round-trips as DataType_1048575 on the original AST but as Literal_UInt64_1048575 after re-parsing — a general numeric-token-in-type-position artifact.

This signature has failed 279 times over the last 6 months across ~249 unrelated PRs (e.g. #105714, #106215, #107125, #88234, #86768, #108055, #79091, #109107, #108414). It was tracked by the now-closed #106358 (exact STID 1941-1bfa), and the same "original AST" family remains open as #108372.

All other checks are green and the PR is approved.

@clickhouse-gh

clickhouse-gh Bot commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

LLVM Coverage Report

Metric Baseline Current Δ
Lines 85.50% 85.50% +0.00%
Functions 92.60% 92.70% +0.10%
Branches 77.70% 77.70% +0.00%

Changed lines: Changed C/C++ lines covered: 11/11 (100.00%) · Uncovered code

Full report · Diff report

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pr-bugfix Pull request with bugfix, not backported by default

Projects

None yet

Development

Successfully merging this pull request may close these issues.

CLONE AS SELECT silently ignores CLONE keyword instead of throwing error

3 participants