public inbox for [email protected]  
help / color / mirror / Atom feed
From: Zsolt Parragi <[email protected]>
To: Jacob Champion <[email protected]>
Cc: VASUKI M <[email protected]>
Cc: PostgreSQL Hackers <[email protected]>
Cc: [email protected]
Cc: Robert Haas <[email protected]>
Cc: [email protected]
Subject: Re: Custom oauth validator options
Date: Wed, 28 Jan 2026 16:04:49 +0000
Message-ID: <CAN4CZFPmF9fGOcFubwOxqXymhVo_RvbUx3bLoYQcfk=f0mwECw@mail.gmail.com> (raw)
In-Reply-To: <CAOYmi+mS=cpKk_jcuUaJz+noB8Gj=M06e9AdDQYcJRpXDARJYA@mail.gmail.com>
References: <CAN4CZFM3b8u5uNNNsY6XCya257u+Dofms3su9f11iMCxvCacag@mail.gmail.com>
	<CAE2r8H55geNFtECuFunpgn0LJK2+rntGuTeqNr6mP7gGhWFRbA@mail.gmail.com>
	<CAN4CZFP_2fe2-18wUoXDZodV8suVe9o++pv=hP8KxxvWkmCx7A@mail.gmail.com>
	<CAOYmi+kMuA7t9ao6rWZ=5kn_Zmd7qtwOay_ocEBXwkzKWbefhQ@mail.gmail.com>
	<CAE2r8H439jg+e5gXJpNNMoroe4CfWauDRfUBZC_9NUNTOhqzBQ@mail.gmail.com>
	<CAN4CZFN9RMF_79kx75SkQZezd91DocUzz89bJeBJrMO=uNuG2w@mail.gmail.com>
	<CAOYmi+krPZDC8K+9z64M2EY9fELTKzLbqw8fD_wK=87YV+TBgw@mail.gmail.com>
	<CAN4CZFPvjAt+eZJd=Rxp=yXRjva8CpJ_BbnF=vQW6uXCqfrjEg@mail.gmail.com>
	<CAOYmi+nbCrvcE9wLQdNCgMwDbbi_UzGYrzfC54txmMBJ9KxO=Q@mail.gmail.com>
	<CAN4CZFNywvG59B+nBgD1_1fHE2uODBH3EcF_gwLmC7Y5U6Ru4Q@mail.gmail.com>
	<CAOYmi+=-OdzHMzqg9i8TwYvgKwE-vroj0d-9SqnRnwbz02SgTg@mail.gmail.com>
	<CAN4CZFPo1POb9fWMihTACFxE=xSxKEANHRkxN4YbMMN-0SML=w@mail.gmail.com>
	<CAOYmi+kVAiKf=WrnyzGxCmx-uu=arPE0=+Mf_AOhuTzkvCNC2w@mail.gmail.com>
	<CAN4CZFMeTuH4uANV1bOox0d-1mycCnyghY49cL+E8PYZ4Y=0Kw@mail.gmail.com>
	<CAOYmi+nKQ_7+pWSzw=rP2_T9UL=URHBsKq005BDeMmmC_=PV8g@mail.gmail.com>
	<CAN4CZFM8TgqDi=5Bot2imtd2heGESjpMfQ7kW4qeFSjO7NTAQQ@mail.gmail.com>
	<CAOYmi+kVCWCbf+yjmFSeddmqxgYTO5vU+CqwFq6EbpyLkpW=Bw@mail.gmail.com>
	<CAN4CZFNt3WVPsORSFfdZo3nYn9Fa1-CkKKdu3_gVNpw4jbenjg@mail.gmail.com>
	<CAOYmi+n9+VDNayxsZuG30YLxOXrVB2Wu=jBR4WrEdJvxjTATKw@mail.gmail.com>
	<CAN4CZFP2N=+bqJL7PUiy0DR0dGwbdd1Na0rdMCpzdpaH50O87w@mail.gmail.com>
	<CAOYmi+nhh-fChn-8K7HV4kwVwsTm_gVy5jBgUBMqfM6Hm5E4zg@mail.gmail.com>
	<CAN4CZFPz-O+fCeSnwDcSd2vXfRT2tn1jG_BwNahYbyMmsAr2CA@mail.gmail.com>
	<CAOYmi+mS=cpKk_jcuUaJz+noB8Gj=M06e9AdDQYcJRpXDARJYA@mail.gmail.com>

I implemented a DefineCustomValidatorStringVariable PoC - I don't like
it that much. It adds too much boilerplate code for a very specific
feature. If you say we should go with a more limited approach, I think
my earlier simple version is better, because it is simple. I'll also
try to think about other approaches.

And also let me go back to my concern that

> Scoping validators to a specific prefix fixes the collision issue, but
> it also goes in a different direction.

I wrote this because of the simple "guc.some_name" example, as the
fixed guc prefix - and previously I also looked into
MarkGUCPrefixReserved, and I realized that there's no easy way to use
that for enforcing prefixes for a library.

And then I realized that maybe that needs an improvement, the behavior
of MarkGUCPrefixReserved and DefineCustom*Variable seems like a legacy
thing and not something that was intentionally designed that way.

What do you think about the following patches?

0001: defines a new guc, guc_prefix_enforcement that potentially
changes the behavior of prefix reservation. It has a few modes, based
on which missing prefix reservations or variables defined outside the
reserved prefix result in warnings or errors during library load time.
This is unrelated to pgc_hba, and applies to all custom variables.

0002: the same patch as before, with your comment (su_backend,
backend, suset, user can be set in pg_hba) addressed, and also always
enforces proper prefix reservation for pg_hba variables using 0001.

* We don't have to worry about collisions, because prefixes are always
enforced in pg_hba, so people can't "redefine" the fixed key/value
pairs or columns
* It also introduces the idea of enforcing guc prefixes for
extensions. In theory this setting should start with a relaxed default
(I would say warning mode), and changed to strict in a later major
version, enforcing proper guc rules by default. That way, third party
extensions won't be able to define gucs like pam_use_hostname.

I realize that

1. This is also scope creep
2. 0001 probably should be a separate thread/discussion

But I first wanted to ask your opinion about the idea / what do you
think about the interaction of the two patches.


Attachments:

  [application/octet-stream] 0001-Guc-prefix-enforcement.patch (33.9K, 2-0001-Guc-prefix-enforcement.patch)
  download | inline diff:
From 223cb59b2558edcef39e88482c0e0b4f093ba75b Mon Sep 17 00:00:00 2001
From: Zsolt Parragi <[email protected]>
Date: Wed, 28 Jan 2026 15:38:25 +0100
Subject: [PATCH 1/2] Guc prefix enforcement

This patch introduces a new guc variable, guc_prefix_enforcement.
This variable aims to enforce proper naming/structuring of guc
variables, by checking the following conditions:

* libraries should reserve at least one prefix if they define guc
  variables
* if a library defined one or more prefixes, all variables should be
  defined within those prefixes
* libraries shouldn't define variables in prefixes reserved by other
  libraries (even if they don't reserve a prefix)

It has
4 possible values:

* off, which is the existing earlier behavior, it does nothing
* warning, in which violation of any of the above conditions results in
  an appropriate warning message
* prefix, in which case the first condition is still only a warning, but
  the second and third result in an error
* strict, in which case any violation results in an error

The current patch sets the default of this value to off (or maybe it
should be warning?), and later major versions can increase it to strict,
after we are sure that most extensions follow these rules properly.
---
 src/backend/utils/fmgr/dfmgr.c                |  38 ++++
 src/backend/utils/init/miscinit.c             |   4 +
 src/backend/utils/misc/guc.c                  | 167 +++++++++++++++++-
 src/backend/utils/misc/guc_parameters.dat     |   9 +
 src/backend/utils/misc/guc_tables.c           |  13 ++
 src/include/fmgr.h                            |   1 +
 src/include/utils/guc.h                       |  17 ++
 src/include/utils/guc_tables.h                |   2 +
 src/test/modules/Makefile                     |   3 +
 src/test/modules/meson.build                  |   1 +
 .../test_guc_prefix_enforcement/Makefile      |  17 ++
 .../test_guc_prefix_enforcement/meson.build   |  80 +++++++++
 .../t/001_prefix_enforcement.pl               | 161 +++++++++++++++++
 .../test_guc_no_prefix.c                      |  45 +++++
 .../test_guc_no_reserve.c                     |  42 +++++
 .../test_guc_prefix_enforcement.c             |  44 +++++
 .../test_guc_wrong_prefix.c                   |  44 +++++
 17 files changed, 682 insertions(+), 6 deletions(-)
 create mode 100644 src/test/modules/test_guc_prefix_enforcement/Makefile
 create mode 100644 src/test/modules/test_guc_prefix_enforcement/meson.build
 create mode 100644 src/test/modules/test_guc_prefix_enforcement/t/001_prefix_enforcement.pl
 create mode 100644 src/test/modules/test_guc_prefix_enforcement/test_guc_no_prefix.c
 create mode 100644 src/test/modules/test_guc_prefix_enforcement/test_guc_no_reserve.c
 create mode 100644 src/test/modules/test_guc_prefix_enforcement/test_guc_prefix_enforcement.c
 create mode 100644 src/test/modules/test_guc_prefix_enforcement/test_guc_wrong_prefix.c

diff --git a/src/backend/utils/fmgr/dfmgr.c b/src/backend/utils/fmgr/dfmgr.c
index e636cc81cf8..44408f2f697 100644
--- a/src/backend/utils/fmgr/dfmgr.c
+++ b/src/backend/utils/fmgr/dfmgr.c
@@ -59,6 +59,12 @@ struct DynamicFileList
 static DynamicFileList *file_list = NULL;
 static DynamicFileList *file_tail = NULL;
 
+/*
+ * Track the library currently being loaded (during _PG_init execution).
+ * This allows GUC code to know which library is defining custom variables.
+ */
+static const char *current_loading_library_name = NULL;
+
 /* stat() call under Win32 returns an st_ino field, but it has no meaning */
 #ifndef WIN32
 #define SAME_INODE(A,B) ((A).st_ino == (B).inode && (A).st_dev == (B).device)
@@ -293,11 +299,33 @@ internal_load_library(const char *libname)
 
 		/*
 		 * If the library has a _PG_init() function, call it.
+		 *
+		 * Set current_loading_library_name so that GUC code can track which
+		 * library is defining custom variables. Use the module name from the
+		 * magic block if available, otherwise extract from the filename.
 		 */
 		PG_init = (PG_init_t) dlsym(file_scanner->handle, "_PG_init");
 		if (PG_init)
+		{
+			if (file_scanner->magic->name != NULL)
+				current_loading_library_name = file_scanner->magic->name;
+			else
+			{
+				/* Extract module name from library path */
+				const char *basename = strrchr(libname, '/');
+
+				if (basename)
+					basename++;
+				else
+					basename = libname;
+				current_loading_library_name = basename;
+			}
+
 			(*PG_init) ();
 
+			current_loading_library_name = NULL;
+		}
+
 		/* OK to link it into list */
 		if (file_list == NULL)
 			file_list = file_scanner;
@@ -746,3 +774,13 @@ RestoreLibraryState(char *start_address)
 		start_address += strlen(start_address) + 1;
 	}
 }
+
+/*
+ * Return the name of the library currently being loaded (during _PG_init),
+ * or NULL if no library is currently being loaded.
+ */
+const char *
+get_current_loading_library_name(void)
+{
+	return current_loading_library_name;
+}
diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c
index 563f20374ff..aaffe943b2b 100644
--- a/src/backend/utils/init/miscinit.c
+++ b/src/backend/utils/init/miscinit.c
@@ -1856,6 +1856,8 @@ process_shared_preload_libraries(void)
 				   false);
 	process_shared_preload_libraries_in_progress = false;
 	process_shared_preload_libraries_done = true;
+
+	check_guc_prefix_reservations();
 }
 
 /*
@@ -1870,6 +1872,8 @@ process_session_preload_libraries(void)
 	load_libraries(local_preload_libraries_string,
 				   "local_preload_libraries",
 				   true);
+
+	check_guc_prefix_reservations();
 }
 
 /*
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index ae9d5f3fb70..1bd573a7e2a 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -32,6 +32,7 @@
 #include "access/xact.h"
 #include "access/xlog.h"
 #include "catalog/objectaccess.h"
+#include "fmgr.h"
 #include "catalog/pg_authid.h"
 #include "catalog/pg_parameter_acl.h"
 #include "catalog/pg_type.h"
@@ -76,6 +77,12 @@
 
 static int	GUC_check_errcode_value;
 
+typedef struct ReservedGUCPrefix
+{
+	char	   *prefix;			/* the reserved prefix (e.g., "myext") */
+	char	   *library_name;	/* library that reserved this prefix, or NULL */
+} ReservedGUCPrefix;
+
 static List *reserved_class_prefix = NIL;
 
 /* global variables for check hook support */
@@ -259,6 +266,8 @@ static void replace_auto_config_value(ConfigVariable **head_p, ConfigVariable **
 static bool valid_custom_variable_name(const char *name);
 static bool assignable_custom_variable_name(const char *name, bool skip_errors,
 											int elevel);
+static ReservedGUCPrefix *find_reserved_prefix_for_variable(const char *varname);
+static bool library_has_reserved_prefix(const char *library_name);
 static void do_serialize(char **destptr, Size *maxbytes,
 						 const char *fmt,...) pg_attribute_printf(3, 4);
 static bool call_bool_check_hook(const struct config_generic *conf, bool *newval,
@@ -1022,10 +1031,10 @@ assignable_custom_variable_name(const char *name, bool skip_errors, int elevel)
 		/* ... and it must not match any previously-reserved prefix */
 		foreach(lc, reserved_class_prefix)
 		{
-			const char *rcprefix = lfirst(lc);
+			ReservedGUCPrefix *reservation = (ReservedGUCPrefix *) lfirst(lc);
 
-			if (strlen(rcprefix) == classLen &&
-				strncmp(name, rcprefix, classLen) == 0)
+			if (strlen(reservation->prefix) == classLen &&
+				strncmp(name, reservation->prefix, classLen) == 0)
 			{
 				if (!skip_errors)
 					ereport(elevel,
@@ -1033,7 +1042,7 @@ assignable_custom_variable_name(const char *name, bool skip_errors, int elevel)
 							 errmsg("invalid configuration parameter name \"%s\"",
 									name),
 							 errdetail("\"%s\" is a reserved prefix.",
-									   rcprefix)));
+									   reservation->prefix)));
 				return false;
 			}
 		}
@@ -4788,6 +4797,19 @@ init_custom_variable(const char *name,
 	gen->flags = flags;
 	gen->vartype = type;
 
+	/*
+	 * Record which library defined this variable, for GUC prefix enforcement.
+	 * This will be NULL for variables defined outside of _PG_init context.
+	 */
+	{
+		const char *library_name = get_current_loading_library_name();
+
+		if (library_name)
+			gen->library_name = guc_strdup(FATAL, library_name);
+		else
+			gen->library_name = NULL;
+	}
+
 	return gen;
 }
 
@@ -5143,6 +5165,8 @@ DefineCustomEnumVariable(const char *name,
  * and then prevents new ones from being created.
  * Extensions should call this after they've defined all of their custom
  * GUCs, to help catch misspelled config-file entries.
+ *
+ * Also records the library that reserved this prefix for enforcement purposes.
  */
 void
 MarkGUCPrefixReserved(const char *className)
@@ -5151,6 +5175,8 @@ MarkGUCPrefixReserved(const char *className)
 	HASH_SEQ_STATUS status;
 	GUCHashEntry *hentry;
 	MemoryContext oldcontext;
+	ReservedGUCPrefix *reservation;
+	const char *library_name = get_current_loading_library_name();
 
 	/*
 	 * Check for existing placeholders.  We must actually remove invalid
@@ -5183,12 +5209,141 @@ MarkGUCPrefixReserved(const char *className)
 		}
 	}
 
-	/* And remember the name so we can prevent future mistakes. */
+	/*
+	 * Remember the prefix and its associated library so we can prevent
+	 * future mistakes and enforce prefix ownership.
+	 */
 	oldcontext = MemoryContextSwitchTo(GUCMemoryContext);
-	reserved_class_prefix = lappend(reserved_class_prefix, pstrdup(className));
+	reservation = (ReservedGUCPrefix *) palloc(sizeof(ReservedGUCPrefix));
+	reservation->prefix = pstrdup(className);
+	if (library_name)
+		reservation->library_name = pstrdup(library_name);
+	else
+		reservation->library_name = NULL;
+	reserved_class_prefix = lappend(reserved_class_prefix, reservation);
 	MemoryContextSwitchTo(oldcontext);
 }
 
+static ReservedGUCPrefix *
+find_reserved_prefix_for_variable(const char *varname)
+{
+	ListCell   *lc;
+	const char *sep;
+	int			classLen;
+
+	/* Find the class (prefix) portion of the variable name */
+	sep = strchr(varname, GUC_QUALIFIER_SEPARATOR);
+	if (sep == NULL)
+		return NULL;
+
+	classLen = sep - varname;
+
+	foreach(lc, reserved_class_prefix)
+	{
+		ReservedGUCPrefix *reservation = (ReservedGUCPrefix *) lfirst(lc);
+
+		if (strlen(reservation->prefix) == classLen &&
+			strncmp(varname, reservation->prefix, classLen) == 0)
+			return reservation;
+	}
+
+	return NULL;
+}
+
+static bool
+library_has_reserved_prefix(const char *library_name)
+{
+	ListCell   *lc;
+
+	if (library_name == NULL)
+		return false;
+
+	foreach(lc, reserved_class_prefix)
+	{
+		ReservedGUCPrefix *reservation = (ReservedGUCPrefix *) lfirst(lc);
+
+		if (reservation->library_name != NULL &&
+			strcmp(reservation->library_name, library_name) == 0)
+			return true;
+	}
+
+	return false;
+}
+
+/*
+ * Check GUC prefix reservations after library loading.
+ *
+ * This function validates that:
+ * - In "prefix" mode: all custom GUCs are defined under a reserved prefix
+ * - In "strict" mode: all libraries that define custom GUCs have called
+ *   MarkGUCPrefixReserved()
+ */
+void
+check_guc_prefix_reservations(void)
+{
+	HASH_SEQ_STATUS status;
+	GUCHashEntry *hentry;
+
+	if (guc_prefix_enforcement == GUC_PREFIX_ENFORCEMENT_OFF)
+		return;
+
+	hash_seq_init(&status, guc_hashtab);
+	while ((hentry = (GUCHashEntry *) hash_seq_search(&status)) != NULL)
+	{
+		struct config_generic *gconf = hentry->gucvar;
+		ReservedGUCPrefix *reservation;
+		int			strict_elevel;
+		int			prefix_elevel;
+		bool		has_reserved_prefix;
+
+		/* Skip placeholders and core variables */
+		if (gconf->flags & GUC_CUSTOM_PLACEHOLDER)
+			continue;
+		if (gconf->library_name == NULL)
+			continue;
+
+		strict_elevel = (guc_prefix_enforcement == GUC_PREFIX_ENFORCEMENT_STRICT)
+			? FATAL : WARNING;
+		prefix_elevel = (guc_prefix_enforcement == GUC_PREFIX_ENFORCEMENT_WARN)
+			? WARNING : FATAL;
+
+		has_reserved_prefix = library_has_reserved_prefix(gconf->library_name);
+		if (!has_reserved_prefix)
+		{
+			ereport(strict_elevel,
+					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+					 errmsg("extension \"%s\" defines GUC variables without calling MarkGUCPrefixReserved()",
+							gconf->library_name),
+					 errdetail("Variable \"%s\" was defined without prefix reservation.",
+							   gconf->name),
+					 errhint("Extensions should call MarkGUCPrefixReserved() after defining their GUC variables.")));
+		}
+
+		reservation = find_reserved_prefix_for_variable(gconf->name);
+
+		if (reservation == NULL)
+		{
+			if (has_reserved_prefix)
+			{
+				ereport(prefix_elevel,
+						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+						 errmsg("extension \"%s\" defines GUC variable \"%s\" outside any reserved prefix",
+								gconf->library_name, gconf->name),
+						 errhint("Extensions should call MarkGUCPrefixReserved() to reserve a prefix for their variables.")));
+			}
+		}
+		else if (reservation->library_name != NULL &&
+				 strcmp(reservation->library_name, gconf->library_name) != 0)
+		{
+			/* Variable is under a prefix reserved by a different library */
+			ereport(prefix_elevel,
+					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+					 errmsg("extension \"%s\" defines GUC variable \"%s\" under prefix reserved by \"%s\"",
+							gconf->library_name, gconf->name, reservation->library_name)));
+		}
+	}
+}
+
 
 /*
  * Return an array of modified GUC options to show in EXPLAIN.
diff --git a/src/backend/utils/misc/guc_parameters.dat b/src/backend/utils/misc/guc_parameters.dat
index 7c60b125564..b1ea6121206 100644
--- a/src/backend/utils/misc/guc_parameters.dat
+++ b/src/backend/utils/misc/guc_parameters.dat
@@ -1160,6 +1160,15 @@
   boot_val => 'false',
 },
 
+{ name => 'guc_prefix_enforcement', type => 'enum', context => 'PGC_SIGHUP', group => 'DEVELOPER_OPTIONS',
+  short_desc => 'Enforcement mode for GUC prefix reservations.',
+  long_desc => 'Controls whether violations of GUC prefix reservations generate warnings or errors.',
+  flags => 'GUC_NOT_IN_SAMPLE',
+  variable => 'guc_prefix_enforcement',
+  boot_val => 'GUC_PREFIX_ENFORCEMENT_OFF',
+  options => 'guc_prefix_enforcement_options',
+},
+
 { name => 'hash_mem_multiplier', type => 'real', context => 'PGC_USERSET', group => 'RESOURCES_MEM',
   short_desc => 'Multiple of "work_mem" to use for hash tables.',
   flags => 'GUC_EXPLAIN',
diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c
index 73ff6ad0a32..0c492fd4fc9 100644
--- a/src/backend/utils/misc/guc_tables.c
+++ b/src/backend/utils/misc/guc_tables.c
@@ -491,6 +491,17 @@ static const struct config_enum_entry file_copy_method_options[] = {
 	{NULL, 0, false}
 };
 
+static const struct config_enum_entry guc_prefix_enforcement_options[] = {
+	{"off", GUC_PREFIX_ENFORCEMENT_OFF, false},
+	{"warn", GUC_PREFIX_ENFORCEMENT_WARN, false},
+	{"prefix", GUC_PREFIX_ENFORCEMENT_PREFIX, false},
+	{"strict", GUC_PREFIX_ENFORCEMENT_STRICT, false},
+	{NULL, 0, false}
+};
+
+StaticAssertDecl(lengthof(guc_prefix_enforcement_options) == (GUC_PREFIX_ENFORCEMENT_STRICT + 2),
+				 "array length mismatch");
+
 /*
  * Options for enum values stored in other modules
  */
@@ -581,6 +592,8 @@ int			huge_pages = HUGE_PAGES_TRY;
 int			huge_page_size;
 int			huge_pages_status = HUGE_PAGES_UNKNOWN;
 
+int			guc_prefix_enforcement = GUC_PREFIX_ENFORCEMENT_OFF;
+
 /*
  * These variables are all dummies that don't do anything, except in some
  * cases provide the value for SHOW to display.  The real state is elsewhere
diff --git a/src/include/fmgr.h b/src/include/fmgr.h
index 22dd6526169..5a4dffc6e30 100644
--- a/src/include/fmgr.h
+++ b/src/include/fmgr.h
@@ -797,6 +797,7 @@ extern void get_loaded_module_details(DynamicFileList *dfptr,
 									  const char **module_name,
 									  const char **module_version);
 extern void **find_rendezvous_variable(const char *varName);
+extern const char *get_current_loading_library_name(void);
 extern Size EstimateLibraryStateSpace(void);
 extern void SerializeLibraryState(Size maxsize, char *start_address);
 extern void RestoreLibraryState(char *start_address);
diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h
index bf39878c43e..ee02b9aa987 100644
--- a/src/include/utils/guc.h
+++ b/src/include/utils/guc.h
@@ -126,6 +126,20 @@ typedef enum
 	PGC_S_SESSION,				/* SET command */
 } GucSource;
 
+/*
+ * Enforcement modes for GUC prefix reservations.
+ *
+ * This controls how strictly we enforce that extensions call
+ * MarkGUCPrefixReserved() and only define GUCs under their reserved prefix.
+ */
+typedef enum
+{
+	GUC_PREFIX_ENFORCEMENT_OFF,		/* no enforcement (earlier behavior) */
+	GUC_PREFIX_ENFORCEMENT_WARN,	/* emit warnings on violations */
+	GUC_PREFIX_ENFORCEMENT_PREFIX,	/* ERROR if GUC defined outside reserved prefix */
+	GUC_PREFIX_ENFORCEMENT_STRICT,	/* ERROR if extension doesn't call MarkGUCPrefixReserved */
+} GucPrefixEnforcement;
+
 /*
  * Parsing the configuration file(s) will return a list of name-value pairs
  * with source location info.  We also abuse this data structure to carry
@@ -287,6 +301,8 @@ extern PGDLLIMPORT bool log_statement_stats;
 extern PGDLLIMPORT bool log_btree_build_stats;
 extern PGDLLIMPORT char *event_source;
 
+extern PGDLLIMPORT int guc_prefix_enforcement;
+
 extern PGDLLIMPORT bool check_function_bodies;
 extern PGDLLIMPORT bool current_role_is_superuser;
 
@@ -456,6 +472,7 @@ extern int	set_config_with_handle(const char *name, config_handle *handle,
 								   GucAction action, bool changeVal,
 								   int elevel, bool is_reload);
 extern config_handle *get_config_handle(const char *name);
+extern void check_guc_prefix_reservations(void);
 extern void AlterSystemSetConfigFile(AlterSystemStmt *altersysstmt);
 extern char *GetConfigOptionByName(const char *name, const char **varname,
 								   bool missing_ok);
diff --git a/src/include/utils/guc_tables.h b/src/include/utils/guc_tables.h
index 71a80161961..1bbaa09212f 100644
--- a/src/include/utils/guc_tables.h
+++ b/src/include/utils/guc_tables.h
@@ -278,6 +278,8 @@ struct config_generic
 	char	   *sourcefile;		/* file current setting is from (NULL if not
 								 * set in config file) */
 	int			sourceline;		/* line in source file */
+	const char *library_name;	/* library that defined this variable, or NULL
+								 * for core variables */
 
 	/* fields for specific variable types */
 	union
diff --git a/src/test/modules/Makefile b/src/test/modules/Makefile
index 4c6d56d97d8..bf811e2ca7e 100644
--- a/src/test/modules/Makefile
+++ b/src/test/modules/Makefile
@@ -29,6 +29,9 @@ SUBDIRS = \
 		  test_escape \
 		  test_extensions \
 		  test_ginpostinglist \
+		  test_guc_prefix_enforcement \
+		  test_hba_guc \
+		  test_hba_guc_contexts \
 		  test_int128 \
 		  test_integerset \
 		  test_json_parser \
diff --git a/src/test/modules/meson.build b/src/test/modules/meson.build
index 1b31c5b98d6..da545219a92 100644
--- a/src/test/modules/meson.build
+++ b/src/test/modules/meson.build
@@ -29,6 +29,7 @@ subdir('test_dsm_registry')
 subdir('test_escape')
 subdir('test_extensions')
 subdir('test_ginpostinglist')
+subdir('test_guc_prefix_enforcement')
 subdir('test_int128')
 subdir('test_integerset')
 subdir('test_json_parser')
diff --git a/src/test/modules/test_guc_prefix_enforcement/Makefile b/src/test/modules/test_guc_prefix_enforcement/Makefile
new file mode 100644
index 00000000000..5265c6ee37d
--- /dev/null
+++ b/src/test/modules/test_guc_prefix_enforcement/Makefile
@@ -0,0 +1,17 @@
+# src/test/modules/test_guc_prefix_enforcement/Makefile
+
+MODULES = test_guc_prefix_enforcement test_guc_no_reserve test_guc_wrong_prefix test_guc_no_prefix
+PGFILEDESC = "test_guc_prefix_enforcement - test module for GUC prefix reservation enforcement"
+
+TAP_TESTS = 1
+
+ifdef USE_PGXS
+PG_CONFIG = pg_config
+PGXS := $(shell $(PG_CONFIG) --pgxs)
+include $(PGXS)
+else
+subdir = src/test/modules/test_guc_prefix_enforcement
+top_builddir = ../../../..
+include $(top_builddir)/src/Makefile.global
+include $(top_srcdir)/contrib/contrib-global.mk
+endif
diff --git a/src/test/modules/test_guc_prefix_enforcement/meson.build b/src/test/modules/test_guc_prefix_enforcement/meson.build
new file mode 100644
index 00000000000..ccf59f5330a
--- /dev/null
+++ b/src/test/modules/test_guc_prefix_enforcement/meson.build
@@ -0,0 +1,80 @@
+# Copyright (c) 2026, PostgreSQL Global Development Group
+
+# Main test module (good behavior)
+test_guc_prefix_enforcement_sources = files(
+  'test_guc_prefix_enforcement.c',
+)
+
+if host_system == 'windows'
+  test_guc_prefix_enforcement_sources += rc_lib_gen.process(win32ver_rc, extra_args: [
+    '--NAME', 'test_guc_prefix_enforcement',
+    '--FILEDESC', 'test_guc_prefix_enforcement - test module for GUC prefix reservation enforcement',])
+endif
+
+test_guc_prefix_enforcement = shared_module('test_guc_prefix_enforcement',
+  test_guc_prefix_enforcement_sources,
+  kwargs: pg_test_mod_args,
+)
+test_install_libs += test_guc_prefix_enforcement
+
+# Test module without reservation
+test_guc_no_reserve_sources = files(
+  'test_guc_no_reserve.c',
+)
+
+if host_system == 'windows'
+  test_guc_no_reserve_sources += rc_lib_gen.process(win32ver_rc, extra_args: [
+    '--NAME', 'test_guc_no_reserve',
+    '--FILEDESC', 'test_guc_no_reserve - test module without prefix reservation',])
+endif
+
+test_guc_no_reserve = shared_module('test_guc_no_reserve',
+  test_guc_no_reserve_sources,
+  kwargs: pg_test_mod_args,
+)
+test_install_libs += test_guc_no_reserve
+
+# Test module with wrong prefix
+test_guc_wrong_prefix_sources = files(
+  'test_guc_wrong_prefix.c',
+)
+
+if host_system == 'windows'
+  test_guc_wrong_prefix_sources += rc_lib_gen.process(win32ver_rc, extra_args: [
+    '--NAME', 'test_guc_wrong_prefix',
+    '--FILEDESC', 'test_guc_wrong_prefix - test module with wrong prefix reservation',])
+endif
+
+test_guc_wrong_prefix = shared_module('test_guc_wrong_prefix',
+  test_guc_wrong_prefix_sources,
+  kwargs: pg_test_mod_args,
+)
+test_install_libs += test_guc_wrong_prefix
+
+# Test module with variable without prefix (no dot)
+test_guc_no_prefix_sources = files(
+  'test_guc_no_prefix.c',
+)
+
+if host_system == 'windows'
+  test_guc_no_prefix_sources += rc_lib_gen.process(win32ver_rc, extra_args: [
+    '--NAME', 'test_guc_no_prefix',
+    '--FILEDESC', 'test_guc_no_prefix - test module with variable without prefix',])
+endif
+
+test_guc_no_prefix = shared_module('test_guc_no_prefix',
+  test_guc_no_prefix_sources,
+  kwargs: pg_test_mod_args,
+)
+test_install_libs += test_guc_no_prefix
+
+tests += {
+  'name': 'test_guc_prefix_enforcement',
+  'sd': meson.current_source_dir(),
+  'bd': meson.current_build_dir(),
+  'tap': {
+    'tests': [
+      't/001_prefix_enforcement.pl',
+    ],
+  },
+}
diff --git a/src/test/modules/test_guc_prefix_enforcement/t/001_prefix_enforcement.pl b/src/test/modules/test_guc_prefix_enforcement/t/001_prefix_enforcement.pl
new file mode 100644
index 00000000000..273f9b18fa1
--- /dev/null
+++ b/src/test/modules/test_guc_prefix_enforcement/t/001_prefix_enforcement.pl
@@ -0,0 +1,161 @@
+# Copyright (c) 2026, PostgreSQL Global Development Group
+
+# Test GUC prefix reservation enforcement modes
+
+use strict;
+use warnings FATAL => 'all';
+use PostgreSQL::Test::Cluster;
+use PostgreSQL::Test::Utils;
+use Test::More;
+
+#
+# Test 1: Default mode (off) - all extensions should load without errors
+#
+my $node = PostgreSQL::Test::Cluster->new('main');
+$node->init;
+$node->append_conf('postgresql.conf', "shared_preload_libraries = 'test_guc_prefix_enforcement'");
+$node->start;
+
+my $result = $node->safe_psql('postgres', 'SHOW test_guc_prefix_enforcement.test_var');
+is($result, 'default', 'good extension loads with default enforcement (off)');
+
+$node->stop;
+
+#
+# Test 2: warn mode with good extension - should load with no warnings
+#
+$node = PostgreSQL::Test::Cluster->new('warn_good');
+$node->init;
+$node->append_conf('postgresql.conf', "guc_prefix_enforcement = 'warn'");
+$node->append_conf('postgresql.conf', "shared_preload_libraries = 'test_guc_prefix_enforcement'");
+$node->start;
+
+$result = $node->safe_psql('postgres', 'SHOW test_guc_prefix_enforcement.test_var');
+is($result, 'default', 'good extension loads with warn enforcement');
+
+# Verify no warnings were emitted
+my $log = $node->logfile;
+my $log_contents = slurp_file($log);
+unlike($log_contents, qr/WARNING.*MarkGUCPrefixReserved/,
+	 'no warning about MarkGUCPrefixReserved for good extension');
+unlike($log_contents, qr/WARNING.*reserved prefix/,
+	 'no warning about reserved prefix for good extension');
+
+$node->stop;
+
+#
+# Test 3: warn mode with no_reserve extension - should load with warning
+#
+$node = PostgreSQL::Test::Cluster->new('warn_no_reserve');
+$node->init;
+$node->append_conf('postgresql.conf', "guc_prefix_enforcement = 'warn'");
+$node->append_conf('postgresql.conf', "shared_preload_libraries = 'test_guc_no_reserve'");
+
+# Start should succeed but log warnings
+$node->start;
+$result = $node->safe_psql('postgres', 'SHOW test_guc_no_reserve.some_var');
+is($result, 'default', 'no_reserve extension loads with warn enforcement');
+
+# Check log for warning
+$log = $node->logfile;
+$log_contents = slurp_file($log);
+like($log_contents, qr/WARNING.*without calling MarkGUCPrefixReserved/,
+	 'warn mode emits warning for extension without prefix reservation');
+
+$node->stop;
+
+#
+# Test 4: strict mode with no_reserve extension - should fail to start
+#
+$node = PostgreSQL::Test::Cluster->new('strict_no_reserve');
+$node->init;
+$node->append_conf('postgresql.conf', "guc_prefix_enforcement = 'strict'");
+$node->append_conf('postgresql.conf', "shared_preload_libraries = 'test_guc_no_reserve'");
+
+# Start should fail
+my $ret = $node->start(fail_ok => 1);
+is($ret, 0, 'strict mode prevents startup with non-compliant extension');
+
+$log = $node->logfile;
+$log_contents = slurp_file($log);
+like($log_contents, qr/FATAL.*without calling MarkGUCPrefixReserved/,
+	 'strict mode emits error for extension without prefix reservation');
+
+#
+# Test 5: prefix mode with wrong_prefix extension - should fail to start
+#
+$node = PostgreSQL::Test::Cluster->new('prefix_wrong');
+$node->init;
+$node->append_conf('postgresql.conf', "guc_prefix_enforcement = 'prefix'");
+$node->append_conf('postgresql.conf', "shared_preload_libraries = 'test_guc_wrong_prefix'");
+
+# Start should fail
+$ret = $node->start(fail_ok => 1);
+is($ret, 0, 'prefix mode prevents startup with wrong prefix extension');
+
+$log = $node->logfile;
+$log_contents = slurp_file($log);
+like($log_contents, qr/FATAL.*outside any reserved prefix/,
+	 'prefix mode emits error for extension defining GUC outside reserved prefix');
+
+#
+# Test 6: warn mode with no_prefix extension - should load with warning
+# (extension reserves a prefix but defines variable without any prefix)
+#
+$node = PostgreSQL::Test::Cluster->new('warn_no_prefix');
+$node->init;
+$node->append_conf('postgresql.conf', "guc_prefix_enforcement = 'warn'");
+$node->append_conf('postgresql.conf', "shared_preload_libraries = 'test_guc_no_prefix'");
+$node->start;
+
+$result = $node->safe_psql('postgres', 'SHOW test_guc_no_prefix_var');
+is($result, 'default', 'no_prefix extension loads with warn enforcement');
+
+$log = $node->logfile;
+$log_contents = slurp_file($log);
+like($log_contents, qr/WARNING.*outside any reserved prefix/,
+	 'warn mode emits warning for extension defining GUC without prefix');
+
+$node->stop;
+
+#
+# Test 7: prefix mode with no_prefix extension - should fail to start
+# (extension reserves a prefix but defines variable without any prefix)
+#
+$node = PostgreSQL::Test::Cluster->new('prefix_no_prefix');
+$node->init;
+$node->append_conf('postgresql.conf', "guc_prefix_enforcement = 'prefix'");
+$node->append_conf('postgresql.conf', "shared_preload_libraries = 'test_guc_no_prefix'");
+
+# Start should fail
+$ret = $node->start(fail_ok => 1);
+is($ret, 0, 'prefix mode prevents startup with unprefixed variable');
+
+$log = $node->logfile;
+$log_contents = slurp_file($log);
+like($log_contents, qr/FATAL.*outside any reserved prefix/,
+	 'prefix mode emits error for extension defining GUC without prefix');
+
+#
+# Test 8: verify good extension works in strict mode
+#
+$node = PostgreSQL::Test::Cluster->new('strict_good');
+$node->init;
+$node->append_conf('postgresql.conf', "guc_prefix_enforcement = 'strict'");
+$node->append_conf('postgresql.conf', "shared_preload_libraries = 'test_guc_prefix_enforcement'");
+$node->start;
+
+$result = $node->safe_psql('postgres', 'SHOW test_guc_prefix_enforcement.test_var');
+is($result, 'default', 'good extension loads successfully in strict mode');
+
+# Verify no warnings or errors were emitted
+$log = $node->logfile;
+$log_contents = slurp_file($log);
+unlike($log_contents, qr/(WARNING|FATAL).*MarkGUCPrefixReserved/,
+	 'no warning/error about MarkGUCPrefixReserved for good extension in strict mode');
+unlike($log_contents, qr/(WARNING|FATAL).*reserved prefix/,
+	 'no warning/error about reserved prefix for good extension in strict mode');
+
+$node->stop;
+
+done_testing();
diff --git a/src/test/modules/test_guc_prefix_enforcement/test_guc_no_prefix.c b/src/test/modules/test_guc_prefix_enforcement/test_guc_no_prefix.c
new file mode 100644
index 00000000000..610c9e15430
--- /dev/null
+++ b/src/test/modules/test_guc_prefix_enforcement/test_guc_no_prefix.c
@@ -0,0 +1,45 @@
+/*-------------------------------------------------------------------------
+ *
+ * test_guc_no_prefix.c
+ *		Test extension that defines a GUC variable without a prefix.
+ *
+ * This extension reserves a prefix but also defines a variable without
+ * a prefix, which should be flagged by prefix enforcement.
+ *
+ * Copyright (c) 2026, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ *	  src/test/modules/test_guc_prefix_enforcement/test_guc_no_prefix.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "fmgr.h"
+#include "utils/guc.h"
+
+PG_MODULE_MAGIC_EXT(
+	.name = "test_guc_no_prefix",
+	.version = PG_VERSION,
+);
+
+static char *test_var = NULL;
+
+void
+_PG_init(void)
+{
+	/* Define a variable without any prefix */
+	DefineCustomStringVariable("test_guc_no_prefix_var",
+							   "Test variable without prefix",
+							   NULL,
+							   &test_var,
+							   "default",
+							   PGC_SUSET,
+							   0,
+							   NULL,
+							   NULL,
+							   NULL);
+
+	MarkGUCPrefixReserved("test_guc_no_prefix");
+}
diff --git a/src/test/modules/test_guc_prefix_enforcement/test_guc_no_reserve.c b/src/test/modules/test_guc_prefix_enforcement/test_guc_no_reserve.c
new file mode 100644
index 00000000000..c57887ba88c
--- /dev/null
+++ b/src/test/modules/test_guc_prefix_enforcement/test_guc_no_reserve.c
@@ -0,0 +1,42 @@
+/*-------------------------------------------------------------------------
+ *
+ * test_guc_no_reserve.c
+ *		Test module that defines GUCs without calling MarkGUCPrefixReserved
+ *
+ * This module intentionally does NOT call MarkGUCPrefixReserved() after
+ * defining its custom GUC variables.
+ *
+ * Copyright (c) 2026, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ *	  src/test/modules/test_guc_prefix_enforcement/test_guc_no_reserve.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "fmgr.h"
+#include "utils/guc.h"
+
+PG_MODULE_MAGIC_EXT(
+	.name = "test_guc_no_reserve",
+	.version = PG_VERSION,
+);
+
+static char *no_reserve_var = NULL;
+
+void
+_PG_init(void)
+{
+	DefineCustomStringVariable("test_guc_no_reserve.some_var",
+							   "A variable without prefix reservation",
+							   NULL,
+							   &no_reserve_var,
+							   "default",
+							   PGC_SUSET,
+							   0,
+							   NULL,
+							   NULL,
+							   NULL);
+}
diff --git a/src/test/modules/test_guc_prefix_enforcement/test_guc_prefix_enforcement.c b/src/test/modules/test_guc_prefix_enforcement/test_guc_prefix_enforcement.c
new file mode 100644
index 00000000000..1ccbbb1fb8e
--- /dev/null
+++ b/src/test/modules/test_guc_prefix_enforcement/test_guc_prefix_enforcement.c
@@ -0,0 +1,44 @@
+/*-------------------------------------------------------------------------
+ *
+ * test_guc_prefix_enforcement.c
+ *		Test extension that properly reserves its GUC prefix.
+ *
+ * This extension defines a GUC variable and calls MarkGUCPrefixReserved(),
+ * demonstrating correct behavior.
+ *
+ * Copyright (c) 2026, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ *	  src/test/modules/test_guc_prefix_enforcement/test_guc_prefix_enforcement.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "fmgr.h"
+#include "utils/guc.h"
+
+PG_MODULE_MAGIC_EXT(
+	.name = "test_guc_prefix_enforcement",
+	.version = PG_VERSION,
+);
+
+static char *test_var = NULL;
+
+void
+_PG_init(void)
+{
+	DefineCustomStringVariable("test_guc_prefix_enforcement.test_var",
+							   "Test variable",
+							   NULL,
+							   &test_var,
+							   "default",
+							   PGC_SUSET,
+							   0,
+							   NULL,
+							   NULL,
+							   NULL);
+
+	MarkGUCPrefixReserved("test_guc_prefix_enforcement");
+}
diff --git a/src/test/modules/test_guc_prefix_enforcement/test_guc_wrong_prefix.c b/src/test/modules/test_guc_prefix_enforcement/test_guc_wrong_prefix.c
new file mode 100644
index 00000000000..4e94b367bb9
--- /dev/null
+++ b/src/test/modules/test_guc_prefix_enforcement/test_guc_wrong_prefix.c
@@ -0,0 +1,44 @@
+/*-------------------------------------------------------------------------
+ *
+ * test_guc_wrong_prefix.c
+ *		Test module that reserves one prefix but defines GUCs under another
+ *
+ * This module reserves the prefix "test_guc_wrong" but defines a variable
+ * under "test_guc_other".
+ *
+ * Copyright (c) 2026, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ *	  src/test/modules/test_guc_prefix_enforcement/test_guc_wrong_prefix.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "fmgr.h"
+#include "utils/guc.h"
+
+PG_MODULE_MAGIC_EXT(
+	.name = "test_guc_wrong_prefix",
+	.version = PG_VERSION,
+);
+
+static char *wrong_prefix_var = NULL;
+
+void
+_PG_init(void)
+{
+	DefineCustomStringVariable("test_guc_other.some_var",
+							   "A variable under a different prefix than reserved",
+							   NULL,
+							   &wrong_prefix_var,
+							   "default",
+							   PGC_SUSET,
+							   0,
+							   NULL,
+							   NULL,
+							   NULL);
+
+	MarkGUCPrefixReserved("test_guc_wrong");
+}
-- 
2.43.0



  [application/octet-stream] 0002-Introduce-PGC_HBA-GUC-variables-settable-in-pg_hba.c.patch (45.0K, 3-0002-Introduce-PGC_HBA-GUC-variables-settable-in-pg_hba.c.patch)
  download | inline diff:
From 5fc38d2d922c3e201210a42e8546cbeb1587771d Mon Sep 17 00:00:00 2001
From: Zsolt Parragi <[email protected]>
Date: Wed, 7 Jan 2026 19:25:11 +0000
Subject: [PATCH 2/2] Introduce PGC_HBA: GUC variables settable in pg_hba.conf

Add a new GUC context level PGC_HBA that allows custom variables
to be set based on which pg_hba.conf line matches during
authentication. This enables authentication plugins and extensions
to receive configuration parameters that vary based on HBA matching
rules (client address, database, user, authentication method, etc.).

Motivation:

OAuth support introduced several OAuth-specific configuration
parameters to pg_hba.conf, and added support for third-party
validator plugins. These plugins often require additional
configuration, which is only possible with GUC variables. As these
plugins execute before authentication completes, they can only
depend on GUC variables defined in postgresql.conf or
postgresql.auto.conf.

This limitation creates a configuration problem: pg_hba.conf allows
administrators to define several different OAuth configurations for
a single PostgreSQL instance, but validator plugin-specific
variables can only be configured once per server.

With the changes in this commit, validator plugins can now define
their GUC variables with PGC_HBA context, allowing different
settings per HBA line.

Another use case is that extensions loaded via
shared_preload_libraries or session_preload_libraries can define
these variables, allowing administrators to configure
authentication/authorization-related settings per HBA line, for
example to help with row level security policies.

User interface:

The new parameters reuse the existing syntax of pg_hba.conf; GUC
variables can be defined the same way as existing hardcoded
parameters:

    host all all 192.168.1.0/24 oauth myext.setting=value1
    host all all 0.0.0.0/0      oauth myext.setting=value2

This also provides an easy migration path in the future to convert
existing pg_hba parameters to GUC variables.

Extension interface:

We define a new GUC context, PGC_HBA, and a new source, PGC_S_HBA.
PGC_HBA variables must be defined during shared_preload_libraries
or session_preload_libraries. This ensures all custom variables
are registered before connections are accepted.

PGC_HBA variables can be set from:
  - postgresql.conf
  - postgresql.auto.conf (ALTER SYSTEM)
  - pg_hba.conf (new)

PGC_HBA variables cannot be set via:
  - ALTER USER SET / ALTER DATABASE SET
  - SET command
  - Connection parameters (PGOPTIONS)

Additionally to PGC_HBA variables, SU_BACKEND, BACKEND, SUSET and USER
variables can also be set in pg_hba.

Error handling change:

This feature requires a behavior change in pg_hba.conf error
handling which may affect users not using the new feature.

Since we allow both shared_preload_libraries and
session_preload_libraries to define PGC_HBA variables (enabling
updates of potentially security-related libraries without a server
restart), we can no longer reject unknown parameters in pg_hba.conf
at postmaster startup.

Instead, unknown parameters are treated as placeholders (similar to
custom GUC variables) and error handling is delayed until after
session_preload_libraries completes. If any placeholder HBA
variables remain undefined at that point, the connection is aborted
with a FATAL error, even if authentication previously succeeded.
---
 src/backend/libpq/hba.c                       |  59 +++++-
 src/backend/utils/init/miscinit.c             |   7 +
 src/backend/utils/init/postinit.c             |   8 +
 src/backend/utils/misc/guc.c                  | 115 +++++++++++
 src/backend/utils/misc/guc_tables.c           |   2 +
 src/include/libpq/hba.h                       |   7 +
 src/include/miscadmin.h                       |   2 +
 src/include/utils/guc.h                       |   3 +
 src/test/modules/meson.build                  |   2 +
 src/test/modules/test_hba_guc/Makefile        |  21 ++
 src/test/modules/test_hba_guc/meson.build     |  35 ++++
 .../test_hba_guc/t/001_hba_guc_variables.pl   |  56 ++++++
 .../test_hba_guc/t/002_hba_guc_sources.pl     | 187 ++++++++++++++++++
 .../test_hba_guc/t/003_hba_guc_precedence.pl  | 105 ++++++++++
 .../test_hba_guc/test_hba_guc--1.0.sql        |  22 +++
 src/test/modules/test_hba_guc/test_hba_guc.c  |  93 +++++++++
 .../modules/test_hba_guc/test_hba_guc.conf    |   3 +
 .../modules/test_hba_guc/test_hba_guc.control |   5 +
 .../modules/test_hba_guc_contexts/Makefile    |  18 ++
 .../modules/test_hba_guc_contexts/meson.build |  28 +++
 .../t/001_context_validation.pl               |  85 ++++++++
 .../test_hba_guc_contexts.c                   |  89 +++++++++
 22 files changed, 943 insertions(+), 9 deletions(-)
 create mode 100644 src/test/modules/test_hba_guc/Makefile
 create mode 100644 src/test/modules/test_hba_guc/meson.build
 create mode 100644 src/test/modules/test_hba_guc/t/001_hba_guc_variables.pl
 create mode 100644 src/test/modules/test_hba_guc/t/002_hba_guc_sources.pl
 create mode 100644 src/test/modules/test_hba_guc/t/003_hba_guc_precedence.pl
 create mode 100644 src/test/modules/test_hba_guc/test_hba_guc--1.0.sql
 create mode 100644 src/test/modules/test_hba_guc/test_hba_guc.c
 create mode 100644 src/test/modules/test_hba_guc/test_hba_guc.conf
 create mode 100644 src/test/modules/test_hba_guc/test_hba_guc.control
 create mode 100644 src/test/modules/test_hba_guc_contexts/Makefile
 create mode 100644 src/test/modules/test_hba_guc_contexts/meson.build
 create mode 100644 src/test/modules/test_hba_guc_contexts/t/001_context_validation.pl
 create mode 100644 src/test/modules/test_hba_guc_contexts/test_hba_guc_contexts.c

diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c
index 87ee541e880..6fc4debf567 100644
--- a/src/backend/libpq/hba.c
+++ b/src/backend/libpq/hba.c
@@ -2507,15 +2507,21 @@ parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline,
 	}
 	else
 	{
-		ereport(elevel,
-				(errcode(ERRCODE_CONFIG_FILE_ERROR),
-				 errmsg("unrecognized authentication option name: \"%s\"",
-						name),
-				 errcontext("line %d of configuration file \"%s\"",
-							line_num, file_name)));
-		*err_msg = psprintf("unrecognized authentication option name: \"%s\"",
-							name);
-		return false;
+		/*
+		 * Unrecognized option name - treat it as a potential GUC variable.
+		 * Store the name=value pair in the HbaLine's guc_options list.
+		 * Actual validation (checking if the GUC is defined) happens at
+		 * connection time after session_preload_libraries completes.
+		 */
+		HbaOption  *opt;
+
+		opt = palloc(sizeof(HbaOption));
+		opt->name = pstrdup(name);
+		opt->value = pstrdup(val);
+		hbaline->guc_options = lappend(hbaline->guc_options, opt);
+
+		elog(DEBUG2, "pg_hba.conf line %d: storing GUC option %s = %s",
+			 line_num, name, val);
 	}
 	return true;
 }
@@ -3113,11 +3119,44 @@ load_ident(void)
 
 
 
+/*
+ * apply_hba_guc_options
+ *		Apply GUC variable settings from the matched HBA line.
+ *
+ * This function processes the guc_options list from the matched pg_hba.conf
+ * line and applies each GUC setting. Variables will either be set (if already
+ * defined) or create placeholders (if not yet defined). Validation of
+ * undefined variables and context checking happens later, after
+ * session_preload_libraries completes in postinit.c.
+ */
+static void
+apply_hba_guc_options(Port *port)
+{
+	ListCell   *lc;
+
+	if (port->hba->guc_options == NIL)
+		return;
+
+	foreach(lc, port->hba->guc_options)
+	{
+		HbaOption  *opt = (HbaOption *) lfirst(lc);
+
+		elog(DEBUG2, "Applying HBA GUC option: %s = %s", opt->name, opt->value);
+
+		(void) set_config_option(opt->name, opt->value,
+								 PGC_HBA, PGC_S_HBA,
+								 GUC_ACTION_SET, true, ERROR, false);
+	}
+}
+
 /*
  *	Determine what authentication method should be used when accessing database
  *	"database" from frontend "raddr", user "user".  Return the method and
  *	an optional argument (stored in fields of *port), and STATUS_OK.
  *
+ *	Also applies all GUC variables from the matched HBA line, as these variables
+ *	might immediately required by authentication plugins.
+ *
  *	If the file does not contain any entry matching the request, we return
  *	method = uaImplicitReject.
  */
@@ -3125,6 +3164,8 @@ void
 hba_getauthmethod(Port *port)
 {
 	check_hba(port);
+
+	apply_hba_guc_options(port);
 }
 
 
diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c
index aaffe943b2b..2a714d308ce 100644
--- a/src/backend/utils/init/miscinit.c
+++ b/src/backend/utils/init/miscinit.c
@@ -1786,6 +1786,10 @@ char	   *local_preload_libraries_string = NULL;
 bool		process_shared_preload_libraries_in_progress = false;
 bool		process_shared_preload_libraries_done = false;
 
+/* Flag telling that we are loading session_preload_libraries */
+bool		process_session_preload_libraries_in_progress = false;
+bool		process_session_preload_libraries_done = false;
+
 shmem_request_hook_type shmem_request_hook = NULL;
 bool		process_shmem_requests_in_progress = false;
 
@@ -1866,9 +1870,12 @@ process_shared_preload_libraries(void)
 void
 process_session_preload_libraries(void)
 {
+	process_session_preload_libraries_in_progress = true;
 	load_libraries(session_preload_libraries_string,
 				   "session_preload_libraries",
 				   false);
+	process_session_preload_libraries_in_progress = false;
+	process_session_preload_libraries_done = true;
 	load_libraries(local_preload_libraries_string,
 				   "local_preload_libraries",
 				   true);
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index 52c05a9d1d5..d3bb88243c6 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -33,6 +33,7 @@
 #include "catalog/pg_database.h"
 #include "catalog/pg_db_role_setting.h"
 #include "catalog/pg_tablespace.h"
+#include "lib/stringinfo.h"
 #include "libpq/auth.h"
 #include "libpq/libpq-be.h"
 #include "mb/pg_wchar.h"
@@ -1225,6 +1226,13 @@ InitPostgres(const char *in_dbname, Oid dboid,
 	if ((flags & INIT_PG_LOAD_SESSION_LIBS) != 0)
 		process_session_preload_libraries();
 
+	/*
+	 * Now that session_preload_libraries has completed, validate that all
+	 * GUC variables set from pg_hba.conf are properly defined with the
+	 * correct context.
+	 */
+	check_hba_guc_variables();
+
 	/* fill in the remainder of this entry in the PgBackendStatus array */
 	if (!bootstrap)
 		pgstat_bestart_final();
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 1bd573a7e2a..3aef1a4d498 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -3403,6 +3403,17 @@ set_config_with_handle(const char *name, config_handle *handle,
 			 * signals to individual backends only.
 			 */
 			break;
+		case PGC_HBA:
+			if (context != PGC_SIGHUP && context != PGC_POSTMASTER &&
+				source != PGC_S_HBA)
+			{
+				ereport(elevel,
+						(errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM),
+						 errmsg("parameter \"%s\" cannot be changed now",
+								record->name)));
+				return 0;
+			}
+			break;
 		case PGC_SU_BACKEND:
 			if (context == PGC_BACKEND)
 			{
@@ -3456,6 +3467,7 @@ set_config_with_handle(const char *name, config_handle *handle,
 			else if (context != PGC_POSTMASTER &&
 					 context != PGC_BACKEND &&
 					 context != PGC_SU_BACKEND &&
+					 context != PGC_HBA &&
 					 source != PGC_S_CLIENT)
 			{
 				ereport(elevel,
@@ -4164,6 +4176,87 @@ get_config_handle(const char *name)
 	return NULL;
 }
 
+/*
+ * check_hba_guc_variables
+ *
+ * Check if any GUC variables set from pg_hba.conf (source = PGC_S_HBA)
+ * are still placeholders (undefined) or have the wrong context.
+ *
+ * Also enforces strict GUC prefix reservation for PGC_HBA variables:
+ * extensions that define PGC_HBA variables MUST call MarkGUCPrefixReserved()
+ * and their variables MUST be under the reserved prefix. This is always
+ * enforced regardless of the guc_prefix_enforcement setting, because
+ * PGC_HBA variables can affect authentication security.
+ *
+ * For each invalid variable, we emit a FATAL error with an appropriate
+ * message explaining the problem.
+ *
+ * This should be called after session_preload_libraries completes to
+ * ensure extensions have had a chance to define their PGC_HBA variables.
+ */
+void
+check_hba_guc_variables(void)
+{
+	HASH_SEQ_STATUS status;
+	GUCHashEntry *hentry;
+
+	hash_seq_init(&status, guc_hashtab);
+	while ((hentry = (GUCHashEntry *) hash_seq_search(&status)) != NULL)
+	{
+		struct config_generic *gconf = hentry->gucvar;
+
+		if (gconf->source != PGC_S_HBA)
+			continue;
+
+		if (gconf->flags & GUC_CUSTOM_PLACEHOLDER)
+		{
+			ereport(FATAL,
+					(errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
+					 errmsg("authentication configuration error"),
+					 errdetail("pg_hba.conf references undefined GUC variable \"%s\"",
+							   gconf->name),
+					 errhint("Ensure the extension defining this variable is loaded in session_preload_libraries or shared_preload_libraries.")));
+		}
+
+		if (gconf->context < PGC_HBA)
+		{
+			ereport(FATAL,
+					(errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM),
+					 errmsg("parameter \"%s\" cannot be set in pg_hba.conf",
+							gconf->name),
+					 errdetail("Only variables with context PGC_HBA or below can be set from pg_hba.conf."),
+					 errhint("This variable has context \"%s\".",
+							 GucContext_Names[gconf->context])));
+		}
+
+		if (gconf->library_name != NULL)
+		{
+			ReservedGUCPrefix *reservation;
+
+			reservation = find_reserved_prefix_for_variable(gconf->name);
+
+			if (reservation == NULL)
+			{
+				ereport(FATAL,
+						(errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
+						 errmsg("authentication configuration error"),
+						 errdetail("Extension \"%s\" defines PGC_HBA variable \"%s\" without a reserved prefix.",
+								   gconf->library_name, gconf->name),
+						 errhint("Extensions that define PGC_HBA variables must call MarkGUCPrefixReserved().")));
+			}
+			else if (reservation->library_name != NULL &&
+					 strcmp(reservation->library_name, gconf->library_name) != 0)
+			{
+				ereport(FATAL,
+						(errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
+						 errmsg("authentication configuration error"),
+						 errdetail("Extension \"%s\" defines PGC_HBA variable \"%s\" under prefix reserved by \"%s\".",
+								   gconf->library_name, gconf->name, reservation->library_name)));
+			}
+		}
+	}
+}
+
 
 /*
  * Set the fields for source file and line number the setting came from.
@@ -4765,6 +4858,19 @@ init_custom_variable(const char *name,
 		!process_shared_preload_libraries_in_progress)
 		elog(FATAL, "cannot create PGC_POSTMASTER variables after startup");
 
+	/*
+	 * Only allow custom PGC_HBA variables to be created before
+	 * session_preload_libraries completes. After that point, authentication
+	 * has already occurred and check_hba_guc_variables has validated all
+	 * PGC_HBA variables, so defining new ones would bypass validation.
+	 */
+	if (context == PGC_HBA && process_session_preload_libraries_done)
+		ereport(ERROR,
+				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+				 errmsg("PGC_HBA variables must be defined before session_preload_libraries completes"),
+				 errdetail("Attempted to define \"%s\" after session preload", name),
+				 errhint("Move the extension defining this variable to shared_preload_libraries or session_preload_libraries")));
+
 	/*
 	 * We can't support custom GUC_LIST_QUOTE variables, because the wrong
 	 * things would happen if such a variable were set or pg_dump'd when the
@@ -6758,6 +6864,15 @@ validate_option_array_item(const char *name, const char *value,
 			 (superuser() ||
 			  pg_parameter_aclcheck(name, GetUserId(), ACL_SET) == ACLCHECK_OK))
 		 /* ok */ ;
+	else if (gconf->context == PGC_HBA)
+	{
+		if (skipIfNoPermissions)
+			return false;
+		ereport(ERROR,
+				(errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM),
+				 errmsg("parameter \"%s\" cannot be set by ALTER USER or ALTER DATABASE", name),
+				 errhint("Use postgresql.conf, ALTER SYSTEM, or pg_hba.conf to set this parameter.")));
+	}
 	else if (skipIfNoPermissions)
 		return false;
 	/* if a permissions error should be thrown, let set_config_option do it */
diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c
index 0c492fd4fc9..58386ecc585 100644
--- a/src/backend/utils/misc/guc_tables.c
+++ b/src/backend/utils/misc/guc_tables.c
@@ -671,6 +671,7 @@ const char *const GucContext_Names[] =
 	[PGC_INTERNAL] = "internal",
 	[PGC_POSTMASTER] = "postmaster",
 	[PGC_SIGHUP] = "sighup",
+	[PGC_HBA] = "hba",
 	[PGC_SU_BACKEND] = "superuser-backend",
 	[PGC_BACKEND] = "backend",
 	[PGC_SUSET] = "superuser",
@@ -697,6 +698,7 @@ const char *const GucSource_Names[] =
 	[PGC_S_USER] = "user",
 	[PGC_S_DATABASE_USER] = "database user",
 	[PGC_S_CLIENT] = "client",
+	[PGC_S_HBA] = "pg_hba.conf",
 	[PGC_S_OVERRIDE] = "override",
 	[PGC_S_INTERACTIVE] = "interactive",
 	[PGC_S_TEST] = "test",
diff --git a/src/include/libpq/hba.h b/src/include/libpq/hba.h
index 7b93ba4a709..e1848ad03d0 100644
--- a/src/include/libpq/hba.h
+++ b/src/include/libpq/hba.h
@@ -92,6 +92,12 @@ typedef struct AuthToken
 	regex_t    *regex;
 } AuthToken;
 
+typedef struct HbaOption
+{
+	char	   *name;
+	char	   *value;
+} HbaOption;
+
 typedef struct HbaLine
 {
 	char	   *sourcefile;
@@ -140,6 +146,7 @@ typedef struct HbaLine
 	char	   *oauth_scope;
 	char	   *oauth_validator;
 	bool		oauth_skip_usermap;
+	List	   *guc_options;
 } HbaLine;
 
 typedef struct IdentLine
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index db559b39c4d..0a5df62f99a 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -512,6 +512,8 @@ extern void BaseInit(void);
 extern PGDLLIMPORT bool IgnoreSystemIndexes;
 extern PGDLLIMPORT bool process_shared_preload_libraries_in_progress;
 extern PGDLLIMPORT bool process_shared_preload_libraries_done;
+extern PGDLLIMPORT bool process_session_preload_libraries_in_progress;
+extern PGDLLIMPORT bool process_session_preload_libraries_done;
 extern PGDLLIMPORT bool process_shmem_requests_in_progress;
 extern PGDLLIMPORT char *session_preload_libraries_string;
 extern PGDLLIMPORT char *shared_preload_libraries_string;
diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h
index ee02b9aa987..26bda29444b 100644
--- a/src/include/utils/guc.h
+++ b/src/include/utils/guc.h
@@ -73,6 +73,7 @@ typedef enum
 	PGC_INTERNAL,
 	PGC_POSTMASTER,
 	PGC_SIGHUP,
+	PGC_HBA,
 	PGC_SU_BACKEND,
 	PGC_BACKEND,
 	PGC_SUSET,
@@ -115,6 +116,7 @@ typedef enum
 	PGC_S_ENV_VAR,				/* postmaster environment variable */
 	PGC_S_FILE,					/* postgresql.conf */
 	PGC_S_ARGV,					/* postmaster command line */
+	PGC_S_HBA,					/* from pg_hba.conf */
 	PGC_S_GLOBAL,				/* global in-database setting */
 	PGC_S_DATABASE,				/* per-database setting */
 	PGC_S_USER,					/* per-user setting */
@@ -473,6 +475,7 @@ extern int	set_config_with_handle(const char *name, config_handle *handle,
 								   int elevel, bool is_reload);
 extern config_handle *get_config_handle(const char *name);
 extern void check_guc_prefix_reservations(void);
+extern void check_hba_guc_variables(void);
 extern void AlterSystemSetConfigFile(AlterSystemStmt *altersysstmt);
 extern char *GetConfigOptionByName(const char *name, const char **varname,
 								   bool missing_ok);
diff --git a/src/test/modules/meson.build b/src/test/modules/meson.build
index da545219a92..f0e51bb4a5d 100644
--- a/src/test/modules/meson.build
+++ b/src/test/modules/meson.build
@@ -30,6 +30,8 @@ subdir('test_escape')
 subdir('test_extensions')
 subdir('test_ginpostinglist')
 subdir('test_guc_prefix_enforcement')
+subdir('test_hba_guc')
+subdir('test_hba_guc_contexts')
 subdir('test_int128')
 subdir('test_integerset')
 subdir('test_json_parser')
diff --git a/src/test/modules/test_hba_guc/Makefile b/src/test/modules/test_hba_guc/Makefile
new file mode 100644
index 00000000000..2b89088e338
--- /dev/null
+++ b/src/test/modules/test_hba_guc/Makefile
@@ -0,0 +1,21 @@
+# src/test/modules/test_hba_guc/Makefile
+
+MODULE_big = test_hba_guc
+OBJS = \
+	$(WIN32RES) \
+	test_hba_guc.o
+PGFILEDESC = "test_hba_guc - test module for PGC_HBA GUC variables"
+
+EXTENSION = test_hba_guc
+DATA = test_hba_guc--1.0.sql
+
+ifdef USE_PGXS
+PG_CONFIG = pg_config
+PGXS := $(shell $(PG_CONFIG) --pgxs)
+include $(PGXS)
+else
+subdir = src/test/modules/test_hba_guc
+top_builddir = ../../../..
+include $(top_builddir)/src/Makefile.global
+include $(top_srcdir)/contrib/contrib-global.mk
+endif
diff --git a/src/test/modules/test_hba_guc/meson.build b/src/test/modules/test_hba_guc/meson.build
new file mode 100644
index 00000000000..d8bf0233056
--- /dev/null
+++ b/src/test/modules/test_hba_guc/meson.build
@@ -0,0 +1,35 @@
+# Copyright (c) 2026, PostgreSQL Global Development Group
+
+test_hba_guc_sources = files(
+  'test_hba_guc.c',
+)
+
+if host_system == 'windows'
+  test_hba_guc_sources += rc_lib_gen.process(win32ver_rc, extra_args: [
+    '--NAME', 'test_hba_guc',
+    '--FILEDESC', 'test_hba_guc - test module for PGC_HBA GUC variables',])
+endif
+
+test_hba_guc = shared_module('test_hba_guc',
+  test_hba_guc_sources,
+  kwargs: pg_test_mod_args,
+)
+test_install_libs += test_hba_guc
+
+test_install_data += files(
+  'test_hba_guc.control',
+  'test_hba_guc--1.0.sql',
+)
+
+tests += {
+  'name': 'test_hba_guc',
+  'sd': meson.current_source_dir(),
+  'bd': meson.current_build_dir(),
+  'tap': {
+    'tests': [
+      't/001_hba_guc_variables.pl',
+      't/002_hba_guc_sources.pl',
+      't/003_hba_guc_precedence.pl',
+    ],
+  },
+}
diff --git a/src/test/modules/test_hba_guc/t/001_hba_guc_variables.pl b/src/test/modules/test_hba_guc/t/001_hba_guc_variables.pl
new file mode 100644
index 00000000000..625f3be1e51
--- /dev/null
+++ b/src/test/modules/test_hba_guc/t/001_hba_guc_variables.pl
@@ -0,0 +1,56 @@
+# Test PGC_HBA GUC variables in pg_hba.conf
+
+use strict;
+use warnings FATAL => 'all';
+use PostgreSQL::Test::Cluster;
+use PostgreSQL::Test::Utils;
+use Test::More;
+
+my $node = PostgreSQL::Test::Cluster->new('main');
+$node->init;
+
+$node->append_conf('postgresql.conf',
+	"session_preload_libraries = 'test_hba_guc'");
+
+my $hba_conf = $node->data_dir . '/pg_hba.conf';
+open my $hba_fh, '>', $hba_conf or die "Could not open $hba_conf: $!";
+print $hba_fh "# Test HBA configuration with GUC variables\n";
+print $hba_fh "local all all trust test_hba_guc.string_var=from_hba test_hba_guc.int_var=999\n";
+close $hba_fh;
+
+$node->start;
+$node->safe_psql('postgres', 'CREATE EXTENSION test_hba_guc;');
+my $result = $node->safe_psql('postgres',
+	"SELECT current_setting('test_hba_guc.string_var'), current_setting('test_hba_guc.int_var');"
+);
+is($result, 'from_hba|999',
+	'GUC variables set from pg_hba.conf are accessible');
+
+$result = $node->safe_psql('postgres',
+	"SELECT source FROM pg_settings WHERE name = 'test_hba_guc.string_var';"
+);
+is($result, 'pg_hba.conf',
+	'GUC variable source shows pg_hba.conf');
+
+my $node_no_ext = PostgreSQL::Test::Cluster->new('no_extension');
+$node_no_ext->init;
+
+my $hba_conf_no_ext = $node_no_ext->data_dir . '/pg_hba.conf';
+open my $hba_no_ext_fh, '>', $hba_conf_no_ext or die "Could not open $hba_conf_no_ext: $!";
+print $hba_no_ext_fh "# Test HBA configuration with undefined GUC variables\n";
+print $hba_no_ext_fh "local all all trust test_hba_guc.undefined_var=value\n";
+close $hba_no_ext_fh;
+
+$node_no_ext->start;
+
+my ($ret, $stdout, $stderr) = $node_no_ext->psql('postgres', 'SELECT 1;');
+isnt($ret, 0, 'Connection rejected when HBA GUC variable is undefined');
+like($stderr, qr/authentication configuration error/,
+	'Error message indicates authentication configuration problem');
+like($stderr, qr/undefined GUC variable/,
+	'Error message mentions undefined GUC variable');
+
+$node_no_ext->stop;
+$node->stop;
+
+done_testing();
diff --git a/src/test/modules/test_hba_guc/t/002_hba_guc_sources.pl b/src/test/modules/test_hba_guc/t/002_hba_guc_sources.pl
new file mode 100644
index 00000000000..e762b8a96d8
--- /dev/null
+++ b/src/test/modules/test_hba_guc/t/002_hba_guc_sources.pl
@@ -0,0 +1,187 @@
+# Test that PGC_HBA variables can only be set from appropriate sources
+#
+# PGC_HBA variables should be settable from:
+# - postgresql.conf
+# - postgresql.auto.conf (ALTER SYSTEM)
+# - pg_hba.conf
+#
+# But NOT from:
+# - ALTER USER SET
+# - ALTER DATABASE SET
+# - Connection parameters (PGOPTIONS)
+
+use strict;
+use warnings FATAL => 'all';
+use PostgreSQL::Test::Cluster;
+use PostgreSQL::Test::Utils;
+use Test::More;
+
+# Test 1: PGC_HBA variable CAN be set in postgresql.conf
+{
+	my $node = PostgreSQL::Test::Cluster->new('conf_allowed');
+	$node->init;
+
+	$node->append_conf('postgresql.conf',
+		"session_preload_libraries = 'test_hba_guc'");
+
+	$node->append_conf('postgresql.conf',
+		"test_hba_guc.string_var = 'from_postgresql_conf'");
+
+	my $hba_conf = $node->data_dir . '/pg_hba.conf';
+	open my $hba_fh, '>', $hba_conf or die "Could not open $hba_conf: $!";
+	print $hba_fh "local all all trust\n";
+	close $hba_fh;
+
+	$node->start;
+
+	my $result = $node->safe_psql('postgres',
+		"SELECT current_setting('test_hba_guc.string_var');");
+	is($result, 'from_postgresql_conf',
+		'PGC_HBA variable can be set in postgresql.conf');
+
+	$result = $node->safe_psql('postgres',
+		"SELECT source FROM pg_settings WHERE name = 'test_hba_guc.string_var';");
+	is($result, 'configuration file',
+		'Source shows configuration file for postgresql.conf setting');
+
+	$node->stop;
+}
+
+# Test 2: PGC_HBA variable CAN be set via ALTER SYSTEM (postgresql.auto.conf)
+{
+	my $node = PostgreSQL::Test::Cluster->new('alter_system_allowed');
+	$node->init;
+
+	$node->append_conf('postgresql.conf',
+		"session_preload_libraries = 'test_hba_guc'");
+
+	my $hba_conf = $node->data_dir . '/pg_hba.conf';
+	open my $hba_fh, '>', $hba_conf or die "Could not open $hba_conf: $!";
+	print $hba_fh "local all all trust\n";
+	close $hba_fh;
+
+	$node->start;
+
+	$node->safe_psql('postgres',
+		"ALTER SYSTEM SET test_hba_guc.string_var = 'from_alter_system';");
+
+	$node->reload;
+
+	my $result = $node->safe_psql('postgres',
+		"SELECT current_setting('test_hba_guc.string_var');");
+	is($result, 'from_alter_system',
+		'PGC_HBA variable can be set via ALTER SYSTEM');
+
+	$result = $node->safe_psql('postgres',
+		"SELECT source FROM pg_settings WHERE name = 'test_hba_guc.string_var';");
+	is($result, 'configuration file',
+		'Source shows configuration file for ALTER SYSTEM setting');
+
+	$node->stop;
+}
+
+# Test 3: PGC_HBA variable CANNOT be set via ALTER USER SET
+{
+	my $node = PostgreSQL::Test::Cluster->new('alter_user_rejected');
+	$node->init;
+
+	$node->append_conf('postgresql.conf',
+		"session_preload_libraries = 'test_hba_guc'");
+
+	my $hba_conf = $node->data_dir . '/pg_hba.conf';
+	open my $hba_fh, '>', $hba_conf or die "Could not open $hba_conf: $!";
+	print $hba_fh "local all all trust\n";
+	close $hba_fh;
+
+	$node->start;
+
+	$node->safe_psql('postgres', "CREATE USER testuser;");
+
+	my ($ret, $stdout, $stderr) = $node->psql('postgres',
+		"ALTER USER testuser SET test_hba_guc.string_var = 'from_alter_user';");
+	isnt($ret, 0, 'ALTER USER SET rejected for PGC_HBA variable');
+	like($stderr, qr/cannot be set by ALTER USER or ALTER DATABASE/,
+		'Error message indicates ALTER USER is not allowed for PGC_HBA');
+
+	$node->stop;
+}
+
+# Test 4: PGC_HBA variable CANNOT be set via ALTER DATABASE SET
+{
+	my $node = PostgreSQL::Test::Cluster->new('alter_database_rejected');
+	$node->init;
+
+	$node->append_conf('postgresql.conf',
+		"session_preload_libraries = 'test_hba_guc'");
+
+	my $hba_conf = $node->data_dir . '/pg_hba.conf';
+	open my $hba_fh, '>', $hba_conf or die "Could not open $hba_conf: $!";
+	print $hba_fh "local all all trust\n";
+	close $hba_fh;
+
+	$node->start;
+
+	my ($ret, $stdout, $stderr) = $node->psql('postgres',
+		"ALTER DATABASE postgres SET test_hba_guc.string_var = 'from_alter_database';");
+	isnt($ret, 0, 'ALTER DATABASE SET rejected for PGC_HBA variable');
+	like($stderr, qr/cannot be set by ALTER USER or ALTER DATABASE/,
+		'Error message indicates ALTER DATABASE is not allowed for PGC_HBA');
+
+	$node->stop;
+}
+
+# Test 5: PGC_HBA variable CANNOT be set via connection parameter (PGOPTIONS)
+{
+	my $node = PostgreSQL::Test::Cluster->new('pgoptions_rejected');
+	$node->init;
+
+	$node->append_conf('postgresql.conf',
+		"session_preload_libraries = 'test_hba_guc'");
+
+	my $hba_conf = $node->data_dir . '/pg_hba.conf';
+	open my $hba_fh, '>', $hba_conf or die "Could not open $hba_conf: $!";
+	print $hba_fh "local all all trust\n";
+	close $hba_fh;
+
+	$node->start;
+
+	# Connection succeeds but parameter is not set (gets default value)
+	local $ENV{PGOPTIONS} = '-c test_hba_guc.string_var=from_pgoptions';
+	my $result = $node->safe_psql('postgres',
+		"SELECT current_setting('test_hba_guc.string_var');");
+	is($result, 'default_value',
+		'PGC_HBA variable from PGOPTIONS is not set, uses default value');
+
+	my $logfile = $node->logfile;
+	my $log_content = slurp_file($logfile);
+	like($log_content, qr/parameter "test_hba_guc\.string_var" cannot be changed now/,
+		'Warning logged when trying to set PGC_HBA via PGOPTIONS');
+
+	$node->stop;
+}
+
+# Test 6: PGC_HBA variable CANNOT be set via SET command
+{
+	my $node = PostgreSQL::Test::Cluster->new('set_rejected');
+	$node->init;
+
+	$node->append_conf('postgresql.conf',
+		"session_preload_libraries = 'test_hba_guc'");
+
+	my $hba_conf = $node->data_dir . '/pg_hba.conf';
+	open my $hba_fh, '>', $hba_conf or die "Could not open $hba_conf: $!";
+	print $hba_fh "local all all trust\n";
+	close $hba_fh;
+
+	$node->start;
+
+	my ($ret, $stdout, $stderr) = $node->psql('postgres',
+		"SET test_hba_guc.string_var = 'from_set';");
+	isnt($ret, 0, 'SET command rejected for PGC_HBA variable');
+	like($stderr, qr/cannot be changed|cannot be set/,
+		'Error message indicates SET is not allowed for PGC_HBA');
+
+	$node->stop;
+}
+
+done_testing();
diff --git a/src/test/modules/test_hba_guc/t/003_hba_guc_precedence.pl b/src/test/modules/test_hba_guc/t/003_hba_guc_precedence.pl
new file mode 100644
index 00000000000..4afaad95c73
--- /dev/null
+++ b/src/test/modules/test_hba_guc/t/003_hba_guc_precedence.pl
@@ -0,0 +1,105 @@
+# Test that PGC_HBA variables from pg_hba.conf respect line precedence
+#
+# pg_hba.conf is evaluated top-to-bottom, and the first matching line wins.
+# This test verifies that GUC values are taken from the correct matching line.
+
+use strict;
+use warnings FATAL => 'all';
+use PostgreSQL::Test::Cluster;
+use PostgreSQL::Test::Utils;
+use Test::More;
+
+# Test 1: First matching line wins (same user, same database, same auth methods)
+{
+	my $node = PostgreSQL::Test::Cluster->new('first_match_wins');
+	$node->init;
+
+	$node->append_conf('postgresql.conf',
+		"session_preload_libraries = 'test_hba_guc'");
+
+	my $hba_conf = $node->data_dir . '/pg_hba.conf';
+	open my $hba_fh, '>', $hba_conf or die "Could not open $hba_conf: $!";
+	print $hba_fh "local all all trust test_hba_guc.string_var=first_line\n";
+	print $hba_fh "local all all trust test_hba_guc.string_var=second_line\n";
+	print $hba_fh "local all all trust test_hba_guc.string_var=third_line\n";
+	close $hba_fh;
+
+	$node->start;
+
+	my $result = $node->safe_psql('postgres',
+		"SELECT current_setting('test_hba_guc.string_var');");
+	is($result, 'first_line',
+		'First matching HBA line wins when multiple lines match');
+
+	$node->stop;
+}
+
+# Test 2: Database-specific GUC values
+{
+	my $node = PostgreSQL::Test::Cluster->new('database_specific');
+	$node->init;
+
+	$node->append_conf('postgresql.conf',
+		"session_preload_libraries = 'test_hba_guc'");
+
+	# Create test databases
+	$node->start;
+	$node->safe_psql('postgres', 'CREATE DATABASE testdb1;');
+	$node->safe_psql('postgres', 'CREATE DATABASE testdb2;');
+	$node->stop;
+
+	# Create pg_hba.conf with database-specific values
+	my $hba_conf = $node->data_dir . '/pg_hba.conf';
+	open my $hba_fh, '>', $hba_conf or die "Could not open $hba_conf: $!";
+	print $hba_fh "local testdb1 all trust test_hba_guc.string_var=from_testdb1\n";
+	print $hba_fh "local testdb2 all trust test_hba_guc.string_var=from_testdb2\n";
+	print $hba_fh "local all all trust test_hba_guc.string_var=from_wildcard\n";
+	close $hba_fh;
+
+	$node->start;
+
+	my $result = $node->safe_psql('testdb1',
+		"SELECT current_setting('test_hba_guc.string_var');");
+	is($result, 'from_testdb1',
+		'Database-specific GUC value applied for testdb1');
+
+	$result = $node->safe_psql('testdb2',
+		"SELECT current_setting('test_hba_guc.string_var');");
+	is($result, 'from_testdb2',
+		'Database-specific GUC value applied for testdb2');
+
+	$result = $node->safe_psql('postgres',
+		"SELECT current_setting('test_hba_guc.string_var');");
+	is($result, 'from_wildcard',
+		'Wildcard GUC value applied for postgres database');
+
+	$node->stop;
+}
+
+# Test 3: Empty/no GUC options on first match, GUC options on later line
+# The first matching line wins even if it has no GUC options
+{
+	my $node = PostgreSQL::Test::Cluster->new('no_gucs_first');
+	$node->init;
+
+	$node->append_conf('postgresql.conf',
+		"session_preload_libraries = 'test_hba_guc'");
+
+	my $hba_conf = $node->data_dir . '/pg_hba.conf';
+	open my $hba_fh, '>', $hba_conf or die "Could not open $hba_conf: $!";
+	print $hba_fh "local all all trust\n";
+	print $hba_fh "local all all trust test_hba_guc.string_var=not_used\n";
+	close $hba_fh;
+
+	$node->start;
+
+	# Should get default value since first matching line has no GUC options
+	my $result = $node->safe_psql('postgres',
+		"SELECT current_setting('test_hba_guc.string_var');");
+	is($result, 'default_value',
+		'Default GUC value when first matching line has no GUC options');
+
+	$node->stop;
+}
+
+done_testing();
diff --git a/src/test/modules/test_hba_guc/test_hba_guc--1.0.sql b/src/test/modules/test_hba_guc/test_hba_guc--1.0.sql
new file mode 100644
index 00000000000..eb29d174f52
--- /dev/null
+++ b/src/test/modules/test_hba_guc/test_hba_guc--1.0.sql
@@ -0,0 +1,22 @@
+/* src/test/modules/test_hba_guc/test_hba_guc--1.0.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION test_hba_guc" to load this file. \quit
+
+-- Function to get current value of test_hba_guc.string_var
+CREATE FUNCTION get_test_hba_string()
+RETURNS text
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT;
+
+-- Function to get current value of test_hba_guc.int_var
+CREATE FUNCTION get_test_hba_int()
+RETURNS integer
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT;
+
+-- Function to get current value of test_hba_guc.bool_var
+CREATE FUNCTION get_test_hba_bool()
+RETURNS boolean
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT;
diff --git a/src/test/modules/test_hba_guc/test_hba_guc.c b/src/test/modules/test_hba_guc/test_hba_guc.c
new file mode 100644
index 00000000000..74b4a559ac5
--- /dev/null
+++ b/src/test/modules/test_hba_guc/test_hba_guc.c
@@ -0,0 +1,93 @@
+/*-------------------------------------------------------------------------
+ *
+ * test_hba_guc.c
+ *		Test module for PGC_HBA GUC variables
+ *
+ * This module tests the PGC_HBA context level for GUC variables, which
+ * allows variables to be set in pg_hba.conf or postgresql.conf but not
+ * by client connections.
+ *
+ * Copyright (c) 2026, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ *	  src/test/modules/test_hba_guc/test_hba_guc.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "fmgr.h"
+#include "utils/builtins.h"
+#include "utils/guc.h"
+
+PG_MODULE_MAGIC;
+
+static char *test_hba_string = NULL;
+static int	test_hba_int = 0;
+static bool test_hba_bool = false;
+
+PG_FUNCTION_INFO_V1(get_test_hba_string);
+PG_FUNCTION_INFO_V1(get_test_hba_int);
+PG_FUNCTION_INFO_V1(get_test_hba_bool);
+
+void
+_PG_init(void)
+{
+	DefineCustomStringVariable("test_hba_guc.string_var",
+							   "Test PGC_HBA string variable",
+							   "This variable can only be set in pg_hba.conf or postgresql.conf",
+							   &test_hba_string,
+							   "default_value",
+							   PGC_HBA,
+							   0,
+							   NULL,
+							   NULL,
+							   NULL);
+
+	DefineCustomIntVariable("test_hba_guc.int_var",
+							"Test PGC_HBA integer variable",
+							"This variable can only be set in pg_hba.conf or postgresql.conf",
+							&test_hba_int,
+							42,
+							0,
+							10000,
+							PGC_HBA,
+							0,
+							NULL,
+							NULL,
+							NULL);
+
+	DefineCustomBoolVariable("test_hba_guc.bool_var",
+							 "Test PGC_HBA boolean variable",
+							 "This variable can only be set in pg_hba.conf or postgresql.conf",
+							 &test_hba_bool,
+							 false,
+							 PGC_HBA,
+							 0,
+							 NULL,
+							 NULL,
+							 NULL);
+
+	MarkGUCPrefixReserved("test_hba_guc");
+}
+
+Datum
+get_test_hba_string(PG_FUNCTION_ARGS)
+{
+	if (test_hba_string == NULL)
+		PG_RETURN_NULL();
+	PG_RETURN_TEXT_P(cstring_to_text(test_hba_string));
+}
+
+Datum
+get_test_hba_int(PG_FUNCTION_ARGS)
+{
+	PG_RETURN_INT32(test_hba_int);
+}
+
+Datum
+get_test_hba_bool(PG_FUNCTION_ARGS)
+{
+	PG_RETURN_BOOL(test_hba_bool);
+}
diff --git a/src/test/modules/test_hba_guc/test_hba_guc.conf b/src/test/modules/test_hba_guc/test_hba_guc.conf
new file mode 100644
index 00000000000..29ee434a784
--- /dev/null
+++ b/src/test/modules/test_hba_guc/test_hba_guc.conf
@@ -0,0 +1,3 @@
+# Configuration for test_hba_guc regression tests
+# Load in session_preload_libraries to test the proper validation flow
+session_preload_libraries = 'test_hba_guc'
diff --git a/src/test/modules/test_hba_guc/test_hba_guc.control b/src/test/modules/test_hba_guc/test_hba_guc.control
new file mode 100644
index 00000000000..1f4a99069ba
--- /dev/null
+++ b/src/test/modules/test_hba_guc/test_hba_guc.control
@@ -0,0 +1,5 @@
+# test_hba_guc extension
+comment = 'Test module for PGC_HBA GUC variables'
+default_version = '1.0'
+module_pathname = '$libdir/test_hba_guc'
+relocatable = true
diff --git a/src/test/modules/test_hba_guc_contexts/Makefile b/src/test/modules/test_hba_guc_contexts/Makefile
new file mode 100644
index 00000000000..7f805b0abd8
--- /dev/null
+++ b/src/test/modules/test_hba_guc_contexts/Makefile
@@ -0,0 +1,18 @@
+# src/test/modules/test_hba_guc_contexts/Makefile
+
+MODULE_big = test_hba_guc_contexts
+OBJS = \
+	$(WIN32RES) \
+	test_hba_guc_contexts.o
+PGFILEDESC = "test_hba_guc_contexts - test module for PGC_HBA context validation"
+
+ifdef USE_PGXS
+PG_CONFIG = pg_config
+PGXS := $(shell $(PG_CONFIG) --pgxs)
+include $(PGXS)
+else
+subdir = src/test/modules/test_hba_guc_contexts
+top_builddir = ../../../..
+include $(top_builddir)/src/Makefile.global
+include $(top_srcdir)/contrib/contrib-global.mk
+endif
diff --git a/src/test/modules/test_hba_guc_contexts/meson.build b/src/test/modules/test_hba_guc_contexts/meson.build
new file mode 100644
index 00000000000..e2c0eeed229
--- /dev/null
+++ b/src/test/modules/test_hba_guc_contexts/meson.build
@@ -0,0 +1,28 @@
+# Copyright (c) 2026, PostgreSQL Global Development Group
+
+test_hba_guc_contexts_sources = files(
+  'test_hba_guc_contexts.c',
+)
+
+if host_system == 'windows'
+  test_hba_guc_contexts_sources += rc_lib_gen.process(win32ver_rc, extra_args: [
+    '--NAME', 'test_hba_guc_contexts',
+    '--FILEDESC', 'test_hba_guc_contexts - test module for PGC_HBA context validation',])
+endif
+
+test_hba_guc_contexts = shared_module('test_hba_guc_contexts',
+  test_hba_guc_contexts_sources,
+  kwargs: pg_test_mod_args,
+)
+test_install_libs += test_hba_guc_contexts
+
+tests += {
+  'name': 'test_hba_guc_contexts',
+  'sd': meson.current_source_dir(),
+  'bd': meson.current_build_dir(),
+  'tap': {
+    'tests': [
+      't/001_context_validation.pl',
+    ],
+  },
+}
diff --git a/src/test/modules/test_hba_guc_contexts/t/001_context_validation.pl b/src/test/modules/test_hba_guc_contexts/t/001_context_validation.pl
new file mode 100644
index 00000000000..35d540c04e8
--- /dev/null
+++ b/src/test/modules/test_hba_guc_contexts/t/001_context_validation.pl
@@ -0,0 +1,85 @@
+# Test that only variables with context PGC_HBA or below can be set from pg_hba.conf
+
+use strict;
+use warnings FATAL => 'all';
+use PostgreSQL::Test::Cluster;
+use PostgreSQL::Test::Utils;
+use Test::More;
+
+sub test_hba_rejected
+{
+	my ($node_name, $var_name, $test_desc) = @_;
+
+	my $node = PostgreSQL::Test::Cluster->new($node_name);
+	$node->init;
+
+	$node->append_conf('postgresql.conf',
+		"shared_preload_libraries = 'test_hba_guc_contexts'");
+
+	my $hba_conf = $node->data_dir . '/pg_hba.conf';
+	open my $hba_fh, '>', $hba_conf or die "Could not open $hba_conf: $!";
+	print $hba_fh "local all all trust test_hba_guc_contexts.$var_name=from_hba\n";
+	close $hba_fh;
+
+	$node->start;
+
+	my ($ret, $stdout, $stderr) = $node->psql('postgres', 'SELECT 1;');
+	isnt($ret, 0, $test_desc);
+	like($stderr, qr/cannot be changed|cannot be set/,
+		'Error message indicates variable cannot be set from pg_hba.conf');
+
+	$node->stop;
+	return;
+}
+
+sub test_hba_accepted
+{
+	my ($node_name, $var_name, $test_desc) = @_;
+
+	my $node = PostgreSQL::Test::Cluster->new($node_name);
+	$node->init;
+
+	$node->append_conf('postgresql.conf',
+		"shared_preload_libraries = 'test_hba_guc_contexts'");
+
+	my $hba_conf = $node->data_dir . '/pg_hba.conf';
+	open my $hba_fh, '>', $hba_conf or die "Could not open $hba_conf: $!";
+	print $hba_fh "local all all trust test_hba_guc_contexts.$var_name=from_hba\n";
+	close $hba_fh;
+
+	$node->start;
+
+	my ($ret, $stdout, $stderr) = $node->psql('postgres',
+		"SHOW test_hba_guc_contexts.$var_name;");
+	is($ret, 0, $test_desc);
+	like($stdout, qr/from_hba/, 'Variable was set to value from pg_hba.conf');
+
+	$node->stop;
+	return;
+}
+
+# Test 1: PGC_POSTMASTER variable should NOT be settable from pg_hba.conf
+test_hba_rejected('postmaster_rejected', 'postmaster_var',
+	'Connection rejected when trying to set PGC_POSTMASTER from pg_hba.conf');
+
+# Test 2: PGC_SIGHUP variable should NOT be settable from pg_hba.conf
+test_hba_rejected('sighup_rejected', 'sighup_var',
+	'Connection rejected when trying to set PGC_SIGHUP from pg_hba.conf');
+
+# Test 3: PGC_SU_BACKEND variable SHOULD be settable from pg_hba.conf
+test_hba_accepted('su_backend_accepted', 'su_backend_var',
+	'Connection accepted when setting PGC_SU_BACKEND from pg_hba.conf');
+
+# Test 4: PGC_BACKEND variable SHOULD be settable from pg_hba.conf
+test_hba_accepted('backend_accepted', 'backend_var',
+	'Connection accepted when setting PGC_BACKEND from pg_hba.conf');
+
+# Test 5: PGC_SUSET variable SHOULD be settable from pg_hba.conf
+test_hba_accepted('suset_accepted', 'suset_var',
+	'Connection accepted when setting PGC_SUSET from pg_hba.conf');
+
+# Test 6: PGC_USERSET variable SHOULD be settable from pg_hba.conf
+test_hba_accepted('userset_accepted', 'userset_var',
+	'Connection accepted when setting PGC_USERSET from pg_hba.conf');
+
+done_testing();
diff --git a/src/test/modules/test_hba_guc_contexts/test_hba_guc_contexts.c b/src/test/modules/test_hba_guc_contexts/test_hba_guc_contexts.c
new file mode 100644
index 00000000000..0859f70174a
--- /dev/null
+++ b/src/test/modules/test_hba_guc_contexts/test_hba_guc_contexts.c
@@ -0,0 +1,89 @@
+/*-------------------------------------------------------------------------
+ *
+ * test_hba_guc_contexts.c
+ *		Test module for validating PGC_HBA context restrictions
+ *
+ * This module defines GUC variables with different context levels to test
+ * that only variables with context PGC_HBA or below can be set from pg_hba.conf.
+ *
+ * Copyright (c) 2026, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ *	  src/test/modules/test_hba_guc_contexts/test_hba_guc_contexts.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "fmgr.h"
+#include "utils/guc.h"
+
+PG_MODULE_MAGIC;
+
+static char *test_postmaster_var = NULL;
+static char *test_sighup_var = NULL;
+static char *test_su_backend_var = NULL;
+static char *test_backend_var = NULL;
+static char *test_suset_var = NULL;
+static char *test_userset_var = NULL;
+
+void
+_PG_init(void)
+{
+	DefineCustomStringVariable("test_hba_guc_contexts.postmaster_var",
+								"Test PGC_POSTMASTER variable",
+								"This should NOT be settable from pg_hba.conf",
+								&test_postmaster_var,
+								"postmaster_default",
+								PGC_POSTMASTER,
+								0,
+								NULL, NULL, NULL);
+
+	DefineCustomStringVariable("test_hba_guc_contexts.sighup_var",
+								"Test PGC_SIGHUP variable",
+								"This should NOT be settable from pg_hba.conf",
+								&test_sighup_var,
+								"sighup_default",
+								PGC_SIGHUP,
+								0,
+								NULL, NULL, NULL);
+
+	DefineCustomStringVariable("test_hba_guc_contexts.su_backend_var",
+								"Test PGC_SU_BACKEND variable",
+								"This SHOULD be settable from pg_hba.conf",
+								&test_su_backend_var,
+								"su_backend_default",
+								PGC_SU_BACKEND,
+								0,
+								NULL, NULL, NULL);
+
+	DefineCustomStringVariable("test_hba_guc_contexts.backend_var",
+								"Test PGC_BACKEND variable",
+								"This SHOULD be settable from pg_hba.conf",
+								&test_backend_var,
+								"backend_default",
+								PGC_BACKEND,
+								0,
+								NULL, NULL, NULL);
+
+	DefineCustomStringVariable("test_hba_guc_contexts.suset_var",
+								"Test PGC_SUSET variable",
+								"This SHOULD be settable from pg_hba.conf",
+								&test_suset_var,
+								"suset_default",
+								PGC_SUSET,
+								0,
+								NULL, NULL, NULL);
+
+	DefineCustomStringVariable("test_hba_guc_contexts.userset_var",
+								"Test PGC_USERSET variable",
+								"This SHOULD be settable from pg_hba.conf",
+								&test_userset_var,
+								"userset_default",
+								PGC_USERSET,
+								0,
+								NULL, NULL, NULL);
+
+	MarkGUCPrefixReserved("test_hba_guc_contexts");
+}
-- 
2.43.0



view thread (7+ messages)

reply

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Reply to all the recipients using the --to and --cc options:
  reply via email

  To: [email protected]
  Cc: [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected]
  Subject: Re: Custom oauth validator options
  In-Reply-To: <CAN4CZFPmF9fGOcFubwOxqXymhVo_RvbUx3bLoYQcfk=f0mwECw@mail.gmail.com>

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox