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, fmstate->retrieved_attrs,
NULL, NULL,
fmstate->temp_cxt); fmstate->temp_cxt);
/* tuple will be deleted when it is cleared from the slot */ /*
ExecStoreHeapTuple(newtup, slot, true); * The returning slot will not necessarily be suitable to store
* heaptuples directly, so allow for conversion.
*/
ExecForceStoreHeapTuple(newtup, slot);
ExecMaterializeSlot(slot);
pfree(newtup);
} }
PG_CATCH(); PG_CATCH();
{ {
@ -3886,6 +3891,7 @@ apply_returning_filter(PgFdwDirectModifyState *dmstate,
TupleTableSlot *slot, TupleTableSlot *slot,
EState *estate) EState *estate)
{ {
ResultRelInfo *relInfo = estate->es_result_relation_info;
TupleDesc resultTupType = RelationGetDescr(dmstate->resultRel); TupleDesc resultTupType = RelationGetDescr(dmstate->resultRel);
TupleTableSlot *resultSlot; TupleTableSlot *resultSlot;
Datum *values; Datum *values;
@ -3895,11 +3901,9 @@ apply_returning_filter(PgFdwDirectModifyState *dmstate,
int i; int i;
/* /*
* Use the trigger tuple slot as a place to store the result tuple. * Use the return tuple slot as a place to store the result tuple.
*/ */
resultSlot = estate->es_trig_tuple_slot; resultSlot = ExecGetReturningSlot(estate, relInfo);
if (resultSlot->tts_tupleDescriptor != resultTupType)
ExecSetSlotDescriptor(resultSlot, resultTupType);
/* /*
* Extract all the values of the scan tuple. * Extract all the values of the scan tuple.

View File

@ -2519,9 +2519,6 @@ CopyFrom(CopyState cstate)
/* Set up a tuple slot too */ /* Set up a tuple slot too */
myslot = ExecInitExtraTupleSlot(estate, tupDesc, myslot = ExecInitExtraTupleSlot(estate, tupDesc,
&TTSOpsHeapTuple); &TTSOpsHeapTuple);
/* Triggers might need a slot as well */
estate->es_trig_tuple_slot = ExecInitExtraTupleSlot(estate, NULL,
&TTSOpsHeapTuple);
/* /*
* Set up a ModifyTableState so we can let FDW(s) init themselves for * Set up a ModifyTableState so we can let FDW(s) init themselves for
@ -2870,7 +2867,7 @@ CopyFrom(CopyState cstate)
* Otherwise, just remember the original unconverted * Otherwise, just remember the original unconverted
* tuple, to avoid a needless round trip conversion. * tuple, to avoid a needless round trip conversion.
*/ */
cstate->transition_capture->tcs_original_insert_tuple = tuple; cstate->transition_capture->tcs_original_insert_tuple = myslot;
cstate->transition_capture->tcs_map = NULL; cstate->transition_capture->tcs_map = NULL;
} }
} }
@ -2907,12 +2904,8 @@ CopyFrom(CopyState cstate)
/* BEFORE ROW INSERT Triggers */ /* BEFORE ROW INSERT Triggers */
if (has_before_insert_row_trig) if (has_before_insert_row_trig)
{ {
slot = ExecBRInsertTriggers(estate, resultRelInfo, slot); if (!ExecBRInsertTriggers(estate, resultRelInfo, slot))
skip_tuple = true; /* "do nothing" */
if (slot == NULL) /* "do nothing" */
skip_tuple = true;
else /* trigger might have changed tuple */
tuple = ExecFetchSlotHeapTuple(slot, true, NULL);
} }
if (!skip_tuple) if (!skip_tuple)
@ -2990,9 +2983,6 @@ CopyFrom(CopyState cstate)
if (slot == NULL) /* "do nothing" */ if (slot == NULL) /* "do nothing" */
continue; /* next tuple please */ continue; /* next tuple please */
/* FDW might have changed tuple */
tuple = ExecFetchSlotHeapTuple(slot, true, NULL);
/* /*
* AFTER ROW Triggers might reference the tableoid * AFTER ROW Triggers might reference the tableoid
* column, so (re-)initialize tts_tableOid before * column, so (re-)initialize tts_tableOid before
@ -3002,6 +2992,7 @@ CopyFrom(CopyState cstate)
} }
else else
{ {
tuple = ExecFetchSlotHeapTuple(slot, true, NULL);
heap_insert(resultRelInfo->ri_RelationDesc, tuple, heap_insert(resultRelInfo->ri_RelationDesc, tuple,
mycid, hi_options, bistate); mycid, hi_options, bistate);
ItemPointerCopy(&tuple->t_self, &slot->tts_tid); ItemPointerCopy(&tuple->t_self, &slot->tts_tid);
@ -3018,7 +3009,7 @@ CopyFrom(CopyState cstate)
NIL); NIL);
/* AFTER ROW INSERT Triggers */ /* AFTER ROW INSERT Triggers */
ExecARInsertTriggers(estate, resultRelInfo, tuple, ExecARInsertTriggers(estate, resultRelInfo, slot,
recheckIndexes, cstate->transition_capture); recheckIndexes, cstate->transition_capture);
list_free(recheckIndexes); list_free(recheckIndexes);
@ -3158,7 +3149,7 @@ CopyFromInsertBatch(CopyState cstate, EState *estate, CommandId mycid,
ExecInsertIndexTuples(myslot, &(bufferedTuples[i]->t_self), ExecInsertIndexTuples(myslot, &(bufferedTuples[i]->t_self),
estate, false, NULL, NIL); estate, false, NULL, NIL);
ExecARInsertTriggers(estate, resultRelInfo, ExecARInsertTriggers(estate, resultRelInfo,
bufferedTuples[i], myslot,
recheckIndexes, cstate->transition_capture); recheckIndexes, cstate->transition_capture);
list_free(recheckIndexes); list_free(recheckIndexes);
} }
@ -3175,8 +3166,9 @@ CopyFromInsertBatch(CopyState cstate, EState *estate, CommandId mycid,
for (i = 0; i < nBufferedTuples; i++) for (i = 0; i < nBufferedTuples; i++)
{ {
cstate->cur_lineno = firstBufferedLineNo + i; cstate->cur_lineno = firstBufferedLineNo + i;
ExecStoreHeapTuple(bufferedTuples[i], myslot, false);
ExecARInsertTriggers(estate, resultRelInfo, ExecARInsertTriggers(estate, resultRelInfo,
bufferedTuples[i], myslot,
NIL, cstate->transition_capture); NIL, cstate->transition_capture);
} }
} }

View File

@ -8921,8 +8921,6 @@ validateForeignKeyConstraint(char *conname,
trigdata.tg_trigtuple = tuple; trigdata.tg_trigtuple = tuple;
trigdata.tg_newtuple = NULL; trigdata.tg_newtuple = NULL;
trigdata.tg_trigger = &trig; trigdata.tg_trigger = &trig;
trigdata.tg_trigtuplebuf = scan->rs_cbuf;
trigdata.tg_newtuplebuf = InvalidBuffer;
fcinfo->context = (Node *) &trigdata; fcinfo->context = (Node *) &trigdata;

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. * Initialize the executor's tuple table to empty.
*/ */
estate->es_tupleTable = NIL; estate->es_tupleTable = NIL;
estate->es_trig_tuple_slot = NULL;
estate->es_trig_oldtup_slot = NULL;
estate->es_trig_newtup_slot = NULL;
/* mark EvalPlanQual not active */ /* mark EvalPlanQual not active */
estate->es_epqTuple = NULL; estate->es_epqTuple = NULL;
@ -1324,6 +1321,9 @@ InitResultRelInfo(ResultRelInfo *resultRelInfo,
resultRelInfo->ri_projectReturning = NULL; resultRelInfo->ri_projectReturning = NULL;
resultRelInfo->ri_onConflictArbiterIndexes = NIL; resultRelInfo->ri_onConflictArbiterIndexes = NIL;
resultRelInfo->ri_onConflict = NULL; resultRelInfo->ri_onConflict = NULL;
resultRelInfo->ri_ReturningSlot = NULL;
resultRelInfo->ri_TrigOldSlot = NULL;
resultRelInfo->ri_TrigNewSlot = NULL;
/* /*
* Partition constraint, which also includes the partition constraint of * Partition constraint, which also includes the partition constraint of

View File

@ -403,10 +403,8 @@ ExecSimpleRelationInsert(EState *estate, TupleTableSlot *slot)
if (resultRelInfo->ri_TrigDesc && if (resultRelInfo->ri_TrigDesc &&
resultRelInfo->ri_TrigDesc->trig_insert_before_row) resultRelInfo->ri_TrigDesc->trig_insert_before_row)
{ {
slot = ExecBRInsertTriggers(estate, resultRelInfo, slot); if (!ExecBRInsertTriggers(estate, resultRelInfo, slot))
skip_tuple = true; /* "do nothing" */
if (slot == NULL) /* "do nothing" */
skip_tuple = true;
} }
if (!skip_tuple) if (!skip_tuple)
@ -432,7 +430,7 @@ ExecSimpleRelationInsert(EState *estate, TupleTableSlot *slot)
NIL); NIL);
/* AFTER ROW INSERT Triggers */ /* AFTER ROW INSERT Triggers */
ExecARInsertTriggers(estate, resultRelInfo, tuple, ExecARInsertTriggers(estate, resultRelInfo, slot,
recheckIndexes, NULL); recheckIndexes, NULL);
/* /*
@ -475,11 +473,10 @@ ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate,
if (resultRelInfo->ri_TrigDesc && if (resultRelInfo->ri_TrigDesc &&
resultRelInfo->ri_TrigDesc->trig_update_before_row) resultRelInfo->ri_TrigDesc->trig_update_before_row)
{ {
slot = ExecBRUpdateTriggers(estate, epqstate, resultRelInfo, if (!ExecBRUpdateTriggers(estate, epqstate, resultRelInfo,
&hsearchslot->tuple->t_self, NULL, slot); &hsearchslot->tuple->t_self,
NULL, slot))
if (slot == NULL) /* "do nothing" */ skip_tuple = true; /* "do nothing" */
skip_tuple = true;
} }
if (!skip_tuple) if (!skip_tuple)
@ -507,7 +504,8 @@ ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate,
/* AFTER ROW UPDATE Triggers */ /* AFTER ROW UPDATE Triggers */
ExecARUpdateTriggers(estate, resultRelInfo, ExecARUpdateTriggers(estate, resultRelInfo,
&hsearchslot->tuple->t_self, NULL, tuple, &(tuple->t_self),
NULL, slot,
recheckIndexes, NULL); recheckIndexes, NULL);
list_free(recheckIndexes); list_free(recheckIndexes);
@ -540,8 +538,9 @@ ExecSimpleRelationDelete(EState *estate, EPQState *epqstate,
resultRelInfo->ri_TrigDesc->trig_delete_before_row) resultRelInfo->ri_TrigDesc->trig_delete_before_row)
{ {
skip_tuple = !ExecBRDeleteTriggers(estate, epqstate, resultRelInfo, skip_tuple = !ExecBRDeleteTriggers(estate, epqstate, resultRelInfo,
&hsearchslot->tuple->t_self, NULL, &hsearchslot->tuple->t_self,
NULL); NULL, NULL);
} }
if (!skip_tuple) if (!skip_tuple)

View File

@ -119,6 +119,7 @@ tts_virtual_clear(TupleTableSlot *slot)
slot->tts_nvalid = 0; slot->tts_nvalid = 0;
slot->tts_flags |= TTS_FLAG_EMPTY; slot->tts_flags |= TTS_FLAG_EMPTY;
ItemPointerSetInvalid(&slot->tts_tid);
} }
/* /*
@ -314,6 +315,7 @@ tts_heap_clear(TupleTableSlot *slot)
slot->tts_nvalid = 0; slot->tts_nvalid = 0;
slot->tts_flags |= TTS_FLAG_EMPTY; slot->tts_flags |= TTS_FLAG_EMPTY;
ItemPointerSetInvalid(&slot->tts_tid);
hslot->off = 0; hslot->off = 0;
hslot->tuple = NULL; hslot->tuple = NULL;
} }
@ -477,6 +479,7 @@ tts_minimal_clear(TupleTableSlot *slot)
slot->tts_nvalid = 0; slot->tts_nvalid = 0;
slot->tts_flags |= TTS_FLAG_EMPTY; slot->tts_flags |= TTS_FLAG_EMPTY;
ItemPointerSetInvalid(&slot->tts_tid);
mslot->off = 0; mslot->off = 0;
mslot->mintuple = NULL; mslot->mintuple = NULL;
} }
@ -658,6 +661,7 @@ tts_buffer_heap_clear(TupleTableSlot *slot)
slot->tts_nvalid = 0; slot->tts_nvalid = 0;
slot->tts_flags |= TTS_FLAG_EMPTY; slot->tts_flags |= TTS_FLAG_EMPTY;
ItemPointerSetInvalid(&slot->tts_tid);
bslot->base.tuple = NULL; bslot->base.tuple = NULL;
bslot->base.off = 0; bslot->base.off = 0;
bslot->buffer = InvalidBuffer; bslot->buffer = InvalidBuffer;

View File

@ -131,9 +131,6 @@ CreateExecutorState(void)
estate->es_tuple_routing_result_relations = NIL; estate->es_tuple_routing_result_relations = NIL;
estate->es_trig_target_relations = NIL; estate->es_trig_target_relations = NIL;
estate->es_trig_tuple_slot = NULL;
estate->es_trig_oldtup_slot = NULL;
estate->es_trig_newtup_slot = NULL;
estate->es_param_list_info = NULL; estate->es_param_list_info = NULL;
estate->es_param_exec_vals = NULL; estate->es_param_exec_vals = NULL;
@ -1102,3 +1099,69 @@ ExecCleanTargetListLength(List *targetlist)
} }
return len; return len;
} }
/*
* Return a relInfo's tuple slot for a trigger's OLD tuples.
*/
TupleTableSlot *
ExecGetTriggerOldSlot(EState *estate, ResultRelInfo *relInfo)
{
if (relInfo->ri_TrigOldSlot == NULL)
{
Relation rel = relInfo->ri_RelationDesc;
MemoryContext oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
relInfo->ri_TrigOldSlot =
ExecInitExtraTupleSlot(estate,
RelationGetDescr(rel),
&TTSOpsBufferHeapTuple);
MemoryContextSwitchTo(oldcontext);
}
return relInfo->ri_TrigOldSlot;
}
/*
* Return a relInfo's tuple slot for a trigger's NEW tuples.
*/
TupleTableSlot *
ExecGetTriggerNewSlot(EState *estate, ResultRelInfo *relInfo)
{
if (relInfo->ri_TrigNewSlot == NULL)
{
Relation rel = relInfo->ri_RelationDesc;
MemoryContext oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
relInfo->ri_TrigNewSlot =
ExecInitExtraTupleSlot(estate,
RelationGetDescr(rel),
&TTSOpsBufferHeapTuple);
MemoryContextSwitchTo(oldcontext);
}
return relInfo->ri_TrigNewSlot;
}
/*
* Return a relInfo's tuple slot for processing returning tuples.
*/
TupleTableSlot *
ExecGetReturningSlot(EState *estate, ResultRelInfo *relInfo)
{
if (relInfo->ri_ReturningSlot == NULL)
{
Relation rel = relInfo->ri_RelationDesc;
MemoryContext oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
relInfo->ri_ReturningSlot =
ExecInitExtraTupleSlot(estate,
RelationGetDescr(rel),
&TTSOpsBufferHeapTuple);
MemoryContextSwitchTo(oldcontext);
}
return relInfo->ri_ReturningSlot;
}

View File

@ -255,7 +255,6 @@ ExecInsert(ModifyTableState *mtstate,
EState *estate, EState *estate,
bool canSetTag) bool canSetTag)
{ {
HeapTuple tuple;
ResultRelInfo *resultRelInfo; ResultRelInfo *resultRelInfo;
Relation resultRelationDesc; Relation resultRelationDesc;
List *recheckIndexes = NIL; List *recheckIndexes = NIL;
@ -264,11 +263,7 @@ ExecInsert(ModifyTableState *mtstate,
ModifyTable *node = (ModifyTable *) mtstate->ps.plan; ModifyTable *node = (ModifyTable *) mtstate->ps.plan;
OnConflictAction onconflict = node->onConflictAction; OnConflictAction onconflict = node->onConflictAction;
/* ExecMaterializeSlot(slot);
* get the heap tuple out of the tuple table slot, making sure we have a
* writable copy
*/
tuple = ExecFetchSlotHeapTuple(slot, true, NULL);
/* /*
* get information on the (current) result relation * get information on the (current) result relation
@ -288,26 +283,16 @@ ExecInsert(ModifyTableState *mtstate,
if (resultRelInfo->ri_TrigDesc && if (resultRelInfo->ri_TrigDesc &&
resultRelInfo->ri_TrigDesc->trig_insert_before_row) resultRelInfo->ri_TrigDesc->trig_insert_before_row)
{ {
slot = ExecBRInsertTriggers(estate, resultRelInfo, slot); if (!ExecBRInsertTriggers(estate, resultRelInfo, slot))
return NULL; /* "do nothing" */
if (slot == NULL) /* "do nothing" */
return NULL;
/* trigger might have changed tuple */
tuple = ExecFetchSlotHeapTuple(slot, true, NULL);
} }
/* INSTEAD OF ROW INSERT Triggers */ /* INSTEAD OF ROW INSERT Triggers */
if (resultRelInfo->ri_TrigDesc && if (resultRelInfo->ri_TrigDesc &&
resultRelInfo->ri_TrigDesc->trig_insert_instead_row) resultRelInfo->ri_TrigDesc->trig_insert_instead_row)
{ {
slot = ExecIRInsertTriggers(estate, resultRelInfo, slot); if (!ExecIRInsertTriggers(estate, resultRelInfo, slot))
return NULL; /* "do nothing" */
if (slot == NULL) /* "do nothing" */
return NULL;
/* trigger might have changed tuple */
tuple = ExecFetchSlotHeapTuple(slot, true, NULL);
} }
else if (resultRelInfo->ri_FdwRoutine) else if (resultRelInfo->ri_FdwRoutine)
{ {
@ -322,9 +307,6 @@ ExecInsert(ModifyTableState *mtstate,
if (slot == NULL) /* "do nothing" */ if (slot == NULL) /* "do nothing" */
return NULL; return NULL;
/* FDW might have changed tuple */
tuple = ExecFetchSlotHeapTuple(slot, true, NULL);
/* /*
* AFTER ROW Triggers or RETURNING expressions might reference the * AFTER ROW Triggers or RETURNING expressions might reference the
* tableoid column, so (re-)initialize tts_tableOid before evaluating * tableoid column, so (re-)initialize tts_tableOid before evaluating
@ -336,6 +318,7 @@ ExecInsert(ModifyTableState *mtstate,
else else
{ {
WCOKind wco_kind; WCOKind wco_kind;
HeapTuple inserttuple;
/* /*
* Constraints might reference the tableoid column, so (re-)initialize * Constraints might reference the tableoid column, so (re-)initialize
@ -441,6 +424,8 @@ ExecInsert(ModifyTableState *mtstate,
} }
} }
inserttuple = ExecFetchSlotHeapTuple(slot, true, NULL);
/* /*
* Before we start insertion proper, acquire our "speculative * Before we start insertion proper, acquire our "speculative
* insertion lock". Others can use that to wait for us to decide * insertion lock". Others can use that to wait for us to decide
@ -448,26 +433,26 @@ ExecInsert(ModifyTableState *mtstate,
* waiting for the whole transaction to complete. * waiting for the whole transaction to complete.
*/ */
specToken = SpeculativeInsertionLockAcquire(GetCurrentTransactionId()); specToken = SpeculativeInsertionLockAcquire(GetCurrentTransactionId());
HeapTupleHeaderSetSpeculativeToken(tuple->t_data, specToken); HeapTupleHeaderSetSpeculativeToken(inserttuple->t_data, specToken);
/* insert the tuple, with the speculative token */ /* insert the tuple, with the speculative token */
heap_insert(resultRelationDesc, tuple, heap_insert(resultRelationDesc, inserttuple,
estate->es_output_cid, estate->es_output_cid,
HEAP_INSERT_SPECULATIVE, HEAP_INSERT_SPECULATIVE,
NULL); NULL);
slot->tts_tableOid = RelationGetRelid(resultRelationDesc); slot->tts_tableOid = RelationGetRelid(resultRelationDesc);
ItemPointerCopy(&tuple->t_self, &slot->tts_tid); ItemPointerCopy(&inserttuple->t_self, &slot->tts_tid);
/* insert index entries for tuple */ /* insert index entries for tuple */
recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self), recheckIndexes = ExecInsertIndexTuples(slot, &(inserttuple->t_self),
estate, true, &specConflict, estate, true, &specConflict,
arbiterIndexes); arbiterIndexes);
/* adjust the tuple's state accordingly */ /* adjust the tuple's state accordingly */
if (!specConflict) if (!specConflict)
heap_finish_speculative(resultRelationDesc, tuple); heap_finish_speculative(resultRelationDesc, inserttuple);
else else
heap_abort_speculative(resultRelationDesc, tuple); heap_abort_speculative(resultRelationDesc, inserttuple);
/* /*
* Wake up anyone waiting for our decision. They will re-check * Wake up anyone waiting for our decision. They will re-check
@ -499,15 +484,16 @@ ExecInsert(ModifyTableState *mtstate,
* Note: heap_insert returns the tid (location) of the new tuple * Note: heap_insert returns the tid (location) of the new tuple
* in the t_self field. * in the t_self field.
*/ */
heap_insert(resultRelationDesc, tuple, inserttuple = ExecFetchSlotHeapTuple(slot, true, NULL);
heap_insert(resultRelationDesc, inserttuple,
estate->es_output_cid, estate->es_output_cid,
0, NULL); 0, NULL);
slot->tts_tableOid = RelationGetRelid(resultRelationDesc); slot->tts_tableOid = RelationGetRelid(resultRelationDesc);
ItemPointerCopy(&tuple->t_self, &slot->tts_tid); ItemPointerCopy(&inserttuple->t_self, &slot->tts_tid);
/* insert index entries for tuple */ /* insert index entries for tuple */
if (resultRelInfo->ri_NumIndices > 0) if (resultRelInfo->ri_NumIndices > 0)
recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self), recheckIndexes = ExecInsertIndexTuples(slot, &(inserttuple->t_self),
estate, false, NULL, estate, false, NULL,
NIL); NIL);
} }
@ -531,7 +517,7 @@ ExecInsert(ModifyTableState *mtstate,
{ {
ExecARUpdateTriggers(estate, resultRelInfo, NULL, ExecARUpdateTriggers(estate, resultRelInfo, NULL,
NULL, NULL,
tuple, slot,
NULL, NULL,
mtstate->mt_transition_capture); mtstate->mt_transition_capture);
@ -543,7 +529,7 @@ ExecInsert(ModifyTableState *mtstate,
} }
/* AFTER ROW INSERT Triggers */ /* AFTER ROW INSERT Triggers */
ExecARInsertTriggers(estate, resultRelInfo, tuple, recheckIndexes, ExecARInsertTriggers(estate, resultRelInfo, slot, recheckIndexes,
ar_insert_trig_tcs); ar_insert_trig_tcs);
list_free(recheckIndexes); list_free(recheckIndexes);
@ -603,7 +589,7 @@ ExecDelete(ModifyTableState *mtstate,
bool canSetTag, bool canSetTag,
bool changingPart, bool changingPart,
bool *tupleDeleted, bool *tupleDeleted,
TupleTableSlot **epqslot) TupleTableSlot **epqreturnslot)
{ {
ResultRelInfo *resultRelInfo; ResultRelInfo *resultRelInfo;
Relation resultRelationDesc; Relation resultRelationDesc;
@ -628,7 +614,7 @@ ExecDelete(ModifyTableState *mtstate,
bool dodelete; bool dodelete;
dodelete = ExecBRDeleteTriggers(estate, epqstate, resultRelInfo, dodelete = ExecBRDeleteTriggers(estate, epqstate, resultRelInfo,
tupleid, oldtuple, epqslot); tupleid, oldtuple, epqreturnslot);
if (!dodelete) /* "do nothing" */ if (!dodelete) /* "do nothing" */
return NULL; return NULL;
@ -651,14 +637,10 @@ ExecDelete(ModifyTableState *mtstate,
/* /*
* delete from foreign table: let the FDW do it * delete from foreign table: let the FDW do it
* *
* We offer the trigger tuple slot as a place to store RETURNING data, * We offer the returning slot as a place to store RETURNING data,
* although the FDW can return some other slot if it wants. Set up * although the FDW can return some other slot if it wants.
* the slot's tupdesc so the FDW doesn't need to do that for itself.
*/ */
slot = estate->es_trig_tuple_slot; slot = ExecGetReturningSlot(estate, resultRelInfo);
if (slot->tts_tupleDescriptor != RelationGetDescr(resultRelationDesc))
ExecSetSlotDescriptor(slot, RelationGetDescr(resultRelationDesc));
slot = resultRelInfo->ri_FdwRoutine->ExecForeignDelete(estate, slot = resultRelInfo->ri_FdwRoutine->ExecForeignDelete(estate,
resultRelInfo, resultRelInfo,
slot, slot,
@ -673,6 +655,8 @@ ExecDelete(ModifyTableState *mtstate,
*/ */
if (TTS_EMPTY(slot)) if (TTS_EMPTY(slot))
ExecStoreAllNullTuple(slot); ExecStoreAllNullTuple(slot);
ExecMaterializeSlot(slot);
slot->tts_tableOid = RelationGetRelid(resultRelationDesc); slot->tts_tableOid = RelationGetRelid(resultRelationDesc);
} }
else else
@ -762,9 +746,9 @@ ldelete:;
* If requested, skip delete and pass back the updated * If requested, skip delete and pass back the updated
* row. * row.
*/ */
if (epqslot) if (epqreturnslot)
{ {
*epqslot = my_epqslot; *epqreturnslot = my_epqslot;
return NULL; return NULL;
} }
else else
@ -832,34 +816,37 @@ ldelete:;
* gotta fetch it. We can use the trigger tuple slot. * gotta fetch it. We can use the trigger tuple slot.
*/ */
TupleTableSlot *rslot; TupleTableSlot *rslot;
HeapTupleData deltuple;
Buffer delbuffer;
if (resultRelInfo->ri_FdwRoutine) if (resultRelInfo->ri_FdwRoutine)
{ {
/* FDW must have provided a slot containing the deleted row */ /* FDW must have provided a slot containing the deleted row */
Assert(!TupIsNull(slot)); Assert(!TupIsNull(slot));
delbuffer = InvalidBuffer;
} }
else else
{ {
slot = estate->es_trig_tuple_slot; slot = ExecGetReturningSlot(estate, resultRelInfo);
if (oldtuple != NULL) if (oldtuple != NULL)
{ {
deltuple = *oldtuple; ExecForceStoreHeapTuple(oldtuple, slot);
delbuffer = InvalidBuffer;
} }
else else
{ {
deltuple.t_self = *tupleid; BufferHeapTupleTableSlot *bslot;
if (!heap_fetch(resultRelationDesc, SnapshotAny, HeapTuple deltuple;
&deltuple, &delbuffer, false, NULL)) Buffer buffer;
elog(ERROR, "failed to fetch deleted tuple for DELETE RETURNING");
}
if (slot->tts_tupleDescriptor != RelationGetDescr(resultRelationDesc)) Assert(TTS_IS_BUFFERTUPLE(slot));
ExecSetSlotDescriptor(slot, RelationGetDescr(resultRelationDesc)); ExecClearTuple(slot);
ExecStoreHeapTuple(&deltuple, slot, false); 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); rslot = ExecProcessReturning(resultRelInfo, slot, planSlot);
@ -871,8 +858,6 @@ ldelete:;
ExecMaterializeSlot(rslot); ExecMaterializeSlot(rslot);
ExecClearTuple(slot); ExecClearTuple(slot);
if (BufferIsValid(delbuffer))
ReleaseBuffer(delbuffer);
return rslot; return rslot;
} }
@ -912,7 +897,7 @@ ExecUpdate(ModifyTableState *mtstate,
EState *estate, EState *estate,
bool canSetTag) bool canSetTag)
{ {
HeapTuple tuple; HeapTuple updatetuple;
ResultRelInfo *resultRelInfo; ResultRelInfo *resultRelInfo;
Relation resultRelationDesc; Relation resultRelationDesc;
HTSU_Result result; HTSU_Result result;
@ -926,11 +911,7 @@ ExecUpdate(ModifyTableState *mtstate,
if (IsBootstrapProcessingMode()) if (IsBootstrapProcessingMode())
elog(ERROR, "cannot UPDATE during bootstrap"); elog(ERROR, "cannot UPDATE during bootstrap");
/* ExecMaterializeSlot(slot);
* get the heap tuple out of the tuple table slot, making sure we have a
* writable copy
*/
tuple = ExecFetchSlotHeapTuple(slot, true, NULL);
/* /*
* get information on the (current) result relation * get information on the (current) result relation
@ -942,28 +923,18 @@ ExecUpdate(ModifyTableState *mtstate,
if (resultRelInfo->ri_TrigDesc && if (resultRelInfo->ri_TrigDesc &&
resultRelInfo->ri_TrigDesc->trig_update_before_row) resultRelInfo->ri_TrigDesc->trig_update_before_row)
{ {
slot = ExecBRUpdateTriggers(estate, epqstate, resultRelInfo, if (!ExecBRUpdateTriggers(estate, epqstate, resultRelInfo,
tupleid, oldtuple, slot); tupleid, oldtuple, slot))
return NULL; /* "do nothing" */
if (slot == NULL) /* "do nothing" */
return NULL;
/* trigger might have changed tuple */
tuple = ExecFetchSlotHeapTuple(slot, true, NULL);
} }
/* INSTEAD OF ROW UPDATE Triggers */ /* INSTEAD OF ROW UPDATE Triggers */
if (resultRelInfo->ri_TrigDesc && if (resultRelInfo->ri_TrigDesc &&
resultRelInfo->ri_TrigDesc->trig_update_instead_row) resultRelInfo->ri_TrigDesc->trig_update_instead_row)
{ {
slot = ExecIRUpdateTriggers(estate, resultRelInfo, if (!ExecIRUpdateTriggers(estate, resultRelInfo,
oldtuple, slot); oldtuple, slot))
return NULL; /* "do nothing" */
if (slot == NULL) /* "do nothing" */
return NULL;
/* trigger might have changed tuple */
tuple = ExecFetchSlotHeapTuple(slot, true, NULL);
} }
else if (resultRelInfo->ri_FdwRoutine) else if (resultRelInfo->ri_FdwRoutine)
{ {
@ -978,9 +949,6 @@ ExecUpdate(ModifyTableState *mtstate,
if (slot == NULL) /* "do nothing" */ if (slot == NULL) /* "do nothing" */
return NULL; return NULL;
/* FDW might have changed tuple */
tuple = ExecFetchSlotHeapTuple(slot, true, NULL);
/* /*
* AFTER ROW Triggers or RETURNING expressions might reference the * AFTER ROW Triggers or RETURNING expressions might reference the
* tableoid column, so (re-)initialize tts_tableOid before evaluating * tableoid column, so (re-)initialize tts_tableOid before evaluating
@ -1107,7 +1075,6 @@ lreplace:;
else else
{ {
slot = ExecFilterJunk(resultRelInfo->ri_junkFilter, epqslot); slot = ExecFilterJunk(resultRelInfo->ri_junkFilter, epqslot);
tuple = ExecFetchSlotHeapTuple(slot, true, NULL);
goto lreplace; goto lreplace;
} }
} }
@ -1178,12 +1145,14 @@ lreplace:;
* needed for referential integrity updates in transaction-snapshot * needed for referential integrity updates in transaction-snapshot
* mode transactions. * mode transactions.
*/ */
result = heap_update(resultRelationDesc, tupleid, tuple, updatetuple = ExecFetchSlotHeapTuple(slot, true, NULL);
result = heap_update(resultRelationDesc, tupleid,
updatetuple,
estate->es_output_cid, estate->es_output_cid,
estate->es_crosscheck_snapshot, estate->es_crosscheck_snapshot,
true /* wait for commit */ , true /* wait for commit */ ,
&hufd, &lockmode); &hufd, &lockmode);
ItemPointerCopy(&tuple->t_self, &slot->tts_tid); ItemPointerCopy(&updatetuple->t_self, &slot->tts_tid);
switch (result) switch (result)
{ {
@ -1249,7 +1218,6 @@ lreplace:;
{ {
*tupleid = hufd.ctid; *tupleid = hufd.ctid;
slot = ExecFilterJunk(resultRelInfo->ri_junkFilter, epqslot); slot = ExecFilterJunk(resultRelInfo->ri_junkFilter, epqslot);
tuple = ExecFetchSlotHeapTuple(slot, true, NULL);
goto lreplace; goto lreplace;
} }
} }
@ -1277,8 +1245,8 @@ lreplace:;
* *
* If it's a HOT update, we mustn't insert new index entries. * If it's a HOT update, we mustn't insert new index entries.
*/ */
if (resultRelInfo->ri_NumIndices > 0 && !HeapTupleIsHeapOnly(tuple)) if (resultRelInfo->ri_NumIndices > 0 && !HeapTupleIsHeapOnly(updatetuple))
recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self), recheckIndexes = ExecInsertIndexTuples(slot, &(updatetuple->t_self),
estate, false, NULL, NIL); estate, false, NULL, NIL);
} }
@ -1286,7 +1254,7 @@ lreplace:;
(estate->es_processed)++; (estate->es_processed)++;
/* AFTER ROW UPDATE Triggers */ /* AFTER ROW UPDATE Triggers */
ExecARUpdateTriggers(estate, resultRelInfo, tupleid, oldtuple, tuple, ExecARUpdateTriggers(estate, resultRelInfo, tupleid, oldtuple, slot,
recheckIndexes, recheckIndexes,
mtstate->operation == CMD_INSERT ? mtstate->operation == CMD_INSERT ?
mtstate->mt_oc_transition_capture : mtstate->mt_oc_transition_capture :
@ -1669,7 +1637,6 @@ ExecPrepareTupleRouting(ModifyTableState *mtstate,
ModifyTable *node; ModifyTable *node;
ResultRelInfo *partrel; ResultRelInfo *partrel;
PartitionRoutingInfo *partrouteinfo; PartitionRoutingInfo *partrouteinfo;
HeapTuple tuple;
TupleConversionMap *map; TupleConversionMap *map;
/* /*
@ -1688,9 +1655,6 @@ ExecPrepareTupleRouting(ModifyTableState *mtstate,
*/ */
estate->es_result_relation_info = partrel; estate->es_result_relation_info = partrel;
/* Get the heap tuple out of the given slot. */
tuple = ExecFetchSlotHeapTuple(slot, true, NULL);
/* /*
* If we're capturing transition tuples, we might need to convert from the * If we're capturing transition tuples, we might need to convert from the
* partition rowtype to root partitioned table's rowtype. * partition rowtype to root partitioned table's rowtype.
@ -1714,7 +1678,7 @@ ExecPrepareTupleRouting(ModifyTableState *mtstate,
* Otherwise, just remember the original unconverted tuple, to * Otherwise, just remember the original unconverted tuple, to
* avoid a needless round trip conversion. * avoid a needless round trip conversion.
*/ */
mtstate->mt_transition_capture->tcs_original_insert_tuple = tuple; mtstate->mt_transition_capture->tcs_original_insert_tuple = slot;
mtstate->mt_transition_capture->tcs_map = NULL; mtstate->mt_transition_capture->tcs_map = NULL;
} }
} }
@ -2541,16 +2505,6 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
} }
} }
/*
* Set up a tuple table slot for use for trigger output tuples. In a plan
* containing multiple ModifyTable nodes, all can share one such slot, so
* we keep it in the estate. The tuple being inserted doesn't come from a
* buffer.
*/
if (estate->es_trig_tuple_slot == NULL)
estate->es_trig_tuple_slot = ExecInitExtraTupleSlot(estate, NULL,
&TTSOpsHeapTuple);
/* /*
* Lastly, if this is not the primary (canSetTag) ModifyTable node, add it * Lastly, if this is not the primary (canSetTag) ModifyTable node, add it
* to estate->es_auxmodifytables so that it will be run to completion by * to estate->es_auxmodifytables so that it will be run to completion by

View File

@ -196,11 +196,6 @@ create_estate_for_relation(LogicalRepRelMapEntry *rel)
estate->es_output_cid = GetCurrentCommandId(true); estate->es_output_cid = GetCurrentCommandId(true);
/* Triggers might need a slot */
if (resultRelInfo->ri_TrigDesc)
estate->es_trig_tuple_slot = ExecInitExtraTupleSlot(estate, NULL,
&TTSOpsVirtual);
/* Prepare to catch AFTER triggers. */ /* Prepare to catch AFTER triggers. */
AfterTriggerBeginQuery(); AfterTriggerBeginQuery();

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, static bool ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel,
HeapTuple old_row, TupleTableSlot *oldslot,
const RI_ConstraintInfo *riinfo); const RI_ConstraintInfo *riinfo);
static Datum ri_restrict(TriggerData *trigdata, bool is_no_action); static Datum ri_restrict(TriggerData *trigdata, bool is_no_action);
static Datum ri_setnull(TriggerData *trigdata); static Datum ri_setnull(TriggerData *trigdata);
@ -205,12 +205,12 @@ static void ri_GenerateQual(StringInfo buf,
Oid opoid, Oid opoid,
const char *rightop, Oid rightoptype); const char *rightop, Oid rightoptype);
static void ri_GenerateQualCollation(StringInfo buf, Oid collation); static void ri_GenerateQualCollation(StringInfo buf, Oid collation);
static int ri_NullCheck(TupleDesc tupdesc, HeapTuple tup, static int ri_NullCheck(TupleDesc tupdesc, TupleTableSlot *slot,
const RI_ConstraintInfo *riinfo, bool rel_is_pk); const RI_ConstraintInfo *riinfo, bool rel_is_pk);
static void ri_BuildQueryKey(RI_QueryKey *key, static void ri_BuildQueryKey(RI_QueryKey *key,
const RI_ConstraintInfo *riinfo, const RI_ConstraintInfo *riinfo,
int32 constr_queryno); int32 constr_queryno);
static bool ri_KeysEqual(Relation rel, HeapTuple oldtup, HeapTuple newtup, static bool ri_KeysEqual(Relation rel, TupleTableSlot *oldslot, TupleTableSlot *newslot,
const RI_ConstraintInfo *riinfo, bool rel_is_pk); const RI_ConstraintInfo *riinfo, bool rel_is_pk);
static bool ri_AttributesEqual(Oid eq_opr, Oid typeid, static bool ri_AttributesEqual(Oid eq_opr, Oid typeid,
Datum oldvalue, Datum newvalue); Datum oldvalue, Datum newvalue);
@ -232,14 +232,14 @@ static SPIPlanPtr ri_PlanCheck(const char *querystr, int nargs, Oid *argtypes,
static bool ri_PerformCheck(const RI_ConstraintInfo *riinfo, static bool ri_PerformCheck(const RI_ConstraintInfo *riinfo,
RI_QueryKey *qkey, SPIPlanPtr qplan, RI_QueryKey *qkey, SPIPlanPtr qplan,
Relation fk_rel, Relation pk_rel, Relation fk_rel, Relation pk_rel,
HeapTuple old_tuple, HeapTuple new_tuple, TupleTableSlot *oldslot, TupleTableSlot *newslot,
bool detectNewRows, int expect_OK); bool detectNewRows, int expect_OK);
static void ri_ExtractValues(Relation rel, HeapTuple tup, static void ri_ExtractValues(Relation rel, TupleTableSlot *slot,
const RI_ConstraintInfo *riinfo, bool rel_is_pk, const RI_ConstraintInfo *riinfo, bool rel_is_pk,
Datum *vals, char *nulls); Datum *vals, char *nulls);
static void ri_ReportViolation(const RI_ConstraintInfo *riinfo, static void ri_ReportViolation(const RI_ConstraintInfo *riinfo,
Relation pk_rel, Relation fk_rel, Relation pk_rel, Relation fk_rel,
HeapTuple violator, TupleDesc tupdesc, TupleTableSlot *violator, TupleDesc tupdesc,
int queryno) pg_attribute_noreturn(); int queryno) pg_attribute_noreturn();
@ -255,8 +255,7 @@ RI_FKey_check(TriggerData *trigdata)
const RI_ConstraintInfo *riinfo; const RI_ConstraintInfo *riinfo;
Relation fk_rel; Relation fk_rel;
Relation pk_rel; Relation pk_rel;
HeapTuple new_row; TupleTableSlot *newslot;
Buffer new_row_buf;
RI_QueryKey qkey; RI_QueryKey qkey;
SPIPlanPtr qplan; SPIPlanPtr qplan;
int i; int i;
@ -268,15 +267,9 @@ RI_FKey_check(TriggerData *trigdata)
trigdata->tg_relation, false); trigdata->tg_relation, false);
if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event)) if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
{ newslot = trigdata->tg_newslot;
new_row = trigdata->tg_newtuple;
new_row_buf = trigdata->tg_newtuplebuf;
}
else else
{ newslot = trigdata->tg_trigslot;
new_row = trigdata->tg_trigtuple;
new_row_buf = trigdata->tg_trigtuplebuf;
}
/* /*
* We should not even consider checking the row if it is no longer valid, * We should not even consider checking the row if it is no longer valid,
@ -285,14 +278,26 @@ RI_FKey_check(TriggerData *trigdata)
* checked). Test its liveness according to SnapshotSelf. We need pin * checked). Test its liveness according to SnapshotSelf. We need pin
* and lock on the buffer to call HeapTupleSatisfiesVisibility. Caller * and lock on the buffer to call HeapTupleSatisfiesVisibility. Caller
* should be holding pin, but not lock. * should be holding pin, but not lock.
*
* XXX: Note that the buffer-tuple specificity will be removed in the near
* future.
*/ */
LockBuffer(new_row_buf, BUFFER_LOCK_SHARE); if (TTS_IS_BUFFERTUPLE(newslot))
if (!HeapTupleSatisfiesVisibility(new_row, SnapshotSelf, new_row_buf))
{ {
LockBuffer(new_row_buf, BUFFER_LOCK_UNLOCK); BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) newslot;
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); return PointerGetDatum(NULL);
} }
LockBuffer(new_row_buf, BUFFER_LOCK_UNLOCK); LockBuffer(bslot->buffer, BUFFER_LOCK_UNLOCK);
}
else
elog(ERROR, "expected buffer tuple");
/* /*
* Get the relation descriptors of the FK and PK tables. * Get the relation descriptors of the FK and PK tables.
@ -308,7 +313,7 @@ RI_FKey_check(TriggerData *trigdata)
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("MATCH PARTIAL not yet implemented"))); errmsg("MATCH PARTIAL not yet implemented")));
switch (ri_NullCheck(RelationGetDescr(fk_rel), new_row, riinfo, false)) switch (ri_NullCheck(RelationGetDescr(fk_rel), newslot, riinfo, false))
{ {
case RI_KEYS_ALL_NULL: case RI_KEYS_ALL_NULL:
@ -438,7 +443,7 @@ RI_FKey_check(TriggerData *trigdata)
*/ */
ri_PerformCheck(riinfo, &qkey, qplan, ri_PerformCheck(riinfo, &qkey, qplan,
fk_rel, pk_rel, fk_rel, pk_rel,
NULL, new_row, NULL, newslot,
false, false,
SPI_OK_SELECT); SPI_OK_SELECT);
@ -506,7 +511,7 @@ RI_FKey_check_upd(PG_FUNCTION_ARGS)
*/ */
static bool static bool
ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel, ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel,
HeapTuple old_row, TupleTableSlot *oldslot,
const RI_ConstraintInfo *riinfo) const RI_ConstraintInfo *riinfo)
{ {
SPIPlanPtr qplan; SPIPlanPtr qplan;
@ -515,7 +520,7 @@ ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel,
bool result; bool result;
/* Only called for non-null rows */ /* Only called for non-null rows */
Assert(ri_NullCheck(RelationGetDescr(pk_rel), old_row, riinfo, true) == RI_KEYS_NONE_NULL); Assert(ri_NullCheck(RelationGetDescr(pk_rel), oldslot, riinfo, true) == RI_KEYS_NONE_NULL);
if (SPI_connect() != SPI_OK_CONNECT) if (SPI_connect() != SPI_OK_CONNECT)
elog(ERROR, "SPI_connect failed"); elog(ERROR, "SPI_connect failed");
@ -573,7 +578,7 @@ ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel,
*/ */
result = ri_PerformCheck(riinfo, &qkey, qplan, result = ri_PerformCheck(riinfo, &qkey, qplan,
fk_rel, pk_rel, fk_rel, pk_rel,
old_row, NULL, oldslot, NULL,
true, /* treat like update */ true, /* treat like update */
SPI_OK_SELECT); SPI_OK_SELECT);
@ -691,7 +696,7 @@ ri_restrict(TriggerData *trigdata, bool is_no_action)
const RI_ConstraintInfo *riinfo; const RI_ConstraintInfo *riinfo;
Relation fk_rel; Relation fk_rel;
Relation pk_rel; Relation pk_rel;
HeapTuple old_row; TupleTableSlot *old_slot;
RI_QueryKey qkey; RI_QueryKey qkey;
SPIPlanPtr qplan; SPIPlanPtr qplan;
@ -709,7 +714,7 @@ ri_restrict(TriggerData *trigdata, bool is_no_action)
*/ */
fk_rel = table_open(riinfo->fk_relid, RowShareLock); fk_rel = table_open(riinfo->fk_relid, RowShareLock);
pk_rel = trigdata->tg_relation; pk_rel = trigdata->tg_relation;
old_row = trigdata->tg_trigtuple; old_slot = trigdata->tg_trigslot;
switch (riinfo->confmatchtype) switch (riinfo->confmatchtype)
{ {
@ -733,7 +738,7 @@ ri_restrict(TriggerData *trigdata, bool is_no_action)
* allow another row to be substituted. * allow another row to be substituted.
*/ */
if (is_no_action && if (is_no_action &&
ri_Check_Pk_Match(pk_rel, fk_rel, old_row, riinfo)) ri_Check_Pk_Match(pk_rel, fk_rel, old_slot, riinfo))
{ {
table_close(fk_rel, RowShareLock); table_close(fk_rel, RowShareLock);
return PointerGetDatum(NULL); return PointerGetDatum(NULL);
@ -801,7 +806,7 @@ ri_restrict(TriggerData *trigdata, bool is_no_action)
*/ */
ri_PerformCheck(riinfo, &qkey, qplan, ri_PerformCheck(riinfo, &qkey, qplan,
fk_rel, pk_rel, fk_rel, pk_rel,
old_row, NULL, old_slot, NULL,
true, /* must detect new rows */ true, /* must detect new rows */
SPI_OK_SELECT); SPI_OK_SELECT);
@ -845,7 +850,7 @@ RI_FKey_cascade_del(PG_FUNCTION_ARGS)
const RI_ConstraintInfo *riinfo; const RI_ConstraintInfo *riinfo;
Relation fk_rel; Relation fk_rel;
Relation pk_rel; Relation pk_rel;
HeapTuple old_row; TupleTableSlot *old_slot;
RI_QueryKey qkey; RI_QueryKey qkey;
SPIPlanPtr qplan; SPIPlanPtr qplan;
int i; int i;
@ -869,7 +874,7 @@ RI_FKey_cascade_del(PG_FUNCTION_ARGS)
*/ */
fk_rel = table_open(riinfo->fk_relid, RowExclusiveLock); fk_rel = table_open(riinfo->fk_relid, RowExclusiveLock);
pk_rel = trigdata->tg_relation; pk_rel = trigdata->tg_relation;
old_row = trigdata->tg_trigtuple; old_slot = trigdata->tg_trigslot;
switch (riinfo->confmatchtype) switch (riinfo->confmatchtype)
{ {
@ -941,7 +946,7 @@ RI_FKey_cascade_del(PG_FUNCTION_ARGS)
*/ */
ri_PerformCheck(riinfo, &qkey, qplan, ri_PerformCheck(riinfo, &qkey, qplan,
fk_rel, pk_rel, fk_rel, pk_rel,
old_row, NULL, old_slot, NULL,
true, /* must detect new rows */ true, /* must detect new rows */
SPI_OK_DELETE); SPI_OK_DELETE);
@ -985,8 +990,8 @@ RI_FKey_cascade_upd(PG_FUNCTION_ARGS)
const RI_ConstraintInfo *riinfo; const RI_ConstraintInfo *riinfo;
Relation fk_rel; Relation fk_rel;
Relation pk_rel; Relation pk_rel;
HeapTuple new_row; TupleTableSlot *new_slot;
HeapTuple old_row; TupleTableSlot *old_slot;
RI_QueryKey qkey; RI_QueryKey qkey;
SPIPlanPtr qplan; SPIPlanPtr qplan;
int i; int i;
@ -1012,8 +1017,8 @@ RI_FKey_cascade_upd(PG_FUNCTION_ARGS)
*/ */
fk_rel = table_open(riinfo->fk_relid, RowExclusiveLock); fk_rel = table_open(riinfo->fk_relid, RowExclusiveLock);
pk_rel = trigdata->tg_relation; pk_rel = trigdata->tg_relation;
new_row = trigdata->tg_newtuple; new_slot = trigdata->tg_newslot;
old_row = trigdata->tg_trigtuple; old_slot = trigdata->tg_trigslot;
switch (riinfo->confmatchtype) switch (riinfo->confmatchtype)
{ {
@ -1097,7 +1102,7 @@ RI_FKey_cascade_upd(PG_FUNCTION_ARGS)
*/ */
ri_PerformCheck(riinfo, &qkey, qplan, ri_PerformCheck(riinfo, &qkey, qplan,
fk_rel, pk_rel, fk_rel, pk_rel,
old_row, new_row, old_slot, new_slot,
true, /* must detect new rows */ true, /* must detect new rows */
SPI_OK_UPDATE); SPI_OK_UPDATE);
@ -1180,7 +1185,7 @@ ri_setnull(TriggerData *trigdata)
const RI_ConstraintInfo *riinfo; const RI_ConstraintInfo *riinfo;
Relation fk_rel; Relation fk_rel;
Relation pk_rel; Relation pk_rel;
HeapTuple old_row; TupleTableSlot *old_slot;
RI_QueryKey qkey; RI_QueryKey qkey;
SPIPlanPtr qplan; SPIPlanPtr qplan;
int i; int i;
@ -1199,7 +1204,7 @@ ri_setnull(TriggerData *trigdata)
*/ */
fk_rel = table_open(riinfo->fk_relid, RowExclusiveLock); fk_rel = table_open(riinfo->fk_relid, RowExclusiveLock);
pk_rel = trigdata->tg_relation; pk_rel = trigdata->tg_relation;
old_row = trigdata->tg_trigtuple; old_slot = trigdata->tg_trigslot;
switch (riinfo->confmatchtype) switch (riinfo->confmatchtype)
{ {
@ -1284,7 +1289,7 @@ ri_setnull(TriggerData *trigdata)
*/ */
ri_PerformCheck(riinfo, &qkey, qplan, ri_PerformCheck(riinfo, &qkey, qplan,
fk_rel, pk_rel, fk_rel, pk_rel,
old_row, NULL, old_slot, NULL,
true, /* must detect new rows */ true, /* must detect new rows */
SPI_OK_UPDATE); SPI_OK_UPDATE);
@ -1367,7 +1372,7 @@ ri_setdefault(TriggerData *trigdata)
const RI_ConstraintInfo *riinfo; const RI_ConstraintInfo *riinfo;
Relation fk_rel; Relation fk_rel;
Relation pk_rel; Relation pk_rel;
HeapTuple old_row; TupleTableSlot *old_slot;
RI_QueryKey qkey; RI_QueryKey qkey;
SPIPlanPtr qplan; SPIPlanPtr qplan;
@ -1385,7 +1390,7 @@ ri_setdefault(TriggerData *trigdata)
*/ */
fk_rel = table_open(riinfo->fk_relid, RowExclusiveLock); fk_rel = table_open(riinfo->fk_relid, RowExclusiveLock);
pk_rel = trigdata->tg_relation; pk_rel = trigdata->tg_relation;
old_row = trigdata->tg_trigtuple; old_slot = trigdata->tg_trigslot;
switch (riinfo->confmatchtype) switch (riinfo->confmatchtype)
{ {
@ -1471,7 +1476,7 @@ ri_setdefault(TriggerData *trigdata)
*/ */
ri_PerformCheck(riinfo, &qkey, qplan, ri_PerformCheck(riinfo, &qkey, qplan,
fk_rel, pk_rel, fk_rel, pk_rel,
old_row, NULL, old_slot, NULL,
true, /* must detect new rows */ true, /* must detect new rows */
SPI_OK_UPDATE); SPI_OK_UPDATE);
@ -1530,7 +1535,7 @@ ri_setdefault(TriggerData *trigdata)
*/ */
bool bool
RI_FKey_pk_upd_check_required(Trigger *trigger, Relation pk_rel, RI_FKey_pk_upd_check_required(Trigger *trigger, Relation pk_rel,
HeapTuple old_row, HeapTuple new_row) TupleTableSlot *old_slot, TupleTableSlot *new_slot)
{ {
const RI_ConstraintInfo *riinfo; const RI_ConstraintInfo *riinfo;
@ -1548,11 +1553,11 @@ RI_FKey_pk_upd_check_required(Trigger *trigger, Relation pk_rel,
* If any old key value is NULL, the row could not have been * If any old key value is NULL, the row could not have been
* referenced by an FK row, so no check is needed. * referenced by an FK row, so no check is needed.
*/ */
if (ri_NullCheck(RelationGetDescr(pk_rel), old_row, riinfo, true) != RI_KEYS_NONE_NULL) if (ri_NullCheck(RelationGetDescr(pk_rel), old_slot, riinfo, true) != RI_KEYS_NONE_NULL)
return false; return false;
/* If all old and new key values are equal, no check is needed */ /* If all old and new key values are equal, no check is needed */
if (new_row && ri_KeysEqual(pk_rel, old_row, new_row, riinfo, true)) if (new_slot && ri_KeysEqual(pk_rel, old_slot, new_slot, riinfo, true))
return false; return false;
/* Else we need to fire the trigger. */ /* Else we need to fire the trigger. */
@ -1587,9 +1592,12 @@ RI_FKey_pk_upd_check_required(Trigger *trigger, Relation pk_rel,
*/ */
bool bool
RI_FKey_fk_upd_check_required(Trigger *trigger, Relation fk_rel, RI_FKey_fk_upd_check_required(Trigger *trigger, Relation fk_rel,
HeapTuple old_row, HeapTuple new_row) TupleTableSlot *old_slot, TupleTableSlot *new_slot)
{ {
const RI_ConstraintInfo *riinfo; const RI_ConstraintInfo *riinfo;
Datum xminDatum;
TransactionId xmin;
bool isnull;
/* /*
* Get arguments. * Get arguments.
@ -1604,7 +1612,7 @@ RI_FKey_fk_upd_check_required(Trigger *trigger, Relation fk_rel,
* If any new key value is NULL, the row must satisfy the * If any new key value is NULL, the row must satisfy the
* constraint, so no check is needed. * constraint, so no check is needed.
*/ */
if (ri_NullCheck(RelationGetDescr(fk_rel), new_row, riinfo, false) != RI_KEYS_NONE_NULL) if (ri_NullCheck(RelationGetDescr(fk_rel), new_slot, riinfo, false) != RI_KEYS_NONE_NULL)
return false; return false;
/* /*
@ -1615,11 +1623,14 @@ RI_FKey_fk_upd_check_required(Trigger *trigger, Relation fk_rel,
* UPDATE check. (We could skip this if we knew the INSERT * UPDATE check. (We could skip this if we knew the INSERT
* trigger already fired, but there is no easy way to know that.) * trigger already fired, but there is no easy way to know that.)
*/ */
if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(old_row->t_data))) xminDatum = slot_getsysattr(old_slot, MinTransactionIdAttributeNumber, &isnull);
Assert(!isnull);
xmin = DatumGetTransactionId(xminDatum);
if (TransactionIdIsCurrentTransactionId(xmin))
return true; return true;
/* If all old and new key values are equal, no check is needed */ /* If all old and new key values are equal, no check is needed */
if (ri_KeysEqual(fk_rel, old_row, new_row, riinfo, false)) if (ri_KeysEqual(fk_rel, old_slot, new_slot, riinfo, false))
return false; return false;
/* Else we need to fire the trigger. */ /* Else we need to fire the trigger. */
@ -1635,7 +1646,7 @@ RI_FKey_fk_upd_check_required(Trigger *trigger, Relation fk_rel,
* invalidated before the constraint is to be checked, but we * invalidated before the constraint is to be checked, but we
* should queue the event to apply the check later. * should queue the event to apply the check later.
*/ */
switch (ri_NullCheck(RelationGetDescr(fk_rel), new_row, riinfo, false)) switch (ri_NullCheck(RelationGetDescr(fk_rel), new_slot, riinfo, false))
{ {
case RI_KEYS_ALL_NULL: case RI_KEYS_ALL_NULL:
return false; return false;
@ -1653,11 +1664,14 @@ RI_FKey_fk_upd_check_required(Trigger *trigger, Relation fk_rel,
* UPDATE check. (We could skip this if we knew the INSERT * UPDATE check. (We could skip this if we knew the INSERT
* trigger already fired, but there is no easy way to know that.) * trigger already fired, but there is no easy way to know that.)
*/ */
if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(old_row->t_data))) xminDatum = slot_getsysattr(old_slot, MinTransactionIdAttributeNumber, &isnull);
Assert(!isnull);
xmin = DatumGetTransactionId(xminDatum);
if (TransactionIdIsCurrentTransactionId(xmin))
return true; return true;
/* If all old and new key values are equal, no check is needed */ /* If all old and new key values are equal, no check is needed */
if (ri_KeysEqual(fk_rel, old_row, new_row, riinfo, false)) if (ri_KeysEqual(fk_rel, old_slot, new_slot, riinfo, false))
return false; return false;
/* Else we need to fire the trigger. */ /* Else we need to fire the trigger. */
@ -1911,10 +1925,17 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
/* Did we find a tuple violating the constraint? */ /* Did we find a tuple violating the constraint? */
if (SPI_processed > 0) if (SPI_processed > 0)
{ {
TupleTableSlot *slot;
HeapTuple tuple = SPI_tuptable->vals[0]; HeapTuple tuple = SPI_tuptable->vals[0];
TupleDesc tupdesc = SPI_tuptable->tupdesc; TupleDesc tupdesc = SPI_tuptable->tupdesc;
RI_ConstraintInfo fake_riinfo; RI_ConstraintInfo fake_riinfo;
slot = MakeSingleTupleTableSlot(tupdesc, &TTSOpsVirtual);
heap_deform_tuple(tuple, tupdesc,
slot->tts_values, slot->tts_isnull);
ExecStoreVirtualTuple(slot);
/* /*
* The columns to look at in the result tuple are 1..N, not whatever * The columns to look at in the result tuple are 1..N, not whatever
* they are in the fk_rel. Hack up riinfo so that the subroutines * they are in the fk_rel. Hack up riinfo so that the subroutines
@ -1934,7 +1955,7 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
* disallows partially-null FK rows. * disallows partially-null FK rows.
*/ */
if (fake_riinfo.confmatchtype == FKCONSTR_MATCH_FULL && if (fake_riinfo.confmatchtype == FKCONSTR_MATCH_FULL &&
ri_NullCheck(tupdesc, tuple, &fake_riinfo, false) != RI_KEYS_NONE_NULL) ri_NullCheck(tupdesc, slot, &fake_riinfo, false) != RI_KEYS_NONE_NULL)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_FOREIGN_KEY_VIOLATION), (errcode(ERRCODE_FOREIGN_KEY_VIOLATION),
errmsg("insert or update on table \"%s\" violates foreign key constraint \"%s\"", errmsg("insert or update on table \"%s\" violates foreign key constraint \"%s\"",
@ -1951,8 +1972,10 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
*/ */
ri_ReportViolation(&fake_riinfo, ri_ReportViolation(&fake_riinfo,
pk_rel, fk_rel, pk_rel, fk_rel,
tuple, tupdesc, slot, tupdesc,
RI_PLAN_CHECK_LOOKUPPK); RI_PLAN_CHECK_LOOKUPPK);
ExecDropSingleTupleTableSlot(slot);
} }
if (SPI_finish() != SPI_OK_FINISH) if (SPI_finish() != SPI_OK_FINISH)
@ -2355,7 +2378,7 @@ static bool
ri_PerformCheck(const RI_ConstraintInfo *riinfo, ri_PerformCheck(const RI_ConstraintInfo *riinfo,
RI_QueryKey *qkey, SPIPlanPtr qplan, RI_QueryKey *qkey, SPIPlanPtr qplan,
Relation fk_rel, Relation pk_rel, Relation fk_rel, Relation pk_rel,
HeapTuple old_tuple, HeapTuple new_tuple, TupleTableSlot *old_slot, TupleTableSlot *new_slot,
bool detectNewRows, int expect_OK) bool detectNewRows, int expect_OK)
{ {
Relation query_rel, Relation query_rel,
@ -2398,17 +2421,17 @@ ri_PerformCheck(const RI_ConstraintInfo *riinfo,
} }
/* Extract the parameters to be passed into the query */ /* Extract the parameters to be passed into the query */
if (new_tuple) if (new_slot)
{ {
ri_ExtractValues(source_rel, new_tuple, riinfo, source_is_pk, ri_ExtractValues(source_rel, new_slot, riinfo, source_is_pk,
vals, nulls); vals, nulls);
if (old_tuple) if (old_slot)
ri_ExtractValues(source_rel, old_tuple, riinfo, source_is_pk, ri_ExtractValues(source_rel, old_slot, riinfo, source_is_pk,
vals + riinfo->nkeys, nulls + riinfo->nkeys); vals + riinfo->nkeys, nulls + riinfo->nkeys);
} }
else else
{ {
ri_ExtractValues(source_rel, old_tuple, riinfo, source_is_pk, ri_ExtractValues(source_rel, old_slot, riinfo, source_is_pk,
vals, nulls); vals, nulls);
} }
@ -2478,7 +2501,7 @@ ri_PerformCheck(const RI_ConstraintInfo *riinfo,
(SPI_processed == 0) == (qkey->constr_queryno == RI_PLAN_CHECK_LOOKUPPK)) (SPI_processed == 0) == (qkey->constr_queryno == RI_PLAN_CHECK_LOOKUPPK))
ri_ReportViolation(riinfo, ri_ReportViolation(riinfo,
pk_rel, fk_rel, pk_rel, fk_rel,
new_tuple ? new_tuple : old_tuple, new_slot ? new_slot : old_slot,
NULL, NULL,
qkey->constr_queryno); qkey->constr_queryno);
@ -2489,11 +2512,10 @@ ri_PerformCheck(const RI_ConstraintInfo *riinfo,
* Extract fields from a tuple into Datum/nulls arrays * Extract fields from a tuple into Datum/nulls arrays
*/ */
static void static void
ri_ExtractValues(Relation rel, HeapTuple tup, ri_ExtractValues(Relation rel, TupleTableSlot *slot,
const RI_ConstraintInfo *riinfo, bool rel_is_pk, const RI_ConstraintInfo *riinfo, bool rel_is_pk,
Datum *vals, char *nulls) Datum *vals, char *nulls)
{ {
TupleDesc tupdesc = rel->rd_att;
const int16 *attnums; const int16 *attnums;
int i; int i;
bool isnull; bool isnull;
@ -2505,8 +2527,7 @@ ri_ExtractValues(Relation rel, HeapTuple tup,
for (i = 0; i < riinfo->nkeys; i++) for (i = 0; i < riinfo->nkeys; i++)
{ {
vals[i] = heap_getattr(tup, attnums[i], tupdesc, vals[i] = slot_getattr(slot, attnums[i], &isnull);
&isnull);
nulls[i] = isnull ? 'n' : ' '; nulls[i] = isnull ? 'n' : ' ';
} }
} }
@ -2523,7 +2544,7 @@ ri_ExtractValues(Relation rel, HeapTuple tup,
static void static void
ri_ReportViolation(const RI_ConstraintInfo *riinfo, ri_ReportViolation(const RI_ConstraintInfo *riinfo,
Relation pk_rel, Relation fk_rel, Relation pk_rel, Relation fk_rel,
HeapTuple violator, TupleDesc tupdesc, TupleTableSlot *violatorslot, TupleDesc tupdesc,
int queryno) int queryno)
{ {
StringInfoData key_names; StringInfoData key_names;
@ -2598,12 +2619,24 @@ ri_ReportViolation(const RI_ConstraintInfo *riinfo,
for (idx = 0; idx < riinfo->nkeys; idx++) for (idx = 0; idx < riinfo->nkeys; idx++)
{ {
int fnum = attnums[idx]; int fnum = attnums[idx];
Form_pg_attribute att = TupleDescAttr(tupdesc, fnum - 1);
char *name, char *name,
*val; *val;
Datum datum;
bool isnull;
name = SPI_fname(tupdesc, fnum); name = NameStr(att->attname);
val = SPI_getvalue(violator, tupdesc, fnum);
if (!val) datum = slot_getattr(violatorslot, fnum, &isnull);
if (!isnull)
{
Oid foutoid;
bool typisvarlena;
getTypeOutputInfo(att->atttypid, &foutoid, &typisvarlena);
val = OidOutputFunctionCall(foutoid, datum);
}
else
val = "null"; val = "null";
if (idx > 0) if (idx > 0)
@ -2656,7 +2689,7 @@ ri_ReportViolation(const RI_ConstraintInfo *riinfo,
*/ */
static int static int
ri_NullCheck(TupleDesc tupDesc, ri_NullCheck(TupleDesc tupDesc,
HeapTuple tup, TupleTableSlot *slot,
const RI_ConstraintInfo *riinfo, bool rel_is_pk) const RI_ConstraintInfo *riinfo, bool rel_is_pk)
{ {
const int16 *attnums; const int16 *attnums;
@ -2671,7 +2704,7 @@ ri_NullCheck(TupleDesc tupDesc,
for (i = 0; i < riinfo->nkeys; i++) for (i = 0; i < riinfo->nkeys; i++)
{ {
if (heap_attisnull(tup, attnums[i], tupDesc)) if (slot_attisnull(slot, attnums[i]))
nonenull = false; nonenull = false;
else else
allnull = false; allnull = false;
@ -2822,10 +2855,9 @@ ri_HashPreparedPlan(RI_QueryKey *key, SPIPlanPtr plan)
* ---------- * ----------
*/ */
static bool static bool
ri_KeysEqual(Relation rel, HeapTuple oldtup, HeapTuple newtup, ri_KeysEqual(Relation rel, TupleTableSlot *oldslot, TupleTableSlot *newslot,
const RI_ConstraintInfo *riinfo, bool rel_is_pk) const RI_ConstraintInfo *riinfo, bool rel_is_pk)
{ {
TupleDesc tupdesc = RelationGetDescr(rel);
const int16 *attnums; const int16 *attnums;
const Oid *eq_oprs; const Oid *eq_oprs;
int i; int i;
@ -2841,6 +2873,7 @@ ri_KeysEqual(Relation rel, HeapTuple oldtup, HeapTuple newtup,
eq_oprs = riinfo->ff_eq_oprs; eq_oprs = riinfo->ff_eq_oprs;
} }
/* XXX: could be worthwhile to fetch all necessary attrs at once */
for (i = 0; i < riinfo->nkeys; i++) for (i = 0; i < riinfo->nkeys; i++)
{ {
Datum oldvalue; Datum oldvalue;
@ -2850,14 +2883,14 @@ ri_KeysEqual(Relation rel, HeapTuple oldtup, HeapTuple newtup,
/* /*
* Get one attribute's oldvalue. If it is NULL - they're not equal. * Get one attribute's oldvalue. If it is NULL - they're not equal.
*/ */
oldvalue = heap_getattr(oldtup, attnums[i], tupdesc, &isnull); oldvalue = slot_getattr(oldslot, attnums[i], &isnull);
if (isnull) if (isnull)
return false; return false;
/* /*
* Get one attribute's newvalue. If it is NULL - they're not equal. * Get one attribute's newvalue. If it is NULL - they're not equal.
*/ */
newvalue = heap_getattr(newtup, attnums[i], tupdesc, &isnull); newvalue = slot_getattr(newslot, attnums[i], &isnull);
if (isnull) if (isnull)
return false; return false;

View File

@ -35,8 +35,8 @@ typedef struct TriggerData
HeapTuple tg_trigtuple; HeapTuple tg_trigtuple;
HeapTuple tg_newtuple; HeapTuple tg_newtuple;
Trigger *tg_trigger; Trigger *tg_trigger;
Buffer tg_trigtuplebuf; TupleTableSlot *tg_trigslot;
Buffer tg_newtuplebuf; TupleTableSlot *tg_newslot;
Tuplestorestate *tg_oldtable; Tuplestorestate *tg_oldtable;
Tuplestorestate *tg_newtable; Tuplestorestate *tg_newtable;
} TriggerData; } TriggerData;
@ -77,9 +77,9 @@ typedef struct TransitionCaptureState
* format to parent format after they have already been converted in the * format to parent format after they have already been converted in the
* opposite direction during routing. In that case we bypass conversion * opposite direction during routing. In that case we bypass conversion
* and allow the inserting code (copy.c and nodeModifyTable.c) to provide * and allow the inserting code (copy.c and nodeModifyTable.c) to provide
* the original tuple directly. * a slot containing the original tuple directly.
*/ */
HeapTuple tcs_original_insert_tuple; TupleTableSlot *tcs_original_insert_tuple;
/* /*
* Private data including the tuplestore(s) into which to insert tuples. * Private data including the tuplestore(s) into which to insert tuples.
@ -186,15 +186,15 @@ extern void ExecBSInsertTriggers(EState *estate,
extern void ExecASInsertTriggers(EState *estate, extern void ExecASInsertTriggers(EState *estate,
ResultRelInfo *relinfo, ResultRelInfo *relinfo,
TransitionCaptureState *transition_capture); TransitionCaptureState *transition_capture);
extern TupleTableSlot *ExecBRInsertTriggers(EState *estate, extern bool ExecBRInsertTriggers(EState *estate,
ResultRelInfo *relinfo, ResultRelInfo *relinfo,
TupleTableSlot *slot); TupleTableSlot *slot);
extern void ExecARInsertTriggers(EState *estate, extern void ExecARInsertTriggers(EState *estate,
ResultRelInfo *relinfo, ResultRelInfo *relinfo,
HeapTuple trigtuple, TupleTableSlot *slot,
List *recheckIndexes, List *recheckIndexes,
TransitionCaptureState *transition_capture); TransitionCaptureState *transition_capture);
extern TupleTableSlot *ExecIRInsertTriggers(EState *estate, extern bool ExecIRInsertTriggers(EState *estate,
ResultRelInfo *relinfo, ResultRelInfo *relinfo,
TupleTableSlot *slot); TupleTableSlot *slot);
extern void ExecBSDeleteTriggers(EState *estate, extern void ExecBSDeleteTriggers(EState *estate,
@ -221,7 +221,7 @@ extern void ExecBSUpdateTriggers(EState *estate,
extern void ExecASUpdateTriggers(EState *estate, extern void ExecASUpdateTriggers(EState *estate,
ResultRelInfo *relinfo, ResultRelInfo *relinfo,
TransitionCaptureState *transition_capture); TransitionCaptureState *transition_capture);
extern TupleTableSlot *ExecBRUpdateTriggers(EState *estate, extern bool ExecBRUpdateTriggers(EState *estate,
EPQState *epqstate, EPQState *epqstate,
ResultRelInfo *relinfo, ResultRelInfo *relinfo,
ItemPointer tupleid, ItemPointer tupleid,
@ -231,10 +231,10 @@ extern void ExecARUpdateTriggers(EState *estate,
ResultRelInfo *relinfo, ResultRelInfo *relinfo,
ItemPointer tupleid, ItemPointer tupleid,
HeapTuple fdw_trigtuple, HeapTuple fdw_trigtuple,
HeapTuple newtuple, TupleTableSlot *slot,
List *recheckIndexes, List *recheckIndexes,
TransitionCaptureState *transition_capture); TransitionCaptureState *transition_capture);
extern TupleTableSlot *ExecIRUpdateTriggers(EState *estate, extern bool ExecIRUpdateTriggers(EState *estate,
ResultRelInfo *relinfo, ResultRelInfo *relinfo,
HeapTuple trigtuple, HeapTuple trigtuple,
TupleTableSlot *slot); TupleTableSlot *slot);
@ -258,9 +258,9 @@ extern bool AfterTriggerPendingOnRel(Oid relid);
* in utils/adt/ri_triggers.c * in utils/adt/ri_triggers.c
*/ */
extern bool RI_FKey_pk_upd_check_required(Trigger *trigger, Relation pk_rel, extern bool RI_FKey_pk_upd_check_required(Trigger *trigger, Relation pk_rel,
HeapTuple old_row, HeapTuple new_row); TupleTableSlot *old_slot, TupleTableSlot *new_slot);
extern bool RI_FKey_fk_upd_check_required(Trigger *trigger, Relation fk_rel, extern bool RI_FKey_fk_upd_check_required(Trigger *trigger, Relation fk_rel,
HeapTuple old_row, HeapTuple new_row); TupleTableSlot *old_slot, TupleTableSlot *new_slot);
extern bool RI_Initial_Check(Trigger *trigger, extern bool RI_Initial_Check(Trigger *trigger,
Relation fk_rel, Relation pk_rel); Relation fk_rel, Relation pk_rel);

View File

@ -560,6 +560,10 @@ extern Datum GetAttributeByNum(HeapTupleHeader tuple, AttrNumber attrno,
extern int ExecTargetListLength(List *targetlist); extern int ExecTargetListLength(List *targetlist);
extern int ExecCleanTargetListLength(List *targetlist); extern int ExecCleanTargetListLength(List *targetlist);
extern TupleTableSlot *ExecGetTriggerOldSlot(EState *estate, ResultRelInfo *relInfo);
extern TupleTableSlot *ExecGetTriggerNewSlot(EState *estate, ResultRelInfo *relInfo);
extern TupleTableSlot *ExecGetReturningSlot(EState *estate, ResultRelInfo *relInfo);
/* /*
* prototypes from functions in execIndexing.c * prototypes from functions in execIndexing.c
*/ */

View File

@ -427,6 +427,11 @@ typedef struct ResultRelInfo
/* optional runtime measurements for triggers */ /* optional runtime measurements for triggers */
Instrumentation *ri_TrigInstrument; Instrumentation *ri_TrigInstrument;
/* On-demand created slots for triggers / returning processing */
TupleTableSlot *ri_ReturningSlot; /* for trigger output tuples */
TupleTableSlot *ri_TrigOldSlot; /* for a trigger's old tuple */
TupleTableSlot *ri_TrigNewSlot; /* for a trigger's new tuple */
/* FDW callback functions, if foreign table */ /* FDW callback functions, if foreign table */
struct FdwRoutine *ri_FdwRoutine; struct FdwRoutine *ri_FdwRoutine;
@ -524,9 +529,6 @@ typedef struct EState
/* Stuff used for firing triggers: */ /* Stuff used for firing triggers: */
List *es_trig_target_relations; /* trigger-only ResultRelInfos */ List *es_trig_target_relations; /* trigger-only ResultRelInfos */
TupleTableSlot *es_trig_tuple_slot; /* for trigger output tuples */
TupleTableSlot *es_trig_oldtup_slot; /* for TriggerEnabled */
TupleTableSlot *es_trig_newtup_slot; /* for TriggerEnabled */
/* Parameter info: */ /* Parameter info: */
ParamListInfo es_param_list_info; /* values of external params */ ParamListInfo es_param_list_info; /* values of external params */