From f11c95298ac3c763a65253847af9cacf787878f1 Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Fri, 30 Oct 2026 14:56:21 +0530 Subject: [PATCH v1] Apply the too_many_rows extra check to EXECUTE ... INTO, too. plpgsql.extra_warnings/extra_errors = 'too_many_rows' is documented as checking whether a query returns more than one row when an INTO clause is used, but only static SQL statements were checked; EXECUTE ... INTO silently used the first row. Checking costs nothing extra here, since EXECUTE fetches all the rows anyway. While here, add the "Make sure the query returns a single row, or use LIMIT 1." hint to the EXECUTE ... INTO STRICT error, so that it matches the error for static SQL. --- src/pl/plpgsql/src/pl_exec.c | 19 +++++++++++++++---- src/test/regress/expected/plpgsql.out | 20 ++++++++++++++++++++ src/test/regress/sql/plpgsql.sql | 14 ++++++++++++++ 3 files changed, 49 insertions(+), 4 deletions(-) diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index e857daa6745..62fbcc909dd 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -4564,6 +4564,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 @@ -4673,7 +4679,8 @@ exec_stmt_dynexecute(PLpgSQL_execstate *estate, /* * 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. + * allow the query to find any number of rows, except that the + * too_many_rows extra check may complain about more than one. */ if (n == 0) { @@ -4696,19 +4703,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, or use LIMIT 1."))); } /* 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 b37b2abaf80..21e597c014d 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, or use LIMIT 1. 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, or use LIMIT 1. 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, or use LIMIT 1. 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, or use LIMIT 1. +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, or use LIMIT 1. 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, or use LIMIT 1. 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, or use LIMIT 1. +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.43.0