@@ -100,6 +100,7 @@ static void AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo,
100100 List * recheckIndexes , Bitmapset * modifiedCols ,
101101 TransitionCaptureState * transition_capture );
102102static void AfterTriggerEnlargeQueryState (void );
103+ static bool before_stmt_triggers_fired (Oid relid , CmdType cmdType );
103104
104105
105106/*
@@ -2229,6 +2230,11 @@ ExecBSInsertTriggers(EState *estate, ResultRelInfo *relinfo)
22292230 if (!trigdesc -> trig_insert_before_statement )
22302231 return ;
22312232
2233+ /* no-op if we already fired BS triggers in this context */
2234+ if (before_stmt_triggers_fired (RelationGetRelid (relinfo -> ri_RelationDesc ),
2235+ CMD_INSERT ))
2236+ return ;
2237+
22322238 LocTriggerData .type = T_TriggerData ;
22332239 LocTriggerData .tg_event = TRIGGER_EVENT_INSERT |
22342240 TRIGGER_EVENT_BEFORE ;
@@ -2439,6 +2445,11 @@ ExecBSDeleteTriggers(EState *estate, ResultRelInfo *relinfo)
24392445 if (!trigdesc -> trig_delete_before_statement )
24402446 return ;
24412447
2448+ /* no-op if we already fired BS triggers in this context */
2449+ if (before_stmt_triggers_fired (RelationGetRelid (relinfo -> ri_RelationDesc ),
2450+ CMD_DELETE ))
2451+ return ;
2452+
24422453 LocTriggerData .type = T_TriggerData ;
24432454 LocTriggerData .tg_event = TRIGGER_EVENT_DELETE |
24442455 TRIGGER_EVENT_BEFORE ;
@@ -2651,6 +2662,11 @@ ExecBSUpdateTriggers(EState *estate, ResultRelInfo *relinfo)
26512662 if (!trigdesc -> trig_update_before_statement )
26522663 return ;
26532664
2665+ /* no-op if we already fired BS triggers in this context */
2666+ if (before_stmt_triggers_fired (RelationGetRelid (relinfo -> ri_RelationDesc ),
2667+ CMD_UPDATE ))
2668+ return ;
2669+
26542670 updatedCols = GetUpdatedColumns (relinfo , estate );
26552671
26562672 LocTriggerData .type = T_TriggerData ;
@@ -3523,11 +3539,11 @@ typedef struct AfterTriggerEventList
35233539 *
35243540 * We create an AfterTriggersTableData struct for each target table of the
35253541 * current query, and each operation mode (INSERT/UPDATE/DELETE), that has
3526- * either transition tables or AFTER STATEMENT triggers. This is used to
3542+ * either transition tables or statement-level triggers. This is used to
35273543 * hold the relevant transition tables, as well as info tracking whether
3528- * we already queued the AFTER STATEMENT triggers. (We use that info to
3529- * prevent, as much as possible, firing the same AFTER STATEMENT trigger
3530- * more than once per statement .) These structs, along with the transition
3544+ * we already queued the statement triggers. (We use that info to prevent
3545+ * firing the same statement triggers more than once per statement, or really
3546+ * once per transition table set .) These structs, along with the transition
35313547 * table tuplestores, live in the (sub)transaction's CurTransactionContext.
35323548 * That's sufficient lifespan because we don't allow transition tables to be
35333549 * used by deferrable triggers, so they only need to survive until
@@ -3576,8 +3592,9 @@ struct AfterTriggersTableData
35763592 Oid relid ; /* target table's OID */
35773593 CmdType cmdType ; /* event type, CMD_INSERT/UPDATE/DELETE */
35783594 bool closed ; /* true when no longer OK to add tuples */
3579- bool stmt_trig_done ; /* did we already queue stmt-level triggers? */
3580- AfterTriggerEventList stmt_trig_events ; /* if so, saved list pointer */
3595+ bool before_trig_done ; /* did we already queue BS triggers? */
3596+ bool after_trig_done ; /* did we already queue AS triggers? */
3597+ AfterTriggerEventList after_trig_events ; /* if so, saved list pointer */
35813598 Tuplestorestate * old_tuplestore ; /* "old" transition table, if any */
35823599 Tuplestorestate * new_tuplestore ; /* "new" transition table, if any */
35833600};
@@ -5650,6 +5667,37 @@ AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo,
56505667 }
56515668}
56525669
5670+ /*
5671+ * Detect whether we already queued BEFORE STATEMENT triggers for the given
5672+ * relation + operation, and set the flag so the next call will report "true".
5673+ */
5674+ static bool
5675+ before_stmt_triggers_fired (Oid relid , CmdType cmdType )
5676+ {
5677+ bool result ;
5678+ AfterTriggersTableData * table ;
5679+
5680+ /* Check state, like AfterTriggerSaveEvent. */
5681+ if (afterTriggers .query_depth < 0 )
5682+ elog (ERROR , "before_stmt_triggers_fired() called outside of query" );
5683+
5684+ /* Be sure we have enough space to record events at this query depth. */
5685+ if (afterTriggers .query_depth >= afterTriggers .maxquerydepth )
5686+ AfterTriggerEnlargeQueryState ();
5687+
5688+ /*
5689+ * We keep this state in the AfterTriggersTableData that also holds
5690+ * transition tables for the relation + operation. In this way, if we are
5691+ * forced to make a new set of transition tables because more tuples get
5692+ * entered after we've already fired triggers, we will allow a new set of
5693+ * statement triggers to get queued.
5694+ */
5695+ table = GetAfterTriggersTableData (relid , cmdType );
5696+ result = table -> before_trig_done ;
5697+ table -> before_trig_done = true;
5698+ return result ;
5699+ }
5700+
56535701/*
56545702 * If we previously queued a set of AFTER STATEMENT triggers for the given
56555703 * relation + operation, and they've not been fired yet, cancel them. The
@@ -5684,7 +5732,7 @@ cancel_prior_stmt_triggers(Oid relid, CmdType cmdType, int tgevent)
56845732 */
56855733 table = GetAfterTriggersTableData (relid , cmdType );
56865734
5687- if (table -> stmt_trig_done )
5735+ if (table -> after_trig_done )
56885736 {
56895737 /*
56905738 * We want to start scanning from the tail location that existed just
@@ -5695,10 +5743,10 @@ cancel_prior_stmt_triggers(Oid relid, CmdType cmdType, int tgevent)
56955743 AfterTriggerEvent event ;
56965744 AfterTriggerEventChunk * chunk ;
56975745
5698- if (table -> stmt_trig_events .tail )
5746+ if (table -> after_trig_events .tail )
56995747 {
5700- chunk = table -> stmt_trig_events .tail ;
5701- event = (AfterTriggerEvent ) table -> stmt_trig_events .tailfree ;
5748+ chunk = table -> after_trig_events .tail ;
5749+ event = (AfterTriggerEvent ) table -> after_trig_events .tailfree ;
57025750 }
57035751 else
57045752 {
@@ -5737,8 +5785,8 @@ cancel_prior_stmt_triggers(Oid relid, CmdType cmdType, int tgevent)
57375785done :
57385786
57395787 /* In any case, save current insertion point for next time */
5740- table -> stmt_trig_done = true;
5741- table -> stmt_trig_events = qs -> events ;
5788+ table -> after_trig_done = true;
5789+ table -> after_trig_events = qs -> events ;
57425790}
57435791
57445792/*
0 commit comments