From 7c7d5c99f05c4c9ecec0adb3acc6efc5b6cecd85 Mon Sep 17 00:00:00 2001
From: jian he <jian.universality@gmail.com>
Date: Sun, 12 Apr 2026 23:01:31 +0800
Subject: [PATCH v1 1/1] UPDATE FOR PORTION OF interact with updatable view

discussion: https://postgr.es/m/
commitfest entry: https://commitfest.postgresql.org/patch/
---
 src/backend/rewrite/rewriteHandler.c          | 34 +++++++++++++++++++
 src/test/regress/expected/updatable_views.out | 10 +++++-
 src/test/regress/sql/updatable_views.sql      |  7 +++-
 3 files changed, 49 insertions(+), 2 deletions(-)

diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index 12dcb947f63..3cc932ea890 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -3270,6 +3270,31 @@ rewriteTargetView(Query *parsetree, Relation view)
 	view_rte = rt_fetch(parsetree->resultRelation, parsetree->rtable);
 	view_perminfo = getRTEPermissionInfo(parsetree->rteperminfos, view_rte);
 
+
+	if (parsetree->commandType == CMD_DELETE &&
+		parsetree->forPortionOf)
+	{
+		char	   *non_updatable_col;
+		Bitmapset  *modified_cols = NULL;
+
+		modified_cols = bms_add_member(modified_cols,
+									   parsetree->forPortionOf->rangeVar->varattno - FirstLowInvalidHeapAttributeNumber);
+
+		auto_update_detail = view_cols_are_auto_updatable(viewquery,
+														  modified_cols,
+														  NULL,
+														  &non_updatable_col);
+		if (auto_update_detail)
+		{
+			ereport(ERROR,
+					errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+					errmsg("DELETE ... FOR PORTION OF is not supported for column \"%s\" on view \"%s\"",
+						   non_updatable_col,
+						   RelationGetRelationName(view)),
+					errdetail_internal("%s", _(auto_update_detail)));
+		}
+	}
+
 	/*
 	 * Are we doing INSERT/UPDATE, or MERGE containing INSERT/UPDATE?  If so,
 	 * various additional checks on the view columns need to be applied, and
@@ -3333,10 +3358,19 @@ rewriteTargetView(Query *parsetree, Relation view)
 		 * updatable.  But rewriteTargetListIU can also remove entries if they
 		 * are DEFAULT markers and the column's default is NULL, so
 		 * considering only the targetlist would also be wrong.
+		 *
+		 * Since updatedCols doesn't track the modification of the FOR PORTION
+		 * OF column, we must manually add it to modified_cols.
 		 */
 		modified_cols = bms_union(view_perminfo->insertedCols,
 								  view_perminfo->updatedCols);
 
+		if (parsetree->forPortionOf)
+		{
+			modified_cols = bms_add_member(modified_cols,
+										   parsetree->forPortionOf->rangeVar->varattno - FirstLowInvalidHeapAttributeNumber);
+		}
+
 		foreach(lc, parsetree->targetList)
 		{
 			TargetEntry *tle = (TargetEntry *) lfirst(lc);
diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out
index 8852160718f..9a19ac96b69 100644
--- a/src/test/regress/expected/updatable_views.out
+++ b/src/test/regress/expected/updatable_views.out
@@ -3722,7 +3722,7 @@ select * from uv_iocu_tab;
 
 drop view uv_iocu_view;
 drop table uv_iocu_tab;
--- Check UPDATE FOR PORTION OF works correctly
+-- Check UPDATE/DELETE FOR PORTION OF works correctly
 create table uv_fpo_tab (id int4range, valid_at tsrange, b float,
     constraint pk_uv_fpo_tab primary key (id, valid_at without overlaps));
 insert into uv_fpo_tab values ('[1,1]', '[2020-01-01, 2030-01-01)', 0);
@@ -3754,6 +3754,14 @@ select * from uv_fpo_view order by id, valid_at;
  0 | 1 | ["Sat Jan 01 00:00:00 2022","Tue Jan 01 00:00:00 2030") | [1,2) | 2.0
 (3 rows)
 
+create table fpo_base (a int, b int, c int);
+create view fpo_basev1 as select a, ('[' || abs(b) || ',20]')::int4range as b from fpo_base;
+update fpo_basev1 for portion of b from 1 to 10 set a = 2; -- error
+ERROR:  cannot update column "b" of view "fpo_basev1"
+DETAIL:  View columns that are not columns of their base relation are not updatable.
+delete from fpo_basev1 for portion of b from 1 to 10;  -- error
+ERROR:  DELETE ... FOR PORTION OF is not supported for column "b" on view "fpo_basev1"
+DETAIL:  View columns that are not columns of their base relation are not updatable.
 -- Test whole-row references to the view
 create table uv_iocu_tab (a int unique, b text);
 create view uv_iocu_view as
diff --git a/src/test/regress/sql/updatable_views.sql b/src/test/regress/sql/updatable_views.sql
index f7646999bd4..8762b62df28 100644
--- a/src/test/regress/sql/updatable_views.sql
+++ b/src/test/regress/sql/updatable_views.sql
@@ -1889,7 +1889,7 @@ select * from uv_iocu_tab;
 drop view uv_iocu_view;
 drop table uv_iocu_tab;
 
--- Check UPDATE FOR PORTION OF works correctly
+-- Check UPDATE/DELETE FOR PORTION OF works correctly
 create table uv_fpo_tab (id int4range, valid_at tsrange, b float,
     constraint pk_uv_fpo_tab primary key (id, valid_at without overlaps));
 insert into uv_fpo_tab values ('[1,1]', '[2020-01-01, 2030-01-01)', 0);
@@ -1903,6 +1903,11 @@ select * from uv_fpo_view order by id, valid_at;
 delete from uv_fpo_view for portion of valid_at from '2017-01-01' to '2022-01-01' where id = '[1,1]';
 select * from uv_fpo_view order by id, valid_at;
 
+create table fpo_base (a int, b int, c int);
+create view fpo_basev1 as select a, ('[' || abs(b) || ',20]')::int4range as b from fpo_base;
+update fpo_basev1 for portion of b from 1 to 10 set a = 2; -- error
+delete from fpo_basev1 for portion of b from 1 to 10;  -- error
+
 -- Test whole-row references to the view
 create table uv_iocu_tab (a int unique, b text);
 create view uv_iocu_view as
-- 
2.34.1

