summaryrefslogtreecommitdiff
path: root/src/backend/commands/trigger.c
diff options
context:
space:
mode:
authorMichael Paquier <michael@paquier.xyz>2025-02-13 16:31:11 +0900
committerMichael Paquier <michael@paquier.xyz>2025-02-13 16:31:11 +0900
commit8e58f8024d0a70b3605da664cf35debf88b84e33 (patch)
tree900862466c09110cdb85c65b018c424b12217d46 /src/backend/commands/trigger.c
parent80b23bf5b858d54c31c3231e480c770de401b7a5 (diff)
Fix MakeTransitionCaptureState() to return a consistent result
When an UPDATE trigger referencing a new table and a DELETE trigger referencing an old table are both present, MakeTransitionCaptureState() returns an inconsistent result for UPDATE commands in its set of flags and tuplestores holding the TransitionCaptureState for transition tables. As proved by the test added here, this issue causes a crash in v14 and earlier versions (down to 11, actually, older versions do not support triggers on partitioned tables) during cross-partition updates on a partitioned table. v15 and newer versions are safe thanks to 7103ebb7aae8. This commit fixes the function so that it returns a consistent state by using portions of the changes made in commit 7103ebb7aae8 for v13 and v14. v15 and newer versions are slightly tweaked to match with the older versions, mainly for consistency across branches. Author: Kyotaro Horiguchi Discussion: https://postgr.es/m/20250207.150238.968446820828052276.horikyota.ntt@gmail.com Backpatch-through: 13
Diffstat (limited to 'src/backend/commands/trigger.c')
-rw-r--r--src/backend/commands/trigger.c36
1 files changed, 20 insertions, 16 deletions
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index d04b4beed91..10cfd59aa52 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -4511,8 +4511,10 @@ TransitionCaptureState *
MakeTransitionCaptureState(TriggerDesc *trigdesc, Oid relid, CmdType cmdType)
{
TransitionCaptureState *state;
- bool need_old,
- need_new;
+ bool need_old_upd,
+ need_new_upd,
+ need_old_del,
+ need_new_ins;
AfterTriggersTableData *table;
MemoryContext oldcxt;
ResourceOwner saveResourceOwner;
@@ -4524,23 +4526,25 @@ MakeTransitionCaptureState(TriggerDesc *trigdesc, Oid relid, CmdType cmdType)
switch (cmdType)
{
case CMD_INSERT:
- need_old = false;
- need_new = trigdesc->trig_insert_new_table;
+ need_old_upd = need_old_del = need_new_upd = false;
+ need_new_ins = trigdesc->trig_insert_new_table;
break;
case CMD_UPDATE:
- need_old = trigdesc->trig_update_old_table;
- need_new = trigdesc->trig_update_new_table;
+ need_old_upd = trigdesc->trig_update_old_table;
+ need_new_upd = trigdesc->trig_update_new_table;
+ need_old_del = need_new_ins = false;
break;
case CMD_DELETE:
- need_old = trigdesc->trig_delete_old_table;
- need_new = false;
+ need_old_del = trigdesc->trig_delete_old_table;
+ need_old_upd = need_new_upd = need_new_ins = false;
break;
default:
elog(ERROR, "unexpected CmdType: %d", (int) cmdType);
- need_old = need_new = false; /* keep compiler quiet */
+ /* keep compiler quiet */
+ need_old_upd = need_new_upd = need_old_del = need_new_ins = false;
break;
}
- if (!need_old && !need_new)
+ if (!need_old_upd && !need_new_upd && !need_new_ins && !need_old_del)
return NULL;
/* Check state, like AfterTriggerSaveEvent. */
@@ -4570,9 +4574,9 @@ MakeTransitionCaptureState(TriggerDesc *trigdesc, Oid relid, CmdType cmdType)
saveResourceOwner = CurrentResourceOwner;
CurrentResourceOwner = CurTransactionResourceOwner;
- if (need_old && table->old_tuplestore == NULL)
+ if ((need_old_upd || need_old_del) && table->old_tuplestore == NULL)
table->old_tuplestore = tuplestore_begin_heap(false, false, work_mem);
- if (need_new && table->new_tuplestore == NULL)
+ if ((need_new_upd || need_new_ins) && table->new_tuplestore == NULL)
table->new_tuplestore = tuplestore_begin_heap(false, false, work_mem);
CurrentResourceOwner = saveResourceOwner;
@@ -4580,10 +4584,10 @@ MakeTransitionCaptureState(TriggerDesc *trigdesc, Oid relid, CmdType cmdType)
/* Now build the TransitionCaptureState struct, in caller's context */
state = (TransitionCaptureState *) palloc0(sizeof(TransitionCaptureState));
- state->tcs_delete_old_table = trigdesc->trig_delete_old_table;
- state->tcs_update_old_table = trigdesc->trig_update_old_table;
- state->tcs_update_new_table = trigdesc->trig_update_new_table;
- state->tcs_insert_new_table = trigdesc->trig_insert_new_table;
+ state->tcs_delete_old_table = need_old_del;
+ state->tcs_update_old_table = need_old_upd;
+ state->tcs_update_new_table = need_new_upd;
+ state->tcs_insert_new_table = need_new_ins;
state->tcs_private = table;
return state;