Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions internal/migration_acceptance_tests/function_cases_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
24 changes: 23 additions & 1 deletion pkg/diff/function_sql_vertex_generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
})
}

Expand Down Expand Up @@ -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:
Expand All @@ -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
}
2 changes: 1 addition & 1 deletion pkg/diff/sql_generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down