Skip to content

fix: order function recreation after dependent view recreation (#480)#488

Merged
tianzhou merged 2 commits into
mainfrom
fix/issue-480-view-func-ordering
Jun 22, 2026
Merged

fix: order function recreation after dependent view recreation (#480)#488
tianzhou merged 2 commits into
mainfrom
fix/issue-480-view-func-ordering

Conversation

@tianzhou

Copy link
Copy Markdown
Contributor

Summary

Fixes a dependency-ordering bug where apply fails with cannot drop view ... because other objects depend on it (SQLSTATE 2BP01).

When a function's RETURNS/parameter type references a view, the function depends on that view's composite rowtype. If both the view must be recreated (DROP + CREATE, e.g. a column added mid-list) and the function's signature changes (so it's a drop+add rather than an in-place CREATE OR REPLACE), pgschema emitted the operations in this order:

DROP FUNCTION fn_create_user(text);          -- drop phase
CREATE OR REPLACE FUNCTION fn_create_user(...) RETURNS vw_users ...;  -- create phase ❌ re-pins old view
ALTER TABLE tb_users ADD COLUMN role ...;    -- modify phase
DROP VIEW vw_users RESTRICT;                  -- modify phase ❌ FAILS: function depends on it
CREATE OR REPLACE VIEW vw_users ...;

The function's DROP already runs in the drop phase, but its CREATE ran in the create phase — before the view's DROP — re-pinning the old view and blocking the RESTRICT drop.

Fix

The existing function-deferral logic only considered newly added views (buildViewLookup(d.addedViews)). This adds the same handling for views being recreated (modifiedViews with RequiresRecreate): added functions whose type references such a view are pulled out of the create phase into functionsAwaitingRecreatedViews and emitted in the modify phase, after generateModifyViewsSQL recreates the view.

Resulting order (now applies cleanly):

DROP FUNCTION fn_create_user(text);
ALTER TABLE tb_users ADD COLUMN role ...;
DROP VIEW vw_users RESTRICT;                  -- ✅ no dependents
CREATE OR REPLACE VIEW vw_users ...;
CREATE OR REPLACE FUNCTION fn_create_user(...) RETURNS vw_users ...;  -- ✅ after view

Test plan

Added testdata/diff/dependency/issue_480_view_function_recreate/ (the issue's exact reproduction: table <- view <- function, adding a role column).

  • internal/diff TestDiffFromFiles — asserts the corrected DDL ordering.
  • cmd TestPlanAndApplyapplies the migration end-to-end against embedded PostgreSQL, proving the new order is valid (previously failed with 2BP01).
PGSCHEMA_TEST_FILTER="dependency/issue_480_view_function_recreate" go test ./internal/diff -run TestDiffFromFiles
PGSCHEMA_TEST_FILTER="dependency/issue_480_view_function_recreate" go test ./cmd -run TestPlanAndApply

Both green; full suite runs in CI.

🤖 Generated with Claude Code

When a function's RETURNS/parameter type references a view that must be
recreated (DROP + CREATE) and the function's signature also changes, the
function was being recreated in the create phase — before the view's DROP
in the modify phase. The freshly-created function re-pinned the old view,
so the subsequent DROP VIEW ... RESTRICT failed with SQLSTATE 2BP01.

The function's DROP already runs in the drop phase, so deferring its CREATE
to the modify phase (after generateModifyViewsSQL recreates the view) leaves
the view free of dependents during its RESTRICT drop. Added functions whose
type references a view with RequiresRecreate are pulled out of the create
phase into functionsAwaitingRecreatedViews and emitted after view recreation.

Fixes #480

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings June 22, 2026 17:35

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes an apply dependency-ordering failure when a function’s return/parameter type references a view that must be recreated (DROP+CREATE), by deferring creation of such functions until after the view recreation step in the modify phase.

Changes:

  • Track views being recreated (RequiresRecreate) and defer added functions whose signature references those view composite types.
  • Emit these deferred functions after generateModifyViewsSQL recreates the views (issue #480).
  • Add a regression test case under testdata/diff/dependency/issue_480_view_function_recreate/ asserting correct DDL order across plan outputs.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
internal/diff/diff.go Defers function creation until after recreated views are dropped/recreated; adds recreated-view lookup helper.
testdata/diff/dependency/issue_480_view_function_recreate/plan.txt Golden plan text showing corrected DDL ordering.
testdata/diff/dependency/issue_480_view_function_recreate/plan.sql Golden plan SQL showing corrected DDL ordering.
testdata/diff/dependency/issue_480_view_function_recreate/plan.json Golden plan JSON capturing the corrected step ordering.
testdata/diff/dependency/issue_480_view_function_recreate/old.sql Repro old-state schema (table → view → function).
testdata/diff/dependency/issue_480_view_function_recreate/new.sql Repro new-state schema that forces view recreation and function signature change.
testdata/diff/dependency/issue_480_view_function_recreate/diff.sql Golden diff SQL reflecting the intended execution order.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread internal/diff/diff.go Outdated
Comment thread internal/diff/diff.go
@greptile-apps

greptile-apps Bot commented Jun 22, 2026

Copy link
Copy Markdown

Greptile Summary

This PR fixes a dependency-ordering bug (#480) where apply would fail with cannot drop view ... because other objects depend on it (2BP01) when a function's return/parameter type references a view that both requires recreation (DROP + CREATE) and whose signature also changes (making it a drop+add rather than an in-place CREATE OR REPLACE).

  • Extends the existing function-deferral logic in generateCreateSQL to also check functions against a newly computed recreatedViewLookup (modified views with RequiresRecreate), pulling matching functions out of both the with-view-deps and without-view-deps buckets into a new functionsAwaitingRecreatedViews slice on the diff struct.
  • Emits those deferred functions in generateModifySQL immediately after generateModifyViewsSQL recreates the view, guaranteeing the function is created only once the view's new composite rowtype exists and the old view has no dependents during its RESTRICT drop.
  • Adds a complete test case covering the exact scenario from the issue report, including both a TestDiffFromFiles DDL-order assertion and a TestPlanAndApply end-to-end integration test.

Confidence Score: 5/5

Safe to merge. The change is a targeted fix to function-deferral ordering that follows the existing pattern exactly, and the test case directly reproduces the previously-failing scenario end-to-end.

The new buildRecreatedViewLookup + functionsAwaitingRecreatedViews path is a faithful mirror of the existing buildViewLookup + functionsAwaitingDeferredViews path. A function referencing both a newly-added view and a recreated view is removed from functionsWithViewDeps before the deferred-view logic runs, so it can only land in one bucket. Guards and nil-map handling are consistent with the rest of the file. Both the DDL-ordering unit test and the end-to-end integration test exercise the fix.

No files require special attention.

Important Files Changed

Filename Overview
internal/diff/diff.go Core fix: adds functionsAwaitingRecreatedViews field and buildRecreatedViewLookup helper; defers matching functions from the create phase to the modify phase. Logic mirrors the existing functionsAwaitingDeferredViews pattern.
testdata/diff/dependency/issue_480_view_function_recreate/diff.sql Expected migration DDL showing correct ordering: DROP FUNCTION then ALTER TABLE then DROP VIEW then CREATE VIEW then CREATE FUNCTION.
testdata/diff/dependency/issue_480_view_function_recreate/old.sql Starting state: table without role column, view selecting 3 columns, function with single-parameter signature returning the view rowtype.
testdata/diff/dependency/issue_480_view_function_recreate/new.sql Desired state: table with role column added mid-list, view selecting 4 columns, function with 2-parameter signature forcing drop+add.
testdata/diff/dependency/issue_480_view_function_recreate/plan.json Plan JSON snapshot: 5 steps in correct dependency order, function create step last.

Reviews (1): Last reviewed commit: "fix: order function recreation after dep..." | Re-trigger Greptile

Address review feedback: the functionsAwaitingRecreatedViews bucket can
also hold genuinely new functions (no old definition), so qualify the
"old definition dropped in the drop phase" reasoning to signature-changed
functions.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@tianzhou tianzhou merged commit 6dad56e into main Jun 22, 2026
1 check passed
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