diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2014-05-14 14:55:48 -0400 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2014-05-14 14:56:08 -0400 |
commit | b23b0f5588d2e2f15edff66e072e339a8c9616a7 (patch) | |
tree | 4f4fc7804d31955fd244a899867e14ee2ebea922 /src/backend/utils/cache/relcache.c | |
parent | ac53295d667e7727d7b70ddf11d82c067870501f (diff) | |
download | postgresql-b23b0f5588d2e2f15edff66e072e339a8c9616a7.tar.gz postgresql-b23b0f5588d2e2f15edff66e072e339a8c9616a7.zip |
Code review for recent changes in relcache.c.
rd_replidindex should be managed the same as rd_oidindex, and rd_keyattr
and rd_idattr should be managed like rd_indexattr. Omissions in this area
meant that the bitmapsets computed for rd_keyattr and rd_idattr would be
leaked during any relcache flush, resulting in a slow but permanent leak in
CacheMemoryContext. There was also a tiny probability of relcache entry
corruption if we ran out of memory at just the wrong point in
RelationGetIndexAttrBitmap. Otherwise, the fields were not zeroed where
expected, which would not bother the code any AFAICS but could greatly
confuse anyone examining the relcache entry while debugging.
Also, create an API function RelationGetReplicaIndex rather than letting
non-relcache code be intimate with the mechanisms underlying caching of
that value (we won't even mention the memory leak there).
Also, fix a relcache flush hazard identified by Andres Freund:
RelationGetIndexAttrBitmap must not assume that rd_replidindex stays valid
across index_open.
The aspects of this involving rd_keyattr date back to 9.3, so back-patch
those changes.
Diffstat (limited to 'src/backend/utils/cache/relcache.c')
-rw-r--r-- | src/backend/utils/cache/relcache.c | 118 |
1 files changed, 82 insertions, 36 deletions
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c index 5ff0d9e4fdf..1ffa24356b3 100644 --- a/src/backend/utils/cache/relcache.c +++ b/src/backend/utils/cache/relcache.c @@ -1901,6 +1901,8 @@ RelationDestroyRelation(Relation relation, bool remember_tupdesc) } list_free(relation->rd_indexlist); bms_free(relation->rd_indexattr); + bms_free(relation->rd_keyattr); + bms_free(relation->rd_idattr); FreeTriggerDesc(relation->trigdesc); if (relation->rd_options) pfree(relation->rd_options); @@ -2547,6 +2549,7 @@ AtEOXact_cleanup(Relation relation, bool isCommit) list_free(relation->rd_indexlist); relation->rd_indexlist = NIL; relation->rd_oidindex = InvalidOid; + relation->rd_replidindex = InvalidOid; relation->rd_indexvalid = 0; } } @@ -2645,6 +2648,7 @@ AtEOSubXact_cleanup(Relation relation, bool isCommit, list_free(relation->rd_indexlist); relation->rd_indexlist = NIL; relation->rd_oidindex = InvalidOid; + relation->rd_replidindex = InvalidOid; relation->rd_indexvalid = 0; } } @@ -3596,6 +3600,10 @@ CheckConstraintFetch(Relation relation) * of the index list. rd_oidindex is valid when rd_indexvalid isn't zero; * it is the pg_class OID of a unique index on OID when the relation has one, * and InvalidOid if there is no such index. + * + * In exactly the same way, we update rd_replidindex, which is the pg_class + * OID of an index to be used as the relation's replication identity index, + * or InvalidOid if there is no such index. */ List * RelationGetIndexList(Relation relation) @@ -3667,8 +3675,8 @@ RelationGetIndexList(Relation relation) /* * Invalid, non-unique, non-immediate or predicate indexes aren't - * interesting for neither oid indexes nor replication identity - * indexes, so don't check them. + * interesting for either oid indexes or replication identity indexes, + * so don't check them. */ if (!IndexIsValid(index) || !index->indisunique || !index->indimmediate || @@ -3681,35 +3689,29 @@ RelationGetIndexList(Relation relation) indclass->values[0] == OID_BTREE_OPS_OID) oidIndex = index->indexrelid; - /* always prefer primary keys */ + /* remember primary key index if any */ if (index->indisprimary) pkeyIndex = index->indexrelid; - /* explicitly chosen index */ + /* remember explicitly chosen replica index */ if (index->indisreplident) candidateIndex = index->indexrelid; } systable_endscan(indscan); - /* primary key */ - if (replident == REPLICA_IDENTITY_DEFAULT && - OidIsValid(pkeyIndex)) - relation->rd_replidindex = pkeyIndex; - /* explicitly chosen index */ - else if (replident == REPLICA_IDENTITY_INDEX && - OidIsValid(candidateIndex)) - relation->rd_replidindex = candidateIndex; - /* nothing */ - else - relation->rd_replidindex = InvalidOid; - heap_close(indrel, AccessShareLock); /* Now save a copy of the completed list in the relcache entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); relation->rd_indexlist = list_copy(result); relation->rd_oidindex = oidIndex; + if (replident == REPLICA_IDENTITY_DEFAULT && OidIsValid(pkeyIndex)) + relation->rd_replidindex = pkeyIndex; + else if (replident == REPLICA_IDENTITY_INDEX && OidIsValid(candidateIndex)) + relation->rd_replidindex = candidateIndex; + else + relation->rd_replidindex = InvalidOid; relation->rd_indexvalid = 1; MemoryContextSwitchTo(oldcxt); @@ -3767,7 +3769,8 @@ insert_ordered_oid(List *list, Oid datum) * correctly with respect to the full index set. It is up to the caller * to ensure that a correct rd_indexattr set has been cached before first * calling RelationSetIndexList; else a subsequent inquiry might cause a - * wrong rd_indexattr set to get computed and cached. + * wrong rd_indexattr set to get computed and cached. Likewise, we do not + * touch rd_keyattr or rd_idattr. */ void RelationSetIndexList(Relation relation, List *indexIds, Oid oidIndex) @@ -3783,6 +3786,8 @@ RelationSetIndexList(Relation relation, List *indexIds, Oid oidIndex) list_free(relation->rd_indexlist); relation->rd_indexlist = indexIds; relation->rd_oidindex = oidIndex; + /* For the moment, assume the target rel hasn't got a replica index */ + relation->rd_replidindex = InvalidOid; relation->rd_indexvalid = 2; /* mark list as forced */ /* Flag relation as needing eoxact cleanup (to reset the list) */ EOXactListAdd(relation); @@ -3817,6 +3822,27 @@ RelationGetOidIndex(Relation relation) } /* + * RelationGetReplicaIndex -- get OID of the relation's replica identity index + * + * Returns InvalidOid if there is no such index. + */ +Oid +RelationGetReplicaIndex(Relation relation) +{ + List *ilist; + + if (relation->rd_indexvalid == 0) + { + /* RelationGetIndexList does the heavy lifting. */ + ilist = RelationGetIndexList(relation); + list_free(ilist); + Assert(relation->rd_indexvalid != 0); + } + + return relation->rd_replidindex; +} + +/* * RelationGetIndexExpressions -- get the index expressions for an index * * We cache the result of transforming pg_index.indexprs into a node tree. @@ -3954,8 +3980,8 @@ RelationGetIndexPredicate(Relation relation) * predicates.) * * Depending on attrKind, a bitmap covering the attnums for all index columns, - * for all key columns or for all the columns the configured replica identity - * are returned. + * for all potential foreign key columns, or for all columns in the configured + * replica identity index is returned. * * Attribute numbers are offset by FirstLowInvalidHeapAttributeNumber so that * we can include system attributes (e.g., OID) in the bitmap representation. @@ -3974,22 +4000,25 @@ RelationGetIndexAttrBitmap(Relation relation, IndexAttrBitmapKind attrKind) Bitmapset *uindexattrs; /* columns in unique indexes */ Bitmapset *idindexattrs; /* columns in the replica identity */ List *indexoidlist; + Oid relreplindex; ListCell *l; MemoryContext oldcxt; /* Quick exit if we already computed the result. */ if (relation->rd_indexattr != NULL) + { switch (attrKind) { - case INDEX_ATTR_BITMAP_IDENTITY_KEY: - return bms_copy(relation->rd_idattr); - case INDEX_ATTR_BITMAP_KEY: - return bms_copy(relation->rd_keyattr); case INDEX_ATTR_BITMAP_ALL: return bms_copy(relation->rd_indexattr); + case INDEX_ATTR_BITMAP_KEY: + return bms_copy(relation->rd_keyattr); + case INDEX_ATTR_BITMAP_IDENTITY_KEY: + return bms_copy(relation->rd_idattr); default: elog(ERROR, "unknown attrKind %u", attrKind); } + } /* Fast path if definitely no indexes */ if (!RelationGetForm(relation)->relhasindex) @@ -4005,6 +4034,15 @@ RelationGetIndexAttrBitmap(Relation relation, IndexAttrBitmapKind attrKind) return NULL; /* + * Copy the 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.) + */ + relreplindex = relation->rd_replidindex; + + /* * For each index, add referenced attributes to indexattrs. * * Note: we consider all indexes returned by RelationGetIndexList, even if @@ -4026,7 +4064,6 @@ RelationGetIndexAttrBitmap(Relation relation, IndexAttrBitmapKind attrKind) bool isKey; /* candidate key */ bool isIDKey; /* replica identity index */ - indexDesc = index_open(indexOid, AccessShareLock); /* Extract index key information from the index's pg_index row */ @@ -4038,7 +4075,7 @@ RelationGetIndexAttrBitmap(Relation relation, IndexAttrBitmapKind attrKind) indexInfo->ii_Predicate == NIL; /* Is this index the configured (or default) replica identity? */ - isIDKey = indexOid == relation->rd_replidindex; + isIDKey = (indexOid == relreplindex); /* Collect simple attribute references */ for (i = 0; i < indexInfo->ii_NumIndexAttrs; i++) @@ -4050,13 +4087,13 @@ RelationGetIndexAttrBitmap(Relation relation, IndexAttrBitmapKind attrKind) indexattrs = bms_add_member(indexattrs, attrnum - FirstLowInvalidHeapAttributeNumber); - if (isIDKey) - idindexattrs = bms_add_member(idindexattrs, - attrnum - FirstLowInvalidHeapAttributeNumber); - if (isKey) uindexattrs = bms_add_member(uindexattrs, attrnum - FirstLowInvalidHeapAttributeNumber); + + if (isIDKey) + idindexattrs = bms_add_member(idindexattrs, + attrnum - FirstLowInvalidHeapAttributeNumber); } } @@ -4071,22 +4108,28 @@ RelationGetIndexAttrBitmap(Relation relation, IndexAttrBitmapKind attrKind) list_free(indexoidlist); - /* Now save a copy of the bitmap in the relcache entry. */ + /* + * 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. + */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); - relation->rd_indexattr = bms_copy(indexattrs); relation->rd_keyattr = bms_copy(uindexattrs); relation->rd_idattr = bms_copy(idindexattrs); + relation->rd_indexattr = bms_copy(indexattrs); MemoryContextSwitchTo(oldcxt); /* We return our original working copy for caller to play with */ switch (attrKind) { - case INDEX_ATTR_BITMAP_IDENTITY_KEY: - return idindexattrs; - case INDEX_ATTR_BITMAP_KEY: - return uindexattrs; case INDEX_ATTR_BITMAP_ALL: return indexattrs; + case INDEX_ATTR_BITMAP_KEY: + return uindexattrs; + case INDEX_ATTR_BITMAP_IDENTITY_KEY: + return idindexattrs; default: elog(ERROR, "unknown attrKind %u", attrKind); return NULL; @@ -4630,8 +4673,11 @@ load_relcache_init_file(bool shared) rel->rd_refcnt = 0; rel->rd_indexvalid = 0; rel->rd_indexlist = NIL; - rel->rd_indexattr = NULL; rel->rd_oidindex = InvalidOid; + rel->rd_replidindex = InvalidOid; + rel->rd_indexattr = NULL; + rel->rd_keyattr = NULL; + rel->rd_idattr = NULL; rel->rd_createSubid = InvalidSubTransactionId; rel->rd_newRelfilenodeSubid = InvalidSubTransactionId; rel->rd_amcache = NULL; |