diff options
Diffstat (limited to 'src/backend/utils/cache/relcache.c')
-rw-r--r-- | src/backend/utils/cache/relcache.c | 143 |
1 files changed, 139 insertions, 4 deletions
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c index ee8b46407e1..23428992724 100644 --- a/src/backend/utils/cache/relcache.c +++ b/src/backend/utils/cache/relcache.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/cache/relcache.c,v 1.205 2004/06/18 06:13:52 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/cache/relcache.c,v 1.206 2004/07/01 00:51:17 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -273,6 +273,8 @@ static void IndexSupportInitialize(Form_pg_index iform, static OpClassCacheEnt *LookupOpclassInfo(Oid operatorClassOid, StrategyNumber numStrats, StrategyNumber numSupport); +static inline void RelationPushReferenceCount(Relation rel); +static inline void RelationPopReferenceCount(Relation rel); /* @@ -1678,6 +1680,8 @@ RelationClearRelation(Relation relation, bool rebuild) list_free(relation->rd_indexlist); if (relation->rd_indexcxt) MemoryContextDelete(relation->rd_indexcxt); + if (relation->rd_prevrefcnt) + pfree(relation->rd_prevrefcnt); /* * If we're really done with the relcache entry, blow it away. But if @@ -1968,7 +1972,7 @@ RelationCacheInvalidate(void) * we must reset refcnts before handling pending invalidations. */ void -AtEOXact_RelationCache(bool commit) +AtEOXact_RelationCache(bool isCommit) { HASH_SEQ_STATUS status; RelIdCacheEnt *idhentry; @@ -1993,7 +1997,7 @@ AtEOXact_RelationCache(bool commit) */ if (relation->rd_isnew) { - if (commit) + if (isCommit) relation->rd_isnew = false; else { @@ -2019,7 +2023,7 @@ AtEOXact_RelationCache(bool commit) */ expected_refcnt = relation->rd_isnailed ? 1 : 0; - if (commit) + if (isCommit) { if (relation->rd_refcnt != expected_refcnt && !IsBootstrapProcessingMode()) @@ -2037,6 +2041,12 @@ AtEOXact_RelationCache(bool commit) } /* + * Reset the refcount stack. Just drop the item count; don't deallocate + * the stack itself so it can be reused by future subtransactions. + */ + relation->rd_numpushed = 0; + + /* * Flush any temporary index list. */ if (relation->rd_indexvalid == 2) @@ -2049,6 +2059,131 @@ AtEOXact_RelationCache(bool commit) } /* + * RelationPushReferenceCount + * + * Push the current reference count into the stack. Don't modify the + * reference count itself. + */ +static inline void +RelationPushReferenceCount(Relation rel) +{ + /* Enlarge the stack if we run out of space. */ + if (rel->rd_numpushed == rel->rd_numalloc) + { + MemoryContext old_cxt = MemoryContextSwitchTo(CacheMemoryContext); + + if (rel->rd_numalloc == 0) + { + rel->rd_numalloc = 8; + rel->rd_prevrefcnt = palloc(rel->rd_numalloc * sizeof(int)); + } + else + { + rel->rd_numalloc *= 2; + rel->rd_prevrefcnt = repalloc(rel->rd_prevrefcnt, rel->rd_numalloc * sizeof(int)); + } + + MemoryContextSwitchTo(old_cxt); + } + + rel->rd_prevrefcnt[rel->rd_numpushed++] = rel->rd_refcnt; +} + +/* + * RelationPopReferenceCount + * + * Pop the latest stored reference count. If there is none, drop it + * to zero; the entry was created in the current subtransaction. + */ +static inline void +RelationPopReferenceCount(Relation rel) +{ + if (rel->rd_numpushed == 0) + { + rel->rd_refcnt = rel->rd_isnailed ? 1 : 0; + return; + } + + rel->rd_refcnt = rel->rd_prevrefcnt[--rel->rd_numpushed]; +} + +/* + * AtEOSubXact_RelationCache + */ +void +AtEOSubXact_RelationCache(bool isCommit) +{ + HASH_SEQ_STATUS status; + RelIdCacheEnt *idhentry; + + /* We'd better not be bootstrapping. */ + Assert(!IsBootstrapProcessingMode()); + + hash_seq_init(&status, RelationIdCache); + + while ((idhentry = (RelIdCacheEnt *) hash_seq_search(&status)) != NULL) + { + Relation relation = idhentry->reldesc; + + /* + * During subtransaction commit, we first check whether the + * current refcount is correct: if there is no item in the stack, + * the relcache entry was created during this subtransaction, it should + * be 0 (or 1 for nailed relations). If the stack has at least one + * item, the expected count is whatever that item is. + */ + if (isCommit) + { + int expected_refcnt; + + if (relation->rd_numpushed == 0) + expected_refcnt = relation->rd_isnailed ? 1 : 0; + else + expected_refcnt = relation->rd_prevrefcnt[relation->rd_numpushed - 1]; + + if (relation->rd_refcnt != expected_refcnt) + { + elog(WARNING, "relcache reference leak: relation \"%s\" has refcnt %d instead of %d", + RelationGetRelationName(relation), + relation->rd_refcnt, expected_refcnt); + } + } + + /* + * On commit, the expected count is stored so there's no harm in + * popping it (and we may need to fix if there was a leak); and during + * abort, the correct refcount has to be restored. + */ + RelationPopReferenceCount(relation); + } +} + +/* + * AtSubStart_RelationCache + * + * At subtransaction start, we push the current reference count into + * the refcount stack, so it can be restored if the subtransaction aborts. + */ +void +AtSubStart_RelationCache(void) +{ + HASH_SEQ_STATUS status; + RelIdCacheEnt *idhentry; + + /* We'd better not be bootstrapping. */ + Assert(!IsBootstrapProcessingMode()); + + hash_seq_init(&status, RelationIdCache); + + while ((idhentry = (RelIdCacheEnt *) hash_seq_search(&status)) != NULL) + { + Relation relation = idhentry->reldesc; + + RelationPushReferenceCount(relation); + } +} + +/* * RelationBuildLocalRelation * Build a relcache entry for an about-to-be-created relation, * and enter it into the relcache. |