@@ -80,6 +80,8 @@ static bool xact_got_connection = false;
8080 * SQL functions
8181 */
8282PG_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 */
8587static void make_new_connection (ConnCacheEntry * entry , UserMapping * user );
@@ -102,6 +104,7 @@ static bool pgfdw_exec_cleanup_query(PGconn *conn, const char *query,
102104static bool pgfdw_get_cleanup_result (PGconn * conn , TimestampTz endtime ,
103105 PGresult * * result );
104106static 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