diff --git a/contrib/postgres_fdw/connection.c b/contrib/postgres_fdw/connection.c
index 82aa14a65d..da62b0f604 100644
--- a/contrib/postgres_fdw/connection.c
+++ b/contrib/postgres_fdw/connection.c
@@ -349,16 +349,20 @@ 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
* and UserMapping. (Some of them might not be libpq options, in
- * which case we'll just waste a few array slots.) Add 3 extra slots
- * for fallback_application_name, client_encoding, end marker.
+ * which case we'll just waste a few array slots.) Add 4 extra slots
+ * for application_name, fallback_application_name, client_encoding,
+ * end marker.
*/
- n = list_length(server->options) + list_length(user->options) + 3;
+ 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,
@@ -366,7 +370,26 @@ connect_pg_server(ForeignServer *server, UserMapping *user)
n += ExtractConnectionOptions(user->options,
keywords + n, values + n);
- /* Use "postgres_fdw" as fallback_application_name. */
+ /* Use GUC paramter if set */
+ if (pgfdw_application_name && *pgfdw_application_name != '\0')
+ {
+ keywords[n] = "application_name";
+ 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";
n++;
@@ -437,6 +460,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 c574ca2cf3..b9dfedf6c7 100644
--- a/contrib/postgres_fdw/option.c
+++ b/contrib/postgres_fdw/option.c
@@ -1,7 +1,7 @@
/*-------------------------------------------------------------------------
*
* option.c
- * FDW option handling for postgres_fdw
+ * FDW and GUC option handling for postgres_fdw
*
* Portions Copyright (c) 2012-2021, PostgreSQL Global Development Group
*
@@ -18,6 +18,9 @@
#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"
@@ -52,6 +55,18 @@ static void InitPgFdwOptions(void);
static bool is_valid_option(const char *keyword, Oid context);
static bool is_libpq_option(const char *keyword);
+/*
+ * GUC parameters
+ */
+char* pgfdw_application_name = NULL;
+
+/*
+ * _PG_init() and check hook
+ */
+
+static bool check_pgfdw_application_name(char **newval, void **extra, GucSource source);
+void _PG_init(void);
+
#include "miscadmin.h"
/*
@@ -435,3 +450,118 @@ ExtractExtensionList(const char *extensionsString, bool warnOnMissing)
list_free(extlist);
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.
+ */
+static bool
+check_pgfdw_application_name(char **newval, void **extra, GucSource source)
+{
+ /* Only allow clean ASCII chars in the application name */
+ if (*newval)
+ pg_clean_ascii(*newval);
+ return true;
+}
+
+/*
+ * Define GUC parameters.
+ */
+void
+_PG_init(void)
+{
+ DefineCustomStringVariable("postgres_fdw.application_name",
+ "Sets the application name. This is used when connects to the remote server.",
+ NULL,
+ &pgfdw_application_name,
+ NULL,
+ PGC_USERSET,
+ GUC_IS_NAME,
+ check_pgfdw_application_name,
+ NULL,
+ NULL);
+}
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index 9d443baf02..a4720f9e0a 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -318,6 +318,8 @@ typedef struct
*/
PG_FUNCTION_INFO_V1(postgres_fdw_handler);
+void _PG_init(void);
+
/*
* FDW callback routines
*/
diff --git a/contrib/postgres_fdw/postgres_fdw.h b/contrib/postgres_fdw/postgres_fdw.h
index ca83306af9..1168ad4b52 100644
--- a/contrib/postgres_fdw/postgres_fdw.h
+++ b/contrib/postgres_fdw/postgres_fdw.h
@@ -158,6 +158,8 @@ 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 */
extern void classifyConditions(PlannerInfo *root,
diff --git a/doc/src/sgml/postgres-fdw.sgml b/doc/src/sgml/postgres-fdw.sgml
index 0075bc3dbb..437aed647a 100644
--- a/doc/src/sgml/postgres-fdw.sgml
+++ b/doc/src/sgml/postgres-fdw.sgml
@@ -105,6 +105,66 @@
of columns to the remote table is by name, not position.
+
+ Configuration Parameters
+
+
+
+ postgres_fdw.application_name (string)
+
+
+ postgres_fdw.application_name configuration parameter
+
+
+
+
+
+ Specifies a value for
+ configuration parameter. This value is used only when a backend process
+ starts to establish the remote connection.
+
+
+
+ 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
+
+
+
+
+
+
+
+
+
+
FDW Options of postgres_fdw