From e3199f3149e77d42ef799858ba3f8b343db09def Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Mon, 5 Oct 2026 18:33:18 -0400 Subject: [PATCH] Fix "SELECT simple_expression INTO" for expression becoming non-simple. Commit ce8d5fe0e failed to think hard enough about the case where we're forced to replan the expression and it becomes no longer simple. The code produced the right expression result, but its handling of FOUND and ROW_COUNT was too simplistic since we might have obtained zero rows. To fix, don't use exec_eval_expr, but go directly to exec_eval_simple_expr, and if that reports failure then fall through to the full SPI code path. Another thinko in ce8d5fe0e is that it added if (expr->plan == NULL) exec_prepare_plan(estate, expr, 0); which is quite useless since we cannot reach here without having planned the expression at least once. (If the call were reachable it'd likely be wrong, since it's not passing the same flags as either the preceding call or the one inside exec_eval_expr.) Author: shihao zhong Reviewed-by: Tom Lane Discussion: https://postgr.es/m/CAGRkXqQB_aqWU9OeVPGvATBVZ7c9EYrNXJyHG0jKXW0FGAy2-g@mail.gmail.com Backpatch-through: 19 --- src/pl/plpgsql/src/pl_exec.c | 45 +++++++++++++++++++++--------------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index 341beb496b8..bc1667f7a13 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -4307,11 +4307,13 @@ exec_stmt_execsql(PLpgSQL_execstate *estate, if (row->nfields == 1) { + /* OK, there is a single target variable */ ErrorContextCallback plerrcontext; Datum value; bool isnull; Oid valtype; int32 valtypmod; + bool simple; /* * Setup error traceback support for ereport(). This is so @@ -4323,13 +4325,15 @@ exec_stmt_execsql(PLpgSQL_execstate *estate, plerrcontext.previous = error_context_stack; error_context_stack = &plerrcontext; - /* If first time through, create a plan for this expression */ - if (expr->plan == NULL) - exec_prepare_plan(estate, expr, 0); - - /* And evaluate the expression */ - value = exec_eval_expr(estate, expr, - &isnull, &valtype, &valtypmod); + /* + * Evaluate the already-planned expression. This fails if the + * expression is busy, or if replanning occurs and we find it + * is no longer simple. In such cases, fall through to the + * SPI code below. + */ + simple = exec_eval_simple_expr(estate, expr, + &value, &isnull, + &valtype, &valtypmod); /* * Pop the error context stack: the code below would not use @@ -4337,20 +4341,23 @@ exec_stmt_execsql(PLpgSQL_execstate *estate, */ error_context_stack = plerrcontext.previous; - /* Assign the result to the INTO target */ - exec_assign_value(estate, estate->datums[row->varnos[0]], - value, isnull, valtype, valtypmod); - exec_eval_cleanup(estate); + if (simple) + { + /* Success; assign the result to the INTO target */ + exec_assign_value(estate, estate->datums[row->varnos[0]], + value, isnull, valtype, valtypmod); + exec_eval_cleanup(estate); - /* - * We must duplicate the other effects of the code below, as - * well. We know that exactly one row was returned, so it - * doesn't matter whether the INTO was STRICT or not. - */ - exec_set_found(estate, true); - estate->eval_processed = 1; + /* + * We must duplicate the other effects of the code below, + * as well. We know that exactly one row was returned, so + * it doesn't matter whether the INTO was STRICT or not. + */ + exec_set_found(estate, true); + estate->eval_processed = 1; - return PLPGSQL_RC_OK; + return PLPGSQL_RC_OK; + } } } }