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.c1023
1 files changed, 1023 insertions, 0 deletions
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
new file mode 100644
index 00000000000..93ea9be8282
--- /dev/null
+++ b/src/backend/utils/cache/catcache.c
@@ -0,0 +1,1023 @@
+/*-------------------------------------------------------------------------
+ *
+ * catcache.c--
+ * System catalog cache for tuples matching a key.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/backend/utils/cache/catcache.c,v 1.1.1.1 1996/07/09 06:22:06 scrappy Exp $
+ *
+ * Notes:
+ * XXX This needs to use exception.h to handle recovery when
+ * an abort occurs during DisableCache.
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <string.h>
+#include "postgres.h"
+#include "access/heapam.h"
+#include "access/genam.h"
+#include "utils/builtins.h"
+#include "utils/tqual.h"
+#include "storage/bufpage.h"
+#include "access/valid.h"
+#include "miscadmin.h"
+#include "utils/portal.h"
+#include "utils/catcache.h"
+#include "fmgr.h" /* for F_BOOLEQ, etc. DANGER */
+#include "utils/elog.h"
+#include "utils/palloc.h"
+#include "utils/mcxt.h"
+#include "utils/rel.h"
+#include "catalog/pg_type.h" /* for OID of int28 type */
+#include "lib/dllist.h"
+
+/* ----------------
+ * variables, macros and other stuff
+ *
+ * note CCSIZE allocates 51 buckets .. one was already allocated in
+ * the catcache structure.
+ * ----------------
+ */
+
+#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
+
+CatCache *Caches = NULL;
+GlobalMemory CacheCxt;
+
+static int DisableCache;
+
+/* ----------------
+ * EQPROC is used in CatalogCacheInitializeCache
+ * XXX this should be replaced by catalog lookups soon
+ * ----------------
+ */
+static long eqproc[] = {
+ F_BOOLEQ, 0l, F_CHAREQ, F_CHAR16EQ, 0l,
+ F_INT2EQ, F_KEYFIRSTEQ, F_INT4EQ, 0l, F_TEXTEQ,
+ F_OIDEQ, 0l, 0l, 0l, F_OID8EQ
+};
+
+#define EQPROC(SYSTEMTYPEOID) eqproc[(SYSTEMTYPEOID)-16]
+
+/* ----------------------------------------------------------------
+ * internal support functions
+ * ----------------------------------------------------------------
+ */
+/* --------------------------------
+ * CatalogCacheInitializeCache
+ * --------------------------------
+ */
+#ifdef CACHEDEBUG
+#define CatalogCacheInitializeCache_DEBUG1 \
+ elog(DEBUG, "CatalogCacheInitializeCache: cache @%08lx", cache); \
+ if (relation) \
+ elog(DEBUG, "CatalogCacheInitializeCache: called w/relation(inval)"); \
+ else \
+ elog(DEBUG, "CatalogCacheInitializeCache: called w/relname %s", \
+ cache->cc_relname)
+#define CatalogCacheInitializeCache_DEBUG2 \
+ if (cache->cc_key[i] > 0) { \
+ elog(DEBUG, "CatalogCacheInitializeCache: load %d/%d w/%d, %d", \
+ i+1, cache->cc_nkeys, cache->cc_key[i], \
+ relation->rd_att->attrs[cache->cc_key[i] - 1]->attlen); \
+ } else { \
+ elog(DEBUG, "CatalogCacheInitializeCache: load %d/%d w/%d", \
+ i+1, cache->cc_nkeys, cache->cc_key[i]); \
+ }
+#else
+#define CatalogCacheInitializeCache_DEBUG1
+#define CatalogCacheInitializeCache_DEBUG2
+#endif
+
+void
+CatalogCacheInitializeCache(struct catcache *cache,
+ Relation relation)
+{
+ MemoryContext oldcxt;
+ short didopen = 0;
+ short i;
+ TupleDesc tupdesc;
+
+ CatalogCacheInitializeCache_DEBUG1;
+
+ /* ----------------
+ * first switch to the cache context so our allocations
+ * do not vanish at the end of a transaction
+ * ----------------
+ */
+ if (!CacheCxt)
+ CacheCxt = CreateGlobalMemory("Cache");
+ oldcxt = MemoryContextSwitchTo((MemoryContext)CacheCxt);
+
+ /* ----------------
+ * If no relation was passed we must open it to get access to
+ * its fields. If one of the other caches has already opened
+ * it we use heap_open() instead of heap_openr()
+ * ----------------
+ */
+ if (! RelationIsValid(relation)) {
+ struct catcache *cp;
+ /* ----------------
+ * scan the caches to see if any other cache has opened the relation
+ * ----------------
+ */
+ for (cp = Caches; cp; cp = cp->cc_next) {
+ if (strncmp(cp->cc_relname, cache->cc_relname, NAMEDATALEN) == 0) {
+ if (cp->relationId != InvalidOid)
+ break;
+ }
+ }
+
+ /* ----------------
+ * open the relation by name or by id
+ * ----------------
+ */
+ if (cp)
+ relation = heap_open(cp->relationId);
+ else
+ {
+ relation = heap_openr(cache->cc_relname);
+ }
+
+ didopen = 1;
+ }
+
+ /* ----------------
+ * initialize the cache's relation id
+ * ----------------
+ */
+ Assert(RelationIsValid(relation));
+ cache->relationId = RelationGetRelationId(relation);
+ tupdesc = cache->cc_tupdesc = RelationGetTupleDescriptor(relation);
+
+ CACHE3_elog(DEBUG, "CatalogCacheInitializeCache: relid %d, %d keys",
+ cache->relationId, cache->cc_nkeys);
+
+ /* ----------------
+ * initialize cache's key information
+ * ----------------
+ */
+ for (i = 0; i < cache->cc_nkeys; ++i) {
+ CatalogCacheInitializeCache_DEBUG2;
+
+ if (cache->cc_key[i] > 0) {
+
+ /*
+ * Yoiks. The implementation of the hashing code and the
+ * implementation of int28's are at loggerheads. The right
+ * thing to do is to throw out the implementation of int28's
+ * altogether; until that happens, we do the right thing here
+ * to guarantee that the hash key generator doesn't try to
+ * dereference an int2 by mistake.
+ */
+
+ if (tupdesc->attrs[cache->cc_key[i]-1]->atttypid == INT28OID)
+ cache->cc_klen[i] = sizeof (short);
+ else
+ cache->cc_klen[i] = tupdesc->attrs[cache->cc_key[i]-1]->attlen;
+
+ cache->cc_skey[i].sk_procedure =
+ EQPROC(tupdesc->attrs[cache->cc_key[i]-1]->atttypid);
+
+ fmgr_info(cache->cc_skey[i].sk_procedure,
+ (func_ptr *) &cache->cc_skey[i].sk_func,
+ (int *) &cache->cc_skey[i].sk_nargs);
+
+ CACHE5_elog(DEBUG, "CatalogCacheInit %16s %d %d %x",
+ &relation->rd_rel->relname,
+ i,
+ tupdesc->attrs[ cache->cc_key[i]-1 ]->attlen,
+ cache);
+ }
+ }
+
+ /* ----------------
+ * close the relation if we opened it
+ * ----------------
+ */
+ if (didopen)
+ heap_close(relation);
+
+ /* ----------------
+ * initialize index information for the cache. this
+ * should only be done once per cache.
+ * ----------------
+ */
+ if (cache->cc_indname != NULL && cache->indexId == InvalidOid)
+ {
+ if (RelationGetRelationTupleForm(relation)->relhasindex)
+ {
+ /*
+ * If the index doesn't exist we are in trouble.
+ */
+ relation = index_openr( cache->cc_indname);
+ Assert(relation);
+ cache->indexId = RelationGetRelationId(relation);
+ index_close(relation);
+ }
+ else
+ cache->cc_indname = NULL;
+ }
+
+ /* ----------------
+ * return to the proper memory context
+ * ----------------
+ */
+ MemoryContextSwitchTo(oldcxt);
+}
+
+/* --------------------------------
+ * CatalogCacheSetId
+ *
+ * XXX temporary function
+ * --------------------------------
+ */
+void
+CatalogCacheSetId(CatCache *cacheInOutP, int id)
+{
+ Assert(id == InvalidCatalogCacheId || id >= 0);
+ cacheInOutP->id = id;
+}
+
+/* ----------------
+ * comphash --
+ * Compute a hash value, somehow.
+ *
+ * XXX explain algorithm here.
+ *
+ * l is length of the attribute value, v
+ * v is the attribute value ("Datum")
+ * ----------------
+ */
+long
+comphash(long l, register char *v)
+{
+ long i;
+ NameData n;
+
+ CACHE3_elog(DEBUG, "comphash (%d,%x)", l, v);
+
+ switch (l) {
+ case 1:
+ case 2:
+ case 4:
+ return((long) v);
+ }
+
+ if (l == NAMEDATALEN) {
+ /* if it's a name, make sure that the values
+ are null-padded.
+
+ Note that this other fixed-length types can also have
+ the same typelen so this may break them - XXX
+ */
+ namestrcpy(&n,v);
+ v = n.data;
+ } else
+ if (l < 0)
+ l = VARSIZE(v);
+
+ i = 0;
+ while (l--) {
+ i += *v++;
+ }
+ return(i);
+}
+
+/* --------------------------------
+ * CatalogCacheComputeHashIndex
+ * --------------------------------
+ */
+Index
+CatalogCacheComputeHashIndex(struct catcache *cacheInP)
+{
+ Index hashIndex;
+ hashIndex = 0x0;
+ CACHE6_elog(DEBUG,"CatalogCacheComputeHashIndex %s %d %d %d %x",
+ cacheInP->cc_relname,
+ cacheInP->cc_nkeys,
+ cacheInP->cc_klen[0],
+ cacheInP->cc_klen[1],
+ cacheInP);
+
+ switch (cacheInP->cc_nkeys) {
+ case 4:
+ hashIndex ^= comphash(cacheInP->cc_klen[3],
+ (char*)cacheInP->cc_skey[3].sk_argument) << 9;
+ /* FALLTHROUGH */
+ case 3:
+ hashIndex ^= comphash(cacheInP->cc_klen[2],
+ (char*)cacheInP->cc_skey[2].sk_argument) << 6;
+ /* FALLTHROUGH */
+ case 2:
+ hashIndex ^= comphash(cacheInP->cc_klen[1],
+ (char*)cacheInP->cc_skey[1].sk_argument) << 3;
+ /* FALLTHROUGH */
+ case 1:
+ hashIndex ^= comphash(cacheInP->cc_klen[0],
+ (char*)cacheInP->cc_skey[0].sk_argument);
+ break;
+ default:
+ elog(FATAL, "CCComputeHashIndex: %d cc_nkeys", cacheInP->cc_nkeys);
+ break;
+ }
+ hashIndex %= cacheInP->cc_size;
+ return (hashIndex);
+}
+
+/* --------------------------------
+ * CatalogCacheComputeTupleHashIndex
+ * --------------------------------
+ */
+Index
+CatalogCacheComputeTupleHashIndex(struct catcache *cacheInOutP,
+ Relation relation,
+ HeapTuple tuple)
+{
+ bool isNull = '\0';
+ if (cacheInOutP->relationId == InvalidOid)
+ CatalogCacheInitializeCache(cacheInOutP, relation);
+ switch (cacheInOutP->cc_nkeys) {
+ case 4:
+ cacheInOutP->cc_skey[3].sk_argument =
+ (cacheInOutP->cc_key[3] == ObjectIdAttributeNumber)
+ ? (Datum)tuple->t_oid
+ : (Datum)fastgetattr(tuple,
+ cacheInOutP->cc_key[3],
+ RelationGetTupleDescriptor(relation),
+ &isNull);
+ Assert(!isNull);
+ /* FALLTHROUGH */
+ case 3:
+ cacheInOutP->cc_skey[2].sk_argument =
+ (cacheInOutP->cc_key[2] == ObjectIdAttributeNumber)
+ ? (Datum)tuple->t_oid
+ : (Datum)fastgetattr(tuple,
+ cacheInOutP->cc_key[2],
+ RelationGetTupleDescriptor(relation),
+ &isNull);
+ Assert(!isNull);
+ /* FALLTHROUGH */
+ case 2:
+ cacheInOutP->cc_skey[1].sk_argument =
+ (cacheInOutP->cc_key[1] == ObjectIdAttributeNumber)
+ ? (Datum)tuple->t_oid
+ : (Datum)fastgetattr(tuple,
+ cacheInOutP->cc_key[1],
+ RelationGetTupleDescriptor(relation),
+ &isNull);
+ Assert(!isNull);
+ /* FALLTHROUGH */
+ case 1:
+ cacheInOutP->cc_skey[0].sk_argument =
+ (cacheInOutP->cc_key[0] == ObjectIdAttributeNumber)
+ ? (Datum)tuple->t_oid
+ : (Datum)fastgetattr(tuple,
+ cacheInOutP->cc_key[0],
+ RelationGetTupleDescriptor(relation),
+ &isNull);
+ Assert(!isNull);
+ break;
+ default:
+ elog(FATAL, "CCComputeTupleHashIndex: %d cc_nkeys",
+ cacheInOutP->cc_nkeys
+ );
+ break;
+ }
+
+ return
+ CatalogCacheComputeHashIndex(cacheInOutP);
+}
+
+/* --------------------------------
+ * CatCacheRemoveCTup
+ * --------------------------------
+ */
+void
+CatCacheRemoveCTup(CatCache *cache, Dlelem *elt)
+{
+ CatCTup *ct;
+ CatCTup *other_ct;
+ Dlelem *other_elt;
+
+ if (elt)
+ ct = (CatCTup*) DLE_VAL(elt);
+ else
+ return;
+
+ other_elt = ct->ct_node;
+ other_ct = (CatCTup*)DLE_VAL(other_elt);
+ DLRemove(other_elt);
+ DLFreeElem(other_elt);
+ free(other_ct);
+ DLRemove(elt);
+ DLFreeElem(elt);
+ free(ct);
+ --cache->cc_ntup;
+}
+
+/* --------------------------------
+ * CatalogCacheIdInvalidate()
+ *
+ * Invalidate a tuple given a cache id. In this case the id should always
+ * be found (whether the cache has opened its relation or not). Of course,
+ * if the cache has yet to open its relation, there will be no tuples so
+ * no problem.
+ * --------------------------------
+ */
+void
+CatalogCacheIdInvalidate(int cacheId, /* XXX */
+ Index hashIndex,
+ ItemPointer pointer)
+{
+ CatCache *ccp;
+ CatCTup *ct;
+ Dlelem *elt;
+ MemoryContext oldcxt;
+
+ /* ----------------
+ * sanity checks
+ * ----------------
+ */
+ Assert(hashIndex < NCCBUCK);
+ Assert(ItemPointerIsValid(pointer));
+ CACHE1_elog(DEBUG, "CatalogCacheIdInvalidate: called");
+
+ /* ----------------
+ * switch to the cache context for our memory allocations
+ * ----------------
+ */
+ if (!CacheCxt)
+ CacheCxt = CreateGlobalMemory("Cache");
+ oldcxt = MemoryContextSwitchTo((MemoryContext)CacheCxt);
+
+ /* ----------------
+ * inspect every cache that could contain the tuple
+ * ----------------
+ */
+ for (ccp = Caches; ccp; ccp = ccp->cc_next) {
+ 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))
+ {
+ ct = (CatCTup*) DLE_VAL(elt);
+ if (ItemPointerEquals(pointer, &ct->ct_tup->t_ctid))
+ break;
+ }
+
+ /* ----------------
+ * if we found a matching tuple, invalidate it.
+ * ----------------
+ */
+
+ if (elt) {
+ CatCacheRemoveCTup(ccp, elt);
+
+ CACHE1_elog(DEBUG, "CatalogCacheIdInvalidate: invalidated");
+ }
+
+ if (cacheId != InvalidCatalogCacheId)
+ break;
+ }
+
+ /* ----------------
+ * return to the proper memory context
+ * ----------------
+ */
+ MemoryContextSwitchTo(oldcxt);
+ /* sendpm('I', "Invalidated tuple"); */
+}
+
+/* ----------------------------------------------------------------
+ * public functions
+ *
+ * ResetSystemCache
+ * InitIndexedSysCache
+ * InitSysCache
+ * SearchSysCache
+ * RelationInvalidateCatalogCacheTuple
+ * ----------------------------------------------------------------
+ */
+/* --------------------------------
+ * ResetSystemCache
+ * --------------------------------
+ */
+void
+ResetSystemCache()
+{
+ MemoryContext oldcxt;
+ struct catcache *cache;
+
+ /* ----------------
+ * sanity checks
+ * ----------------
+ */
+ CACHE1_elog(DEBUG, "ResetSystemCache called");
+ if (DisableCache) {
+ elog(WARN, "ResetSystemCache: Called while cache disabled");
+ return;
+ }
+
+ /* ----------------
+ * first switch to the cache context so our allocations
+ * do not vanish at the end of a transaction
+ * ----------------
+ */
+ if (!CacheCxt)
+ CacheCxt = CreateGlobalMemory("Cache");
+
+ oldcxt = MemoryContextSwitchTo((MemoryContext)CacheCxt);
+
+ /* ----------------
+ * 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 (cache = Caches; PointerIsValid(cache); cache = cache->cc_next) {
+ int hash;
+ for (hash = 0; hash < NCCBUCK; hash += 1) {
+ Dlelem *elt, *nextelt;
+ for (elt = DLGetHead(cache->cc_cache[hash]); elt; elt = nextelt) {
+ nextelt = DLGetSucc(elt);
+ CatCacheRemoveCTup(cache, elt);
+ if (cache->cc_ntup == -1)
+ elog(WARN, "ResetSystemCache: cc_ntup<0 (software error)");
+ }
+ }
+ cache->cc_ntup = 0; /* in case of WARN error above */
+ }
+
+ CACHE1_elog(DEBUG, "end of ResetSystemCache call");
+
+ /* ----------------
+ * back to the old context before we return...
+ * ----------------
+ */
+ MemoryContextSwitchTo(oldcxt);
+}
+
+/* --------------------------------
+ * InitIndexedSysCache
+ *
+ * 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 InitSysCache_DEBUG1 \
+elog(DEBUG, "InitSysCache: rid=%d id=%d nkeys=%d size=%d\n", \
+ cp->relationId, cp->id, cp->cc_nkeys, cp->cc_size); \
+ for (i = 0; i < nkeys; i += 1) { \
+ elog(DEBUG, "InitSysCache: key=%d len=%d skey=[%d %d %d %d]\n", \
+ cp->cc_key[i], cp->cc_klen[i], \
+ cp->cc_skey[i].sk_flags, \
+ cp->cc_skey[i].sk_attno, \
+ cp->cc_skey[i].sk_procedure, \
+ cp->cc_skey[i].sk_argument); \
+ }
+#else
+#define InitSysCache_DEBUG1
+#endif
+
+CatCache*
+InitSysCache(char *relname,
+ char *iname,
+ int id,
+ int nkeys,
+ int key[],
+ HeapTuple (*iScanfuncP)())
+{
+ CatCache *cp;
+ register int i;
+ MemoryContext oldcxt;
+
+ char *indname;
+
+ indname = (iname) ? iname : NULL;
+
+ /* ----------------
+ * first switch to the cache context so our allocations
+ * do not vanish at the end of a transaction
+ * ----------------
+ */
+ if (!CacheCxt)
+ CacheCxt = CreateGlobalMemory("Cache");
+
+ oldcxt = MemoryContextSwitchTo((MemoryContext)CacheCxt);
+
+ /* ----------------
+ * allocate a new cache structure
+ * ----------------
+ */
+ cp = (CatCache *)palloc(sizeof(CatCache));
+ memset((char*)cp, 0, sizeof(CatCache));
+
+ /* ----------------
+ * initialize the cache buckets (each bucket is a list header)
+ * and the LRU tuple list
+ * ----------------
+ */
+ for (i = 0; i <= NCCBUCK; ++i) {
+ cp->cc_cache[i] = DLNewList();
+ }
+
+ cp->cc_lrulist = DLNewList();
+
+ /* ----------------
+ * Caches is the pointer to the head of the list of all the
+ * system caches. here we add the new cache to the top of the list.
+ * ----------------
+ */
+ cp->cc_next = Caches; /* list of caches (single link) */
+ Caches = cp;
+
+ /* ----------------
+ * initialize the cache's relation information for the relation
+ * corresponding to this cache and initialize some of the the new
+ * cache's other internal fields.
+ * ----------------
+ */
+ cp->relationId = InvalidOid;
+ cp->indexId = InvalidOid;
+ cp->cc_relname = relname;
+ cp->cc_indname = indname;
+ cp->cc_tupdesc = (TupleDesc) NULL;
+ cp->id = id;
+ cp->cc_maxtup = MAXTUP;
+ cp->cc_size = NCCBUCK;
+ cp->cc_nkeys = nkeys;
+ cp->cc_iscanfunc = iScanfuncP;
+
+ /* ----------------
+ * initialize the cache's key information
+ * ----------------
+ */
+ for (i = 0; i < nkeys; ++i) {
+ cp->cc_key[i] = key[i];
+ if (!key[i]) {
+ elog(FATAL, "InitSysCache: called with 0 key[%d]", i);
+ }
+ if (key[i] < 0) {
+ if (key[i] != ObjectIdAttributeNumber) {
+ elog(FATAL, "InitSysCache: called with %d key[%d]", key[i], i);
+ } else {
+ cp->cc_klen[i] = sizeof(Oid);
+ /*
+ * ScanKeyEntryData and struct skey are equivalent. It looks
+ * like a move was made to obsolete struct skey, but it
+ * didn't reach this file. Someday we should clean up this
+ * code and consolidate to ScanKeyEntry - mer 10 Nov 1991
+ */
+ ScanKeyEntryInitialize(&cp->cc_skey[i],
+ (bits16)0,
+ (AttrNumber)key[i],
+ (RegProcedure)F_OIDEQ,
+ (Datum)0);
+ continue;
+ }
+ }
+
+ cp->cc_skey[i].sk_attno = key[i];
+ }
+
+ /* ----------------
+ * all done. new cache is initialized. print some debugging
+ * information, if appropriate.
+ * ----------------
+ */
+ InitSysCache_DEBUG1;
+
+ /* ----------------
+ * back to the old context before we return...
+ * ----------------
+ */
+ MemoryContextSwitchTo(oldcxt);
+ return(cp);
+}
+
+
+/* --------------------------------
+ * SearchSysCache
+ *
+ * This call searches a system cache for a tuple, opening the relation
+ * if necessary (the first access to a particular cache).
+ * --------------------------------
+ */
+HeapTuple
+SearchSysCache(struct catcache *cache,
+ Datum v1,
+ Datum v2,
+ Datum v3,
+ Datum v4)
+{
+ unsigned hash;
+ CatCTup *ct;
+ CatCTup *nct;
+ CatCTup *nct2;
+ Dlelem *elt;
+ HeapTuple ntp;
+ Buffer buffer;
+
+ Relation relation;
+ MemoryContext oldcxt;
+
+ /* ----------------
+ * sanity checks
+ * ----------------
+ */
+ if (cache->relationId == InvalidOid)
+ CatalogCacheInitializeCache(cache, NULL);
+
+ /* ----------------
+ * initialize the search key information
+ * ----------------
+ */
+ cache->cc_skey[0].sk_argument = v1;
+ cache->cc_skey[1].sk_argument = v2;
+ cache->cc_skey[2].sk_argument = v3;
+ cache->cc_skey[3].sk_argument = v4;
+
+ /* ----------------
+ * find the hash bucket in which to look for the tuple
+ * ----------------
+ */
+ hash = CatalogCacheComputeHashIndex(cache);
+
+ /* ----------------
+ * scan the hash bucket until we find a match or exhaust our tuples
+ * ----------------
+ */
+ for (elt = DLGetHead(cache->cc_cache[hash]);
+ elt;
+ elt = DLGetSucc(elt))
+ {
+ ct = (CatCTup*)DLE_VAL(elt);
+ /* ----------------
+ * see if the cached tuple matches our key.
+ * (should we be worried about time ranges? -cim 10/2/90)
+ * ----------------
+ */
+ if (heap_keytest(ct->ct_tup,
+ cache->cc_tupdesc,
+ cache->cc_nkeys,
+ cache->cc_skey))
+ break;
+ }
+
+ /* ----------------
+ * if we found a tuple in the cache, move it to the top of the
+ * lru list, and return it.
+ * ----------------
+ */
+ if (elt) {
+ Dlelem* old_lru_elt;
+ old_lru_elt = ((CatCTup*)DLE_VAL(elt))->ct_node;
+ DLRemove(old_lru_elt);
+ DLAddHead(cache->cc_lrulist, old_lru_elt);
+
+#ifdef CACHEDEBUG
+ relation = heap_open(cache->relationId);
+ CACHE3_elog(DEBUG, "SearchSysCache(%s): found in bucket %d",
+ RelationGetRelationName(relation), hash);
+ heap_close(relation);
+#endif /* CACHEDEBUG */
+
+ return (ct->ct_tup);
+ }
+
+ /* ----------------
+ * Tuple was not found in cache, so we have to try and
+ * retrieve it directly from the relation. If it's found,
+ * we add it to the cache. We must avoid recursion here,
+ * so we disable cache operations. If operations are
+ * currently disabled and we couldn't find the requested item
+ * in the cache, then this may be a recursive request, and we
+ * abort with an error.
+ * ----------------
+ */
+
+ if (DisableCache) {
+ elog(WARN, "SearchSysCache: Called while cache disabled");
+ return((HeapTuple) NULL);
+ }
+
+ /* ----------------
+ * open the relation associated with the cache
+ * ----------------
+ */
+ relation = heap_open(cache->relationId);
+ CACHE2_elog(DEBUG, "SearchSysCache(%s)",
+ RelationGetRelationName(relation));
+
+ /* ----------------
+ * DisableCache and then switch to the cache memory context.
+ * ----------------
+ */
+ DisableCache = 1;
+
+ if (!CacheCxt)
+ CacheCxt = CreateGlobalMemory("Cache");
+
+ oldcxt = MemoryContextSwitchTo((MemoryContext)CacheCxt);
+
+ /* ----------------
+ * Scan the relation to find the tuple. If there's an index, and
+ * if this isn't bootstrap (initdb) time, use the index.
+ * ----------------
+ */
+ CACHE2_elog(DEBUG, "SearchSysCache: performing scan (override==%d)",
+ heapisoverride());
+
+ if ((RelationGetRelationTupleForm(relation))->relhasindex
+ && !IsBootstrapProcessingMode())
+ {
+ Assert(cache->cc_iscanfunc);
+ switch(cache->cc_nkeys)
+ {
+ case 4: ntp = cache->cc_iscanfunc(relation,v1,v2,v3,v4); break;
+ case 3: ntp = cache->cc_iscanfunc(relation,v1,v2,v3); break;
+ case 2: ntp = cache->cc_iscanfunc(relation,v1,v2); break;
+ case 1: ntp = cache->cc_iscanfunc(relation,v1); break;
+ }
+ }
+ else
+ {
+ HeapScanDesc sd;
+
+ sd = heap_beginscan(relation, 0, NowTimeQual,
+ cache->cc_nkeys, cache->cc_skey);
+
+ ntp = heap_getnext(sd, 0, &buffer);
+
+ if (HeapTupleIsValid(ntp)) {
+ CACHE1_elog(DEBUG, "SearchSysCache: found tuple");
+ ntp = heap_copytuple(ntp);
+ }
+
+ heap_endscan(sd);
+ }
+
+ DisableCache = 0;
+
+ /* ----------------
+ * scan is complete. if tup is valid, we copy it and add the copy to
+ * the cache.
+ * ----------------
+ */
+ 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;
+
+ /* 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*) malloc(sizeof(CatCTup));
+ nct->ct_tup = ntp;
+ elt = DLNewElem(nct);
+ nct2 = (CatCTup*) malloc(sizeof(CatCTup));
+ nct2->ct_tup = ntp;
+ lru_elt = DLNewElem(nct2);
+ nct2->ct_node = elt;
+ nct->ct_node = lru_elt;
+
+ DLAddHead(cache->cc_lrulist, lru_elt);
+ DLAddHead(cache->cc_cache[hash], elt);
+
+ /* ----------------
+ * deal with hash bucket overflow
+ * ----------------
+ */
+ if (++cache->cc_ntup > cache->cc_maxtup) {
+ CatCTup *ct;
+ elt = DLGetTail(cache->cc_lrulist);
+ ct = (CatCTup *) DLE_VAL(elt);
+
+ if (ct != nct) {
+ CACHE2_elog(DEBUG, "SearchSysCache(%s): Overflow, LRU removal",
+ RelationGetRelationName(relation));
+
+ CatCacheRemoveCTup(cache, elt);
+
+ }
+ }
+
+ CACHE4_elog(DEBUG, "SearchSysCache(%s): Contains %d/%d tuples",
+ RelationGetRelationName(relation),
+ cache->cc_ntup, cache->cc_maxtup);
+ CACHE3_elog(DEBUG, "SearchSysCache(%s): put in bucket %d",
+ RelationGetRelationName(relation), hash);
+ }
+
+ /* ----------------
+ * close the relation, switch back to the original memory context
+ * and return the tuple we found (or NULL)
+ * ----------------
+ */
+ heap_close(relation);
+
+ MemoryContextSwitchTo(oldcxt);
+ return ntp;
+}
+
+/* --------------------------------
+ * RelationInvalidateCatalogCacheTuple()
+ *
+ * Invalidate a tuple from a specific relation. This call determines the
+ * cache in question and calls CatalogCacheIdInvalidate(). It is -ok-
+ * if the relation cannot be found, it simply means this backend has yet
+ * to open it.
+ * --------------------------------
+ */
+void
+RelationInvalidateCatalogCacheTuple(Relation relation,
+ HeapTuple tuple,
+ void (*function)())
+{
+ struct catcache *ccp;
+ MemoryContext oldcxt;
+ Oid relationId;
+
+ /* ----------------
+ * sanity checks
+ * ----------------
+ */
+ Assert(RelationIsValid(relation));
+ Assert(HeapTupleIsValid(tuple));
+ CACHE1_elog(DEBUG, "RelationInvalidateCatalogCacheTuple: called");
+
+ /* ----------------
+ * switch to the cache memory context
+ * ----------------
+ */
+ if (!CacheCxt)
+ CacheCxt = CreateGlobalMemory("Cache");
+ oldcxt = MemoryContextSwitchTo((MemoryContext)CacheCxt);
+
+ /* ----------------
+ * for each cache
+ * if the cache contains tuples from the specified relation
+ * call the invalidation function on the tuples
+ * in the proper hash bucket
+ * ----------------
+ */
+ relationId = RelationGetRelationId(relation);
+
+ for (ccp = Caches; ccp; ccp = ccp->cc_next) {
+ if (relationId != ccp->relationId)
+ continue;
+
+ /* OPT inline simplification of CatalogCacheIdInvalidate */
+ if (!PointerIsValid(function)) {
+ function = CatalogCacheIdInvalidate;
+ }
+
+ (*function)(ccp->id,
+ CatalogCacheComputeTupleHashIndex(ccp, relation, tuple),
+ &tuple->t_ctid);
+
+ heap_close(relation);
+ }
+
+ /* ----------------
+ * return to the proper memory context
+ * ----------------
+ */
+ MemoryContextSwitchTo(oldcxt);
+
+ /* sendpm('I', "Invalidated tuple"); */
+}
+