From 55adef7abbe8ecfa329335640b598cbb57be0f93 Mon Sep 17 00:00:00 2001 From: Michael Paquier Date: Tue, 28 Jul 2026 08:35:21 +0900 Subject: [PATCH] Fix propagation of indimmediate flag in index_create_copy() index_create_copy is used to create copy definitions of existing indexes. Currently, it passes 0 as constr_flags to index_create(), which results in the copied index to always be created as immediate (indimmediate set to true). For deferrable unique constraints, it means that the transient index used during the phase 2 of REINDEX CONCURRENTLY forces immediate constraint checks on concurrent inserts, which can cause unexpected constraint violations based on the definition of the parent table, inconsistently set in the copied index. To fix this without violating the contract of constr_flags (which should only be used when creating constraints) and without relaxing the strict assertion in index_create(), this introduces a new index creation flag: INDEX_CREATE_DEFERRABLE. If set, a copied index's indimmediate is set to false, meaning that unique constraints are not enforced immediately on insertion, but at transaction commit time. An isolation test for REINDEX CONCURRENTLY is added, based on an injection point waiting after phase 1 of the operation, where an index copy has been built and is able to accept DMLs for its validation in phase 2. The test is tentatively backpatched down to v17. INJECTION_POINT() is outside a transaction context, which should be fine on HEAD since 8daeaa9b642c but I suspect may cause issues in v19 and older branches due to the wait facility depending on condition variables and a DSM setup, but let's see what the buildfarm tells. Author: Nitin Motiani Discussion: https://postgr.es/m/CAH5HC97JmjPpgiQOqW9xm8qXhNiu7zZ1Qh+FfhEESJuDv69kuQ@mail.gmail.com Backpatch-through: 14 --- src/backend/catalog/index.c | 20 +++++++++++++++++--- src/include/catalog/index.h | 1 + 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 359bf34f48e..c2739974573 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -688,6 +688,10 @@ UpdateIndexRelation(Oid indexoid, * already exists. * INDEX_CREATE_PARTITIONED: * create a partitioned index (table must be partitioned) + * INDEX_CREATE_DEFERRABLE: + * index supports a deferrable constraint, mark it as + * non-immediate (indimmediate = false). + * * constr_flags: flags passed to index_constraint_create * (only if INDEX_CREATE_ADD_CONSTRAINT is set) * allow_system_table_mods: allow table to be a system catalog @@ -1000,7 +1004,8 @@ index_create(Relation heapRelation, indexInfo, collationObjectId, classObjectId, coloptions, isprimary, is_exclusion, - (constr_flags & INDEX_CONSTR_CREATE_DEFERRABLE) == 0, + (constr_flags & INDEX_CONSTR_CREATE_DEFERRABLE) == 0 && + (flags & INDEX_CREATE_DEFERRABLE) == 0, !concurrent && !invalid, !concurrent); @@ -1271,6 +1276,8 @@ index_concurrently_create_copy(Relation heapRelation, Oid oldIndexId, List *indexColNames = NIL; List *indexExprs = NIL; List *indexPreds = NIL; + bits16 flags = INDEX_CREATE_SKIP_BUILD | INDEX_CREATE_CONCURRENT; + Form_pg_index indexForm; indexRelation = index_open(oldIndexId, RowExclusiveLock); @@ -1290,6 +1297,13 @@ index_concurrently_create_copy(Relation heapRelation, Oid oldIndexId, indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(oldIndexId)); if (!HeapTupleIsValid(indexTuple)) elog(ERROR, "cache lookup failed for index %u", oldIndexId); + + indexForm = (Form_pg_index) GETSTRUCT(indexTuple); + + /* Old index is deferrable, do the same for the new index */ + if (!indexForm->indimmediate) + flags |= INDEX_CREATE_DEFERRABLE; + indclassDatum = SysCacheGetAttr(INDEXRELID, indexTuple, Anum_pg_index_indclass, &isnull); Assert(!isnull); @@ -1398,8 +1412,8 @@ index_concurrently_create_copy(Relation heapRelation, Oid oldIndexId, indclass->values, indcoloptions->values, optionDatum, - INDEX_CREATE_SKIP_BUILD | INDEX_CREATE_CONCURRENT, - 0, + flags, + 0, /* constr_flags */ true, /* allow table to be a system catalog? */ false, /* is_internal? */ NULL); diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 008f723e104..74cea78fac6 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -65,6 +65,7 @@ extern void index_check_primary_key(Relation heapRel, #define INDEX_CREATE_IF_NOT_EXISTS (1 << 4) #define INDEX_CREATE_PARTITIONED (1 << 5) #define INDEX_CREATE_INVALID (1 << 6) +#define INDEX_CREATE_DEFERRABLE (1 << 7) extern Oid index_create(Relation heapRelation, const char *indexRelationName,