From 5f4acf427b443dd85b1cdfe2654d6714a95821e5 Mon Sep 17 00:00:00 2001
From: "Paul A. Jungwirth" <pj@illuminatedcomputing.com>
Date: Fri, 15 May 2026 13:43:10 -0700
Subject: [PATCH v8 2/2] Move FOR PORTION OF volatile check into planner

Like checking for GENERATED columns, this needs to be wary of the function
volatility changing after we check it. It's easy to do that if FOR PORTION OF
appears inside a BEGIN ATOMIC SQL function.
---
 src/backend/optimizer/plan/planner.c | 11 +++++++++++
 src/backend/parser/analyze.c         |  3 ---
 2 files changed, 11 insertions(+), 3 deletions(-)

diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index f4689e7c9f8..df6ac74ffa9 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -1081,6 +1081,17 @@ subquery_planner(PlannerGlobal *glob, Query *parse, char *plan_name,
 		/* exclRelTlist contains only Vars, so no preprocessing needed */
 	}
 
+	if (parse->forPortionOf)
+	{
+		parse->forPortionOf->targetRange =
+			preprocess_expression(root,
+								  parse->forPortionOf->targetRange,
+								  EXPRKIND_TARGET);
+		if (contain_volatile_functions(parse->forPortionOf->targetRange))
+			ereport(ERROR,
+					(errmsg("FOR PORTION OF bounds cannot contain volatile functions")));
+	}
+
 	foreach(l, parse->mergeActionList)
 	{
 		MergeAction *action = (MergeAction *) lfirst(l);
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index 04bae9939b9..285c9817b1c 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -1483,9 +1483,6 @@ transformForPortionOfClause(ParseState *pstate,
 													args,
 													InvalidOid, InvalidOid, COERCE_EXPLICIT_CALL);
 	}
-	if (contain_volatile_functions_after_planning((Expr *) result->targetRange))
-		ereport(ERROR,
-				(errmsg("FOR PORTION OF bounds cannot contain volatile functions")));
 
 	/*
 	 * Build overlapsExpr to use as an extra qual. This means we only hit rows
-- 
2.47.3

