fix-connection-handling-in-postgres-fdw-v2.patch
application/octet-stream
Filename: fix-connection-handling-in-postgres-fdw-v2.patch
Type: application/octet-stream
Part: 0
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: unified
Series: patch v2
| File | + | − |
|---|---|---|
| contrib/postgres_fdw/connection.c | 5 | 4 |
| contrib/postgres_fdw/expected/postgres_fdw.out | 50 | 0 |
| contrib/postgres_fdw/sql/postgres_fdw.sql | 36 | 0 |
diff --git a/contrib/postgres_fdw/connection.c b/contrib/postgres_fdw/connection.c
index 06673017bcf..e7f077bb588 100644
--- a/contrib/postgres_fdw/connection.c
+++ b/contrib/postgres_fdw/connection.c
@@ -1480,6 +1480,10 @@ pgfdw_inval_callback(Datum arg, SysCacheIdentifier cacheid, uint32 hashvalue)
* Such connections can't safely be further used. Re-establishing the
* connection would change the snapshot and roll back any writes already
* performed, so that's not an option, either. Thus, we must abort.
+ *
+ * Note: even if the connection cache entry is marked as such, we will retain
+ * it until abort cleanup of the main transaction because there might be open
+ * cursors that refer to its PGconn.
*/
static void
pgfdw_reject_incomplete_xact_state_change(ConnCacheEntry *entry)
@@ -1490,15 +1494,12 @@ pgfdw_reject_incomplete_xact_state_change(ConnCacheEntry *entry)
if (entry->conn == NULL || !entry->changing_xact_state)
return;
- /* make sure this entry is inactive */
- disconnect_pg_server(entry);
-
/* find server name to be shown in the message below */
server = GetForeignServer(entry->serverid);
ereport(ERROR,
(errcode(ERRCODE_CONNECTION_EXCEPTION),
- errmsg("connection to server \"%s\" was lost",
+ errmsg("connection to server \"%s\" cannot be used due to abort cleanup failure",
server->servername)));
}
diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.out
index 10e87acabef..a2bc212d949 100644
--- a/contrib/postgres_fdw/expected/postgres_fdw.out
+++ b/contrib/postgres_fdw/expected/postgres_fdw.out
@@ -12977,3 +12977,53 @@ SELECT server_name,
-- Clean up
\set VERBOSITY default
RESET debug_discard_caches;
+-- ===================================================================
+-- test handling of abort-cleanup-failed connections
+-- ===================================================================
+CREATE VIEW my_backend_pid (pid) AS SELECT pg_backend_pid();
+CREATE FOREIGN TABLE remote_backend_pid (pid int)
+ SERVER loopback OPTIONS (table_name 'my_backend_pid');
+CREATE FUNCTION wait_for_backend_termination(int) RETURNS void AS $$
+ BEGIN
+ WHILE (SELECT count(*) FROM pg_stat_activity WHERE pid = $1) > 0
+ LOOP
+ PERFORM pg_stat_clear_snapshot();
+ END LOOP;
+ END
+$$ LANGUAGE plpgsql;
+SET client_min_messages = 'ERROR';
+BEGIN;
+DECLARE c CURSOR FOR SELECT * FROM ft1 ORDER BY c1;
+FETCH c;
+ c1 | c2 | c3 | c4 | c5 | c6 | c7 | c8
+----+----+-------------------+------------------------------+--------------------------+----+------------+-----
+ 1 | 2 | 00001_trig_update | Fri Jan 02 00:00:00 1970 PST | Fri Jan 02 00:00:00 1970 | 1 | 1 | foo
+(1 row)
+
+SAVEPOINT s;
+SELECT pid AS rempid FROM remote_backend_pid \gset
+SELECT pg_terminate_backend(:rempid);
+ pg_terminate_backend
+----------------------
+ t
+(1 row)
+
+SELECT wait_for_backend_termination(:rempid);
+ wait_for_backend_termination
+------------------------------
+
+(1 row)
+
+ROLLBACK TO SAVEPOINT s;
+SELECT pid FROM remote_backend_pid;
+ERROR: connection to server "loopback" cannot be used due to abort cleanup failure
+ROLLBACK TO SAVEPOINT s;
+RELEASE SAVEPOINT s;
+CLOSE c;
+ERROR: no connection to the server
+CONTEXT: remote SQL command: CLOSE c1
+ABORT;
+RESET client_min_messages;
+DROP FUNCTION wait_for_backend_termination(int);
+DROP FOREIGN TABLE remote_backend_pid;
+DROP VIEW my_backend_pid;
diff --git a/contrib/postgres_fdw/sql/postgres_fdw.sql b/contrib/postgres_fdw/sql/postgres_fdw.sql
index 79ad5be8bf9..f00144a88a0 100644
--- a/contrib/postgres_fdw/sql/postgres_fdw.sql
+++ b/contrib/postgres_fdw/sql/postgres_fdw.sql
@@ -4620,3 +4620,39 @@ SELECT server_name,
-- Clean up
\set VERBOSITY default
RESET debug_discard_caches;
+
+-- ===================================================================
+-- test handling of abort-cleanup-failed connections
+-- ===================================================================
+
+CREATE VIEW my_backend_pid (pid) AS SELECT pg_backend_pid();
+CREATE FOREIGN TABLE remote_backend_pid (pid int)
+ SERVER loopback OPTIONS (table_name 'my_backend_pid');
+CREATE FUNCTION wait_for_backend_termination(int) RETURNS void AS $$
+ BEGIN
+ WHILE (SELECT count(*) FROM pg_stat_activity WHERE pid = $1) > 0
+ LOOP
+ PERFORM pg_stat_clear_snapshot();
+ END LOOP;
+ END
+$$ LANGUAGE plpgsql;
+
+SET client_min_messages = 'ERROR';
+BEGIN;
+DECLARE c CURSOR FOR SELECT * FROM ft1 ORDER BY c1;
+FETCH c;
+SAVEPOINT s;
+SELECT pid AS rempid FROM remote_backend_pid \gset
+SELECT pg_terminate_backend(:rempid);
+SELECT wait_for_backend_termination(:rempid);
+ROLLBACK TO SAVEPOINT s;
+SELECT pid FROM remote_backend_pid;
+ROLLBACK TO SAVEPOINT s;
+RELEASE SAVEPOINT s;
+CLOSE c;
+ABORT;
+RESET client_min_messages;
+
+DROP FUNCTION wait_for_backend_termination(int);
+DROP FOREIGN TABLE remote_backend_pid;
+DROP VIEW my_backend_pid;