diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c index e6aa36a9d8..c2a643e5a8 100644 --- a/src/backend/commands/trigger.c +++ b/src/backend/commands/trigger.c @@ -94,15 +94,14 @@ static HeapTuple ExecCallTriggerFunc(TriggerData *trigdata, FmgrInfo *finfo, Instrumentation *instr, MemoryContext per_tuple_context); -static void AfterTriggerSaveEvent(EState *estate, - ModifyTableState *mtstate, - ResultRelInfo *relinfo, +static void AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo, ResultRelInfo *src_partinfo, ResultRelInfo *dst_partinfo, int event, bool row_trigger, TupleTableSlot *oldtup, TupleTableSlot *newtup, List *recheckIndexes, Bitmapset *modifiedCols, - TransitionCaptureState *transition_capture); + TransitionCaptureState *transition_capture, + bool is_crosspart_update); static void AfterTriggerEnlargeQueryState(void); static bool before_stmt_triggers_fired(Oid relid, CmdType cmdType); @@ -2462,10 +2461,10 @@ ExecASInsertTriggers(EState *estate, ResultRelInfo *relinfo, TriggerDesc *trigdesc = relinfo->ri_TrigDesc; if (trigdesc && trigdesc->trig_insert_after_statement) - AfterTriggerSaveEvent(estate, NULL, relinfo, - NULL, NULL, + AfterTriggerSaveEvent(estate, relinfo, NULL, NULL, TRIGGER_EVENT_INSERT, - false, NULL, NULL, NIL, NULL, transition_capture); + false, NULL, NULL, NIL, NULL, transition_capture, + false); } bool @@ -2553,12 +2552,12 @@ ExecARInsertTriggers(EState *estate, ResultRelInfo *relinfo, if ((trigdesc && trigdesc->trig_insert_after_row) || (transition_capture && transition_capture->tcs_insert_new_table)) - AfterTriggerSaveEvent(estate, NULL, relinfo, - NULL, NULL, + AfterTriggerSaveEvent(estate, relinfo, NULL, NULL, TRIGGER_EVENT_INSERT, true, NULL, slot, recheckIndexes, NULL, - transition_capture); + transition_capture, + false); } bool @@ -2680,10 +2679,10 @@ ExecASDeleteTriggers(EState *estate, ResultRelInfo *relinfo, TriggerDesc *trigdesc = relinfo->ri_TrigDesc; if (trigdesc && trigdesc->trig_delete_after_statement) - AfterTriggerSaveEvent(estate, NULL, relinfo, - NULL, NULL, + AfterTriggerSaveEvent(estate, relinfo, NULL, NULL, TRIGGER_EVENT_DELETE, - false, NULL, NULL, NIL, NULL, transition_capture); + false, NULL, NULL, NIL, NULL, transition_capture, + false); } /* @@ -2778,12 +2777,17 @@ ExecBRDeleteTriggers(EState *estate, EPQState *epqstate, return result; } +/* + * Note: is_crosspart_update must be true if the DELETE is being performed + * as part of a cross-partition update. + */ void -ExecARDeleteTriggers(EState *estate, ModifyTableState *mtstate, +ExecARDeleteTriggers(EState *estate, ResultRelInfo *relinfo, ItemPointer tupleid, HeapTuple fdw_trigtuple, - TransitionCaptureState *transition_capture) + TransitionCaptureState *transition_capture, + bool is_crosspart_update) { TriggerDesc *trigdesc = relinfo->ri_TrigDesc; @@ -2804,11 +2808,11 @@ ExecARDeleteTriggers(EState *estate, ModifyTableState *mtstate, else ExecForceStoreHeapTuple(fdw_trigtuple, slot, false); - AfterTriggerSaveEvent(estate, mtstate, relinfo, - NULL, NULL, + AfterTriggerSaveEvent(estate, relinfo, NULL, NULL, TRIGGER_EVENT_DELETE, true, slot, NULL, NIL, NULL, - transition_capture); + transition_capture, + is_crosspart_update); } } @@ -2927,12 +2931,12 @@ ExecASUpdateTriggers(EState *estate, ResultRelInfo *relinfo, Assert(relinfo->ri_RootResultRelInfo == NULL); if (trigdesc && trigdesc->trig_update_after_statement) - AfterTriggerSaveEvent(estate, NULL, relinfo, - NULL, NULL, + AfterTriggerSaveEvent(estate, relinfo, NULL, NULL, TRIGGER_EVENT_UPDATE, false, NULL, NULL, NIL, ExecGetAllUpdatedCols(relinfo, estate), - transition_capture); + transition_capture, + false); } bool @@ -3068,24 +3072,25 @@ ExecBRUpdateTriggers(EState *estate, EPQState *epqstate, } /* - * 'src_partinfo' and 'dst_partinfo', when non-NULL, refer to the source and - * destination partitions, respectively, of a cross-partition update of the - * root partitioned table mentioned in the query, given by 'relinfo'. + * Note: 'src_partinfo' and 'dst_partinfo', when non-NULL, refer to the source + * and destination partitions, respectively, of a cross-partition update of + * the root partitioned table mentioned in the query, given by 'relinfo'. * 'tupleid' in that case refers to the ctid of the "old" tuple in the source * partition, and 'newslot' contains the "new" tuple in the destination * partition. This interface allows to support the requirements of - * ExecCrossPartitionUpdateForeignKey(). + * ExecCrossPartitionUpdateForeignKey(); is_crosspart_update must be true in + * that case. */ void -ExecARUpdateTriggers(EState *estate, ModifyTableState *mtstate, - ResultRelInfo *relinfo, +ExecARUpdateTriggers(EState *estate, ResultRelInfo *relinfo, ResultRelInfo *src_partinfo, ResultRelInfo *dst_partinfo, ItemPointer tupleid, HeapTuple fdw_trigtuple, TupleTableSlot *newslot, List *recheckIndexes, - TransitionCaptureState *transition_capture) + TransitionCaptureState *transition_capture, + bool is_crosspart_update) { TriggerDesc *trigdesc = relinfo->ri_TrigDesc; @@ -3103,6 +3108,9 @@ ExecARUpdateTriggers(EState *estate, ModifyTableState *mtstate, TupleTableSlot *oldslot; ResultRelInfo *tupsrc; + Assert((src_partinfo != NULL && dst_partinfo != NULL) || + !is_crosspart_update); + tupsrc = src_partinfo ? src_partinfo : relinfo; oldslot = ExecGetTriggerOldSlot(estate, tupsrc); @@ -3119,12 +3127,14 @@ ExecARUpdateTriggers(EState *estate, ModifyTableState *mtstate, else ExecClearTuple(oldslot); - AfterTriggerSaveEvent(estate, mtstate, relinfo, + AfterTriggerSaveEvent(estate, relinfo, src_partinfo, dst_partinfo, TRIGGER_EVENT_UPDATE, - true, oldslot, newslot, recheckIndexes, + true, + oldslot, newslot, recheckIndexes, ExecGetAllUpdatedCols(relinfo, estate), - transition_capture); + transition_capture, + is_crosspart_update); } } @@ -3247,10 +3257,11 @@ ExecASTruncateTriggers(EState *estate, ResultRelInfo *relinfo) TriggerDesc *trigdesc = relinfo->ri_TrigDesc; if (trigdesc && trigdesc->trig_truncate_after_statement) - AfterTriggerSaveEvent(estate, NULL, relinfo, + AfterTriggerSaveEvent(estate, relinfo, NULL, NULL, TRIGGER_EVENT_TRUNCATE, - false, NULL, NULL, NIL, NULL, NULL); + false, NULL, NULL, NIL, NULL, NULL, + false); } @@ -5829,24 +5840,26 @@ AfterTriggerPendingOnRel(Oid relid) * UPDATE; in this case, this function is called with relinfo as the * partitioned table, and src_partinfo and dst_partinfo referring to the * source and target leaf partitions, respectively. + * + * is_crosspart_update is true either when a DELETE event is fired on the + * source partition (which is to be ignored) or an UPDATE event is fired on + * the root partitioned table. * ---------- */ static void -AfterTriggerSaveEvent(EState *estate, ModifyTableState *mtstate, - ResultRelInfo *relinfo, +AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo, ResultRelInfo *src_partinfo, ResultRelInfo *dst_partinfo, int event, bool row_trigger, TupleTableSlot *oldslot, TupleTableSlot *newslot, List *recheckIndexes, Bitmapset *modifiedCols, - TransitionCaptureState *transition_capture) + TransitionCaptureState *transition_capture, + bool is_crosspart_update) { Relation rel = relinfo->ri_RelationDesc; - Relation rootRel; TriggerDesc *trigdesc = relinfo->ri_TrigDesc; AfterTriggerEventData new_event; AfterTriggerSharedData new_shared; - bool maybe_crosspart_update; char relkind = rel->rd_rel->relkind; int tgtype_event; int tgtype_level; @@ -5957,16 +5970,9 @@ AfterTriggerSaveEvent(EState *estate, ModifyTableState *mtstate, * queue an update event on the root target partitioned table, also * passing the source and destination partitions and their tuples. */ - rootRel = relinfo->ri_RootResultRelInfo ? - relinfo->ri_RootResultRelInfo->ri_RelationDesc : NULL; - maybe_crosspart_update = - (row_trigger && mtstate && - mtstate->operation == CMD_UPDATE && - (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE || - (rootRel && rootRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE))); Assert(!row_trigger || rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE || - (maybe_crosspart_update && + (is_crosspart_update && TRIGGER_FIRED_BY_UPDATE(event) && src_partinfo != NULL && dst_partinfo != NULL)); @@ -6161,7 +6167,7 @@ AfterTriggerSaveEvent(EState *estate, ModifyTableState *mtstate, * (partitioned) target table will be used to perform the * necessary foreign key enforcement action. */ - if (maybe_crosspart_update && + if (is_crosspart_update && TRIGGER_FIRED_BY_DELETE(event) && trigger->tgisclone) continue; diff --git a/src/backend/executor/execReplication.c b/src/backend/executor/execReplication.c index e2a338ba33..f978e28ba9 100644 --- a/src/backend/executor/execReplication.c +++ b/src/backend/executor/execReplication.c @@ -516,10 +516,10 @@ ExecSimpleRelationUpdate(ResultRelInfo *resultRelInfo, NULL, NIL); /* AFTER ROW UPDATE Triggers */ - ExecARUpdateTriggers(estate, NULL, resultRelInfo, + ExecARUpdateTriggers(estate, resultRelInfo, NULL, NULL, tid, NULL, slot, - recheckIndexes, NULL); + recheckIndexes, NULL, false); list_free(recheckIndexes); } @@ -557,8 +557,8 @@ ExecSimpleRelationDelete(ResultRelInfo *resultRelInfo, simple_table_tuple_delete(rel, tid, estate->es_snapshot); /* AFTER ROW DELETE Triggers */ - ExecARDeleteTriggers(estate, NULL, resultRelInfo, - tid, NULL, NULL); + ExecARDeleteTriggers(estate, resultRelInfo, + tid, NULL, NULL, false); } } diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c index 6a16d0e673..204126a29f 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -962,13 +962,14 @@ ExecInsert(ModifyTableState *mtstate, if (mtstate->operation == CMD_UPDATE && mtstate->mt_transition_capture && mtstate->mt_transition_capture->tcs_update_new_table) { - ExecARUpdateTriggers(estate, mtstate, resultRelInfo, + ExecARUpdateTriggers(estate, resultRelInfo, NULL, NULL, NULL, NULL, slot, NULL, - mtstate->mt_transition_capture); + mtstate->mt_transition_capture, + false); /* * We've already captured the NEW TABLE row, so make sure any AR @@ -1359,13 +1360,14 @@ ldelete:; if (mtstate->operation == CMD_UPDATE && mtstate->mt_transition_capture && mtstate->mt_transition_capture->tcs_update_old_table) { - ExecARUpdateTriggers(estate, mtstate, resultRelInfo, + ExecARUpdateTriggers(estate, resultRelInfo, NULL, NULL, tupleid, oldtuple, NULL, NULL, - mtstate->mt_transition_capture); + mtstate->mt_transition_capture, + false); /* * We've already captured the NEW TABLE row, so make sure any AR @@ -1375,8 +1377,8 @@ ldelete:; } /* AFTER ROW DELETE Triggers */ - ExecARDeleteTriggers(estate, mtstate, resultRelInfo, tupleid, oldtuple, - ar_delete_trig_tcs); + ExecARDeleteTriggers(estate, resultRelInfo, tupleid, oldtuple, + ar_delete_trig_tcs, changingPart); /* Process RETURNING if present and if requested */ if (processReturning && resultRelInfo->ri_projectReturning) @@ -1642,13 +1644,13 @@ GetAncestorResultRels(ResultRelInfo *resultRelInfo) * keys pointing into it. */ static void -ExecCrossPartitionUpdateForeignKey(ResultRelInfo *sourcePartInfo, +ExecCrossPartitionUpdateForeignKey(ModifyTableState *mtstate, + EState *estate, + ResultRelInfo *sourcePartInfo, ResultRelInfo *destPartInfo, ItemPointer tupleid, TupleTableSlot *oldslot, - TupleTableSlot *newslot, - ModifyTableState *mtstate, - EState *estate) + TupleTableSlot *newslot) { ListCell *lc; ResultRelInfo *rootRelInfo = sourcePartInfo->ri_RootResultRelInfo; @@ -1699,10 +1701,9 @@ ExecCrossPartitionUpdateForeignKey(ResultRelInfo *sourcePartInfo, } /* Perform the root table's triggers. */ - ExecARUpdateTriggers(estate, mtstate, rootRelInfo, - sourcePartInfo, destPartInfo, - tupleid, NULL, - newslot, NIL, NULL); + ExecARUpdateTriggers(estate, + rootRelInfo, sourcePartInfo, destPartInfo, + tupleid, NULL, newslot, NIL, NULL, true); } /* ---------------------------------------------------------------- @@ -1920,11 +1921,11 @@ lreplace:; if (insert_destrel && resultRelInfo->ri_TrigDesc && resultRelInfo->ri_TrigDesc->trig_update_after_row) - ExecCrossPartitionUpdateForeignKey(resultRelInfo, + ExecCrossPartitionUpdateForeignKey(mtstate, estate, + resultRelInfo, insert_destrel, tupleid, oldslot, - inserted_tuple, - mtstate, estate); + inserted_tuple); return returning_slot; } @@ -2105,14 +2106,15 @@ lreplace:; (estate->es_processed)++; /* AFTER ROW UPDATE Triggers */ - ExecARUpdateTriggers(estate, mtstate, resultRelInfo, + ExecARUpdateTriggers(estate, resultRelInfo, NULL, NULL, tupleid, oldtuple, slot, recheckIndexes, mtstate->operation == CMD_INSERT ? mtstate->mt_oc_transition_capture : - mtstate->mt_transition_capture); + mtstate->mt_transition_capture, + false); list_free(recheckIndexes); diff --git a/src/include/commands/trigger.h b/src/include/commands/trigger.h index 1ba3a54499..66bf6c16e3 100644 --- a/src/include/commands/trigger.h +++ b/src/include/commands/trigger.h @@ -211,11 +211,11 @@ extern bool ExecBRDeleteTriggers(EState *estate, HeapTuple fdw_trigtuple, TupleTableSlot **epqslot); extern void ExecARDeleteTriggers(EState *estate, - ModifyTableState *mtstate, ResultRelInfo *relinfo, ItemPointer tupleid, HeapTuple fdw_trigtuple, - TransitionCaptureState *transition_capture); + TransitionCaptureState *transition_capture, + bool is_crosspart_update); extern bool ExecIRDeleteTriggers(EState *estate, ResultRelInfo *relinfo, HeapTuple trigtuple); @@ -231,7 +231,6 @@ extern bool ExecBRUpdateTriggers(EState *estate, HeapTuple fdw_trigtuple, TupleTableSlot *slot); extern void ExecARUpdateTriggers(EState *estate, - ModifyTableState *mtstate, ResultRelInfo *relinfo, ResultRelInfo *src_partinfo, ResultRelInfo *dst_partinfo, @@ -239,7 +238,8 @@ extern void ExecARUpdateTriggers(EState *estate, HeapTuple fdw_trigtuple, TupleTableSlot *slot, List *recheckIndexes, - TransitionCaptureState *transition_capture); + TransitionCaptureState *transition_capture, + bool is_crosspart_update); extern bool ExecIRUpdateTriggers(EState *estate, ResultRelInfo *relinfo, HeapTuple trigtuple, diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 6c7eef1e54..37ad2b7663 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -530,7 +530,10 @@ typedef struct ResultRelInfo /* for use by copyfrom.c when performing multi-inserts */ struct CopyMultiInsertBuffer *ri_CopyMultiInsertBuffer; - /* Used during cross-partition updates on partitioned tables. */ + /* + * Used when a leaf partition is involved in a cross-partition update of + * one of its ancestors; see ExecCrossPartitionUpdateForeignKey(). + */ List *ri_ancestorResultRels; } ResultRelInfo;