diff options
Diffstat (limited to 'src/backend/utils/cache/catcache.c')
-rw-r--r-- | src/backend/utils/cache/catcache.c | 262 |
1 files changed, 136 insertions, 126 deletions
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index abe0aa060c0..24b8836f07b 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/cache/catcache.c,v 1.121 2005/05/06 17:24:54 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/cache/catcache.c,v 1.122 2005/08/08 19:17:22 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -530,62 +530,43 @@ CreateCacheMemoryContext(void) * * Clean up catcaches at end of main transaction (either commit or abort) * - * We scan the caches to reset refcounts to zero. This is of course - * necessary in the abort case, since elog() may have interrupted routines. - * In the commit case, any nonzero counts indicate failure to call - * ReleaseSysCache, so we put out a notice for debugging purposes. + * As of PostgreSQL 8.1, catcache pins should get released by the + * ResourceOwner mechanism. This routine is just a debugging + * cross-check that no pins remain. */ void AtEOXact_CatCache(bool isCommit) { - CatCache *ccp; - Dlelem *elt, - *nextelt; - - /* - * First clean up CatCLists - */ - for (ccp = CacheHdr->ch_caches; ccp; ccp = ccp->cc_next) +#ifdef USE_ASSERT_CHECKING + if (assert_enabled) { - for (elt = DLGetHead(&ccp->cc_lists); elt; elt = nextelt) - { - CatCList *cl = (CatCList *) DLE_VAL(elt); - - nextelt = DLGetSucc(elt); + CatCache *ccp; + Dlelem *elt; - if (cl->refcount != 0) + /* Check CatCLists */ + for (ccp = CacheHdr->ch_caches; ccp; ccp = ccp->cc_next) + { + for (elt = DLGetHead(&ccp->cc_lists); elt; elt = DLGetSucc(elt)) { - if (isCommit) - PrintCatCacheListLeakWarning(cl); - cl->refcount = 0; - } + CatCList *cl = (CatCList *) DLE_VAL(elt); - /* Clean up any now-deletable dead entries */ - if (cl->dead) - CatCacheRemoveCList(ccp, cl); + Assert(cl->cl_magic == CL_MAGIC); + Assert(cl->refcount == 0); + Assert(!cl->dead); + } } - } - - /* - * Now clean up tuples; we can scan them all using the global LRU list - */ - for (elt = DLGetHead(&CacheHdr->ch_lrulist); elt; elt = nextelt) - { - CatCTup *ct = (CatCTup *) DLE_VAL(elt); - - nextelt = DLGetSucc(elt); - if (ct->refcount != 0) + /* Check individual tuples */ + for (elt = DLGetHead(&CacheHdr->ch_lrulist); elt; elt = DLGetSucc(elt)) { - if (isCommit) - PrintCatCacheLeakWarning(&ct->tuple); - ct->refcount = 0; - } + CatCTup *ct = (CatCTup *) DLE_VAL(elt); - /* Clean up any now-deletable dead entries */ - if (ct->dead) - CatCacheRemoveCTup(ct->my_cache, ct); + Assert(ct->ct_magic == CT_MAGIC); + Assert(ct->refcount == 0); + Assert(!ct->dead); + } } +#endif } /* @@ -1329,11 +1310,9 @@ SearchCatCacheList(CatCache *cache, Dlelem *elt; CatCList *cl; CatCTup *ct; - List *ctlist; + List * volatile ctlist; ListCell *ctlist_item; int nmembers; - Relation relation; - SysScanDesc scandesc; bool ordered; HeapTuple ntp; MemoryContext oldcxt; @@ -1433,98 +1412,131 @@ SearchCatCacheList(CatCache *cache, * List was not found in cache, so we have to build it by reading the * relation. For each matching tuple found in the relation, use an * existing cache entry if possible, else build a new one. + * + * We have to bump the member refcounts immediately to ensure they + * won't get dropped from the cache while loading other members. + * We use a PG_TRY block to ensure we can undo those refcounts if + * we get an error before we finish constructing the CatCList. */ - relation = heap_open(cache->cc_reloid, AccessShareLock); - - scandesc = systable_beginscan(relation, - cache->cc_indexoid, - true, - SnapshotNow, - nkeys, - cur_skey); - - /* The list will be ordered iff we are doing an index scan */ - ordered = (scandesc->irel != NULL); + ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); ctlist = NIL; - nmembers = 0; - while (HeapTupleIsValid(ntp = systable_getnext(scandesc))) + PG_TRY(); { - uint32 hashValue; - Index hashIndex; + Relation relation; + SysScanDesc scandesc; - /* - * See if there's an entry for this tuple already. - */ - ct = NULL; - hashValue = CatalogCacheComputeTupleHashValue(cache, ntp); - hashIndex = HASH_INDEX(hashValue, cache->cc_nbuckets); + relation = heap_open(cache->cc_reloid, AccessShareLock); - for (elt = DLGetHead(&cache->cc_bucket[hashIndex]); - elt; - elt = DLGetSucc(elt)) - { - ct = (CatCTup *) DLE_VAL(elt); + scandesc = systable_beginscan(relation, + cache->cc_indexoid, + true, + SnapshotNow, + nkeys, + cur_skey); - if (ct->dead || ct->negative) - continue; /* ignore dead and negative entries */ + /* The list will be ordered iff we are doing an index scan */ + ordered = (scandesc->irel != NULL); - if (ct->hash_value != hashValue) - continue; /* quickly skip entry if wrong hash val */ - - if (!ItemPointerEquals(&(ct->tuple.t_self), &(ntp->t_self))) - continue; /* not same tuple */ + while (HeapTupleIsValid(ntp = systable_getnext(scandesc))) + { + uint32 hashValue; + Index hashIndex; /* - * Found a match, but can't use it if it belongs to another - * list already + * See if there's an entry for this tuple already. */ - if (ct->c_list) - continue; + ct = NULL; + hashValue = CatalogCacheComputeTupleHashValue(cache, ntp); + hashIndex = HASH_INDEX(hashValue, cache->cc_nbuckets); - /* Found a match, so move it to front */ - DLMoveToFront(&ct->lrulist_elem); + for (elt = DLGetHead(&cache->cc_bucket[hashIndex]); + elt; + elt = DLGetSucc(elt)) + { + ct = (CatCTup *) DLE_VAL(elt); - break; - } + if (ct->dead || ct->negative) + continue; /* ignore dead and negative entries */ - if (elt == NULL) - { - /* We didn't find a usable entry, so make a new one */ - ct = CatalogCacheCreateEntry(cache, ntp, - hashValue, hashIndex, - false); + if (ct->hash_value != hashValue) + continue; /* quickly skip entry if wrong hash val */ + + if (!ItemPointerEquals(&(ct->tuple.t_self), &(ntp->t_self))) + continue; /* not same tuple */ + + /* + * Found a match, but can't use it if it belongs to another + * list already + */ + if (ct->c_list) + continue; + + /* Found a match, so move it to front */ + DLMoveToFront(&ct->lrulist_elem); + + break; + } + + if (elt == NULL) + { + /* We didn't find a usable entry, so make a new one */ + ct = CatalogCacheCreateEntry(cache, ntp, + hashValue, hashIndex, + false); + } + + /* Careful here: add entry to ctlist, then bump its refcount */ + ctlist = lappend(ctlist, ct); + ct->refcount++; } + systable_endscan(scandesc); + + heap_close(relation, AccessShareLock); + /* - * We have to bump the member refcounts immediately to ensure they - * won't get dropped from the cache while loading other members. - * If we get an error before we finish constructing the CatCList - * then we will leak those reference counts. This is annoying but - * it has no real consequence beyond possibly generating some - * warning messages at the next transaction commit, so it's not - * worth fixing. + * Now we can build the CatCList entry. First we need a dummy tuple + * containing the key values... */ - ct->refcount++; - ctlist = lappend(ctlist, ct); - nmembers++; - } + ntp = build_dummy_tuple(cache, nkeys, cur_skey); + oldcxt = MemoryContextSwitchTo(CacheMemoryContext); + nmembers = list_length(ctlist); + cl = (CatCList *) + palloc(sizeof(CatCList) + nmembers * sizeof(CatCTup *)); + heap_copytuple_with_tuple(ntp, &cl->tuple); + MemoryContextSwitchTo(oldcxt); + heap_freetuple(ntp); - systable_endscan(scandesc); + /* + * We are now past the last thing that could trigger an elog before + * we have finished building the CatCList and remembering it in the + * resource owner. So it's OK to fall out of the PG_TRY, and indeed + * we'd better do so before we start marking the members as belonging + * to the list. + */ - heap_close(relation, AccessShareLock); + } + PG_CATCH(); + { + foreach(ctlist_item, ctlist) + { + ct = (CatCTup *) lfirst(ctlist_item); + Assert(ct->c_list == NULL); + Assert(ct->refcount > 0); + ct->refcount--; + if (ct->refcount == 0 +#ifndef CATCACHE_FORCE_RELEASE + && ct->dead +#endif + ) + CatCacheRemoveCTup(cache, ct); + } - /* - * Now we can build the CatCList entry. First we need a dummy tuple - * containing the key values... - */ - ntp = build_dummy_tuple(cache, nkeys, cur_skey); - oldcxt = MemoryContextSwitchTo(CacheMemoryContext); - cl = (CatCList *) palloc(sizeof(CatCList) + nmembers * sizeof(CatCTup *)); - heap_copytuple_with_tuple(ntp, &cl->tuple); - MemoryContextSwitchTo(oldcxt); - heap_freetuple(ntp); + PG_RE_THROW(); + } + PG_END_TRY(); cl->cl_magic = CL_MAGIC; cl->my_cache = cache; @@ -1536,29 +1548,27 @@ SearchCatCacheList(CatCache *cache, cl->hash_value = lHashValue; cl->n_members = nmembers; - Assert(nmembers == list_length(ctlist)); - ctlist_item = list_head(ctlist); - for (i = 0; i < nmembers; i++) + i = 0; + foreach(ctlist_item, ctlist) { - cl->members[i] = ct = (CatCTup *) lfirst(ctlist_item); + cl->members[i++] = ct = (CatCTup *) lfirst(ctlist_item); Assert(ct->c_list == NULL); ct->c_list = cl; /* mark list dead if any members already dead */ if (ct->dead) cl->dead = true; - ctlist_item = lnext(ctlist_item); } + Assert(i == nmembers); DLAddHead(&cache->cc_lists, &cl->cache_elem); - CACHE3_elog(DEBUG2, "SearchCatCacheList(%s): made list of %d members", - cache->cc_relname, nmembers); - /* Finally, bump the list's refcount and return it */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); cl->refcount++; ResourceOwnerRememberCatCacheListRef(CurrentResourceOwner, cl); + CACHE3_elog(DEBUG2, "SearchCatCacheList(%s): made list of %d members", + cache->cc_relname, nmembers); + return cl; } |