diff options
author | Andres Freund <andres@anarazel.de> | 2019-02-26 20:30:28 -0800 |
---|---|---|
committer | Andres Freund <andres@anarazel.de> | 2019-02-26 20:31:38 -0800 |
commit | ff11e7f4b9ae017585c3ba146db7ba39c31f209a (patch) | |
tree | 37828b8d2ab7bda386b276129b456793c7ac908b /src/backend/commands/trigger.c | |
parent | b8d71745eac0a12740a70dc78cbcdedadade37f8 (diff) | |
download | postgresql-ff11e7f4b9ae017585c3ba146db7ba39c31f209a.tar.gz postgresql-ff11e7f4b9ae017585c3ba146db7ba39c31f209a.zip |
Use slots in trigger infrastructure, except for the actual invocation.
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
Diffstat (limited to 'src/backend/commands/trigger.c')
-rw-r--r-- | src/backend/commands/trigger.c | 673 |
1 files changed, 348 insertions, 325 deletions
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c index 7e5bf0d27f8..c5e588e8011 100644 --- a/src/backend/commands/trigger.c +++ b/src/backend/commands/trigger.c @@ -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; @@ -3510,40 +3520,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); } } |