diff --git a/src/backend/utils/mmgr/portalmem.c b/src/backend/utils/mmgr/portalmem.c index a53673c..632b202 100644 --- a/src/backend/utils/mmgr/portalmem.c +++ b/src/backend/utils/mmgr/portalmem.c @@ -762,13 +762,22 @@ AtAbort_Portals(void) hash_seq_init(&status, PortalHashTable); while ((hentry = (PortalHashEnt *) hash_seq_search(&status)) != NULL) { Portal portal = hentry->portal; - /* Any portal that was actually running has to be considered broken */ + /* + * See similar code in AtSubAbort_Portals(). This would fire if code + * orchestrating multiple top-level transactions within a portal, such + * as VACUUM, caught errors and continued under the same portal with a + * fresh transaction. No part of core PostgreSQL functions that way, + * though it's a fair thing to want. Such code would wish the portal + * to remain ACTIVE, as in PreCommit_Portals(); we don't cater to + * that. Instead, like AtSubAbort_Portals(), interpret this as a bug. + */ + Assert(portal->status != PORTAL_ACTIVE); if (portal->status == PORTAL_ACTIVE) MarkPortalFailed(portal); /* * Do nothing else to cursors held over from a previous transaction. */ @@ -916,26 +925,29 @@ AtSubAbort_Portals(SubTransactionId mySubid, if (portal->activeSubid == mySubid) { /* Maintain activeSubid until the portal is removed */ portal->activeSubid = parentSubid; /* - * Upper-level portals that failed while running in this - * subtransaction must be forced into FAILED state, for the - * same reasons discussed below. + * If a bug in a MarkPortalActive() caller has it miss cleanup + * after having failed while running an upper-level portal in + * this subtransaction, we don't know what else in the portal + * is wrong. Force it into FAILED state, for the same reasons + * discussed below. * * We assume we can get away without forcing upper-level READY * portals to fail, even if they were run and then suspended. * In theory a suspended upper-level portal could have * acquired some references to objects that are about to be * destroyed, but there should be sufficient defenses against * such cases: the portal's original query cannot contain such * references, and any references within, say, cached plans of * PL/pgSQL functions are not from active queries and should * be protected by revalidation logic. */ + Assert(portal->status != PORTAL_ACTIVE); if (portal->status == PORTAL_ACTIVE) MarkPortalFailed(portal); /* * Also, if we failed it during the current subtransaction * (either just above, or earlier), reattach its resource @@ -957,14 +969,19 @@ AtSubAbort_Portals(SubTransactionId mySubid, } /* * Force any live portals of my own subtransaction into FAILED state. * We have to do this because they might refer to objects created or * changed in the failed subtransaction, leading to crashes within - * ExecutorEnd when portalcmds.c tries to close down the portal. + * ExecutorEnd when portalcmds.c tries to close down the portal. Each + * MarkPortalActive() caller ensures it updates the portal status + * again before relinquishing control, so ACTIVE can't happen here. + * If a bug does make it happen, dispose the portal like a normal + * MarkPortalActive() caller would. */ + Assert(portal->status != PORTAL_ACTIVE); if (portal->status == PORTAL_READY || portal->status == PORTAL_ACTIVE) MarkPortalFailed(portal); /* * Allow portalcmds.c to clean up the state it knows about, if we