aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2024-07-20 13:40:15 -0400
committerTom Lane <tgl@sss.pgh.pa.us>2024-07-20 13:40:15 -0400
commitfeca6c688cd99c68ce10caf21f8566b2483ca3f2 (patch)
tree8369ea122d5c18dfe950beba01956faf6f646e03
parent4f962815871f6ac4eb3b516832b5c95a2f628f1b (diff)
downloadpostgresql-feca6c688cd99c68ce10caf21f8566b2483ca3f2.tar.gz
postgresql-feca6c688cd99c68ce10caf21f8566b2483ca3f2.zip
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
-rw-r--r--src/backend/rewrite/rewriteHandler.c29
-rw-r--r--src/test/regress/expected/updatable_views.out3
-rw-r--r--src/test/regress/sql/updatable_views.sql1
3 files changed, 23 insertions, 10 deletions
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index b16cbd236c1..2b38ea93adc 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -2949,7 +2949,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 *
@@ -3046,6 +3046,9 @@ rewriteTargetView(Query *parsetree, Relation view)
*/
viewquery = copyObject(get_view_query(view));
+ /* Locate RTE describing the view in the outer query */
+ view_rte = rt_fetch(parsetree->resultRelation, parsetree->rtable);
+
/* The view must be updatable, else fail */
auto_update_detail =
view_query_is_auto_updatable(viewquery,
@@ -3088,17 +3091,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_rte->insertedCols,
+ view_rte->updatedCols);
+
foreach(lc, parsetree->targetList)
{
TargetEntry *tle = (TargetEntry *) lfirst(lc);
@@ -3156,9 +3168,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.
diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out
index 05eb12fde32..124f0680e22 100644
--- a/src/test/regress/expected/updatable_views.out
+++ b/src/test/regress/expected/updatable_views.out
@@ -1361,6 +1361,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
-----+-------------------+-------------------
diff --git a/src/test/regress/sql/updatable_views.sql b/src/test/regress/sql/updatable_views.sql
index 08983f17136..3fe86858549 100644
--- a/src/test/regress/sql/updatable_views.sql
+++ b/src/test/regress/sql/updatable_views.sql
@@ -665,6 +665,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