OSDN Git Service

Clean up some minor problems exposed by further thought about Panon's bug
authorTom Lane <tgl@sss.pgh.pa.us>
Fri, 1 Jun 2001 02:41:36 +0000 (02:41 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Fri, 1 Jun 2001 02:41:36 +0000 (02:41 +0000)
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.

16 files changed:
doc/src/sgml/trigger.sgml
src/backend/access/common/scankey.c
src/backend/access/index/indexam.c
src/backend/access/index/istrat.c
src/backend/catalog/index.c
src/backend/catalog/pg_operator.c
src/backend/commands/copy.c
src/backend/commands/trigger.c
src/backend/executor/execMain.c
src/backend/utils/cache/catcache.c
src/backend/utils/cache/relcache.c
src/backend/utils/fmgr/fmgr.c
src/include/access/skey.h
src/include/commands/trigger.h
src/include/nodes/execnodes.h
src/include/utils/rel.h

index 686c958..bb88ccc 100644 (file)
@@ -366,7 +366,6 @@ typedef struct Trigger
     Oid         tgoid;
     char       *tgname;
     Oid         tgfoid;
-    FmgrInfo    tgfunc;
     int16       tgtype;
     bool        tgenabled;
     bool        tgisconstraint;
index dc2d36b..eb66d41 100644 (file)
@@ -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));
 }
index 3cea689..5b12930 100644 (file)
@@ -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;
 }
 
 /* ----------------
@@ -346,18 +354,8 @@ 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,
index 8cd1284..188f69b 100644 (file)
@@ -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);
index b6420a1..9b98bd7 100644 (file)
@@ -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);
 
        /*
index db54cee..40ee84c 100644 (file)
@@ -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);
 }
 
index fbbade1..a19f1a3 100644 (file)
@@ -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++)
index 70c1465..0132218 100644 (file)
@@ -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 $
  *
  *-------------------------------------------------------------------------
  */
 #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);
 
index 3ba9b91..38c34f0 100644 (file)
@@ -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 *
index f843f2b..cd54ff7 100644 (file)
@@ -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)
index d04e0cd..6431bd9 100644 (file)
@@ -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
index 0908071..544dc84 100644 (file)
@@ -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.
index 0cb9dc5..2a00e26 100644 (file)
@@ -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
index 46c4a49..ddb9a32 100644 (file)
@@ -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);
 
 
 /* ----------
index 1890336..1dc68c1 100644 (file)
@@ -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;
index 5853be7..f1574b3 100644 (file)
@@ -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;