mirror of
				https://github.com/postgres/postgres.git
				synced 2025-11-03 09:13:20 +03:00 
			
		
		
		
	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:
		@@ -2963,7 +2963,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 *
 | 
			
		||||
@@ -3063,6 +3063,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);
 | 
			
		||||
 | 
			
		||||
	/* The view must be updatable, else fail */
 | 
			
		||||
	auto_update_detail =
 | 
			
		||||
		view_query_is_auto_updatable(viewquery,
 | 
			
		||||
@@ -3105,17 +3109,26 @@ rewriteTargetView(Query *parsetree, Relation view)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * For 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.
 | 
			
		||||
	 * For INSERT/UPDATE the modified columns must all be updatable.
 | 
			
		||||
	 */
 | 
			
		||||
	if (parsetree->commandType != CMD_DELETE)
 | 
			
		||||
	{
 | 
			
		||||
		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);
 | 
			
		||||
@@ -3173,9 +3186,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.
 | 
			
		||||
@@ -3270,10 +3280,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
 | 
			
		||||
 
 | 
			
		||||
@@ -1598,6 +1598,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         
 | 
			
		||||
-----+-------------------+-------------------
 | 
			
		||||
 
 | 
			
		||||
@@ -845,6 +845,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
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user