catcache.c

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

C
1,777
字号
	 *	 * NOTE: it is possible for recursive cache lookups to occur while	 * reading the relation --- for example, due to shared-cache-inval	 * messages being processed during heap_open().  This is OK.  It's	 * even possible for one of those lookups to find and enter the very	 * same tuple we are trying to fetch here.	If that happens, we will	 * enter a second copy of the tuple into the cache.  The first copy	 * will never be referenced again, and will eventually age out of the	 * cache, so there's no functional problem.  This case is rare enough	 * that it's not worth expending extra cycles to detect.	 */	relation = heap_open(cache->cc_reloid, AccessShareLock);	scandesc = systable_beginscan(relation,								  cache->cc_indname,								  IndexScanOK(cache, cur_skey),								  SnapshotNow,								  cache->cc_nkeys,								  cur_skey);	ct = NULL;	while (HeapTupleIsValid(ntp = systable_getnext(scandesc)))	{		ct = CatalogCacheCreateEntry(cache, ntp,									 hashValue, hashIndex,									 false);		break;					/* assume only one match */	}	systable_endscan(scandesc);	heap_close(relation, AccessShareLock);	/*	 * 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.	 */	if (ct == NULL)	{		ntp = build_dummy_tuple(cache, cache->cc_nkeys, cur_skey);		ct = CatalogCacheCreateEntry(cache, ntp,									 hashValue, hashIndex,									 true);		heap_freetuple(ntp);		CACHE4_elog(DEBUG2, "SearchCatCache(%s): Contains %d/%d tuples",					cache->cc_relname, cache->cc_ntup, CacheHdr->ch_ntup);		CACHE3_elog(DEBUG2, "SearchCatCache(%s): put neg entry in bucket %d",					cache->cc_relname, hashIndex);		/*		 * We are not returning the new entry to the caller, so reset its		 * refcount.		 */		ct->refcount = 0;		/* negative entries never have refs */		return NULL;	}	CACHE4_elog(DEBUG2, "SearchCatCache(%s): Contains %d/%d tuples",				cache->cc_relname, cache->cc_ntup, CacheHdr->ch_ntup);	CACHE3_elog(DEBUG2, "SearchCatCache(%s): put in bucket %d",				cache->cc_relname, hashIndex);#ifdef CATCACHE_STATS	cache->cc_newloads++;#endif	return &ct->tuple;}/* *	ReleaseCatCache * *	Decrement the reference count of a catcache entry (releasing the *	hold grabbed by a successful SearchCatCache). * *	NOTE: if compiled with -DCATCACHE_FORCE_RELEASE then catcache entries *	will be freed as soon as their refcount goes to zero.  In combination *	with aset.c's CLOBBER_FREED_MEMORY option, this provides a good test *	to catch references to already-released catcache entries. */voidReleaseCatCache(HeapTuple tuple){	CatCTup    *ct = (CatCTup *) (((char *) tuple) -								  offsetof(CatCTup, tuple));	/* Safety checks to ensure we were handed a cache entry */	Assert(ct->ct_magic == CT_MAGIC);	Assert(ct->refcount > 0);	ct->refcount--;	if (ct->refcount == 0#ifndef CATCACHE_FORCE_RELEASE		&& ct->dead#endif		)		CatCacheRemoveCTup(ct->my_cache, ct);}/* *	SearchCatCacheList * *		Generate a list of all tuples matching a partial key (that is, *		a key specifying just the first K of the cache's N key columns). * *		The caller must not modify the list object or the pointed-to tuples, *		and must call ReleaseCatCacheList() when done with the list. */CatCList *SearchCatCacheList(CatCache *cache,				   int nkeys,				   Datum v1,				   Datum v2,				   Datum v3,				   Datum v4){	ScanKeyData cur_skey[4];	uint32		lHashValue;	Dlelem	   *elt;	CatCList   *cl;	CatCTup    *ct;	List	   *ctlist;	int			nmembers;	Relation	relation;	SysScanDesc scandesc;	bool		ordered;	HeapTuple	ntp;	MemoryContext oldcxt;	int			i;	/*	 * one-time startup overhead for each cache	 */	if (cache->cc_tupdesc == NULL)		CatalogCacheInitializeCache(cache);	Assert(nkeys > 0 && nkeys < cache->cc_nkeys);#ifdef CATCACHE_STATS	cache->cc_lsearches++;#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;	/*	 * compute a hash value of the given keys for faster search.  We don't	 * presently divide the CatCList items into buckets, but this still	 * lets us skip non-matching items quickly most of the time.	 */	lHashValue = CatalogCacheComputeHashValue(cache, nkeys, cur_skey);	/*	 * scan the items until we find a match or exhaust our list	 */	for (elt = DLGetHead(&cache->cc_lists);		 elt;		 elt = DLGetSucc(elt))	{		bool		res;		cl = (CatCList *) DLE_VAL(elt);		if (cl->dead)			continue;			/* ignore dead entries */		if (cl->hash_value != lHashValue)			continue;			/* quickly skip entry if wrong hash val */		/*		 * see if the cached list matches our key.		 */		if (cl->nkeys != nkeys)			continue;		HeapKeyTest(&cl->tuple,					cache->cc_tupdesc,					nkeys,					cur_skey,					res);		if (!res)			continue;		/*		 * we found a matching list: move each of its members to the front		 * of the global LRU list.	Also move the list itself to the front		 * of the cache's list-of-lists, to speed subsequent searches. (We		 * 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.		 */		for (i = 0; i < cl->n_members; i++)		{			cl->members[i]->refcount++;			DLMoveToFront(&cl->members[i]->lrulist_elem);		}		DLMoveToFront(&cl->cache_elem);		/* Bump the list's refcount and return it */		cl->refcount++;		CACHE2_elog(DEBUG2, "SearchCatCacheList(%s): found list",					cache->cc_relname);#ifdef CATCACHE_STATS		cache->cc_lhits++;#endif		return cl;	}	/*	 * List was not found in cache, so we have to build it by reading the	 * relation.  For each matching tuple found in the relation, use an	 * existing cache entry if possible, else build a new one.	 */	relation = heap_open(cache->cc_reloid, AccessShareLock);	scandesc = systable_beginscan(relation,								  cache->cc_indname,								  true,								  SnapshotNow,								  nkeys,								  cur_skey);	/* The list will be ordered iff we are doing an index scan */	ordered = (scandesc->irel != NULL);	ctlist = NIL;	nmembers = 0;	while (HeapTupleIsValid(ntp = systable_getnext(scandesc)))	{		uint32		hashValue;		Index		hashIndex;		/*		 * See if there's an entry for this tuple already.		 */		ct = NULL;		hashValue = CatalogCacheComputeTupleHashValue(cache, ntp);		hashIndex = HASH_INDEX(hashValue, cache->cc_nbuckets);		for (elt = DLGetHead(&cache->cc_bucket[hashIndex]);			 elt;			 elt = DLGetSucc(elt))		{			ct = (CatCTup *) DLE_VAL(elt);			if (ct->dead || ct->negative)				continue;		/* ignore dead and negative entries */			if (ct->hash_value != hashValue)				continue;		/* quickly skip entry if wrong hash val */			if (!ItemPointerEquals(&(ct->tuple.t_self), &(ntp->t_self)))				continue;		/* not same tuple */			/*			 * Found a match, but can't use it if it belongs to another			 * list already			 */			if (ct->c_list)				continue;			/* Found a match, so bump its refcount and move to front */			ct->refcount++;			DLMoveToFront(&ct->lrulist_elem);			break;		}		if (elt == NULL)		{			/* We didn't find a usable entry, so make a new one */			ct = CatalogCacheCreateEntry(cache, ntp,										 hashValue, hashIndex,										 false);		}		ctlist = lcons(ct, ctlist);		nmembers++;	}	systable_endscan(scandesc);	heap_close(relation, AccessShareLock);	/*	 * Now we can build the CatCList entry.  First we need a dummy tuple	 * containing the key values...	 */	ntp = build_dummy_tuple(cache, nkeys, cur_skey);	oldcxt = MemoryContextSwitchTo(CacheMemoryContext);	cl = (CatCList *) palloc(sizeof(CatCList) + nmembers * sizeof(CatCTup *));	heap_copytuple_with_tuple(ntp, &cl->tuple);	MemoryContextSwitchTo(oldcxt);	heap_freetuple(ntp);	cl->cl_magic = CL_MAGIC;	cl->my_cache = cache;	DLInitElem(&cl->cache_elem, (void *) cl);	cl->refcount = 1;			/* count this first reference */	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;)	{		cl->members[i] = ct = (CatCTup *) lfirst(ctlist);		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);	}	DLAddHead(&cache->cc_lists, &cl->cache_elem);	CACHE3_elog(DEBUG2, "SearchCatCacheList(%s): made list of %d members",				cache->cc_relname, nmembers);	return cl;}/* *	ReleaseCatCacheList * *	Decrement the reference counts of a catcache list. */voidReleaseCatCacheList(CatCList *list){	int			i;	/* Safety checks to ensure we were handed a cache entry */	Assert(list->cl_magic == CL_MAGIC);	Assert(list->refcount > 0);	for (i = list->n_members; --i >= 0;)	{		CatCTup    *ct = list->members[i];		Assert(ct->refcount > 0);		ct->refcount--;		if (ct->dead)			list->dead = true;		/* can't remove tuple before list is removed */	}	list->refcount--;	if (list->refcount == 0#ifndef CATCACHE_FORCE_RELEASE		&& list->dead#endif		)		CatCacheRemoveCList(list->my_cache, list);}/* * CatalogCacheCreateEntry *		Create a new CatCTup entry, copying the given HeapTuple and other *		supplied data into it.	The new entry is given refcount 1. */static CatCTup *CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp,						uint32 hashValue, Index hashIndex, bool negative){	CatCTup    *ct;	MemoryContext oldcxt;	/*	 * Allocate CatCTup header in cache memory, and copy the tuple there	 * too.	 */	oldcxt = MemoryContextSwitchTo(CacheMemoryContext);	ct = (CatCTup *) palloc(sizeof(CatCTup));	heap_copytuple_with_tuple(ntp, &ct->tuple);	MemoryContextSwitchTo(oldcxt);	/*	 * Finish initializing the CatCTup header, and add it to the cache's	 * linked lists and counts.	 */	ct->ct_magic = CT_MAGIC;	ct->my_cache = cache;	DLInitElem(&ct->lrulist_elem, (void *) ct);	DLInitElem(&ct->cache_elem, (void *) ct);	ct->c_list = NULL;	ct->refcount = 1;			/* count this first reference */	ct->dead = false;	ct->negative = negative;	ct->hash_value = hashValue;	DLAddHead(&CacheHdr->ch_lrulist, &ct->lrulist_elem);	DLAddHead(&cache->cc_bucket[hashIndex], &ct->cache_elem);	cache->cc_ntup++;	CacheHdr->ch_ntup++;	/*	 * 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.	 */	if (CacheHdr->ch_ntup > CacheHdr->ch_maxtup)	{		Dlelem	   *elt,				   *prevelt;		for (elt = DLGetTail(&CacheHdr->ch_lrulist); elt; elt = prevelt)		{			CatCTup    *oldct = (CatCTup *) DLE_VAL(elt);			prevelt = DLGetPred(elt);			if (oldct->refcount == 0)			{				CACHE2_elog(DEBUG2, "CatCacheCreateEntry(%s): Overflow, LRU removal",							cache->cc_relname);#ifdef CATCACHE_STATS				oldct->my_cache->cc_discards++;#endif				CatCacheRemoveCTup(oldct->my_cache, oldct);				if (CacheHdr->ch_ntup <= CacheHdr->ch_maxtup)					break;			}		}	}	return ct;}/* * build_dummy_tuple *		Generate a palloc'd HeapTuple that contains the specified key *		columns, and NULLs for other columns. * * This is used to store the keys for negative cache entries and CatCList * entries, which don't have real tuples associated with them. */static HeapTuplebuild_dummy_tuple(CatCache *cache, int nkeys, ScanKey skeys){	HeapTuple	ntp;	TupleDesc	tupDesc = cache->cc_tupdesc;	Datum	   *values;	char	   *nulls;	Oid			tupOid = InvalidOid;	NameData	tempNames[4];	int			i;	values = (Datum *) palloc(tupDesc->natts * sizeof(Datum));	nulls = (char *) palloc(tupDesc->natts * sizeof(char));	memset(values, 0, tupDesc->natts * sizeof(Datum));	memset(nulls, 'n', tupDesc->natts * sizeof(char));	for (i = 0; i < nkeys; i++)	{		int			attindex = cache->cc_key[i];		Datum		keyval = skeys[i].sk_argument;		if (attindex > 0)		{			/*			 * Here we must be careful in case the caller passed a C			 * string where a NAME is wanted: convert the given argument			 * to a correctly padded NAME.	Otherwise the memcpy() done in			 * heap_formtuple could fall off the end of memory.			 */			if (cache->cc_isname[i])			{				Name		newval = &tempNames[i];				namestrcpy(newval, DatumGetCString(keyval));				keyval = NameGetDatum(newval);			}			values[attindex - 1] = keyval;			nulls[attindex - 1] = ' ';		}		else		{			Assert(attindex == ObjectIdAttributeNumber);			tupOid = DatumGetObjectId(keyval);		}	}	ntp = heap_formtuple(tupDesc, values, nulls);	if (tupOid != InvalidOid)		HeapTupleSetOid(ntp, tupOid);	pfree(values);	pfree(nulls);	return ntp;}/* *	PrepareToInvalidateCacheTuple() * *	This is part of a rather subtle chain of events, so pay attention: * *	When a tuple is inserted or deleted, it cannot be flushed from the *	catcaches immediately, for reasons explained at the top of cache/inval.c. *	Instead we have to add entry(s) for the tuple to a list of pending tuple *	invalidations that will be done at the end of the command or transaction. * *	The lists of tuples that need to be flushed are kept by inval.c.  This *	routine is a helper routine for inval.c.  Given a tuple belonging to *	the specified relation, find all catcaches it could be in, compute the *	correct hash value for each such catcache, and call the specified function *	to record the cache id, hash value, and tuple ItemPointer in inval.c's *	lists.	CatalogCacheIdInvalidate will be called later, if appropriate, *	using the recorded information. * *	Note that it is irrelevant whether the given tuple is actually loaded *	into the catcache at the moment.  Even if it's not there now, it might *	be by the end of the command, or there might be a matching negative entry *	to flush --- or other backends' caches might have such entries --- so *	we have to make list entries to flush it later. * *	Also note that it's not an error if there are no catcaches for the *	specified relation.  inval.c doesn't know exactly which rels have *	catcaches --- it will call this routine for any tuple that's in a *	system relation. */voidPrepareToInvalidateCacheTuple(Relation relation,							  HeapTuple tuple,						void (*function) (int, uint32, ItemPointer, Oid)){	CatCache   *ccp;	Oid			reloid;	CACHE1_elog(DEBUG2, "PrepareToInvalidateCacheTuple: called");	/*	 * sanity checks	 */	Assert(RelationIsValid(relation));	Assert(HeapTupleIsValid(tuple));	Assert(PointerIsValid(function));	Assert(CacheHdr != NULL);	reloid = RelationGetRelid(relation);	/* ----------------	 *	for each cache	 *	   if the cache contains tuples from the specified relation	 *		   compute the tuple's hash value in this cache,	 *		   and call the passed function to register the information.	 * ----------------	 */	for (ccp = CacheHdr->ch_caches; ccp; ccp = ccp->cc_next)	{		/* Just in case cache hasn't finished initialization yet... */		if (ccp->cc_tupdesc == NULL)			CatalogCacheInitializeCache(ccp);		if (ccp->cc_reloid != reloid)			continue;		(*function) (ccp->id,					 CatalogCacheComputeTupleHashValue(ccp, tuple),					 &tuple->t_self,					 ccp->cc_relisshared ? (Oid) 0 : MyDatabaseId);	}}

⌨️ 快捷键说明

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