Skip to content

fix(pg-delta): order dependent view drops before column type rewrites#278

Closed
avallete wants to merge 5 commits into
mainfrom
claude/pr-273-review-duPY4
Closed

fix(pg-delta): order dependent view drops before column type rewrites#278
avallete wants to merge 5 commits into
mainfrom
claude/pr-273-review-duPY4

Conversation

@avallete

@avallete avallete commented Jun 8, 2026

Copy link
Copy Markdown
Member

Summary

Fixes ordering of schema changes when a column type is altered in place and dependent views exist. Views must be dropped before the column type rewrite and recreated after, since the rewrite invalidates the view's definition.

Changes

  • Added invalidates property to BaseChange: A new getter that returns stable identifiers of objects that are mutated in place. Unlike drops, the object keeps its identity but its dependents must be torn down and rebuilt around the mutation.

  • Implemented invalidates in AlterTableAlterColumnType: Reports the column being altered as invalidated, since ALTER COLUMN ... TYPE rewrites the column in place and breaks dependent views.

  • Updated graph builder to handle invalidations: In the drop phase, invalidated ids are treated like dropped ids for ordering purposes — dependents are ordered before the mutation, ensuring views are dropped before the column type change.

  • Added test coverage:

    • Unit test in sort-changes.test.ts verifying correct ordering: DropViewAlterTableAlterColumnTypeCreateView
    • Integration test in alter-table-operations.test.ts validating the full roundtrip with actual database operations
  • Added changeset for patch release

Implementation Details

The invalidates mechanism is distinct from drops:

  • drops: Object is removed entirely; phase assignment, filtering, and serialization all change
  • invalidates: Object is mutated in place; only ordering is affected via the dependency graph

This allows the sorter to use pg_depend edges to order dependent teardowns before the mutation, without changing how the mutation itself is classified or serialized.

https://claude.ai/code/session_01Fnhs5Jt3LhiLQzF1c8JguH

claude added 2 commits June 8, 2026 14:29
…column rewrite

Adds a sorter unit test and an end-to-end roundtrip integration test for the
case where an ALTER COLUMN ... TYPE rewrite is referenced by a view that is
dropped and recreated in the same plan.

RED against current code (drop phase has no producer for the rewritten
column, so the main-catalog pg_depend edge can't order the dependent drop):

  expect(received).toEqual(expected)
  - "DropView"
    "AlterTableAlterColumnType"
  + "DropView"
    "CreateView"

i.e. the rewrite is emitted before the dependent DROP VIEW, which PostgreSQL
rejects with 0A000 "cannot alter type of a column used by a view or rule".

https://claude.ai/code/session_01Fnhs5Jt3LhiLQzF1c8JguH
An ALTER COLUMN ... TYPE rewrite fails when the original column is still
referenced by a view that is dropped and recreated in the same plan. The diff
already produced the right change set (AlterTableAlterColumnType, DropView,
CreateView); only the order was wrong.

getExecutionPhase already routes a built-in-target rewrite into the drop
phase, but the rewrite only had a `requires` edge for the column, so the drop
phase had no producer for that column stable id. Without a producer, the
main-catalog pg_depend edge from the view to the column could not order
DROP VIEW before the rewrite.

Rather than couple the generic graph builder to a specific change subtype,
introduce a first-class `invalidates` member on Change (sibling to
creates/drops/requires). It declares the stable ids a change mutates in
place: the object keeps its identity, but dependents bound to the old
definition must be torn down before the change and rebuilt after.
AlterTableAlterColumnType reports its column; buildGraphData folds
`invalidates` into the drop-phase producer set exactly like `drops`, so the
existing pg_depend edges order dependents' drops ahead of the rewrite.

This is ordering-only: `invalidates` does not feed Change.drops, so phase
assignment, filtering, fingerprints, and serialization are unchanged.
Recreation order needs no help because the create phase runs entirely after
the drop phase.

GREEN:
- src/core/sort/sort-changes.test.ts: 3 pass
- tests/integration/alter-table-operations.test.ts (pg17): 22 pass
  (focused "change column type after dropping dependent view" snapshot emits
  DROP VIEW before the ALTER COLUMN ... TYPE)

https://claude.ai/code/session_01Fnhs5Jt3LhiLQzF1c8JguH
@changeset-bot

changeset-bot Bot commented Jun 8, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 86d80b1

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@supabase/pg-delta Patch

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

@pkg-pr-new

pkg-pr-new Bot commented Jun 8, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/supabase/pg-toolbelt/@supabase/pg-delta@278
npm i https://pkg.pr.new/supabase/pg-toolbelt/@supabase/pg-topo@278

commit: 86d80b1

@avallete avallete marked this pull request as ready for review June 8, 2026 16:25
@avallete avallete changed the title Order dependent view drops before column type rewrites fix(pg-delta): order dependent view drops before column type rewrites Jun 8, 2026
Add a bullet to the Cycle Breaking / Normalization taxonomy describing when
to declare `invalidates` (in-place mutations that invalidate dependents) so
the next contributor reaches for the Change member instead of an instanceof
in the generic graph builder.

https://claude.ai/code/session_01Fnhs5Jt3LhiLQzF1c8JguH
The integration test snapshotted the full recreated CREATE VIEW body, but
pg_get_viewdef output (column qualification, whitespace) varies across
PostgreSQL builds — CI deparsed `alter_column_type_view_dependent_users.age`
while the locally-pulled image emitted bare `age`, so the inline snapshot
failed on PG15/PG17 shards even though the fix ordered the statements
correctly.

Assert the deparse-independent property the fix actually guarantees instead:
DROP VIEW before the in-place ALTER COLUMN ... TYPE, and the rewrite before
the recreated CREATE VIEW. roundtripFidelityTest still applies the SQL, so a
wrong order would also fail at apply with 0A000. The exact ordering remains
pinned deterministically by the sort-changes unit test.

https://claude.ai/code/session_01Fnhs5Jt3LhiLQzF1c8JguH
@avallete

avallete commented Jun 9, 2026

Copy link
Copy Markdown
Member Author

Closing in favor of: #273

@avallete avallete closed this Jun 9, 2026
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