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