@@ -80,6 +80,8 @@ static bool xact_got_connection = false;
80
80
* SQL functions
81
81
*/
82
82
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 );
83
85
84
86
/* prototypes of private functions */
85
87
static void make_new_connection (ConnCacheEntry * entry , UserMapping * user );
@@ -102,6 +104,7 @@ static bool pgfdw_exec_cleanup_query(PGconn *conn, const char *query,
102
104
static bool pgfdw_get_cleanup_result (PGconn * conn , TimestampTz endtime ,
103
105
PGresult * * result );
104
106
static bool UserMappingPasswordRequired (UserMapping * user );
107
+ static bool disconnect_cached_connections (Oid serverid );
105
108
106
109
/*
107
110
* 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)
1428
1431
* Even though the server is dropped in the current transaction, the
1429
1432
* cache can still have associated active connection entry, say we
1430
1433
* 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.
1433
1436
*
1434
1437
* We could have done better by storing the server name in the cache
1435
1438
* 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)
1447
1450
/*
1448
1451
* If the server has been dropped in the current explicit
1449
1452
* 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
1451
1454
* that this connection would not have been closed in
1452
1455
* pgfdw_inval_callback because it is still being used in the
1453
1456
* current explicit transaction. So, assert that here.
@@ -1470,3 +1473,129 @@ postgres_fdw_get_connections(PG_FUNCTION_ARGS)
1470
1473
1471
1474
PG_RETURN_VOID ();
1472
1475
}
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