diff --git a/contrib/postgres_fdw/connection.c b/contrib/postgres_fdw/connection.c
index 54ab8edfab..6a61d83862 100644
--- a/contrib/postgres_fdw/connection.c
+++ b/contrib/postgres_fdw/connection.c
@@ -59,6 +59,8 @@ typedef struct ConnCacheEntry
bool have_error; /* have any subxacts aborted in this xact? */
bool changing_xact_state; /* xact state change in process */
bool invalidated; /* true if reconnect is pending */
+ bool keep_connections; /* setting value of keep_connections
+ * server option */
Oid serverid; /* foreign server OID used to get server name */
uint32 server_hashvalue; /* hash value of foreign server OID */
uint32 mapping_hashvalue; /* hash value of user mapping OID */
@@ -286,6 +288,7 @@ static void
make_new_connection(ConnCacheEntry *entry, UserMapping *user)
{
ForeignServer *server = GetForeignServer(user->serverid);
+ ListCell *lc;
Assert(entry->conn == NULL);
@@ -304,6 +307,26 @@ make_new_connection(ConnCacheEntry *entry, UserMapping *user)
ObjectIdGetDatum(user->umid));
memset(&entry->state, 0, sizeof(entry->state));
+ /*
+ * Determine whether to keep the connection that we're about to make here
+ * open even after the transaction using it ends, so that the subsequent
+ * transactions can re-use it.
+ *
+ * It's enough to determine this only when making new connection because
+ * all the connections to the foreign server whose keep_connections option
+ * is changed will be closed and re-made later.
+ *
+ * By default, all the connections to any foreign servers are kept open.
+ */
+ entry->keep_connections = true;
+ foreach(lc, server->options)
+ {
+ DefElem *def = (DefElem *) lfirst(lc);
+
+ if (strcmp(def->defname, "keep_connections") == 0)
+ entry->keep_connections = defGetBoolean(def);
+ }
+
/* Now try to make the connection */
entry->conn = connect_pg_server(server, user);
@@ -970,14 +993,16 @@ pgfdw_xact_callback(XactEvent event, void *arg)
entry->xact_depth = 0;
/*
- * If the connection isn't in a good idle state or it is marked as
- * invalid, then discard it to recover. Next GetConnection will open a
- * new connection.
+ * If the connection isn't in a good idle state, it is marked as
+ * invalid or keep_connections option of its server is disabled, then
+ * discard it to recover. Next GetConnection will open a new
+ * connection.
*/
if (PQstatus(entry->conn) != CONNECTION_OK ||
PQtransactionStatus(entry->conn) != PQTRANS_IDLE ||
entry->changing_xact_state ||
- entry->invalidated)
+ entry->invalidated ||
+ !entry->keep_connections)
{
elog(DEBUG3, "discarding connection %p", entry->conn);
disconnect_pg_server(entry);
diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.out
index eff7b04f11..5da68415ed 100644
--- a/contrib/postgres_fdw/expected/postgres_fdw.out
+++ b/contrib/postgres_fdw/expected/postgres_fdw.out
@@ -8908,7 +8908,7 @@ DO $d$
END;
$d$;
ERROR: invalid option "password"
-HINT: Valid options in this context are: service, passfile, channel_binding, connect_timeout, dbname, host, hostaddr, port, options, application_name, keepalives, keepalives_idle, keepalives_interval, keepalives_count, tcp_user_timeout, sslmode, sslcompression, sslcert, sslkey, sslrootcert, sslcrl, sslcrldir, requirepeer, ssl_min_protocol_version, ssl_max_protocol_version, gssencmode, krbsrvname, gsslib, target_session_attrs, use_remote_estimate, fdw_startup_cost, fdw_tuple_cost, extensions, updatable, fetch_size, batch_size, async_capable
+HINT: Valid options in this context are: service, passfile, channel_binding, connect_timeout, dbname, host, hostaddr, port, options, application_name, keepalives, keepalives_idle, keepalives_interval, keepalives_count, tcp_user_timeout, sslmode, sslcompression, sslcert, sslkey, sslrootcert, sslcrl, sslcrldir, requirepeer, ssl_min_protocol_version, ssl_max_protocol_version, gssencmode, krbsrvname, gsslib, target_session_attrs, use_remote_estimate, fdw_startup_cost, fdw_tuple_cost, extensions, updatable, fetch_size, batch_size, async_capable, keep_connections
CONTEXT: SQL statement "ALTER SERVER loopback_nopw OPTIONS (ADD password 'dummypw')"
PL/pgSQL function inline_code_block line 3 at EXECUTE
-- If we add a password for our user mapping instead, we should get a different
@@ -9244,6 +9244,27 @@ DROP USER MAPPING FOR regress_multi_conn_user2 SERVER loopback;
DROP ROLE regress_multi_conn_user1;
DROP ROLE regress_multi_conn_user2;
-- ===================================================================
+-- Test foreign server level option keep_connections
+-- ===================================================================
+-- By default, the connections associated with foreign server are cached i.e.
+-- keep_connections option is on. Set it to off.
+ALTER SERVER loopback OPTIONS (keep_connections 'off');
+-- connection to loopback server is closed at the end of xact
+-- as keep_connections was set to off.
+SELECT 1 FROM ft1 LIMIT 1;
+ ?column?
+----------
+ 1
+(1 row)
+
+-- No cached connections, so no records should be output.
+SELECT server_name FROM postgres_fdw_get_connections() ORDER BY 1;
+ server_name
+-------------
+(0 rows)
+
+ALTER SERVER loopback OPTIONS (SET keep_connections 'on');
+-- ===================================================================
-- batch insert
-- ===================================================================
BEGIN;
diff --git a/contrib/postgres_fdw/option.c b/contrib/postgres_fdw/option.c
index 530d7a66d4..f1d0c8bd41 100644
--- a/contrib/postgres_fdw/option.c
+++ b/contrib/postgres_fdw/option.c
@@ -108,7 +108,8 @@ postgres_fdw_validator(PG_FUNCTION_ARGS)
*/
if (strcmp(def->defname, "use_remote_estimate") == 0 ||
strcmp(def->defname, "updatable") == 0 ||
- strcmp(def->defname, "async_capable") == 0)
+ strcmp(def->defname, "async_capable") == 0 ||
+ strcmp(def->defname, "keep_connections") == 0)
{
/* these accept only boolean values */
(void) defGetBoolean(def);
@@ -221,6 +222,7 @@ InitPgFdwOptions(void)
/* async_capable is available on both server and table */
{"async_capable", ForeignServerRelationId, false},
{"async_capable", ForeignTableRelationId, false},
+ {"keep_connections", ForeignServerRelationId, false},
{"password_required", UserMappingRelationId, false},
/*
diff --git a/contrib/postgres_fdw/sql/postgres_fdw.sql b/contrib/postgres_fdw/sql/postgres_fdw.sql
index 806a5bca28..85a968f7f0 100644
--- a/contrib/postgres_fdw/sql/postgres_fdw.sql
+++ b/contrib/postgres_fdw/sql/postgres_fdw.sql
@@ -2819,6 +2819,19 @@ DROP USER MAPPING FOR regress_multi_conn_user2 SERVER loopback;
DROP ROLE regress_multi_conn_user1;
DROP ROLE regress_multi_conn_user2;
+-- ===================================================================
+-- Test foreign server level option keep_connections
+-- ===================================================================
+-- By default, the connections associated with foreign server are cached i.e.
+-- keep_connections option is on. Set it to off.
+ALTER SERVER loopback OPTIONS (keep_connections 'off');
+-- connection to loopback server is closed at the end of xact
+-- as keep_connections was set to off.
+SELECT 1 FROM ft1 LIMIT 1;
+-- No cached connections, so no records should be output.
+SELECT server_name FROM postgres_fdw_get_connections() ORDER BY 1;
+ALTER SERVER loopback OPTIONS (SET keep_connections 'on');
+
-- ===================================================================
-- batch insert
-- ===================================================================
diff --git a/doc/src/sgml/postgres-fdw.sgml b/doc/src/sgml/postgres-fdw.sgml
index c21e9be209..a0c6b8f0b1 100644
--- a/doc/src/sgml/postgres-fdw.sgml
+++ b/doc/src/sgml/postgres-fdw.sgml
@@ -518,6 +518,33 @@ OPTIONS (ADD password_required 'false');
+
+
+ Connection Management Options
+
+
+ By default all the open connections that postgres_fdw
+ established to the foreign servers are kept in local session for re-use.
+
+
+
+
+
+ keep_connections
+
+
+ This option controls whether postgres_fdw keeps
+ the connections to the foreign server open so that the subsequent
+ queries can re-use them. It can only be specified for a foreign server.
+ The default is true. If set to off,
+ all connections to this foreign server will be discarded at the end of
+ transaction.
+
+
+
+
+
+
@@ -605,8 +632,10 @@ postgres=# SELECT postgres_fdw_disconnect_all();
postgres_fdw establishes a connection to a
foreign server during the first query that uses a foreign table
- associated with the foreign server. This connection is kept and
- re-used for subsequent queries in the same session. However, if
+ associated with the foreign server. By default this connection
+ is kept and re-used for subsequent queries in the same session.
+ This behavior can be controlled using
+ keep_connections option for a foreign server. If
multiple user identities (user mappings) are used to access the foreign
server, a connection is established for each user mapping.
@@ -622,8 +651,10 @@ postgres=# SELECT postgres_fdw_disconnect_all();
Once a connection to a foreign server has been established,
- it's usually kept until the local or corresponding remote
+ it's by default kept until the local or corresponding remote
session exits. To disconnect a connection explicitly,
+ keep_connections option for a foreign server
+ may be disabled, or
postgres_fdw_disconnect and
postgres_fdw_disconnect_all functions
may be used. For example, these are useful to close