From 7ccf7d7074ce34b61835d3c74acba853c9a394d3 Mon Sep 17 00:00:00 2001
From: Jacob Champion <jacob.champion@enterprisedb.com>
Date: Fri, 10 Oct 2025 15:56:02 -0700
Subject: [PATCH 6/6] WIP: pg_service: implement PGNODEFAULTS

Tests need to ensure that they have full control of the default
connection state. While PGSYSCONFDIR was already redirected as needed,
the user's config file would still be consulted when PGSERVICEFILE was
empty. Add a PGNODEFAULTS envvar to turn off the previous feature, and
make use of it in pg_regress and Test::Cluster connections.
---
 src/interfaces/libpq/fe-connect.c      | 33 +++++++++++++++++---------
 src/interfaces/libpq/t/006_service.pl  | 10 ++++++++
 src/test/perl/PostgreSQL/Test/Utils.pm |  3 +++
 src/test/regress/pg_regress.c          |  9 +++++++
 4 files changed, 44 insertions(+), 11 deletions(-)

diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index 4fbaf4727ef..000001de05d 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -5935,6 +5935,7 @@ parseServiceInfo(PQconninfoOption *defaults, PQconninfoOption *options,
 	const char *service_fname = conninfo_getval(options, "servicefile");
 	char		userServiceFile[MAXPGPATH];
 	char		systemServiceFile[MAXPGPATH];
+	bool		load_defaults = true;
 	bool		have_user_file = false;
 	bool		have_system_file = false;
 	char	   *env;
@@ -5951,6 +5952,10 @@ parseServiceInfo(PQconninfoOption *defaults, PQconninfoOption *options,
 	if (service == NULL)
 		service = getenv("PGSERVICE");
 
+	/* PGNODEFAULTS=1 disables the use of defaults sections. */
+	if ((env = getenv("PGNODEFAULTS")) != NULL && strcmp(env, "1") == 0)
+		load_defaults = false;
+
 	/*
 	 * First, try the "servicefile" option in connection string.  Then, try
 	 * the PGSERVICEFILE environment variable.  Finally, check
@@ -5971,13 +5976,16 @@ parseServiceInfo(PQconninfoOption *defaults, PQconninfoOption *options,
 			goto next_file;
 	}
 
-	/*
-	 * Pull defaults out of the user file first, if one exists. They take
-	 * precedence over any defaults in the system file.
-	 */
-	status = parseServiceFile(userServiceFile, NULL, defaults, errorMessage, &group_found);
-	if (status != 0)
-		return status;
+	if (load_defaults)
+	{
+		/*
+		 * Pull defaults out of the user file first, if one exists. They take
+		 * precedence over any defaults in the system file.
+		 */
+		status = parseServiceFile(userServiceFile, NULL, defaults, errorMessage, &group_found);
+		if (status != 0)
+			return status;
+	}
 
 	have_user_file = true;
 
@@ -5992,10 +6000,13 @@ next_file:
 	if (stat(systemServiceFile, &stat_buf) != 0)
 		goto last_file;
 
-	/* Fill in system defaults for any options not given in the user file. */
-	status = parseServiceFile(systemServiceFile, NULL, defaults, errorMessage, &group_found);
-	if (status != 0)
-		return status;
+	if (load_defaults)
+	{
+		/* Fill in system defaults for any options not given in the user file. */
+		status = parseServiceFile(systemServiceFile, NULL, defaults, errorMessage, &group_found);
+		if (status != 0)
+			return status;
+	}
 
 	have_system_file = true;
 
diff --git a/src/interfaces/libpq/t/006_service.pl b/src/interfaces/libpq/t/006_service.pl
index 973035aac9b..cbb0b2d1c46 100644
--- a/src/interfaces/libpq/t/006_service.pl
+++ b/src/interfaces/libpq/t/006_service.pl
@@ -146,6 +146,8 @@ options=-O
 		append_to_file($srvfile_defaults, $param . "\n");
 	}
 
+	# Test::Utils will have disabled dynamic defaults.
+	local $ENV{PGNODEFAULTS} = "0";
 	local $ENV{PGSERVICEFILE} = $srvfile_defaults;
 	$dummy_node->connect_ok(
 		'',
@@ -210,6 +212,14 @@ options=-O
 		'',
 		'defaults section must be first in the file',
 		expected_stderr => qr/only the first section may be marked default/);
+
+	{
+		# Re-enable PGNODEFAULTS and check that we can continue using the
+		# working service, even though the defaults section is broken.
+		local $ENV{PGNODEFAULTS} = "1";
+		$dummy_node->connect_ok('service=my_srv',
+			'connection with bad defaults section, but PGNODEFAULTS=1');
+	}
 }
 
 # Checks case of incorrect service file.
diff --git a/src/test/perl/PostgreSQL/Test/Utils.pm b/src/test/perl/PostgreSQL/Test/Utils.pm
index 85d36a3171e..3f27f315a4d 100644
--- a/src/test/perl/PostgreSQL/Test/Utils.pm
+++ b/src/test/perl/PostgreSQL/Test/Utils.pm
@@ -111,6 +111,9 @@ BEGIN
 	$ENV{LC_NUMERIC} = 'C';
 	setlocale(LC_ALL, "");
 
+	# Disable any defaults coming from pg_service.conf.
+	$ENV{PGNODEFAULTS} = "1";
+
 	# This list should be kept in sync with pg_regress.c.
 	my @envkeys = qw (
 	  PGCHANNELBINDING
diff --git a/src/test/regress/pg_regress.c b/src/test/regress/pg_regress.c
index 61c035a3983..c8b424a8336 100644
--- a/src/test/regress/pg_regress.c
+++ b/src/test/regress/pg_regress.c
@@ -728,6 +728,15 @@ initialize_environment(void)
 	 */
 	setenv("PGAPPNAME", "pg_regress", 1);
 
+	/*
+	 * Disable any defaults coming from pg_service.conf, which would thwart our
+	 * unsetenv()s below.
+	 *
+	 * TODO this would need to be documented for installcheck: only environment
+	 * variables can be used to point to the system under test
+	 */
+	setenv("PGNODEFAULTS", "1", 1);
+
 	/*
 	 * Set variables that the test scripts may need to refer to.
 	 */
-- 
2.34.1

