diff options
author | Fujii Masao <fujii@postgresql.org> | 2021-12-24 16:55:11 +0900 |
---|---|---|
committer | Fujii Masao <fujii@postgresql.org> | 2021-12-24 16:55:11 +0900 |
commit | 6e0cb3dec10e460288d68a128e3d79d16a230cdb (patch) | |
tree | 5d106381fe2fa91fb9b0dbf3a81061c7d9356764 | |
parent | 94226d4506e66d6e7cbf4b391f1e7393c1962841 (diff) | |
download | postgresql-6e0cb3dec10e460288d68a128e3d79d16a230cdb.tar.gz postgresql-6e0cb3dec10e460288d68a128e3d79d16a230cdb.zip |
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
-rw-r--r-- | contrib/postgres_fdw/connection.c | 36 | ||||
-rw-r--r-- | contrib/postgres_fdw/expected/postgres_fdw.out | 32 | ||||
-rw-r--r-- | contrib/postgres_fdw/option.c | 62 | ||||
-rw-r--r-- | contrib/postgres_fdw/postgres_fdw.h | 1 | ||||
-rw-r--r-- | contrib/postgres_fdw/sql/postgres_fdw.sql | 21 | ||||
-rw-r--r-- | doc/src/sgml/postgres-fdw.sgml | 67 |
6 files changed, 218 insertions, 1 deletions
diff --git a/contrib/postgres_fdw/connection.c b/contrib/postgres_fdw/connection.c index 6bac4ad23eb..80db19e401c 100644 --- a/contrib/postgres_fdw/connection.c +++ b/contrib/postgres_fdw/connection.c @@ -348,6 +348,7 @@ connect_pg_server(ForeignServer *server, UserMapping *user) { const char **keywords; const char **values; + char *appname = NULL; int n; /* @@ -383,6 +384,39 @@ connect_pg_server(ForeignServer *server, UserMapping *user) n++; } + /* + * Search the parameter arrays to find application_name setting, and + * replace escape sequences in it with status information if found. + * The arrays are searched backwards because the last value is used if + * application_name is repeatedly set. + */ + for (int i = n - 1; i >= 0; i--) + { + if (strcmp(keywords[i], "application_name") == 0 && + *(values[i]) != '\0') + { + /* + * Use this application_name setting if it's not empty string + * even after any escape sequences in it are replaced. + */ + appname = process_pgfdw_appname(values[i]); + if (appname[0] != '\0') + { + values[i] = appname; + break; + } + + /* + * This empty application_name is not used, so we set + * values[i] to NULL and keep searching the array to find the + * next one. + */ + values[i] = NULL; + pfree(appname); + appname = NULL; + } + } + /* Use "postgres_fdw" as fallback_application_name */ keywords[n] = "fallback_application_name"; values[n] = "postgres_fdw"; @@ -452,6 +486,8 @@ connect_pg_server(ForeignServer *server, UserMapping *user) /* Prepare new session for use */ configure_remote_session(conn); + if (appname != NULL) + pfree(appname); pfree(keywords); pfree(values); } diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.out index 7720ab9c581..6ce7ec24f08 100644 --- a/contrib/postgres_fdw/expected/postgres_fdw.out +++ b/contrib/postgres_fdw/expected/postgres_fdw.out @@ -10825,3 +10825,35 @@ ERROR: invalid value for integer option "batch_size": 100$%$#$# ALTER FOREIGN DATA WRAPPER postgres_fdw OPTIONS (nonexistent 'fdw'); ERROR: invalid option "nonexistent" HINT: There are no valid options in this context. +-- =================================================================== +-- test postgres_fdw.application_name GUC +-- =================================================================== +-- Close all the existing cached connections so that new connection +-- will be established with new setting of postgres_fdw.application_name. +SELECT 1 FROM postgres_fdw_disconnect_all(); + ?column? +---------- + 1 +(1 row) + +-- Add some escape sequences into postgres_fdw.application_name +-- so as to test that they are replaced with status information expectedly. +SET postgres_fdw.application_name TO '%a%u%d%p%%'; +BEGIN; +SELECT 1 FROM ft6 LIMIT 1; + ?column? +---------- + 1 +(1 row) + +SELECT count(*) FROM pg_stat_activity + WHERE application_name = current_setting('application_name') || + CURRENT_USER || current_database() || pg_backend_pid() || '%'; + count +------- + 1 +(1 row) + +COMMIT; +--Clean up +RESET postgres_fdw.application_name; diff --git a/contrib/postgres_fdw/option.c b/contrib/postgres_fdw/option.c index 36555398ecb..c2c4e36802c 100644 --- a/contrib/postgres_fdw/option.c +++ b/contrib/postgres_fdw/option.c @@ -18,6 +18,7 @@ #include "catalog/pg_user_mapping.h" #include "commands/defrem.h" #include "commands/extension.h" +#include "libpq/libpq-be.h" #include "postgres_fdw.h" #include "utils/builtins.h" #include "utils/guc.h" @@ -446,6 +447,67 @@ ExtractExtensionList(const char *extensionsString, bool warnOnMissing) } /* + * Replace escape sequences beginning with % character in the given + * application_name with status information, and return it. + * + * This function always returns a palloc'd string, so the caller is + * responsible for pfreeing it. + */ +char * +process_pgfdw_appname(const char *appname) +{ + const char *p; + StringInfoData buf; + + Assert(MyProcPort != NULL); + + initStringInfo(&buf); + + for (p = appname; *p != '\0'; p++) + { + if (*p != '%') + { + /* literal char, just copy */ + appendStringInfoChar(&buf, *p); + continue; + } + + /* must be a '%', so skip to the next char */ + p++; + if (*p == '\0') + break; /* format error - ignore it */ + else if (*p == '%') + { + /* string contains %% */ + appendStringInfoChar(&buf, '%'); + continue; + } + + /* process the option */ + switch (*p) + { + case 'a': + appendStringInfoString(&buf, application_name); + break; + case 'd': + appendStringInfoString(&buf, MyProcPort->database_name); + break; + case 'p': + appendStringInfo(&buf, "%d", MyProcPid); + break; + case 'u': + appendStringInfoString(&buf, MyProcPort->user_name); + break; + default: + /* format error - ignore it */ + break; + } + } + + return buf.data; +} + +/* * Module load callback */ void diff --git a/contrib/postgres_fdw/postgres_fdw.h b/contrib/postgres_fdw/postgres_fdw.h index 90b72e9ec55..fa2af8bb8ad 100644 --- a/contrib/postgres_fdw/postgres_fdw.h +++ b/contrib/postgres_fdw/postgres_fdw.h @@ -158,6 +158,7 @@ extern int ExtractConnectionOptions(List *defelems, const char **values); extern List *ExtractExtensionList(const char *extensionsString, bool warnOnMissing); +extern char *process_pgfdw_appname(const char *appname); extern char *pgfdw_application_name; /* in deparse.c */ diff --git a/contrib/postgres_fdw/sql/postgres_fdw.sql b/contrib/postgres_fdw/sql/postgres_fdw.sql index beeac8af1ed..105b133bbd1 100644 --- a/contrib/postgres_fdw/sql/postgres_fdw.sql +++ b/contrib/postgres_fdw/sql/postgres_fdw.sql @@ -3452,3 +3452,24 @@ CREATE FOREIGN TABLE inv_bsz (c1 int ) -- No option is allowed to be specified at foreign data wrapper level ALTER FOREIGN DATA WRAPPER postgres_fdw OPTIONS (nonexistent 'fdw'); + +-- =================================================================== +-- test postgres_fdw.application_name GUC +-- =================================================================== +-- Close all the existing cached connections so that new connection +-- will be established with new setting of postgres_fdw.application_name. +SELECT 1 FROM postgres_fdw_disconnect_all(); + +-- Add some escape sequences into postgres_fdw.application_name +-- so as to test that they are replaced with status information expectedly. +SET postgres_fdw.application_name TO '%a%u%d%p%%'; + +BEGIN; +SELECT 1 FROM ft6 LIMIT 1; +SELECT count(*) FROM pg_stat_activity + WHERE application_name = current_setting('application_name') || + CURRENT_USER || current_database() || pg_backend_pid() || '%'; +COMMIT; + +--Clean up +RESET postgres_fdw.application_name; diff --git a/doc/src/sgml/postgres-fdw.sgml b/doc/src/sgml/postgres-fdw.sgml index 0c55e0f0fd5..41cdb9ea1b5 100644 --- a/doc/src/sgml/postgres-fdw.sgml +++ b/doc/src/sgml/postgres-fdw.sgml @@ -132,6 +132,20 @@ </listitem> <listitem> <para> + <literal>application_name</literal> - this may appear in + <emphasis>either or both</emphasis> a connection and + <xref linkend="guc-pgfdw-application-name"/>. + If both are present, <varname>postgres_fdw.application_name</varname> + overrides the connection setting. + Unlike <application>libpq</application>, + <filename>postgres_fdw</filename> allows + <varname>application_name</varname> to include + <quote>escape sequences</quote>. + See <xref linkend="guc-pgfdw-application-name"/> for details. + </para> + </listitem> + <listitem> + <para> <literal>fallback_application_name</literal> (always set to <literal>postgres_fdw</literal>) </para> @@ -920,7 +934,7 @@ postgres=# SELECT postgres_fdw_disconnect_all(); <title>Configuration Parameters</title> <variablelist> - <varlistentry> + <varlistentry id="guc-pgfdw-application-name" xreflabel="postgres_fdw.application_name"> <term> <varname>postgres_fdw.application_name</varname> (<type>string</type>) <indexterm> @@ -946,6 +960,57 @@ postgres=# SELECT postgres_fdw_disconnect_all(); marks (<literal>?</literal>). See <xref linkend="guc-application-name"/> for details. </para> + + <para> + <literal>%</literal> characters begin <quote>escape sequences</quote> + that are replaced with status information as outlined below. + Unrecognized escapes are ignored. Other characters are copied straight + to the application name. Note that it's not allowed to specify a + plus/minus sign or a numeric literal after the <literal>%</literal> + and before the option, for alignment and padding. + </para> + + <informaltable> + <tgroup cols="2"> + <thead> + <row> + <entry>Escape</entry> + <entry>Effect</entry> + </row> + </thead> + <tbody> + <row> + <entry><literal>%a</literal></entry> + <entry>Application name in local server</entry> + </row> + <row> + <entry><literal>%u</literal></entry> + <entry>User name in local server</entry> + </row> + <row> + <entry><literal>%d</literal></entry> + <entry>Database name in local server</entry> + </row> + <row> + <entry><literal>%p</literal></entry> + <entry>Process ID of backend in local server</entry> + </row> + <row> + <entry><literal>%%</literal></entry> + <entry>Literal %</entry> + </row> + </tbody> + </tgroup> + </informaltable> + + <para> + For example, suppose user <literal>local_user</literal> establishes + a connection from database <literal>local_db</literal> to + <literal>foreign_db</literal> as user <literal>foreign_user</literal>, + the setting <literal>'db=%d, user=%u'</literal> is replaced with + <literal>'db=local_db, user=local_user'</literal>. + </para> + </listitem> </varlistentry> </variablelist> |