diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c index 8a7c560..f06d345 100644 --- a/src/backend/utils/cache/relcache.c +++ b/src/backend/utils/cache/relcache.c @@ -4745,9 +4745,11 @@ RelationGetIndexPredicate(Relation relation) * Attribute numbers are offset by FirstLowInvalidHeapAttributeNumber so that * we can include system attributes (e.g., OID) in the bitmap representation. * - * Caller had better hold at least RowExclusiveLock on the target relation - * to ensure that it has a stable set of indexes. This also makes it safe - * (deadlock-free) for us to take locks on the relation's indexes. + * While all callers should at least RowExclusiveLock on the target relation, + * we still can't guarantee a stable set of indexes because CREATE INDEX + * CONCURRENTLY and DROP INDEX CONCURRENTLY can change set of indexes, without + * taking any conflicting lock. So we must be prepared to handle changes in the + * set of indexes and recompute bitmaps, when necessary. * * The returned result is palloc'd in the caller's memory context and should * be bms_free'd when not needed anymore. @@ -4787,6 +4789,7 @@ RelationGetIndexAttrBitmap(Relation relation, IndexAttrBitmapKind attrKind) if (!RelationGetForm(relation)->relhasindex) return NULL; +recheck: /* * Get cached list of index OIDs */ @@ -4798,15 +4801,16 @@ RelationGetIndexAttrBitmap(Relation relation, IndexAttrBitmapKind attrKind) /* * Copy the rd_pkindex and rd_replidindex value computed by - * RelationGetIndexList before proceeding. This is needed because a - * relcache flush could occur inside index_open below, resetting the - * fields managed by RelationGetIndexList. (The values we're computing - * will still be valid, assuming that caller has a sufficient lock on - * the relation.) + * RelationGetIndexList before proceeding. If a relcache flush could occur + * inside index_open below, resetting the fields managed by + * RelationGetIndexList, we compare the old and new values and recompute + * attribute maps again. */ relpkindex = relation->rd_pkindex; relreplindex = relation->rd_replidindex; + Assert(relation->rd_indexvalid != 0); + /* * For each index, add referenced attributes to indexattrs. * @@ -4882,6 +4886,30 @@ RelationGetIndexAttrBitmap(Relation relation, IndexAttrBitmapKind attrKind) list_free(indexoidlist); + /* + * If a relcache flush happened during index_open, we may have computed + * stale values of the bitmaps since we used the old information. Since all + * these bitmaps are closely tied with the index list as well as other + * information such as the primary index and the replica identity, we must + * recompute everything. Otherwise we could be end up with a wrongly cached + * values of these bitmaps. + * + * Note that a subsequent call to RelationGetIndexList() will reload the + * new list of indexes, but that does not invalidate the cached attribute + * bitmaps. So we handle that here itself. + */ + if (relpkindex != relation->rd_pkindex || + relreplindex != relation->rd_replidindex || + relation->rd_indexvalid == 0) + { + bms_free(uindexattrs); + bms_free(pkindexattrs); + bms_free(idindexattrs); + bms_free(indexattrs); + + goto recheck; + } + /* Don't leak the old values of these bitmaps, if any */ bms_free(relation->rd_indexattr); relation->rd_indexattr = NULL;