Repository navigation
Conversation
There was a problem hiding this comment.
🟡 Changes recommended
Dependency discovery misses affected descendants, and the classifier incorrectly refuses explicit CASCADE drops.
2 open issues
What changed in this PR
Adds live view-dependency checks so pg-sprite can refuse blocked column changes during planning and name the affected views.
Changes:
- Reads column dependencies for imperative and declarative planning.
- Adds
dependent-viewdiagnostics and thedependent_viewsdecision field. - Updates tests, documentation, and plan/lint contract versions.
| File | Description |
|---|---|
| README.md | Summarizes the new refusal. |
| pkg/preflight/dependent_views.go | Reads column-to-view dependencies. |
| pkg/preflight/dependent_views_integration_test.go | Tests catalog dependency discovery. |
| pkg/planner/planner.go | Adds dependency facts and refusal classification. |
| pkg/planner/planner_test.go | Tests refusal and unaffected operations. |
| pkg/plan/plan.go | Bumps plan format to 10. |
| pkg/plan/plan_test.go | Updates contract assertions. |
| pkg/migrate/verdicts.go | Names views in refusal details. |
| pkg/migrate/facts.go | Loads dependencies for imperative planning. |
| pkg/migrate/dependent_view_integration_test.go | Tests imperative refusal and allowed changes. |
| pkg/lint/lint.go | Bumps lint format to 4. |
| pkg/lint/lint_test.go | Updates JSON version assertions. |
| pkg/diffplan/diffplan.go | Loads dependencies for declarative planning. |
| pkg/diffplan/dependent_view_integration_test.go | Tests declarative refusal. |
| pkg/capabilities/capabilities.yaml | Records dependency-related restrictions. |
| internal/cli/dryrun_text.go | Renders view names and remediation. |
| internal/cli/dryrun_text_test.go | Tests refusal rendering. |
| docs/postgres-online-ddl-reference.md | Explains the new reason. |
| docs/plan-report.md | Documents the version and field. |
| docs/optimistic-attempt.md | Records plan-time refusal behavior. |
| docs/lint-report.md | Documents the vocabulary version change. |
| docs/limitations.md | Describes dependency restrictions. |
| docs/cli-output-examples.md | Updates JSON versions. |
| docs/capabilities.md | Updates column-change capabilities. |
| docs/binary-coercible-type-changes.md | Explains early dependency refusal. |
| demo/tour.sh | Updates contract-version checks. |
Review effort: Balanced
PostgreSQL refuses ALTER COLUMN TYPE and DROP COLUMN on a column that a view or materialized view reads, even for a type change it would otherwise relabel without a rewrite. The planner classified both as native, so the statement reached the server and failed there with an untyped error that did not say which views were in the way. Both front doors now read, for the target table, which views and materialized views pg_depend records as reading each column, and the planner refuses a type change or drop of such a column with the new dependent-view reason. The decision names the views in a new dependent_views field, and the refusal detail and dry-run text tell the operator to drop or redefine them first. A rename is still allowed, since a view follows the column. The server's own refusal remains the backstop for a view created after planning. The new reason grows the closed Reasons vocabulary and the decision gains a field, so the plan report moves to format version 10 and the lint report, which embeds the reasons, to version 4. The linter has no live facts and never reports the reason. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A DROP COLUMN ... CASCADE drops the views that read the column, which the server accepts, so it is no longer refused; it stays destructive. The parser records CASCADE on the operation. A change without ONLY reaches the column on every partition and inheritance child, so a view on one of them blocks it too. The lookup now walks the inheritance tree and reports, per column of the table, the views on the whole tree and on the table alone; a change under ONLY is judged by the table's own. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
aparajon
force-pushed
the
armand/dependent-view-refusal
branch
from
October 17, 2026 15:53
d52166f to
7f5473a
Compare
aparajon
marked this pull request as ready for review
October 17, 2026 16:13
aparajon
requested review from
JashLal,
Kiran01bm,
eeSeeGee,
jayjanssen,
jemiahw and
morgo
as code owners
October 17, 2026 16:13
…ction' into armand/dependent-view-refusal # Conflicts: # pkg/migrate/facts.go # pkg/planner/planner.go
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.

Why
PostgreSQL refuses
ALTER COLUMN TYPEandDROP COLUMNon a column a view or materialized view reads, even for a type change it would otherwise relabel without a rewrite. The planner classified both as native, so the dry-run said "runs as written" and the real run then failed at the server with an untyped error that did not say which views were in the way. Copy-and-swap already refuses tables with dependent views, but only by count, and the native path never checked at all.What
migrateand the declarative diff) read, frompg_depend, which views and materialized views read each column of the target table (preflight.ColumnDependentViews).dependent-viewreason. The decision names the views in a newdependent_viewsfield, and the refusal detail and dry-run text name them and say to drop or redefine them first.format_version10, and the lint report, which embeds the reasons, to 4. The linter has no live facts and never reports the reason.Stacked on #222, since both bump the lint
format_version.An apply widens
app.users.emailfromvarchar(50)tovarchar(100)whileapp.active_usersandapp.mail_reportselect it.Dry-run, before
The run then failed:
Dry-run, after
The run is refused the same way:
🤖 Generated with Claude Code