Skip to content

Comments

fix: defer functions with view dependencies to after view creation (#300)#302

Merged
tianzhou merged 1 commit intomainfrom
fix/issue-300-ddl-dependency-ordering
Feb 20, 2026
Merged

fix: defer functions with view dependencies to after view creation (#300)#302
tianzhou merged 1 commit intomainfrom
fix/issue-300-ddl-dependency-ordering

Conversation

@tianzhou
Copy link
Contributor

Summary

  • Functions that reference views in their return type (e.g., RETURNS SETOF view_name) or parameter types were being created before the referenced views, causing type does not exist errors during pgschema apply
  • Split function creation into two phases: functions without view dependencies are created before views (existing behavior), and functions with view dependencies are created after views
  • Fixed return type normalization to strip same-schema qualifiers from SETOF types (e.g., SETOF public.actorSETOF actor) for consistent comparison across different search_path contexts

Fixes #300

Test plan

  • Added testdata/diff/dependency/issue_300_function_table_composite_type/ — tests a function using a table composite type parameter and a view return type
  • Added testdata/diff/dependency/issue_300_view_depends_on_view/ — confirms view-to-view dependency ordering works correctly (already handled by existing topological sort)
  • Run: PGSCHEMA_TEST_FILTER="dependency/" go test -v ./cmd -run TestPlanAndApply — all 12 dependency tests pass
  • Run: PGSCHEMA_TEST_FILTER="create_function/" go test -v ./cmd -run TestPlanAndApply — all function tests pass
  • Run: PGSCHEMA_TEST_FILTER="create_view/" go test -v ./cmd -run TestPlanAndApply — all view tests pass

🤖 Generated with Claude Code

)

Functions that reference views in their return type (e.g., RETURNS SETOF
view_name) or parameter types were created before the referenced views,
causing "type does not exist" errors during apply.

Split function creation into two phases: functions without view
dependencies are created before views (existing behavior), and functions
with view dependencies are created after views. Also fix return type
normalization to strip same-schema qualifiers from SETOF types for
consistent comparison across different search_path contexts.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings February 20, 2026 06:50
@greptile-apps
Copy link

greptile-apps bot commented Feb 20, 2026

Greptile Summary

This PR fixes a dependency ordering issue where functions referencing views in their return types or parameters were being created before the referenced views, causing type does not exist errors.

Key changes:

  • Split function creation into two phases in internal/diff/diff.go:1547-1595 - functions without view dependencies are created before views, and functions with view dependencies are created after views
  • Added helper functions (buildViewLookup, functionReferencesNewView, extractBaseTypeName, typeMatchesLookup) to detect when functions reference views in their signatures
  • Fixed return type normalization in ir/normalize.go:297-300 to strip same-schema qualifiers from types (e.g., SETOF public.actorSETOF actor) for consistent comparison across different search_path contexts
  • Refactored buildFunctionLookup to use a shared buildSchemaNameLookup helper for code reuse

Test coverage:

  • issue_300_function_table_composite_type/ validates functions using both table composite types and view return types
  • issue_300_view_depends_on_view/ confirms view-to-view dependency ordering continues to work correctly

Confidence Score: 5/5

  • This PR is safe to merge with minimal risk
  • The implementation is well-designed with proper dependency analysis, comprehensive test coverage for both the main fix and edge cases, and follows the existing codebase patterns. The change is isolated to dependency ordering logic with no breaking changes to existing functionality.
  • No files require special attention

Important Files Changed

Filename Overview
internal/diff/diff.go Implements two-phase function creation to defer functions with view dependencies until after views are created; adds helper functions for view lookup and dependency detection
ir/normalize.go Adds schema qualifier stripping from function return types to ensure consistent comparison across different search_path contexts
testdata/diff/dependency/issue_300_function_table_composite_type/new.sql Test input: defines function using table composite type parameter and view return type
testdata/diff/dependency/issue_300_view_depends_on_view/new.sql Test input: defines views with dependencies to confirm existing topological sort handles them

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    Start[Generate DDL Migration] --> BuildLookup[Build view lookup map]
    BuildLookup --> SeparateFns[Separate functions by view dependencies]
    
    SeparateFns --> CheckDep{Function references<br/>new view?}
    CheckDep -->|Yes| FnWithDeps[functionsWithViewDeps]
    CheckDep -->|No| FnWithoutDeps[functionsWithoutViewDeps]
    
    FnWithoutDeps --> CreateTables[Create tables]
    CreateTables --> CreateFnsPhase1[Create functions WITHOUT view deps]
    CreateFnsPhase1 --> CreateDomains[Create domains]
    CreateDomains --> CreateProcs[Create procedures]
    CreateProcs --> CreateViews[Create views]
    
    FnWithDeps --> WaitForViews[Wait for views...]
    CreateViews --> CreateFnsPhase2[Create functions WITH view deps]
    WaitForViews -.-> CreateFnsPhase2
    
    CreateFnsPhase2 --> CreatePrivs[Create privileges]
    CreatePrivs --> Done[Migration complete]
    
    style CheckDep fill:#e1f5ff
    style CreateViews fill:#d4edda
    style CreateFnsPhase2 fill:#d4edda
Loading

Last reviewed commit: ade173c

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This pull request addresses issue #300 by fixing dependency ordering problems when creating functions that reference views in their return types or parameters. The fix implements a two-phase function creation strategy: functions without view dependencies are created before views (preserving existing behavior for table composite types), while functions with view dependencies are deferred until after views are created.

Changes:

  • Split function creation into two phases based on view dependencies to ensure proper DDL ordering
  • Added return type normalization to strip same-schema qualifiers from SETOF types for consistent comparison
  • Introduced helper functions to detect view dependencies in function signatures

Reviewed changes

Copilot reviewed 14 out of 14 changed files in this pull request and generated 3 comments.

File Description
ir/normalize.go Added stripSchemaFromReturnType and stripSchemaPrefix functions to normalize return types by removing same-schema qualifiers
internal/diff/diff.go Implemented two-phase function creation logic, refactored lookup building into shared buildSchemaNameLookup, added view dependency detection
testdata/diff/dependency/issue_300_view_depends_on_view/* Test case verifying view-to-view dependency ordering (already handled by topological sort)
testdata/diff/dependency/issue_300_function_table_composite_type/* Test case verifying functions with table composite types and view return types are created in correct order

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

}

// Check parameter types
for _, param := range fn.Parameters {
Copy link

Copilot AI Feb 20, 2026

Choose a reason for hiding this comment

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

Missing nil check for parameter. If a parameter in fn.Parameters is nil, accessing param.DataType will cause a panic. Consider adding a nil check before accessing param.DataType.

Suggested change
for _, param := range fn.Parameters {
for _, param := range fn.Parameters {
if param == nil {
continue
}

Copilot uses AI. Check for mistakes.
Comment on lines +1877 to +1879
names := make([]struct{ schema, name string }, len(functions))
for i, fn := range functions {
names[i] = struct{ schema, name string }{fn.Schema, fn.Name}
Copy link

Copilot AI Feb 20, 2026

Choose a reason for hiding this comment

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

The refactored buildFunctionLookup has lost the nil check that existed in the previous implementation. The old code checked if fn == nil || fn.Name == "" before accessing fn.Name. While the current implementation may work if functions are never nil in practice, this removes a defensive programming safeguard that was previously in place. Consider adding a nil check in buildSchemaNameLookup or in the callers before constructing the names slice.

Suggested change
names := make([]struct{ schema, name string }, len(functions))
for i, fn := range functions {
names[i] = struct{ schema, name string }{fn.Schema, fn.Name}
names := make([]struct{ schema, name string }, 0, len(functions))
for _, fn := range functions {
if fn == nil || fn.Name == "" {
continue
}
names = append(names, struct{ schema, name string }{fn.Schema, fn.Name})

Copilot uses AI. Check for mistakes.
Comment on lines +1884 to +1891
// buildViewLookup returns case-insensitive lookup keys for newly added views.
func buildViewLookup(views []*ir.View) map[string]struct{} {
names := make([]struct{ schema, name string }, len(views))
for i, v := range views {
names[i] = struct{ schema, name string }{v.Schema, v.Name}
}
return buildSchemaNameLookup(names)
}
Copy link

Copilot AI Feb 20, 2026

Choose a reason for hiding this comment

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

Similar to buildFunctionLookup, this function should handle potential nil views defensively. Consider adding a nil check before accessing v.Schema and v.Name, or ensure that views in the slice are never nil.

Copilot uses AI. Check for mistakes.
@tianzhou tianzhou merged commit bd9a3cf into main Feb 20, 2026
6 checks 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.

DDL dependency issues

1 participant