From 4703d041168ab6448971f1e1a5c9fd39169015bc Mon Sep 17 00:00:00 2001
From: Jelte Fennema-Nio <jelte.fennema@microsoft.com>
Date: Fri, 5 Jan 2024 14:45:04 +0100
Subject: [PATCH v4 4/7] Prepare server code for addition of protocol
 extensions

This commit adds a new GUC flag that should be set on protocol
extensions parameters: GUC_PROTOCOL_EXTENSION. GUCs with this flag can
only be configured by being part of the StartupMessage (i.e.
PGC_S_CLIENT).

The reason they are not allowed to be set at the server configuration is
because these parameters control protocol behaviour, and such changes
should be purely opt-in by a client. Otherwise it's very likely that
clients will break that don't support this protocol feature.

The reason these parameters are not allowed to be changed using regular
SET/SET LOCAL commands is because otherwise the behaviour of the
protocol might by changed by someone with only SQL access without the
client realizing. Even a client/pooler that parses queries and detects
SET commands is unable to detect all GUC changes, since a function call
might change GUCs internally.
---
 src/backend/postmaster/postmaster.c |  8 ++++----
 src/backend/utils/misc/guc.c        | 13 +++++++++++--
 src/include/utils/guc.h             |  3 +++
 src/include/utils/guc_tables.h      |  1 +
 4 files changed, 19 insertions(+), 6 deletions(-)

diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index feb471dd1df..0c14b830c30 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -124,6 +124,7 @@
 #include "tcop/tcopprot.h"
 #include "utils/builtins.h"
 #include "utils/datetime.h"
+#include "utils/guc_tables.h"
 #include "utils/memutils.h"
 #include "utils/pidfile.h"
 #include "utils/ps_status.h"
@@ -2210,12 +2211,11 @@ retry1:
 									valptr),
 							 errhint("Valid values are: \"false\", 0, \"true\", 1, \"database\".")));
 			}
-			else if (strncmp(nameptr, "_pq_.", 5) == 0)
+			else if (strncmp(nameptr, "_pq_.", 5) == 0 && !find_option(nameptr, false, true, ERROR))
 			{
 				/*
-				 * Any option beginning with _pq_. is reserved for use as a
-				 * protocol-level option, but at present no such options are
-				 * defined.
+				 * We report unkown protocol extensions using the
+				 * NegotiateProtocolVersion message instead of erroring
 				 */
 				unrecognized_protocol_options =
 					lappend(unrecognized_protocol_options, pstrdup(nameptr));
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 8f65ef3d896..5998e9d656a 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -2011,7 +2011,7 @@ ResetAllOptions(void)
 			gconf->context != PGC_USERSET)
 			continue;
 		/* Don't reset if special exclusion from RESET ALL */
-		if (gconf->flags & GUC_NO_RESET_ALL)
+		if (gconf->flags & (GUC_NO_RESET_ALL | GUC_PROTOCOL_EXTENSION))
 			continue;
 		/* No need to reset if wasn't SET */
 		if (gconf->source <= PGC_S_OVERRIDE)
@@ -3573,6 +3573,14 @@ set_config_with_handle(const char *name, config_handle *handle,
 			break;
 	}
 
+	if (record->flags & GUC_PROTOCOL_EXTENSION && source != PGC_S_CLIENT)
+	{
+		ereport(elevel,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 errmsg("parameter can only be set at the protocol level \"%s\"", name)));
+		return 0;
+	}
+
 	/*
 	 * Disallow changing GUC_NOT_WHILE_SEC_REST values if we are inside a
 	 * security restriction context.  We can reject this regardless of the GUC
@@ -4606,7 +4614,8 @@ AlterSystemSetConfigFile(AlterSystemStmt *altersysstmt)
 			 */
 			if ((record->context == PGC_INTERNAL) ||
 				(record->flags & GUC_DISALLOW_IN_FILE) ||
-				(record->flags & GUC_DISALLOW_IN_AUTO_FILE))
+				(record->flags & GUC_DISALLOW_IN_AUTO_FILE) ||
+				(record->flags & GUC_PROTOCOL_EXTENSION))
 				ereport(ERROR,
 						(errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM),
 						 errmsg("parameter \"%s\" cannot be changed",
diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h
index 471d53da8f0..9890d09b3c8 100644
--- a/src/include/utils/guc.h
+++ b/src/include/utils/guc.h
@@ -223,6 +223,9 @@ typedef enum
 #define GUC_DISALLOW_IN_AUTO_FILE \
 							   0x002000 /* can't set in PG_AUTOCONF_FILENAME */
 #define GUC_RUNTIME_COMPUTED   0x004000 /* delay processing in 'postgres -C' */
+#define GUC_PROTOCOL_EXTENSION   \
+							   0x008000 /* only allowed to be set using
+										 * ParameterSet and StartupMessage */
 
 #define GUC_UNIT_KB			 0x01000000 /* value is in kilobytes */
 #define GUC_UNIT_BLOCKS		 0x02000000 /* value is in blocks */
diff --git a/src/include/utils/guc_tables.h b/src/include/utils/guc_tables.h
index 0a2e274ebb2..4bba3dc3ccd 100644
--- a/src/include/utils/guc_tables.h
+++ b/src/include/utils/guc_tables.h
@@ -99,6 +99,7 @@ enum config_group
 	PRESET_OPTIONS,
 	CUSTOM_OPTIONS,
 	DEVELOPER_OPTIONS,
+	PROTOCOL_EXTENSION,
 };
 
 /*
-- 
2.34.1

