fix: drop triggers on dropped tables before their functions (#505)#511
Conversation
When a table with a trigger and the trigger's function are both being
dropped, pgschema emitted DROP FUNCTION before the trigger was removed,
causing SQLSTATE 2BP01 ("cannot drop function because other objects
depend on it").
Emit DROP TRIGGER for triggers on dropped tables and views before the
function drop phase, mirroring what already existed for modified tables.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR fixes dependency-ordering during drops by ensuring triggers on relations that are being dropped are explicitly dropped before the function-drop phase, preventing SQLSTATE 2BP01 when a trigger function would otherwise still be referenced.
Changes:
- Emit
DROP TRIGGERstatements for triggers on dropped tables beforeDROP FUNCTION. - Emit
DROP TRIGGERstatements for triggers on dropped views (skipping pre-dropped views) beforeDROP FUNCTION. - Add a regression fixture for dropping a table with a trigger + its trigger function.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| internal/diff/trigger.go | Adds trigger-drop generation for dropped tables/views (pre-function drop ordering). |
| internal/diff/diff.go | Wires the new trigger-drop generation into the drop phase ordering. |
| testdata/diff/dependency/issue_505_drop_trigger_function_with_table/plan.txt | New expected plan output showing trigger dropped before function/table. |
| testdata/diff/dependency/issue_505_drop_trigger_function_with_table/plan.sql | New expected SQL plan statements for the regression scenario. |
| testdata/diff/dependency/issue_505_drop_trigger_function_with_table/plan.json | New expected JSON plan output including trigger-drop step ordering. |
| testdata/diff/dependency/issue_505_drop_trigger_function_with_table/old.sql | Schema setup with table + trigger + trigger function. |
| testdata/diff/dependency/issue_505_drop_trigger_function_with_table/new.sql | Target schema (empty) to force drops. |
| testdata/diff/dependency/issue_505_drop_trigger_function_with_table/diff.sql | Expected DDL diff showing trigger dropped before function/table. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Greptile SummaryThis PR fixes issue #505 by explicitly emitting
Confidence Score: 4/5Safe to merge — the fix is minimal, correctly positioned, and verified by a passing integration test; the only concerns are a non-stable sort for the rare case of same-named triggers across multiple dropped tables, and a missing trailing newline in one test fixture. The core logic change is straightforward and well-tested. The sort-by-name-only ordering in both new functions is technically non-deterministic when multiple dropped tables share a trigger name, which could surface as flaky plan output in that edge case. The missing trailing newline in plan.txt is inconsistent with all other plan.txt fixtures and could cause test comparison failures if the fixture is regenerated. internal/diff/trigger.go — sort comparator in both new functions; testdata/diff/dependency/issue_505_drop_trigger_function_with_table/plan.txt — missing trailing newline Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[generateDropSQL] --> B[REVOKE privileges]
B --> C[generateDropTriggersFromModifiedTables]
C --> D[generateDropTriggersFromModifiedViews]
D --> E["generateDropTriggersFromDroppedTables NEW"]
E --> F["generateDropTriggersFromDroppedViews NEW"]
F --> G[generateDropAggregatesSQL]
G --> H[generateDropFunctionsSQL]
H --> I[generateDropProceduresSQL]
I --> J[generateDropViewsSQL]
J --> K[generateDropTablesSQL]
style E fill:#d4edda,stroke:#28a745
style F fill:#d4edda,stroke:#28a745
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
A[generateDropSQL] --> B[REVOKE privileges]
B --> C[generateDropTriggersFromModifiedTables]
C --> D[generateDropTriggersFromModifiedViews]
D --> E["generateDropTriggersFromDroppedTables NEW"]
E --> F["generateDropTriggersFromDroppedViews NEW"]
F --> G[generateDropAggregatesSQL]
G --> H[generateDropFunctionsSQL]
H --> I[generateDropProceduresSQL]
I --> J[generateDropViewsSQL]
J --> K[generateDropTablesSQL]
style E fill:#d4edda,stroke:#28a745
style F fill:#d4edda,stroke:#28a745
Reviews (1): Last reviewed commit: "fix: drop triggers on dropped tables bef..." | Re-trigger Greptile |
|
Re-checked the current head with green CI. Verdict: ready to merge. The remaining Greptile notes are non-blocking polish, not correctness issues in the shipped fix:
From the reviewer side, I would merge this as-is and treat any sort-key tightening or newline cleanup as optional follow-up. |
Sort dropped triggers by table name then trigger name, so output is stable when multiple dropped tables have same-named triggers. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Summary
Fixes #505.
When a table with a trigger and the trigger's function are both being dropped, pgschema emitted
DROP FUNCTIONbefore the trigger was removed, causingSQLSTATE 2BP01("cannot drop function because other objects depend on it").generateDropSQLalready emittedDROP TRIGGERfor triggers on modified tables/views before dropping functionsDROP TABLE CASCADE, which comes afterDROP FUNCTIONgenerateDropTriggersFromDroppedTablesandgenerateDropTriggersFromDroppedViewsto emitDROP TRIGGERfor all triggers on dropped tables/views before the function drop phaseTest plan
dependency/issue_505_drop_trigger_function_with_table— drops a table with trigger + its trigger functiondependency/function_to_triggerstill passes (modified-table path unchanged)🤖 Generated with Claude Code