From 004a2f0753877ab93b149ea38c1c0ed0661b1f2e Mon Sep 17 00:00:00 2001 From: Daniel Gustafsson Date: Tue, 5 Sep 2023 13:39:42 +0200 Subject: [PATCH v6] Add GUC for temporarily disabling event triggers In order to troubleshoot misbehaving or buggy event triggers, the documented advice is to enter single-user mode. In an attempt to reduce the number of situations where single-user mode is required for non-extraordinary maintenance, this GUC allows to temporarily suspend event triggers. This was originally extracted from a larger patchset which aimed at supporting event triggers on login events. Reviewed-by: Ted Yu Reviewed-by: Mikhail Gribkov Reviewed-by: Justin Pryzby Reviewed-by: Michael Paquier + + debug_event_trigger (boolean) + + debug_event_trigger + configuration parameter + + + + + Allow temporarily disabling execution of event triggers in order to + troubleshoot and repair faulty event triggers. All event triggers will + be disabled by setting it to true. Setting the value + to false will not disable any event triggers, this + is the default value. Only superusers and users with the appropriate + SET privilege can change this setting. + + + + diff --git a/doc/src/sgml/ref/create_event_trigger.sgml b/doc/src/sgml/ref/create_event_trigger.sgml index 22c8119198..40ca5fe6fd 100644 --- a/doc/src/sgml/ref/create_event_trigger.sgml +++ b/doc/src/sgml/ref/create_event_trigger.sgml @@ -123,7 +123,9 @@ CREATE EVENT TRIGGER name Event triggers are disabled in single-user mode (see ). If an erroneous event trigger disables the database so much that you can't even drop the trigger, restart in - single-user mode and you'll be able to do that. + single-user mode and you'll be able to do that. Event triggers can also be + temporarily disabled for such troubleshooting, see + . diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c index d4b00d1a82..bb1375c3f7 100644 --- a/src/backend/commands/event_trigger.c +++ b/src/backend/commands/event_trigger.c @@ -72,6 +72,9 @@ typedef struct EventTriggerQueryState static EventTriggerQueryState *currentEventTriggerState = NULL; +/* GUC parameter */ +bool debug_event_trigger = false; + /* Support for dropped objects */ typedef struct SQLDropObject { @@ -657,8 +660,11 @@ EventTriggerDDLCommandStart(Node *parsetree) * 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.) + * + * Additionally, event triggers can be disabled with a superuser-only GUC + * to make fixing database easier as per 1 above. */ - if (!IsUnderPostmaster) + if (!IsUnderPostmaster || debug_event_trigger) return; runlist = EventTriggerCommonSetup(parsetree, @@ -692,9 +698,9 @@ EventTriggerDDLCommandEnd(Node *parsetree) /* * See EventTriggerDDLCommandStart for a discussion about why event - * triggers are disabled in single user mode. + * triggers are disabled in single user mode or via GUC. */ - if (!IsUnderPostmaster) + if (!IsUnderPostmaster || debug_event_trigger) return; /* @@ -740,9 +746,9 @@ EventTriggerSQLDrop(Node *parsetree) /* * See EventTriggerDDLCommandStart for a discussion about why event - * triggers are disabled in single user mode. + * triggers are disabled in single user mode or via a GUC. */ - if (!IsUnderPostmaster) + if (!IsUnderPostmaster || debug_event_trigger) return; /* @@ -811,9 +817,9 @@ EventTriggerTableRewrite(Node *parsetree, Oid tableOid, int reason) /* * See EventTriggerDDLCommandStart for a discussion about why event - * triggers are disabled in single user mode. + * triggers are disabled in single user mode or via a GUC. */ - if (!IsUnderPostmaster) + if (!IsUnderPostmaster || debug_event_trigger) return; /* diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index e565a3092f..bafb1aef88 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -37,6 +37,7 @@ #include "catalog/namespace.h" #include "catalog/storage.h" #include "commands/async.h" +#include "commands/event_trigger.h" #include "commands/tablespace.h" #include "commands/trigger.h" #include "commands/user.h" @@ -1999,6 +2000,17 @@ struct config_bool ConfigureNamesBool[] = NULL, NULL, NULL }, + { + {"debug_event_trigger", PGC_SUSET, CLIENT_CONN_STATEMENT, + gettext_noop("Enables event triggers."), + NULL, + GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE + }, + &debug_event_trigger, + false, + NULL, NULL, NULL + }, + /* End-of-list marker */ { {NULL, 0, 0, NULL, NULL}, NULL, false, NULL, NULL, NULL diff --git a/src/include/commands/event_trigger.h b/src/include/commands/event_trigger.h index 5ed6ece555..3fd27489aa 100644 --- a/src/include/commands/event_trigger.h +++ b/src/include/commands/event_trigger.h @@ -29,6 +29,8 @@ typedef struct EventTriggerData CommandTag tag; } EventTriggerData; +extern PGDLLIMPORT bool debug_event_trigger; + #define AT_REWRITE_ALTER_PERSISTENCE 0x01 #define AT_REWRITE_DEFAULT_VAL 0x02 #define AT_REWRITE_COLUMN_REWRITE 0x04 diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out index 2c8a6b2212..81b000f5d4 100644 --- a/src/test/regress/expected/event_trigger.out +++ b/src/test/regress/expected/event_trigger.out @@ -616,3 +616,25 @@ SELECT DROP EVENT TRIGGER start_rls_command; DROP EVENT TRIGGER end_rls_command; DROP EVENT TRIGGER sql_drop_command; +-- Check the GUC for disabling event triggers +CREATE FUNCTION test_event_trigger_guc() RETURNS event_trigger +LANGUAGE plpgsql AS $$ +DECLARE + obj record; +BEGIN + FOR obj IN SELECT * FROM pg_event_trigger_dropped_objects() + LOOP + RAISE NOTICE '% dropped %', tg_tag, obj.object_type; + END LOOP; +END; +$$; +CREATE EVENT TRIGGER test_event_trigger_guc + ON sql_drop + WHEN TAG IN ('DROP POLICY') EXECUTE FUNCTION test_event_trigger_guc(); +SET debug_event_trigger = 'false'; +CREATE POLICY pguc ON event_trigger_test USING (FALSE); +DROP POLICY pguc ON event_trigger_test; +NOTICE: DROP POLICY dropped policy +CREATE POLICY pguc ON event_trigger_test USING (FALSE); +SET debug_event_trigger = 'true'; +DROP POLICY pguc ON event_trigger_test; diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql index 1aeaddbe71..abfce0c7b4 100644 --- a/src/test/regress/sql/event_trigger.sql +++ b/src/test/regress/sql/event_trigger.sql @@ -471,3 +471,27 @@ SELECT DROP EVENT TRIGGER start_rls_command; DROP EVENT TRIGGER end_rls_command; DROP EVENT TRIGGER sql_drop_command; + +-- Check the GUC for disabling event triggers +CREATE FUNCTION test_event_trigger_guc() RETURNS event_trigger +LANGUAGE plpgsql AS $$ +DECLARE + obj record; +BEGIN + FOR obj IN SELECT * FROM pg_event_trigger_dropped_objects() + LOOP + RAISE NOTICE '% dropped %', tg_tag, obj.object_type; + END LOOP; +END; +$$; +CREATE EVENT TRIGGER test_event_trigger_guc + ON sql_drop + WHEN TAG IN ('DROP POLICY') EXECUTE FUNCTION test_event_trigger_guc(); + +SET debug_event_trigger = 'false'; +CREATE POLICY pguc ON event_trigger_test USING (FALSE); +DROP POLICY pguc ON event_trigger_test; + +CREATE POLICY pguc ON event_trigger_test USING (FALSE); +SET debug_event_trigger = 'true'; +DROP POLICY pguc ON event_trigger_test; -- 2.32.1 (Apple Git-133)