diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index cd54ff766570ef0541caf5b55791a7a9a8ef76d7..4c1d908611182306fe3f601a0c845e2e0dc23ad0 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/cache/catcache.c,v 1.78 2001/06/01 02:41:36 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/cache/catcache.c,v 1.79 2001/06/18 03:35:07 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -29,17 +29,15 @@
 #include "utils/syscache.h"
 
 
-static void CatCacheRemoveCTup(CatCache *cache, CatCTup *ct);
-static Index CatalogCacheComputeHashIndex(CatCache *cache,
-							 ScanKey cur_skey);
-static Index CatalogCacheComputeTupleHashIndex(CatCache *cache,
-								  HeapTuple tuple);
-static void CatalogCacheInitializeCache(CatCache *cache);
-static Datum cc_hashname(PG_FUNCTION_ARGS);
+/* #define CACHEDEBUG */		/* turns DEBUG elogs on */
+
+/* voodoo constants */
+#define NCCBUCKETS 257			/* Hash buckets per CatCache (prime!) */
+#define MAXCCTUPLES 5000		/* Maximum # of tuples in all caches */
+
 
 /*
  *		variables, macros and other stuff
- *
  */
 
 #ifdef CACHEDEBUG
@@ -58,8 +56,8 @@ static Datum cc_hashname(PG_FUNCTION_ARGS);
 #define CACHE6_elog(a,b,c,d,e,f,g)
 #endif
 
-static CatCache *Caches = NULL; /* head of list of caches */
-
+/* Cache management header --- pointer is NULL until created */
+static CatCacheHeader *CacheHdr = NULL;
 
 /*
  *		EQPROC is used in CatalogCacheInitializeCache to find the equality
@@ -68,7 +66,6 @@ static CatCache *Caches = NULL; /* head of list of caches */
  *
  *		XXX this should be replaced by catalog lookups,
  *		but that seems to pose considerable risk of circularity...
- *
  */
 static const Oid eqproc[] = {
 	F_BOOLEQ, InvalidOid, F_CHAREQ, F_NAMEEQ, InvalidOid,
@@ -78,9 +75,18 @@ static const Oid eqproc[] = {
 
 #define EQPROC(SYSTEMTYPEOID)	eqproc[(SYSTEMTYPEOID)-BOOLOID]
 
+
+static void CatCacheRemoveCTup(CatCache *cache, CatCTup *ct);
+static Index CatalogCacheComputeHashIndex(CatCache *cache,
+							 ScanKey cur_skey);
+static Index CatalogCacheComputeTupleHashIndex(CatCache *cache,
+								  HeapTuple tuple);
+static void CatalogCacheInitializeCache(CatCache *cache);
+static Datum cc_hashname(PG_FUNCTION_ARGS);
+
+
 /*
  *					internal support functions
- *
  */
 
 static PGFunction
@@ -88,8 +94,8 @@ GetCCHashFunc(Oid keytype)
 {
 	switch (keytype)
 	{
-			case BOOLOID:
-			case CHAROID:
+		case BOOLOID:
+		case CHAROID:
 			return hashchar;
 		case NAMEOID:
 			return cc_hashname;
@@ -116,7 +122,6 @@ GetCCHashFunc(Oid keytype)
 static Datum
 cc_hashname(PG_FUNCTION_ARGS)
 {
-
 	/*
 	 * We need our own variant of hashname because we want to accept
 	 * null-terminated C strings as search values for name fields. So, we
@@ -141,7 +146,6 @@ cc_hashname(PG_FUNCTION_ARGS)
 void
 CreateCacheMemoryContext(void)
 {
-
 	/*
 	 * Purely for paranoia, check that context doesn't exist; caller
 	 * probably did so already.
@@ -161,7 +165,6 @@ CreateCacheMemoryContext(void)
  * This function does final initialization of a catcache: obtain the tuple
  * descriptor and set up the hash and equality function links.	We assume
  * that the relcache entry can be opened at this point!
- *
  */
 #ifdef CACHEDEBUG
 #define CatalogCacheInitializeCache_DEBUG1 \
@@ -191,7 +194,7 @@ CatalogCacheInitializeCache(CatCache *cache)
 	Relation	relation;
 	MemoryContext oldcxt;
 	TupleDesc	tupdesc;
-	short		i;
+	int			i;
 
 	CatalogCacheInitializeCache_DEBUG1;
 
@@ -206,8 +209,7 @@ CatalogCacheInitializeCache(CatCache *cache)
 	 * switch to the cache context so our allocations do not vanish at the
 	 * end of a transaction
 	 */
-	if (!CacheMemoryContext)
-		CreateCacheMemoryContext();
+	Assert(CacheMemoryContext != NULL);
 
 	oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
 
@@ -286,7 +288,6 @@ CatalogCacheInitializeCache(CatCache *cache)
 
 /*
  *		CatalogCacheComputeHashIndex
- *
  */
 static Index
 CatalogCacheComputeHashIndex(CatCache *cache, ScanKey cur_skey)
@@ -330,7 +331,6 @@ CatalogCacheComputeHashIndex(CatCache *cache, ScanKey cur_skey)
 
 /*
  *		CatalogCacheComputeTupleHashIndex
- *
  */
 static Index
 CatalogCacheComputeTupleHashIndex(CatCache *cache,
@@ -396,12 +396,12 @@ CatalogCacheComputeTupleHashIndex(CatCache *cache,
 
 /*
  *		CatCacheRemoveCTup
- *
  */
 static void
 CatCacheRemoveCTup(CatCache *cache, CatCTup *ct)
 {
 	Assert(ct->refcount == 0);
+	Assert(ct->my_cache == cache);
 
 	/* delink from linked lists */
 	DLRemove(&ct->lrulist_elem);
@@ -413,6 +413,7 @@ CatCacheRemoveCTup(CatCache *cache, CatCTup *ct)
 	pfree(ct);
 
 	--cache->cc_ntup;
+	--CacheHdr->ch_ntup;
 }
 
 /*
@@ -422,7 +423,6 @@ CatCacheRemoveCTup(CatCache *cache, CatCTup *ct)
  *	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,
@@ -433,17 +433,14 @@ CatalogCacheIdInvalidate(int cacheId,
 
 	/*
 	 * sanity checks
-	 *
 	 */
-	Assert(hashIndex < NCCBUCK);
 	Assert(ItemPointerIsValid(pointer));
 	CACHE1_elog(DEBUG, "CatalogCacheIdInvalidate: called");
 
 	/*
 	 * inspect caches to find the proper cache
-	 *
 	 */
-	for (ccp = Caches; ccp; ccp = ccp->cc_next)
+	for (ccp = CacheHdr->ch_caches; ccp; ccp = ccp->cc_next)
 	{
 		Dlelem	   *elt,
 				   *nextelt;
@@ -451,11 +448,12 @@ CatalogCacheIdInvalidate(int cacheId,
 		if (cacheId != ccp->id)
 			continue;
 
+		Assert(hashIndex < ccp->cc_size);
+
 		/*
 		 * inspect the hash bucket until we find a match or exhaust
-		 *
 		 */
-		for (elt = DLGetHead(&ccp->cc_cache[hashIndex]); elt; elt = nextelt)
+		for (elt = DLGetHead(&ccp->cc_bucket[hashIndex]); elt; elt = nextelt)
 		{
 			CatCTup    *ct = (CatCTup *) DLE_VAL(elt);
 
@@ -479,7 +477,7 @@ CatalogCacheIdInvalidate(int cacheId,
  *					   public functions
  *
  *		AtEOXact_CatCache
- *		ResetSystemCache
+ *		ResetCatalogCaches
  *		InitCatCache
  *		SearchCatCache
  *		ReleaseCatCache
@@ -497,68 +495,55 @@ CatalogCacheIdInvalidate(int cacheId,
  * necessary in the abort case, since elog() may have interrupted routines.
  * In the commit case, any nonzero counts indicate failure to call
  * ReleaseSysCache, so we put out a notice for debugging purposes.
- *
  */
 void
 AtEOXact_CatCache(bool isCommit)
 {
-	CatCache   *cache;
+	Dlelem	   *elt,
+			   *nextelt;
 
-	for (cache = Caches; cache; cache = cache->cc_next)
+	for (elt = DLGetHead(&CacheHdr->ch_lrulist); elt; elt = nextelt)
 	{
-		Dlelem	   *elt,
-				   *nextelt;
+		CatCTup    *ct = (CatCTup *) DLE_VAL(elt);
 
-		for (elt = DLGetHead(&cache->cc_lrulist); elt; elt = nextelt)
-		{
-			CatCTup    *ct = (CatCTup *) DLE_VAL(elt);
+		nextelt = DLGetSucc(elt);
 
-			nextelt = DLGetSucc(elt);
-
-			if (ct->refcount != 0)
-			{
-				if (isCommit)
-					elog(NOTICE, "Cache reference leak: cache %s (%d), tuple %u has count %d",
-						 cache->cc_relname, cache->id,
-						 ct->tuple.t_data->t_oid,
-						 ct->refcount);
-				ct->refcount = 0;
-			}
-
-			/* Clean up any now-deletable dead entries */
-			if (ct->dead)
-				CatCacheRemoveCTup(cache, ct);
+		if (ct->refcount != 0)
+		{
+			if (isCommit)
+				elog(NOTICE, "Cache reference leak: cache %s (%d), tuple %u has count %d",
+					 ct->my_cache->cc_relname, ct->my_cache->id,
+					 ct->tuple.t_data->t_oid,
+					 ct->refcount);
+			ct->refcount = 0;
 		}
+
+		/* Clean up any now-deletable dead entries */
+		if (ct->dead)
+			CatCacheRemoveCTup(ct->my_cache, ct);
 	}
 }
 
 /*
- *		ResetSystemCache
+ *		ResetCatalogCache
  *
- * Reset caches when a shared cache inval event forces it
+ * Reset one catalog cache to empty.
  *
+ * This is not very efficient if the target cache is nearly empty.
+ * However, it shouldn't need to be efficient; we don't invoke it often.
  */
-void
-ResetSystemCache(void)
+static void
+ResetCatalogCache(CatCache *cache)
 {
-	CatCache   *cache;
-
-	CACHE1_elog(DEBUG, "ResetSystemCache called");
+	int			i;
 
-	/* ----------------
-	 *	here we purge the contents of all the caches
-	 *
-	 *	for each system cache
-	 *		for each tuple
-	 *			remove the tuple, or at least mark it dead
-	 * ----------------
-	 */
-	for (cache = Caches; cache; cache = cache->cc_next)
+	/* Remove each tuple in this cache, or at least mark it dead */
+	for (i = 0; i < cache->cc_size; i++)
 	{
 		Dlelem	   *elt,
 				   *nextelt;
 
-		for (elt = DLGetHead(&cache->cc_lrulist); elt; elt = nextelt)
+		for (elt = DLGetHead(&cache->cc_bucket[i]); elt; elt = nextelt)
 		{
 			CatCTup    *ct = (CatCTup *) DLE_VAL(elt);
 
@@ -570,12 +555,28 @@ ResetSystemCache(void)
 				CatCacheRemoveCTup(cache, ct);
 		}
 	}
+}
 
-	CACHE1_elog(DEBUG, "end of ResetSystemCache call");
+/*
+ *		ResetCatalogCaches
+ *
+ * Reset all caches when a shared cache inval event forces it
+ */
+void
+ResetCatalogCaches(void)
+{
+	CatCache   *cache;
+
+	CACHE1_elog(DEBUG, "ResetCatalogCaches called");
+
+	for (cache = CacheHdr->ch_caches; cache; cache = cache->cc_next)
+		ResetCatalogCache(cache);
+
+	CACHE1_elog(DEBUG, "end of ResetCatalogCaches call");
 }
 
 /*
- *		SystemCacheRelationFlushed
+ *		CatalogCacheFlushRelation
  *
  *	This is called by RelationFlushRelation() to clear out cached information
  *	about a relation being dropped.  (This could be a DROP TABLE command,
@@ -586,26 +587,80 @@ ResetSystemCache(void)
  *	A special case occurs when relId is itself one of the cacheable system
  *	tables --- although those'll never be dropped, they can get flushed from
  *	the relcache (VACUUM causes this, for example).  In that case we need
- *	to flush all cache entries from that table.  The brute-force method
- *	currently used takes care of that quite handily.  (At one point we
+ *	to flush all cache entries that came from that table.  (At one point we
  *	also tried to force re-execution of CatalogCacheInitializeCache for
  *	the cache(s) on that table.  This is a bad idea since it leads to all
  *	kinds of trouble if a cache flush occurs while loading cache entries.
  *	We now avoid the need to do it by copying cc_tupdesc out of the relcache,
  *	rather than relying on the relcache to keep a tupdesc for us.  Of course
  *	this assumes the tupdesc of a cachable system table will not change...)
- *
  */
 void
-SystemCacheRelationFlushed(Oid relId)
+CatalogCacheFlushRelation(Oid relId)
 {
+	CatCache   *cache;
 
-	/*
-	 * XXX Ideally we'd search the caches and just zap entries that
-	 * actually refer to or come from the indicated relation.  For now, we
-	 * take the brute-force approach: just flush the caches entirely.
-	 */
-	ResetSystemCache();
+	CACHE2_elog(DEBUG, "CatalogCacheFlushRelation called for %u", relId);
+
+	for (cache = CacheHdr->ch_caches; cache; cache = cache->cc_next)
+	{
+		int			i;
+
+		/* We can ignore uninitialized caches, since they must be empty */
+		if (cache->cc_tupdesc == NULL)
+			continue;
+
+		/* Does this cache store tuples of the target relation itself? */
+		if (cache->cc_tupdesc->attrs[0]->attrelid == relId)
+		{
+			/* Yes, so flush all its contents */
+			ResetCatalogCache(cache);
+			continue;
+		}
+
+		/* Does this cache store tuples associated with relations at all? */
+		if (cache->cc_reloidattr == 0)
+			continue;			/* nope, leave it alone */
+
+		/* Yes, scan the tuples and remove those related to relId */
+		for (i = 0; i < cache->cc_size; i++)
+		{
+			Dlelem	   *elt,
+					   *nextelt;
+
+			for (elt = DLGetHead(&cache->cc_bucket[i]); elt; elt = nextelt)
+			{
+				CatCTup    *ct = (CatCTup *) DLE_VAL(elt);
+				Oid			tupRelid;
+
+				nextelt = DLGetSucc(elt);
+
+				if (cache->cc_reloidattr == ObjectIdAttributeNumber)
+					tupRelid = ct->tuple.t_data->t_oid;
+				else
+				{
+					bool	isNull;
+
+					tupRelid = DatumGetObjectId(
+						fastgetattr(&ct->tuple,
+									cache->cc_reloidattr,
+									cache->cc_tupdesc,
+									&isNull));
+					Assert(!isNull);
+				}
+
+				if (tupRelid == relId)
+				{
+					if (ct->refcount > 0)
+						ct->dead = true;
+					else
+						CatCacheRemoveCTup(cache, ct);
+				}
+			}
+		}
+	}
+
+	CACHE1_elog(DEBUG, "end of CatalogCacheFlushRelation call");
 }
 
 /*
@@ -615,7 +670,6 @@ SystemCacheRelationFlushed(Oid relId)
  *	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 InitCatCache_DEBUG1 \
@@ -628,10 +682,11 @@ do { \
 #define InitCatCache_DEBUG1
 #endif
 
-CatCache   *
+CatCache *
 InitCatCache(int id,
 			 char *relname,
 			 char *indname,
+			 int reloidattr,
 			 int nkeys,
 			 int *key)
 {
@@ -642,7 +697,6 @@ InitCatCache(int id,
 	/*
 	 * first switch to the cache context so our allocations do not vanish
 	 * at the end of a transaction
-	 *
 	 */
 	if (!CacheMemoryContext)
 		CreateCacheMemoryContext();
@@ -650,55 +704,60 @@ InitCatCache(int id,
 	oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
 
 	/*
-	 * allocate a new cache structure
-	 *
+	 * if first time through, initialize the cache group header,
+	 * including global LRU list header
 	 */
-	cp = (CatCache *) palloc(sizeof(CatCache));
-	MemSet((char *) cp, 0, sizeof(CatCache));
+	if (CacheHdr == NULL)
+	{
+		CacheHdr = (CatCacheHeader *) palloc(sizeof(CatCacheHeader));
+		CacheHdr->ch_caches = NULL;
+		CacheHdr->ch_ntup = 0;
+		CacheHdr->ch_maxtup = MAXCCTUPLES;
+		DLInitList(&CacheHdr->ch_lrulist);
+	}
 
 	/*
-	 * initialize the cache buckets (each bucket is a list header) and the
-	 * LRU tuple list
-	 *
+	 * allocate a new cache structure
 	 */
-	DLInitList(&cp->cc_lrulist);
-	for (i = 0; i < NCCBUCK; ++i)
-		DLInitList(&cp->cc_cache[i]);
+	cp = (CatCache *) palloc(sizeof(CatCache) + NCCBUCKETS * sizeof(Dllist));
+	MemSet((char *) cp, 0, sizeof(CatCache) + NCCBUCKETS * sizeof(Dllist));
 
 	/*
-	 * 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.
-	 *
+	 * initialize the cache buckets (each bucket is a list header)
 	 */
-	cp->cc_next = Caches;		/* list of caches (single link) */
-	Caches = cp;
+	for (i = 0; i < NCCBUCKETS; ++i)
+		DLInitList(&cp->cc_bucket[i]);
 
 	/*
 	 * initialize the cache's relation information for the relation
 	 * corresponding to this cache, and initialize some of the new cache's
 	 * other internal fields.  But don't open the relation yet.
-	 *
 	 */
+	cp->id = id;
 	cp->cc_relname = relname;
 	cp->cc_indname = indname;
+	cp->cc_reloidattr = reloidattr;
 	cp->cc_tupdesc = (TupleDesc) NULL;
-	cp->id = id;
-	cp->cc_maxtup = MAXTUP;
-	cp->cc_size = NCCBUCK;
+	cp->cc_ntup = 0;
+	cp->cc_size = NCCBUCKETS;
 	cp->cc_nkeys = nkeys;
 	for (i = 0; i < nkeys; ++i)
 		cp->cc_key[i] = key[i];
 
 	/*
-	 * all done.  new cache is initialized.  print some debugging
-	 * information, if appropriate.
-	 *
+	 * new cache is initialized as far as we can go for now.
+	 * print some debugging information, if appropriate.
 	 */
 	InitCatCache_DEBUG1;
 
+	/*
+	 * add completed cache to top of group header's list
+	 */
+	cp->cc_next = CacheHdr->ch_caches;
+	CacheHdr->ch_caches = cp;
+
 	/*
 	 * back to the old context before we return...
-	 *
 	 */
 	MemoryContextSwitchTo(oldcxt);
 
@@ -766,7 +825,6 @@ IndexScanOK(CatCache *cache, ScanKey cur_skey)
  *
  *		This call searches a system cache for a tuple, opening the relation
  *		if necessary (the first access to a particular cache).
- *
  */
 HeapTuple
 SearchCatCache(CatCache *cache,
@@ -785,14 +843,12 @@ SearchCatCache(CatCache *cache,
 
 	/*
 	 * one-time startup overhead
-	 *
 	 */
 	if (cache->cc_tupdesc == NULL)
 		CatalogCacheInitializeCache(cache);
 
 	/*
 	 * initialize the search key information
-	 *
 	 */
 	memcpy(cur_skey, cache->cc_skey, sizeof(cur_skey));
 	cur_skey[0].sk_argument = v1;
@@ -802,15 +858,13 @@ SearchCatCache(CatCache *cache,
 
 	/*
 	 * find the hash bucket in which to look for the tuple
-	 *
 	 */
 	hash = CatalogCacheComputeHashIndex(cache, cur_skey);
 
 	/*
 	 * scan the hash bucket until we find a match or exhaust our tuples
-	 *
 	 */
-	for (elt = DLGetHead(&cache->cc_cache[hash]);
+	for (elt = DLGetHead(&cache->cc_bucket[hash]);
 		 elt;
 		 elt = DLGetSucc(elt))
 	{
@@ -824,7 +878,6 @@ SearchCatCache(CatCache *cache,
 		/*
 		 * see if the cached tuple matches our key. (should we be worried
 		 * about time ranges? -cim 10/2/90)
-		 *
 		 */
 		HeapKeyTest(&ct->tuple,
 					cache->cc_tupdesc,
@@ -841,7 +894,6 @@ SearchCatCache(CatCache *cache,
 		 * subsequent searches.  (The most frequently accessed elements in
 		 * any hashbucket will tend to be near the front of the
 		 * hashbucket's list.)
-		 *
 		 */
 		ct->refcount++;
 
@@ -869,19 +921,16 @@ SearchCatCache(CatCache *cache,
 	 * will never be referenced again, and will eventually age out of the
 	 * cache, so there's no functional problem.  This case is rare enough
 	 * that it's not worth expending extra cycles to detect.
-	 *
 	 */
 
 	/*
 	 * open the relation associated with the cache
-	 *
 	 */
 	relation = heap_openr(cache->cc_relname, AccessShareLock);
 
 	/*
 	 * Scan the relation to find the tuple.  If there's an index, and if
 	 * it's safe to do so, use the index.  Else do a heap scan.
-	 *
 	 */
 	ct = NULL;
 
@@ -958,13 +1007,11 @@ SearchCatCache(CatCache *cache,
 
 	/*
 	 * close the relation
-	 *
 	 */
 	heap_close(relation, AccessShareLock);
 
 	/*
 	 * scan is complete.  if tup was found, we can add it to the cache.
-	 *
 	 */
 	if (ct == NULL)
 		return NULL;
@@ -972,27 +1019,27 @@ SearchCatCache(CatCache *cache,
 	/*
 	 * Finish initializing the CatCTup header, and add it to the linked
 	 * lists.
-	 *
 	 */
 	CACHE1_elog(DEBUG, "SearchCatCache: found tuple");
 
 	ct->ct_magic = CT_MAGIC;
+	ct->my_cache = cache;
 	DLInitElem(&ct->lrulist_elem, (void *) ct);
 	DLInitElem(&ct->cache_elem, (void *) ct);
 	ct->refcount = 1;			/* count this first reference */
 	ct->dead = false;
 
-	DLAddHead(&cache->cc_lrulist, &ct->lrulist_elem);
-	DLAddHead(&cache->cc_cache[hash], &ct->cache_elem);
+	DLAddHead(&CacheHdr->ch_lrulist, &ct->lrulist_elem);
+	DLAddHead(&cache->cc_bucket[hash], &ct->cache_elem);
 
 	/*
-	 * If we've exceeded the desired size of this cache, try to throw away
+	 * If we've exceeded the desired size of the caches, try to throw away
 	 * the least recently used entry.
-	 *
 	 */
-	if (++cache->cc_ntup > cache->cc_maxtup)
+	++cache->cc_ntup;
+	if (++CacheHdr->ch_ntup > CacheHdr->ch_maxtup)
 	{
-		for (elt = DLGetTail(&cache->cc_lrulist);
+		for (elt = DLGetTail(&CacheHdr->ch_lrulist);
 			 elt;
 			 elt = DLGetPred(elt))
 		{
@@ -1002,14 +1049,14 @@ SearchCatCache(CatCache *cache,
 			{
 				CACHE2_elog(DEBUG, "SearchCatCache(%s): Overflow, LRU removal",
 							cache->cc_relname);
-				CatCacheRemoveCTup(cache, oldct);
+				CatCacheRemoveCTup(oldct->my_cache, oldct);
 				break;
 			}
 		}
 	}
 
 	CACHE4_elog(DEBUG, "SearchCatCache(%s): Contains %d/%d tuples",
-				cache->cc_relname, cache->cc_ntup, cache->cc_maxtup);
+				cache->cc_relname, cache->cc_ntup, CacheHdr->ch_ntup);
 	CACHE3_elog(DEBUG, "SearchCatCache(%s): put in bucket %d",
 				cache->cc_relname, hash);
 
@@ -1026,7 +1073,6 @@ SearchCatCache(CatCache *cache,
  *	will be freed as soon as their refcount goes to zero.  In combination
  *	with aset.c's CLOBBER_FREED_MEMORY option, this provides a good test
  *	to catch references to already-released catcache entries.
- *
  */
 void
 ReleaseCatCache(HeapTuple tuple)
@@ -1046,12 +1092,7 @@ ReleaseCatCache(HeapTuple tuple)
 #endif
 		)
 	{
-		/* We can find the associated cache using the dllist pointers */
-		Dllist	   *lru = DLGetListHdr(&ct->lrulist_elem);
-		CatCache   *cache = (CatCache *) (((char *) lru) -
-										  offsetof(CatCache, cc_lrulist));
-
-		CatCacheRemoveCTup(cache, ct);
+		CatCacheRemoveCTup(ct->my_cache, ct);
 	}
 }
 
@@ -1061,7 +1102,7 @@ ReleaseCatCache(HeapTuple tuple)
  *	This is part of a rather subtle chain of events, so pay attention:
  *
  *	When a tuple is updated or deleted, it cannot be flushed from the
- *	catcaches immediately, for reasons explained at the top of inval.c.
+ *	catcaches immediately, for reasons explained at the top of cache/inval.c.
  *	Instead we have to add entry(s) for the tuple to a list of pending tuple
  *	invalidations that will be done at the end of the command or transaction.
  *
@@ -1081,7 +1122,6 @@ ReleaseCatCache(HeapTuple tuple)
  *	specified relation.  inval.c doesn't know exactly which rels have
  *	catcaches --- it will call this routine for any tuple that's in a
  *	system relation.
- *
  */
 void
 PrepareToInvalidateCacheTuple(Relation relation,
@@ -1090,14 +1130,15 @@ PrepareToInvalidateCacheTuple(Relation relation,
 {
 	CatCache   *ccp;
 
+	CACHE1_elog(DEBUG, "PrepareToInvalidateCacheTuple: called");
+
 	/*
 	 * sanity checks
-	 *
 	 */
 	Assert(RelationIsValid(relation));
 	Assert(HeapTupleIsValid(tuple));
 	Assert(PointerIsValid(function));
-	CACHE1_elog(DEBUG, "PrepareToInvalidateCacheTuple: called");
+	Assert(CacheHdr != NULL);
 
 	/* ----------------
 	 *	for each cache
@@ -1107,7 +1148,7 @@ PrepareToInvalidateCacheTuple(Relation relation,
 	 * ----------------
 	 */
 
-	for (ccp = Caches; ccp; ccp = ccp->cc_next)
+	for (ccp = CacheHdr->ch_caches; ccp; ccp = ccp->cc_next)
 	{
 		if (strcmp(ccp->cc_relname, RelationGetRelationName(relation)) != 0)
 			continue;
diff --git a/src/backend/utils/cache/inval.c b/src/backend/utils/cache/inval.c
index b15eac025eadde16cafe1489e4fdd0ec13ab1234..91884a4b9c129dcb166fbdb8fd7de9b0d1fde87f 100644
--- a/src/backend/utils/cache/inval.c
+++ b/src/backend/utils/cache/inval.c
@@ -34,7 +34,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/cache/inval.c,v 1.43 2001/06/01 20:23:06 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/cache/inval.c,v 1.44 2001/06/18 03:35:07 tgl Exp $
  *
  * Note - this code is real crufty... badly needs a rewrite to improve
  * readability and portability.  (Shouldn't assume Oid == Index, for example)
@@ -478,16 +478,20 @@ CacheIdInvalidate(Index cacheId,
 }
 
 /*
- *		ResetSystemCaches
+ *		InvalidateSystemCaches
  *
  *		This blows away all tuples in the system catalog caches and
  *		all the cached relation descriptors (and closes their files too).
  *		Relation descriptors that have positive refcounts are then rebuilt.
+ *
+ *		We call this when we see a shared-inval-queue overflow signal,
+ *		since that tells us we've lost some shared-inval messages and hence
+ *		don't know what needs to be invalidated.
  */
 static void
-ResetSystemCaches(void)
+InvalidateSystemCaches(void)
 {
-	ResetSystemCache();
+	ResetCatalogCaches();
 	RelationCacheInvalidate();
 }
 
@@ -643,7 +647,7 @@ DiscardInvalid(void)
 	elog(DEBUG, "DiscardInvalid called");
 #endif	 /* defined(INVALIDDEBUG) */
 
-	InvalidateSharedInvalid(CacheIdInvalidate, ResetSystemCaches);
+	InvalidateSharedInvalid(CacheIdInvalidate, InvalidateSystemCaches);
 }
 
 /*
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 4c8c3c4398cf218518178c1b3b732d98902829ad..d44c3dc2340e3eb9d5c67fb39f96a576392cf697 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/cache/relcache.c,v 1.137 2001/06/12 05:55:49 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/cache/relcache.c,v 1.138 2001/06/18 03:35:07 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1657,7 +1657,7 @@ RelationClearRelation(Relation relation, bool rebuildIt)
 	MemoryContextSwitchTo(oldcxt);
 
 	/* Clear out catcache's entries for this relation */
-	SystemCacheRelationFlushed(RelationGetRelid(relation));
+	CatalogCacheFlushRelation(RelationGetRelid(relation));
 
 	/*
 	 * Free all the subsidiary data structures of the relcache entry. We
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index 222650fcbea5b45098293202aaa6ff6c91cdd7f4..0e64aacf61ef463795b733b66aae7a97c828c140 100644
--- a/src/backend/utils/cache/syscache.c
+++ b/src/backend/utils/cache/syscache.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/cache/syscache.c,v 1.62 2001/06/12 05:55:49 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/cache/syscache.c,v 1.63 2001/06/18 03:35:07 tgl Exp $
  *
  * NOTES
  *	  These routines allow the parser/planner/executor to perform
@@ -54,7 +54,12 @@
 
 	Add your entry to the cacheinfo[] array below.	All cache lists are
 	alphabetical, so add it in the proper place.  Specify the relation
-	name, index name, number of keys, and key attribute numbers.
+	name, index name, number of keys, and key attribute numbers.  If the
+	relation contains tuples that are associated with a particular relation
+	(for example, its attributes, rules, triggers, etc) then specify the
+	attribute number that contains the OID of the associated relation.
+	This is used by CatalogCacheFlushRelation() to remove the correct
+	tuples during a table drop or relcache invalidation event.
 
 	In include/catalog/indexing.h, add a define for the number of indexes
 	on the relation, add define(s) for the index name(s), add an extern
@@ -76,12 +81,12 @@
 
 /*
  *		struct cachedesc: information defining a single syscache
- *
  */
 struct cachedesc
 {
 	char	   *name;			/* name of the relation being cached */
 	char	   *indname;		/* name of index relation for this cache */
+	int			reloidattr;		/* attr number of rel OID reference, or 0 */
 	int			nkeys;			/* # of keys needed for cache lookup */
 	int			key[4];			/* attribute numbers of key attrs */
 };
@@ -89,6 +94,7 @@ struct cachedesc
 static struct cachedesc cacheinfo[] = {
 	{AggregateRelationName,		/* AGGNAME */
 		AggregateNameTypeIndex,
+		0,
 		2,
 		{
 			Anum_pg_aggregate_aggname,
@@ -98,6 +104,7 @@ static struct cachedesc cacheinfo[] = {
 	}},
 	{AccessMethodRelationName,	/* AMNAME */
 		AmNameIndex,
+		0,
 		1,
 		{
 			Anum_pg_am_amname,
@@ -107,6 +114,7 @@ static struct cachedesc cacheinfo[] = {
 	}},
 	{AccessMethodOperatorRelationName,	/* AMOPOPID */
 		AccessMethodOpidIndex,
+		0,
 		3,
 		{
 			Anum_pg_amop_amopclaid,
@@ -116,6 +124,7 @@ static struct cachedesc cacheinfo[] = {
 	}},
 	{AccessMethodOperatorRelationName,	/* AMOPSTRATEGY */
 		AccessMethodStrategyIndex,
+		0,
 		3,
 		{
 			Anum_pg_amop_amopid,
@@ -125,6 +134,7 @@ static struct cachedesc cacheinfo[] = {
 	}},
 	{AttributeRelationName,		/* ATTNAME */
 		AttributeRelidNameIndex,
+		Anum_pg_attribute_attrelid,
 		2,
 		{
 			Anum_pg_attribute_attrelid,
@@ -134,6 +144,7 @@ static struct cachedesc cacheinfo[] = {
 	}},
 	{AttributeRelationName,		/* ATTNUM */
 		AttributeRelidNumIndex,
+		Anum_pg_attribute_attrelid,
 		2,
 		{
 			Anum_pg_attribute_attrelid,
@@ -143,6 +154,7 @@ static struct cachedesc cacheinfo[] = {
 	}},
 	{OperatorClassRelationName, /* CLADEFTYPE */
 		OpclassDeftypeIndex,
+		0,
 		1,
 		{
 			Anum_pg_opclass_opcdeftype,
@@ -152,6 +164,7 @@ static struct cachedesc cacheinfo[] = {
 	}},
 	{OperatorClassRelationName, /* CLANAME */
 		OpclassNameIndex,
+		0,
 		1,
 		{
 			Anum_pg_opclass_opcname,
@@ -161,6 +174,7 @@ static struct cachedesc cacheinfo[] = {
 	}},
 	{GroupRelationName,			/* GRONAME */
 		GroupNameIndex,
+		0,
 		1,
 		{
 			Anum_pg_group_groname,
@@ -170,6 +184,7 @@ static struct cachedesc cacheinfo[] = {
 	}},
 	{GroupRelationName,			/* GROSYSID */
 		GroupSysidIndex,
+		0,
 		1,
 		{
 			Anum_pg_group_grosysid,
@@ -179,6 +194,7 @@ static struct cachedesc cacheinfo[] = {
 	}},
 	{IndexRelationName,			/* INDEXRELID */
 		IndexRelidIndex,
+		Anum_pg_index_indrelid,
 		1,
 		{
 			Anum_pg_index_indexrelid,
@@ -188,6 +204,7 @@ static struct cachedesc cacheinfo[] = {
 	}},
 	{InheritsRelationName,		/* INHRELID */
 		InheritsRelidSeqnoIndex,
+		Anum_pg_inherits_inhrelid,
 		2,
 		{
 			Anum_pg_inherits_inhrelid,
@@ -197,6 +214,7 @@ static struct cachedesc cacheinfo[] = {
 	}},
 	{LanguageRelationName,		/* LANGNAME */
 		LanguageNameIndex,
+		0,
 		1,
 		{
 			Anum_pg_language_lanname,
@@ -206,6 +224,7 @@ static struct cachedesc cacheinfo[] = {
 	}},
 	{LanguageRelationName,		/* LANGOID */
 		LanguageOidIndex,
+		0,
 		1,
 		{
 			ObjectIdAttributeNumber,
@@ -215,6 +234,7 @@ static struct cachedesc cacheinfo[] = {
 	}},
 	{OperatorRelationName,		/* OPERNAME */
 		OperatorNameIndex,
+		0,
 		4,
 		{
 			Anum_pg_operator_oprname,
@@ -224,6 +244,7 @@ static struct cachedesc cacheinfo[] = {
 	}},
 	{OperatorRelationName,		/* OPEROID */
 		OperatorOidIndex,
+		0,
 		1,
 		{
 			ObjectIdAttributeNumber,
@@ -233,6 +254,7 @@ static struct cachedesc cacheinfo[] = {
 	}},
 	{ProcedureRelationName,		/* PROCNAME */
 		ProcedureNameIndex,
+		0,
 		3,
 		{
 			Anum_pg_proc_proname,
@@ -242,6 +264,7 @@ static struct cachedesc cacheinfo[] = {
 	}},
 	{ProcedureRelationName,		/* PROCOID */
 		ProcedureOidIndex,
+		0,
 		1,
 		{
 			ObjectIdAttributeNumber,
@@ -251,6 +274,7 @@ static struct cachedesc cacheinfo[] = {
 	}},
 	{RelationRelationName,		/* RELNAME */
 		ClassNameIndex,
+		ObjectIdAttributeNumber,
 		1,
 		{
 			Anum_pg_class_relname,
@@ -260,6 +284,7 @@ static struct cachedesc cacheinfo[] = {
 	}},
 	{RelationRelationName,		/* RELOID */
 		ClassOidIndex,
+		ObjectIdAttributeNumber,
 		1,
 		{
 			ObjectIdAttributeNumber,
@@ -269,6 +294,7 @@ static struct cachedesc cacheinfo[] = {
 	}},
 	{RewriteRelationName,		/* RULENAME */
 		RewriteRulenameIndex,
+		Anum_pg_rewrite_ev_class,
 		1,
 		{
 			Anum_pg_rewrite_rulename,
@@ -278,6 +304,7 @@ static struct cachedesc cacheinfo[] = {
 	}},
 	{ShadowRelationName,		/* SHADOWNAME */
 		ShadowNameIndex,
+		0,
 		1,
 		{
 			Anum_pg_shadow_usename,
@@ -287,6 +314,7 @@ static struct cachedesc cacheinfo[] = {
 	}},
 	{ShadowRelationName,		/* SHADOWSYSID */
 		ShadowSysidIndex,
+		0,
 		1,
 		{
 			Anum_pg_shadow_usesysid,
@@ -296,6 +324,7 @@ static struct cachedesc cacheinfo[] = {
 	}},
 	{StatisticRelationName,		/* STATRELATT */
 		StatisticRelidAttnumIndex,
+		Anum_pg_statistic_starelid,
 		2,
 		{
 			Anum_pg_statistic_starelid,
@@ -305,6 +334,7 @@ static struct cachedesc cacheinfo[] = {
 	}},
 	{TypeRelationName,			/* TYPENAME */
 		TypeNameIndex,
+		Anum_pg_type_typrelid,
 		1,
 		{
 			Anum_pg_type_typname,
@@ -314,6 +344,7 @@ static struct cachedesc cacheinfo[] = {
 	}},
 	{TypeRelationName,			/* TYPEOID */
 		TypeOidIndex,
+		Anum_pg_type_typrelid,
 		1,
 		{
 			ObjectIdAttributeNumber,
@@ -323,8 +354,7 @@ static struct cachedesc cacheinfo[] = {
 	}}
 };
 
-static CatCache *SysCache[
-						  lengthof(cacheinfo)];
+static CatCache *SysCache[lengthof(cacheinfo)];
 static int	SysCacheSize = lengthof(cacheinfo);
 static bool CacheInitialized = false;
 
@@ -358,6 +388,7 @@ InitCatalogCache(void)
 		SysCache[cacheId] = InitCatCache(cacheId,
 										 cacheinfo[cacheId].name,
 										 cacheinfo[cacheId].indname,
+										 cacheinfo[cacheId].reloidattr,
 										 cacheinfo[cacheId].nkeys,
 										 cacheinfo[cacheId].key);
 		if (!PointerIsValid(SysCache[cacheId]))
diff --git a/src/include/utils/catcache.h b/src/include/utils/catcache.h
index a2d99458a9d548f652d021d7b6cf574e94f0e854..a2d3064a8b9b6667a053c7056c8a498746a830c4 100644
--- a/src/include/utils/catcache.h
+++ b/src/include/utils/catcache.h
@@ -13,32 +13,49 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: catcache.h,v 1.32 2001/03/22 04:01:11 momjian Exp $
+ * $Id: catcache.h,v 1.33 2001/06/18 03:35:07 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #ifndef CATCACHE_H
 #define CATCACHE_H
 
- /* #define CACHEDEBUG *//* turns DEBUG elogs on */
-
 #include "access/htup.h"
 #include "lib/dllist.h"
 
 /*
  *		struct catctup:			individual tuple in the cache.
  *		struct catcache:		information for managing a cache.
+ *		struct catcacheheader:	information for managing all the caches.
  */
 
+typedef struct catcache
+{
+	int			id;				/* cache identifier --- see syscache.h */
+	struct catcache *cc_next;	/* link to next catcache */
+	char	   *cc_relname;		/* name of relation the tuples come from */
+	char	   *cc_indname;		/* name of index matching cache keys */
+	int			cc_reloidattr;	/* AttrNumber of relation OID, or 0 */
+	TupleDesc	cc_tupdesc;		/* tuple descriptor (copied from reldesc) */
+	int			cc_ntup;		/* # of tuples currently in this cache */
+	int			cc_size;		/* # of hash buckets in this cache */
+	int			cc_nkeys;		/* number of keys (1..4) */
+	int			cc_key[4];		/* AttrNumber of each key */
+	PGFunction	cc_hashfunc[4]; /* hash function to use for each key */
+	ScanKeyData cc_skey[4];		/* precomputed key info for heap scans */
+	Dllist		cc_bucket[1];	/* hash buckets --- VARIABLE LENGTH ARRAY */
+} CatCache;						/* VARIABLE LENGTH STRUCT */
+
+
 typedef struct catctup
 {
 	int			ct_magic;		/* for Assert checks */
 #define CT_MAGIC   0x57261502
-
+	CatCache   *my_cache;		/* link to owning catcache */
 	/*
 	 * Each tuple in a cache is a member of two lists: one lists all the
-	 * elements in that cache in LRU order, and the other lists just the
-	 * elements in one hashbucket, also in LRU order.
+	 * elements in all the caches in LRU order, and the other lists just
+	 * the elements in one hashbucket of one cache, also in LRU order.
 	 *
 	 * A tuple marked "dead" must not be returned by subsequent searches.
 	 * However, it won't be physically deleted from the cache until its
@@ -52,30 +69,14 @@ typedef struct catctup
 } CatCTup;
 
 
-/* voodoo constants */
-#define NCCBUCK 500				/* CatCache buckets */
-#define MAXTUP 500				/* Maximum # of tuples stored per cache */
-
-
-typedef struct catcache
+typedef struct catcacheheader
 {
-	int			id;				/* cache identifier --- see syscache.h */
-	struct catcache *cc_next;	/* link to next catcache */
-	char	   *cc_relname;		/* name of relation the tuples come from */
-	char	   *cc_indname;		/* name of index matching cache keys */
-	TupleDesc	cc_tupdesc;		/* tuple descriptor (copied from reldesc) */
-	short		cc_ntup;		/* # of tuples in this cache */
-	short		cc_maxtup;		/* max # of tuples allowed (LRU) */
-	short		cc_size;		/* # of hash buckets in this cache */
-	short		cc_nkeys;		/* number of keys (1..4) */
-	short		cc_key[4];		/* AttrNumber of each key */
-	PGFunction	cc_hashfunc[4]; /* hash function to use for each key */
-	ScanKeyData cc_skey[4];		/* precomputed key info for heap scans */
-	Dllist		cc_lrulist;		/* overall LRU list, most recent first */
-	Dllist		cc_cache[NCCBUCK];		/* hash buckets */
-} CatCache;
+	CatCache   *ch_caches;		/* head of list of CatCache structs */
+	int			ch_ntup;		/* # of tuples in all caches */
+	int			ch_maxtup;		/* max # of tuples allowed (LRU) */
+	Dllist		ch_lrulist;		/* overall LRU list, most recent first */
+} CatCacheHeader;
 
-#define InvalidCatalogCacheId	(-1)
 
 /* this extern duplicates utils/memutils.h... */
 extern MemoryContext CacheMemoryContext;
@@ -84,15 +85,16 @@ extern void CreateCacheMemoryContext(void);
 extern void AtEOXact_CatCache(bool isCommit);
 
 extern CatCache *InitCatCache(int id, char *relname, char *indname,
-			 int nkeys, int *key);
+							  int reloidattr,
+							  int nkeys, int *key);
 
 extern HeapTuple SearchCatCache(CatCache *cache,
 			   Datum v1, Datum v2,
 			   Datum v3, Datum v4);
 extern void ReleaseCatCache(HeapTuple tuple);
 
-extern void ResetSystemCache(void);
-extern void SystemCacheRelationFlushed(Oid relId);
+extern void ResetCatalogCaches(void);
+extern void CatalogCacheFlushRelation(Oid relId);
 extern void CatalogCacheIdInvalidate(int cacheId, Index hashIndex,
 						 ItemPointer pointer);
 extern void PrepareToInvalidateCacheTuple(Relation relation,