diff --git a/contrib/postgres_fdw/connection.c b/contrib/postgres_fdw/connection.c
index 705f60a3ae..bd0266313c 100644
--- a/contrib/postgres_fdw/connection.c
+++ b/contrib/postgres_fdw/connection.c
@@ -17,6 +17,7 @@
#include "catalog/pg_user_mapping.h"
#include "commands/defrem.h"
#include "funcapi.h"
+#include "lib/stringinfo.h"
#include "mb/pg_wchar.h"
#include "miscadmin.h"
#include "pgstat.h"
@@ -349,6 +350,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 +363,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,
@@ -383,6 +387,29 @@ connect_pg_server(ForeignServer *server, UserMapping *user)
n++;
}
+ /*
+ * Search application_name and replace it if found.
+ *
+ * We search paramters from the end because the later
+ * one have higher priority. See also the above comment.
+ */
+ for (i = n - 1; i >= 0; i--)
+ {
+ if (strcmp(keywords[i], "application_name") == 0)
+ {
+ parse_pgfdw_appname(&buf, values[i]);
+ values[i] = buf.data;
+
+ /*
+ * If the input format is wrong and the string becomes '\0',
+ * this parameter is no longer used.
+ * We conitnue searching application_name.
+ */
+ if (*values[i] != '\0')
+ break;
+ }
+ }
+
/* Use "postgres_fdw" as fallback_application_name */
keywords[n] = "fallback_application_name";
values[n] = "postgres_fdw";
@@ -454,6 +481,7 @@ connect_pg_server(ForeignServer *server, UserMapping *user)
pfree(keywords);
pfree(values);
+ pfree(buf.data);
}
PG_CATCH();
{
diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.out
index e3ee30f1aa..ba821a9c26 100644
--- a/contrib/postgres_fdw/expected/postgres_fdw.out
+++ b/contrib/postgres_fdw/expected/postgres_fdw.out
@@ -10761,3 +10761,33 @@ ERROR: invalid value for integer option "fetch_size": 100$%$#$#
CREATE FOREIGN TABLE inv_bsz (c1 int )
SERVER loopback OPTIONS (batch_size '100$%$#$#');
ERROR: invalid value for integer option "batch_size": 100$%$#$#
+-- ===================================================================
+-- test postgres_fdw.application_name GUC
+-- ===================================================================
+SET debug_discard_caches TO 0;
+-- Some escapes can be used for this GUC.
+SET postgres_fdw.application_name TO '%a%u%d%p%%';
+-- All escape candidates depend on the runtime environment
+-- and it causes some fails for this tests.
+-- Hence we just count number of rows here. It returns a row if works well.
+SELECT 1 FROM postgres_fdw_disconnect_all();
+ ?column?
+----------
+ 1
+(1 row)
+
+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)
+
+--Clean up
+RESET postgres_fdw.application_name;
+RESET debug_discard_caches;
diff --git a/contrib/postgres_fdw/option.c b/contrib/postgres_fdw/option.c
index 5bb1af4084..4fc492cfbb 100644
--- a/contrib/postgres_fdw/option.c
+++ b/contrib/postgres_fdw/option.c
@@ -18,11 +18,13 @@
#include "catalog/pg_user_mapping.h"
#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"
#include "utils/varlena.h"
-
/*
* Describes the valid options for objects that this wrapper uses.
*/
@@ -443,6 +445,112 @@ ExtractExtensionList(const char *extensionsString, bool warnOnMissing)
return extensionOids;
}
+/*
+ * parse postgres_fdw.application_name and set escaped string.
+ * This function is almost same as log_line_prefix(), but
+ * accepted escape sequence is different.
+ *
+ * Note that argument buf must be initialized.
+ */
+void
+parse_pgfdw_appname(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;
+ }
+
+ /*
+ * Process any formatting which may exist after the '%'. Note that
+ * process_padding moves p past the padding number if it
+ * exists.
+ *
+ * For other notes, see log_line_prefix().
+ */
+ if (*p > '9')
+ padding = 0;
+ else if ((p = process_padding(p, &padding)) == NULL)
+ break;
+
+ /* process the option */
+ switch (*p)
+ {
+ case 'a':
+ if (MyProcPort)
+ {
+ const char *appname = application_name;
+
+ if (appname == NULL || *appname == '\0')
+ appname = "[unknown]";
+ if (padding != 0)
+ appendStringInfo(buf, "%*s", padding, appname);
+ else
+ appendStringInfoString(buf, appname);
+ }
+ else if (padding != 0)
+ appendStringInfoSpaces(buf, abs(padding));
+ break;
+ case 'u':
+ if (MyProcPort)
+ {
+ const char *username = MyProcPort->user_name;
+
+ if (username == NULL || *username == '\0')
+ username = "[unknown]";
+ if (padding != 0)
+ appendStringInfo(buf, "%*s", padding, username);
+ else
+ appendStringInfoString(buf, username);
+ }
+ else if (padding != 0)
+ appendStringInfoSpaces(buf, abs(padding));
+ break;
+ case 'd':
+ if (MyProcPort)
+ {
+ const char *dbname = MyProcPort->database_name;
+
+ if (dbname == NULL || *dbname == '\0')
+ dbname = "[unknown]";
+ if (padding != 0)
+ appendStringInfo(buf, "%*s", padding, dbname);
+ else
+ appendStringInfoString(buf, dbname);
+ }
+ else if (padding != 0)
+ appendStringInfoSpaces(buf, abs(padding));
+ break;
+ case 'p':
+ if (padding != 0)
+ appendStringInfo(buf, "%*d", padding, MyProcPid);
+ else
+ appendStringInfo(buf, "%d", MyProcPid);
+ break;
+ default:
+ /* format error - ignore it */
+ break;
+ }
+ }
+}
+
/*
* Module load callback
*/
diff --git a/contrib/postgres_fdw/postgres_fdw.h b/contrib/postgres_fdw/postgres_fdw.h
index 90b72e9ec5..2f036391f0 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_pgfdw_appname(StringInfo buf, const char *name);
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 30b5175da5..ae49f7786a 100644
--- a/contrib/postgres_fdw/sql/postgres_fdw.sql
+++ b/contrib/postgres_fdw/sql/postgres_fdw.sql
@@ -3422,3 +3422,20 @@ CREATE FOREIGN TABLE inv_fsz (c1 int )
-- Invalid batch_size option
CREATE FOREIGN TABLE inv_bsz (c1 int )
SERVER loopback OPTIONS (batch_size '100$%$#$#');
+
+-- ===================================================================
+-- test postgres_fdw.application_name GUC
+-- ===================================================================
+SET debug_discard_caches TO 0;
+-- Some escapes can be used for this GUC.
+SET postgres_fdw.application_name TO '%a%u%d%p%%';
+-- All escape candidates depend on the runtime environment
+-- and it causes some fails for this tests.
+-- Hence we just count number of rows here. It returns a row if works well.
+SELECT 1 FROM postgres_fdw_disconnect_all();
+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() || '%';
+
+--Clean up
+RESET postgres_fdw.application_name;
+RESET debug_discard_caches;
diff --git a/doc/src/sgml/postgres-fdw.sgml b/doc/src/sgml/postgres-fdw.sgml
index bf95da9721..141f3ce5fa 100644
--- a/doc/src/sgml/postgres-fdw.sgml
+++ b/doc/src/sgml/postgres-fdw.sgml
@@ -180,6 +180,10 @@ OPTIONS (ADD password_required 'false');
relationship granted by authentication modes like peer
or ident authentication.
+
+ You can use escape sequences for application_name even if
+ it is set as a connection option. Please refer the later section.
+
@@ -909,7 +913,7 @@ postgres=# SELECT postgres_fdw_disconnect_all();
Configuration Parameters
-
+
postgres_fdw.application_name (string)
@@ -925,6 +929,53 @@ postgres=# SELECT postgres_fdw_disconnect_all();
Note that change of this parameter doesn't affect any existing
connections until they are re-established.
+
+
+ Same as , this is a
+ printf-style string. Accepted escapes are
+ bit different from ,
+ but padding can be used like as it.
+
+
+
+
+
+
+ Escape
+ Effect
+
+
+
+
+ %a
+ Application name
+
+
+ %u
+ Local user name
+
+
+ %d
+ Local database name
+
+
+ %p
+ Local backend process ID
+
+
+ %%
+ Liteal %
+
+
+
+
+
+
+ Note that if embedded strings have Non-ASCII,
+ these characters will be replaced with the question marks (?).
+ This limitation is caused by application_name.
+
+
diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c
index 816b071afa..33fd515f93 100644
--- a/src/backend/utils/error/elog.c
+++ b/src/backend/utils/error/elog.c
@@ -68,6 +68,7 @@
#include "access/transam.h"
#include "access/xact.h"
+#include "common/string.h"
#include "libpq/libpq.h"
#include "libpq/pqformat.h"
#include "mb/pg_wchar.h"
@@ -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..dee8a83581 100644
--- a/src/common/string.c
+++ b/src/common/string.c
@@ -128,3 +128,44 @@ pg_strip_crlf(char *str)
return len;
}
+
+/*
+ * process_padding --- helper function for processing the format string
+ *
+ * This converts initial part of the string
+ * and returns the first non-numeric character.
+ * The result of the conversion will be stored to the second argument.
+ *
+ * This function is similar to strtol(),
+ * but this follows the printf-format more precisely than that.
+ *
+ * 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;
+}
diff --git a/src/include/common/string.h b/src/include/common/string.h
index 686c158efe..672dbce62f 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);