PL/pgSQL: apply the too_many_rows extra check to EXECUTE ... INTO

Started by Jeevan Chalke3 days ago4 messageshackers
Beta feature

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.

appliessuccessCI history

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:t254477
psql -h localhost -U postgres

Built from patchset v1 (message #1), November 01, 2026 at 03:53 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 t254477_1 https://github.com/hackorum-dev/postgres.git

In a checkout you already have, add the fork once:

git remote add hackorum https://github.com/hackorum-dev/postgres.git

then, for this patchset and every later one:

git fetch hackorum t254477_1 && git checkout t254477_1

Patchset v1 (message #1) is on t254477_1

Jump to latest
#1Jeevan Chalke
jeevan.chalke@enterprisedb.com

Hi,

The documentation for plpgsql.extra_warnings / plpgsql.extra_errors says
that the too_many_rows check will "*check if a given query returns more
than one row when an INTO clause is used*" *[1]*. Only static SQL
statements implement this (exec_stmt_execsql()). exec_stmt_dynexecute()
only looks at STRICT, so EXECUTE ... INTO silently uses the first row and
nothing is reported:

set plpgsql.extra_warnings = 'too_many_rows';
do $$ declare x int;
begin
execute 'select g from generate_series(1,2) g' into x;
end $$;
DO

The same statement written as static SQL does warn (or fail, with
extra_errors).

The attached patch computes too_many_rows_level in exec_stmt_dynexecute()
the same way exec_stmt_execsql() does, and reports at that level when more
than one row comes back and STRICT was not specified. With it:

WARNING: query returned more than one row
HINT: Make sure the query returns a single row, or use LIMIT 1.
DO

and with plpgsql.extra_errors the same message is raised as an ERROR.
Checking costs nothing extra here, since EXECUTE already fetches all the
rows (no row limit is passed to SPI).

While there, I added the "Make sure the query returns a single row, or use
LIMIT 1." hint to the error raised for EXECUTE ... INTO STRICT, so that it
matches the error for static SQL. This changes the expected output of
three existing STRICT tests, which now show the HINT line.

The new tests are in the "runtime extra checks" section of plpgsql.sql and
cover EXECUTE ... INTO with both extra_warnings and extra_errors.

This is a behavior change: someone who has too_many_rows in extra_errors
may start getting errors from EXECUTE ... INTO statements that used to
pass. So I think this should go to master only (or v19, as it is yet to
release), but I'd like to hear what others think.

Thanks

*[1]
https://www.postgresql.org/docs/current/plpgsql-development-tips.html#PLPGSQL-EXTRA-CHECKS
<https://www.postgresql.org/docs/current/plpgsql-development-tips.html#PLPGSQL-EXTRA-CHECKS&gt;*

--
*Jeevan Chalke*
*Senior Principal Engineer, Engineering Manager*
*Product Development*

enterprisedb.com <https://www.enterprisedb.com&gt;

Attachments:

t254477_1
v1-0001-Apply-the-too_many_rows-extra-check-to-EXECUTE-.-.patchapplication/octet-stream; name=v1-0001-Apply-the-too_many_rows-extra-check-to-EXECUTE-.-.patchDownload+49-5
#2Pavel Stehule
pavel.stehule@gmail.com
In reply to: Jeevan Chalke (#1)
Re: PL/pgSQL: apply the too_many_rows extra check to EXECUTE ... INTO

Hi

pá 30. 10. 2026 v 12:43 odesílatel Jeevan Chalke <
jeevan.chalke@enterprisedb.com> napsal:

Hi,

The documentation for plpgsql.extra_warnings / plpgsql.extra_errors says
that the too_many_rows check will "*check if a given query returns more
than one row when an INTO clause is used*" *[1]*. Only static SQL
statements implement this (exec_stmt_execsql()). exec_stmt_dynexecute()
only looks at STRICT, so EXECUTE ... INTO silently uses the first row and
nothing is reported:

set plpgsql.extra_warnings = 'too_many_rows';
do $$ declare x int;
begin
execute 'select g from generate_series(1,2) g' into x;
end $$;
DO

The same statement written as static SQL does warn (or fail, with
extra_errors).

The attached patch computes too_many_rows_level in exec_stmt_dynexecute()
the same way exec_stmt_execsql() does, and reports at that level when
more than one row comes back and STRICT was not specified. With it:

WARNING: query returned more than one row
HINT: Make sure the query returns a single row, or use LIMIT 1.
DO

and with plpgsql.extra_errors the same message is raised as an ERROR.
Checking costs nothing extra here, since EXECUTE already fetches all the
rows (no row limit is passed to SPI).

While there, I added the "Make sure the query returns a single row, or
use LIMIT 1." hint to the error raised for EXECUTE ... INTO STRICT, so
that it matches the error for static SQL. This changes the expected output
of three existing STRICT tests, which now show the HINT line.

The new tests are in the "runtime extra checks" section of plpgsql.sql and
cover EXECUTE ... INTO with both extra_warnings and extra_errors.

This is a behavior change: someone who has too_many_rows in extra_errors
may start getting errors from EXECUTE ... INTO statements that used to
pass. So I think this should go to master only (or v19, as it is yet to
release), but I'd like to hear what others think.

Yes, this is my omission.

The dynamic SQL is more problematic - it allows multiquery

(2026-10-30 14:24:06) postgres=# do $$
declare r record;
begin
execute 'select 1 union select 2; select 2' into strict r;
raise notice '%', r;
end;
$$;
NOTICE: (2)
DO

Maybe extra errors should handle this case too.

regards

Pavel

Show quoted text

Thanks

*[1]
https://www.postgresql.org/docs/current/plpgsql-development-tips.html#PLPGSQL-EXTRA-CHECKS
<https://www.postgresql.org/docs/current/plpgsql-development-tips.html#PLPGSQL-EXTRA-CHECKS&gt;*

--
*Jeevan Chalke*
*Senior Principal Engineer, Engineering Manager*
*Product Development*

enterprisedb.com <https://www.enterprisedb.com&gt;

#3Jeevan Chalke
jeevan.chalke@enterprisedb.com
In reply to: Pavel Stehule (#2)
Re: PL/pgSQL: apply the too_many_rows extra check to EXECUTE ... INTO

On Fri, Oct 30, 2026 at 6:57 PM Pavel Stehule <pavel.stehule@gmail.com>
wrote:

Hi

pá 30. 10. 2026 v 12:43 odesílatel Jeevan Chalke <
jeevan.chalke@enterprisedb.com> napsal:

Hi,

The documentation for plpgsql.extra_warnings / plpgsql.extra_errors says
that the too_many_rows check will "*check if a given query returns more
than one row when an INTO clause is used*" *[1]*. Only static SQL
statements implement this (exec_stmt_execsql()). exec_stmt_dynexecute()
only looks at STRICT, so EXECUTE ... INTO silently uses the first row
and nothing is reported:

set plpgsql.extra_warnings = 'too_many_rows';
do $$ declare x int;
begin
execute 'select g from generate_series(1,2) g' into x;
end $$;
DO

The same statement written as static SQL does warn (or fail, with
extra_errors).

The attached patch computes too_many_rows_level in exec_stmt_dynexecute()
the same way exec_stmt_execsql() does, and reports at that level when
more than one row comes back and STRICT was not specified. With it:

WARNING: query returned more than one row
HINT: Make sure the query returns a single row, or use LIMIT 1.
DO

and with plpgsql.extra_errors the same message is raised as an ERROR.
Checking costs nothing extra here, since EXECUTE already fetches all the
rows (no row limit is passed to SPI).

While there, I added the "Make sure the query returns a single row, or
use LIMIT 1." hint to the error raised for EXECUTE ... INTO STRICT, so
that it matches the error for static SQL. This changes the expected output
of three existing STRICT tests, which now show the HINT line.

The new tests are in the "runtime extra checks" section of plpgsql.sql and
cover EXECUTE ... INTO with both extra_warnings and extra_errors.

This is a behavior change: someone who has too_many_rows in extra_errors
may start getting errors from EXECUTE ... INTO statements that used to
pass. So I think this should go to master only (or v19, as it is yet to
release), but I'd like to hear what others think.

Yes, this is my omission.

The dynamic SQL is more problematic - it allows multiquery

(2026-10-30 14:24:06) postgres=# do $$
declare r record;
begin
execute 'select 1 union select 2; select 2' into strict r;
raise notice '%', r;
end;
$$;
NOTICE: (2)
DO

Maybe extra errors should handle this case too.

Hi Pavel,

Thanks for looking at this.

I confirmed the multi-statement case. With INTO, only the result of the
last statement is used, so even *STRICT* passes for:

do $$
declare r record;
begin
execute 'select 1 union select 2; select 2' into strict r;
raise notice '%', r;
end;
$$;
NOTICE: (2)

That behavior is independent of this patch, which counts the rows of the
statement whose result is actually used, so I'd prefer to keep this patch
scoped to that.

Seems like a separate feature altogether.

Detecting multiple statements would need a different check, since
exec_stmt_dynexecute() currently hands the query string straight to
SPI_execute_extended() and has no way to see how many statements it
contained.

Thanks

regards

Pavel

Thanks

*[1]
https://www.postgresql.org/docs/current/plpgsql-development-tips.html#PLPGSQL-EXTRA-CHECKS
<https://www.postgresql.org/docs/current/plpgsql-development-tips.html#PLPGSQL-EXTRA-CHECKS&gt;*

--
*Jeevan Chalke*
*Senior Principal Engineer, Engineering Manager*
*Product Development*

enterprisedb.com <https://www.enterprisedb.com&gt;

--
*Jeevan Chalke*
*Senior Principal Engineer, Engineering Manager*
*Product Development*

enterprisedb.com <https://www.enterprisedb.com&gt;

#4Pavel Stehule
pavel.stehule@gmail.com
In reply to: Jeevan Chalke (#3)
Re: PL/pgSQL: apply the too_many_rows extra check to EXECUTE ... INTO

po 2. 11. 2026 v 12:35 odesílatel Jeevan Chalke <
jeevan.chalke@enterprisedb.com> napsal:

On Fri, Oct 30, 2026 at 6:57 PM Pavel Stehule <pavel.stehule@gmail.com>
wrote:

Hi

pá 30. 10. 2026 v 12:43 odesílatel Jeevan Chalke <
jeevan.chalke@enterprisedb.com> napsal:

Hi,

The documentation for plpgsql.extra_warnings / plpgsql.extra_errors
says that the too_many_rows check will "*check if a given query returns
more than one row when an INTO clause is used*" *[1]*. Only static SQL
statements implement this (exec_stmt_execsql()). exec_stmt_dynexecute()
only looks at STRICT, so EXECUTE ... INTO silently uses the first row
and nothing is reported:

set plpgsql.extra_warnings = 'too_many_rows';
do $$ declare x int;
begin
execute 'select g from generate_series(1,2) g' into x;
end $$;
DO

The same statement written as static SQL does warn (or fail, with
extra_errors).

The attached patch computes too_many_rows_level in
exec_stmt_dynexecute() the same way exec_stmt_execsql() does, and
reports at that level when more than one row comes back and STRICT was
not specified. With it:

WARNING: query returned more than one row
HINT: Make sure the query returns a single row, or use LIMIT 1.
DO

and with plpgsql.extra_errors the same message is raised as an ERROR.
Checking costs nothing extra here, since EXECUTE already fetches all
the rows (no row limit is passed to SPI).

While there, I added the "Make sure the query returns a single row, or
use LIMIT 1." hint to the error raised for EXECUTE ... INTO STRICT, so
that it matches the error for static SQL. This changes the expected output
of three existing STRICT tests, which now show the HINT line.

The new tests are in the "runtime extra checks" section of plpgsql.sql and
cover EXECUTE ... INTO with both extra_warnings and extra_errors.

This is a behavior change: someone who has too_many_rows in extra_errors
may start getting errors from EXECUTE ... INTO statements that used to
pass. So I think this should go to master only (or v19, as it is yet to
release), but I'd like to hear what others think.

Yes, this is my omission.

The dynamic SQL is more problematic - it allows multiquery

(2026-10-30 14:24:06) postgres=# do $$
declare r record;
begin
execute 'select 1 union select 2; select 2' into strict r;
raise notice '%', r;
end;
$$;
NOTICE: (2)
DO

Maybe extra errors should handle this case too.

Hi Pavel,

Thanks for looking at this.

I confirmed the multi-statement case. With INTO, only the result of the
last statement is used, so even *STRICT* passes for:

do $$
declare r record;
begin
execute 'select 1 union select 2; select 2' into strict r;
raise notice '%', r;
end;
$$;
NOTICE: (2)

That behavior is independent of this patch, which counts the rows of the
statement whose result is actually used, so I'd prefer to keep this patch
scoped to that.

Seems like a separate feature altogether.

Detecting multiple statements would need a different check, since
exec_stmt_dynexecute() currently hands the query string straight to
SPI_execute_extended() and has no way to see how many statements it
contained.

Yes, it is hard to say what is expected behaviour in the mentioned case.

Maybe it should be documented, so this case is not covered by checks.

Regards

Pavel

Show quoted text

Thanks

regards

Pavel

Thanks

*[1]
https://www.postgresql.org/docs/current/plpgsql-development-tips.html#PLPGSQL-EXTRA-CHECKS
<https://www.postgresql.org/docs/current/plpgsql-development-tips.html#PLPGSQL-EXTRA-CHECKS&gt;*

--
*Jeevan Chalke*
*Senior Principal Engineer, Engineering Manager*
*Product Development*

enterprisedb.com <https://www.enterprisedb.com&gt;

--
*Jeevan Chalke*
*Senior Principal Engineer, Engineering Manager*
*Product Development*

enterprisedb.com <https://www.enterprisedb.com&gt;