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
138 changes: 138 additions & 0 deletions internal/migration_acceptance_tests/index_cases_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -824,6 +824,144 @@ var indexAcceptanceTestCases = []acceptanceTestCase{
diff.MigrationHazardTypeIndexDropped,
},
},
{
name: "Add a deferrable unique constraint",
oldSchemaDDL: []string{
`
CREATE TABLE foobar(
id INT,
rank INT NOT NULL
);
`,
},
newSchemaDDL: []string{
`
CREATE TABLE foobar(
id INT,
rank INT NOT NULL
);
ALTER TABLE foobar ADD CONSTRAINT foobar_id_rank_key UNIQUE (id, rank) DEFERRABLE;
`,
},
expectedHazardTypes: []diff.MigrationHazardType{
diff.MigrationHazardTypeIndexBuild,
},
},
{
name: "Add a deferrable initially deferred unique constraint",
oldSchemaDDL: []string{
`
CREATE TABLE foobar(
id INT,
rank INT NOT NULL
);
`,
},
newSchemaDDL: []string{
`
CREATE TABLE foobar(
id INT,
rank INT NOT NULL
);
ALTER TABLE foobar ADD CONSTRAINT foobar_id_rank_key UNIQUE (id, rank) DEFERRABLE INITIALLY DEFERRED;
`,
},
expectedHazardTypes: []diff.MigrationHazardType{
diff.MigrationHazardTypeIndexBuild,
},
},
{
name: "No-op with deferrable unique constraint",
oldSchemaDDL: []string{
`
CREATE TABLE foobar(
id INT,
rank INT NOT NULL
);
ALTER TABLE foobar ADD CONSTRAINT foobar_id_rank_key UNIQUE (id, rank) DEFERRABLE;
`,
},
newSchemaDDL: []string{
`
CREATE TABLE foobar(
id INT,
rank INT NOT NULL
);
ALTER TABLE foobar ADD CONSTRAINT foobar_id_rank_key UNIQUE (id, rank) DEFERRABLE;
`,
},
},
{
name: "No-op with deferrable initially deferred unique constraint",
oldSchemaDDL: []string{
`
CREATE TABLE foobar(
id INT,
rank INT NOT NULL
);
ALTER TABLE foobar ADD CONSTRAINT foobar_id_rank_key UNIQUE (id, rank) DEFERRABLE INITIALLY DEFERRED;
`,
},
newSchemaDDL: []string{
`
CREATE TABLE foobar(
id INT,
rank INT NOT NULL
);
ALTER TABLE foobar ADD CONSTRAINT foobar_id_rank_key UNIQUE (id, rank) DEFERRABLE INITIALLY DEFERRED;
`,
},
},
{
name: "Change unique constraint from not deferrable to deferrable",
oldSchemaDDL: []string{
`
CREATE TABLE foobar(
id INT,
rank INT NOT NULL
);
ALTER TABLE foobar ADD CONSTRAINT foobar_id_rank_key UNIQUE (id, rank);
`,
},
newSchemaDDL: []string{
`
CREATE TABLE foobar(
id INT,
rank INT NOT NULL
);
ALTER TABLE foobar ADD CONSTRAINT foobar_id_rank_key UNIQUE (id, rank) DEFERRABLE;
`,
},
expectedHazardTypes: []diff.MigrationHazardType{
diff.MigrationHazardTypeIndexDropped,
diff.MigrationHazardTypeIndexBuild,
},
},
{
name: "Change unique constraint from deferrable to not deferrable",
oldSchemaDDL: []string{
`
CREATE TABLE foobar(
id INT,
rank INT NOT NULL
);
ALTER TABLE foobar ADD CONSTRAINT foobar_id_rank_key UNIQUE (id, rank) DEFERRABLE;
`,
},
newSchemaDDL: []string{
`
CREATE TABLE foobar(
id INT,
rank INT NOT NULL
);
ALTER TABLE foobar ADD CONSTRAINT foobar_id_rank_key UNIQUE (id, rank);
`,
},
expectedHazardTypes: []diff.MigrationHazardType{
diff.MigrationHazardTypeIndexDropped,
diff.MigrationHazardTypeIndexBuild,
},
},
}

func TestIndexTestCases(t *testing.T) {
Expand Down
4 changes: 3 additions & 1 deletion internal/queries/queries.sql
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,9 @@ SELECT
pg_catalog.pg_attribute AS att
ON att.attrelid = table_c.oid AND indkey_ord.attnum = att.attnum
)::TEXT [] AS column_names,
COALESCE(con.conislocal, false) AS constraint_is_local
COALESCE(con.conislocal, false) AS constraint_is_local,
COALESCE(con.condeferrable, false) AS constraint_is_deferrable,
COALESCE(con.condeferred, false) AS constraint_is_initially_deferred
FROM pg_catalog.pg_class AS c
INNER JOIN pg_catalog.pg_index AS i ON (c.oid = i.indexrelid)
INNER JOIN pg_catalog.pg_class AS table_c ON (i.indrelid = table_c.oid)
Expand Down
10 changes: 8 additions & 2 deletions internal/queries/queries.sql.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions internal/schema/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,8 @@ type (
EscapedConstraintName string
ConstraintDef string
IsLocal bool
IsDeferrable bool
IsInitiallyDeferred bool
}

Index struct {
Expand Down Expand Up @@ -1165,6 +1167,8 @@ func (s *schemaFetcher) buildIndex(rawIndex queries.GetIndexesRow) Index {
EscapedConstraintName: EscapeIdentifier(rawIndex.ConstraintName),
ConstraintDef: rawIndex.ConstraintDef,
IsLocal: rawIndex.ConstraintIsLocal,
IsDeferrable: rawIndex.ConstraintIsDeferrable,
IsInitiallyDeferred: rawIndex.ConstraintIsInitiallyDeferred,
}
}

Expand Down
16 changes: 12 additions & 4 deletions pkg/diff/sql_generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -1927,11 +1927,19 @@ func (isg *indexSQLVertexGenerator) addIndexConstraint(index schema.Index) (Stat
if err != nil {
return Statement{}, fmt.Errorf("getting constraint type as SQL: %w", err)
}
ddl := fmt.Sprintf("%s %s USING INDEX %s",
addConstraintPrefix(index.OwningRelName, index.Constraint.EscapedConstraintName),
sqlConstraintType,
index.GetSchemaQualifiedName().EscapedName)
if index.Constraint.IsDeferrable {
if index.Constraint.IsInitiallyDeferred {
ddl += " DEFERRABLE INITIALLY DEFERRED"
} else {
ddl += " DEFERRABLE INITIALLY IMMEDIATE"
}
}
return Statement{
DDL: fmt.Sprintf("%s %s USING INDEX %s",
addConstraintPrefix(index.OwningRelName, index.Constraint.EscapedConstraintName),
sqlConstraintType,
index.GetSchemaQualifiedName().EscapedName),
DDL: ddl,
Timeout: statementTimeoutDefault,
LockTimeout: lockTimeoutDefault,
}, nil
Expand Down