diff options
Diffstat (limited to 'src/backend/utils/cache')
-rw-r--r-- | src/backend/utils/cache/Makefile.inc | 15 | ||||
-rw-r--r-- | src/backend/utils/cache/catcache.c | 1023 | ||||
-rw-r--r-- | src/backend/utils/cache/fcache.c | 297 | ||||
-rw-r--r-- | src/backend/utils/cache/inval.c | 612 | ||||
-rw-r--r-- | src/backend/utils/cache/lsyscache.c | 484 | ||||
-rw-r--r-- | src/backend/utils/cache/rel.c | 77 | ||||
-rw-r--r-- | src/backend/utils/cache/relcache.c | 1795 | ||||
-rw-r--r-- | src/backend/utils/cache/syscache.c | 630 |
8 files changed, 4933 insertions, 0 deletions
diff --git a/src/backend/utils/cache/Makefile.inc b/src/backend/utils/cache/Makefile.inc new file mode 100644 index 00000000000..c6a688e5e83 --- /dev/null +++ b/src/backend/utils/cache/Makefile.inc @@ -0,0 +1,15 @@ +#------------------------------------------------------------------------- +# +# Makefile.inc-- +# Makefile for utils/cache +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/backend/utils/cache/Attic/Makefile.inc,v 1.1.1.1 1996/07/09 06:22:06 scrappy Exp $ +# +#------------------------------------------------------------------------- + +SUBSRCS+= catcache.c inval.c rel.c relcache.c syscache.c lsyscache.c fcache.c + 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"); */ +} + diff --git a/src/backend/utils/cache/fcache.c b/src/backend/utils/cache/fcache.c new file mode 100644 index 00000000000..070f457c280 --- /dev/null +++ b/src/backend/utils/cache/fcache.c @@ -0,0 +1,297 @@ +/*------------------------------------------------------------------------- + * + * fcache.c-- + * Code for the 'function cache' used in Oper and Func nodes.... + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/cache/Attic/fcache.c,v 1.1.1.1 1996/07/09 06:22:06 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include "c.h" +#include "access/htup.h" +#include "utils/catcache.h" +#include "utils/syscache.h" +#include "catalog/pg_type.h" +#include "catalog/pg_proc.h" +#include "catalog/pg_language.h" +#include "catalog/pg_class.h" +#include "parser/parsetree.h" /* for getrelname() */ +#include "utils/builtins.h" +#include "utils/fcache.h" +#include "utils/elog.h" +#include "utils/palloc.h" +#include "nodes/primnodes.h" +#include "nodes/execnodes.h" + +static Oid GetDynamicFuncArgType(Var *arg, ExprContext *econtext); +static FunctionCachePtr init_fcache(Oid foid, + bool use_syscache, + List *argList, + ExprContext *econtext); + +/*----------------------------------------------------------------- + * + * Initialize the 'FunctionCache' given the PG_PROC oid. + * + * + * NOTE: This function can be called when the system cache is being + * initialized. Therefore, use_syscache should ONLY be true + * when the function return type is interesting (ie: set_fcache). + *----------------------------------------------------------------- + */ +#define FuncArgTypeIsDynamic(arg) \ + (IsA(arg,Var) && ((Var*)arg)->varattno == InvalidAttrNumber) + +static Oid +GetDynamicFuncArgType(Var *arg, ExprContext *econtext) +{ + char *relname; + int rtid; + HeapTuple tup; + + Assert(IsA(arg,Var)); + + rtid = ((Var*)arg)->varno; + relname = (char*)getrelname(rtid, econtext->ecxt_range_table); + + + tup = SearchSysCacheTuple(TYPNAME, PointerGetDatum(relname), + 0,0,0); + if (!tup) + elog(WARN, "Lookup failed on type tuple for class %s", + relname); + + return tup->t_oid; +} + +static FunctionCachePtr +init_fcache(Oid foid, + bool use_syscache, + List *argList, + ExprContext *econtext) +{ + HeapTuple procedureTuple; + HeapTuple typeTuple; + Form_pg_proc procedureStruct; + TypeTupleForm typeStruct; + FunctionCachePtr retval; + text *tmp; + int nargs; + + /* ---------------- + * get the procedure tuple corresponding to the given + * functionOid. If this fails, returnValue has been + * pre-initialized to "null" so we just return it. + * ---------------- + */ + retval = (FunctionCachePtr) palloc(sizeof(FunctionCache)); + + if (!use_syscache) + elog(WARN, "what the ????, init the fcache without the catalogs?"); + + procedureTuple = SearchSysCacheTuple(PROOID, + ObjectIdGetDatum(foid), + 0,0,0); + + if (!HeapTupleIsValid(procedureTuple)) + elog(WARN, + "init_fcache: %s %d", + "Cache lookup failed for procedure", foid); + + /* ---------------- + * get the return type from the procedure tuple + * ---------------- + */ + procedureStruct = (Form_pg_proc) GETSTRUCT(procedureTuple); + + /* ---------------- + * get the type tuple corresponding to the return type + * If this fails, returnValue has been pre-initialized + * to "null" so we just return it. + * ---------------- + */ + typeTuple = SearchSysCacheTuple(TYPOID, + ObjectIdGetDatum(procedureStruct->prorettype), + 0,0,0); + + if (!HeapTupleIsValid(typeTuple)) + elog(WARN, + "init_fcache: %s %d", + "Cache lookup failed for type", + (procedureStruct)->prorettype); + + /* ---------------- + * get the type length and by-value from the type tuple and + * save the information in our one element cache. + * ---------------- + */ + typeStruct = (TypeTupleForm) GETSTRUCT(typeTuple); + + retval->typlen = (typeStruct)->typlen; + if ((typeStruct)->typrelid == InvalidOid) { + /* The return type is not a relation, so just use byval */ + retval->typbyval = (typeStruct)->typbyval ? true : false ; + } else { + /* This is a hack. We assume here that any function returning + * a relation returns it by reference. This needs to be + * fixed. + */ + retval->typbyval = false; + } + retval->foid = foid; + retval->language = procedureStruct->prolang; + retval->func_state = (char *)NULL; + retval->setArg = NULL; + retval->hasSetArg = false; + retval->oneResult = ! procedureStruct->proretset; + retval->istrusted = procedureStruct->proistrusted; + + /* + * If we are returning exactly one result then we have to copy + * tuples and by reference results because we have to end the execution + * before we return the results. When you do this everything allocated + * by the executor (i.e. slots and tuples) is freed. + */ + if ((retval->language == SQLlanguageId) && + (retval->oneResult) && + !(retval->typbyval)) + { + Form_pg_class relationStruct; + HeapTuple relationTuple; + TupleDesc td; + TupleTableSlot *slot; + + slot = makeNode(TupleTableSlot); + slot->ttc_shouldFree = true; + slot->ttc_descIsNew = true; + slot->ttc_tupleDescriptor = (TupleDesc) NULL; + slot->ttc_buffer = InvalidBuffer; + slot->ttc_whichplan = -1; + retval->funcSlot = (Pointer)slot; + + relationTuple = (HeapTuple) + SearchSysCacheTuple(RELNAME, + PointerGetDatum(&typeStruct->typname), + 0,0,0); + + if (relationTuple) + { + relationStruct = (Form_pg_class)GETSTRUCT(relationTuple); + td = CreateTemplateTupleDesc(relationStruct->relnatts); + } + else + td = CreateTemplateTupleDesc(1); + + ((TupleTableSlot*)retval->funcSlot)->ttc_tupleDescriptor = td; + } + else + retval->funcSlot = (char *)NULL; + + nargs = procedureStruct->pronargs; + retval->nargs = nargs; + + if (nargs > 0) + { + Oid *argTypes; + + retval->nullVect = (bool *)palloc((retval->nargs)*sizeof(bool)); + + if (retval->language == SQLlanguageId) + { + int i; + List *oneArg; + + retval->argOidVect = + (Oid *)palloc(retval->nargs*sizeof(Oid)); + argTypes = procedureStruct->proargtypes; + memmove(retval->argOidVect, + argTypes, + (retval->nargs)*sizeof(Oid)); + + for (i=0; + argList; + i++, argList = lnext(argList)) + { + oneArg = lfirst(argList); + if (FuncArgTypeIsDynamic(oneArg)) + retval->argOidVect[i] = GetDynamicFuncArgType((Var*)oneArg, + econtext); + } + } + else + retval->argOidVect = (Oid *)NULL; + } + else + { + retval->argOidVect = (Oid *)NULL; + retval->nullVect = (BoolPtr)NULL; + } + + /* + * XXX this is the first varlena in the struct. If the order + * changes for some reason this will fail. + */ + if (procedureStruct->prolang == SQLlanguageId) + { + retval->src = textout(&(procedureStruct->prosrc)); + retval->bin = (char *) NULL; + } + else + { + + /* + * I'm not sure that we even need to do this at all. + */ + + /* + * We do for untrusted functions. + */ + + if (procedureStruct->proistrusted) + retval->bin = (char *) NULL; + else { + tmp = (text *) + SearchSysCacheGetAttribute(PROOID, + Anum_pg_proc_probin, + ObjectIdGetDatum(foid), + 0,0,0); + retval->bin = textout(tmp); + } + retval->src = (char *) NULL; + } + + + + + if (retval->language != SQLlanguageId) + fmgr_info(foid, &(retval->func), &(retval->nargs)); + else + retval->func = (func_ptr)NULL; + + + return(retval); +} + +void +setFcache(Node *node, Oid foid, List *argList, ExprContext *econtext) +{ + Func *fnode; + Oper *onode; + FunctionCachePtr fcache; + + fcache = init_fcache(foid, true, argList, econtext); + + if (IsA(node,Oper)) { + onode = (Oper*) node; + onode->op_fcache = fcache; + }else if (IsA(node,Func)) { + fnode = (Func*) node; + fnode->func_fcache = fcache; + }else { + elog(WARN, "init_fcache: node must be Oper or Func!"); + } +} diff --git a/src/backend/utils/cache/inval.c b/src/backend/utils/cache/inval.c new file mode 100644 index 00000000000..7bd214d085b --- /dev/null +++ b/src/backend/utils/cache/inval.c @@ -0,0 +1,612 @@ +/*------------------------------------------------------------------------- + * + * inval.c-- + * POSTGRES cache invalidation dispatcher code. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/cache/inval.c,v 1.1.1.1 1996/07/09 06:22:06 scrappy Exp $ + * + * Note - this code is real crufty... + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" +#include "access/heapam.h" /* XXX to support hacks below */ +#include "access/htup.h" +#include "catalog/catalog.h" +#include "storage/bufpage.h" +#include "storage/buf.h" /* XXX for InvalidBuffer */ +#include "storage/ipc.h" +#include "storage/sinval.h" +#include "utils/catcache.h" +#include "utils/inval.h" +#include "utils/elog.h" +#include "utils/rel.h" +#include "utils/relcache.h" +#include "catalog/catname.h" /* XXX to support hacks below */ +#include "utils/syscache.h" /* XXX to support the hacks below */ + +/* ---------------- + * private invalidation structures + * ---------------- + */ +typedef struct CatalogInvalidationData { + Index cacheId; + Index hashIndex; + ItemPointerData pointerData; +} CatalogInvalidationData; + +typedef struct RelationInvalidationData { + Oid relationId; + Oid objectId; +} RelationInvalidationData; + +typedef union AnyInvalidation { + CatalogInvalidationData catalog; + RelationInvalidationData relation; +} AnyInvalidation; + +typedef struct InvalidationMessageData { + char kind; + AnyInvalidation any; +} InvalidationMessageData; + +typedef InvalidationMessageData *InvalidationMessage; + +/* ---------------- + * variables and macros + * ---------------- + */ +static LocalInvalid Invalid = EmptyLocalInvalid; /* XXX global */ +static bool RefreshWhenInvalidate = false; + +Oid MyRelationRelationId = InvalidOid; +Oid MyAttributeRelationId = InvalidOid; +Oid MyAMRelationId = InvalidOid; +Oid MyAMOPRelationId = InvalidOid; + +#define ValidateHacks() \ + if (!OidIsValid(MyRelationRelationId)) getmyrelids() + +/* ---------------------------------------------------------------- + * "local" invalidation support functions + * ---------------------------------------------------------------- + */ + +/* -------------------------------- + * InvalidationEntryAllocate-- + * Allocates an invalidation entry. + * -------------------------------- + */ +InvalidationEntry +InvalidationEntryAllocate(uint16 size) +{ + InvalidationEntryData *entryDataP; + entryDataP = (InvalidationEntryData *) + malloc(sizeof (char *) + size); /* XXX alignment */ + entryDataP->nextP = NULL; + return ((Pointer) &entryDataP->userData); +} + +/* -------------------------------- + * LocalInvalidRegister -- + * Returns a new local cache invalidation state containing a new entry. + * -------------------------------- + */ +LocalInvalid +LocalInvalidRegister(LocalInvalid invalid, + InvalidationEntry entry) +{ + Assert(PointerIsValid(entry)); + + ((InvalidationUserData *)entry)->dataP[-1] = + (InvalidationUserData *)invalid; + + return (entry); +} + +/* -------------------------------- + * LocalInvalidInvalidate-- + * Processes, then frees all entries in a local cache + * invalidation state. + * -------------------------------- + */ +void +LocalInvalidInvalidate(LocalInvalid invalid, void (*function)()) +{ + InvalidationEntryData *entryDataP; + + while (PointerIsValid(invalid)) { + entryDataP = (InvalidationEntryData *) + &((InvalidationUserData *)invalid)->dataP[-1]; + + if (PointerIsValid(function)) { + (*function)((Pointer) &entryDataP->userData); + } + + invalid = (Pointer) entryDataP->nextP; + + /* help catch errors */ + entryDataP->nextP = (InvalidationUserData *) NULL; + + free((Pointer)entryDataP); + } +} + +/* ---------------------------------------------------------------- + * private support functions + * ---------------------------------------------------------------- + */ +/* -------------------------------- + * CacheIdRegisterLocalInvalid + * -------------------------------- + */ +#ifdef INVALIDDEBUG +#define CacheIdRegisterLocalInvalid_DEBUG1 \ +elog(DEBUG, "CacheIdRegisterLocalInvalid(%d, %d, [%d, %d])", \ + cacheId, hashIndex, ItemPointerGetBlockNumber(pointer), \ + ItemPointerGetOffsetNumber(pointer)) +#else +#define CacheIdRegisterLocalInvalid_DEBUG1 +#endif /* INVALIDDEBUG */ + +static void +CacheIdRegisterLocalInvalid(Index cacheId, + Index hashIndex, + ItemPointer pointer) +{ + InvalidationMessage message; + + /* ---------------- + * debugging stuff + * ---------------- + */ + CacheIdRegisterLocalInvalid_DEBUG1; + + /* ---------------- + * create a message describing the system catalog tuple + * we wish to invalidate. + * ---------------- + */ + message = (InvalidationMessage) + InvalidationEntryAllocate(sizeof (InvalidationMessageData)); + + message->kind = 'c'; + message->any.catalog.cacheId = cacheId; + message->any.catalog.hashIndex = hashIndex; + + ItemPointerCopy(pointer, &message->any.catalog.pointerData); + + /* ---------------- + * Note: Invalid is a global variable + * ---------------- + */ + Invalid = LocalInvalidRegister(Invalid, (InvalidationEntry)message); +} + +/* -------------------------------- + * RelationIdRegisterLocalInvalid + * -------------------------------- + */ +static void +RelationIdRegisterLocalInvalid(Oid relationId, Oid objectId) +{ + InvalidationMessage message; + + /* ---------------- + * debugging stuff + * ---------------- + */ +#ifdef INVALIDDEBUG + elog(DEBUG, "RelationRegisterLocalInvalid(%d, %d)", relationId, + objectId); +#endif /* defined(INVALIDDEBUG) */ + + /* ---------------- + * create a message describing the relation descriptor + * we wish to invalidate. + * ---------------- + */ + message = (InvalidationMessage) + InvalidationEntryAllocate(sizeof (InvalidationMessageData)); + + message->kind = 'r'; + message->any.relation.relationId = relationId; + message->any.relation.objectId = objectId; + + /* ---------------- + * Note: Invalid is a global variable + * ---------------- + */ + Invalid = LocalInvalidRegister(Invalid, (InvalidationEntry)message); +} + +/* -------------------------------- + * getmyrelids + * -------------------------------- + */ +void +getmyrelids() +{ + HeapTuple tuple; + + tuple = SearchSysCacheTuple(RELNAME, + PointerGetDatum(RelationRelationName), + 0,0,0); + Assert(HeapTupleIsValid(tuple)); + MyRelationRelationId = tuple->t_oid; + + tuple = SearchSysCacheTuple(RELNAME, + PointerGetDatum(AttributeRelationName), + 0,0,0); + Assert(HeapTupleIsValid(tuple)); + MyAttributeRelationId = tuple->t_oid; + + tuple = SearchSysCacheTuple(RELNAME, + PointerGetDatum(AccessMethodRelationName), + 0,0,0); + Assert(HeapTupleIsValid(tuple)); + MyAMRelationId = tuple->t_oid; + + tuple = SearchSysCacheTuple(RELNAME, + PointerGetDatum(AccessMethodOperatorRelationName), + 0,0,0); + Assert(HeapTupleIsValid(tuple)); + MyAMOPRelationId = tuple->t_oid; +} + +/* -------------------------------- + * CacheIdInvalidate + * + * This routine can invalidate an tuple in a system catalog cache + * or a cached relation descriptor. You pay your money and you + * take your chances... + * -------------------------------- + */ +#ifdef INVALIDDEBUG +#define CacheIdInvalidate_DEBUG1 \ +elog(DEBUG, "CacheIdInvalidate(%d, %d, 0x%x[%d])", cacheId, hashIndex,\ + pointer, ItemPointerIsValid(pointer)) +#else +#define CacheIdInvalidate_DEBUG1 +#endif /* defined(INVALIDDEBUG) */ + +static void +CacheIdInvalidate(Index cacheId, + Index hashIndex, + ItemPointer pointer) +{ + /* ---------------- + * assume that if the item pointer is valid, then we are + * invalidating an item in the specified system catalog cache. + * ---------------- + */ + if (ItemPointerIsValid(pointer)) { + CatalogCacheIdInvalidate(cacheId, hashIndex, pointer); + return; + } + + CacheIdInvalidate_DEBUG1; + + ValidateHacks(); /* XXX */ + + /* ---------------- + * if the cacheId is the oid of any of the tuples in the + * following system relations, then assume we are invalidating + * a relation descriptor + * ---------------- + */ + if (cacheId == MyRelationRelationId) { + RelationIdInvalidateRelationCacheByRelationId(hashIndex); + return; + } + + if (cacheId == MyAttributeRelationId) { + RelationIdInvalidateRelationCacheByRelationId(hashIndex); + return; + } + + if (cacheId == MyAMRelationId) { + RelationIdInvalidateRelationCacheByAccessMethodId(hashIndex); + return; + } + + if (cacheId == MyAMOPRelationId) { + RelationIdInvalidateRelationCacheByAccessMethodId(InvalidOid); + return; + } + + /* ---------------- + * Yow! the caller asked us to invalidate something else. + * ---------------- + */ + elog(FATAL, "CacheIdInvalidate: cacheId=%d relation id?", cacheId); +} + +/* -------------------------------- + * ResetSystemCaches + * + * this blows away all tuples in the system catalog caches and + * all the cached relation descriptors (and closes the files too). + * -------------------------------- + */ +static void +ResetSystemCaches() +{ + ResetSystemCache(); + RelationCacheInvalidate(false); +} + +/* -------------------------------- + * InvalidationMessageRegisterSharedInvalid + * -------------------------------- + */ +#ifdef INVALIDDEBUG +#define InvalidationMessageRegisterSharedInvalid_DEBUG1 \ +elog(DEBUG,\ + "InvalidationMessageRegisterSharedInvalid(c, %d, %d, [%d, %d])",\ + message->any.catalog.cacheId,\ + message->any.catalog.hashIndex,\ + ItemPointerGetBlockNumber(&message->any.catalog.pointerData),\ + ItemPointerGetOffsetNumber(&message->any.catalog.pointerData)) +#define InvalidationMessageRegisterSharedInvalid_DEBUG2 \ + elog(DEBUG, \ + "InvalidationMessageRegisterSharedInvalid(r, %d, %d)", \ + message->any.relation.relationId, \ + message->any.relation.objectId) +#else +#define InvalidationMessageRegisterSharedInvalid_DEBUG1 +#define InvalidationMessageRegisterSharedInvalid_DEBUG2 +#endif /* INVALIDDEBUG */ + +static void +InvalidationMessageRegisterSharedInvalid(InvalidationMessage message) +{ + Assert(PointerIsValid(message)); + + switch (message->kind) { + case 'c': /* cached system catalog tuple */ + InvalidationMessageRegisterSharedInvalid_DEBUG1; + + RegisterSharedInvalid(message->any.catalog.cacheId, + message->any.catalog.hashIndex, + &message->any.catalog.pointerData); + break; + + case 'r': /* cached relation descriptor */ + InvalidationMessageRegisterSharedInvalid_DEBUG2; + + RegisterSharedInvalid(message->any.relation.relationId, + message->any.relation.objectId, + (ItemPointer) NULL); + break; + + default: + elog(FATAL, + "InvalidationMessageRegisterSharedInvalid: `%c' kind", + message->kind); + } +} + +/* -------------------------------- + * InvalidationMessageCacheInvalidate + * -------------------------------- + */ +#ifdef INVALIDDEBUG +#define InvalidationMessageCacheInvalidate_DEBUG1 \ +elog(DEBUG, "InvalidationMessageCacheInvalidate(c, %d, %d, [%d, %d])",\ + message->any.catalog.cacheId,\ + message->any.catalog.hashIndex,\ + ItemPointerGetBlockNumber(&message->any.catalog.pointerData),\ + ItemPointerGetOffsetNumber(&message->any.catalog.pointerData)) +#define InvalidationMessageCacheInvalidate_DEBUG2 \ + elog(DEBUG, "InvalidationMessageCacheInvalidate(r, %d, %d)", \ + message->any.relation.relationId, \ + message->any.relation.objectId) +#else +#define InvalidationMessageCacheInvalidate_DEBUG1 +#define InvalidationMessageCacheInvalidate_DEBUG2 +#endif /* defined(INVALIDDEBUG) */ + +static void +InvalidationMessageCacheInvalidate(InvalidationMessage message) +{ + Assert(PointerIsValid(message)); + + switch (message->kind) { + case 'c': /* cached system catalog tuple */ + InvalidationMessageCacheInvalidate_DEBUG1; + + CatalogCacheIdInvalidate(message->any.catalog.cacheId, + message->any.catalog.hashIndex, + &message->any.catalog.pointerData); + break; + + case 'r': /* cached relation descriptor */ + InvalidationMessageCacheInvalidate_DEBUG2; + + /* XXX ignore this--is this correct ??? */ + break; + + default: + elog(FATAL, "InvalidationMessageCacheInvalidate: `%c' kind", + message->kind); + } +} + +/* -------------------------------- + * RelationInvalidateRelationCache + * -------------------------------- + */ +static void +RelationInvalidateRelationCache(Relation relation, + HeapTuple tuple, + void (*function)()) +{ + Oid relationId; + Oid objectId; + + /* ---------------- + * get the relation object id + * ---------------- + */ + ValidateHacks(); /* XXX */ + relationId = RelationGetRelationId(relation); + + /* ---------------- + * + * ---------------- + */ + if (relationId == MyRelationRelationId) { + objectId = tuple->t_oid; + } else if (relationId == MyAttributeRelationId) { + objectId = ((AttributeTupleForm)GETSTRUCT(tuple))->attrelid; + } else if (relationId == MyAMRelationId) { + objectId = tuple->t_oid; + } else if (relationId == MyAMOPRelationId) { + ; /* objectId is unused */ + } else + return; + + /* ---------------- + * can't handle immediate relation descriptor invalidation + * ---------------- + */ + Assert(PointerIsValid(function)); + + (*function)(relationId, objectId); +} + +/* + * DiscardInvalid -- + * Causes the invalidated cache state to be discarded. + * + * Note: + * This should be called as the first step in processing a transaction. + * This should be called while waiting for a query from the front end + * when other backends are active. + */ +void +DiscardInvalid() +{ + /* ---------------- + * debugging stuff + * ---------------- + */ +#ifdef INVALIDDEBUG + elog(DEBUG, "DiscardInvalid called"); +#endif /* defined(INVALIDDEBUG) */ + + InvalidateSharedInvalid(CacheIdInvalidate, ResetSystemCaches); +} + +/* + * RegisterInvalid -- + * Causes registration of invalidated state with other backends iff true. + * + * Note: + * This should be called as the last step in processing a transaction. + */ +void +RegisterInvalid(bool send) +{ + /* ---------------- + * debugging stuff + * ---------------- + */ +#ifdef INVALIDDEBUG + elog(DEBUG, "RegisterInvalid(%d) called", send); +#endif /* defined(INVALIDDEBUG) */ + + /* ---------------- + * Note: Invalid is a global variable + * ---------------- + */ + if (send) + LocalInvalidInvalidate(Invalid, + InvalidationMessageRegisterSharedInvalid); + else + LocalInvalidInvalidate(Invalid, + InvalidationMessageCacheInvalidate); + + Invalid = EmptyLocalInvalid; +} + +/* + * SetRefreshWhenInvalidate -- + * Causes the local caches to be immediately refreshed iff true. + */ +void +SetRefreshWhenInvalidate(bool on) +{ +#ifdef INVALIDDEBUG + elog(DEBUG, "RefreshWhenInvalidate(%d) called", on); +#endif /* defined(INVALIDDEBUG) */ + + RefreshWhenInvalidate = on; +} + +/* + * RelationIdInvalidateHeapTuple -- + * Causes the given tuple in a relation to be invalidated. + * + * Note: + * Assumes object id is valid. + * Assumes tuple is valid. + */ +#ifdef INVALIDDEBUG +#define RelationInvalidateHeapTuple_DEBUG1 \ +elog(DEBUG, "RelationInvalidateHeapTuple(%.16s, [%d,%d])", \ + RelationGetRelationName(relation), \ + ItemPointerGetBlockNumber(&tuple->t_ctid), \ + ItemPointerGetOffsetNumber(&tuple->t_ctid)) +#else +#define RelationInvalidateHeapTuple_DEBUG1 +#endif /* defined(INVALIDDEBUG) */ + +void +RelationInvalidateHeapTuple(Relation relation, HeapTuple tuple) +{ + /* ---------------- + * sanity checks + * ---------------- + */ + Assert(RelationIsValid(relation)); + Assert(HeapTupleIsValid(tuple)); + + if (IsBootstrapProcessingMode()) + return; + /* ---------------- + * this only works for system relations now + * ---------------- + */ + if (! IsSystemRelationName(RelationGetRelationTupleForm(relation)->relname.data)) + return; + + /* ---------------- + * debugging stuff + * ---------------- + */ + RelationInvalidateHeapTuple_DEBUG1; + + /* ---------------- + * + * ---------------- + */ + RelationInvalidateCatalogCacheTuple(relation, + tuple, + CacheIdRegisterLocalInvalid); + + RelationInvalidateRelationCache(relation, + tuple, + RelationIdRegisterLocalInvalid); + + if (RefreshWhenInvalidate) + RelationInvalidateCatalogCacheTuple(relation, + tuple, + (void (*)()) NULL); +} + diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c new file mode 100644 index 00000000000..b7fc5e26201 --- /dev/null +++ b/src/backend/utils/cache/lsyscache.c @@ -0,0 +1,484 @@ +/*------------------------------------------------------------------------- + * + * lsyscache.c-- + * Routines to access information within system caches + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.1.1.1 1996/07/09 06:22:06 scrappy Exp $ + * + * NOTES + * Eventually, the index information should go through here, too. + * + * Most of these routines call SearchSysCacheStruct() and thus simply + * (1) allocate some space for the return struct and (2) call it. + * + *------------------------------------------------------------------------- + */ +#include <string.h> +#include "postgres.h" + +#include "nodes/pg_list.h" +#include "utils/syscache.h" +#include "utils/lsyscache.h" +#include "access/tupmacs.h" +#include "utils/rel.h" +#include "utils/palloc.h" +#include "utils/elog.h" +#include "access/attnum.h" +#include "access/heapam.h" + +#include "catalog/pg_amop.h" +#include "catalog/pg_type.h" + +/* ---------- AMOP CACHES ---------- */ + +/* + * op_class - + * + * Return t iff operator 'opno' is in operator class 'opclass'. + * + */ +bool +op_class(Oid opno, int32 opclass, Oid amopid) +{ + FormData_pg_amop amoptup; + + if (SearchSysCacheStruct(AMOPOPID, + (char *) &amoptup, + ObjectIdGetDatum(opclass), + ObjectIdGetDatum(opno), + ObjectIdGetDatum(amopid), + 0)) + return(true); + else + return(false); +} + +/* ---------- ATTRIBUTE CACHES ---------- */ + +/* + * get_attname - + * + * Given the relation id and the attribute number, + * return the "attname" field from the attribute relation. + * + */ +char* +get_attname(Oid relid, AttrNumber attnum) +{ + FormData_pg_attribute att_tup; + char *retval; + + if (SearchSysCacheStruct(ATTNUM, + (char*)&att_tup, + ObjectIdGetDatum(relid), + UInt16GetDatum(attnum), + 0,0)) { + retval = pstrdup(att_tup.attname.data); + + return(retval); + } + else + return(NULL); +} + +/* + * get_attnum - + * + * Given the relation id and the attribute name, + * return the "attnum" field from the attribute relation. + * + */ +AttrNumber +get_attnum(Oid relid, char *attname) +{ + FormData_pg_attribute att_tup; + + if (SearchSysCacheStruct(ATTNAME, (char *) &att_tup, + ObjectIdGetDatum(relid), + PointerGetDatum(attname), + 0,0)) + return(att_tup.attnum); + else + return(InvalidAttrNumber); +} + +/* + * get_atttype - + * + * Given the relation OID and the attribute number with the relation, + * return the attribute type OID. + * + */ +Oid +get_atttype(Oid relid, AttrNumber attnum) +{ + AttributeTupleForm att_tup = (AttributeTupleForm)palloc(sizeof(*att_tup)); + + if (SearchSysCacheStruct(ATTNUM, + (char *) att_tup, + ObjectIdGetDatum(relid), + UInt16GetDatum(attnum), + 0,0)) + return(att_tup->atttypid); + else + return((Oid)NULL); +} + +/* This routine uses the attname instead of the attnum because it + * replaces the routine find_atttype, which is called sometimes when + * only the attname, not the attno, is available. + */ +bool +get_attisset(Oid relid, char *attname) +{ + HeapTuple htup; + AttrNumber attno; + AttributeTupleForm att_tup; + + attno = get_attnum(relid, attname); + + htup = SearchSysCacheTuple(ATTNAME, + ObjectIdGetDatum(relid), + PointerGetDatum(attname), + 0,0); + if (!HeapTupleIsValid(htup)) + elog(WARN, "get_attisset: no attribute %.16s in relation %d", + attname, relid); + if (heap_attisnull(htup, attno)) + return(false); + else { + att_tup = (AttributeTupleForm)GETSTRUCT(htup); + return(att_tup->attisset); + } +} + +/* ---------- INDEX CACHE ---------- */ + +/* watch this space... + */ + +/* ---------- OPERATOR CACHE ---------- */ + +/* + * get_opcode - + * + * Returns the regproc id of the routine used to implement an + * operator given the operator uid. + * + */ +RegProcedure +get_opcode(Oid opno) +{ + FormData_pg_operator optup; + + if (SearchSysCacheStruct(OPROID, (char *) &optup, + ObjectIdGetDatum(opno), + 0,0,0)) + return(optup.oprcode); + else + return((RegProcedure)NULL); +} + +/* + * get_opname - + * returns the name of the operator with the given opno + * + * Note: return the struct so that it gets copied. + */ +char* +get_opname(Oid opno) +{ + FormData_pg_operator optup; + + if (SearchSysCacheStruct(OPROID, (char *) &optup, + ObjectIdGetDatum(opno), + 0,0,0)) + return (pstrdup(optup.oprname.data)); + else { + elog(WARN, "can't look up operator %d\n", opno); + return NULL; + } +} + +/* + * op_mergesortable - + * + * Returns the left and right sort operators and types corresponding to a + * mergesortable operator, or nil if the operator is not mergesortable. + * + */ +bool +op_mergesortable(Oid opno, Oid ltype, Oid rtype, Oid *leftOp, Oid *rightOp) +{ + FormData_pg_operator optup; + + if (SearchSysCacheStruct(OPROID, (char *) &optup, + ObjectIdGetDatum(opno), + 0,0,0) && + optup.oprlsortop && + optup.oprrsortop && + optup.oprleft == ltype && + optup.oprright == rtype) { + + *leftOp = ObjectIdGetDatum(optup.oprlsortop); + *rightOp = ObjectIdGetDatum(optup.oprrsortop); + return TRUE; + } else { + return FALSE; + } +} + +/* + * op_hashjoinable-- + * + * Returns the hash operator corresponding to a hashjoinable operator, + * or nil if the operator is not hashjoinable. + * + */ +Oid +op_hashjoinable(Oid opno, Oid ltype, Oid rtype) +{ + FormData_pg_operator optup; + + if (SearchSysCacheStruct(OPROID, (char *) &optup, + ObjectIdGetDatum(opno), + 0,0,0) && + optup.oprcanhash && + optup.oprleft == ltype && + optup.oprright == rtype) + return(opno); + else + return(InvalidOid); +} + +/* + * get_commutator - + * + * Returns the corresponding commutator of an operator. + * + */ +Oid +get_commutator(Oid opno) +{ + FormData_pg_operator optup; + + if (SearchSysCacheStruct(OPROID, (char *) &optup, + ObjectIdGetDatum(opno), + 0,0,0)) + return(optup.oprcom); + else + return((Oid)NULL); +} + +HeapTuple +get_operator_tuple(Oid opno) +{ + HeapTuple optup; + + if ((optup = SearchSysCacheTuple(OPROID, + ObjectIdGetDatum(opno), + 0,0,0))) + return(optup); + else + return((HeapTuple)NULL); +} + +/* + * get_negator - + * + * Returns the corresponding negator of an operator. + * + */ +Oid +get_negator(Oid opno) +{ + FormData_pg_operator optup; + + if (SearchSysCacheStruct(OPROID, (char *) &optup, + ObjectIdGetDatum(opno), + 0,0,0)) + return(optup.oprnegate); + else + return((Oid)NULL); +} + +/* + * get_oprrest - + * + * Returns procedure id for computing selectivity of an operator. + * + */ +RegProcedure +get_oprrest(Oid opno) +{ + FormData_pg_operator optup; + + if (SearchSysCacheStruct(OPROID, (char *) &optup, + ObjectIdGetDatum(opno), + 0,0,0)) + return(optup.oprrest ); + else + return((RegProcedure) NULL); +} + +/* + * get_oprjoin - + * + * Returns procedure id for computing selectivity of a join. + * + */ +RegProcedure +get_oprjoin(Oid opno) +{ + FormData_pg_operator optup; + + if (SearchSysCacheStruct(OPROID, (char *) &optup, + ObjectIdGetDatum(opno), + 0,0,0)) + return(optup.oprjoin); + else + return((RegProcedure)NULL); +} + +/* ---------- RELATION CACHE ---------- */ + +/* + * get_relnatts - + * + * Returns the number of attributes for a given relation. + * + */ +int +get_relnatts(Oid relid) +{ + FormData_pg_class reltup; + + if (SearchSysCacheStruct(RELOID, (char *) &reltup, + ObjectIdGetDatum(relid), + 0,0,0)) + return(reltup.relnatts); + else + return(InvalidAttrNumber); +} + +/* + * get_rel_name - + * + * Returns the name of a given relation. + * + */ +char* +get_rel_name(Oid relid) +{ + FormData_pg_class reltup; + + if ((SearchSysCacheStruct(RELOID, + (char*)&reltup, + ObjectIdGetDatum(relid), + 0,0,0))) { + return (pstrdup(reltup.relname.data)); + } else + return(NULL); +} + +/* ---------- TYPE CACHE ---------- */ + +/* + * get_typlen - + * + * Given the type OID, return the length of the type. + * + */ +int16 +get_typlen(Oid typid) +{ + TypeTupleFormData typtup; + + if (SearchSysCacheStruct(TYPOID, (char *) &typtup, + ObjectIdGetDatum(typid), + 0,0,0)) + return(typtup.typlen); + else + return((int16)NULL); +} + +/* + * get_typbyval - + * + * Given the type OID, determine whether the type is returned by value or + * not. Returns 1 if by value, 0 if by reference. + * + */ +bool +get_typbyval(Oid typid) +{ + TypeTupleFormData typtup; + + if (SearchSysCacheStruct(TYPOID, (char *) &typtup, + ObjectIdGetDatum(typid), + 0,0,0)) + return((bool)typtup.typbyval); + else + return(false); +} + +/* + * get_typbyval - + * + * Given the type OID, determine whether the type is returned by value or + * not. Returns 1 if by value, 0 if by reference. + * + */ +char +get_typalign(Oid typid) +{ + TypeTupleFormData typtup; + + if (SearchSysCacheStruct(TYPOID, (char *) &typtup, + ObjectIdGetDatum(typid), + 0,0,0)) + return(typtup.typalign); + else + return ('i'); +} + +/* + * get_typdefault - + * + * Given the type OID, return the default value of the ADT. + * + */ +struct varlena * +get_typdefault(Oid typid) +{ + struct varlena *typdefault = + (struct varlena *)TypeDefaultRetrieve (typid); + return(typdefault); +} + +/* + * get_typtype - + * + * Given the type OID, find if it is a basic type, a named relation + * or the generic type 'relation'. + * It returns the null char if the cache lookup fails... + * + */ +char +get_typtype(Oid typid) +{ + TypeTupleFormData typtup; + + if (SearchSysCacheStruct(TYPOID, (char *) &typtup, + ObjectIdGetDatum(typid), + 0,0,0)) { + return(typtup.typtype); + } else { + return('\0'); + } +} + diff --git a/src/backend/utils/cache/rel.c b/src/backend/utils/cache/rel.c new file mode 100644 index 00000000000..33eabad1a85 --- /dev/null +++ b/src/backend/utils/cache/rel.c @@ -0,0 +1,77 @@ +/*------------------------------------------------------------------------- + * + * rel.c-- + * POSTGRES relation descriptor code. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/cache/Attic/rel.c,v 1.1.1.1 1996/07/09 06:22:06 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +/* #define RELREFDEBUG 1 */ + +#include "postgres.h" +#include "miscadmin.h" +#include "access/istrat.h" +#include "access/tupdesc.h" +#include "utils/rel.h" +#include "storage/fd.h" + + +/* + * RelationIsValid is now a macro in rel.h -cim 4/27/91 + * + * Many of the RelationGet...() functions are now macros in rel.h + * -mer 3/2/92 + */ + +/* + * RelationGetIndexStrategy -- + * Returns index strategy for a relation. + * + * Note: + * Assumes relation descriptor is valid. + * Assumes relation descriptor is for an index relation. + */ +IndexStrategy +RelationGetIndexStrategy(Relation relation) +{ + return relation->rd_istrat; +} + +/* + * RelationSetIndexSupport -- + * Sets index strategy and support info for a relation. + * + * Note: + * Assumes relation descriptor is a valid pointer to sufficient space. + * Assumes index strategy is valid. Assumes support is valid if non- + * NULL. + */ +/* ---------------- + * RelationSetIndexSupport + * + * This routine saves two pointers -- one to the IndexStrategy, and + * one to the RegProcs that support the indexed access method. These + * pointers are stored in the space following the attribute data in the + * reldesc. + * + * NEW: the index strategy and support are now stored in real fields + * at the end of the structure - jolly + * ---------------- + */ +void +RelationSetIndexSupport(Relation relation, + IndexStrategy strategy, + RegProcedure *support) +{ + Assert(PointerIsValid(relation)); + Assert(IndexStrategyIsValid(strategy)); + + relation->rd_istrat = strategy; + relation->rd_support = support; +} + diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c new file mode 100644 index 00000000000..4e3f28491b7 --- /dev/null +++ b/src/backend/utils/cache/relcache.c @@ -0,0 +1,1795 @@ +/*------------------------------------------------------------------------- + * + * relcache.c-- + * POSTGRES relation descriptor cache code + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/cache/relcache.c,v 1.1.1.1 1996/07/09 06:22:06 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +/* + * INTERFACE ROUTINES + * RelationInitialize - initialize relcache + * RelationIdCacheGetRelation - get a reldesc from the cache (id) + * RelationNameCacheGetRelation - get a reldesc from the cache (name) + * RelationIdGetRelation - get a reldesc by relation id + * RelationNameGetRelation - get a reldesc by relation name + * RelationClose - close an open relation + * RelationFlushRelation - flush relation information + * + * NOTES + * This file is in the process of being cleaned up + * before I add system attribute indexing. -cim 1/13/91 + * + * The following code contains many undocumented hacks. Please be + * careful.... + * + */ +#include <stdio.h> /* for sprintf() */ +#include <errno.h> +#include <sys/file.h> +#include <string.h> + +#include "postgres.h" +#include "miscadmin.h" + +#include "access/attnum.h" +#include "access/genam.h" +#include "access/heapam.h" +#include "access/htup.h" +#include "access/istrat.h" +#include "access/itup.h" +#include "access/skey.h" +#include "utils/builtins.h" +#include "utils/tqual.h" /* for NowTimeQual */ +#include "access/tupdesc.h" +#include "access/tupmacs.h" +#include "access/xact.h" + +#include "storage/buf.h" +#include "storage/fd.h" /* for SEEK_ */ +#include "storage/lmgr.h" +#include "storage/bufmgr.h" + + +#include "lib/hasht.h" + +#include "utils/memutils.h" +#include "utils/elog.h" +#include "utils/mcxt.h" +#include "utils/rel.h" +#include "utils/relcache.h" +#include "utils/hsearch.h" +#include "utils/palloc.h" +#include "utils/relcache.h" + +#include "catalog/catname.h" +#include "catalog/catalog.h" +#include "utils/syscache.h" + +#include "catalog/pg_attribute.h" +#include "catalog/pg_aggregate.h" +#include "catalog/pg_index.h" +#include "catalog/pg_proc.h" +#include "catalog/pg_class.h" +#include "catalog/pg_rewrite.h" +#include "catalog/pg_type.h" + +#include "catalog/pg_variable.h" +#include "catalog/pg_log.h" +#include "catalog/pg_time.h" +#include "catalog/indexing.h" +#include "catalog/index.h" +#include "fmgr.h" + +/* ---------------- + * defines + * ---------------- + */ +#define private static +#define INIT_FILENAME "pg_internal.init" + +/* ---------------- + * externs + * ---------------- + */ +extern bool AMI_OVERRIDE; /* XXX style */ +extern GlobalMemory CacheCxt; /* from utils/cache/catcache.c */ + +/* ---------------- + * hardcoded tuple descriptors. see lib/backend/catalog/pg_attribute.h + * ---------------- + */ +FormData_pg_attribute Desc_pg_class[Natts_pg_class] = { Schema_pg_class }; +FormData_pg_attribute Desc_pg_attribute[Natts_pg_attribute] = { Schema_pg_attribute }; +FormData_pg_attribute Desc_pg_proc[Natts_pg_proc] = { Schema_pg_proc }; +FormData_pg_attribute Desc_pg_type[Natts_pg_type] = { Schema_pg_type }; +FormData_pg_attribute Desc_pg_variable[Natts_pg_variable] = { Schema_pg_variable }; +FormData_pg_attribute Desc_pg_log[Natts_pg_log] = { Schema_pg_log }; +FormData_pg_attribute Desc_pg_time[Natts_pg_time] = { Schema_pg_time }; + +/* ---------------- + * global variables + * + * Relations are cached two ways, by name and by id, + * thus there are two hash tables for referencing them. + * ---------------- + */ +HTAB *RelationNameCache; +HTAB *RelationIdCache; + +/* ---------------- + * RelationBuildDescInfo exists so code can be shared + * between RelationIdGetRelation() and RelationNameGetRelation() + * ---------------- + */ +typedef struct RelationBuildDescInfo { + int infotype; /* lookup by id or by name */ +#define INFO_RELID 1 +#define INFO_RELNAME 2 + union { + Oid info_id; /* relation object id */ + char *info_name; /* relation name */ + } i; +} RelationBuildDescInfo; + +typedef struct relidcacheent { + Oid reloid; + Relation reldesc; +} RelIdCacheEnt; + +typedef struct relnamecacheent { + NameData relname; + Relation reldesc; +} RelNameCacheEnt; + +/* ----------------- + * macros to manipulate name cache and id cache + * ----------------- + */ +#define RelationCacheInsert(RELATION) \ + { RelIdCacheEnt *idhentry; RelNameCacheEnt *namehentry; \ + char *relname; Oid reloid; bool found; \ + relname = (RELATION->rd_rel->relname).data; \ + namehentry = (RelNameCacheEnt*)hash_search(RelationNameCache, \ + relname, \ + HASH_ENTER, \ + &found); \ + if (namehentry == NULL) { \ + elog(FATAL, "can't insert into relation descriptor cache"); \ + } \ + if (found && !IsBootstrapProcessingMode()) { \ + /* used to give notice -- now just keep quiet */ ; \ + } \ + namehentry->reldesc = RELATION; \ + reloid = RELATION->rd_id; \ + idhentry = (RelIdCacheEnt*)hash_search(RelationIdCache, \ + (char *)&reloid, \ + HASH_ENTER, \ + &found); \ + if (idhentry == NULL) { \ + elog(FATAL, "can't insert into relation descriptor cache"); \ + } \ + if (found && !IsBootstrapProcessingMode()) { \ + /* used to give notice -- now just keep quiet */ ; \ + } \ + idhentry->reldesc = RELATION; \ + } +#define RelationNameCacheLookup(NAME, RELATION) \ + { RelNameCacheEnt *hentry; bool found; \ + hentry = (RelNameCacheEnt*)hash_search(RelationNameCache, \ + (char *)NAME,HASH_FIND,&found); \ + if (hentry == NULL) { \ + elog(FATAL, "error in CACHE"); \ + } \ + if (found) { \ + RELATION = hentry->reldesc; \ + } \ + else { \ + RELATION = NULL; \ + } \ + } +#define RelationIdCacheLookup(ID, RELATION) \ + { RelIdCacheEnt *hentry; bool found; \ + hentry = (RelIdCacheEnt*)hash_search(RelationIdCache, \ + (char *)&(ID),HASH_FIND, &found); \ + if (hentry == NULL) { \ + elog(FATAL, "error in CACHE"); \ + } \ + if (found) { \ + RELATION = hentry->reldesc; \ + } \ + else { \ + RELATION = NULL; \ + } \ + } +#define RelationCacheDelete(RELATION) \ + { RelNameCacheEnt *namehentry; RelIdCacheEnt *idhentry; \ + char *relname; Oid reloid; bool found; \ + relname = (RELATION->rd_rel->relname).data; \ + namehentry = (RelNameCacheEnt*)hash_search(RelationNameCache, \ + relname, \ + HASH_REMOVE, \ + &found); \ + if (namehentry == NULL) { \ + elog(FATAL, "can't delete from relation descriptor cache"); \ + } \ + if (!found) { \ + elog(NOTICE, "trying to delete a reldesc that does not exist."); \ + } \ + reloid = RELATION->rd_id; \ + idhentry = (RelIdCacheEnt*)hash_search(RelationIdCache, \ + (char *)&reloid, \ + HASH_REMOVE, &found); \ + if (idhentry == NULL) { \ + elog(FATAL, "can't delete from relation descriptor cache"); \ + } \ + if (!found) { \ + elog(NOTICE, "trying to delete a reldesc that does not exist."); \ + } \ + } + +/* non-export function prototypes */ +static void formrdesc(char *relationName, u_int natts, + FormData_pg_attribute att[]); + +static void RelationFlushIndexes(Relation *r, Oid accessMethodId); + +static char *BuildDescInfoError(RelationBuildDescInfo buildinfo); +static HeapTuple ScanPgRelation(RelationBuildDescInfo buildinfo); +static HeapTuple scan_pg_rel_seq(RelationBuildDescInfo buildinfo); +static HeapTuple scan_pg_rel_ind(RelationBuildDescInfo buildinfo); +static Relation AllocateRelationDesc(u_int natts, Form_pg_class relp); +static void RelationBuildTupleDesc(RelationBuildDescInfo buildinfo, + Relation relation, AttributeTupleForm attp, u_int natts); +static void build_tupdesc_seq(RelationBuildDescInfo buildinfo, + Relation relation, AttributeTupleForm attp, u_int natts); +static void build_tupdesc_ind(RelationBuildDescInfo buildinfo, + Relation relation, AttributeTupleForm attp, u_int natts); +static Relation RelationBuildDesc(RelationBuildDescInfo buildinfo); +static void IndexedAccessMethodInitialize(Relation relation); + +/* ---------------------------------------------------------------- + * RelationIdGetRelation() and RelationNameGetRelation() + * support functions + * ---------------------------------------------------------------- + */ + + +/* -------------------------------- + * BuildDescInfoError returns a string appropriate to + * the buildinfo passed to it + * -------------------------------- + */ +static char * +BuildDescInfoError(RelationBuildDescInfo buildinfo) +{ + static char errBuf[64]; + + memset(errBuf, 0, (int) sizeof(errBuf)); + switch(buildinfo.infotype) { + case INFO_RELID: + sprintf(errBuf, "(relation id %d)", buildinfo.i.info_id); + break; + case INFO_RELNAME: + sprintf(errBuf, "(relation name %.*s)", NAMEDATALEN, buildinfo.i.info_name); + break; + } + + return errBuf; +} + +/* -------------------------------- + * ScanPgRelation + * + * this is used by RelationBuildDesc to find a pg_class + * tuple matching either a relation name or a relation id + * as specified in buildinfo. + * -------------------------------- + */ +static HeapTuple +ScanPgRelation(RelationBuildDescInfo buildinfo) +{ + /* + * If this is bootstrap time (initdb), then we can't use the system + * catalog indices, because they may not exist yet. Otherwise, we + * can, and do. + */ + + if (IsBootstrapProcessingMode()) + return (scan_pg_rel_seq(buildinfo)); + else + return (scan_pg_rel_ind(buildinfo)); +} + +static HeapTuple +scan_pg_rel_seq(RelationBuildDescInfo buildinfo) +{ + HeapTuple pg_class_tuple; + HeapTuple return_tuple; + Relation pg_class_desc; + HeapScanDesc pg_class_scan; + ScanKeyData key; + Buffer buf; + + /* ---------------- + * form a scan key + * ---------------- + */ + switch (buildinfo.infotype) { + case INFO_RELID: + ScanKeyEntryInitialize(&key, 0, + ObjectIdAttributeNumber, + ObjectIdEqualRegProcedure, + ObjectIdGetDatum(buildinfo.i.info_id)); + break; + + case INFO_RELNAME: + ScanKeyEntryInitialize(&key, 0, + Anum_pg_class_relname, + Character16EqualRegProcedure, + NameGetDatum(buildinfo.i.info_name)); + break; + + default: + elog(WARN, "ScanPgRelation: bad buildinfo"); + return NULL; + } + + /* ---------------- + * open pg_class and fetch a tuple + * ---------------- + */ + pg_class_desc = heap_openr(RelationRelationName); + if (!IsInitProcessingMode()) + RelationSetLockForRead(pg_class_desc); + pg_class_scan = + heap_beginscan(pg_class_desc, 0, NowTimeQual, 1, &key); + pg_class_tuple = heap_getnext(pg_class_scan, 0, &buf); + + /* ---------------- + * get set to return tuple + * ---------------- + */ + if (! HeapTupleIsValid(pg_class_tuple)) { + return_tuple = pg_class_tuple; + } else { + /* ------------------ + * a satanic bug used to live here: pg_class_tuple used to be + * returned here without having the corresponding buffer pinned. + * so when the buffer gets replaced, all hell breaks loose. + * this bug is discovered and killed by wei on 9/27/91. + * ------------------- + */ + return_tuple = (HeapTuple) palloc((Size) pg_class_tuple->t_len); + memmove((char *) return_tuple, + (char *) pg_class_tuple, + (int) pg_class_tuple->t_len); + ReleaseBuffer(buf); + } + + /* all done */ + heap_endscan(pg_class_scan); + if (!IsInitProcessingMode()) + RelationUnsetLockForRead(pg_class_desc); + heap_close(pg_class_desc); + + return return_tuple; +} + +static HeapTuple +scan_pg_rel_ind(RelationBuildDescInfo buildinfo) +{ + Relation pg_class_desc; + HeapTuple return_tuple; + + pg_class_desc = heap_openr(RelationRelationName); + if (!IsInitProcessingMode()) + RelationSetLockForRead(pg_class_desc); + + switch (buildinfo.infotype) { + case INFO_RELID: + return_tuple = ClassOidIndexScan(pg_class_desc, buildinfo.i.info_id); + break; + + case INFO_RELNAME: + return_tuple = ClassNameIndexScan(pg_class_desc, + buildinfo.i.info_name); + break; + + default: + elog(WARN, "ScanPgRelation: bad buildinfo"); + } + + /* all done */ + if (!IsInitProcessingMode()) + RelationUnsetLockForRead(pg_class_desc); + heap_close(pg_class_desc); + + return return_tuple; +} + +/* ---------------- + * AllocateRelationDesc + * + * This is used to allocate memory for a new relation descriptor + * and initialize the rd_rel field. + * ---------------- + */ +static Relation +AllocateRelationDesc(u_int natts, Form_pg_class relp) +{ + Relation relation; + Size len; + Form_pg_class relationTupleForm; + + /* ---------------- + * allocate space for the relation tuple form + * ---------------- + */ + relationTupleForm = (Form_pg_class) + palloc((Size) (sizeof(FormData_pg_class))); + + memmove((char *) relationTupleForm, (char *) relp, CLASS_TUPLE_SIZE); + + /* ---------------- + * allocate space for new relation descriptor + */ + len = sizeof(RelationData) + 10; /* + 10 is voodoo XXX mao */ + + relation = (Relation) palloc(len); + + /* ---------------- + * clear new reldesc + * ---------------- + */ + memset((char *) relation, 0, len); + + /* initialize attribute tuple form */ + relation->rd_att = CreateTemplateTupleDesc(natts); + + /*and initialize relation tuple form */ + relation->rd_rel = relationTupleForm; + + return relation; +} + +/* -------------------------------- + * RelationBuildTupleDesc + * + * Form the relation's tuple descriptor from information in + * the pg_attribute system catalog. + * -------------------------------- + */ +static void +RelationBuildTupleDesc(RelationBuildDescInfo buildinfo, + Relation relation, + AttributeTupleForm attp, + u_int natts) +{ + /* + * If this is bootstrap time (initdb), then we can't use the system + * catalog indices, because they may not exist yet. Otherwise, we + * can, and do. + */ + + if (IsBootstrapProcessingMode()) + build_tupdesc_seq(buildinfo, relation, attp, natts); + else + build_tupdesc_ind(buildinfo, relation, attp, natts); +} + +static void +build_tupdesc_seq(RelationBuildDescInfo buildinfo, + Relation relation, + AttributeTupleForm attp, + u_int natts) +{ + HeapTuple pg_attribute_tuple; + Relation pg_attribute_desc; + HeapScanDesc pg_attribute_scan; + ScanKeyData key; + int need; + + /* ---------------- + * form a scan key + * ---------------- + */ + ScanKeyEntryInitialize(&key, 0, + Anum_pg_attribute_attrelid, + ObjectIdEqualRegProcedure, + ObjectIdGetDatum(relation->rd_id)); + + /* ---------------- + * open pg_attribute and begin a scan + * ---------------- + */ + pg_attribute_desc = heap_openr(AttributeRelationName); + pg_attribute_scan = + heap_beginscan(pg_attribute_desc, 0, NowTimeQual, 1, &key); + + /* ---------------- + * add attribute data to relation->rd_att + * ---------------- + */ + need = natts; + pg_attribute_tuple = heap_getnext(pg_attribute_scan, 0, (Buffer *) NULL); + while (HeapTupleIsValid(pg_attribute_tuple) && need > 0) { + attp = (AttributeTupleForm) GETSTRUCT(pg_attribute_tuple); + + if (attp->attnum > 0) { + relation->rd_att->attrs[attp->attnum - 1] = + (AttributeTupleForm)palloc(ATTRIBUTE_TUPLE_SIZE); + + memmove((char *) (relation->rd_att->attrs[attp->attnum - 1]), + (char *) attp, + ATTRIBUTE_TUPLE_SIZE); + need--; + } + pg_attribute_tuple = heap_getnext(pg_attribute_scan, + 0, (Buffer *) NULL); + } + + if (need > 0) + elog(WARN, "catalog is missing %d attribute%s for relid %d", + need, (need == 1 ? "" : "s"), relation->rd_id); + + /* ---------------- + * end the scan and close the attribute relation + * ---------------- + */ + heap_endscan(pg_attribute_scan); + heap_close(pg_attribute_desc); +} + +static void +build_tupdesc_ind(RelationBuildDescInfo buildinfo, + Relation relation, + AttributeTupleForm attp, + u_int natts) +{ + Relation attrel; + HeapTuple atttup; + int i; + + attrel = heap_openr(AttributeRelationName); + + for (i = 1; i <= relation->rd_rel->relnatts; i++) { + + atttup = (HeapTuple) AttributeNumIndexScan(attrel, relation->rd_id, i); + + if (!HeapTupleIsValid(atttup)) + elog(WARN, "cannot find attribute %d of relation %.16s", i, + &(relation->rd_rel->relname.data[0])); + attp = (AttributeTupleForm) GETSTRUCT(atttup); + + relation->rd_att->attrs[i - 1] = + (AttributeTupleForm) palloc(ATTRIBUTE_TUPLE_SIZE); + + memmove((char *) (relation->rd_att->attrs[i - 1]), + (char *) attp, + ATTRIBUTE_TUPLE_SIZE); + } + + heap_close(attrel); +} + +/* -------------------------------- + * RelationBuildRuleLock + * + * Form the relation's rewrite rules from information in + * the pg_rewrite system catalog. + * -------------------------------- + */ +static void +RelationBuildRuleLock(Relation relation) +{ + HeapTuple pg_rewrite_tuple; + Relation pg_rewrite_desc; + TupleDesc pg_rewrite_tupdesc; + HeapScanDesc pg_rewrite_scan; + ScanKeyData key; + RuleLock *rulelock; + int numlocks; + RewriteRule **rules; + int maxlocks; + + /* ---------------- + * form an array to hold the rewrite rules (the array is extended if + * necessary) + * ---------------- + */ + maxlocks = 4; + rules = (RewriteRule **)palloc(sizeof(RewriteRule*)*maxlocks); + numlocks = 0; + + /* ---------------- + * form a scan key + * ---------------- + */ + ScanKeyEntryInitialize(&key, 0, + Anum_pg_rewrite_ev_class, + ObjectIdEqualRegProcedure, + ObjectIdGetDatum(relation->rd_id)); + + /* ---------------- + * open pg_attribute and begin a scan + * ---------------- + */ + pg_rewrite_desc = heap_openr(RewriteRelationName); + pg_rewrite_scan = + heap_beginscan(pg_rewrite_desc, 0, NowTimeQual, 1, &key); + pg_rewrite_tupdesc = + RelationGetTupleDescriptor(pg_rewrite_desc); + + /* ---------------- + * add attribute data to relation->rd_att + * ---------------- + */ + while ((pg_rewrite_tuple = heap_getnext(pg_rewrite_scan, 0, + (Buffer *) NULL)) != NULL) { + bool isnull; + char *ruleaction = NULL; + char *rule_evqual_string; + RewriteRule *rule; + + rule = (RewriteRule *)palloc(sizeof(RewriteRule)); + + rule->ruleId = pg_rewrite_tuple->t_oid; + + /* XXX too lazy to fix the type cast problem + * (see rewriteDefine.c:121) + */ + rule->event = + (CmdType)((char)heap_getattr(pg_rewrite_tuple, InvalidBuffer, + Anum_pg_rewrite_ev_type, pg_rewrite_tupdesc, + &isnull) - 48); + rule->attrno = + (AttrNumber)heap_getattr(pg_rewrite_tuple, InvalidBuffer, + Anum_pg_rewrite_ev_attr, pg_rewrite_tupdesc, + &isnull); + rule->isInstead = + (bool)heap_getattr(pg_rewrite_tuple, InvalidBuffer, + Anum_pg_rewrite_is_instead, pg_rewrite_tupdesc, + &isnull); + + ruleaction = + heap_getattr(pg_rewrite_tuple, InvalidBuffer, + Anum_pg_rewrite_action, pg_rewrite_tupdesc, + &isnull); + rule_evqual_string = + heap_getattr(pg_rewrite_tuple, InvalidBuffer, + Anum_pg_rewrite_ev_qual, pg_rewrite_tupdesc, + &isnull); + + ruleaction = textout((struct varlena *)ruleaction); + rule_evqual_string = textout((struct varlena *)rule_evqual_string); + + rule->actions = (List*)stringToNode(ruleaction); + rule->qual = (Node*)stringToNode(rule_evqual_string); + + rules[numlocks++] = rule; + if (numlocks==maxlocks) { + maxlocks *= 2; + rules = + (RewriteRule **)repalloc(rules, sizeof(RewriteRule*)*maxlocks); + } + } + + /* ---------------- + * end the scan and close the attribute relation + * ---------------- + */ + heap_endscan(pg_rewrite_scan); + heap_close(pg_rewrite_desc); + + /* ---------------- + * form a RuleLock and insert into relation + * ---------------- + */ + rulelock = (RuleLock *)palloc(sizeof(RuleLock)); + rulelock->numLocks = numlocks; + rulelock->rules = rules; + + relation->rd_rules = rulelock; + return; +} + + +/* -------------------------------- + * RelationBuildDesc + * + * To build a relation descriptor, we have to allocate space, + * open the underlying unix file and initialize the following + * fields: + * + * File rd_fd; open file descriptor + * int rd_nblocks; number of blocks in rel + * it will be set in ambeginscan() + * uint16 rd_refcnt; reference count + * Form_pg_am rd_am; AM tuple + * Form_pg_class rd_rel; RELATION tuple + * Oid rd_id; relations's object id + * Pointer lockInfo; ptr. to misc. info. + * TupleDesc rd_att; tuple desciptor + * + * Note: rd_ismem (rel is in-memory only) is currently unused + * by any part of the system. someday this will indicate that + * the relation lives only in the main-memory buffer pool + * -cim 2/4/91 + * -------------------------------- + */ +static Relation +RelationBuildDesc(RelationBuildDescInfo buildinfo) +{ + File fd; + Relation relation; + u_int natts; + Oid relid; + Oid relam; + Form_pg_class relp; + AttributeTupleForm attp = NULL; + + MemoryContext oldcxt; + + HeapTuple pg_class_tuple; + + oldcxt = MemoryContextSwitchTo((MemoryContext)CacheCxt); + + /* ---------------- + * find the tuple in pg_class corresponding to the given relation id + * ---------------- + */ + pg_class_tuple = ScanPgRelation(buildinfo); + + /* ---------------- + * if no such tuple exists, return NULL + * ---------------- + */ + if (! HeapTupleIsValid(pg_class_tuple)) { + + MemoryContextSwitchTo(oldcxt); + + return NULL; + } + + /* ---------------- + * get information from the pg_class_tuple + * ---------------- + */ + relid = pg_class_tuple->t_oid; + relp = (Form_pg_class) GETSTRUCT(pg_class_tuple); + natts = relp->relnatts; + + /* ---------------- + * allocate storage for the relation descriptor, + * initialize relation->rd_rel and get the access method id. + * ---------------- + */ + relation = AllocateRelationDesc(natts, relp); + relam = relation->rd_rel->relam; + + /* ---------------- + * initialize the relation's relation id (relation->rd_id) + * ---------------- + */ + relation->rd_id = relid; + + /* ---------------- + * initialize relation->rd_refcnt + * ---------------- + */ + RelationSetReferenceCount(relation, 1); + + /* ---------------- + * normal relations are not nailed into the cache + * ---------------- + */ + relation->rd_isnailed = false; + + /* ---------------- + * initialize the access method information (relation->rd_am) + * ---------------- + */ + if (OidIsValid(relam)) { + relation->rd_am = (Form_pg_am) + AccessMethodObjectIdGetAccessMethodTupleForm(relam); + } + + /* ---------------- + * initialize the tuple descriptor (relation->rd_att). + * remember, rd_att is an array of attribute pointers that lives + * off the end of the relation descriptor structure so space was + * already allocated for it by AllocateRelationDesc. + * ---------------- + */ + RelationBuildTupleDesc(buildinfo, relation, attp, natts); + + /* ---------------- + * initialize rules that affect this relation + * ---------------- + */ + if (relp->relhasrules) { + RelationBuildRuleLock(relation); + } else { + relation->rd_rules = NULL; + } + + /* ---------------- + * initialize index strategy and support information for this relation + * ---------------- + */ + if (OidIsValid(relam)) { + IndexedAccessMethodInitialize(relation); + } + + /* ---------------- + * initialize the relation lock manager information + * ---------------- + */ + RelationInitLockInfo(relation); /* see lmgr.c */ + + /* ---------------- + * open the relation and assign the file descriptor returned + * by the storage manager code to rd_fd. + * ---------------- + */ + fd = smgropen(relp->relsmgr, relation); + + Assert (fd >= -1); + if (fd == -1) + elog(NOTICE, "RelationIdBuildRelation: smgropen(%s): %m", + &relp->relname); + + relation->rd_fd = fd; + + /* ---------------- + * insert newly created relation into proper relcaches, + * restore memory context and return the new reldesc. + * ---------------- + */ + + RelationCacheInsert(relation); + + /* ------------------- + * free the memory allocated for pg_class_tuple + * and for lock data pointed to by pg_class_tuple + * ------------------- + */ + pfree(pg_class_tuple); + + MemoryContextSwitchTo(oldcxt); + + return relation; +} + +static void +IndexedAccessMethodInitialize(Relation relation) +{ + IndexStrategy strategy; + RegProcedure *support; + int natts; + Size stratSize; + Size supportSize; + uint16 relamstrategies; + uint16 relamsupport; + + natts = relation->rd_rel->relnatts; + relamstrategies = relation->rd_am->amstrategies; + stratSize = AttributeNumberGetIndexStrategySize(natts, relamstrategies); + strategy = (IndexStrategy) palloc(stratSize); + relamsupport = relation->rd_am->amsupport; + + if (relamsupport > 0) { + supportSize = natts * (relamsupport * sizeof (RegProcedure)); + support = (RegProcedure *) palloc(supportSize); + } else { + support = (RegProcedure *) NULL; + } + + IndexSupportInitialize(strategy, support, + relation->rd_att->attrs[0]->attrelid, + relation->rd_rel->relam, + relamstrategies, relamsupport, natts); + + RelationSetIndexSupport(relation, strategy, support); +} + +/* -------------------------------- + * formrdesc + * + * This is a special version of RelationBuildDesc() + * used by RelationInitialize() in initializing the + * relcache. The system relation descriptors built + * here are all nailed in the descriptor caches, for + * bootstrapping. + * -------------------------------- + */ +static void +formrdesc(char *relationName, + u_int natts, + FormData_pg_attribute att[]) +{ + Relation relation; + Size len; + int i; + + /* ---------------- + * allocate new relation desc + * ---------------- + */ + len = sizeof (RelationData); + relation = (Relation) palloc(len); + memset((char *)relation, 0,len); + + /* ---------------- + * don't open the unix file yet.. + * ---------------- + */ + relation->rd_fd = -1; + + /* ---------------- + * initialize reference count + * ---------------- + */ + RelationSetReferenceCount(relation, 1); + + /* ---------------- + * initialize relation tuple form + * ---------------- + */ + relation->rd_rel = (Form_pg_class) + palloc((Size) (sizeof(*relation->rd_rel))); + memset(relation->rd_rel, 0, sizeof(FormData_pg_class)); + namestrcpy(&relation->rd_rel->relname, relationName); + + /* ---------------- + initialize attribute tuple form + */ + relation->rd_att = CreateTemplateTupleDesc(natts); + + /* + * For debugging purposes, it's important to distinguish between + * shared and non-shared relations, even at bootstrap time. There's + * code in the buffer manager that traces allocations that has to + * know about this. + */ + + if (IsSystemRelationName(relationName)) { + relation->rd_rel->relowner = 6; /* XXX use sym const */ + relation->rd_rel->relisshared = + IsSharedSystemRelationName(relationName); + } else { + relation->rd_rel->relowner = InvalidOid; /* XXX incorrect*/ + relation->rd_rel->relisshared = false; + } + + relation->rd_rel->relpages = 1; /* XXX */ + relation->rd_rel->reltuples = 1; /* XXX */ + relation->rd_rel->relkind = RELKIND_RELATION; + relation->rd_rel->relarch = 'n'; + relation->rd_rel->relnatts = (uint16) natts; + relation->rd_isnailed = true; + + /* ---------------- + * initialize tuple desc info + * ---------------- + */ + for (i = 0; i < natts; i++) { + relation->rd_att->attrs[i] = + (AttributeTupleForm)palloc(ATTRIBUTE_TUPLE_SIZE); + + memset((char *)relation->rd_att->attrs[i], 0, + ATTRIBUTE_TUPLE_SIZE); + memmove((char *)relation->rd_att->attrs[i], + (char *)&att[i], + ATTRIBUTE_TUPLE_SIZE); + } + + /* ---------------- + * initialize relation id + * ---------------- + */ + relation->rd_id = relation->rd_att->attrs[0]->attrelid; + + /* ---------------- + * add new reldesc to relcache + * ---------------- + */ + RelationCacheInsert(relation); + /* + * Determining this requires a scan on pg_class, but to do the + * scan the rdesc for pg_class must already exist. Therefore + * we must do the check (and possible set) after cache insertion. + */ + relation->rd_rel->relhasindex = + CatalogHasIndex(relationName, relation->rd_id); +} + + +/* ---------------------------------------------------------------- + * Relation Descriptor Lookup Interface + * ---------------------------------------------------------------- + */ + +/* -------------------------------- + * RelationIdCacheGetRelation + * + * only try to get the reldesc by looking up the cache + * do not go to the disk. this is used by BlockPrepareFile() + * and RelationIdGetRelation below. + * -------------------------------- + */ +Relation +RelationIdCacheGetRelation(Oid relationId) +{ + Relation rd; + + RelationIdCacheLookup(relationId, rd); + + if (RelationIsValid(rd)) { + if (rd->rd_fd == -1) { + rd->rd_fd = smgropen(rd->rd_rel->relsmgr, rd); + Assert(rd->rd_fd != -1); + } + + RelationIncrementReferenceCount(rd); + RelationSetLockForDescriptorOpen(rd); + + } + + return(rd); +} + +/* -------------------------------- + * RelationNameCacheGetRelation + * -------------------------------- + */ +Relation +RelationNameCacheGetRelation(char *relationName) +{ + Relation rd; + NameData name; + + /* make sure that the name key used for hash lookup is properly + null-padded */ + memset(&name,0, NAMEDATALEN); + namestrcpy(&name, relationName); + RelationNameCacheLookup(name.data, rd); + + if (RelationIsValid(rd)) { + if (rd->rd_fd == -1) { + rd->rd_fd = smgropen(rd->rd_rel->relsmgr, rd); + Assert(rd->rd_fd != -1); + } + + RelationIncrementReferenceCount(rd); + RelationSetLockForDescriptorOpen(rd); + + } + + return(rd); +} + +/* -------------------------------- + * RelationIdGetRelation + * + * return a relation descriptor based on its id. + * return a cached value if possible + * -------------------------------- + */ +Relation +RelationIdGetRelation(Oid relationId) +{ + Relation rd; + RelationBuildDescInfo buildinfo; + + /* ---------------- + * increment access statistics + * ---------------- + */ + IncrHeapAccessStat(local_RelationIdGetRelation); + IncrHeapAccessStat(global_RelationIdGetRelation); + + /* ---------------- + * first try and get a reldesc from the cache + * ---------------- + */ + rd = RelationIdCacheGetRelation(relationId); + if (RelationIsValid(rd)) + return rd; + + /* ---------------- + * no reldesc in the cache, so have RelationBuildDesc() + * build one and add it. + * ---------------- + */ + buildinfo.infotype = INFO_RELID; + buildinfo.i.info_id = relationId; + + rd = RelationBuildDesc(buildinfo); + return + rd; +} + +/* -------------------------------- + * RelationNameGetRelation + * + * return a relation descriptor based on its name. + * return a cached value if possible + * -------------------------------- + */ +Relation +RelationNameGetRelation(char *relationName) +{ + Relation rd; + RelationBuildDescInfo buildinfo; + + /* ---------------- + * increment access statistics + * ---------------- + */ + IncrHeapAccessStat(local_RelationNameGetRelation); + IncrHeapAccessStat(global_RelationNameGetRelation); + + /* ---------------- + * first try and get a reldesc from the cache + * ---------------- + */ + rd = RelationNameCacheGetRelation(relationName); + if (RelationIsValid(rd)) + return rd; + + /* ---------------- + * no reldesc in the cache, so have RelationBuildDesc() + * build one and add it. + * ---------------- + */ + buildinfo.infotype = INFO_RELNAME; + buildinfo.i.info_name = relationName; + + rd = RelationBuildDesc(buildinfo); + return rd; +} + +/* ---------------- + * old "getreldesc" interface. + * ---------------- + */ +Relation +getreldesc(char *relationName) +{ + /* ---------------- + * increment access statistics + * ---------------- + */ + IncrHeapAccessStat(local_getreldesc); + IncrHeapAccessStat(global_getreldesc); + + return RelationNameGetRelation(relationName); +} + +/* ---------------------------------------------------------------- + * cache invalidation support routines + * ---------------------------------------------------------------- + */ + +/* -------------------------------- + * RelationClose - close an open relation + * -------------------------------- + */ +void +RelationClose(Relation relation) +{ + /* Note: no locking manipulations needed */ + RelationDecrementReferenceCount(relation); +} + +/* -------------------------------- + * RelationFlushRelation + * + * Actually blows away a relation... RelationFree doesn't do + * anything anymore. + * -------------------------------- + */ +void +RelationFlushRelation(Relation *relationPtr, + bool onlyFlushReferenceCountZero) +{ + int i; + AttributeTupleForm *p; + MemoryContext oldcxt; + Relation relation = *relationPtr; + + if (relation->rd_isnailed) { + /* this is a nailed special relation for bootstraping */ + return; + } + + if (!onlyFlushReferenceCountZero || + RelationHasReferenceCountZero(relation)) { + + oldcxt = MemoryContextSwitchTo((MemoryContext)CacheCxt); + + RelationCacheDelete(relation); + + FileInvalidate(RelationGetSystemPort(relation)); + + i = relation->rd_rel->relnatts - 1; + p = &relation->rd_att->attrs[i]; + while ((i -= 1) >= 0) { + pfree(*p--); + } + +#if 0 + if (relation->rd_rules) { + int j; + for(j=0; j < relation->rd_rules->numLocks; j++) { + pfree(relation->rd_rules->rules[j]); + } + pfree(relation->rd_rules->rules); + pfree(relation->rd_rules); + } +#endif + + pfree(RelationGetLockInfo(relation)); + pfree(RelationGetRelationTupleForm(relation)); + pfree(relation); + + MemoryContextSwitchTo(oldcxt); + } +} + +/* -------------------------------- + * RelationIdInvalidateRelationCacheByRelationId + * -------------------------------- + */ +void +RelationIdInvalidateRelationCacheByRelationId(Oid relationId) +{ + Relation relation; + + RelationIdCacheLookup(relationId, relation); + + /* + * "local" relations are invalidated by RelationPurgeLocalRelation. + * (This is to make LocalBufferSync's life easier: want the descriptor + * to hang around for a while. In fact, won't we want this for + * BufferSync also? But I'll leave it for now since I don't want to + * break anything.) - ay 3/95 + */ + if (PointerIsValid(relation) && !relation->rd_islocal) { + /* + * The boolean onlyFlushReferenceCountZero in RelationFlushReln() + * should be set to true when we are incrementing the command + * counter and to false when we are starting a new xaction. This + * can be determined by checking the current xaction status. + */ + RelationFlushRelation(&relation, CurrentXactInProgress()); + } +} + +/* -------------------------------- + * RelationIdInvalidateRelationCacheByAccessMethodId + * + * RelationFlushIndexes is needed for use with HashTableWalk.. + * -------------------------------- + */ +static void +RelationFlushIndexes(Relation *r, + Oid accessMethodId) +{ + Relation relation = *r; + + if (!RelationIsValid(relation)) { + elog(NOTICE, "inval call to RFI"); + return; + } + + if (relation->rd_rel->relkind == RELKIND_INDEX && /* XXX style */ + (!OidIsValid(accessMethodId) || + relation->rd_rel->relam == accessMethodId)) + { + RelationFlushRelation(&relation, false); + } +} + +void +RelationIdInvalidateRelationCacheByAccessMethodId(Oid accessMethodId) +{ +# if 0 + /* + * 25 aug 1992: mao commented out the ht walk below. it should be + * doing the right thing, in theory, but flushing reldescs for index + * relations apparently doesn't work. we want to cut 4.0.1, and i + * don't want to introduce new bugs. this code never executed before, + * so i'm turning it off for now. after the release is cut, i'll + * fix this up. + */ + + HashTableWalk(RelationNameCache, (HashtFunc) RelationFlushIndexes, + accessMethodId); +# else + return; +# endif +} + +/* + * RelationCacheInvalidate + * + * Will blow away either all the cached relation descriptors or + * those that have a zero reference count. + * + */ +void +RelationCacheInvalidate(bool onlyFlushReferenceCountZero) +{ + HashTableWalk(RelationNameCache, (HashtFunc) RelationFlushRelation, + onlyFlushReferenceCountZero); + + /* + * nailed-in reldescs will still be in the cache... + * 7 hardwired heaps + 3 hardwired indices == 10 total. + */ + if (!onlyFlushReferenceCountZero) { + Assert(RelationNameCache->hctl->nkeys == 10); + Assert(RelationIdCache->hctl->nkeys == 10); + } +} + + +/* + * newlyCreatedRelns - + * relations created during this transaction. We need to keep track of + * these + */ +static List *newlyCreatedRelns = NULL; + + +/* -------------------------------- + * RelationRegisterRelation - + * register the Relation descriptor of a newly created relation + * with the relation descriptor Cache. + * -------------------------------- + */ +void +RelationRegisterRelation(Relation relation) +{ + MemoryContext oldcxt; + + oldcxt = MemoryContextSwitchTo((MemoryContext)CacheCxt); + + if (oldcxt != (MemoryContext)CacheCxt) + elog(NOIND,"RelationRegisterRelation: WARNING: Context != CacheCxt"); + + RelationCacheInsert(relation); + + RelationInitLockInfo(relation); + + /* + * we've just created the relation. It is invisible to anyone else + * before the transaction is committed. Setting rd_islocal allows us + * to use the local buffer manager for select/insert/etc before the end + * of transaction. (We also need to keep track of relations + * created during a transaction and does the necessary clean up at + * the end of the transaction.) - ay 3/95 + */ + relation->rd_islocal = TRUE; + newlyCreatedRelns = lcons(relation, newlyCreatedRelns); + + MemoryContextSwitchTo(oldcxt); +} + +/* + * RelationPurgeLocalRelation - + * find all the Relation descriptors marked rd_islocal and reset them. + * This should be called at the end of a transaction (commit/abort) when + * the "local" relations will become visible to others and the multi-user + * buffer pool should be used. + */ +void +RelationPurgeLocalRelation(bool xactCommitted) +{ + MemoryContext oldcxt; + + if (newlyCreatedRelns==NULL) + return; + + oldcxt = MemoryContextSwitchTo((MemoryContext)CacheCxt); + + while (newlyCreatedRelns) { + List *l = newlyCreatedRelns; + Relation reln = lfirst(l); + + Assert(reln!=NULL && reln->rd_islocal); + + if (!xactCommitted) { + /* + * remove the file if we abort. This is so that files for + * tables created inside a transaction block get removed. + */ + smgrunlink(reln->rd_rel->relsmgr, reln); + } + + reln->rd_islocal = FALSE; + + if (!IsBootstrapProcessingMode()) + RelationFlushRelation(&reln, FALSE); + + newlyCreatedRelns = lnext(newlyCreatedRelns); + pfree(l); + } + + MemoryContextSwitchTo(oldcxt); +} + +/* -------------------------------- + * RelationInitialize + * + * This initializes the relation descriptor cache. + * -------------------------------- + */ + +#define INITRELCACHESIZE 400 + +void +RelationInitialize() +{ + MemoryContext oldcxt; + HASHCTL ctl; + + /* ---------------- + * switch to cache memory context + * ---------------- + */ + if (!CacheCxt) + CacheCxt = CreateGlobalMemory("Cache"); + + oldcxt = MemoryContextSwitchTo((MemoryContext)CacheCxt); + + /* ---------------- + * create global caches + * ---------------- + */ + memset(&ctl,0, (int) sizeof(ctl)); + ctl.keysize = sizeof(NameData); + ctl.datasize = sizeof(Relation); + RelationNameCache = hash_create(INITRELCACHESIZE, &ctl, HASH_ELEM); + + ctl.keysize = sizeof(Oid); + ctl.hash = tag_hash; + RelationIdCache = hash_create(INITRELCACHESIZE, &ctl, + HASH_ELEM | HASH_FUNCTION); + + /* ---------------- + * initialize the cache with pre-made relation descriptors + * for some of the more important system relations. These + * relations should always be in the cache. + * ---------------- + */ + formrdesc(RelationRelationName, Natts_pg_class, Desc_pg_class); + formrdesc(AttributeRelationName, Natts_pg_attribute, Desc_pg_attribute); + formrdesc(ProcedureRelationName, Natts_pg_proc, Desc_pg_proc); + formrdesc(TypeRelationName, Natts_pg_type, Desc_pg_type); + formrdesc(VariableRelationName, Natts_pg_variable, Desc_pg_variable); + formrdesc(LogRelationName, Natts_pg_log, Desc_pg_log); + formrdesc(TimeRelationName, Natts_pg_time, Desc_pg_time); + + /* + * If this isn't initdb time, then we want to initialize some index + * relation descriptors, as well. The descriptors are for pg_attnumind + * (to make building relation descriptors fast) and possibly others, + * as they're added. + */ + + if (!IsBootstrapProcessingMode()) + init_irels(); + + MemoryContextSwitchTo(oldcxt); +} + +/* + * init_irels(), write_irels() -- handle special-case initialization of + * index relation descriptors. + * + * In late 1992, we started regularly having databases with more than + * a thousand classes in them. With this number of classes, it became + * critical to do indexed lookups on the system catalogs. + * + * Bootstrapping these lookups is very hard. We want to be able to + * use an index on pg_attribute, for example, but in order to do so, + * we must have read pg_attribute for the attributes in the index, + * which implies that we need to use the index. + * + * In order to get around the problem, we do the following: + * + * + When the database system is initialized (at initdb time), we + * don't use indices on pg_attribute. We do sequential scans. + * + * + When the backend is started up in normal mode, we load an image + * of the appropriate relation descriptors, in internal format, + * from an initialization file in the data/base/... directory. + * + * + If the initialization file isn't there, then we create the + * relation descriptor using sequential scans and write it to + * the initialization file for use by subsequent backends. + * + * This is complicated and interferes with system changes, but + * performance is so bad that we're willing to pay the tax. + */ + +/* pg_attnumind, pg_classnameind, pg_classoidind */ +#define Num_indices_bootstrap 3 + +void +init_irels() +{ + Size len; + int nread; + File fd; + Relation irel[Num_indices_bootstrap]; + Relation ird; + Form_pg_am am; + Form_pg_class relform; + IndexStrategy strat; + RegProcedure *support; + int i; + int relno; + + if ((fd = FileNameOpenFile(INIT_FILENAME, O_RDONLY, 0600)) < 0) { + write_irels(); + return; + } + + (void) FileSeek(fd, 0L, SEEK_SET); + + for (relno = 0; relno < Num_indices_bootstrap; relno++) { + /* first read the relation descriptor length*/ + if ((nread = FileRead(fd, (char*)&len, sizeof(int))) != sizeof(int)) { + write_irels(); + return; + } + + ird = irel[relno] = (Relation) palloc(len); + memset(ird, 0, len); + + /* then, read the Relation structure */ + if ((nread = FileRead(fd, (char*)ird, len)) != len) { + write_irels(); + return; + } + + /* the file descriptor is not yet opened */ + ird->rd_fd = -1; + + /* lock info is not initialized */ + ird->lockInfo = (char *) NULL; + + /* next, read the access method tuple form */ + if ((nread = FileRead(fd, (char*)&len, sizeof(int))) != sizeof(int)) { + write_irels(); + return; + } + + am = (Form_pg_am) palloc(len); + if ((nread = FileRead(fd, (char*)am, len)) != len) { + write_irels(); + return; + } + + ird->rd_am = am; + + /* next read the relation tuple form */ + if ((nread = FileRead(fd, (char*)&len, sizeof(int))) != sizeof(int)) { + write_irels(); + return; + } + + relform = (Form_pg_class) palloc(len); + if ((nread = FileRead(fd, (char*)relform, len)) != len) { + write_irels(); + return; + } + + ird->rd_rel = relform; + + /* initialize attribute tuple forms */ + ird->rd_att = CreateTemplateTupleDesc(relform->relnatts); + + /* next read all the attribute tuple form data entries */ + len = ATTRIBUTE_TUPLE_SIZE; + for (i = 0; i < relform->relnatts; i++) { + if ((nread = FileRead(fd, (char*)&len, sizeof(int))) != sizeof(int)) { + write_irels(); + return; + } + + ird->rd_att->attrs[i] = (AttributeTupleForm) palloc(len); + + if ((nread = FileRead(fd, (char*)ird->rd_att->attrs[i], len)) != len) { + write_irels(); + return; + } + } + + /* next, read the index strategy map */ + if ((nread = FileRead(fd, (char*)&len, sizeof(int))) != sizeof(int)) { + write_irels(); + return; + } + + strat = (IndexStrategy) palloc(len); + if ((nread = FileRead(fd, (char*)strat, len)) != len) { + write_irels(); + return; + } + + /* oh, for god's sake... */ +#define SMD(i) strat[0].strategyMapData[i].entry[0] + + /* have to reinit the function pointers in the strategy maps */ + for (i = 0; i < am->amstrategies; i++) + fmgr_info(SMD(i).sk_procedure, + &(SMD(i).sk_func), &(SMD(i).sk_nargs)); + + + /* use a real field called rd_istrat instead of the + bogosity of hanging invisible fields off the end of a structure + - jolly */ + ird->rd_istrat = strat; + + /* finally, read the vector of support procedures */ + if ((nread = FileRead(fd, (char*)&len, sizeof(int))) != sizeof(int)) { + write_irels(); + return; + } + + support = (RegProcedure *) palloc(len); + if ((nread = FileRead(fd, (char*)support, len)) != len) { + write_irels(); + return; + } + + /* + p += sizeof(IndexStrategy); + *((RegProcedure **) p) = support; + */ + + ird->rd_support = support; + + RelationCacheInsert(ird); + } +} + +void +write_irels() +{ + int len; + int nwritten; + File fd; + Relation irel[Num_indices_bootstrap]; + Relation ird; + Form_pg_am am; + Form_pg_class relform; + IndexStrategy strat; + RegProcedure *support; + ProcessingMode oldmode; + int i; + int relno; + RelationBuildDescInfo bi; + + fd = FileNameOpenFile(INIT_FILENAME, O_WRONLY|O_CREAT|O_TRUNC, 0600); + if (fd < 0) + elog(FATAL, "cannot create init file %s", INIT_FILENAME); + + (void) FileSeek(fd, 0L, SEEK_SET); + + /* + * Build a relation descriptor for pg_attnumind without resort to the + * descriptor cache. In order to do this, we set ProcessingMode + * to Bootstrap. The effect of this is to disable indexed relation + * searches -- a necessary step, since we're trying to instantiate + * the index relation descriptors here. + */ + + oldmode = GetProcessingMode(); + SetProcessingMode(BootstrapProcessing); + + bi.infotype = INFO_RELNAME; + bi.i.info_name = AttributeNumIndex; + irel[0] = RelationBuildDesc(bi); + irel[0]->rd_isnailed = true; + + bi.i.info_name = ClassNameIndex; + irel[1] = RelationBuildDesc(bi); + irel[1]->rd_isnailed = true; + + bi.i.info_name = ClassOidIndex; + irel[2] = RelationBuildDesc(bi); + irel[2]->rd_isnailed = true; + + SetProcessingMode(oldmode); + + /* nail the descriptor in the cache */ + for (relno = 0; relno < Num_indices_bootstrap; relno++) { + ird = irel[relno]; + + /* save the volatile fields in the relation descriptor */ + am = ird->rd_am; + ird->rd_am = (Form_pg_am) NULL; + relform = ird->rd_rel; + ird->rd_rel = (Form_pg_class) NULL; + strat = ird->rd_istrat; + support = ird->rd_support; + + /* first write the relation descriptor , excluding strategy and support */ + len = sizeof(RelationData); + + /* first, write the relation descriptor length */ + if ((nwritten = FileWrite(fd, (char*) &len, sizeof(int))) + != sizeof(int)) + elog(FATAL, "cannot write init file -- descriptor length"); + + /* next, write out the Relation structure */ + if ((nwritten = FileWrite(fd, (char*) ird, len)) != len) + elog(FATAL, "cannot write init file -- reldesc"); + + /* next, write the access method tuple form */ + len = sizeof(FormData_pg_am); + if ((nwritten = FileWrite(fd, (char*) &len, sizeof(int))) + != sizeof(int)) + elog(FATAL, "cannot write init file -- am tuple form length"); + + if ((nwritten = FileWrite(fd, (char*) am, len)) != len) + elog(FATAL, "cannot write init file -- am tuple form"); + + /* next write the relation tuple form */ + len = sizeof(FormData_pg_class); + if ((nwritten = FileWrite(fd, (char*) &len, sizeof(int))) + != sizeof(int)) + elog(FATAL, "cannot write init file -- relation tuple form length"); + + if ((nwritten = FileWrite(fd, (char*) relform, len)) != len) + elog(FATAL, "cannot write init file -- relation tuple form"); + + /* next, do all the attribute tuple form data entries */ + len = ATTRIBUTE_TUPLE_SIZE; + for (i = 0; i < relform->relnatts; i++) { + if ((nwritten = FileWrite(fd, (char*) &len, sizeof(int))) + != sizeof(int)) + elog(FATAL, "cannot write init file -- length of attdesc %d", i); + if ((nwritten = FileWrite(fd, (char*) ird->rd_att->attrs[i], len)) + != len) + elog(FATAL, "cannot write init file -- attdesc %d", i); + } + + /* next, write the index strategy map */ + len = AttributeNumberGetIndexStrategySize(relform->relnatts, + am->amstrategies); + if ((nwritten = FileWrite(fd, (char*) &len, sizeof(int))) + != sizeof(int)) + elog(FATAL, "cannot write init file -- strategy map length"); + + if ((nwritten = FileWrite(fd, (char*) strat, len)) != len) + elog(FATAL, "cannot write init file -- strategy map"); + + /* finally, write the vector of support procedures */ + len = relform->relnatts * (am->amsupport * sizeof(RegProcedure)); + if ((nwritten = FileWrite(fd, (char*) &len, sizeof(int))) + != sizeof(int)) + elog(FATAL, "cannot write init file -- support vector length"); + + if ((nwritten = FileWrite(fd, (char*) support, len)) != len) + elog(FATAL, "cannot write init file -- support vector"); + + /* restore volatile fields */ + ird->rd_am = am; + ird->rd_rel = relform; + } + + (void) FileClose(fd); +} diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c new file mode 100644 index 00000000000..36b46d9b99e --- /dev/null +++ b/src/backend/utils/cache/syscache.c @@ -0,0 +1,630 @@ +/*------------------------------------------------------------------------- + * + * syscache.c-- + * System cache management routines + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/cache/syscache.c,v 1.1.1.1 1996/07/09 06:22:07 scrappy Exp $ + * + * NOTES + * These routines allow the parser/planner/executor to perform + * rapid lookups on the contents of the system catalogs. + * + * see catalog/syscache.h for a list of the cache id's + * + *------------------------------------------------------------------------- + */ +#include "c.h" + +#include "access/heapam.h" +#include "access/htup.h" +#include "catalog/catname.h" +#include "utils/catcache.h" +#include "utils/elog.h" +#include "utils/palloc.h" +#include "nodes/pg_list.h" + +/* ---------------- + * hardwired attribute information comes from system catalog files. + * ---------------- + */ +#include "catalog/pg_am.h" +#include "catalog/pg_amop.h" +#include "catalog/pg_attribute.h" +#include "catalog/pg_group.h" +#include "catalog/pg_index.h" +#include "catalog/pg_inherits.h" +#include "catalog/pg_language.h" +#include "catalog/pg_opclass.h" +#include "catalog/pg_operator.h" +#include "catalog/pg_proc.h" +#include "catalog/pg_class.h" +#include "catalog/pg_type.h" +#include "catalog/pg_rewrite.h" +#include "catalog/pg_aggregate.h" +#include "catalog/pg_user.h" +#include "storage/large_object.h" +#include "catalog/pg_listener.h" + +extern bool AMI_OVERRIDE; /* XXX style */ + +#include "utils/syscache.h" +#include "catalog/indexing.h" + +typedef HeapTuple (*ScanFunc)(); + +/* ---------------- + * Warning: cacheinfo[] below is changed, then be sure and + * update the magic constants in syscache.h! + * ---------------- + */ +static struct cachedesc cacheinfo[] = { + { AccessMethodOperatorRelationName, /* AMOPOPID */ + 3, + { Anum_pg_amop_amopclaid, + Anum_pg_amop_amopopr, + Anum_pg_amop_amopid, + 0 }, + sizeof(FormData_pg_amop), + NULL, + (ScanFunc) NULL }, + { AccessMethodOperatorRelationName, /* AMOPSTRATEGY */ + 3, + { Anum_pg_amop_amopid, + Anum_pg_amop_amopclaid, + Anum_pg_amop_amopstrategy, + 0 }, + sizeof(FormData_pg_amop), + NULL, + (ScanFunc) NULL }, + { AttributeRelationName, /* ATTNAME */ + 2, + { Anum_pg_attribute_attrelid, + Anum_pg_attribute_attname, + 0, + 0 }, + ATTRIBUTE_TUPLE_SIZE, + AttributeNameIndex, + (ScanFunc) AttributeNameIndexScan }, + { AttributeRelationName, /* ATTNUM */ + 2, + { Anum_pg_attribute_attrelid, + Anum_pg_attribute_attnum, + 0, + 0 }, + ATTRIBUTE_TUPLE_SIZE, + AttributeNumIndex, + (ScanFunc) AttributeNumIndexScan }, + { IndexRelationName, /* INDEXRELID */ + 1, + { Anum_pg_index_indexrelid, + 0, + 0, + 0 }, + offsetof(FormData_pg_index, indpred), + NULL, + NULL }, + { LanguageRelationName, /* LANNAME */ + 1, + { Anum_pg_language_lanname, + 0, + 0, + 0 }, + offsetof(FormData_pg_language, lancompiler), + NULL, + NULL }, + { OperatorRelationName, /* OPRNAME */ + 4, + { Anum_pg_operator_oprname, + Anum_pg_operator_oprleft, + Anum_pg_operator_oprright, + Anum_pg_operator_oprkind }, + sizeof(FormData_pg_operator), + NULL, + NULL }, + { OperatorRelationName, /* OPROID */ + 1, + { ObjectIdAttributeNumber, + 0, + 0, + 0 }, + sizeof(FormData_pg_operator), + NULL, + (ScanFunc) NULL }, + { ProcedureRelationName, /* PRONAME */ + 3, + { Anum_pg_proc_proname, + Anum_pg_proc_pronargs, + Anum_pg_proc_proargtypes, + 0 }, + offsetof(FormData_pg_proc, prosrc), + ProcedureNameIndex, + (ScanFunc) ProcedureNameIndexScan }, + { ProcedureRelationName, /* PROOID */ + 1, + { ObjectIdAttributeNumber, + 0, + 0, + 0 }, + offsetof(FormData_pg_proc, prosrc), + ProcedureOidIndex, + (ScanFunc) ProcedureOidIndexScan }, + { RelationRelationName, /* RELNAME */ + 1, + { Anum_pg_class_relname, + 0, + 0, + 0 }, + CLASS_TUPLE_SIZE, + ClassNameIndex, + (ScanFunc) ClassNameIndexScan }, + { RelationRelationName, /* RELOID */ + 1, + { ObjectIdAttributeNumber, + 0, + 0, + 0 }, + CLASS_TUPLE_SIZE, + ClassOidIndex, + (ScanFunc) ClassOidIndexScan }, + { TypeRelationName, /* TYPNAME */ + 1, + { Anum_pg_type_typname, + 0, + 0, + 0 }, + offsetof(TypeTupleFormData,typalign)+sizeof(char), + TypeNameIndex, + TypeNameIndexScan }, + { TypeRelationName, /* TYPOID */ + 1, + { ObjectIdAttributeNumber, + 0, + 0, + 0}, + offsetof(TypeTupleFormData,typalign)+sizeof(char), + TypeOidIndex, + TypeOidIndexScan }, + { AccessMethodRelationName, /* AMNAME */ + 1, + { Anum_pg_am_amname, + 0, + 0, + 0}, + sizeof(FormData_pg_am), + NULL, + NULL }, + { OperatorClassRelationName, /* CLANAME */ + 1, + { Anum_pg_opclass_opcname, + 0, + 0, + 0}, + sizeof(FormData_pg_opclass), + NULL, + NULL }, + { IndexRelationName, /* INDRELIDKEY */ + 2, + { Anum_pg_index_indrelid, + Anum_pg_index_indkey, + 0, + 0}, + offsetof(FormData_pg_index, indpred), + NULL, + (ScanFunc) NULL }, + { InheritsRelationName, /* INHRELID */ + 2, + { Anum_pg_inherits_inhrel, + Anum_pg_inherits_inhseqno, + 0, + 0}, + sizeof(FormData_pg_inherits), + NULL, + (ScanFunc) NULL }, + { RewriteRelationName, /* RULOID */ + 1, + { ObjectIdAttributeNumber, + 0, + 0, + 0 }, + offsetof(FormData_pg_rewrite, ev_qual), + NULL, + (ScanFunc) NULL }, + { AggregateRelationName, /*AGGNAME*/ + 2, + { Anum_pg_aggregate_aggname, + Anum_pg_aggregate_aggbasetype, + 0, + 0 }, + offsetof(FormData_pg_aggregate, agginitval1), + NULL, + (ScanFunc) NULL }, + { ListenerRelationName, /* LISTENREL */ + 2, + { Anum_pg_listener_relname, + Anum_pg_listener_pid, + 0, + 0 }, + sizeof(FormData_pg_listener), + NULL, + (ScanFunc) NULL }, + { UserRelationName, /* USENAME */ + 1, + { Anum_pg_user_usename, + 0, + 0, + 0 }, + sizeof(FormData_pg_user), + NULL, + (ScanFunc) NULL }, + { UserRelationName, /* USESYSID */ + 1, + { Anum_pg_user_usesysid, + 0, + 0, + 0 }, + sizeof(FormData_pg_user), + NULL, + (ScanFunc) NULL }, + { GroupRelationName, /* GRONAME */ + 1, + { Anum_pg_group_groname, + 0, + 0, + 0 }, + offsetof(FormData_pg_group, grolist[0]), + NULL, + (ScanFunc) NULL }, + { GroupRelationName, /* GROSYSID */ + 1, + { Anum_pg_group_grosysid, + 0, + 0, + 0 }, + offsetof(FormData_pg_group, grolist[0]), + NULL, + (ScanFunc) NULL }, + { RewriteRelationName, /* REWRITENAME */ + 1, + { Anum_pg_rewrite_rulename, + 0, + 0, + 0 }, + offsetof(FormData_pg_rewrite, ev_qual), + NULL, + (ScanFunc) NULL }, + { ProcedureRelationName, /* PROSRC */ + 1, + { Anum_pg_proc_prosrc, + 0, + 0, + 0 }, + offsetof(FormData_pg_proc, prosrc), + ProcedureSrcIndex, + (ScanFunc) ProcedureSrcIndexScan } +}; + +static struct catcache *SysCache[lengthof(cacheinfo)]; +static int32 SysCacheSize = lengthof(cacheinfo); + + +/* + * zerocaches-- + * + * Make sure the SysCache structure is zero'd. + */ +void +zerocaches() +{ + memset((char *) SysCache, 0, SysCacheSize * sizeof(struct catcache *)); +} + +/* + * Note: + * This function was written because the initialized catalog caches + * are used to determine which caches may contain tuples which need + * to be invalidated in other backends. + */ +void +InitCatalogCache() +{ + int cacheId; /* XXX type */ + + if (!AMI_OVERRIDE) { + for (cacheId = 0; cacheId < SysCacheSize; cacheId += 1) { + + Assert(!PointerIsValid((Pointer)SysCache[cacheId])); + + SysCache[cacheId] = + InitSysCache(cacheinfo[cacheId].name, + cacheinfo[cacheId].indname, + cacheId, + cacheinfo[cacheId].nkeys, + cacheinfo[cacheId].key, + cacheinfo[cacheId].iScanFunc); + if (!PointerIsValid((char *)SysCache[cacheId])) { + elog(WARN, + "InitCatalogCache: Can't init cache %.16s(%d)", + cacheinfo[cacheId].name, + cacheId); + } + + } + } +} + +/* + * SearchSysCacheTuple-- + * + * A layer on top of SearchSysCache that does the initialization and + * key-setting for you. + * + * Returns the tuple if one is found, NULL if not. + * + * XXX The tuple that is returned is NOT supposed to be pfree'd! + */ +HeapTuple +SearchSysCacheTuple(int cacheId, /* cache selection code */ + Datum key1, + Datum key2, + Datum key3, + Datum key4) +{ + register HeapTuple tp; + + if (cacheId < 0 || cacheId >= SysCacheSize) { + elog(WARN, "SearchSysCacheTuple: Bad cache id %d", cacheId); + return((HeapTuple) NULL); + } + + if (!AMI_OVERRIDE) { + Assert(PointerIsValid(SysCache[cacheId])); + } else { + if (!PointerIsValid(SysCache[cacheId])) { + SysCache[cacheId] = + InitSysCache(cacheinfo[cacheId].name, + cacheinfo[cacheId].indname, + cacheId, + cacheinfo[cacheId].nkeys, + cacheinfo[cacheId].key, + cacheinfo[cacheId].iScanFunc); + if (!PointerIsValid(SysCache[cacheId])) { + elog(WARN, + "InitCatalogCache: Can't init cache %.16s(%d)", + cacheinfo[cacheId].name, + cacheId); + } + + } + } + + tp = SearchSysCache(SysCache[cacheId], key1, key2, key3, key4); + if (!HeapTupleIsValid(tp)) { +#ifdef CACHEDEBUG + elog(DEBUG, + "SearchSysCacheTuple: Search %s(%d) %d %d %d %d failed", + (*cacheinfo[cacheId].name)->data, + cacheId, key1, key2, key3, key4); +#endif + return((HeapTuple) NULL); + } + return(tp); +} + +/* + * SearchSysCacheStruct-- + * Fills 's' with the information retrieved by calling SearchSysCache() + * with arguments key1...key4. Retrieves only the portion of the tuple + * which is not variable-length. + * + * NOTE: we are assuming that non-variable-length fields in the system + * catalogs will always be defined! + * + * Returns 1L if a tuple was found, 0L if not. + */ +int32 +SearchSysCacheStruct(int cacheId, /* cache selection code */ + char *returnStruct, /* (preallocated!) */ + Datum key1, + Datum key2, + Datum key3, + Datum key4) +{ + HeapTuple tp; + + if (!PointerIsValid(returnStruct)) { + elog(WARN, "SearchSysCacheStruct: No receiving struct"); + return(0); + } + tp = SearchSysCacheTuple(cacheId, key1, key2, key3, key4); + if (!HeapTupleIsValid(tp)) + return(0); + memmove(returnStruct, (char *) GETSTRUCT(tp), cacheinfo[cacheId].size); + return(1); +} + + +/* + * SearchSysCacheGetAttribute-- + * Returns the attribute corresponding to 'attributeNumber' for + * a given cached tuple. + * + * XXX This re-opens a relation, so this is slower. + * + * [callers all assume this returns a (struct varlena *). -ay 10/94] + */ +void * +SearchSysCacheGetAttribute(int cacheId, + AttrNumber attributeNumber, + Datum key1, + Datum key2, + Datum key3, + Datum key4) +{ + HeapTuple tp; + char *cacheName; + Relation relation; + int32 attributeLength, attributeByValue; + bool isNull; + char *attributeValue; + void *returnValue; + + tp = SearchSysCacheTuple(cacheId, key1, key2, key3, key4); + cacheName = cacheinfo[cacheId].name; + + if (!HeapTupleIsValid(tp)) { +#ifdef CACHEDEBUG + elog(DEBUG, + "SearchSysCacheGetAttribute: Lookup in %s(%d) failed", + cacheName, cacheId); +#endif /* defined(CACHEDEBUG) */ + return(NULL); + } + + relation = heap_openr(cacheName); + + if (attributeNumber < 0 && + attributeNumber > FirstLowInvalidHeapAttributeNumber) { + attributeLength = heap_sysattrlen(attributeNumber); + attributeByValue = heap_sysattrbyval(attributeNumber); + } else if (attributeNumber > 0 && + attributeNumber <= relation->rd_rel->relnatts) { + attributeLength = + relation->rd_att->attrs[attributeNumber-1]->attlen; + attributeByValue = + relation->rd_att->attrs[attributeNumber-1]->attbyval; + } else { + elog(WARN, + "SearchSysCacheGetAttribute: Bad attr # %d in %s(%d)", + attributeNumber, cacheName, cacheId); + return(NULL); + } + + attributeValue = heap_getattr(tp, + (Buffer) 0, + attributeNumber, + RelationGetTupleDescriptor(relation), + &isNull); + + if (isNull) { + /* + * Used to be an elog(DEBUG, ...) here and a claim that it should + * be a FATAL error, I don't think either is warranted -mer 6/9/92 + */ + return(NULL); + } + + if (attributeByValue) { + returnValue = (void *)attributeValue; + } else { + char *tmp; + int size = (attributeLength < 0) + ? VARSIZE((struct varlena *) attributeValue) /* variable length */ + : attributeLength; /* fixed length */ + + tmp = (char *) palloc(size); + memmove(tmp, attributeValue, size); + returnValue = (void *)tmp; + } + + heap_close(relation); + return(returnValue); +} + +/* + * TypeDefaultRetrieve-- + * + * Given a type OID, return the typdefault field associated with that + * type. The typdefault is returned as the car of a dotted pair which + * is passed to TypeDefaultRetrieve by the calling routine. + * + * Returns a fixnum for types which are passed by value and a ppreserve'd + * vectori for types which are not. + * + * [identical to get_typdefault, expecting a (struct varlena *) as ret val. + * some day, either of the functions should be removed -ay 10/94] + */ +void * +TypeDefaultRetrieve(Oid typId) +{ + HeapTuple typeTuple; + TypeTupleForm type; + int32 typByVal, typLen; + struct varlena *typDefault; + int32 dataSize; + void *returnValue; + + typeTuple = SearchSysCacheTuple(TYPOID, + ObjectIdGetDatum(typId), + 0,0,0); + + if (!HeapTupleIsValid(typeTuple)) { +#ifdef CACHEDEBUG + elog(DEBUG, "TypeDefaultRetrieve: Lookup in %s(%d) failed", + (*cacheinfo[TYPOID].name)->data, TYPOID); +#endif /* defined(CACHEDEBUG) */ + return(NULL); + } + + type = (TypeTupleForm) GETSTRUCT(typeTuple); + typByVal = type->typbyval; + typLen = type->typlen; + + typDefault = (struct varlena *) + SearchSysCacheGetAttribute(TYPOID, + Anum_pg_type_typdefault, + ObjectIdGetDatum(typId), + 0,0,0); + + if (typDefault == (struct varlena *)NULL) { +#ifdef CACHEDEBUG + elog(DEBUG, "TypeDefaultRetrieve: No extractable typdefault", + (*cacheinfo[TYPOID].name)->data, TYPOID); +#endif /* defined(CACHEDEBUG) */ + return (NULL); + + } + + dataSize = VARSIZE(typDefault) - VARHDRSZ; + + if (typByVal) { + int8 i8; + int16 i16; + int32 i32; + + if (dataSize == typLen) { + switch (typLen) { + case sizeof(int8): + memmove((char *) &i8, VARDATA(typDefault), sizeof(int8)); + i32 = i8; + break; + case sizeof(int16): + memmove((char *) &i16, VARDATA(typDefault), sizeof(int16)); + i32 = i16; + break; + case sizeof(int32): + memmove((char *) &i32, VARDATA(typDefault), sizeof(int32)); + break; + } + returnValue = (void *)i32; + } else { + returnValue = NULL; + } + } else { + if ((typLen < 0 && dataSize < 0) || dataSize != typLen) + returnValue = NULL; + else { + returnValue = (void *)palloc(VARSIZE(typDefault)); + memmove((char *) returnValue, + (char *) typDefault, + (int) VARSIZE(typDefault)); + } + } + + return(returnValue); +} + + |