From 732b8e82496e1a45fdd7ccbd1f87105dc0b22b95 Mon Sep 17 00:00:00 2001 From: Amit Khandekar Date: Mon, 19 Nov 2018 14:25:30 +0530 Subject: [PATCH 2/2] Trigger tuple slotification. Extracted the trigger-related changes from the pluggable storage patch present in this repository : https://github.com/anarazel/postgres-pluggable-storage.git Extracted changes from this commit : commit 6066202a97c9a2d540fd2a2e5fd2cad22f307cd2 Author: Andres Freund Date: Tue Oct 2 22:15:23 2018 -0700 Pluggable Storage. --- contrib/postgres_fdw/postgres_fdw.c | 2 +- src/backend/commands/copy.c | 23 +- src/backend/commands/tablecmds.c | 8 +- src/backend/commands/trigger.c | 559 +++++++++++++++---------------- src/backend/executor/execMain.c | 2 +- src/backend/executor/execReplication.c | 17 +- src/backend/executor/execUtils.c | 70 +++- src/backend/executor/nodeModifyTable.c | 60 +--- src/backend/replication/logical/worker.c | 5 - src/backend/utils/adt/ri_triggers.c | 149 ++++---- src/include/commands/trigger.h | 20 +- src/include/executor/executor.h | 4 + src/include/nodes/execnodes.h | 2 +- 13 files changed, 468 insertions(+), 453 deletions(-) diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c index 6f9c6e1..472f0a5 100644 --- a/contrib/postgres_fdw/postgres_fdw.c +++ b/contrib/postgres_fdw/postgres_fdw.c @@ -3904,7 +3904,7 @@ apply_returning_filter(PgFdwDirectModifyState *dmstate, /* * Use the trigger tuple slot as a place to store the result tuple. */ - resultSlot = estate->es_trig_tuple_slot; + resultSlot = ExecTriggerGetReturnSlot(estate, dmstate->resultRel); if (resultSlot->tts_tupleDescriptor != resultTupType) ExecSetSlotDescriptor(resultSlot, resultTupType); diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index 93badf7..2ecd6e7 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -2490,9 +2490,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 @@ -2862,12 +2859,15 @@ 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; + if (!ExecBRInsertTriggers(estate, resultRelInfo, slot)) + skip_tuple = true; /* "do nothing" */ else /* trigger might have changed tuple */ - tuple = ExecFetchSlotHeapTuple(slot, true, NULL); + { + /* Similar to above, get a tuple copy in per-tuple context */ + oldcontext = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate)); + tuple = ExecCopySlotHeapTuple(slot); + MemoryContextSwitchTo(oldcontext); + } } if (!skip_tuple) @@ -2968,7 +2968,7 @@ CopyFrom(CopyState cstate) NIL); /* AFTER ROW INSERT Triggers */ - ExecARInsertTriggers(estate, resultRelInfo, tuple, + ExecARInsertTriggers(estate, resultRelInfo, slot, recheckIndexes, cstate->transition_capture); list_free(recheckIndexes); @@ -3106,7 +3106,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); } @@ -3123,8 +3123,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); } } diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index dfa15a5..2ed69ea 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -8578,6 +8578,7 @@ validateForeignKeyConstraint(char *conname, { HeapScanDesc scan; HeapTuple tuple; + TupleTableSlot *slot; Trigger trig; Snapshot snapshot; @@ -8613,12 +8614,15 @@ validateForeignKeyConstraint(char *conname, */ snapshot = RegisterSnapshot(GetLatestSnapshot()); scan = heap_beginscan(rel, snapshot, 0, NULL); + slot = MakeSingleTupleTableSlot(RelationGetDescr(rel), &TTSOpsBufferHeapTuple); while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) { FunctionCallInfoData fcinfo; TriggerData trigdata; + ExecStoreBufferHeapTuple(tuple, slot, scan->rs_cbuf); + /* * Make a call to the trigger function * @@ -8633,10 +8637,9 @@ validateForeignKeyConstraint(char *conname, trigdata.tg_event = TRIGGER_EVENT_INSERT | TRIGGER_EVENT_ROW; trigdata.tg_relation = rel; trigdata.tg_trigtuple = tuple; + trigdata.tg_trigslot = slot; trigdata.tg_newtuple = NULL; trigdata.tg_trigger = &trig; - trigdata.tg_trigtuplebuf = scan->rs_cbuf; - trigdata.tg_newtuplebuf = InvalidBuffer; fcinfo.context = (Node *) &trigdata; @@ -8645,6 +8648,7 @@ validateForeignKeyConstraint(char *conname, heap_endscan(scan); UnregisterSnapshot(snapshot); + ExecDropSingleTupleTableSlot(slot); } static void diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c index b91ebdb..9febe99 100644 --- a/src/backend/commands/trigger.c +++ b/src/backend/commands/trigger.c @@ -80,16 +80,18 @@ 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 **newSlot); + TupleTableSlot *oldslot, + TupleTableSlot *newslot, + bool *is_epq); 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, @@ -97,7 +99,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); @@ -2469,10 +2471,11 @@ 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]; @@ -2512,7 +2515,7 @@ ExecASInsertTriggers(EState *estate, ResultRelInfo *relinfo, false, NULL, NULL, NIL, NULL, transition_capture); } -TupleTableSlot * +bool ExecBRInsertTriggers(EState *estate, ResultRelInfo *relinfo, TupleTableSlot *slot) { @@ -2529,10 +2532,13 @@ 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]; @@ -2543,52 +2549,37 @@ ExecBRInsertTriggers(EState *estate, ResultRelInfo *relinfo, TRIGGER_TYPE_INSERT)) continue; if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event, - NULL, NULL, newtuple)) + NULL, NULL, slot)) continue; + 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" */ + return false; } - } - - 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 (newslot->tts_tupleDescriptor != tupdesc) - ExecSetSlotDescriptor(newslot, tupdesc); - ExecStoreHeapTuple(newtuple, newslot, false); - slot = newslot; + if (newtuple != oldtuple) + ExecForceStoreHeapTuple(newtuple, slot); } 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; @@ -2596,12 +2587,12 @@ 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) { @@ -2618,10 +2609,13 @@ 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]; @@ -2632,47 +2626,33 @@ ExecIRInsertTriggers(EState *estate, ResultRelInfo *relinfo, TRIGGER_TYPE_INSERT)) continue; if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event, - NULL, NULL, newtuple)) + NULL, NULL, slot)) continue; + 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" */ + return false; /* "do nothing" */ + } + if (oldtuple != newtuple) + { + ExecForceStoreHeapTuple(newtuple, LocTriggerData.tg_trigslot); + newtuple = ExecFetchSlotHeapTuple(slot, false, NULL); } - } - - 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 (newslot->tts_tupleDescriptor != tupdesc) - ExecSetSlotDescriptor(newslot, tupdesc); - ExecStoreHeapTuple(newtuple, newslot, false); - slot = newslot; } if (should_free) heap_freetuple(slottuple); - return slot; + return true; } void @@ -2700,10 +2680,11 @@ 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]; @@ -2757,20 +2738,20 @@ ExecBRDeleteTriggers(EState *estate, EPQState *epqstate, HeapTuple fdw_trigtuple, TupleTableSlot **epqslot) { + TupleTableSlot *slot = ExecTriggerGetOldSlot(estate, relinfo->ri_RelationDesc); TriggerDesc *trigdesc = relinfo->ri_TrigDesc; bool result = true; TriggerData LocTriggerData; HeapTuple trigtuple; - HeapTuple newtuple; - TupleTableSlot *newSlot; int i; Assert(HeapTupleIsValid(fdw_trigtuple) ^ ItemPointerIsValid(tupleid)); if (fdw_trigtuple == NULL) { - trigtuple = GetTupleForTrigger(estate, epqstate, relinfo, tupleid, - LockTupleExclusive, &newSlot); - if (trigtuple == NULL) + bool is_epqtuple; + + if (!GetTupleForTrigger(estate, epqstate, relinfo, tupleid, + LockTupleExclusive, slot, NULL, &is_epqtuple)) return false; /* @@ -2778,27 +2759,36 @@ ExecBRDeleteTriggers(EState *estate, EPQState *epqstate, * function requested for the updated tuple, skip the trigger * execution. */ - if (newSlot != NULL && epqslot != NULL) + if (is_epqtuple && epqslot != NULL) { - *epqslot = newSlot; - heap_freetuple(trigtuple); + *epqslot = slot; return false; } + + trigtuple = ExecFetchSlotHeapTuple(slot, false, NULL); + } 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, @@ -2807,11 +2797,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, @@ -2823,10 +2813,10 @@ ExecBRDeleteTriggers(EState *estate, EPQState *epqstate, result = false; /* tell caller to suppress delete */ break; } - if (newtuple != trigtuple) + if (false && newtuple != trigtuple) heap_freetuple(newtuple); } - if (trigtuple != fdw_trigtuple) + if (false && trigtuple != fdw_trigtuple) heap_freetuple(trigtuple); return result; @@ -2839,28 +2829,31 @@ ExecARDeleteTriggers(EState *estate, ResultRelInfo *relinfo, TransitionCaptureState *transition_capture) { TriggerDesc *trigdesc = relinfo->ri_TrigDesc; + TupleTableSlot *slot = ExecTriggerGetOldSlot(estate, relinfo->ri_RelationDesc); 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, + 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); } } @@ -2869,8 +2862,8 @@ ExecIRDeleteTriggers(EState *estate, ResultRelInfo *relinfo, HeapTuple trigtuple) { TriggerDesc *trigdesc = relinfo->ri_TrigDesc; + TupleTableSlot *slot = ExecTriggerGetOldSlot(estate, relinfo->ri_RelationDesc); TriggerData LocTriggerData; - HeapTuple rettuple; int i; LocTriggerData.type = T_TriggerData; @@ -2878,12 +2871,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, @@ -2892,11 +2891,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, @@ -2905,7 +2904,7 @@ ExecIRDeleteTriggers(EState *estate, ResultRelInfo *relinfo, GetPerTupleMemoryContext(estate)); if (rettuple == NULL) return false; /* Delete was suppressed */ - if (rettuple != trigtuple) + if (false && rettuple != trigtuple) heap_freetuple(rettuple); } return true; @@ -2939,10 +2938,11 @@ 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]; @@ -2984,20 +2984,19 @@ 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); + TupleTableSlot *oldslot = ExecTriggerGetOldSlot(estate, relinfo->ri_RelationDesc); + HeapTuple slottuple = ExecFetchSlotHeapTuple(newslot, true, NULL); HeapTuple newtuple = slottuple; TriggerData LocTriggerData; HeapTuple trigtuple; - HeapTuple oldtuple; - TupleTableSlot *newSlot; int i; Bitmapset *updatedCols; LockTupleMode lockmode; @@ -3008,37 +3007,41 @@ ExecBRUpdateTriggers(EState *estate, EPQState *epqstate, Assert(HeapTupleIsValid(fdw_trigtuple) ^ ItemPointerIsValid(tupleid)); if (fdw_trigtuple == NULL) { + bool is_epqtuple = false; + /* 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, &is_epqtuple)) + 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 (is_epqtuple) + { + TupleTableSlot *slot = ExecFilterJunk(relinfo->ri_junkFilter, newslot); + + ExecCopySlot(newslot, slot); + slottuple = ExecFetchSlotHeapTuple(newslot, false, NULL); + newtuple = slottuple; + } + + trigtuple = ExecFetchSlotHeapTuple(oldslot, false, NULL); } 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 | @@ -3050,6 +3053,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, @@ -3057,67 +3061,47 @@ ExecBRUpdateTriggers(EState *estate, EPQState *epqstate, TRIGGER_TYPE_UPDATE)) continue; if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event, - updatedCols, trigtuple, newtuple)) + updatedCols, oldslot, newslot)) continue; + 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) - heap_freetuple(trigtuple); - return NULL; /* "do nothing" */ - } - } - if (trigtuple != fdw_trigtuple && trigtuple != newtuple) - heap_freetuple(trigtuple); - - 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); + return false; /* "do nothing" */ - if (newslot->tts_tupleDescriptor != tupdesc) - ExecSetSlotDescriptor(newslot, tupdesc); - ExecStoreHeapTuple(newtuple, newslot, false); - slot = newslot; + if (newtuple != oldtuple) + ExecForceStoreHeapTuple(newtuple, newslot); } - return slot; + + 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 = ExecTriggerGetOldSlot(estate, relinfo->ri_RelationDesc); + + 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 @@ -3125,30 +3109,31 @@ 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, + 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); + TupleTableSlot *oldslot = ExecTriggerGetOldSlot(estate, relinfo->ri_RelationDesc); + HeapTuple slottuple = ExecFetchSlotHeapTuple(newslot, true, NULL); HeapTuple newtuple = slottuple; TriggerData LocTriggerData; HeapTuple oldtuple; @@ -3161,6 +3146,9 @@ 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]; @@ -3171,42 +3159,30 @@ ExecIRUpdateTriggers(EState *estate, ResultRelInfo *relinfo, TRIGGER_TYPE_UPDATE)) continue; if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event, - NULL, trigtuple, newtuple)) + NULL, oldslot, newslot)) continue; + 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) + if (false && oldtuple != newtuple && oldtuple != slottuple) heap_freetuple(oldtuple); if (newtuple == NULL) - return NULL; /* "do nothing" */ - } + return false; /* "do nothing" */ - 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 (newslot->tts_tupleDescriptor != tupdesc) - ExecSetSlotDescriptor(newslot, tupdesc); - ExecStoreHeapTuple(newtuple, newslot, false); - slot = newslot; + if (oldtuple != newtuple) + ExecForceStoreHeapTuple(newtuple, newslot); } - return slot; + + return true; } void @@ -3229,10 +3205,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]; @@ -3272,25 +3249,27 @@ ExecASTruncateTriggers(EState *estate, ResultRelInfo *relinfo) } -static HeapTuple +static bool GetTupleForTrigger(EState *estate, EPQState *epqstate, ResultRelInfo *relinfo, ItemPointer tid, LockTupleMode lockmode, - TupleTableSlot **newSlot) + TupleTableSlot *oldslot, + TupleTableSlot *newslot, + bool *is_epqtuple) { Relation relation = relinfo->ri_RelationDesc; HeapTupleData tuple; - HeapTuple result; + HeapTuple result = &tuple; Buffer buffer; - if (newSlot != NULL) + if (is_epqtuple) { HTSU_Result test; HeapUpdateFailureData hufd; - *newSlot = NULL; + *is_epqtuple = false; /* caller must pass an epqstate if EvalPlanQual is possible */ Assert(epqstate != NULL); @@ -3324,7 +3303,7 @@ ltrmark:; /* treat it as deleted; do not process */ ReleaseBuffer(buffer); - return NULL; + return false; case HeapTupleMayBeUpdated: break; @@ -3355,7 +3334,12 @@ ltrmark:; if (!TupIsNull(epqslot)) { *tid = hufd.ctid; - *newSlot = epqslot; + + if (newslot) + ExecCopySlot(newslot, epqslot); + /* oldslot is updated in the function end */ + + *is_epqtuple = true; /* * EvalPlanQual already locked the tuple, but we @@ -3371,7 +3355,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"); @@ -3380,7 +3364,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 @@ -3415,11 +3399,13 @@ ltrmark:; if (HeapTupleHeaderGetNatts(tuple.t_data) < relation->rd_att->natts) result = heap_expand_tuple(&tuple, relation->rd_att); - else - result = heap_copytuple(&tuple); + + ExecStoreBufferHeapTuple(result, oldslot, buffer); + ExecMaterializeSlot(oldslot); + ReleaseBuffer(buffer); - return result; + return true; } /* @@ -3429,7 +3415,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) @@ -3471,11 +3457,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; @@ -3515,40 +3498,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. */ @@ -3881,7 +3830,8 @@ struct AfterTriggersTableData static AfterTriggersData afterTriggers; -static void AfterTriggerExecute(AfterTriggerEvent event, +static void AfterTriggerExecute(EState *estate, + AfterTriggerEvent event, Relation rel, TriggerDesc *trigdesc, FmgrInfo *finfo, Instrumentation *instr, @@ -4216,7 +4166,8 @@ afterTriggerDeleteHeadEventChunk(AfterTriggersQueryData *qs) * ---------- */ static void -AfterTriggerExecute(AfterTriggerEvent event, +AfterTriggerExecute(EState *estate, + AfterTriggerEvent event, Relation rel, TriggerDesc *trigdesc, FmgrInfo *finfo, Instrumentation *instr, MemoryContext per_tuple_context, @@ -4229,14 +4180,15 @@ AfterTriggerExecute(AfterTriggerEvent event, HeapTupleData tuple1; HeapTupleData tuple2; HeapTuple rettuple; - Buffer buffer1 = InvalidBuffer; - Buffer buffer2 = InvalidBuffer; int tgindx; /* * 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) @@ -4286,31 +4238,35 @@ 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, false, NULL); + 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, false, NULL) : NULL; break; default: if (ItemPointerIsValid(&(event->ate_ctid1))) { + Buffer buffer; + 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_trigslot = ExecTriggerGetOldSlot(estate, rel); + ExecStoreBufferHeapTuple(&tuple1, + LocTriggerData.tg_trigslot, buffer); LocTriggerData.tg_trigtuple = &tuple1; - LocTriggerData.tg_trigtuplebuf = buffer1; + ReleaseBuffer(buffer); } else { LocTriggerData.tg_trigtuple = NULL; - LocTriggerData.tg_trigtuplebuf = InvalidBuffer; } /* don't touch ctid2 if not there */ @@ -4318,16 +4274,20 @@ AfterTriggerExecute(AfterTriggerEvent event, AFTER_TRIGGER_2CTID && ItemPointerIsValid(&(event->ate_ctid2))) { + Buffer buffer; + 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_newslot = ExecTriggerGetNewSlot(estate, rel); + ExecStoreBufferHeapTuple(&tuple2, + LocTriggerData.tg_newslot, buffer); LocTriggerData.tg_newtuple = &tuple2; - LocTriggerData.tg_newtuplebuf = buffer2; + ReleaseBuffer(buffer); } else { LocTriggerData.tg_newtuple = NULL; - LocTriggerData.tg_newtuplebuf = InvalidBuffer; } } @@ -4379,12 +4339,12 @@ AfterTriggerExecute(AfterTriggerEvent event, heap_freetuple(rettuple); /* - * Release buffers + * Release resources */ - if (buffer1 != InvalidBuffer) - ReleaseBuffer(buffer1); - if (buffer2 != InvalidBuffer) - ReleaseBuffer(buffer2); + 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 @@ -4561,7 +4521,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, rel, trigdesc, finfo, instr, per_tuple_context, slot1, slot2); /* @@ -4605,6 +4565,7 @@ afterTriggerInvokeEvents(AfterTriggerEventList *events, if (local_estate) { ExecCleanUpTriggerState(estate); + ExecResetTupleTable(estate->es_tupleTable, false); FreeExecutorState(estate); } @@ -5740,7 +5701,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) { @@ -5789,11 +5750,11 @@ AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo, * 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))) { @@ -5803,15 +5764,17 @@ AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo, if (map != NULL) { - HeapTuple converted = execute_attr_map_tuple(oldtup, map); + HeapTuple tuple = ExecFetchSlotHeapTuple(oldslot, false, NULL); + HeapTuple converted; + converted = execute_attr_map_tuple(tuple, map); tuplestore_puttuple(old_tuplestore, converted); pfree(converted); } 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))) { @@ -5823,13 +5786,15 @@ AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo, tuplestore_puttuple(new_tuplestore, original_insert_tuple); else if (map != NULL) { - HeapTuple converted = execute_attr_map_tuple(newtup, map); + HeapTuple tuple = ExecFetchSlotHeapTuple(newslot, false, NULL); + HeapTuple converted; + converted = execute_attr_map_tuple(tuple, map); tuplestore_puttuple(new_tuplestore, converted); pfree(converted); } else - tuplestore_puttuple(new_tuplestore, newtup); + tuplestore_puttupleslot(new_tuplestore, newslot); } /* @@ -5843,7 +5808,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; } @@ -5865,15 +5830,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), @@ -5884,15 +5849,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), @@ -5903,15 +5868,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), @@ -5920,8 +5885,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; @@ -5948,7 +5913,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) @@ -5975,7 +5940,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; @@ -5985,7 +5950,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; @@ -6039,10 +6004,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); } } diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index 757df07..2e4d8eb 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -977,7 +977,7 @@ 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_return_slot = NULL; estate->es_trig_oldtup_slot = NULL; estate->es_trig_newtup_slot = NULL; diff --git a/src/backend/executor/execReplication.c b/src/backend/executor/execReplication.c index fc00b49..8557d5a 100644 --- a/src/backend/executor/execReplication.c +++ b/src/backend/executor/execReplication.c @@ -407,10 +407,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) @@ -435,7 +433,7 @@ ExecSimpleRelationInsert(EState *estate, TupleTableSlot *slot) NIL); /* AFTER ROW INSERT Triggers */ - ExecARInsertTriggers(estate, resultRelInfo, tuple, + ExecARInsertTriggers(estate, resultRelInfo, slot, recheckIndexes, NULL); /* @@ -478,11 +476,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 (!ExecBRUpdateTriggers(estate, epqstate, resultRelInfo, + &hsearchslot->tuple->t_self, NULL, slot)) - if (slot == NULL) /* "do nothing" */ - skip_tuple = true; + skip_tuple = true; /* "do nothing" */ } if (!skip_tuple) @@ -510,7 +507,7 @@ ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate, /* AFTER ROW UPDATE Triggers */ ExecARUpdateTriggers(estate, resultRelInfo, - &hsearchslot->tuple->t_self, NULL, tuple, + &hsearchslot->tuple->t_self, NULL, slot, recheckIndexes, NULL); list_free(recheckIndexes); diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index f39be12..7bf9bc4 100644 --- a/src/backend/executor/execUtils.c +++ b/src/backend/executor/execUtils.c @@ -130,7 +130,7 @@ CreateExecutorState(void) estate->es_tuple_routing_result_relations = NIL; estate->es_trig_target_relations = NIL; - estate->es_trig_tuple_slot = NULL; + estate->es_trig_return_slot = NULL; estate->es_trig_oldtup_slot = NULL; estate->es_trig_newtup_slot = NULL; @@ -420,6 +420,74 @@ MakePerTupleExprContext(EState *estate) return estate->es_per_tuple_exprcontext; } +static TupleTableSlot * +ExecTriggerGetSlot(EState *estate, Relation rel, TupleTableSlot *slot) +{ + TupleDesc reldesc = RelationGetDescr(rel); + MemoryContext oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); + + if (slot != NULL) + { + /* + * Foreign tables need a non-buffer tuple slot, whereas local tables + * need buffer tuple slots. For any other combinations, need to + * re-create a matching tuple slot type. + */ + if (!(TTS_IS_BUFFERTUPLE(slot) && + rel->rd_rel->relkind != RELKIND_FOREIGN_TABLE) + && + !(!TTS_IS_BUFFERTUPLE(slot) && + rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)) + { + /* tuple slot type does not match. */ + ExecDropSingleTupleTableSlot(slot); + slot = NULL; + } + } + + if (slot == NULL) + { + /* For foreign tables, there are no buffer tuples */ + slot = + ExecInitExtraTupleSlot(estate, NULL, + rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE ? + &TTSOpsHeapTuple : &TTSOpsBufferHeapTuple); + } + + if (slot->tts_tupleDescriptor != reldesc) + ExecSetSlotDescriptor(slot, reldesc); + + MemoryContextSwitchTo(oldcontext); + + return slot; +} + +TupleTableSlot * +ExecTriggerGetOldSlot(EState *estate, Relation rel) +{ + estate->es_trig_oldtup_slot = + ExecTriggerGetSlot(estate, rel, estate->es_trig_oldtup_slot); + + return estate->es_trig_oldtup_slot; +} + +TupleTableSlot * +ExecTriggerGetNewSlot(EState *estate, Relation rel) +{ + estate->es_trig_newtup_slot = + ExecTriggerGetSlot(estate, rel, estate->es_trig_newtup_slot); + + return estate->es_trig_newtup_slot; +} + +TupleTableSlot * +ExecTriggerGetReturnSlot(EState *estate, Relation rel) +{ + estate->es_trig_return_slot = + ExecTriggerGetSlot(estate, rel, estate->es_trig_return_slot); + + return estate->es_trig_return_slot; +} /* ---------------------------------------------------------------- * miscellaneous node-init support functions diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c index 9786d95..73b2f83 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -308,10 +308,8 @@ 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; + if (!ExecBRInsertTriggers(estate, resultRelInfo, slot)) + return NULL; /* "do nothing" */ /* trigger might have changed tuple */ tuple = ExecFetchSlotHeapTuple(slot, true, NULL); @@ -321,10 +319,8 @@ ExecInsert(ModifyTableState *mtstate, if (resultRelInfo->ri_TrigDesc && resultRelInfo->ri_TrigDesc->trig_insert_instead_row) { - slot = ExecIRInsertTriggers(estate, resultRelInfo, slot); - - if (slot == NULL) /* "do nothing" */ - return NULL; + if (!ExecIRInsertTriggers(estate, resultRelInfo, slot)) + return NULL; /* "do nothing" */ /* trigger might have changed tuple */ tuple = ExecFetchSlotHeapTuple(slot, true, NULL); @@ -552,7 +548,7 @@ ExecInsert(ModifyTableState *mtstate, { ExecARUpdateTriggers(estate, resultRelInfo, NULL, NULL, - tuple, + slot, NULL, mtstate->mt_transition_capture); @@ -564,7 +560,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); @@ -678,10 +674,7 @@ ExecDelete(ModifyTableState *mtstate, * 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. */ - slot = estate->es_trig_tuple_slot; - if (slot->tts_tupleDescriptor != RelationGetDescr(resultRelationDesc)) - ExecSetSlotDescriptor(slot, RelationGetDescr(resultRelationDesc)); - + slot = ExecTriggerGetReturnSlot(estate, resultRelationDesc); slot = resultRelInfo->ri_FdwRoutine->ExecForeignDelete(estate, resultRelInfo, slot, @@ -857,7 +850,7 @@ ldelete:; */ TupleTableSlot *rslot; HeapTupleData deltuple; - Buffer delbuffer; + Buffer delbuffer = InvalidBuffer; if (resultRelInfo->ri_FdwRoutine) { @@ -867,11 +860,10 @@ ldelete:; } else { - slot = estate->es_trig_tuple_slot; + slot = ExecTriggerGetReturnSlot(estate, resultRelationDesc); if (oldtuple != NULL) { - deltuple = *oldtuple; - delbuffer = InvalidBuffer; + ExecForceStoreHeapTuple(oldtuple, slot); } else { @@ -879,11 +871,8 @@ ldelete:; if (!heap_fetch(resultRelationDesc, SnapshotAny, &deltuple, &delbuffer, false, NULL)) elog(ERROR, "failed to fetch deleted tuple for DELETE RETURNING"); + ExecStoreBufferHeapTuple(&deltuple, slot, delbuffer); } - - if (slot->tts_tupleDescriptor != RelationGetDescr(resultRelationDesc)) - ExecSetSlotDescriptor(slot, RelationGetDescr(resultRelationDesc)); - ExecStoreHeapTuple(&deltuple, slot, false); } rslot = ExecProcessReturning(resultRelInfo, slot, planSlot); @@ -966,11 +955,9 @@ 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; + if (!ExecBRUpdateTriggers(estate, epqstate, resultRelInfo, + tupleid, oldtuple, slot)) + return NULL; /* "do nothing" */ /* trigger might have changed tuple */ tuple = ExecFetchSlotHeapTuple(slot, true, NULL); @@ -980,11 +967,9 @@ ExecUpdate(ModifyTableState *mtstate, if (resultRelInfo->ri_TrigDesc && resultRelInfo->ri_TrigDesc->trig_update_instead_row) { - slot = ExecIRUpdateTriggers(estate, resultRelInfo, - oldtuple, slot); - - if (slot == NULL) /* "do nothing" */ - return NULL; + if (!ExecIRUpdateTriggers(estate, resultRelInfo, + oldtuple, slot)) + return NULL; /* "do nothing" */ /* trigger might have changed tuple */ tuple = ExecFetchSlotHeapTuple(slot, true, NULL); @@ -1309,7 +1294,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 : @@ -2566,15 +2551,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 diff --git a/src/backend/replication/logical/worker.c b/src/backend/replication/logical/worker.c index 3cd1e0d..3906eed 100644 --- a/src/backend/replication/logical/worker.c +++ b/src/backend/replication/logical/worker.c @@ -211,11 +211,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(); diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c index cdda860..a94751b 100644 --- a/src/backend/utils/adt/ri_triggers.c +++ b/src/backend/utils/adt/ri_triggers.c @@ -191,7 +191,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); @@ -204,12 +204,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); @@ -231,14 +231,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(); @@ -254,8 +254,8 @@ RI_FKey_check(TriggerData *trigdata) const RI_ConstraintInfo *riinfo; Relation fk_rel; Relation pk_rel; - HeapTuple new_row; - Buffer new_row_buf; + TupleTableSlot *newslot; + BufferHeapTupleTableSlot *bslot; RI_QueryKey qkey; SPIPlanPtr qplan; int i; @@ -267,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,13 +279,14 @@ RI_FKey_check(TriggerData *trigdata) * and lock on the buffer to call HeapTupleSatisfiesVisibility. Caller * should be holding pin, but not lock. */ - LockBuffer(new_row_buf, BUFFER_LOCK_SHARE); - if (!HeapTupleSatisfiesVisibility(new_row, SnapshotSelf, new_row_buf)) + bslot = (BufferHeapTupleTableSlot *) newslot; + LockBuffer(bslot->buffer, BUFFER_LOCK_SHARE); + if (!HeapTupleSatisfiesVisibility(bslot->base.tuple, SnapshotSelf, bslot->buffer)) { - LockBuffer(new_row_buf, BUFFER_LOCK_UNLOCK); + LockBuffer(bslot->buffer, BUFFER_LOCK_UNLOCK); return PointerGetDatum(NULL); } - LockBuffer(new_row_buf, BUFFER_LOCK_UNLOCK); + LockBuffer(bslot->buffer, BUFFER_LOCK_UNLOCK); /* * Get the relation descriptors of the FK and PK tables. @@ -307,7 +302,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: @@ -437,7 +432,7 @@ RI_FKey_check(TriggerData *trigdata) */ ri_PerformCheck(riinfo, &qkey, qplan, fk_rel, pk_rel, - NULL, new_row, + NULL, newslot, false, SPI_OK_SELECT); @@ -505,7 +500,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; @@ -514,7 +509,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"); @@ -572,7 +567,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); @@ -690,7 +685,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; @@ -708,7 +703,7 @@ ri_restrict(TriggerData *trigdata, bool is_no_action) */ fk_rel = heap_open(riinfo->fk_relid, RowShareLock); pk_rel = trigdata->tg_relation; - old_row = trigdata->tg_trigtuple; + old_slot = trigdata->tg_trigslot; switch (riinfo->confmatchtype) { @@ -732,7 +727,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)) { heap_close(fk_rel, RowShareLock); return PointerGetDatum(NULL); @@ -800,7 +795,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); @@ -844,7 +839,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; @@ -868,7 +863,7 @@ RI_FKey_cascade_del(PG_FUNCTION_ARGS) */ fk_rel = heap_open(riinfo->fk_relid, RowExclusiveLock); pk_rel = trigdata->tg_relation; - old_row = trigdata->tg_trigtuple; + old_slot = trigdata->tg_trigslot; switch (riinfo->confmatchtype) { @@ -940,7 +935,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); @@ -984,8 +979,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; @@ -1011,8 +1006,8 @@ RI_FKey_cascade_upd(PG_FUNCTION_ARGS) */ fk_rel = heap_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) { @@ -1096,7 +1091,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); @@ -1179,7 +1174,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; @@ -1198,7 +1193,7 @@ ri_setnull(TriggerData *trigdata) */ fk_rel = heap_open(riinfo->fk_relid, RowExclusiveLock); pk_rel = trigdata->tg_relation; - old_row = trigdata->tg_trigtuple; + old_slot = trigdata->tg_trigslot; switch (riinfo->confmatchtype) { @@ -1283,7 +1278,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); @@ -1366,7 +1361,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; @@ -1384,7 +1379,7 @@ ri_setdefault(TriggerData *trigdata) */ fk_rel = heap_open(riinfo->fk_relid, RowExclusiveLock); pk_rel = trigdata->tg_relation; - old_row = trigdata->tg_trigtuple; + old_slot = trigdata->tg_trigslot; switch (riinfo->confmatchtype) { @@ -1470,7 +1465,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); @@ -1529,7 +1524,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; @@ -1547,11 +1542,12 @@ 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 (!TupIsNull(new_slot) && + ri_KeysEqual(pk_rel, old_slot, new_slot, riinfo, true)) return false; /* Else we need to fire the trigger. */ @@ -1586,9 +1582,10 @@ 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; + HeapTuple old_row = ((HeapTupleTableSlot *) old_slot)->tuple; /* * Get arguments. @@ -1603,7 +1600,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; /* @@ -1618,7 +1615,7 @@ RI_FKey_fk_upd_check_required(Trigger *trigger, Relation fk_rel, 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. */ @@ -1634,7 +1631,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; @@ -1656,7 +1653,7 @@ RI_FKey_fk_upd_check_required(Trigger *trigger, Relation fk_rel, 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. */ @@ -1910,10 +1907,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 @@ -1933,7 +1937,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\"", @@ -1950,8 +1954,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) @@ -2429,7 +2435,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, @@ -2472,17 +2478,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); } @@ -2552,7 +2558,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); @@ -2563,11 +2569,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; @@ -2579,8 +2584,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' : ' '; } } @@ -2597,7 +2601,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; @@ -2676,7 +2680,8 @@ ri_ReportViolation(const RI_ConstraintInfo *riinfo, *val; name = SPI_fname(tupdesc, fnum); - val = SPI_getvalue(violator, tupdesc, fnum); + val = SPI_getvalue(ExecFetchSlotHeapTuple(violatorslot, false, NULL), + tupdesc, fnum); if (!val) val = "null"; @@ -2730,7 +2735,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; @@ -2745,7 +2750,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; @@ -2896,10 +2901,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; @@ -2915,6 +2919,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; @@ -2924,14 +2929,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; diff --git a/src/include/commands/trigger.h b/src/include/commands/trigger.h index 1031448..0f02bae 100644 --- a/src/include/commands/trigger.h +++ b/src/include/commands/trigger.h @@ -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; @@ -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); diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index 14c4e3a..369d1e6 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -493,6 +493,10 @@ extern void ReScanExprContext(ExprContext *econtext); extern ExprContext *MakePerTupleExprContext(EState *estate); +extern TupleTableSlot *ExecTriggerGetOldSlot(EState *estate, Relation rel); +extern TupleTableSlot *ExecTriggerGetNewSlot(EState *estate, Relation rel); +extern TupleTableSlot *ExecTriggerGetReturnSlot(EState *estate, Relation rel); + /* Get an EState's per-output-tuple exprcontext, making it if first use */ #define GetPerTupleExprContext(estate) \ ((estate)->es_per_tuple_exprcontext ? \ diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 569cc7c..c7e6e88 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -525,7 +525,7 @@ 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_return_slot; /* for trigger output tuples */ TupleTableSlot *es_trig_oldtup_slot; /* for TriggerEnabled */ TupleTableSlot *es_trig_newtup_slot; /* for TriggerEnabled */ -- 2.1.4