Commit ff11e7f4 authored by Andres Freund's avatar Andres Freund

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