mirror of
https://github.com/postgres/postgres.git
synced 2025-04-25 21:42:33 +03:00
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.
This commit is contained in:
parent
c0d5be5d6a
commit
7981c34279
@ -12,7 +12,7 @@
|
|||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/utils/mmgr/portalmem.c,v 1.116 2010/01/18 02:30:25 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/utils/mmgr/portalmem.c,v 1.117 2010/02/18 03:06:46 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -668,6 +668,7 @@ AtAbort_Portals(void)
|
|||||||
{
|
{
|
||||||
Portal portal = hentry->portal;
|
Portal portal = hentry->portal;
|
||||||
|
|
||||||
|
/* Any portal that was actually running has to be considered broken */
|
||||||
if (portal->status == PORTAL_ACTIVE)
|
if (portal->status == PORTAL_ACTIVE)
|
||||||
portal->status = PORTAL_FAILED;
|
portal->status = PORTAL_FAILED;
|
||||||
|
|
||||||
@ -677,6 +678,15 @@ AtAbort_Portals(void)
|
|||||||
if (portal->createSubid == InvalidSubTransactionId)
|
if (portal->createSubid == InvalidSubTransactionId)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If it was created in the current transaction, we can't do normal
|
||||||
|
* shutdown on a READY portal either; it might refer to objects
|
||||||
|
* created in the failed transaction. See comments in
|
||||||
|
* AtSubAbort_Portals.
|
||||||
|
*/
|
||||||
|
if (portal->status == PORTAL_READY)
|
||||||
|
portal->status = PORTAL_FAILED;
|
||||||
|
|
||||||
/* let portalcmds.c clean up the state it knows about */
|
/* let portalcmds.c clean up the state it knows about */
|
||||||
if (PointerIsValid(portal->cleanup))
|
if (PointerIsValid(portal->cleanup))
|
||||||
{
|
{
|
||||||
@ -789,61 +799,41 @@ AtSubAbort_Portals(SubTransactionId mySubid,
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Force any active portals of my own transaction into FAILED state.
|
* Force any live portals of my own subtransaction into FAILED state.
|
||||||
* This is mostly to ensure that a portal running a FETCH will go
|
* We have to do this because they might refer to objects created or
|
||||||
* FAILED if the underlying cursor fails. (Note we do NOT want to do
|
* changed in the failed subtransaction, leading to crashes if
|
||||||
* this to upper-level portals, since they may be able to continue.)
|
* execution is resumed, or even if we just try to run ExecutorEnd.
|
||||||
*
|
* (Note we do NOT do this to upper-level portals, since they cannot
|
||||||
* This is only needed to dodge the sanity check in PortalDrop.
|
* have such references and hence may be able to continue.)
|
||||||
*/
|
*/
|
||||||
if (portal->status == PORTAL_ACTIVE)
|
if (portal->status == PORTAL_READY ||
|
||||||
|
portal->status == PORTAL_ACTIVE)
|
||||||
portal->status = PORTAL_FAILED;
|
portal->status = PORTAL_FAILED;
|
||||||
|
|
||||||
|
/* let portalcmds.c clean up the state it knows about */
|
||||||
|
if (PointerIsValid(portal->cleanup))
|
||||||
|
{
|
||||||
|
(*portal->cleanup) (portal);
|
||||||
|
portal->cleanup = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* drop cached plan reference, if any */
|
||||||
|
PortalReleaseCachedPlan(portal);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the portal is READY then allow it to survive into the parent
|
* Any resources belonging to the portal will be released in the
|
||||||
* transaction; otherwise shut it down.
|
* upcoming transaction-wide cleanup; they will be gone before we
|
||||||
*
|
* run PortalDrop.
|
||||||
* Currently, we can't actually support that because the portal's
|
|
||||||
* query might refer to objects created or changed in the failed
|
|
||||||
* subtransaction, leading to crashes if execution is resumed. So,
|
|
||||||
* even READY portals are deleted. It would be nice to detect whether
|
|
||||||
* the query actually depends on any such object, instead.
|
|
||||||
*/
|
*/
|
||||||
#ifdef NOT_USED
|
portal->resowner = NULL;
|
||||||
if (portal->status == PORTAL_READY)
|
|
||||||
{
|
|
||||||
portal->createSubid = parentSubid;
|
|
||||||
if (portal->resowner)
|
|
||||||
ResourceOwnerNewParent(portal->resowner, parentXactOwner);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
/* let portalcmds.c clean up the state it knows about */
|
|
||||||
if (PointerIsValid(portal->cleanup))
|
|
||||||
{
|
|
||||||
(*portal->cleanup) (portal);
|
|
||||||
portal->cleanup = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* drop cached plan reference, if any */
|
/*
|
||||||
PortalReleaseCachedPlan(portal);
|
* Although we can't delete the portal data structure proper, we
|
||||||
|
* can release any memory in subsidiary contexts, such as executor
|
||||||
/*
|
* state. The cleanup hook was the last thing that might have
|
||||||
* Any resources belonging to the portal will be released in the
|
* needed data there.
|
||||||
* upcoming transaction-wide cleanup; they will be gone before we
|
*/
|
||||||
* run PortalDrop.
|
MemoryContextDeleteChildren(PortalGetHeapMemory(portal));
|
||||||
*/
|
|
||||||
portal->resowner = NULL;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Although we can't delete the portal data structure proper, we
|
|
||||||
* can release any memory in subsidiary contexts, such as executor
|
|
||||||
* state. The cleanup hook was the last thing that might have
|
|
||||||
* needed data there.
|
|
||||||
*/
|
|
||||||
MemoryContextDeleteChildren(PortalGetHeapMemory(portal));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user