commit 3dbcec16979d57d433c5ae7fdf11c27cec609afd
Author: kuroda.hayato%40jp.fujitsu.com <kuroda.hayato@jp.fujitsu.com>
Date:   Wed Sep 1 09:48:30 2021 +0000

    Allow to specify special escape characters

diff --git a/contrib/postgres_fdw/connection.c b/contrib/postgres_fdw/connection.c
index af9ae8bc7d..3c0fa4413f 100644
--- a/contrib/postgres_fdw/connection.c
+++ b/contrib/postgres_fdw/connection.c
@@ -349,6 +349,8 @@ connect_pg_server(ForeignServer *server, UserMapping *user)
 		const char **keywords;
 		const char **values;
 		int			n;
+		int			i;
+		StringInfoData buf;
 
 		/*
 		 * Construct connection params from generic options of ForeignServer
@@ -360,6 +362,7 @@ connect_pg_server(ForeignServer *server, UserMapping *user)
 		n = list_length(server->options) + list_length(user->options) + 4;
 		keywords = (const char **) palloc(n * sizeof(char *));
 		values = (const char **) palloc(n * sizeof(char *));
+		initStringInfo(&buf);
 
 		n = 0;
 		n += ExtractConnectionOptions(server->options,
@@ -374,6 +377,17 @@ connect_pg_server(ForeignServer *server, UserMapping *user)
 			values[n] = pgfdw_application_name;
 			n++;
 		}
+		/* Search application_name and replace it */
+		for (i = n - 1; i >= 0; i--)
+		{
+			if (strcmp(keywords[i], "application_name") == 0)
+			{
+				parse_application_name(&buf, values[i]);
+				values[i] = buf.data;
+				break;
+			}
+		}
+
 		/* Use "postgres_fdw" as fallback_application_name */
 		keywords[n] = "fallback_application_name";
 		values[n] = "postgres_fdw";
@@ -445,6 +459,7 @@ connect_pg_server(ForeignServer *server, UserMapping *user)
 
 		pfree(keywords);
 		pfree(values);
+		pfree(buf.data);
 	}
 	PG_CATCH();
 	{
diff --git a/contrib/postgres_fdw/option.c b/contrib/postgres_fdw/option.c
index da693cb74c..efd7053d01 100644
--- a/contrib/postgres_fdw/option.c
+++ b/contrib/postgres_fdw/option.c
@@ -19,6 +19,8 @@
 #include "commands/defrem.h"
 #include "commands/extension.h"
 #include "common/string.h"
+#include "lib/stringinfo.h"
+#include "libpq/libpq-be.h"
 #include "postgres_fdw.h"
 #include "utils/builtins.h"
 #include "utils/guc.h"
@@ -449,6 +451,91 @@ ExtractExtensionList(const char *extensionsString, bool warnOnMissing)
 	return extensionOids;
 }
 
+/*
+ * parse application_name and set escaped string.
+ * This function is almost same as log_line_prefix(), but
+ * accepted escape sequence is different.
+ *
+ * buf must be initialized.
+ */
+void
+parse_application_name(StringInfo buf, const char *name)
+{
+	int			padding;
+	const char *p;
+
+	for(p = name; *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;
+		}
+		if (*p > '9')
+			padding = 0;
+		else if ((p = process_padding(p, &padding)) == NULL)
+			break;
+
+		/* process the option */
+		switch (*p)
+		{
+			case 'a':
+				{
+					const char *appname = application_name;
+
+					if (*appname == '\0')
+						appname = "[unknown]";
+					if (padding != 0)
+						appendStringInfo(buf, "%*s", padding, appname);
+					else
+						appendStringInfoString(buf, appname);
+				}
+				break;
+			case 'u':
+				{
+					const char *username = MyProcPort->user_name;
+
+					if (padding != 0)
+						appendStringInfo(buf, "%*s", padding, username);
+					else
+						appendStringInfoString(buf, username);
+				}
+				break;
+			case 'd':
+				{
+					const char *dbname = MyProcPort->database_name;
+
+					if (padding != 0)
+						appendStringInfo(buf, "%*s", padding, dbname);
+					else
+						appendStringInfoString(buf, dbname);
+				}
+				break;
+			case 'p':
+				if (padding != 0)
+					appendStringInfo(buf, "%*d", padding, MyProcPid);
+				else
+					appendStringInfo(buf, "%d", MyProcPid);
+				break;
+			default:
+				/* format error - ignore it */
+				break;
+		}
+	}
+}
+
 /*
  * Completely same as server-side.
  */
diff --git a/contrib/postgres_fdw/postgres_fdw.h b/contrib/postgres_fdw/postgres_fdw.h
index 90b72e9ec5..1168ad4b52 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 void parse_application_name(StringInfo buf, const char *name);
 extern char *pgfdw_application_name;
 
 /* in deparse.c */
diff --git a/doc/src/sgml/postgres-fdw.sgml b/doc/src/sgml/postgres-fdw.sgml
index caa10519e0..437aed647a 100644
--- a/doc/src/sgml/postgres-fdw.sgml
+++ b/doc/src/sgml/postgres-fdw.sgml
@@ -123,6 +123,43 @@
       configuration parameter. This value is used only when a backend process
       starts to establish the remote connection. 
      </para>
+
+     <para>
+      Same as <xref linkend="guc-log-line-prefix"/>, this is a
+      <function>printf</function>-style string. Accepted escapes are
+      bit different from <xref linkend="guc-log-line-prefix"/>,
+      but padding can be used like as it.
+     </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</entry>
+        </row>
+        <row>
+         <entry><literal>%u</literal></entry>
+         <entry>Local user name</entry>
+        </row>
+        <row>
+         <entry><literal>%d</literal></entry>
+         <entry>Local database name</entry>
+        </row>
+        <row>
+         <entry><literal>%p</literal></entry>
+         <entry>Local backend process ID</entry>
+        </row>
+       </tbody>
+      </tgroup>
+     </informaltable>
+
     </listitem>
    </varlistentry>
   </variablelist>
diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c
index a3e1c59a82..d4b3350912 100644
--- a/src/backend/utils/error/elog.c
+++ b/src/backend/utils/error/elog.c
@@ -82,6 +82,7 @@
 #include "utils/guc.h"
 #include "utils/memutils.h"
 #include "utils/ps_status.h"
+#include "common/string.h"
 
 
 /* In this module, access gettext() via err_gettext() */
@@ -177,7 +178,6 @@ static void set_errdata_field(MemoryContextData *cxt, char **ptr, const char *st
 static void write_console(const char *line, int len);
 static void setup_formatted_log_time(void);
 static void setup_formatted_start_time(void);
-static const char *process_log_prefix_padding(const char *p, int *padding);
 static void log_line_prefix(StringInfo buf, ErrorData *edata);
 static void write_csvlog(ErrorData *edata);
 static void send_message_to_server_log(ErrorData *edata);
@@ -2338,41 +2338,6 @@ setup_formatted_start_time(void)
 				pg_localtime(&stamp_time, log_timezone));
 }
 
-/*
- * process_log_prefix_padding --- helper function for processing the format
- * string in log_line_prefix
- *
- * Note: This function returns NULL if it finds something which
- * it deems invalid in the format string.
- */
-static const char *
-process_log_prefix_padding(const char *p, int *ppadding)
-{
-	int			paddingsign = 1;
-	int			padding = 0;
-
-	if (*p == '-')
-	{
-		p++;
-
-		if (*p == '\0')			/* Did the buf end in %- ? */
-			return NULL;
-		paddingsign = -1;
-	}
-
-	/* generate an int version of the numerical string */
-	while (*p >= '0' && *p <= '9')
-		padding = padding * 10 + (*p++ - '0');
-
-	/* format is invalid if it ends with the padding number */
-	if (*p == '\0')
-		return NULL;
-
-	padding *= paddingsign;
-	*ppadding = padding;
-	return p;
-}
-
 /*
  * Format tag info for log lines; append to the provided buffer.
  */
@@ -2427,7 +2392,7 @@ log_line_prefix(StringInfo buf, ErrorData *edata)
 
 		/*
 		 * Process any formatting which may exist after the '%'.  Note that
-		 * process_log_prefix_padding moves p past the padding number if it
+		 * process_padding moves p past the padding number if it
 		 * exists.
 		 *
 		 * Note: Since only '-', '0' to '9' are valid formatting characters we
@@ -2441,7 +2406,7 @@ log_line_prefix(StringInfo buf, ErrorData *edata)
 		 */
 		if (*p > '9')
 			padding = 0;
-		else if ((p = process_log_prefix_padding(p, &padding)) == NULL)
+		else if ((p = process_padding(p, &padding)) == NULL)
 			break;
 
 		/* process the option */
diff --git a/src/common/string.c b/src/common/string.c
index 3aa378c051..ea5d4e87bc 100644
--- a/src/common/string.c
+++ b/src/common/string.c
@@ -107,6 +107,40 @@ pg_is_ascii(const char *str)
 	return true;
 }
 
+/*
+ * process_padding --- helper function for processing the format
+ * string
+ *
+ * Note: This function returns NULL if it finds something which
+ * it deems invalid in the format string.
+ */
+const char *
+process_padding(const char *p, int *ppadding)
+{
+	int			paddingsign = 1;
+	int			padding = 0;
+
+	if (*p == '-')
+	{
+		p++;
+
+		if (*p == '\0')			/* Did the buf end in %- ? */
+			return NULL;
+		paddingsign = -1;
+	}
+
+	/* generate an int version of the numerical string */
+	while (*p >= '0' && *p <= '9')
+		padding = padding * 10 + (*p++ - '0');
+
+	/* format is invalid if it ends with the padding number */
+	if (*p == '\0')
+		return NULL;
+
+	padding *= paddingsign;
+	*ppadding = padding;
+	return p;
+}
 
 /*
  * pg_strip_crlf -- Remove any trailing newline and carriage return
diff --git a/src/include/common/string.h b/src/include/common/string.h
index 686c158efe..1f14e3124f 100644
--- a/src/include/common/string.h
+++ b/src/include/common/string.h
@@ -19,6 +19,7 @@ extern int	strtoint(const char *pg_restrict str, char **pg_restrict endptr,
 extern void pg_clean_ascii(char *str);
 extern int	pg_strip_crlf(char *str);
 extern bool pg_is_ascii(const char *str);
+extern const char* process_padding(const char *p, int *ppadding);
 
 /* functions in src/common/pg_get_line.c */
 extern char *pg_get_line(FILE *stream);
