]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Fix UPDATE/DELETE ... WHERE CURRENT OF on a table with virtual columns.
authorDean Rasheed <dean.a.rasheed@gmail.com>
Wed, 22 Apr 2026 10:50:18 +0000 (11:50 +0100)
committerDean Rasheed <dean.a.rasheed@gmail.com>
Wed, 22 Apr 2026 10:50:18 +0000 (11:50 +0100)
Formerly, attempting to use WHERE CURRENT OF to update or delete from
a table with virtual generated columns would fail with the error
"WHERE CURRENT OF on a view is not implemented".

The reason was that the check preventing WHERE CURRENT OF from being
used on a view was in replace_rte_variables_mutator(), which presumed
that the only way it could get there was as part of rewriting a query
on a view. That is no longer the case, since replace_rte_variables()
is now also used to expand the virtual generated columns of a table.

Fix by doing the check for WHERE CURRENT OF on a view at parse time.
This is safe, since it is no longer possible for the relkind to change
after the query is parsed (as of b23cd185f).

Reported-by: Satyanarayana Narlapuram <satyanarlapuram@gmail.com>
Author: Satyanarayana Narlapuram <satyanarlapuram@gmail.com>
Author: Dean Rasheed <dean.a.rasheed@gmail.com>
Discussion: https://postgr.es/m/CAHg+QDc_TwzSgb=B_QgNLt3mvZdmRK23rLb+RkanSQkDF40GjA@mail.gmail.com
Backpatch-through: 18

src/backend/parser/analyze.c
src/backend/rewrite/rewriteManip.c
src/test/regress/expected/generated_virtual.out
src/test/regress/expected/portals.out
src/test/regress/sql/generated_virtual.sql
src/test/regress/sql/portals.sql

index 0bcdeabd9ab729ad77aa6c054d346cc6c1c1dd38..2fec9ce810656e775e45314d607519d04faf4826 100644 (file)
@@ -573,6 +573,14 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt)
                                                                                 ACL_DELETE);
        nsitem = pstate->p_target_nsitem;
 
+       /* disallow DELETE ... WHERE CURRENT OF on a view */
+       if (stmt->whereClause &&
+               IsA(stmt->whereClause, CurrentOfExpr) &&
+               pstate->p_target_relation->rd_rel->relkind == RELKIND_VIEW)
+               ereport(ERROR,
+                               errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                               errmsg("WHERE CURRENT OF on a view is not implemented"));
+
        /* there's no DISTINCT in DELETE */
        qry->distinctClause = NIL;
 
@@ -2484,6 +2492,14 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt)
                                                                                 ACL_UPDATE);
        nsitem = pstate->p_target_nsitem;
 
+       /* disallow UPDATE ... WHERE CURRENT OF on a view */
+       if (stmt->whereClause &&
+               IsA(stmt->whereClause, CurrentOfExpr) &&
+               pstate->p_target_relation->rd_rel->relkind == RELKIND_VIEW)
+               ereport(ERROR,
+                               errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                               errmsg("WHERE CURRENT OF on a view is not implemented"));
+
        /* subqueries in FROM cannot access the result relation */
        nsitem->p_lateral_only = true;
        nsitem->p_lateral_ok = false;
index 781ab862b67ecb2f24e30a7546bbb8ff3117867e..2e75f286b8b6d3e7f35980184bd14a5ba2b84158 100644 (file)
@@ -1513,25 +1513,6 @@ replace_rte_variables_mutator(Node *node,
                }
                /* otherwise fall through to copy the var normally */
        }
-       else if (IsA(node, CurrentOfExpr))
-       {
-               CurrentOfExpr *cexpr = (CurrentOfExpr *) node;
-
-               if (cexpr->cvarno == context->target_varno &&
-                       context->sublevels_up == 0)
-               {
-                       /*
-                        * We get here if a WHERE CURRENT OF expression turns out to apply
-                        * to a view.  Someday we might be able to translate the
-                        * expression to apply to an underlying table of the view, but
-                        * right now it's not implemented.
-                        */
-                       ereport(ERROR,
-                                       (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                                        errmsg("WHERE CURRENT OF on a view is not implemented")));
-               }
-               /* otherwise fall through to copy the expr normally */
-       }
        else if (IsA(node, Query))
        {
                /* Recurse into RTE subquery or not-yet-planned sublink subquery */
index 72da55a90bf19deb8bd9ea24d085b66776b64966..171d68ba2e251db31f955100b0568ee17637e810 100644 (file)
@@ -1740,3 +1740,33 @@ insert into gtest34p values (1, 2)
 (1 row)
 
 drop table gtest34p;
+-- Ensure that virtual generated columns work with WHERE CURRENT OF
+create table gtest_cursor (id int primary key, a int, b int generated always as (a * 2) virtual);
+insert into gtest_cursor values (1, 10), (2, 20), (3, 30);
+begin;
+declare curs cursor for select * from gtest_cursor order by id for update;
+fetch 1 from curs;
+ id | a  | b  
+----+----+----
+  1 | 10 | 20
+(1 row)
+
+update gtest_cursor set a = 99 where current of curs;
+select * from gtest_cursor order by id;
+ id | a  |  b  
+----+----+-----
+  1 | 99 | 198
+  2 | 20 |  40
+  3 | 30 |  60
+(3 rows)
+
+delete from gtest_cursor where current of curs;
+select * from gtest_cursor order by id;
+ id | a  | b  
+----+----+----
+  2 | 20 | 40
+  3 | 30 | 60
+(2 rows)
+
+commit;
+drop table gtest_cursor;
index 06726ed4ab7ae687539643977f8d9df7356e6e0e..c1fbfa105efa4d0dc5a32f6fd8aefb2d2e38b6e2 100644 (file)
@@ -1336,6 +1336,17 @@ FETCH FROM c1;
 DELETE FROM ucview WHERE CURRENT OF c1; -- fail, views not supported
 ERROR:  WHERE CURRENT OF on a view is not implemented
 ROLLBACK;
+BEGIN;
+DECLARE c1 CURSOR FOR SELECT * FROM ucview;
+FETCH FROM c1;
+ f1 |  f2   
+----+-------
+ 13 | three
+(1 row)
+
+UPDATE ucview SET f1 = f1 + 10 WHERE CURRENT OF c1; -- fail, views not supported
+ERROR:  WHERE CURRENT OF on a view is not implemented
+ROLLBACK;
 -- Check WHERE CURRENT OF with an index-only scan
 BEGIN;
 EXPLAIN (costs off)
index 01af4ae0e34a3ac4dee39241142ef602559fcf49..0ba8c626047c1a1fd44ccf79d0e943089c0609fa 100644 (file)
@@ -929,3 +929,18 @@ insert into gtest34p values (1, 7)
 insert into gtest34p values (1, 2)
     on conflict (id) do update set a = gtest34p.c + excluded.c returning *;
 drop table gtest34p;
+
+-- Ensure that virtual generated columns work with WHERE CURRENT OF
+create table gtest_cursor (id int primary key, a int, b int generated always as (a * 2) virtual);
+insert into gtest_cursor values (1, 10), (2, 20), (3, 30);
+
+begin;
+declare curs cursor for select * from gtest_cursor order by id for update;
+fetch 1 from curs;
+update gtest_cursor set a = 99 where current of curs;
+select * from gtest_cursor order by id;
+delete from gtest_cursor where current of curs;
+select * from gtest_cursor order by id;
+commit;
+
+drop table gtest_cursor;
index fc4cccb96c0fe93eeba4d49abdb387884c180f3c..196b862c7560dd14a84aae684ead9b12d5e4fea4 100644 (file)
@@ -508,6 +508,11 @@ DECLARE c1 CURSOR FOR SELECT * FROM ucview;
 FETCH FROM c1;
 DELETE FROM ucview WHERE CURRENT OF c1; -- fail, views not supported
 ROLLBACK;
+BEGIN;
+DECLARE c1 CURSOR FOR SELECT * FROM ucview;
+FETCH FROM c1;
+UPDATE ucview SET f1 = f1 + 10 WHERE CURRENT OF c1; -- fail, views not supported
+ROLLBACK;
 
 -- Check WHERE CURRENT OF with an index-only scan
 BEGIN;