Correctly check updatability of columns targeted by INSERT...DEFAULT.

If a view has some updatable and some non-updatable columns, we failed
to verify updatability of any columns for which an INSERT or UPDATE
on the view explicitly specifies a DEFAULT item (unless the view has
a declared default for that column, which is rare anyway, and one
would almost certainly not write one for a non-updatable column).
This would lead to an unexpected "attribute number N not found in
view targetlist" error rather than the intended error.

Per bug #18546 from Alexander Lakhin.  This bug is old, so back-patch
to all supported branches.

Discussion: https://postgr.es/m/18546-84a292e759a9361d@postgresql.org
This commit is contained in:
Tom Lane 2024-07-20 13:40:15 -04:00
parent 8720a15e9a
commit 220003b9b9
3 changed files with 25 additions and 14 deletions

View File

@ -2986,7 +2986,7 @@ relation_is_updatable(Oid reloid,
*
* This is used with simply-updatable views to map column-permissions sets for
* the view columns onto the matching columns in the underlying base relation.
* The targetlist is expected to be a list of plain Vars of the underlying
* Relevant entries in the targetlist must be plain Vars of the underlying
* relation (as per the checks above in view_query_is_auto_updatable).
*/
static Bitmapset *
@ -3186,6 +3186,10 @@ rewriteTargetView(Query *parsetree, Relation view)
*/
viewquery = copyObject(get_view_query(view));
/* Locate RTE and perminfo describing the view in the outer query */
view_rte = rt_fetch(parsetree->resultRelation, parsetree->rtable);
view_perminfo = getRTEPermissionInfo(parsetree->rteperminfos, view_rte);
/*
* Are we doing INSERT/UPDATE, or MERGE containing INSERT/UPDATE? If so,
* various additional checks on the view columns need to be applied, and
@ -3225,17 +3229,26 @@ rewriteTargetView(Query *parsetree, Relation view)
/*
* For INSERT/UPDATE (or MERGE containing INSERT/UPDATE) the modified
* columns must all be updatable. Note that we get the modified columns
* from the query's targetlist, not from the result RTE's insertedCols
* and/or updatedCols set, since rewriteTargetListIU may have added
* additional targetlist entries for view defaults, and these must also be
* updatable.
* columns must all be updatable.
*/
if (insert_or_update)
{
Bitmapset *modified_cols = NULL;
Bitmapset *modified_cols;
char *non_updatable_col;
/*
* Compute the set of modified columns as those listed in the result
* RTE's insertedCols and/or updatedCols sets plus those that are
* targets of the query's targetlist(s). We must consider the query's
* targetlist because rewriteTargetListIU may have added additional
* targetlist entries for view defaults, and these must also be
* 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.
*/
modified_cols = bms_union(view_perminfo->insertedCols,
view_perminfo->updatedCols);
foreach(lc, parsetree->targetList)
{
TargetEntry *tle = (TargetEntry *) lfirst(lc);
@ -3337,9 +3350,6 @@ rewriteTargetView(Query *parsetree, Relation view)
}
}
/* Locate RTE describing the view in the outer query */
view_rte = rt_fetch(parsetree->resultRelation, parsetree->rtable);
/*
* If we get here, view_query_is_auto_updatable() has verified that the
* view contains a single base relation.
@ -3434,10 +3444,7 @@ rewriteTargetView(Query *parsetree, Relation view)
* Note: the original view's RTEPermissionInfo remains in the query's
* rteperminfos so that the executor still performs appropriate
* permissions checks for the query caller's use of the view.
*/
view_perminfo = getRTEPermissionInfo(parsetree->rteperminfos, view_rte);
/*
*
* Disregard the perminfo in viewquery->rteperminfos that the base_rte
* would currently be pointing at, because we'd like it to point now to a
* new one that will be filled below. Must set perminfoindex to 0 to not

View File

@ -2101,6 +2101,9 @@ DETAIL: View columns that refer to system columns are not updatable.
INSERT INTO rw_view1 (s, c, a) VALUES (null, null, 1.1); -- should fail
ERROR: cannot insert into column "s" of view "rw_view1"
DETAIL: View columns that are not columns of their base relation are not updatable.
INSERT INTO rw_view1 (s, c, a) VALUES (default, default, 1.1); -- should fail
ERROR: cannot insert into column "s" of view "rw_view1"
DETAIL: View columns that are not columns of their base relation are not updatable.
INSERT INTO rw_view1 (a) VALUES (1.1) RETURNING a, s, c; -- OK
a | s | c
-----+-------------------+-------------------

View File

@ -1111,6 +1111,7 @@ CREATE VIEW rw_view1 AS
INSERT INTO rw_view1 VALUES (null, null, 1.1, null); -- should fail
INSERT INTO rw_view1 (s, c, a) VALUES (null, null, 1.1); -- should fail
INSERT INTO rw_view1 (s, c, a) VALUES (default, default, 1.1); -- should fail
INSERT INTO rw_view1 (a) VALUES (1.1) RETURNING a, s, c; -- OK
UPDATE rw_view1 SET s = s WHERE a = 1.1; -- should fail
UPDATE rw_view1 SET a = 1.05 WHERE a = 1.1 RETURNING s; -- OK