diff --git a/src/backend/catalog/pg_depend.c b/src/backend/catalog/pg_depend.c index 2ea05f350b..c6f75c5aa8 100644 --- a/src/backend/catalog/pg_depend.c +++ b/src/backend/catalog/pg_depend.c @@ -616,8 +616,8 @@ getOwnedSequence(Oid relid, AttrNumber attnum) /* * get_constraint_index - * Given the OID of a unique or primary-key constraint, return the - * OID of the underlying unique index. + * Given the OID of a unique, primary-key, or exclusion constraint, + * return the OID of the underlying index. * * Return InvalidOid if the index couldn't be found; this suggests the * given OID is bogus, but we leave it to caller to decide what to do. @@ -664,10 +664,13 @@ get_constraint_index(Oid constraintId) { char relkind = get_rel_relkind(deprec->objid); - /* This is pure paranoia; there shouldn't be any such */ + /* + * This is pure paranoia; there shouldn't be any other relkinds + * dependent on a constraint. + */ if (relkind != RELKIND_INDEX && relkind != RELKIND_PARTITIONED_INDEX) - break; + continue; indexId = deprec->objid; break; @@ -682,8 +685,9 @@ get_constraint_index(Oid constraintId) /* * get_index_constraint - * Given the OID of an index, return the OID of the owning unique or - * primary-key constraint, or InvalidOid if no such constraint. + * Given the OID of an index, return the OID of the owning unique, + * primary-key, or exclusion constraint, or InvalidOid if there + * is no owning constraint. */ Oid get_index_constraint(Oid indexId) diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 96e85bd6ce..5c278463c6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -9785,6 +9785,9 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel, SysScanDesc scan; HeapTuple depTup; ObjectAddress address; + ListCell *lc; + ListCell *prev; + ListCell *next; /* * Clear all the missing values if we're rewriting the table, since this @@ -9915,14 +9918,20 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel, if (relKind == RELKIND_INDEX || relKind == RELKIND_PARTITIONED_INDEX) { + /* + * Indexes that are directly dependent on the table + * might be regular indexes or constraint indexes. + * Constraint indexes typically have only indirect + * dependencies; but there are exceptions, notably + * partial exclusion constraints. Hence we must check + * whether the index depends on any constraint that's + * due to be rebuilt, which we'll do below after we've + * found all such constraints. + */ Assert(foundObject.objectSubId == 0); - if (!list_member_oid(tab->changedIndexOids, foundObject.objectId)) - { - tab->changedIndexOids = lappend_oid(tab->changedIndexOids, - foundObject.objectId); - tab->changedIndexDefs = lappend(tab->changedIndexDefs, - pg_get_indexdef_string(foundObject.objectId)); - } + tab->changedIndexOids = + list_append_unique_oid(tab->changedIndexOids, + foundObject.objectId); } else if (relKind == RELKIND_SEQUENCE) { @@ -10073,6 +10082,41 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel, systable_endscan(scan); + /* + * Check the collected index OIDs to see which ones belong to the + * constraint(s) of the table, and drop those from the list of indexes + * that we need to process; rebuilding the constraints will handle them. + */ + prev = NULL; + for (lc = list_head(tab->changedIndexOids); lc; lc = next) + { + Oid indexoid = lfirst_oid(lc); + Oid conoid; + + next = lnext(lc); + + conoid = get_index_constraint(indexoid); + if (OidIsValid(conoid) && + list_member_oid(tab->changedConstraintOids, conoid)) + tab->changedIndexOids = list_delete_cell(tab->changedIndexOids, + lc, prev); + else + prev = lc; + } + + /* + * Now collect the definitions of the indexes that must be rebuilt. (We + * could merge this into the previous loop, but it'd be more complicated + * for little gain.) + */ + foreach(lc, tab->changedIndexOids) + { + Oid indexoid = lfirst_oid(lc); + + tab->changedIndexDefs = lappend(tab->changedIndexDefs, + pg_get_indexdef_string(indexoid)); + } + /* * Now scan for dependencies of this column on other things. The only * thing we should find is the dependency on the column datatype, which we diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out index c3a650d4f2..ed11538568 100644 --- a/src/test/regress/expected/alter_table.out +++ b/src/test/regress/expected/alter_table.out @@ -1990,6 +1990,46 @@ select * from anothertab; f | IT WAS NULL! (3 rows) +drop table anothertab; +-- Test alter table column type with constraint indexes (cf. bug #15835) +create table anothertab(f1 int primary key, f2 int unique, f3 int, f4 int); +alter table anothertab + add exclude using btree (f3 with =); +alter table anothertab + add exclude using btree (f4 with =) where (f4 is not null); +\d anothertab + Table "public.anothertab" + Column | Type | Collation | Nullable | Default +--------+---------+-----------+----------+--------- + f1 | integer | | not null | + f2 | integer | | | + f3 | integer | | | + f4 | integer | | | +Indexes: + "anothertab_pkey" PRIMARY KEY, btree (f1) + "anothertab_f2_key" UNIQUE CONSTRAINT, btree (f2) + "anothertab_f3_excl" EXCLUDE USING btree (f3 WITH =) + "anothertab_f4_excl" EXCLUDE USING btree (f4 WITH =) WHERE (f4 IS NOT NULL) + +alter table anothertab alter column f1 type bigint; +alter table anothertab + alter column f2 type bigint, + alter column f3 type bigint, + alter column f4 type bigint; +\d anothertab + Table "public.anothertab" + Column | Type | Collation | Nullable | Default +--------+--------+-----------+----------+--------- + f1 | bigint | | not null | + f2 | bigint | | | + f3 | bigint | | | + f4 | bigint | | | +Indexes: + "anothertab_pkey" PRIMARY KEY, btree (f1) + "anothertab_f2_key" UNIQUE CONSTRAINT, btree (f2) + "anothertab_f3_excl" EXCLUDE USING btree (f3 WITH =) + "anothertab_f4_excl" EXCLUDE USING btree (f4 WITH =) WHERE (f4 IS NOT NULL) + drop table anothertab; create table another (f1 int, f2 text); insert into another values(1, 'one'); diff --git a/src/test/regress/sql/alter_table.sql b/src/test/regress/sql/alter_table.sql index abb3a6aa13..d5c3b93131 100644 --- a/src/test/regress/sql/alter_table.sql +++ b/src/test/regress/sql/alter_table.sql @@ -1357,6 +1357,23 @@ select * from anothertab; drop table anothertab; +-- Test alter table column type with constraint indexes (cf. bug #15835) +create table anothertab(f1 int primary key, f2 int unique, f3 int, f4 int); +alter table anothertab + add exclude using btree (f3 with =); +alter table anothertab + add exclude using btree (f4 with =) where (f4 is not null); + +\d anothertab +alter table anothertab alter column f1 type bigint; +alter table anothertab + alter column f2 type bigint, + alter column f3 type bigint, + alter column f4 type bigint; +\d anothertab + +drop table anothertab; + create table another (f1 int, f2 text); insert into another values(1, 'one');