Skip to content

Commit 411ae64

Browse files
committed
postgres_fdw: Add functions to discard cached connections.
This commit introduces two new functions postgres_fdw_disconnect() and postgres_fdw_disconnect_all(). The former function discards the cached connections to the specified foreign server. The latter discards all the cached connections. If the connection is used in the current transaction, it's not closed and a warning message is emitted. For example, these functions are useful when users want to explicitly close the foreign server connections that are no longer necessary and then to prevent them from eating up the foreign servers connections capacity. Author: Bharath Rupireddy, tweaked a bit by Fujii Masao Reviewed-by: Alexey Kondratov, Zhijie Hou, Zhihong Yu, Fujii Masao Discussion: https://postgr.es/m/CALj2ACVvrp5=AVp2PupEm+nAC8S4buqR3fJMmaCoc7ftT0aD2A@mail.gmail.com
1 parent ee895a6 commit 411ae64

File tree

5 files changed

+505
-13
lines changed

5 files changed

+505
-13
lines changed

contrib/postgres_fdw/connection.c

+132-3
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@ static bool xact_got_connection = false;
8080
* SQL functions
8181
*/
8282
PG_FUNCTION_INFO_V1(postgres_fdw_get_connections);
83+
PG_FUNCTION_INFO_V1(postgres_fdw_disconnect);
84+
PG_FUNCTION_INFO_V1(postgres_fdw_disconnect_all);
8385

8486
/* prototypes of private functions */
8587
static void make_new_connection(ConnCacheEntry *entry, UserMapping *user);
@@ -102,6 +104,7 @@ static bool pgfdw_exec_cleanup_query(PGconn *conn, const char *query,
102104
static bool pgfdw_get_cleanup_result(PGconn *conn, TimestampTz endtime,
103105
PGresult **result);
104106
static bool UserMappingPasswordRequired(UserMapping *user);
107+
static bool disconnect_cached_connections(Oid serverid);
105108

106109
/*
107110
* Get a PGconn which can be used to execute queries on the remote PostgreSQL
@@ -1428,8 +1431,8 @@ postgres_fdw_get_connections(PG_FUNCTION_ARGS)
14281431
* Even though the server is dropped in the current transaction, the
14291432
* cache can still have associated active connection entry, say we
14301433
* call such connections dangling. Since we can not fetch the server
1431-
* name from system catalogs for dangling connections, instead we
1432-
* show NULL value for server name in output.
1434+
* name from system catalogs for dangling connections, instead we show
1435+
* NULL value for server name in output.
14331436
*
14341437
* We could have done better by storing the server name in the cache
14351438
* entry instead of server oid so that it could be used in the output.
@@ -1447,7 +1450,7 @@ postgres_fdw_get_connections(PG_FUNCTION_ARGS)
14471450
/*
14481451
* If the server has been dropped in the current explicit
14491452
* transaction, then this entry would have been invalidated in
1450-
* pgfdw_inval_callback at the end of drop sever command. Note
1453+
* pgfdw_inval_callback at the end of drop server command. Note
14511454
* that this connection would not have been closed in
14521455
* pgfdw_inval_callback because it is still being used in the
14531456
* current explicit transaction. So, assert that here.
@@ -1470,3 +1473,129 @@ postgres_fdw_get_connections(PG_FUNCTION_ARGS)
14701473

14711474
PG_RETURN_VOID();
14721475
}
1476+
1477+
/*
1478+
* Disconnect the specified cached connections.
1479+
*
1480+
* This function discards the open connections that are established by
1481+
* postgres_fdw from the local session to the foreign server with
1482+
* the given name. Note that there can be multiple connections to
1483+
* the given server using different user mappings. If the connections
1484+
* are used in the current local transaction, they are not disconnected
1485+
* and warning messages are reported. This function returns true
1486+
* if it disconnects at least one connection, otherwise false. If no
1487+
* foreign server with the given name is found, an error is reported.
1488+
*/
1489+
Datum
1490+
postgres_fdw_disconnect(PG_FUNCTION_ARGS)
1491+
{
1492+
ForeignServer *server;
1493+
char *servername;
1494+
1495+
servername = text_to_cstring(PG_GETARG_TEXT_PP(0));
1496+
server = GetForeignServerByName(servername, false);
1497+
1498+
PG_RETURN_BOOL(disconnect_cached_connections(server->serverid));
1499+
}
1500+
1501+
/*
1502+
* Disconnect all the cached connections.
1503+
*
1504+
* This function discards all the open connections that are established by
1505+
* postgres_fdw from the local session to the foreign servers.
1506+
* If the connections are used in the current local transaction, they are
1507+
* not disconnected and warning messages are reported. This function
1508+
* returns true if it disconnects at least one connection, otherwise false.
1509+
*/
1510+
Datum
1511+
postgres_fdw_disconnect_all(PG_FUNCTION_ARGS)
1512+
{
1513+
PG_RETURN_BOOL(disconnect_cached_connections(InvalidOid));
1514+
}
1515+
1516+
/*
1517+
* Workhorse to disconnect cached connections.
1518+
*
1519+
* This function scans all the connection cache entries and disconnects
1520+
* the open connections whose foreign server OID matches with
1521+
* the specified one. If InvalidOid is specified, it disconnects all
1522+
* the cached connections.
1523+
*
1524+
* This function emits a warning for each connection that's used in
1525+
* the current transaction and doesn't close it. It returns true if
1526+
* it disconnects at least one connection, otherwise false.
1527+
*
1528+
* Note that this function disconnects even the connections that are
1529+
* established by other users in the same local session using different
1530+
* user mappings. This leads even non-superuser to be able to close
1531+
* the connections established by superusers in the same local session.
1532+
*
1533+
* XXX As of now we don't see any security risk doing this. But we should
1534+
* set some restrictions on that, for example, prevent non-superuser
1535+
* from closing the connections established by superusers even
1536+
* in the same session?
1537+
*/
1538+
static bool
1539+
disconnect_cached_connections(Oid serverid)
1540+
{
1541+
HASH_SEQ_STATUS scan;
1542+
ConnCacheEntry *entry;
1543+
bool all = !OidIsValid(serverid);
1544+
bool result = false;
1545+
1546+
/*
1547+
* Connection cache hashtable has not been initialized yet in this
1548+
* session, so return false.
1549+
*/
1550+
if (!ConnectionHash)
1551+
return false;
1552+
1553+
hash_seq_init(&scan, ConnectionHash);
1554+
while ((entry = (ConnCacheEntry *) hash_seq_search(&scan)))
1555+
{
1556+
/* Ignore cache entry if no open connection right now. */
1557+
if (!entry->conn)
1558+
continue;
1559+
1560+
if (all || entry->serverid == serverid)
1561+
{
1562+
/*
1563+
* Emit a warning because the connection to close is used in the
1564+
* current transaction and cannot be disconnected right now.
1565+
*/
1566+
if (entry->xact_depth > 0)
1567+
{
1568+
ForeignServer *server;
1569+
1570+
server = GetForeignServerExtended(entry->serverid,
1571+
FSV_MISSING_OK);
1572+
1573+
if (!server)
1574+
{
1575+
/*
1576+
* If the foreign server was dropped while its connection
1577+
* was used in the current transaction, the connection
1578+
* must have been marked as invalid by
1579+
* pgfdw_inval_callback at the end of DROP SERVER command.
1580+
*/
1581+
Assert(entry->invalidated);
1582+
1583+
ereport(WARNING,
1584+
(errmsg("cannot close dropped server connection because it is still in use")));
1585+
}
1586+
else
1587+
ereport(WARNING,
1588+
(errmsg("cannot close connection for server \"%s\" because it is still in use",
1589+
server->servername)));
1590+
}
1591+
else
1592+
{
1593+
elog(DEBUG3, "discarding connection %p", entry->conn);
1594+
disconnect_pg_server(entry);
1595+
result = true;
1596+
}
1597+
}
1598+
}
1599+
1600+
return result;
1601+
}

0 commit comments

Comments
 (0)