diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c index 1ad826d18f..150a0e8914 100644 --- a/src/backend/access/transam/xact.c +++ b/src/backend/access/transam/xact.c @@ -4208,6 +4208,9 @@ AbortOutOfAnyTransaction(void) { TransactionState s = CurrentTransactionState; + /* Ensure we're not running in a doomed memory context */ + AtAbort_Memory(); + /* * Get out of any transaction or nested transaction */ @@ -4249,7 +4252,14 @@ AbortOutOfAnyTransaction(void) break; case TBLOCK_ABORT: case TBLOCK_ABORT_END: - /* AbortTransaction already done, still need Cleanup */ + + /* + * AbortTransaction is already done, still need Cleanup. + * However, if we failed partway through running ROLLBACK, + * there will be an active portal running that command, which + * we need to shut down before doing CleanupTransaction. + */ + AtAbort_Portals(); CleanupTransaction(); s->blockState = TBLOCK_DEFAULT; break; @@ -4272,6 +4282,14 @@ AbortOutOfAnyTransaction(void) case TBLOCK_SUBABORT_END: case TBLOCK_SUBABORT_RESTART: /* As above, but AbortSubTransaction already done */ + if (s->curTransactionOwner) + { + /* As in TBLOCK_ABORT, might have a live portal to zap */ + AtSubAbort_Portals(s->subTransactionId, + s->parent->subTransactionId, + s->curTransactionOwner, + s->parent->curTransactionOwner); + } CleanupSubTransaction(); s = CurrentTransactionState; /* changed by pop */ break; @@ -4280,6 +4298,9 @@ AbortOutOfAnyTransaction(void) /* Should be out of all subxacts now */ Assert(s->parent == NULL); + + /* If we didn't actually have anything to do, revert to TopMemoryContext */ + AtCleanup_Memory(); } /* diff --git a/src/backend/utils/mmgr/portalmem.c b/src/backend/utils/mmgr/portalmem.c index 8286800380..684cb6a261 100644 --- a/src/backend/utils/mmgr/portalmem.c +++ b/src/backend/utils/mmgr/portalmem.c @@ -436,8 +436,8 @@ MarkPortalDone(Portal portal) * 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 caused by - * reaching AtCleanup_Portals with the cleanup hook still unexecuted. + * aborted transaction, this is necessary, or we'd reach AtCleanup_Portals + * with the cleanup hook still unexecuted. */ if (PointerIsValid(portal->cleanup)) { @@ -464,8 +464,8 @@ MarkPortalFailed(Portal portal) * well do that now, since the portal can't be executed any more. * * In some cases involving cleanup of an already aborted transaction, this - * prevents an assertion failure caused by reaching AtCleanup_Portals with - * the cleanup hook still unexecuted. + * is necessary, or we'd reach AtCleanup_Portals with the cleanup hook + * still unexecuted. */ if (PointerIsValid(portal->cleanup)) { @@ -863,8 +863,15 @@ AtCleanup_Portals(void) if (portal->portalPinned) portal->portalPinned = false; - /* We had better not be calling any user-defined code here */ - Assert(portal->cleanup == NULL); + /* + * We had better not call any user-defined code during cleanup, so if + * the cleanup hook hasn't been run yet, too bad; we'll just skip it. + */ + if (PointerIsValid(portal->cleanup)) + { + elog(WARNING, "skipping cleanup for portal \"%s\"", portal->name); + portal->cleanup = NULL; + } /* Zap it. */ PortalDrop(portal, false); @@ -1047,8 +1054,15 @@ AtSubCleanup_Portals(SubTransactionId mySubid) if (portal->portalPinned) portal->portalPinned = false; - /* We had better not be calling any user-defined code here */ - Assert(portal->cleanup == NULL); + /* + * We had better not call any user-defined code during cleanup, so if + * the cleanup hook hasn't been run yet, too bad; we'll just skip it. + */ + if (PointerIsValid(portal->cleanup)) + { + elog(WARNING, "skipping cleanup for portal \"%s\"", portal->name); + portal->cleanup = NULL; + } /* Zap it. */ PortalDrop(portal, false);