From: Peter Eisentraut Date: Wed, 13 May 2026 11:34:09 +0000 (+0200) Subject: Fix FOR PORTION OF with non-updatable view columns X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=7ca8c9429675b34600e679525da6b5280c1cafa5;p=thirdparty%2Fpostgresql.git Fix FOR PORTION OF with non-updatable view columns Both UPDATE and DELETE were failing to test that the application-time column was updatable. The column is not part of perminfo->updatedCols, because it should not be checked for permissions. And it needs to be checked in the DELETE case as well, since we might insert leftovers with a value for that column. Author: Paul A. Jungwirth Co-authored-by: jian he Discussion: https://www.postgresql.org/message-id/CACJufxFRqg8%3DgbZ-Q6ZS_UQ%2BYdwfZpk%2B9rf7jgWrk8m4RMUm%3DA%40mail.gmail.com --- diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index 77b2c9bc622..e7ae9cce65f 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -3481,6 +3481,53 @@ rewriteTargetView(Query *parsetree, Relation view) } } + /* + * Similarly, make sure the FOR PORTION OF column is updateable. This is + * not included in the columns tested above, and we have to test it even + * for DELETEs. + */ + if (parsetree->forPortionOf) + { + AttrNumber rangeAttno; + Bitmapset *fpo_cols; + char *non_updatable_col; + const char *fpo_update_detail; + + rangeAttno = parsetree->forPortionOf->rangeVar->varattno; + fpo_cols = bms_make_singleton(rangeAttno - FirstLowInvalidHeapAttributeNumber); + + fpo_update_detail = view_cols_are_auto_updatable(viewquery, + fpo_cols, + NULL, + &non_updatable_col); + if (fpo_update_detail) + { + switch (parsetree->commandType) + { + case CMD_UPDATE: + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot update column \"%s\" of view \"%s\"", + non_updatable_col, + RelationGetRelationName(view)), + errdetail_internal("%s", _(fpo_update_detail)))); + break; + case CMD_DELETE: + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot delete from view \"%s\" using FOR PORTION OF \"%s\"", + RelationGetRelationName(view), + non_updatable_col), + errdetail_internal("%s", _(fpo_update_detail)))); + break; + default: + elog(ERROR, "unrecognized CmdType: %d", + (int) parsetree->commandType); + break; + } + } + } + /* * For MERGE, there must not be any INSTEAD OF triggers on an otherwise * updatable view. The caller already checked that there isn't a full set diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out index 8852160718f..7b00c742776 100644 --- a/src/test/regress/expected/updatable_views.out +++ b/src/test/regress/expected/updatable_views.out @@ -3754,6 +3754,20 @@ 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) +-- UPDATE/DELETE FOR PORTION fails if the column is not updatable +-- (e.g. a computed expression, not a base column): +create view uv_fpo_view_nonupd as + select id, '[1,20]'::int4range as valid_at, b + from uv_fpo_tab; +-- Updating fails: +update uv_fpo_view_nonupd for portion of valid_at from 1 to 10 set b = 2; +ERROR: cannot update column "valid_at" of view "uv_fpo_view_nonupd" +DETAIL: View columns that are not columns of their base relation are not updatable. +-- Deleting fails: +delete from uv_fpo_view_nonupd for portion of valid_at from 1 to 10; +ERROR: cannot delete from view "uv_fpo_view_nonupd" using FOR PORTION OF "valid_at" +DETAIL: View columns that are not columns of their base relation are not updatable. +drop view uv_fpo_view_nonupd; -- 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..4a60126ec90 100644 --- a/src/test/regress/sql/updatable_views.sql +++ b/src/test/regress/sql/updatable_views.sql @@ -1903,6 +1903,17 @@ 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; +-- UPDATE/DELETE FOR PORTION fails if the column is not updatable +-- (e.g. a computed expression, not a base column): +create view uv_fpo_view_nonupd as + select id, '[1,20]'::int4range as valid_at, b + from uv_fpo_tab; +-- Updating fails: +update uv_fpo_view_nonupd for portion of valid_at from 1 to 10 set b = 2; +-- Deleting fails: +delete from uv_fpo_view_nonupd for portion of valid_at from 1 to 10; +drop view uv_fpo_view_nonupd; + -- Test whole-row references to the view create table uv_iocu_tab (a int unique, b text); create view uv_iocu_view as