From b277c7b12984bef11a6970417b9e7ff6d368d9fb Mon Sep 17 00:00:00 2001 From: dilame Date: Wed, 13 May 2026 12:17:45 +0800 Subject: [PATCH] fix: order sql functions after relations --- .../function_cases_test.go | 14 +++++++++++ pkg/diff/function_sql_vertex_generator.go | 24 ++++++++++++++++++- pkg/diff/sql_generator.go | 2 +- 3 files changed, 38 insertions(+), 2 deletions(-) diff --git a/internal/migration_acceptance_tests/function_cases_test.go b/internal/migration_acceptance_tests/function_cases_test.go index 61a69ef..45d140a 100644 --- a/internal/migration_acceptance_tests/function_cases_test.go +++ b/internal/migration_acceptance_tests/function_cases_test.go @@ -72,6 +72,20 @@ var functionAcceptanceTestCases = []acceptanceTestCase{ `, }, }, + { + name: "Create sql function after table referenced by body", + oldSchemaDDL: nil, + newSchemaDDL: []string{ + ` + CREATE SCHEMA app; + CREATE TABLE app.dead_letter(id int); + CREATE FUNCTION app.record_dead_letter() + RETURNS void + LANGUAGE sql + AS $$ INSERT INTO app.dead_letter(id) VALUES (1) $$; + `, + }, + }, { name: "Create functions with quoted names (with conflicting names)", oldSchemaDDL: nil, diff --git a/pkg/diff/function_sql_vertex_generator.go b/pkg/diff/function_sql_vertex_generator.go index 088ff6a..21fe8a9 100644 --- a/pkg/diff/function_sql_vertex_generator.go +++ b/pkg/diff/function_sql_vertex_generator.go @@ -11,11 +11,13 @@ type functionSQLVertexGenerator struct { // functionsInNewSchemaByName is a map of function name to functions in the new schema. // These functions are not necessarily new functionsInNewSchemaByName map[string]schema.Function + newSchema schema.Schema } -func newFunctionSqlVertexGenerator(functionsInNewSchemaByName map[string]schema.Function) sqlVertexGenerator[schema.Function, functionDiff] { +func newFunctionSqlVertexGenerator(functionsInNewSchemaByName map[string]schema.Function, newSchema schema.Schema) sqlVertexGenerator[schema.Function, functionDiff] { return legacyToNewSqlVertexGenerator[schema.Function, functionDiff](&functionSQLVertexGenerator{ functionsInNewSchemaByName: functionsInNewSchemaByName, + newSchema: newSchema, }) } @@ -86,6 +88,9 @@ func (f *functionSQLVertexGenerator) GetAddAlterDependencies(newFunction, oldFun for _, depFunction := range newFunction.DependsOnFunctions { deps = append(deps, mustRun(f.GetSQLVertexId(newFunction, diffTypeAddAlter)).after(buildFunctionVertexId(depFunction, diffTypeAddAlter))) } + if canFunctionDependenciesBeTracked(newFunction) { + deps = append(deps, f.getRelationAddAlterDependencies(newFunction)...) + } if !cmp.Equal(oldFunction, schema.Function{}) { // If the function is being altered: @@ -106,3 +111,20 @@ func (f *functionSQLVertexGenerator) GetDeleteDependencies(function schema.Funct } return deps, nil } + +func (f *functionSQLVertexGenerator) getRelationAddAlterDependencies(function schema.Function) []dependency { + var deps []dependency + + // SQL functions validate table and sequence references in their body at + // CREATE time, but PostgreSQL does not expose those body relation references + // as pg_proc -> pg_class dependencies in pg_depend. Keep this deliberately + // broad, mirroring the procedure generator's best-effort relation ordering. + for _, table := range f.newSchema.Tables { + deps = append(deps, mustRun(f.GetSQLVertexId(function, diffTypeAddAlter)).after(buildTableVertexId(table.SchemaQualifiedName, diffTypeAddAlter))) + } + for _, seq := range f.newSchema.Sequences { + deps = append(deps, mustRun(f.GetSQLVertexId(function, diffTypeAddAlter)).after(buildSequenceVertexId(seq.SchemaQualifiedName, diffTypeAddAlter))) + } + + return deps +} diff --git a/pkg/diff/sql_generator.go b/pkg/diff/sql_generator.go index 6c31e22..3a1c4d8 100644 --- a/pkg/diff/sql_generator.go +++ b/pkg/diff/sql_generator.go @@ -653,7 +653,7 @@ func (s schemaSQLGenerator) Alter(diff schemaDiff) ([]Statement, error) { } partialGraph = concatPartialGraphs(partialGraph, sequenceOwnershipsPartialGraph) - functionGenerator := newFunctionSqlVertexGenerator(functionsInNewSchemaByName) + functionGenerator := newFunctionSqlVertexGenerator(functionsInNewSchemaByName, diff.new) functionsPartialGraph, err := generatePartialGraph(functionGenerator, diff.functionDiffs) if err != nil { return nil, fmt.Errorf("resolving function diff: %w", err)