catcache.c
来自「PostgreSQL7.4.6 for Linux」· C语言 代码 · 共 1,777 行 · 第 1/3 页
C
1,777 行
/*------------------------------------------------------------------------- * * catcache.c * System catalog cache for tuples matching a key. * * Portions Copyright (c) 1996-2003, 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.108 2003/08/04 02:40:06 momjian Exp $ * *------------------------------------------------------------------------- */#include "postgres.h"#include "access/genam.h"#include "access/hash.h"#include "access/heapam.h"#include "access/valid.h"#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#include "storage/ipc.h" /* for on_proc_exit */#endif#include "utils/builtins.h"#include "utils/fmgroids.h"#include "utils/catcache.h"#include "utils/relcache.h"#include "utils/syscache.h" /* #define CACHEDEBUG */ /* turns DEBUG elogs on *//* * Constants related to size of the catcache. * * NCCBUCKETS must be a power of two and must be less than 64K (because * SharedInvalCatcacheMsg crams hash indexes into a uint16 field). In * practice it should be a lot less, anyway, to avoid chewing up too much * space on hash bucket headers. * * MAXCCTUPLES could be as small as a few hundred, if per-backend memory * consumption is at a premium. */#define NCCBUCKETS 256 /* Hash buckets per CatCache */#define MAXCCTUPLES 5000 /* Maximum # of tuples in all caches *//* * Given a hash value and the size of the hash table, find the bucket * in which the hash value belongs. Since the hash table must contain * a power-of-2 number of elements, this is a simple bitmask. */#define HASH_INDEX(h, sz) ((Index) ((h) & ((sz) - 1)))/* * variables, macros and other stuff */#ifdef CACHEDEBUG#define CACHE1_elog(a,b) elog(a,b)#define CACHE2_elog(a,b,c) elog(a,b,c)#define CACHE3_elog(a,b,c,d) elog(a,b,c,d)#define CACHE4_elog(a,b,c,d,e) elog(a,b,c,d,e)#define CACHE5_elog(a,b,c,d,e,f) elog(a,b,c,d,e,f)#define CACHE6_elog(a,b,c,d,e,f,g) elog(a,b,c,d,e,f,g)#else#define CACHE1_elog(a,b)#define CACHE2_elog(a,b,c)#define CACHE3_elog(a,b,c,d)#define CACHE4_elog(a,b,c,d,e)#define CACHE5_elog(a,b,c,d,e,f)#define CACHE6_elog(a,b,c,d,e,f,g)#endif/* Cache management header --- pointer is NULL until created */static CatCacheHeader *CacheHdr = NULL;static uint32 CatalogCacheComputeHashValue(CatCache *cache, int nkeys, ScanKey cur_skey);static uint32 CatalogCacheComputeTupleHashValue(CatCache *cache, HeapTuple tuple);#ifdef CATCACHE_STATSstatic void CatCachePrintStats(void);#endifstatic void CatCacheRemoveCTup(CatCache *cache, CatCTup *ct);static void CatCacheRemoveCList(CatCache *cache, CatCList *cl);static void CatalogCacheInitializeCache(CatCache *cache);static CatCTup *CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, uint32 hashValue, Index hashIndex, bool negative);static HeapTuple build_dummy_tuple(CatCache *cache, int nkeys, ScanKey skeys);/* * internal support functions *//* * 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 voidGetCCHashEqFuncs(Oid keytype, PGFunction *hashfunc, RegProcedure *eqfunc){ switch (keytype) { case BOOLOID: *hashfunc = hashchar; *eqfunc = F_BOOLEQ; break; case CHAROID: *hashfunc = hashchar; *eqfunc = F_CHAREQ; break; case NAMEOID: *hashfunc = hashname; *eqfunc = F_NAMEEQ; break; case INT2OID: *hashfunc = hashint2; *eqfunc = F_INT2EQ; break; case INT2VECTOROID: *hashfunc = hashint2vector; *eqfunc = F_INT2VECTOREQ; break; case INT4OID: *hashfunc = hashint4; *eqfunc = F_INT4EQ; break; case TEXTOID: *hashfunc = hashtext; *eqfunc = F_TEXTEQ; break; case OIDOID: case REGPROCOID: case REGPROCEDUREOID: case REGOPEROID: case REGOPERATOROID: case REGCLASSOID: case REGTYPEOID: *hashfunc = hashoid; *eqfunc = F_OIDEQ; break; case OIDVECTOROID: *hashfunc = hashoidvector; *eqfunc = F_OIDVECTOREQ; break; default: elog(FATAL, "type %u not supported as catcache key", keytype); break; }}/* * CatalogCacheComputeHashValue * * Compute the hash value associated with a given set of lookup keys */static uint32CatalogCacheComputeHashValue(CatCache *cache, int nkeys, ScanKey cur_skey){ uint32 hashValue = 0; CACHE4_elog(DEBUG2, "CatalogCacheComputeHashValue %s %d %p", cache->cc_relname, nkeys, cache); switch (nkeys) { case 4: hashValue ^= DatumGetUInt32(DirectFunctionCall1(cache->cc_hashfunc[3], cur_skey[3].sk_argument)) << 9; /* FALLTHROUGH */ case 3: hashValue ^= DatumGetUInt32(DirectFunctionCall1(cache->cc_hashfunc[2], cur_skey[2].sk_argument)) << 6; /* FALLTHROUGH */ case 2: hashValue ^= DatumGetUInt32(DirectFunctionCall1(cache->cc_hashfunc[1], cur_skey[1].sk_argument)) << 3; /* FALLTHROUGH */ case 1: hashValue ^= DatumGetUInt32(DirectFunctionCall1(cache->cc_hashfunc[0], cur_skey[0].sk_argument)); break; default: elog(FATAL, "wrong number of hash keys: %d", nkeys); break; } return hashValue;}/* * CatalogCacheComputeTupleHashValue * * Compute the hash value associated with a given tuple to be cached */static uint32CatalogCacheComputeTupleHashValue(CatCache *cache, HeapTuple tuple){ ScanKeyData cur_skey[4]; bool isNull = false; /* Copy pre-initialized overhead data for scankey */ memcpy(cur_skey, cache->cc_skey, sizeof(cur_skey)); /* Now extract key fields from tuple, insert into scankey */ switch (cache->cc_nkeys) { case 4: cur_skey[3].sk_argument = (cache->cc_key[3] == ObjectIdAttributeNumber) ? ObjectIdGetDatum(HeapTupleGetOid(tuple)) : fastgetattr(tuple, cache->cc_key[3], cache->cc_tupdesc, &isNull); Assert(!isNull); /* FALLTHROUGH */ case 3: cur_skey[2].sk_argument = (cache->cc_key[2] == ObjectIdAttributeNumber) ? ObjectIdGetDatum(HeapTupleGetOid(tuple)) : fastgetattr(tuple, cache->cc_key[2], cache->cc_tupdesc, &isNull); Assert(!isNull); /* FALLTHROUGH */ case 2: cur_skey[1].sk_argument = (cache->cc_key[1] == ObjectIdAttributeNumber) ? ObjectIdGetDatum(HeapTupleGetOid(tuple)) : fastgetattr(tuple, cache->cc_key[1], cache->cc_tupdesc, &isNull); Assert(!isNull); /* FALLTHROUGH */ case 1: cur_skey[0].sk_argument = (cache->cc_key[0] == ObjectIdAttributeNumber) ? ObjectIdGetDatum(HeapTupleGetOid(tuple)) : fastgetattr(tuple, cache->cc_key[0], cache->cc_tupdesc, &isNull); Assert(!isNull); break; default: elog(FATAL, "wrong number of hash keys: %d", cache->cc_nkeys); break; } return CatalogCacheComputeHashValue(cache, cache->cc_nkeys, cur_skey);}#ifdef CATCACHE_STATSstatic voidCatCachePrintStats(void){ CatCache *cache; long cc_searches = 0; long cc_hits = 0; long cc_neg_hits = 0; long cc_newloads = 0; long cc_invals = 0; long cc_discards = 0; long cc_lsearches = 0; long cc_lhits = 0; 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", cache->cc_relname, cache->cc_indname, cache->cc_ntup, cache->cc_searches, cache->cc_hits, cache->cc_neg_hits, cache->cc_hits + cache->cc_neg_hits, cache->cc_newloads, cache->cc_searches - cache->cc_hits - cache->cc_neg_hits - cache->cc_newloads, cache->cc_searches - cache->cc_hits - cache->cc_neg_hits, cache->cc_invals, cache->cc_discards, cache->cc_lsearches, cache->cc_lhits); cc_searches += cache->cc_searches; cc_hits += cache->cc_hits; cc_neg_hits += cache->cc_neg_hits; cc_newloads += cache->cc_newloads; cc_invals += cache->cc_invals; cc_discards += cache->cc_discards; 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", CacheHdr->ch_ntup, cc_searches, cc_hits, cc_neg_hits, cc_hits + cc_neg_hits, cc_newloads, cc_searches - cc_hits - cc_neg_hits - cc_newloads, cc_searches - cc_hits - cc_neg_hits, cc_invals, cc_discards, cc_lsearches, cc_lhits);}#endif /* CATCACHE_STATS *//* * CatCacheRemoveCTup * * Unlink and delete the given cache entry * * NB: if it is a member of a CatCList, the CatCList is deleted too. */static voidCatCacheRemoveCTup(CatCache *cache, CatCTup *ct){ Assert(ct->refcount == 0); Assert(ct->my_cache == cache); if (ct->c_list) CatCacheRemoveCList(cache, ct->c_list); /* delink from linked lists */ DLRemove(&ct->lrulist_elem); DLRemove(&ct->cache_elem); /* free associated tuple data */ if (ct->tuple.t_data != NULL) pfree(ct->tuple.t_data); pfree(ct); --cache->cc_ntup; --CacheHdr->ch_ntup;}/* * CatCacheRemoveCList * * Unlink and delete the given cache list entry */static voidCatCacheRemoveCList(CatCache *cache, CatCList *cl){ int i; Assert(cl->refcount == 0); Assert(cl->my_cache == cache); /* delink from member tuples */ for (i = cl->n_members; --i >= 0;) { CatCTup *ct = cl->members[i]; Assert(ct->c_list == cl); ct->c_list = NULL; } /* delink from linked list */ DLRemove(&cl->cache_elem); /* free associated tuple data */ if (cl->tuple.t_data != NULL) pfree(cl->tuple.t_data); pfree(cl);}/* * CatalogCacheIdInvalidate * * Invalidate entries in the specified cache, given a hash value and * item pointer. Positive entries are deleted if they match the item * pointer. Negative entries must be deleted if they match the hash * value (since we do not have the exact key of the tuple that's being * inserted). But this should only rarely result in loss of a cache * entry that could have been kept. * * Note that it's not very relevant whether the tuple identified by * the item pointer is being inserted or deleted. We don't expect to * find matching positive entries in the one case, and we don't expect * to find matching negative entries in the other; but we will do the * right things in any case. * * This routine is only quasi-public: it should only be used by inval.c. */voidCatalogCacheIdInvalidate(int cacheId, uint32 hashValue, ItemPointer pointer){ CatCache *ccp; /* * sanity checks */ Assert(ItemPointerIsValid(pointer)); CACHE1_elog(DEBUG2, "CatalogCacheIdInvalidate: called"); /* * inspect caches to find the proper cache */ for (ccp = CacheHdr->ch_caches; ccp; ccp = ccp->cc_next) { Index hashIndex; Dlelem *elt, *nextelt; if (cacheId != ccp->id) continue; /* * We don't bother to check whether the cache has finished * initialization yet; if not, there will be no entries in it so * no problem. */ /* * Invalidate *all* CatCLists in this cache; it's too hard to tell * which searches might still be correct, so just zap 'em all. */ for (elt = DLGetHead(&ccp->cc_lists); elt; elt = nextelt) { CatCList *cl = (CatCList *) DLE_VAL(elt); nextelt = DLGetSucc(elt); if (cl->refcount > 0) cl->dead = true; else CatCacheRemoveCList(ccp, cl); } /* * inspect the proper hash bucket for tuple matches */ hashIndex = HASH_INDEX(hashValue, ccp->cc_nbuckets); for (elt = DLGetHead(&ccp->cc_bucket[hashIndex]); elt; elt = nextelt) { CatCTup *ct = (CatCTup *) DLE_VAL(elt); nextelt = DLGetSucc(elt); if (hashValue != ct->hash_value) continue; /* ignore non-matching hash values */ if (ct->negative || ItemPointerEquals(pointer, &ct->tuple.t_self)) { if (ct->refcount > 0) ct->dead = true; else CatCacheRemoveCTup(ccp, ct); CACHE1_elog(DEBUG2, "CatalogCacheIdInvalidate: invalidated");#ifdef CATCACHE_STATS ccp->cc_invals++;#endif /* could be multiple matches, so keep looking! */ } } break; /* need only search this one cache */ }}/* ---------------------------------------------------------------- * public functions * ---------------------------------------------------------------- *//* * Standard routine for creating cache context if it doesn't exist yet * * There are a lot of places (probably far more than necessary) that check * whether CacheMemoryContext exists yet and want to create it if not. * We centralize knowledge of exactly how to create it here. */voidCreateCacheMemoryContext(void){ /* * Purely for paranoia, check that context doesn't exist; caller * probably did so already. */ if (!CacheMemoryContext) CacheMemoryContext = AllocSetContextCreate(TopMemoryContext, "CacheMemoryContext", ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE);}/* * AtEOXact_CatCache * * Clean up catcaches at end of 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. */voidAtEOXact_CatCache(bool isCommit){ CatCache *ccp; Dlelem *elt, *nextelt; /* * First clean up CatCLists */ for (ccp = CacheHdr->ch_caches; ccp; ccp = ccp->cc_next) { for (elt = DLGetHead(&ccp->cc_lists); elt; elt = nextelt) { CatCList *cl = (CatCList *) DLE_VAL(elt); nextelt = DLGetSucc(elt); 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); cl->refcount = 0; } /* Clean up any now-deletable dead entries */ if (cl->dead) CatCacheRemoveCList(ccp, cl); } } /* * 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) { 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); ct->refcount = 0; } /* Clean up any now-deletable dead entries */ if (ct->dead) CatCacheRemoveCTup(ct->my_cache, ct); }}
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?