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