/*------------------------------------------------------------------------- * * 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.27 1998/04/26 04:08:01 momjian Exp $ * * Notes: * XXX This needs to use exception.h to handle recovery when * an abort occurs during DisableCache. * *------------------------------------------------------------------------- */ #include #include "postgres.h" #include "access/heapam.h" #include "access/genam.h" #include "utils/tqual.h" #include "utils/builtins.h" #include "utils/portal.h" #include "utils/catcache.h" #include "utils/elog.h" #include "utils/palloc.h" #include "utils/mcxt.h" #include "utils/rel.h" #include "storage/bufpage.h" #include "access/valid.h" #include "miscadmin.h" #include "fmgr.h" /* for F_BOOLEQ, etc. DANGER */ #include "catalog/pg_type.h" /* for OID of int28 type */ #include "lib/dllist.h" static void CatCacheRemoveCTup(CatCache *cache, Dlelem *e); static Index CatalogCacheComputeHashIndex(struct catcache * cacheInP); static Index CatalogCacheComputeTupleHashIndex(struct catcache * cacheInOutP, Relation relation, HeapTuple tuple); static void CatalogCacheInitializeCache(struct catcache * cache, Relation relation); static long comphash(long l, char *v); /* ---------------- * 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_NAMEEQ, 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 static 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, &cache->cc_skey[i].sk_func); cache->cc_skey[i].sk_nargs = cache->cc_skey[i].sk_func.fn_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 * -------------------------------- */ #ifdef NOT_USED void CatalogCacheSetId(CatCache *cacheInOutP, int id) { Assert(id == InvalidCatalogCacheId || id >= 0); cacheInOutP->id = id; } #endif /* ---------------- * comphash -- * Compute a hash value, somehow. * * XXX explain algorithm here. * * l is length of the attribute value, v * v is the attribute value ("Datum") * ---------------- */ static long comphash(long l, 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 * -------------------------------- */ static 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 * -------------------------------- */ static 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 : 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 : 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 : 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 : 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 * -------------------------------- */ static 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(ERROR, "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(ERROR, "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); } /* -------------------------------- * SystemCacheRelationFlushed * * RelationFlushRelation() frees some information referenced in the * cache structures. So we get informed when this is done and arrange * for the next SearchSysCache() call that this information is setup * again. * -------------------------------- */ void SystemCacheRelationFlushed(Oid relId) { struct catcache *cache; for (cache = Caches; PointerIsValid(cache); cache = cache->cc_next) { if (cache->relationId == relId) { cache->relationId = InvalidOid; } } } /* -------------------------------- * 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; 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 * ---------------- */ { /* * We can only do this optimization because the number of hash * buckets never changes. Without it, we call malloc() too much. * We could move this to dllist.c, but the way we do this is not * dynamic/portabl, so why allow other routines to use it. */ Dllist *cache_begin = malloc((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(); /* ---------------- * 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 = NULL; CatCTup *nct; CatCTup *nct2; Dlelem *elt; HeapTuple ntp = 0; 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)) { bool res; ct = (CatCTup *) DLE_VAL(elt); /* ---------------- * see if the cached tuple matches our key. * (should we be worried about time ranges? -cim 10/2/90) * ---------------- */ HeapKeyTest(ct->ct_tup, cache->cc_tupdesc, cache->cc_nkeys, cache->cc_skey, res); if (res) 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(ERROR, "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()) { /* ---------- * Switch back to old memory context so memory not freed * in the scan function will go away at transaction end. * wieck - 10/18/1996 * ---------- */ MemoryContextSwitchTo(oldcxt); 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; } /* ---------- * Back to Cache context. If we got a tuple copy it * into our context. * wieck - 10/18/1996 * ---------- */ MemoryContextSwitchTo((MemoryContext) CacheCxt); if (HeapTupleIsValid(ntp)) { ntp = heap_copytuple(ntp); } } else { HeapScanDesc sd; /* ---------- * As above do the lookup in the callers memory * context. * wieck - 10/18/1996 * ---------- */ MemoryContextSwitchTo(oldcxt); sd = heap_beginscan(relation, 0, false, cache->cc_nkeys, cache->cc_skey); /* should this buffer be ReleaseBuffer'd? --djm 8/20/96 */ ntp = heap_getnext(sd, 0, &buffer); MemoryContextSwitchTo((MemoryContext) CacheCxt); if (HeapTupleIsValid(ntp)) { CACHE1_elog(DEBUG, "SearchSysCache: found tuple"); ntp = heap_copytuple(ntp); } MemoryContextSwitchTo(oldcxt); heap_endscan(sd); MemoryContextSwitchTo((MemoryContext) CacheCxt); } 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) (int, Index, ItemPointer)) { 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"); */ }