diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c index 2ee4d0720ac..8ecd9df2078 100644 --- a/src/backend/commands/trigger.c +++ b/src/backend/commands/trigger.c @@ -4413,8 +4413,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; @@ -4426,23 +4428,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. */ @@ -4472,9 +4476,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; @@ -4482,10 +4486,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; diff --git a/src/test/regress/expected/triggers.out b/src/test/regress/expected/triggers.out index 0e027decbb4..a3211056481 100644 --- a/src/test/regress/expected/triggers.out +++ b/src/test/regress/expected/triggers.out @@ -2988,6 +2988,55 @@ drop trigger child_row_trig on child; alter table parent attach partition child for values in ('AAA'); drop table child, parent; -- +-- Verify access of transition tables with UPDATE triggers and tuples +-- moved across partitions. +-- +create or replace function dump_update_new() returns trigger language plpgsql as +$$ + begin + raise notice 'trigger = %, new table = %', TG_NAME, + (select string_agg(new_table::text, ', ' order by a) from new_table); + return null; + end; +$$; +create or replace function dump_update_old() returns trigger language plpgsql as +$$ + begin + raise notice 'trigger = %, old table = %', TG_NAME, + (select string_agg(old_table::text, ', ' order by a) from old_table); + return null; + end; +$$; +create table trans_tab_parent (a text) partition by list (a); +create table trans_tab_child1 partition of trans_tab_parent for values in ('AAA1', 'AAA2'); +create table trans_tab_child2 partition of trans_tab_parent for values in ('BBB1', 'BBB2'); +create trigger trans_tab_parent_update_trig + after update on trans_tab_parent referencing old table as old_table + for each statement execute procedure dump_update_old(); +create trigger trans_tab_parent_insert_trig + after insert on trans_tab_parent referencing new table as new_table + for each statement execute procedure dump_insert(); +create trigger trans_tab_parent_delete_trig + after delete on trans_tab_parent referencing old table as old_table + for each statement execute procedure dump_delete(); +insert into trans_tab_parent values ('AAA1'), ('BBB1'); +NOTICE: trigger = trans_tab_parent_insert_trig, new table = (AAA1), (BBB1) +-- should not trigger access to new table when moving across partitions. +update trans_tab_parent set a = 'BBB2' where a = 'AAA1'; +NOTICE: trigger = trans_tab_parent_update_trig, old table = (AAA1) +drop trigger trans_tab_parent_update_trig on trans_tab_parent; +create trigger trans_tab_parent_update_trig + after update on trans_tab_parent referencing new table as new_table + for each statement execute procedure dump_update_new(); +-- should not trigger access to old table when moving across partitions. +update trans_tab_parent set a = 'AAA2' where a = 'BBB1'; +NOTICE: trigger = trans_tab_parent_update_trig, new table = (AAA2) +delete from trans_tab_parent; +NOTICE: trigger = trans_tab_parent_delete_trig, old table = (AAA2), (BBB2) +-- clean up +drop table trans_tab_parent, trans_tab_child1, trans_tab_child2; +drop function dump_update_new, dump_update_old; +-- -- Verify behavior of statement triggers on (non-partition) -- inheritance hierarchy with transition tables; similar to the -- partition case, except there is no rerouting on insertion and child diff --git a/src/test/regress/sql/triggers.sql b/src/test/regress/sql/triggers.sql index 605d5cf05ee..e12a8d27023 100644 --- a/src/test/regress/sql/triggers.sql +++ b/src/test/regress/sql/triggers.sql @@ -2115,6 +2115,52 @@ alter table parent attach partition child for values in ('AAA'); drop table child, parent; +-- +-- Verify access of transition tables with UPDATE triggers and tuples +-- moved across partitions. +-- +create or replace function dump_update_new() returns trigger language plpgsql as +$$ + begin + raise notice 'trigger = %, new table = %', TG_NAME, + (select string_agg(new_table::text, ', ' order by a) from new_table); + return null; + end; +$$; +create or replace function dump_update_old() returns trigger language plpgsql as +$$ + begin + raise notice 'trigger = %, old table = %', TG_NAME, + (select string_agg(old_table::text, ', ' order by a) from old_table); + return null; + end; +$$; +create table trans_tab_parent (a text) partition by list (a); +create table trans_tab_child1 partition of trans_tab_parent for values in ('AAA1', 'AAA2'); +create table trans_tab_child2 partition of trans_tab_parent for values in ('BBB1', 'BBB2'); +create trigger trans_tab_parent_update_trig + after update on trans_tab_parent referencing old table as old_table + for each statement execute procedure dump_update_old(); +create trigger trans_tab_parent_insert_trig + after insert on trans_tab_parent referencing new table as new_table + for each statement execute procedure dump_insert(); +create trigger trans_tab_parent_delete_trig + after delete on trans_tab_parent referencing old table as old_table + for each statement execute procedure dump_delete(); +insert into trans_tab_parent values ('AAA1'), ('BBB1'); +-- should not trigger access to new table when moving across partitions. +update trans_tab_parent set a = 'BBB2' where a = 'AAA1'; +drop trigger trans_tab_parent_update_trig on trans_tab_parent; +create trigger trans_tab_parent_update_trig + after update on trans_tab_parent referencing new table as new_table + for each statement execute procedure dump_update_new(); +-- should not trigger access to old table when moving across partitions. +update trans_tab_parent set a = 'AAA2' where a = 'BBB1'; +delete from trans_tab_parent; +-- clean up +drop table trans_tab_parent, trans_tab_child1, trans_tab_child2; +drop function dump_update_new, dump_update_old; + -- -- Verify behavior of statement triggers on (non-partition) -- inheritance hierarchy with transition tables; similar to the