Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 6e0cb3d

Browse files
committed
postgres_fdw: Allow postgres_fdw.application_name to include escape sequences.
application_name that used when postgres_fdw establishes a connection to a foreign server can be specified in either or both a connection parameter of a server object and GUC postgres_fdw.application_name. This commit allows those parameters to include escape sequences that begins with % character. Then postgres_fdw replaces those escape sequences with status information. For example, %d and %u are replaced with user name and database name in local server, respectively. This feature enables us to add information more easily to track remote transactions or queries, into application_name of a remote connection. Author: Hayato Kuroda Reviewed-by: Kyotaro Horiguchi, Masahiro Ikeda, Hou Zhijie, Fujii Masao Discussion: https://postgr.es/m/TYAPR01MB5866FAE71C66547C64616584F5EB9@TYAPR01MB5866.jpnprd01.prod.outlook.com Discussion: https://postgr.es/m/TYCPR01MB5870D1E8B949DAF6D3B84E02F5F29@TYCPR01MB5870.jpnprd01.prod.outlook.com
1 parent 94226d4 commit 6e0cb3d

File tree

6 files changed

+218
-1
lines changed

6 files changed

+218
-1
lines changed

contrib/postgres_fdw/connection.c

+36
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,7 @@ connect_pg_server(ForeignServer *server, UserMapping *user)
348348
{
349349
const char **keywords;
350350
const char **values;
351+
char *appname = NULL;
351352
int n;
352353

353354
/*
@@ -383,6 +384,39 @@ connect_pg_server(ForeignServer *server, UserMapping *user)
383384
n++;
384385
}
385386

387+
/*
388+
* Search the parameter arrays to find application_name setting, and
389+
* replace escape sequences in it with status information if found.
390+
* The arrays are searched backwards because the last value is used if
391+
* application_name is repeatedly set.
392+
*/
393+
for (int i = n - 1; i >= 0; i--)
394+
{
395+
if (strcmp(keywords[i], "application_name") == 0 &&
396+
*(values[i]) != '\0')
397+
{
398+
/*
399+
* Use this application_name setting if it's not empty string
400+
* even after any escape sequences in it are replaced.
401+
*/
402+
appname = process_pgfdw_appname(values[i]);
403+
if (appname[0] != '\0')
404+
{
405+
values[i] = appname;
406+
break;
407+
}
408+
409+
/*
410+
* This empty application_name is not used, so we set
411+
* values[i] to NULL and keep searching the array to find the
412+
* next one.
413+
*/
414+
values[i] = NULL;
415+
pfree(appname);
416+
appname = NULL;
417+
}
418+
}
419+
386420
/* Use "postgres_fdw" as fallback_application_name */
387421
keywords[n] = "fallback_application_name";
388422
values[n] = "postgres_fdw";
@@ -452,6 +486,8 @@ connect_pg_server(ForeignServer *server, UserMapping *user)
452486
/* Prepare new session for use */
453487
configure_remote_session(conn);
454488

489+
if (appname != NULL)
490+
pfree(appname);
455491
pfree(keywords);
456492
pfree(values);
457493
}

contrib/postgres_fdw/expected/postgres_fdw.out

+32
Original file line numberDiff line numberDiff line change
@@ -10825,3 +10825,35 @@ ERROR: invalid value for integer option "batch_size": 100$%$#$#
1082510825
ALTER FOREIGN DATA WRAPPER postgres_fdw OPTIONS (nonexistent 'fdw');
1082610826
ERROR: invalid option "nonexistent"
1082710827
HINT: There are no valid options in this context.
10828+
-- ===================================================================
10829+
-- test postgres_fdw.application_name GUC
10830+
-- ===================================================================
10831+
-- Close all the existing cached connections so that new connection
10832+
-- will be established with new setting of postgres_fdw.application_name.
10833+
SELECT 1 FROM postgres_fdw_disconnect_all();
10834+
?column?
10835+
----------
10836+
1
10837+
(1 row)
10838+
10839+
-- Add some escape sequences into postgres_fdw.application_name
10840+
-- so as to test that they are replaced with status information expectedly.
10841+
SET postgres_fdw.application_name TO '%a%u%d%p%%';
10842+
BEGIN;
10843+
SELECT 1 FROM ft6 LIMIT 1;
10844+
?column?
10845+
----------
10846+
1
10847+
(1 row)
10848+
10849+
SELECT count(*) FROM pg_stat_activity
10850+
WHERE application_name = current_setting('application_name') ||
10851+
CURRENT_USER || current_database() || pg_backend_pid() || '%';
10852+
count
10853+
-------
10854+
1
10855+
(1 row)
10856+
10857+
COMMIT;
10858+
--Clean up
10859+
RESET postgres_fdw.application_name;

contrib/postgres_fdw/option.c

+62
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "catalog/pg_user_mapping.h"
1919
#include "commands/defrem.h"
2020
#include "commands/extension.h"
21+
#include "libpq/libpq-be.h"
2122
#include "postgres_fdw.h"
2223
#include "utils/builtins.h"
2324
#include "utils/guc.h"
@@ -445,6 +446,67 @@ ExtractExtensionList(const char *extensionsString, bool warnOnMissing)
445446
return extensionOids;
446447
}
447448

449+
/*
450+
* Replace escape sequences beginning with % character in the given
451+
* application_name with status information, and return it.
452+
*
453+
* This function always returns a palloc'd string, so the caller is
454+
* responsible for pfreeing it.
455+
*/
456+
char *
457+
process_pgfdw_appname(const char *appname)
458+
{
459+
const char *p;
460+
StringInfoData buf;
461+
462+
Assert(MyProcPort != NULL);
463+
464+
initStringInfo(&buf);
465+
466+
for (p = appname; *p != '\0'; p++)
467+
{
468+
if (*p != '%')
469+
{
470+
/* literal char, just copy */
471+
appendStringInfoChar(&buf, *p);
472+
continue;
473+
}
474+
475+
/* must be a '%', so skip to the next char */
476+
p++;
477+
if (*p == '\0')
478+
break; /* format error - ignore it */
479+
else if (*p == '%')
480+
{
481+
/* string contains %% */
482+
appendStringInfoChar(&buf, '%');
483+
continue;
484+
}
485+
486+
/* process the option */
487+
switch (*p)
488+
{
489+
case 'a':
490+
appendStringInfoString(&buf, application_name);
491+
break;
492+
case 'd':
493+
appendStringInfoString(&buf, MyProcPort->database_name);
494+
break;
495+
case 'p':
496+
appendStringInfo(&buf, "%d", MyProcPid);
497+
break;
498+
case 'u':
499+
appendStringInfoString(&buf, MyProcPort->user_name);
500+
break;
501+
default:
502+
/* format error - ignore it */
503+
break;
504+
}
505+
}
506+
507+
return buf.data;
508+
}
509+
448510
/*
449511
* Module load callback
450512
*/

contrib/postgres_fdw/postgres_fdw.h

+1
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,7 @@ extern int ExtractConnectionOptions(List *defelems,
158158
const char **values);
159159
extern List *ExtractExtensionList(const char *extensionsString,
160160
bool warnOnMissing);
161+
extern char *process_pgfdw_appname(const char *appname);
161162
extern char *pgfdw_application_name;
162163

163164
/* in deparse.c */

contrib/postgres_fdw/sql/postgres_fdw.sql

+21
Original file line numberDiff line numberDiff line change
@@ -3452,3 +3452,24 @@ CREATE FOREIGN TABLE inv_bsz (c1 int )
34523452

34533453
-- No option is allowed to be specified at foreign data wrapper level
34543454
ALTER FOREIGN DATA WRAPPER postgres_fdw OPTIONS (nonexistent 'fdw');
3455+
3456+
-- ===================================================================
3457+
-- test postgres_fdw.application_name GUC
3458+
-- ===================================================================
3459+
-- Close all the existing cached connections so that new connection
3460+
-- will be established with new setting of postgres_fdw.application_name.
3461+
SELECT 1 FROM postgres_fdw_disconnect_all();
3462+
3463+
-- Add some escape sequences into postgres_fdw.application_name
3464+
-- so as to test that they are replaced with status information expectedly.
3465+
SET postgres_fdw.application_name TO '%a%u%d%p%%';
3466+
3467+
BEGIN;
3468+
SELECT 1 FROM ft6 LIMIT 1;
3469+
SELECT count(*) FROM pg_stat_activity
3470+
WHERE application_name = current_setting('application_name') ||
3471+
CURRENT_USER || current_database() || pg_backend_pid() || '%';
3472+
COMMIT;
3473+
3474+
--Clean up
3475+
RESET postgres_fdw.application_name;

doc/src/sgml/postgres-fdw.sgml

+66-1
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,20 @@
130130
server encoding)
131131
</para>
132132
</listitem>
133+
<listitem>
134+
<para>
135+
<literal>application_name</literal> - this may appear in
136+
<emphasis>either or both</emphasis> a connection and
137+
<xref linkend="guc-pgfdw-application-name"/>.
138+
If both are present, <varname>postgres_fdw.application_name</varname>
139+
overrides the connection setting.
140+
Unlike <application>libpq</application>,
141+
<filename>postgres_fdw</filename> allows
142+
<varname>application_name</varname> to include
143+
<quote>escape sequences</quote>.
144+
See <xref linkend="guc-pgfdw-application-name"/> for details.
145+
</para>
146+
</listitem>
133147
<listitem>
134148
<para>
135149
<literal>fallback_application_name</literal> (always set to
@@ -920,7 +934,7 @@ postgres=# SELECT postgres_fdw_disconnect_all();
920934
<title>Configuration Parameters</title>
921935

922936
<variablelist>
923-
<varlistentry>
937+
<varlistentry id="guc-pgfdw-application-name" xreflabel="postgres_fdw.application_name">
924938
<term>
925939
<varname>postgres_fdw.application_name</varname> (<type>string</type>)
926940
<indexterm>
@@ -946,6 +960,57 @@ postgres=# SELECT postgres_fdw_disconnect_all();
946960
marks (<literal>?</literal>).
947961
See <xref linkend="guc-application-name"/> for details.
948962
</para>
963+
964+
<para>
965+
<literal>%</literal> characters begin <quote>escape sequences</quote>
966+
that are replaced with status information as outlined below.
967+
Unrecognized escapes are ignored. Other characters are copied straight
968+
to the application name. Note that it's not allowed to specify a
969+
plus/minus sign or a numeric literal after the <literal>%</literal>
970+
and before the option, for alignment and padding.
971+
</para>
972+
973+
<informaltable>
974+
<tgroup cols="2">
975+
<thead>
976+
<row>
977+
<entry>Escape</entry>
978+
<entry>Effect</entry>
979+
</row>
980+
</thead>
981+
<tbody>
982+
<row>
983+
<entry><literal>%a</literal></entry>
984+
<entry>Application name in local server</entry>
985+
</row>
986+
<row>
987+
<entry><literal>%u</literal></entry>
988+
<entry>User name in local server</entry>
989+
</row>
990+
<row>
991+
<entry><literal>%d</literal></entry>
992+
<entry>Database name in local server</entry>
993+
</row>
994+
<row>
995+
<entry><literal>%p</literal></entry>
996+
<entry>Process ID of backend in local server</entry>
997+
</row>
998+
<row>
999+
<entry><literal>%%</literal></entry>
1000+
<entry>Literal %</entry>
1001+
</row>
1002+
</tbody>
1003+
</tgroup>
1004+
</informaltable>
1005+
1006+
<para>
1007+
For example, suppose user <literal>local_user</literal> establishes
1008+
a connection from database <literal>local_db</literal> to
1009+
<literal>foreign_db</literal> as user <literal>foreign_user</literal>,
1010+
the setting <literal>'db=%d, user=%u'</literal> is replaced with
1011+
<literal>'db=local_db, user=local_user'</literal>.
1012+
</para>
1013+
9491014
</listitem>
9501015
</varlistentry>
9511016
</variablelist>

0 commit comments

Comments
 (0)