diff options
Diffstat (limited to 'src/backend/utils/mmgr/portalmem.c')
-rw-r--r-- | src/backend/utils/mmgr/portalmem.c | 82 |
1 files changed, 74 insertions, 8 deletions
diff --git a/src/backend/utils/mmgr/portalmem.c b/src/backend/utils/mmgr/portalmem.c index 75304986a07..44a87f7dea8 100644 --- a/src/backend/utils/mmgr/portalmem.c +++ b/src/backend/utils/mmgr/portalmem.c @@ -232,6 +232,7 @@ CreatePortal(const char *name, bool allowDup, bool dupSilent) portal->status = PORTAL_NEW; portal->cleanup = PortalCleanup; portal->createSubid = GetCurrentSubTransactionId(); + portal->activeSubid = portal->createSubid; portal->strategy = PORTAL_MULTI_QUERY; portal->cursorOptions = CURSOR_OPT_NO_SCROLL; portal->atStart = true; @@ -403,6 +404,25 @@ UnpinPortal(Portal portal) } /* + * MarkPortalActive + * Transition a portal from READY to ACTIVE state. + * + * NOTE: never set portal->status = PORTAL_ACTIVE directly; call this instead. + */ +void +MarkPortalActive(Portal portal) +{ + /* For safety, this is a runtime test not just an Assert */ + if (portal->status != PORTAL_READY) + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("portal \"%s\" cannot be run", portal->name))); + /* Perform the state transition */ + portal->status = PORTAL_ACTIVE; + portal->activeSubid = GetCurrentSubTransactionId(); +} + +/* * MarkPortalDone * Transition a portal from ACTIVE to DONE state. * @@ -690,6 +710,7 @@ PreCommit_Portals(bool isPrepare) * not belonging to this transaction. */ portal->createSubid = InvalidSubTransactionId; + portal->activeSubid = InvalidSubTransactionId; /* Report we changed state */ result = true; @@ -836,8 +857,8 @@ AtCleanup_Portals(void) /* * Pre-subcommit processing for portals. * - * Reassign the portals created in the current subtransaction to the parent - * subtransaction. + * Reassign portals created or used in the current subtransaction to the + * parent subtransaction. */ void AtSubCommit_Portals(SubTransactionId mySubid, @@ -859,14 +880,16 @@ AtSubCommit_Portals(SubTransactionId mySubid, if (portal->resowner) ResourceOwnerNewParent(portal->resowner, parentXactOwner); } + if (portal->activeSubid == mySubid) + portal->activeSubid = parentSubid; } } /* * Subtransaction abort handling for portals. * - * Deactivate portals created during the failed subtransaction. - * Note that per AtSubCommit_Portals, this will catch portals created + * Deactivate portals created or used during the failed subtransaction. + * Note that per AtSubCommit_Portals, this will catch portals created/used * in descendants of the subtransaction too. * * We don't destroy any portals here; that's done in AtSubCleanup_Portals. @@ -874,6 +897,7 @@ AtSubCommit_Portals(SubTransactionId mySubid, void AtSubAbort_Portals(SubTransactionId mySubid, SubTransactionId parentSubid, + ResourceOwner myXactOwner, ResourceOwner parentXactOwner) { HASH_SEQ_STATUS status; @@ -885,16 +909,58 @@ AtSubAbort_Portals(SubTransactionId mySubid, { Portal portal = hentry->portal; + /* Was it created in this subtransaction? */ if (portal->createSubid != mySubid) + { + /* No, but maybe it was used in this subtransaction? */ + 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. + * + * 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. + */ + if (portal->status == PORTAL_ACTIVE) + MarkPortalFailed(portal); + + /* + * Also, if we failed it during the current subtransaction + * (either just above, or earlier), reattach its resource + * owner to the current subtransaction's resource owner, so + * that any resources it still holds will be released while + * cleaning up this subtransaction. This prevents some corner + * cases wherein we might get Asserts or worse while cleaning + * up objects created during the current subtransaction + * (because they're still referenced within this portal). + */ + if (portal->status == PORTAL_FAILED && portal->resowner) + { + ResourceOwnerNewParent(portal->resowner, myXactOwner); + portal->resowner = NULL; + } + } + /* Done if it wasn't created in this subtransaction */ continue; + } /* * 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 if - * 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 - * have such references and hence may be able to continue.) + * changed in the failed subtransaction, leading to crashes within + * ExecutorEnd when portalcmds.c tries to close down the portal. */ if (portal->status == PORTAL_READY || portal->status == PORTAL_ACTIVE) |