diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c index 8a7c560..d1543f1 100644 --- a/src/backend/utils/cache/relcache.c +++ b/src/backend/utils/cache/relcache.c @@ -3053,6 +3053,23 @@ AtEOXact_cleanup(Relation relation, bool isCommit) relation->rd_replidindex = InvalidOid; relation->rd_indexvalid = 0; } + + /* + * Flush any temporary index bitmaps. + */ + if (relation->rd_bitmapsvalid == 2) + { + bms_free(relation->rd_indexattr); + relation->rd_indexattr = NULL; + bms_free(relation->rd_keyattr); + relation->rd_keyattr = NULL; + bms_free(relation->rd_pkattr); + relation->rd_pkattr = NULL; + bms_free(relation->rd_idattr); + relation->rd_idattr = NULL; + + relation->rd_bitmapsvalid = 0; + } } /* @@ -3166,6 +3183,23 @@ AtEOSubXact_cleanup(Relation relation, bool isCommit, relation->rd_replidindex = InvalidOid; relation->rd_indexvalid = 0; } + + /* + * Flush any temporary index bitmaps. + */ + if (relation->rd_bitmapsvalid == 2) + { + bms_free(relation->rd_indexattr); + relation->rd_indexattr = NULL; + bms_free(relation->rd_keyattr); + relation->rd_keyattr = NULL; + bms_free(relation->rd_pkattr); + relation->rd_pkattr = NULL; + bms_free(relation->rd_idattr); + relation->rd_idattr = NULL; + + relation->rd_bitmapsvalid = 0; + } } @@ -4745,9 +4779,12 @@ 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. + * 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. Note that + * a concurrent CREATE/DROP INDEX CONCURRENTLY can lead to an outdated list + * being returned (will be recomputed at the next access), the CONCURRENTLY + * code deals with that. * * The returned result is palloc'd in the caller's memory context and should * be bms_free'd when not needed anymore. @@ -4760,13 +4797,14 @@ RelationGetIndexAttrBitmap(Relation relation, IndexAttrBitmapKind attrKind) Bitmapset *pkindexattrs; /* columns in the primary index */ Bitmapset *idindexattrs; /* columns in the replica identity */ List *indexoidlist; + List *newindexoidlist; Oid relpkindex; Oid relreplindex; ListCell *l; MemoryContext oldcxt; /* Quick exit if we already computed the result. */ - if (relation->rd_indexattr != NULL) + if (relation->rd_bitmapsvalid != 0) { switch (attrKind) { @@ -4880,8 +4918,6 @@ RelationGetIndexAttrBitmap(Relation relation, IndexAttrBitmapKind attrKind) index_close(indexDesc, AccessShareLock); } - list_free(indexoidlist); - /* Don't leak the old values of these bitmaps, if any */ bms_free(relation->rd_indexattr); relation->rd_indexattr = NULL; @@ -4892,13 +4928,7 @@ RelationGetIndexAttrBitmap(Relation relation, IndexAttrBitmapKind attrKind) bms_free(relation->rd_idattr); relation->rd_idattr = NULL; - /* - * Now save copies of the bitmaps in the relcache entry. We intentionally - * set rd_indexattr last, because that's the one that signals validity of - * the values; if we run out of memory before making that copy, we won't - * leave the relcache entry looking like the other ones are valid but - * empty. - */ + /* now save copies of the bitmaps in the relcache entry */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); relation->rd_keyattr = bms_copy(uindexattrs); relation->rd_pkattr = bms_copy(pkindexattrs); @@ -4906,6 +4936,48 @@ RelationGetIndexAttrBitmap(Relation relation, IndexAttrBitmapKind attrKind) relation->rd_indexattr = bms_copy(indexattrs); MemoryContextSwitchTo(oldcxt); + /* + * During one of the index_opens in the above loop, we might have received + * a relcache flush event on this relcache entry, which might have been + * signaling a change in the rel's index list. If so, we'd better start + * over to ensure we deliver up-to-date attribute bitmaps. + */ + newindexoidlist = RelationGetIndexList(relation); + if (equal(indexoidlist, newindexoidlist) && + relpkindex == relation->rd_pkindex && + relreplindex == relation->rd_replidindex) + { + /* Still the same index set, so proceed */ + list_free(newindexoidlist); + list_free(indexoidlist); + } + else + { + /* + * Signal that the attribute bitmaps are being built. If there's any + * relcache invalidations while building them, rd_bitmapsvalid will be + * be set to 2. In that case return the bitmaps, but don't mark them as + * valid. + */ + relation->rd_bitmapsvalid = 2; + /* Flag relation as needing eoxact cleanup (to reset the list) */ + EOXactListAdd(relation); + + /* Gotta do it over ... might as well not leak memory */ + list_free(newindexoidlist); + list_free(indexoidlist); + } + + /* + * If there've been no invalidations while building the entry, mark the + * stored bitmaps as being valid. Need to do so after the copies above, + * as we could run out of memory while doing so. + * + * NB: No relcache accesses should happen inside this routine after this. + */ + if (relation->rd_bitmapsvalid != 2) + relation->rd_bitmapsvalid = 1; + /* We return our original working copy for caller to play with */ switch (attrKind) { @@ -5519,6 +5591,7 @@ load_relcache_init_file(bool shared) else rel->rd_refcnt = 0; rel->rd_indexvalid = 0; + rel->rd_bitmapsvalid = 0; rel->rd_fkeylist = NIL; rel->rd_fkeyvalid = false; rel->rd_indexlist = NIL; diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h index a617a7c..6442127 100644 --- a/src/include/utils/rel.h +++ b/src/include/utils/rel.h @@ -137,6 +137,8 @@ typedef struct RelationData Oid rd_replidindex; /* OID of replica identity index, if any */ /* data managed by RelationGetIndexAttrBitmap: */ + int rd_bitmapsvalid; /* state of rd_indexattr: 0 = not valid, 1 = + * valid, 2 = temporarily forced */ Bitmapset *rd_indexattr; /* identifies columns used in indexes */ Bitmapset *rd_keyattr; /* cols that can be ref'd by foreign keys */ Bitmapset *rd_pkattr; /* cols included in primary key */