[PG19] plpgsql: SELECT INTO sets FOUND wrongly after a function becomes a SRF
Hackorum builds and tests every patch posted to the lists, not only commitfest submissions. This is Hackorum's own CI rather than the PostgreSQL project's, and it is still under testing - please report anything that looks wrong.
This thread has been committed, so CI has stopped here. Anything below is the last result it produced.
You can run a PostgreSQL built from this patch straight from Docker, with no checkout and no build:
docker run --rm -p 5432:5432 ghcr.io/hackorum-dev/postgres-patch:t254071psql -h localhost -U postgresBuilt from patchset v1 (message #1), October 05, 2026 at 04:46 PM.
Every patchset is also pushed to a branch of our PostgreSQL fork, so you can check out the same tree CI built. Without a PostgreSQL checkout:
git clone --branch t254071_1 https://github.com/hackorum-dev/postgres.gitIn a checkout you already have, add the fork once:
git remote add hackorum https://github.com/hackorum-dev/postgres.gitthen, for this patchset and every later one:
git fetch hackorum t254071_1 && git checkout t254071_1Patchset v1 (message #1) is on t254071_1
Hi hackers,
I used Opus to analyze the new features in PG19, and it found a problem
with the "SELECT simple-expression INTO var" fast path from ce8d5fe0e28.
create function f1() returns int language sql as 'select 1';
create function caller() returns bool language plpgsql as $$
declare v int;
begin
select f1() into v;
return found;
end $$;
select caller();
drop function f1();
create function f1() returns setof int language sql
as 'select 1 where false';
select caller();
The last call gives f on 18 and t on 19. Only the first call after the
change is wrong. The attached .sql has more cases.
It is hard to hit. f1() has to be recreated as set-returning while a
session has caller() cached, for example by an extension update.
I think it is because exec_stmt_execsql() calls exec_eval_expr(), which
falls back to SPI when a replan finds the query is no longer simple.
FOUND is then set as if one row came back.
0001 calls exec_eval_simple_expr() directly and uses the regular SPI
code when it returns false. That is the only time the new branch runs,
so the fast path costs the same. It also removes a doubled 'SQL
statement' context line in errors raised under recursion. 0002 is a
test, it is optional.
It is a regression from 18, but a narrow one, so I will not set the open
items list.
Thanks,
Shihao
Attachments:
t254071_1v1-0001-plpgsql-fix-SELECT-INTO-when-the-expression-is-no.patchapplication/octet-stream; name=v1-0001-plpgsql-fix-SELECT-INTO-when-the-expression-is-no.patchDownload+23-16
v1-0002-plpgsql-test-SELECT-INTO-after-a-function-becomes.patchapplication/octet-stream; name=v1-0002-plpgsql-test-SELECT-INTO-after-a-function-becomes.patchDownload+65-1
shihao zhong <zhong950419@gmail.com> writes:
I used Opus to analyze the new features in PG19, and it found a problem
with the "SELECT simple-expression INTO var" fast path from ce8d5fe0e28.
Good catch, and I concur with your fix. While looking at it
I also realized that the bit with
if (expr->plan == NULL)
exec_prepare_plan(estate, expr, 0);
is quite useless: we dealt with expr->plan == NULL a few lines
earlier, so this if-test can never succeed. Pushed with that
change.
regards, tom lane
Hi Tom,
is quite useless: we dealt with expr->plan == NULL a few lines
earlier, so this if-test can never succeed. Pushed with that
change.
Thanks for pushing, and for the quick look. Agreed on the plan check.
Thanks,
Shihao