diff --git a/contrib/postgres_fdw/connection.c b/contrib/postgres_fdw/connection.c
index 82aa14a65d..8ce9aea024 100644
--- a/contrib/postgres_fdw/connection.c
+++ b/contrib/postgres_fdw/connection.c
@@ -353,10 +353,11 @@ connect_pg_server(ForeignServer *server, UserMapping *user)
/*
* 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 *));
@@ -366,7 +367,23 @@ connect_pg_server(ForeignServer *server, UserMapping *user)
n += ExtractConnectionOptions(user->options,
keywords + n, values + n);
- /* Use "postgres_fdw" as fallback_application_name. */
+ /*
+ * Use pgfdw_application_name as application_name if set.
+ *
+ * PQconnectdbParams() processes the parameter arrays from start to end.
+ * If any key word is repeated, the last value is used. Therefore note that
+ * pgfdw_application_name must be added to the arrays after options of
+ * ForeignServer are, so that it can override application_name set in
+ * ForeignServer.
+ */
+ if (pgfdw_application_name && *pgfdw_application_name != '\0')
+ {
+ keywords[n] = "application_name";
+ values[n] = pgfdw_application_name;
+ n++;
+ }
+
+ /* Use "postgres_fdw" as fallback_application_name */
keywords[n] = "fallback_application_name";
values[n] = "postgres_fdw";
n++;
diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.out
index e3ee30f1aa..739b686aca 100644
--- a/contrib/postgres_fdw/expected/postgres_fdw.out
+++ b/contrib/postgres_fdw/expected/postgres_fdw.out
@@ -10761,3 +10761,104 @@ 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
+-- ===================================================================
+-- Disconnect once because the value is used only when establishing connections
+SELECT 1 FROM postgres_fdw_disconnect_all();
+ ?column?
+----------
+ 1
+(1 row)
+
+-- Remove application_name from server option
+ALTER SERVER loopback OPTIONS (DROP application_name);
+-- Start transaction because make sure remote connections are alive
+-- If appname is not set in neither GUC nor server option,
+-- the default value is used. In our environment, it is "pg_regress"
+BEGIN;
+SELECT 1 FROM ft1 LIMIT 1;
+ ?column?
+----------
+ 1
+(1 row)
+
+SELECT COUNT(*) FROM pg_stat_activity WHERE application_name LIKE 'pg_regress';
+ count
+-------
+ 1
+(1 row)
+
+COMMIT;
+-- Add application_name server option only to loopback2
+ALTER SERVER loopback2 OPTIONS (ADD application_name 'loopback2');
+BEGIN;
+-- If appname is set in server option but not in GUC,
+-- appname in server option is used
+SELECT 1 FROM ft6 LIMIT 1;
+ ?column?
+----------
+ 1
+(1 row)
+
+SELECT COUNT(*) FROM pg_stat_activity WHERE application_name LIKE 'loopback2';
+ count
+-------
+ 1
+(1 row)
+
+COMMIT;
+SELECT 1 FROM postgres_fdw_disconnect_all();
+ ?column?
+----------
+ 1
+(1 row)
+
+SET postgres_fdw.application_name TO 'guc_application_name';
+BEGIN;
+-- If appname is set in GUC but not foreign server,
+-- appname in GUC is used
+SELECT 1 FROM ft1 LIMIT 1;
+ ?column?
+----------
+ 1
+(1 row)
+
+SELECT COUNT(*) FROM pg_stat_activity WHERE application_name LIKE 'guc_application_name';
+ count
+-------
+ 1
+(1 row)
+
+COMMIT;
+SELECT 1 FROM postgres_fdw_disconnect('loopback');
+ ?column?
+----------
+ 1
+(1 row)
+
+BEGIN;
+-- If appname is set both in GUC and foreign server,
+-- appname in GUC is used
+SELECT 1 FROM ft6 LIMIT 1;
+ ?column?
+----------
+ 1
+(1 row)
+
+SELECT COUNT(*) FROM pg_stat_activity WHERE application_name LIKE 'loopback2';
+ count
+-------
+ 0
+(1 row)
+
+SELECT COUNT(*) FROM pg_stat_activity WHERE application_name LIKE 'guc_application_name';
+ count
+-------
+ 1
+(1 row)
+
+COMMIT;
+--Clean up
+ALTER SERVER loopback2 OPTIONS (DROP application_name);
+RESET postgres_fdw.application_name;
diff --git a/contrib/postgres_fdw/option.c b/contrib/postgres_fdw/option.c
index c574ca2cf3..e5de766a72 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
*
@@ -52,6 +52,16 @@ 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()
+ */
+void _PG_init(void);
+
#include "miscadmin.h"
/*
@@ -435,3 +445,21 @@ ExtractExtensionList(const char *extensionsString, bool warnOnMissing)
list_free(extlist);
return extensionOids;
}
+
+/*
+ * Define GUC parameters.
+ */
+void
+_PG_init(void)
+{
+ DefineCustomStringVariable("postgres_fdw.application_name",
+ "Sets the application name to be used on the remote server.",
+ NULL,
+ &pgfdw_application_name,
+ NULL,
+ PGC_USERSET,
+ 0,
+ NULL,
+ NULL,
+ NULL);
+}
diff --git a/contrib/postgres_fdw/postgres_fdw.h b/contrib/postgres_fdw/postgres_fdw.h
index ca83306af9..90b72e9ec5 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 *pgfdw_application_name;
/* in deparse.c */
extern void classifyConditions(PlannerInfo *root,
diff --git a/contrib/postgres_fdw/sql/postgres_fdw.sql b/contrib/postgres_fdw/sql/postgres_fdw.sql
index 30b5175da5..81bddacbb5 100644
--- a/contrib/postgres_fdw/sql/postgres_fdw.sql
+++ b/contrib/postgres_fdw/sql/postgres_fdw.sql
@@ -3422,3 +3422,53 @@ 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
+-- ===================================================================
+-- Disconnect once because the value is used only when establishing connections
+SELECT 1 FROM postgres_fdw_disconnect_all();
+-- Remove application_name from server option
+ALTER SERVER loopback OPTIONS (DROP application_name);
+
+-- Start transaction because make sure remote connections are alive
+-- If appname is not set in neither GUC nor server option,
+-- the default value is used. In our environment, it is "pg_regress"
+BEGIN;
+SELECT 1 FROM ft1 LIMIT 1;
+SELECT COUNT(*) FROM pg_stat_activity WHERE application_name LIKE 'pg_regress';
+COMMIT;
+
+-- Add application_name server option only to loopback2
+ALTER SERVER loopback2 OPTIONS (ADD application_name 'loopback2');
+
+BEGIN;
+-- If appname is set in server option but not in GUC,
+-- appname in server option is used
+SELECT 1 FROM ft6 LIMIT 1;
+SELECT COUNT(*) FROM pg_stat_activity WHERE application_name LIKE 'loopback2';
+COMMIT;
+
+SELECT 1 FROM postgres_fdw_disconnect_all();
+SET postgres_fdw.application_name TO 'guc_application_name';
+
+BEGIN;
+-- If appname is set in GUC but not foreign server,
+-- appname in GUC is used
+SELECT 1 FROM ft1 LIMIT 1;
+SELECT COUNT(*) FROM pg_stat_activity WHERE application_name LIKE 'guc_application_name';
+COMMIT;
+
+SELECT 1 FROM postgres_fdw_disconnect('loopback');
+
+BEGIN;
+-- If appname is set both in GUC and foreign server,
+-- appname in GUC is used
+SELECT 1 FROM ft6 LIMIT 1;
+SELECT COUNT(*) FROM pg_stat_activity WHERE application_name LIKE 'loopback2';
+SELECT COUNT(*) FROM pg_stat_activity WHERE application_name LIKE 'guc_application_name';
+COMMIT;
+
+--Clean up
+ALTER SERVER loopback2 OPTIONS (DROP application_name);
+RESET postgres_fdw.application_name;
diff --git a/doc/src/sgml/postgres-fdw.sgml b/doc/src/sgml/postgres-fdw.sgml
index 0075bc3dbb..e7861b02dd 100644
--- a/doc/src/sgml/postgres-fdw.sgml
+++ b/doc/src/sgml/postgres-fdw.sgml
@@ -105,6 +105,34 @@
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.
+
+
+ Note that this parameter has higher priority than sever-level options.
+ If application_name is specified as the
+ option of the server object, and set this parameter at the same time,
+ postgres_fdw.application_name will be used.
+
+
+
+
+
FDW Options of postgres_fdw