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 + -
显示快捷键?