From 1b812afb0eafe125b820cc3b95e7ca03821aa675 Mon Sep 17 00:00:00 2001
From: Robert Haas <rhaas@postgresql.org>
Date: Mon, 16 May 2016 11:19:10 -0400
Subject: [PATCH] Fix multiple problems in postgres_fdw query cancellation
 logic.

First, even if we cancel a query, we still have to roll back the
containing transaction; otherwise, the session will be left in a
failed transaction state.

Second, we need to support canceling queries whe aborting a
subtransaction as well as when aborting a toplevel transaction.

Etsuro Fujita, reviewed by Michael Paquier
---
 contrib/postgres_fdw/connection.c | 28 +++++++++++++++++++++++++---
 1 file changed, 25 insertions(+), 3 deletions(-)

diff --git a/contrib/postgres_fdw/connection.c b/contrib/postgres_fdw/connection.c
index 16ef38fff78..43c7fc9e080 100644
--- a/contrib/postgres_fdw/connection.c
+++ b/contrib/postgres_fdw/connection.c
@@ -677,8 +677,7 @@ pgfdw_xact_callback(XactEvent event, void *arg)
 					 * using an asynchronous execution function, the command
 					 * might not have yet completed.  Check to see if a command
 					 * is still being processed by the remote server, and if so,
-					 * request cancellation of the command; if not, abort
-					 * gracefully.
+					 * request cancellation of the command.
 					 */
 					if (PQtransactionStatus(entry->conn) == PQTRANS_ACTIVE)
 					{
@@ -694,7 +693,6 @@ pgfdw_xact_callback(XactEvent event, void *arg)
 												errbuf)));
 							PQfreeCancel(cancel);
 						}
-						break;
 					}
 
 					/* If we're aborting, abort all remote transactions too */
@@ -798,6 +796,30 @@ pgfdw_subxact_callback(SubXactEvent event, SubTransactionId mySubid,
 		{
 			/* Assume we might have lost track of prepared statements */
 			entry->have_error = true;
+
+			/*
+			 * If a command has been submitted to the remote server by using an
+			 * asynchronous execution function, the command might not have yet
+			 * completed.  Check to see if a command is still being processed by
+			 * the remote server, and if so, request cancellation of the
+			 * command.
+			 */
+			if (PQtransactionStatus(entry->conn) == PQTRANS_ACTIVE)
+			{
+				PGcancel   *cancel;
+				char		errbuf[256];
+
+				if ((cancel = PQgetCancel(entry->conn)))
+				{
+					if (!PQcancel(cancel, errbuf, sizeof(errbuf)))
+						ereport(WARNING,
+								(errcode(ERRCODE_CONNECTION_FAILURE),
+								 errmsg("could not send cancel request: %s",
+										errbuf)));
+					PQfreeCancel(cancel);
+				}
+			}
+
 			/* Rollback all remote subtransactions during abort */
 			snprintf(sql, sizeof(sql),
 					 "ROLLBACK TO SAVEPOINT s%d; RELEASE SAVEPOINT s%d",
-- 
GitLab