aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2021-09-09 13:36:31 -0400
committerTom Lane <tgl@sss.pgh.pa.us>2021-09-09 13:36:44 -0400
commitb7056c0a25e545f4c1efcc1e2a6118e4626e377b (patch)
tree17f7efbdb20d249a0b2ccf6d5329d878260e469e
parentb27d0cd3147e2a0215253f61944c392a30265db8 (diff)
downloadpostgresql-b7056c0a25e545f4c1efcc1e2a6118e4626e377b.tar.gz
postgresql-b7056c0a25e545f4c1efcc1e2a6118e4626e377b.zip
Avoid fetching from an already-terminated plan.
Some plan node types don't react well to being called again after they've already returned NULL. PortalRunSelect() has long dealt with this by calling the executor with NoMovementScanDirection if it sees that we've already run the portal to the end. However, commit ba2c6d6ce overlooked this point, so that persisting an already-fully-fetched cursor would fail if it had such a plan. Per report from Tomas Barton. Back-patch to v11, as the faulty commit was. (I've omitted a test case because the type of plan that causes a problem isn't all that stable.) Discussion: https://postgr.es/m/CAPV2KRjd=ErgVGbvO2Ty20tKTEZZr6cYsYLxgN_W3eAo9pf5sw@mail.gmail.com
-rw-r--r--src/backend/commands/portalcmds.c18
1 files changed, 15 insertions, 3 deletions
diff --git a/src/backend/commands/portalcmds.c b/src/backend/commands/portalcmds.c
index 3ea30bcbc99..b8590d422b6 100644
--- a/src/backend/commands/portalcmds.c
+++ b/src/backend/commands/portalcmds.c
@@ -358,6 +358,8 @@ PersistHoldablePortal(Portal portal)
savePortalContext = PortalContext;
PG_TRY();
{
+ ScanDirection direction = ForwardScanDirection;
+
ActivePortal = portal;
if (portal->resowner)
CurrentResourceOwner = portal->resowner;
@@ -380,10 +382,20 @@ PersistHoldablePortal(Portal portal)
}
else
{
- /* We must reset the cursor state as though at start of query */
+ /* Disallow moving backwards from here */
portal->atStart = true;
- portal->atEnd = false;
portal->portalPos = 0;
+
+ /*
+ * If we already reached end-of-query, set the direction to
+ * NoMovement to avoid trying to fetch any tuples. (This check
+ * exists because not all plan node types are robust about being
+ * called again if they've already returned NULL once.) We'll
+ * still set up an empty tuplestore, though, to keep this from
+ * being a special case later.
+ */
+ if (portal->atEnd)
+ direction = NoMovementScanDirection;
}
/*
@@ -400,7 +412,7 @@ PersistHoldablePortal(Portal portal)
NULL);
/* Fetch the result set into the tuplestore */
- ExecutorRun(queryDesc, ForwardScanDirection, 0L, false);
+ ExecutorRun(queryDesc, direction, 0L, false);
queryDesc->dest->rDestroy(queryDesc->dest);
queryDesc->dest = NULL;