From 59a7b99b391ba42947d52733aae4f5a3e62f8ebd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Rodr=C3=ADguez?= Date: Thu, 14 May 2026 16:00:27 +0200 Subject: [PATCH v5 3/3] Skip index rewriting for PK-associated indexes when possible on ALTER TABLE ALTER COLUMN TYPE for partitioned tables This is a follow-up to the same fix for non-constraint indexes. Just as in there, the parent constraint is dropped first, and cascades to the child constraints, so that the associated indexes cannot be recreated properly. We remove the condition that preented child constraints from being re-added. However, this causes the constraints to be added twice (once when the parent is recreated, and once when the constraints themselves are recreated). We need to make sure that this happens in the right order, so we first create the child constraints (reusing the index if possible), and then the parent constraint, which should automatically reuse all child constraints and indexes. --- src/backend/commands/tablecmds.c | 34 +++++++++++++++++++---- src/test/regress/expected/alter_table.out | 26 ++++++++--------- 2 files changed, 41 insertions(+), 19 deletions(-) diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 9dc356fa865..298ea163f4c 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -16187,10 +16187,13 @@ ATPostAlterTypeCleanup(List **wqueue, ObjectAddresses *objects, AlteredTableInfo * If the constraint is inherited (only), we don't want to inject a * new definition here; it'll get recreated when * ATAddCheckNNConstraint recurses from adding the parent table's - * constraint. But we had to carry the info this far so that we can - * drop the constraint below. + * constraint. But we had to carry the info this far so that we can + * drop the constraint below. The exception to this are primary key + * or unique constraints: we need to process the child constraints + * explicitly so we save the necessary information to avoid re-indexing. */ - if (!conislocal) + if (!conislocal && con->contype != CONSTRAINT_PRIMARY + && con->contype != CONSTRAINT_UNIQUE) continue; /* @@ -16432,9 +16435,14 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, Oid ownerId, { IndexStmt *indstmt; Oid indoid; + char relkind; + AlterTablePass indexCreationPass; + bool ispartition; indstmt = castNode(IndexStmt, cmd->def); indoid = get_constraint_index(oldId); + relkind = get_rel_relkind(indoid); + ispartition = get_rel_relispartition(indoid); if (!rewrite) TryReuseIndex(indoid, indstmt); @@ -16444,12 +16452,26 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, Oid ownerId, indstmt->reset_default_tblspc = true; cmd->subtype = AT_ReAddIndex; - tab->subcmds[AT_PASS_OLD_INDEX] = - lappend(tab->subcmds[AT_PASS_OLD_INDEX], cmd); + + /* + * We process constraints backed by a partitioned index at + * a later stage. Otherwise, we might create the child + * indexes twice, which will error out. We mimic here + * the behavior for partitioned indexes, although in here + * the logic relies on parent constraints automatically + * reusing existing child constraints. + */ + if (relkind == RELKIND_PARTITIONED_INDEX && !ispartition) + indexCreationPass = AT_PASS_OLD_PARTITIONED_INDEX; + else + indexCreationPass = AT_PASS_OLD_INDEX; + + tab->subcmds[indexCreationPass] = + lappend(tab->subcmds[indexCreationPass], cmd); /* recreate any comment on the constraint */ RebuildConstraintComment(tab, - AT_PASS_OLD_INDEX, + indexCreationPass, oldId, rel, NIL, diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out index 4051922c1d4..9ba415cd5c5 100644 --- a/src/test/regress/expected/alter_table.out +++ b/src/test/regress/expected/alter_table.out @@ -2329,23 +2329,23 @@ select relname, from pg_class c left join old_oids using (relname) where relname like 'at_partitioned%' order by relname; - relname | orig_oid | storage | desc -------------------------------+----------+---------+-------------- + relname | orig_oid | storage | desc +------------------------------+----------+---------+--------------- at_partitioned | t | none | at_partitioned_0 | t | own | - at_partitioned_0_id_name_key | f | own | + at_partitioned_0_id_name_key | f | orig | child 0 index at_partitioned_1 | t | own | - at_partitioned_1_id_name_key | f | own | + at_partitioned_1_id_name_key | f | orig | child 1 index at_partitioned_id_name_key | f | none | parent index (6 rows) select conname, obj_description(oid, 'pg_constraint') as desc from pg_constraint where conname like 'at_partitioned%' order by conname; - conname | desc -------------------------------+------------------- - at_partitioned_0_id_name_key | - at_partitioned_1_id_name_key | + conname | desc +------------------------------+-------------------- + at_partitioned_0_id_name_key | child 0 constraint + at_partitioned_1_id_name_key | child 1 constraint at_partitioned_id_name_key | parent constraint (3 rows) @@ -2383,10 +2383,10 @@ select relname, at_idx_part_code_idx | f | none at_idx_part_p1 | t | own at_idx_part_p1_code_idx | f | orig - at_idx_part_p1_pkey | f | own + at_idx_part_p1_pkey | f | orig at_idx_part_p2 | t | own at_idx_part_p2_code_idx | f | orig - at_idx_part_p2_pkey | f | own + at_idx_part_p2_pkey | f | orig at_idx_part_pkey | f | none (9 rows) @@ -2427,15 +2427,15 @@ select relname, at_idx_part2_p1 | t | none at_idx_part2_p1_1 | t | own at_idx_part2_p1_1_code_idx | f | orig - at_idx_part2_p1_1_pkey | f | own + at_idx_part2_p1_1_pkey | f | orig at_idx_part2_p1_2 | t | own at_idx_part2_p1_2_code_idx | f | orig - at_idx_part2_p1_2_pkey | f | own + at_idx_part2_p1_2_pkey | f | orig at_idx_part2_p1_code_idx | f | none at_idx_part2_p1_pkey | f | none at_idx_part2_p2 | t | own at_idx_part2_p2_code_idx | f | orig - at_idx_part2_p2_pkey | f | own + at_idx_part2_p2_pkey | f | orig at_idx_part2_pkey | f | none (15 rows) -- 2.52.0