Fix UPDATE/DELETE ... WHERE CURRENT OF on a table with virtual columns.

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
This commit is contained in:
Dean Rasheed 2026-04-22 11:50:17 +01:00
parent 7834251758
commit 5548a969b6
6 changed files with 77 additions and 19 deletions

View file

@ -595,6 +595,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;
@ -2868,6 +2876,14 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt)
true,
ACL_UPDATE);
/* 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"));
if (stmt->forPortionOf)
qry->forPortionOf = transformForPortionOfClause(pstate,
qry->resultRelation,

View file

@ -1514,25 +1514,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 */

View file

@ -1805,3 +1805,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;

View 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)

View file

@ -954,3 +954,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;

View 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;