Skip to content

Commit b8f9937

Browse files
author
Commitfest Bot
committed
[CF 5387] v13 - Allow PGSERVICEFILE to be specified as part of the connection string
This branch was automatically generated by a robot using patches from an email thread registered at: https://commitfest.postgresql.org/patch/5387 The branch will be overwritten each time a new patch version is posted to the thread, and also periodically to check for bitrot caused by changes on the master branch. Patch(es): https://www.postgresql.org/message-id/[email protected] Author(s): Torsten Foertsch
2 parents 990571a + 79adc54 commit b8f9937

File tree

6 files changed

+160
-7
lines changed

6 files changed

+160
-7
lines changed

doc/src/sgml/libpq.sgml

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2320,6 +2320,16 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
23202320
</listitem>
23212321
</varlistentry>
23222322

2323+
<varlistentry id="libpq-connect-servicefile" xreflabel="servicefile">
2324+
<term><literal>servicefile</literal></term>
2325+
<listitem>
2326+
<para>
2327+
This option specifies the location of connection service file
2328+
(see <xref linkend="libpq-pgservice"/>).
2329+
</para>
2330+
</listitem>
2331+
</varlistentry>
2332+
23232333
<varlistentry id="libpq-connect-target-session-attrs" xreflabel="target_session_attrs">
23242334
<term><literal>target_session_attrs</literal></term>
23252335
<listitem>
@@ -9146,6 +9156,8 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough)
91469156
Defaults to <filename>~/.pg_service.conf</filename>, or
91479157
<filename>%APPDATA%\postgresql\.pg_service.conf</filename> on
91489158
Microsoft Windows.
9159+
<envar>This environment variable</envar> behaves the same as the <xref
9160+
linkend="libpq-connect-servicefile"/> connection parameter.
91499161
</para>
91509162
</listitem>
91519163

@@ -9576,7 +9588,8 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough)
95769588
On Microsoft Windows, it is named
95779589
<filename>%APPDATA%\postgresql\.pg_service.conf</filename> (where
95789590
<filename>%APPDATA%</filename> refers to the Application Data subdirectory
9579-
in the user's profile). A different file name can be specified by
9591+
in the user's profile). A different file name can be specified using the
9592+
<literal>servicefile</literal> key word in a libpq connection string or by
95809593
setting the environment variable <envar>PGSERVICEFILE</envar>.
95819594
The system-wide file is named <filename>pg_service.conf</filename>.
95829595
By default it is sought in the <filename>etc</filename> directory

doc/src/sgml/ref/psql-ref.sgml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4623,6 +4623,15 @@ bar
46234623
</listitem>
46244624
</varlistentry>
46254625

4626+
<varlistentry id="app-psql-variables-servicefile">
4627+
<term><varname>SERVICEFILE</varname></term>
4628+
<listitem>
4629+
<para>
4630+
The service file name, if applicable.
4631+
</para>
4632+
</listitem>
4633+
</varlistentry>
4634+
46264635
<varlistentry id="app-psql-variables-shell-error">
46274636
<term><varname>SHELL_ERROR</varname></term>
46284637
<listitem>

src/bin/psql/command.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4481,6 +4481,7 @@ SyncVariables(void)
44814481
char vbuf[32];
44824482
const char *server_version;
44834483
char *service_name;
4484+
char *service_file;
44844485

44854486
/* get stuff from connection */
44864487
pset.encoding = PQclientEncoding(pset.db);
@@ -4500,6 +4501,11 @@ SyncVariables(void)
45004501
if (service_name)
45014502
pg_free(service_name);
45024503

4504+
service_file = get_conninfo_value("servicefile");
4505+
SetVariable(pset.vars, "SERVICEFILE", service_file);
4506+
if (service_file)
4507+
pg_free(service_file);
4508+
45034509
/* this bit should match connection_warnings(): */
45044510
/* Try to get full text form of version, might include "devel" etc */
45054511
server_version = PQparameterStatus(pset.db, "server_version");
@@ -4529,6 +4535,7 @@ UnsyncVariables(void)
45294535
{
45304536
SetVariable(pset.vars, "DBNAME", NULL);
45314537
SetVariable(pset.vars, "SERVICE", NULL);
4538+
SetVariable(pset.vars, "SERVICEFILE", NULL);
45324539
SetVariable(pset.vars, "USER", NULL);
45334540
SetVariable(pset.vars, "HOST", NULL);
45344541
SetVariable(pset.vars, "PORT", NULL);

src/interfaces/libpq/fe-connect.c

Lines changed: 51 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,10 @@ static const internalPQconninfoOption PQconninfoOptions[] = {
201201
"Database-Service", "", 20,
202202
offsetof(struct pg_conn, pgservice)},
203203

204+
{"servicefile", "PGSERVICEFILE", NULL, NULL,
205+
"Database-Service-File", "", 64,
206+
offsetof(struct pg_conn, pgservicefile)},
207+
204208
{"user", "PGUSER", NULL, NULL,
205209
"Database-User", "", 20,
206210
offsetof(struct pg_conn, pguser)},
@@ -5062,6 +5066,7 @@ freePGconn(PGconn *conn)
50625066
free(conn->dbName);
50635067
free(conn->replication);
50645068
free(conn->pgservice);
5069+
free(conn->pgservicefile);
50655070
free(conn->pguser);
50665071
if (conn->pgpass)
50675072
{
@@ -5914,6 +5919,7 @@ static int
59145919
parseServiceInfo(PQconninfoOption *options, PQExpBuffer errorMessage)
59155920
{
59165921
const char *service = conninfo_getval(options, "service");
5922+
const char *service_fname = conninfo_getval(options, "servicefile");
59175923
char serviceFile[MAXPGPATH];
59185924
char *env;
59195925
bool group_found = false;
@@ -5933,10 +5939,13 @@ parseServiceInfo(PQconninfoOption *options, PQExpBuffer errorMessage)
59335939
return 0;
59345940

59355941
/*
5936-
* Try PGSERVICEFILE if specified, else try ~/.pg_service.conf (if that
5937-
* exists).
5942+
* First, try the "servicefile" option in connection string. Then, try
5943+
* the PGSERVICEFILE environment variable. Finally, check
5944+
* ~/.pg_service.conf (if that exists).
59385945
*/
5939-
if ((env = getenv("PGSERVICEFILE")) != NULL)
5946+
if (service_fname != NULL)
5947+
strlcpy(serviceFile, service_fname, sizeof(serviceFile));
5948+
else if ((env = getenv("PGSERVICEFILE")) != NULL)
59405949
strlcpy(serviceFile, env, sizeof(serviceFile));
59415950
else
59425951
{
@@ -6092,7 +6101,17 @@ parseServiceFile(const char *serviceFile,
60926101
if (strcmp(key, "service") == 0)
60936102
{
60946103
libpq_append_error(errorMessage,
6095-
"nested service specifications not supported in service file \"%s\", line %d",
6104+
"nested \"service\" specifications not supported in service file \"%s\", line %d",
6105+
serviceFile,
6106+
linenr);
6107+
result = 3;
6108+
goto exit;
6109+
}
6110+
6111+
if (strcmp(key, "servicefile") == 0)
6112+
{
6113+
libpq_append_error(errorMessage,
6114+
"nested \"servicefile\" specifications not supported in service file \"%s\", line %d",
60966115
serviceFile,
60976116
linenr);
60986117
result = 3;
@@ -6135,6 +6154,34 @@ parseServiceFile(const char *serviceFile,
61356154
}
61366155

61376156
exit:
6157+
6158+
/*
6159+
* If a service has been successfully found, set the "servicefile" option
6160+
* if not already set. This matters for the cases where we use a default
6161+
* service file or a service file set with PGSERVICEFILE, where we want to
6162+
* be able track the value used.
6163+
*/
6164+
if (*group_found && result == 0)
6165+
{
6166+
for (i = 0; options[i].keyword; i++)
6167+
{
6168+
if (strcmp(options[i].keyword, "servicefile") != 0)
6169+
continue;
6170+
6171+
/* If value is already set, nothing to do */
6172+
if (options[i].val != NULL)
6173+
break;
6174+
6175+
options[i].val = strdup(serviceFile);
6176+
if (options[i].val == NULL)
6177+
{
6178+
libpq_append_error(errorMessage, "out of memory");
6179+
result = 3;
6180+
}
6181+
break;
6182+
}
6183+
}
6184+
61386185
fclose(f);
61396186

61406187
return result;

src/interfaces/libpq/libpq-int.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,8 @@ struct pg_conn
389389
char *dbName; /* database name */
390390
char *replication; /* connect as the replication standby? */
391391
char *pgservice; /* Postgres service, if any */
392+
char *pgservicefile; /* path to a service file containing
393+
* service(s) */
392394
char *pguser; /* Postgres username and password, if any */
393395
char *pgpass;
394396
char *pgpassfile; /* path to a file containing password(s) */

src/interfaces/libpq/t/006_service.pl

Lines changed: 77 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,13 @@
5353
or die "Could not copy $srvfile_valid to $srvfile_nested: $!";
5454
append_to_file($srvfile_nested, 'service=invalid_srv' . $newline);
5555

56+
# Service file with nested "servicefile" defined.
57+
my $srvfile_nested_2 = "$td/pg_service_nested_2.conf";
58+
copy($srvfile_valid, $srvfile_nested_2)
59+
or die "Could not copy $srvfile_valid to $srvfile_nested_2: $!";
60+
append_to_file($srvfile_nested_2,
61+
'servicefile=' . $srvfile_default . $newline);
62+
5663
# Set the fallback directory lookup of the service file to the temporary
5764
# directory of this test. PGSYSCONFDIR is used if the service file
5865
# defined in PGSERVICEFILE cannot be found, or when a service file is
@@ -158,9 +165,77 @@
158165

159166
$dummy_node->connect_fails(
160167
'service=my_srv',
161-
'connection with nested service file',
168+
'connection with "service" in nested service file',
169+
expected_stderr =>
170+
qr/nested "service" specifications not supported in service file/);
171+
172+
local $ENV{PGSERVICEFILE} = $srvfile_nested_2;
173+
174+
$dummy_node->connect_fails(
175+
'service=my_srv',
176+
'connection with "servicefile" in nested service file',
162177
expected_stderr =>
163-
qr/nested service specifications not supported in service file/);
178+
qr/nested "servicefile" specifications not supported in service file/
179+
);
180+
}
181+
182+
# Properly escape backslashes in the path, to ensure the generation of
183+
# correct connection strings.
184+
my $srvfile_win_cared = $srvfile_valid;
185+
$srvfile_win_cared =~ s/\\/\\\\/g;
186+
187+
# Checks that the "servicefile" option works as expected
188+
{
189+
$dummy_node->connect_ok(
190+
q{service=my_srv servicefile='} . $srvfile_win_cared . q{'},
191+
'connection with valid servicefile in connection string',
192+
sql => "SELECT 'connect3_1'",
193+
expected_stdout => qr/connect3_1/);
194+
195+
# Encode slashes and backslash
196+
my $encoded_srvfile = $srvfile_valid =~ s{([\\/])}{
197+
$1 eq '/' ? '%2F' : '%5C'
198+
}ger;
199+
200+
# Additionally encode a colon in servicefile path of Windows
201+
$encoded_srvfile =~ s/:/%3A/g;
202+
203+
$dummy_node->connect_ok(
204+
'postgresql:///?service=my_srv&servicefile=' . $encoded_srvfile,
205+
'connection with valid servicefile in URI',
206+
sql => "SELECT 'connect3_2'",
207+
expected_stdout => qr/connect3_2/);
208+
209+
local $ENV{PGSERVICE} = 'my_srv';
210+
$dummy_node->connect_ok(
211+
q{servicefile='} . $srvfile_win_cared . q{'},
212+
'connection with PGSERVICE and servicefile in connection string',
213+
sql => "SELECT 'connect3_3'",
214+
expected_stdout => qr/connect3_3/);
215+
216+
$dummy_node->connect_ok(
217+
'postgresql://?servicefile=' . $encoded_srvfile,
218+
'connection with PGSERVICE and servicefile in URI',
219+
sql => "SELECT 'connect3_4'",
220+
expected_stdout => qr/connect3_4/);
221+
}
222+
223+
# Check that the "servicefile" option takes priority over the PGSERVICEFILE
224+
# environment variable.
225+
{
226+
local $ENV{PGSERVICEFILE} = 'non-existent-file.conf';
227+
228+
$dummy_node->connect_fails(
229+
'service=my_srv',
230+
'connection with invalid PGSERVICEFILE',
231+
expected_stderr =>
232+
qr/service file "non-existent-file\.conf" not found/);
233+
234+
$dummy_node->connect_ok(
235+
q{service=my_srv servicefile='} . $srvfile_win_cared . q{'},
236+
'connection with both servicefile and PGSERVICEFILE',
237+
sql => "SELECT 'connect4_1'",
238+
expected_stdout => qr/connect4_1/);
164239
}
165240

166241
$node->teardown_node;

0 commit comments

Comments
 (0)