diff --git a/internal/migration_acceptance_tests/view_cases_test.go b/internal/migration_acceptance_tests/view_cases_test.go index 7ca196b..909399d 100644 --- a/internal/migration_acceptance_tests/view_cases_test.go +++ b/internal/migration_acceptance_tests/view_cases_test.go @@ -543,6 +543,105 @@ var viewAcceptanceTestCases = []acceptanceTestCase{ `, }, }, + { + name: "Recreate view due to dependent column type change", + oldSchemaDDL: []string{ + ` + CREATE TABLE foobar( + id INT PRIMARY KEY, + foo VARCHAR(255), + price NUMERIC(10,2) + ); + + CREATE VIEW foobar_view AS + SELECT id, foo, price + FROM foobar; + `, + }, + newSchemaDDL: []string{ + ` + CREATE TABLE foobar( + id INT PRIMARY KEY, + foo VARCHAR(255), + price INTEGER + ); + + CREATE VIEW foobar_view AS + SELECT id, foo, price + FROM foobar; + `, + }, + expectedHazardTypes: []diff.MigrationHazardType{ + diff.MigrationHazardTypeAcquiresAccessExclusiveLock, + diff.MigrationHazardTypeImpactsDatabasePerformance, + }, + }, + { + name: "Recreate view due to multiple dependent column type changes", + oldSchemaDDL: []string{ + ` + CREATE TABLE plans( + id INT PRIMARY KEY, + annual_fee NUMERIC(10,2), + monthly_fee NUMERIC(10,2) + ); + + CREATE VIEW plans_view AS + SELECT id, annual_fee, monthly_fee + FROM plans; + `, + }, + newSchemaDDL: []string{ + ` + CREATE TABLE plans( + id INT PRIMARY KEY, + annual_fee INTEGER, + monthly_fee INTEGER + ); + + CREATE VIEW plans_view AS + SELECT id, annual_fee, monthly_fee + FROM plans; + `, + }, + expectedHazardTypes: []diff.MigrationHazardType{ + diff.MigrationHazardTypeAcquiresAccessExclusiveLock, + diff.MigrationHazardTypeImpactsDatabasePerformance, + }, + }, + { + name: "No view recreate when non-dependent column type changes", + oldSchemaDDL: []string{ + ` + CREATE TABLE foobar( + id INT PRIMARY KEY, + foo VARCHAR(255), + bar NUMERIC(10,2) + ); + + CREATE VIEW foobar_view AS + SELECT id, foo + FROM foobar; + `, + }, + newSchemaDDL: []string{ + ` + CREATE TABLE foobar( + id INT PRIMARY KEY, + foo VARCHAR(255), + bar INTEGER + ); + + CREATE VIEW foobar_view AS + SELECT id, foo + FROM foobar; + `, + }, + expectedHazardTypes: []diff.MigrationHazardType{ + diff.MigrationHazardTypeAcquiresAccessExclusiveLock, + diff.MigrationHazardTypeImpactsDatabasePerformance, + }, + }, } func TestViewTestCases(t *testing.T) { diff --git a/pkg/diff/view_sql_generator.go b/pkg/diff/view_sql_generator.go index d96f28d..0bb419a 100644 --- a/pkg/diff/view_sql_generator.go +++ b/pkg/diff/view_sql_generator.go @@ -54,6 +54,17 @@ func buildViewDiff( return viewDiff{}, true, nil } } + // Also recreate if a dependent column's type is being changed. + // ALTER TABLE ... ALTER COLUMN ... SET DATA TYPE fails when a view depends + // on that column; the view must be dropped first and recreated after. + alteredColumnDiffsByName := buildDiffByNameMap[schema.Column, columnDiff](td.columnsDiff.alters) + for _, c := range t.Columns { + if colDiff, ok := alteredColumnDiffsByName[c]; ok { + if !strings.EqualFold(colDiff.old.Type, colDiff.new.Type) { + return viewDiff{}, true, nil + } + } + } } // Recreate if the view SQL generator cannot alter the view.