Use slots in trigger infrastructure, except for the actual invocation.

In preparation for abstracting table storage, convert trigger.c to
track tuples in slots. Which also happens to make code calling
triggers simpler.

As the calling interface for triggers themselves is not changed in
this patch, HeapTuples still are extracted from the slot at that
time. But that's handled solely inside trigger.c, not visible to
callers. It's quite likely that we'll want to revise the external
trigger interface, but that's a separate large project.

As part of this work the slots used for old/new/return tuples are
moved from EState into ResultRelInfo, as different updated tables
might need different slots. The slots are now also now created
on-demand, which is good both from an efficiency POV, but also makes
the modifying code simpler.

Author: Andres Freund, Amit Khandekar and Ashutosh Bapat
Discussion: https://postgr.es/m/20180703070645.wchpu5muyto5n647@alap3.anarazel.de
This commit is contained in:
Andres Freund 2019-02-26 20:30:28 -08:00
parent b8d71745ea
commit ff11e7f4b9
14 changed files with 649 additions and 578 deletions

View File

@ -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.

View File

@ -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);
}
}

View File

@ -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;

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -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)

View File

@ -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;

View File

@ -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;
}

View File

@ -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;
if (!heap_fetch(resultRelationDesc, SnapshotAny,
&deltuple, &delbuffer, false, NULL))
elog(ERROR, "failed to fetch deleted tuple for DELETE RETURNING");
}
BufferHeapTupleTableSlot *bslot;
HeapTuple deltuple;
Buffer buffer;
if (slot->tts_tupleDescriptor != RelationGetDescr(resultRelationDesc))
ExecSetSlotDescriptor(slot, RelationGetDescr(resultRelationDesc));
ExecStoreHeapTuple(&deltuple, slot, false);
Assert(TTS_IS_BUFFERTUPLE(slot));
ExecClearTuple(slot);
bslot = (BufferHeapTupleTableSlot *) slot;
deltuple = &bslot->base.tupdata;
deltuple->t_self = *tupleid;
if (!heap_fetch(resultRelationDesc, SnapshotAny,
deltuple, &buffer, false, NULL))
elog(ERROR, "failed to fetch deleted tuple for DELETE RETURNING");
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;
}
}
@ -2541,16 +2505,6 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
}
}
/*
* Set up a tuple table slot for use for trigger output tuples. In a plan
* containing multiple ModifyTable nodes, all can share one such slot, so
* we keep it in the estate. The tuple being inserted doesn't come from a
* buffer.
*/
if (estate->es_trig_tuple_slot == NULL)
estate->es_trig_tuple_slot = ExecInitExtraTupleSlot(estate, NULL,
&TTSOpsHeapTuple);
/*
* Lastly, if this is not the primary (canSetTag) ModifyTable node, add it
* to estate->es_auxmodifytables so that it will be run to completion by

View File

@ -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();

View File

@ -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;

View File

@ -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);

View File

@ -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
*/

View File

@ -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 */