GUC for temporarily disabling event triggers
In the thread discussing the login event trigger patch it was argued that we
want to avoid recommending single-user mode for troubleshooting tasks, and a
GUC for temporarily disabling event triggers was proposed.
Since the login event trigger patch lost momentum, I've broken out the GUC part
into a separate patch to see if there is interest in that part alone, to chip
away at situations requiring single-user mode.
The patch adds a new GUC, ignore_event_trigger with two option values, 'all'
and 'none' (the login event patch had 'login' as well). This can easily be
expanded to have the different types of events, or pared down to a boolean
on/off. I think it makes more sense to make it more fine-grained but I think
there is merit in either direction.
If there is interest in this I'm happy to pursue a polished version of this
patch.
--
Daniel Gustafsson https://vmware.com/
Attachments:
v1-0001-Add-GUC-for-temporarily-disabling-event-triggers.patchapplication/octet-stream; name=v1-0001-Add-GUC-for-temporarily-disabling-event-triggers.patch; x-unix-mode=0644Download
From 3c458f693cc787e6459b7d4ad38ebee706206689 Mon Sep 17 00:00:00 2001
From: Daniel Gustafsson <dgustafsson@postgresql.org>
Date: Thu, 3 Nov 2022 16:24:06 +0100
Subject: [PATCH v1] 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
suspending event triggers.
This was extracted from a larger patchset which aimed at supporting
event triggers on login events.
---
doc/src/sgml/config.sgml | 19 ++++++++++
doc/src/sgml/ref/create_event_trigger.sgml | 4 ++-
src/backend/commands/event_trigger.c | 40 +++++++++++++++++----
src/backend/utils/misc/guc_tables.c | 17 +++++++++
src/include/commands/event_trigger.h | 8 +++++
src/test/regress/expected/event_trigger.out | 21 +++++++++++
src/test/regress/sql/event_trigger.sql | 22 ++++++++++++
7 files changed, 123 insertions(+), 8 deletions(-)
diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index 559eb898a9..790e904df6 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -9377,6 +9377,25 @@ SET XML OPTION { DOCUMENT | CONTENT };
</listitem>
</varlistentry>
+ <varlistentry id="guc-ignore-event-trigger" xreflabel="ignore_event_trigger">
+ <term><varname>ignore_event_trigger</varname> (<type>enum</type>)
+ <indexterm>
+ <primary><varname>ignore_event_trigger</varname></primary>
+ <secondary>configuration parameter</secondary>
+ </indexterm>
+ </term>
+ <listitem>
+ <para>
+ Allows to temporarily disable event triggers from executing in order
+ to troubleshoot and repair faulty event triggers. The default value
+ is <literal>none</literal>, with which all event triggers are enabled.
+ When set to <literal>all</literal> then all event triggers will be
+ disabled. Only superusers and users with the appropriate
+ <literal>SET</literal> privilege can change this setting.
+ </para>
+ </listitem>
+ </varlistentry>
+
</variablelist>
</sect2>
<sect2 id="runtime-config-client-format">
diff --git a/doc/src/sgml/ref/create_event_trigger.sgml b/doc/src/sgml/ref/create_event_trigger.sgml
index 22c8119198..f6922c3de3 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 <replaceable class="parameter">name</replaceable>
Event triggers are disabled in single-user mode (see <xref
linkend="app-postgres"/>). 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
+ <xref linkend="guc-ignore-event-trigger"/>.
</para>
</refsect1>
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index 8d36b66488..91be722b75 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 */
+int ignore_event_trigger = IGNORE_EVENT_TRIGGER_NONE;
+
/* Support for dropped objects */
typedef struct SQLDropObject
{
@@ -100,6 +103,7 @@ static void validate_table_rewrite_tags(const char *filtervar, List *taglist);
static void EventTriggerInvoke(List *fn_oid_list, EventTriggerData *trigdata);
static const char *stringify_grant_objtype(ObjectType objtype);
static const char *stringify_adefprivs_objtype(ObjectType objtype);
+static bool ignore_event_trigger_check(EventTriggerEvent event);
/*
* Create an event trigger.
@@ -657,8 +661,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 || ignore_event_trigger_check(EVT_DDLCommandStart))
return;
runlist = EventTriggerCommonSetup(parsetree,
@@ -692,9 +699,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 || ignore_event_trigger_check(EVT_DDLCommandEnd))
return;
/*
@@ -740,9 +747,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 || ignore_event_trigger_check(EVT_SQLDrop))
return;
/*
@@ -811,9 +818,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 || ignore_event_trigger_check(EVT_TableRewrite))
return;
/*
@@ -2175,3 +2182,22 @@ stringify_adefprivs_objtype(ObjectType objtype)
return "???"; /* keep compiler quiet */
}
+
+
+/*
+ * Checks whether the specified event is ignored by the ignore_event_trigger
+ * GUC or not. Currently, the GUC only supports ignoring all or nothing but
+ * that might change so the function takes an event to aid that.
+ */
+static bool
+ignore_event_trigger_check(EventTriggerEvent event)
+{
+ (void) event; /* unused parameter */
+
+ if (ignore_event_trigger == IGNORE_EVENT_TRIGGER_ALL)
+ return true;
+
+ /* IGNORE_EVENT_TRIGGER_NONE */
+ return false;
+
+}
diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c
index 836b49484a..0d319987ca 100644
--- a/src/backend/utils/misc/guc_tables.c
+++ b/src/backend/utils/misc/guc_tables.c
@@ -36,6 +36,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"
@@ -446,6 +447,12 @@ static const struct config_enum_entry wal_compression_options[] = {
{NULL, 0, false}
};
+static const struct config_enum_entry ignore_event_trigger_options[] = {
+ {"none", IGNORE_EVENT_TRIGGER_NONE, false},
+ {"all", IGNORE_EVENT_TRIGGER_ALL, false},
+ {NULL, 0, false}
+};
+
/*
* Options for enum values stored in other modules
*/
@@ -4704,6 +4711,16 @@ struct config_enum ConfigureNamesEnum[] =
NULL, NULL, NULL
},
+ {
+ {"ignore_event_trigger", PGC_SUSET, CLIENT_CONN_STATEMENT,
+ gettext_noop("Disable event triggers for the duration of the session."),
+ NULL
+ },
+ &ignore_event_trigger,
+ IGNORE_EVENT_TRIGGER_NONE, ignore_event_trigger_options,
+ NULL, NULL, NULL
+ },
+
{
{"wal_level", PGC_POSTMASTER, WAL_SETTINGS,
gettext_noop("Sets the level of information written to the WAL."),
diff --git a/src/include/commands/event_trigger.h b/src/include/commands/event_trigger.h
index 10091c3aaf..e8f188df23 100644
--- a/src/include/commands/event_trigger.h
+++ b/src/include/commands/event_trigger.h
@@ -29,6 +29,14 @@ typedef struct EventTriggerData
CommandTag tag;
} EventTriggerData;
+typedef enum ignore_event_trigger_events
+{
+ IGNORE_EVENT_TRIGGER_NONE,
+ IGNORE_EVENT_TRIGGER_ALL
+} IgnoreEventTriggersEvents;
+
+extern PGDLLIMPORT int ignore_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 5a10958df5..747ffefaa6 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -614,3 +614,24 @@ 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();
+CREATE POLICY pguc ON event_trigger_test USING (FALSE);
+DROP POLICY pguc ON event_trigger_test;
+NOTICE: DROP POLICY dropped policy
+SET ignore_event_trigger = 'all';
+CREATE POLICY pguc ON event_trigger_test USING (FALSE);
+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..c4845a7e6b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -471,3 +471,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();
+
+CREATE POLICY pguc ON event_trigger_test USING (FALSE);
+DROP POLICY pguc ON event_trigger_test;
+SET ignore_event_trigger = 'all';
+CREATE POLICY pguc ON event_trigger_test USING (FALSE);
+DROP POLICY pguc ON event_trigger_test;
--
2.32.1 (Apple Git-133)
On 3 Nov 2022, at 21:47, Daniel Gustafsson <daniel@yesql.se> wrote:
The patch adds a new GUC, ignore_event_trigger with two option values, 'all'
and 'none' (the login event patch had 'login' as well).
The attached v2 fixes a small bug which caused testfailures the CFBot.
--
Daniel Gustafsson https://vmware.com/
Attachments:
v2-0001-Add-GUC-for-temporarily-disabling-event-triggers.patchapplication/octet-stream; name=v2-0001-Add-GUC-for-temporarily-disabling-event-triggers.patch; x-unix-mode=0644Download
From d0f8470100d1f7cc9041d53394c227ce28cb0680 Mon Sep 17 00:00:00 2001
From: Daniel Gustafsson <dgustafsson@postgresql.org>
Date: Thu, 3 Nov 2022 16:24:06 +0100
Subject: [PATCH v2] 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
suspending event triggers.
This was extracted from a larger patchset which aimed at supporting
event triggers on login events.
---
doc/src/sgml/config.sgml | 19 +++++++++
doc/src/sgml/ref/create_event_trigger.sgml | 4 +-
src/backend/commands/event_trigger.c | 40 +++++++++++++++----
src/backend/utils/misc/guc_tables.c | 17 ++++++++
src/backend/utils/misc/postgresql.conf.sample | 1 +
src/include/commands/event_trigger.h | 8 ++++
src/test/regress/expected/event_trigger.out | 21 ++++++++++
src/test/regress/sql/event_trigger.sql | 22 ++++++++++
8 files changed, 124 insertions(+), 8 deletions(-)
diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index 82df89b1a9..0c9b488211 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -9363,6 +9363,25 @@ SET XML OPTION { DOCUMENT | CONTENT };
</listitem>
</varlistentry>
+ <varlistentry id="guc-ignore-event-trigger" xreflabel="ignore_event_trigger">
+ <term><varname>ignore_event_trigger</varname> (<type>enum</type>)
+ <indexterm>
+ <primary><varname>ignore_event_trigger</varname></primary>
+ <secondary>configuration parameter</secondary>
+ </indexterm>
+ </term>
+ <listitem>
+ <para>
+ Allows to temporarily disable event triggers from executing in order
+ to troubleshoot and repair faulty event triggers. The default value
+ is <literal>none</literal>, with which all event triggers are enabled.
+ When set to <literal>all</literal> then all event triggers will be
+ disabled. Only superusers and users with the appropriate
+ <literal>SET</literal> privilege can change this setting.
+ </para>
+ </listitem>
+ </varlistentry>
+
</variablelist>
</sect2>
<sect2 id="runtime-config-client-format">
diff --git a/doc/src/sgml/ref/create_event_trigger.sgml b/doc/src/sgml/ref/create_event_trigger.sgml
index 22c8119198..f6922c3de3 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 <replaceable class="parameter">name</replaceable>
Event triggers are disabled in single-user mode (see <xref
linkend="app-postgres"/>). 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
+ <xref linkend="guc-ignore-event-trigger"/>.
</para>
</refsect1>
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index a3bdc5db07..0afd254173 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 */
+int ignore_event_trigger = IGNORE_EVENT_TRIGGER_NONE;
+
/* Support for dropped objects */
typedef struct SQLDropObject
{
@@ -100,6 +103,7 @@ static void validate_table_rewrite_tags(const char *filtervar, List *taglist);
static void EventTriggerInvoke(List *fn_oid_list, EventTriggerData *trigdata);
static const char *stringify_grant_objtype(ObjectType objtype);
static const char *stringify_adefprivs_objtype(ObjectType objtype);
+static bool ignore_event_trigger_check(EventTriggerEvent event);
/*
* Create an event trigger.
@@ -657,8 +661,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 || ignore_event_trigger_check(EVT_DDLCommandStart))
return;
runlist = EventTriggerCommonSetup(parsetree,
@@ -692,9 +699,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 || ignore_event_trigger_check(EVT_DDLCommandEnd))
return;
/*
@@ -740,9 +747,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 || ignore_event_trigger_check(EVT_SQLDrop))
return;
/*
@@ -811,9 +818,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 || ignore_event_trigger_check(EVT_TableRewrite))
return;
/*
@@ -2175,3 +2182,22 @@ stringify_adefprivs_objtype(ObjectType objtype)
return "???"; /* keep compiler quiet */
}
+
+
+/*
+ * Checks whether the specified event is ignored by the ignore_event_trigger
+ * GUC or not. Currently, the GUC only supports ignoring all or nothing but
+ * that might change so the function takes an event to aid that.
+ */
+static bool
+ignore_event_trigger_check(EventTriggerEvent event)
+{
+ (void) event; /* unused parameter */
+
+ if (ignore_event_trigger == IGNORE_EVENT_TRIGGER_ALL)
+ return true;
+
+ /* IGNORE_EVENT_TRIGGER_NONE */
+ return false;
+
+}
diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c
index 1bf14eec66..423671c14a 100644
--- a/src/backend/utils/misc/guc_tables.c
+++ b/src/backend/utils/misc/guc_tables.c
@@ -36,6 +36,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"
@@ -446,6 +447,12 @@ static const struct config_enum_entry wal_compression_options[] = {
{NULL, 0, false}
};
+static const struct config_enum_entry ignore_event_trigger_options[] = {
+ {"none", IGNORE_EVENT_TRIGGER_NONE, false},
+ {"all", IGNORE_EVENT_TRIGGER_ALL, false},
+ {NULL, 0, false}
+};
+
/*
* Options for enum values stored in other modules
*/
@@ -4714,6 +4721,16 @@ struct config_enum ConfigureNamesEnum[] =
NULL, NULL, NULL
},
+ {
+ {"ignore_event_trigger", PGC_SUSET, CLIENT_CONN_STATEMENT,
+ gettext_noop("Disable event triggers for the duration of the session."),
+ NULL
+ },
+ &ignore_event_trigger,
+ IGNORE_EVENT_TRIGGER_NONE, ignore_event_trigger_options,
+ NULL, NULL, NULL
+ },
+
{
{"wal_level", PGC_POSTMASTER, WAL_SETTINGS,
gettext_noop("Sets the level of information written to the WAL."),
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index 043864597f..9801eb9377 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -701,6 +701,7 @@
#xmlbinary = 'base64'
#xmloption = 'content'
#gin_pending_list_limit = 4MB
+#ignore_event_trigger = 0
# - Locale and Formatting -
diff --git a/src/include/commands/event_trigger.h b/src/include/commands/event_trigger.h
index 10091c3aaf..e8f188df23 100644
--- a/src/include/commands/event_trigger.h
+++ b/src/include/commands/event_trigger.h
@@ -29,6 +29,14 @@ typedef struct EventTriggerData
CommandTag tag;
} EventTriggerData;
+typedef enum ignore_event_trigger_events
+{
+ IGNORE_EVENT_TRIGGER_NONE,
+ IGNORE_EVENT_TRIGGER_ALL
+} IgnoreEventTriggersEvents;
+
+extern PGDLLIMPORT int ignore_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 5a10958df5..747ffefaa6 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -614,3 +614,24 @@ 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();
+CREATE POLICY pguc ON event_trigger_test USING (FALSE);
+DROP POLICY pguc ON event_trigger_test;
+NOTICE: DROP POLICY dropped policy
+SET ignore_event_trigger = 'all';
+CREATE POLICY pguc ON event_trigger_test USING (FALSE);
+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..c4845a7e6b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -471,3 +471,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();
+
+CREATE POLICY pguc ON event_trigger_test USING (FALSE);
+DROP POLICY pguc ON event_trigger_test;
+SET ignore_event_trigger = 'all';
+CREATE POLICY pguc ON event_trigger_test USING (FALSE);
+DROP POLICY pguc ON event_trigger_test;
--
2.32.1 (Apple Git-133)
On Tue, 29 Nov 2022 at 18:16, Daniel Gustafsson <daniel@yesql.se> wrote:
On 3 Nov 2022, at 21:47, Daniel Gustafsson <daniel@yesql.se> wrote:
The patch adds a new GUC, ignore_event_trigger with two option values, 'all'
and 'none' (the login event patch had 'login' as well).The attached v2 fixes a small bug which caused testfailures the CFBot.
The patch does not apply on top of HEAD as in [1]http://cfbot.cputube.org/patch_41_4013.log, please post a rebased patch:
=== Applying patches on top of PostgreSQL commit ID
5f6401f81cb24bd3930e0dc589fc4aa8b5424cdc ===
=== applying patch
./v2-0001-Add-GUC-for-temporarily-disabling-event-triggers.patch
patching file doc/src/sgml/config.sgml
Hunk #1 succeeded at 9480 (offset 117 lines).
.....
patching file src/backend/utils/misc/postgresql.conf.sample
Hunk #1 FAILED at 701.
1 out of 1 hunk FAILED -- saving rejects to file
src/backend/utils/misc/postgresql.conf.sample.rej
[1]: http://cfbot.cputube.org/patch_41_4013.log
Regards,
Vignesh
On 11 Jan 2023, at 17:38, vignesh C <vignesh21@gmail.com> wrote:
On Tue, 29 Nov 2022 at 18:16, Daniel Gustafsson <daniel@yesql.se> wrote:
On 3 Nov 2022, at 21:47, Daniel Gustafsson <daniel@yesql.se> wrote:
The patch adds a new GUC, ignore_event_trigger with two option values, 'all'
and 'none' (the login event patch had 'login' as well).The attached v2 fixes a small bug which caused testfailures the CFBot.
The patch does not apply on top of HEAD as in [1], please post a rebased patch:
The attached rebased v3 fixes the conflict.
--
Daniel Gustafsson https://vmware.com/
Attachments:
v3-0001-Add-GUC-for-temporarily-disabling-event-triggers.patchapplication/octet-stream; name=v3-0001-Add-GUC-for-temporarily-disabling-event-triggers.patch; x-unix-mode=0644Download
From cfd13b71cf7469d4d72b67d285f44aec9c49a864 Mon Sep 17 00:00:00 2001
From: Daniel Gustafsson <dgustafsson@postgresql.org>
Date: Thu, 12 Jan 2023 21:19:01 +0100
Subject: [PATCH v3] 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
suspending event triggers.
This was extracted from a larger patchset which aimed at supporting
event triggers on login events.
---
doc/src/sgml/config.sgml | 19 +++++++++
doc/src/sgml/ref/create_event_trigger.sgml | 4 +-
src/backend/commands/event_trigger.c | 40 +++++++++++++++----
src/backend/utils/misc/guc_tables.c | 17 ++++++++
src/backend/utils/misc/postgresql.conf.sample | 2 +
src/include/commands/event_trigger.h | 8 ++++
src/test/regress/expected/event_trigger.out | 21 ++++++++++
src/test/regress/sql/event_trigger.sql | 22 ++++++++++
8 files changed, 125 insertions(+), 8 deletions(-)
diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index 77574e2d4e..3686939736 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -9480,6 +9480,25 @@ SET XML OPTION { DOCUMENT | CONTENT };
</listitem>
</varlistentry>
+ <varlistentry id="guc-ignore-event-trigger" xreflabel="ignore_event_trigger">
+ <term><varname>ignore_event_trigger</varname> (<type>enum</type>)
+ <indexterm>
+ <primary><varname>ignore_event_trigger</varname></primary>
+ <secondary>configuration parameter</secondary>
+ </indexterm>
+ </term>
+ <listitem>
+ <para>
+ Allows to temporarily disable event triggers from executing in order
+ to troubleshoot and repair faulty event triggers. The default value
+ is <literal>none</literal>, with which all event triggers are enabled.
+ When set to <literal>all</literal> then all event triggers will be
+ disabled. Only superusers and users with the appropriate
+ <literal>SET</literal> privilege can change this setting.
+ </para>
+ </listitem>
+ </varlistentry>
+
</variablelist>
</sect2>
<sect2 id="runtime-config-client-format">
diff --git a/doc/src/sgml/ref/create_event_trigger.sgml b/doc/src/sgml/ref/create_event_trigger.sgml
index 22c8119198..f6922c3de3 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 <replaceable class="parameter">name</replaceable>
Event triggers are disabled in single-user mode (see <xref
linkend="app-postgres"/>). 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
+ <xref linkend="guc-ignore-event-trigger"/>.
</para>
</refsect1>
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index d4b00d1a82..acd1b9805c 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 */
+int ignore_event_trigger = IGNORE_EVENT_TRIGGER_NONE;
+
/* Support for dropped objects */
typedef struct SQLDropObject
{
@@ -100,6 +103,7 @@ static void validate_table_rewrite_tags(const char *filtervar, List *taglist);
static void EventTriggerInvoke(List *fn_oid_list, EventTriggerData *trigdata);
static const char *stringify_grant_objtype(ObjectType objtype);
static const char *stringify_adefprivs_objtype(ObjectType objtype);
+static bool ignore_event_trigger_check(EventTriggerEvent event);
/*
* Create an event trigger.
@@ -657,8 +661,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 || ignore_event_trigger_check(EVT_DDLCommandStart))
return;
runlist = EventTriggerCommonSetup(parsetree,
@@ -692,9 +699,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 || ignore_event_trigger_check(EVT_DDLCommandEnd))
return;
/*
@@ -740,9 +747,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 || ignore_event_trigger_check(EVT_SQLDrop))
return;
/*
@@ -811,9 +818,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 || ignore_event_trigger_check(EVT_TableRewrite))
return;
/*
@@ -2175,3 +2182,22 @@ stringify_adefprivs_objtype(ObjectType objtype)
return "???"; /* keep compiler quiet */
}
+
+
+/*
+ * Checks whether the specified event is ignored by the ignore_event_trigger
+ * GUC or not. Currently, the GUC only supports ignoring all or nothing but
+ * that might change so the function takes an event to aid that.
+ */
+static bool
+ignore_event_trigger_check(EventTriggerEvent event)
+{
+ (void) event; /* unused parameter */
+
+ if (ignore_event_trigger == IGNORE_EVENT_TRIGGER_ALL)
+ return true;
+
+ /* IGNORE_EVENT_TRIGGER_NONE */
+ return false;
+
+}
diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c
index 5025e80f89..db4f1ed2e1 100644
--- a/src/backend/utils/misc/guc_tables.c
+++ b/src/backend/utils/misc/guc_tables.c
@@ -36,6 +36,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"
@@ -452,6 +453,12 @@ static const struct config_enum_entry wal_compression_options[] = {
{NULL, 0, false}
};
+static const struct config_enum_entry ignore_event_trigger_options[] = {
+ {"none", IGNORE_EVENT_TRIGGER_NONE, false},
+ {"all", IGNORE_EVENT_TRIGGER_ALL, false},
+ {NULL, 0, false}
+};
+
/*
* Options for enum values stored in other modules
*/
@@ -4759,6 +4766,16 @@ struct config_enum ConfigureNamesEnum[] =
NULL, NULL, NULL
},
+ {
+ {"ignore_event_trigger", PGC_SUSET, CLIENT_CONN_STATEMENT,
+ gettext_noop("Disable event triggers for the duration of the session."),
+ NULL
+ },
+ &ignore_event_trigger,
+ IGNORE_EVENT_TRIGGER_NONE, ignore_event_trigger_options,
+ NULL, NULL, NULL
+ },
+
{
{"wal_level", PGC_POSTMASTER, WAL_SETTINGS,
gettext_noop("Sets the level of information written to the WAL."),
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index 4cceda4162..324254586f 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -704,6 +704,8 @@
#xmloption = 'content'
#gin_pending_list_limit = 4MB
#createrole_self_grant = '' # set and/or inherit
+#ignore_event_trigger = 0
+
# - Locale and Formatting -
diff --git a/src/include/commands/event_trigger.h b/src/include/commands/event_trigger.h
index 5ed6ece555..11510f1858 100644
--- a/src/include/commands/event_trigger.h
+++ b/src/include/commands/event_trigger.h
@@ -29,6 +29,14 @@ typedef struct EventTriggerData
CommandTag tag;
} EventTriggerData;
+typedef enum ignore_event_trigger_events
+{
+ IGNORE_EVENT_TRIGGER_NONE,
+ IGNORE_EVENT_TRIGGER_ALL
+} IgnoreEventTriggersEvents;
+
+extern PGDLLIMPORT int ignore_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 5a10958df5..747ffefaa6 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -614,3 +614,24 @@ 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();
+CREATE POLICY pguc ON event_trigger_test USING (FALSE);
+DROP POLICY pguc ON event_trigger_test;
+NOTICE: DROP POLICY dropped policy
+SET ignore_event_trigger = 'all';
+CREATE POLICY pguc ON event_trigger_test USING (FALSE);
+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..c4845a7e6b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -471,3 +471,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();
+
+CREATE POLICY pguc ON event_trigger_test USING (FALSE);
+DROP POLICY pguc ON event_trigger_test;
+SET ignore_event_trigger = 'all';
+CREATE POLICY pguc ON event_trigger_test USING (FALSE);
+DROP POLICY pguc ON event_trigger_test;
--
2.32.1 (Apple Git-133)
On Thu, Jan 12, 2023 at 12:26 PM Daniel Gustafsson <daniel@yesql.se> wrote:
On 11 Jan 2023, at 17:38, vignesh C <vignesh21@gmail.com> wrote:
On Tue, 29 Nov 2022 at 18:16, Daniel Gustafsson <daniel@yesql.se> wrote:
On 3 Nov 2022, at 21:47, Daniel Gustafsson <daniel@yesql.se> wrote:
The patch adds a new GUC, ignore_event_trigger with two option values,
'all'
and 'none' (the login event patch had 'login' as well).
The attached v2 fixes a small bug which caused testfailures the CFBot.
The patch does not apply on top of HEAD as in [1], please post a rebased
patch:
The attached rebased v3 fixes the conflict.
--
Daniel Gustafsson https://vmware.com/Hi,
`this GUC allows to temporarily suspending event triggers.`
It would be better to mention the name of GUC in the description.
Typo: suspending -> suspend
w.r.t. guc `ignore_event_trigger`, since it is supposed to disable event
triggers for a short period of time, is there mechanism to turn it off
(IGNORE_EVENT_TRIGGER_ALL) automatically ?
Cheers
The following review has been posted through the commitfest application:
make installcheck-world: tested, passed
Implements feature: tested, passed
Spec compliant: tested, passed
Documentation: tested, passed
Hi Daniel,
I have reviewed the patch and I liked it (well I did liked it already since it was a part of login trigger patch previously).
All tests are passed and the manual experiments with all types of event triggers are also passed.
Everything is fine and I think It can be marked as Ready for Committer, although I have one final question.
There is a complete framework for disabling various types of the event triggers separately, but, the list of valid GUC values only include 'all' and 'none'. Why not adding support for all the event trigger types separately? Everything is already there in the patch; the only thing needed is expanding couple of enums. It's cheap in terms of code size and even cheaper in terms of performance. And moreover - it would be a good example for anyone adding new trigger types.
The new status of this patch is: Waiting on Author
On 27 Jan 2023, at 15:00, Mikhail Gribkov <youzhick@gmail.com> wrote:
There is a complete framework for disabling various types of the event triggers separately, but, the list of valid GUC values only include 'all' and 'none'. Why not adding support for all the event trigger types separately? Everything is already there in the patch; the only thing needed is expanding couple of enums. It's cheap in terms of code size and even cheaper in terms of performance. And moreover - it would be a good example for anyone adding new trigger types.
I can't exactly recall my reasoning, but I do think you're right that if we're
to have this GUC it should support the types of existing EVTs. The updated v4
implements that as well as a rebase on top of master and fixing a typo
discovered upthread.
--
Daniel Gustafsson
Attachments:
v4-0001-Add-GUC-for-temporarily-disabling-event-triggers.patchapplication/octet-stream; name=v4-0001-Add-GUC-for-temporarily-disabling-event-triggers.patch; x-unix-mode=0644Download
From 238b0717b0f4371c3e1d84fc0d0857004baec38b Mon Sep 17 00:00:00 2001
From: Daniel Gustafsson <dgustafsson@postgresql.org>
Date: Mon, 6 Mar 2023 13:15:58 +0100
Subject: [PATCH v4] 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 <yuzhihong@gmail.com>
Reviewed-by: Mikhail Gribkov <youzhick@gmail.com>
Discussion: https://postgr.es/m/9140106E-F9BF-4D85-8FC8-F2D3C094A6D9@yesql.se
---
doc/src/sgml/config.sgml | 23 ++++++++
doc/src/sgml/ref/create_event_trigger.sgml | 4 +-
src/backend/commands/event_trigger.c | 53 ++++++++++++++++---
src/backend/utils/misc/guc_tables.c | 21 ++++++++
src/backend/utils/misc/postgresql.conf.sample | 1 +
src/include/commands/event_trigger.h | 12 +++++
src/test/regress/expected/event_trigger.out | 29 ++++++++++
src/test/regress/sql/event_trigger.sql | 32 +++++++++++
8 files changed, 167 insertions(+), 8 deletions(-)
diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index e5c41cc6c6..27c63a8afd 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -9519,6 +9519,29 @@ SET XML OPTION { DOCUMENT | CONTENT };
</listitem>
</varlistentry>
+ <varlistentry id="guc-ignore-event-trigger" xreflabel="ignore_event_trigger">
+ <term><varname>ignore_event_trigger</varname> (<type>enum</type>)
+ <indexterm>
+ <primary><varname>ignore_event_trigger</varname></primary>
+ <secondary>configuration parameter</secondary>
+ </indexterm>
+ </term>
+ <listitem>
+ <para>
+ Allows to temporarily disable event triggers from executing in order
+ to troubleshoot and repair faulty event triggers. The value matches
+ the type of event trigger to be ignored:
+ <literal>ddl_command_start</literal>, <literal>ddl_command_end</literal>,
+ <literal>table_rewrite</literal> and <literal>sql_drop</literal>.
+ Additionally, all event triggers can be disabled by setting it to
+ <literal>all</literal>. Setting the value to <literal>none</literal>
+ will ensure that all event triggers are enabled, this is the default
+ value. Only superusers and users with the appropriate
+ <literal>SET</literal> privilege can change this setting.
+ </para>
+ </listitem>
+ </varlistentry>
+
</variablelist>
</sect2>
<sect2 id="runtime-config-client-format">
diff --git a/doc/src/sgml/ref/create_event_trigger.sgml b/doc/src/sgml/ref/create_event_trigger.sgml
index 22c8119198..f6922c3de3 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 <replaceable class="parameter">name</replaceable>
Event triggers are disabled in single-user mode (see <xref
linkend="app-postgres"/>). 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
+ <xref linkend="guc-ignore-event-trigger"/>.
</para>
</refsect1>
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index d4b00d1a82..e37fffd21b 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 */
+int ignore_event_trigger = IGNORE_EVENT_TRIGGER_NONE;
+
/* Support for dropped objects */
typedef struct SQLDropObject
{
@@ -100,6 +103,7 @@ static void validate_table_rewrite_tags(const char *filtervar, List *taglist);
static void EventTriggerInvoke(List *fn_oid_list, EventTriggerData *trigdata);
static const char *stringify_grant_objtype(ObjectType objtype);
static const char *stringify_adefprivs_objtype(ObjectType objtype);
+static bool ignore_event_trigger_check(EventTriggerEvent event);
/*
* Create an event trigger.
@@ -657,8 +661,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 || ignore_event_trigger_check(EVT_DDLCommandStart))
return;
runlist = EventTriggerCommonSetup(parsetree,
@@ -692,9 +699,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 || ignore_event_trigger_check(EVT_DDLCommandEnd))
return;
/*
@@ -740,9 +747,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 || ignore_event_trigger_check(EVT_SQLDrop))
return;
/*
@@ -811,9 +818,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 || ignore_event_trigger_check(EVT_TableRewrite))
return;
/*
@@ -2175,3 +2182,35 @@ stringify_adefprivs_objtype(ObjectType objtype)
return "???"; /* keep compiler quiet */
}
+
+
+/*
+ * Checks whether the specified event is ignored by the ignore_event_trigger
+ * GUC or not.
+ */
+static bool
+ignore_event_trigger_check(EventTriggerEvent event)
+{
+ if (ignore_event_trigger == IGNORE_EVENT_TRIGGER_NONE)
+ return false;
+
+ if (ignore_event_trigger == IGNORE_EVENT_TRIGGER_ALL)
+ return true;
+
+ switch (event)
+ {
+ case EVT_DDLCommandStart:
+ return ignore_event_trigger == IGNORE_EVENT_TRIGGER_DDL_START;
+ case EVT_DDLCommandEnd:
+ return ignore_event_trigger == IGNORE_EVENT_TRIGGER_DDL_END;
+ case EVT_SQLDrop:
+ return ignore_event_trigger == IGNORE_EVENT_TRIGGER_SQL_DROP;
+ case EVT_TableRewrite:
+ return ignore_event_trigger == IGNORE_EVENT_TRIGGER_TABLE_REWRITE;
+
+ default:
+ elog(ERROR, "unsupport event trigger: %d", event);
+ }
+
+ return false; /* unreachable */
+}
diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c
index 1c0583fe26..970fab3e2e 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"
@@ -453,6 +454,16 @@ static const struct config_enum_entry wal_compression_options[] = {
{NULL, 0, false}
};
+static const struct config_enum_entry ignore_event_trigger_options[] = {
+ {"none", IGNORE_EVENT_TRIGGER_NONE, false},
+ {"all", IGNORE_EVENT_TRIGGER_ALL, false},
+ {"ddl_command_start", IGNORE_EVENT_TRIGGER_DDL_START, false},
+ {"ddl_command_end", IGNORE_EVENT_TRIGGER_DDL_END, false},
+ {"table_rewrite", IGNORE_EVENT_TRIGGER_TABLE_REWRITE, false},
+ {"sql_drop", IGNORE_EVENT_TRIGGER_SQL_DROP, false},
+ {NULL, 0, false}
+};
+
/*
* Options for enum values stored in other modules
*/
@@ -4771,6 +4782,16 @@ struct config_enum ConfigureNamesEnum[] =
NULL, NULL, NULL
},
+ {
+ {"ignore_event_trigger", PGC_SUSET, CLIENT_CONN_STATEMENT,
+ gettext_noop("Disable event triggers for the duration of the session."),
+ NULL
+ },
+ &ignore_event_trigger,
+ IGNORE_EVENT_TRIGGER_NONE, ignore_event_trigger_options,
+ NULL, NULL, NULL
+ },
+
{
{"wal_level", PGC_POSTMASTER, WAL_SETTINGS,
gettext_noop("Sets the level of information written to the WAL."),
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index d06074b86f..12be5f0acd 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -705,6 +705,7 @@
#xmloption = 'content'
#gin_pending_list_limit = 4MB
#createrole_self_grant = '' # set and/or inherit
+#ignore_event_trigger = none
# - Locale and Formatting -
diff --git a/src/include/commands/event_trigger.h b/src/include/commands/event_trigger.h
index 5ed6ece555..85b93a8320 100644
--- a/src/include/commands/event_trigger.h
+++ b/src/include/commands/event_trigger.h
@@ -29,6 +29,18 @@ typedef struct EventTriggerData
CommandTag tag;
} EventTriggerData;
+typedef enum ignore_event_trigger_events
+{
+ IGNORE_EVENT_TRIGGER_NONE,
+ IGNORE_EVENT_TRIGGER_DDL_START,
+ IGNORE_EVENT_TRIGGER_DDL_END,
+ IGNORE_EVENT_TRIGGER_TABLE_REWRITE,
+ IGNORE_EVENT_TRIGGER_SQL_DROP,
+ IGNORE_EVENT_TRIGGER_ALL
+} IgnoreEventTriggersEvents;
+
+extern PGDLLIMPORT int ignore_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 5a10958df5..34c3b80e4f 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -614,3 +614,32 @@ 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 ignore_event_trigger = 'none';
+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 ignore_event_trigger = 'sql_drop';
+DROP POLICY pguc ON event_trigger_test;
+CREATE POLICY pguc ON event_trigger_test USING (FALSE);
+SET ignore_event_trigger = 'all';
+DROP POLICY pguc ON event_trigger_test;
+CREATE POLICY pguc ON event_trigger_test USING (FALSE);
+SET ignore_event_trigger = 'ddl_command_start';
+DROP POLICY pguc ON event_trigger_test;
+NOTICE: DROP POLICY dropped policy
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 1aeaddbe71..eb18d23fd3 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -471,3 +471,35 @@ 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 ignore_event_trigger = 'none';
+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 ignore_event_trigger = 'sql_drop';
+DROP POLICY pguc ON event_trigger_test;
+
+CREATE POLICY pguc ON event_trigger_test USING (FALSE);
+SET ignore_event_trigger = 'all';
+DROP POLICY pguc ON event_trigger_test;
+
+CREATE POLICY pguc ON event_trigger_test USING (FALSE);
+SET ignore_event_trigger = 'ddl_command_start';
+DROP POLICY pguc ON event_trigger_test;
--
2.32.1 (Apple Git-133)
The following review has been posted through the commitfest application:
make installcheck-world: tested, passed
Implements feature: tested, passed
Spec compliant: tested, passed
Documentation: tested, passed
I like it now.
* The patch does what it intends to do;
* The implementation way is clear;
* All test are passed;
* No additional problems catched - at least by my eyes;
I think it can be marked as Ready for Committer
N.B. In fact I've encountered couple of failed tests during installcheck-world, although the same fails are there even for master branch. Thus doesn't seem to be this patch issue.
The new status of this patch is: Ready for Committer
On 7 Mar 2023, at 16:02, Mikhail Gribkov <youzhick@gmail.com> wrote:
* The patch does what it intends to do;
* The implementation way is clear;
* All test are passed;
* No additional problems catched - at least by my eyes;I think it can be marked as Ready for Committer
This patch has been RFC for some time, and has been all green in the CFbot. I
would like to go ahead with it this cycle since it gives a tool for admins to
avoid single-user mode - which is something we want to move away from. Even
though login event triggers aren't going in (which is where this originated),
there are still lots of ways to break things with other ev triggers.
Any objections?
--
Daniel Gustafsson
On Mon, Mar 06, 2023 at 01:24:56PM +0100, Daniel Gustafsson wrote:
On 27 Jan 2023, at 15:00, Mikhail Gribkov <youzhick@gmail.com> wrote:
There is a complete framework for disabling various types of the event triggers separately, but, the list of valid GUC values only include 'all' and 'none'. Why not adding support for all the event trigger types separately? Everything is already there in the patch; the only thing needed is expanding couple of enums. It's cheap in terms of code size and even cheaper in terms of performance. And moreover - it would be a good example for anyone adding new trigger types.
I can't exactly recall my reasoning, but I do think you're right that if we're
to have this GUC it should support the types of existing EVTs. The updated v4
implements that as well as a rebase on top of master and fixing a typo
discovered upthread.
+ gettext_noop("Disable event triggers for the duration of the session."),
Why does is it say "for the duration of the session" ?
It's possible to disable ignoring, and within the same session.
GUCs are typically "for the duration of the session" .. but they don't
say so (and don't need to).
+ elog(ERROR, "unsupport event trigger: %d", event);
typo: unsupported
+ Allows to temporarily disable event triggers from executing in order
=> Allow temporarily disabling execution of event triggers ..
+ to troubleshoot and repair faulty event triggers. The value matches
+ the type of event trigger to be ignored:
+ <literal>ddl_command_start</literal>, <literal>ddl_command_end</literal>,
+ <literal>table_rewrite</literal> and <literal>sql_drop</literal>.
+ Additionally, all event triggers can be disabled by setting it to
+ <literal>all</literal>. Setting the value to <literal>none</literal>
+ will ensure that all event triggers are enabled, this is the default
It doesn't technically "ensure that they're enabled", since they can be
disabled by ALTER. Better to say that it "doesn't disable any even triggers".
--
Justin
On 2 Apr 2023, at 21:48, Justin Pryzby <pryzby@telsasoft.com> wrote:
+ gettext_noop("Disable event triggers for the duration of the session."),
Why does is it say "for the duration of the session" ?
It's possible to disable ignoring, and within the same session.
GUCs are typically "for the duration of the session" .. but they don't
say so (and don't need to).+ elog(ERROR, "unsupport event trigger: %d", event);
typo: unsupported
+ Allows to temporarily disable event triggers from executing in order
=> Allow temporarily disabling execution of event triggers ..
+ to troubleshoot and repair faulty event triggers. The value matches + the type of event trigger to be ignored: + <literal>ddl_command_start</literal>, <literal>ddl_command_end</literal>, + <literal>table_rewrite</literal> and <literal>sql_drop</literal>. + Additionally, all event triggers can be disabled by setting it to + <literal>all</literal>. Setting the value to <literal>none</literal> + will ensure that all event triggers are enabled, this is the defaultIt doesn't technically "ensure that they're enabled", since they can be
disabled by ALTER. Better to say that it "doesn't disable any even triggers".
All comments above addressed in the attached v5, thanks for review!
--
Daniel Gustafsson
Attachments:
v5-0001-Add-GUC-for-temporarily-disabling-event-triggers.patchapplication/octet-stream; name=v5-0001-Add-GUC-for-temporarily-disabling-event-triggers.patch; x-unix-mode=0644Download
From a775df518b78a82a2d95637c07709bb8a7821cb4 Mon Sep 17 00:00:00 2001
From: Daniel Gustafsson <dgustafsson@postgresql.org>
Date: Mon, 3 Apr 2023 14:33:03 +0200
Subject: [PATCH v5] 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 <yuzhihong@gmail.com>
Reviewed-by: Mikhail Gribkov <youzhick@gmail.com>
Reviewed-by: Justin Pryzby <pryzby@telsasoft.com>
Discussion: https://postgr.es/m/9140106E-F9BF-4D85-8FC8-F2D3C094A6D9@yesql.se
---
doc/src/sgml/config.sgml | 23 ++++++++
doc/src/sgml/ref/create_event_trigger.sgml | 4 +-
src/backend/commands/event_trigger.c | 53 ++++++++++++++++---
src/backend/utils/misc/guc_tables.c | 21 ++++++++
src/backend/utils/misc/postgresql.conf.sample | 1 +
src/include/commands/event_trigger.h | 12 +++++
src/test/regress/expected/event_trigger.out | 29 ++++++++++
src/test/regress/sql/event_trigger.sql | 32 +++++++++++
8 files changed, 167 insertions(+), 8 deletions(-)
diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index fcb53c6997..989ab42701 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -9547,6 +9547,29 @@ SET XML OPTION { DOCUMENT | CONTENT };
</listitem>
</varlistentry>
+ <varlistentry id="guc-ignore-event-trigger" xreflabel="ignore_event_trigger">
+ <term><varname>ignore_event_trigger</varname> (<type>enum</type>)
+ <indexterm>
+ <primary><varname>ignore_event_trigger</varname></primary>
+ <secondary>configuration parameter</secondary>
+ </indexterm>
+ </term>
+ <listitem>
+ <para>
+ Allow temporarily disabling execution of event triggers in order
+ to troubleshoot and repair faulty event triggers. The value matches
+ the type of event trigger to be ignored:
+ <literal>ddl_command_start</literal>, <literal>ddl_command_end</literal>,
+ <literal>table_rewrite</literal> and <literal>sql_drop</literal>.
+ Additionally, all event triggers can be disabled by setting it to
+ <literal>all</literal>. Setting the value to <literal>none</literal>
+ will not disable any event triggers, this is the default value. Only
+ superusers and users with the appropriate <literal>SET</literal>
+ privilege can change this setting.
+ </para>
+ </listitem>
+ </varlistentry>
+
</variablelist>
</sect2>
<sect2 id="runtime-config-client-format">
diff --git a/doc/src/sgml/ref/create_event_trigger.sgml b/doc/src/sgml/ref/create_event_trigger.sgml
index 22c8119198..f6922c3de3 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 <replaceable class="parameter">name</replaceable>
Event triggers are disabled in single-user mode (see <xref
linkend="app-postgres"/>). 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
+ <xref linkend="guc-ignore-event-trigger"/>.
</para>
</refsect1>
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index d4b00d1a82..0eaf1f4553 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 */
+int ignore_event_trigger = IGNORE_EVENT_TRIGGER_NONE;
+
/* Support for dropped objects */
typedef struct SQLDropObject
{
@@ -100,6 +103,7 @@ static void validate_table_rewrite_tags(const char *filtervar, List *taglist);
static void EventTriggerInvoke(List *fn_oid_list, EventTriggerData *trigdata);
static const char *stringify_grant_objtype(ObjectType objtype);
static const char *stringify_adefprivs_objtype(ObjectType objtype);
+static bool ignore_event_trigger_check(EventTriggerEvent event);
/*
* Create an event trigger.
@@ -657,8 +661,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 || ignore_event_trigger_check(EVT_DDLCommandStart))
return;
runlist = EventTriggerCommonSetup(parsetree,
@@ -692,9 +699,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 || ignore_event_trigger_check(EVT_DDLCommandEnd))
return;
/*
@@ -740,9 +747,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 || ignore_event_trigger_check(EVT_SQLDrop))
return;
/*
@@ -811,9 +818,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 || ignore_event_trigger_check(EVT_TableRewrite))
return;
/*
@@ -2175,3 +2182,35 @@ stringify_adefprivs_objtype(ObjectType objtype)
return "???"; /* keep compiler quiet */
}
+
+
+/*
+ * Checks whether the specified event is ignored by the ignore_event_trigger
+ * GUC or not.
+ */
+static bool
+ignore_event_trigger_check(EventTriggerEvent event)
+{
+ if (ignore_event_trigger == IGNORE_EVENT_TRIGGER_NONE)
+ return false;
+
+ if (ignore_event_trigger == IGNORE_EVENT_TRIGGER_ALL)
+ return true;
+
+ switch (event)
+ {
+ case EVT_DDLCommandStart:
+ return ignore_event_trigger == IGNORE_EVENT_TRIGGER_DDL_START;
+ case EVT_DDLCommandEnd:
+ return ignore_event_trigger == IGNORE_EVENT_TRIGGER_DDL_END;
+ case EVT_SQLDrop:
+ return ignore_event_trigger == IGNORE_EVENT_TRIGGER_SQL_DROP;
+ case EVT_TableRewrite:
+ return ignore_event_trigger == IGNORE_EVENT_TRIGGER_TABLE_REWRITE;
+
+ default:
+ elog(ERROR, "unsupported event trigger: %d", (int) event);
+ }
+
+ return false; /* unreachable */
+}
diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c
index 8062589efd..377a755782 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"
@@ -471,6 +472,16 @@ static const struct config_enum_entry wal_compression_options[] = {
{NULL, 0, false}
};
+static const struct config_enum_entry ignore_event_trigger_options[] = {
+ {"none", IGNORE_EVENT_TRIGGER_NONE, false},
+ {"all", IGNORE_EVENT_TRIGGER_ALL, false},
+ {"ddl_command_start", IGNORE_EVENT_TRIGGER_DDL_START, false},
+ {"ddl_command_end", IGNORE_EVENT_TRIGGER_DDL_END, false},
+ {"table_rewrite", IGNORE_EVENT_TRIGGER_TABLE_REWRITE, false},
+ {"sql_drop", IGNORE_EVENT_TRIGGER_SQL_DROP, false},
+ {NULL, 0, false}
+};
+
/*
* Options for enum values stored in other modules
*/
@@ -4810,6 +4821,16 @@ struct config_enum ConfigureNamesEnum[] =
NULL, NULL, NULL
},
+ {
+ {"ignore_event_trigger", PGC_SUSET, CLIENT_CONN_STATEMENT,
+ gettext_noop("Disable event triggers."),
+ NULL
+ },
+ &ignore_event_trigger,
+ IGNORE_EVENT_TRIGGER_NONE, ignore_event_trigger_options,
+ NULL, NULL, NULL
+ },
+
{
{"wal_level", PGC_POSTMASTER, WAL_SETTINGS,
gettext_noop("Sets the level of information written to the WAL."),
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index ee49ca3937..b17a98eba5 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -706,6 +706,7 @@
#xmloption = 'content'
#gin_pending_list_limit = 4MB
#createrole_self_grant = '' # set and/or inherit
+#ignore_event_trigger = none
# - Locale and Formatting -
diff --git a/src/include/commands/event_trigger.h b/src/include/commands/event_trigger.h
index 5ed6ece555..85b93a8320 100644
--- a/src/include/commands/event_trigger.h
+++ b/src/include/commands/event_trigger.h
@@ -29,6 +29,18 @@ typedef struct EventTriggerData
CommandTag tag;
} EventTriggerData;
+typedef enum ignore_event_trigger_events
+{
+ IGNORE_EVENT_TRIGGER_NONE,
+ IGNORE_EVENT_TRIGGER_DDL_START,
+ IGNORE_EVENT_TRIGGER_DDL_END,
+ IGNORE_EVENT_TRIGGER_TABLE_REWRITE,
+ IGNORE_EVENT_TRIGGER_SQL_DROP,
+ IGNORE_EVENT_TRIGGER_ALL
+} IgnoreEventTriggersEvents;
+
+extern PGDLLIMPORT int ignore_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 5a10958df5..34c3b80e4f 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -614,3 +614,32 @@ 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 ignore_event_trigger = 'none';
+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 ignore_event_trigger = 'sql_drop';
+DROP POLICY pguc ON event_trigger_test;
+CREATE POLICY pguc ON event_trigger_test USING (FALSE);
+SET ignore_event_trigger = 'all';
+DROP POLICY pguc ON event_trigger_test;
+CREATE POLICY pguc ON event_trigger_test USING (FALSE);
+SET ignore_event_trigger = 'ddl_command_start';
+DROP POLICY pguc ON event_trigger_test;
+NOTICE: DROP POLICY dropped policy
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 1aeaddbe71..eb18d23fd3 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -471,3 +471,35 @@ 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 ignore_event_trigger = 'none';
+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 ignore_event_trigger = 'sql_drop';
+DROP POLICY pguc ON event_trigger_test;
+
+CREATE POLICY pguc ON event_trigger_test USING (FALSE);
+SET ignore_event_trigger = 'all';
+DROP POLICY pguc ON event_trigger_test;
+
+CREATE POLICY pguc ON event_trigger_test USING (FALSE);
+SET ignore_event_trigger = 'ddl_command_start';
+DROP POLICY pguc ON event_trigger_test;
--
2.32.1 (Apple Git-133)
On Mon, Apr 3, 2023 at 8:46 AM Daniel Gustafsson <daniel@yesql.se> wrote:
All comments above addressed in the attached v5, thanks for review!
I continue to think it's odd that the sense of this is inverted as
compared with row_security.
--
Robert Haas
EDB: http://www.enterprisedb.com
On 3 Apr 2023, at 15:09, Robert Haas <robertmhaas@gmail.com> wrote:
On Mon, Apr 3, 2023 at 8:46 AM Daniel Gustafsson <daniel@yesql.se> wrote:
All comments above addressed in the attached v5, thanks for review!
I continue to think it's odd that the sense of this is inverted as
compared with row_security.
I'm not sure I follow. Do you propose that the GUC enables classes of event
triggers, the default being "all" (or similar) and one would remove the type of
EVT for which debugging is needed? That doesn't seem like a bad idea, just one
that hasn't come up in the discussion (and I didn't think about).
--
Daniel Gustafsson
On Mon, Apr 3, 2023 at 9:15 AM Daniel Gustafsson <daniel@yesql.se> wrote:
On 3 Apr 2023, at 15:09, Robert Haas <robertmhaas@gmail.com> wrote:
On Mon, Apr 3, 2023 at 8:46 AM Daniel Gustafsson <daniel@yesql.se> wrote:All comments above addressed in the attached v5, thanks for review!
I continue to think it's odd that the sense of this is inverted as
compared with row_security.I'm not sure I follow. Do you propose that the GUC enables classes of event
triggers, the default being "all" (or similar) and one would remove the type of
EVT for which debugging is needed? That doesn't seem like a bad idea, just one
that hasn't come up in the discussion (and I didn't think about).
Right. Although to be fair, that idea doesn't sound as good if we're
going to have settings other than "on" or "off". If this is just
disable_event_triggers = on | off, then why not flip the sense around
and make it event_triggers = off | on, just as we do for row_security?
But if we're going to allow specific types of event triggers to be
disabled, and we think it's likely that people will want to disable
one specific type of event trigger while leaving the others alone,
that might not be very convenient, because you could end up having to
list all the things you do want instead of the one thing you don't
want. On the third hand, in other contexts, I've often advocating for
giving options a positive sense (what are we going to do?) rather than
a negative sense (what are we not going to do?). For example, the
TIMING option to EXPLAIN was originally proposed with a name like
DISABLE_TIMING or something, and the value inverted, and I said let's
not do that. And similarly in other places. A case where I didn't
follow that principle is VACUUM (DISABLE_PAGE_SKIPPING) which now
seems like a wart to me. Why isn't it VACUUM (PAGE_SKIPPING) again
with the opposite value?
I'm not sure what the best thing to do is here, I just think it
deserves some thought.
--
Robert Haas
EDB: http://www.enterprisedb.com
On 3 Apr 2023, at 16:09, Robert Haas <robertmhaas@gmail.com> wrote:
On Mon, Apr 3, 2023 at 9:15 AM Daniel Gustafsson <daniel@yesql.se> wrote:On 3 Apr 2023, at 15:09, Robert Haas <robertmhaas@gmail.com> wrote:
I continue to think it's odd that the sense of this is inverted as
compared with row_security.I'm not sure I follow. Do you propose that the GUC enables classes of event
triggers, the default being "all" (or similar) and one would remove the type of
EVT for which debugging is needed? That doesn't seem like a bad idea, just one
that hasn't come up in the discussion (and I didn't think about).Right. Although to be fair, that idea doesn't sound as good if we're
going to have settings other than "on" or "off".
Yeah. The patch as it stands allow for disabling specific types rather than
all-or-nothing, which is why the name was "ignore".
I'm not sure what the best thing to do is here, I just think it
deserves some thought.
Absolutely, the discussion is much appreciated. Having done some thinking I
think I'm still partial to framing it as a disabling GUC rather than an
enabling; with the act of setting it being "As an admin I want to skip
execution of all evt's of type X".
--
Daniel Gustafsson
On Mon, Apr 03, 2023 at 11:35:14PM +0200, Daniel Gustafsson wrote:
Yeah. The patch as it stands allow for disabling specific types rather than
all-or-nothing, which is why the name was "ignore".
FWIW, I agree with Robert's points here:
- disable_event_triggers or ignore_event_triggers = off leads to a
double-negative meaning, which is a positive. Depending on one's
native language that can be confusing.
- Being able to write a list of event triggers working would be much
more interesting than just individual elements.
- There may be an argument for negated patterns? Say,
a "!sql_drop,!ddl_command_start" would cause sql_drop and
ddl_command_start to be disabled with all the others enabled, and one
should not ne able to mix negated and non-negated patterns.
A few days before the end of the commit fest, perhaps you'd better
head towards having only an event_trigger = on | off or all | none and
consider expanding that later on? From what I get at the top of the
thread, this would satisfy the main use case you seemed to worry
about to begin with.
--
Michael
On 5 Apr 2023, at 10:10, Michael Paquier <michael@paquier.xyz> wrote:
On Mon, Apr 03, 2023 at 11:35:14PM +0200, Daniel Gustafsson wrote:
Yeah. The patch as it stands allow for disabling specific types rather than
all-or-nothing, which is why the name was "ignore".FWIW, I agree with Robert's points here:
- disable_event_triggers or ignore_event_triggers = off leads to a
double-negative meaning, which is a positive. Depending on one's
native language that can be confusing.
I agree that ignore=off would be suboptimal, but the patch doesn't have that
but instead ignore_event_trigger=none for that case, which I personally don't
think carries the same issue.
- Being able to write a list of event triggers working would be much
more interesting than just individual elements.
- There may be an argument for negated patterns? Say,
a "!sql_drop,!ddl_command_start" would cause sql_drop and
ddl_command_start to be disabled with all the others enabled, and one
should not ne able to mix negated and non-negated patterns.
I'm not convinced that it's in our interest to offer a GUC to configure the
cluster by selectively turning off SQL features. The ones we have for planner
tuning which is a different beast. At the very least it should be in a thread
covering that topic, as it might be a bit hidden here.
The use case envisioned here is to allow an admin to log in to a database with
a broken EVT without having to use single user mode. Basically, it should be a
convenient way of temporarily halting the execution of buggy code, not a
vehicle for permanent cluster config (even though in light of the above para
it's clear that it can be misused like that). Maybe there should be a log
event highlighting that the cluster is running with an EVT type ignored?
And/or logging the names of the EVT's that otherwise would've been executed?
A few days before the end of the commit fest, perhaps you'd better
head towards having only an event_trigger = on | off or all | none and
consider expanding that later on? From what I get at the top of the
thread, this would satisfy the main use case you seemed to worry
about to begin with.
If there are concerns with any part of the patch at this point, and the
comments above indicate that, I'd say it's better to push this to the v17
cycle.
--
Daniel Gustafsson
On Wed, Apr 5, 2023 at 4:57 AM Daniel Gustafsson <daniel@yesql.se> wrote:
- Being able to write a list of event triggers working would be much
more interesting than just individual elements.
- There may be an argument for negated patterns? Say,
a "!sql_drop,!ddl_command_start" would cause sql_drop and
ddl_command_start to be disabled with all the others enabled, and one
should not ne able to mix negated and non-negated patterns.I'm not convinced that it's in our interest to offer a GUC to configure the
cluster by selectively turning off SQL features. The ones we have for planner
tuning which is a different beast. At the very least it should be in a thread
covering that topic, as it might be a bit hidden here.
But, isn't that exactly what you're proposing?
I mean if this was just event_triggers = on | off it would be exactly
like row_security and as far as I'm concerned there would be nothing
to debate. But it sounded like you wanted something finer-grained that
could disable certain kinds of event triggers. That's also what
MIchael is proposing, just with different syntax. In other words,
where you would say ignore_event_triggers = sql_drop, he'd say
event_triggers = !sql_drop.
Maybe we should back up and ask why we need more than "on" and "off".
If somebody is using this feature in any form more than very
occasionally, they should really go home and reconsider their database
schema.
--
Robert Haas
EDB: http://www.enterprisedb.com
Robert Haas <robertmhaas@gmail.com> writes:
Maybe we should back up and ask why we need more than "on" and "off".
If somebody is using this feature in any form more than very
occasionally, they should really go home and reconsider their database
schema.
+1 ... this seems perhaps overdesigned.
regards, tom lane
On 5 Apr 2023, at 16:30, Robert Haas <robertmhaas@gmail.com> wrote:
On Wed, Apr 5, 2023 at 4:57 AM Daniel Gustafsson <daniel@yesql.se> wrote:
- Being able to write a list of event triggers working would be much
more interesting than just individual elements.
- There may be an argument for negated patterns? Say,
a "!sql_drop,!ddl_command_start" would cause sql_drop and
ddl_command_start to be disabled with all the others enabled, and one
should not ne able to mix negated and non-negated patterns.I'm not convinced that it's in our interest to offer a GUC to configure the
cluster by selectively turning off SQL features. The ones we have for planner
tuning which is a different beast. At the very least it should be in a thread
covering that topic, as it might be a bit hidden here.But, isn't that exactly what you're proposing?
Yeah, but it didn't really strike me until after typing and sending that email.
I mean if this was just event_triggers = on | off it would be exactly
like row_security and as far as I'm concerned there would be nothing
to debate.
Which is what v1-v3 did until I changed it based on review input, but I agree
with the reasoning here so will revert back (with some internal changes too).
Moving this to the next CF for another stab at it when the tree re-opens.
--
Daniel Gustafsson
On Wed, Apr 05, 2023 at 10:43:23AM -0400, Tom Lane wrote:
Robert Haas <robertmhaas@gmail.com> writes:
Maybe we should back up and ask why we need more than "on" and "off".
If somebody is using this feature in any form more than very
occasionally, they should really go home and reconsider their database
schema.+1 ... this seems perhaps overdesigned.
Yes. If you begin with an "on"/"off" switch, it could always be
extended later if someone makes a case for it, with a grammar like one
I mentioned upthread, or even something else. If there is no strong
case for more than a boolean for now, simpler is better.
--
Michael
On 6 Apr 2023, at 00:06, Michael Paquier <michael@paquier.xyz> wrote:
If there is no strong
case for more than a boolean for now, simpler is better.
The attached version of the patch replaces it with a boolean flag for turning
off all event triggers, and I also renamed it to the debug_xxx "GUC namespace"
which seemed more appropriate.
--
Daniel Gustafsson
Attachments:
v6-0001-Add-GUC-for-temporarily-disabling-event-triggers.patchapplication/octet-stream; name=v6-0001-Add-GUC-for-temporarily-disabling-event-triggers.patch; x-unix-mode=0644Download
From 004a2f0753877ab93b149ea38c1c0ed0661b1f2e Mon Sep 17 00:00:00 2001
From: Daniel Gustafsson <dgustafsson@postgresql.org>
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 <yuzhihong@gmail.com>
Reviewed-by: Mikhail Gribkov <youzhick@gmail.com>
Reviewed-by: Justin Pryzby <pryzby@telsasoft.com>
Reviewed-by: Michael Paquier <michael@paquier.xyz
Discussion: https://postgr.es/m/9140106E-F9BF-4D85-8FC8-F2D3C094A6D9@yesql.se
---
doc/src/sgml/config.sgml | 19 ++++++++++++++++
doc/src/sgml/ref/create_event_trigger.sgml | 4 +++-
src/backend/commands/event_trigger.c | 20 +++++++++++------
src/backend/utils/misc/guc_tables.c | 12 +++++++++++
src/include/commands/event_trigger.h | 2 ++
src/test/regress/expected/event_trigger.out | 22 +++++++++++++++++++
src/test/regress/sql/event_trigger.sql | 24 +++++++++++++++++++++
7 files changed, 95 insertions(+), 8 deletions(-)
diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index 694d667bf9..988494fafa 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -9494,6 +9494,25 @@ SET XML OPTION { DOCUMENT | CONTENT };
</listitem>
</varlistentry>
+ <varlistentry id="guc-ignore-event-trigger" xreflabel="debug_event_trigger">
+ <term><varname>debug_event_trigger</varname> (<type>boolean</type>)
+ <indexterm>
+ <primary><varname>debug_event_trigger</varname></primary>
+ <secondary>configuration parameter</secondary>
+ </indexterm>
+ </term>
+ <listitem>
+ <para>
+ 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 <literal>true</literal>. Setting the value
+ to <literal>false</literal> will not disable any event triggers, this
+ is the default value. Only superusers and users with the appropriate
+ <literal>SET</literal> privilege can change this setting.
+ </para>
+ </listitem>
+ </varlistentry>
+
</variablelist>
</sect2>
<sect2 id="runtime-config-client-format">
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 <replaceable class="parameter">name</replaceable>
Event triggers are disabled in single-user mode (see <xref
linkend="app-postgres"/>). 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
+ <xref linkend="guc-debug-event-trigger"/>.
</para>
</refsect1>
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)
On Tue, Sep 5, 2023 at 8:12 AM Daniel Gustafsson <daniel@yesql.se> wrote:
On 6 Apr 2023, at 00:06, Michael Paquier <michael@paquier.xyz> wrote:
If there is no strong
case for more than a boolean for now, simpler is better.The attached version of the patch replaces it with a boolean flag for turning
off all event triggers, and I also renamed it to the debug_xxx "GUC namespace"
which seemed more appropriate.
I don't care for the debug_xxx renaming, myself. I think that should
be reserved for things where we believe there's no reason to ever use
it in production/real life, or for things whose purpose is to emit
debugging messages. Neither is the case here.
--
Robert Haas
EDB: http://www.enterprisedb.com
On 5 Sep 2023, at 17:29, Robert Haas <robertmhaas@gmail.com> wrote:
On Tue, Sep 5, 2023 at 8:12 AM Daniel Gustafsson <daniel@yesql.se> wrote:
The attached version of the patch replaces it with a boolean flag for turning
off all event triggers, and I also renamed it to the debug_xxx "GUC namespace"
which seemed more appropriate.I don't care for the debug_xxx renaming, myself. I think that should
be reserved for things where we believe there's no reason to ever use
it in production/real life, or for things whose purpose is to emit
debugging messages. Neither is the case here.
Fair enough, how about disable_event_trigger instead as per the attached?
--
Daniel Gustafsson
Attachments:
v7-0001-Add-GUC-for-temporarily-disabling-event-triggers.patchapplication/octet-stream; name=v7-0001-Add-GUC-for-temporarily-disabling-event-triggers.patch; x-unix-mode=0644Download
From 955a44b40e67e93fbf40b3158819accd9f059b59 Mon Sep 17 00:00:00 2001
From: Daniel Gustafsson <dgustafsson@postgresql.org>
Date: Wed, 6 Sep 2023 10:48:50 +0200
Subject: [PATCH v7] 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 <yuzhihong@gmail.com>
Reviewed-by: Mikhail Gribkov <youzhick@gmail.com>
Reviewed-by: Justin Pryzby <pryzby@telsasoft.com>
Reviewed-by: Michael Paquier <michael@paquier.xyz
Reviewed-by: Robert Haas <robertmhaas@gmail.com>
Discussion: https://postgr.es/m/9140106E-F9BF-4D85-8FC8-F2D3C094A6D9@yesql.se
---
doc/src/sgml/config.sgml | 19 ++++++++++++++++
doc/src/sgml/ref/create_event_trigger.sgml | 4 +++-
src/backend/commands/event_trigger.c | 20 +++++++++++------
src/backend/utils/misc/guc_tables.c | 12 +++++++++++
src/include/commands/event_trigger.h | 2 ++
src/test/regress/expected/event_trigger.out | 22 +++++++++++++++++++
src/test/regress/sql/event_trigger.sql | 24 +++++++++++++++++++++
7 files changed, 95 insertions(+), 8 deletions(-)
diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index f0a50a5f9a..4f4128b234 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -9425,6 +9425,25 @@ SET XML OPTION { DOCUMENT | CONTENT };
</listitem>
</varlistentry>
+ <varlistentry id="guc-disable-event-trigger" xreflabel="disable_event_trigger">
+ <term><varname>disable_event_trigger</varname> (<type>boolean</type>)
+ <indexterm>
+ <primary><varname>disable_event_trigger</varname></primary>
+ <secondary>configuration parameter</secondary>
+ </indexterm>
+ </term>
+ <listitem>
+ <para>
+ 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 <literal>true</literal>. Setting the value
+ to <literal>false</literal> will not disable any event triggers, this
+ is the default value. Only superusers and users with the appropriate
+ <literal>SET</literal> privilege can change this setting.
+ </para>
+ </listitem>
+ </varlistentry>
+
</variablelist>
</sect2>
<sect2 id="runtime-config-client-format">
diff --git a/doc/src/sgml/ref/create_event_trigger.sgml b/doc/src/sgml/ref/create_event_trigger.sgml
index 22c8119198..62c02a454f 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 <replaceable class="parameter">name</replaceable>
Event triggers are disabled in single-user mode (see <xref
linkend="app-postgres"/>). 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
+ <xref linkend="guc-disable-event-trigger"/>.
</para>
</refsect1>
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index d4b00d1a82..42eb1d1a32 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 disable_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 || disable_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 || disable_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 || disable_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 || disable_event_trigger)
return;
/*
diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c
index 4107d0a735..b705b26d3d 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
},
+ {
+ {"disable_event_trigger", PGC_SUSET, CLIENT_CONN_STATEMENT,
+ gettext_noop("Enables event triggers."),
+ NULL,
+ GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
+ },
+ &disable_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..08cdb0ea57 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 disable_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..5a20bb0daa 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 disable_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 disable_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..f1e356d775 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 disable_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 disable_event_trigger = 'true';
+DROP POLICY pguc ON event_trigger_test;
--
2.32.1 (Apple Git-133)
On Wed, Sep 6, 2023 at 4:50 AM Daniel Gustafsson <daniel@yesql.se> wrote:
On 5 Sep 2023, at 17:29, Robert Haas <robertmhaas@gmail.com> wrote:
On Tue, Sep 5, 2023 at 8:12 AM Daniel Gustafsson <daniel@yesql.se> wrote:The attached version of the patch replaces it with a boolean flag for turning
off all event triggers, and I also renamed it to the debug_xxx "GUC namespace"
which seemed more appropriate.I don't care for the debug_xxx renaming, myself. I think that should
be reserved for things where we believe there's no reason to ever use
it in production/real life, or for things whose purpose is to emit
debugging messages. Neither is the case here.Fair enough, how about disable_event_trigger instead as per the attached?
I usually prefer to give things a positive sense, talking about
whether things are enabled rather than disabled. I'd do event_triggers
= off | on, like we have for row_security. YMMV, though.
--
Robert Haas
EDB: http://www.enterprisedb.com
On 6 Sep 2023, at 16:22, Robert Haas <robertmhaas@gmail.com> wrote:
On Wed, Sep 6, 2023 at 4:50 AM Daniel Gustafsson <daniel@yesql.se> wrote:
Fair enough, how about disable_event_trigger instead as per the attached?
I usually prefer to give things a positive sense, talking about
whether things are enabled rather than disabled. I'd do event_triggers
= off | on, like we have for row_security. YMMV, though.
Fair enough, I don't have strong opinions and I do agree that making this work
like row_security is a good thing for consistency. Done in the attached.
--
Daniel Gustafsson
Attachments:
v8-0001-Add-GUC-for-temporarily-disabling-event-triggers.patchapplication/octet-stream; name=v8-0001-Add-GUC-for-temporarily-disabling-event-triggers.patch; x-unix-mode=0644Download
From cc6f85ecbf4c8d5036036d6f0f30cdca017fa556 Mon Sep 17 00:00:00 2001
From: Daniel Gustafsson <dgustafsson@postgresql.org>
Date: Wed, 6 Sep 2023 10:48:50 +0200
Subject: [PATCH v8] 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 <yuzhihong@gmail.com>
Reviewed-by: Mikhail Gribkov <youzhick@gmail.com>
Reviewed-by: Justin Pryzby <pryzby@telsasoft.com>
Reviewed-by: Michael Paquier <michael@paquier.xyz
Reviewed-by: Robert Haas <robertmhaas@gmail.com>
Discussion: https://postgr.es/m/9140106E-F9BF-4D85-8FC8-F2D3C094A6D9@yesql.se
---
doc/src/sgml/config.sgml | 19 ++++++++++++++++
doc/src/sgml/ref/create_event_trigger.sgml | 4 +++-
src/backend/commands/event_trigger.c | 20 +++++++++++------
src/backend/utils/misc/guc_tables.c | 12 +++++++++++
src/include/commands/event_trigger.h | 2 ++
src/test/regress/expected/event_trigger.out | 22 +++++++++++++++++++
src/test/regress/sql/event_trigger.sql | 24 +++++++++++++++++++++
7 files changed, 95 insertions(+), 8 deletions(-)
diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index f0a50a5f9a..ade6e89d4e 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -9425,6 +9425,25 @@ SET XML OPTION { DOCUMENT | CONTENT };
</listitem>
</varlistentry>
+ <varlistentry id="guc-event-triggers" xreflabel="event_triggers">
+ <term><varname>event_triggers</varname> (<type>boolean</type>)
+ <indexterm>
+ <primary><varname>event_triggers</varname></primary>
+ <secondary>configuration parameter</secondary>
+ </indexterm>
+ </term>
+ <listitem>
+ <para>
+ 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 <literal>true</literal>. Setting the value
+ to <literal>false</literal> will not disable any event triggers, this
+ is the default value. Only superusers and users with the appropriate
+ <literal>SET</literal> privilege can change this setting.
+ </para>
+ </listitem>
+ </varlistentry>
+
</variablelist>
</sect2>
<sect2 id="runtime-config-client-format">
diff --git a/doc/src/sgml/ref/create_event_trigger.sgml b/doc/src/sgml/ref/create_event_trigger.sgml
index 22c8119198..f4683570b5 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 <replaceable class="parameter">name</replaceable>
Event triggers are disabled in single-user mode (see <xref
linkend="app-postgres"/>). 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
+ <xref linkend="guc-event-triggers"/>.
</para>
</refsect1>
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index d4b00d1a82..bd812e42d9 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 event_triggers = true;
+
/* 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 || !event_triggers)
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 || !event_triggers)
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 || !event_triggers)
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 || !event_triggers)
return;
/*
diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c
index 4107d0a735..5c88180a9c 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
},
+ {
+ {"event_triggers", PGC_SUSET, CLIENT_CONN_STATEMENT,
+ gettext_noop("Enables event triggers."),
+ NULL,
+ GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
+ },
+ &event_triggers,
+ true,
+ 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..1c925dbf25 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 event_triggers;
+
#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..0b87a42d0a 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 event_triggers = 'on';
+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 event_triggers = 'off';
+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..6f0933b9e8 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 event_triggers = 'on';
+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 event_triggers = 'off';
+DROP POLICY pguc ON event_trigger_test;
--
2.32.1 (Apple Git-133)
On Wed, Sep 06, 2023 at 10:23:55PM +0200, Daniel Gustafsson wrote:
On 6 Sep 2023, at 16:22, Robert Haas <robertmhaas@gmail.com> wrote:
I usually prefer to give things a positive sense, talking about
whether things are enabled rather than disabled. I'd do event_triggers
= off | on, like we have for row_security. YMMV, though.Fair enough, I don't have strong opinions and I do agree that making this work
like row_security is a good thing for consistency. Done in the attached.
This point has been raised a couple of months ago:
/messages/by-id/ZC0s+BRMqRupDInQ@paquier.xyz
+SET event_triggers = 'on';
+CREATE POLICY pguc ON event_trigger_test USING (FALSE);
+DROP POLICY pguc ON event_trigger_test;
This provides checks for the start, end and drop events. Shouldn't
table_rewrite also be covered?
+ GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
I am a bit surprised by these two additions. Setting this GUC at
file-level can be useful, as is documenting it in the control file if
it provides some control of how a statement behaves, no?
+ 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 <literal>true</literal>. Setting the value
+ to <literal>false</literal> will not disable any event triggers, this
+ is the default value. Only superusers and users with the appropriate
+ <literal>SET</literal> privilege can change this setting.
Event triggers are disabled if setting this GUC to false, while true,
the default, allows event triggers. The values are reversed in this
description.
--
Michael
On Thu, Sep 7, 2023 at 1:57 AM Michael Paquier <michael@paquier.xyz> wrote:
+ GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
I am a bit surprised by these two additions. Setting this GUC at
file-level can be useful, as is documenting it in the control file if
it provides some control of how a statement behaves, no?
Yeah, I don't think these options should be used.
+ 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 <literal>true</literal>. Setting the value + to <literal>false</literal> will not disable any event triggers, this + is the default value. Only superusers and users with the appropriate + <literal>SET</literal> privilege can change this setting.Event triggers are disabled if setting this GUC to false, while true,
the default, allows event triggers. The values are reversed in this
description.
Woops.
--
Robert Haas
EDB: http://www.enterprisedb.com
On 7 Sep 2023, at 21:02, Robert Haas <robertmhaas@gmail.com> wrote:
On Thu, Sep 7, 2023 at 1:57 AM Michael Paquier <michael@paquier.xyz> wrote:
+ GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
I am a bit surprised by these two additions. Setting this GUC at
file-level can be useful, as is documenting it in the control file if
it provides some control of how a statement behaves, no?Yeah, I don't think these options should be used.
Removed.
+ 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 <literal>true</literal>. Setting the value + to <literal>false</literal> will not disable any event triggers, this + is the default value. Only superusers and users with the appropriate + <literal>SET</literal> privilege can change this setting.Event triggers are disabled if setting this GUC to false, while true,
the default, allows event triggers. The values are reversed in this
description.Woops.
Fixed.
Since the main driver for this is to reduce the usage/need for single-user mode
I also reworded the patch slightly. Instead of phrasing this as an alternative
to single-user mode, I reversed it such that single-user mode is an alternative
to this GUC.
--
Daniel Gustafsson
Attachments:
v9-0001-Add-GUC-for-temporarily-disabling-event-triggers.patchapplication/octet-stream; name=v9-0001-Add-GUC-for-temporarily-disabling-event-triggers.patch; x-unix-mode=0644Download
From 6449c90a8febde3cc5c42cb89fa04909edd8593b Mon Sep 17 00:00:00 2001
From: Daniel Gustafsson <dgustafsson@postgresql.org>
Date: Fri, 22 Sep 2023 16:39:07 +0200
Subject: [PATCH v9] 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
(or even recommended) 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 <yuzhihong@gmail.com>
Reviewed-by: Mikhail Gribkov <youzhick@gmail.com>
Reviewed-by: Justin Pryzby <pryzby@telsasoft.com>
Reviewed-by: Michael Paquier <michael@paquier.xyz
Reviewed-by: Robert Haas <robertmhaas@gmail.com>
Discussion: https://postgr.es/m/9140106E-F9BF-4D85-8FC8-F2D3C094A6D9@yesql.se
---
doc/src/sgml/config.sgml | 19 ++++++++++++++++
doc/src/sgml/ref/create_event_trigger.sgml | 9 +++++---
src/backend/commands/event_trigger.c | 20 +++++++++++------
src/backend/utils/misc/guc_tables.c | 11 ++++++++++
src/include/commands/event_trigger.h | 2 ++
src/test/regress/expected/event_trigger.out | 22 +++++++++++++++++++
src/test/regress/sql/event_trigger.sql | 24 +++++++++++++++++++++
7 files changed, 97 insertions(+), 10 deletions(-)
diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index 6bc1b215db..2bb3b35cf5 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -9425,6 +9425,25 @@ SET XML OPTION { DOCUMENT | CONTENT };
</listitem>
</varlistentry>
+ <varlistentry id="guc-event-triggers" xreflabel="event_triggers">
+ <term><varname>event_triggers</varname> (<type>boolean</type>)
+ <indexterm>
+ <primary><varname>event_triggers</varname></primary>
+ <secondary>configuration parameter</secondary>
+ </indexterm>
+ </term>
+ <listitem>
+ <para>
+ 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 <literal>false</literal>. Setting the value
+ to <literal>true</literal> will not disable any event triggers, this
+ is the default value. Only superusers and users with the appropriate
+ <literal>SET</literal> privilege can change this setting.
+ </para>
+ </listitem>
+ </varlistentry>
+
</variablelist>
</sect2>
<sect2 id="runtime-config-client-format">
diff --git a/doc/src/sgml/ref/create_event_trigger.sgml b/doc/src/sgml/ref/create_event_trigger.sgml
index 22c8119198..ef12cfa20d 100644
--- a/doc/src/sgml/ref/create_event_trigger.sgml
+++ b/doc/src/sgml/ref/create_event_trigger.sgml
@@ -121,9 +121,12 @@ CREATE EVENT TRIGGER <replaceable class="parameter">name</replaceable>
<para>
Event triggers are disabled in single-user mode (see <xref
- linkend="app-postgres"/>). 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.
+ linkend="app-postgres"/>) as well as when
+ <xref linkend="guc-event-triggers"/> is set to <literal>false</literal>.
+ If an erroneous event trigger disables the database so much that you can't
+ even drop the trigger, restart with <xref linkend="guc-event-triggers"/>
+ set to <literal>false</literal> to temporarily disable event triggers, or
+ in single-user mode, and you'll be able to do that.
</para>
</refsect1>
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index d4b00d1a82..bd812e42d9 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 event_triggers = true;
+
/* 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 || !event_triggers)
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 || !event_triggers)
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 || !event_triggers)
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 || !event_triggers)
return;
/*
diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c
index bdb26e2b77..16ec6c5ef0 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"
@@ -2000,6 +2001,16 @@ struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
+ {
+ {"event_triggers", PGC_SUSET, CLIENT_CONN_STATEMENT,
+ gettext_noop("Enables event triggers."),
+ gettext_noop("When enabled, event triggers will fire for all applicable statements."),
+ },
+ &event_triggers,
+ true,
+ 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..1c925dbf25 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 event_triggers;
+
#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..0b87a42d0a 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 event_triggers = 'on';
+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 event_triggers = 'off';
+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..6f0933b9e8 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 event_triggers = 'on';
+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 event_triggers = 'off';
+DROP POLICY pguc ON event_trigger_test;
--
2.32.1 (Apple Git-133)
On Fri, Sep 22, 2023 at 05:29:01PM +0200, Daniel Gustafsson wrote:
Since the main driver for this is to reduce the usage/need for single-user mode
I also reworded the patch slightly. Instead of phrasing this as an alternative
to single-user mode, I reversed it such that single-user mode is an alternative
to this GUC.
Okay.
+ be disabled by setting it to <literal>false</literal>. Setting the value
+ to <literal>true</literal> will not disable any event triggers, this
This uses a double negation. Perhaps just "Setting this value to true
allows all event triggers to run."
003_check_guc.pl has detected a failure because event_triggers is
missing in postgresql.conf.sample while it is not marked with
GUC_NOT_IN_SAMPLE anymore.
Keeping the docs consistent with the sample file, I would suggest the
attached on top of your v9.
--
Michael
Attachments:
v9-0001-Add-GUC-for-temporarily-disabling-event-triggers.patchtext/x-diff; charset=us-asciiDownload
From 2ad4989a71c40b7d6de8fcb095b057377cd05bf5 Mon Sep 17 00:00:00 2001
From: Daniel Gustafsson <dgustafsson@postgresql.org>
Date: Fri, 22 Sep 2023 16:39:07 +0200
Subject: [PATCH v9 1/2] 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
(or even recommended) 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 <yuzhihong@gmail.com>
Reviewed-by: Mikhail Gribkov <youzhick@gmail.com>
Reviewed-by: Justin Pryzby <pryzby@telsasoft.com>
Reviewed-by: Michael Paquier <michael@paquier.xyz
Reviewed-by: Robert Haas <robertmhaas@gmail.com>
Discussion: https://postgr.es/m/9140106E-F9BF-4D85-8FC8-F2D3C094A6D9@yesql.se
---
src/include/commands/event_trigger.h | 2 ++
src/backend/commands/event_trigger.c | 20 +++++++++++------
src/backend/utils/misc/guc_tables.c | 11 ++++++++++
src/test/regress/expected/event_trigger.out | 22 +++++++++++++++++++
src/test/regress/sql/event_trigger.sql | 24 +++++++++++++++++++++
doc/src/sgml/config.sgml | 19 ++++++++++++++++
doc/src/sgml/ref/create_event_trigger.sgml | 9 +++++---
7 files changed, 97 insertions(+), 10 deletions(-)
diff --git a/src/include/commands/event_trigger.h b/src/include/commands/event_trigger.h
index 5ed6ece555..1c925dbf25 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 event_triggers;
+
#define AT_REWRITE_ALTER_PERSISTENCE 0x01
#define AT_REWRITE_DEFAULT_VAL 0x02
#define AT_REWRITE_COLUMN_REWRITE 0x04
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index d4b00d1a82..bd812e42d9 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 event_triggers = true;
+
/* 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 || !event_triggers)
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 || !event_triggers)
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 || !event_triggers)
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 || !event_triggers)
return;
/*
diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c
index bdb26e2b77..16ec6c5ef0 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"
@@ -2000,6 +2001,16 @@ struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
+ {
+ {"event_triggers", PGC_SUSET, CLIENT_CONN_STATEMENT,
+ gettext_noop("Enables event triggers."),
+ gettext_noop("When enabled, event triggers will fire for all applicable statements."),
+ },
+ &event_triggers,
+ true,
+ NULL, NULL, NULL
+ },
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, false, NULL, NULL, NULL
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 2c8a6b2212..0b87a42d0a 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 event_triggers = 'on';
+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 event_triggers = 'off';
+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..6f0933b9e8 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 event_triggers = 'on';
+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 event_triggers = 'off';
+DROP POLICY pguc ON event_trigger_test;
diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index 6bc1b215db..2bb3b35cf5 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -9425,6 +9425,25 @@ SET XML OPTION { DOCUMENT | CONTENT };
</listitem>
</varlistentry>
+ <varlistentry id="guc-event-triggers" xreflabel="event_triggers">
+ <term><varname>event_triggers</varname> (<type>boolean</type>)
+ <indexterm>
+ <primary><varname>event_triggers</varname></primary>
+ <secondary>configuration parameter</secondary>
+ </indexterm>
+ </term>
+ <listitem>
+ <para>
+ 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 <literal>false</literal>. Setting the value
+ to <literal>true</literal> will not disable any event triggers, this
+ is the default value. Only superusers and users with the appropriate
+ <literal>SET</literal> privilege can change this setting.
+ </para>
+ </listitem>
+ </varlistentry>
+
</variablelist>
</sect2>
<sect2 id="runtime-config-client-format">
diff --git a/doc/src/sgml/ref/create_event_trigger.sgml b/doc/src/sgml/ref/create_event_trigger.sgml
index 22c8119198..ef12cfa20d 100644
--- a/doc/src/sgml/ref/create_event_trigger.sgml
+++ b/doc/src/sgml/ref/create_event_trigger.sgml
@@ -121,9 +121,12 @@ CREATE EVENT TRIGGER <replaceable class="parameter">name</replaceable>
<para>
Event triggers are disabled in single-user mode (see <xref
- linkend="app-postgres"/>). 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.
+ linkend="app-postgres"/>) as well as when
+ <xref linkend="guc-event-triggers"/> is set to <literal>false</literal>.
+ If an erroneous event trigger disables the database so much that you can't
+ even drop the trigger, restart with <xref linkend="guc-event-triggers"/>
+ set to <literal>false</literal> to temporarily disable event triggers, or
+ in single-user mode, and you'll be able to do that.
</para>
</refsect1>
--
2.40.1
v9-0002-Fix-failure-in-TAP-tests.patchtext/x-diff; charset=us-asciiDownload
From bd5ce7bbfc2b57f17ae647390e2de461b19e710a Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@paquier.xyz>
Date: Mon, 25 Sep 2023 08:18:34 +0900
Subject: [PATCH v9 2/2] Fix failure in TAP tests.
---
src/backend/utils/misc/postgresql.conf.sample | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index 6bb39d39ba..d08d55c3fe 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -706,6 +706,7 @@
#xmloption = 'content'
#gin_pending_list_limit = 4MB
#createrole_self_grant = '' # set and/or inherit
+#event_triggers = on
# - Locale and Formatting -
--
2.40.1
On 25 Sep 2023, at 01:35, Michael Paquier <michael@paquier.xyz> wrote:
On Fri, Sep 22, 2023 at 05:29:01PM +0200, Daniel Gustafsson wrote:
Since the main driver for this is to reduce the usage/need for single-user mode
I also reworded the patch slightly. Instead of phrasing this as an alternative
to single-user mode, I reversed it such that single-user mode is an alternative
to this GUC.Okay.
+ be disabled by setting it to <literal>false</literal>. Setting the value + to <literal>true</literal> will not disable any event triggers, thisThis uses a double negation. Perhaps just "Setting this value to true
allows all event triggers to run."
Fair enough, although I used "fire" instead of "run" which is consistent with
the event trigger documentation.
003_check_guc.pl has detected a failure because event_triggers is
missing in postgresql.conf.sample while it is not marked with
GUC_NOT_IN_SAMPLE anymore.Keeping the docs consistent with the sample file, I would suggest the
attached on top of your v9.
Ah, yes.
The attached v10 has the above to fixes.
--
Daniel Gustafsson
Attachments:
v10-0001-Add-GUC-for-temporarily-disabling-event-triggers.patchapplication/octet-stream; name=v10-0001-Add-GUC-for-temporarily-disabling-event-triggers.patch; x-unix-mode=0644Download
From b33efe85e2186b53681aaaff65348265a5fd897d Mon Sep 17 00:00:00 2001
From: Daniel Gustafsson <dgustafsson@postgresql.org>
Date: Fri, 22 Sep 2023 16:39:07 +0200
Subject: [PATCH v10] 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
(or even recommended) 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 <yuzhihong@gmail.com>
Reviewed-by: Mikhail Gribkov <youzhick@gmail.com>
Reviewed-by: Justin Pryzby <pryzby@telsasoft.com>
Reviewed-by: Michael Paquier <michael@paquier.xyz
Reviewed-by: Robert Haas <robertmhaas@gmail.com>
Discussion: https://postgr.es/m/9140106E-F9BF-4D85-8FC8-F2D3C094A6D9@yesql.se
---
doc/src/sgml/config.sgml | 19 +++++++++++++++
doc/src/sgml/ref/create_event_trigger.sgml | 9 ++++---
src/backend/commands/event_trigger.c | 20 ++++++++++------
src/backend/utils/misc/guc_tables.c | 11 +++++++++
src/backend/utils/misc/postgresql.conf.sample | 1 +
src/include/commands/event_trigger.h | 2 ++
src/test/regress/expected/event_trigger.out | 22 +++++++++++++++++
src/test/regress/sql/event_trigger.sql | 24 +++++++++++++++++++
8 files changed, 98 insertions(+), 10 deletions(-)
diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index 6bc1b215db..d42b7d63ee 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -9425,6 +9425,25 @@ SET XML OPTION { DOCUMENT | CONTENT };
</listitem>
</varlistentry>
+ <varlistentry id="guc-event-triggers" xreflabel="event_triggers">
+ <term><varname>event_triggers</varname> (<type>boolean</type>)
+ <indexterm>
+ <primary><varname>event_triggers</varname></primary>
+ <secondary>configuration parameter</secondary>
+ </indexterm>
+ </term>
+ <listitem>
+ <para>
+ 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 <literal>false</literal>. Setting the value
+ to <literal>true</literal> allows all event triggers to fire, this
+ is the default value. Only superusers and users with the appropriate
+ <literal>SET</literal> privilege can change this setting.
+ </para>
+ </listitem>
+ </varlistentry>
+
</variablelist>
</sect2>
<sect2 id="runtime-config-client-format">
diff --git a/doc/src/sgml/ref/create_event_trigger.sgml b/doc/src/sgml/ref/create_event_trigger.sgml
index 22c8119198..ef12cfa20d 100644
--- a/doc/src/sgml/ref/create_event_trigger.sgml
+++ b/doc/src/sgml/ref/create_event_trigger.sgml
@@ -121,9 +121,12 @@ CREATE EVENT TRIGGER <replaceable class="parameter">name</replaceable>
<para>
Event triggers are disabled in single-user mode (see <xref
- linkend="app-postgres"/>). 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.
+ linkend="app-postgres"/>) as well as when
+ <xref linkend="guc-event-triggers"/> is set to <literal>false</literal>.
+ If an erroneous event trigger disables the database so much that you can't
+ even drop the trigger, restart with <xref linkend="guc-event-triggers"/>
+ set to <literal>false</literal> to temporarily disable event triggers, or
+ in single-user mode, and you'll be able to do that.
</para>
</refsect1>
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index d4b00d1a82..bd812e42d9 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 event_triggers = true;
+
/* 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 || !event_triggers)
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 || !event_triggers)
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 || !event_triggers)
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 || !event_triggers)
return;
/*
diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c
index bdb26e2b77..16ec6c5ef0 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"
@@ -2000,6 +2001,16 @@ struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
+ {
+ {"event_triggers", PGC_SUSET, CLIENT_CONN_STATEMENT,
+ gettext_noop("Enables event triggers."),
+ gettext_noop("When enabled, event triggers will fire for all applicable statements."),
+ },
+ &event_triggers,
+ true,
+ NULL, NULL, NULL
+ },
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, false, NULL, NULL, NULL
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index 6bb39d39ba..d08d55c3fe 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -706,6 +706,7 @@
#xmloption = 'content'
#gin_pending_list_limit = 4MB
#createrole_self_grant = '' # set and/or inherit
+#event_triggers = on
# - Locale and Formatting -
diff --git a/src/include/commands/event_trigger.h b/src/include/commands/event_trigger.h
index 5ed6ece555..1c925dbf25 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 event_triggers;
+
#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..0b87a42d0a 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 event_triggers = 'on';
+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 event_triggers = 'off';
+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..6f0933b9e8 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 event_triggers = 'on';
+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 event_triggers = 'off';
+DROP POLICY pguc ON event_trigger_test;
--
2.32.1 (Apple Git-133)
On Mon, Sep 25, 2023 at 09:19:35AM +0200, Daniel Gustafsson wrote:
Fair enough, although I used "fire" instead of "run" which is consistent with
the event trigger documentation.
Okay by me.
--
Michael
On 25 Sep 2023, at 09:50, Michael Paquier <michael@paquier.xyz> wrote:
On Mon, Sep 25, 2023 at 09:19:35AM +0200, Daniel Gustafsson wrote:
Fair enough, although I used "fire" instead of "run" which is consistent with
the event trigger documentation.Okay by me.
Great, I'll go ahead and apply this version then. Thanks for review!
--
Daniel Gustafsson
On 25 Sep 2023, at 09:52, Daniel Gustafsson <daniel@yesql.se> wrote:
On 25 Sep 2023, at 09:50, Michael Paquier <michael@paquier.xyz> wrote:
On Mon, Sep 25, 2023 at 09:19:35AM +0200, Daniel Gustafsson wrote:
Fair enough, although I used "fire" instead of "run" which is consistent with
the event trigger documentation.Okay by me.
Great, I'll go ahead and apply this version then. Thanks for review!
And applied, closing the CF entry.
--
Daniel Gustafsson