aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFujii Masao <fujii@postgresql.org>2021-12-24 16:55:11 +0900
committerFujii Masao <fujii@postgresql.org>2021-12-24 16:55:11 +0900
commit6e0cb3dec10e460288d68a128e3d79d16a230cdb (patch)
tree5d106381fe2fa91fb9b0dbf3a81061c7d9356764
parent94226d4506e66d6e7cbf4b391f1e7393c1962841 (diff)
downloadpostgresql-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.c36
-rw-r--r--contrib/postgres_fdw/expected/postgres_fdw.out32
-rw-r--r--contrib/postgres_fdw/option.c62
-rw-r--r--contrib/postgres_fdw/postgres_fdw.h1
-rw-r--r--contrib/postgres_fdw/sql/postgres_fdw.sql21
-rw-r--r--doc/src/sgml/postgres-fdw.sgml67
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>