v4-0001-Add-GUC-for-temporarily-disabling-event-triggers.patch
application/octet-stream
Filename: v4-0001-Add-GUC-for-temporarily-disabling-event-triggers.patch
Type: application/octet-stream
Part: 0
Patch
Format: format-patch
Series: patch v4-0001
Subject: Add GUC for temporarily disabling event triggers
| File | + | − |
|---|---|---|
| doc/src/sgml/config.sgml | 23 | 0 |
| doc/src/sgml/ref/create_event_trigger.sgml | 3 | 1 |
| src/backend/commands/event_trigger.c | 46 | 7 |
| src/backend/utils/misc/guc_tables.c | 21 | 0 |
| src/backend/utils/misc/postgresql.conf.sample | 1 | 0 |
| src/include/commands/event_trigger.h | 12 | 0 |
| src/test/regress/expected/event_trigger.out | 29 | 0 |
| src/test/regress/sql/event_trigger.sql | 32 | 0 |
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)