Skip to content

sql: support IF NOT EXISTS for CREATE CLUSTER and CREATE CLUSTER REPLICA - #38061

Open
aljoscha wants to merge 1 commit into
MaterializeInc:mainfrom
aljoscha:sql-601-cluster-if-not-exists
Open

sql: support IF NOT EXISTS for CREATE CLUSTER and CREATE CLUSTER REPLICA#38061
aljoscha wants to merge 1 commit into
MaterializeInc:mainfrom
aljoscha:sql-601-cluster-if-not-exists

Conversation

@aljoscha

@aljoscha aljoscha commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

CREATE CLUSTER and CREATE CLUSTER REPLICA now accept an IF NOT EXISTS
clause, matching the 13 other CREATE statements that already support it.
With it, creating a cluster or replica whose name is already taken succeeds and
emits a cluster "c" already exists, skipping notice (SQLSTATE 42710) instead
of failing, so idempotent provisioning scripts no longer need a pre-flight
existence check.

Skipping leaves the existing object completely untouched. The configuration in
the statement is not applied, so this is not an upsert.

Previously these two statements produced a misleading parse error, because IF
was consumed as the object name and the failure surfaced one token later:

CREATE CLUSTER IF NOT EXISTS c (SIZE '25cc')
ERROR: Expected one of AUTO or AVAILABILITY or DISK or ..., found NOT

Implementation

The machinery already exists end to end, so this is mostly threading a flag:
if_not_exists on the two AST nodes and the two plans, a parse_if_not_exists()
call in each parse function, and the established "turn the catalog's
already-exists error into a notice" arm in the sequencer.

Two things worth calling out:

  • The skip happens at the catalog transaction, where every sibling path puts
    it, rather than short-circuiting early on the in-memory catalog. That means
    the statement is validated in full first, so CREATE CLUSTER IF NOT EXISTS c (SIZE 'typo') still reports the bad size rather than silently succeeding
    because c happens to exist. The cost is that a skipped statement still
    allocates (and discards) a cluster id, which is a durable catalog write. That
    is the same cost every other CREATE ... IF NOT EXISTS already pays, and
    silently accepting a typo'd config in a provisioning script is the worse
    failure mode.

  • A bare if identifier now renders quoted. The IF [NOT] EXISTS clause sits
    exactly where the object name goes, so a rendered CREATE CLUSTER if (SIZE …)
    no longer reparses, which breaks both the plan/unplan roundtrip assertion in
    plan_create_cluster and SHOW CREATE CLUSTER for a cluster named if.
    This joins the existing list of keywords quoted for exactly this reason
    (PREPARE, INTO, WHEN, …), and it covers the same hazard for every other
    statement whose IF EXISTS clause sits where the name goes. Note the flip
    side: CREATE CLUSTER if … and CREATE CLUSTER REPLICA if.r … used to parse
    (the parser accepts keywords as identifiers) and now do not. Quoting still
    works, and DROP CLUSTER if was already broken the same way.

SHOW CREATE CLUSTER does not render IF NOT EXISTS. It is a property of the
statement, not of the cluster.

Tests

  • src/sql-parser/tests/testdata/ddl: syntax for both statements, the
    IF EXISTS typo, and a cluster literally named if.
  • test/sqllogictest/cluster.slt: idempotence, that the existing object is left
    untouched, that a bad SIZE is still reported, the managed and unmanaged
    cluster variants, FEATURES, reserved names, case sensitivity, full
    SHOW CREATE CLUSTER output, that the replica form still requires the cluster
    to exist, and that it does not soften the managed-cluster rejection.
  • test/pgtest-mz/notice.pt: the wire-level notice for both statements.

Fixes SQL-601

Both statements now accept an `IF NOT EXISTS` clause, matching the 13
other `CREATE` statements that already support it. A name that is already
taken yields an "already exists, skipping" notice and success instead of
an error, leaving the existing object untouched.

The skip happens where every sibling path puts it, at the catalog
transaction, so the statement is still validated in full first. A typo'd
`SIZE` does not pass silently just because the cluster happens to exist.

A bare `if` identifier now renders quoted, since the `IF [NOT] EXISTS`
clause sits exactly where the object name goes and an unquoted `if` name
would no longer reparse.

Fixes SQL-601
@aljoscha
aljoscha force-pushed the sql-601-cluster-if-not-exists branch from f9f363d to 0d7238c Compare August 5, 2026 17:31
@aljoscha
aljoscha marked this pull request as ready for review August 5, 2026 17:42
@aljoscha
aljoscha requested review from a team as code owners August 5, 2026 17:42

@mgree mgree left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM. It's worth confirming that nobody has an existing cluster named if, which will get borked by this change.

Comment on lines +465 to +468
// `IF NOT EXISTS` is a property of the statement that created the
// cluster, not of the cluster itself, so the reconstructed plan
// never carries it.
if_not_exists: false,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Reading this, I don't have the context to know what a "reconstructed plan" is. Someone who knows this file might, though!

Comment on lines +364 to +369
// The `IF [NOT] EXISTS` clauses of CREATE/DROP/ALTER sit
// exactly where the object name goes, so a bare `if` name
// is consumed as the start of such a clause on reparse
// (`CREATE CLUSTER if (SIZE …)` -> "expected NOT, found
// left parenthesis").
|| kw == IF

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Could this be breaking for existing customers' queries? (Feels crazy to me to use if as a name for something, but that's programmer bias I'm sure.)

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.

2 participants