catcache.c

来自「PostgreSQL7.4.6 for Linux」· C语言 代码 · 共 1,777 行 · 第 1/3 页

C
1,777
字号
/* *		ResetCatalogCache * * Reset one catalog cache to empty. * * This is not very efficient if the target cache is nearly empty. * However, it shouldn't need to be efficient; we don't invoke it often. */static voidResetCatalogCache(CatCache *cache){	Dlelem	   *elt,			   *nextelt;	int			i;	/* Remove each list in this cache, or at least mark it dead */	for (elt = DLGetHead(&cache->cc_lists); elt; elt = nextelt)	{		CatCList   *cl = (CatCList *) DLE_VAL(elt);		nextelt = DLGetSucc(elt);		if (cl->refcount > 0)			cl->dead = true;		else			CatCacheRemoveCList(cache, cl);	}	/* Remove each tuple in this cache, or at least mark it dead */	for (i = 0; i < cache->cc_nbuckets; i++)	{		for (elt = DLGetHead(&cache->cc_bucket[i]); elt; elt = nextelt)		{			CatCTup    *ct = (CatCTup *) DLE_VAL(elt);			nextelt = DLGetSucc(elt);			if (ct->refcount > 0)				ct->dead = true;			else				CatCacheRemoveCTup(cache, ct);#ifdef CATCACHE_STATS			cache->cc_invals++;#endif		}	}}/* *		ResetCatalogCaches * * Reset all caches when a shared cache inval event forces it */voidResetCatalogCaches(void){	CatCache   *cache;	CACHE1_elog(DEBUG2, "ResetCatalogCaches called");	for (cache = CacheHdr->ch_caches; cache; cache = cache->cc_next)		ResetCatalogCache(cache);	CACHE1_elog(DEBUG2, "end of ResetCatalogCaches call");}/* *		CatalogCacheFlushRelation * *	This is called by RelationFlushRelation() to clear out cached information *	about a relation being dropped.  (This could be a DROP TABLE command, *	or a temp table being dropped at end of transaction, or a table created *	during the current transaction that is being dropped because of abort.) *	Remove all cache entries relevant to the specified relation OID. * *	A special case occurs when relId is itself one of the cacheable system *	tables --- although those'll never be dropped, they can get flushed from *	the relcache (VACUUM causes this, for example).  In that case we need *	to flush all cache entries that came from that table.  (At one point we *	also tried to force re-execution of CatalogCacheInitializeCache for *	the cache(s) on that table.  This is a bad idea since it leads to all *	kinds of trouble if a cache flush occurs while loading cache entries. *	We now avoid the need to do it by copying cc_tupdesc out of the relcache, *	rather than relying on the relcache to keep a tupdesc for us.  Of course *	this assumes the tupdesc of a cachable system table will not change...) */voidCatalogCacheFlushRelation(Oid relId){	CatCache   *cache;	CACHE2_elog(DEBUG2, "CatalogCacheFlushRelation called for %u", relId);	for (cache = CacheHdr->ch_caches; cache; cache = cache->cc_next)	{		int			i;		/* We can ignore uninitialized caches, since they must be empty */		if (cache->cc_tupdesc == NULL)			continue;		/* Does this cache store tuples of the target relation itself? */		if (cache->cc_tupdesc->attrs[0]->attrelid == relId)		{			/* Yes, so flush all its contents */			ResetCatalogCache(cache);			continue;		}		/* Does this cache store tuples associated with relations at all? */		if (cache->cc_reloidattr == 0)			continue;			/* nope, leave it alone */		/* Yes, scan the tuples and remove those related to relId */		for (i = 0; i < cache->cc_nbuckets; i++)		{			Dlelem	   *elt,					   *nextelt;			for (elt = DLGetHead(&cache->cc_bucket[i]); elt; elt = nextelt)			{				CatCTup    *ct = (CatCTup *) DLE_VAL(elt);				Oid			tupRelid;				nextelt = DLGetSucc(elt);				/*				 * Negative entries are never considered related to a rel,				 * even if the rel is part of their lookup key.				 */				if (ct->negative)					continue;				if (cache->cc_reloidattr == ObjectIdAttributeNumber)					tupRelid = HeapTupleGetOid(&ct->tuple);				else				{					bool		isNull;					tupRelid =						DatumGetObjectId(fastgetattr(&ct->tuple,													 cache->cc_reloidattr,													 cache->cc_tupdesc,													 &isNull));					Assert(!isNull);				}				if (tupRelid == relId)				{					if (ct->refcount > 0)						ct->dead = true;					else						CatCacheRemoveCTup(cache, ct);#ifdef CATCACHE_STATS					cache->cc_invals++;#endif				}			}		}	}	CACHE1_elog(DEBUG2, "end of CatalogCacheFlushRelation call");}/* *		InitCatCache * *	This allocates and initializes a cache for a system catalog relation. *	Actually, the cache is only partially initialized to avoid opening the *	relation.  The relation will be opened and the rest of the cache *	structure initialized on the first access. */#ifdef CACHEDEBUG#define InitCatCache_DEBUG2 \do { \	elog(DEBUG2, "InitCatCache: rel=%s id=%d nkeys=%d size=%d", \		cp->cc_relname, cp->id, cp->cc_nkeys, cp->cc_nbuckets); \} while(0)#else#define InitCatCache_DEBUG2#endifCatCache *InitCatCache(int id,			 const char *relname,			 const char *indname,			 int reloidattr,			 int nkeys,			 const int *key){	CatCache   *cp;	MemoryContext oldcxt;	int			i;	/*	 * first switch to the cache context so our allocations do not vanish	 * at the end of a transaction	 */	if (!CacheMemoryContext)		CreateCacheMemoryContext();	oldcxt = MemoryContextSwitchTo(CacheMemoryContext);	/*	 * if first time through, initialize the cache group header, including	 * global LRU list header	 */	if (CacheHdr == NULL)	{		CacheHdr = (CatCacheHeader *) palloc(sizeof(CatCacheHeader));		CacheHdr->ch_caches = NULL;		CacheHdr->ch_ntup = 0;		CacheHdr->ch_maxtup = MAXCCTUPLES;		DLInitList(&CacheHdr->ch_lrulist);#ifdef CATCACHE_STATS		on_proc_exit(CatCachePrintStats, 0);#endif	}	/*	 * allocate a new cache structure	 *	 * Note: we assume zeroing initializes the Dllist headers correctly	 */	cp = (CatCache *) palloc0(sizeof(CatCache) + NCCBUCKETS * sizeof(Dllist));	/*	 * initialize the cache's relation information for the relation	 * corresponding to this cache, and initialize some of the new cache's	 * 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_relisshared = false; /* temporary */	cp->cc_tupdesc = (TupleDesc) NULL;	cp->cc_reloidattr = reloidattr;	cp->cc_ntup = 0;	cp->cc_nbuckets = NCCBUCKETS;	cp->cc_nkeys = nkeys;	for (i = 0; i < nkeys; ++i)		cp->cc_key[i] = key[i];	/*	 * new cache is initialized as far as we can go for now. print some	 * debugging information, if appropriate.	 */	InitCatCache_DEBUG2;	/*	 * add completed cache to top of group header's list	 */	cp->cc_next = CacheHdr->ch_caches;	CacheHdr->ch_caches = cp;	/*	 * back to the old context before we return...	 */	MemoryContextSwitchTo(oldcxt);	return cp;}/* *		CatalogCacheInitializeCache * * This function does final initialization of a catcache: obtain the tuple * descriptor and set up the hash and equality function links.	We assume * 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_DEBUG2 \do { \		if (cache->cc_key[i] > 0) { \			elog(DEBUG2, "CatalogCacheInitializeCache: load %d/%d w/%d, %u", \				i+1, cache->cc_nkeys, cache->cc_key[i], \				 tupdesc->attrs[cache->cc_key[i] - 1]->atttypid); \		} else { \			elog(DEBUG2, "CatalogCacheInitializeCache: load %d/%d w/%d", \				i+1, cache->cc_nkeys, cache->cc_key[i]); \		} \} while(0)#else#define CatalogCacheInitializeCache_DEBUG2#define CatalogCacheInitializeCache_DEBUG2#endifstatic voidCatalogCacheInitializeCache(CatCache *cache){	Relation	relation;	MemoryContext oldcxt;	TupleDesc	tupdesc;	int			i;	CatalogCacheInitializeCache_DEBUG2;	/*	 * Open the relation without locking --- we only need the tupdesc,	 * which we assume will never change ...	 */	relation = heap_openr(cache->cc_relname, NoLock);	Assert(RelationIsValid(relation));	/*	 * switch to the cache context so our allocations do not vanish at the	 * end of a transaction	 */	Assert(CacheMemoryContext != NULL);	oldcxt = MemoryContextSwitchTo(CacheMemoryContext);	/*	 * copy the relcache's tuple descriptor to permanent cache storage	 */	tupdesc = CreateTupleDescCopyConstr(RelationGetDescr(relation));	/*	 * get the relation's OID and relisshared flag, too	 */	cache->cc_reloid = RelationGetRelid(relation);	cache->cc_relisshared = RelationGetForm(relation)->relisshared;	/*	 * return to the caller's memory context and close the rel	 */	MemoryContextSwitchTo(oldcxt);	heap_close(relation, NoLock);	CACHE3_elog(DEBUG2, "CatalogCacheInitializeCache: %s, %d keys",				cache->cc_relname, cache->cc_nkeys);	/*	 * initialize cache's key information	 */	for (i = 0; i < cache->cc_nkeys; ++i)	{		Oid			keytype;		CatalogCacheInitializeCache_DEBUG2;		if (cache->cc_key[i] > 0)			keytype = tupdesc->attrs[cache->cc_key[i] - 1]->atttypid;		else		{			if (cache->cc_key[i] != ObjectIdAttributeNumber)				elog(FATAL, "only sys attr supported in caches is OID");			keytype = OIDOID;		}		GetCCHashEqFuncs(keytype,						 &cache->cc_hashfunc[i],						 &cache->cc_skey[i].sk_procedure);		cache->cc_isname[i] = (keytype == NAMEOID);		/*		 * Do equality-function lookup (we assume this won't need a		 * catalog lookup for any supported type)		 */		fmgr_info_cxt(cache->cc_skey[i].sk_procedure,					  &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];		CACHE4_elog(DEBUG2, "CatalogCacheInit %s %d %p",					cache->cc_relname,					i,					cache);	}	/*	 * mark this cache fully initialized	 */	cache->cc_tupdesc = tupdesc;}/* * InitCatCachePhase2 -- external interface for CatalogCacheInitializeCache * * The only reason to call this routine is to ensure that the relcache * has created entries for all the catalogs and indexes referenced by * catcaches.  Therefore, open the index too.  An exception is the indexes * on pg_am, which we don't use (cf. IndexScanOK). */voidInitCatCachePhase2(CatCache *cache){	if (cache->cc_tupdesc == NULL)		CatalogCacheInitializeCache(cache);	if (cache->id != AMOID &&		cache->id != AMNAME)	{		Relation	idesc;		idesc = index_openr(cache->cc_indname);		index_close(idesc);	}}/* *		IndexScanOK * *		This function checks for tuples that will be fetched by *		IndexSupportInitialize() during relcache initialization for *		certain system indexes that support critical syscaches. *		We can't use an indexscan to fetch these, else we'll get into *		infinite recursion.  A plain heap scan will work, however. * *		Once we have completed relcache initialization (signaled by *		criticalRelcachesBuilt), we don't have to worry anymore. */static boolIndexScanOK(CatCache *cache, ScanKey cur_skey){	if (cache->id == INDEXRELID)	{		/*		 * Since the OIDs of indexes aren't hardwired, it's painful to		 * figure out which is which.  Just force all pg_index searches to		 * be heap scans while building the relcaches.		 */		if (!criticalRelcachesBuilt)			return false;	}	else if (cache->id == AMOID ||			 cache->id == AMNAME)	{		/*		 * Always do heap scans in pg_am, because it's so small there's		 * not much point in an indexscan anyway.  We *must* do this when		 * initially building critical relcache entries, but we might as		 * well just always do it.		 */		return false;	}	else if (cache->id == OPEROID)	{		if (!criticalRelcachesBuilt)		{			/* Looking for an OID comparison function? */			Oid			lookup_oid = DatumGetObjectId(cur_skey[0].sk_argument);			if (lookup_oid >= MIN_OIDCMP && lookup_oid <= MAX_OIDCMP)				return false;		}	}	/* Normal case, allow index scan */	return true;}/* *	SearchCatCache * *		This call searches a system cache for a tuple, opening the relation *		if necessary (on the first access to a particular cache). * *		The result is NULL if not found, or a pointer to a HeapTuple in *		the cache.	The caller must not modify the tuple, and must call *		ReleaseCatCache() when done with it. * * The search key values should be expressed as Datums of the key columns' * datatype(s).  (Pass zeroes for any unused parameters.)  As a special * exception, the passed-in key for a NAME column can be just a C string; * the caller need not go to the trouble of converting it to a fully * null-padded NAME. */HeapTupleSearchCatCache(CatCache *cache,			   Datum v1,			   Datum v2,			   Datum v3,			   Datum v4){	ScanKeyData cur_skey[4];	uint32		hashValue;	Index		hashIndex;	Dlelem	   *elt;	CatCTup    *ct;	Relation	relation;	SysScanDesc scandesc;	HeapTuple	ntp;	/*	 * one-time startup overhead for each cache	 */	if (cache->cc_tupdesc == NULL)		CatalogCacheInitializeCache(cache);#ifdef CATCACHE_STATS	cache->cc_searches++;#endif	/*	 * initialize the search key information	 */	memcpy(cur_skey, cache->cc_skey, sizeof(cur_skey));	cur_skey[0].sk_argument = v1;	cur_skey[1].sk_argument = v2;	cur_skey[2].sk_argument = v3;	cur_skey[3].sk_argument = v4;	/*	 * find the hash bucket in which to look for the tuple	 */	hashValue = CatalogCacheComputeHashValue(cache, cache->cc_nkeys, cur_skey);	hashIndex = HASH_INDEX(hashValue, cache->cc_nbuckets);	/*	 * scan the hash bucket until we find a match or exhaust our tuples	 */	for (elt = DLGetHead(&cache->cc_bucket[hashIndex]);		 elt;		 elt = DLGetSucc(elt))	{		bool		res;		ct = (CatCTup *) DLE_VAL(elt);		if (ct->dead)			continue;			/* ignore dead entries */		if (ct->hash_value != hashValue)			continue;			/* quickly skip entry if wrong hash val */		/*		 * see if the cached tuple matches our key.		 */		HeapKeyTest(&ct->tuple,					cache->cc_tupdesc,					cache->cc_nkeys,					cur_skey,					res);		if (!res)			continue;		/*		 * we found a match in the cache: move it to the front of the		 * global LRU list.  We also move it to the front of the list for		 * its hashbucket, in order to speed subsequent searches.  (The		 * most frequently accessed elements in any hashbucket will tend		 * to be near the front of the hashbucket's list.)		 */		DLMoveToFront(&ct->lrulist_elem);		DLMoveToFront(&ct->cache_elem);		/*		 * If it's a positive entry, bump its refcount and return it. If		 * it's negative, we can report failure to the caller.		 */		if (!ct->negative)		{			ct->refcount++;			CACHE3_elog(DEBUG2, "SearchCatCache(%s): found in bucket %d",						cache->cc_relname, hashIndex);#ifdef CATCACHE_STATS			cache->cc_hits++;#endif			return &ct->tuple;		}		else		{			CACHE3_elog(DEBUG2, "SearchCatCache(%s): found neg entry in bucket %d",						cache->cc_relname, hashIndex);#ifdef CATCACHE_STATS			cache->cc_neg_hits++;#endif			return NULL;		}	}	/*	 * Tuple was not found in cache, so we have to try to retrieve it	 * directly from the relation.	If found, we will add it to the cache;	 * if not found, we will add a negative cache entry instead.

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?