From 1af091f1a54c1cc8c6b63af307192e3dd554e0b2 Mon Sep 17 00:00:00 2001
From: shihao zhong <zhong950419@gmail.com>
Date: Mon, 5 Oct 2026 11:38:02 -0400
Subject: [PATCH v1 1/2] plpgsql: fix SELECT INTO when the expression is no
 longer simple

The "SELECT simple-expression INTO var" path added by ce8d5fe0e28
calls exec_eval_expr(), which runs the query through SPI when
replanning finds it is no longer simple.  FOUND and ROW_COUNT were
then set as if one row came back.  Call exec_eval_simple_expr()
directly and fall through to the regular code when it fails.
---
 src/pl/plpgsql/src/pl_exec.c | 38 ++++++++++++++++++++++--------------
 1 file changed, 23 insertions(+), 15 deletions(-)

diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index 341beb496b8..c641c25d98d 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -4312,6 +4312,7 @@ exec_stmt_execsql(PLpgSQL_execstate *estate,
 				bool		isnull;
 				Oid			valtype;
 				int32		valtypmod;
+				bool		simple;
 
 				/*
 				 * Setup error traceback support for ereport().  This is so
@@ -4327,9 +4328,13 @@ exec_stmt_execsql(PLpgSQL_execstate *estate,
 				if (expr->plan == NULL)
 					exec_prepare_plan(estate, expr, 0);
 
-				/* And evaluate the expression */
-				value = exec_eval_expr(estate, expr,
-									   &isnull, &valtype, &valtypmod);
+				/*
+				 * And evaluate the expression.  This fails if the expression
+				 * is busy, or if replanning found it is no longer simple.  In
+				 * that case 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 +4342,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)
+				{
+					/* 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;
+				}
 			}
 		}
 	}
-- 
2.37.1 (Apple Git-137.1)

