From 63b6699d5c03405f36396dd98c54f53a4bcf40d3 Mon Sep 17 00:00:00 2001
From: Aleksander Alekseev <aleksander@tigerdata.com>
Date: Tue, 21 Apr 2026 15:46:04 +0300
Subject: [PATCH v1] Forbid FOR PORTION OF on views with INSTEAD OF triggers

Priviously an attempt to use these features together caused a crash.
Oversight of commit 8e72d914c528.

Author: Aleksander Alekseev <aleksander@tigerdata.com>
Reviewed-by: TODO FIXME
Discussion: TODO FIXME
---
 src/backend/parser/analyze.c                  | 11 +++++++++
 src/test/regress/expected/updatable_views.out | 24 +++++++++++++++++++
 src/test/regress/sql/updatable_views.sql      | 24 +++++++++++++++++++
 3 files changed, 59 insertions(+)

diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index cb4e5019c2f..4fc0ae7199e 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -1333,6 +1333,17 @@ transformForPortionOfClause(ParseState *pstate,
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 				 errmsg("foreign tables don't support FOR PORTION OF")));
 
+	/* We don't support FOR PORTION OF on views with INSTEAD OF triggers. */
+	if (targetrel->rd_rel->relkind == RELKIND_VIEW &&
+		targetrel->rd_rel->relhastriggers &&
+		targetrel->trigdesc != NULL &&
+		(isUpdate ? targetrel->trigdesc->trig_update_instead_row
+				  : targetrel->trigdesc->trig_delete_instead_row))
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("views with INSTEAD OF triggers do not support FOR PORTION OF"),
+				 parser_errposition(pstate, forPortionOf->location)));
+
 	result = makeNode(ForPortionOfExpr);
 
 	/* Look up the FOR PORTION OF name requested. */
diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out
index 8852160718f..4701938bfe2 100644
--- a/src/test/regress/expected/updatable_views.out
+++ b/src/test/regress/expected/updatable_views.out
@@ -4151,3 +4151,27 @@ select * from base_tab order by a;
 
 drop view base_tab_view;
 drop table base_tab;
+-- FOR PORTION OF is not supported on views with INSTEAD OF triggers
+create view uv_fpo_instead_view as select id, valid_at, b from uv_fpo_tab;
+create function uv_fpo_instead_trig() returns trigger language plpgsql as
+$$ begin return null; end $$;
+create trigger uv_fpo_instead_upd_trig
+  instead of update on uv_fpo_instead_view
+  for each row execute function uv_fpo_instead_trig();
+create trigger uv_fpo_instead_del_trig
+  instead of delete on uv_fpo_instead_view
+  for each row execute function uv_fpo_instead_trig();
+update uv_fpo_instead_view
+  for portion of valid_at from '2015-01-01' to '2020-01-01'
+  set b = 99 where id = '[1,1]'; -- error
+ERROR:  views with INSTEAD OF triggers do not support FOR PORTION OF
+LINE 2:   for portion of valid_at from '2015-01-01' to '2020-01-01'
+                         ^
+delete from uv_fpo_instead_view
+  for portion of valid_at from '2017-01-01' to '2022-01-01'
+  where id = '[1,1]'; -- error
+ERROR:  views with INSTEAD OF triggers do not support FOR PORTION OF
+LINE 2:   for portion of valid_at from '2017-01-01' to '2022-01-01'
+                         ^
+drop view uv_fpo_instead_view;
+drop function uv_fpo_instead_trig();
diff --git a/src/test/regress/sql/updatable_views.sql b/src/test/regress/sql/updatable_views.sql
index f7646999bd4..30c2db7ad7d 100644
--- a/src/test/regress/sql/updatable_views.sql
+++ b/src/test/regress/sql/updatable_views.sql
@@ -2137,3 +2137,27 @@ values (1, 2, default, 5, 4, default, 3), (10, 11, 'C value', 14, 13, 100, 12);
 select * from base_tab order by a;
 drop view base_tab_view;
 drop table base_tab;
+
+-- FOR PORTION OF is not supported on views with INSTEAD OF triggers
+create view uv_fpo_instead_view as select id, valid_at, b from uv_fpo_tab;
+
+create function uv_fpo_instead_trig() returns trigger language plpgsql as
+$$ begin return null; end $$;
+
+create trigger uv_fpo_instead_upd_trig
+  instead of update on uv_fpo_instead_view
+  for each row execute function uv_fpo_instead_trig();
+create trigger uv_fpo_instead_del_trig
+  instead of delete on uv_fpo_instead_view
+  for each row execute function uv_fpo_instead_trig();
+
+update uv_fpo_instead_view
+  for portion of valid_at from '2015-01-01' to '2020-01-01'
+  set b = 99 where id = '[1,1]'; -- error
+
+delete from uv_fpo_instead_view
+  for portion of valid_at from '2017-01-01' to '2022-01-01'
+  where id = '[1,1]'; -- error
+
+drop view uv_fpo_instead_view;
+drop function uv_fpo_instead_trig();
-- 
2.43.0

