X-Git-Url: https://granicus.if.org/sourcecode?a=blobdiff_plain;f=src%2Fbackend%2Futils%2Fcache%2Fcatcache.c;h=abe0aa060c0a4eb7cd47082f66261773dec4f1f7;hb=278bd0cc224615e41219e24304d04898941ab046;hp=65d716f6ba5bc2c92e2d96c733efcd632188d20e;hpb=98b6f37e47a9eb3540493caabf57d0f952ebdc6d;p=postgresql diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index 65d716f6ba..abe0aa060c 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -3,12 +3,12 @@ * catcache.c * System catalog cache for tuples matching a key. * - * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group + * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/cache/catcache.c,v 1.103 2003/05/27 17:49:46 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/utils/cache/catcache.c,v 1.121 2005/05/06 17:24:54 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -21,7 +21,6 @@ #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" #include "catalog/pg_type.h" -#include "catalog/catname.h" #include "catalog/indexing.h" #include "miscadmin.h" #ifdef CATCACHE_STATS @@ -30,11 +29,13 @@ #include "utils/builtins.h" #include "utils/fmgroids.h" #include "utils/catcache.h" +#include "utils/memutils.h" #include "utils/relcache.h" +#include "utils/resowner.h" #include "utils/syscache.h" - /* #define CACHEDEBUG */ /* turns DEBUG elogs on */ +/* #define CACHEDEBUG */ /* turns DEBUG elogs on */ /* * Constants related to size of the catcache. @@ -81,22 +82,6 @@ /* Cache management header --- pointer is NULL until created */ static CatCacheHeader *CacheHdr = NULL; -/* - * EQPROC is used in CatalogCacheInitializeCache to find the equality - * functions for system types that are used as cache key fields. - * See also GetCCHashFunc, which should support the same set of types. - * - * XXX this should be replaced by catalog lookups, - * but that seems to pose considerable risk of circularity... - */ -static const Oid eqproc[] = { - F_BOOLEQ, InvalidOid, F_CHAREQ, F_NAMEEQ, InvalidOid, - F_INT2EQ, F_INT2VECTOREQ, F_INT4EQ, F_OIDEQ, F_TEXTEQ, - F_OIDEQ, InvalidOid, InvalidOid, InvalidOid, F_OIDVECTOREQ -}; - -#define EQPROC(SYSTEMTYPEOID) eqproc[(SYSTEMTYPEOID)-BOOLOID] - static uint32 CatalogCacheComputeHashValue(CatCache *cache, int nkeys, ScanKey cur_skey); @@ -119,24 +104,46 @@ static HeapTuple build_dummy_tuple(CatCache *cache, int nkeys, ScanKey skeys); * internal support functions */ -static PGFunction -GetCCHashFunc(Oid keytype) +/* + * Look up the hash and equality functions for system types that are used + * as cache key fields. + * + * XXX this should be replaced by catalog lookups, + * but that seems to pose considerable risk of circularity... + */ +static void +GetCCHashEqFuncs(Oid keytype, PGFunction *hashfunc, RegProcedure *eqfunc) { switch (keytype) { case BOOLOID: + *hashfunc = hashchar; + *eqfunc = F_BOOLEQ; + break; case CHAROID: - return hashchar; + *hashfunc = hashchar; + *eqfunc = F_CHAREQ; + break; case NAMEOID: - return hashname; + *hashfunc = hashname; + *eqfunc = F_NAMEEQ; + break; case INT2OID: - return hashint2; + *hashfunc = hashint2; + *eqfunc = F_INT2EQ; + break; case INT2VECTOROID: - return hashint2vector; + *hashfunc = hashint2vector; + *eqfunc = F_INT2VECTOREQ; + break; case INT4OID: - return hashint4; + *hashfunc = hashint4; + *eqfunc = F_INT4EQ; + break; case TEXTOID: - return hashvarlena; + *hashfunc = hashtext; + *eqfunc = F_TEXTEQ; + break; case OIDOID: case REGPROCOID: case REGPROCEDUREOID: @@ -144,13 +151,16 @@ GetCCHashFunc(Oid keytype) case REGOPERATOROID: case REGCLASSOID: case REGTYPEOID: - return hashoid; + *hashfunc = hashoid; + *eqfunc = F_OIDEQ; + break; case OIDVECTOROID: - return hashoidvector; + *hashfunc = hashoidvector; + *eqfunc = F_OIDVECTOREQ; + break; default: - elog(FATAL, "GetCCHashFunc: type %u unsupported as catcache key", - keytype); - return (PGFunction) NULL; + elog(FATAL, "type %u not supported as catcache key", keytype); + break; } } @@ -192,7 +202,7 @@ CatalogCacheComputeHashValue(CatCache *cache, int nkeys, ScanKey cur_skey) cur_skey[0].sk_argument)); break; default: - elog(FATAL, "CCComputeHashValue: %d nkeys", nkeys); + elog(FATAL, "wrong number of hash keys: %d", nkeys); break; } @@ -257,8 +267,7 @@ CatalogCacheComputeTupleHashValue(CatCache *cache, HeapTuple tuple) Assert(!isNull); break; default: - elog(FATAL, "CCComputeTupleHashValue: %d cc_nkeys", - cache->cc_nkeys); + elog(FATAL, "wrong number of hash keys: %d", cache->cc_nkeys); break; } @@ -281,16 +290,16 @@ CatCachePrintStats(void) long cc_lsearches = 0; long cc_lhits = 0; - elog(DEBUG2, "Catcache stats dump: %d/%d tuples in catcaches", + elog(DEBUG2, "catcache stats dump: %d/%d tuples in catcaches", CacheHdr->ch_ntup, CacheHdr->ch_maxtup); for (cache = CacheHdr->ch_caches; cache; cache = cache->cc_next) { if (cache->cc_ntup == 0 && cache->cc_searches == 0) continue; /* don't print unused caches */ - elog(DEBUG2, "Catcache %s/%s: %d tup, %ld srch, %ld+%ld=%ld hits, %ld+%ld=%ld loads, %ld invals, %ld discards, %ld lsrch, %ld lhits", + elog(DEBUG2, "catcache %s/%u: %d tup, %ld srch, %ld+%ld=%ld hits, %ld+%ld=%ld loads, %ld invals, %ld discards, %ld lsrch, %ld lhits", cache->cc_relname, - cache->cc_indname, + cache->cc_indexoid, cache->cc_ntup, cache->cc_searches, cache->cc_hits, @@ -312,7 +321,7 @@ CatCachePrintStats(void) cc_lsearches += cache->cc_lsearches; cc_lhits += cache->cc_lhits; } - elog(DEBUG2, "Catcache totals: %d tup, %ld srch, %ld+%ld=%ld hits, %ld+%ld=%ld loads, %ld invals, %ld discards, %ld lsrch, %ld lhits", + elog(DEBUG2, "catcache totals: %d tup, %ld srch, %ld+%ld=%ld hits, %ld+%ld=%ld loads, %ld invals, %ld discards, %ld lsrch, %ld lhits", CacheHdr->ch_ntup, cc_searches, cc_hits, @@ -519,7 +528,7 @@ CreateCacheMemoryContext(void) /* * AtEOXact_CatCache * - * Clean up catcaches at end of transaction (either commit or abort) + * 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. @@ -547,8 +556,7 @@ AtEOXact_CatCache(bool isCommit) if (cl->refcount != 0) { if (isCommit) - elog(WARNING, "Cache reference leak: cache %s (%d), list %p has count %d", - ccp->cc_relname, ccp->id, cl, cl->refcount); + PrintCatCacheListLeakWarning(cl); cl->refcount = 0; } @@ -570,10 +578,7 @@ AtEOXact_CatCache(bool isCommit) if (ct->refcount != 0) { if (isCommit) - elog(WARNING, "Cache reference leak: cache %s (%d), tuple %u has count %d", - ct->my_cache->cc_relname, ct->my_cache->id, - HeapTupleGetOid(&ct->tuple), - ct->refcount); + PrintCatCacheLeakWarning(&ct->tuple); ct->refcount = 0; } @@ -758,8 +763,9 @@ CatalogCacheFlushRelation(Oid relId) #ifdef CACHEDEBUG #define InitCatCache_DEBUG2 \ do { \ - elog(DEBUG2, "InitCatCache: rel=%s id=%d nkeys=%d size=%d\n", \ - cp->cc_relname, cp->id, cp->cc_nkeys, cp->cc_nbuckets); \ + elog(DEBUG2, "InitCatCache: rel=%u ind=%u id=%d nkeys=%d size=%d", \ + cp->cc_reloid, cp->cc_indexoid, cp->id, \ + cp->cc_nkeys, cp->cc_nbuckets); \ } while(0) #else @@ -768,8 +774,8 @@ do { \ CatCache * InitCatCache(int id, - const char *relname, - const char *indname, + Oid reloid, + Oid indexoid, int reloidattr, int nkeys, const int *key) @@ -816,9 +822,9 @@ InitCatCache(int id, * other internal fields. But don't open the relation yet. */ cp->id = id; - cp->cc_relname = relname; - cp->cc_indname = indname; - cp->cc_reloid = InvalidOid; /* temporary */ + cp->cc_relname = "(not known yet)"; + cp->cc_reloid = reloid; + cp->cc_indexoid = indexoid; cp->cc_relisshared = false; /* temporary */ cp->cc_tupdesc = (TupleDesc) NULL; cp->cc_reloidattr = reloidattr; @@ -856,9 +862,9 @@ InitCatCache(int id, * that the relcache entry can be opened at this point! */ #ifdef CACHEDEBUG -#define CatalogCacheInitializeCache_DEBUG2 \ - elog(DEBUG2, "CatalogCacheInitializeCache: cache @%p %s", cache, \ - cache->cc_relname) +#define CatalogCacheInitializeCache_DEBUG1 \ + elog(DEBUG2, "CatalogCacheInitializeCache: cache @%p rel=%u", cache, \ + cache->cc_reloid) #define CatalogCacheInitializeCache_DEBUG2 \ do { \ @@ -873,7 +879,7 @@ do { \ } while(0) #else -#define CatalogCacheInitializeCache_DEBUG2 +#define CatalogCacheInitializeCache_DEBUG1 #define CatalogCacheInitializeCache_DEBUG2 #endif @@ -885,13 +891,13 @@ CatalogCacheInitializeCache(CatCache *cache) TupleDesc tupdesc; int i; - CatalogCacheInitializeCache_DEBUG2; + CatalogCacheInitializeCache_DEBUG1; /* * Open the relation without locking --- we only need the tupdesc, * which we assume will never change ... */ - relation = heap_openr(cache->cc_relname, NoLock); + relation = heap_open(cache->cc_reloid, NoLock); Assert(RelationIsValid(relation)); /* @@ -908,9 +914,10 @@ CatalogCacheInitializeCache(CatCache *cache) tupdesc = CreateTupleDescCopyConstr(RelationGetDescr(relation)); /* - * get the relation's OID and relisshared flag, too + * save the relation's name and relisshared flag, too (cc_relname + * is used only for debugging purposes) */ - cache->cc_reloid = RelationGetRelid(relation); + cache->cc_relname = pstrdup(RelationGetRelationName(relation)); cache->cc_relisshared = RelationGetForm(relation)->relisshared; /* @@ -929,6 +936,7 @@ CatalogCacheInitializeCache(CatCache *cache) for (i = 0; i < cache->cc_nkeys; ++i) { Oid keytype; + RegProcedure eqfunc; CatalogCacheInitializeCache_DEBUG2; @@ -937,27 +945,31 @@ CatalogCacheInitializeCache(CatCache *cache) else { if (cache->cc_key[i] != ObjectIdAttributeNumber) - elog(FATAL, "CatalogCacheInit: only sys attr supported is OID"); + elog(FATAL, "only sys attr supported in caches is OID"); keytype = OIDOID; } - cache->cc_hashfunc[i] = GetCCHashFunc(keytype); + GetCCHashEqFuncs(keytype, + &cache->cc_hashfunc[i], + &eqfunc); cache->cc_isname[i] = (keytype == NAMEOID); /* - * If GetCCHashFunc liked the type, safe to index into eqproc[] + * Do equality-function lookup (we assume this won't need a + * catalog lookup for any supported type) */ - cache->cc_skey[i].sk_procedure = EQPROC(keytype); - - /* Do function lookup */ - fmgr_info_cxt(cache->cc_skey[i].sk_procedure, + fmgr_info_cxt(eqfunc, &cache->cc_skey[i].sk_func, CacheMemoryContext); /* Initialize sk_attno suitably for HeapKeyTest() and heap scans */ cache->cc_skey[i].sk_attno = cache->cc_key[i]; + /* Fill in sk_strategy as well --- always standard equality */ + cache->cc_skey[i].sk_strategy = BTEqualStrategyNumber; + cache->cc_skey[i].sk_subtype = InvalidOid; + CACHE4_elog(DEBUG2, "CatalogCacheInit %s %d %p", cache->cc_relname, i, @@ -989,7 +1001,7 @@ InitCatCachePhase2(CatCache *cache) { Relation idesc; - idesc = index_openr(cache->cc_indname); + idesc = index_open(cache->cc_indexoid); index_close(idesc); } } @@ -1148,7 +1160,9 @@ SearchCatCache(CatCache *cache, */ if (!ct->negative) { + ResourceOwnerEnlargeCatCacheRefs(CurrentResourceOwner); ct->refcount++; + ResourceOwnerRememberCatCacheRef(CurrentResourceOwner, &ct->tuple); CACHE3_elog(DEBUG2, "SearchCatCache(%s): found in bucket %d", cache->cc_relname, hashIndex); @@ -1190,7 +1204,7 @@ SearchCatCache(CatCache *cache, relation = heap_open(cache->cc_reloid, AccessShareLock); scandesc = systable_beginscan(relation, - cache->cc_indname, + cache->cc_indexoid, IndexScanOK(cache, cur_skey), SnapshotNow, cache->cc_nkeys, @@ -1203,6 +1217,10 @@ SearchCatCache(CatCache *cache, ct = CatalogCacheCreateEntry(cache, ntp, hashValue, hashIndex, false); + /* immediately set the refcount to 1 */ + ResourceOwnerEnlargeCatCacheRefs(CurrentResourceOwner); + ct->refcount++; + ResourceOwnerRememberCatCacheRef(CurrentResourceOwner, &ct->tuple); break; /* assume only one match */ } @@ -1214,9 +1232,17 @@ SearchCatCache(CatCache *cache, * If tuple was not found, we need to build a negative cache entry * containing a fake tuple. The fake tuple has the correct key * columns, but nulls everywhere else. + * + * In bootstrap mode, we don't build negative entries, because the + * cache invalidation mechanism isn't alive and can't clear them + * if the tuple gets created later. (Bootstrap doesn't do UPDATEs, + * so it doesn't need cache inval for that.) */ if (ct == NULL) { + if (IsBootstrapProcessingMode()) + return NULL; + ntp = build_dummy_tuple(cache, cache->cc_nkeys, cur_skey); ct = CatalogCacheCreateEntry(cache, ntp, hashValue, hashIndex, @@ -1229,10 +1255,9 @@ SearchCatCache(CatCache *cache, cache->cc_relname, hashIndex); /* - * We are not returning the new entry to the caller, so reset its - * refcount. + * We are not returning the negative entry to the caller, so leave + * its refcount zero. */ - ct->refcount = 0; /* negative entries never have refs */ return NULL; } @@ -1271,6 +1296,7 @@ ReleaseCatCache(HeapTuple tuple) Assert(ct->refcount > 0); ct->refcount--; + ResourceOwnerForgetCatCacheRef(CurrentResourceOwner, &ct->tuple); if (ct->refcount == 0 #ifndef CATCACHE_FORCE_RELEASE @@ -1304,6 +1330,7 @@ SearchCatCacheList(CatCache *cache, CatCList *cl; CatCTup *ct; List *ctlist; + ListCell *ctlist_item; int nmembers; Relation relation; SysScanDesc scandesc; @@ -1377,7 +1404,10 @@ SearchCatCacheList(CatCache *cache, * do not move the members to the fronts of their hashbucket * lists, however, since there's no point in that unless they are * searched for individually.) Also bump the members' refcounts. + * (member refcounts are NOT registered separately with the + * resource owner.) */ + ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); for (i = 0; i < cl->n_members; i++) { cl->members[i]->refcount++; @@ -1387,6 +1417,7 @@ SearchCatCacheList(CatCache *cache, /* Bump the list's refcount and return it */ cl->refcount++; + ResourceOwnerRememberCatCacheListRef(CurrentResourceOwner, cl); CACHE2_elog(DEBUG2, "SearchCatCacheList(%s): found list", cache->cc_relname); @@ -1406,7 +1437,7 @@ SearchCatCacheList(CatCache *cache, relation = heap_open(cache->cc_reloid, AccessShareLock); scandesc = systable_beginscan(relation, - cache->cc_indname, + cache->cc_indexoid, true, SnapshotNow, nkeys, @@ -1452,9 +1483,7 @@ SearchCatCacheList(CatCache *cache, if (ct->c_list) continue; - /* Found a match, so bump its refcount and move to front */ - ct->refcount++; - + /* Found a match, so move it to front */ DLMoveToFront(&ct->lrulist_elem); break; @@ -1468,7 +1497,17 @@ SearchCatCacheList(CatCache *cache, false); } - ctlist = lcons(ct, ctlist); + /* + * 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. + */ + ct->refcount++; + ctlist = lappend(ctlist, ct); nmembers++; } @@ -1489,23 +1528,25 @@ SearchCatCacheList(CatCache *cache, cl->cl_magic = CL_MAGIC; cl->my_cache = cache; - DLInitElem(&cl->cache_elem, (void *) cl); - cl->refcount = 1; /* count this first reference */ + DLInitElem(&cl->cache_elem, cl); + cl->refcount = 0; /* for the moment */ cl->dead = false; cl->ordered = ordered; cl->nkeys = nkeys; cl->hash_value = lHashValue; cl->n_members = nmembers; - /* The list is backwards because we built it with lcons */ - for (i = nmembers; --i >= 0;) + + Assert(nmembers == list_length(ctlist)); + ctlist_item = list_head(ctlist); + for (i = 0; i < nmembers; i++) { - cl->members[i] = ct = (CatCTup *) lfirst(ctlist); + 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 = lnext(ctlist); + ctlist_item = lnext(ctlist_item); } DLAddHead(&cache->cc_lists, &cl->cache_elem); @@ -1513,6 +1554,11 @@ SearchCatCacheList(CatCache *cache, 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); + return cl; } @@ -1544,6 +1590,7 @@ ReleaseCatCacheList(CatCList *list) } list->refcount--; + ResourceOwnerForgetCatCacheListRef(CurrentResourceOwner, list); if (list->refcount == 0 #ifndef CATCACHE_FORCE_RELEASE @@ -1557,7 +1604,7 @@ ReleaseCatCacheList(CatCList *list) /* * CatalogCacheCreateEntry * Create a new CatCTup entry, copying the given HeapTuple and other - * supplied data into it. The new entry is given refcount 1. + * supplied data into it. The new entry initially has refcount 0. */ static CatCTup * CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, @@ -1584,7 +1631,7 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, DLInitElem(&ct->lrulist_elem, (void *) ct); DLInitElem(&ct->cache_elem, (void *) ct); ct->c_list = NULL; - ct->refcount = 1; /* count this first reference */ + ct->refcount = 0; /* for the moment */ ct->dead = false; ct->negative = negative; ct->hash_value = hashValue; @@ -1597,8 +1644,8 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, /* * If we've exceeded the desired size of the caches, try to throw away - * the least recently used entry. NB: the newly-built entry cannot - * get thrown away here, because it has positive refcount. + * the least recently used entry. NB: be careful not to throw away + * the newly-built entry... */ if (CacheHdr->ch_ntup > CacheHdr->ch_maxtup) { @@ -1611,7 +1658,7 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, prevelt = DLGetPred(elt); - if (oldct->refcount == 0) + if (oldct->refcount == 0 && oldct != ct) { CACHE2_elog(DEBUG2, "CatCacheCreateEntry(%s): Overflow, LRU removal", cache->cc_relname); @@ -1766,3 +1813,32 @@ PrepareToInvalidateCacheTuple(Relation relation, ccp->cc_relisshared ? (Oid) 0 : MyDatabaseId); } } + + +/* + * Subroutines for warning about reference leaks. These are exported so + * that resowner.c can call them. + */ +void +PrintCatCacheLeakWarning(HeapTuple tuple) +{ + CatCTup *ct = (CatCTup *) (((char *) tuple) - + offsetof(CatCTup, tuple)); + + /* Safety check to ensure we were handed a cache entry */ + Assert(ct->ct_magic == CT_MAGIC); + + elog(WARNING, "cache reference leak: cache %s (%d), tuple %u/%u has count %d", + ct->my_cache->cc_relname, ct->my_cache->id, + ItemPointerGetBlockNumber(&(tuple->t_self)), + ItemPointerGetOffsetNumber(&(tuple->t_self)), + ct->refcount); +} + +void +PrintCatCacheListLeakWarning(CatCList *list) +{ + elog(WARNING, "cache reference leak: cache %s (%d), list %p has count %d", + list->my_cache->cc_relname, list->my_cache->id, + list, list->refcount); +}