From 562c0037d6929c14093254ee859c2630b19aac5d Mon Sep 17 00:00:00 2001
From: Daniel Gustafsson <daniel@yesql.se>
Date: Mon, 14 Mar 2022 14:24:09 +0100
Subject: [PATCH v30 1/2] Provide a GUC for temporarily ignoring event triggers

In order to allow troubleshooting and repair without the need for
restarting in single-user mode, allow to ignore event triggers per
session.
---
 doc/src/sgml/config.sgml                   | 15 ++++++++
 doc/src/sgml/ref/create_event_trigger.sgml |  4 ++-
 src/backend/commands/event_trigger.c       | 40 ++++++++++++++++++----
 src/backend/utils/misc/guc.c               | 17 +++++++++
 src/include/commands/event_trigger.h       |  8 +++++
 5 files changed, 76 insertions(+), 8 deletions(-)

diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index 9788e831bc..84c08035fb 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -9224,6 +9224,21 @@ 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.
+       </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..1a78555c64 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. Even triggers can also be
+   temporarily disabled for such troubleshooting, see
+   <xref linkend="gux-ignore-event-trigger"/>.
   </para>
  </refsect1>
 
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index 3c3fc2515b..70995cfc49 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.
@@ -658,8 +662,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,
@@ -693,9 +700,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;
 
 	/*
@@ -741,9 +748,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;
 
 	/*
@@ -812,9 +819,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 ignoreing all or nothing but
+ * that will most likely 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.c b/src/backend/utils/misc/guc.c
index eb3a03b976..1c5b277fb0 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -47,6 +47,7 @@
 #include "catalog/pg_authid.h"
 #include "catalog/storage.h"
 #include "commands/async.h"
+#include "commands/event_trigger.h"
 #include "commands/prepare.h"
 #include "commands/tablespace.h"
 #include "commands/trigger.h"
@@ -566,6 +567,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
  */
@@ -4917,6 +4924,16 @@ static struct config_enum ConfigureNamesEnum[] =
 		NULL, NULL, NULL
 	},
 
+	{
+		{"ignore_event_trigger", PGC_SUSET, CLIENT_CONN_STATEMENT,
+			gettext_noop("Disable event triggers during 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..9e7671e63f 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 int ignore_event_trigger;
+
 #define AT_REWRITE_ALTER_PERSISTENCE	0x01
 #define AT_REWRITE_DEFAULT_VAL			0x02
 #define AT_REWRITE_COLUMN_REWRITE		0x04
-- 
2.24.3 (Apple Git-128)

