diff options
Diffstat (limited to 'src/backend')
-rw-r--r-- | src/backend/executor/spi.c | 17 | ||||
-rw-r--r-- | src/backend/utils/mmgr/portalmem.c | 26 |
2 files changed, 35 insertions, 8 deletions
diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c index 6e262d1a3ad..22d0fe5ac4f 100644 --- a/src/backend/executor/spi.c +++ b/src/backend/executor/spi.c @@ -241,6 +241,14 @@ _SPI_commit(bool chain) (errcode(ERRCODE_INVALID_TRANSACTION_TERMINATION), errmsg("cannot commit while a subtransaction is active"))); + /* + * Hold any pinned portals that any PLs might be using. We have to do + * this before changing transaction state, since this will run + * user-defined code that might throw an error. + */ + HoldPinnedPortals(); + + /* Start the actual commit */ _SPI_current->internal_xact = true; /* @@ -294,6 +302,15 @@ _SPI_rollback(bool chain) (errcode(ERRCODE_INVALID_TRANSACTION_TERMINATION), errmsg("cannot roll back while a subtransaction is active"))); + /* + * Hold any pinned portals that any PLs might be using. We have to do + * this before changing transaction state, since this will run + * user-defined code that might throw an error, and in any case couldn't + * be run in an already-aborted transaction. + */ + HoldPinnedPortals(); + + /* Start the actual rollback */ _SPI_current->internal_xact = true; if (chain) diff --git a/src/backend/utils/mmgr/portalmem.c b/src/backend/utils/mmgr/portalmem.c index a92b4541bd0..334e35bb6a3 100644 --- a/src/backend/utils/mmgr/portalmem.c +++ b/src/backend/utils/mmgr/portalmem.c @@ -1226,13 +1226,19 @@ ThereAreNoReadyPortals(void) /* * Hold all pinned portals. * - * A procedural language implementation that uses pinned portals for its - * internally generated cursors can call this in its COMMIT command to convert - * those cursors to held cursors, so that they survive the transaction end. - * We mark those portals as "auto-held" so that exception exit knows to clean - * them up. (In normal, non-exception code paths, the PL needs to clean those - * portals itself, since transaction end won't do it anymore, but that should - * be normal practice anyway.) + * When initiating a COMMIT or ROLLBACK inside a procedure, this must be + * called to protect internally-generated cursors from being dropped during + * the transaction shutdown. Currently, SPI calls this automatically; PLs + * that initiate COMMIT or ROLLBACK some other way are on the hook to do it + * themselves. (Note that we couldn't do this in, say, AtAbort_Portals + * because we need to run user-defined code while persisting a portal. + * It's too late to do that once transaction abort has started.) + * + * We protect such portals by converting them to held cursors. We mark them + * as "auto-held" so that exception exit knows to clean them up. (In normal, + * non-exception code paths, the PL needs to clean such portals itself, since + * transaction end won't do it anymore; but that should be normal practice + * anyway.) */ void HoldPinnedPortals(void) @@ -1262,8 +1268,12 @@ HoldPinnedPortals(void) (errcode(ERRCODE_INVALID_TRANSACTION_TERMINATION), errmsg("cannot perform transaction commands inside a cursor loop that is not read-only"))); - portal->autoHeld = true; + /* Verify it's in a suitable state to be held */ + if (portal->status != PORTAL_READY) + elog(ERROR, "pinned portal is not ready to be auto-held"); + HoldPortal(portal); + portal->autoHeld = true; } } } |