From d42ef75a39a24fd9667f0a48ef510064f5472880 Mon Sep 17 00:00:00 2001 From: "Paul A. Jungwirth" Date: Tue, 16 Jun 2026 21:23:24 -0700 Subject: [PATCH v1] Forbid FOR PORTION OF with WHERE CURRENT OF It is not clear how the implicit condition of FOR PORTION OF should interact with the use of a cursor. Normally we forbid combining WHERE CURRENT OF with other WHERE conditions. The SQL standard only includes FOR PORTION OF with and , not or , so it is easy for us to exclude the functionality, at least for now. --- doc/src/sgml/ref/delete.sgml | 7 ++-- doc/src/sgml/ref/update.sgml | 7 ++-- src/backend/parser/analyze.c | 11 +++++ src/test/regress/expected/for_portion_of.out | 42 ++++++++++++++++++++ src/test/regress/sql/for_portion_of.sql | 28 +++++++++++++ 5 files changed, 87 insertions(+), 8 deletions(-) diff --git a/doc/src/sgml/ref/delete.sgml b/doc/src/sgml/ref/delete.sgml index 9066d7ea83d..ffdcd7fc4fa 100644 --- a/doc/src/sgml/ref/delete.sgml +++ b/doc/src/sgml/ref/delete.sgml @@ -231,10 +231,9 @@ DELETE FROM [ ONLY ] table_name [ * from this cursor. The cursor must be a non-grouping query on the DELETE's target table. Note that WHERE CURRENT OF cannot be - specified together with a Boolean condition. See - - for more information about using cursors with - WHERE CURRENT OF. + specified together with a Boolean condition or FOR PORTION + OF. See for more information + about using cursors with WHERE CURRENT OF. diff --git a/doc/src/sgml/ref/update.sgml b/doc/src/sgml/ref/update.sgml index dd57bead90c..21a8fd8b037 100644 --- a/doc/src/sgml/ref/update.sgml +++ b/doc/src/sgml/ref/update.sgml @@ -287,10 +287,9 @@ UPDATE [ ONLY ] table_name [ * ] from this cursor. The cursor must be a non-grouping query on the UPDATE's target table. Note that WHERE CURRENT OF cannot be - specified together with a Boolean condition. See - - for more information about using cursors with - WHERE CURRENT OF. + specified together with a Boolean condition or FOR PORTION + OF. See for more information + about using cursors with WHERE CURRENT OF. diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index 93fa66ae57c..7caf11bfb94 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -81,6 +81,7 @@ static OnConflictExpr *transformOnConflictClause(ParseState *pstate, static ForPortionOfExpr *transformForPortionOfClause(ParseState *pstate, int rtindex, const ForPortionOfClause *forPortionOf, + const Node *whereClause, bool isUpdate); static int count_rowexpr_columns(ParseState *pstate, Node *expr); static Query *transformSelectStmt(ParseState *pstate, SelectStmt *stmt, @@ -626,6 +627,7 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt) qry->forPortionOf = transformForPortionOfClause(pstate, qry->resultRelation, stmt->forPortionOf, + stmt->whereClause, false); qual = transformWhereClause(pstate, stmt->whereClause, @@ -1319,6 +1321,7 @@ static ForPortionOfExpr * transformForPortionOfClause(ParseState *pstate, int rtindex, const ForPortionOfClause *forPortionOf, + const Node *whereClause, bool isUpdate) { Relation targetrel = pstate->p_target_relation; @@ -1335,6 +1338,13 @@ transformForPortionOfClause(ParseState *pstate, ForPortionOfExpr *result; Var *rangeVar; + /* disallow FOR PORTION OF ... WHERE CURRENT OF */ + if (whereClause && + IsA(whereClause, CurrentOfExpr)) + ereport(ERROR, + errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("WHERE CURRENT OF with FOR PORTION OF is not implemented")); + /* We don't support FOR PORTION OF FDW queries. */ if (targetrel->rd_rel->relkind == RELKIND_FOREIGN_TABLE) ereport(ERROR, @@ -2884,6 +2894,7 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt) qry->forPortionOf = transformForPortionOfClause(pstate, qry->resultRelation, stmt->forPortionOf, + stmt->whereClause, true); nsitem = pstate->p_target_nsitem; diff --git a/src/test/regress/expected/for_portion_of.out b/src/test/regress/expected/for_portion_of.out index 43408972117..207e370627e 100644 --- a/src/test/regress/expected/for_portion_of.out +++ b/src/test/regress/expected/for_portion_of.out @@ -2446,4 +2446,46 @@ NOTICE: fpo_before_row1: BEFORE UPDATE ROW: NOTICE: old: [10,100) NOTICE: new: [30,70) DROP TABLE fpo_update_of_trigger; +-- CURSORs +CREATE TABLE fpo_cursed ( + id int, + valid_at int4range +); +INSERT INTO fpo_cursed (id, valid_at) VALUES (1, '[10,100)'); +-- UPDATE FOR PORTION OF is not permitted with a CURSOR: +BEGIN; +DECLARE fpo_cur CURSOR FOR SELECT * FROM fpo_cursed; +FETCH NEXT FROM fpo_cur; + id | valid_at +----+---------- + 1 | [10,100) +(1 row) + +UPDATE fpo_cursed + FOR PORTION OF valid_at FROM 5 TO 6 + SET id = 2 + WHERE CURRENT OF fpo_cur; +ERROR: WHERE CURRENT OF with FOR PORTION OF is not implemented +ROLLBACK; +-- DELETE FOR PORTION OF is not permitted with a CURSOR: +BEGIN; +DECLARE fpo_cur CURSOR FOR SELECT * FROM fpo_cursed; +FETCH NEXT FROM fpo_cur; + id | valid_at +----+---------- + 1 | [10,100) +(1 row) + +DELETE FROM fpo_cursed + FOR PORTION OF valid_at FROM 8 TO 9 + WHERE CURRENT OF fpo_cur; +ERROR: WHERE CURRENT OF with FOR PORTION OF is not implemented +ROLLBACK; +SELECT * FROM fpo_cursed; + id | valid_at +----+---------- + 1 | [10,100) +(1 row) + +DROP TABLE fpo_cursed; RESET datestyle; diff --git a/src/test/regress/sql/for_portion_of.sql b/src/test/regress/sql/for_portion_of.sql index 7b08f8cf45e..a3c41abf7b7 100644 --- a/src/test/regress/sql/for_portion_of.sql +++ b/src/test/regress/sql/for_portion_of.sql @@ -1591,4 +1591,32 @@ UPDATE fpo_update_of_trigger SET id = 2; DROP TABLE fpo_update_of_trigger; +-- CURSORs +CREATE TABLE fpo_cursed ( + id int, + valid_at int4range +); +INSERT INTO fpo_cursed (id, valid_at) VALUES (1, '[10,100)'); + +-- UPDATE FOR PORTION OF is not permitted with a CURSOR: +BEGIN; +DECLARE fpo_cur CURSOR FOR SELECT * FROM fpo_cursed; +FETCH NEXT FROM fpo_cur; +UPDATE fpo_cursed + FOR PORTION OF valid_at FROM 5 TO 6 + SET id = 2 + WHERE CURRENT OF fpo_cur; +ROLLBACK; + +-- DELETE FOR PORTION OF is not permitted with a CURSOR: +BEGIN; +DECLARE fpo_cur CURSOR FOR SELECT * FROM fpo_cursed; +FETCH NEXT FROM fpo_cur; +DELETE FROM fpo_cursed + FOR PORTION OF valid_at FROM 8 TO 9 + WHERE CURRENT OF fpo_cur; +ROLLBACK; +SELECT * FROM fpo_cursed; +DROP TABLE fpo_cursed; + RESET datestyle; -- 2.45.0