aboutsummaryrefslogtreecommitdiff
path: root/src/backend/utils/cache
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/utils/cache')
-rw-r--r--src/backend/utils/cache/Makefile.inc15
-rw-r--r--src/backend/utils/cache/catcache.c1023
-rw-r--r--src/backend/utils/cache/fcache.c297
-rw-r--r--src/backend/utils/cache/inval.c612
-rw-r--r--src/backend/utils/cache/lsyscache.c484
-rw-r--r--src/backend/utils/cache/rel.c77
-rw-r--r--src/backend/utils/cache/relcache.c1795
-rw-r--r--src/backend/utils/cache/syscache.c630
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);
+}
+
+