aboutsummaryrefslogtreecommitdiff
path: root/src/backend/utils/cache/catcache.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/utils/cache/catcache.c')
-rw-r--r--src/backend/utils/cache/catcache.c391
1 files changed, 214 insertions, 177 deletions
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index 39e05d0fb09..3d8e7d80ba8 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/utils/cache/catcache.c,v 1.71 2000/11/10 00:33:10 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/cache/catcache.c,v 1.72 2000/11/16 22:30:33 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -28,7 +28,8 @@
#include "utils/catcache.h"
#include "utils/syscache.h"
-static void CatCacheRemoveCTup(CatCache *cache, Dlelem *e);
+
+static void CatCacheRemoveCTup(CatCache *cache, CatCTup *ct);
static Index CatalogCacheComputeHashIndex(CatCache *cache,
ScanKey cur_skey);
static Index CatalogCacheComputeTupleHashIndex(CatCache *cache,
@@ -388,28 +389,17 @@ CatalogCacheComputeTupleHashIndex(CatCache *cache,
* --------------------------------
*/
static void
-CatCacheRemoveCTup(CatCache *cache, Dlelem *elt)
+CatCacheRemoveCTup(CatCache *cache, CatCTup *ct)
{
- CatCTup *ct;
- CatCTup *other_ct;
- Dlelem *other_elt;
-
- if (!elt) /* probably-useless safety check */
- return;
-
- /* We need to zap both linked-list elements as well as the tuple */
+ Assert(ct->refcount == 0);
- ct = (CatCTup *) DLE_VAL(elt);
- other_elt = ct->ct_node;
- other_ct = (CatCTup *) DLE_VAL(other_elt);
+ /* delink from linked lists */
+ DLRemove(&ct->lrulist_elem);
+ DLRemove(&ct->cache_elem);
- heap_freetuple(ct->ct_tup);
-
- DLRemove(other_elt);
- DLFreeElem(other_elt);
- pfree(other_ct);
- DLRemove(elt);
- DLFreeElem(elt);
+ /* free associated tuple data */
+ if (ct->tuple.t_data != NULL)
+ pfree(ct->tuple.t_data);
pfree(ct);
--cache->cc_ntup;
@@ -425,13 +415,11 @@ CatCacheRemoveCTup(CatCache *cache, Dlelem *elt)
* --------------------------------
*/
void
-CatalogCacheIdInvalidate(int cacheId, /* XXX */
+CatalogCacheIdInvalidate(int cacheId,
Index hashIndex,
ItemPointer pointer)
{
CatCache *ccp;
- CatCTup *ct;
- Dlelem *elt;
/* ----------------
* sanity checks
@@ -442,54 +430,101 @@ CatalogCacheIdInvalidate(int cacheId, /* XXX */
CACHE1_elog(DEBUG, "CatalogCacheIdInvalidate: called");
/* ----------------
- * inspect every cache that could contain the tuple
+ * inspect caches to find the proper cache
* ----------------
*/
for (ccp = Caches; ccp; ccp = ccp->cc_next)
{
+ Dlelem *elt,
+ *nextelt;
+
if (cacheId != ccp->id)
continue;
/* ----------------
* inspect the hash bucket until we find a match or exhaust
* ----------------
*/
- for (elt = DLGetHead(ccp->cc_cache[hashIndex]);
- elt;
- elt = DLGetSucc(elt))
+ for (elt = DLGetHead(&ccp->cc_cache[hashIndex]); elt; elt = nextelt)
{
- ct = (CatCTup *) DLE_VAL(elt);
- if (ItemPointerEquals(pointer, &ct->ct_tup->t_self))
- break;
- }
-
- /* ----------------
- * if we found a matching tuple, invalidate it.
- * ----------------
- */
+ CatCTup *ct = (CatCTup *) DLE_VAL(elt);
- if (elt)
- {
- CatCacheRemoveCTup(ccp, elt);
+ nextelt = DLGetSucc(elt);
- CACHE1_elog(DEBUG, "CatalogCacheIdInvalidate: invalidated");
+ if (ItemPointerEquals(pointer, &ct->tuple.t_self))
+ {
+ if (ct->refcount > 0)
+ ct->dead = true;
+ else
+ CatCacheRemoveCTup(ccp, ct);
+ CACHE1_elog(DEBUG, "CatalogCacheIdInvalidate: invalidated");
+ /* could be multiple matches, so keep looking! */
+ }
}
-
- if (cacheId != InvalidCatalogCacheId)
- break;
+ break; /* need only search this one cache */
}
}
/* ----------------------------------------------------------------
* public functions
*
+ * AtEOXact_CatCache
* ResetSystemCache
- * InitSysCache
- * SearchSysCache
+ * InitCatCache
+ * SearchCatCache
+ * ReleaseCatCache
* RelationInvalidateCatalogCacheTuple
* ----------------------------------------------------------------
*/
+
+
+/* --------------------------------
+ * 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.
+ * --------------------------------
+ */
+void
+AtEOXact_CatCache(bool isCommit)
+{
+ CatCache *cache;
+
+ for (cache = Caches; cache; cache = cache->cc_next)
+ {
+ Dlelem *elt,
+ *nextelt;
+
+ for (elt = DLGetHead(&cache->cc_lrulist); elt; elt = nextelt)
+ {
+ CatCTup *ct = (CatCTup *) DLE_VAL(elt);
+
+ nextelt = DLGetSucc(elt);
+
+ if (ct->refcount != 0)
+ {
+ if (isCommit)
+ elog(NOTICE, "Cache reference leak: cache %s (%d), tuple %u has count %d",
+ cache->cc_relname, cache->id,
+ ct->tuple.t_data->t_oid,
+ ct->refcount);
+ ct->refcount = 0;
+ }
+
+ /* Clean up any now-deletable dead entries */
+ if (ct->dead)
+ CatCacheRemoveCTup(cache, ct);
+ }
+ }
+}
+
/* --------------------------------
* ResetSystemCache
+ *
+ * Reset caches when a shared cache inval event forces it
* --------------------------------
*/
void
@@ -503,34 +538,25 @@ ResetSystemCache(void)
* here we purge the contents of all the caches
*
* for each system cache
- * for each hash bucket
- * for each tuple in hash bucket
- * remove the tuple
+ * for each tuple
+ * remove the tuple, or at least mark it dead
* ----------------
*/
- for (cache = Caches; PointerIsValid(cache); cache = cache->cc_next)
+ for (cache = Caches; cache; cache = cache->cc_next)
{
- int hash;
+ Dlelem *elt,
+ *nextelt;
- for (hash = 0; hash < NCCBUCK; hash += 1)
+ for (elt = DLGetHead(&cache->cc_lrulist); elt; elt = nextelt)
{
- Dlelem *elt,
- *nextelt;
+ CatCTup *ct = (CatCTup *) DLE_VAL(elt);
- for (elt = DLGetHead(cache->cc_cache[hash]); elt; elt = nextelt)
- {
- nextelt = DLGetSucc(elt);
- CatCacheRemoveCTup(cache, elt);
- }
- }
+ nextelt = DLGetSucc(elt);
- /* double-check that ntup is now zero */
- if (cache->cc_ntup != 0)
- {
- elog(NOTICE,
- "ResetSystemCache: cache %d has cc_ntup = %d, should be 0",
- cache->id, cache->cc_ntup);
- cache->cc_ntup = 0;
+ if (ct->refcount > 0)
+ ct->dead = true;
+ else
+ CatCacheRemoveCTup(cache, ct);
}
}
@@ -572,7 +598,7 @@ SystemCacheRelationFlushed(Oid relId)
}
/* --------------------------------
- * InitSysCache
+ * InitCatCache
*
* This allocates and initializes a cache for a system catalog relation.
* Actually, the cache is only partially initialized to avoid opening the
@@ -581,18 +607,18 @@ SystemCacheRelationFlushed(Oid relId)
* --------------------------------
*/
#ifdef CACHEDEBUG
-#define InitSysCache_DEBUG1 \
+#define InitCatCache_DEBUG1 \
do { \
- elog(DEBUG, "InitSysCache: rel=%s id=%d nkeys=%d size=%d\n", \
+ elog(DEBUG, "InitCatCache: rel=%s id=%d nkeys=%d size=%d\n", \
cp->cc_relname, cp->id, cp->cc_nkeys, cp->cc_size); \
} while(0)
#else
-#define InitSysCache_DEBUG1
+#define InitCatCache_DEBUG1
#endif
CatCache *
-InitSysCache(int id,
+InitCatCache(int id,
char *relname,
char *indname,
int nkeys,
@@ -624,25 +650,9 @@ InitSysCache(int id,
* and the LRU tuple list
* ----------------
*/
- {
-
- /*
- * We can only do this optimization because the number of hash
- * buckets never changes. Without it, we call palloc() too much.
- * We could move this to dllist.c, but the way we do this is not
- * dynamic/portable, so why allow other routines to use it.
- */
- Dllist *cache_begin = palloc((NCCBUCK + 1) * sizeof(Dllist));
-
- for (i = 0; i <= NCCBUCK; ++i)
- {
- cp->cc_cache[i] = &cache_begin[i];
- cp->cc_cache[i]->dll_head = 0;
- cp->cc_cache[i]->dll_tail = 0;
- }
- }
-
- cp->cc_lrulist = DLNewList();
+ DLInitList(&cp->cc_lrulist);
+ for (i = 0; i < NCCBUCK; ++i)
+ DLInitList(&cp->cc_cache[i]);
/* ----------------
* Caches is the pointer to the head of the list of all the
@@ -673,7 +683,7 @@ InitSysCache(int id,
* information, if appropriate.
* ----------------
*/
- InitSysCache_DEBUG1;
+ InitCatCache_DEBUG1;
/* ----------------
* back to the old context before we return...
@@ -742,14 +752,14 @@ IndexScanOK(CatCache *cache, ScanKey cur_skey)
}
/* --------------------------------
- * SearchSysCache
+ * SearchCatCache
*
* This call searches a system cache for a tuple, opening the relation
* if necessary (the first access to a particular cache).
* --------------------------------
*/
HeapTuple
-SearchSysCache(CatCache *cache,
+SearchCatCache(CatCache *cache,
Datum v1,
Datum v2,
Datum v3,
@@ -757,10 +767,8 @@ SearchSysCache(CatCache *cache,
{
ScanKeyData cur_skey[4];
Index hash;
- CatCTup *ct = NULL;
- CatCTup *nct;
- CatCTup *nct2;
Dlelem *elt;
+ CatCTup *ct;
HeapTuple ntp;
Relation relation;
MemoryContext oldcxt;
@@ -792,48 +800,50 @@ SearchSysCache(CatCache *cache,
* scan the hash bucket until we find a match or exhaust our tuples
* ----------------
*/
- for (elt = DLGetHead(cache->cc_cache[hash]);
+ for (elt = DLGetHead(&cache->cc_cache[hash]);
elt;
elt = DLGetSucc(elt))
{
bool res;
ct = (CatCTup *) DLE_VAL(elt);
+
+ if (ct->dead)
+ continue; /* ignore dead entries */
+
/* ----------------
* see if the cached tuple matches our key.
* (should we be worried about time ranges? -cim 10/2/90)
* ----------------
*/
- HeapKeyTest(ct->ct_tup,
+ HeapKeyTest(&ct->tuple,
cache->cc_tupdesc,
cache->cc_nkeys,
cur_skey,
res);
- if (res)
- break;
- }
+ if (! res)
+ continue;
- /* ----------------
- * if we found a tuple in the cache, move it to the top of the
- * lru list, and return it. 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.)
- * ----------------
- */
- if (elt)
- {
- Dlelem *old_lru_elt = ((CatCTup *) DLE_VAL(elt))->ct_node;
+ /* ----------------
+ * we found a tuple in the cache: bump its refcount, move it to
+ * the front of the LRU list, and return it. 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.)
+ * ----------------
+ */
+ ct->refcount++;
- DLMoveToFront(old_lru_elt);
- DLMoveToFront(elt);
+ DLMoveToFront(&ct->lrulist_elem);
+ DLMoveToFront(&ct->cache_elem);
#ifdef CACHEDEBUG
- CACHE3_elog(DEBUG, "SearchSysCache(%s): found in bucket %d",
+ CACHE3_elog(DEBUG, "SearchCatCache(%s): found in bucket %d",
cache->cc_relname, hash);
#endif /* CACHEDEBUG */
- return ct->ct_tup;
+ return &ct->tuple;
}
/* ----------------
@@ -864,7 +874,7 @@ SearchSysCache(CatCache *cache,
* if it's safe to do so, use the index. Else do a heap scan.
* ----------------
*/
- ntp = NULL;
+ ct = NULL;
if ((RelationGetForm(relation))->relhasindex &&
!IsIgnoringSystemIndexes() &&
@@ -876,7 +886,7 @@ SearchSysCache(CatCache *cache,
HeapTupleData tuple;
Buffer buffer;
- CACHE2_elog(DEBUG, "SearchSysCache(%s): performing index scan",
+ CACHE2_elog(DEBUG, "SearchCatCache(%s): performing index scan",
cache->cc_relname);
idesc = index_openr(cache->cc_indname);
@@ -892,7 +902,8 @@ SearchSysCache(CatCache *cache,
{
/* Copy tuple into our context */
oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
- ntp = heap_copytuple(&tuple);
+ ct = (CatCTup *) palloc(sizeof(CatCTup));
+ heap_copytuple_with_tuple(&tuple, &ct->tuple);
MemoryContextSwitchTo(oldcxt);
ReleaseBuffer(buffer);
break;
@@ -906,7 +917,7 @@ SearchSysCache(CatCache *cache,
HeapScanDesc sd;
int i;
- CACHE2_elog(DEBUG, "SearchSysCache(%s): performing heap scan",
+ CACHE2_elog(DEBUG, "SearchCatCache(%s): performing heap scan",
cache->cc_relname);
/*
@@ -925,7 +936,8 @@ SearchSysCache(CatCache *cache,
{
/* Copy tuple into our context */
oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
- ntp = heap_copytuple(ntp);
+ ct = (CatCTup *) palloc(sizeof(CatCTup));
+ heap_copytuple_with_tuple(ntp, &ct->tuple);
MemoryContextSwitchTo(oldcxt);
/* We should not free the result of heap_getnext... */
}
@@ -934,77 +946,102 @@ SearchSysCache(CatCache *cache,
}
/* ----------------
- * scan is complete. if tup is valid, we can add it to the cache.
- * note we have already copied it into the cache memory context.
+ * close the relation
* ----------------
*/
- if (HeapTupleIsValid(ntp))
- {
- /* ----------------
- * allocate a new cache tuple holder, store the pointer
- * to the heap tuple there and initialize the list pointers.
- * ----------------
- */
- Dlelem *lru_elt;
-
- CACHE1_elog(DEBUG, "SearchSysCache: found tuple");
+ heap_close(relation, AccessShareLock);
- oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
+ /* ----------------
+ * scan is complete. if tup was found, we can add it to the cache.
+ * ----------------
+ */
+ if (ct == NULL)
+ return NULL;
- /*
- * this is a little cumbersome here because we want the Dlelem's
- * in both doubly linked lists to point to one another. That makes
- * it easier to remove something from both the cache bucket and
- * the lru list at the same time
- */
- nct = (CatCTup *) palloc(sizeof(CatCTup));
- nct->ct_tup = ntp;
- elt = DLNewElem(nct);
- nct2 = (CatCTup *) palloc(sizeof(CatCTup));
- nct2->ct_tup = ntp;
- lru_elt = DLNewElem(nct2);
- nct2->ct_node = elt;
- nct->ct_node = lru_elt;
+ /* ----------------
+ * Finish initializing the CatCTup header, and add it to the
+ * linked lists.
+ * ----------------
+ */
+ CACHE1_elog(DEBUG, "SearchCatCache: found tuple");
- DLAddHead(cache->cc_lrulist, lru_elt);
- DLAddHead(cache->cc_cache[hash], elt);
+ ct->ct_magic = CT_MAGIC;
+ DLInitElem(&ct->lrulist_elem, (void *) ct);
+ DLInitElem(&ct->cache_elem, (void *) ct);
+ ct->refcount = 1; /* count this first reference */
+ ct->dead = false;
- MemoryContextSwitchTo(oldcxt);
+ DLAddHead(&cache->cc_lrulist, &ct->lrulist_elem);
+ DLAddHead(&cache->cc_cache[hash], &ct->cache_elem);
- /* ----------------
- * If we've exceeded the desired size of this cache,
- * throw away the least recently used entry.
- * ----------------
- */
- if (++cache->cc_ntup > cache->cc_maxtup)
+ /* ----------------
+ * If we've exceeded the desired size of this cache,
+ * try to throw away the least recently used entry.
+ * ----------------
+ */
+ if (++cache->cc_ntup > cache->cc_maxtup)
+ {
+ for (elt = DLGetTail(&cache->cc_lrulist);
+ elt;
+ elt = DLGetPred(elt))
{
- CatCTup *ct;
+ CatCTup *oldct = (CatCTup *) DLE_VAL(elt);
- elt = DLGetTail(cache->cc_lrulist);
- ct = (CatCTup *) DLE_VAL(elt);
-
- if (ct != nct) /* shouldn't be possible, but be safe... */
+ if (oldct->refcount == 0)
{
- CACHE2_elog(DEBUG, "SearchSysCache(%s): Overflow, LRU removal",
+ CACHE2_elog(DEBUG, "SearchCatCache(%s): Overflow, LRU removal",
cache->cc_relname);
-
- CatCacheRemoveCTup(cache, elt);
+ CatCacheRemoveCTup(cache, oldct);
+ break;
}
}
-
- CACHE4_elog(DEBUG, "SearchSysCache(%s): Contains %d/%d tuples",
- cache->cc_relname, cache->cc_ntup, cache->cc_maxtup);
- CACHE3_elog(DEBUG, "SearchSysCache(%s): put in bucket %d",
- cache->cc_relname, hash);
}
- /* ----------------
- * close the relation and return the tuple we found (or NULL)
- * ----------------
- */
- heap_close(relation, AccessShareLock);
+ CACHE4_elog(DEBUG, "SearchCatCache(%s): Contains %d/%d tuples",
+ cache->cc_relname, cache->cc_ntup, cache->cc_maxtup);
+ CACHE3_elog(DEBUG, "SearchCatCache(%s): put in bucket %d",
+ cache->cc_relname, hash);
+
+ return &ct->tuple;
+}
- return ntp;
+/* --------------------------------
+ * 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.
+ * --------------------------------
+ */
+void
+ReleaseCatCache(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
+ )
+ {
+ /* We can find the associated cache using the dllist pointers */
+ Dllist *lru = DLGetListHdr(&ct->lrulist_elem);
+ CatCache *cache = (CatCache *) (((char *) lru) -
+ offsetof(CatCache, cc_lrulist));
+
+ CatCacheRemoveCTup(cache, ct);
+ }
}
/* --------------------------------