コード例 #1
0
ファイル: catcache.c プロジェクト: sunyangkobe/cscd43
/*
 * 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;
}
コード例 #2
0
ファイル: catcache.c プロジェクト: gavioto/tPostgres
/*
 *	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[CATCACHE_MAXKEYS];
	uint32		lHashValue;
	Dlelem	   *elt;
	CatCList   *cl;
	CatCTup    *ct;
	List	   *volatile ctlist;
	ListCell   *ctlist_item;
	int			nmembers;
	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 the list 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.)
		 */
		DLMoveToFront(&cl->cache_elem);

		/* Bump the list's refcount and return it */
		ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner);
		cl->refcount++;
		ResourceOwnerRememberCatCacheListRef(CurrentResourceOwner, cl);

		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.
	 *
	 * We have to bump the member refcounts temporarily to ensure they won't
	 * get dropped from the cache while loading other members. We use a PG_TRY
	 * block to ensure we can undo those refcounts if we get an error before
	 * we finish constructing the CatCList.
	 */
	ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner);

	ctlist = NIL;

	PG_TRY();
	{
		Relation	relation;
		SysScanDesc scandesc;

		relation = heap_open(cache->cc_reloid, AccessShareLock);

		scandesc = systable_beginscan(relation,
									  cache->cc_indexoid,
									  IndexScanOK(cache, cur_skey),
									  SnapshotNow,
									  nkeys,
									  cur_skey);

		/* The list will be ordered iff we are doing an index scan */
		ordered = (scandesc->irel != NULL);

		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;

				break;			/* A-OK */
			}

			if (elt == NULL)
			{
				/* We didn't find a usable entry, so make a new one */
				ct = CatalogCacheCreateEntry(cache, ntp,
											 hashValue, hashIndex,
											 false);
			}

			/* Careful here: add entry to ctlist, then bump its refcount */
			/* This way leaves state correct if lappend runs out of memory */
			ctlist = lappend(ctlist, ct);
			ct->refcount++;
		}

		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);
		nmembers = list_length(ctlist);
		cl = (CatCList *)
			palloc(sizeof(CatCList) + nmembers * sizeof(CatCTup *));
		heap_copytuple_with_tuple(ntp, &cl->tuple);
		MemoryContextSwitchTo(oldcxt);
		heap_freetuple(ntp);

		/*
		 * We are now past the last thing that could trigger an elog before we
		 * have finished building the CatCList and remembering it in the
		 * resource owner.	So it's OK to fall out of the PG_TRY, and indeed
		 * we'd better do so before we start marking the members as belonging
		 * to the list.
		 */

	}
	PG_CATCH();
	{
		foreach(ctlist_item, ctlist)
		{
			ct = (CatCTup *) lfirst(ctlist_item);
			Assert(ct->c_list == NULL);
			Assert(ct->refcount > 0);
			ct->refcount--;
			if (
#ifndef CATCACHE_FORCE_RELEASE
				ct->dead &&
#endif
				ct->refcount == 0 &&
				(ct->c_list == NULL || ct->c_list->refcount == 0))
				CatCacheRemoveCTup(cache, ct);
		}

		PG_RE_THROW();
	}
コード例 #3
0
ファイル: catcache.c プロジェクト: sunyangkobe/cscd43
/*
 *	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;
}