From 0b370ea7c81228339da5a447057dbf5f874e0197 Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Fri, 1 Jun 2001 02:41:36 +0000
Subject: [PATCH] Clean up some minor problems exposed by further thought about
 Panon's bug report on old-style functions invoked by RI triggers.  We had a
 number of other places that were being sloppy about which memory context
 FmgrInfo subsidiary data will be allocated in.  Turns out none of them
 actually cause a problem in 7.1, but this is for arcane reasons such as the
 fact that old-style triggers aren't supported anyway.  To avoid getting burnt
 later, I've restructured the trigger support so that we don't keep trigger
 FmgrInfo structs in relcache memory.  Some other related cleanups too: it's
 not really necessary to call fmgr_info at all while setting up the index
 support info in relcache entries, because those ScanKeyEntry structs are
 never used to invoke the functions.  This should speed up relcache
 initialization a tiny bit.

---
 doc/src/sgml/trigger.sgml           |   1 -
 src/backend/access/common/scankey.c |   7 +-
 src/backend/access/index/indexam.c  |  28 +--
 src/backend/access/index/istrat.c   |  34 ++-
 src/backend/catalog/index.c         |  12 +-
 src/backend/catalog/pg_operator.c   |  83 +++----
 src/backend/commands/copy.c         |  13 +-
 src/backend/commands/trigger.c      | 365 ++++++++++++++++++----------
 src/backend/executor/execMain.c     |  35 +--
 src/backend/utils/cache/catcache.c  |  24 +-
 src/backend/utils/cache/relcache.c  |  36 +--
 src/backend/utils/fmgr/fmgr.c       |  21 +-
 src/include/access/skey.h           |  14 +-
 src/include/commands/trigger.h      |  28 ++-
 src/include/nodes/execnodes.h       |  14 +-
 src/include/utils/rel.h             |  31 ++-
 16 files changed, 448 insertions(+), 298 deletions(-)

diff --git a/doc/src/sgml/trigger.sgml b/doc/src/sgml/trigger.sgml
index 686c9580edf..bb88ccc4ded 100644
--- a/doc/src/sgml/trigger.sgml
+++ b/doc/src/sgml/trigger.sgml
@@ -366,7 +366,6 @@ typedef struct Trigger
     Oid         tgoid;
     char       *tgname;
     Oid         tgfoid;
-    FmgrInfo    tgfunc;
     int16       tgtype;
     bool        tgenabled;
     bool        tgisconstraint;
diff --git a/src/backend/access/common/scankey.c b/src/backend/access/common/scankey.c
index dc2d36b52f4..eb66d41bf26 100644
--- a/src/backend/access/common/scankey.c
+++ b/src/backend/access/common/scankey.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/access/common/scankey.c,v 1.18 2001/01/24 19:42:47 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/access/common/scankey.c,v 1.19 2001/06/01 02:41:35 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -40,11 +40,13 @@ ScanKeyEntrySetIllegal(ScanKey entry)
 	entry->sk_flags = 0;		/* just in case... */
 	entry->sk_attno = InvalidAttrNumber;
 	entry->sk_procedure = 0;	/* should be InvalidRegProcedure */
+	entry->sk_func.fn_oid = InvalidOid;
+	entry->sk_argument = (Datum) 0;
 }
 
 /*
  * ScanKeyEntryInitialize
- *		Initializes an scan key entry.
+ *		Initializes a scan key entry.
  *
  * Note:
  *		Assumes the scan key entry is valid.
@@ -64,7 +66,6 @@ ScanKeyEntryInitialize(ScanKey entry,
 	entry->sk_procedure = procedure;
 	entry->sk_argument = argument;
 	fmgr_info(procedure, &entry->sk_func);
-	entry->sk_nargs = entry->sk_func.fn_nargs;
 
 	Assert(ScanKeyEntryIsLegal(entry));
 }
diff --git a/src/backend/access/index/indexam.c b/src/backend/access/index/indexam.c
index 3cea6895f31..5b129301148 100644
--- a/src/backend/access/index/indexam.c
+++ b/src/backend/access/index/indexam.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/access/index/indexam.c,v 1.49 2001/05/31 18:16:54 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/access/index/indexam.c,v 1.50 2001/06/01 02:41:35 tgl Exp $
  *
  * INTERFACE ROUTINES
  *		index_open		- open an index relation by relationId
@@ -232,7 +232,7 @@ index_beginscan(Relation relation,
 				uint16 numberOfKeys,
 				ScanKey key)
 {
-	IndexScanDesc scandesc;
+	IndexScanDesc scan;
 	RegProcedure procedure;
 
 	RELATION_CHECKS;
@@ -249,14 +249,22 @@ index_beginscan(Relation relation,
 	 */
 	LockRelation(relation, AccessShareLock);
 
-	scandesc = (IndexScanDesc)
+	scan = (IndexScanDesc)
 		DatumGetPointer(OidFunctionCall4(procedure,
 										 PointerGetDatum(relation),
 										 BoolGetDatum(scanFromEnd),
 										 UInt16GetDatum(numberOfKeys),
 										 PointerGetDatum(key)));
 
-	return scandesc;
+	/*
+	 * We want to look up the amgettuple procedure just once per scan,
+	 * not once per index_getnext call.  So do it here and save
+	 * the fmgr info result in the scan descriptor.
+	 */
+	GET_SCAN_PROCEDURE(beginscan, amgettuple);
+	fmgr_info(procedure, &scan->fn_getnext);
+
+	return scan;
 }
 
 /* ----------------
@@ -345,19 +353,9 @@ index_getnext(IndexScanDesc scan,
 
 	SCAN_CHECKS;
 
-	/*
-	 * Look up the access procedure only once per scan.
-	 */
-	if (scan->fn_getnext.fn_oid == InvalidOid)
-	{
-		RegProcedure procedure;
-
-		GET_SCAN_PROCEDURE(getnext, amgettuple);
-		fmgr_info(procedure, &scan->fn_getnext);
-	}
-
 	/*
 	 * have the am's gettuple proc do all the work.
+	 * index_beginscan already set up fn_getnext.
 	 */
 	result = (RetrieveIndexResult)
 		DatumGetPointer(FunctionCall2(&scan->fn_getnext,
diff --git a/src/backend/access/index/istrat.c b/src/backend/access/index/istrat.c
index 8cd1284972e..188f69b5719 100644
--- a/src/backend/access/index/istrat.c
+++ b/src/backend/access/index/istrat.c
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/access/index/Attic/istrat.c,v 1.50 2001/05/30 19:53:40 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/access/index/Attic/istrat.c,v 1.51 2001/06/01 02:41:35 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -458,6 +458,8 @@ RelationInvokeStrategy(Relation relation,
 
 /* ----------------
  *		OperatorRelationFillScanKeyEntry
+ *
+ * Initialize a ScanKey entry given already-opened pg_operator relation.
  * ----------------
  */
 static void
@@ -498,6 +500,7 @@ OperatorRelationFillScanKeyEntry(Relation operatorRelation,
 			 operatorObjectId);
 	}
 
+	MemSet(entry, 0, sizeof(*entry));
 	entry->sk_flags = 0;
 	entry->sk_procedure = ((Form_pg_operator) GETSTRUCT(tuple))->oprcode;
 
@@ -511,14 +514,29 @@ OperatorRelationFillScanKeyEntry(Relation operatorRelation,
 		"OperatorRelationFillScanKeyEntry: no procedure for operator %u",
 			 operatorObjectId);
 
-	fmgr_info(entry->sk_procedure, &entry->sk_func);
-	entry->sk_nargs = entry->sk_func.fn_nargs;
+	/*
+	 * Formerly we initialized entry->sk_func here, but that's a waste of
+	 * time because ScanKey entries in strategy maps are never actually
+	 * used to invoke the operator.  Furthermore, to do that we'd have to
+	 * worry about setting the proper memory context (the map is probably
+	 * not allocated in the current memory context!)
+	 */
 }
 
 
 /*
  * IndexSupportInitialize
  *		Initializes an index strategy and associated support procedures.
+ *
+ * Data is returned into *indexStrategy, *indexSupport, and *isUnique,
+ * all of which are objects allocated by the caller.
+ *
+ * The primary input keys are indexObjectId and accessMethodObjectId.
+ * The caller also passes maxStrategyNumber, maxSupportNumber, and
+ * maxAttributeNumber, since these indicate the size of the indexStrategy
+ * and indexSupport arrays it has allocated --- but in practice these
+ * numbers must always match those obtainable from the system catalog
+ * entries for the index and access method.
  */
 void
 IndexSupportInitialize(IndexStrategy indexStrategy,
@@ -578,7 +596,7 @@ IndexSupportInitialize(IndexStrategy indexStrategy,
 		if (!OidIsValid(iform->indkey[attIndex]))
 		{
 			if (attIndex == InvalidAttrNumber)
-				elog(ERROR, "IndexSupportInitialize: no pg_index tuple");
+				elog(ERROR, "IndexSupportInitialize: bogus pg_index tuple");
 			break;
 		}
 
@@ -637,6 +655,7 @@ IndexSupportInitialize(IndexStrategy indexStrategy,
 		heap_close(relation, AccessShareLock);
 	}
 
+	/* Now load the strategy information for the index operators */
 	ScanKeyEntryInitialize(&entry[0], 0,
 						   Anum_pg_amop_amopid,
 						   F_OIDEQ,
@@ -644,7 +663,8 @@ IndexSupportInitialize(IndexStrategy indexStrategy,
 
 	ScanKeyEntryInitialize(&entry[1], 0,
 						   Anum_pg_amop_amopclaid,
-						   F_OIDEQ, 0);
+						   F_OIDEQ,
+						   0);	/* will fill below */
 
 	relation = heap_openr(AccessMethodOperatorRelationName, AccessShareLock);
 	operatorRelation = heap_openr(OperatorRelationName, AccessShareLock);
@@ -670,9 +690,11 @@ IndexSupportInitialize(IndexStrategy indexStrategy,
 			Form_pg_amop aform;
 
 			aform = (Form_pg_amop) GETSTRUCT(tuple);
+			strategy = aform->amopstrategy;
+			Assert(strategy > 0 && strategy <= maxStrategyNumber);
 			OperatorRelationFillScanKeyEntry(operatorRelation,
 											 aform->amopopr,
-				   StrategyMapGetScanKeyEntry(map, aform->amopstrategy));
+				   StrategyMapGetScanKeyEntry(map, strategy));
 		}
 
 		heap_endscan(scan);
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index b6420a10e9a..9b98bd75e9e 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/catalog/index.c,v 1.152 2001/05/30 20:52:32 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/catalog/index.c,v 1.153 2001/06/01 02:41:35 tgl Exp $
  *
  *
  * INTERFACE ROUTINES
@@ -722,6 +722,9 @@ UpdateIndexPredicate(Oid indexoid, Node *oldPred, Node *predicate)
 
 /* ----------------------------------------------------------------
  *		InitIndexStrategy
+ *
+ * XXX this is essentially the same as relcache.c's
+ * IndexedAccessMethodInitialize(), and probably ought to be merged with it.
  * ----------------------------------------------------------------
  */
 void
@@ -733,18 +736,16 @@ InitIndexStrategy(int numatts,
 	RegProcedure *support;
 	uint16		amstrategies;
 	uint16		amsupport;
-	Oid			attrelid;
 	Size		strsize;
 
 	/*
 	 * get information from the index relation descriptor
 	 */
-	attrelid = indexRelation->rd_att->attrs[0]->attrelid;
 	amstrategies = indexRelation->rd_am->amstrategies;
 	amsupport = indexRelation->rd_am->amsupport;
 
 	/*
-	 * get the size of the strategy
+	 * compute the size of the strategy array
 	 */
 	strsize = AttributeNumberGetIndexStrategySize(numatts, amstrategies);
 
@@ -779,7 +780,8 @@ InitIndexStrategy(int numatts,
 
 	IndexSupportInitialize(strategy, support,
 						   &indexRelation->rd_uniqueindex,
-						   attrelid, accessMethodObjectId,
+						   RelationGetRelid(indexRelation),
+						   accessMethodObjectId,
 						   amstrategies, amsupport, numatts);
 
 	/*
diff --git a/src/backend/catalog/pg_operator.c b/src/backend/catalog/pg_operator.c
index db54ceede2f..40ee84c0186 100644
--- a/src/backend/catalog/pg_operator.c
+++ b/src/backend/catalog/pg_operator.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/catalog/pg_operator.c,v 1.58 2001/05/20 20:28:17 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/catalog/pg_operator.c,v 1.59 2001/06/01 02:41:35 tgl Exp $
  *
  * NOTES
  *	  these routines moved here from commands/define.c and somewhat cleaned up.
@@ -71,12 +71,13 @@ static void OperatorUpd(Oid baseId, Oid commId, Oid negId);
  *
  *		performs a scan on pg_operator for an operator tuple
  *		with given name and left/right type oids.
- * ----------------------------------------------------------------
+ *
  *	  pg_operator_desc	-- reldesc for pg_operator
  *	  operatorName		-- name of operator to fetch
  *	  leftObjectId		-- left data type oid of operator to fetch
  *	  rightObjectId		-- right data type oid of operator to fetch
  *	  defined			-- set TRUE if defined (not a shell)
+ * ----------------------------------------------------------------
  */
 static Oid
 OperatorGetWithOpenRelation(Relation pg_operator_desc,
@@ -88,26 +89,23 @@ OperatorGetWithOpenRelation(Relation pg_operator_desc,
 	HeapScanDesc pg_operator_scan;
 	Oid			operatorObjectId;
 	HeapTuple	tup;
-
-	static ScanKeyData opKey[3] = {
-		{0, Anum_pg_operator_oprname, F_NAMEEQ},
-		{0, Anum_pg_operator_oprleft, F_OIDEQ},
-		{0, Anum_pg_operator_oprright, F_OIDEQ},
-	};
-
-	fmgr_info(F_NAMEEQ, &opKey[0].sk_func);
-	fmgr_info(F_OIDEQ, &opKey[1].sk_func);
-	fmgr_info(F_OIDEQ, &opKey[2].sk_func);
-	opKey[0].sk_nargs = opKey[0].sk_func.fn_nargs;
-	opKey[1].sk_nargs = opKey[1].sk_func.fn_nargs;
-	opKey[2].sk_nargs = opKey[2].sk_func.fn_nargs;
+	ScanKeyData	opKey[3];
 
 	/*
 	 * form scan key
 	 */
-	opKey[0].sk_argument = PointerGetDatum(operatorName);
-	opKey[1].sk_argument = ObjectIdGetDatum(leftObjectId);
-	opKey[2].sk_argument = ObjectIdGetDatum(rightObjectId);
+	ScanKeyEntryInitialize(&opKey[0], 0x0,
+						   Anum_pg_operator_oprname,
+						   F_NAMEEQ,
+						   PointerGetDatum(operatorName));
+	ScanKeyEntryInitialize(&opKey[1], 0x0,
+						   Anum_pg_operator_oprleft,
+						   F_OIDEQ,
+						   ObjectIdGetDatum(leftObjectId));
+	ScanKeyEntryInitialize(&opKey[2], 0x0,
+						   Anum_pg_operator_oprright,
+						   F_OIDEQ,
+						   ObjectIdGetDatum(rightObjectId));
 
 	/*
 	 * begin the scan
@@ -451,7 +449,6 @@ OperatorDef(char *operatorName,
 	int			i,
 				j;
 	Relation	pg_operator_desc;
-
 	HeapScanDesc pg_operator_scan;
 	HeapTuple	tup;
 	char		nulls[Natts_pg_operator];
@@ -471,19 +468,7 @@ OperatorDef(char *operatorName,
 	int			nargs;
 	NameData	oname;
 	TupleDesc	tupDesc;
-
-	static ScanKeyData opKey[3] = {
-		{0, Anum_pg_operator_oprname, F_NAMEEQ},
-		{0, Anum_pg_operator_oprleft, F_OIDEQ},
-		{0, Anum_pg_operator_oprright, F_OIDEQ},
-	};
-
-	fmgr_info(F_NAMEEQ, &opKey[0].sk_func);
-	fmgr_info(F_OIDEQ, &opKey[1].sk_func);
-	fmgr_info(F_OIDEQ, &opKey[2].sk_func);
-	opKey[0].sk_nargs = opKey[0].sk_func.fn_nargs;
-	opKey[1].sk_nargs = opKey[1].sk_func.fn_nargs;
-	opKey[2].sk_nargs = opKey[2].sk_func.fn_nargs;
+	ScanKeyData	opKey[3];
 
 	operatorObjectId = OperatorGet(operatorName,
 								   leftTypeName,
@@ -753,13 +738,22 @@ OperatorDef(char *operatorName,
 	 */
 	if (operatorObjectId)
 	{
-		opKey[0].sk_argument = PointerGetDatum(operatorName);
-		opKey[1].sk_argument = ObjectIdGetDatum(leftTypeId);
-		opKey[2].sk_argument = ObjectIdGetDatum(rightTypeId);
-
 		/* Make sure we can see the shell even if it is new in current cmd */
 		CommandCounterIncrement();
 
+		ScanKeyEntryInitialize(&opKey[0], 0x0,
+							   Anum_pg_operator_oprname,
+							   F_NAMEEQ,
+							   PointerGetDatum(operatorName));
+		ScanKeyEntryInitialize(&opKey[1], 0x0,
+							   Anum_pg_operator_oprleft,
+							   F_OIDEQ,
+							   ObjectIdGetDatum(leftTypeId));
+		ScanKeyEntryInitialize(&opKey[2], 0x0,
+							   Anum_pg_operator_oprright,
+							   F_OIDEQ,
+							   ObjectIdGetDatum(rightTypeId));
+
 		pg_operator_scan = heap_beginscan(pg_operator_desc,
 										  0,
 										  SnapshotSelf, /* no cache? */
@@ -789,7 +783,6 @@ OperatorDef(char *operatorName,
 
 		heap_insert(pg_operator_desc, tup);
 		operatorObjectId = tup->t_data->t_oid;
-
 	}
 
 	if (RelationGetForm(pg_operator_desc)->relhasindex)
@@ -841,17 +834,11 @@ OperatorUpd(Oid baseId, Oid commId, Oid negId)
 	char		nulls[Natts_pg_operator];
 	char		replaces[Natts_pg_operator];
 	Datum		values[Natts_pg_operator];
-
-	static ScanKeyData opKey[1] = {
-		{0, ObjectIdAttributeNumber, F_OIDEQ},
-	};
-
-	fmgr_info(F_OIDEQ, &opKey[0].sk_func);
-	opKey[0].sk_nargs = opKey[0].sk_func.fn_nargs;
+	ScanKeyData	opKey[1];
 
 	for (i = 0; i < Natts_pg_operator; ++i)
 	{
-		values[i] = (Datum) NULL;
+		values[i] = (Datum) 0;
 		replaces[i] = ' ';
 		nulls[i] = ' ';
 	}
@@ -865,7 +852,10 @@ OperatorUpd(Oid baseId, Oid commId, Oid negId)
 	 */
 	CommandCounterIncrement();
 
-	opKey[0].sk_argument = ObjectIdGetDatum(commId);
+	ScanKeyEntryInitialize(&opKey[0], 0x0,
+						   ObjectIdAttributeNumber,
+						   F_OIDEQ,
+						   ObjectIdGetDatum(commId));
 
 	pg_operator_scan = heap_beginscan(pg_operator_desc,
 									  0,
@@ -993,7 +983,6 @@ OperatorUpd(Oid baseId, Oid commId, Oid negId)
 
 	heap_endscan(pg_operator_scan);
 
-
 	heap_close(pg_operator_desc, RowExclusiveLock);
 }
 
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index fbbade10331..a19f1a303bb 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.137 2001/05/27 09:59:29 petere Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.138 2001/06/01 02:41:35 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -636,6 +636,7 @@ CopyFrom(Relation rel, bool binary, bool oids, FILE *fp,
 	resultRelInfo = makeNode(ResultRelInfo);
 	resultRelInfo->ri_RangeTableIndex = 1;		/* dummy */
 	resultRelInfo->ri_RelationDesc = rel;
+	resultRelInfo->ri_TrigDesc = rel->trigdesc;
 
 	ExecOpenIndices(resultRelInfo);
 
@@ -868,12 +869,12 @@ CopyFrom(Relation rel, bool binary, bool oids, FILE *fp,
 		skip_tuple = false;
 
 		/* BEFORE ROW INSERT Triggers */
-		if (rel->trigdesc &&
-			rel->trigdesc->n_before_row[TRIGGER_EVENT_INSERT] > 0)
+		if (resultRelInfo->ri_TrigDesc &&
+			resultRelInfo->ri_TrigDesc->n_before_row[TRIGGER_EVENT_INSERT] > 0)
 		{
 			HeapTuple	newtuple;
 
-			newtuple = ExecBRInsertTriggers(estate, rel, tuple);
+			newtuple = ExecBRInsertTriggers(estate, resultRelInfo, tuple);
 
 			if (newtuple == NULL)		/* "do nothing" */
 				skip_tuple = true;
@@ -903,8 +904,8 @@ CopyFrom(Relation rel, bool binary, bool oids, FILE *fp,
 				ExecInsertIndexTuples(slot, &(tuple->t_self), estate, false);
 
 			/* AFTER ROW INSERT Triggers */
-			if (rel->trigdesc)
-				ExecARInsertTriggers(estate, rel, tuple);
+			if (resultRelInfo->ri_TrigDesc)
+				ExecARInsertTriggers(estate, resultRelInfo, tuple);
 		}
 
 		for (i = 0; i < attr_count; i++)
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 70c146530f1..013221860a2 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/trigger.c,v 1.91 2001/05/27 09:59:29 petere Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/trigger.c,v 1.92 2001/06/01 02:41:35 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -32,14 +32,19 @@
 #include "utils/syscache.h"
 
 
-static void DescribeTrigger(TriggerDesc *trigdesc, Trigger *trigger);
-static HeapTuple GetTupleForTrigger(EState *estate, ItemPointer tid,
-				   TupleTableSlot **newSlot);
-static HeapTuple ExecCallTriggerFunc(Trigger *trigger,
-					TriggerData *trigdata,
-					MemoryContext per_tuple_context);
-static void DeferredTriggerSaveEvent(Relation rel, int event,
-						 HeapTuple oldtup, HeapTuple newtup);
+static void InsertTrigger(TriggerDesc *trigdesc, Trigger *trigger, int indx);
+static HeapTuple GetTupleForTrigger(EState *estate,
+									ResultRelInfo *relinfo,
+									ItemPointer tid,
+									TupleTableSlot **newSlot);
+static HeapTuple ExecCallTriggerFunc(TriggerData *trigdata,
+									 FmgrInfo *finfo,
+									 MemoryContext per_tuple_context);
+static void DeferredTriggerSaveEvent(ResultRelInfo *relinfo, int event,
+									 HeapTuple oldtup, HeapTuple newtup);
+static void DeferredTriggerExecute(DeferredTriggerEvent event, int itemno,
+								   Relation rel, FmgrInfo *finfo,
+								   MemoryContext per_tuple_context);
 
 
 void
@@ -577,8 +582,6 @@ RelationBuildTriggers(Relation relation)
 							 DatumGetCString(DirectFunctionCall1(nameout,
 									NameGetDatum(&pg_trigger->tgname))));
 		build->tgfoid = pg_trigger->tgfoid;
-		build->tgfunc.fn_oid = InvalidOid;		/* mark FmgrInfo as
-												 * uninitialized */
 		build->tgtype = pg_trigger->tgtype;
 		build->tgenabled = pg_trigger->tgenabled;
 		build->tgisconstraint = pg_trigger->tgisconstraint;
@@ -641,21 +644,22 @@ RelationBuildTriggers(Relation relation)
 	trigdesc->triggers = triggers;
 	trigdesc->numtriggers = ntrigs;
 	for (found = 0; found < ntrigs; found++)
-		DescribeTrigger(trigdesc, &(triggers[found]));
+		InsertTrigger(trigdesc, &(triggers[found]), found);
 
 	relation->trigdesc = trigdesc;
 }
 
+/* Insert the given trigger into the appropriate index list(s) for it */
 static void
-DescribeTrigger(TriggerDesc *trigdesc, Trigger *trigger)
+InsertTrigger(TriggerDesc *trigdesc, Trigger *trigger, int indx)
 {
 	uint16	   *n;
-	Trigger  ***t,
-			 ***tp;
+	int		  **t,
+			  **tp;
 
-	if (TRIGGER_FOR_ROW(trigger->tgtype))		/* Is ROW/STATEMENT
-												 * trigger */
+	if (TRIGGER_FOR_ROW(trigger->tgtype))
 	{
+		/* ROW trigger */
 		if (TRIGGER_FOR_BEFORE(trigger->tgtype))
 		{
 			n = trigdesc->n_before_row;
@@ -668,8 +672,8 @@ DescribeTrigger(TriggerDesc *trigdesc, Trigger *trigger)
 		}
 	}
 	else
-/* STATEMENT (NI) */
 	{
+		/* STATEMENT trigger */
 		if (TRIGGER_FOR_BEFORE(trigger->tgtype))
 		{
 			n = trigdesc->n_before_statement;
@@ -686,12 +690,12 @@ DescribeTrigger(TriggerDesc *trigdesc, Trigger *trigger)
 	{
 		tp = &(t[TRIGGER_EVENT_INSERT]);
 		if (*tp == NULL)
-			*tp = (Trigger **) MemoryContextAlloc(CacheMemoryContext,
-												  sizeof(Trigger *));
+			*tp = (int *) MemoryContextAlloc(CacheMemoryContext,
+											 sizeof(int));
 		else
-			*tp = (Trigger **) repalloc(*tp, (n[TRIGGER_EVENT_INSERT] + 1) *
-										sizeof(Trigger *));
-		(*tp)[n[TRIGGER_EVENT_INSERT]] = trigger;
+			*tp = (int *) repalloc(*tp, (n[TRIGGER_EVENT_INSERT] + 1) *
+								   sizeof(int));
+		(*tp)[n[TRIGGER_EVENT_INSERT]] = indx;
 		(n[TRIGGER_EVENT_INSERT])++;
 	}
 
@@ -699,12 +703,12 @@ DescribeTrigger(TriggerDesc *trigdesc, Trigger *trigger)
 	{
 		tp = &(t[TRIGGER_EVENT_DELETE]);
 		if (*tp == NULL)
-			*tp = (Trigger **) MemoryContextAlloc(CacheMemoryContext,
-												  sizeof(Trigger *));
+			*tp = (int *) MemoryContextAlloc(CacheMemoryContext,
+											 sizeof(int));
 		else
-			*tp = (Trigger **) repalloc(*tp, (n[TRIGGER_EVENT_DELETE] + 1) *
-										sizeof(Trigger *));
-		(*tp)[n[TRIGGER_EVENT_DELETE]] = trigger;
+			*tp = (int *) repalloc(*tp, (n[TRIGGER_EVENT_DELETE] + 1) *
+								   sizeof(int));
+		(*tp)[n[TRIGGER_EVENT_DELETE]] = indx;
 		(n[TRIGGER_EVENT_DELETE])++;
 	}
 
@@ -712,21 +716,20 @@ DescribeTrigger(TriggerDesc *trigdesc, Trigger *trigger)
 	{
 		tp = &(t[TRIGGER_EVENT_UPDATE]);
 		if (*tp == NULL)
-			*tp = (Trigger **) MemoryContextAlloc(CacheMemoryContext,
-												  sizeof(Trigger *));
+			*tp = (int *) MemoryContextAlloc(CacheMemoryContext,
+											 sizeof(int));
 		else
-			*tp = (Trigger **) repalloc(*tp, (n[TRIGGER_EVENT_UPDATE] + 1) *
-										sizeof(Trigger *));
-		(*tp)[n[TRIGGER_EVENT_UPDATE]] = trigger;
+			*tp = (int *) repalloc(*tp, (n[TRIGGER_EVENT_UPDATE] + 1) *
+								   sizeof(int));
+		(*tp)[n[TRIGGER_EVENT_UPDATE]] = indx;
 		(n[TRIGGER_EVENT_UPDATE])++;
 	}
-
 }
 
 void
 FreeTriggerDesc(TriggerDesc *trigdesc)
 {
-	Trigger  ***t;
+	int		  **t;
 	Trigger    *trigger;
 	int			i;
 
@@ -734,19 +737,19 @@ FreeTriggerDesc(TriggerDesc *trigdesc)
 		return;
 
 	t = trigdesc->tg_before_statement;
-	for (i = 0; i < 4; i++)
+	for (i = 0; i < TRIGGER_NUM_EVENT_CLASSES; i++)
 		if (t[i] != NULL)
 			pfree(t[i]);
 	t = trigdesc->tg_before_row;
-	for (i = 0; i < 4; i++)
+	for (i = 0; i < TRIGGER_NUM_EVENT_CLASSES; i++)
 		if (t[i] != NULL)
 			pfree(t[i]);
 	t = trigdesc->tg_after_row;
-	for (i = 0; i < 4; i++)
+	for (i = 0; i < TRIGGER_NUM_EVENT_CLASSES; i++)
 		if (t[i] != NULL)
 			pfree(t[i]);
 	t = trigdesc->tg_after_statement;
-	for (i = 0; i < 4; i++)
+	for (i = 0; i < TRIGGER_NUM_EVENT_CLASSES; i++)
 		if (t[i] != NULL)
 			pfree(t[i]);
 
@@ -806,7 +809,6 @@ equalTriggerDescs(TriggerDesc *trigdesc1, TriggerDesc *trigdesc2)
 				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)
@@ -832,9 +834,18 @@ equalTriggerDescs(TriggerDesc *trigdesc1, TriggerDesc *trigdesc2)
 	return true;
 }
 
+/*
+ * Call a trigger function.
+ *
+ *		trigdata: trigger descriptor.
+ *		finfo: possibly-cached call info for the function.
+ *		per_tuple_context: memory context to execute the function in.
+ *
+ * Returns the tuple (or NULL) as returned by the function.
+ */
 static HeapTuple
-ExecCallTriggerFunc(Trigger *trigger,
-					TriggerData *trigdata,
+ExecCallTriggerFunc(TriggerData *trigdata,
+					FmgrInfo *finfo,
 					MemoryContext per_tuple_context)
 {
 	FunctionCallInfoData fcinfo;
@@ -842,11 +853,13 @@ ExecCallTriggerFunc(Trigger *trigger,
 	MemoryContext oldContext;
 
 	/*
-	 * Fmgr lookup info is cached in the Trigger structure, so that we
-	 * need not repeat the lookup on every call.
+	 * We cache fmgr lookup info, to avoid making the lookup
+	 * again on each call.
 	 */
-	if (trigger->tgfunc.fn_oid == InvalidOid)
-		fmgr_info(trigger->tgfoid, &trigger->tgfunc);
+	if (finfo->fn_oid == InvalidOid)
+		fmgr_info(trigdata->tg_trigger->tgfoid, finfo);
+
+	Assert(finfo->fn_oid == trigdata->tg_trigger->tgfoid);
 
 	/*
 	 * Do the function evaluation in the per-tuple memory context, so that
@@ -861,7 +874,7 @@ ExecCallTriggerFunc(Trigger *trigger,
 	 */
 	MemSet(&fcinfo, 0, sizeof(fcinfo));
 
-	fcinfo.flinfo = &trigger->tgfunc;
+	fcinfo.flinfo = finfo;
 	fcinfo.context = (Node *) trigdata;
 
 	result = FunctionCallInvoke(&fcinfo);
@@ -880,26 +893,40 @@ ExecCallTriggerFunc(Trigger *trigger,
 }
 
 HeapTuple
-ExecBRInsertTriggers(EState *estate, Relation rel, HeapTuple trigtuple)
+ExecBRInsertTriggers(EState *estate, ResultRelInfo *relinfo,
+					 HeapTuple trigtuple)
 {
-	int			ntrigs = rel->trigdesc->n_before_row[TRIGGER_EVENT_INSERT];
-	Trigger   **trigger = rel->trigdesc->tg_before_row[TRIGGER_EVENT_INSERT];
+	TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
+	int			ntrigs = trigdesc->n_before_row[TRIGGER_EVENT_INSERT];
+	int		   *tgindx = trigdesc->tg_before_row[TRIGGER_EVENT_INSERT];
 	HeapTuple	newtuple = trigtuple;
 	HeapTuple	oldtuple;
 	TriggerData LocTriggerData;
 	int			i;
 
+	/* Allocate cache space for fmgr lookup info, if not done yet */
+	if (relinfo->ri_TrigFunctions == NULL)
+	{
+		relinfo->ri_TrigFunctions = (FmgrInfo *)
+			palloc(trigdesc->numtriggers * sizeof(FmgrInfo));
+		MemSet(relinfo->ri_TrigFunctions, 0,
+			   trigdesc->numtriggers * sizeof(FmgrInfo));
+	}
+
 	LocTriggerData.type = T_TriggerData;
 	LocTriggerData.tg_event = TRIGGER_EVENT_INSERT | TRIGGER_EVENT_ROW | TRIGGER_EVENT_BEFORE;
-	LocTriggerData.tg_relation = rel;
+	LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
 	LocTriggerData.tg_newtuple = NULL;
 	for (i = 0; i < ntrigs; i++)
 	{
-		if (!trigger[i]->tgenabled)
+		Trigger *trigger = &trigdesc->triggers[tgindx[i]];
+
+		if (!trigger->tgenabled)
 			continue;
 		LocTriggerData.tg_trigtuple = oldtuple = newtuple;
-		LocTriggerData.tg_trigger = trigger[i];
-		newtuple = ExecCallTriggerFunc(trigger[i], &LocTriggerData,
+		LocTriggerData.tg_trigger = trigger;
+		newtuple = ExecCallTriggerFunc(&LocTriggerData,
+									   relinfo->ri_TrigFunctions + tgindx[i],
 									   GetPerTupleMemoryContext(estate));
 		if (oldtuple != newtuple && oldtuple != trigtuple)
 			heap_freetuple(oldtuple);
@@ -910,42 +937,59 @@ ExecBRInsertTriggers(EState *estate, Relation rel, HeapTuple trigtuple)
 }
 
 void
-ExecARInsertTriggers(EState *estate, Relation rel, HeapTuple trigtuple)
+ExecARInsertTriggers(EState *estate, ResultRelInfo *relinfo,
+					 HeapTuple trigtuple)
 {
+	TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
+
 	/* Must save info if there are any deferred triggers on this rel */
-	if (rel->trigdesc->n_after_row[TRIGGER_EVENT_INSERT] > 0 ||
-		rel->trigdesc->n_after_row[TRIGGER_EVENT_UPDATE] > 0 ||
-		rel->trigdesc->n_after_row[TRIGGER_EVENT_DELETE] > 0)
-		DeferredTriggerSaveEvent(rel, TRIGGER_EVENT_INSERT, NULL, trigtuple);
+	if (trigdesc->n_after_row[TRIGGER_EVENT_INSERT] > 0 ||
+		trigdesc->n_after_row[TRIGGER_EVENT_UPDATE] > 0 ||
+		trigdesc->n_after_row[TRIGGER_EVENT_DELETE] > 0)
+		DeferredTriggerSaveEvent(relinfo, TRIGGER_EVENT_INSERT,
+								 NULL, trigtuple);
 }
 
 bool
-ExecBRDeleteTriggers(EState *estate, ItemPointer tupleid)
+ExecBRDeleteTriggers(EState *estate, ResultRelInfo *relinfo,
+					 ItemPointer tupleid)
 {
-	Relation	rel = estate->es_result_relation_info->ri_RelationDesc;
-	int			ntrigs = rel->trigdesc->n_before_row[TRIGGER_EVENT_DELETE];
-	Trigger   **trigger = rel->trigdesc->tg_before_row[TRIGGER_EVENT_DELETE];
+	TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
+	int			ntrigs = trigdesc->n_before_row[TRIGGER_EVENT_DELETE];
+	int		   *tgindx = trigdesc->tg_before_row[TRIGGER_EVENT_DELETE];
 	TriggerData LocTriggerData;
 	HeapTuple	trigtuple;
 	HeapTuple	newtuple = NULL;
 	TupleTableSlot *newSlot;
 	int			i;
 
-	trigtuple = GetTupleForTrigger(estate, tupleid, &newSlot);
+	trigtuple = GetTupleForTrigger(estate, relinfo, tupleid, &newSlot);
 	if (trigtuple == NULL)
 		return false;
 
+	/* Allocate cache space for fmgr lookup info, if not done yet */
+	if (relinfo->ri_TrigFunctions == NULL)
+	{
+		relinfo->ri_TrigFunctions = (FmgrInfo *)
+			palloc(trigdesc->numtriggers * sizeof(FmgrInfo));
+		MemSet(relinfo->ri_TrigFunctions, 0,
+			   trigdesc->numtriggers * sizeof(FmgrInfo));
+	}
+
 	LocTriggerData.type = T_TriggerData;
 	LocTriggerData.tg_event = TRIGGER_EVENT_DELETE | TRIGGER_EVENT_ROW | TRIGGER_EVENT_BEFORE;
-	LocTriggerData.tg_relation = rel;
+	LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
 	LocTriggerData.tg_newtuple = NULL;
 	for (i = 0; i < ntrigs; i++)
 	{
-		if (!trigger[i]->tgenabled)
+		Trigger *trigger = &trigdesc->triggers[tgindx[i]];
+
+		if (!trigger->tgenabled)
 			continue;
 		LocTriggerData.tg_trigtuple = trigtuple;
-		LocTriggerData.tg_trigger = trigger[i];
-		newtuple = ExecCallTriggerFunc(trigger[i], &LocTriggerData,
+		LocTriggerData.tg_trigger = trigger;
+		newtuple = ExecCallTriggerFunc(&LocTriggerData,
+									   relinfo->ri_TrigFunctions + tgindx[i],
 									   GetPerTupleMemoryContext(estate));
 		if (newtuple == NULL)
 			break;
@@ -958,27 +1002,31 @@ ExecBRDeleteTriggers(EState *estate, ItemPointer tupleid)
 }
 
 void
-ExecARDeleteTriggers(EState *estate, ItemPointer tupleid)
+ExecARDeleteTriggers(EState *estate, ResultRelInfo *relinfo,
+					 ItemPointer tupleid)
 {
-	Relation	rel = estate->es_result_relation_info->ri_RelationDesc;
+	TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
 
 	/* Must save info if there are upd/del deferred triggers on this rel */
-	if (rel->trigdesc->n_after_row[TRIGGER_EVENT_UPDATE] > 0 ||
-		rel->trigdesc->n_after_row[TRIGGER_EVENT_DELETE] > 0)
+	if (trigdesc->n_after_row[TRIGGER_EVENT_UPDATE] > 0 ||
+		trigdesc->n_after_row[TRIGGER_EVENT_DELETE] > 0)
 	{
-		HeapTuple	trigtuple = GetTupleForTrigger(estate, tupleid, NULL);
+		HeapTuple	trigtuple = GetTupleForTrigger(estate, relinfo,
+												   tupleid, NULL);
 
-		DeferredTriggerSaveEvent(rel, TRIGGER_EVENT_DELETE, trigtuple, NULL);
+		DeferredTriggerSaveEvent(relinfo, TRIGGER_EVENT_DELETE,
+								 trigtuple, NULL);
 		heap_freetuple(trigtuple);
 	}
 }
 
 HeapTuple
-ExecBRUpdateTriggers(EState *estate, ItemPointer tupleid, HeapTuple newtuple)
+ExecBRUpdateTriggers(EState *estate, ResultRelInfo *relinfo,
+					 ItemPointer tupleid, HeapTuple newtuple)
 {
-	Relation	rel = estate->es_result_relation_info->ri_RelationDesc;
-	int			ntrigs = rel->trigdesc->n_before_row[TRIGGER_EVENT_UPDATE];
-	Trigger   **trigger = rel->trigdesc->tg_before_row[TRIGGER_EVENT_UPDATE];
+	TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
+	int			ntrigs = trigdesc->n_before_row[TRIGGER_EVENT_UPDATE];
+	int		   *tgindx = trigdesc->tg_before_row[TRIGGER_EVENT_UPDATE];
 	TriggerData LocTriggerData;
 	HeapTuple	trigtuple;
 	HeapTuple	oldtuple;
@@ -986,7 +1034,7 @@ ExecBRUpdateTriggers(EState *estate, ItemPointer tupleid, HeapTuple newtuple)
 	TupleTableSlot *newSlot;
 	int			i;
 
-	trigtuple = GetTupleForTrigger(estate, tupleid, &newSlot);
+	trigtuple = GetTupleForTrigger(estate, relinfo, tupleid, &newSlot);
 	if (trigtuple == NULL)
 		return NULL;
 
@@ -997,17 +1045,29 @@ ExecBRUpdateTriggers(EState *estate, ItemPointer tupleid, HeapTuple newtuple)
 	if (newSlot != NULL)
 		intuple = newtuple = ExecRemoveJunk(estate->es_junkFilter, newSlot);
 
+	/* Allocate cache space for fmgr lookup info, if not done yet */
+	if (relinfo->ri_TrigFunctions == NULL)
+	{
+		relinfo->ri_TrigFunctions = (FmgrInfo *)
+			palloc(trigdesc->numtriggers * sizeof(FmgrInfo));
+		MemSet(relinfo->ri_TrigFunctions, 0,
+			   trigdesc->numtriggers * sizeof(FmgrInfo));
+	}
+
 	LocTriggerData.type = T_TriggerData;
 	LocTriggerData.tg_event = TRIGGER_EVENT_UPDATE | TRIGGER_EVENT_ROW | TRIGGER_EVENT_BEFORE;
-	LocTriggerData.tg_relation = rel;
+	LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
 	for (i = 0; i < ntrigs; i++)
 	{
-		if (!trigger[i]->tgenabled)
+		Trigger *trigger = &trigdesc->triggers[tgindx[i]];
+
+		if (!trigger->tgenabled)
 			continue;
 		LocTriggerData.tg_trigtuple = trigtuple;
 		LocTriggerData.tg_newtuple = oldtuple = newtuple;
-		LocTriggerData.tg_trigger = trigger[i];
-		newtuple = ExecCallTriggerFunc(trigger[i], &LocTriggerData,
+		LocTriggerData.tg_trigger = trigger;
+		newtuple = ExecCallTriggerFunc(&LocTriggerData,
+									   relinfo->ri_TrigFunctions + tgindx[i],
 									   GetPerTupleMemoryContext(estate));
 		if (oldtuple != newtuple && oldtuple != intuple)
 			heap_freetuple(oldtuple);
@@ -1019,26 +1079,30 @@ ExecBRUpdateTriggers(EState *estate, ItemPointer tupleid, HeapTuple newtuple)
 }
 
 void
-ExecARUpdateTriggers(EState *estate, ItemPointer tupleid, HeapTuple newtuple)
+ExecARUpdateTriggers(EState *estate, ResultRelInfo *relinfo,
+					 ItemPointer tupleid, HeapTuple newtuple)
 {
-	Relation	rel = estate->es_result_relation_info->ri_RelationDesc;
+	TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
 
 	/* Must save info if there are upd/del deferred triggers on this rel */
-	if (rel->trigdesc->n_after_row[TRIGGER_EVENT_UPDATE] > 0 ||
-		rel->trigdesc->n_after_row[TRIGGER_EVENT_DELETE] > 0)
+	if (trigdesc->n_after_row[TRIGGER_EVENT_UPDATE] > 0 ||
+		trigdesc->n_after_row[TRIGGER_EVENT_DELETE] > 0)
 	{
-		HeapTuple	trigtuple = GetTupleForTrigger(estate, tupleid, NULL);
+		HeapTuple	trigtuple = GetTupleForTrigger(estate, relinfo,
+												   tupleid, NULL);
 
-		DeferredTriggerSaveEvent(rel, TRIGGER_EVENT_UPDATE, trigtuple, newtuple);
+		DeferredTriggerSaveEvent(relinfo, TRIGGER_EVENT_UPDATE,
+								 trigtuple, newtuple);
 		heap_freetuple(trigtuple);
 	}
 }
 
 
 static HeapTuple
-GetTupleForTrigger(EState *estate, ItemPointer tid, TupleTableSlot **newSlot)
+GetTupleForTrigger(EState *estate, ResultRelInfo *relinfo,
+				   ItemPointer tid, TupleTableSlot **newSlot)
 {
-	Relation	relation = estate->es_result_relation_info->ri_RelationDesc;
+	Relation	relation = relinfo->ri_RelationDesc;
 	HeapTupleData tuple;
 	HeapTuple	result;
 	Buffer		buffer;
@@ -1070,8 +1134,8 @@ ltrmark:;
 				else if (!(ItemPointerEquals(&(tuple.t_self), tid)))
 				{
 					TupleTableSlot *epqslot = EvalPlanQual(estate,
-					 estate->es_result_relation_info->ri_RangeTableIndex,
-														&(tuple.t_self));
+														   relinfo->ri_RangeTableIndex,
+														   &(tuple.t_self));
 
 					if (!(TupIsNull(epqslot)))
 					{
@@ -1293,35 +1357,46 @@ deferredTriggerGetPreviousEvent(Oid relid, ItemPointer ctid)
 
 
 /* ----------
- * deferredTriggerExecute()
+ * DeferredTriggerExecute()
  *
  *	Fetch the required tuples back from the heap and fire one
  *	single trigger function.
+ *
+ *	Frequently, this will be fired many times in a row for triggers of
+ *	a single relation.  Therefore, we cache the open relation and provide
+ *	fmgr lookup cache space at the caller level.
+ *
+ *	event: event currently being fired.
+ *	itemno: item within event currently being fired.
+ *	rel: open relation for event.
+ *	finfo: array of fmgr lookup cache entries (one per trigger of relation).
+ *	per_tuple_context: memory context to call trigger function in.
  * ----------
  */
 static void
-deferredTriggerExecute(DeferredTriggerEvent event, int itemno,
+DeferredTriggerExecute(DeferredTriggerEvent event, int itemno,
+					   Relation rel, FmgrInfo *finfo,
 					   MemoryContext per_tuple_context)
 {
-	Relation	rel;
+	Oid			tgoid = event->dte_item[itemno].dti_tgoid;
+	TriggerDesc *trigdesc = rel->trigdesc;
 	TriggerData LocTriggerData;
 	HeapTupleData oldtuple;
 	HeapTupleData newtuple;
 	HeapTuple	rettuple;
 	Buffer		oldbuffer;
 	Buffer		newbuffer;
+	int			tgindx;
 
 	/*
-	 * Open the heap and fetch the required OLD and NEW tuples.
+	 * Fetch the required OLD and NEW tuples.
 	 */
-	rel = heap_open(event->dte_relid, NoLock);
-
 	if (ItemPointerIsValid(&(event->dte_oldctid)))
 	{
 		ItemPointerCopy(&(event->dte_oldctid), &(oldtuple.t_self));
 		heap_fetch(rel, SnapshotAny, &oldtuple, &oldbuffer);
 		if (!oldtuple.t_data)
-			elog(ERROR, "deferredTriggerExecute: failed to fetch old tuple");
+			elog(ERROR, "DeferredTriggerExecute: failed to fetch old tuple");
 	}
 
 	if (ItemPointerIsValid(&(event->dte_newctid)))
@@ -1329,7 +1404,7 @@ deferredTriggerExecute(DeferredTriggerEvent event, int itemno,
 		ItemPointerCopy(&(event->dte_newctid), &(newtuple.t_self));
 		heap_fetch(rel, SnapshotAny, &newtuple, &newbuffer);
 		if (!newtuple.t_data)
-			elog(ERROR, "deferredTriggerExecute: failed to fetch new tuple");
+			elog(ERROR, "DeferredTriggerExecute: failed to fetch new tuple");
 	}
 
 	/*
@@ -1340,27 +1415,33 @@ deferredTriggerExecute(DeferredTriggerEvent event, int itemno,
 		TRIGGER_EVENT_ROW;
 	LocTriggerData.tg_relation = rel;
 
+	LocTriggerData.tg_trigger = NULL;
+	for (tgindx = 0; tgindx < trigdesc->numtriggers; tgindx++)
+	{
+		if (trigdesc->triggers[tgindx].tgoid == tgoid)
+		{
+			LocTriggerData.tg_trigger = &(trigdesc->triggers[tgindx]);
+			break;
+		}
+	}
+	if (LocTriggerData.tg_trigger == NULL)
+		elog(ERROR, "DeferredTriggerExecute: can't find trigger %u", tgoid);
+
 	switch (event->dte_event & TRIGGER_EVENT_OPMASK)
 	{
 		case TRIGGER_EVENT_INSERT:
 			LocTriggerData.tg_trigtuple = &newtuple;
 			LocTriggerData.tg_newtuple = NULL;
-			LocTriggerData.tg_trigger =
-				rel->trigdesc->tg_after_row[TRIGGER_EVENT_INSERT][itemno];
 			break;
 
 		case TRIGGER_EVENT_UPDATE:
 			LocTriggerData.tg_trigtuple = &oldtuple;
 			LocTriggerData.tg_newtuple = &newtuple;
-			LocTriggerData.tg_trigger =
-				rel->trigdesc->tg_after_row[TRIGGER_EVENT_UPDATE][itemno];
 			break;
 
 		case TRIGGER_EVENT_DELETE:
 			LocTriggerData.tg_trigtuple = &oldtuple;
 			LocTriggerData.tg_newtuple = NULL;
-			LocTriggerData.tg_trigger =
-				rel->trigdesc->tg_after_row[TRIGGER_EVENT_DELETE][itemno];
 			break;
 	}
 
@@ -1368,8 +1449,8 @@ deferredTriggerExecute(DeferredTriggerEvent event, int itemno,
 	 * Call the trigger and throw away an eventually returned updated
 	 * tuple.
 	 */
-	rettuple = ExecCallTriggerFunc(LocTriggerData.tg_trigger,
-								   &LocTriggerData,
+	rettuple = ExecCallTriggerFunc(&LocTriggerData,
+								   finfo + tgindx,
 								   per_tuple_context);
 	if (rettuple != NULL && rettuple != &oldtuple && rettuple != &newtuple)
 		heap_freetuple(rettuple);
@@ -1381,14 +1462,12 @@ deferredTriggerExecute(DeferredTriggerEvent event, int itemno,
 	ReferentialIntegritySnapshotOverride = false;
 
 	/*
-	 * Release buffers and close the relation
+	 * Release buffers
 	 */
 	if (ItemPointerIsValid(&(event->dte_oldctid)))
 		ReleaseBuffer(oldbuffer);
 	if (ItemPointerIsValid(&(event->dte_newctid)))
 		ReleaseBuffer(newbuffer);
-
-	heap_close(rel, NoLock);
 }
 
 
@@ -1403,9 +1482,9 @@ static void
 deferredTriggerInvokeEvents(bool immediate_only)
 {
 	DeferredTriggerEvent event;
-	int			still_deferred_ones;
-	int			i;
 	MemoryContext per_tuple_context;
+	Relation	rel = NULL;
+	FmgrInfo   *finfo = NULL;
 
 	/*
 	 * For now we process all events - to speedup transaction blocks we
@@ -1426,6 +1505,8 @@ deferredTriggerInvokeEvents(bool immediate_only)
 
 	for (event = deftrig_events; event != NULL; event = event->dte_next)
 	{
+		bool		still_deferred_ones;
+		int			i;
 
 		/*
 		 * Check if event is completely done.
@@ -1458,9 +1539,32 @@ deferredTriggerInvokeEvents(bool immediate_only)
 			}
 
 			/*
-			 * So let's fire it...
+			 * So let's fire it... but first, open the correct relation
+			 * if this is not the same relation as before.
 			 */
-			deferredTriggerExecute(event, i, per_tuple_context);
+			if (rel == NULL || rel->rd_id != event->dte_relid)
+			{
+				if (rel)
+					heap_close(rel, NoLock);
+				if (finfo)
+					pfree(finfo);
+				/*
+				 * We assume that an appropriate lock is still held by the
+				 * executor, so grab no new lock here.
+				 */
+				rel = heap_open(event->dte_relid, NoLock);
+				/*
+				 * Allocate space to cache fmgr lookup info for triggers
+				 * of this relation.
+				 */
+				finfo = (FmgrInfo *)
+					palloc(rel->trigdesc->numtriggers * sizeof(FmgrInfo));
+				MemSet(finfo, 0,
+					   rel->trigdesc->numtriggers * sizeof(FmgrInfo));
+			}
+
+			DeferredTriggerExecute(event, i, rel, finfo, per_tuple_context);
+
 			event->dte_item[i].dti_state |= TRIGGER_DEFERRED_DONE;
 		}
 
@@ -1471,6 +1575,10 @@ deferredTriggerInvokeEvents(bool immediate_only)
 			event->dte_event |= TRIGGER_DEFERRED_DONE;
 	}
 
+	if (rel)
+		heap_close(rel, NoLock);
+	if (finfo)
+		pfree(finfo);
 	MemoryContextDelete(per_tuple_context);
 }
 
@@ -1892,16 +2000,18 @@ DeferredTriggerSetState(ConstraintsSetStmt *stmt)
  * ----------
  */
 static void
-DeferredTriggerSaveEvent(Relation rel, int event,
+DeferredTriggerSaveEvent(ResultRelInfo *relinfo, int event,
 						 HeapTuple oldtup, HeapTuple newtup)
 {
+	Relation	rel = relinfo->ri_RelationDesc;
+	TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
 	MemoryContext oldcxt;
 	DeferredTriggerEvent new_event;
 	DeferredTriggerEvent prev_event;
 	int			new_size;
 	int			i;
 	int			ntriggers;
-	Trigger   **triggers;
+	int		   *tgindx;
 	ItemPointerData oldctid;
 	ItemPointerData newctid;
 	TriggerData LocTriggerData;
@@ -1927,8 +2037,8 @@ DeferredTriggerSaveEvent(Relation rel, int event,
 	 */
 	oldcxt = MemoryContextSwitchTo(deftrig_cxt);
 
-	ntriggers = rel->trigdesc->n_after_row[event];
-	triggers = rel->trigdesc->tg_after_row[event];
+	ntriggers = trigdesc->n_after_row[event];
+	tgindx = trigdesc->tg_after_row[event];
 	new_size = offsetof(DeferredTriggerEventData, dte_item[0]) +
 		ntriggers * sizeof(DeferredTriggerEventItem);
 
@@ -1941,13 +2051,15 @@ DeferredTriggerSaveEvent(Relation rel, int event,
 	new_event->dte_n_items = ntriggers;
 	for (i = 0; i < ntriggers; i++)
 	{
-		new_event->dte_item[i].dti_tgoid = triggers[i]->tgoid;
+		Trigger *trigger = &trigdesc->triggers[tgindx[i]];
+
+		new_event->dte_item[i].dti_tgoid = trigger->tgoid;
 		new_event->dte_item[i].dti_state =
-			((triggers[i]->tgdeferrable) ?
+			((trigger->tgdeferrable) ?
 			 TRIGGER_DEFERRED_DEFERRABLE : 0) |
-			((triggers[i]->tginitdeferred) ?
+			((trigger->tginitdeferred) ?
 			 TRIGGER_DEFERRED_INITDEFERRED : 0) |
-			((rel->trigdesc->n_before_row[event] > 0) ?
+			((trigdesc->n_before_row[event] > 0) ?
 			 TRIGGER_DEFERRED_HAS_BEFORE : 0);
 	}
 
@@ -1978,13 +2090,14 @@ DeferredTriggerSaveEvent(Relation rel, int event,
 			 */
 			for (i = 0; i < ntriggers; i++)
 			{
+				Trigger	   *trigger = &trigdesc->triggers[tgindx[i]];
 				bool		is_ri_trigger;
 				bool		key_unchanged;
 
 				/*
 				 * We are interested in RI_FKEY triggers only.
 				 */
-				switch (triggers[i]->tgfoid)
+				switch (trigger->tgfoid)
 				{
 					case F_RI_FKEY_NOACTION_UPD:
 					case F_RI_FKEY_CASCADE_UPD:
@@ -2006,7 +2119,7 @@ DeferredTriggerSaveEvent(Relation rel, int event,
 				LocTriggerData.tg_relation = rel;
 				LocTriggerData.tg_trigtuple = oldtup;
 				LocTriggerData.tg_newtuple = newtup;
-				LocTriggerData.tg_trigger = triggers[i];
+				LocTriggerData.tg_trigger = trigger;
 
 				key_unchanged = RI_FKey_keyequal_upd(&LocTriggerData);
 
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 3ba9b9136f5..38c34f03be7 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -27,7 +27,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.142 2001/05/27 20:48:51 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.143 2001/06/01 02:41:35 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -830,6 +830,8 @@ initResultRelInfo(ResultRelInfo *resultRelInfo,
 	resultRelInfo->ri_NumIndices = 0;
 	resultRelInfo->ri_IndexRelationDescs = NULL;
 	resultRelInfo->ri_IndexRelationInfo = NULL;
+	resultRelInfo->ri_TrigDesc = resultRelationDesc->trigdesc;
+	resultRelInfo->ri_TrigFunctions = NULL;
 	resultRelInfo->ri_ConstraintExprs = NULL;
 	resultRelInfo->ri_junkFilter = NULL;
 
@@ -1232,12 +1234,12 @@ ExecAppend(TupleTableSlot *slot,
 	resultRelationDesc = resultRelInfo->ri_RelationDesc;
 
 	/* BEFORE ROW INSERT Triggers */
-	if (resultRelationDesc->trigdesc &&
-	resultRelationDesc->trigdesc->n_before_row[TRIGGER_EVENT_INSERT] > 0)
+	if (resultRelInfo->ri_TrigDesc &&
+		resultRelInfo->ri_TrigDesc->n_before_row[TRIGGER_EVENT_INSERT] > 0)
 	{
 		HeapTuple	newtuple;
 
-		newtuple = ExecBRInsertTriggers(estate, resultRelationDesc, tuple);
+		newtuple = ExecBRInsertTriggers(estate, resultRelInfo, tuple);
 
 		if (newtuple == NULL)	/* "do nothing" */
 			return;
@@ -1283,8 +1285,8 @@ ExecAppend(TupleTableSlot *slot,
 		ExecInsertIndexTuples(slot, &(tuple->t_self), estate, false);
 
 	/* AFTER ROW INSERT Triggers */
-	if (resultRelationDesc->trigdesc)
-		ExecARInsertTriggers(estate, resultRelationDesc, tuple);
+	if (resultRelInfo->ri_TrigDesc)
+		ExecARInsertTriggers(estate, resultRelInfo, tuple);
 }
 
 /* ----------------------------------------------------------------
@@ -1311,12 +1313,12 @@ ExecDelete(TupleTableSlot *slot,
 	resultRelationDesc = resultRelInfo->ri_RelationDesc;
 
 	/* BEFORE ROW DELETE Triggers */
-	if (resultRelationDesc->trigdesc &&
-	resultRelationDesc->trigdesc->n_before_row[TRIGGER_EVENT_DELETE] > 0)
+	if (resultRelInfo->ri_TrigDesc &&
+		resultRelInfo->ri_TrigDesc->n_before_row[TRIGGER_EVENT_DELETE] > 0)
 	{
 		bool		dodelete;
 
-		dodelete = ExecBRDeleteTriggers(estate, tupleid);
+		dodelete = ExecBRDeleteTriggers(estate, resultRelInfo, tupleid);
 
 		if (!dodelete)			/* "do nothing" */
 			return;
@@ -1370,8 +1372,8 @@ ldelete:;
 	 */
 
 	/* AFTER ROW DELETE Triggers */
-	if (resultRelationDesc->trigdesc)
-		ExecARDeleteTriggers(estate, tupleid);
+	if (resultRelInfo->ri_TrigDesc)
+		ExecARDeleteTriggers(estate, resultRelInfo, tupleid);
 }
 
 /* ----------------------------------------------------------------
@@ -1418,12 +1420,13 @@ ExecReplace(TupleTableSlot *slot,
 	resultRelationDesc = resultRelInfo->ri_RelationDesc;
 
 	/* BEFORE ROW UPDATE Triggers */
-	if (resultRelationDesc->trigdesc &&
-	resultRelationDesc->trigdesc->n_before_row[TRIGGER_EVENT_UPDATE] > 0)
+	if (resultRelInfo->ri_TrigDesc &&
+		resultRelInfo->ri_TrigDesc->n_before_row[TRIGGER_EVENT_UPDATE] > 0)
 	{
 		HeapTuple	newtuple;
 
-		newtuple = ExecBRUpdateTriggers(estate, tupleid, tuple);
+		newtuple = ExecBRUpdateTriggers(estate, resultRelInfo,
+										tupleid, tuple);
 
 		if (newtuple == NULL)	/* "do nothing" */
 			return;
@@ -1519,8 +1522,8 @@ lreplace:;
 		ExecInsertIndexTuples(slot, &(tuple->t_self), estate, true);
 
 	/* AFTER ROW UPDATE Triggers */
-	if (resultRelationDesc->trigdesc)
-		ExecARUpdateTriggers(estate, tupleid, tuple);
+	if (resultRelInfo->ri_TrigDesc)
+		ExecARUpdateTriggers(estate, resultRelInfo, tupleid, tuple);
 }
 
 static char *
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index f843f2bb166..cd54ff76657 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.77 2001/03/22 03:59:55 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/cache/catcache.c,v 1.78 2001/06/01 02:41:36 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -205,7 +205,6 @@ CatalogCacheInitializeCache(CatCache *cache)
 	/*
 	 * switch to the cache context so our allocations do not vanish at the
 	 * end of a transaction
-	 *
 	 */
 	if (!CacheMemoryContext)
 		CreateCacheMemoryContext();
@@ -214,13 +213,11 @@ CatalogCacheInitializeCache(CatCache *cache)
 
 	/*
 	 * copy the relcache's tuple descriptor to permanent cache storage
-	 *
 	 */
 	tupdesc = CreateTupleDescCopyConstr(RelationGetDescr(relation));
 
 	/*
 	 * return to the caller's memory context and close the rel
-	 *
 	 */
 	MemoryContextSwitchTo(oldcxt);
 
@@ -231,7 +228,6 @@ CatalogCacheInitializeCache(CatCache *cache)
 
 	/*
 	 * initialize cache's key information
-	 *
 	 */
 	for (i = 0; i < cache->cc_nkeys; ++i)
 	{
@@ -255,9 +251,23 @@ CatalogCacheInitializeCache(CatCache *cache)
 		 */
 		cache->cc_skey[i].sk_procedure = EQPROC(keytype);
 
+		/*
+		 * Note: to avoid any possible leakage of scan temporary data into
+		 * the cache context, we do not switch into CacheMemoryContext while
+		 * calling fmgr_info here.  Instead set fn_mcxt on return.  This
+		 * would fail to work correctly if fmgr_info allocated any subsidiary
+		 * data structures to attach to the FmgrInfo record; but it doesn't
+		 * do so for built-in functions, and all the comparator functions
+		 * for system caches should most assuredly be built-in functions.
+		 * Currently there's no real need to fix fn_mcxt either, but let's do
+		 * that anyway just to make sure it's not pointing to a dead context
+		 * later on.
+		 */
+
 		fmgr_info(cache->cc_skey[i].sk_procedure,
 				  &cache->cc_skey[i].sk_func);
-		cache->cc_skey[i].sk_nargs = cache->cc_skey[i].sk_func.fn_nargs;
+
+		cache->cc_skey[i].sk_func.fn_mcxt = CacheMemoryContext;
 
 		/* Initialize sk_attno suitably for HeapKeyTest() and heap scans */
 		cache->cc_skey[i].sk_attno = cache->cc_key[i];
@@ -270,7 +280,6 @@ CatalogCacheInitializeCache(CatCache *cache)
 
 	/*
 	 * mark this cache fully initialized
-	 *
 	 */
 	cache->cc_tupdesc = tupdesc;
 }
@@ -705,7 +714,6 @@ InitCatCache(int id,
  *		certain system indexes that support critical syscaches.
  *		We can't use an indexscan to fetch these, else we'll get into
  *		infinite recursion.  A plain heap scan will work, however.
- *
  */
 static bool
 IndexScanOK(CatCache *cache, ScanKey cur_skey)
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index d04e0cd5592..6431bd9382e 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.135 2001/05/30 14:15:26 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/cache/relcache.c,v 1.136 2001/06/01 02:41:36 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1161,19 +1161,20 @@ IndexedAccessMethodInitialize(Relation relation)
 	int			natts;
 	Size		stratSize;
 	Size		supportSize;
-	uint16		relamstrategies;
-	uint16		relamsupport;
+	uint16		amstrategies;
+	uint16		amsupport;
 
 	natts = relation->rd_rel->relnatts;
-	relamstrategies = relation->rd_am->amstrategies;
-	stratSize = AttributeNumberGetIndexStrategySize(natts, relamstrategies);
+	amstrategies = relation->rd_am->amstrategies;
+	amsupport = relation->rd_am->amsupport;
+
+	stratSize = AttributeNumberGetIndexStrategySize(natts, amstrategies);
 	strategy = (IndexStrategy) MemoryContextAlloc(CacheMemoryContext,
 												  stratSize);
 
-	relamsupport = relation->rd_am->amsupport;
-	if (relamsupport > 0)
+	if (amsupport > 0)
 	{
-		supportSize = natts * (relamsupport * sizeof(RegProcedure));
+		supportSize = natts * (amsupport * sizeof(RegProcedure));
 		support = (RegProcedure *) MemoryContextAlloc(CacheMemoryContext,
 													  supportSize);
 	}
@@ -1182,9 +1183,9 @@ IndexedAccessMethodInitialize(Relation relation)
 
 	IndexSupportInitialize(strategy, support,
 						   &relation->rd_uniqueindex,
-						   relation->rd_att->attrs[0]->attrelid,
+						   RelationGetRelid(relation),
 						   relation->rd_rel->relam,
-						   relamstrategies, relamsupport, natts);
+						   amstrategies, amsupport, natts);
 
 	RelationSetIndexSupport(relation, strategy, support);
 }
@@ -1212,26 +1213,22 @@ formrdesc(char *relationName,
 
 	/*
 	 * allocate new relation desc
-	 *
 	 */
 	relation = (Relation) palloc(sizeof(RelationData));
 	MemSet((char *) relation, 0, sizeof(RelationData));
 
 	/*
 	 * don't open the unix file yet..
-	 *
 	 */
 	relation->rd_fd = -1;
 
 	/*
 	 * initialize reference count
-	 *
 	 */
 	RelationSetReferenceCount(relation, 1);
 
 	/*
 	 * all entries built with this routine are nailed-in-cache
-	 *
 	 */
 	relation->rd_isnailed = true;
 
@@ -1241,7 +1238,6 @@ formrdesc(char *relationName,
 	 * The data we insert here is pretty incomplete/bogus, but it'll serve to
 	 * get us launched.  RelationCacheInitializePhase2() will read the
 	 * real data from pg_class and replace what we've done here.
-	 *
 	 */
 	relation->rd_rel = (Form_pg_class) palloc(CLASS_TUPLE_SIZE);
 	MemSet(relation->rd_rel, 0, CLASS_TUPLE_SIZE);
@@ -1266,13 +1262,11 @@ formrdesc(char *relationName,
 
 	/*
 	 * initialize attribute tuple form
-	 *
 	 */
 	relation->rd_att = CreateTemplateTupleDesc(natts);
 
 	/*
 	 * initialize tuple desc info
-	 *
 	 */
 	for (i = 0; i < natts; i++)
 	{
@@ -1283,14 +1277,12 @@ formrdesc(char *relationName,
 	}
 
 	/*
-	 * initialize relation id
-	 *
+	 * initialize relation id from info in att array (my, this is ugly)
 	 */
 	RelationGetRelid(relation) = relation->rd_att->attrs[0]->attrelid;
 
 	/*
 	 * initialize the relation's lock manager and RelFileNode information
-	 *
 	 */
 	RelationInitLockInfo(relation);		/* see lmgr.c */
 
@@ -1303,7 +1295,6 @@ formrdesc(char *relationName,
 
 	/*
 	 * initialize the rel-has-index flag, using hardwired knowledge
-	 *
 	 */
 	relation->rd_rel->relhasindex = false;
 
@@ -1322,7 +1313,6 @@ formrdesc(char *relationName,
 
 	/*
 	 * add new reldesc to relcache
-	 *
 	 */
 	RelationCacheInsert(relation);
 }
@@ -2755,10 +2745,8 @@ init_irels(void)
 		{
 			fmgr_info(SMD(i).sk_procedure,
 					  &(SMD(i).sk_func));
-			SMD(i).sk_nargs = SMD(i).sk_func.fn_nargs;
 		}
 
-
 		/*
 		 * use a real field called rd_istrat instead of the bogosity of
 		 * hanging invisible fields off the end of a structure - jolly
diff --git a/src/backend/utils/fmgr/fmgr.c b/src/backend/utils/fmgr/fmgr.c
index 090807129f5..544dc840c19 100644
--- a/src/backend/utils/fmgr/fmgr.c
+++ b/src/backend/utils/fmgr/fmgr.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/fmgr/fmgr.c,v 1.52 2001/05/19 09:28:08 petere Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/fmgr/fmgr.c,v 1.53 2001/06/01 02:41:36 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -114,6 +114,15 @@ fmgr_lookupByName(const char *name)
 /*
  * This routine fills a FmgrInfo struct, given the OID
  * of the function to be called.
+ *
+ * The caller's CurrentMemoryContext is used as the fn_mcxt of the info
+ * struct; this means that any subsidiary data attached to the info struct
+ * (either by fmgr_info itself, or later on by a function call handler)
+ * will be allocated in that context.  The caller must ensure that this
+ * context is at least as long-lived as the info struct itself.  This is
+ * not a problem in typical cases where the info struct is on the stack or
+ * in freshly-palloc'd space, but one must take extra care when the info
+ * struct is in a long-lived table.
  */
 void
 fmgr_info(Oid functionId, FmgrInfo *finfo)
@@ -124,8 +133,9 @@ fmgr_info(Oid functionId, FmgrInfo *finfo)
 	char	   *prosrc;
 
 	/*
-	 * fn_oid *must* be filled in last.  Code may assume that is fn_oid is valid,
-	 * the whole struct is valid.  Some FmgrInfo struct's do survive elogs.
+	 * fn_oid *must* be filled in last.  Some code assumes that if fn_oid is
+	 * valid, the whole struct is valid.  Some FmgrInfo struct's do survive
+	 * elogs.
 	 */
 	finfo->fn_oid = InvalidOid;
 	finfo->fn_extra = NULL;
@@ -133,10 +143,8 @@ fmgr_info(Oid functionId, FmgrInfo *finfo)
 
 	if ((fbp = fmgr_isbuiltin(functionId)) != NULL)
 	{
-
 		/*
-		 * Fast path for builtin functions: don't bother consulting
-		 * pg_proc
+		 * Fast path for builtin functions: don't bother consulting pg_proc
 		 */
 		finfo->fn_nargs = fbp->nargs;
 		finfo->fn_strict = fbp->strict;
@@ -171,7 +179,6 @@ fmgr_info(Oid functionId, FmgrInfo *finfo)
 	switch (procedureStruct->prolang)
 	{
 		case INTERNALlanguageId:
-
 			/*
 			 * For an ordinary builtin function, we should never get here
 			 * because the isbuiltin() search above will have succeeded.
diff --git a/src/include/access/skey.h b/src/include/access/skey.h
index 0cb9dc5d68b..2a00e26744e 100644
--- a/src/include/access/skey.h
+++ b/src/include/access/skey.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: skey.h,v 1.14 2001/01/24 19:43:19 momjian Exp $
+ * $Id: skey.h,v 1.15 2001/06/01 02:41:36 tgl Exp $
  *
  *
  * Note:
@@ -25,18 +25,18 @@ typedef struct ScanKeyData
 	bits16		sk_flags;		/* flags */
 	AttrNumber	sk_attno;		/* domain number */
 	RegProcedure sk_procedure;	/* procedure OID */
-	FmgrInfo	sk_func;
-	int32		sk_nargs;
+	FmgrInfo	sk_func;		/* fmgr call info for procedure */
 	Datum		sk_argument;	/* data to compare */
 } ScanKeyData;
 
 typedef ScanKeyData *ScanKey;
 
+/* ScanKeyData flags */
+#define SK_ISNULL		0x1		/* sk_argument is NULL */
+#define SK_UNARY		0x2		/* unary function (currently unsupported) */
+#define SK_NEGATE		0x4		/* negate function result */
+#define SK_COMMUTE		0x8		/* commute function (not fully supported) */
 
-#define SK_ISNULL		0x1
-#define SK_UNARY		0x2
-#define SK_NEGATE		0x4
-#define SK_COMMUTE		0x8
 
 #define ScanUnmarked			0x01
 #define ScanUncheckedPrevious	0x02
diff --git a/src/include/commands/trigger.h b/src/include/commands/trigger.h
index 46c4a497646..ddb9a327db0 100644
--- a/src/include/commands/trigger.h
+++ b/src/include/commands/trigger.h
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: trigger.h,v 1.26 2001/03/22 04:00:43 momjian Exp $
+ * $Id: trigger.h,v 1.27 2001/06/01 02:41:36 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -90,15 +90,25 @@ extern void FreeTriggerDesc(TriggerDesc *trigdesc);
 extern bool equalTriggerDescs(TriggerDesc *trigdesc1, TriggerDesc *trigdesc2);
 
 extern HeapTuple ExecBRInsertTriggers(EState *estate,
-					 Relation rel, HeapTuple tuple);
+									  ResultRelInfo *relinfo,
+									  HeapTuple trigtuple);
 extern void ExecARInsertTriggers(EState *estate,
-					 Relation rel, HeapTuple tuple);
-extern bool ExecBRDeleteTriggers(EState *estate, ItemPointer tupleid);
-extern void ExecARDeleteTriggers(EState *estate, ItemPointer tupleid);
-extern HeapTuple ExecBRUpdateTriggers(EState *estate, ItemPointer tupleid,
-					 HeapTuple tuple);
-extern void ExecARUpdateTriggers(EState *estate, ItemPointer tupleid,
-					 HeapTuple tuple);
+								 ResultRelInfo *relinfo,
+								 HeapTuple trigtuple);
+extern bool ExecBRDeleteTriggers(EState *estate,
+								 ResultRelInfo *relinfo,
+								 ItemPointer tupleid);
+extern void ExecARDeleteTriggers(EState *estate,
+								 ResultRelInfo *relinfo,
+								 ItemPointer tupleid);
+extern HeapTuple ExecBRUpdateTriggers(EState *estate,
+									  ResultRelInfo *relinfo,
+									  ItemPointer tupleid,
+									  HeapTuple newtuple);
+extern void ExecARUpdateTriggers(EState *estate,
+								 ResultRelInfo *relinfo,
+								 ItemPointer tupleid,
+								 HeapTuple newtuple);
 
 
 /* ----------
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 18903369c17..1dc68c192fe 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: execnodes.h,v 1.60 2001/05/27 20:48:51 tgl Exp $
+ * $Id: execnodes.h,v 1.61 2001/06/01 02:41:36 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -198,16 +198,18 @@ typedef struct JunkFilter
 /* ----------------
  *	  ResultRelInfo information
  *
- *		whenever we update an existing relation, we have to
- *		update indices on the relation.  The ResultRelInfo class
- *		is used to hold all the information on result relations,
- *		including indices.. -cim 10/15/89
+ *		Whenever we update an existing relation, we have to
+ *		update indices on the relation, and perhaps also fire triggers.
+ *		The ResultRelInfo class is used to hold all the information needed
+ *		about a result relation, including indices.. -cim 10/15/89
  *
  *		RangeTableIndex			result relation's range table index
  *		RelationDesc			relation descriptor for result relation
  *		NumIndices				# of indices existing on result relation
  *		IndexRelationDescs		array of relation descriptors for indices
  *		IndexRelationInfo		array of key/attr info for indices
+ *		TrigDesc				triggers to be fired, if any
+ *		TrigFunctions			cached lookup info for trigger functions
  *		ConstraintExprs			array of constraint-checking expressions
  *		junkFilter				for removing junk attributes from tuples
  * ----------------
@@ -220,6 +222,8 @@ typedef struct ResultRelInfo
 	int			ri_NumIndices;
 	RelationPtr ri_IndexRelationDescs;
 	IndexInfo **ri_IndexRelationInfo;
+	TriggerDesc *ri_TrigDesc;
+	FmgrInfo   *ri_TrigFunctions;
 	List	  **ri_ConstraintExprs;
 	JunkFilter *ri_junkFilter;
 } ResultRelInfo;
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index 5853be716d1..f1574b3ffd5 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: rel.h,v 1.45 2001/03/22 04:01:14 momjian Exp $
+ * $Id: rel.h,v 1.46 2001/06/01 02:41:36 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -47,13 +47,11 @@ typedef LockInfoData *LockInfo;
  * Likewise, this struct really belongs to trigger.h, but for convenience
  * we put it here.
  */
-
 typedef struct Trigger
 {
 	Oid			tgoid;
 	char	   *tgname;
 	Oid			tgfoid;
-	FmgrInfo	tgfunc;
 	int16		tgtype;
 	bool		tgenabled;
 	bool		tgisconstraint;
@@ -66,16 +64,23 @@ 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];
-	uint16		n_after_statement[4];
-	Trigger   **tg_before_statement[4];
-	Trigger   **tg_before_row[4];
-	Trigger   **tg_after_row[4];
-	Trigger   **tg_after_statement[4];
-	/* the actual array of triggers is here */
+	/*
+	 * Index data to identify which triggers are which.  Since each trigger
+	 * can appear in more than one class, for each class we provide a list
+	 * of integer indexes into the triggers array.
+	 */
+#define TRIGGER_NUM_EVENT_CLASSES  4
+
+	uint16		n_before_statement[TRIGGER_NUM_EVENT_CLASSES];
+	uint16		n_before_row[TRIGGER_NUM_EVENT_CLASSES];
+	uint16		n_after_row[TRIGGER_NUM_EVENT_CLASSES];
+	uint16		n_after_statement[TRIGGER_NUM_EVENT_CLASSES];
+	int		   *tg_before_statement[TRIGGER_NUM_EVENT_CLASSES];
+	int		   *tg_before_row[TRIGGER_NUM_EVENT_CLASSES];
+	int		   *tg_after_row[TRIGGER_NUM_EVENT_CLASSES];
+	int		   *tg_after_statement[TRIGGER_NUM_EVENT_CLASSES];
+
+	/* The actual array of triggers is here */
 	Trigger    *triggers;
 	int			numtriggers;
 } TriggerDesc;
-- 
GitLab