Add apply mode for declarative, idempotent CREATE DDL#230
Open
ryannedolan wants to merge 1 commit into
Open
Conversation
Introduces a `mode` connection property on the JDBC driver that
selects between the existing strict CREATE semantics (`mode=create`,
the default) and a new K8s-style apply mode (`mode=apply`). In apply
mode, plain `CREATE` converges the resource to the declared
definition rather than failing when it already exists, making
checked-in `.sql` files idempotent and reconcilable from CI.
The dispatch is centralized in a new `HoptimatorDdlUtils.effectiveMode`
helper that combines the statement's `OR REPLACE` flag with the
connection mode and resolves to `DdlMode.CREATE` or `DdlMode.UPDATE`.
All five `CREATE` execute paths (VIEW, MATERIALIZED VIEW, TABLE,
TRIGGER, DATABASE) route through it, and the existence-checks inside
`processCreateMaterializedView` / `processCreateTable` now key off
the resolved `DdlMode` so apply-mode plain CREATEs no longer hit the
"already exists" error. SPECIFY (dry-run) preserves its original
syntax-driven semantics so `!specify` previews still match what a
real run would do.
`CREATE OR REPLACE` keeps converging behavior in both modes;
destructive metadata-change detection (FORCE semantics) is left for a
follow-up once per-resource diff policy lands. Data is never
destroyed by any CREATE form — that safety guarantee is what makes
apply mode usable in production.
DROP remains strictly imperative; prune-by-absence is explicitly out
of scope.
Tested:
- New JUnit5 coverage for `effectiveMode` / `isApplyMode` across
default, explicit `create`, `apply`, case variants, unknown
values, and null properties.
- New Quidem integration script `k8s-apply-mode.id` exercising
idempotent plain CREATE for views and tables, and DROP under
apply mode (wired via `run(..., "mode=apply")`).
- Adjusted one pre-existing unit test that bypassed the executor
to pass `DdlMode.UPDATE` directly when supplying `OR REPLACE`
SQL — the helper now treats `DdlMode` as authoritative.
Docs: `docs/user-guide/ddl-reference.md` describes the two modes,
the JDBC property, the data-safety guarantee, and the out-of-scope
list.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
modeJDBC connection property:create(default, strict) orapply(declarative, idempotent).CREATEconverges the resource to the declared definition rather than failing on conflict — so checked-in.sqlfiles become K8s-style reconcilable manifests for CI/CD.HoptimatorDdlUtils.effectiveMode(orReplace, conn)helper that resolves the connection mode and the statement'sOR REPLACEflag into aDdlMode.processCreateMaterializedView/processCreateTablenow key off the resolvedDdlMode. SPECIFY (dry-run) preserves its original syntax-driven preview so!specifystill reflects what a real run would do.DROPstays imperative and metadata-only; prune-by-absence is explicitly out of scope.CREATEandCREATE OR REPLACEare equivalent because the deployer can't yet distinguish compatible vs. incompatible diffs.Usage:
CREATECREATE OR REPLACEcreate(default)applyCREATE.Testing Done
effectiveMode/isApplyModecovering default, explicitcreate,apply, case insensitivity, unknown values, and null properties.DdlMode.UPDATEdirectly when supplyingOR REPLACESQL — the helper now treatsDdlModeas authoritative.k8s-apply-mode.idexercising idempotent re-CREATE for views and tables, the OR-REPLACE-equals-CREATE invariant under apply, and DROP still working. Wired intoTestSqlScriptsviarun("k8s-apply-mode.id", "mode=apply")../gradlew :hoptimator-jdbc:test— 508 tests pass../gradlew compileTestJava— all modules compile clean.make integration-tests— deferred to CI (needs the K8s cluster).Docs
docs/user-guide/ddl-reference.mdgets a new "CREATE semantics" section describing the two modes, the JDBC property, the data-safety guarantee, and the out-of-scope list.