]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Forbid FOR PORTION OF with WHERE CURRENT OF
authorPeter Eisentraut <peter@eisentraut.org>
Mon, 29 Jun 2026 13:13:45 +0000 (15:13 +0200)
committerPeter Eisentraut <peter@eisentraut.org>
Mon, 29 Jun 2026 13:17:10 +0000 (15:17 +0200)
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 <update statement: searched> and <delete
statement: searched>, not <update statement: positioned> or <delete
statement: positioned>, so it is easy for us to exclude the
functionality, at least for now.

Author: Paul A. Jungwirth <pj@illuminatedcomputing.com>
Discussion: https://www.postgresql.org/message-id/flat/CA%2BrenyUEKPexUYsH4qeU8_o1jqKsUkEWca1keS6n21shgG1g%2BA%40mail.gmail.com

doc/src/sgml/ref/delete.sgml
doc/src/sgml/ref/update.sgml
src/backend/parser/analyze.c
src/test/regress/expected/for_portion_of.out
src/test/regress/sql/for_portion_of.sql

index 9066d7ea83df8ea985f7c39d60ca6b07b5a45c5a..ffdcd7fc4fafa6bc6534779a2d01f2f2474e1858 100644 (file)
@@ -231,10 +231,9 @@ DELETE FROM [ ONLY ] <replaceable class="parameter">table_name</replaceable> [ *
       from this cursor.  The cursor must be a non-grouping
       query on the <command>DELETE</command>'s target table.
       Note that <literal>WHERE CURRENT OF</literal> cannot be
-      specified together with a Boolean condition.  See
-      <xref linkend="sql-declare"/>
-      for more information about using cursors with
-      <literal>WHERE CURRENT OF</literal>.
+      specified together with a Boolean condition or <literal>FOR PORTION
+      OF</literal>.  See <xref linkend="sql-declare"/> for more information
+      about using cursors with <literal>WHERE CURRENT OF</literal>.
      </para>
     </listitem>
    </varlistentry>
index dd57bead90cdb244b2d3a18b6aeca2a170705267..21a8fd8b0374a1aa47762e2f66113c83ae90a318 100644 (file)
@@ -287,10 +287,9 @@ UPDATE [ ONLY ] <replaceable class="parameter">table_name</replaceable> [ * ]
       from this cursor.  The cursor must be a non-grouping
       query on the <command>UPDATE</command>'s target table.
       Note that <literal>WHERE CURRENT OF</literal> cannot be
-      specified together with a Boolean condition.  See
-      <xref linkend="sql-declare"/>
-      for more information about using cursors with
-      <literal>WHERE CURRENT OF</literal>.
+      specified together with a Boolean condition or <literal>FOR PORTION
+      OF</literal>.  See <xref linkend="sql-declare"/> for more information
+      about using cursors with <literal>WHERE CURRENT OF</literal>.
      </para>
     </listitem>
    </varlistentry>
index dc65a505c16572ee749e27cf4bb9acbb8694dbde..2932d17a107b89bafdcc923462a64b41aaf6c27e 100644 (file)
@@ -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,12 @@ 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"));
+
        result = makeNode(ForPortionOfExpr);
 
        /* Look up the FOR PORTION OF name requested. */
@@ -2875,6 +2884,7 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt)
                qry->forPortionOf = transformForPortionOfClause(pstate,
                                                                                                                qry->resultRelation,
                                                                                                                stmt->forPortionOf,
+                                                                                                               stmt->whereClause,
                                                                                                                true);
 
        nsitem = pstate->p_target_nsitem;
index 43408972117711f6eb9bc1a7207ea202d81d310c..207e370627e2daee8c6bee2e12015a388216cd4d 100644 (file)
@@ -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;
index 7b08f8cf45e14ae920511b6d2f72f786ff440035..a3c41abf7b7dde308345bd151cbf8eacc21dace7 100644 (file)
@@ -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;