feat(postgres): execute greenfield CREATE TABLE through the native-safe path - #1209
Conversation
There was a problem hiding this comment.
Pull request overview
This PR expands SchemaBot’s PostgreSQL “native-safe” execution envelope to include greenfield CREATE TABLE, routing creates through pg-sprite’s dedicated create executor path so they can be planned as executable (when schema CREATE access is present) and applied with fail-closed absence/privilege proofs minted in the executing session.
Changes:
- Route
TierCreateTableprivilege checks to schema-scopedCheckCreatePrivileges, and block non-create dependents when the target table is provably absent. - Execute greenfield
CREATE TABLEviaexecutor.ExecuteCreate, adding typed permanent refusal classifications for create collisions and unsupported create shapes. - Add/adjust integration tests for executable planning and end-to-end create apply behavior; bump the
pg-spritedependency and update docs.
Reviewed changes
Copilot reviewed 8 out of 10 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| pkg/engine/postgres/postgres.go | Updates plan-time privilege checking to support schema-scoped CREATE checks and dependency-based blocking when the table is absent. |
| pkg/engine/postgres/postgres_test.go | Adjusts unit test expectations for the absent-table dependency reason and ensures no privilege probe occurs. |
| pkg/engine/postgres/postgres_integration_test.go | Updates plan test to expect executable greenfield CREATE and adds integration coverage for create apply + collision refusal. |
| pkg/engine/postgres/apply.go | Adds the greenfield create execution path using pg-sprite’s create executor and new refusal classifications. |
| pkg/engine/postgres/apply_test.go | Updates acceptance-time validation test to use a non-native-supported shape now that CREATE is supported. |
| go.mod | Bumps pg-sprite to a newer pseudo-version containing the native-safe create support. |
| go.sum | Records checksum entries for the updated pg-sprite version. |
| e2e/consumermodule/go.mod | Updates the consumer module’s indirect pg-sprite pin to match the root module. |
| e2e/consumermodule/go.sum | Updates checksums for the consumer module’s updated pg-sprite version. |
| docs/configuration.md | Documents that the native-safe table size ceiling does not apply to greenfield CREATE TABLE. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
|
🤖 Adversarial correctness review, requested by @aparajon and performed by their agent. Reviewed at head Verdict: the routing is right and the create path is proved end to end — but two operator-facing messages on the greenfield plan are wrong in a way I could reproduce, and the pg-sprite pin points at a commit that is not on pg-sprite CI ran only three checks on this draft (Semgrep, zizmor, DCO) — the test suite has not run — so I ran the package's unit and integration tests locally at head instead; all pass. The branch is 10 commits behind Findings1. When the role cannot create in the schema, the plan tells the operator to do something the same plan says they cannot do. Reproduced against a real target with a role holding
There is no creating change to apply. The cause is ordering: 2. The create tier's privilege refusal names a table that does not exist, and the wrong scope. Same reproduction, step 0: "lacks access for create a new table on table "users"". 3. The pin is a commit that is not on pg-sprite 4. Interlock: Action items
Verified (tried to break, couldn't)
This review was generated by Claude Code (claude-opus-5). |
…fe path Bump the pg-sprite pin and route the off-ladder create tier through pg-sprite's create path: absence and schema-CREATE proofs are minted in the executing session, collisions and unsupported create shapes surface as permanent refusals directing a re-plan, and steps that depend on a not-yet-created table are blocked with that dependency as the reason instead of an unanswerable privilege probe. The table size ceiling does not apply to creates: it bounds rewrites of existing data.
85a67c9 to
598ad7d
Compare
Review follow-up: the refusal classifier hand-mirrored a subset of pg-sprite sentinels, so unmapped outcomes (notably if-not-exists) stayed retryable forever. Classification is now total over executor.Codes(), invariant violations fail closed, create-tier privilege wording is schema-scoped, the unnamed-target guard runs before dependent-step blocking, and the absent-table dependent reason stays neutral about the create step's undecided verdict. Pin moves to the tagged v0.2.0 release, clearing the orphan pseudo-version from go.sum.
|
Review response from Kiran's (@Kiran01bm) code review assessment agent (Amp / Claude Opus 4.5)
|
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
Greenfield
CREATE TABLEnow executes through the PostgreSQL native-safe path instead of planning as a blocked shape.Why
The native-safe envelope stopped at ALTERs on existing tables, so a first table in a schema file could be reviewed but never applied. pg-sprite now provides a fail-closed create path (block/pg-sprite#63): absence and schema-
CREATEproofs minted in the executing session, typed collision refusals, and declared-index sequencing. This PR adopts it so the envelope widens deliberately.What
executor.ExecuteCreate: the existing-table preflight ladder never runs for a greenfield target (it states facts about a table that has none).CheckCreatePrivileges), not the table; steps that depend on a not-yet-created table are blocked with that dependency as the reason instead of an unanswerable "table not found" probe.Before / after
Plan verdict rendering: greenfield CREATE TABLE
Before — always blocked:
After — executable when the role can create in the schema; a step that depends on the table's creation reads: