fix(pg-delta): order view drops before column type rewrites#273
Conversation
🦋 Changeset detectedLatest commit: 4e8172e The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
Follow-up from deeper issue #263 validation: I found one adjacent case in the same column-rewrite dependent-relation family, so I added it to this PR rather than opening a new PR. A materialized view depending on a column whose type is rewritten was already being dropped and recreated, but Fix added here:
RED evidence: the focused PG17 regression failed after six retry attempts with Validation run locally:
|
|
One extra local-only validation after the materialized-view metadata fix: I added a deep probe for the cascade-reach risk mentioned in issue #263. Scenario covered locally:
The PG17 generated plan drops the downstream view and index before This was kept on a local validation branch only; no extra remote test branch was pushed. |
dc6859c to
32682f8
Compare
Signed-off-by: xiaominghao <xiaominghao@baidu.com>
RED: PG17 focused integration failed because ALTER TABLE ... ALTER COLUMN age TYPE was generated before DROP VIEW public.alter_column_type_view_dependent_user_ages. The inline snapshot showed the wrong order on all six retry attempts. Signed-off-by: xiaominghao <xiaominghao@baidu.com>
…rite RED: PGDELTA_TEST_POSTGRES_VERSIONS=17 bun run test --test-name-pattern "preserves metadata" tests/integration/alter-table-operations.test.ts failed because the generated plan recreated the dependent view and comment but omitted GRANT SELECT ON public.alter_column_type_view_metadata_user_ages TO alter_column_type_view_metadata_reader. Signed-off-by: xiaominghao <xiaominghao@baidu.com>
GREEN: the RED regression for view-dependent column rewrites with comment and GRANT now passes on PG17. The generated plan keeps DROP VIEW before ALTER COLUMN TYPE, recreates the view, restores COMMENT ON VIEW, and re-emits GRANT SELECT for the recreated view. Signed-off-by: xiaominghao <xiaominghao@baidu.com>
Signed-off-by: xiaominghao <xiaominghao@baidu.com>
RED: PG17 materialized-view-operations.test.ts failed for "restore materialized view metadata when replacing for column type rewrite" because commentMatviewIdx was -1 after six retry attempts. The generated plan dropped and recreated the materialized view but did not emit COMMENT ON MATERIALIZED VIEW or the dependent GRANT. GREEN: the focused regression now passes, the full PG17 materialized-view integration file passes, and the materialized-view diff plus expand-replace-dependencies unit files pass. Signed-off-by: xiaominghao <xiaominghao@baidu.com>
f572259 to
5fa96aa
Compare
|
Reviewed this and it's correct — the diagnosis (rewrite routed to the drop phase but missing a producer for the column, so the main-catalog One design suggestion on where the anchor lives. The fix puts an An alternative that keeps I pushed that variant as #278 so you can compare side by side — same tests and behavior, the difference is purely that the knowledge sits on the change class instead of in the generic builder. Either is fine functionally; flagging it because the generic-builder coupling is the kind of thing that tends to accrete more |
Signed-off-by: xiaominghao <xiaominghao@baidu.com>
|
Thanks @avallete, this design point makes sense. I updated this PR to adopt the
Validation after the update:
|
commit: |
avallete
left a comment
There was a problem hiding this comment.
Should document the new primitive in the CLAUDE.md for later work something like:
- **In-place mutations that invalidate dependents declare `invalidates`, not a graph hack**. When a change keeps an object's identity but rewrites it so dependents bound to the old definition must be dropped before it and rebuilt after (the canonical case is `AlterTableAlterColumnType`, whose `ALTER COLUMN … TYPE` forces a PostgreSQL table rewrite), override the `invalidates` getter on the change (sibling to `creates`/`drops`/`requires` in `base.change.ts`) to return the affected stable id. `buildGraphData` folds `invalidates` into the drop-phase producer set exactly like `drops`, so the existing `pg_depend` edges order each dependent's teardown ahead of the mutation. This is ordering-only: `invalidates` does NOT feed `Change.drops`, so phase assignment (`getExecutionPhase`), filtering, fingerprints, and serialization are unchanged, and recreation order needs no help because the create phase always runs after the entire drop phase. Prefer this over adding a change-type `instanceof` to the otherwise generic `graph-builder.ts`.
Rule of thumb: if the fix changes a valid final `Change[]` before graph construction, it is post-diff; if it reacts to a concrete unbreakable dependency cycle and needs to inject or rebuild changes, it belongs in the sort-phase cycle breakers; if it needs only one object's semantics, it belongs in that object's `diff*`; if it only removes a graph edge without changing emitted SQL, it belongs in the sort filter; if a change mutates an object in place such that its dependents must be torn down first, it declares `invalidates`.
Overall a bit more comments around the code changes / additions for context might be useful.
Signed-off-by: xiaominghao <xiaominghao@baidu.com>
|
Follow-up pushed in What changed:
Local validation after the change:
I also replied directly on the three inline threads with the specific change made for each one. |
avallete
left a comment
There was a problem hiding this comment.
The review-feedback commit (8dea6e7) resolves all four threads cleanly — comments are accurate and the invalidates primitive is now documented in CLAUDE.md. Verified locally: 1064 core unit tests pass and tsc --noEmit is clean.
Leaving a few non-blocking follow-ups inline (view-path duplication, a missing regression for the privilege-filter guard, and a changeset that undersells the PR). None of these block the fix, which is correct and well-scoped.
Generated by Claude Code
Signed-off-by: xiaominghao <xiaominghao@baidu.com>
|
Follow-up pushed in Addressed the three non-blocking review follow-ups:
RED evidence for the new guard regression: after adding the test, temporarily removing the Validation after the final files:
I also replied directly on each inline thread. |
RED: catalog.diff.test.ts failed for replacement-created view revokes because the dropped-target privilege filter removed RevokeViewPrivileges when a recreated view inherited SELECT from ALTER DEFAULT PRIVILEGES but the branch model wanted no reader ACL. The failure was Expected: true / Received: false for RevokeViewPrivileges. GREEN: catalog.diff.test.ts now passes, the related unit set for catalog/view/materialized-view replacement and sort invalidates passes, PG17 focused integration for dependent view and materialized-view column rewrite cases passes, and format-and-lint/check-types/knip pass. Signed-off-by: xiaominghao <xiaominghao@baidu.com>
|
Follow-up from the final review pass on this PR: I found one more case in the same replacement-created ACL metadata area, so I kept it in #273 rather than opening a new PR. The previous guard preserved replacement Pushed
Validation after the change:
|
RED: after rebasing onto supabase#273, the design regression using a generic invalidating change failed in expand-replace-dependencies.test.ts with Expected: true / Received: false for DropRlsPolicy because the helper still checked AlterTableAlterColumnType directly. GREEN: expand-replace-dependencies.test.ts now passes, PG17 policy-dependencies.test.ts focused and full-file runs pass, and format-and-lint/check-types/knip pass. Signed-off-by: xiaominghao <xiaominghao@baidu.com>
RED: after rebasing onto supabase#273, the design regression using a generic invalidating change failed in expand-replace-dependencies.test.ts with Expected: true / Received: false for DropRlsPolicy because the helper still checked AlterTableAlterColumnType directly. GREEN: expand-replace-dependencies.test.ts now passes, PG17 policy-dependencies.test.ts focused and full-file runs pass, and format-and-lint/check-types/knip pass. Signed-off-by: xiaominghao <xiaominghao@baidu.com>
RED: after rebasing onto supabase#273, the design regression using a generic invalidating change failed in expand-replace-dependencies.test.ts with Expected: true / Received: false for DropRlsPolicy because the helper still checked AlterTableAlterColumnType directly. GREEN: expand-replace-dependencies.test.ts now passes, PG17 policy-dependencies.test.ts focused and full-file runs pass, and format-and-lint/check-types/knip pass. Signed-off-by: xiaominghao <xiaominghao@baidu.com>
Refs #263
This is the next small fix for issue #263, scoped to Case 2:
ALTER COLUMN ... TYPEcan fail when an existing dependent relation still references the old column definition while the same plan is trying to drop and recreate that dependent relation.What changed
AlterTableAlterColumnTypenow declares the rewritten column through the ordering-onlyinvalidatesgetter.buildGraphDatastays generic and foldsinvalidatesinto the drop-phase producer set, so existingpg_dependrows can order dependent teardown before the in-place rewrite without changingChange.drops, filtering semantics, fingerprints, or serialization.CREATEstatements.GRANTs andREVOKE/REVOKE GRANT OPTIONchanges generated to subtract privileges inherited fromALTER DEFAULT PRIVILEGESat create time.RED evidence
ALTER TABLE public.alter_column_type_view_dependent_users ALTER COLUMN age TYPE integer ...beforeDROP VIEW public.alter_column_type_view_dependent_user_ages. Manual apply reproduced PostgreSQL error0A000: cannot alter type of a column used by a view or rule.GRANT SELECT.COMMENT ON MATERIALIZED VIEW/ the dependent grant (commentMatviewIdxwas-1).SELECTfromALTER DEFAULT PRIVILEGESbut the branch model wants no reader ACL,catalog.diff.test.tsfailed withExpected: true / Received: falseforRevokeViewPrivileges.Local validation
catalog.diff.test.ts,view.diff.test.ts,materialized-view.diff.test.ts,expand-replace-dependencies.test.ts, andsort-changes.test.ts:32 pass / 0 fail.catalog.diff.test.tsrerun after the ACL filter/comment updates:2 pass / 0 fail.alter-table-operations.test.ts+materialized-view-operations.test.tswith--test-name-pattern "change column type after dropping dependent view|restore materialized view metadata":3 pass / 0 fail../node_modules/.bin/bun run format-and-lint:fix: passed../node_modules/.bin/bun run check-types: passed../node_modules/.bin/bun run knip --fix: passed; knip only reported the existing configuration hints.Local integration validation used
PGDELTA_SKIP_DUMMY_SECLABEL_BUILD=1to avoid rebuilding the local dummy security-label image; CI should keep security-label coverage enabled.Case 1 from #263 remains intentionally covered by the separate follow-up PR.