v1-0001-PGSERVICEFILE-as-part-of-the-connection-string.patch
text/x-patch
Filename: v1-0001-PGSERVICEFILE-as-part-of-the-connection-string.patch
Type: text/x-patch
Part: 0
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: format-patch
Series: patch v1-0001
Subject: PGSERVICEFILE as part of the connection string
| File | + | − |
|---|---|---|
| doc/src/sgml/libpq.sgml | 15 | 1 |
| src/interfaces/libpq/fe-connect.c | 17 | 1 |
| src/interfaces/libpq/t/006_service.pl | 97 | 0 |
From f419584d2fc7766c143d304ba2f2fad98501d9ea Mon Sep 17 00:00:00 2001
From: Torsten Foertsch <tfoertsch123@gmail.com>
Date: Sat, 16 Nov 2024 20:17:20 +0100
Subject: [PATCH v1] PGSERVICEFILE as part of the connection string
Libpq interprets the PGSERVICEFILE environment variable. However,
the servicefile cannot be specified as part of the connection
string itself.
This change implements that.
---
doc/src/sgml/libpq.sgml | 16 ++++-
src/interfaces/libpq/fe-connect.c | 18 ++++-
src/interfaces/libpq/t/006_service.pl | 97 +++++++++++++++++++++++++++
3 files changed, 129 insertions(+), 2 deletions(-)
create mode 100644 src/interfaces/libpq/t/006_service.pl
diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index bfefb1289e..de8830bf51 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -2202,6 +2202,19 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
</listitem>
</varlistentry>
+ <varlistentry id="libpq-connect-servicefile" xreflabel="servicefile">
+ <term><literal>servicefile</literal></term>
+ <listitem>
+ <para>
+ This option specifies the name of the per-user connection service file
+ (see <xref linkend="libpq-pgservice"/>).
+ Defaults to <filename>~/.pg_service.conf</filename>, or
+ <filename>%APPDATA%\postgresql\.pg_service.conf</filename> on
+ Microsoft Windows.
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry id="libpq-connect-target-session-attrs" xreflabel="target_session_attrs">
<term><literal>target_session_attrs</literal></term>
<listitem>
@@ -9337,7 +9350,8 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough)
On Microsoft Windows, it is named
<filename>%APPDATA%\postgresql\.pg_service.conf</filename> (where
<filename>%APPDATA%</filename> refers to the Application Data subdirectory
- in the user's profile). A different file name can be specified by
+ in the user's profile). A different file name can be specified using the
+ <literal>servicefile</literal> key word in a libpq connection string or by
setting the environment variable <envar>PGSERVICEFILE</envar>.
The system-wide file is named <filename>pg_service.conf</filename>.
By default it is sought in the <filename>etc</filename> directory
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index 51083dcfd8..117588c0e8 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -192,6 +192,9 @@ static const internalPQconninfoOption PQconninfoOptions[] = {
{"service", "PGSERVICE", NULL, NULL,
"Database-Service", "", 20, -1},
+ {"servicefile", "PGSERVICEFILE", NULL, NULL,
+ "Database-Service-File", "", 64, -1},
+
{"user", "PGUSER", NULL, NULL,
"Database-User", "", 20,
offsetof(struct pg_conn, pguser)},
@@ -5493,6 +5496,7 @@ static int
parseServiceInfo(PQconninfoOption *options, PQExpBuffer errorMessage)
{
const char *service = conninfo_getval(options, "service");
+ const char *service_fname = conninfo_getval(options, "servicefile");
char serviceFile[MAXPGPATH];
char *env;
bool group_found = false;
@@ -5515,7 +5519,9 @@ parseServiceInfo(PQconninfoOption *options, PQExpBuffer errorMessage)
* Try PGSERVICEFILE if specified, else try ~/.pg_service.conf (if that
* exists).
*/
- if ((env = getenv("PGSERVICEFILE")) != NULL)
+ if (service_fname != NULL)
+ strlcpy(serviceFile, service_fname, sizeof(serviceFile));
+ else if ((env = getenv("PGSERVICEFILE")) != NULL)
strlcpy(serviceFile, env, sizeof(serviceFile));
else
{
@@ -5678,6 +5684,16 @@ parseServiceFile(const char *serviceFile,
goto exit;
}
+ if (strcmp(key, "servicefile") == 0)
+ {
+ libpq_append_error(errorMessage,
+ "nested servicefile specifications not supported in service file \"%s\", line %d",
+ serviceFile,
+ linenr);
+ result = 3;
+ goto exit;
+ }
+
/*
* Set the parameter --- but don't override any previous
* explicit setting.
diff --git a/src/interfaces/libpq/t/006_service.pl b/src/interfaces/libpq/t/006_service.pl
new file mode 100644
index 0000000000..5de84b78af
--- /dev/null
+++ b/src/interfaces/libpq/t/006_service.pl
@@ -0,0 +1,97 @@
+# Copyright (c) 2023-2024, PostgreSQL Global Development Group
+use strict;
+use warnings FATAL => 'all';
+use Config;
+use PostgreSQL::Test::Utils;
+use PostgreSQL::Test::Cluster;
+use Test::More;
+
+# This tests "service" and "servicefile"
+
+# Cluster setup which is shared for testing both load balancing methods
+my $node = PostgreSQL::Test::Cluster->new('node');
+
+# Create a data directory with initdb
+$node->init();
+
+# Start the PostgreSQL server
+$node->start();
+
+my $td = PostgreSQL::Test::Utils::tempdir;
+my $srvfile = "$td/pgsrv.conf";
+
+open my $fh, '>', $srvfile or die $!;
+print $fh "[my_srv]\n";
+print $fh +($node->connstr =~ s/ /\n/gr), "\n";
+close $fh;
+
+{
+ local $ENV{PGSERVICEFILE} = $srvfile;
+ $node->connect_ok(
+ 'service=my_srv',
+ 'service=my_srv',
+ sql => "SELECT 'connect1'",
+ expected_stdout => qr/connect1/);
+
+ $node->connect_ok(
+ 'postgres://?service=my_srv',
+ 'postgres://?service=my_srv',
+ sql => "SELECT 'connect2'",
+ expected_stdout => qr/connect2/);
+
+ local $ENV{PGSERVICE} = 'my_srv';
+ $node->connect_ok(
+ '',
+ 'envvar: PGSERVICE=my_srv',
+ sql => "SELECT 'connect3'",
+ expected_stdout => qr/connect3/);
+
+ $node->connect_fails(
+ 'service=non-existent-service',
+ 'service=non-existent-service',
+ expected_stderr => qr/definition of service "non-existent-service" not found/);
+}
+
+{
+ $node->connect_ok(
+ q{service=my_srv servicefile='}.$srvfile.q{'},
+ 'service=my_srv servicefile=...',
+ sql => "SELECT 'connect4'",
+ expected_stdout => qr/connect4/);
+
+ $node->connect_ok(
+ 'postgresql:///?service=my_srv&servicefile='.($srvfile =~ s!/!%2F!gr),
+ 'postgresql:///?service=my_srv&servicefile=...',
+ sql => "SELECT 'connect5'",
+ expected_stdout => qr/connect5/);
+
+ local $ENV{PGSERVICE} = 'my_srv';
+ $node->connect_ok(
+ q{servicefile='}.$srvfile.q{'},
+ 'envvar: PGSERVICE=my_srv + servicefile=...',
+ sql => "SELECT 'connect6'",
+ expected_stdout => qr/connect6/);
+
+ $node->connect_ok(
+ 'postgresql://?servicefile='.($srvfile =~ s!/!%2F!gr),
+ 'envvar: PGSERVICE=my_srv + postgresql://?servicefile=...',
+ sql => "SELECT 'connect6'",
+ expected_stdout => qr/connect6/);
+}
+
+{
+ local $ENV{PGSERVICEFILE} = 'non-existent-file.conf';
+
+ $node->connect_fails(
+ 'service=my_srv',
+ 'service=... fails with wrong PGSERVICEFILE',
+ expected_stderr => qr/service file "non-existent-file\.conf" not found/);
+
+ $node->connect_ok(
+ q{service=my_srv servicefile='}.$srvfile.q{'},
+ 'servicefile= takes precedence over PGSERVICEFILE',
+ sql => "SELECT 'connect7'",
+ expected_stdout => qr/connect7/);
+}
+
+done_testing();
--
2.34.1