Laurenz,

Thank you for the review.

Included with this email are 2 patches.
`0004-Add-ldapservice-connection-parameter.patch` contains all of the
changes you recommended:
- Removed the redundant/unneeded check for the string ldap
- The function has  now been moved from parseServiceInfo() to
conninfo_add_defaults().
  - I will say though the reason I included this in parseServiceInfo()
was I consider this new LDAP functionality and the existing
pg_service.conf() functionality all different ways of accessing a
postgres "service". I thought it made sense to handle all service
parsing in a single function. Happy to keep it moved it if I was
incorrect about this
- A failed parseServiceInfo() now returns false in conninfo_add_defaults()
- Made documentation a bit more explicit.

Also I added support for specifying PGLDAPPSERVICE in the form of an
env var as well. Also added additional tests.

> For the same reason, I am not entirely happy with the name "ldapservice", but 
> I can't think of anything better.
I have a few suggestions here. Maybe `remoteserviceuri` would be  a
better name. This tells the reader that it's a uri and not the name of
a service. LDAP is not mentioned because maybe in the future this
functionality could be expanded to read connection parameters  from an
HTTP server, postgres side channel, etc.

Alternatively we could bypass the naming altogether and add this
functionality into the existing pgservice parameter. I have attached a
second patch `0001-Allow-LDAP-lookup-from-pgservice-connection-paramete.patch`
that has an implementation of this. The idea being that if you pass a
string that starts with ldap:// it will look up the service at the
given LDAP uri instead of looking for the corresponding service in the
pg_service.conf file. This would be a breaking change for anyone who
populated their service names  as  LDAP uris, I would imagine this
would be a very small number of users, if any. It also ends up being a
smaller patch overall.

> I don't have an LDAP server handy, so I couldn't test the patch

On the off chance that it helps, we now have unit tests over the LDAP
service functionality. It should automatically start a slapd server,
etc. If you add a false assertion in the perl code it should fail the
test and also dump a bunch of files into the src/test/ldap directory.
You can copy paste the slapd invocation that is left there. I did have
to fight with App Armor a bit to get it to be okay with a slapd
process reading files in unexpected locations.

Thanks again for the review,
Andrew Jackson
From d883f5a8c0340667a13c41f1a33852deff427a1b Mon Sep 17 00:00:00 2001
From: Andrew Jackson <[email protected]>
Date: Sat, 28 Mar 2026 23:29:48 -0500
Subject: [PATCH] Add ldapservice connection parameter

Currently there exists, only in pg_service.conf, the ability to look
up connection parameters from a centralized LDAP server. This patch
expands the usability of this be allowing it to be specified directly in
a connection string instead of only in a pg_service.conf file.

This adds the PGLDAPSERVICE env var that provides an envvar interface
to this functionality. Also the functionality has been moved to
conninfo_add_defaults after review. Also 2 tests have been added. One to
validate the env var functionality and one to validate that this
parameter is ignored when used in pg_service.conf files
---
 doc/src/sgml/libpq.sgml                       | 22 ++++++++++++++
 src/interfaces/libpq/fe-connect.c             | 14 +++++++++
 src/interfaces/libpq/libpq-int.h              |  1 +
 .../t/003_ldap_connection_param_lookup.pl     | 29 +++++++++++++++++++
 4 files changed, 66 insertions(+)

diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index 6db823808fc..bbd31cf6f95 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -2350,6 +2350,18 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
       </listitem>
      </varlistentry>
 
+     <varlistentry id="libpq-connect-ldapservice" xreflabel="ldapservice">
+      <term><literal>ldapservice</literal></term>
+      <listitem>
+       <para>
+        This option specifies an LDAP query that can be used to reference connection parameters
+        stored on an LDAP server. Any connection parameter that is looked up in this way is
+        overridden by explicitly named connection parameters. This parameter is ignored when used
+        in a pg_service.conf file. This functionality is described in more detail in <xref linkend="libpq-ldap"/>.
+        </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry id="libpq-connect-target-session-attrs" xreflabel="target_session_attrs">
       <term><literal>target_session_attrs</literal></term>
       <listitem>
@@ -9170,6 +9182,16 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough)
      </para>
     </listitem>
 
+    <listitem>
+     <para>
+      <indexterm>
+       <primary><envar>PGLDAPSERVICE</envar></primary>
+      </indexterm>
+      <envar>PGLDAPSERVICE</envar> behaves the same as the
+      <xref linkend="libpq-connect-ldapservice"/> connection parameter.
+     </para>
+    </listitem>
+
     <listitem>
      <para>
       <indexterm>
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index db9b4c8edbf..b4b9bb1bb24 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -207,6 +207,10 @@ static const internalPQconninfoOption PQconninfoOptions[] = {
 		"Database-Service-File", "", 64,
 	offsetof(struct pg_conn, pgservicefile)},
 
+	{"ldapservice", "PGLDAPSERVICE", NULL, NULL,
+		"Database-LDAP-Service", "", 20,
+	offsetof(struct pg_conn, pgldapservice)},
+
 	{"user", "PGUSER", NULL, NULL,
 		"Database-User", "", 20,
 	offsetof(struct pg_conn, pguser)},
@@ -6726,6 +6730,16 @@ conninfo_add_defaults(PQconninfoOption *options, PQExpBuffer errorMessage)
 	PQconninfoOption *sslmode_default = NULL,
 			   *sslrootcert = NULL;
 	char	   *tmp;
+#ifdef USE_LDAP
+	const char *ldapservice = conninfo_getval(options, "ldapservice");
+
+	if (ldapservice == NULL)
+		ldapservice = getenv("PGLDAPSERVICE");
+
+	if (ldapservice != NULL)
+		if (ldapServiceLookup(ldapservice, options, errorMessage) != 0)
+			return false;
+#endif
 
 	/*
 	 * If there's a service spec, use it to obtain any not-explicitly-given
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index bd7eb59f5f8..d8510cc0928 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -392,6 +392,7 @@ struct pg_conn
 	char	   *pgservice;		/* Postgres service, if any */
 	char	   *pgservicefile;	/* path to a service file containing
 								 * service(s) */
+	char	   *pgldapservice;	/* Postgres LDAP service, if any */
 	char	   *pguser;			/* Postgres username and password, if any */
 	char	   *pgpass;
 	char	   *pgpassfile;		/* path to a file containing password(s) */
diff --git a/src/test/ldap/t/003_ldap_connection_param_lookup.pl b/src/test/ldap/t/003_ldap_connection_param_lookup.pl
index 359fc7a998a..03d276120b4 100644
--- a/src/test/ldap/t/003_ldap_connection_param_lookup.pl
+++ b/src/test/ldap/t/003_ldap_connection_param_lookup.pl
@@ -80,6 +80,9 @@ append_to_file(
 	$srvfile_valid, qq{
 [my_srv]
 ldap://localhost:$ldap_port/dc=example,dc=net?description?one?(cn=mydatabase)
+
+[my_srv_2]
+ldapservice=ldap://localhost:$ldap_port/dc=example,dc=net?description?one?(cn=mydatabase)
 });
 
 # File defined with no contents, used as default value for
@@ -196,6 +199,32 @@ local $ENV{PGSERVICEFILE} = "$srvfile_empty";
 		expected_stdout =>
 		  qr/definition of service "undefined-service" not found/);
 
+	delete $ENV{PGSERVICE};
+
+	$dummy_node->connect_ok(
+		"ldapservice=ldap://localhost:$ldap_port/dc=example,dc=net?description?one?(cn=mydatabase)",
+		'connection with correct "ldapservice" string',
+		sql => "SELECT 'connect2_4'",
+		expected_stdout => qr/connect2_4/);
+
+	$dummy_node->connect_ok(
+		"postgres://?ldapservice=ldap%3A%2F%2Flocalhost%3A$ldap_port%2Fdc%3Dexample%2Cdc%3Dnet%3Fdescription%3Fone%3F%28cn%3Dmydatabase%29",
+		'connection with correct "ldapservice"',
+		sql => "SELECT 'connect2_5'",
+		expected_stdout => qr/connect2_5/);
+
+	local $ENV{PGLDAPSERVICE} = "ldap://localhost:$ldap_port/dc=example,dc=net?description?one?(cn=mydatabase)";
+	$dummy_node->connect_ok(
+		"",
+		'connection with correct "ldapservice" provided by env var',
+		sql => "SELECT 'connect2_6'",
+		expected_stdout => qr/connect2_6/);
+	delete $ENV{PGLDAPSERVICE};
+
+	$dummy_node->connect_fails(
+		'',
+		'connection fails with ldapservice specified in pg_service.conf file');
+
 	# Remove default pg_service.conf.
 	unlink($srvfile_default);
 }
-- 
2.51.2

From 5f9a720a526471286864d92559e6361460ca04cb Mon Sep 17 00:00:00 2001
From: Andrew Jackson <[email protected]>
Date: Sat, 28 Mar 2026 23:29:48 -0500
Subject: [PATCH] Allow LDAP lookup from pgservice connection parameter

Currently there exists, only in pg_service.conf, the ability to look
up connection parameters from a centralized LDAP server. This patch
expands the usability of this be allowing it to be specified directly in
a connection string instead of only in a pg_service.conf file.

This patch adds a check in parseServiceInfo that checks if pgservice is
an LDAP scheme address and if so attempts to connect to that LDAP server
and grab connection parameters from there. This is a breaking change in
that it is possible that people previously named their pgservice after
an LDAP address.
---
 doc/src/sgml/libpq.sgml                       |  4 +++
 src/interfaces/libpq/fe-connect.c             |  5 ++++
 .../t/003_ldap_connection_param_lookup.pl     | 25 +++++++++++++++++++
 3 files changed, 34 insertions(+)

diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index 6db823808fc..4d230a2a65f 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -2333,6 +2333,10 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
         name in <filename>pg_service.conf</filename> that holds additional connection parameters.
         This allows applications to specify only a service name so connection parameters
         can be centrally maintained. See <xref linkend="libpq-pgservice"/>.
+
+        You can also specify and LDAP address here which will look up parameters from an LDAP
+        server. See <xref linkend="libpq-ldap"/>. Please note that if an LDAP address is specified
+        no attempt will be made to look up parameters in <filename>pg_service.conf</filename>.
        </para>
       </listitem>
      </varlistentry>
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index db9b4c8edbf..50ada2939b6 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -6006,6 +6006,11 @@ parseServiceInfo(PQconninfoOption *options, PQExpBuffer errorMessage)
 	if (service == NULL)
 		return 0;
 
+#ifdef USE_LDAP
+	if (pg_strncasecmp(service, LDAP_URL, strlen(LDAP_URL)) == 0)
+		return ldapServiceLookup(service, options, errorMessage);
+#endif
+
 	/*
 	 * First, try the "servicefile" option in connection string.  Then, try
 	 * the PGSERVICEFILE environment variable.  Finally, check
diff --git a/src/test/ldap/t/003_ldap_connection_param_lookup.pl b/src/test/ldap/t/003_ldap_connection_param_lookup.pl
index 359fc7a998a..67da90578cf 100644
--- a/src/test/ldap/t/003_ldap_connection_param_lookup.pl
+++ b/src/test/ldap/t/003_ldap_connection_param_lookup.pl
@@ -80,6 +80,9 @@ append_to_file(
 	$srvfile_valid, qq{
 [my_srv]
 ldap://localhost:$ldap_port/dc=example,dc=net?description?one?(cn=mydatabase)
+
+[my_srv_2]
+ldapservice=ldap://localhost:$ldap_port/dc=example,dc=net?description?one?(cn=mydatabase)
 });
 
 # File defined with no contents, used as default value for
@@ -196,6 +199,28 @@ local $ENV{PGSERVICEFILE} = "$srvfile_empty";
 		expected_stdout =>
 		  qr/definition of service "undefined-service" not found/);
 
+	delete $ENV{PGSERVICE};
+
+	$dummy_node->connect_ok(
+		"service=ldap://localhost:$ldap_port/dc=example,dc=net?description?one?(cn=mydatabase)",
+		'connection with correct "service" string populated with LDAP address',
+		sql => "SELECT 'connect2_4'",
+		expected_stdout => qr/connect2_4/);
+
+	$dummy_node->connect_ok(
+		"postgres://?service=ldap%3A%2F%2Flocalhost%3A$ldap_port%2Fdc%3Dexample%2Cdc%3Dnet%3Fdescription%3Fone%3F%28cn%3Dmydatabase%29",
+		'connection with correct "ldapservice" string populated with LDAP address',
+		sql => "SELECT 'connect2_5'",
+		expected_stdout => qr/connect2_5/);
+
+	local $ENV{PGSERVICE} = "ldap://localhost:$ldap_port/dc=example,dc=net?description?one?(cn=mydatabase)";
+	$dummy_node->connect_ok(
+		"",
+		'connection with correct "service" provided by env var populated with LDAP address',
+		sql => "SELECT 'connect2_6'",
+		expected_stdout => qr/connect2_6/);
+	delete $ENV{PGLDAPSERVICE};
+
 	# Remove default pg_service.conf.
 	unlink($srvfile_default);
 }
-- 
2.51.2

Reply via email to