aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2024-06-20 14:21:36 -0400
committerTom Lane <tgl@sss.pgh.pa.us>2024-06-20 14:21:36 -0400
commitb0037bbefda3339c821ff4b5d04d672274bf7238 (patch)
tree29816a73bff61e118818e44193fcc09ae0f5494c /src
parent3e3e2ebea79c6327f1dbf279d1ee894153508558 (diff)
downloadpostgresql-b0037bbefda3339c821ff4b5d04d672274bf7238.tar.gz
postgresql-b0037bbefda3339c821ff4b5d04d672274bf7238.zip
Don't throw an error if a queued AFTER trigger no longer exists.
afterTriggerInvokeEvents and AfterTriggerExecute have always treated it as an error if the trigger OID mentioned in a queued after-trigger event can't be found. However, that fails to account for the edge case where the trigger's been dropped in the current transaction since queueing the event. There seems no very good reason to disallow that case, so instead silently do nothing if the trigger OID can't be found. This does give up a little bit of bug-detection ability, but I don't recall that these error messages have ever actually revealed a bug, so it seems mostly theoretical. Alternatives such as marking pending events DONE at the time of dropping a trigger would be complicated and perhaps introduce bugs of their own. Per bug #18517 from Alexander Lakhin. Back-patch to all supported branches. Discussion: https://postgr.es/m/18517-af2d19882240902c@postgresql.org
Diffstat (limited to 'src')
-rw-r--r--src/backend/commands/trigger.c16
-rw-r--r--src/test/regress/expected/triggers.out11
-rw-r--r--src/test/regress/sql/triggers.sql14
3 files changed, 34 insertions, 7 deletions
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 794bfe1f149..7f3285b0718 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -4239,13 +4239,17 @@ AfterTriggerExecute(EState *estate,
bool should_free_trig = false;
bool should_free_new = false;
- /*
- * Locate trigger in trigdesc.
- */
LocTriggerData.tg_trigger = NULL;
LocTriggerData.tg_trigslot = NULL;
LocTriggerData.tg_newslot = NULL;
+ /*
+ * Locate trigger in trigdesc. It might not be present, and in fact the
+ * trigdesc could be NULL, if the trigger was dropped since the event was
+ * queued. In that case, silently do nothing.
+ */
+ if (trigdesc == NULL)
+ return;
for (tgindx = 0; tgindx < trigdesc->numtriggers; tgindx++)
{
if (trigdesc->triggers[tgindx].tgoid == tgoid)
@@ -4255,7 +4259,7 @@ AfterTriggerExecute(EState *estate,
}
}
if (LocTriggerData.tg_trigger == NULL)
- elog(ERROR, "could not find trigger %u", tgoid);
+ return;
/*
* If doing EXPLAIN ANALYZE, start charging time to this trigger. We want
@@ -4575,6 +4579,7 @@ afterTriggerInvokeEvents(AfterTriggerEventList *events,
rInfo = ExecGetTriggerResultRel(estate, evtshared->ats_relid);
rel = rInfo->ri_RelationDesc;
trigdesc = rInfo->ri_TrigDesc;
+ /* caution: trigdesc could be NULL here */
finfo = rInfo->ri_TrigFunctions;
instr = rInfo->ri_TrigInstrument;
if (slot1 != NULL)
@@ -4590,9 +4595,6 @@ afterTriggerInvokeEvents(AfterTriggerEventList *events,
slot2 = MakeSingleTupleTableSlot(rel->rd_att,
&TTSOpsMinimalTuple);
}
- if (trigdesc == NULL) /* should not happen */
- elog(ERROR, "relation %u has no triggers",
- evtshared->ats_relid);
}
/*
diff --git a/src/test/regress/expected/triggers.out b/src/test/regress/expected/triggers.out
index 79e5e4b3533..58ee950b57e 100644
--- a/src/test/regress/expected/triggers.out
+++ b/src/test/regress/expected/triggers.out
@@ -3124,6 +3124,17 @@ select * from trig_table;
drop table refd_table, trig_table;
--
+-- Test that we can drop a not-yet-fired deferred trigger
+--
+create table refd_table (id int primary key);
+create table trig_table (fk int references refd_table initially deferred);
+begin;
+insert into trig_table values (1);
+drop table refd_table cascade;
+NOTICE: drop cascades to constraint trig_table_fk_fkey on table trig_table
+commit;
+drop table trig_table;
+--
-- self-referential FKs are even more fun
--
create table self_ref (a int primary key,
diff --git a/src/test/regress/sql/triggers.sql b/src/test/regress/sql/triggers.sql
index d1d6f9974ca..f889d251429 100644
--- a/src/test/regress/sql/triggers.sql
+++ b/src/test/regress/sql/triggers.sql
@@ -2304,6 +2304,20 @@ select * from trig_table;
drop table refd_table, trig_table;
--
+-- Test that we can drop a not-yet-fired deferred trigger
+--
+
+create table refd_table (id int primary key);
+create table trig_table (fk int references refd_table initially deferred);
+
+begin;
+insert into trig_table values (1);
+drop table refd_table cascade;
+commit;
+
+drop table trig_table;
+
+--
-- self-referential FKs are even more fun
--