Trigger tuple slotification (extracted from pluggable storage patch)
Hi,
In [1]https://commitfest.postgresql.org/14/1283/, there was a plan to do the trigger tuple slotification
changes as a separate patch, and not include them in the pluggable
storage changes. I have come up with such a patch by extracting the
trigger-specific changes from the pluggable storage changes proposed
in [1]https://commitfest.postgresql.org/14/1283/. I used this repository to extract the changes :
https://github.com/anarazel/postgres-pluggable-storage.git
Extracted changes from this commit :
commit 6066202a97c9a2d540fd2a2e5fd2cad22f307cd2
Author: Andres Freund <andres@anarazel.de>
Date: Tue Oct 2 22:15:23 2018 -0700
Pluggable Storage.
Basically, the attached patch contains changes that slotify the
trigger tuple handling. Details below :
In the pluggable storage changes, there were some TODOs such as this
in trigger.c :
- /* TODO : */
- if (false && oldtuple != newtuple && oldtuple != slottuple)
I removed this condition. Andres, you had put this in the pluggable
storage patch, probably to check whether it is important. But after
giving a thought, I think it is not required.
Did all the slotification changes in ri_trigger.c also, because they
are all dependent on the slotification of Trigger tuples.
In AfterTriggerSaveEvent(), newslot->tts_tid value is copied over to
new_event.ate_ctid1. Since this function only accepts slot now,
there's no point in retrieving the tid from the slot tuple since later
on we are going to have tid in the slot, looking at the pluggable
storage changes. So in a separate patch
(0001-Populate-slot-tts_tid-wherever-tuple-t_self-changes.patch), I
have just added this field in the TupleTableSlot, and populated
slot->tts_tid wherever tuple->t_self changes.
[1]: https://commitfest.postgresql.org/14/1283/
--
Thanks,
-Amit Khandekar
EnterpriseDB Corporation
The Postgres Database Company
Attachments:
0001-Populate-slot-tts_tid-wherever-tuple-t_self-changes.patchapplication/octet-stream; name=0001-Populate-slot-tts_tid-wherever-tuple-t_self-changes.patchDownload
From c3f827a729f457c8b3bf760553b6c85d5b0a0db1 Mon Sep 17 00:00:00 2001
From: Amit Khandekar <amit.khandekar@enterprisedb.com>
Date: Mon, 19 Nov 2018 12:09:20 +0530
Subject: [PATCH 1/2] Populate &slot->tts_tid wherever tuple->t_self changes.
Author: Andres Freund.
---
src/backend/commands/copy.c | 4 +++-
src/backend/commands/createas.c | 1 +
src/backend/commands/matview.c | 1 +
src/backend/commands/tablecmds.c | 3 +++
src/backend/executor/execReplication.c | 1 +
src/backend/executor/execTuples.c | 2 ++
src/backend/executor/nodeModifyTable.c | 4 ++++
src/include/executor/tuptable.h | 1 +
8 files changed, 16 insertions(+), 1 deletion(-)
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index a61f3bc..93badf7 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -2953,9 +2953,11 @@ CopyFrom(CopyState cstate)
tuple->t_tableOid = RelationGetRelid(resultRelInfo->ri_RelationDesc);
}
else
+ {
heap_insert(resultRelInfo->ri_RelationDesc, tuple,
mycid, hi_options, bistate);
-
+ ItemPointerCopy(&tuple->t_self, &slot->tts_tid);
+ }
/* And create index entries for it */
if (resultRelInfo->ri_NumIndices > 0)
recheckIndexes = ExecInsertIndexTuples(slot,
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 7b60aa9..7df40c6 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -602,6 +602,7 @@ intorel_receive(TupleTableSlot *slot, DestReceiver *self)
myState->output_cid,
myState->hi_options,
myState->bistate);
+ ItemPointerCopy(&tuple->t_self, &slot->tts_tid);
/* We know this is a newly created relation, so there are no indexes */
diff --git a/src/backend/commands/matview.c b/src/backend/commands/matview.c
index fd12288..f0120e4 100644
--- a/src/backend/commands/matview.c
+++ b/src/backend/commands/matview.c
@@ -491,6 +491,7 @@ transientrel_receive(TupleTableSlot *slot, DestReceiver *self)
myState->output_cid,
myState->hi_options,
myState->bistate);
+ ItemPointerCopy(&tuple->t_self, &slot->tts_tid);
/* We know this is a newly created relation, so there are no indexes */
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index a15e604..dfa15a5 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -4875,7 +4875,10 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
/* Write the tuple out to the new relation */
if (newrel)
+ {
heap_insert(newrel, tuple, mycid, hi_options, bistate);
+ ItemPointerCopy(&tuple->t_self, &newslot->tts_tid);
+ }
ResetExprContext(econtext);
diff --git a/src/backend/executor/execReplication.c b/src/backend/executor/execReplication.c
index 5bd3bbc..fc00b49 100644
--- a/src/backend/executor/execReplication.c
+++ b/src/backend/executor/execReplication.c
@@ -500,6 +500,7 @@ ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate,
/* OK, update the tuple and index entries for it */
simple_heap_update(rel, &hsearchslot->tuple->t_self, hslot->tuple);
+ ItemPointerCopy(&hslot->tuple->t_self, &hslot->base.tts_tid);
if (resultRelInfo->ri_NumIndices > 0 &&
!HeapTupleIsHeapOnly(hslot->tuple))
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index c9523c5..47aed0a 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -432,6 +432,7 @@ tts_heap_store_tuple(TupleTableSlot *slot, HeapTuple tuple, bool shouldFree)
hslot->tuple = tuple;
hslot->off = 0;
slot->tts_flags &= ~TTS_FLAG_EMPTY;
+ slot->tts_tid = tuple->t_self;
if (shouldFree)
slot->tts_flags |= TTS_FLAG_SHOULDFREE;
@@ -810,6 +811,7 @@ tts_buffer_heap_store_tuple(TupleTableSlot *slot, HeapTuple tuple, Buffer buffer
slot->tts_nvalid = 0;
bslot->base.tuple = tuple;
bslot->base.off = 0;
+ slot->tts_tid = tuple->t_self;
/*
* If tuple is on a disk page, keep the page pinned as long as we hold a
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index 7e05c15..9786d95 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -477,6 +477,7 @@ ExecInsert(ModifyTableState *mtstate,
estate->es_output_cid,
HEAP_INSERT_SPECULATIVE,
NULL);
+ ItemPointerCopy(&tuple->t_self, &slot->tts_tid);
/* insert index entries for tuple */
recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self),
@@ -522,6 +523,7 @@ ExecInsert(ModifyTableState *mtstate,
newId = heap_insert(resultRelationDesc, tuple,
estate->es_output_cid,
0, NULL);
+ ItemPointerCopy(&tuple->t_self, &slot->tts_tid);
/* insert index entries for tuple */
if (resultRelInfo->ri_NumIndices > 0)
@@ -1204,6 +1206,8 @@ lreplace:;
estate->es_crosscheck_snapshot,
true /* wait for commit */ ,
&hufd, &lockmode);
+ ItemPointerCopy(&tuple->t_self, &slot->tts_tid);
+
switch (result)
{
case HeapTupleSelfUpdated:
diff --git a/src/include/executor/tuptable.h b/src/include/executor/tuptable.h
index db5031f..6990f6e 100644
--- a/src/include/executor/tuptable.h
+++ b/src/include/executor/tuptable.h
@@ -125,6 +125,7 @@ typedef struct TupleTableSlot
#define FIELDNO_TUPLETABLESLOT_ISNULL 6
bool *tts_isnull; /* current per-attribute isnull flags */
MemoryContext tts_mcxt; /* slot itself is in this context */
+ ItemPointerData tts_tid; /* tid of the underlying tuple */
} TupleTableSlot;
/* routines for a TupleTableSlot implementation */
--
2.1.4
0002-Trigger-tuple-slotification.patchapplication/octet-stream; name=0002-Trigger-tuple-slotification.patchDownload
From 732b8e82496e1a45fdd7ccbd1f87105dc0b22b95 Mon Sep 17 00:00:00 2001
From: Amit Khandekar <amit.khandekar@enterprisedb.com>
Date: Mon, 19 Nov 2018 14:25:30 +0530
Subject: [PATCH 2/2] Trigger tuple slotification.
Extracted the trigger-related changes from the pluggable storage patch
present in this repository :
https://github.com/anarazel/postgres-pluggable-storage.git
Extracted changes from this commit :
commit 6066202a97c9a2d540fd2a2e5fd2cad22f307cd2
Author: Andres Freund <andres@anarazel.de>
Date: Tue Oct 2 22:15:23 2018 -0700
Pluggable Storage.
---
contrib/postgres_fdw/postgres_fdw.c | 2 +-
src/backend/commands/copy.c | 23 +-
src/backend/commands/tablecmds.c | 8 +-
src/backend/commands/trigger.c | 559 +++++++++++++++----------------
src/backend/executor/execMain.c | 2 +-
src/backend/executor/execReplication.c | 17 +-
src/backend/executor/execUtils.c | 70 +++-
src/backend/executor/nodeModifyTable.c | 60 +---
src/backend/replication/logical/worker.c | 5 -
src/backend/utils/adt/ri_triggers.c | 149 ++++----
src/include/commands/trigger.h | 20 +-
src/include/executor/executor.h | 4 +
src/include/nodes/execnodes.h | 2 +-
13 files changed, 468 insertions(+), 453 deletions(-)
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index 6f9c6e1..472f0a5 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -3904,7 +3904,7 @@ apply_returning_filter(PgFdwDirectModifyState *dmstate,
/*
* Use the trigger tuple slot as a place to store the result tuple.
*/
- resultSlot = estate->es_trig_tuple_slot;
+ resultSlot = ExecTriggerGetReturnSlot(estate, dmstate->resultRel);
if (resultSlot->tts_tupleDescriptor != resultTupType)
ExecSetSlotDescriptor(resultSlot, resultTupType);
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index 93badf7..2ecd6e7 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -2490,9 +2490,6 @@ CopyFrom(CopyState cstate)
/* Set up a tuple slot too */
myslot = ExecInitExtraTupleSlot(estate, tupDesc,
&TTSOpsHeapTuple);
- /* Triggers might need a slot as well */
- estate->es_trig_tuple_slot = ExecInitExtraTupleSlot(estate, NULL,
- &TTSOpsHeapTuple);
/*
* Set up a ModifyTableState so we can let FDW(s) init themselves for
@@ -2862,12 +2859,15 @@ CopyFrom(CopyState cstate)
/* BEFORE ROW INSERT Triggers */
if (has_before_insert_row_trig)
{
- slot = ExecBRInsertTriggers(estate, resultRelInfo, slot);
-
- if (slot == NULL) /* "do nothing" */
- skip_tuple = true;
+ if (!ExecBRInsertTriggers(estate, resultRelInfo, slot))
+ skip_tuple = true; /* "do nothing" */
else /* trigger might have changed tuple */
- tuple = ExecFetchSlotHeapTuple(slot, true, NULL);
+ {
+ /* Similar to above, get a tuple copy in per-tuple context */
+ oldcontext = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
+ tuple = ExecCopySlotHeapTuple(slot);
+ MemoryContextSwitchTo(oldcontext);
+ }
}
if (!skip_tuple)
@@ -2968,7 +2968,7 @@ CopyFrom(CopyState cstate)
NIL);
/* AFTER ROW INSERT Triggers */
- ExecARInsertTriggers(estate, resultRelInfo, tuple,
+ ExecARInsertTriggers(estate, resultRelInfo, slot,
recheckIndexes, cstate->transition_capture);
list_free(recheckIndexes);
@@ -3106,7 +3106,7 @@ CopyFromInsertBatch(CopyState cstate, EState *estate, CommandId mycid,
ExecInsertIndexTuples(myslot, &(bufferedTuples[i]->t_self),
estate, false, NULL, NIL);
ExecARInsertTriggers(estate, resultRelInfo,
- bufferedTuples[i],
+ myslot,
recheckIndexes, cstate->transition_capture);
list_free(recheckIndexes);
}
@@ -3123,8 +3123,9 @@ CopyFromInsertBatch(CopyState cstate, EState *estate, CommandId mycid,
for (i = 0; i < nBufferedTuples; i++)
{
cstate->cur_lineno = firstBufferedLineNo + i;
+ ExecStoreHeapTuple(bufferedTuples[i], myslot, false);
ExecARInsertTriggers(estate, resultRelInfo,
- bufferedTuples[i],
+ myslot,
NIL, cstate->transition_capture);
}
}
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index dfa15a5..2ed69ea 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -8578,6 +8578,7 @@ validateForeignKeyConstraint(char *conname,
{
HeapScanDesc scan;
HeapTuple tuple;
+ TupleTableSlot *slot;
Trigger trig;
Snapshot snapshot;
@@ -8613,12 +8614,15 @@ validateForeignKeyConstraint(char *conname,
*/
snapshot = RegisterSnapshot(GetLatestSnapshot());
scan = heap_beginscan(rel, snapshot, 0, NULL);
+ slot = MakeSingleTupleTableSlot(RelationGetDescr(rel), &TTSOpsBufferHeapTuple);
while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
{
FunctionCallInfoData fcinfo;
TriggerData trigdata;
+ ExecStoreBufferHeapTuple(tuple, slot, scan->rs_cbuf);
+
/*
* Make a call to the trigger function
*
@@ -8633,10 +8637,9 @@ validateForeignKeyConstraint(char *conname,
trigdata.tg_event = TRIGGER_EVENT_INSERT | TRIGGER_EVENT_ROW;
trigdata.tg_relation = rel;
trigdata.tg_trigtuple = tuple;
+ trigdata.tg_trigslot = slot;
trigdata.tg_newtuple = NULL;
trigdata.tg_trigger = &trig;
- trigdata.tg_trigtuplebuf = scan->rs_cbuf;
- trigdata.tg_newtuplebuf = InvalidBuffer;
fcinfo.context = (Node *) &trigdata;
@@ -8645,6 +8648,7 @@ validateForeignKeyConstraint(char *conname,
heap_endscan(scan);
UnregisterSnapshot(snapshot);
+ ExecDropSingleTupleTableSlot(slot);
}
static void
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index b91ebdb..9febe99 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -80,16 +80,18 @@ static int MyTriggerDepth = 0;
/* Local function prototypes */
static void ConvertTriggerToFK(CreateTrigStmt *stmt, Oid funcoid);
static void SetTriggerFlags(TriggerDesc *trigdesc, Trigger *trigger);
-static HeapTuple GetTupleForTrigger(EState *estate,
+static bool GetTupleForTrigger(EState *estate,
EPQState *epqstate,
ResultRelInfo *relinfo,
ItemPointer tid,
LockTupleMode lockmode,
- TupleTableSlot **newSlot);
+ TupleTableSlot *oldslot,
+ TupleTableSlot *newslot,
+ bool *is_epq);
static bool TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
Trigger *trigger, TriggerEvent event,
Bitmapset *modifiedCols,
- HeapTuple oldtup, HeapTuple newtup);
+ TupleTableSlot *oldslot, TupleTableSlot *newslot);
static HeapTuple ExecCallTriggerFunc(TriggerData *trigdata,
int tgindx,
FmgrInfo *finfo,
@@ -97,7 +99,7 @@ static HeapTuple ExecCallTriggerFunc(TriggerData *trigdata,
MemoryContext per_tuple_context);
static void AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo,
int event, bool row_trigger,
- HeapTuple oldtup, HeapTuple newtup,
+ TupleTableSlot *oldtup, TupleTableSlot *newtup,
List *recheckIndexes, Bitmapset *modifiedCols,
TransitionCaptureState *transition_capture);
static void AfterTriggerEnlargeQueryState(void);
@@ -2469,10 +2471,11 @@ ExecBSInsertTriggers(EState *estate, ResultRelInfo *relinfo)
LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
LocTriggerData.tg_trigtuple = NULL;
LocTriggerData.tg_newtuple = NULL;
+ LocTriggerData.tg_trigslot = NULL;
+ LocTriggerData.tg_newslot = NULL;
LocTriggerData.tg_oldtable = NULL;
LocTriggerData.tg_newtable = NULL;
- LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
- LocTriggerData.tg_newtuplebuf = InvalidBuffer;
+
for (i = 0; i < trigdesc->numtriggers; i++)
{
Trigger *trigger = &trigdesc->triggers[i];
@@ -2512,7 +2515,7 @@ ExecASInsertTriggers(EState *estate, ResultRelInfo *relinfo,
false, NULL, NULL, NIL, NULL, transition_capture);
}
-TupleTableSlot *
+bool
ExecBRInsertTriggers(EState *estate, ResultRelInfo *relinfo,
TupleTableSlot *slot)
{
@@ -2529,10 +2532,13 @@ ExecBRInsertTriggers(EState *estate, ResultRelInfo *relinfo,
TRIGGER_EVENT_ROW |
TRIGGER_EVENT_BEFORE;
LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
+ LocTriggerData.tg_trigtuple = NULL;
LocTriggerData.tg_newtuple = NULL;
+ LocTriggerData.tg_trigslot = NULL;
+ LocTriggerData.tg_newslot = NULL;
LocTriggerData.tg_oldtable = NULL;
LocTriggerData.tg_newtable = NULL;
- LocTriggerData.tg_newtuplebuf = InvalidBuffer;
+
for (i = 0; i < trigdesc->numtriggers; i++)
{
Trigger *trigger = &trigdesc->triggers[i];
@@ -2543,52 +2549,37 @@ ExecBRInsertTriggers(EState *estate, ResultRelInfo *relinfo,
TRIGGER_TYPE_INSERT))
continue;
if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
- NULL, NULL, newtuple))
+ NULL, NULL, slot))
continue;
+ LocTriggerData.tg_trigslot = slot;
LocTriggerData.tg_trigtuple = oldtuple = newtuple;
- LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
+
LocTriggerData.tg_trigger = trigger;
newtuple = ExecCallTriggerFunc(&LocTriggerData,
i,
relinfo->ri_TrigFunctions,
relinfo->ri_TrigInstrument,
GetPerTupleMemoryContext(estate));
- if (oldtuple != newtuple && oldtuple != slottuple)
- heap_freetuple(oldtuple);
+
if (newtuple == NULL)
{
if (should_free)
heap_freetuple(slottuple);
- return NULL; /* "do nothing" */
+ return false;
}
- }
-
- if (newtuple != slottuple)
- {
- /*
- * Return the modified tuple using the es_trig_tuple_slot. We assume
- * the tuple was allocated in per-tuple memory context, and therefore
- * will go away by itself. The tuple table slot should not try to
- * clear it.
- */
- TupleTableSlot *newslot = estate->es_trig_tuple_slot;
- TupleDesc tupdesc = RelationGetDescr(relinfo->ri_RelationDesc);
-
- if (newslot->tts_tupleDescriptor != tupdesc)
- ExecSetSlotDescriptor(newslot, tupdesc);
- ExecStoreHeapTuple(newtuple, newslot, false);
- slot = newslot;
+ if (newtuple != oldtuple)
+ ExecForceStoreHeapTuple(newtuple, slot);
}
if (should_free)
heap_freetuple(slottuple);
- return slot;
+ return true;
}
void
ExecARInsertTriggers(EState *estate, ResultRelInfo *relinfo,
- HeapTuple trigtuple, List *recheckIndexes,
+ TupleTableSlot *slot, List *recheckIndexes,
TransitionCaptureState *transition_capture)
{
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
@@ -2596,12 +2587,12 @@ ExecARInsertTriggers(EState *estate, ResultRelInfo *relinfo,
if ((trigdesc && trigdesc->trig_insert_after_row) ||
(transition_capture && transition_capture->tcs_insert_new_table))
AfterTriggerSaveEvent(estate, relinfo, TRIGGER_EVENT_INSERT,
- true, NULL, trigtuple,
+ true, NULL, slot,
recheckIndexes, NULL,
transition_capture);
}
-TupleTableSlot *
+bool
ExecIRInsertTriggers(EState *estate, ResultRelInfo *relinfo,
TupleTableSlot *slot)
{
@@ -2618,10 +2609,13 @@ ExecIRInsertTriggers(EState *estate, ResultRelInfo *relinfo,
TRIGGER_EVENT_ROW |
TRIGGER_EVENT_INSTEAD;
LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
+ LocTriggerData.tg_trigtuple = NULL;
LocTriggerData.tg_newtuple = NULL;
+ LocTriggerData.tg_trigslot = NULL;
+ LocTriggerData.tg_newslot = NULL;
LocTriggerData.tg_oldtable = NULL;
LocTriggerData.tg_newtable = NULL;
- LocTriggerData.tg_newtuplebuf = InvalidBuffer;
+
for (i = 0; i < trigdesc->numtriggers; i++)
{
Trigger *trigger = &trigdesc->triggers[i];
@@ -2632,47 +2626,33 @@ ExecIRInsertTriggers(EState *estate, ResultRelInfo *relinfo,
TRIGGER_TYPE_INSERT))
continue;
if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
- NULL, NULL, newtuple))
+ NULL, NULL, slot))
continue;
+ LocTriggerData.tg_trigslot = slot;
LocTriggerData.tg_trigtuple = oldtuple = newtuple;
- LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
LocTriggerData.tg_trigger = trigger;
newtuple = ExecCallTriggerFunc(&LocTriggerData,
i,
relinfo->ri_TrigFunctions,
relinfo->ri_TrigInstrument,
GetPerTupleMemoryContext(estate));
- if (oldtuple != newtuple && oldtuple != slottuple)
- heap_freetuple(oldtuple);
if (newtuple == NULL)
{
if (should_free)
heap_freetuple(slottuple);
- return NULL; /* "do nothing" */
+ return false; /* "do nothing" */
+ }
+ if (oldtuple != newtuple)
+ {
+ ExecForceStoreHeapTuple(newtuple, LocTriggerData.tg_trigslot);
+ newtuple = ExecFetchSlotHeapTuple(slot, false, NULL);
}
- }
-
- if (newtuple != slottuple)
- {
- /*
- * Return the modified tuple using the es_trig_tuple_slot. We assume
- * the tuple was allocated in per-tuple memory context, and therefore
- * will go away by itself. The tuple table slot should not try to
- * clear it.
- */
- TupleTableSlot *newslot = estate->es_trig_tuple_slot;
- TupleDesc tupdesc = RelationGetDescr(relinfo->ri_RelationDesc);
-
- if (newslot->tts_tupleDescriptor != tupdesc)
- ExecSetSlotDescriptor(newslot, tupdesc);
- ExecStoreHeapTuple(newtuple, newslot, false);
- slot = newslot;
}
if (should_free)
heap_freetuple(slottuple);
- return slot;
+ return true;
}
void
@@ -2700,10 +2680,11 @@ ExecBSDeleteTriggers(EState *estate, ResultRelInfo *relinfo)
LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
LocTriggerData.tg_trigtuple = NULL;
LocTriggerData.tg_newtuple = NULL;
+ LocTriggerData.tg_trigslot = NULL;
+ LocTriggerData.tg_newslot = NULL;
LocTriggerData.tg_oldtable = NULL;
LocTriggerData.tg_newtable = NULL;
- LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
- LocTriggerData.tg_newtuplebuf = InvalidBuffer;
+
for (i = 0; i < trigdesc->numtriggers; i++)
{
Trigger *trigger = &trigdesc->triggers[i];
@@ -2757,20 +2738,20 @@ ExecBRDeleteTriggers(EState *estate, EPQState *epqstate,
HeapTuple fdw_trigtuple,
TupleTableSlot **epqslot)
{
+ TupleTableSlot *slot = ExecTriggerGetOldSlot(estate, relinfo->ri_RelationDesc);
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
bool result = true;
TriggerData LocTriggerData;
HeapTuple trigtuple;
- HeapTuple newtuple;
- TupleTableSlot *newSlot;
int i;
Assert(HeapTupleIsValid(fdw_trigtuple) ^ ItemPointerIsValid(tupleid));
if (fdw_trigtuple == NULL)
{
- trigtuple = GetTupleForTrigger(estate, epqstate, relinfo, tupleid,
- LockTupleExclusive, &newSlot);
- if (trigtuple == NULL)
+ bool is_epqtuple;
+
+ if (!GetTupleForTrigger(estate, epqstate, relinfo, tupleid,
+ LockTupleExclusive, slot, NULL, &is_epqtuple))
return false;
/*
@@ -2778,27 +2759,36 @@ ExecBRDeleteTriggers(EState *estate, EPQState *epqstate,
* function requested for the updated tuple, skip the trigger
* execution.
*/
- if (newSlot != NULL && epqslot != NULL)
+ if (is_epqtuple && epqslot != NULL)
{
- *epqslot = newSlot;
- heap_freetuple(trigtuple);
+ *epqslot = slot;
return false;
}
+
+ trigtuple = ExecFetchSlotHeapTuple(slot, false, NULL);
+
}
else
+ {
trigtuple = fdw_trigtuple;
+ ExecForceStoreHeapTuple(trigtuple, slot);
+ }
LocTriggerData.type = T_TriggerData;
LocTriggerData.tg_event = TRIGGER_EVENT_DELETE |
TRIGGER_EVENT_ROW |
TRIGGER_EVENT_BEFORE;
LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
+ LocTriggerData.tg_trigtuple = NULL;
LocTriggerData.tg_newtuple = NULL;
+ LocTriggerData.tg_trigslot = NULL;
+ LocTriggerData.tg_newslot = NULL;
LocTriggerData.tg_oldtable = NULL;
LocTriggerData.tg_newtable = NULL;
- LocTriggerData.tg_newtuplebuf = InvalidBuffer;
+
for (i = 0; i < trigdesc->numtriggers; i++)
{
+ HeapTuple newtuple;
Trigger *trigger = &trigdesc->triggers[i];
if (!TRIGGER_TYPE_MATCHES(trigger->tgtype,
@@ -2807,11 +2797,11 @@ ExecBRDeleteTriggers(EState *estate, EPQState *epqstate,
TRIGGER_TYPE_DELETE))
continue;
if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
- NULL, trigtuple, NULL))
+ NULL, slot, NULL))
continue;
+ LocTriggerData.tg_trigslot = slot;
LocTriggerData.tg_trigtuple = trigtuple;
- LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
LocTriggerData.tg_trigger = trigger;
newtuple = ExecCallTriggerFunc(&LocTriggerData,
i,
@@ -2823,10 +2813,10 @@ ExecBRDeleteTriggers(EState *estate, EPQState *epqstate,
result = false; /* tell caller to suppress delete */
break;
}
- if (newtuple != trigtuple)
+ if (false && newtuple != trigtuple)
heap_freetuple(newtuple);
}
- if (trigtuple != fdw_trigtuple)
+ if (false && trigtuple != fdw_trigtuple)
heap_freetuple(trigtuple);
return result;
@@ -2839,28 +2829,31 @@ ExecARDeleteTriggers(EState *estate, ResultRelInfo *relinfo,
TransitionCaptureState *transition_capture)
{
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
+ TupleTableSlot *slot = ExecTriggerGetOldSlot(estate, relinfo->ri_RelationDesc);
if ((trigdesc && trigdesc->trig_delete_after_row) ||
(transition_capture && transition_capture->tcs_delete_old_table))
{
- HeapTuple trigtuple;
-
Assert(HeapTupleIsValid(fdw_trigtuple) ^ ItemPointerIsValid(tupleid));
if (fdw_trigtuple == NULL)
- trigtuple = GetTupleForTrigger(estate,
- NULL,
- relinfo,
- tupleid,
- LockTupleExclusive,
- NULL);
+ {
+ GetTupleForTrigger(estate,
+ NULL,
+ relinfo,
+ tupleid,
+ LockTupleExclusive,
+ slot,
+ NULL,
+ NULL);
+ }
else
- trigtuple = fdw_trigtuple;
+ {
+ ExecForceStoreHeapTuple(fdw_trigtuple, slot);
+ }
AfterTriggerSaveEvent(estate, relinfo, TRIGGER_EVENT_DELETE,
- true, trigtuple, NULL, NIL, NULL,
+ true, slot, NULL, NIL, NULL,
transition_capture);
- if (trigtuple != fdw_trigtuple)
- heap_freetuple(trigtuple);
}
}
@@ -2869,8 +2862,8 @@ ExecIRDeleteTriggers(EState *estate, ResultRelInfo *relinfo,
HeapTuple trigtuple)
{
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
+ TupleTableSlot *slot = ExecTriggerGetOldSlot(estate, relinfo->ri_RelationDesc);
TriggerData LocTriggerData;
- HeapTuple rettuple;
int i;
LocTriggerData.type = T_TriggerData;
@@ -2878,12 +2871,18 @@ ExecIRDeleteTriggers(EState *estate, ResultRelInfo *relinfo,
TRIGGER_EVENT_ROW |
TRIGGER_EVENT_INSTEAD;
LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
+ LocTriggerData.tg_trigtuple = NULL;
LocTriggerData.tg_newtuple = NULL;
+ LocTriggerData.tg_trigslot = NULL;
+ LocTriggerData.tg_newslot = NULL;
LocTriggerData.tg_oldtable = NULL;
LocTriggerData.tg_newtable = NULL;
- LocTriggerData.tg_newtuplebuf = InvalidBuffer;
+
+ ExecForceStoreHeapTuple(trigtuple, slot);
+
for (i = 0; i < trigdesc->numtriggers; i++)
{
+ HeapTuple rettuple;
Trigger *trigger = &trigdesc->triggers[i];
if (!TRIGGER_TYPE_MATCHES(trigger->tgtype,
@@ -2892,11 +2891,11 @@ ExecIRDeleteTriggers(EState *estate, ResultRelInfo *relinfo,
TRIGGER_TYPE_DELETE))
continue;
if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
- NULL, trigtuple, NULL))
+ NULL, slot, NULL))
continue;
+ LocTriggerData.tg_trigslot = slot;
LocTriggerData.tg_trigtuple = trigtuple;
- LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
LocTriggerData.tg_trigger = trigger;
rettuple = ExecCallTriggerFunc(&LocTriggerData,
i,
@@ -2905,7 +2904,7 @@ ExecIRDeleteTriggers(EState *estate, ResultRelInfo *relinfo,
GetPerTupleMemoryContext(estate));
if (rettuple == NULL)
return false; /* Delete was suppressed */
- if (rettuple != trigtuple)
+ if (false && rettuple != trigtuple)
heap_freetuple(rettuple);
}
return true;
@@ -2939,10 +2938,11 @@ ExecBSUpdateTriggers(EState *estate, ResultRelInfo *relinfo)
LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
LocTriggerData.tg_trigtuple = NULL;
LocTriggerData.tg_newtuple = NULL;
+ LocTriggerData.tg_trigslot = NULL;
+ LocTriggerData.tg_newslot = NULL;
LocTriggerData.tg_oldtable = NULL;
LocTriggerData.tg_newtable = NULL;
- LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
- LocTriggerData.tg_newtuplebuf = InvalidBuffer;
+
for (i = 0; i < trigdesc->numtriggers; i++)
{
Trigger *trigger = &trigdesc->triggers[i];
@@ -2984,20 +2984,19 @@ ExecASUpdateTriggers(EState *estate, ResultRelInfo *relinfo,
transition_capture);
}
-TupleTableSlot *
+bool
ExecBRUpdateTriggers(EState *estate, EPQState *epqstate,
ResultRelInfo *relinfo,
ItemPointer tupleid,
HeapTuple fdw_trigtuple,
- TupleTableSlot *slot)
+ TupleTableSlot *newslot)
{
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
- HeapTuple slottuple = ExecFetchSlotHeapTuple(slot, true, NULL);
+ TupleTableSlot *oldslot = ExecTriggerGetOldSlot(estate, relinfo->ri_RelationDesc);
+ HeapTuple slottuple = ExecFetchSlotHeapTuple(newslot, true, NULL);
HeapTuple newtuple = slottuple;
TriggerData LocTriggerData;
HeapTuple trigtuple;
- HeapTuple oldtuple;
- TupleTableSlot *newSlot;
int i;
Bitmapset *updatedCols;
LockTupleMode lockmode;
@@ -3008,37 +3007,41 @@ ExecBRUpdateTriggers(EState *estate, EPQState *epqstate,
Assert(HeapTupleIsValid(fdw_trigtuple) ^ ItemPointerIsValid(tupleid));
if (fdw_trigtuple == NULL)
{
+ bool is_epqtuple = false;
+
/* get a copy of the on-disk tuple we are planning to update */
- trigtuple = GetTupleForTrigger(estate, epqstate, relinfo, tupleid,
- lockmode, &newSlot);
- if (trigtuple == NULL)
- return NULL; /* cancel the update action */
+ if (!GetTupleForTrigger(estate, epqstate, relinfo, tupleid,
+ lockmode, oldslot, newslot, &is_epqtuple))
+ return false; /* cancel the update action */
+
+ /*
+ * In READ COMMITTED isolation level it's possible that target tuple was
+ * changed due to concurrent update. In that case we have a raw subplan
+ * output tuple in newSlot, and need to run it through the junk filter to
+ * produce an insertable tuple.
+ *
+ * Caution: more than likely, the passed-in slot is the same as the
+ * junkfilter's output slot, so we are clobbering the original value of
+ * slottuple by doing the filtering. This is OK since neither we nor our
+ * caller have any more interest in the prior contents of that slot.
+ */
+ if (is_epqtuple)
+ {
+ TupleTableSlot *slot = ExecFilterJunk(relinfo->ri_junkFilter, newslot);
+
+ ExecCopySlot(newslot, slot);
+ slottuple = ExecFetchSlotHeapTuple(newslot, false, NULL);
+ newtuple = slottuple;
+ }
+
+ trigtuple = ExecFetchSlotHeapTuple(oldslot, false, NULL);
}
else
{
+ ExecForceStoreHeapTuple(fdw_trigtuple, oldslot);
trigtuple = fdw_trigtuple;
- newSlot = NULL;
}
- /*
- * In READ COMMITTED isolation level it's possible that target tuple was
- * changed due to concurrent update. In that case we have a raw subplan
- * output tuple in newSlot, and need to run it through the junk filter to
- * produce an insertable tuple.
- *
- * Caution: more than likely, the passed-in slot is the same as the
- * junkfilter's output slot, so we are clobbering the original value of
- * slottuple by doing the filtering. This is OK since neither we nor our
- * caller have any more interest in the prior contents of that slot.
- */
- if (newSlot != NULL)
- {
- slot = ExecFilterJunk(relinfo->ri_junkFilter, newSlot);
- slottuple = ExecFetchSlotHeapTuple(slot, true, NULL);
- newtuple = slottuple;
- }
-
-
LocTriggerData.type = T_TriggerData;
LocTriggerData.tg_event = TRIGGER_EVENT_UPDATE |
TRIGGER_EVENT_ROW |
@@ -3050,6 +3053,7 @@ ExecBRUpdateTriggers(EState *estate, EPQState *epqstate,
for (i = 0; i < trigdesc->numtriggers; i++)
{
Trigger *trigger = &trigdesc->triggers[i];
+ HeapTuple oldtuple;
if (!TRIGGER_TYPE_MATCHES(trigger->tgtype,
TRIGGER_TYPE_ROW,
@@ -3057,67 +3061,47 @@ ExecBRUpdateTriggers(EState *estate, EPQState *epqstate,
TRIGGER_TYPE_UPDATE))
continue;
if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
- updatedCols, trigtuple, newtuple))
+ updatedCols, oldslot, newslot))
continue;
+ LocTriggerData.tg_trigslot = oldslot;
LocTriggerData.tg_trigtuple = trigtuple;
LocTriggerData.tg_newtuple = oldtuple = newtuple;
- LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
- LocTriggerData.tg_newtuplebuf = InvalidBuffer;
+ LocTriggerData.tg_newslot = newslot;
LocTriggerData.tg_trigger = trigger;
newtuple = ExecCallTriggerFunc(&LocTriggerData,
i,
relinfo->ri_TrigFunctions,
relinfo->ri_TrigInstrument,
GetPerTupleMemoryContext(estate));
- if (oldtuple != newtuple && oldtuple != slottuple)
- heap_freetuple(oldtuple);
if (newtuple == NULL)
- {
- if (trigtuple != fdw_trigtuple)
- heap_freetuple(trigtuple);
- return NULL; /* "do nothing" */
- }
- }
- if (trigtuple != fdw_trigtuple && trigtuple != newtuple)
- heap_freetuple(trigtuple);
-
- if (newtuple != slottuple)
- {
- /*
- * Return the modified tuple using the es_trig_tuple_slot. We assume
- * the tuple was allocated in per-tuple memory context, and therefore
- * will go away by itself. The tuple table slot should not try to
- * clear it.
- */
- TupleTableSlot *newslot = estate->es_trig_tuple_slot;
- TupleDesc tupdesc = RelationGetDescr(relinfo->ri_RelationDesc);
+ return false; /* "do nothing" */
- if (newslot->tts_tupleDescriptor != tupdesc)
- ExecSetSlotDescriptor(newslot, tupdesc);
- ExecStoreHeapTuple(newtuple, newslot, false);
- slot = newslot;
+ if (newtuple != oldtuple)
+ ExecForceStoreHeapTuple(newtuple, newslot);
}
- return slot;
+
+ return true;
}
void
ExecARUpdateTriggers(EState *estate, ResultRelInfo *relinfo,
ItemPointer tupleid,
HeapTuple fdw_trigtuple,
- HeapTuple newtuple,
+ TupleTableSlot *newslot,
List *recheckIndexes,
TransitionCaptureState *transition_capture)
{
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
+ TupleTableSlot *oldslot = ExecTriggerGetOldSlot(estate, relinfo->ri_RelationDesc);
+
+ ExecClearTuple(oldslot);
if ((trigdesc && trigdesc->trig_update_after_row) ||
(transition_capture &&
(transition_capture->tcs_update_old_table ||
transition_capture->tcs_update_new_table)))
{
- HeapTuple trigtuple;
-
/*
* Note: if the UPDATE is converted into a DELETE+INSERT as part of
* update-partition-key operation, then this function is also called
@@ -3125,30 +3109,31 @@ ExecARUpdateTriggers(EState *estate, ResultRelInfo *relinfo,
* In such case, either old tuple or new tuple can be NULL.
*/
if (fdw_trigtuple == NULL && ItemPointerIsValid(tupleid))
- trigtuple = GetTupleForTrigger(estate,
- NULL,
- relinfo,
- tupleid,
- LockTupleExclusive,
- NULL);
- else
- trigtuple = fdw_trigtuple;
+ GetTupleForTrigger(estate,
+ NULL,
+ relinfo,
+ tupleid,
+ LockTupleExclusive,
+ oldslot,
+ NULL,
+ NULL);
+ else if (fdw_trigtuple != NULL)
+ ExecForceStoreHeapTuple(fdw_trigtuple, oldslot);
AfterTriggerSaveEvent(estate, relinfo, TRIGGER_EVENT_UPDATE,
- true, trigtuple, newtuple, recheckIndexes,
+ true, oldslot, newslot, recheckIndexes,
GetUpdatedColumns(relinfo, estate),
transition_capture);
- if (trigtuple != fdw_trigtuple)
- heap_freetuple(trigtuple);
}
}
-TupleTableSlot *
+bool
ExecIRUpdateTriggers(EState *estate, ResultRelInfo *relinfo,
- HeapTuple trigtuple, TupleTableSlot *slot)
+ HeapTuple trigtuple, TupleTableSlot *newslot)
{
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
- HeapTuple slottuple = ExecFetchSlotHeapTuple(slot, true, NULL);
+ TupleTableSlot *oldslot = ExecTriggerGetOldSlot(estate, relinfo->ri_RelationDesc);
+ HeapTuple slottuple = ExecFetchSlotHeapTuple(newslot, true, NULL);
HeapTuple newtuple = slottuple;
TriggerData LocTriggerData;
HeapTuple oldtuple;
@@ -3161,6 +3146,9 @@ ExecIRUpdateTriggers(EState *estate, ResultRelInfo *relinfo,
LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
LocTriggerData.tg_oldtable = NULL;
LocTriggerData.tg_newtable = NULL;
+
+ ExecForceStoreHeapTuple(trigtuple, oldslot);
+
for (i = 0; i < trigdesc->numtriggers; i++)
{
Trigger *trigger = &trigdesc->triggers[i];
@@ -3171,42 +3159,30 @@ ExecIRUpdateTriggers(EState *estate, ResultRelInfo *relinfo,
TRIGGER_TYPE_UPDATE))
continue;
if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
- NULL, trigtuple, newtuple))
+ NULL, oldslot, newslot))
continue;
+ LocTriggerData.tg_trigslot = oldslot;
LocTriggerData.tg_trigtuple = trigtuple;
+ LocTriggerData.tg_newslot = newslot;
LocTriggerData.tg_newtuple = oldtuple = newtuple;
- LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
- LocTriggerData.tg_newtuplebuf = InvalidBuffer;
+
LocTriggerData.tg_trigger = trigger;
newtuple = ExecCallTriggerFunc(&LocTriggerData,
i,
relinfo->ri_TrigFunctions,
relinfo->ri_TrigInstrument,
GetPerTupleMemoryContext(estate));
- if (oldtuple != newtuple && oldtuple != slottuple)
+ if (false && oldtuple != newtuple && oldtuple != slottuple)
heap_freetuple(oldtuple);
if (newtuple == NULL)
- return NULL; /* "do nothing" */
- }
+ return false; /* "do nothing" */
- if (newtuple != slottuple)
- {
- /*
- * Return the modified tuple using the es_trig_tuple_slot. We assume
- * the tuple was allocated in per-tuple memory context, and therefore
- * will go away by itself. The tuple table slot should not try to
- * clear it.
- */
- TupleTableSlot *newslot = estate->es_trig_tuple_slot;
- TupleDesc tupdesc = RelationGetDescr(relinfo->ri_RelationDesc);
-
- if (newslot->tts_tupleDescriptor != tupdesc)
- ExecSetSlotDescriptor(newslot, tupdesc);
- ExecStoreHeapTuple(newtuple, newslot, false);
- slot = newslot;
+ if (oldtuple != newtuple)
+ ExecForceStoreHeapTuple(newtuple, newslot);
}
- return slot;
+
+ return true;
}
void
@@ -3229,10 +3205,11 @@ ExecBSTruncateTriggers(EState *estate, ResultRelInfo *relinfo)
LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
LocTriggerData.tg_trigtuple = NULL;
LocTriggerData.tg_newtuple = NULL;
+ LocTriggerData.tg_trigslot = NULL;
+ LocTriggerData.tg_newslot = NULL;
LocTriggerData.tg_oldtable = NULL;
LocTriggerData.tg_newtable = NULL;
- LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
- LocTriggerData.tg_newtuplebuf = InvalidBuffer;
+
for (i = 0; i < trigdesc->numtriggers; i++)
{
Trigger *trigger = &trigdesc->triggers[i];
@@ -3272,25 +3249,27 @@ ExecASTruncateTriggers(EState *estate, ResultRelInfo *relinfo)
}
-static HeapTuple
+static bool
GetTupleForTrigger(EState *estate,
EPQState *epqstate,
ResultRelInfo *relinfo,
ItemPointer tid,
LockTupleMode lockmode,
- TupleTableSlot **newSlot)
+ TupleTableSlot *oldslot,
+ TupleTableSlot *newslot,
+ bool *is_epqtuple)
{
Relation relation = relinfo->ri_RelationDesc;
HeapTupleData tuple;
- HeapTuple result;
+ HeapTuple result = &tuple;
Buffer buffer;
- if (newSlot != NULL)
+ if (is_epqtuple)
{
HTSU_Result test;
HeapUpdateFailureData hufd;
- *newSlot = NULL;
+ *is_epqtuple = false;
/* caller must pass an epqstate if EvalPlanQual is possible */
Assert(epqstate != NULL);
@@ -3324,7 +3303,7 @@ ltrmark:;
/* treat it as deleted; do not process */
ReleaseBuffer(buffer);
- return NULL;
+ return false;
case HeapTupleMayBeUpdated:
break;
@@ -3355,7 +3334,12 @@ ltrmark:;
if (!TupIsNull(epqslot))
{
*tid = hufd.ctid;
- *newSlot = epqslot;
+
+ if (newslot)
+ ExecCopySlot(newslot, epqslot);
+ /* oldslot is updated in the function end */
+
+ *is_epqtuple = true;
/*
* EvalPlanQual already locked the tuple, but we
@@ -3371,7 +3355,7 @@ ltrmark:;
* if tuple was deleted or PlanQual failed for updated tuple -
* we must not process this tuple!
*/
- return NULL;
+ return false;
case HeapTupleInvisible:
elog(ERROR, "attempted to lock invisible tuple");
@@ -3380,7 +3364,7 @@ ltrmark:;
default:
ReleaseBuffer(buffer);
elog(ERROR, "unrecognized heap_lock_tuple status: %u", test);
- return NULL; /* keep compiler quiet */
+ return false; /* keep compiler quiet */
}
}
else
@@ -3415,11 +3399,13 @@ ltrmark:;
if (HeapTupleHeaderGetNatts(tuple.t_data) < relation->rd_att->natts)
result = heap_expand_tuple(&tuple, relation->rd_att);
- else
- result = heap_copytuple(&tuple);
+
+ ExecStoreBufferHeapTuple(result, oldslot, buffer);
+ ExecMaterializeSlot(oldslot);
+
ReleaseBuffer(buffer);
- return result;
+ return true;
}
/*
@@ -3429,7 +3415,7 @@ static bool
TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
Trigger *trigger, TriggerEvent event,
Bitmapset *modifiedCols,
- HeapTuple oldtup, HeapTuple newtup)
+ TupleTableSlot *oldslot, TupleTableSlot *newslot)
{
/* Check replication-role-dependent enable state */
if (SessionReplicationRole == SESSION_REPLICATION_ROLE_REPLICA)
@@ -3471,11 +3457,8 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
/* Check for WHEN clause */
if (trigger->tgqual)
{
- TupleDesc tupdesc = RelationGetDescr(relinfo->ri_RelationDesc);
ExprState **predicate;
ExprContext *econtext;
- TupleTableSlot *oldslot = NULL;
- TupleTableSlot *newslot = NULL;
MemoryContext oldContext;
int i;
@@ -3515,40 +3498,6 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
econtext = GetPerTupleExprContext(estate);
/*
- * Put OLD and NEW tuples into tupleslots for expression evaluation.
- * These slots can be shared across the whole estate, but be careful
- * that they have the current resultrel's tupdesc.
- */
- if (HeapTupleIsValid(oldtup))
- {
- if (estate->es_trig_oldtup_slot == NULL)
- {
- oldContext = MemoryContextSwitchTo(estate->es_query_cxt);
- estate->es_trig_oldtup_slot =
- ExecInitExtraTupleSlot(estate, NULL, &TTSOpsHeapTuple);
- MemoryContextSwitchTo(oldContext);
- }
- oldslot = estate->es_trig_oldtup_slot;
- if (oldslot->tts_tupleDescriptor != tupdesc)
- ExecSetSlotDescriptor(oldslot, tupdesc);
- ExecStoreHeapTuple(oldtup, oldslot, false);
- }
- if (HeapTupleIsValid(newtup))
- {
- if (estate->es_trig_newtup_slot == NULL)
- {
- oldContext = MemoryContextSwitchTo(estate->es_query_cxt);
- estate->es_trig_newtup_slot =
- ExecInitExtraTupleSlot(estate, NULL, &TTSOpsHeapTuple);
- MemoryContextSwitchTo(oldContext);
- }
- newslot = estate->es_trig_newtup_slot;
- if (newslot->tts_tupleDescriptor != tupdesc)
- ExecSetSlotDescriptor(newslot, tupdesc);
- ExecStoreHeapTuple(newtup, newslot, false);
- }
-
- /*
* Finally evaluate the expression, making the old and/or new tuples
* available as INNER_VAR/OUTER_VAR respectively.
*/
@@ -3881,7 +3830,8 @@ struct AfterTriggersTableData
static AfterTriggersData afterTriggers;
-static void AfterTriggerExecute(AfterTriggerEvent event,
+static void AfterTriggerExecute(EState *estate,
+ AfterTriggerEvent event,
Relation rel, TriggerDesc *trigdesc,
FmgrInfo *finfo,
Instrumentation *instr,
@@ -4216,7 +4166,8 @@ afterTriggerDeleteHeadEventChunk(AfterTriggersQueryData *qs)
* ----------
*/
static void
-AfterTriggerExecute(AfterTriggerEvent event,
+AfterTriggerExecute(EState *estate,
+ AfterTriggerEvent event,
Relation rel, TriggerDesc *trigdesc,
FmgrInfo *finfo, Instrumentation *instr,
MemoryContext per_tuple_context,
@@ -4229,14 +4180,15 @@ AfterTriggerExecute(AfterTriggerEvent event,
HeapTupleData tuple1;
HeapTupleData tuple2;
HeapTuple rettuple;
- Buffer buffer1 = InvalidBuffer;
- Buffer buffer2 = InvalidBuffer;
int tgindx;
/*
* Locate trigger in trigdesc.
*/
LocTriggerData.tg_trigger = NULL;
+ LocTriggerData.tg_trigslot = NULL;
+ LocTriggerData.tg_newslot = NULL;
+
for (tgindx = 0; tgindx < trigdesc->numtriggers; tgindx++)
{
if (trigdesc->triggers[tgindx].tgoid == tgoid)
@@ -4286,31 +4238,35 @@ AfterTriggerExecute(AfterTriggerEvent event,
* that is stored as a heap tuple, constructed in different memory
* context, in the slot anyway.
*/
- LocTriggerData.tg_trigtuple = ExecFetchSlotHeapTuple(trig_tuple_slot1,
- true, NULL);
- LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
+ LocTriggerData.tg_trigslot = trig_tuple_slot1;
+ LocTriggerData.tg_trigtuple =
+ ExecFetchSlotHeapTuple(trig_tuple_slot1, false, NULL);
+ LocTriggerData.tg_newslot = trig_tuple_slot2;
LocTriggerData.tg_newtuple =
((evtshared->ats_event & TRIGGER_EVENT_OPMASK) ==
TRIGGER_EVENT_UPDATE) ?
- ExecFetchSlotHeapTuple(trig_tuple_slot2, true, NULL) : NULL;
- LocTriggerData.tg_newtuplebuf = InvalidBuffer;
+ ExecFetchSlotHeapTuple(trig_tuple_slot2, false, NULL) : NULL;
break;
default:
if (ItemPointerIsValid(&(event->ate_ctid1)))
{
+ Buffer buffer;
+
ItemPointerCopy(&(event->ate_ctid1), &(tuple1.t_self));
- if (!heap_fetch(rel, SnapshotAny, &tuple1, &buffer1, false, NULL))
+ if (!heap_fetch(rel, SnapshotAny, &tuple1, &buffer, false, NULL))
elog(ERROR, "failed to fetch tuple1 for AFTER trigger");
+ LocTriggerData.tg_trigslot = ExecTriggerGetOldSlot(estate, rel);
+ ExecStoreBufferHeapTuple(&tuple1,
+ LocTriggerData.tg_trigslot, buffer);
LocTriggerData.tg_trigtuple = &tuple1;
- LocTriggerData.tg_trigtuplebuf = buffer1;
+ ReleaseBuffer(buffer);
}
else
{
LocTriggerData.tg_trigtuple = NULL;
- LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
}
/* don't touch ctid2 if not there */
@@ -4318,16 +4274,20 @@ AfterTriggerExecute(AfterTriggerEvent event,
AFTER_TRIGGER_2CTID &&
ItemPointerIsValid(&(event->ate_ctid2)))
{
+ Buffer buffer;
+
ItemPointerCopy(&(event->ate_ctid2), &(tuple2.t_self));
- if (!heap_fetch(rel, SnapshotAny, &tuple2, &buffer2, false, NULL))
+ if (!heap_fetch(rel, SnapshotAny, &tuple2, &buffer, false, NULL))
elog(ERROR, "failed to fetch tuple2 for AFTER trigger");
+ LocTriggerData.tg_newslot = ExecTriggerGetNewSlot(estate, rel);
+ ExecStoreBufferHeapTuple(&tuple2,
+ LocTriggerData.tg_newslot, buffer);
LocTriggerData.tg_newtuple = &tuple2;
- LocTriggerData.tg_newtuplebuf = buffer2;
+ ReleaseBuffer(buffer);
}
else
{
LocTriggerData.tg_newtuple = NULL;
- LocTriggerData.tg_newtuplebuf = InvalidBuffer;
}
}
@@ -4379,12 +4339,12 @@ AfterTriggerExecute(AfterTriggerEvent event,
heap_freetuple(rettuple);
/*
- * Release buffers
+ * Release resources
*/
- if (buffer1 != InvalidBuffer)
- ReleaseBuffer(buffer1);
- if (buffer2 != InvalidBuffer)
- ReleaseBuffer(buffer2);
+ if (LocTriggerData.tg_trigslot)
+ ExecClearTuple(LocTriggerData.tg_trigslot);
+ if (LocTriggerData.tg_newslot)
+ ExecClearTuple(LocTriggerData.tg_newslot);
/*
* If doing EXPLAIN ANALYZE, stop charging time to this trigger, and count
@@ -4561,7 +4521,7 @@ afterTriggerInvokeEvents(AfterTriggerEventList *events,
* still set, so recursive examinations of the event list
* won't try to re-fire it.
*/
- AfterTriggerExecute(event, rel, trigdesc, finfo, instr,
+ AfterTriggerExecute(estate, event, rel, trigdesc, finfo, instr,
per_tuple_context, slot1, slot2);
/*
@@ -4605,6 +4565,7 @@ afterTriggerInvokeEvents(AfterTriggerEventList *events,
if (local_estate)
{
ExecCleanUpTriggerState(estate);
+ ExecResetTupleTable(estate->es_tupleTable, false);
FreeExecutorState(estate);
}
@@ -5740,7 +5701,7 @@ AfterTriggerPendingOnRel(Oid relid)
static void
AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo,
int event, bool row_trigger,
- HeapTuple oldtup, HeapTuple newtup,
+ TupleTableSlot *oldslot, TupleTableSlot *newslot,
List *recheckIndexes, Bitmapset *modifiedCols,
TransitionCaptureState *transition_capture)
{
@@ -5789,11 +5750,11 @@ AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo,
* deleted.
*/
Assert(!(event == TRIGGER_EVENT_DELETE && delete_old_table &&
- oldtup == NULL));
+ TupIsNull(oldslot)));
Assert(!(event == TRIGGER_EVENT_INSERT && insert_new_table &&
- newtup == NULL));
+ TupIsNull(newslot)));
- if (oldtup != NULL &&
+ if (!TupIsNull(oldslot) &&
((event == TRIGGER_EVENT_DELETE && delete_old_table) ||
(event == TRIGGER_EVENT_UPDATE && update_old_table)))
{
@@ -5803,15 +5764,17 @@ AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo,
if (map != NULL)
{
- HeapTuple converted = execute_attr_map_tuple(oldtup, map);
+ HeapTuple tuple = ExecFetchSlotHeapTuple(oldslot, false, NULL);
+ HeapTuple converted;
+ converted = execute_attr_map_tuple(tuple, map);
tuplestore_puttuple(old_tuplestore, converted);
pfree(converted);
}
else
- tuplestore_puttuple(old_tuplestore, oldtup);
+ tuplestore_puttupleslot(old_tuplestore, oldslot);
}
- if (newtup != NULL &&
+ if (!TupIsNull(newslot) &&
((event == TRIGGER_EVENT_INSERT && insert_new_table) ||
(event == TRIGGER_EVENT_UPDATE && update_new_table)))
{
@@ -5823,13 +5786,15 @@ AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo,
tuplestore_puttuple(new_tuplestore, original_insert_tuple);
else if (map != NULL)
{
- HeapTuple converted = execute_attr_map_tuple(newtup, map);
+ HeapTuple tuple = ExecFetchSlotHeapTuple(newslot, false, NULL);
+ HeapTuple converted;
+ converted = execute_attr_map_tuple(tuple, map);
tuplestore_puttuple(new_tuplestore, converted);
pfree(converted);
}
else
- tuplestore_puttuple(new_tuplestore, newtup);
+ tuplestore_puttupleslot(new_tuplestore, newslot);
}
/*
@@ -5843,7 +5808,7 @@ AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo,
(event == TRIGGER_EVENT_DELETE && !trigdesc->trig_delete_after_row) ||
(event == TRIGGER_EVENT_INSERT && !trigdesc->trig_insert_after_row) ||
(event == TRIGGER_EVENT_UPDATE && !trigdesc->trig_update_after_row) ||
- (event == TRIGGER_EVENT_UPDATE && ((oldtup == NULL) ^ (newtup == NULL))))
+ (event == TRIGGER_EVENT_UPDATE && (TupIsNull(oldslot) ^ TupIsNull(newslot))))
return;
}
@@ -5865,15 +5830,15 @@ AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo,
tgtype_event = TRIGGER_TYPE_INSERT;
if (row_trigger)
{
- Assert(oldtup == NULL);
- Assert(newtup != NULL);
- ItemPointerCopy(&(newtup->t_self), &(new_event.ate_ctid1));
+ Assert(oldslot == NULL);
+ Assert(newslot != NULL);
+ ItemPointerCopy(&(newslot->tts_tid), &(new_event.ate_ctid1));
ItemPointerSetInvalid(&(new_event.ate_ctid2));
}
else
{
- Assert(oldtup == NULL);
- Assert(newtup == NULL);
+ Assert(oldslot == NULL);
+ Assert(newslot == NULL);
ItemPointerSetInvalid(&(new_event.ate_ctid1));
ItemPointerSetInvalid(&(new_event.ate_ctid2));
cancel_prior_stmt_triggers(RelationGetRelid(rel),
@@ -5884,15 +5849,15 @@ AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo,
tgtype_event = TRIGGER_TYPE_DELETE;
if (row_trigger)
{
- Assert(oldtup != NULL);
- Assert(newtup == NULL);
- ItemPointerCopy(&(oldtup->t_self), &(new_event.ate_ctid1));
+ Assert(oldslot != NULL);
+ Assert(newslot == NULL);
+ ItemPointerCopy(&(oldslot->tts_tid), &(new_event.ate_ctid1));
ItemPointerSetInvalid(&(new_event.ate_ctid2));
}
else
{
- Assert(oldtup == NULL);
- Assert(newtup == NULL);
+ Assert(oldslot == NULL);
+ Assert(newslot == NULL);
ItemPointerSetInvalid(&(new_event.ate_ctid1));
ItemPointerSetInvalid(&(new_event.ate_ctid2));
cancel_prior_stmt_triggers(RelationGetRelid(rel),
@@ -5903,15 +5868,15 @@ AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo,
tgtype_event = TRIGGER_TYPE_UPDATE;
if (row_trigger)
{
- Assert(oldtup != NULL);
- Assert(newtup != NULL);
- ItemPointerCopy(&(oldtup->t_self), &(new_event.ate_ctid1));
- ItemPointerCopy(&(newtup->t_self), &(new_event.ate_ctid2));
+ Assert(oldslot != NULL);
+ Assert(newslot != NULL);
+ ItemPointerCopy(&(oldslot->tts_tid), &(new_event.ate_ctid1));
+ ItemPointerCopy(&(newslot->tts_tid), &(new_event.ate_ctid2));
}
else
{
- Assert(oldtup == NULL);
- Assert(newtup == NULL);
+ Assert(oldslot == NULL);
+ Assert(newslot == NULL);
ItemPointerSetInvalid(&(new_event.ate_ctid1));
ItemPointerSetInvalid(&(new_event.ate_ctid2));
cancel_prior_stmt_triggers(RelationGetRelid(rel),
@@ -5920,8 +5885,8 @@ AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo,
break;
case TRIGGER_EVENT_TRUNCATE:
tgtype_event = TRIGGER_TYPE_TRUNCATE;
- Assert(oldtup == NULL);
- Assert(newtup == NULL);
+ Assert(oldslot == NULL);
+ Assert(newslot == NULL);
ItemPointerSetInvalid(&(new_event.ate_ctid1));
ItemPointerSetInvalid(&(new_event.ate_ctid2));
break;
@@ -5948,7 +5913,7 @@ AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo,
tgtype_event))
continue;
if (!TriggerEnabled(estate, relinfo, trigger, event,
- modifiedCols, oldtup, newtup))
+ modifiedCols, oldslot, newslot))
continue;
if (relkind == RELKIND_FOREIGN_TABLE && row_trigger)
@@ -5975,7 +5940,7 @@ AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo,
case RI_TRIGGER_PK:
/* Update or delete on trigger's PK table */
if (!RI_FKey_pk_upd_check_required(trigger, rel,
- oldtup, newtup))
+ oldslot, newslot))
{
/* skip queuing this event */
continue;
@@ -5985,7 +5950,7 @@ AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo,
case RI_TRIGGER_FK:
/* Update on trigger's FK table */
if (!RI_FKey_fk_upd_check_required(trigger, rel,
- oldtup, newtup))
+ oldslot, newslot))
{
/* skip queuing this event */
continue;
@@ -6039,10 +6004,10 @@ AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo,
*/
if (fdw_tuplestore)
{
- if (oldtup != NULL)
- tuplestore_puttuple(fdw_tuplestore, oldtup);
- if (newtup != NULL)
- tuplestore_puttuple(fdw_tuplestore, newtup);
+ if (oldslot != NULL)
+ tuplestore_puttupleslot(fdw_tuplestore, oldslot);
+ if (newslot != NULL)
+ tuplestore_puttupleslot(fdw_tuplestore, newslot);
}
}
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 757df07..2e4d8eb 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -977,7 +977,7 @@ InitPlan(QueryDesc *queryDesc, int eflags)
* Initialize the executor's tuple table to empty.
*/
estate->es_tupleTable = NIL;
- estate->es_trig_tuple_slot = NULL;
+ estate->es_trig_return_slot = NULL;
estate->es_trig_oldtup_slot = NULL;
estate->es_trig_newtup_slot = NULL;
diff --git a/src/backend/executor/execReplication.c b/src/backend/executor/execReplication.c
index fc00b49..8557d5a 100644
--- a/src/backend/executor/execReplication.c
+++ b/src/backend/executor/execReplication.c
@@ -407,10 +407,8 @@ ExecSimpleRelationInsert(EState *estate, TupleTableSlot *slot)
if (resultRelInfo->ri_TrigDesc &&
resultRelInfo->ri_TrigDesc->trig_insert_before_row)
{
- slot = ExecBRInsertTriggers(estate, resultRelInfo, slot);
-
- if (slot == NULL) /* "do nothing" */
- skip_tuple = true;
+ if (!ExecBRInsertTriggers(estate, resultRelInfo, slot))
+ skip_tuple = true; /* "do nothing" */
}
if (!skip_tuple)
@@ -435,7 +433,7 @@ ExecSimpleRelationInsert(EState *estate, TupleTableSlot *slot)
NIL);
/* AFTER ROW INSERT Triggers */
- ExecARInsertTriggers(estate, resultRelInfo, tuple,
+ ExecARInsertTriggers(estate, resultRelInfo, slot,
recheckIndexes, NULL);
/*
@@ -478,11 +476,10 @@ ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate,
if (resultRelInfo->ri_TrigDesc &&
resultRelInfo->ri_TrigDesc->trig_update_before_row)
{
- slot = ExecBRUpdateTriggers(estate, epqstate, resultRelInfo,
- &hsearchslot->tuple->t_self, NULL, slot);
+ if (!ExecBRUpdateTriggers(estate, epqstate, resultRelInfo,
+ &hsearchslot->tuple->t_self, NULL, slot))
- if (slot == NULL) /* "do nothing" */
- skip_tuple = true;
+ skip_tuple = true; /* "do nothing" */
}
if (!skip_tuple)
@@ -510,7 +507,7 @@ ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate,
/* AFTER ROW UPDATE Triggers */
ExecARUpdateTriggers(estate, resultRelInfo,
- &hsearchslot->tuple->t_self, NULL, tuple,
+ &hsearchslot->tuple->t_self, NULL, slot,
recheckIndexes, NULL);
list_free(recheckIndexes);
diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c
index f39be12..7bf9bc4 100644
--- a/src/backend/executor/execUtils.c
+++ b/src/backend/executor/execUtils.c
@@ -130,7 +130,7 @@ CreateExecutorState(void)
estate->es_tuple_routing_result_relations = NIL;
estate->es_trig_target_relations = NIL;
- estate->es_trig_tuple_slot = NULL;
+ estate->es_trig_return_slot = NULL;
estate->es_trig_oldtup_slot = NULL;
estate->es_trig_newtup_slot = NULL;
@@ -420,6 +420,74 @@ MakePerTupleExprContext(EState *estate)
return estate->es_per_tuple_exprcontext;
}
+static TupleTableSlot *
+ExecTriggerGetSlot(EState *estate, Relation rel, TupleTableSlot *slot)
+{
+ TupleDesc reldesc = RelationGetDescr(rel);
+ MemoryContext oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
+
+ if (slot != NULL)
+ {
+ /*
+ * Foreign tables need a non-buffer tuple slot, whereas local tables
+ * need buffer tuple slots. For any other combinations, need to
+ * re-create a matching tuple slot type.
+ */
+ if (!(TTS_IS_BUFFERTUPLE(slot) &&
+ rel->rd_rel->relkind != RELKIND_FOREIGN_TABLE)
+ &&
+ !(!TTS_IS_BUFFERTUPLE(slot) &&
+ rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE))
+ {
+ /* tuple slot type does not match. */
+ ExecDropSingleTupleTableSlot(slot);
+ slot = NULL;
+ }
+ }
+
+ if (slot == NULL)
+ {
+ /* For foreign tables, there are no buffer tuples */
+ slot =
+ ExecInitExtraTupleSlot(estate, NULL,
+ rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE ?
+ &TTSOpsHeapTuple : &TTSOpsBufferHeapTuple);
+ }
+
+ if (slot->tts_tupleDescriptor != reldesc)
+ ExecSetSlotDescriptor(slot, reldesc);
+
+ MemoryContextSwitchTo(oldcontext);
+
+ return slot;
+}
+
+TupleTableSlot *
+ExecTriggerGetOldSlot(EState *estate, Relation rel)
+{
+ estate->es_trig_oldtup_slot =
+ ExecTriggerGetSlot(estate, rel, estate->es_trig_oldtup_slot);
+
+ return estate->es_trig_oldtup_slot;
+}
+
+TupleTableSlot *
+ExecTriggerGetNewSlot(EState *estate, Relation rel)
+{
+ estate->es_trig_newtup_slot =
+ ExecTriggerGetSlot(estate, rel, estate->es_trig_newtup_slot);
+
+ return estate->es_trig_newtup_slot;
+}
+
+TupleTableSlot *
+ExecTriggerGetReturnSlot(EState *estate, Relation rel)
+{
+ estate->es_trig_return_slot =
+ ExecTriggerGetSlot(estate, rel, estate->es_trig_return_slot);
+
+ return estate->es_trig_return_slot;
+}
/* ----------------------------------------------------------------
* miscellaneous node-init support functions
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index 9786d95..73b2f83 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -308,10 +308,8 @@ ExecInsert(ModifyTableState *mtstate,
if (resultRelInfo->ri_TrigDesc &&
resultRelInfo->ri_TrigDesc->trig_insert_before_row)
{
- slot = ExecBRInsertTriggers(estate, resultRelInfo, slot);
-
- if (slot == NULL) /* "do nothing" */
- return NULL;
+ if (!ExecBRInsertTriggers(estate, resultRelInfo, slot))
+ return NULL; /* "do nothing" */
/* trigger might have changed tuple */
tuple = ExecFetchSlotHeapTuple(slot, true, NULL);
@@ -321,10 +319,8 @@ ExecInsert(ModifyTableState *mtstate,
if (resultRelInfo->ri_TrigDesc &&
resultRelInfo->ri_TrigDesc->trig_insert_instead_row)
{
- slot = ExecIRInsertTriggers(estate, resultRelInfo, slot);
-
- if (slot == NULL) /* "do nothing" */
- return NULL;
+ if (!ExecIRInsertTriggers(estate, resultRelInfo, slot))
+ return NULL; /* "do nothing" */
/* trigger might have changed tuple */
tuple = ExecFetchSlotHeapTuple(slot, true, NULL);
@@ -552,7 +548,7 @@ ExecInsert(ModifyTableState *mtstate,
{
ExecARUpdateTriggers(estate, resultRelInfo, NULL,
NULL,
- tuple,
+ slot,
NULL,
mtstate->mt_transition_capture);
@@ -564,7 +560,7 @@ ExecInsert(ModifyTableState *mtstate,
}
/* AFTER ROW INSERT Triggers */
- ExecARInsertTriggers(estate, resultRelInfo, tuple, recheckIndexes,
+ ExecARInsertTriggers(estate, resultRelInfo, slot, recheckIndexes,
ar_insert_trig_tcs);
list_free(recheckIndexes);
@@ -678,10 +674,7 @@ ExecDelete(ModifyTableState *mtstate,
* although the FDW can return some other slot if it wants. Set up
* the slot's tupdesc so the FDW doesn't need to do that for itself.
*/
- slot = estate->es_trig_tuple_slot;
- if (slot->tts_tupleDescriptor != RelationGetDescr(resultRelationDesc))
- ExecSetSlotDescriptor(slot, RelationGetDescr(resultRelationDesc));
-
+ slot = ExecTriggerGetReturnSlot(estate, resultRelationDesc);
slot = resultRelInfo->ri_FdwRoutine->ExecForeignDelete(estate,
resultRelInfo,
slot,
@@ -857,7 +850,7 @@ ldelete:;
*/
TupleTableSlot *rslot;
HeapTupleData deltuple;
- Buffer delbuffer;
+ Buffer delbuffer = InvalidBuffer;
if (resultRelInfo->ri_FdwRoutine)
{
@@ -867,11 +860,10 @@ ldelete:;
}
else
{
- slot = estate->es_trig_tuple_slot;
+ slot = ExecTriggerGetReturnSlot(estate, resultRelationDesc);
if (oldtuple != NULL)
{
- deltuple = *oldtuple;
- delbuffer = InvalidBuffer;
+ ExecForceStoreHeapTuple(oldtuple, slot);
}
else
{
@@ -879,11 +871,8 @@ ldelete:;
if (!heap_fetch(resultRelationDesc, SnapshotAny,
&deltuple, &delbuffer, false, NULL))
elog(ERROR, "failed to fetch deleted tuple for DELETE RETURNING");
+ ExecStoreBufferHeapTuple(&deltuple, slot, delbuffer);
}
-
- if (slot->tts_tupleDescriptor != RelationGetDescr(resultRelationDesc))
- ExecSetSlotDescriptor(slot, RelationGetDescr(resultRelationDesc));
- ExecStoreHeapTuple(&deltuple, slot, false);
}
rslot = ExecProcessReturning(resultRelInfo, slot, planSlot);
@@ -966,11 +955,9 @@ ExecUpdate(ModifyTableState *mtstate,
if (resultRelInfo->ri_TrigDesc &&
resultRelInfo->ri_TrigDesc->trig_update_before_row)
{
- slot = ExecBRUpdateTriggers(estate, epqstate, resultRelInfo,
- tupleid, oldtuple, slot);
-
- if (slot == NULL) /* "do nothing" */
- return NULL;
+ if (!ExecBRUpdateTriggers(estate, epqstate, resultRelInfo,
+ tupleid, oldtuple, slot))
+ return NULL; /* "do nothing" */
/* trigger might have changed tuple */
tuple = ExecFetchSlotHeapTuple(slot, true, NULL);
@@ -980,11 +967,9 @@ ExecUpdate(ModifyTableState *mtstate,
if (resultRelInfo->ri_TrigDesc &&
resultRelInfo->ri_TrigDesc->trig_update_instead_row)
{
- slot = ExecIRUpdateTriggers(estate, resultRelInfo,
- oldtuple, slot);
-
- if (slot == NULL) /* "do nothing" */
- return NULL;
+ if (!ExecIRUpdateTriggers(estate, resultRelInfo,
+ oldtuple, slot))
+ return NULL; /* "do nothing" */
/* trigger might have changed tuple */
tuple = ExecFetchSlotHeapTuple(slot, true, NULL);
@@ -1309,7 +1294,7 @@ lreplace:;
(estate->es_processed)++;
/* AFTER ROW UPDATE Triggers */
- ExecARUpdateTriggers(estate, resultRelInfo, tupleid, oldtuple, tuple,
+ ExecARUpdateTriggers(estate, resultRelInfo, tupleid, oldtuple, slot,
recheckIndexes,
mtstate->operation == CMD_INSERT ?
mtstate->mt_oc_transition_capture :
@@ -2566,15 +2551,6 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
}
}
- /*
- * Set up a tuple table slot for use for trigger output tuples. In a plan
- * containing multiple ModifyTable nodes, all can share one such slot, so
- * we keep it in the estate. The tuple being inserted doesn't come from a
- * buffer.
- */
- if (estate->es_trig_tuple_slot == NULL)
- estate->es_trig_tuple_slot = ExecInitExtraTupleSlot(estate, NULL,
- &TTSOpsHeapTuple);
/*
* Lastly, if this is not the primary (canSetTag) ModifyTable node, add it
diff --git a/src/backend/replication/logical/worker.c b/src/backend/replication/logical/worker.c
index 3cd1e0d..3906eed 100644
--- a/src/backend/replication/logical/worker.c
+++ b/src/backend/replication/logical/worker.c
@@ -211,11 +211,6 @@ create_estate_for_relation(LogicalRepRelMapEntry *rel)
estate->es_output_cid = GetCurrentCommandId(true);
- /* Triggers might need a slot */
- if (resultRelInfo->ri_TrigDesc)
- estate->es_trig_tuple_slot = ExecInitExtraTupleSlot(estate, NULL,
- &TTSOpsVirtual);
-
/* Prepare to catch AFTER triggers. */
AfterTriggerBeginQuery();
diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c
index cdda860..a94751b 100644
--- a/src/backend/utils/adt/ri_triggers.c
+++ b/src/backend/utils/adt/ri_triggers.c
@@ -191,7 +191,7 @@ static int ri_constraint_cache_valid_count = 0;
* ----------
*/
static bool ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel,
- HeapTuple old_row,
+ TupleTableSlot *oldslot,
const RI_ConstraintInfo *riinfo);
static Datum ri_restrict(TriggerData *trigdata, bool is_no_action);
static Datum ri_setnull(TriggerData *trigdata);
@@ -204,12 +204,12 @@ static void ri_GenerateQual(StringInfo buf,
Oid opoid,
const char *rightop, Oid rightoptype);
static void ri_GenerateQualCollation(StringInfo buf, Oid collation);
-static int ri_NullCheck(TupleDesc tupdesc, HeapTuple tup,
+static int ri_NullCheck(TupleDesc tupdesc, TupleTableSlot *slot,
const RI_ConstraintInfo *riinfo, bool rel_is_pk);
static void ri_BuildQueryKey(RI_QueryKey *key,
const RI_ConstraintInfo *riinfo,
int32 constr_queryno);
-static bool ri_KeysEqual(Relation rel, HeapTuple oldtup, HeapTuple newtup,
+static bool ri_KeysEqual(Relation rel, TupleTableSlot *oldslot, TupleTableSlot *newslot,
const RI_ConstraintInfo *riinfo, bool rel_is_pk);
static bool ri_AttributesEqual(Oid eq_opr, Oid typeid,
Datum oldvalue, Datum newvalue);
@@ -231,14 +231,14 @@ static SPIPlanPtr ri_PlanCheck(const char *querystr, int nargs, Oid *argtypes,
static bool ri_PerformCheck(const RI_ConstraintInfo *riinfo,
RI_QueryKey *qkey, SPIPlanPtr qplan,
Relation fk_rel, Relation pk_rel,
- HeapTuple old_tuple, HeapTuple new_tuple,
+ TupleTableSlot *oldslot, TupleTableSlot *newslot,
bool detectNewRows, int expect_OK);
-static void ri_ExtractValues(Relation rel, HeapTuple tup,
+static void ri_ExtractValues(Relation rel, TupleTableSlot *slot,
const RI_ConstraintInfo *riinfo, bool rel_is_pk,
Datum *vals, char *nulls);
static void ri_ReportViolation(const RI_ConstraintInfo *riinfo,
Relation pk_rel, Relation fk_rel,
- HeapTuple violator, TupleDesc tupdesc,
+ TupleTableSlot *violator, TupleDesc tupdesc,
int queryno) pg_attribute_noreturn();
@@ -254,8 +254,8 @@ RI_FKey_check(TriggerData *trigdata)
const RI_ConstraintInfo *riinfo;
Relation fk_rel;
Relation pk_rel;
- HeapTuple new_row;
- Buffer new_row_buf;
+ TupleTableSlot *newslot;
+ BufferHeapTupleTableSlot *bslot;
RI_QueryKey qkey;
SPIPlanPtr qplan;
int i;
@@ -267,15 +267,9 @@ RI_FKey_check(TriggerData *trigdata)
trigdata->tg_relation, false);
if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
- {
- new_row = trigdata->tg_newtuple;
- new_row_buf = trigdata->tg_newtuplebuf;
- }
+ newslot = trigdata->tg_newslot;
else
- {
- new_row = trigdata->tg_trigtuple;
- new_row_buf = trigdata->tg_trigtuplebuf;
- }
+ newslot = trigdata->tg_trigslot;
/*
* We should not even consider checking the row if it is no longer valid,
@@ -285,13 +279,14 @@ RI_FKey_check(TriggerData *trigdata)
* and lock on the buffer to call HeapTupleSatisfiesVisibility. Caller
* should be holding pin, but not lock.
*/
- LockBuffer(new_row_buf, BUFFER_LOCK_SHARE);
- if (!HeapTupleSatisfiesVisibility(new_row, SnapshotSelf, new_row_buf))
+ bslot = (BufferHeapTupleTableSlot *) newslot;
+ LockBuffer(bslot->buffer, BUFFER_LOCK_SHARE);
+ if (!HeapTupleSatisfiesVisibility(bslot->base.tuple, SnapshotSelf, bslot->buffer))
{
- LockBuffer(new_row_buf, BUFFER_LOCK_UNLOCK);
+ LockBuffer(bslot->buffer, BUFFER_LOCK_UNLOCK);
return PointerGetDatum(NULL);
}
- LockBuffer(new_row_buf, BUFFER_LOCK_UNLOCK);
+ LockBuffer(bslot->buffer, BUFFER_LOCK_UNLOCK);
/*
* Get the relation descriptors of the FK and PK tables.
@@ -307,7 +302,7 @@ RI_FKey_check(TriggerData *trigdata)
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("MATCH PARTIAL not yet implemented")));
- switch (ri_NullCheck(RelationGetDescr(fk_rel), new_row, riinfo, false))
+ switch (ri_NullCheck(RelationGetDescr(fk_rel), newslot, riinfo, false))
{
case RI_KEYS_ALL_NULL:
@@ -437,7 +432,7 @@ RI_FKey_check(TriggerData *trigdata)
*/
ri_PerformCheck(riinfo, &qkey, qplan,
fk_rel, pk_rel,
- NULL, new_row,
+ NULL, newslot,
false,
SPI_OK_SELECT);
@@ -505,7 +500,7 @@ RI_FKey_check_upd(PG_FUNCTION_ARGS)
*/
static bool
ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel,
- HeapTuple old_row,
+ TupleTableSlot *oldslot,
const RI_ConstraintInfo *riinfo)
{
SPIPlanPtr qplan;
@@ -514,7 +509,7 @@ ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel,
bool result;
/* Only called for non-null rows */
- Assert(ri_NullCheck(RelationGetDescr(pk_rel), old_row, riinfo, true) == RI_KEYS_NONE_NULL);
+ Assert(ri_NullCheck(RelationGetDescr(pk_rel), oldslot, riinfo, true) == RI_KEYS_NONE_NULL);
if (SPI_connect() != SPI_OK_CONNECT)
elog(ERROR, "SPI_connect failed");
@@ -572,7 +567,7 @@ ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel,
*/
result = ri_PerformCheck(riinfo, &qkey, qplan,
fk_rel, pk_rel,
- old_row, NULL,
+ oldslot, NULL,
true, /* treat like update */
SPI_OK_SELECT);
@@ -690,7 +685,7 @@ ri_restrict(TriggerData *trigdata, bool is_no_action)
const RI_ConstraintInfo *riinfo;
Relation fk_rel;
Relation pk_rel;
- HeapTuple old_row;
+ TupleTableSlot *old_slot;
RI_QueryKey qkey;
SPIPlanPtr qplan;
@@ -708,7 +703,7 @@ ri_restrict(TriggerData *trigdata, bool is_no_action)
*/
fk_rel = heap_open(riinfo->fk_relid, RowShareLock);
pk_rel = trigdata->tg_relation;
- old_row = trigdata->tg_trigtuple;
+ old_slot = trigdata->tg_trigslot;
switch (riinfo->confmatchtype)
{
@@ -732,7 +727,7 @@ ri_restrict(TriggerData *trigdata, bool is_no_action)
* allow another row to be substituted.
*/
if (is_no_action &&
- ri_Check_Pk_Match(pk_rel, fk_rel, old_row, riinfo))
+ ri_Check_Pk_Match(pk_rel, fk_rel, old_slot, riinfo))
{
heap_close(fk_rel, RowShareLock);
return PointerGetDatum(NULL);
@@ -800,7 +795,7 @@ ri_restrict(TriggerData *trigdata, bool is_no_action)
*/
ri_PerformCheck(riinfo, &qkey, qplan,
fk_rel, pk_rel,
- old_row, NULL,
+ old_slot, NULL,
true, /* must detect new rows */
SPI_OK_SELECT);
@@ -844,7 +839,7 @@ RI_FKey_cascade_del(PG_FUNCTION_ARGS)
const RI_ConstraintInfo *riinfo;
Relation fk_rel;
Relation pk_rel;
- HeapTuple old_row;
+ TupleTableSlot *old_slot;
RI_QueryKey qkey;
SPIPlanPtr qplan;
int i;
@@ -868,7 +863,7 @@ RI_FKey_cascade_del(PG_FUNCTION_ARGS)
*/
fk_rel = heap_open(riinfo->fk_relid, RowExclusiveLock);
pk_rel = trigdata->tg_relation;
- old_row = trigdata->tg_trigtuple;
+ old_slot = trigdata->tg_trigslot;
switch (riinfo->confmatchtype)
{
@@ -940,7 +935,7 @@ RI_FKey_cascade_del(PG_FUNCTION_ARGS)
*/
ri_PerformCheck(riinfo, &qkey, qplan,
fk_rel, pk_rel,
- old_row, NULL,
+ old_slot, NULL,
true, /* must detect new rows */
SPI_OK_DELETE);
@@ -984,8 +979,8 @@ RI_FKey_cascade_upd(PG_FUNCTION_ARGS)
const RI_ConstraintInfo *riinfo;
Relation fk_rel;
Relation pk_rel;
- HeapTuple new_row;
- HeapTuple old_row;
+ TupleTableSlot *new_slot;
+ TupleTableSlot *old_slot;
RI_QueryKey qkey;
SPIPlanPtr qplan;
int i;
@@ -1011,8 +1006,8 @@ RI_FKey_cascade_upd(PG_FUNCTION_ARGS)
*/
fk_rel = heap_open(riinfo->fk_relid, RowExclusiveLock);
pk_rel = trigdata->tg_relation;
- new_row = trigdata->tg_newtuple;
- old_row = trigdata->tg_trigtuple;
+ new_slot = trigdata->tg_newslot;
+ old_slot = trigdata->tg_trigslot;
switch (riinfo->confmatchtype)
{
@@ -1096,7 +1091,7 @@ RI_FKey_cascade_upd(PG_FUNCTION_ARGS)
*/
ri_PerformCheck(riinfo, &qkey, qplan,
fk_rel, pk_rel,
- old_row, new_row,
+ old_slot, new_slot,
true, /* must detect new rows */
SPI_OK_UPDATE);
@@ -1179,7 +1174,7 @@ ri_setnull(TriggerData *trigdata)
const RI_ConstraintInfo *riinfo;
Relation fk_rel;
Relation pk_rel;
- HeapTuple old_row;
+ TupleTableSlot *old_slot;
RI_QueryKey qkey;
SPIPlanPtr qplan;
int i;
@@ -1198,7 +1193,7 @@ ri_setnull(TriggerData *trigdata)
*/
fk_rel = heap_open(riinfo->fk_relid, RowExclusiveLock);
pk_rel = trigdata->tg_relation;
- old_row = trigdata->tg_trigtuple;
+ old_slot = trigdata->tg_trigslot;
switch (riinfo->confmatchtype)
{
@@ -1283,7 +1278,7 @@ ri_setnull(TriggerData *trigdata)
*/
ri_PerformCheck(riinfo, &qkey, qplan,
fk_rel, pk_rel,
- old_row, NULL,
+ old_slot, NULL,
true, /* must detect new rows */
SPI_OK_UPDATE);
@@ -1366,7 +1361,7 @@ ri_setdefault(TriggerData *trigdata)
const RI_ConstraintInfo *riinfo;
Relation fk_rel;
Relation pk_rel;
- HeapTuple old_row;
+ TupleTableSlot *old_slot;
RI_QueryKey qkey;
SPIPlanPtr qplan;
@@ -1384,7 +1379,7 @@ ri_setdefault(TriggerData *trigdata)
*/
fk_rel = heap_open(riinfo->fk_relid, RowExclusiveLock);
pk_rel = trigdata->tg_relation;
- old_row = trigdata->tg_trigtuple;
+ old_slot = trigdata->tg_trigslot;
switch (riinfo->confmatchtype)
{
@@ -1470,7 +1465,7 @@ ri_setdefault(TriggerData *trigdata)
*/
ri_PerformCheck(riinfo, &qkey, qplan,
fk_rel, pk_rel,
- old_row, NULL,
+ old_slot, NULL,
true, /* must detect new rows */
SPI_OK_UPDATE);
@@ -1529,7 +1524,7 @@ ri_setdefault(TriggerData *trigdata)
*/
bool
RI_FKey_pk_upd_check_required(Trigger *trigger, Relation pk_rel,
- HeapTuple old_row, HeapTuple new_row)
+ TupleTableSlot *old_slot, TupleTableSlot *new_slot)
{
const RI_ConstraintInfo *riinfo;
@@ -1547,11 +1542,12 @@ RI_FKey_pk_upd_check_required(Trigger *trigger, Relation pk_rel,
* If any old key value is NULL, the row could not have been
* referenced by an FK row, so no check is needed.
*/
- if (ri_NullCheck(RelationGetDescr(pk_rel), old_row, riinfo, true) != RI_KEYS_NONE_NULL)
+ if (ri_NullCheck(RelationGetDescr(pk_rel), old_slot, riinfo, true) != RI_KEYS_NONE_NULL)
return false;
/* If all old and new key values are equal, no check is needed */
- if (new_row && ri_KeysEqual(pk_rel, old_row, new_row, riinfo, true))
+ if (!TupIsNull(new_slot) &&
+ ri_KeysEqual(pk_rel, old_slot, new_slot, riinfo, true))
return false;
/* Else we need to fire the trigger. */
@@ -1586,9 +1582,10 @@ RI_FKey_pk_upd_check_required(Trigger *trigger, Relation pk_rel,
*/
bool
RI_FKey_fk_upd_check_required(Trigger *trigger, Relation fk_rel,
- HeapTuple old_row, HeapTuple new_row)
+ TupleTableSlot *old_slot, TupleTableSlot *new_slot)
{
const RI_ConstraintInfo *riinfo;
+ HeapTuple old_row = ((HeapTupleTableSlot *) old_slot)->tuple;
/*
* Get arguments.
@@ -1603,7 +1600,7 @@ RI_FKey_fk_upd_check_required(Trigger *trigger, Relation fk_rel,
* If any new key value is NULL, the row must satisfy the
* constraint, so no check is needed.
*/
- if (ri_NullCheck(RelationGetDescr(fk_rel), new_row, riinfo, false) != RI_KEYS_NONE_NULL)
+ if (ri_NullCheck(RelationGetDescr(fk_rel), new_slot, riinfo, false) != RI_KEYS_NONE_NULL)
return false;
/*
@@ -1618,7 +1615,7 @@ RI_FKey_fk_upd_check_required(Trigger *trigger, Relation fk_rel,
return true;
/* If all old and new key values are equal, no check is needed */
- if (ri_KeysEqual(fk_rel, old_row, new_row, riinfo, false))
+ if (ri_KeysEqual(fk_rel, old_slot, new_slot, riinfo, false))
return false;
/* Else we need to fire the trigger. */
@@ -1634,7 +1631,7 @@ RI_FKey_fk_upd_check_required(Trigger *trigger, Relation fk_rel,
* invalidated before the constraint is to be checked, but we
* should queue the event to apply the check later.
*/
- switch (ri_NullCheck(RelationGetDescr(fk_rel), new_row, riinfo, false))
+ switch (ri_NullCheck(RelationGetDescr(fk_rel), new_slot, riinfo, false))
{
case RI_KEYS_ALL_NULL:
return false;
@@ -1656,7 +1653,7 @@ RI_FKey_fk_upd_check_required(Trigger *trigger, Relation fk_rel,
return true;
/* If all old and new key values are equal, no check is needed */
- if (ri_KeysEqual(fk_rel, old_row, new_row, riinfo, false))
+ if (ri_KeysEqual(fk_rel, old_slot, new_slot, riinfo, false))
return false;
/* Else we need to fire the trigger. */
@@ -1910,10 +1907,17 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
/* Did we find a tuple violating the constraint? */
if (SPI_processed > 0)
{
+ TupleTableSlot *slot;
HeapTuple tuple = SPI_tuptable->vals[0];
TupleDesc tupdesc = SPI_tuptable->tupdesc;
RI_ConstraintInfo fake_riinfo;
+ slot = MakeSingleTupleTableSlot(tupdesc, &TTSOpsVirtual);
+
+ heap_deform_tuple(tuple, tupdesc,
+ slot->tts_values, slot->tts_isnull);
+ ExecStoreVirtualTuple(slot);
+
/*
* The columns to look at in the result tuple are 1..N, not whatever
* they are in the fk_rel. Hack up riinfo so that the subroutines
@@ -1933,7 +1937,7 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
* disallows partially-null FK rows.
*/
if (fake_riinfo.confmatchtype == FKCONSTR_MATCH_FULL &&
- ri_NullCheck(tupdesc, tuple, &fake_riinfo, false) != RI_KEYS_NONE_NULL)
+ ri_NullCheck(tupdesc, slot, &fake_riinfo, false) != RI_KEYS_NONE_NULL)
ereport(ERROR,
(errcode(ERRCODE_FOREIGN_KEY_VIOLATION),
errmsg("insert or update on table \"%s\" violates foreign key constraint \"%s\"",
@@ -1950,8 +1954,10 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
*/
ri_ReportViolation(&fake_riinfo,
pk_rel, fk_rel,
- tuple, tupdesc,
+ slot, tupdesc,
RI_PLAN_CHECK_LOOKUPPK);
+
+ ExecDropSingleTupleTableSlot(slot);
}
if (SPI_finish() != SPI_OK_FINISH)
@@ -2429,7 +2435,7 @@ static bool
ri_PerformCheck(const RI_ConstraintInfo *riinfo,
RI_QueryKey *qkey, SPIPlanPtr qplan,
Relation fk_rel, Relation pk_rel,
- HeapTuple old_tuple, HeapTuple new_tuple,
+ TupleTableSlot *old_slot, TupleTableSlot *new_slot,
bool detectNewRows, int expect_OK)
{
Relation query_rel,
@@ -2472,17 +2478,17 @@ ri_PerformCheck(const RI_ConstraintInfo *riinfo,
}
/* Extract the parameters to be passed into the query */
- if (new_tuple)
+ if (new_slot)
{
- ri_ExtractValues(source_rel, new_tuple, riinfo, source_is_pk,
+ ri_ExtractValues(source_rel, new_slot, riinfo, source_is_pk,
vals, nulls);
- if (old_tuple)
- ri_ExtractValues(source_rel, old_tuple, riinfo, source_is_pk,
+ if (old_slot)
+ ri_ExtractValues(source_rel, old_slot, riinfo, source_is_pk,
vals + riinfo->nkeys, nulls + riinfo->nkeys);
}
else
{
- ri_ExtractValues(source_rel, old_tuple, riinfo, source_is_pk,
+ ri_ExtractValues(source_rel, old_slot, riinfo, source_is_pk,
vals, nulls);
}
@@ -2552,7 +2558,7 @@ ri_PerformCheck(const RI_ConstraintInfo *riinfo,
(SPI_processed == 0) == (qkey->constr_queryno == RI_PLAN_CHECK_LOOKUPPK))
ri_ReportViolation(riinfo,
pk_rel, fk_rel,
- new_tuple ? new_tuple : old_tuple,
+ new_slot ? new_slot : old_slot,
NULL,
qkey->constr_queryno);
@@ -2563,11 +2569,10 @@ ri_PerformCheck(const RI_ConstraintInfo *riinfo,
* Extract fields from a tuple into Datum/nulls arrays
*/
static void
-ri_ExtractValues(Relation rel, HeapTuple tup,
+ri_ExtractValues(Relation rel, TupleTableSlot *slot,
const RI_ConstraintInfo *riinfo, bool rel_is_pk,
Datum *vals, char *nulls)
{
- TupleDesc tupdesc = rel->rd_att;
const int16 *attnums;
int i;
bool isnull;
@@ -2579,8 +2584,7 @@ ri_ExtractValues(Relation rel, HeapTuple tup,
for (i = 0; i < riinfo->nkeys; i++)
{
- vals[i] = heap_getattr(tup, attnums[i], tupdesc,
- &isnull);
+ vals[i] = slot_getattr(slot, attnums[i], &isnull);
nulls[i] = isnull ? 'n' : ' ';
}
}
@@ -2597,7 +2601,7 @@ ri_ExtractValues(Relation rel, HeapTuple tup,
static void
ri_ReportViolation(const RI_ConstraintInfo *riinfo,
Relation pk_rel, Relation fk_rel,
- HeapTuple violator, TupleDesc tupdesc,
+ TupleTableSlot *violatorslot, TupleDesc tupdesc,
int queryno)
{
StringInfoData key_names;
@@ -2676,7 +2680,8 @@ ri_ReportViolation(const RI_ConstraintInfo *riinfo,
*val;
name = SPI_fname(tupdesc, fnum);
- val = SPI_getvalue(violator, tupdesc, fnum);
+ val = SPI_getvalue(ExecFetchSlotHeapTuple(violatorslot, false, NULL),
+ tupdesc, fnum);
if (!val)
val = "null";
@@ -2730,7 +2735,7 @@ ri_ReportViolation(const RI_ConstraintInfo *riinfo,
*/
static int
ri_NullCheck(TupleDesc tupDesc,
- HeapTuple tup,
+ TupleTableSlot *slot,
const RI_ConstraintInfo *riinfo, bool rel_is_pk)
{
const int16 *attnums;
@@ -2745,7 +2750,7 @@ ri_NullCheck(TupleDesc tupDesc,
for (i = 0; i < riinfo->nkeys; i++)
{
- if (heap_attisnull(tup, attnums[i], tupDesc))
+ if (slot_attisnull(slot, attnums[i]))
nonenull = false;
else
allnull = false;
@@ -2896,10 +2901,9 @@ ri_HashPreparedPlan(RI_QueryKey *key, SPIPlanPtr plan)
* ----------
*/
static bool
-ri_KeysEqual(Relation rel, HeapTuple oldtup, HeapTuple newtup,
+ri_KeysEqual(Relation rel, TupleTableSlot *oldslot, TupleTableSlot *newslot,
const RI_ConstraintInfo *riinfo, bool rel_is_pk)
{
- TupleDesc tupdesc = RelationGetDescr(rel);
const int16 *attnums;
const Oid *eq_oprs;
int i;
@@ -2915,6 +2919,7 @@ ri_KeysEqual(Relation rel, HeapTuple oldtup, HeapTuple newtup,
eq_oprs = riinfo->ff_eq_oprs;
}
+ /* XXX: could be worthwhile to fetch all necessary attrs at once */
for (i = 0; i < riinfo->nkeys; i++)
{
Datum oldvalue;
@@ -2924,14 +2929,14 @@ ri_KeysEqual(Relation rel, HeapTuple oldtup, HeapTuple newtup,
/*
* Get one attribute's oldvalue. If it is NULL - they're not equal.
*/
- oldvalue = heap_getattr(oldtup, attnums[i], tupdesc, &isnull);
+ oldvalue = slot_getattr(oldslot, attnums[i], &isnull);
if (isnull)
return false;
/*
* Get one attribute's newvalue. If it is NULL - they're not equal.
*/
- newvalue = heap_getattr(newtup, attnums[i], tupdesc, &isnull);
+ newvalue = slot_getattr(newslot, attnums[i], &isnull);
if (isnull)
return false;
diff --git a/src/include/commands/trigger.h b/src/include/commands/trigger.h
index 1031448..0f02bae 100644
--- a/src/include/commands/trigger.h
+++ b/src/include/commands/trigger.h
@@ -35,8 +35,8 @@ typedef struct TriggerData
HeapTuple tg_trigtuple;
HeapTuple tg_newtuple;
Trigger *tg_trigger;
- Buffer tg_trigtuplebuf;
- Buffer tg_newtuplebuf;
+ TupleTableSlot *tg_trigslot;
+ TupleTableSlot *tg_newslot;
Tuplestorestate *tg_oldtable;
Tuplestorestate *tg_newtable;
} TriggerData;
@@ -186,15 +186,15 @@ extern void ExecBSInsertTriggers(EState *estate,
extern void ExecASInsertTriggers(EState *estate,
ResultRelInfo *relinfo,
TransitionCaptureState *transition_capture);
-extern TupleTableSlot *ExecBRInsertTriggers(EState *estate,
+extern bool ExecBRInsertTriggers(EState *estate,
ResultRelInfo *relinfo,
TupleTableSlot *slot);
extern void ExecARInsertTriggers(EState *estate,
ResultRelInfo *relinfo,
- HeapTuple trigtuple,
+ TupleTableSlot *slot,
List *recheckIndexes,
TransitionCaptureState *transition_capture);
-extern TupleTableSlot *ExecIRInsertTriggers(EState *estate,
+extern bool ExecIRInsertTriggers(EState *estate,
ResultRelInfo *relinfo,
TupleTableSlot *slot);
extern void ExecBSDeleteTriggers(EState *estate,
@@ -221,7 +221,7 @@ extern void ExecBSUpdateTriggers(EState *estate,
extern void ExecASUpdateTriggers(EState *estate,
ResultRelInfo *relinfo,
TransitionCaptureState *transition_capture);
-extern TupleTableSlot *ExecBRUpdateTriggers(EState *estate,
+extern bool ExecBRUpdateTriggers(EState *estate,
EPQState *epqstate,
ResultRelInfo *relinfo,
ItemPointer tupleid,
@@ -231,10 +231,10 @@ extern void ExecARUpdateTriggers(EState *estate,
ResultRelInfo *relinfo,
ItemPointer tupleid,
HeapTuple fdw_trigtuple,
- HeapTuple newtuple,
+ TupleTableSlot *slot,
List *recheckIndexes,
TransitionCaptureState *transition_capture);
-extern TupleTableSlot *ExecIRUpdateTriggers(EState *estate,
+extern bool ExecIRUpdateTriggers(EState *estate,
ResultRelInfo *relinfo,
HeapTuple trigtuple,
TupleTableSlot *slot);
@@ -258,9 +258,9 @@ extern bool AfterTriggerPendingOnRel(Oid relid);
* in utils/adt/ri_triggers.c
*/
extern bool RI_FKey_pk_upd_check_required(Trigger *trigger, Relation pk_rel,
- HeapTuple old_row, HeapTuple new_row);
+ TupleTableSlot *old_slot, TupleTableSlot *new_slot);
extern bool RI_FKey_fk_upd_check_required(Trigger *trigger, Relation fk_rel,
- HeapTuple old_row, HeapTuple new_row);
+ TupleTableSlot *old_slot, TupleTableSlot *new_slot);
extern bool RI_Initial_Check(Trigger *trigger,
Relation fk_rel, Relation pk_rel);
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index 14c4e3a..369d1e6 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -493,6 +493,10 @@ extern void ReScanExprContext(ExprContext *econtext);
extern ExprContext *MakePerTupleExprContext(EState *estate);
+extern TupleTableSlot *ExecTriggerGetOldSlot(EState *estate, Relation rel);
+extern TupleTableSlot *ExecTriggerGetNewSlot(EState *estate, Relation rel);
+extern TupleTableSlot *ExecTriggerGetReturnSlot(EState *estate, Relation rel);
+
/* Get an EState's per-output-tuple exprcontext, making it if first use */
#define GetPerTupleExprContext(estate) \
((estate)->es_per_tuple_exprcontext ? \
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 569cc7c..c7e6e88 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -525,7 +525,7 @@ typedef struct EState
/* Stuff used for firing triggers: */
List *es_trig_target_relations; /* trigger-only ResultRelInfos */
- TupleTableSlot *es_trig_tuple_slot; /* for trigger output tuples */
+ TupleTableSlot *es_trig_return_slot; /* for trigger output tuples */
TupleTableSlot *es_trig_oldtup_slot; /* for TriggerEnabled */
TupleTableSlot *es_trig_newtup_slot; /* for TriggerEnabled */
--
2.1.4