diff options
author | Tom Lane | 2024-07-20 17:40:15 +0000 |
---|---|---|
committer | Tom Lane | 2024-07-20 17:40:15 +0000 |
commit | 220003b9b93729af1ffa861d1ae5f4724ce22cd8 (patch) | |
tree | ea7d175d888d9ad1cba912887190be109eb5f380 /src/backend/rewrite/rewriteHandler.c | |
parent | 8720a15e9ab121e49174d889eaeafae8ac89de7b (diff) |
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
Diffstat (limited to 'src/backend/rewrite/rewriteHandler.c')
-rw-r--r-- | src/backend/rewrite/rewriteHandler.c | 35 |
1 files changed, 21 insertions, 14 deletions
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index 8a29fbbc465..e1d805d113e 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -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 |