diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.out index f0c842a607..5c135becf1 100644 --- a/contrib/postgres_fdw/expected/postgres_fdw.out +++ b/contrib/postgres_fdw/expected/postgres_fdw.out @@ -6459,6 +6459,36 @@ DROP TRIGGER trig_row_after ON rem1; DROP TRIGGER trig_stmt_before ON rem1; DROP TRIGGER trig_stmt_after ON rem1; DELETE from rem1; +-- Test multiple AFTER ROW triggers on a foreign table +CREATE TRIGGER trig_row_after1 +AFTER INSERT OR UPDATE OR DELETE ON rem1 +FOR EACH ROW EXECUTE PROCEDURE trigger_data(23,'skidoo'); +CREATE TRIGGER trig_row_after2 +AFTER INSERT OR UPDATE OR DELETE ON rem1 +FOR EACH ROW EXECUTE PROCEDURE trigger_data(23,'skidoo'); +insert into rem1 values(1,'insert'); +NOTICE: trig_row_after1(23, skidoo) AFTER ROW INSERT ON rem1 +NOTICE: NEW: (1,insert) +NOTICE: trig_row_after2(23, skidoo) AFTER ROW INSERT ON rem1 +NOTICE: NEW: (1,insert) +update rem1 set f2 = 'update' where f1 = 1; +NOTICE: trig_row_after1(23, skidoo) AFTER ROW UPDATE ON rem1 +NOTICE: OLD: (1,insert),NEW: (1,update) +NOTICE: trig_row_after2(23, skidoo) AFTER ROW UPDATE ON rem1 +NOTICE: OLD: (1,insert),NEW: (1,update) +update rem1 set f2 = f2 || f2; +NOTICE: trig_row_after1(23, skidoo) AFTER ROW UPDATE ON rem1 +NOTICE: OLD: (1,update),NEW: (1,updateupdate) +NOTICE: trig_row_after2(23, skidoo) AFTER ROW UPDATE ON rem1 +NOTICE: OLD: (1,update),NEW: (1,updateupdate) +delete from rem1; +NOTICE: trig_row_after1(23, skidoo) AFTER ROW DELETE ON rem1 +NOTICE: OLD: (1,updateupdate) +NOTICE: trig_row_after2(23, skidoo) AFTER ROW DELETE ON rem1 +NOTICE: OLD: (1,updateupdate) +-- cleanup +DROP TRIGGER trig_row_after1 ON rem1; +DROP TRIGGER trig_row_after2 ON rem1; -- Test WHEN conditions CREATE TRIGGER trig_row_before_insupd BEFORE INSERT OR UPDATE ON rem1 @@ -6689,7 +6719,7 @@ NOTICE: trig_row_after(23, skidoo) AFTER ROW INSERT ON rem1 NOTICE: NEW: (13,"test triggered !") ctid -------- - (0,29) + (0,32) (1 row) -- cleanup diff --git a/contrib/postgres_fdw/sql/postgres_fdw.sql b/contrib/postgres_fdw/sql/postgres_fdw.sql index 630b803e26..0404222b7a 100644 --- a/contrib/postgres_fdw/sql/postgres_fdw.sql +++ b/contrib/postgres_fdw/sql/postgres_fdw.sql @@ -1485,6 +1485,23 @@ DROP TRIGGER trig_stmt_after ON rem1; DELETE from rem1; +-- Test multiple AFTER ROW triggers on a foreign table +CREATE TRIGGER trig_row_after1 +AFTER INSERT OR UPDATE OR DELETE ON rem1 +FOR EACH ROW EXECUTE PROCEDURE trigger_data(23,'skidoo'); + +CREATE TRIGGER trig_row_after2 +AFTER INSERT OR UPDATE OR DELETE ON rem1 +FOR EACH ROW EXECUTE PROCEDURE trigger_data(23,'skidoo'); + +insert into rem1 values(1,'insert'); +update rem1 set f2 = 'update' where f1 = 1; +update rem1 set f2 = f2 || f2; +delete from rem1; + +-- cleanup +DROP TRIGGER trig_row_after1 ON rem1; +DROP TRIGGER trig_row_after2 ON rem1; -- Test WHEN conditions diff --git a/doc/src/sgml/trigger.sgml b/doc/src/sgml/trigger.sgml index f62f420c19..ba94acad69 100644 --- a/doc/src/sgml/trigger.sgml +++ b/doc/src/sgml/trigger.sgml @@ -511,8 +511,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; @@ -714,21 +714,21 @@ typedef struct Trigger - tg_trigtuplebuf + tg_trigslot - The buffer containing tg_trigtuple, or InvalidBuffer if there - is no such tuple or it is not stored in a disk buffer. + The slot containing tg_trigtuple, + or a NULL pointer if there is no such tuple. - tg_newtuplebuf + tg_newslot - The buffer containing tg_newtuple, or InvalidBuffer if there - is no such tuple or it is not stored in a disk buffer. + The slot containing tg_newtuple, + or a NULL pointer if there is no such tuple. diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c index 21719c0bb1..063d77eea2 100644 --- a/src/backend/commands/trigger.c +++ b/src/backend/commands/trigger.c @@ -4258,12 +4258,17 @@ AfterTriggerExecute(EState *estate, LocTriggerData.tg_trigtuple = ExecFetchSlotHeapTuple(trig_tuple_slot1, true, &should_free_trig); - LocTriggerData.tg_newslot = trig_tuple_slot2; - LocTriggerData.tg_newtuple = - ((evtshared->ats_event & TRIGGER_EVENT_OPMASK) == - TRIGGER_EVENT_UPDATE) ? - ExecFetchSlotHeapTuple(trig_tuple_slot2, true, &should_free_new) : NULL; - + if ((evtshared->ats_event & TRIGGER_EVENT_OPMASK) == + TRIGGER_EVENT_UPDATE) + { + LocTriggerData.tg_newslot = trig_tuple_slot2; + LocTriggerData.tg_newtuple = + ExecFetchSlotHeapTuple(trig_tuple_slot2, true, &should_free_new); + } + else + { + LocTriggerData.tg_newtuple = NULL; + } break; default: @@ -4358,10 +4363,14 @@ AfterTriggerExecute(EState *estate, if (should_free_new) heap_freetuple(LocTriggerData.tg_newtuple); - if (LocTriggerData.tg_trigslot) - ExecClearTuple(LocTriggerData.tg_trigslot); - if (LocTriggerData.tg_newslot) - ExecClearTuple(LocTriggerData.tg_newslot); + /* don't clear slots' contents if foreign table */ + if (trig_tuple_slot1 == NULL) + { + if (LocTriggerData.tg_trigslot) + ExecClearTuple(LocTriggerData.tg_trigslot); + if (LocTriggerData.tg_newslot) + ExecClearTuple(LocTriggerData.tg_newslot); + } /* * If doing EXPLAIN ANALYZE, stop charging time to this trigger, and count @@ -4515,13 +4524,14 @@ afterTriggerInvokeEvents(AfterTriggerEventList *events, trigdesc = rInfo->ri_TrigDesc; finfo = rInfo->ri_TrigFunctions; instr = rInfo->ri_TrigInstrument; + if (slot1 != NULL) + { + ExecDropSingleTupleTableSlot(slot1); + ExecDropSingleTupleTableSlot(slot2); + slot1 = slot2 = NULL; + } if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE) { - if (slot1 != NULL) - { - ExecDropSingleTupleTableSlot(slot1); - ExecDropSingleTupleTableSlot(slot2); - } slot1 = MakeSingleTupleTableSlot(rel->rd_att, &TTSOpsMinimalTuple); slot2 = MakeSingleTupleTableSlot(rel->rd_att,