From 6252c4f9e201f619e5eebda12fa867acd4e4200e Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Thu, 3 Mar 2011 13:03:34 -0500
Subject: [PATCH] Run a portal's cleanup hook immediately when pushing it to
 DONE state.

This works around the problem noted by Yamamoto Takashi in bug #5906,
that there were code paths whereby we could reach AtCleanup_Portals
with a portal's cleanup hook still unexecuted.  The changes I made
a few days ago were intended to prevent that from happening, and
I think that on balance it's still a good thing to avoid, so I don't
want to remove the Assert in AtCleanup_Portals.  Hence do this instead.
---
 src/backend/tcop/pquery.c          |  2 +-
 src/backend/utils/mmgr/portalmem.c | 26 ++++++++++++++++++++++++++
 src/include/utils/portal.h         |  1 +
 3 files changed, 28 insertions(+), 1 deletion(-)

diff --git a/src/backend/tcop/pquery.c b/src/backend/tcop/pquery.c
index edde5642ee7..1286edee729 100644
--- a/src/backend/tcop/pquery.c
+++ b/src/backend/tcop/pquery.c
@@ -814,7 +814,7 @@ PortalRun(Portal portal, long count, bool isTopLevel,
 							   dest, altdest, completionTag);
 
 				/* Prevent portal's commands from being re-executed */
-				portal->status = PORTAL_DONE;
+				MarkPortalDone(portal);
 
 				/* Always complete at end of RunMulti */
 				result = true;
diff --git a/src/backend/utils/mmgr/portalmem.c b/src/backend/utils/mmgr/portalmem.c
index 0ca5e110393..7fa66b42212 100644
--- a/src/backend/utils/mmgr/portalmem.c
+++ b/src/backend/utils/mmgr/portalmem.c
@@ -401,6 +401,32 @@ UnpinPortal(Portal portal)
 	portal->portalPinned = false;
 }
 
+/*
+ * MarkPortalDone
+ *		Transition a portal from ACTIVE to DONE state.
+ */
+void
+MarkPortalDone(Portal portal)
+{
+	/* Perform the state transition */
+	Assert(portal->status == PORTAL_ACTIVE);
+	portal->status = PORTAL_DONE;
+
+	/*
+	 * Allow portalcmds.c to clean up the state it knows about.  We might
+	 * as well do that now, since the portal can't be executed any more.
+	 *
+	 * In some cases involving execution of a ROLLBACK command in an already
+	 * aborted transaction, this prevents an assertion failure from reaching
+	 * AtCleanup_Portals with the cleanup hook still unexecuted.
+	 */
+	if (PointerIsValid(portal->cleanup))
+	{
+		(*portal->cleanup) (portal);
+		portal->cleanup = NULL;
+	}
+}
+
 /*
  * PortalDrop
  *		Destroy the portal.
diff --git a/src/include/utils/portal.h b/src/include/utils/portal.h
index dd88451f951..3068003caa3 100644
--- a/src/include/utils/portal.h
+++ b/src/include/utils/portal.h
@@ -206,6 +206,7 @@ extern Portal CreatePortal(const char *name, bool allowDup, bool dupSilent);
 extern Portal CreateNewPortal(void);
 extern void PinPortal(Portal portal);
 extern void UnpinPortal(Portal portal);
+extern void MarkPortalDone(Portal portal);
 extern void PortalDrop(Portal portal, bool isTopCommit);
 extern Portal GetPortalByName(const char *name);
 extern void PortalDefineQuery(Portal portal,
-- 
GitLab