]> granicus.if.org Git - postgresql/commitdiff
Use slots in trigger infrastructure, except for the actual invocation.
authorAndres Freund <andres@anarazel.de>
Wed, 27 Feb 2019 04:30:28 +0000 (20:30 -0800)
committerAndres Freund <andres@anarazel.de>
Wed, 27 Feb 2019 04:31:38 +0000 (20:31 -0800)
In preparation for abstracting table storage, convert trigger.c to
track tuples in slots. Which also happens to make code calling
triggers simpler.

As the calling interface for triggers themselves is not changed in
this patch, HeapTuples still are extracted from the slot at that
time. But that's handled solely inside trigger.c, not visible to
callers. It's quite likely that we'll want to revise the external
trigger interface, but that's a separate large project.

As part of this work the slots used for old/new/return tuples are
moved from EState into ResultRelInfo, as different updated tables
might need different slots. The slots are now also now created
on-demand, which is good both from an efficiency POV, but also makes
the modifying code simpler.

Author: Andres Freund, Amit Khandekar and Ashutosh Bapat
Discussion: https://postgr.es/m/20180703070645.wchpu5muyto5n647@alap3.anarazel.de

14 files changed:
contrib/postgres_fdw/postgres_fdw.c
src/backend/commands/copy.c
src/backend/commands/tablecmds.c
src/backend/commands/trigger.c
src/backend/executor/execMain.c
src/backend/executor/execReplication.c
src/backend/executor/execTuples.c
src/backend/executor/execUtils.c
src/backend/executor/nodeModifyTable.c
src/backend/replication/logical/worker.c
src/backend/utils/adt/ri_triggers.c
src/include/commands/trigger.h
src/include/executor/executor.h
src/include/nodes/execnodes.h

index 6b96e7de0a41191712ad9c5953210a8cafcd1395..2f387fac422a11dd4fb0e011223cbe199d947a9f 100644 (file)
@@ -3507,8 +3507,13 @@ store_returning_result(PgFdwModifyState *fmstate,
                                                                                        fmstate->retrieved_attrs,
                                                                                        NULL,
                                                                                        fmstate->temp_cxt);
-               /* tuple will be deleted when it is cleared from the slot */
-               ExecStoreHeapTuple(newtup, slot, true);
+               /*
+                * The returning slot will not necessarily be suitable to store
+                * heaptuples directly, so allow for conversion.
+                */
+               ExecForceStoreHeapTuple(newtup, slot);
+               ExecMaterializeSlot(slot);
+               pfree(newtup);
        }
        PG_CATCH();
        {
@@ -3886,6 +3891,7 @@ apply_returning_filter(PgFdwDirectModifyState *dmstate,
                                           TupleTableSlot *slot,
                                           EState *estate)
 {
+       ResultRelInfo *relInfo = estate->es_result_relation_info;
        TupleDesc       resultTupType = RelationGetDescr(dmstate->resultRel);
        TupleTableSlot *resultSlot;
        Datum      *values;
@@ -3895,11 +3901,9 @@ apply_returning_filter(PgFdwDirectModifyState *dmstate,
        int                     i;
 
        /*
-        * Use the trigger tuple slot as a place to store the result tuple.
+        * Use the return tuple slot as a place to store the result tuple.
         */
-       resultSlot = estate->es_trig_tuple_slot;
-       if (resultSlot->tts_tupleDescriptor != resultTupType)
-               ExecSetSlotDescriptor(resultSlot, resultTupType);
+       resultSlot = ExecGetReturningSlot(estate, relInfo);
 
        /*
         * Extract all the values of the scan tuple.
index 93aa1631775e0af3e8f23921fbed9fd36397317a..5dd6fe02c6e5741449f476533bd1d47430b89f9f 100644 (file)
@@ -2519,9 +2519,6 @@ CopyFrom(CopyState cstate)
        /* Set up a tuple slot too */
        myslot = ExecInitExtraTupleSlot(estate, tupDesc,
                                                                        &TTSOpsHeapTuple);
-       /* Triggers might need a slot as well */
-       estate->es_trig_tuple_slot = ExecInitExtraTupleSlot(estate, NULL,
-                                                                                                               &TTSOpsHeapTuple);
 
        /*
         * Set up a ModifyTableState so we can let FDW(s) init themselves for
@@ -2870,7 +2867,7 @@ CopyFrom(CopyState cstate)
                                         * Otherwise, just remember the original unconverted
                                         * tuple, to avoid a needless round trip conversion.
                                         */
-                                       cstate->transition_capture->tcs_original_insert_tuple = tuple;
+                                       cstate->transition_capture->tcs_original_insert_tuple = myslot;
                                        cstate->transition_capture->tcs_map = NULL;
                                }
                        }
@@ -2907,12 +2904,8 @@ CopyFrom(CopyState cstate)
                /* BEFORE ROW INSERT Triggers */
                if (has_before_insert_row_trig)
                {
-                       slot = ExecBRInsertTriggers(estate, resultRelInfo, slot);
-
-                       if (slot == NULL)       /* "do nothing" */
-                               skip_tuple = true;
-                       else                            /* trigger might have changed tuple */
-                               tuple = ExecFetchSlotHeapTuple(slot, true, NULL);
+                       if (!ExecBRInsertTriggers(estate, resultRelInfo, slot))
+                               skip_tuple = true;      /* "do nothing" */
                }
 
                if (!skip_tuple)
@@ -2990,9 +2983,6 @@ CopyFrom(CopyState cstate)
                                                if (slot == NULL)       /* "do nothing" */
                                                        continue;       /* next tuple please */
 
-                                               /* FDW might have changed tuple */
-                                               tuple = ExecFetchSlotHeapTuple(slot, true, NULL);
-
                                                /*
                                                 * AFTER ROW Triggers might reference the tableoid
                                                 * column, so (re-)initialize tts_tableOid before
@@ -3002,6 +2992,7 @@ CopyFrom(CopyState cstate)
                                        }
                                        else
                                        {
+                                               tuple = ExecFetchSlotHeapTuple(slot, true, NULL);
                                                heap_insert(resultRelInfo->ri_RelationDesc, tuple,
                                                                        mycid, hi_options, bistate);
                                                ItemPointerCopy(&tuple->t_self, &slot->tts_tid);
@@ -3018,7 +3009,7 @@ CopyFrom(CopyState cstate)
                                                                                                                           NIL);
 
                                        /* AFTER ROW INSERT Triggers */
-                                       ExecARInsertTriggers(estate, resultRelInfo, tuple,
+                                       ExecARInsertTriggers(estate, resultRelInfo, slot,
                                                                                 recheckIndexes, cstate->transition_capture);
 
                                        list_free(recheckIndexes);
@@ -3158,7 +3149,7 @@ CopyFromInsertBatch(CopyState cstate, EState *estate, CommandId mycid,
                                ExecInsertIndexTuples(myslot, &(bufferedTuples[i]->t_self),
                                                                          estate, false, NULL, NIL);
                        ExecARInsertTriggers(estate, resultRelInfo,
-                                                                bufferedTuples[i],
+                                                                myslot,
                                                                 recheckIndexes, cstate->transition_capture);
                        list_free(recheckIndexes);
                }
@@ -3175,8 +3166,9 @@ CopyFromInsertBatch(CopyState cstate, EState *estate, CommandId mycid,
                for (i = 0; i < nBufferedTuples; i++)
                {
                        cstate->cur_lineno = firstBufferedLineNo + i;
+                       ExecStoreHeapTuple(bufferedTuples[i], myslot, false);
                        ExecARInsertTriggers(estate, resultRelInfo,
-                                                                bufferedTuples[i],
+                                                                myslot,
                                                                 NIL, cstate->transition_capture);
                }
        }
index 0fb0b186bb43441e14f53ff8bf6d067d6efd8dcb..a93b13c2fe4554ce1922a1297164bd4690e0c07e 100644 (file)
@@ -8921,8 +8921,6 @@ validateForeignKeyConstraint(char *conname,
                trigdata.tg_trigtuple = tuple;
                trigdata.tg_newtuple = NULL;
                trigdata.tg_trigger = &trig;
-               trigdata.tg_trigtuplebuf = scan->rs_cbuf;
-               trigdata.tg_newtuplebuf = InvalidBuffer;
 
                fcinfo->context = (Node *) &trigdata;
 
index 7e5bf0d27f800a6aeb13306652eda96549c934db..c5e588e80118228b4fbdb327fe28eec65bb73f46 100644 (file)
@@ -79,16 +79,17 @@ static int  MyTriggerDepth = 0;
 /* Local function prototypes */
 static void ConvertTriggerToFK(CreateTrigStmt *stmt, Oid funcoid);
 static void SetTriggerFlags(TriggerDesc *trigdesc, Trigger *trigger);
-static HeapTuple GetTupleForTrigger(EState *estate,
+static bool GetTupleForTrigger(EState *estate,
                                   EPQState *epqstate,
                                   ResultRelInfo *relinfo,
                                   ItemPointer tid,
                                   LockTupleMode lockmode,
+                                  TupleTableSlot *oldslot,
                                   TupleTableSlot **newSlot);
 static bool TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
                           Trigger *trigger, TriggerEvent event,
                           Bitmapset *modifiedCols,
-                          HeapTuple oldtup, HeapTuple newtup);
+                          TupleTableSlot *oldslot, TupleTableSlot *newslot);
 static HeapTuple ExecCallTriggerFunc(TriggerData *trigdata,
                                        int tgindx,
                                        FmgrInfo *finfo,
@@ -96,7 +97,7 @@ static HeapTuple ExecCallTriggerFunc(TriggerData *trigdata,
                                        MemoryContext per_tuple_context);
 static void AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo,
                                          int event, bool row_trigger,
-                                         HeapTuple oldtup, HeapTuple newtup,
+                                         TupleTableSlot *oldtup, TupleTableSlot *newtup,
                                          List *recheckIndexes, Bitmapset *modifiedCols,
                                          TransitionCaptureState *transition_capture);
 static void AfterTriggerEnlargeQueryState(void);
@@ -2467,10 +2468,10 @@ ExecBSInsertTriggers(EState *estate, ResultRelInfo *relinfo)
        LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
        LocTriggerData.tg_trigtuple = NULL;
        LocTriggerData.tg_newtuple = NULL;
+       LocTriggerData.tg_trigslot = NULL;
+       LocTriggerData.tg_newslot = NULL;
        LocTriggerData.tg_oldtable = NULL;
        LocTriggerData.tg_newtable = NULL;
-       LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
-       LocTriggerData.tg_newtuplebuf = InvalidBuffer;
        for (i = 0; i < trigdesc->numtriggers; i++)
        {
                Trigger    *trigger = &trigdesc->triggers[i];
@@ -2510,15 +2511,13 @@ ExecASInsertTriggers(EState *estate, ResultRelInfo *relinfo,
                                                          false, NULL, NULL, NIL, NULL, transition_capture);
 }
 
-TupleTableSlot *
+bool
 ExecBRInsertTriggers(EState *estate, ResultRelInfo *relinfo,
                                         TupleTableSlot *slot)
 {
        TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
+       HeapTuple       newtuple = false;
        bool            should_free;
-       HeapTuple       slottuple = ExecFetchSlotHeapTuple(slot, true, &should_free);
-       HeapTuple       newtuple = slottuple;
-       HeapTuple       oldtuple;
        TriggerData LocTriggerData;
        int                     i;
 
@@ -2527,13 +2526,16 @@ ExecBRInsertTriggers(EState *estate, ResultRelInfo *relinfo,
                TRIGGER_EVENT_ROW |
                TRIGGER_EVENT_BEFORE;
        LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
+       LocTriggerData.tg_trigtuple = NULL;
        LocTriggerData.tg_newtuple = NULL;
+       LocTriggerData.tg_trigslot = NULL;
+       LocTriggerData.tg_newslot = NULL;
        LocTriggerData.tg_oldtable = NULL;
        LocTriggerData.tg_newtable = NULL;
-       LocTriggerData.tg_newtuplebuf = InvalidBuffer;
        for (i = 0; i < trigdesc->numtriggers; i++)
        {
                Trigger    *trigger = &trigdesc->triggers[i];
+               HeapTuple       oldtuple;
 
                if (!TRIGGER_TYPE_MATCHES(trigger->tgtype,
                                                                  TRIGGER_TYPE_ROW,
@@ -2541,52 +2543,44 @@ ExecBRInsertTriggers(EState *estate, ResultRelInfo *relinfo,
                                                                  TRIGGER_TYPE_INSERT))
                        continue;
                if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
-                                                       NULL, NULL, newtuple))
+                                                       NULL, NULL, slot))
                        continue;
 
+               if (!newtuple)
+                       newtuple = ExecFetchSlotHeapTuple(slot, true, &should_free);
+
+               LocTriggerData.tg_trigslot = slot;
                LocTriggerData.tg_trigtuple = oldtuple = newtuple;
-               LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
                LocTriggerData.tg_trigger = trigger;
                newtuple = ExecCallTriggerFunc(&LocTriggerData,
                                                                           i,
                                                                           relinfo->ri_TrigFunctions,
                                                                           relinfo->ri_TrigInstrument,
                                                                           GetPerTupleMemoryContext(estate));
-               if (oldtuple != newtuple && oldtuple != slottuple)
-                       heap_freetuple(oldtuple);
                if (newtuple == NULL)
                {
                        if (should_free)
-                               heap_freetuple(slottuple);
-                       return NULL;            /* "do nothing" */
+                               heap_freetuple(oldtuple);
+                       return false;           /* "do nothing" */
                }
-       }
+               else if (newtuple != oldtuple)
+               {
+                       ExecForceStoreHeapTuple(newtuple, slot);
 
-       if (newtuple != slottuple)
-       {
-               /*
-                * Return the modified tuple using the es_trig_tuple_slot.  We assume
-                * the tuple was allocated in per-tuple memory context, and therefore
-                * will go away by itself. The tuple table slot should not try to
-                * clear it.
-                */
-               TupleTableSlot *newslot = estate->es_trig_tuple_slot;
-               TupleDesc       tupdesc = RelationGetDescr(relinfo->ri_RelationDesc);
+                       if (should_free)
+                               heap_freetuple(oldtuple);
 
-               if (newslot->tts_tupleDescriptor != tupdesc)
-                       ExecSetSlotDescriptor(newslot, tupdesc);
-               ExecStoreHeapTuple(newtuple, newslot, false);
-               slot = newslot;
+                       /* signal tuple should be re-fetched if used */
+                       newtuple = NULL;
+               }
        }
 
-       if (should_free)
-               heap_freetuple(slottuple);
-       return slot;
+       return true;
 }
 
 void
 ExecARInsertTriggers(EState *estate, ResultRelInfo *relinfo,
-                                        HeapTuple trigtuple, List *recheckIndexes,
+                                        TupleTableSlot *slot, List *recheckIndexes,
                                         TransitionCaptureState *transition_capture)
 {
        TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
@@ -2594,20 +2588,18 @@ ExecARInsertTriggers(EState *estate, ResultRelInfo *relinfo,
        if ((trigdesc && trigdesc->trig_insert_after_row) ||
                (transition_capture && transition_capture->tcs_insert_new_table))
                AfterTriggerSaveEvent(estate, relinfo, TRIGGER_EVENT_INSERT,
-                                                         true, NULL, trigtuple,
+                                                         true, NULL, slot,
                                                          recheckIndexes, NULL,
                                                          transition_capture);
 }
 
-TupleTableSlot *
+bool
 ExecIRInsertTriggers(EState *estate, ResultRelInfo *relinfo,
                                         TupleTableSlot *slot)
 {
        TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
+       HeapTuple       newtuple = NULL;
        bool            should_free;
-       HeapTuple       slottuple = ExecFetchSlotHeapTuple(slot, true, &should_free);
-       HeapTuple       newtuple = slottuple;
-       HeapTuple       oldtuple;
        TriggerData LocTriggerData;
        int                     i;
 
@@ -2616,13 +2608,16 @@ ExecIRInsertTriggers(EState *estate, ResultRelInfo *relinfo,
                TRIGGER_EVENT_ROW |
                TRIGGER_EVENT_INSTEAD;
        LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
+       LocTriggerData.tg_trigtuple = NULL;
        LocTriggerData.tg_newtuple = NULL;
+       LocTriggerData.tg_trigslot = NULL;
+       LocTriggerData.tg_newslot = NULL;
        LocTriggerData.tg_oldtable = NULL;
        LocTriggerData.tg_newtable = NULL;
-       LocTriggerData.tg_newtuplebuf = InvalidBuffer;
        for (i = 0; i < trigdesc->numtriggers; i++)
        {
                Trigger    *trigger = &trigdesc->triggers[i];
+               HeapTuple       oldtuple;
 
                if (!TRIGGER_TYPE_MATCHES(trigger->tgtype,
                                                                  TRIGGER_TYPE_ROW,
@@ -2630,47 +2625,39 @@ ExecIRInsertTriggers(EState *estate, ResultRelInfo *relinfo,
                                                                  TRIGGER_TYPE_INSERT))
                        continue;
                if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
-                                                       NULL, NULL, newtuple))
+                                                       NULL, NULL, slot))
                        continue;
 
+               if (!newtuple)
+                       newtuple = ExecFetchSlotHeapTuple(slot, true, &should_free);
+
+               LocTriggerData.tg_trigslot = slot;
                LocTriggerData.tg_trigtuple = oldtuple = newtuple;
-               LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
                LocTriggerData.tg_trigger = trigger;
                newtuple = ExecCallTriggerFunc(&LocTriggerData,
                                                                           i,
                                                                           relinfo->ri_TrigFunctions,
                                                                           relinfo->ri_TrigInstrument,
                                                                           GetPerTupleMemoryContext(estate));
-               if (oldtuple != newtuple && oldtuple != slottuple)
-                       heap_freetuple(oldtuple);
                if (newtuple == NULL)
                {
                        if (should_free)
-                               heap_freetuple(slottuple);
-                       return NULL;            /* "do nothing" */
+                               heap_freetuple(oldtuple);
+                       return false;           /* "do nothing" */
                }
-       }
+               else if (newtuple != oldtuple)
+               {
+                       ExecForceStoreHeapTuple(newtuple, slot);
 
-       if (newtuple != slottuple)
-       {
-               /*
-                * Return the modified tuple using the es_trig_tuple_slot.  We assume
-                * the tuple was allocated in per-tuple memory context, and therefore
-                * will go away by itself. The tuple table slot should not try to
-                * clear it.
-                */
-               TupleTableSlot *newslot = estate->es_trig_tuple_slot;
-               TupleDesc       tupdesc = RelationGetDescr(relinfo->ri_RelationDesc);
+                       if (should_free)
+                               heap_freetuple(oldtuple);
 
-               if (newslot->tts_tupleDescriptor != tupdesc)
-                       ExecSetSlotDescriptor(newslot, tupdesc);
-               ExecStoreHeapTuple(newtuple, newslot, false);
-               slot = newslot;
+                       /* signal tuple should be re-fetched if used */
+                       newtuple = NULL;
+               }
        }
 
-       if (should_free)
-               heap_freetuple(slottuple);
-       return slot;
+       return true;
 }
 
 void
@@ -2698,10 +2685,10 @@ ExecBSDeleteTriggers(EState *estate, ResultRelInfo *relinfo)
        LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
        LocTriggerData.tg_trigtuple = NULL;
        LocTriggerData.tg_newtuple = NULL;
+       LocTriggerData.tg_trigslot = NULL;
+       LocTriggerData.tg_newslot = NULL;
        LocTriggerData.tg_oldtable = NULL;
        LocTriggerData.tg_newtable = NULL;
-       LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
-       LocTriggerData.tg_newtuplebuf = InvalidBuffer;
        for (i = 0; i < trigdesc->numtriggers; i++)
        {
                Trigger    *trigger = &trigdesc->triggers[i];
@@ -2755,20 +2742,21 @@ ExecBRDeleteTriggers(EState *estate, EPQState *epqstate,
                                         HeapTuple fdw_trigtuple,
                                         TupleTableSlot **epqslot)
 {
+       TupleTableSlot *slot = ExecGetTriggerOldSlot(estate, relinfo);
        TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
        bool            result = true;
        TriggerData LocTriggerData;
        HeapTuple       trigtuple;
-       HeapTuple       newtuple;
-       TupleTableSlot *newSlot;
+       bool            should_free = false;
        int                     i;
 
        Assert(HeapTupleIsValid(fdw_trigtuple) ^ ItemPointerIsValid(tupleid));
        if (fdw_trigtuple == NULL)
        {
-               trigtuple = GetTupleForTrigger(estate, epqstate, relinfo, tupleid,
-                                                                          LockTupleExclusive, &newSlot);
-               if (trigtuple == NULL)
+               TupleTableSlot *newSlot;
+
+               if (!GetTupleForTrigger(estate, epqstate, relinfo, tupleid,
+                                                               LockTupleExclusive, slot, &newSlot))
                        return false;
 
                /*
@@ -2779,24 +2767,32 @@ ExecBRDeleteTriggers(EState *estate, EPQState *epqstate,
                if (newSlot != NULL && epqslot != NULL)
                {
                        *epqslot = newSlot;
-                       heap_freetuple(trigtuple);
                        return false;
                }
+
+               trigtuple = ExecFetchSlotHeapTuple(slot, true, &should_free);
+
        }
        else
+       {
                trigtuple = fdw_trigtuple;
+               ExecForceStoreHeapTuple(trigtuple, slot);
+       }
 
        LocTriggerData.type = T_TriggerData;
        LocTriggerData.tg_event = TRIGGER_EVENT_DELETE |
                TRIGGER_EVENT_ROW |
                TRIGGER_EVENT_BEFORE;
        LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
+       LocTriggerData.tg_trigtuple = NULL;
        LocTriggerData.tg_newtuple = NULL;
+       LocTriggerData.tg_trigslot = NULL;
+       LocTriggerData.tg_newslot = NULL;
        LocTriggerData.tg_oldtable = NULL;
        LocTriggerData.tg_newtable = NULL;
-       LocTriggerData.tg_newtuplebuf = InvalidBuffer;
        for (i = 0; i < trigdesc->numtriggers; i++)
        {
+               HeapTuple       newtuple;
                Trigger    *trigger = &trigdesc->triggers[i];
 
                if (!TRIGGER_TYPE_MATCHES(trigger->tgtype,
@@ -2805,11 +2801,11 @@ ExecBRDeleteTriggers(EState *estate, EPQState *epqstate,
                                                                  TRIGGER_TYPE_DELETE))
                        continue;
                if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
-                                                       NULL, trigtuple, NULL))
+                                                       NULL, slot, NULL))
                        continue;
 
+               LocTriggerData.tg_trigslot = slot;
                LocTriggerData.tg_trigtuple = trigtuple;
-               LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
                LocTriggerData.tg_trigger = trigger;
                newtuple = ExecCallTriggerFunc(&LocTriggerData,
                                                                           i,
@@ -2824,7 +2820,7 @@ ExecBRDeleteTriggers(EState *estate, EPQState *epqstate,
                if (newtuple != trigtuple)
                        heap_freetuple(newtuple);
        }
-       if (trigtuple != fdw_trigtuple)
+       if (should_free)
                heap_freetuple(trigtuple);
 
        return result;
@@ -2837,28 +2833,26 @@ ExecARDeleteTriggers(EState *estate, ResultRelInfo *relinfo,
                                         TransitionCaptureState *transition_capture)
 {
        TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
+       TupleTableSlot *slot = ExecGetTriggerOldSlot(estate, relinfo);
 
        if ((trigdesc && trigdesc->trig_delete_after_row) ||
                (transition_capture && transition_capture->tcs_delete_old_table))
        {
-               HeapTuple       trigtuple;
-
                Assert(HeapTupleIsValid(fdw_trigtuple) ^ ItemPointerIsValid(tupleid));
                if (fdw_trigtuple == NULL)
-                       trigtuple = GetTupleForTrigger(estate,
-                                                                                  NULL,
-                                                                                  relinfo,
-                                                                                  tupleid,
-                                                                                  LockTupleExclusive,
-                                                                                  NULL);
+                       GetTupleForTrigger(estate,
+                                                          NULL,
+                                                          relinfo,
+                                                          tupleid,
+                                                          LockTupleExclusive,
+                                                          slot,
+                                                          NULL);
                else
-                       trigtuple = fdw_trigtuple;
+                       ExecForceStoreHeapTuple(fdw_trigtuple, slot);
 
                AfterTriggerSaveEvent(estate, relinfo, TRIGGER_EVENT_DELETE,
-                                                         true, trigtuple, NULL, NIL, NULL,
+                                                         true, slot, NULL, NIL, NULL,
                                                          transition_capture);
-               if (trigtuple != fdw_trigtuple)
-                       heap_freetuple(trigtuple);
        }
 }
 
@@ -2867,8 +2861,8 @@ ExecIRDeleteTriggers(EState *estate, ResultRelInfo *relinfo,
                                         HeapTuple trigtuple)
 {
        TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
+       TupleTableSlot *slot = ExecGetTriggerOldSlot(estate, relinfo);
        TriggerData LocTriggerData;
-       HeapTuple       rettuple;
        int                     i;
 
        LocTriggerData.type = T_TriggerData;
@@ -2876,12 +2870,18 @@ ExecIRDeleteTriggers(EState *estate, ResultRelInfo *relinfo,
                TRIGGER_EVENT_ROW |
                TRIGGER_EVENT_INSTEAD;
        LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
+       LocTriggerData.tg_trigtuple = NULL;
        LocTriggerData.tg_newtuple = NULL;
+       LocTriggerData.tg_trigslot = NULL;
+       LocTriggerData.tg_newslot = NULL;
        LocTriggerData.tg_oldtable = NULL;
        LocTriggerData.tg_newtable = NULL;
-       LocTriggerData.tg_newtuplebuf = InvalidBuffer;
+
+       ExecForceStoreHeapTuple(trigtuple, slot);
+
        for (i = 0; i < trigdesc->numtriggers; i++)
        {
+               HeapTuple       rettuple;
                Trigger    *trigger = &trigdesc->triggers[i];
 
                if (!TRIGGER_TYPE_MATCHES(trigger->tgtype,
@@ -2890,11 +2890,11 @@ ExecIRDeleteTriggers(EState *estate, ResultRelInfo *relinfo,
                                                                  TRIGGER_TYPE_DELETE))
                        continue;
                if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
-                                                       NULL, trigtuple, NULL))
+                                                       NULL, slot, NULL))
                        continue;
 
+               LocTriggerData.tg_trigslot = slot;
                LocTriggerData.tg_trigtuple = trigtuple;
-               LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
                LocTriggerData.tg_trigger = trigger;
                rettuple = ExecCallTriggerFunc(&LocTriggerData,
                                                                           i,
@@ -2937,10 +2937,10 @@ ExecBSUpdateTriggers(EState *estate, ResultRelInfo *relinfo)
        LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
        LocTriggerData.tg_trigtuple = NULL;
        LocTriggerData.tg_newtuple = NULL;
+       LocTriggerData.tg_trigslot = NULL;
+       LocTriggerData.tg_newslot = NULL;
        LocTriggerData.tg_oldtable = NULL;
        LocTriggerData.tg_newtable = NULL;
-       LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
-       LocTriggerData.tg_newtuplebuf = InvalidBuffer;
        for (i = 0; i < trigdesc->numtriggers; i++)
        {
                Trigger    *trigger = &trigdesc->triggers[i];
@@ -2982,20 +2982,20 @@ ExecASUpdateTriggers(EState *estate, ResultRelInfo *relinfo,
                                                          transition_capture);
 }
 
-TupleTableSlot *
+bool
 ExecBRUpdateTriggers(EState *estate, EPQState *epqstate,
                                         ResultRelInfo *relinfo,
                                         ItemPointer tupleid,
                                         HeapTuple fdw_trigtuple,
-                                        TupleTableSlot *slot)
+                                        TupleTableSlot *newslot)
 {
        TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
-       HeapTuple       slottuple = ExecFetchSlotHeapTuple(slot, true, NULL);
-       HeapTuple       newtuple = slottuple;
-       TriggerData LocTriggerData;
+       TupleTableSlot *oldslot = ExecGetTriggerOldSlot(estate, relinfo);
+       HeapTuple       newtuple = NULL;
        HeapTuple       trigtuple;
-       HeapTuple       oldtuple;
-       TupleTableSlot *newSlot;
+       bool            should_free_trig = false;
+       bool            should_free_new = false;
+       TriggerData LocTriggerData;
        int                     i;
        Bitmapset  *updatedCols;
        LockTupleMode lockmode;
@@ -3006,37 +3006,40 @@ ExecBRUpdateTriggers(EState *estate, EPQState *epqstate,
        Assert(HeapTupleIsValid(fdw_trigtuple) ^ ItemPointerIsValid(tupleid));
        if (fdw_trigtuple == NULL)
        {
+               TupleTableSlot *newSlot = NULL;
+
                /* get a copy of the on-disk tuple we are planning to update */
-               trigtuple = GetTupleForTrigger(estate, epqstate, relinfo, tupleid,
-                                                                          lockmode, &newSlot);
-               if (trigtuple == NULL)
-                       return NULL;            /* cancel the update action */
+               if (!GetTupleForTrigger(estate, epqstate, relinfo, tupleid,
+                                                               lockmode, oldslot, &newSlot))
+                       return false;           /* cancel the update action */
+
+               /*
+                * In READ COMMITTED isolation level it's possible that target tuple
+                * was changed due to concurrent update.  In that case we have a raw
+                * subplan output tuple in newSlot, and need to run it through the
+                * junk filter to produce an insertable tuple.
+                *
+                * Caution: more than likely, the passed-in slot is the same as the
+                * junkfilter's output slot, so we are clobbering the original value
+                * of slottuple by doing the filtering.  This is OK since neither we
+                * nor our caller have any more interest in the prior contents of that
+                * slot.
+                */
+               if (newSlot != NULL)
+               {
+                       TupleTableSlot *slot = ExecFilterJunk(relinfo->ri_junkFilter, newSlot);
+
+                       ExecCopySlot(newslot, slot);
+               }
+
+               trigtuple = ExecFetchSlotHeapTuple(oldslot, true, &should_free_trig);
        }
        else
        {
+               ExecForceStoreHeapTuple(fdw_trigtuple, oldslot);
                trigtuple = fdw_trigtuple;
-               newSlot = NULL;
-       }
-
-       /*
-        * In READ COMMITTED isolation level it's possible that target tuple was
-        * changed due to concurrent update.  In that case we have a raw subplan
-        * output tuple in newSlot, and need to run it through the junk filter to
-        * produce an insertable tuple.
-        *
-        * Caution: more than likely, the passed-in slot is the same as the
-        * junkfilter's output slot, so we are clobbering the original value of
-        * slottuple by doing the filtering.  This is OK since neither we nor our
-        * caller have any more interest in the prior contents of that slot.
-        */
-       if (newSlot != NULL)
-       {
-               slot = ExecFilterJunk(relinfo->ri_junkFilter, newSlot);
-               slottuple = ExecFetchSlotHeapTuple(slot, true, NULL);
-               newtuple = slottuple;
        }
 
-
        LocTriggerData.type = T_TriggerData;
        LocTriggerData.tg_event = TRIGGER_EVENT_UPDATE |
                TRIGGER_EVENT_ROW |
@@ -3048,6 +3051,7 @@ ExecBRUpdateTriggers(EState *estate, EPQState *epqstate,
        for (i = 0; i < trigdesc->numtriggers; i++)
        {
                Trigger    *trigger = &trigdesc->triggers[i];
+               HeapTuple       oldtuple;
 
                if (!TRIGGER_TYPE_MATCHES(trigger->tgtype,
                                                                  TRIGGER_TYPE_ROW,
@@ -3055,67 +3059,66 @@ ExecBRUpdateTriggers(EState *estate, EPQState *epqstate,
                                                                  TRIGGER_TYPE_UPDATE))
                        continue;
                if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
-                                                       updatedCols, trigtuple, newtuple))
+                                                       updatedCols, oldslot, newslot))
                        continue;
 
+               if (!newtuple)
+                       newtuple = ExecFetchSlotHeapTuple(newslot, true, &should_free_new);
+
+               LocTriggerData.tg_trigslot = oldslot;
                LocTriggerData.tg_trigtuple = trigtuple;
                LocTriggerData.tg_newtuple = oldtuple = newtuple;
-               LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
-               LocTriggerData.tg_newtuplebuf = InvalidBuffer;
+               LocTriggerData.tg_newslot = newslot;
                LocTriggerData.tg_trigger = trigger;
                newtuple = ExecCallTriggerFunc(&LocTriggerData,
                                                                           i,
                                                                           relinfo->ri_TrigFunctions,
                                                                           relinfo->ri_TrigInstrument,
                                                                           GetPerTupleMemoryContext(estate));
-               if (oldtuple != newtuple && oldtuple != slottuple)
-                       heap_freetuple(oldtuple);
+
                if (newtuple == NULL)
                {
-                       if (trigtuple != fdw_trigtuple)
+                       if (should_free_trig)
                                heap_freetuple(trigtuple);
-                       return NULL;            /* "do nothing" */
+                       if (should_free_new)
+                               heap_freetuple(oldtuple);
+                       return false;           /* "do nothing" */
                }
-       }
-       if (trigtuple != fdw_trigtuple && trigtuple != newtuple)
-               heap_freetuple(trigtuple);
+               else if (newtuple != oldtuple)
+               {
+                       ExecForceStoreHeapTuple(newtuple, newslot);
 
-       if (newtuple != slottuple)
-       {
-               /*
-                * Return the modified tuple using the es_trig_tuple_slot.  We assume
-                * the tuple was allocated in per-tuple memory context, and therefore
-                * will go away by itself. The tuple table slot should not try to
-                * clear it.
-                */
-               TupleTableSlot *newslot = estate->es_trig_tuple_slot;
-               TupleDesc       tupdesc = RelationGetDescr(relinfo->ri_RelationDesc);
+                       if (should_free_new)
+                               heap_freetuple(oldtuple);
 
-               if (newslot->tts_tupleDescriptor != tupdesc)
-                       ExecSetSlotDescriptor(newslot, tupdesc);
-               ExecStoreHeapTuple(newtuple, newslot, false);
-               slot = newslot;
+                       /* signal tuple should be re-fetched if used */
+                       newtuple = NULL;
+               }
        }
-       return slot;
+       if (should_free_trig)
+               heap_freetuple(trigtuple);
+
+       return true;
 }
 
 void
 ExecARUpdateTriggers(EState *estate, ResultRelInfo *relinfo,
                                         ItemPointer tupleid,
                                         HeapTuple fdw_trigtuple,
-                                        HeapTuple newtuple,
+                                        TupleTableSlot *newslot,
                                         List *recheckIndexes,
                                         TransitionCaptureState *transition_capture)
 {
        TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
+       TupleTableSlot *oldslot = ExecGetTriggerOldSlot(estate, relinfo);
+
+       ExecClearTuple(oldslot);
 
        if ((trigdesc && trigdesc->trig_update_after_row) ||
                (transition_capture &&
                 (transition_capture->tcs_update_old_table ||
                  transition_capture->tcs_update_new_table)))
        {
-               HeapTuple       trigtuple;
-
                /*
                 * Note: if the UPDATE is converted into a DELETE+INSERT as part of
                 * update-partition-key operation, then this function is also called
@@ -3123,33 +3126,32 @@ ExecARUpdateTriggers(EState *estate, ResultRelInfo *relinfo,
                 * In such case, either old tuple or new tuple can be NULL.
                 */
                if (fdw_trigtuple == NULL && ItemPointerIsValid(tupleid))
-                       trigtuple = GetTupleForTrigger(estate,
-                                                                                  NULL,
-                                                                                  relinfo,
-                                                                                  tupleid,
-                                                                                  LockTupleExclusive,
-                                                                                  NULL);
-               else
-                       trigtuple = fdw_trigtuple;
+                       GetTupleForTrigger(estate,
+                                                          NULL,
+                                                          relinfo,
+                                                          tupleid,
+                                                          LockTupleExclusive,
+                                                          oldslot,
+                                                          NULL);
+               else if (fdw_trigtuple != NULL)
+                       ExecForceStoreHeapTuple(fdw_trigtuple, oldslot);
 
                AfterTriggerSaveEvent(estate, relinfo, TRIGGER_EVENT_UPDATE,
-                                                         true, trigtuple, newtuple, recheckIndexes,
+                                                         true, oldslot, newslot, recheckIndexes,
                                                          GetUpdatedColumns(relinfo, estate),
                                                          transition_capture);
-               if (trigtuple != fdw_trigtuple)
-                       heap_freetuple(trigtuple);
        }
 }
 
-TupleTableSlot *
+bool
 ExecIRUpdateTriggers(EState *estate, ResultRelInfo *relinfo,
-                                        HeapTuple trigtuple, TupleTableSlot *slot)
+                                        HeapTuple trigtuple, TupleTableSlot *newslot)
 {
        TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
-       HeapTuple       slottuple = ExecFetchSlotHeapTuple(slot, true, NULL);
-       HeapTuple       newtuple = slottuple;
+       TupleTableSlot *oldslot = ExecGetTriggerOldSlot(estate, relinfo);
+       HeapTuple       newtuple = false;
+       bool            should_free;
        TriggerData LocTriggerData;
-       HeapTuple       oldtuple;
        int                     i;
 
        LocTriggerData.type = T_TriggerData;
@@ -3159,9 +3161,13 @@ ExecIRUpdateTriggers(EState *estate, ResultRelInfo *relinfo,
        LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
        LocTriggerData.tg_oldtable = NULL;
        LocTriggerData.tg_newtable = NULL;
+
+       ExecForceStoreHeapTuple(trigtuple, oldslot);
+
        for (i = 0; i < trigdesc->numtriggers; i++)
        {
                Trigger    *trigger = &trigdesc->triggers[i];
+               HeapTuple       oldtuple;
 
                if (!TRIGGER_TYPE_MATCHES(trigger->tgtype,
                                                                  TRIGGER_TYPE_ROW,
@@ -3169,42 +3175,40 @@ ExecIRUpdateTriggers(EState *estate, ResultRelInfo *relinfo,
                                                                  TRIGGER_TYPE_UPDATE))
                        continue;
                if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
-                                                       NULL, trigtuple, newtuple))
+                                                       NULL, oldslot, newslot))
                        continue;
 
+               if (!newtuple)
+                       newtuple = ExecFetchSlotHeapTuple(newslot, true, &should_free);
+
+               LocTriggerData.tg_trigslot = oldslot;
                LocTriggerData.tg_trigtuple = trigtuple;
+               LocTriggerData.tg_newslot = newslot;
                LocTriggerData.tg_newtuple = oldtuple = newtuple;
-               LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
-               LocTriggerData.tg_newtuplebuf = InvalidBuffer;
+
                LocTriggerData.tg_trigger = trigger;
                newtuple = ExecCallTriggerFunc(&LocTriggerData,
                                                                           i,
                                                                           relinfo->ri_TrigFunctions,
                                                                           relinfo->ri_TrigInstrument,
                                                                           GetPerTupleMemoryContext(estate));
-               if (oldtuple != newtuple && oldtuple != slottuple)
-                       heap_freetuple(oldtuple);
                if (newtuple == NULL)
-                       return NULL;            /* "do nothing" */
-       }
+               {
+                       return false;           /* "do nothing" */
+               }
+               else if (newtuple != oldtuple)
+               {
+                       ExecForceStoreHeapTuple(newtuple, newslot);
 
-       if (newtuple != slottuple)
-       {
-               /*
-                * Return the modified tuple using the es_trig_tuple_slot.  We assume
-                * the tuple was allocated in per-tuple memory context, and therefore
-                * will go away by itself. The tuple table slot should not try to
-                * clear it.
-                */
-               TupleTableSlot *newslot = estate->es_trig_tuple_slot;
-               TupleDesc       tupdesc = RelationGetDescr(relinfo->ri_RelationDesc);
+                       if (should_free)
+                               heap_freetuple(oldtuple);
 
-               if (newslot->tts_tupleDescriptor != tupdesc)
-                       ExecSetSlotDescriptor(newslot, tupdesc);
-               ExecStoreHeapTuple(newtuple, newslot, false);
-               slot = newslot;
+                       /* signal tuple should be re-fetched if used */
+                       newtuple = NULL;
+               }
        }
-       return slot;
+
+       return true;
 }
 
 void
@@ -3227,10 +3231,11 @@ ExecBSTruncateTriggers(EState *estate, ResultRelInfo *relinfo)
        LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
        LocTriggerData.tg_trigtuple = NULL;
        LocTriggerData.tg_newtuple = NULL;
+       LocTriggerData.tg_trigslot = NULL;
+       LocTriggerData.tg_newslot = NULL;
        LocTriggerData.tg_oldtable = NULL;
        LocTriggerData.tg_newtable = NULL;
-       LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
-       LocTriggerData.tg_newtuplebuf = InvalidBuffer;
+
        for (i = 0; i < trigdesc->numtriggers; i++)
        {
                Trigger    *trigger = &trigdesc->triggers[i];
@@ -3270,18 +3275,24 @@ ExecASTruncateTriggers(EState *estate, ResultRelInfo *relinfo)
 }
 
 
-static HeapTuple
+static bool
 GetTupleForTrigger(EState *estate,
                                   EPQState *epqstate,
                                   ResultRelInfo *relinfo,
                                   ItemPointer tid,
                                   LockTupleMode lockmode,
+                                  TupleTableSlot *oldslot,
                                   TupleTableSlot **newSlot)
 {
        Relation        relation = relinfo->ri_RelationDesc;
-       HeapTupleData tuple;
-       HeapTuple       result;
+       HeapTuple       tuple;
        Buffer          buffer;
+       BufferHeapTupleTableSlot *boldslot;
+
+       Assert(TTS_IS_BUFFERTUPLE(oldslot));
+       ExecClearTuple(oldslot);
+       boldslot = (BufferHeapTupleTableSlot *) oldslot;
+       tuple = &boldslot->base.tupdata;
 
        if (newSlot != NULL)
        {
@@ -3297,8 +3308,8 @@ GetTupleForTrigger(EState *estate,
                 * lock tuple for update
                 */
 ltrmark:;
-               tuple.t_self = *tid;
-               test = heap_lock_tuple(relation, &tuple,
+               tuple->t_self = *tid;
+               test = heap_lock_tuple(relation, tuple,
                                                           estate->es_output_cid,
                                                           lockmode, LockWaitBlock,
                                                           false, &buffer, &hufd);
@@ -3322,9 +3333,11 @@ ltrmark:;
 
                                /* treat it as deleted; do not process */
                                ReleaseBuffer(buffer);
-                               return NULL;
+                               return false;
 
                        case HeapTupleMayBeUpdated:
+                               ExecStorePinnedBufferHeapTuple(tuple, oldslot, buffer);
+
                                break;
 
                        case HeapTupleUpdated:
@@ -3338,7 +3351,7 @@ ltrmark:;
                                                        (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
                                                         errmsg("tuple to be locked was already moved to another partition due to concurrent update")));
 
-                               if (!ItemPointerEquals(&hufd.ctid, &tuple.t_self))
+                               if (!ItemPointerEquals(&hufd.ctid, &tuple->t_self))
                                {
                                        /* it was updated, so look at the updated version */
                                        TupleTableSlot *epqslot;
@@ -3353,6 +3366,7 @@ ltrmark:;
                                        if (!TupIsNull(epqslot))
                                        {
                                                *tid = hufd.ctid;
+
                                                *newSlot = epqslot;
 
                                                /*
@@ -3369,7 +3383,7 @@ ltrmark:;
                                 * if tuple was deleted or PlanQual failed for updated tuple -
                                 * we must not process this tuple!
                                 */
-                               return NULL;
+                               return false;
 
                        case HeapTupleInvisible:
                                elog(ERROR, "attempted to lock invisible tuple");
@@ -3378,7 +3392,7 @@ ltrmark:;
                        default:
                                ReleaseBuffer(buffer);
                                elog(ERROR, "unrecognized heap_lock_tuple status: %u", test);
-                               return NULL;    /* keep compiler quiet */
+                               return false;   /* keep compiler quiet */
                }
        }
        else
@@ -3403,18 +3417,17 @@ ltrmark:;
 
                Assert(ItemIdIsNormal(lp));
 
-               tuple.t_data = (HeapTupleHeader) PageGetItem(page, lp);
-               tuple.t_len = ItemIdGetLength(lp);
-               tuple.t_self = *tid;
-               tuple.t_tableOid = RelationGetRelid(relation);
+               tuple->t_data = (HeapTupleHeader) PageGetItem(page, lp);
+               tuple->t_len = ItemIdGetLength(lp);
+               tuple->t_self = *tid;
+               tuple->t_tableOid = RelationGetRelid(relation);
 
                LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
-       }
 
-       result = heap_copytuple(&tuple);
-       ReleaseBuffer(buffer);
+               ExecStorePinnedBufferHeapTuple(tuple, oldslot, buffer);
+       }
 
-       return result;
+       return true;
 }
 
 /*
@@ -3424,7 +3437,7 @@ static bool
 TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
                           Trigger *trigger, TriggerEvent event,
                           Bitmapset *modifiedCols,
-                          HeapTuple oldtup, HeapTuple newtup)
+                          TupleTableSlot *oldslot, TupleTableSlot *newslot)
 {
        /* Check replication-role-dependent enable state */
        if (SessionReplicationRole == SESSION_REPLICATION_ROLE_REPLICA)
@@ -3466,11 +3479,8 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
        /* Check for WHEN clause */
        if (trigger->tgqual)
        {
-               TupleDesc       tupdesc = RelationGetDescr(relinfo->ri_RelationDesc);
                ExprState **predicate;
                ExprContext *econtext;
-               TupleTableSlot *oldslot = NULL;
-               TupleTableSlot *newslot = NULL;
                MemoryContext oldContext;
                int                     i;
 
@@ -3509,40 +3519,6 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
                 */
                econtext = GetPerTupleExprContext(estate);
 
-               /*
-                * Put OLD and NEW tuples into tupleslots for expression evaluation.
-                * These slots can be shared across the whole estate, but be careful
-                * that they have the current resultrel's tupdesc.
-                */
-               if (HeapTupleIsValid(oldtup))
-               {
-                       if (estate->es_trig_oldtup_slot == NULL)
-                       {
-                               oldContext = MemoryContextSwitchTo(estate->es_query_cxt);
-                               estate->es_trig_oldtup_slot =
-                                       ExecInitExtraTupleSlot(estate, NULL, &TTSOpsHeapTuple);
-                               MemoryContextSwitchTo(oldContext);
-                       }
-                       oldslot = estate->es_trig_oldtup_slot;
-                       if (oldslot->tts_tupleDescriptor != tupdesc)
-                               ExecSetSlotDescriptor(oldslot, tupdesc);
-                       ExecStoreHeapTuple(oldtup, oldslot, false);
-               }
-               if (HeapTupleIsValid(newtup))
-               {
-                       if (estate->es_trig_newtup_slot == NULL)
-                       {
-                               oldContext = MemoryContextSwitchTo(estate->es_query_cxt);
-                               estate->es_trig_newtup_slot =
-                                       ExecInitExtraTupleSlot(estate, NULL, &TTSOpsHeapTuple);
-                               MemoryContextSwitchTo(oldContext);
-                       }
-                       newslot = estate->es_trig_newtup_slot;
-                       if (newslot->tts_tupleDescriptor != tupdesc)
-                               ExecSetSlotDescriptor(newslot, tupdesc);
-                       ExecStoreHeapTuple(newtup, newslot, false);
-               }
-
                /*
                 * Finally evaluate the expression, making the old and/or new tuples
                 * available as INNER_VAR/OUTER_VAR respectively.
@@ -3872,12 +3848,15 @@ struct AfterTriggersTableData
        AfterTriggerEventList after_trig_events;        /* if so, saved list pointer */
        Tuplestorestate *old_tuplestore;        /* "old" transition table, if any */
        Tuplestorestate *new_tuplestore;        /* "new" transition table, if any */
+       TupleTableSlot *storeslot;      /* for converting to tuplestore's format */
 };
 
 static AfterTriggersData afterTriggers;
 
-static void AfterTriggerExecute(AfterTriggerEvent event,
-                                       Relation rel, TriggerDesc *trigdesc,
+static void AfterTriggerExecute(EState *estate,
+                                       AfterTriggerEvent event,
+                                       ResultRelInfo *relInfo,
+                                       TriggerDesc *trigdesc,
                                        FmgrInfo *finfo,
                                        Instrumentation *instr,
                                        MemoryContext per_tuple_context,
@@ -4211,27 +4190,33 @@ afterTriggerDeleteHeadEventChunk(AfterTriggersQueryData *qs)
  * ----------
  */
 static void
-AfterTriggerExecute(AfterTriggerEvent event,
-                                       Relation rel, TriggerDesc *trigdesc,
+AfterTriggerExecute(EState *estate,
+                                       AfterTriggerEvent event,
+                                       ResultRelInfo *relInfo,
+                                       TriggerDesc *trigdesc,
                                        FmgrInfo *finfo, Instrumentation *instr,
                                        MemoryContext per_tuple_context,
                                        TupleTableSlot *trig_tuple_slot1,
                                        TupleTableSlot *trig_tuple_slot2)
 {
+       Relation        rel = relInfo->ri_RelationDesc;
        AfterTriggerShared evtshared = GetTriggerSharedData(event);
        Oid                     tgoid = evtshared->ats_tgoid;
        TriggerData LocTriggerData;
        HeapTupleData tuple1;
        HeapTupleData tuple2;
        HeapTuple       rettuple;
-       Buffer          buffer1 = InvalidBuffer;
-       Buffer          buffer2 = InvalidBuffer;
        int                     tgindx;
+       bool            should_free_trig = false;
+       bool            should_free_new = false;
 
        /*
         * Locate trigger in trigdesc.
         */
        LocTriggerData.tg_trigger = NULL;
+       LocTriggerData.tg_trigslot = NULL;
+       LocTriggerData.tg_newslot = NULL;
+
        for (tgindx = 0; tgindx < trigdesc->numtriggers; tgindx++)
        {
                if (trigdesc->triggers[tgindx].tgoid == tgoid)
@@ -4273,7 +4258,7 @@ AfterTriggerExecute(AfterTriggerEvent event,
                case AFTER_TRIGGER_FDW_REUSE:
 
                        /*
-                        * Materialize tuple in the slot so that tg_trigtuple does not
+                        * Store tuple in the slot so that tg_trigtuple does not
                         * reference tuplestore memory.  (It is formally possible for the
                         * trigger function to queue trigger events that add to the same
                         * tuplestore, which can push other tuples out of memory.)  The
@@ -4281,31 +4266,38 @@ AfterTriggerExecute(AfterTriggerEvent event,
                         * that is stored as a heap tuple, constructed in different memory
                         * context, in the slot anyway.
                         */
-                       LocTriggerData.tg_trigtuple = ExecFetchSlotHeapTuple(trig_tuple_slot1,
-                                                                                                                                       true, NULL);
-                       LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
+                       LocTriggerData.tg_trigslot = trig_tuple_slot1;
+                       LocTriggerData.tg_trigtuple =
+                               ExecFetchSlotHeapTuple(trig_tuple_slot1, true, &should_free_trig);
 
+                       LocTriggerData.tg_newslot = trig_tuple_slot2;
                        LocTriggerData.tg_newtuple =
                                ((evtshared->ats_event & TRIGGER_EVENT_OPMASK) ==
                                 TRIGGER_EVENT_UPDATE) ?
-                               ExecFetchSlotHeapTuple(trig_tuple_slot2, true, NULL) : NULL;
-                       LocTriggerData.tg_newtuplebuf = InvalidBuffer;
+                               ExecFetchSlotHeapTuple(trig_tuple_slot2, true, &should_free_new) : NULL;
 
                        break;
 
                default:
                        if (ItemPointerIsValid(&(event->ate_ctid1)))
                        {
+                               Buffer buffer;
+
+                               LocTriggerData.tg_trigslot = ExecGetTriggerOldSlot(estate, relInfo);
+
                                ItemPointerCopy(&(event->ate_ctid1), &(tuple1.t_self));
-                               if (!heap_fetch(rel, SnapshotAny, &tuple1, &buffer1, false, NULL))
+                               if (!heap_fetch(rel, SnapshotAny, &tuple1, &buffer, false, NULL))
                                        elog(ERROR, "failed to fetch tuple1 for AFTER trigger");
-                               LocTriggerData.tg_trigtuple = &tuple1;
-                               LocTriggerData.tg_trigtuplebuf = buffer1;
+                               ExecStorePinnedBufferHeapTuple(&tuple1,
+                                                                                          LocTriggerData.tg_trigslot,
+                                                                                          buffer);
+                               LocTriggerData.tg_trigtuple =
+                                       ExecFetchSlotHeapTuple(LocTriggerData.tg_trigslot, false,
+                                                                                  &should_free_trig);
                        }
                        else
                        {
                                LocTriggerData.tg_trigtuple = NULL;
-                               LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
                        }
 
                        /* don't touch ctid2 if not there */
@@ -4313,16 +4305,23 @@ AfterTriggerExecute(AfterTriggerEvent event,
                                AFTER_TRIGGER_2CTID &&
                                ItemPointerIsValid(&(event->ate_ctid2)))
                        {
+                               Buffer buffer;
+
+                               LocTriggerData.tg_newslot = ExecGetTriggerNewSlot(estate, relInfo);
+
                                ItemPointerCopy(&(event->ate_ctid2), &(tuple2.t_self));
-                               if (!heap_fetch(rel, SnapshotAny, &tuple2, &buffer2, false, NULL))
+                               if (!heap_fetch(rel, SnapshotAny, &tuple2, &buffer, false, NULL))
                                        elog(ERROR, "failed to fetch tuple2 for AFTER trigger");
-                               LocTriggerData.tg_newtuple = &tuple2;
-                               LocTriggerData.tg_newtuplebuf = buffer2;
+                               ExecStorePinnedBufferHeapTuple(&tuple2,
+                                                                                          LocTriggerData.tg_newslot,
+                                                                                          buffer);
+                               LocTriggerData.tg_newtuple =
+                                       ExecFetchSlotHeapTuple(LocTriggerData.tg_newslot, false,
+                                                                                  &should_free_new);
                        }
                        else
                        {
                                LocTriggerData.tg_newtuple = NULL;
-                               LocTriggerData.tg_newtuplebuf = InvalidBuffer;
                        }
        }
 
@@ -4374,12 +4373,17 @@ AfterTriggerExecute(AfterTriggerEvent event,
                heap_freetuple(rettuple);
 
        /*
-        * Release buffers
+        * Release resources
         */
-       if (buffer1 != InvalidBuffer)
-               ReleaseBuffer(buffer1);
-       if (buffer2 != InvalidBuffer)
-               ReleaseBuffer(buffer2);
+       if (should_free_trig)
+               heap_freetuple(LocTriggerData.tg_trigtuple);
+       if (should_free_new)
+               heap_freetuple(LocTriggerData.tg_newtuple);
+
+       if (LocTriggerData.tg_trigslot)
+               ExecClearTuple(LocTriggerData.tg_trigslot);
+       if (LocTriggerData.tg_newslot)
+               ExecClearTuple(LocTriggerData.tg_newslot);
 
        /*
         * If doing EXPLAIN ANALYZE, stop charging time to this trigger, and count
@@ -4486,6 +4490,7 @@ afterTriggerInvokeEvents(AfterTriggerEventList *events,
        AfterTriggerEventChunk *chunk;
        MemoryContext per_tuple_context;
        bool            local_estate = false;
+       ResultRelInfo *rInfo;
        Relation        rel = NULL;
        TriggerDesc *trigdesc = NULL;
        FmgrInfo   *finfo = NULL;
@@ -4527,8 +4532,6 @@ afterTriggerInvokeEvents(AfterTriggerEventList *events,
                                 */
                                if (rel == NULL || RelationGetRelid(rel) != evtshared->ats_relid)
                                {
-                                       ResultRelInfo *rInfo;
-
                                        rInfo = ExecGetTriggerResultRel(estate, evtshared->ats_relid);
                                        rel = rInfo->ri_RelationDesc;
                                        trigdesc = rInfo->ri_TrigDesc;
@@ -4556,7 +4559,7 @@ afterTriggerInvokeEvents(AfterTriggerEventList *events,
                                 * still set, so recursive examinations of the event list
                                 * won't try to re-fire it.
                                 */
-                               AfterTriggerExecute(event, rel, trigdesc, finfo, instr,
+                               AfterTriggerExecute(estate, event, rInfo, trigdesc, finfo, instr,
                                                                        per_tuple_context, slot1, slot2);
 
                                /*
@@ -4600,6 +4603,7 @@ afterTriggerInvokeEvents(AfterTriggerEventList *events,
        if (local_estate)
        {
                ExecCleanUpTriggerState(estate);
+               ExecResetTupleTable(estate->es_tupleTable, false);
                FreeExecutorState(estate);
        }
 
@@ -5737,7 +5741,7 @@ AfterTriggerPendingOnRel(Oid relid)
 static void
 AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo,
                                          int event, bool row_trigger,
-                                         HeapTuple oldtup, HeapTuple newtup,
+                                         TupleTableSlot *oldslot, TupleTableSlot *newslot,
                                          List *recheckIndexes, Bitmapset *modifiedCols,
                                          TransitionCaptureState *transition_capture)
 {
@@ -5769,7 +5773,7 @@ AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo,
         */
        if (row_trigger && transition_capture != NULL)
        {
-               HeapTuple       original_insert_tuple = transition_capture->tcs_original_insert_tuple;
+               TupleTableSlot *original_insert_tuple = transition_capture->tcs_original_insert_tuple;
                TupleConversionMap *map = transition_capture->tcs_map;
                bool            delete_old_table = transition_capture->tcs_delete_old_table;
                bool            update_old_table = transition_capture->tcs_update_old_table;
@@ -5777,20 +5781,19 @@ AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo,
                bool            insert_new_table = transition_capture->tcs_insert_new_table;
 
                /*
-                * For INSERT events newtup should be non-NULL, for DELETE events
-                * oldtup should be non-NULL, whereas for UPDATE events normally both
-                * oldtup and newtup are non-NULL.  But for UPDATE events fired for
-                * capturing transition tuples during UPDATE partition-key row
-                * movement, oldtup is NULL when the event is for a row being
-                * inserted, whereas newtup is NULL when the event is for a row being
-                * deleted.
+                * For INSERT events NEW should be non-NULL, for DELETE events OLD
+                * should be non-NULL, whereas for UPDATE events normally both OLD and
+                * NEW are non-NULL.  But for UPDATE events fired for capturing
+                * transition tuples during UPDATE partition-key row movement, OLD is
+                * NULL when the event is for a row being inserted, whereas NEW is
+                * NULL when the event is for a row being deleted.
                 */
                Assert(!(event == TRIGGER_EVENT_DELETE && delete_old_table &&
-                                oldtup == NULL));
+                                TupIsNull(oldslot)));
                Assert(!(event == TRIGGER_EVENT_INSERT && insert_new_table &&
-                                newtup == NULL));
+                                TupIsNull(newslot)));
 
-               if (oldtup != NULL &&
+               if (!TupIsNull(oldslot) &&
                        ((event == TRIGGER_EVENT_DELETE && delete_old_table) ||
                         (event == TRIGGER_EVENT_UPDATE && update_old_table)))
                {
@@ -5800,15 +5803,24 @@ AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo,
 
                        if (map != NULL)
                        {
-                               HeapTuple       converted = execute_attr_map_tuple(oldtup, map);
+                               TupleTableSlot *storeslot;
+
+                               storeslot = transition_capture->tcs_private->storeslot;
+                               if (!storeslot)
+                               {
+                                       storeslot = ExecAllocTableSlot(&estate->es_tupleTable,
+                                                                                                  map->outdesc,
+                                                                                                  &TTSOpsVirtual);
+                                       transition_capture->tcs_private->storeslot = storeslot;
+                               }
 
-                               tuplestore_puttuple(old_tuplestore, converted);
-                               pfree(converted);
+                               execute_attr_map_slot(map->attrMap, oldslot, storeslot);
+                               tuplestore_puttupleslot(old_tuplestore, storeslot);
                        }
                        else
-                               tuplestore_puttuple(old_tuplestore, oldtup);
+                               tuplestore_puttupleslot(old_tuplestore, oldslot);
                }
-               if (newtup != NULL &&
+               if (!TupIsNull(newslot) &&
                        ((event == TRIGGER_EVENT_INSERT && insert_new_table) ||
                         (event == TRIGGER_EVENT_UPDATE && update_new_table)))
                {
@@ -5817,16 +5829,27 @@ AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo,
                        new_tuplestore = transition_capture->tcs_private->new_tuplestore;
 
                        if (original_insert_tuple != NULL)
-                               tuplestore_puttuple(new_tuplestore, original_insert_tuple);
+                               tuplestore_puttupleslot(new_tuplestore,
+                                                                               original_insert_tuple);
                        else if (map != NULL)
                        {
-                               HeapTuple       converted = execute_attr_map_tuple(newtup, map);
+                               TupleTableSlot *storeslot;
+
+                               storeslot = transition_capture->tcs_private->storeslot;
+
+                               if (!storeslot)
+                               {
+                                       storeslot = ExecAllocTableSlot(&estate->es_tupleTable,
+                                                                                                  map->outdesc,
+                                                                                                  &TTSOpsVirtual);
+                                       transition_capture->tcs_private->storeslot = storeslot;
+                               }
 
-                               tuplestore_puttuple(new_tuplestore, converted);
-                               pfree(converted);
+                               execute_attr_map_slot(map->attrMap, newslot, storeslot);
+                               tuplestore_puttupleslot(new_tuplestore, storeslot);
                        }
                        else
-                               tuplestore_puttuple(new_tuplestore, newtup);
+                               tuplestore_puttupleslot(new_tuplestore, newslot);
                }
 
                /*
@@ -5840,7 +5863,7 @@ AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo,
                        (event == TRIGGER_EVENT_DELETE && !trigdesc->trig_delete_after_row) ||
                        (event == TRIGGER_EVENT_INSERT && !trigdesc->trig_insert_after_row) ||
                        (event == TRIGGER_EVENT_UPDATE && !trigdesc->trig_update_after_row) ||
-                       (event == TRIGGER_EVENT_UPDATE && ((oldtup == NULL) ^ (newtup == NULL))))
+                       (event == TRIGGER_EVENT_UPDATE && (TupIsNull(oldslot) ^ TupIsNull(newslot))))
                        return;
        }
 
@@ -5862,15 +5885,15 @@ AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo,
                        tgtype_event = TRIGGER_TYPE_INSERT;
                        if (row_trigger)
                        {
-                               Assert(oldtup == NULL);
-                               Assert(newtup != NULL);
-                               ItemPointerCopy(&(newtup->t_self), &(new_event.ate_ctid1));
+                               Assert(oldslot == NULL);
+                               Assert(newslot != NULL);
+                               ItemPointerCopy(&(newslot->tts_tid), &(new_event.ate_ctid1));
                                ItemPointerSetInvalid(&(new_event.ate_ctid2));
                        }
                        else
                        {
-                               Assert(oldtup == NULL);
-                               Assert(newtup == NULL);
+                               Assert(oldslot == NULL);
+                               Assert(newslot == NULL);
                                ItemPointerSetInvalid(&(new_event.ate_ctid1));
                                ItemPointerSetInvalid(&(new_event.ate_ctid2));
                                cancel_prior_stmt_triggers(RelationGetRelid(rel),
@@ -5881,15 +5904,15 @@ AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo,
                        tgtype_event = TRIGGER_TYPE_DELETE;
                        if (row_trigger)
                        {
-                               Assert(oldtup != NULL);
-                               Assert(newtup == NULL);
-                               ItemPointerCopy(&(oldtup->t_self), &(new_event.ate_ctid1));
+                               Assert(oldslot != NULL);
+                               Assert(newslot == NULL);
+                               ItemPointerCopy(&(oldslot->tts_tid), &(new_event.ate_ctid1));
                                ItemPointerSetInvalid(&(new_event.ate_ctid2));
                        }
                        else
                        {
-                               Assert(oldtup == NULL);
-                               Assert(newtup == NULL);
+                               Assert(oldslot == NULL);
+                               Assert(newslot == NULL);
                                ItemPointerSetInvalid(&(new_event.ate_ctid1));
                                ItemPointerSetInvalid(&(new_event.ate_ctid2));
                                cancel_prior_stmt_triggers(RelationGetRelid(rel),
@@ -5900,15 +5923,15 @@ AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo,
                        tgtype_event = TRIGGER_TYPE_UPDATE;
                        if (row_trigger)
                        {
-                               Assert(oldtup != NULL);
-                               Assert(newtup != NULL);
-                               ItemPointerCopy(&(oldtup->t_self), &(new_event.ate_ctid1));
-                               ItemPointerCopy(&(newtup->t_self), &(new_event.ate_ctid2));
+                               Assert(oldslot != NULL);
+                               Assert(newslot != NULL);
+                               ItemPointerCopy(&(oldslot->tts_tid), &(new_event.ate_ctid1));
+                               ItemPointerCopy(&(newslot->tts_tid), &(new_event.ate_ctid2));
                        }
                        else
                        {
-                               Assert(oldtup == NULL);
-                               Assert(newtup == NULL);
+                               Assert(oldslot == NULL);
+                               Assert(newslot == NULL);
                                ItemPointerSetInvalid(&(new_event.ate_ctid1));
                                ItemPointerSetInvalid(&(new_event.ate_ctid2));
                                cancel_prior_stmt_triggers(RelationGetRelid(rel),
@@ -5917,8 +5940,8 @@ AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo,
                        break;
                case TRIGGER_EVENT_TRUNCATE:
                        tgtype_event = TRIGGER_TYPE_TRUNCATE;
-                       Assert(oldtup == NULL);
-                       Assert(newtup == NULL);
+                       Assert(oldslot == NULL);
+                       Assert(newslot == NULL);
                        ItemPointerSetInvalid(&(new_event.ate_ctid1));
                        ItemPointerSetInvalid(&(new_event.ate_ctid2));
                        break;
@@ -5945,7 +5968,7 @@ AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo,
                                                                  tgtype_event))
                        continue;
                if (!TriggerEnabled(estate, relinfo, trigger, event,
-                                                       modifiedCols, oldtup, newtup))
+                                                       modifiedCols, oldslot, newslot))
                        continue;
 
                if (relkind == RELKIND_FOREIGN_TABLE && row_trigger)
@@ -5972,7 +5995,7 @@ AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo,
                                case RI_TRIGGER_PK:
                                        /* Update or delete on trigger's PK table */
                                        if (!RI_FKey_pk_upd_check_required(trigger, rel,
-                                                                                                          oldtup, newtup))
+                                                                                                          oldslot, newslot))
                                        {
                                                /* skip queuing this event */
                                                continue;
@@ -5982,7 +6005,7 @@ AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo,
                                case RI_TRIGGER_FK:
                                        /* Update on trigger's FK table */
                                        if (!RI_FKey_fk_upd_check_required(trigger, rel,
-                                                                                                          oldtup, newtup))
+                                                                                                          oldslot, newslot))
                                        {
                                                /* skip queuing this event */
                                                continue;
@@ -6036,10 +6059,10 @@ AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo,
         */
        if (fdw_tuplestore)
        {
-               if (oldtup != NULL)
-                       tuplestore_puttuple(fdw_tuplestore, oldtup);
-               if (newtup != NULL)
-                       tuplestore_puttuple(fdw_tuplestore, newtup);
+               if (oldslot != NULL)
+                       tuplestore_puttupleslot(fdw_tuplestore, oldslot);
+               if (newslot != NULL)
+                       tuplestore_puttupleslot(fdw_tuplestore, newslot);
        }
 }
 
index 9a20460e762678e27d95dd9a9f9a53931a98413e..00d8e8fc585ba02a7373685df7eeeac4522e711c 100644 (file)
@@ -975,9 +975,6 @@ InitPlan(QueryDesc *queryDesc, int eflags)
         * Initialize the executor's tuple table to empty.
         */
        estate->es_tupleTable = NIL;
-       estate->es_trig_tuple_slot = NULL;
-       estate->es_trig_oldtup_slot = NULL;
-       estate->es_trig_newtup_slot = NULL;
 
        /* mark EvalPlanQual not active */
        estate->es_epqTuple = NULL;
@@ -1324,6 +1321,9 @@ InitResultRelInfo(ResultRelInfo *resultRelInfo,
        resultRelInfo->ri_projectReturning = NULL;
        resultRelInfo->ri_onConflictArbiterIndexes = NIL;
        resultRelInfo->ri_onConflict = NULL;
+       resultRelInfo->ri_ReturningSlot = NULL;
+       resultRelInfo->ri_TrigOldSlot = NULL;
+       resultRelInfo->ri_TrigNewSlot = NULL;
 
        /*
         * Partition constraint, which also includes the partition constraint of
index 663d6e326427355e61293daa8dd7258e43278339..5c5aa96e7fb029acac1f5434993ae97b31e34ee4 100644 (file)
@@ -403,10 +403,8 @@ ExecSimpleRelationInsert(EState *estate, TupleTableSlot *slot)
        if (resultRelInfo->ri_TrigDesc &&
                resultRelInfo->ri_TrigDesc->trig_insert_before_row)
        {
-               slot = ExecBRInsertTriggers(estate, resultRelInfo, slot);
-
-               if (slot == NULL)               /* "do nothing" */
-                       skip_tuple = true;
+               if (!ExecBRInsertTriggers(estate, resultRelInfo, slot))
+                       skip_tuple = true;              /* "do nothing" */
        }
 
        if (!skip_tuple)
@@ -432,7 +430,7 @@ ExecSimpleRelationInsert(EState *estate, TupleTableSlot *slot)
                                                                                                   NIL);
 
                /* AFTER ROW INSERT Triggers */
-               ExecARInsertTriggers(estate, resultRelInfo, tuple,
+               ExecARInsertTriggers(estate, resultRelInfo, slot,
                                                         recheckIndexes, NULL);
 
                /*
@@ -475,11 +473,10 @@ ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate,
        if (resultRelInfo->ri_TrigDesc &&
                resultRelInfo->ri_TrigDesc->trig_update_before_row)
        {
-               slot = ExecBRUpdateTriggers(estate, epqstate, resultRelInfo,
-                                                                       &hsearchslot->tuple->t_self, NULL, slot);
-
-               if (slot == NULL)               /* "do nothing" */
-                       skip_tuple = true;
+               if (!ExecBRUpdateTriggers(estate, epqstate, resultRelInfo,
+                                                                 &hsearchslot->tuple->t_self,
+                                                                 NULL, slot))
+                       skip_tuple = true;              /* "do nothing" */
        }
 
        if (!skip_tuple)
@@ -507,7 +504,8 @@ ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate,
 
                /* AFTER ROW UPDATE Triggers */
                ExecARUpdateTriggers(estate, resultRelInfo,
-                                                        &hsearchslot->tuple->t_self, NULL, tuple,
+                                                        &(tuple->t_self),
+                                                        NULL, slot,
                                                         recheckIndexes, NULL);
 
                list_free(recheckIndexes);
@@ -540,8 +538,9 @@ ExecSimpleRelationDelete(EState *estate, EPQState *epqstate,
                resultRelInfo->ri_TrigDesc->trig_delete_before_row)
        {
                skip_tuple = !ExecBRDeleteTriggers(estate, epqstate, resultRelInfo,
-                                                                                  &hsearchslot->tuple->t_self, NULL,
-                                                                                  NULL);
+                                                                                  &hsearchslot->tuple->t_self,
+                                                                                  NULL, NULL);
+
        }
 
        if (!skip_tuple)
index e04c048b54399c434f36028f786b9e148f62326c..3a1425182144191763935c53a84682fac96a8415 100644 (file)
@@ -119,6 +119,7 @@ tts_virtual_clear(TupleTableSlot *slot)
 
        slot->tts_nvalid = 0;
        slot->tts_flags |= TTS_FLAG_EMPTY;
+       ItemPointerSetInvalid(&slot->tts_tid);
 }
 
 /*
@@ -314,6 +315,7 @@ tts_heap_clear(TupleTableSlot *slot)
 
        slot->tts_nvalid = 0;
        slot->tts_flags |= TTS_FLAG_EMPTY;
+       ItemPointerSetInvalid(&slot->tts_tid);
        hslot->off = 0;
        hslot->tuple = NULL;
 }
@@ -477,6 +479,7 @@ tts_minimal_clear(TupleTableSlot *slot)
 
        slot->tts_nvalid = 0;
        slot->tts_flags |= TTS_FLAG_EMPTY;
+       ItemPointerSetInvalid(&slot->tts_tid);
        mslot->off = 0;
        mslot->mintuple = NULL;
 }
@@ -658,6 +661,7 @@ tts_buffer_heap_clear(TupleTableSlot *slot)
 
        slot->tts_nvalid = 0;
        slot->tts_flags |= TTS_FLAG_EMPTY;
+       ItemPointerSetInvalid(&slot->tts_tid);
        bslot->base.tuple = NULL;
        bslot->base.off = 0;
        bslot->buffer = InvalidBuffer;
index 312a0dc8056c56c6846c64a982f357895b3f80fe..5136269348140e4d9bb06bf19a281f25bfbf6b4f 100644 (file)
@@ -131,9 +131,6 @@ CreateExecutorState(void)
        estate->es_tuple_routing_result_relations = NIL;
 
        estate->es_trig_target_relations = NIL;
-       estate->es_trig_tuple_slot = NULL;
-       estate->es_trig_oldtup_slot = NULL;
-       estate->es_trig_newtup_slot = NULL;
 
        estate->es_param_list_info = NULL;
        estate->es_param_exec_vals = NULL;
@@ -1102,3 +1099,69 @@ ExecCleanTargetListLength(List *targetlist)
        }
        return len;
 }
+
+/*
+ * Return a relInfo's tuple slot for a trigger's OLD tuples.
+ */
+TupleTableSlot *
+ExecGetTriggerOldSlot(EState *estate, ResultRelInfo *relInfo)
+{
+       if (relInfo->ri_TrigOldSlot == NULL)
+       {
+               Relation        rel = relInfo->ri_RelationDesc;
+               MemoryContext oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
+
+               relInfo->ri_TrigOldSlot =
+                       ExecInitExtraTupleSlot(estate,
+                                                                  RelationGetDescr(rel),
+                                                                  &TTSOpsBufferHeapTuple);
+
+               MemoryContextSwitchTo(oldcontext);
+       }
+
+       return relInfo->ri_TrigOldSlot;
+}
+
+/*
+ * Return a relInfo's tuple slot for a trigger's NEW tuples.
+ */
+TupleTableSlot *
+ExecGetTriggerNewSlot(EState *estate, ResultRelInfo *relInfo)
+{
+       if (relInfo->ri_TrigNewSlot == NULL)
+       {
+               Relation        rel = relInfo->ri_RelationDesc;
+               MemoryContext oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
+
+               relInfo->ri_TrigNewSlot =
+                       ExecInitExtraTupleSlot(estate,
+                                                                  RelationGetDescr(rel),
+                                                                  &TTSOpsBufferHeapTuple);
+
+               MemoryContextSwitchTo(oldcontext);
+       }
+
+       return relInfo->ri_TrigNewSlot;
+}
+
+/*
+ * Return a relInfo's tuple slot for processing returning tuples.
+ */
+TupleTableSlot *
+ExecGetReturningSlot(EState *estate, ResultRelInfo *relInfo)
+{
+       if (relInfo->ri_ReturningSlot == NULL)
+       {
+               Relation        rel = relInfo->ri_RelationDesc;
+               MemoryContext oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
+
+               relInfo->ri_ReturningSlot =
+                       ExecInitExtraTupleSlot(estate,
+                                                                  RelationGetDescr(rel),
+                                                                  &TTSOpsBufferHeapTuple);
+
+               MemoryContextSwitchTo(oldcontext);
+       }
+
+       return relInfo->ri_ReturningSlot;
+}
index fe62da06ead1c2eb5cba191fef38c7b552d60acb..76175aaa6bed21b496d9fcb4189d1314d43a4ee3 100644 (file)
@@ -255,7 +255,6 @@ ExecInsert(ModifyTableState *mtstate,
                   EState *estate,
                   bool canSetTag)
 {
-       HeapTuple       tuple;
        ResultRelInfo *resultRelInfo;
        Relation        resultRelationDesc;
        List       *recheckIndexes = NIL;
@@ -264,11 +263,7 @@ ExecInsert(ModifyTableState *mtstate,
        ModifyTable *node = (ModifyTable *) mtstate->ps.plan;
        OnConflictAction onconflict = node->onConflictAction;
 
-       /*
-        * get the heap tuple out of the tuple table slot, making sure we have a
-        * writable copy
-        */
-       tuple = ExecFetchSlotHeapTuple(slot, true, NULL);
+       ExecMaterializeSlot(slot);
 
        /*
         * get information on the (current) result relation
@@ -288,26 +283,16 @@ ExecInsert(ModifyTableState *mtstate,
        if (resultRelInfo->ri_TrigDesc &&
                resultRelInfo->ri_TrigDesc->trig_insert_before_row)
        {
-               slot = ExecBRInsertTriggers(estate, resultRelInfo, slot);
-
-               if (slot == NULL)               /* "do nothing" */
-                       return NULL;
-
-               /* trigger might have changed tuple */
-               tuple = ExecFetchSlotHeapTuple(slot, true, NULL);
+               if (!ExecBRInsertTriggers(estate, resultRelInfo, slot))
+                       return NULL;            /* "do nothing" */
        }
 
        /* INSTEAD OF ROW INSERT Triggers */
        if (resultRelInfo->ri_TrigDesc &&
                resultRelInfo->ri_TrigDesc->trig_insert_instead_row)
        {
-               slot = ExecIRInsertTriggers(estate, resultRelInfo, slot);
-
-               if (slot == NULL)               /* "do nothing" */
-                       return NULL;
-
-               /* trigger might have changed tuple */
-               tuple = ExecFetchSlotHeapTuple(slot, true, NULL);
+               if (!ExecIRInsertTriggers(estate, resultRelInfo, slot))
+                       return NULL;            /* "do nothing" */
        }
        else if (resultRelInfo->ri_FdwRoutine)
        {
@@ -322,9 +307,6 @@ ExecInsert(ModifyTableState *mtstate,
                if (slot == NULL)               /* "do nothing" */
                        return NULL;
 
-               /* FDW might have changed tuple */
-               tuple = ExecFetchSlotHeapTuple(slot, true, NULL);
-
                /*
                 * AFTER ROW Triggers or RETURNING expressions might reference the
                 * tableoid column, so (re-)initialize tts_tableOid before evaluating
@@ -336,6 +318,7 @@ ExecInsert(ModifyTableState *mtstate,
        else
        {
                WCOKind         wco_kind;
+               HeapTuple       inserttuple;
 
                /*
                 * Constraints might reference the tableoid column, so (re-)initialize
@@ -441,6 +424,8 @@ ExecInsert(ModifyTableState *mtstate,
                                }
                        }
 
+                       inserttuple = ExecFetchSlotHeapTuple(slot, true, NULL);
+
                        /*
                         * Before we start insertion proper, acquire our "speculative
                         * insertion lock".  Others can use that to wait for us to decide
@@ -448,26 +433,26 @@ ExecInsert(ModifyTableState *mtstate,
                         * waiting for the whole transaction to complete.
                         */
                        specToken = SpeculativeInsertionLockAcquire(GetCurrentTransactionId());
-                       HeapTupleHeaderSetSpeculativeToken(tuple->t_data, specToken);
+                       HeapTupleHeaderSetSpeculativeToken(inserttuple->t_data, specToken);
 
                        /* insert the tuple, with the speculative token */
-                       heap_insert(resultRelationDesc, tuple,
+                       heap_insert(resultRelationDesc, inserttuple,
                                                estate->es_output_cid,
                                                HEAP_INSERT_SPECULATIVE,
                                                NULL);
                        slot->tts_tableOid = RelationGetRelid(resultRelationDesc);
-                       ItemPointerCopy(&tuple->t_self, &slot->tts_tid);
+                       ItemPointerCopy(&inserttuple->t_self, &slot->tts_tid);
 
                        /* insert index entries for tuple */
-                       recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self),
+                       recheckIndexes = ExecInsertIndexTuples(slot, &(inserttuple->t_self),
                                                                                                   estate, true, &specConflict,
                                                                                                   arbiterIndexes);
 
                        /* adjust the tuple's state accordingly */
                        if (!specConflict)
-                               heap_finish_speculative(resultRelationDesc, tuple);
+                               heap_finish_speculative(resultRelationDesc, inserttuple);
                        else
-                               heap_abort_speculative(resultRelationDesc, tuple);
+                               heap_abort_speculative(resultRelationDesc, inserttuple);
 
                        /*
                         * Wake up anyone waiting for our decision.  They will re-check
@@ -499,15 +484,16 @@ ExecInsert(ModifyTableState *mtstate,
                         * Note: heap_insert returns the tid (location) of the new tuple
                         * in the t_self field.
                         */
-                       heap_insert(resultRelationDesc, tuple,
+                       inserttuple = ExecFetchSlotHeapTuple(slot, true, NULL);
+                       heap_insert(resultRelationDesc, inserttuple,
                                                estate->es_output_cid,
                                                0, NULL);
                        slot->tts_tableOid = RelationGetRelid(resultRelationDesc);
-                       ItemPointerCopy(&tuple->t_self, &slot->tts_tid);
+                       ItemPointerCopy(&inserttuple->t_self, &slot->tts_tid);
 
                        /* insert index entries for tuple */
                        if (resultRelInfo->ri_NumIndices > 0)
-                               recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self),
+                               recheckIndexes = ExecInsertIndexTuples(slot, &(inserttuple->t_self),
                                                                                                           estate, false, NULL,
                                                                                                           NIL);
                }
@@ -531,7 +517,7 @@ ExecInsert(ModifyTableState *mtstate,
        {
                ExecARUpdateTriggers(estate, resultRelInfo, NULL,
                                                         NULL,
-                                                        tuple,
+                                                        slot,
                                                         NULL,
                                                         mtstate->mt_transition_capture);
 
@@ -543,7 +529,7 @@ ExecInsert(ModifyTableState *mtstate,
        }
 
        /* AFTER ROW INSERT Triggers */
-       ExecARInsertTriggers(estate, resultRelInfo, tuple, recheckIndexes,
+       ExecARInsertTriggers(estate, resultRelInfo, slot, recheckIndexes,
                                                 ar_insert_trig_tcs);
 
        list_free(recheckIndexes);
@@ -603,7 +589,7 @@ ExecDelete(ModifyTableState *mtstate,
                   bool canSetTag,
                   bool changingPart,
                   bool *tupleDeleted,
-                  TupleTableSlot **epqslot)
+                  TupleTableSlot **epqreturnslot)
 {
        ResultRelInfo *resultRelInfo;
        Relation        resultRelationDesc;
@@ -628,7 +614,7 @@ ExecDelete(ModifyTableState *mtstate,
                bool            dodelete;
 
                dodelete = ExecBRDeleteTriggers(estate, epqstate, resultRelInfo,
-                                                                               tupleid, oldtuple, epqslot);
+                                                                               tupleid, oldtuple, epqreturnslot);
 
                if (!dodelete)                  /* "do nothing" */
                        return NULL;
@@ -651,14 +637,10 @@ ExecDelete(ModifyTableState *mtstate,
                /*
                 * delete from foreign table: let the FDW do it
                 *
-                * We offer the trigger tuple slot as a place to store RETURNING data,
-                * although the FDW can return some other slot if it wants.  Set up
-                * the slot's tupdesc so the FDW doesn't need to do that for itself.
+                * We offer the returning slot as a place to store RETURNING data,
+                * although the FDW can return some other slot if it wants.
                 */
-               slot = estate->es_trig_tuple_slot;
-               if (slot->tts_tupleDescriptor != RelationGetDescr(resultRelationDesc))
-                       ExecSetSlotDescriptor(slot, RelationGetDescr(resultRelationDesc));
-
+               slot = ExecGetReturningSlot(estate, resultRelInfo);
                slot = resultRelInfo->ri_FdwRoutine->ExecForeignDelete(estate,
                                                                                                                           resultRelInfo,
                                                                                                                           slot,
@@ -673,6 +655,8 @@ ExecDelete(ModifyTableState *mtstate,
                 */
                if (TTS_EMPTY(slot))
                        ExecStoreAllNullTuple(slot);
+               ExecMaterializeSlot(slot);
+
                slot->tts_tableOid = RelationGetRelid(resultRelationDesc);
        }
        else
@@ -762,9 +746,9 @@ ldelete:;
                                                 * If requested, skip delete and pass back the updated
                                                 * row.
                                                 */
-                                               if (epqslot)
+                                               if (epqreturnslot)
                                                {
-                                                       *epqslot = my_epqslot;
+                                                       *epqreturnslot = my_epqslot;
                                                        return NULL;
                                                }
                                                else
@@ -832,34 +816,37 @@ ldelete:;
                 * gotta fetch it.  We can use the trigger tuple slot.
                 */
                TupleTableSlot *rslot;
-               HeapTupleData deltuple;
-               Buffer          delbuffer;
 
                if (resultRelInfo->ri_FdwRoutine)
                {
                        /* FDW must have provided a slot containing the deleted row */
                        Assert(!TupIsNull(slot));
-                       delbuffer = InvalidBuffer;
                }
                else
                {
-                       slot = estate->es_trig_tuple_slot;
+                       slot = ExecGetReturningSlot(estate, resultRelInfo);
                        if (oldtuple != NULL)
                        {
-                               deltuple = *oldtuple;
-                               delbuffer = InvalidBuffer;
+                               ExecForceStoreHeapTuple(oldtuple, slot);
                        }
                        else
                        {
-                               deltuple.t_self = *tupleid;
+                               BufferHeapTupleTableSlot *bslot;
+                               HeapTuple deltuple;
+                               Buffer buffer;
+
+                               Assert(TTS_IS_BUFFERTUPLE(slot));
+                               ExecClearTuple(slot);
+                               bslot = (BufferHeapTupleTableSlot *) slot;
+                               deltuple = &bslot->base.tupdata;
+
+                               deltuple->t_self = *tupleid;
                                if (!heap_fetch(resultRelationDesc, SnapshotAny,
-                                                               &deltuple, &delbuffer, false, NULL))
+                                                               deltuple, &buffer, false, NULL))
                                        elog(ERROR, "failed to fetch deleted tuple for DELETE RETURNING");
-                       }
 
-                       if (slot->tts_tupleDescriptor != RelationGetDescr(resultRelationDesc))
-                               ExecSetSlotDescriptor(slot, RelationGetDescr(resultRelationDesc));
-                       ExecStoreHeapTuple(&deltuple, slot, false);
+                               ExecStorePinnedBufferHeapTuple(deltuple, slot, buffer);
+                       }
                }
 
                rslot = ExecProcessReturning(resultRelInfo, slot, planSlot);
@@ -871,8 +858,6 @@ ldelete:;
                ExecMaterializeSlot(rslot);
 
                ExecClearTuple(slot);
-               if (BufferIsValid(delbuffer))
-                       ReleaseBuffer(delbuffer);
 
                return rslot;
        }
@@ -912,7 +897,7 @@ ExecUpdate(ModifyTableState *mtstate,
                   EState *estate,
                   bool canSetTag)
 {
-       HeapTuple       tuple;
+       HeapTuple       updatetuple;
        ResultRelInfo *resultRelInfo;
        Relation        resultRelationDesc;
        HTSU_Result result;
@@ -926,11 +911,7 @@ ExecUpdate(ModifyTableState *mtstate,
        if (IsBootstrapProcessingMode())
                elog(ERROR, "cannot UPDATE during bootstrap");
 
-       /*
-        * get the heap tuple out of the tuple table slot, making sure we have a
-        * writable copy
-        */
-       tuple = ExecFetchSlotHeapTuple(slot, true, NULL);
+       ExecMaterializeSlot(slot);
 
        /*
         * get information on the (current) result relation
@@ -942,28 +923,18 @@ ExecUpdate(ModifyTableState *mtstate,
        if (resultRelInfo->ri_TrigDesc &&
                resultRelInfo->ri_TrigDesc->trig_update_before_row)
        {
-               slot = ExecBRUpdateTriggers(estate, epqstate, resultRelInfo,
-                                                                       tupleid, oldtuple, slot);
-
-               if (slot == NULL)               /* "do nothing" */
-                       return NULL;
-
-               /* trigger might have changed tuple */
-               tuple = ExecFetchSlotHeapTuple(slot, true, NULL);
+               if (!ExecBRUpdateTriggers(estate, epqstate, resultRelInfo,
+                                                                 tupleid, oldtuple, slot))
+                       return NULL;        /* "do nothing" */
        }
 
        /* INSTEAD OF ROW UPDATE Triggers */
        if (resultRelInfo->ri_TrigDesc &&
                resultRelInfo->ri_TrigDesc->trig_update_instead_row)
        {
-               slot = ExecIRUpdateTriggers(estate, resultRelInfo,
-                                                                       oldtuple, slot);
-
-               if (slot == NULL)               /* "do nothing" */
-                       return NULL;
-
-               /* trigger might have changed tuple */
-               tuple = ExecFetchSlotHeapTuple(slot, true, NULL);
+               if (!ExecIRUpdateTriggers(estate, resultRelInfo,
+                                                                 oldtuple, slot))
+                       return NULL;        /* "do nothing" */
        }
        else if (resultRelInfo->ri_FdwRoutine)
        {
@@ -978,9 +949,6 @@ ExecUpdate(ModifyTableState *mtstate,
                if (slot == NULL)               /* "do nothing" */
                        return NULL;
 
-               /* FDW might have changed tuple */
-               tuple = ExecFetchSlotHeapTuple(slot, true, NULL);
-
                /*
                 * AFTER ROW Triggers or RETURNING expressions might reference the
                 * tableoid column, so (re-)initialize tts_tableOid before evaluating
@@ -1107,7 +1075,6 @@ lreplace:;
                                else
                                {
                                        slot = ExecFilterJunk(resultRelInfo->ri_junkFilter, epqslot);
-                                       tuple = ExecFetchSlotHeapTuple(slot, true, NULL);
                                        goto lreplace;
                                }
                        }
@@ -1178,12 +1145,14 @@ lreplace:;
                 * needed for referential integrity updates in transaction-snapshot
                 * mode transactions.
                 */
-               result = heap_update(resultRelationDesc, tupleid, tuple,
+               updatetuple = ExecFetchSlotHeapTuple(slot, true, NULL);
+               result = heap_update(resultRelationDesc, tupleid,
+                                                        updatetuple,
                                                         estate->es_output_cid,
                                                         estate->es_crosscheck_snapshot,
                                                         true /* wait for commit */ ,
                                                         &hufd, &lockmode);
-               ItemPointerCopy(&tuple->t_self, &slot->tts_tid);
+               ItemPointerCopy(&updatetuple->t_self, &slot->tts_tid);
 
                switch (result)
                {
@@ -1249,7 +1218,6 @@ lreplace:;
                                        {
                                                *tupleid = hufd.ctid;
                                                slot = ExecFilterJunk(resultRelInfo->ri_junkFilter, epqslot);
-                                               tuple = ExecFetchSlotHeapTuple(slot, true, NULL);
                                                goto lreplace;
                                        }
                                }
@@ -1277,8 +1245,8 @@ lreplace:;
                 *
                 * If it's a HOT update, we mustn't insert new index entries.
                 */
-               if (resultRelInfo->ri_NumIndices > 0 && !HeapTupleIsHeapOnly(tuple))
-                       recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self),
+               if (resultRelInfo->ri_NumIndices > 0 && !HeapTupleIsHeapOnly(updatetuple))
+                       recheckIndexes = ExecInsertIndexTuples(slot, &(updatetuple->t_self),
                                                                                                   estate, false, NULL, NIL);
        }
 
@@ -1286,7 +1254,7 @@ lreplace:;
                (estate->es_processed)++;
 
        /* AFTER ROW UPDATE Triggers */
-       ExecARUpdateTriggers(estate, resultRelInfo, tupleid, oldtuple, tuple,
+       ExecARUpdateTriggers(estate, resultRelInfo, tupleid, oldtuple, slot,
                                                 recheckIndexes,
                                                 mtstate->operation == CMD_INSERT ?
                                                 mtstate->mt_oc_transition_capture :
@@ -1669,7 +1637,6 @@ ExecPrepareTupleRouting(ModifyTableState *mtstate,
        ModifyTable *node;
        ResultRelInfo *partrel;
        PartitionRoutingInfo *partrouteinfo;
-       HeapTuple       tuple;
        TupleConversionMap *map;
 
        /*
@@ -1688,9 +1655,6 @@ ExecPrepareTupleRouting(ModifyTableState *mtstate,
         */
        estate->es_result_relation_info = partrel;
 
-       /* Get the heap tuple out of the given slot. */
-       tuple = ExecFetchSlotHeapTuple(slot, true, NULL);
-
        /*
         * If we're capturing transition tuples, we might need to convert from the
         * partition rowtype to root partitioned table's rowtype.
@@ -1714,7 +1678,7 @@ ExecPrepareTupleRouting(ModifyTableState *mtstate,
                         * Otherwise, just remember the original unconverted tuple, to
                         * avoid a needless round trip conversion.
                         */
-                       mtstate->mt_transition_capture->tcs_original_insert_tuple = tuple;
+                       mtstate->mt_transition_capture->tcs_original_insert_tuple = slot;
                        mtstate->mt_transition_capture->tcs_map = NULL;
                }
        }
@@ -2541,16 +2505,6 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
                }
        }
 
-       /*
-        * Set up a tuple table slot for use for trigger output tuples. In a plan
-        * containing multiple ModifyTable nodes, all can share one such slot, so
-        * we keep it in the estate. The tuple being inserted doesn't come from a
-        * buffer.
-        */
-       if (estate->es_trig_tuple_slot == NULL)
-               estate->es_trig_tuple_slot = ExecInitExtraTupleSlot(estate, NULL,
-                                                                                                                       &TTSOpsHeapTuple);
-
        /*
         * Lastly, if this is not the primary (canSetTag) ModifyTable node, add it
         * to estate->es_auxmodifytables so that it will be run to completion by
index f9516515bc4783bc5a6b9a55d8944b946422fa18..27934cb4a90b34f2bdf615feebe8aa0cc338d61a 100644 (file)
@@ -196,11 +196,6 @@ create_estate_for_relation(LogicalRepRelMapEntry *rel)
 
        estate->es_output_cid = GetCurrentCommandId(true);
 
-       /* Triggers might need a slot */
-       if (resultRelInfo->ri_TrigDesc)
-               estate->es_trig_tuple_slot = ExecInitExtraTupleSlot(estate, NULL,
-                                                                                                                       &TTSOpsVirtual);
-
        /* Prepare to catch AFTER triggers. */
        AfterTriggerBeginQuery();
 
index e1aa3d0044f17461c3086a3c0d9724252665ef05..5b4579fe83e8ea98bf3f853ce77a0bd0bf229e40 100644 (file)
@@ -192,7 +192,7 @@ static int  ri_constraint_cache_valid_count = 0;
  * ----------
  */
 static bool ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel,
-                                 HeapTuple old_row,
+                                 TupleTableSlot *oldslot,
                                  const RI_ConstraintInfo *riinfo);
 static Datum ri_restrict(TriggerData *trigdata, bool is_no_action);
 static Datum ri_setnull(TriggerData *trigdata);
@@ -205,12 +205,12 @@ static void ri_GenerateQual(StringInfo buf,
                                Oid opoid,
                                const char *rightop, Oid rightoptype);
 static void ri_GenerateQualCollation(StringInfo buf, Oid collation);
-static int ri_NullCheck(TupleDesc tupdesc, HeapTuple tup,
+static int ri_NullCheck(TupleDesc tupdesc, TupleTableSlot *slot,
                         const RI_ConstraintInfo *riinfo, bool rel_is_pk);
 static void ri_BuildQueryKey(RI_QueryKey *key,
                                 const RI_ConstraintInfo *riinfo,
                                 int32 constr_queryno);
-static bool ri_KeysEqual(Relation rel, HeapTuple oldtup, HeapTuple newtup,
+static bool ri_KeysEqual(Relation rel, TupleTableSlot *oldslot, TupleTableSlot *newslot,
                         const RI_ConstraintInfo *riinfo, bool rel_is_pk);
 static bool ri_AttributesEqual(Oid eq_opr, Oid typeid,
                                   Datum oldvalue, Datum newvalue);
@@ -232,14 +232,14 @@ static SPIPlanPtr ri_PlanCheck(const char *querystr, int nargs, Oid *argtypes,
 static bool ri_PerformCheck(const RI_ConstraintInfo *riinfo,
                                RI_QueryKey *qkey, SPIPlanPtr qplan,
                                Relation fk_rel, Relation pk_rel,
-                               HeapTuple old_tuple, HeapTuple new_tuple,
+                               TupleTableSlot *oldslot, TupleTableSlot *newslot,
                                bool detectNewRows, int expect_OK);
-static void ri_ExtractValues(Relation rel, HeapTuple tup,
+static void ri_ExtractValues(Relation rel, TupleTableSlot *slot,
                                 const RI_ConstraintInfo *riinfo, bool rel_is_pk,
                                 Datum *vals, char *nulls);
 static void ri_ReportViolation(const RI_ConstraintInfo *riinfo,
                                   Relation pk_rel, Relation fk_rel,
-                                  HeapTuple violator, TupleDesc tupdesc,
+                                  TupleTableSlot *violator, TupleDesc tupdesc,
                                   int queryno) pg_attribute_noreturn();
 
 
@@ -255,8 +255,7 @@ RI_FKey_check(TriggerData *trigdata)
        const RI_ConstraintInfo *riinfo;
        Relation        fk_rel;
        Relation        pk_rel;
-       HeapTuple       new_row;
-       Buffer          new_row_buf;
+       TupleTableSlot *newslot;
        RI_QueryKey qkey;
        SPIPlanPtr      qplan;
        int                     i;
@@ -268,15 +267,9 @@ RI_FKey_check(TriggerData *trigdata)
                                                                        trigdata->tg_relation, false);
 
        if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
-       {
-               new_row = trigdata->tg_newtuple;
-               new_row_buf = trigdata->tg_newtuplebuf;
-       }
+               newslot = trigdata->tg_newslot;
        else
-       {
-               new_row = trigdata->tg_trigtuple;
-               new_row_buf = trigdata->tg_trigtuplebuf;
-       }
+               newslot = trigdata->tg_trigslot;
 
        /*
         * We should not even consider checking the row if it is no longer valid,
@@ -285,14 +278,26 @@ RI_FKey_check(TriggerData *trigdata)
         * checked).  Test its liveness according to SnapshotSelf.  We need pin
         * and lock on the buffer to call HeapTupleSatisfiesVisibility.  Caller
         * should be holding pin, but not lock.
+        *
+        * XXX: Note that the buffer-tuple specificity will be removed in the near
+        * future.
         */
-       LockBuffer(new_row_buf, BUFFER_LOCK_SHARE);
-       if (!HeapTupleSatisfiesVisibility(new_row, SnapshotSelf, new_row_buf))
+       if (TTS_IS_BUFFERTUPLE(newslot))
        {
-               LockBuffer(new_row_buf, BUFFER_LOCK_UNLOCK);
-               return PointerGetDatum(NULL);
+               BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) newslot;
+
+               Assert(BufferIsValid(bslot->buffer));
+
+               LockBuffer(bslot->buffer, BUFFER_LOCK_SHARE);
+               if (!HeapTupleSatisfiesVisibility(bslot->base.tuple, SnapshotSelf, bslot->buffer))
+               {
+                       LockBuffer(bslot->buffer, BUFFER_LOCK_UNLOCK);
+                       return PointerGetDatum(NULL);
+               }
+               LockBuffer(bslot->buffer, BUFFER_LOCK_UNLOCK);
        }
-       LockBuffer(new_row_buf, BUFFER_LOCK_UNLOCK);
+       else
+               elog(ERROR, "expected buffer tuple");
 
        /*
         * Get the relation descriptors of the FK and PK tables.
@@ -308,7 +313,7 @@ RI_FKey_check(TriggerData *trigdata)
                                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                                 errmsg("MATCH PARTIAL not yet implemented")));
 
-       switch (ri_NullCheck(RelationGetDescr(fk_rel), new_row, riinfo, false))
+       switch (ri_NullCheck(RelationGetDescr(fk_rel), newslot, riinfo, false))
        {
                case RI_KEYS_ALL_NULL:
 
@@ -438,7 +443,7 @@ RI_FKey_check(TriggerData *trigdata)
         */
        ri_PerformCheck(riinfo, &qkey, qplan,
                                        fk_rel, pk_rel,
-                                       NULL, new_row,
+                                       NULL, newslot,
                                        false,
                                        SPI_OK_SELECT);
 
@@ -506,7 +511,7 @@ RI_FKey_check_upd(PG_FUNCTION_ARGS)
  */
 static bool
 ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel,
-                                 HeapTuple old_row,
+                                 TupleTableSlot *oldslot,
                                  const RI_ConstraintInfo *riinfo)
 {
        SPIPlanPtr      qplan;
@@ -515,7 +520,7 @@ ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel,
        bool            result;
 
        /* Only called for non-null rows */
-       Assert(ri_NullCheck(RelationGetDescr(pk_rel), old_row, riinfo, true) == RI_KEYS_NONE_NULL);
+       Assert(ri_NullCheck(RelationGetDescr(pk_rel), oldslot, riinfo, true) == RI_KEYS_NONE_NULL);
 
        if (SPI_connect() != SPI_OK_CONNECT)
                elog(ERROR, "SPI_connect failed");
@@ -573,7 +578,7 @@ ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel,
         */
        result = ri_PerformCheck(riinfo, &qkey, qplan,
                                                         fk_rel, pk_rel,
-                                                        old_row, NULL,
+                                                        oldslot, NULL,
                                                         true,  /* treat like update */
                                                         SPI_OK_SELECT);
 
@@ -691,7 +696,7 @@ ri_restrict(TriggerData *trigdata, bool is_no_action)
        const RI_ConstraintInfo *riinfo;
        Relation        fk_rel;
        Relation        pk_rel;
-       HeapTuple       old_row;
+       TupleTableSlot *old_slot;
        RI_QueryKey qkey;
        SPIPlanPtr      qplan;
 
@@ -709,7 +714,7 @@ ri_restrict(TriggerData *trigdata, bool is_no_action)
         */
        fk_rel = table_open(riinfo->fk_relid, RowShareLock);
        pk_rel = trigdata->tg_relation;
-       old_row = trigdata->tg_trigtuple;
+       old_slot = trigdata->tg_trigslot;
 
        switch (riinfo->confmatchtype)
        {
@@ -733,7 +738,7 @@ ri_restrict(TriggerData *trigdata, bool is_no_action)
                         * allow another row to be substituted.
                         */
                        if (is_no_action &&
-                               ri_Check_Pk_Match(pk_rel, fk_rel, old_row, riinfo))
+                               ri_Check_Pk_Match(pk_rel, fk_rel, old_slot, riinfo))
                        {
                                table_close(fk_rel, RowShareLock);
                                return PointerGetDatum(NULL);
@@ -801,7 +806,7 @@ ri_restrict(TriggerData *trigdata, bool is_no_action)
                         */
                        ri_PerformCheck(riinfo, &qkey, qplan,
                                                        fk_rel, pk_rel,
-                                                       old_row, NULL,
+                                                       old_slot, NULL,
                                                        true,   /* must detect new rows */
                                                        SPI_OK_SELECT);
 
@@ -845,7 +850,7 @@ RI_FKey_cascade_del(PG_FUNCTION_ARGS)
        const RI_ConstraintInfo *riinfo;
        Relation        fk_rel;
        Relation        pk_rel;
-       HeapTuple       old_row;
+       TupleTableSlot *old_slot;
        RI_QueryKey qkey;
        SPIPlanPtr      qplan;
        int                     i;
@@ -869,7 +874,7 @@ RI_FKey_cascade_del(PG_FUNCTION_ARGS)
         */
        fk_rel = table_open(riinfo->fk_relid, RowExclusiveLock);
        pk_rel = trigdata->tg_relation;
-       old_row = trigdata->tg_trigtuple;
+       old_slot = trigdata->tg_trigslot;
 
        switch (riinfo->confmatchtype)
        {
@@ -941,7 +946,7 @@ RI_FKey_cascade_del(PG_FUNCTION_ARGS)
                         */
                        ri_PerformCheck(riinfo, &qkey, qplan,
                                                        fk_rel, pk_rel,
-                                                       old_row, NULL,
+                                                       old_slot, NULL,
                                                        true,   /* must detect new rows */
                                                        SPI_OK_DELETE);
 
@@ -985,8 +990,8 @@ RI_FKey_cascade_upd(PG_FUNCTION_ARGS)
        const RI_ConstraintInfo *riinfo;
        Relation        fk_rel;
        Relation        pk_rel;
-       HeapTuple       new_row;
-       HeapTuple       old_row;
+       TupleTableSlot *new_slot;
+       TupleTableSlot *old_slot;
        RI_QueryKey qkey;
        SPIPlanPtr      qplan;
        int                     i;
@@ -1012,8 +1017,8 @@ RI_FKey_cascade_upd(PG_FUNCTION_ARGS)
         */
        fk_rel = table_open(riinfo->fk_relid, RowExclusiveLock);
        pk_rel = trigdata->tg_relation;
-       new_row = trigdata->tg_newtuple;
-       old_row = trigdata->tg_trigtuple;
+       new_slot = trigdata->tg_newslot;
+       old_slot = trigdata->tg_trigslot;
 
        switch (riinfo->confmatchtype)
        {
@@ -1097,7 +1102,7 @@ RI_FKey_cascade_upd(PG_FUNCTION_ARGS)
                         */
                        ri_PerformCheck(riinfo, &qkey, qplan,
                                                        fk_rel, pk_rel,
-                                                       old_row, new_row,
+                                                       old_slot, new_slot,
                                                        true,   /* must detect new rows */
                                                        SPI_OK_UPDATE);
 
@@ -1180,7 +1185,7 @@ ri_setnull(TriggerData *trigdata)
        const RI_ConstraintInfo *riinfo;
        Relation        fk_rel;
        Relation        pk_rel;
-       HeapTuple       old_row;
+       TupleTableSlot *old_slot;
        RI_QueryKey qkey;
        SPIPlanPtr      qplan;
        int                     i;
@@ -1199,7 +1204,7 @@ ri_setnull(TriggerData *trigdata)
         */
        fk_rel = table_open(riinfo->fk_relid, RowExclusiveLock);
        pk_rel = trigdata->tg_relation;
-       old_row = trigdata->tg_trigtuple;
+       old_slot = trigdata->tg_trigslot;
 
        switch (riinfo->confmatchtype)
        {
@@ -1284,7 +1289,7 @@ ri_setnull(TriggerData *trigdata)
                         */
                        ri_PerformCheck(riinfo, &qkey, qplan,
                                                        fk_rel, pk_rel,
-                                                       old_row, NULL,
+                                                       old_slot, NULL,
                                                        true,   /* must detect new rows */
                                                        SPI_OK_UPDATE);
 
@@ -1367,7 +1372,7 @@ ri_setdefault(TriggerData *trigdata)
        const RI_ConstraintInfo *riinfo;
        Relation        fk_rel;
        Relation        pk_rel;
-       HeapTuple       old_row;
+       TupleTableSlot *old_slot;
        RI_QueryKey qkey;
        SPIPlanPtr      qplan;
 
@@ -1385,7 +1390,7 @@ ri_setdefault(TriggerData *trigdata)
         */
        fk_rel = table_open(riinfo->fk_relid, RowExclusiveLock);
        pk_rel = trigdata->tg_relation;
-       old_row = trigdata->tg_trigtuple;
+       old_slot = trigdata->tg_trigslot;
 
        switch (riinfo->confmatchtype)
        {
@@ -1471,7 +1476,7 @@ ri_setdefault(TriggerData *trigdata)
                         */
                        ri_PerformCheck(riinfo, &qkey, qplan,
                                                        fk_rel, pk_rel,
-                                                       old_row, NULL,
+                                                       old_slot, NULL,
                                                        true,   /* must detect new rows */
                                                        SPI_OK_UPDATE);
 
@@ -1530,7 +1535,7 @@ ri_setdefault(TriggerData *trigdata)
  */
 bool
 RI_FKey_pk_upd_check_required(Trigger *trigger, Relation pk_rel,
-                                                         HeapTuple old_row, HeapTuple new_row)
+                                                         TupleTableSlot *old_slot, TupleTableSlot *new_slot)
 {
        const RI_ConstraintInfo *riinfo;
 
@@ -1548,11 +1553,11 @@ RI_FKey_pk_upd_check_required(Trigger *trigger, Relation pk_rel,
                         * If any old key value is NULL, the row could not have been
                         * referenced by an FK row, so no check is needed.
                         */
-                       if (ri_NullCheck(RelationGetDescr(pk_rel), old_row, riinfo, true) != RI_KEYS_NONE_NULL)
+                       if (ri_NullCheck(RelationGetDescr(pk_rel), old_slot, riinfo, true) != RI_KEYS_NONE_NULL)
                                return false;
 
                        /* If all old and new key values are equal, no check is needed */
-                       if (new_row && ri_KeysEqual(pk_rel, old_row, new_row, riinfo, true))
+                       if (new_slot && ri_KeysEqual(pk_rel, old_slot, new_slot, riinfo, true))
                                return false;
 
                        /* Else we need to fire the trigger. */
@@ -1587,9 +1592,12 @@ RI_FKey_pk_upd_check_required(Trigger *trigger, Relation pk_rel,
  */
 bool
 RI_FKey_fk_upd_check_required(Trigger *trigger, Relation fk_rel,
-                                                         HeapTuple old_row, HeapTuple new_row)
+                                                         TupleTableSlot *old_slot, TupleTableSlot *new_slot)
 {
        const RI_ConstraintInfo *riinfo;
+       Datum           xminDatum;
+       TransactionId xmin;
+       bool            isnull;
 
        /*
         * Get arguments.
@@ -1604,7 +1612,7 @@ RI_FKey_fk_upd_check_required(Trigger *trigger, Relation fk_rel,
                         * If any new key value is NULL, the row must satisfy the
                         * constraint, so no check is needed.
                         */
-                       if (ri_NullCheck(RelationGetDescr(fk_rel), new_row, riinfo, false) != RI_KEYS_NONE_NULL)
+                       if (ri_NullCheck(RelationGetDescr(fk_rel), new_slot, riinfo, false) != RI_KEYS_NONE_NULL)
                                return false;
 
                        /*
@@ -1615,11 +1623,14 @@ RI_FKey_fk_upd_check_required(Trigger *trigger, Relation fk_rel,
                         * UPDATE check.  (We could skip this if we knew the INSERT
                         * trigger already fired, but there is no easy way to know that.)
                         */
-                       if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(old_row->t_data)))
+                       xminDatum = slot_getsysattr(old_slot, MinTransactionIdAttributeNumber, &isnull);
+                       Assert(!isnull);
+                       xmin = DatumGetTransactionId(xminDatum);
+                       if (TransactionIdIsCurrentTransactionId(xmin))
                                return true;
 
                        /* If all old and new key values are equal, no check is needed */
-                       if (ri_KeysEqual(fk_rel, old_row, new_row, riinfo, false))
+                       if (ri_KeysEqual(fk_rel, old_slot, new_slot, riinfo, false))
                                return false;
 
                        /* Else we need to fire the trigger. */
@@ -1635,7 +1646,7 @@ RI_FKey_fk_upd_check_required(Trigger *trigger, Relation fk_rel,
                         * invalidated before the constraint is to be checked, but we
                         * should queue the event to apply the check later.
                         */
-                       switch (ri_NullCheck(RelationGetDescr(fk_rel), new_row, riinfo, false))
+                       switch (ri_NullCheck(RelationGetDescr(fk_rel), new_slot, riinfo, false))
                        {
                                case RI_KEYS_ALL_NULL:
                                        return false;
@@ -1653,11 +1664,14 @@ RI_FKey_fk_upd_check_required(Trigger *trigger, Relation fk_rel,
                         * UPDATE check.  (We could skip this if we knew the INSERT
                         * trigger already fired, but there is no easy way to know that.)
                         */
-                       if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(old_row->t_data)))
+                       xminDatum = slot_getsysattr(old_slot, MinTransactionIdAttributeNumber, &isnull);
+                       Assert(!isnull);
+                       xmin = DatumGetTransactionId(xminDatum);
+                       if (TransactionIdIsCurrentTransactionId(xmin))
                                return true;
 
                        /* If all old and new key values are equal, no check is needed */
-                       if (ri_KeysEqual(fk_rel, old_row, new_row, riinfo, false))
+                       if (ri_KeysEqual(fk_rel, old_slot, new_slot, riinfo, false))
                                return false;
 
                        /* Else we need to fire the trigger. */
@@ -1911,10 +1925,17 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
        /* Did we find a tuple violating the constraint? */
        if (SPI_processed > 0)
        {
+               TupleTableSlot *slot;
                HeapTuple       tuple = SPI_tuptable->vals[0];
                TupleDesc       tupdesc = SPI_tuptable->tupdesc;
                RI_ConstraintInfo fake_riinfo;
 
+               slot = MakeSingleTupleTableSlot(tupdesc, &TTSOpsVirtual);
+
+               heap_deform_tuple(tuple, tupdesc,
+                                                 slot->tts_values, slot->tts_isnull);
+               ExecStoreVirtualTuple(slot);
+
                /*
                 * The columns to look at in the result tuple are 1..N, not whatever
                 * they are in the fk_rel.  Hack up riinfo so that the subroutines
@@ -1934,7 +1955,7 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
                 * disallows partially-null FK rows.
                 */
                if (fake_riinfo.confmatchtype == FKCONSTR_MATCH_FULL &&
-                       ri_NullCheck(tupdesc, tuple, &fake_riinfo, false) != RI_KEYS_NONE_NULL)
+                       ri_NullCheck(tupdesc, slot, &fake_riinfo, false) != RI_KEYS_NONE_NULL)
                        ereport(ERROR,
                                        (errcode(ERRCODE_FOREIGN_KEY_VIOLATION),
                                         errmsg("insert or update on table \"%s\" violates foreign key constraint \"%s\"",
@@ -1951,8 +1972,10 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
                 */
                ri_ReportViolation(&fake_riinfo,
                                                   pk_rel, fk_rel,
-                                                  tuple, tupdesc,
+                                                  slot, tupdesc,
                                                   RI_PLAN_CHECK_LOOKUPPK);
+
+               ExecDropSingleTupleTableSlot(slot);
        }
 
        if (SPI_finish() != SPI_OK_FINISH)
@@ -2355,7 +2378,7 @@ static bool
 ri_PerformCheck(const RI_ConstraintInfo *riinfo,
                                RI_QueryKey *qkey, SPIPlanPtr qplan,
                                Relation fk_rel, Relation pk_rel,
-                               HeapTuple old_tuple, HeapTuple new_tuple,
+                               TupleTableSlot *old_slot, TupleTableSlot *new_slot,
                                bool detectNewRows, int expect_OK)
 {
        Relation        query_rel,
@@ -2398,17 +2421,17 @@ ri_PerformCheck(const RI_ConstraintInfo *riinfo,
        }
 
        /* Extract the parameters to be passed into the query */
-       if (new_tuple)
+       if (new_slot)
        {
-               ri_ExtractValues(source_rel, new_tuple, riinfo, source_is_pk,
+               ri_ExtractValues(source_rel, new_slot, riinfo, source_is_pk,
                                                 vals, nulls);
-               if (old_tuple)
-                       ri_ExtractValues(source_rel, old_tuple, riinfo, source_is_pk,
+               if (old_slot)
+                       ri_ExtractValues(source_rel, old_slot, riinfo, source_is_pk,
                                                         vals + riinfo->nkeys, nulls + riinfo->nkeys);
        }
        else
        {
-               ri_ExtractValues(source_rel, old_tuple, riinfo, source_is_pk,
+               ri_ExtractValues(source_rel, old_slot, riinfo, source_is_pk,
                                                 vals, nulls);
        }
 
@@ -2478,7 +2501,7 @@ ri_PerformCheck(const RI_ConstraintInfo *riinfo,
                (SPI_processed == 0) == (qkey->constr_queryno == RI_PLAN_CHECK_LOOKUPPK))
                ri_ReportViolation(riinfo,
                                                   pk_rel, fk_rel,
-                                                  new_tuple ? new_tuple : old_tuple,
+                                                  new_slot ? new_slot : old_slot,
                                                   NULL,
                                                   qkey->constr_queryno);
 
@@ -2489,11 +2512,10 @@ ri_PerformCheck(const RI_ConstraintInfo *riinfo,
  * Extract fields from a tuple into Datum/nulls arrays
  */
 static void
-ri_ExtractValues(Relation rel, HeapTuple tup,
+ri_ExtractValues(Relation rel, TupleTableSlot *slot,
                                 const RI_ConstraintInfo *riinfo, bool rel_is_pk,
                                 Datum *vals, char *nulls)
 {
-       TupleDesc       tupdesc = rel->rd_att;
        const int16 *attnums;
        int                     i;
        bool            isnull;
@@ -2505,8 +2527,7 @@ ri_ExtractValues(Relation rel, HeapTuple tup,
 
        for (i = 0; i < riinfo->nkeys; i++)
        {
-               vals[i] = heap_getattr(tup, attnums[i], tupdesc,
-                                                          &isnull);
+               vals[i] = slot_getattr(slot, attnums[i], &isnull);
                nulls[i] = isnull ? 'n' : ' ';
        }
 }
@@ -2523,7 +2544,7 @@ ri_ExtractValues(Relation rel, HeapTuple tup,
 static void
 ri_ReportViolation(const RI_ConstraintInfo *riinfo,
                                   Relation pk_rel, Relation fk_rel,
-                                  HeapTuple violator, TupleDesc tupdesc,
+                                  TupleTableSlot *violatorslot, TupleDesc tupdesc,
                                   int queryno)
 {
        StringInfoData key_names;
@@ -2598,12 +2619,24 @@ ri_ReportViolation(const RI_ConstraintInfo *riinfo,
                for (idx = 0; idx < riinfo->nkeys; idx++)
                {
                        int                     fnum = attnums[idx];
+                       Form_pg_attribute att = TupleDescAttr(tupdesc, fnum - 1);
                        char       *name,
                                           *val;
+                       Datum           datum;
+                       bool            isnull;
 
-                       name = SPI_fname(tupdesc, fnum);
-                       val = SPI_getvalue(violator, tupdesc, fnum);
-                       if (!val)
+                       name = NameStr(att->attname);
+
+                       datum = slot_getattr(violatorslot, fnum, &isnull);
+                       if (!isnull)
+                       {
+                               Oid                     foutoid;
+                               bool            typisvarlena;
+
+                               getTypeOutputInfo(att->atttypid, &foutoid, &typisvarlena);
+                               val = OidOutputFunctionCall(foutoid, datum);
+                       }
+                       else
                                val = "null";
 
                        if (idx > 0)
@@ -2656,7 +2689,7 @@ ri_ReportViolation(const RI_ConstraintInfo *riinfo,
  */
 static int
 ri_NullCheck(TupleDesc tupDesc,
-                        HeapTuple tup,
+                        TupleTableSlot *slot,
                         const RI_ConstraintInfo *riinfo, bool rel_is_pk)
 {
        const int16 *attnums;
@@ -2671,7 +2704,7 @@ ri_NullCheck(TupleDesc tupDesc,
 
        for (i = 0; i < riinfo->nkeys; i++)
        {
-               if (heap_attisnull(tup, attnums[i], tupDesc))
+               if (slot_attisnull(slot, attnums[i]))
                        nonenull = false;
                else
                        allnull = false;
@@ -2822,10 +2855,9 @@ ri_HashPreparedPlan(RI_QueryKey *key, SPIPlanPtr plan)
  * ----------
  */
 static bool
-ri_KeysEqual(Relation rel, HeapTuple oldtup, HeapTuple newtup,
+ri_KeysEqual(Relation rel, TupleTableSlot *oldslot, TupleTableSlot *newslot,
                         const RI_ConstraintInfo *riinfo, bool rel_is_pk)
 {
-       TupleDesc       tupdesc = RelationGetDescr(rel);
        const int16 *attnums;
        const Oid  *eq_oprs;
        int                     i;
@@ -2841,6 +2873,7 @@ ri_KeysEqual(Relation rel, HeapTuple oldtup, HeapTuple newtup,
                eq_oprs = riinfo->ff_eq_oprs;
        }
 
+       /* XXX: could be worthwhile to fetch all necessary attrs at once */
        for (i = 0; i < riinfo->nkeys; i++)
        {
                Datum           oldvalue;
@@ -2850,14 +2883,14 @@ ri_KeysEqual(Relation rel, HeapTuple oldtup, HeapTuple newtup,
                /*
                 * Get one attribute's oldvalue. If it is NULL - they're not equal.
                 */
-               oldvalue = heap_getattr(oldtup, attnums[i], tupdesc, &isnull);
+               oldvalue = slot_getattr(oldslot, attnums[i], &isnull);
                if (isnull)
                        return false;
 
                /*
                 * Get one attribute's newvalue. If it is NULL - they're not equal.
                 */
-               newvalue = heap_getattr(newtup, attnums[i], tupdesc, &isnull);
+               newvalue = slot_getattr(newslot, attnums[i], &isnull);
                if (isnull)
                        return false;
 
index 9f212ac24bf829e108cb7c816ae55a3dc168f500..846679ecc123778fcab4dd9e483f92c4351ab5f0 100644 (file)
@@ -35,8 +35,8 @@ typedef struct TriggerData
        HeapTuple       tg_trigtuple;
        HeapTuple       tg_newtuple;
        Trigger    *tg_trigger;
-       Buffer          tg_trigtuplebuf;
-       Buffer          tg_newtuplebuf;
+       TupleTableSlot *tg_trigslot;
+       TupleTableSlot *tg_newslot;
        Tuplestorestate *tg_oldtable;
        Tuplestorestate *tg_newtable;
 } TriggerData;
@@ -77,9 +77,9 @@ typedef struct TransitionCaptureState
         * format to parent format after they have already been converted in the
         * opposite direction during routing.  In that case we bypass conversion
         * and allow the inserting code (copy.c and nodeModifyTable.c) to provide
-        * the original tuple directly.
+        * a slot containing the original tuple directly.
         */
-       HeapTuple       tcs_original_insert_tuple;
+       TupleTableSlot  *tcs_original_insert_tuple;
 
        /*
         * Private data including the tuplestore(s) into which to insert tuples.
@@ -186,15 +186,15 @@ extern void ExecBSInsertTriggers(EState *estate,
 extern void ExecASInsertTriggers(EState *estate,
                                         ResultRelInfo *relinfo,
                                         TransitionCaptureState *transition_capture);
-extern TupleTableSlot *ExecBRInsertTriggers(EState *estate,
+extern bool ExecBRInsertTriggers(EState *estate,
                                         ResultRelInfo *relinfo,
                                         TupleTableSlot *slot);
 extern void ExecARInsertTriggers(EState *estate,
                                         ResultRelInfo *relinfo,
-                                        HeapTuple trigtuple,
+                                        TupleTableSlot *slot,
                                         List *recheckIndexes,
                                         TransitionCaptureState *transition_capture);
-extern TupleTableSlot *ExecIRInsertTriggers(EState *estate,
+extern bool ExecIRInsertTriggers(EState *estate,
                                         ResultRelInfo *relinfo,
                                         TupleTableSlot *slot);
 extern void ExecBSDeleteTriggers(EState *estate,
@@ -221,7 +221,7 @@ extern void ExecBSUpdateTriggers(EState *estate,
 extern void ExecASUpdateTriggers(EState *estate,
                                         ResultRelInfo *relinfo,
                                         TransitionCaptureState *transition_capture);
-extern TupleTableSlot *ExecBRUpdateTriggers(EState *estate,
+extern bool ExecBRUpdateTriggers(EState *estate,
                                         EPQState *epqstate,
                                         ResultRelInfo *relinfo,
                                         ItemPointer tupleid,
@@ -231,10 +231,10 @@ extern void ExecARUpdateTriggers(EState *estate,
                                         ResultRelInfo *relinfo,
                                         ItemPointer tupleid,
                                         HeapTuple fdw_trigtuple,
-                                        HeapTuple newtuple,
+                                        TupleTableSlot *slot,
                                         List *recheckIndexes,
                                         TransitionCaptureState *transition_capture);
-extern TupleTableSlot *ExecIRUpdateTriggers(EState *estate,
+extern bool ExecIRUpdateTriggers(EState *estate,
                                         ResultRelInfo *relinfo,
                                         HeapTuple trigtuple,
                                         TupleTableSlot *slot);
@@ -258,9 +258,9 @@ extern bool AfterTriggerPendingOnRel(Oid relid);
  * in utils/adt/ri_triggers.c
  */
 extern bool RI_FKey_pk_upd_check_required(Trigger *trigger, Relation pk_rel,
-                                                         HeapTuple old_row, HeapTuple new_row);
+                                                         TupleTableSlot *old_slot, TupleTableSlot *new_slot);
 extern bool RI_FKey_fk_upd_check_required(Trigger *trigger, Relation fk_rel,
-                                                         HeapTuple old_row, HeapTuple new_row);
+                                                         TupleTableSlot *old_slot, TupleTableSlot  *new_slot);
 extern bool RI_Initial_Check(Trigger *trigger,
                                 Relation fk_rel, Relation pk_rel);
 
index 7a63e9a7cc370bae0c4b333768418558bcde14fc..25b72c59e222b712af824696224554043f91baa8 100644 (file)
@@ -560,6 +560,10 @@ extern Datum GetAttributeByNum(HeapTupleHeader tuple, AttrNumber attrno,
 extern int     ExecTargetListLength(List *targetlist);
 extern int     ExecCleanTargetListLength(List *targetlist);
 
+extern TupleTableSlot *ExecGetTriggerOldSlot(EState *estate, ResultRelInfo *relInfo);
+extern TupleTableSlot *ExecGetTriggerNewSlot(EState *estate, ResultRelInfo *relInfo);
+extern TupleTableSlot *ExecGetReturningSlot(EState *estate, ResultRelInfo *relInfo);
+
 /*
  * prototypes from functions in execIndexing.c
  */
index 3b789ee7cf329a6e3dc06ee6c948d174ded66751..09f8217c8002238fb852f9dee56e99e6f693ba7f 100644 (file)
@@ -427,6 +427,11 @@ typedef struct ResultRelInfo
        /* optional runtime measurements for triggers */
        Instrumentation *ri_TrigInstrument;
 
+       /* On-demand created slots for triggers / returning processing */
+       TupleTableSlot *ri_ReturningSlot; /* for trigger output tuples */
+       TupleTableSlot *ri_TrigOldSlot; /* for a trigger's old tuple */
+       TupleTableSlot *ri_TrigNewSlot; /* for a trigger's new tuple */
+
        /* FDW callback functions, if foreign table */
        struct FdwRoutine *ri_FdwRoutine;
 
@@ -524,9 +529,6 @@ typedef struct EState
 
        /* Stuff used for firing triggers: */
        List       *es_trig_target_relations;   /* trigger-only ResultRelInfos */
-       TupleTableSlot *es_trig_tuple_slot; /* for trigger output tuples */
-       TupleTableSlot *es_trig_oldtup_slot;    /* for TriggerEnabled */
-       TupleTableSlot *es_trig_newtup_slot;    /* for TriggerEnabled */
 
        /* Parameter info: */
        ParamListInfo es_param_list_info;       /* values of external params */