diff --git a/src/backend/access/common/tupdesc.c b/src/backend/access/common/tupdesc.c
index 567fcab1cd8e7c128714972c75036293bb6e8343..b2af4ff932ecac4ba53f1dd9e78e9b8b04e5bd1e 100644
--- a/src/backend/access/common/tupdesc.c
+++ b/src/backend/access/common/tupdesc.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/access/common/tupdesc.c,v 1.60 2000/01/26 05:55:53 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/access/common/tupdesc.c,v 1.61 2000/01/31 04:35:48 tgl Exp $
  *
  * NOTES
  *	  some of the executor utility code such as "ExecTypeFromTL" should be
@@ -226,6 +226,71 @@ FreeTupleDesc(TupleDesc tupdesc)
 
 }
 
+bool
+equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2)
+{
+	int		i;
+
+	if (tupdesc1->natts != tupdesc2->natts)
+		return false;
+	for (i = 0; i < tupdesc1->natts; i++)
+	{
+		Form_pg_attribute	attr1 = tupdesc1->attrs[i];
+		Form_pg_attribute	attr2 = tupdesc2->attrs[i];
+
+		/* We do not need to check every single field here, and in fact
+		 * some fields such as attdisbursion probably shouldn't be compared.
+		 */
+		if (strcmp(NameStr(attr1->attname), NameStr(attr2->attname)) != 0)
+			return false;
+		if (attr1->atttypid != attr2->atttypid)
+			return false;
+		if (attr1->atttypmod != attr2->atttypmod)
+			return false;
+		if (attr1->attstorage != attr2->attstorage)
+			return false;
+		if (attr1->attnotnull != attr2->attnotnull)
+			return false;
+	}
+	if (tupdesc1->constr != NULL)
+	{
+		TupleConstr	   *constr1 = tupdesc1->constr;
+		TupleConstr	   *constr2 = tupdesc2->constr;
+
+		if (constr2 == NULL)
+			return false;
+		if (constr1->num_defval != constr2->num_defval)
+			return false;
+		for (i = 0; i < (int) constr1->num_defval; i++)
+		{
+			AttrDefault	   *defval1 = constr1->defval + i;
+			AttrDefault	   *defval2 = constr2->defval + i;
+
+			if (defval1->adnum != defval2->adnum)
+				return false;
+			if (strcmp(defval1->adbin, defval2->adbin) != 0)
+				return false;
+		}
+		if (constr1->num_check != constr2->num_check)
+			return false;
+		for (i = 0; i < (int) constr1->num_check; i++)
+		{
+			ConstrCheck	   *check1 = constr1->check + i;
+			ConstrCheck	   *check2 = constr2->check + i;
+
+			if (strcmp(check1->ccname, check2->ccname) != 0)
+				return false;
+			if (strcmp(check1->ccbin, check2->ccbin) != 0)
+				return false;
+		}
+		if (constr1->has_not_null != constr2->has_not_null)
+			return false;
+	}
+	else if (tupdesc2->constr != NULL)
+		return false;
+	return true;
+}
+
 /* ----------------------------------------------------------------
  *		TupleDescInitEntry
  *
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 79957e7e8c2eeb15227c304940cae208dd382457..89c20ce5fa70ec2023087dceeba0f4ff39bce5ef 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.119 2000/01/26 05:56:10 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.120 2000/01/31 04:35:48 tgl Exp $
  *
  *
  * INTERFACE ROUTINES
@@ -2098,18 +2098,6 @@ AddRelationRawConstraints(Relation rel,
 
 	heap_close(relrel, RowExclusiveLock);
 	heap_freetuple(reltup);
-
-	/*
-	 * Force rebuild of our own relcache entry, otherwise subsequent commands
-	 * in this transaction won't see the new defaults/constraints.
-	 * Must bump command counter or relcache rebuild won't see 'em either.
-	 *
-	 * (This might seem unnecessary, since we are sending out an SI message;
-	 * but if the relation has just been created then relcache.c will ignore
-	 * the SI message on the grounds that the rel is transaction-local...)
-	 */
-	CommandCounterIncrement();
-	RelationRebuildRelation(rel);
 }
 
 static void
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 86bacf1be5c14b4ee7f44a4645851445c4e3554b..f3c919a1ed9ac506564af98a30d7ce0133571878 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -3,11 +3,16 @@
  * trigger.c
  *	  PostgreSQL TRIGGERs support code.
  *
+ * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/trigger.c,v 1.56 2000/01/31 04:35:49 tgl Exp $
+ *
  *-------------------------------------------------------------------------
  */
 #include "postgres.h"
 
-
 #include "access/genam.h"
 #include "access/heapam.h"
 #include "catalog/catalog.h"
@@ -28,14 +33,13 @@
 
 DLLIMPORT TriggerData *CurrentTriggerData = NULL;
 
-void		RelationBuildTriggers(Relation relation);
-void		FreeTriggerDesc(Relation relation);
+/* XXX no points for style */
+extern TupleTableSlot *EvalPlanQual(EState *estate, Index rti, ItemPointer tid);
 
 static void DescribeTrigger(TriggerDesc *trigdesc, Trigger *trigger);
 static HeapTuple GetTupleForTrigger(EState *estate, ItemPointer tid,
 				   TupleTableSlot **newSlot);
 
-extern GlobalMemory CacheCxt;
 
 void
 CreateTrigger(CreateTrigStmt *stmt)
@@ -52,7 +56,6 @@ CreateTrigger(CreateTrigStmt *stmt)
 	HeapTuple	tuple;
 	Relation	idescs[Num_pg_trigger_indices];
 	Relation	ridescs[Num_pg_class_indices];
-	MemoryContext oldcxt;
 	Oid			fargtypes[FUNC_MAX_ARGS];
 	int			found = 0;
 	int			i;
@@ -258,13 +261,11 @@ CreateTrigger(CreateTrigStmt *stmt)
 	CatalogCloseIndices(Num_pg_class_indices, ridescs);
 	heap_freetuple(tuple);
 	heap_close(pgrel, RowExclusiveLock);
-
-	CommandCounterIncrement();
-	oldcxt = MemoryContextSwitchTo((MemoryContext) CacheCxt);
-	FreeTriggerDesc(rel);
-	rel->rd_rel->reltriggers = found + 1;
-	RelationBuildTriggers(rel);
-	MemoryContextSwitchTo(oldcxt);
+	/*
+	 * We used to try to update the rel's relcache entry here, but that's
+	 * fairly pointless since it will happen as a byproduct of the upcoming
+	 * CommandCounterIncrement...
+	 */
 	/* Keep lock on target rel until end of xact */
 	heap_close(rel, NoLock);
 }
@@ -279,7 +280,6 @@ DropTrigger(DropTrigStmt *stmt)
 	Relation	pgrel;
 	HeapTuple	tuple;
 	Relation	ridescs[Num_pg_class_indices];
-	MemoryContext oldcxt;
 	int			found = 0;
 	int			tgfound = 0;
 
@@ -337,14 +337,11 @@ DropTrigger(DropTrigStmt *stmt)
 	CatalogCloseIndices(Num_pg_class_indices, ridescs);
 	heap_freetuple(tuple);
 	heap_close(pgrel, RowExclusiveLock);
-
-	CommandCounterIncrement();
-	oldcxt = MemoryContextSwitchTo((MemoryContext) CacheCxt);
-	FreeTriggerDesc(rel);
-	rel->rd_rel->reltriggers = found;
-	if (found > 0)
-		RelationBuildTriggers(rel);
-	MemoryContextSwitchTo(oldcxt);
+	/*
+	 * We used to try to update the rel's relcache entry here, but that's
+	 * fairly pointless since it will happen as a byproduct of the upcoming
+	 * CommandCounterIncrement...
+	 */
 	/* Keep lock on target rel until end of xact */
 	heap_close(rel, NoLock);
 }
@@ -356,7 +353,6 @@ RelationRemoveTriggers(Relation rel)
 	HeapScanDesc tgscan;
 	ScanKeyData key;
 	HeapTuple	tup;
-	Form_pg_trigger	pg_trigger;
 
 	tgrel = heap_openr(TriggerRelationName, RowExclusiveLock);
 	ScanKeyEntryInitialize(&key, 0, Anum_pg_trigger_tgrelid,
@@ -387,6 +383,7 @@ RelationRemoveTriggers(Relation rel)
 	tgscan = heap_beginscan(tgrel, 0, SnapshotNow, 1, &key);
 	while (HeapTupleIsValid(tup = heap_getnext(tgscan, 0)))
 	{
+		Form_pg_trigger	pg_trigger;
 		Relation		refrel;
 		DropTrigStmt	stmt;
 
@@ -396,14 +393,14 @@ RelationRemoveTriggers(Relation rel)
 		stmt.relname = pstrdup(RelationGetRelationName(refrel));
 		stmt.trigname = nameout(&pg_trigger->tgname);
 
+		heap_close(refrel, NoLock);
+
 		elog(NOTICE, "DROP TABLE implicitly drops referential integrity trigger from table \"%s\"", stmt.relname);
 
 		DropTrigger(&stmt);
 
 		pfree(stmt.relname);
 		pfree(stmt.trigname);
-
-		heap_close(refrel, NoLock);
 	}
 	heap_endscan(tgscan);
 
@@ -453,8 +450,8 @@ RelationBuildTriggers(Relation relation)
 		if (!tuple.t_data)
 			continue;
 		if (found == ntrigs)
-			elog(ERROR, "RelationBuildTriggers: unexpected record found for rel %.*s",
-				 NAMEDATALEN, RelationGetRelationName(relation));
+			elog(ERROR, "RelationBuildTriggers: unexpected record found for rel %s",
+				 RelationGetRelationName(relation));
 
 		pg_trigger = (Form_pg_trigger) GETSTRUCT(&tuple);
 
@@ -479,8 +476,8 @@ RelationBuildTriggers(Relation relation)
 											 Anum_pg_trigger_tgargs,
 											 tgrel->rd_att, &isnull);
 		if (isnull)
-			elog(ERROR, "RelationBuildTriggers: tgargs IS NULL for rel %.*s",
-				 NAMEDATALEN, RelationGetRelationName(relation));
+			elog(ERROR, "RelationBuildTriggers: tgargs IS NULL for rel %s",
+				 RelationGetRelationName(relation));
 		if (build->tgnargs > 0)
 		{
 			char	   *p;
@@ -490,14 +487,13 @@ RelationBuildTriggers(Relation relation)
 												 Anum_pg_trigger_tgargs,
 												 tgrel->rd_att, &isnull);
 			if (isnull)
-				elog(ERROR, "RelationBuildTriggers: tgargs IS NULL for rel %.*s",
-					 NAMEDATALEN, RelationGetRelationName(relation));
+				elog(ERROR, "RelationBuildTriggers: tgargs IS NULL for rel %s",
+					 RelationGetRelationName(relation));
 			p = (char *) VARDATA(val);
 			build->tgargs = (char **) palloc(build->tgnargs * sizeof(char *));
 			for (i = 0; i < build->tgnargs; i++)
 			{
-				build->tgargs[i] = (char *) palloc(strlen(p) + 1);
-				strcpy(build->tgargs[i], p);
+				build->tgargs[i] = pstrdup(p);
 				p += strlen(p) + 1;
 			}
 		}
@@ -509,9 +505,9 @@ RelationBuildTriggers(Relation relation)
 	}
 
 	if (found < ntrigs)
-		elog(ERROR, "RelationBuildTriggers: %d record not found for rel %.*s",
+		elog(ERROR, "RelationBuildTriggers: %d record(s) not found for rel %s",
 			 ntrigs - found,
-			 NAMEDATALEN, RelationGetRelationName(relation));
+			 RelationGetRelationName(relation));
 
 	index_endscan(sd);
 	index_close(irel);
@@ -519,60 +515,11 @@ RelationBuildTriggers(Relation relation)
 
 	/* Build trigdesc */
 	trigdesc->triggers = triggers;
+	trigdesc->numtriggers = ntrigs;
 	for (found = 0; found < ntrigs; found++)
-	{
-		build = &(triggers[found]);
-		DescribeTrigger(trigdesc, build);
-	}
+		DescribeTrigger(trigdesc, &(triggers[found]));
 
 	relation->trigdesc = trigdesc;
-
-}
-
-void
-FreeTriggerDesc(Relation relation)
-{
-	TriggerDesc *trigdesc = relation->trigdesc;
-	Trigger  ***t;
-	Trigger    *trigger;
-	int			i;
-
-	if (trigdesc == NULL)
-		return;
-
-	t = trigdesc->tg_before_statement;
-	for (i = 0; i < 3; i++)
-		if (t[i] != NULL)
-			pfree(t[i]);
-	t = trigdesc->tg_before_row;
-	for (i = 0; i < 3; i++)
-		if (t[i] != NULL)
-			pfree(t[i]);
-	t = trigdesc->tg_after_row;
-	for (i = 0; i < 3; i++)
-		if (t[i] != NULL)
-			pfree(t[i]);
-	t = trigdesc->tg_after_statement;
-	for (i = 0; i < 3; i++)
-		if (t[i] != NULL)
-			pfree(t[i]);
-
-	trigger = trigdesc->triggers;
-	for (i = 0; i < relation->rd_rel->reltriggers; i++)
-	{
-		pfree(trigger->tgname);
-		if (trigger->tgnargs > 0)
-		{
-			while (--(trigger->tgnargs) >= 0)
-				pfree(trigger->tgargs[trigger->tgnargs]);
-			pfree(trigger->tgargs);
-		}
-		trigger++;
-	}
-	pfree(trigdesc->triggers);
-	pfree(trigdesc);
-	relation->trigdesc = NULL;
-	return;
 }
 
 static void
@@ -649,6 +596,119 @@ DescribeTrigger(TriggerDesc *trigdesc, Trigger *trigger)
 
 }
 
+void
+FreeTriggerDesc(TriggerDesc *trigdesc)
+{
+	Trigger  ***t;
+	Trigger    *trigger;
+	int			i;
+
+	if (trigdesc == NULL)
+		return;
+
+	t = trigdesc->tg_before_statement;
+	for (i = 0; i < 4; i++)
+		if (t[i] != NULL)
+			pfree(t[i]);
+	t = trigdesc->tg_before_row;
+	for (i = 0; i < 4; i++)
+		if (t[i] != NULL)
+			pfree(t[i]);
+	t = trigdesc->tg_after_row;
+	for (i = 0; i < 4; i++)
+		if (t[i] != NULL)
+			pfree(t[i]);
+	t = trigdesc->tg_after_statement;
+	for (i = 0; i < 4; i++)
+		if (t[i] != NULL)
+			pfree(t[i]);
+
+	trigger = trigdesc->triggers;
+	for (i = 0; i < trigdesc->numtriggers; i++)
+	{
+		pfree(trigger->tgname);
+		if (trigger->tgnargs > 0)
+		{
+			while (--(trigger->tgnargs) >= 0)
+				pfree(trigger->tgargs[trigger->tgnargs]);
+			pfree(trigger->tgargs);
+		}
+		trigger++;
+	}
+	pfree(trigdesc->triggers);
+	pfree(trigdesc);
+}
+
+bool
+equalTriggerDescs(TriggerDesc *trigdesc1, TriggerDesc *trigdesc2)
+{
+	int		i,
+			j;
+
+	/*
+	 * We need not examine the "index" data, just the trigger array itself;
+	 * if we have the same triggers with the same types, the derived index
+	 * data should match.
+	 *
+	 * XXX It seems possible that the same triggers could appear in different
+	 * orders in the two trigger arrays; do we need to handle that?
+	 */
+	if (trigdesc1 != NULL)
+	{
+		if (trigdesc2 == NULL)
+			return false;
+		if (trigdesc1->numtriggers != trigdesc2->numtriggers)
+			return false;
+		for (i = 0; i < trigdesc1->numtriggers; i++)
+		{
+			Trigger	   *trig1 = trigdesc1->triggers + i;
+			Trigger	   *trig2 = NULL;
+
+			/*
+			 * We can't assume that the triggers are always read from
+			 * pg_trigger in the same order; so use the trigger OIDs to
+			 * identify the triggers to compare.  (We assume here that the
+			 * same OID won't appear twice in either trigger set.)
+			 */
+			for (j = 0; j < trigdesc2->numtriggers; j++)
+			{
+				trig2 = trigdesc2->triggers + i;
+				if (trig1->tgoid == trig2->tgoid)
+					break;
+			}
+			if (j >= trigdesc2->numtriggers)
+				return false;
+			if (strcmp(trig1->tgname, trig2->tgname) != 0)
+				return false;
+			if (trig1->tgfoid != trig2->tgfoid)
+				return false;
+			/* need not examine tgfunc, if tgfoid matches */
+			if (trig1->tgtype != trig2->tgtype)
+				return false;
+			if (trig1->tgenabled != trig2->tgenabled)
+				return false;
+			if (trig1->tgisconstraint != trig2->tgisconstraint)
+				return false;
+			if (trig1->tgdeferrable != trig2->tgdeferrable)
+				return false;
+			if (trig1->tginitdeferred != trig2->tginitdeferred)
+				return false;
+			if (trig1->tgnargs != trig2->tgnargs)
+				return false;
+			if (memcmp(trig1->tgattr, trig2->tgattr,
+					   sizeof(trig1->tgattr)) != 0)
+				return false;
+			for (j = 0; j < trig1->tgnargs; j++)
+				if (strcmp(trig1->tgargs[j], trig2->tgargs[j]) != 0)
+					return false;
+		}
+	}
+	else if (trigdesc2 != NULL)
+		return false;
+	return true;
+}
+
+
 static HeapTuple
 ExecCallTriggerFunc(Trigger *trigger)
 {
@@ -811,7 +871,6 @@ ExecARUpdateTriggers(EState *estate, ItemPointer tupleid, HeapTuple newtuple)
 	return;
 }
 
-extern TupleTableSlot *EvalPlanQual(EState *estate, Index rti, ItemPointer tid);
 
 static HeapTuple
 GetTupleForTrigger(EState *estate, ItemPointer tid, TupleTableSlot **newSlot)
diff --git a/src/backend/lib/hasht.c b/src/backend/lib/hasht.c
index ad3aa660e27445bc0be9c191aa436d8f2ed4b3ef..5caeabd281ab3530dea037a2827f9622677be8d2 100644
--- a/src/backend/lib/hasht.c
+++ b/src/backend/lib/hasht.c
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/lib/Attic/hasht.c,v 1.12 2000/01/26 05:56:26 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/lib/Attic/hasht.c,v 1.13 2000/01/31 04:35:51 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -22,14 +22,14 @@
  *		HashTableWalk
  *
  *		call function on every element in hashtable
- *		one extra argument, arg may be supplied
+ *		one extra argument (arg) may be supplied
  * -----------------------------------
  */
 void
 HashTableWalk(HTAB *hashtable, HashtFunc function, int arg)
 {
 	long	   *hashent;
-	long	   *data;
+	void	   *data;
 	int			keysize;
 
 	keysize = hashtable->hctl->keysize;
@@ -43,7 +43,7 @@ HashTableWalk(HTAB *hashtable, HashtFunc function, int arg)
 		 * XXX the corresponding hash table insertion does NOT LONGALIGN
 		 * -- make sure the keysize is ok
 		 */
-		data = (long *) LONGALIGN((char *) hashent + keysize);
+		data = (void *) LONGALIGN((char *) hashent + keysize);
 		(*function) (data, arg);
 	}
 }
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index e5e22a7ab31ad857f2d1f5cdd187c738c6792fdd..f96a3956053159c4aad44a15de66b27718aea066 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.58 2000/01/26 05:57:17 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/cache/catcache.c,v 1.59 2000/01/31 04:35:51 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -35,9 +35,6 @@ static long comphash(long l, char *v);
 
 /* ----------------
  *		variables, macros and other stuff
- *
- *	note CCSIZE allocates 51 buckets .. one was already allocated in
- *	the catcache structure.
  * ----------------
  */
 
@@ -64,17 +61,20 @@ GlobalMemory CacheCxt;			/* context in which caches are allocated */
 
 
 /* ----------------
- *		EQPROC is used in CatalogCacheInitializeCache
- *		XXX this should be replaced by catalog lookups soon
+ *		EQPROC is used in CatalogCacheInitializeCache to find the equality
+ *		functions for system types that are used as cache key fields.
+ *
+ *		XXX this should be replaced by catalog lookups,
+ *		but that seems to pose considerable risk of circularity...
  * ----------------
  */
-static long eqproc[] = {
-	F_BOOLEQ, 0l, F_CHAREQ, F_NAMEEQ, 0l,
-	F_INT2EQ, F_KEYFIRSTEQ, F_INT4EQ, 0l, F_TEXTEQ,
-	F_OIDEQ, 0l, 0l, 0l, F_OIDVECTOREQ
+static const Oid eqproc[] = {
+	F_BOOLEQ, InvalidOid, F_CHAREQ, F_NAMEEQ, InvalidOid,
+	F_INT2EQ, F_KEYFIRSTEQ, F_INT4EQ, F_OIDEQ, F_TEXTEQ,
+	F_OIDEQ, InvalidOid, InvalidOid, InvalidOid, F_OIDVECTOREQ
 };
 
-#define EQPROC(SYSTEMTYPEOID)	eqproc[(SYSTEMTYPEOID)-16]
+#define EQPROC(SYSTEMTYPEOID)	eqproc[(SYSTEMTYPEOID)-BOOLOID]
 
 /* ----------------------------------------------------------------
  *					internal support functions
@@ -169,12 +169,13 @@ CatalogCacheInitializeCache(struct catcache * cache,
 	}
 
 	/* ----------------
-	 *	initialize the cache's relation id
+	 *	initialize the cache's relation id and tuple descriptor
 	 * ----------------
 	 */
 	Assert(RelationIsValid(relation));
 	cache->relationId = RelationGetRelid(relation);
-	tupdesc = cache->cc_tupdesc = RelationGetDescr(relation);
+	tupdesc = CreateTupleDescCopyConstr(RelationGetDescr(relation));
+	cache->cc_tupdesc = tupdesc;
 
 	CACHE3_elog(DEBUG, "CatalogCacheInitializeCache: relid %u, %d keys",
 				cache->relationId, cache->cc_nkeys);
@@ -254,22 +255,6 @@ CatalogCacheInitializeCache(struct catcache * cache,
 	MemoryContextSwitchTo(oldcxt);
 }
 
-/* --------------------------------
- *		CatalogCacheSetId
- *
- *		XXX temporary function
- * --------------------------------
- */
-#ifdef NOT_USED
-void
-CatalogCacheSetId(CatCache *cacheInOutP, int id)
-{
-	Assert(id == InvalidCatalogCacheId || id >= 0);
-	cacheInOutP->id = id;
-}
-
-#endif
-
 /* ----------------
  * comphash
  *		Compute a hash value, somehow.
@@ -369,10 +354,12 @@ CatalogCacheComputeTupleHashIndex(struct catcache * cacheInOutP,
 								  Relation relation,
 								  HeapTuple tuple)
 {
-	bool		isNull = '\0';
+	bool		isNull = false;
 
+	/* XXX is this really needed? */
 	if (cacheInOutP->relationId == InvalidOid)
 		CatalogCacheInitializeCache(cacheInOutP, relation);
+
 	switch (cacheInOutP->cc_nkeys)
 	{
 		case 4:
@@ -417,8 +404,7 @@ CatalogCacheComputeTupleHashIndex(struct catcache * cacheInOutP,
 			break;
 		default:
 			elog(FATAL, "CCComputeTupleHashIndex: %d cc_nkeys",
-				 cacheInOutP->cc_nkeys
-				);
+				 cacheInOutP->cc_nkeys);
 			break;
 	}
 
@@ -427,6 +413,8 @@ CatalogCacheComputeTupleHashIndex(struct catcache * cacheInOutP,
 
 /* --------------------------------
  *		CatCacheRemoveCTup
+ *
+ *		NB: assumes caller has switched to CacheCxt
  * --------------------------------
  */
 static void
@@ -436,19 +424,24 @@ CatCacheRemoveCTup(CatCache *cache, Dlelem *elt)
 	CatCTup    *other_ct;
 	Dlelem	   *other_elt;
 
-	if (elt)
-		ct = (CatCTup *) DLE_VAL(elt);
-	else
+	if (!elt)					/* probably-useless safety check */
 		return;
 
+	/* We need to zap both linked-list elements as well as the tuple */
+
+	ct = (CatCTup *) DLE_VAL(elt);
 	other_elt = ct->ct_node;
 	other_ct = (CatCTup *) DLE_VAL(other_elt);
+
+	heap_freetuple(ct->ct_tup);
+
 	DLRemove(other_elt);
 	DLFreeElem(other_elt);
-	free(other_ct);
+	pfree(other_ct);
 	DLRemove(elt);
 	DLFreeElem(elt);
-	free(ct);
+	pfree(ct);
+
 	--cache->cc_ntup;
 }
 
@@ -529,7 +522,6 @@ CatalogCacheIdInvalidate(int cacheId,	/* XXX */
 	 * ----------------
 	 */
 	MemoryContextSwitchTo(oldcxt);
-	/* sendpm('I', "Invalidated tuple"); */
 }
 
 /* ----------------------------------------------------------------
@@ -615,34 +607,26 @@ ResetSystemCache()
  *
  *	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
- *	force the next SearchSysCache() call to reinitialize the cache itself,
- *	because we have info (such as cc_tupdesc) that is pointing at the about-
- *	to-be-deleted relcache entry.
+ *	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
+ *	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)
 {
-	struct catcache *cache;
-
 	/*
 	 * XXX Ideally we'd search the caches and just zap entries that actually
-	 * refer to the indicated relation.  For now, we take the brute-force
-	 * approach: just flush the caches entirely.
+	 * refer to or come from the indicated relation.  For now, we take the
+	 * brute-force approach: just flush the caches entirely.
 	 */
 	ResetSystemCache();
-
-	/*
-	 * If relcache is dropping a system relation's cache entry, mark the
-	 * associated cache structures invalid, so we can rebuild them from
-	 * scratch (not just repopulate them) next time they are used.
-	 */
-	for (cache = Caches; PointerIsValid(cache); cache = cache->cc_next)
-	{
-		if (cache->relationId == relId)
-			cache->relationId = InvalidOid;
-	}
 }
 
 /* --------------------------------
@@ -715,11 +699,11 @@ InitSysCache(char *relname,
 	{
 		/*
 		 * We can only do this optimization because the number of hash
-		 * buckets never changes.  Without it, we call malloc() too much.
+		 * buckets never changes.  Without it, we call palloc() too much.
 		 * We could move this to dllist.c, but the way we do this is not
-		 * dynamic/portabl, so why allow other routines to use it.
+		 * dynamic/portable, so why allow other routines to use it.
 		 */
-		Dllist	   *cache_begin = malloc((NCCBUCK + 1) * sizeof(Dllist));
+		Dllist	   *cache_begin = palloc((NCCBUCK + 1) * sizeof(Dllist));
 
 		for (i = 0; i <= NCCBUCK; ++i)
 		{
@@ -927,7 +911,7 @@ SearchSysCache(struct catcache * cache,
 	MemoryContext oldcxt;
 
 	/* ----------------
-	 *	sanity checks
+	 *	one-time startup overhead
 	 * ----------------
 	 */
 	if (cache->relationId == InvalidOid)
@@ -946,7 +930,7 @@ SearchSysCache(struct catcache * cache,
 	 *	resolve self referencing informtion
 	 */
 	if ((ntp = SearchSelfReferences(cache)))
-		return	heap_copytuple(ntp);
+		return ntp;
 
 	/* ----------------
 	 *	find the hash bucket in which to look for the tuple
@@ -995,10 +979,8 @@ SearchSysCache(struct catcache * cache,
 		DLMoveToFront(elt);
 
 #ifdef CACHEDEBUG
-		relation = heap_open(cache->relationId, NoLock);
 		CACHE3_elog(DEBUG, "SearchSysCache(%s): found in bucket %d",
-					RelationGetRelationName(relation), hash);
-		heap_close(relation, NoLock);
+					cache->cc_relname, hash);
 #endif	 /* CACHEDEBUG */
 
 		return ct->ct_tup;
@@ -1020,9 +1002,7 @@ SearchSysCache(struct catcache * cache,
 	 */
 
 	if (cache->busy)
-	{
 		elog(ERROR, "SearchSysCache: recursive use of cache %d", cache->id);
-	}
 	cache->busy = true;
 
 	/* ----------------
@@ -1140,10 +1120,10 @@ SearchSysCache(struct catcache * cache,
 		 * it easier to remove something from both the cache bucket and
 		 * the lru list at the same time
 		 */
-		nct = (CatCTup *) malloc(sizeof(CatCTup));
+		nct = (CatCTup *) palloc(sizeof(CatCTup));
 		nct->ct_tup = ntp;
 		elt = DLNewElem(nct);
-		nct2 = (CatCTup *) malloc(sizeof(CatCTup));
+		nct2 = (CatCTup *) palloc(sizeof(CatCTup));
 		nct2->ct_tup = ntp;
 		lru_elt = DLNewElem(nct2);
 		nct2->ct_node = elt;
diff --git a/src/backend/utils/cache/inval.c b/src/backend/utils/cache/inval.c
index 473978bd410343e3bc3d68e4d2aab4fab1dd852d..17071f8e2352849c3da6a0a9557888f4121589e9 100644
--- a/src/backend/utils/cache/inval.c
+++ b/src/backend/utils/cache/inval.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/cache/inval.c,v 1.33 2000/01/29 19:51:59 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/cache/inval.c,v 1.34 2000/01/31 04:35:52 tgl Exp $
  *
  * Note - this code is real crufty...
  *
@@ -557,7 +557,7 @@ static void
 ResetSystemCaches()
 {
 	ResetSystemCache();
-	RelationCacheInvalidate(true);
+	RelationCacheInvalidate();
 }
 
 /* --------------------------------
diff --git a/src/backend/utils/cache/rel.c b/src/backend/utils/cache/rel.c
index d24c1ca0c9cfa1c0b2cd8d9a74d005aaff941e87..50edb4224684db452848232b3e7ac86a41bf56e5 100644
--- a/src/backend/utils/cache/rel.c
+++ b/src/backend/utils/cache/rel.c
@@ -8,11 +8,10 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/cache/Attic/rel.c,v 1.7 2000/01/26 05:57:17 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/cache/Attic/rel.c,v 1.8 2000/01/31 04:35:52 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
-/* #define RELREFDEBUG	1 */
 
 #include "postgres.h"
 #include "access/istrat.h"
@@ -21,45 +20,22 @@
 /*
  *		RelationIsValid is now a macro in rel.h -cim 4/27/91
  *
- *		Many of the RelationGet...() functions are now macros in rel.h
+ *		All 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.
  *
+ *		This routine saves two pointers -- one to the IndexStrategy, and
+ *		one to the RegProcs that support the indexed access method.
+ *
  * Note:
- *		Assumes relation descriptor is a valid pointer to sufficient space.
+ *		Assumes relation descriptor is valid.
  *		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,
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 4a6c86d84d9f60e745aa160fa3e489c7a1ee3456..7c993d3d73f12c6c675ffe608bc55d60c1fbe488 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.88 2000/01/29 19:51:59 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/cache/relcache.c,v 1.89 2000/01/31 04:35:52 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -20,7 +20,6 @@
  *		RelationIdGetRelation			- get a reldesc by relation id
  *		RelationNameGetRelation			- get a reldesc by relation name
  *		RelationClose					- close an open relation
- *		RelationRebuildRelation			- rebuild relation information
  *
  * NOTES
  *		This file is in the process of being cleaned up
@@ -37,7 +36,6 @@
 
 #include "postgres.h"
 
-#include "utils/builtins.h"
 #include "access/genam.h"
 #include "access/heapam.h"
 #include "access/istrat.h"
@@ -52,50 +50,45 @@
 #include "catalog/pg_rewrite.h"
 #include "catalog/pg_type.h"
 #include "catalog/pg_variable.h"
+#include "commands/trigger.h"
 #include "lib/hasht.h"
 #include "miscadmin.h"
+#include "storage/bufmgr.h"
 #include "storage/smgr.h"
+#include "utils/builtins.h"
 #include "utils/catcache.h"
 #include "utils/relcache.h"
 #include "utils/temprel.h"
 
 
-static void RelationClearRelation(Relation relation, bool rebuildIt);
-static void RelationFlushRelation(Relation *relationPtr,
-								  bool onlyFlushReferenceCountZero);
-static Relation RelationNameCacheGetRelation(const char *relationName);
-static void RelationCacheAbortWalker(Relation *relationPtr,
-									 int dummy);
-static void init_irels(void);
-static void write_irels(void);
-
-/* ----------------
- *		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};
+static FormData_pg_attribute Desc_pg_class[Natts_pg_class] = {Schema_pg_class};
+static FormData_pg_attribute Desc_pg_attribute[Natts_pg_attribute] = {Schema_pg_attribute};
+static FormData_pg_attribute Desc_pg_proc[Natts_pg_proc] = {Schema_pg_proc};
+static FormData_pg_attribute Desc_pg_type[Natts_pg_type] = {Schema_pg_type};
+static FormData_pg_attribute Desc_pg_variable[Natts_pg_variable] = {Schema_pg_variable};
+static FormData_pg_attribute Desc_pg_log[Natts_pg_log] = {Schema_pg_log};
 
 /* ----------------
- *		global variables
+ *		Hash tables that index the relation cache
  *
  *		Relations are cached two ways, by name and by id,
  *		thus there are two hash tables for referencing them.
  * ----------------
  */
-HTAB	   *RelationNameCache;
-HTAB	   *RelationIdCache;
+static HTAB	   *RelationNameCache;
+static HTAB	   *RelationIdCache;
+
+/*
+ * newlyCreatedRelns -
+ *	  relations created during this transaction. We need to keep track of
+ *	  these.
+ */
+static List *newlyCreatedRelns = NULL;
+
 
 /* ----------------
  *		RelationBuildDescInfo exists so code can be shared
@@ -207,8 +200,17 @@ do { \
 } while(0)
 
 /* non-export function prototypes */
+
+static void RelationClearRelation(Relation relation, bool rebuildIt);
+static void RelationFlushRelation(Relation *relationPtr,
+								  int skipLocalRelations);
+static Relation RelationNameCacheGetRelation(const char *relationName);
+static void RelationCacheAbortWalker(Relation *relationPtr, int dummy);
+static void init_irels(void);
+static void write_irels(void);
+
 static void formrdesc(char *relationName, u_int natts,
-		  FormData_pg_attribute *att);
+					  FormData_pg_attribute *att);
 
 static HeapTuple ScanPgRelation(RelationBuildDescInfo buildinfo);
 static HeapTuple scan_pg_rel_seq(RelationBuildDescInfo buildinfo);
@@ -227,16 +229,6 @@ static void IndexedAccessMethodInitialize(Relation relation);
 static void AttrDefaultFetch(Relation relation);
 static void RelCheckFetch(Relation relation);
 
-extern void RelationBuildTriggers(Relation relation);
-extern void FreeTriggerDesc(Relation relation);
-
-/*
- * newlyCreatedRelns -
- *	  relations created during this transaction. We need to keep track of
- *	  these.
- */
-static List *newlyCreatedRelns = NULL;
-
 /* ----------------------------------------------------------------
  *		RelationIdGetRelation() and RelationNameGetRelation()
  *						support functions
@@ -632,22 +624,20 @@ RelationBuildRuleLock(Relation relation)
 						   ObjectIdGetDatum(RelationGetRelid(relation)));
 
 	/* ----------------
-	 *	open pg_attribute and begin a scan
+	 *	open pg_rewrite and begin a scan
 	 * ----------------
 	 */
 	pg_rewrite_desc = heap_openr(RewriteRelationName, AccessShareLock);
 	pg_rewrite_scan = heap_beginscan(pg_rewrite_desc, 0, SnapshotNow, 1, &key);
 	pg_rewrite_tupdesc = RelationGetDescr(pg_rewrite_desc);
 
-	/* ----------------
-	 *	add attribute data to relation->rd_att
-	 * ----------------
-	 */
 	while (HeapTupleIsValid(pg_rewrite_tuple = heap_getnext(pg_rewrite_scan, 0)))
 	{
 		bool		isnull;
 		Datum		ruleaction;
-		Datum		rule_evqual_string;
+		Datum		rule_evqual;
+		char	   *ruleaction_str;
+		char	   *rule_evqual_str;
 		RewriteRule *rule;
 
 		rule = (RewriteRule *) palloc(sizeof(RewriteRule));
@@ -665,24 +655,27 @@ RelationBuildRuleLock(Relation relation)
 										 &isnull);
 
 		ruleaction = heap_getattr(pg_rewrite_tuple,
-						   Anum_pg_rewrite_ev_action, pg_rewrite_tupdesc,
+								  Anum_pg_rewrite_ev_action,
+								  pg_rewrite_tupdesc,
 								  &isnull);
-		rule_evqual_string = heap_getattr(pg_rewrite_tuple,
-							 Anum_pg_rewrite_ev_qual, pg_rewrite_tupdesc,
-										  &isnull);
-
-		ruleaction = PointerGetDatum(textout((text *) DatumGetPointer(ruleaction)));
-		rule_evqual_string = PointerGetDatum(textout((text *) DatumGetPointer(rule_evqual_string)));
-
-		rule->actions = (List *) stringToNode(DatumGetPointer(ruleaction));
-		rule->qual = (Node *) stringToNode(DatumGetPointer(rule_evqual_string));
-
-		rules[numlocks++] = rule;
-		if (numlocks == maxlocks)
+		ruleaction_str = textout((text *) DatumGetPointer(ruleaction));
+		rule->actions = (List *) stringToNode(ruleaction_str);
+		pfree(ruleaction_str);
+
+		rule_evqual = heap_getattr(pg_rewrite_tuple,
+								   Anum_pg_rewrite_ev_qual,
+								   pg_rewrite_tupdesc,
+								   &isnull);
+		rule_evqual_str = textout((text *) DatumGetPointer(rule_evqual));
+		rule->qual = (Node *) stringToNode(rule_evqual_str);
+		pfree(rule_evqual_str);
+
+		if (numlocks >= maxlocks)
 		{
 			maxlocks *= 2;
 			rules = (RewriteRule **) repalloc(rules, sizeof(RewriteRule *) * maxlocks);
 		}
+		rules[numlocks++] = rule;
 	}
 
 	/* ----------------
@@ -701,7 +694,91 @@ RelationBuildRuleLock(Relation relation)
 	rulelock->rules = rules;
 
 	relation->rd_rules = rulelock;
-	return;
+}
+
+/* --------------------------------
+ *		FreeRuleLock
+ *
+ *		Release the storage used for a set of rewrite rules.
+ *
+ *		Probably this should be in the rules code someplace...
+ * --------------------------------
+ */
+static void
+FreeRuleLock(RuleLock *rlock)
+{
+	int		i;
+
+	if (rlock == NULL)
+		return;
+	for (i = 0; i < rlock->numLocks; i++)
+	{
+		RewriteRule *rule = rlock->rules[i];
+
+#if 0							/* does freefuncs.c still work?  Not sure */
+		freeObject(rule->actions);
+		freeObject(rule->qual);
+#endif
+		pfree(rule);
+	}
+	pfree(rlock->rules);
+	pfree(rlock);
+}
+
+/* --------------------------------
+ *		equalRuleLocks
+ *
+ *		Determine whether two RuleLocks are equivalent
+ *
+ *		Probably this should be in the rules code someplace...
+ * --------------------------------
+ */
+static bool
+equalRuleLocks(RuleLock *rlock1, RuleLock *rlock2)
+{
+	int		i,
+			j;
+
+	if (rlock1 != NULL)
+	{
+		if (rlock2 == NULL)
+			return false;
+		if (rlock1->numLocks != rlock2->numLocks)
+			return false;
+		for (i = 0; i < rlock1->numLocks; i++)
+		{
+			RewriteRule *rule1 = rlock1->rules[i];
+			RewriteRule *rule2 = NULL;
+
+			/*
+			 * We can't assume that the rules are always read from
+			 * pg_rewrite in the same order; so use the rule OIDs to
+			 * identify the rules to compare.  (We assume here that the
+			 * same OID won't appear twice in either ruleset.)
+			 */
+			for (j = 0; j < rlock2->numLocks; j++)
+			{
+				rule2 = rlock2->rules[j];
+				if (rule1->ruleId == rule2->ruleId)
+					break;
+			}
+			if (j >= rlock2->numLocks)
+				return false;
+			if (rule1->event != rule2->event)
+				return false;
+			if (rule1->attrno != rule2->attrno)
+				return false;
+			if (rule1->isInstead != rule2->isInstead)
+				return false;
+			if (! equal(rule1->qual, rule2->qual))
+				return false;
+			if (! equal(rule1->actions, rule2->actions))
+				return false;
+		}
+	}
+	else if (rlock2 != NULL)
+		return false;
+	return true;
 }
 
 
@@ -800,7 +877,7 @@ RelationBuildDesc(RelationBuildDescInfo buildinfo,
 	 * ----------------
 	 */
 	if (OidIsValid(relam))
-		relation->rd_am = (Form_pg_am) AccessMethodObjectIdGetForm(relam);
+		relation->rd_am = AccessMethodObjectIdGetForm(relam);
 
 	/* ----------------
 	 *	initialize the tuple descriptor (relation->rd_att).
@@ -1213,6 +1290,9 @@ RelationClose(Relation relation)
  *	 usually used when we are notified of a change to an open relation
  *	 (one with refcount > 0).  However, this routine just does whichever
  *	 it's told to do; callers must determine which they want.
+ *
+ *	 If we detect a change in the relation's TupleDesc or trigger data
+ *	 while rebuilding, we complain unless refcount is 0.
  * --------------------------------
  */
 static void
@@ -1252,26 +1332,53 @@ RelationClearRelation(Relation relation, bool rebuildIt)
 	/* Clear out catcache's entries for this relation */
 	SystemCacheRelationFlushed(RelationGetRelid(relation));
 
-	/* Free all the subsidiary data structures of the relcache entry */
-	FreeTupleDesc(relation->rd_att);
-	FreeTriggerDesc(relation);
-	pfree(RelationGetForm(relation));
+	/*
+	 * Free all the subsidiary data structures of the relcache entry.
+	 * We cannot free rd_att if we are trying to rebuild the entry,
+	 * however, because pointers to it may be cached in various places.
+	 * The trigger manager might also have pointers into the trigdesc,
+	 * and the rule manager might have pointers into the rewrite rules.
+	 * So to begin with, we can only get rid of these fields:
+	 */
+	if (relation->rd_am)
+		pfree(relation->rd_am);
+	if (relation->rd_rel)
+		pfree(relation->rd_rel);
+	if (relation->rd_istrat)
+		pfree(relation->rd_istrat);
+	if (relation->rd_support)
+		pfree(relation->rd_support);
 
 	/*
 	 * If we're really done with the relcache entry, blow it away.
 	 * But if someone is still using it, reconstruct the whole deal
 	 * without moving the physical RelationData record (so that the
-	 * someone's pointer is still valid).  Must preserve ref count
-	 * and myxactonly flag, too.
+	 * someone's pointer is still valid).
 	 */
 	if (! rebuildIt)
 	{
+		/* ok to zap remaining substructure */
+		FreeTupleDesc(relation->rd_att);
+		FreeRuleLock(relation->rd_rules);
+		FreeTriggerDesc(relation->trigdesc);
 		pfree(relation);
 	}
 	else
 	{
-		uint16		old_refcnt = relation->rd_refcnt;
-		bool		old_myxactonly = relation->rd_myxactonly;
+		/*
+		 * When rebuilding an open relcache entry, must preserve ref count
+		 * and myxactonly flag.  Also attempt to preserve the tupledesc,
+		 * rewrite rules, and trigger substructures in place.
+		 * Furthermore we save/restore rd_nblocks (in case it is a local
+		 * relation) *and* call RelationGetNumberOfBlocks (in case it isn't).
+		 */
+		uint16			old_refcnt = relation->rd_refcnt;
+		bool			old_myxactonly = relation->rd_myxactonly;
+		TupleDesc		old_att = relation->rd_att;
+		RuleLock	   *old_rules = relation->rd_rules;
+		TriggerDesc	   *old_trigdesc = relation->trigdesc;
+		int				old_nblocks = relation->rd_nblocks;
+		bool			relDescChanged = false;
 		RelationBuildDescInfo buildinfo;
 
 		buildinfo.infotype = INFO_RELID;
@@ -1280,12 +1387,54 @@ RelationClearRelation(Relation relation, bool rebuildIt)
 		if (RelationBuildDesc(buildinfo, relation) != relation)
 		{
 			/* Should only get here if relation was deleted */
+			FreeTupleDesc(old_att);
+			FreeRuleLock(old_rules);
+			FreeTriggerDesc(old_trigdesc);
 			pfree(relation);
 			elog(ERROR, "RelationClearRelation: relation %u deleted while still in use",
 				 buildinfo.i.info_id);
 		}
 		RelationSetReferenceCount(relation, old_refcnt);
 		relation->rd_myxactonly = old_myxactonly;
+		if (equalTupleDescs(old_att, relation->rd_att))
+		{
+			FreeTupleDesc(relation->rd_att);
+			relation->rd_att = old_att;
+		}
+		else
+		{
+			FreeTupleDesc(old_att);
+			relDescChanged = true;
+		}
+		if (equalRuleLocks(old_rules, relation->rd_rules))
+		{
+			FreeRuleLock(relation->rd_rules);
+			relation->rd_rules = old_rules;
+		}
+		else
+		{
+			FreeRuleLock(old_rules);
+			relDescChanged = true;
+		}
+		if (equalTriggerDescs(old_trigdesc, relation->trigdesc))
+		{
+			FreeTriggerDesc(relation->trigdesc);
+			relation->trigdesc = old_trigdesc;
+		}
+		else
+		{
+			FreeTriggerDesc(old_trigdesc);
+			relDescChanged = true;
+		}
+		relation->rd_nblocks = old_nblocks;
+		/* this is kind of expensive, but I think we must do it in case
+		 * relation has been truncated...
+		 */
+		relation->rd_nblocks = RelationGetNumberOfBlocks(relation);
+
+		if (relDescChanged && ! RelationHasReferenceCountZero(relation))
+			elog(ERROR, "RelationClearRelation: relation %u modified while in use",
+				 buildinfo.i.info_id);
 	}
 
 	MemoryContextSwitchTo(oldcxt);
@@ -1295,32 +1444,40 @@ RelationClearRelation(Relation relation, bool rebuildIt)
  * RelationFlushRelation
  *
  *	 Rebuild the relation if it is open (refcount > 0), else blow it away.
- *	 Setting onlyFlushReferenceCountZero to FALSE overrides refcount check.
- *	 This is currently only used to process SI invalidation notifications.
+ *	 If skipLocalRelations is TRUE, xact-local relations are ignored
+ *	 (which is useful when processing SI cache reset, since xact-local
+ *	 relations could not be targets of notifications from other backends).
+ *
  *	 The peculiar calling convention (pointer to pointer to relation)
  *	 is needed so that we can use this routine as a hash table walker.
  * --------------------------------
  */
 static void
 RelationFlushRelation(Relation *relationPtr,
-					  bool onlyFlushReferenceCountZero)
+					  int skipLocalRelations)
 {
 	Relation	relation = *relationPtr;
+	bool		rebuildIt;
 
-	/*
-	 * Do nothing to transaction-local relations, since they cannot be
-	 * subjects of SI notifications from other backends.
-	 */
 	if (relation->rd_myxactonly)
-		return;
+	{
+		if (skipLocalRelations)
+			return;				/* don't touch local rels if so commanded */
+		/*
+		 * Local rels should always be rebuilt, not flushed; the relcache
+		 * entry must live until RelationPurgeLocalRelation().
+		 */
+		rebuildIt = true;
+	}
+	else
+	{
+		/*
+		 * Nonlocal rels can be dropped from the relcache if not open.
+		 */
+		rebuildIt = ! RelationHasReferenceCountZero(relation);
+	}
 
-	/*
-	 * Zap it.  Rebuild if it has nonzero ref count and we did not get
-	 * the override flag.
-	 */
-	RelationClearRelation(relation,
-						  (onlyFlushReferenceCountZero &&
-						   ! RelationHasReferenceCountZero(relation)));
+	RelationClearRelation(relation, rebuildIt);
 }
 
 /* --------------------------------
@@ -1373,21 +1530,16 @@ RelationForgetRelation(Oid rid)
 	}
 }
 
-/* --------------------------------
- * RelationRebuildRelation -
- *
- *		   Force a relcache entry to be rebuilt from catalog entries.
- *		   This is needed, eg, after modifying an attribute of the rel.
- * --------------------------------
- */
-void
-RelationRebuildRelation(Relation relation)
-{
-	RelationClearRelation(relation, true);
-}
-
 /* --------------------------------
  *		RelationIdInvalidateRelationCacheByRelationId
+ *
+ *		This routine is invoked for SI cache flush messages.
+ *
+ *		We used to skip local relations, on the grounds that they could
+ *		not be targets of cross-backend SI update messages; but it seems
+ *		safer to process them, so that our *own* SI update messages will
+ *		have the same effects during CommandCounterIncrement for both
+ *		local and nonlocal relations.
  * --------------------------------
  */
 void
@@ -1397,36 +1549,8 @@ RelationIdInvalidateRelationCacheByRelationId(Oid relationId)
 
 	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_myxactonly)
-	{
-#if 1
-		/*
-		 * Seems safest just to NEVER flush rels with positive refcounts.
-		 * I think the code only had that proviso as a rather lame method of
-		 * cleaning up unused relcache entries that had dangling refcounts
-		 * (following elog(ERROR) with an open rel).  Now we rely on
-		 * RelationCacheAbort to clean up dangling refcounts, so there's no
-		 * good reason to ever risk flushing a rel with positive refcount.
-		 * IMHO anyway --- tgl 1/29/00.
-		 */
-		RelationFlushRelation(&relation, true);
-#else
-		/*
-		 * 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());
-#endif
-	}
+	if (PointerIsValid(relation))
+		RelationFlushRelation(&relation, false);
 }
 
 #if NOT_USED
@@ -1448,7 +1572,7 @@ RelationFlushIndexes(Relation *r,
 	if (relation->rd_rel->relkind == RELKIND_INDEX &&	/* XXX style */
 		(!OidIsValid(accessMethodId) ||
 		 relation->rd_rel->relam == accessMethodId))
-		RelationFlushRelation(&relation, true);
+		RelationFlushRelation(&relation, false);
 }
 
 #endif
@@ -1477,37 +1601,19 @@ RelationIdInvalidateRelationCacheByAccessMethodId(Oid accessMethodId)
 
 /*
  * RelationCacheInvalidate
- *
- *	 Will blow away either all the cached relation descriptors or
- *	 those that have a zero reference count.
- *
- *	 CAUTION: this is only called with onlyFlushReferenceCountZero=true
- *	 at present, so that relation descriptors with positive refcounts
- *	 are rebuilt rather than clobbered.  It would only be safe to use a
- *	 "false" parameter in a totally idle backend with no open relations.
+ *	 Blow away cached relation descriptors that have zero reference counts,
+ *	 and rebuild those with positive reference counts.
  *
  *	 This is currently used only to recover from SI message buffer overflow,
- *	 so we do not blow away transaction-local relations; they cannot be
- *	 targets of SI updates.
+ *	 so we do not touch transaction-local relations; they cannot be targets
+ *	 of cross-backend SI updates (and our own updates now go through a
+ *	 separate linked list that isn't limited by the SI message buffer size).
  */
 void
-RelationCacheInvalidate(bool onlyFlushReferenceCountZero)
+RelationCacheInvalidate(void)
 {
 	HashTableWalk(RelationNameCache, (HashtFunc) RelationFlushRelation,
-				  onlyFlushReferenceCountZero);
-
-	if (!onlyFlushReferenceCountZero)
-	{
-		/*
-		 * Debugging check: what's left should be transaction-local relations
-		 * plus nailed-in reldescs.  There should be 6 hardwired heaps
-		 * + 3 hardwired indices == 9 total.
-		 */
-		int		numRels = length(newlyCreatedRelns) + 9;
-
-		Assert(RelationNameCache->hctl->nkeys == numRels);
-		Assert(RelationIdCache->hctl->nkeys == numRels);
-	}
+				  (int) true);
 }
 
 /*
@@ -1672,8 +1778,6 @@ RelationInitialize(void)
 	 *	initialize the cache with pre-made relation descriptors
 	 *	for some of the more important system relations.  These
 	 *	relations should always be in the cache.
-	 *
-	 *	NB: if you change this list, fix the count in RelationCacheInvalidate!
 	 * ----------------
 	 */
 	formrdesc(RelationRelationName, Natts_pg_class, Desc_pg_class);
@@ -2008,7 +2112,7 @@ init_irels(void)
 		}
 
 		/* oh, for god's sake... */
-#define SMD(i)	strat[0].strategyMapData[i].entry[0]
+#define SMD(i)	strat->strategyMapData[i].entry[0]
 
 		/* have to reinit the function pointers in the strategy maps */
 		for (i = 0; i < am->amstrategies * relform->relnatts; i++)
@@ -2038,11 +2142,6 @@ init_irels(void)
 			write_irels();
 			return;
 		}
-
-		/*
-		 * p += sizeof(IndexStrategy); ((RegProcedure **) p) = support;
-		 */
-
 		ird->rd_support = support;
 
 		RelationInitLockInfo(ird);
@@ -2085,8 +2184,6 @@ write_irels(void)
 	 * relation searches -- a necessary step, since we're trying to
 	 * instantiate the index relation descriptors here.  Once we have the
 	 * descriptors, nail them into cache so we never lose them.
-	 *
-	 * NB: if you change this list, fix the count in RelationCacheInvalidate!
 	 */
 
 	oldmode = GetProcessingMode();
diff --git a/src/backend/utils/mmgr/aset.c b/src/backend/utils/mmgr/aset.c
index 1ee38a928b0e999db02a74cc19ddd263637a0c9b..afc6da38eb4c771e5fa04dd400e4573d4c7d0563 100644
--- a/src/backend/utils/mmgr/aset.c
+++ b/src/backend/utils/mmgr/aset.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/mmgr/aset.c,v 1.23 2000/01/26 05:57:30 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/mmgr/aset.c,v 1.24 2000/01/31 04:35:53 tgl Exp $
  *
  * NOTE:
  *	This is a new (Feb. 05, 1999) implementation of the allocation set
@@ -389,6 +389,11 @@ AllocSetFree(AllocSet set, AllocPointer pointer)
 
 	chunk = AllocPointerGetChunk(pointer);
 
+#ifdef CLOBBER_FREED_MEMORY
+	/* Wipe freed memory for debugging purposes */
+	memset(pointer, 0x7F, chunk->size);
+#endif
+
 	if (chunk->size >= ALLOC_BIGCHUNK_LIMIT)
 	{
 		/* Big chunks are certain to have been allocated as single-chunk
diff --git a/src/backend/utils/mmgr/portalmem.c b/src/backend/utils/mmgr/portalmem.c
index f19e1c2d468c5b044478f61d4e3078a3e7d3c47e..78da0bc9edb95d0e5ebda9f3c9a37acfc183f93d 100644
--- a/src/backend/utils/mmgr/portalmem.c
+++ b/src/backend/utils/mmgr/portalmem.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/mmgr/portalmem.c,v 1.33 2000/01/26 05:57:31 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/mmgr/portalmem.c,v 1.34 2000/01/31 04:35:53 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -468,7 +468,7 @@ CollectNamedPortals(Portal *portalP, int destroy)
 void
 AtEOXact_portals()
 {
-	HashTableWalk(PortalHashTable, CollectNamedPortals, 0);
+	HashTableWalk(PortalHashTable, (HashtFunc) CollectNamedPortals, 0);
 	CollectNamedPortals(NULL, 1);
 }
 
@@ -478,7 +478,7 @@ AtEOXact_portals()
  */
 #ifdef NOT_USED
 static void
-PortalDump(Portal *thisP)
+PortalDump(Portal *thisP, int dummy)
 {
 	/* XXX state/argument checking here */
 
@@ -498,7 +498,7 @@ DumpPortals()
 {
 	/* XXX state checking here */
 
-	HashTableWalk(PortalHashTable, PortalDump, 0);
+	HashTableWalk(PortalHashTable, (HashtFunc) PortalDump, 0);
 }
 
 #endif
@@ -556,7 +556,7 @@ EnablePortalManager(bool on)
 		/*
 		 * Each portal must free its non-memory resources specially.
 		 */
-		HashTableWalk(PortalHashTable, PortalDrop, 0);
+		HashTableWalk(PortalHashTable, (HashtFunc) PortalDrop, 0);
 		hash_destroy(PortalHashTable);
 		PortalHashTable = NULL;
 
diff --git a/src/include/access/tupdesc.h b/src/include/access/tupdesc.h
index 727fee212aa96de53b57e1eccf746245f750073e..525cd6267e114e904d9bbd1ab1f1becddf774518 100644
--- a/src/include/access/tupdesc.h
+++ b/src/include/access/tupdesc.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: tupdesc.h,v 1.26 2000/01/26 05:57:51 momjian Exp $
+ * $Id: tupdesc.h,v 1.27 2000/01/31 04:35:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -47,12 +47,11 @@ typedef struct tupleConstr
  */
 typedef struct tupleDesc
 {
-	int			natts;
-	/* Number of attributes in the tuple */
+	int			natts;			/* Number of attributes in the tuple */
 	Form_pg_attribute *attrs;
 	/* attrs[N] is a pointer to the description of Attribute Number N+1.  */
 	TupleConstr *constr;
-}		   *TupleDesc;
+} *TupleDesc;
 
 extern TupleDesc CreateTemplateTupleDesc(int natts);
 
@@ -64,6 +63,8 @@ extern TupleDesc CreateTupleDescCopyConstr(TupleDesc tupdesc);
 
 extern void FreeTupleDesc(TupleDesc tupdesc);
 
+extern bool equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2);
+
 extern bool TupleDescInitEntry(TupleDesc desc,
 				   AttrNumber attributeNumber,
 				   char *attributeName,
diff --git a/src/include/commands/trigger.h b/src/include/commands/trigger.h
index ddb9f7ca5b45e160bdcd1f0ceb9058b56d745b25..b2c258c2f8322d44aa2250a52d15c4a8ca9fab46 100644
--- a/src/include/commands/trigger.h
+++ b/src/include/commands/trigger.h
@@ -3,6 +3,10 @@
  * trigger.h
  *	  prototypes for trigger.c.
  *
+ * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id: trigger.h,v 1.18 2000/01/31 04:35:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -70,6 +74,12 @@ extern void CreateTrigger(CreateTrigStmt *stmt);
 extern void DropTrigger(DropTrigStmt *stmt);
 extern void RelationRemoveTriggers(Relation rel);
 
+extern void RelationBuildTriggers(Relation relation);
+
+extern void FreeTriggerDesc(TriggerDesc *trigdesc);
+
+extern bool equalTriggerDescs(TriggerDesc *trigdesc1, TriggerDesc *trigdesc2);
+
 extern HeapTuple ExecBRInsertTriggers(Relation rel, HeapTuple tuple);
 extern void ExecARInsertTriggers(Relation rel, HeapTuple tuple);
 extern bool ExecBRDeleteTriggers(EState *estate, ItemPointer tupleid);
diff --git a/src/include/lib/hasht.h b/src/include/lib/hasht.h
index 6f64022e23ae3fabd4070fa5de73d81f5a051ed3..78318f954d46592506ff6e5df3ba2636e7fef722 100644
--- a/src/include/lib/hasht.h
+++ b/src/include/lib/hasht.h
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: hasht.h,v 1.9 2000/01/26 05:58:09 momjian Exp $
+ * $Id: hasht.h,v 1.10 2000/01/31 04:35:55 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -17,7 +17,7 @@
 
 #include "utils/hsearch.h"
 
-typedef void (*HashtFunc) ();
+typedef void (*HashtFunc) (void *hashitem, int arg);
 
 extern void HashTableWalk(HTAB *hashtable, HashtFunc function, int arg);
 
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index 867ffa604d5ba3208b094f33b49388b9131078db..c8238008021160a5d637d1f40c9038e9ad3b8c5e 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -1,13 +1,13 @@
 /*-------------------------------------------------------------------------
  *
  * rel.h
- *	  POSTGRES relation descriptor definitions.
+ *	  POSTGRES relation descriptor (a/k/a relcache entry) definitions.
  *
  *
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: rel.h,v 1.33 2000/01/26 05:58:38 momjian Exp $
+ * $Id: rel.h,v 1.34 2000/01/31 04:35:57 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -21,6 +21,9 @@
 #include "rewrite/prs2lock.h"
 #include "storage/fd.h"
 
+/* added to prevent circular dependency.  bjm 1999/11/15 */
+extern char *get_temp_rel_by_physicalname(const char *relname);
+
 /*
  * LockRelId and LockInfo really belong to lmgr.h, but it's more convenient
  * to declare them here so we can have a LockInfoData field in a Relation.
@@ -39,6 +42,10 @@ typedef struct LockInfoData
 
 typedef LockInfoData *LockInfo;
 
+/*
+ * Likewise, this struct really belongs to trigger.h, but for convenience
+ * we put it here.
+ */
 
 typedef struct Trigger
 {
@@ -58,6 +65,7 @@ typedef struct Trigger
 
 typedef struct TriggerDesc
 {
+	/* index data to identify which triggers are which */
 	uint16		n_before_statement[4];
 	uint16		n_before_row[4];
 	uint16		n_after_row[4];
@@ -66,9 +74,14 @@ typedef struct TriggerDesc
 	Trigger   **tg_before_row[4];
 	Trigger   **tg_after_row[4];
 	Trigger   **tg_after_statement[4];
+	/* the actual array of triggers is here */
 	Trigger    *triggers;
+	int			numtriggers;
 } TriggerDesc;
 
+/*
+ * Here are the contents of a relation cache entry.
+ */
 
 typedef struct RelationData
 {
@@ -87,7 +100,7 @@ typedef struct RelationData
 	RuleLock   *rd_rules;		/* rewrite rules */
 	IndexStrategy rd_istrat;
 	RegProcedure *rd_support;
-	TriggerDesc *trigdesc;
+	TriggerDesc *trigdesc;		/* Trigger info, or NULL if rel has none */
 } RelationData;
 
 typedef RelationData *Relation;
@@ -110,15 +123,6 @@ typedef Relation *RelationPtr;
 
 #define InvalidRelation ((Relation) NULL)
 
-/*
- * RelationGetSystemPort
- *		Returns system port of a relation.
- *
- * Note:
- *		Assumes relation descriptor is valid.
- */
-#define RelationGetSystemPort(relation) ((relation)->rd_fd)
-
 /*
  * RelationHasReferenceCountZero
  *		True iff relation reference count is zero.
@@ -149,7 +153,7 @@ typedef Relation *RelationPtr;
 
 /*
  * RelationGetForm
- *		Returns relation attribute values for a relation.
+ *		Returns pg_class tuple for a relation.
  *
  * Note:
  *		Assumes relation descriptor is valid.
@@ -159,15 +163,14 @@ typedef Relation *RelationPtr;
 /*
  * RelationGetRelid
  *
- *	returns the object id of the relation
- *
+ *	returns the OID of the relation
  */
 #define RelationGetRelid(relation) ((relation)->rd_id)
 
 /*
  * RelationGetFile
  *
- *	  Returns the open File decscriptor
+ *	  Returns the open file descriptor for the rel
  */
 #define RelationGetFile(relation) ((relation)->rd_fd)
 
@@ -176,8 +179,6 @@ typedef Relation *RelationPtr;
  *
  *	  Returns a Relation Name
  */
-/* added to prevent circular dependency.  bjm 1999/11/15 */
-char 	   *get_temp_rel_by_physicalname(const char *relname);
 #define RelationGetRelationName(relation) \
 (\
 	(strncmp(RelationGetPhysicalRelationName(relation), \
@@ -210,10 +211,19 @@ char 	   *get_temp_rel_by_physicalname(const char *relname);
  */
 #define RelationGetDescr(relation) ((relation)->rd_att)
 
+/*
+ * RelationGetIndexStrategy
+ *		Returns index strategy for a relation.
+ *
+ * Note:
+ *		Assumes relation descriptor is valid.
+ *		Assumes relation descriptor is for an index relation.
+ */
+#define RelationGetIndexStrategy(relation) ((relation)->rd_istrat)
 
-extern IndexStrategy RelationGetIndexStrategy(Relation relation);
 
-extern void RelationSetIndexSupport(Relation relation, IndexStrategy strategy,
-						RegProcedure *support);
+extern void RelationSetIndexSupport(Relation relation,
+									IndexStrategy strategy,
+									RegProcedure *support);
 
 #endif	 /* REL_H */
diff --git a/src/include/utils/relcache.h b/src/include/utils/relcache.h
index cb14e1b6d3d0476fb2421170bafe0a868036c93e..073c846e4b304ce46b61beac96b98bf7c05c1534 100644
--- a/src/include/utils/relcache.h
+++ b/src/include/utils/relcache.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: relcache.h,v 1.18 2000/01/26 05:58:38 momjian Exp $
+ * $Id: relcache.h,v 1.19 2000/01/31 04:35:57 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -29,11 +29,9 @@ extern void RelationForgetRelation(Oid rid);
 /*
  * Routines for flushing/rebuilding relcache entries in various scenarios
  */
-extern void RelationRebuildRelation(Relation relation);
-
 extern void RelationIdInvalidateRelationCacheByRelationId(Oid relationId);
 
-extern void RelationCacheInvalidate(bool onlyFlushReferenceCountZero);
+extern void RelationCacheInvalidate(void);
 
 extern void RelationRegisterRelation(Relation relation);
 extern void RelationPurgeLocalRelation(bool xactComitted);