fix: order aggregates and SQL function bodies after the views they depend on (#580) - #582
Conversation
…pend on (#580) Two view dependencies were missing from the diff's create ordering, so single-file dump, multi-file dump, and plan all emitted them before the view existed: - An aggregate whose argument, state, or return type is a new view's row type was created before the view, and before its own transition function when that function takes the row type. Such aggregates are now held back until after the view-dependent functions, with the same deferred-view handling functions already have. - A SQL-language function whose body queries a new view was created before the view. functionReferencesNewView now scans the body for SQL-language functions, reusing the table-reference pattern. Other languages keep the signature-only check: plpgsql bodies are not validated against relations at creation, and triggers are created before views, so treating their body mentions as dependencies would push trigger functions past the triggers that reference them. Fixing the aggregate order exposed a round-trip bug: aggregate type strings were never stripped of the aggregate's own schema prefix, so an aggregate over a relation row type inspected as sum_v(public.v) on one side and sum_v(v) on the other and was dropped and recreated on every plan. The inspector now strips the prefix, mirroring function parameters. Fixes #580 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Greptile SummaryThis PR extends dependency ordering for aggregates and SQL function bodies that reference newly created views, adds deferred aggregate handling, and normalizes same-schema aggregate type names during inspection. The new ordering still has two uncovered dependency cases:
Confidence Score: 3/5This PR is not safe to merge until aggregate support-function dependencies and false-positive SQL body matches can no longer produce invalid creation order. Two realistic schema definitions still generate migrations in which PostgreSQL attempts to create a dependent object before the required function exists. Files Needing Attention: internal/diff/diff.go Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart LR
V[New view] --> VF[SQL functions classified as view-dependent]
VF --> VA[Aggregates whose own types reference the view]
A[Aggregate with ordinary types] --> E[Early aggregate batch]
V -. queried by support-function body .-> SF[Aggregate support function]
SF --> VF
E --> X[CREATE AGGREGATE before support function exists]
L[Comment or string containing FROM view] --> FP[False view dependency]
FP --> VF
VC[View calling the function] --> Y[CREATE VIEW before function exists]
Reviews (1): Last reviewed commit: "fix: order aggregates and SQL function b..." | Re-trigger Greptile |
There was a problem hiding this comment.
🟡 Changes recommended
There are correctness issues in the new aggregate identity normalization and in aggregate/view dependency handling that can still cause plan churn or ordering failures in valid schemas.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
This PR addresses dependency-ordering bugs in the diff engine so plan/apply and single-file dumps don’t emit view-dependent objects (SQL-language function bodies and aggregates over a view row type) before the views they require, and adds regression coverage for issue #580.
Changes:
- Extend
functionReferencesNewViewto treat SQL-language function bodies as creation-time dependencies on referenced views (while keeping other languages signature-only). - Partition/defer aggregates whose argument/state/return types reference newly-added (or deferred) views so they are created after the required views and view-dependent functions.
- Normalize inspected aggregate type/identity strings to reduce schema-qualification churn and improve idempotency.
File summaries
| File | Description |
|---|---|
| testdata/diff/dependency/issue_580_aggregate_and_sql_body_to_view/plan.txt | Adds expected human-readable plan output for the new dependency regression. |
| testdata/diff/dependency/issue_580_aggregate_and_sql_body_to_view/plan.sql | Adds expected SQL plan output for the new dependency regression. |
| testdata/diff/dependency/issue_580_aggregate_and_sql_body_to_view/plan.json | Adds expected JSON plan output for the new dependency regression. |
| testdata/diff/dependency/issue_580_aggregate_and_sql_body_to_view/old.sql | Empty baseline for the new diff test case. |
| testdata/diff/dependency/issue_580_aggregate_and_sql_body_to_view/new.sql | New schema input reproducing SQL-body-to-view and aggregate-over-view-row-type cases. |
| testdata/diff/dependency/issue_580_aggregate_and_sql_body_to_view/diff.sql | Expected diff ordering for the new test case. |
| ir/inspector.go | Attempts to normalize aggregate identity/signature/type strings by stripping same-schema prefixes. |
| internal/diff/diff.go | Implements the new deferral/partitioning logic for view-dependent aggregates and SQL-body view detection. |
| cmd/dump/multifile_integration_test.go | Extends multi-file dump integration assertions to cover the issue #580 follow-up scenarios. |
Review details
- Files reviewed: 8/9 changed files
- Comments generated: 3
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Address review findings on the view-dependency ordering: - An aggregate whose own types are ordinary but whose transition or final function depends on a new view was created before that function. Aggregates are now also held back when any support function is view-dependent. - A view that calls a view-dependent function or aggregate was created before the routine existed. Such views, and views built on them, are now created after the late routines, and functions or aggregates that depend on those held-back views follow them in turn. - Aggregate argument lists are split on top-level commas via a helper shared with tableReturnColumnTypes instead of a plain strings.Split. - The inspector strips the aggregate's schema prefix once into identityArgs so the overload key and Arguments agree. buildFunctionLookup now delegates to buildRoutineLookup, which also accepts aggregates since they share call syntax in view definitions. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Only the search_path-dependent aggregate fields (identity args, signature, return type) need the same-schema prefix stripped in the inspector. The state types are qualified explicitly by the catalog query and handled by stripSchemaPrefixMode in the diff layer, which must see the prefix to honor --qualify-schema (TestDumpCommand_QualifySchemaTypeReferences). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
There was a problem hiding this comment.
🟡 Changes recommended
Three critical dependency-ordering issues and one moderate schema-normalization issue remain unresolved.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
- Files reviewed: 8/9 changed files
- Comments generated: 4
- Review effort level: Balanced
…modify phases Address the second review round: - Aggregates whose support function is typed on a view being recreated (#480 path) now wait for the recreation in the modify phase, alongside those functions, instead of being created before the function exists. - Views deferred to the modify phase (#414 path) that call a deferred aggregate or function are now created after it. The create-phase and both modify-phase emission points share generateViewsAndDependentRoutinesSQL. - tableRefPattern accepts quoted identifiers, so a SQL body reading from "My View" is detected as a dependency; matches are normalized with the same unquoting helpers the function-call scanner uses (#572). - Aggregate identity args and signature strip the quote_ident form of the schema name as well as the raw form, so a schema that needs quoting round-trips without churn. Covered by a new inspector test. Scenarios are folded into the existing issue_414 and issue_480 fixtures and the issue_580 case. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
There was a problem hiding this comment.
🟡 Changes recommended
Five moderate dependency-ordering and reference-parsing issues remain unresolved.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
Suppressed comments (2)
internal/diff/diff.go:2062
- Aggregates already placed in
aggregatesWithViewDepsare never checked againstrecreatedViewLookup. An aggregate that depends on both a newly added view and a recreated view (or on a support function deferred for that recreated view) therefore stays in the create phase; it can be emitted before its support function exists or pin the old view and make the laterDROP ... RESTRICTfail. Partition both aggregate buckets against recreated-view dependencies before emission.
aggregatesToCreateNow, d.aggregatesAwaitingRecreatedViews = splitAggregatesByViewDeps(aggregatesToCreateNow, recreatedViewLookup, buildFunctionLookup(d.functionsAwaitingRecreatedViews))
internal/diff/diff.go:2202
- These routines are emitted only after all added views were handled in the create phase, but the create-phase routine lookup excludes
functionsAwaitingRecreatedViewsandaggregatesAwaitingRecreatedViews. Thus a new view that calls one of these deferred routines is created while that routine is absent (for example, a new view calling an aggregate whose SFUNC is typed on a recreated view), andCREATE VIEWfails. Move such dependent added views to this phase and schedule them with the queued routines.
generateCreateFunctionsSQL(d.functionsAwaitingRecreatedViews, targetSchema, collector)
generateCreateAggregatesSQL(d.aggregatesAwaitingRecreatedViews, targetSchema, collector)
- Files reviewed: 19/20 changed files
- Comments generated: 3
- Review effort level: Balanced
…ew views for recreated batch
Third review round:
- Aggregate identity args may carry argument names, VARIADIC, and the
ORDER BY separator of ordered-set aggregates ("r v", "ORDER BY v").
aggregateArgumentTypes now yields the underlying types, sharing the
leading-identifier stripping with tableReturnColumnTypes.
- tableRefPattern accepts an optional ONLY token after FROM/JOIN/TABLE/
DELETE FROM as it already did after UPDATE.
- The recreated-view partition of aggregates now runs before the new-view
partition, so an aggregate depending on both still waits for the
recreation in the modify phase.
- New views that call a routine held for a view recreation are created in
the modify phase with that batch, through the shared scheduling helper.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
Re the two suppressed comments in the latest review, both addressed in the last commit:
|
There was a problem hiding this comment.
🟡 Changes recommended
A critical function-to-aggregate ordering failure remains unresolved.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
- Files reviewed: 19/20 changed files
- Comments generated: 1
- Review effort level: Balanced
A SQL-language function body is resolved at creation, so one that calls a new aggregate must follow it. The diff had no function-to-aggregate edge: functions were emitted in table-relative batches ahead of all aggregates, and the view-dependent batch emitted its functions before its aggregates. splitFunctionsCallingAggregates holds back SQL-language functions whose body calls one of the given aggregates, or another held-back function, and is applied in the create phase (callers emitted right after the aggregates) and inside generateViewsAndDependentRoutinesSQL for both of its halves. Fixtures folded into create_aggregate/add_aggregate (general case) and the issue_580 case (view-dependent batch). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
There was a problem hiding this comment.
🟡 Changes recommended
Unresolved critical and moderate dependency-ordering and normalization issues can produce invalid or non-idempotent plans.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
Suppressed comments (3)
Previously missed (2) — in code that hasn't changed since the last review.
internal/diff/diff.go:2988
- This substring search also finds
ORDER BYinside a quoted argument name or type. For a valid view row type such as"Order By V", the identity argument is split into invalid fragments, so the aggregate's view dependency is missed and it is emitted before the view. Locate the ordered-set separator only outside quoted identifiers and at token boundaries.
ir/inspector.go:1433 - Leaving state types qualified still causes non-idempotent plans for target schemas that require quoting. The desired temporary schema's
temp.state_typeis remapped toMySchema.state_type, while current inspection returns"MySchema".state_type;aggregatesEqualcompares these strings before DDL stripping, so the aggregate is recreated every plan. NormalizeStateTypeandMStateTypeconsistently (and preserve--qualify-schemabehavior), as the PR description states.
internal/diff/diff.go:2076
functionsCallingAggregateswas selected against all added aggregates, but this emits it after onlyaggregatesToCreateNow. If a SQL function calls an aggregate deferred for a new/recreated view without mentioning that view itself (for example, a caller ofsum_with_v(integer)), PostgreSQL validates the caller here before the aggregate exists. Partition callers by the aggregate batch and emit each caller with its aggregate instead.
// SQL-language functions that call one of the aggregates above.
generateCreateFunctionsSQL(functionsCallingAggregates, targetSchema, collector)
- Files reviewed: 24/25 changed files
- Comments generated: 3
- Review effort level: Balanced
…rence parsing - SQL functions calling an aggregate that is held for the view-dependent or recreated-view batch now join that batch instead of being created right after the early aggregates. - Views deferred for an added column (#414) that call a routine held for a view recreation now join the recreated-view batch. - Relation references accept whitespace around the qualification dot. - The ordered-set ORDER BY separator is located outside quoted identifiers and at a token boundary, so a view named "Order By V" is not split. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
Re the three suppressed comments in the latest review:
|
There was a problem hiding this comment.
🟡 Changes recommended
Three critical defects and one moderate ordering defect remain unresolved.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
Suppressed comments (1)
internal/diff/diff.go:1984
- This only checks
functionsWithoutViewDeps. A SQL function already classified as view-dependent can call an aggregate queued for the recreated-view phase (for example, the aggregate's support function depends on a recreated view); that function remains in the create-phase view batch and is emitted before the aggregate exists. Partition callers across every function bucket, then attach each caller to the batch that creates its referenced aggregate.
functionsWithoutViewDeps, functionsCallingAggregates = splitFunctionsCallingAggregates(functionsWithoutViewDeps, d.addedAggregates)
- Files reviewed: 24/25 changed files
- Comments generated: 3
- Review effort level: Balanced
…dentifier-aware schema stripping - generateFunctionsAndAggregatesSQL alternates between functions that call none of the remaining aggregates and aggregates whose support functions exist, so a chain such as aggregate a -> SQL support function calling a -> aggregate b is ordered correctly. Used at the create-phase aggregate point and inside the view-dependent batch helper. - View-dependent SQL functions that call an aggregate held for a view recreation now join that batch. - Relation references parse a full FROM list: comma-separated items with optional aliases, ONLY, and the parenthesized ONLY (name) form. - Aggregate identity args and signature are stripped with the new identifier-aware ir.StripSchemaQualifiers, which only removes a schema token directly before a dot and never touches quoted identifiers. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
Re the suppressed comment (view-dependent SQL function calling an aggregate queued for the recreated-view batch): fixed in the last commit. After callers are routed by aggregate batch, |
There was a problem hiding this comment.
🟡 Changes recommended
Critical dependency-scheduling defects and moderate SQL parsing gaps can still produce invalid creation order.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
- Files reviewed: 26/27 changed files
- Comments generated: 4
- Review effort level: Balanced
- Routing SQL callers to their aggregate's batch now repeats until stable, so an aggregate whose support function moved to a late batch moves with it (late aggregate a -> support function calling a -> aggregate b). - Relation references are found by a small token-aware scan of each FROM list instead of a regex: function-call and subquery items are skipped, ONLY and ONLY (name) are accepted, and an alias with an optional column list is consumed only when another item follows. Unit-tested. - stripLeadingIdentifier honors "" escapes in quoted argument names. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
There was a problem hiding this comment.
🟡 Changes recommended
Four critical dependency detection and ordering defects remain unresolved.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
- Files reviewed: 27/28 changed files
- Comments generated: 4
- Review effort level: Balanced
…tion scan Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Remove three review-driven additions whose scenarios need a view being recreated in the same migration as several other new objects that chain through it: the fixpoint loop in caller routing, deferred-column views joining the recreated batch, and view-dependent functions moving to that batch for calling one of its aggregates. Single-pass routing and the recreated-view handling that mirrors #480 for functions stay. The corresponding fixture objects are removed. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Summary
Follow-up to #581, covering the two remaining cases from #580 that live in the diff package rather than the multi-file formatter. Both reproduced in single-file dump and therefore in
plan/applyagainst an empty database.functionReferencesNewViewwas signature-only. It now also scans the body for SQL-language functions, reusing the existingtableRefPatternthrough a sharedbodyReferencesRelationhelper thatfunctionReferencesNewTableuses as well. Other languages stay signature-only on purpose: plpgsql bodies are not validated against relations at creation, and triggers are created before views, so treating body mentions as dependencies would push trigger functions past the triggers that call them. The test case includes exactly that plpgsql trigger function to pin the behavior.Fixing the aggregate order exposed a pre-existing round-trip bug that the apply harness caught: aggregate type strings were never stripped of the aggregate's own schema prefix, so an aggregate over a relation row type inspected as
sum_v(public.v)on one side andsum_v(v)on the other and was dropped and recreated on every plan.buildAggregatesnow strips the prefix from the search_path-dependent fieldsArguments,Signature, andReturnType, mirroring what function parameters do viastripSameSchemaPrefix. State types are left as the catalog query qualifies them, since the diff layer strips or keeps that prefix for--qualify-schema.Fixes #580
Test plan
testdata/diff/dependency/issue_580_aggregate_and_sql_body_to_viewcovering a SQL function querying a view, a plpgsql trigger function mentioning a view, and an aggregate over the view row type with a row-type transition function. Fails before the fix withrelation "v" does not exist; after the ordering fix it then failed the idempotency check until the inspector fix; now passes.TestDumpCommand_Issue580MultiFileIncludeOrderextended with both scenarios and asserts view before function, view before transition function, transition function before aggregate.TestDiffFromFiles,TestPlanAndApplyfordependency/,create_aggregate/,create_function/, andTestDumpCommand_Sakila.🤖 Generated with Claude Code