diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c index 91800d1fac..5524535e53 100644 --- a/src/backend/commands/event_trigger.c +++ b/src/backend/commands/event_trigger.c @@ -662,6 +662,31 @@ EventTriggerCommonSetup(Node *parsetree, return runlist; } +bool +EventTriggerValidContext(bool requireState) +{ + /* + * See EventTriggerDDLCommandStart for a discussion about why event + * triggers are disabled in single user mode. + */ + if (!IsUnderPostmaster) + return false; + + if (requireState) { + /* + * Only move forward if our state is set up. This is required + * to handle concurrency - if we proceed, without state already set up, + * and allow EventTriggerCommonSetup to run it may find triggers that + * didn't exist when the command started. + */ + if (!currentEventTriggerState) + return false; + } + + return true; +} + + /* * Fire ddl_command_start triggers. */ @@ -671,23 +696,8 @@ EventTriggerDDLCommandStart(Node *parsetree) List *runlist; EventTriggerData trigdata; - /* - * Event Triggers are completely disabled in standalone mode. There are - * (at least) two reasons for this: - * - * 1. A sufficiently broken event trigger might not only render the - * database unusable, but prevent disabling itself to fix the situation. - * In this scenario, restarting in standalone mode provides an escape - * hatch. - * - * 2. BuildEventTriggerCache relies on systable_beginscan_ordered, and - * therefore will malfunction if pg_event_trigger's indexes are damaged. - * To allow recovery from a damaged index, we need some operating mode - * wherein event triggers are disabled. (Or we could implement - * heapscan-and-sort logic for that case, but having disaster recovery - * scenarios depend on code that's otherwise untested isn't appetizing.) - */ - if (!IsUnderPostmaster) + /* We pass false for requireState since ... */ + if (!EventTriggerValidContext(false)) return; runlist = EventTriggerCommonSetup(parsetree, @@ -719,24 +729,8 @@ EventTriggerDDLCommandEnd(Node *parsetree) List *runlist; EventTriggerData trigdata; - /* - * See EventTriggerDDLCommandStart for a discussion about why event - * triggers are disabled in single user mode. - */ - if (!IsUnderPostmaster) - return; - - /* - * Also do nothing if our state isn't set up, which it won't be if there - * weren't any relevant event triggers at the start of the current DDL - * command. This test might therefore seem optional, but it's important - * because EventTriggerCommonSetup might find triggers that didn't exist - * at the time the command started. Although this function itself - * wouldn't crash, the event trigger functions would presumably call - * pg_event_trigger_ddl_commands which would fail. Better to do nothing - * until the next command. - */ - if (!currentEventTriggerState) + /* Seems not helpful to mention that this might to crash even if there is no state... */ + if (!EventTriggerValidContext(true)) return; runlist = EventTriggerCommonSetup(parsetree, @@ -767,22 +761,14 @@ EventTriggerSQLDrop(Node *parsetree) List *runlist; EventTriggerData trigdata; - /* - * See EventTriggerDDLCommandStart for a discussion about why event - * triggers are disabled in single user mode. - */ - if (!IsUnderPostmaster) - return; + if (!EventTriggerValidContext(true)) + return /* - * Use current state to determine whether this event fires at all. If - * there are no triggers for the sql_drop event, then we don't have - * anything to do here. Note that dropped object collection is disabled - * if this is the case, so even if we were to try to run, the list would - * be empty. + * While triggers do exist the dropped object collection is empty + * if none of them are triggers for the sql_drop event. */ - if (!currentEventTriggerState || - slist_is_empty(¤tEventTriggerState->SQLDropList)) + if (slist_is_empty(¤tEventTriggerState->SQLDropList)) return; runlist = EventTriggerCommonSetup(parsetree, @@ -838,33 +824,7 @@ EventTriggerTableRewrite(Node *parsetree, Oid tableOid, int reason) List *runlist; EventTriggerData trigdata; - /* - * Event Triggers are completely disabled in standalone mode. There are - * (at least) two reasons for this: - * - * 1. A sufficiently broken event trigger might not only render the - * database unusable, but prevent disabling itself to fix the situation. - * In this scenario, restarting in standalone mode provides an escape - * hatch. - * - * 2. BuildEventTriggerCache relies on systable_beginscan_ordered, and - * therefore will malfunction if pg_event_trigger's indexes are damaged. - * To allow recovery from a damaged index, we need some operating mode - * wherein event triggers are disabled. (Or we could implement - * heapscan-and-sort logic for that case, but having disaster recovery - * scenarios depend on code that's otherwise untested isn't appetizing.) - */ - if (!IsUnderPostmaster) - return; - - /* - * Also do nothing if our state isn't set up, which it won't be if there - * weren't any relevant event triggers at the start of the current DDL - * command. This test might therefore seem optional, but it's - * *necessary*, because EventTriggerCommonSetup might find triggers that - * didn't exist at the time the command started. - */ - if (!currentEventTriggerState) + if (!EventTriggerValidContext(true)) return; runlist = EventTriggerCommonSetup(parsetree,