Re: Assert failure on running a completed portal again

From: Tom Lane <tgl(at)sss(dot)pgh(dot)pa(dot)us>
To: Robert Haas <robertmhaas(at)gmail(dot)com>
Cc: Yugo Nagata <nagata(at)sraoss(dot)co(dot)jp>, pgsql-hackers(at)postgresql(dot)org
Subject: Re: Assert failure on running a completed portal again
Date: 2024-12-10 18:37:30
Message-ID: 87760.1733855850@sss.pgh.pa.us
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

I wrote:
> My recollection is that even before parallelism we had some plan node
> types that didn't cope well with being called again after they'd once
> returned EOF (ie a null tuple). So maybe a defense against trying to
> do that would be wise.

I spent a little time trying to run that recollection to ground,
and after awhile arrived at this comment in heapam.c:

* Note: when we fall off the end of the scan in either direction, we
* reset rs_inited. This means that a further request with the same
* scan direction will restart the scan, which is a bit odd, but a
* request with the opposite scan direction will start a fresh scan
* in the proper direction. The latter is required behavior for cursors,
* while the former case is generally undefined behavior in Postgres
* so we don't care too much.

So indeed nodes types as basic as SeqScan will misbehave if that
happens. However, there is also logic in pquery.c that intends
to provide defenses against this, for instance in PortalRunSelect:

* Determine which direction to go in, and check to see if we're already
* at the end of the available tuples in that direction. If so, 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.) Then call the
* executor (we must not skip this, because the destination needs to see a
* setup and shutdown even if no tuples are available). Finally, update
* the portal position state depending on the number of tuples that were
* retrieved.
*/
if (forward)
{
if (portal->atEnd || count <= 0)
{
direction = NoMovementScanDirection;
count = 0; /* don't pass negative count to executor */
}

And ExecutorRun skips ExecutePlan if direction is
NoMovementScanDirection. So unless there are gaps in pquery.c's EOF
checks, I think we're OK: the "re-execution" call will not reach
ExecutePlan. This could probably do with more commentary though.

regards, tom lane

In response to

Responses

Browse pgsql-hackers by date

  From Date Subject
Next Message Euler Taveira 2024-12-10 18:41:08 Separate GUC for replication origins
Previous Message Masahiko Sawada 2024-12-10 18:14:24 Re: Memory leak in WAL sender with pgoutput (v10~)