From 50da7cad2cd0f71c669b364d7eb682d71c143491 Mon Sep 17 00:00:00 2001 From: Mohamed Ali Date: Fri, 27 Mar 2026 19:38:32 -0700 Subject: [PATCH] fix: Validate parent partitioned index after REINDEX of child After REINDEX (or REINDEX CONCURRENTLY) repairs an invalid partition index, the parent partitioned index remains permanently stuck with indisvalid=false because validatePartitionedIndex() is never called from any REINDEX code path. Fix this by: 1. Making validatePartitionedIndex() non-static and adding its declaration to tablecmds.h so it can be called from other modules. 2. Calling validatePartitionedIndex() from reindex_index() (for regular REINDEX) after marking a partition index valid. A CommandCounterIncrement() is needed before the call so that the child's updated indisvalid is visible to the syscache lookup performed by validatePartitionedIndex(). 3. Calling validatePartitionedIndex() from ReindexRelationConcurrently() (for REINDEX CONCURRENTLY) after the index swap and CommandCounterIncrement, at which point the new valid index has taken the old invalid index's place in the partition hierarchy. validatePartitionedIndex() already handles multi-level partition hierarchies by recursing upward when it marks a mid-level parent valid, so this fix automatically cascades through grandparent and higher-level partitioned indexes. --- src/backend/catalog/index.c | 32 ++++++++++++++++++++++++++++++++ src/backend/commands/indexcmds.c | 29 +++++++++++++++++++++++++++++ src/backend/commands/tablecmds.c | 9 ++++++--- src/include/commands/tablecmds.h | 2 ++ 4 files changed, 69 insertions(+), 3 deletions(-) diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index d8219b1..bf1a25c 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -3885,6 +3885,38 @@ reindex_index(const ReindexStmt *stmt, Oid indexId, CacheInvalidateRelcache(heapRelation); } + /* + * If this index is a partition of a partitioned index, and we just + * marked it valid, check if the parent partitioned index can now be + * marked valid too. This handles the case where an invalid partition + * index was attached to a partitioned index (making the parent + * invalid), then later fixed via REINDEX. validatePartitionedIndex + * will recurse up the hierarchy if needed. + */ + if (index_bad && iRel->rd_rel->relispartition) + { + Oid parentIdxId; + + /* Make the child's indisvalid update visible for validation */ + CommandCounterIncrement(); + + parentIdxId = get_partition_parent(indexId, false); + if (OidIsValid(parentIdxId)) + { + Relation parentIdx; + Relation parentTbl; + + parentIdx = index_open(parentIdxId, AccessShareLock); + parentTbl = table_open(parentIdx->rd_index->indrelid, + AccessShareLock); + + validatePartitionedIndex(parentIdx, parentTbl); + + table_close(parentTbl, AccessShareLock); + index_close(parentIdx, AccessShareLock); + } + } + table_close(pg_index, RowExclusiveLock); } diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index dd593cc..ab677d7 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -34,6 +34,7 @@ #include "catalog/pg_constraint.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" +#include "catalog/partition.h" #include "catalog/pg_namespace.h" #include "catalog/pg_opclass.h" #include "catalog/pg_tablespace.h" @@ -4317,6 +4318,34 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein * characters. */ CommandCounterIncrement(); + + /* + * If the new (swapped-in) index is a partition of a partitioned + * index, check if the parent can now be marked valid. This handles + * REINDEX CONCURRENTLY on a partition index that was previously + * invalid: the new (valid) index replaces the old (invalid) one, + * and the parent should be re-validated. + */ + if (get_rel_relispartition(newidx->indexId)) + { + Oid parentIdxId; + + parentIdxId = get_partition_parent(newidx->indexId, true); + if (OidIsValid(parentIdxId)) + { + Relation parentIdx; + Relation parentTbl; + + parentIdx = index_open(parentIdxId, AccessShareLock); + parentTbl = table_open(parentIdx->rd_index->indrelid, + AccessShareLock); + + validatePartitionedIndex(parentIdx, parentTbl); + + table_close(parentTbl, AccessShareLock); + index_close(parentIdx, AccessShareLock); + } + } } /* Commit this transaction and make index swaps visible */ diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index c69c12d..8612067 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -746,7 +746,6 @@ static void DetachPartitionFinalize(Relation rel, Relation partRel, static ObjectAddress ATExecDetachPartitionFinalize(Relation rel, RangeVar *name); static ObjectAddress ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx, RangeVar *name); -static void validatePartitionedIndex(Relation partedIdx, Relation partedTbl); static void refuseDupeIndexAttach(Relation parentIdx, Relation partIdx, Relation partitionTbl); static void verifyPartitionIndexNotNull(IndexInfo *iinfo, Relation partition); @@ -22058,12 +22057,16 @@ refuseDupeIndexAttach(Relation parentIdx, Relation partIdx, Relation partitionTb } /* + * validatePartitionedIndex + * * Verify whether the set of attached partition indexes to a parent index on * a partitioned table is complete. If it is, mark the parent index valid. * - * This should be called each time a partition index is attached. + * This should be called each time a partition index is attached, and also + * after a partition index is repaired via REINDEX, so that the parent can + * be marked valid once all children are valid. */ -static void +void validatePartitionedIndex(Relation partedIdx, Relation partedTbl) { Relation inheritsRel; diff --git a/src/include/commands/tablecmds.h b/src/include/commands/tablecmds.h index c3d8518..84983fb 100644 --- a/src/include/commands/tablecmds.h +++ b/src/include/commands/tablecmds.h @@ -108,4 +108,6 @@ extern void RangeVarCallbackOwnsRelation(const RangeVar *relation, extern bool PartConstraintImpliedByRelConstraint(Relation scanrel, List *partConstraint); +extern void validatePartitionedIndex(Relation partedIdx, Relation partedTbl); + #endif /* TABLECMDS_H */ -- 2.50.1 (Apple Git-155)