On Tue, Apr 21, 2026 at 11:24 PM Tom Lane <[email protected]> wrote:
>
> * contain_volatile_functions_after_planning is utterly wrong
> to apply here. That should happen somewhere in the planner,
> where it'd be cheaper as well as not premature.
>
typedef struct ForPortionOfExpr
{
NodeTag type;
Var *rangeVar; /* Range column */
char *range_name; /* Range name */
Node *targetFrom; /* FOR PORTION OF FROM bound, if given */
Node *targetTo; /* FOR PORTION OF TO bound, if given */
Node *targetRange; /* FOR PORTION OF bounds as a
range/multirange */
Oid rangeType; /* (base)type of targetRange */
bool isDomain; /* Is rangeVar a domain? */
Node *overlapsExpr; /* range && targetRange */
List *rangeTargetList; /* List of TargetEntrys to set the time
* column(s) */
Oid withoutPortionProc; /* SRF proc for old_range -
target_range */
ParseLoc location; /* token location, or -1 if unknown */
ParseLoc targetLocation; /* token location, or -1 if unknown */
} ForPortionOfExpr;
targetFrom and targetTo is only for deparsing purpose, skip
eval_const_expressions should be fine.
RewriteQuery, we have:
``````
AddQual(parsetree, parsetree->forPortionOf->overlapsExpr);
/* Update FOR PORTION OF column(s) automatically. */
foreach(tl, parsetree->forPortionOf->rangeTargetList)
{
TargetEntry *tle = (TargetEntry *) lfirst(tl);
parsetree->targetList = lappend(parsetree->targetList, tle);
}
``````
rangeTargetList and overlapsExpr will go through eval_const_expressions.
Only ForPortionOfExpr->targetRange really needs to be dealt with.
In ExecInitModifyTable, we already did
ExecPrepareExpr(forPortionOf->targetRange),
which will do eval_const_expressions(forPortionOf->targetRange).
moving contain_volatile_functions_after_planning to
ExecInitModifyTable should be fine.
In ExecInitModifyTable, we can't just
```
exprState = ExecPrepareExpr((Expr *) forPortionOf->targetRange, estate);
if (contain_volatile_functions_after_planning(exprState->expr)
```
Because of the comments below in execnodes.h:
typedef struct ExprState
/* original expression tree, for debugging only */
Expr *expr;
While at it, add errcode to the surrounding code.
--
jian
https://www.enterprisedb.com/
From fc1d68953aae07a0eddd35283ec13eda95160c0c Mon Sep 17 00:00:00 2001
From: jian he <[email protected]>
Date: Fri, 24 Apr 2026 10:55:58 +0800
Subject: [PATCH v6 1/1] FOR PORTION OF move TargetRange check to
ExecInitModifyTable
While at it, add errcode to surrounding code
discussion: https://postgr.es/m/626986.1776785090%40sss.pgh.pa.us
commitfest entry: https://commitfest.postgresql.org/patch/
---
src/backend/executor/nodeModifyTable.c | 6 ++++++
src/backend/parser/analyze.c | 3 ---
2 files changed, 6 insertions(+), 3 deletions(-)
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index 4cb057ca4f9..4c4eff10b20 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -5591,6 +5591,11 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
ExecAssignExprContext(estate, &mtstate->ps);
econtext = mtstate->ps.ps_ExprContext;
+ if (contain_volatile_functions_after_planning((Expr *) forPortionOf->targetRange))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("FOR PORTION OF bounds cannot contain volatile functions"));
+
exprState = ExecPrepareExpr((Expr *) forPortionOf->targetRange, estate);
targetRange = ExecEvalExpr(exprState, econtext, &isNull);
@@ -5600,6 +5605,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
*/
if (isNull)
ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
(errmsg("FOR PORTION OF target was null")),
executor_errposition(estate, forPortionOf->targetLocation));
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index ffcf25a6be7..216fbe2367c 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -1489,9 +1489,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.34.1