📄 catcache.c
字号:
/*------------------------------------------------------------------------- * * catcache.c * System catalog cache for tuples matching a key. * * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION * $PostgreSQL: pgsql/src/backend/utils/cache/catcache.c,v 1.140.2.1 2008/03/05 17:01:33 tgl Exp $ * *------------------------------------------------------------------------- */#include "postgres.h"#include "access/genam.h"#include "access/hash.h"#include "access/heapam.h"#include "access/valid.h"#include "catalog/pg_operator.h"#include "catalog/pg_type.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/memutils.h"#include "utils/relcache.h"#include "utils/resowner.h"#include "utils/syscache.h" /* #define CACHEDEBUG */ /* turns DEBUG elogs on *//* * 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(int code, Datum arg);#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: case REGCONFIGOID: case REGDICTIONARYOID: *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); *hashfunc = NULL; /* keep compiler quiet */ *eqfunc = InvalidOid; 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; uint32 oneHash; CACHE4_elog(DEBUG2, "CatalogCacheComputeHashValue %s %d %p", cache->cc_relname, nkeys, cache); switch (nkeys) { case 4: oneHash = DatumGetUInt32(DirectFunctionCall1(cache->cc_hashfunc[3], cur_skey[3].sk_argument)); hashValue ^= oneHash << 24; hashValue ^= oneHash >> 8; /* FALLTHROUGH */ case 3: oneHash = DatumGetUInt32(DirectFunctionCall1(cache->cc_hashfunc[2], cur_skey[2].sk_argument)); hashValue ^= oneHash << 16; hashValue ^= oneHash >> 16; /* FALLTHROUGH */ case 2: oneHash = DatumGetUInt32(DirectFunctionCall1(cache->cc_hashfunc[1], cur_skey[1].sk_argument)); hashValue ^= oneHash << 8; hashValue ^= oneHash >> 24; /* FALLTHROUGH */ case 1: oneHash = DatumGetUInt32(DirectFunctionCall1(cache->cc_hashfunc[0], cur_skey[0].sk_argument)); hashValue ^= oneHash; 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(int code, Datum arg){ 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_lsearches = 0; long cc_lhits = 0; 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/%u: %d tup, %ld srch, %ld+%ld=%ld hits, %ld+%ld=%ld loads, %ld invals, %ld lsrch, %ld lhits", cache->cc_relname, cache->cc_indexoid, 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_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_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 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_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. * Both the cache entry and the list had better have zero refcount. */static voidCatCacheRemoveCTup(CatCache *cache, CatCTup *ct){ Assert(ct->refcount == 0); Assert(ct->my_cache == cache); if (ct->c_list) { /* * The cleanest way to handle this is to call CatCacheRemoveCList, * which will recurse back to me, and the recursive call will do the * work. Set the "dead" flag to make sure it does recurse. */ ct->dead = true; CatCacheRemoveCList(cache, ct->c_list); return; /* nothing left to do */ } /* delink from linked list */ 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 * * NB: any dead member entries that become unreferenced are deleted too. */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; /* if the member is dead and now has no references, remove it */ if (#ifndef CATCACHE_FORCE_RELEASE ct->dead &&#endif ct->refcount == 0) CatCacheRemoveCTup(cache, ct); } /* 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. */ /*
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -