]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Fix FOR PORTION OF with non-updatable view columns
authorPeter Eisentraut <peter@eisentraut.org>
Wed, 13 May 2026 11:34:09 +0000 (13:34 +0200)
committerPeter Eisentraut <peter@eisentraut.org>
Wed, 13 May 2026 11:44:28 +0000 (13:44 +0200)
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 <pj@illuminatedcomputing.com>
Co-authored-by: jian he <jian.universality@gmail.com>
Discussion: https://www.postgresql.org/message-id/CACJufxFRqg8%3DgbZ-Q6ZS_UQ%2BYdwfZpk%2B9rf7jgWrk8m4RMUm%3DA%40mail.gmail.com

src/backend/rewrite/rewriteHandler.c
src/test/regress/expected/updatable_views.out
src/test/regress/sql/updatable_views.sql

index 77b2c9bc622c288d09f50879d288c1f736a9b232..e7ae9cce65fe03f52c508d29177fcb69b542fcf7 100644 (file)
@@ -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
index 8852160718fbf1e18c268398ffce5d1e84aac508..7b00c742776688d9656a2ef4f75f5693db522cd4 100644 (file)
@@ -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
index f7646999bd46a4d16be30478aa8a364d1d24ee8e..4a60126ec907981677f7e5a5cbd1fc5bf2ceebe6 100644 (file)
@@ -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