Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 7981c34

Browse files
committed
Force READY portals into FAILED state when a transaction or subtransaction
is aborted, if they were created within the failed xact. This prevents ExecutorEnd from being run on them, which is a good idea because they may contain references to tables or other objects that no longer exist. In particular this is hazardous when auto_explain is active, but it's really rather surprising that nobody has seen an issue with this before. I'm back-patching this to 8.4, since that's the first version that contains auto_explain or an ExecutorEnd hook, but I wonder whether we shouldn't back-patch further.
1 parent c0d5be5 commit 7981c34

File tree

1 file changed

+38
-48
lines changed

1 file changed

+38
-48
lines changed

src/backend/utils/mmgr/portalmem.c

Lines changed: 38 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
* Portions Copyright (c) 1994, Regents of the University of California
1313
*
1414
* IDENTIFICATION
15-
* $PostgreSQL: pgsql/src/backend/utils/mmgr/portalmem.c,v 1.116 2010/01/18 02:30:25 tgl Exp $
15+
* $PostgreSQL: pgsql/src/backend/utils/mmgr/portalmem.c,v 1.117 2010/02/18 03:06:46 tgl Exp $
1616
*
1717
*-------------------------------------------------------------------------
1818
*/
@@ -668,6 +668,7 @@ AtAbort_Portals(void)
668668
{
669669
Portal portal = hentry->portal;
670670

671+
/* Any portal that was actually running has to be considered broken */
671672
if (portal->status == PORTAL_ACTIVE)
672673
portal->status = PORTAL_FAILED;
673674

@@ -677,6 +678,15 @@ AtAbort_Portals(void)
677678
if (portal->createSubid == InvalidSubTransactionId)
678679
continue;
679680

681+
/*
682+
* If it was created in the current transaction, we can't do normal
683+
* shutdown on a READY portal either; it might refer to objects
684+
* created in the failed transaction. See comments in
685+
* AtSubAbort_Portals.
686+
*/
687+
if (portal->status == PORTAL_READY)
688+
portal->status = PORTAL_FAILED;
689+
680690
/* let portalcmds.c clean up the state it knows about */
681691
if (PointerIsValid(portal->cleanup))
682692
{
@@ -789,61 +799,41 @@ AtSubAbort_Portals(SubTransactionId mySubid,
789799
continue;
790800

791801
/*
792-
* Force any active portals of my own transaction into FAILED state.
793-
* This is mostly to ensure that a portal running a FETCH will go
794-
* FAILED if the underlying cursor fails. (Note we do NOT want to do
795-
* this to upper-level portals, since they may be able to continue.)
796-
*
797-
* This is only needed to dodge the sanity check in PortalDrop.
802+
* Force any live portals of my own subtransaction into FAILED state.
803+
* We have to do this because they might refer to objects created or
804+
* changed in the failed subtransaction, leading to crashes if
805+
* execution is resumed, or even if we just try to run ExecutorEnd.
806+
* (Note we do NOT do this to upper-level portals, since they cannot
807+
* have such references and hence may be able to continue.)
798808
*/
799-
if (portal->status == PORTAL_ACTIVE)
809+
if (portal->status == PORTAL_READY ||
810+
portal->status == PORTAL_ACTIVE)
800811
portal->status = PORTAL_FAILED;
801812

802-
/*
803-
* If the portal is READY then allow it to survive into the parent
804-
* transaction; otherwise shut it down.
805-
*
806-
* Currently, we can't actually support that because the portal's
807-
* query might refer to objects created or changed in the failed
808-
* subtransaction, leading to crashes if execution is resumed. So,
809-
* even READY portals are deleted. It would be nice to detect whether
810-
* the query actually depends on any such object, instead.
811-
*/
812-
#ifdef NOT_USED
813-
if (portal->status == PORTAL_READY)
813+
/* let portalcmds.c clean up the state it knows about */
814+
if (PointerIsValid(portal->cleanup))
814815
{
815-
portal->createSubid = parentSubid;
816-
if (portal->resowner)
817-
ResourceOwnerNewParent(portal->resowner, parentXactOwner);
816+
(*portal->cleanup) (portal);
817+
portal->cleanup = NULL;
818818
}
819-
else
820-
#endif
821-
{
822-
/* let portalcmds.c clean up the state it knows about */
823-
if (PointerIsValid(portal->cleanup))
824-
{
825-
(*portal->cleanup) (portal);
826-
portal->cleanup = NULL;
827-
}
828819

829-
/* drop cached plan reference, if any */
830-
PortalReleaseCachedPlan(portal);
820+
/* drop cached plan reference, if any */
821+
PortalReleaseCachedPlan(portal);
831822

832-
/*
833-
* Any resources belonging to the portal will be released in the
834-
* upcoming transaction-wide cleanup; they will be gone before we
835-
* run PortalDrop.
836-
*/
837-
portal->resowner = NULL;
823+
/*
824+
* Any resources belonging to the portal will be released in the
825+
* upcoming transaction-wide cleanup; they will be gone before we
826+
* run PortalDrop.
827+
*/
828+
portal->resowner = NULL;
838829

839-
/*
840-
* Although we can't delete the portal data structure proper, we
841-
* can release any memory in subsidiary contexts, such as executor
842-
* state. The cleanup hook was the last thing that might have
843-
* needed data there.
844-
*/
845-
MemoryContextDeleteChildren(PortalGetHeapMemory(portal));
846-
}
830+
/*
831+
* Although we can't delete the portal data structure proper, we
832+
* can release any memory in subsidiary contexts, such as executor
833+
* state. The cleanup hook was the last thing that might have
834+
* needed data there.
835+
*/
836+
MemoryContextDeleteChildren(PortalGetHeapMemory(portal));
847837
}
848838
}
849839

0 commit comments

Comments
 (0)