On 2021/12/10 16:35, kuroda.hay...@fujitsu.com wrote:
How about adding that new paragraph just after the existing one, instead?

Fixed.

Thanks for the fix! Attached is the updated version of 0001 patch.
I added "See <xref linkend="guc-application-name"/> for details."
into the description. Barring any objection, I will commit
this patch at first.

Thanks! But, since the term "local server" is already used in the docs, we can 
use
"the setting value of application_name in local server" etc?

I like the word "local server," so I reworte descriptions.

Thanks! Attached is the updated version of 0002 patch. I applied
some cosmetic changes, improved comments and docs. Could you review
this version?

Regards,

--
Fujii Masao
Advanced Computing Technology Center
Research and Development Headquarters
NTT DATA CORPORATION
diff --git a/doc/src/sgml/postgres-fdw.sgml b/doc/src/sgml/postgres-fdw.sgml
index 41c952fbe3..f4210c878f 100644
--- a/doc/src/sgml/postgres-fdw.sgml
+++ b/doc/src/sgml/postgres-fdw.sgml
@@ -936,6 +936,16 @@ postgres=# SELECT postgres_fdw_disconnect_all();
       Note that change of this parameter doesn't affect any existing
       connections until they are re-established.
      </para>
+     <para>
+     <varname>postgres_fdw.application_name</varname> can be any string
+     of any length and contain even non-ASCII characters.  However when
+     it's passed to and used as <varname>application_name</varname>
+     in a foreign server, note that it will be truncated to less than
+     <symbol>NAMEDATALEN</symbol> characters and any characters other
+     than printable ASCII ones in it will be replaced with question
+     marks (<literal>?</literal>).
+     See <xref linkend="guc-application-name"/> for details.
+     </para>
     </listitem>
    </varlistentry>
   </variablelist>
diff --git a/contrib/postgres_fdw/connection.c 
b/contrib/postgres_fdw/connection.c
index 6bac4ad23e..664487b013 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] != NULL && *(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 5196e4797a..ca6ae6749b 100644
--- a/contrib/postgres_fdw/expected/postgres_fdw.out
+++ b/contrib/postgres_fdw/expected/postgres_fdw.out
@@ -10822,3 +10822,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 48c7417e6e..a9416598d8 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"
@@ -445,6 +446,67 @@ ExtractExtensionList(const char *extensionsString, bool 
warnOnMissing)
        return extensionOids;
 }
 
+/*
+ * 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
  */
diff --git a/contrib/postgres_fdw/postgres_fdw.h 
b/contrib/postgres_fdw/postgres_fdw.h
index 90b72e9ec5..fa2af8bb8a 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 666d21962a..a67e156688 100644
--- a/contrib/postgres_fdw/sql/postgres_fdw.sql
+++ b/contrib/postgres_fdw/sql/postgres_fdw.sql
@@ -3449,3 +3449,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 f4210c878f..461a5e4a0b 100644
--- a/doc/src/sgml/postgres-fdw.sgml
+++ b/doc/src/sgml/postgres-fdw.sgml
@@ -130,6 +130,20 @@
        server encoding)
       </para>
      </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
@@ -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>

Reply via email to