From 7d05761a20f3b2ece49c9b8b7e7af0acd236f801 Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Tue, 3 Nov 2026 18:17:21 +0530 Subject: [PATCH v4 2/2] plpgsql: Apply the too_many_rows check to EXECUTE ... INTO. EXECUTE ... INTO without STRICT silently discarded all but the first result row even when plpgsql.extra_warnings or plpgsql.extra_errors included too_many_rows. Although the documented check was not limited to static SQL, dynamic SQL only checked for multiple rows when STRICT was specified. Honor the too_many_rows setting for dynamic SQL as well, reporting a warning or error as configured. STRICT continues to require exactly one row regardless of these settings. Do not backpatch, since enabling this check can cause previously successful statements to fail. Author: Jeevan Chalke Reviewed-by: Pavel Stehule Reviewed-by: Fujii Masao Discussion: https://postgr.es/m/CAM2+6=WikOK3t=LN+jhzNBchrO3uy0tzVPuPygnz9yoUTwnb6Q@mail.gmail.com --- src/pl/plpgsql/src/pl_exec.c | 22 ++++++++++++++++------ src/test/regress/expected/plpgsql.out | 20 ++++++++++++++++++++ src/test/regress/sql/plpgsql.sql | 14 ++++++++++++++ 3 files changed, 50 insertions(+), 6 deletions(-) diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index 7fc35033aad..7540c8c565e 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -4580,6 +4580,12 @@ exec_stmt_dynexecute(PLpgSQL_execstate *estate, ParamListInfo paramLI; SPIExecuteOptions options; MemoryContext stmt_mcontext = get_stmt_mcontext(estate); + int too_many_rows_level = 0; + + if (plpgsql_extra_errors & PLPGSQL_XCHECK_TOOMANYROWS) + too_many_rows_level = ERROR; + else if (plpgsql_extra_warnings & PLPGSQL_XCHECK_TOOMANYROWS) + too_many_rows_level = WARNING; /* * First we evaluate the string expression after the EXECUTE keyword. Its @@ -4687,9 +4693,9 @@ exec_stmt_dynexecute(PLpgSQL_execstate *estate, target = (PLpgSQL_variable *) estate->datums[stmt->target->dno]; /* - * If SELECT ... INTO specified STRICT, and the query didn't find - * exactly one row, throw an error. If STRICT was not specified, then - * allow the query to find any number of rows. + * If STRICT was specified, require exactly one row. Otherwise, allow + * any number of rows, but report a warning or error for multiple rows + * if the too_many_rows extra check is enabled. */ if (n == 0) { @@ -4712,19 +4718,23 @@ exec_stmt_dynexecute(PLpgSQL_execstate *estate, } else { - if (n > 1 && stmt->strict) + if (n > 1 && (stmt->strict || too_many_rows_level)) { char *errdetail; + int errlevel; if (estate->func->print_strict_params) errdetail = format_preparedparamsdata(estate, paramLI); else errdetail = NULL; - ereport(ERROR, + errlevel = stmt->strict ? ERROR : too_many_rows_level; + + ereport(errlevel, (errcode(ERRCODE_TOO_MANY_ROWS), errmsg("query returned more than one row"), - errdetail ? errdetail_internal("parameters: %s", errdetail) : 0)); + errdetail ? errdetail_internal("parameters: %s", errdetail) : 0, + errhint("Make sure the query returns a single row, for example by using LIMIT 1 with SELECT."))); } /* Put the first result row into the target */ diff --git a/src/test/regress/expected/plpgsql.out b/src/test/regress/expected/plpgsql.out index 55325ea3b87..8f3e214bb42 100644 --- a/src/test/regress/expected/plpgsql.out +++ b/src/test/regress/expected/plpgsql.out @@ -2841,6 +2841,7 @@ begin end$$ language plpgsql; select stricttest(); ERROR: query returned more than one row +HINT: Make sure the query returns a single row, for example by using LIMIT 1 with SELECT. CONTEXT: PL/pgSQL function stricttest() line 5 at EXECUTE drop function stricttest(); -- test printing parameters after failure due to STRICT @@ -2920,6 +2921,7 @@ end$$ language plpgsql; select stricttest(); ERROR: query returned more than one row DETAIL: parameters: $1 = '1' +HINT: Make sure the query returns a single row, for example by using LIMIT 1 with SELECT. CONTEXT: PL/pgSQL function stricttest() line 5 at EXECUTE create or replace function stricttest() returns void as $$ declare x record; @@ -2930,6 +2932,7 @@ begin end$$ language plpgsql; select stricttest(); ERROR: query returned more than one row +HINT: Make sure the query returns a single row, for example by using LIMIT 1 with SELECT. CONTEXT: PL/pgSQL function stricttest() line 5 at EXECUTE create or replace function stricttest() returns void as $$ -- override the global @@ -3097,6 +3100,14 @@ end; $$; WARNING: query returned more than one row HINT: Make sure the query returns a single row, for example by using LIMIT 1 with SELECT. +do $$ +declare x int; +begin + execute 'select v from generate_series(1,2) g(v)' into x; +end; +$$; +WARNING: query returned more than one row +HINT: Make sure the query returns a single row, for example by using LIMIT 1 with SELECT. set plpgsql.extra_errors to 'too_many_rows'; do $$ declare x int; @@ -3107,6 +3118,15 @@ $$; ERROR: query returned more than one row HINT: Make sure the query returns a single row, for example by using LIMIT 1 with SELECT. CONTEXT: PL/pgSQL function inline_code_block line 4 at SQL statement +do $$ +declare x int; +begin + execute 'select v from generate_series(1,2) g(v)' into x; +end; +$$; +ERROR: query returned more than one row +HINT: Make sure the query returns a single row, for example by using LIMIT 1 with SELECT. +CONTEXT: PL/pgSQL function inline_code_block line 4 at EXECUTE reset plpgsql.extra_errors; reset plpgsql.extra_warnings; set plpgsql.extra_warnings to 'strict_multi_assignment'; diff --git a/src/test/regress/sql/plpgsql.sql b/src/test/regress/sql/plpgsql.sql index ae6b67e3e22..9d8de5aaaf0 100644 --- a/src/test/regress/sql/plpgsql.sql +++ b/src/test/regress/sql/plpgsql.sql @@ -2628,6 +2628,13 @@ begin end; $$; +do $$ +declare x int; +begin + execute 'select v from generate_series(1,2) g(v)' into x; +end; +$$; + set plpgsql.extra_errors to 'too_many_rows'; do $$ @@ -2637,6 +2644,13 @@ begin end; $$; +do $$ +declare x int; +begin + execute 'select v from generate_series(1,2) g(v)' into x; +end; +$$; + reset plpgsql.extra_errors; reset plpgsql.extra_warnings; -- 2.56.0