summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/backend/rewrite/rewriteHandler.c94
-rw-r--r--src/test/regress/expected/updatable_views.out37
-rw-r--r--src/test/regress/sql/updatable_views.sql14
3 files changed, 105 insertions, 40 deletions
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index fe3633af077..54219858e4a 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -51,14 +51,13 @@ static Query *rewriteRuleAction(Query *parsetree,
CmdType event,
bool *returning_flag);
static List *adjustJoinTreeList(Query *parsetree, bool removert, int rt_index);
-static void rewriteTargetListIU(Query *parsetree, Relation target_relation,
- List **attrno_list);
+static void rewriteTargetListIU(Query *parsetree, Relation target_relation);
static TargetEntry *process_matched_tle(TargetEntry *src_tle,
TargetEntry *prior_tle,
const char *attrName);
static Node *get_assignment_input(Node *node);
-static bool rewriteValuesRTE(Query *parsetree, RangeTblEntry *rte,
- Relation target_relation, List *attrnos, bool force_nulls);
+static bool rewriteValuesRTE(Query *parsetree, RangeTblEntry *rte, int rti,
+ Relation target_relation, bool force_nulls);
static void rewriteTargetListUD(Query *parsetree, RangeTblEntry *target_rte,
Relation target_relation);
static void markQueryForLocking(Query *qry, Node *jtnode,
@@ -671,15 +670,9 @@ adjustJoinTreeList(Query *parsetree, bool removert, int rt_index)
* references to NEW.foo will produce wrong or incomplete results. Item 4
* is not needed for rewriting, but will be needed by the planner, and we
* can do it essentially for free while handling the other items.
- *
- * If attrno_list isn't NULL, we return an additional output besides the
- * rewritten targetlist: an integer list of the assigned-to attnums, in
- * order of the original tlist's non-junk entries. This is needed for
- * processing VALUES RTEs.
*/
static void
-rewriteTargetListIU(Query *parsetree, Relation target_relation,
- List **attrno_list)
+rewriteTargetListIU(Query *parsetree, Relation target_relation)
{
CmdType commandType = parsetree->commandType;
TargetEntry **new_tles;
@@ -691,9 +684,6 @@ rewriteTargetListIU(Query *parsetree, Relation target_relation,
numattrs;
ListCell *temp;
- if (attrno_list) /* initialize optional result list */
- *attrno_list = NIL;
-
/*
* We process the normal (non-junk) attributes by scanning the input tlist
* once and transferring TLEs into an array, then scanning the array to
@@ -719,10 +709,6 @@ rewriteTargetListIU(Query *parsetree, Relation target_relation,
elog(ERROR, "bogus resno %d in targetlist", attrno);
att_tup = target_relation->rd_att->attrs[attrno - 1];
- /* put attrno into attrno_list even if it's dropped */
- if (attrno_list)
- *attrno_list = lappend_int(*attrno_list, attrno);
-
/* We can (and must) ignore deleted attributes */
if (att_tup->attisdropped)
continue;
@@ -1154,22 +1140,26 @@ searchForDefault(RangeTblEntry *rte)
* an insert into an auto-updatable view, and the product queries are inserts
* into a rule-updatable view.
*
- * Note that we currently can't support subscripted or field assignment
- * in the multi-VALUES case. The targetlist will contain simple Vars
- * referencing the VALUES RTE, and therefore process_matched_tle() will
- * reject any such attempt with "multiple assignments to same column".
+ * Note that we may have subscripted or field assignment targetlist entries,
+ * as well as more complex expressions from already-replaced DEFAULT items if
+ * we have recursed to here for an auto-updatable view. However, it ought to
+ * be impossible for such entries to have DEFAULTs assigned to them --- we
+ * should only have to replace DEFAULT items for targetlist entries that
+ * contain simple Vars referencing the VALUES RTE.
*
* Returns true if all DEFAULT items were replaced, and false if some were
* left untouched.
*/
static bool
-rewriteValuesRTE(Query *parsetree, RangeTblEntry *rte,
- Relation target_relation, List *attrnos, bool force_nulls)
+rewriteValuesRTE(Query *parsetree, RangeTblEntry *rte, int rti,
+ Relation target_relation, bool force_nulls)
{
List *newValues;
ListCell *lc;
bool isAutoUpdatableView;
bool allReplaced;
+ int numattrs;
+ int *attrnos;
/*
* Rebuilding all the lists is a pretty expensive proposition in a big
@@ -1182,8 +1172,33 @@ rewriteValuesRTE(Query *parsetree, RangeTblEntry *rte,
if (!force_nulls && !searchForDefault(rte))
return true; /* nothing to do */
- /* Check list lengths (we can assume all the VALUES sublists are alike) */
- Assert(list_length(attrnos) == list_length(linitial(rte->values_lists)));
+ /*
+ * Scan the targetlist for entries referring to the VALUES RTE, and note
+ * the target attributes. As noted above, we should only need to do this
+ * for targetlist entries containing simple Vars --- nothing else in the
+ * VALUES RTE should contain DEFAULT items, and we complain if such a
+ * thing does occur.
+ */
+ numattrs = list_length(linitial(rte->values_lists));
+ attrnos = (int *) palloc0(numattrs * sizeof(int));
+
+ foreach(lc, parsetree->targetList)
+ {
+ TargetEntry *tle = (TargetEntry *) lfirst(lc);
+
+ if (IsA(tle->expr, Var))
+ {
+ Var *var = (Var *) tle->expr;
+
+ if (var->varno == rti)
+ {
+ int attrno = var->varattno;
+
+ Assert(attrno >= 1 && attrno <= numattrs);
+ attrnos[attrno - 1] = tle->resno;
+ }
+ }
+ }
/*
* Check if the target relation is an auto-updatable view, in which case
@@ -1233,18 +1248,23 @@ rewriteValuesRTE(Query *parsetree, RangeTblEntry *rte,
List *sublist = (List *) lfirst(lc);
List *newList = NIL;
ListCell *lc2;
- ListCell *lc3;
+ int i;
+
+ Assert(list_length(sublist) == numattrs);
- forboth(lc2, sublist, lc3, attrnos)
+ i = 0;
+ foreach(lc2, sublist)
{
Node *col = (Node *) lfirst(lc2);
- int attrno = lfirst_int(lc3);
+ int attrno = attrnos[i++];
if (IsA(col, SetToDefault))
{
Form_pg_attribute att_tup;
Node *new_expr;
+ if (attrno == 0)
+ elog(ERROR, "cannot set value in column %d to DEFAULT", i);
att_tup = target_relation->rd_att->attrs[attrno - 1];
if (!force_nulls && !att_tup->attisdropped)
@@ -1292,6 +1312,8 @@ rewriteValuesRTE(Query *parsetree, RangeTblEntry *rte,
}
rte->values_lists = newValues;
+ pfree(attrnos);
+
return allReplaced;
}
@@ -3146,7 +3168,6 @@ RewriteQuery(Query *parsetree, List *rewrite_events)
Relation rt_entry_relation;
List *locks;
List *product_queries;
- List *attrnos = NIL;
int values_rte_index = 0;
bool defaults_remaining = false;
@@ -3192,21 +3213,21 @@ RewriteQuery(Query *parsetree, List *rewrite_events)
if (values_rte)
{
/* Process the main targetlist ... */
- rewriteTargetListIU(parsetree, rt_entry_relation, &attrnos);
+ rewriteTargetListIU(parsetree, rt_entry_relation);
/* ... and the VALUES expression lists */
- if (!rewriteValuesRTE(parsetree, values_rte,
- rt_entry_relation, attrnos, false))
+ if (!rewriteValuesRTE(parsetree, values_rte, values_rte_index,
+ rt_entry_relation, false))
defaults_remaining = true;
}
else
{
/* Process just the main targetlist */
- rewriteTargetListIU(parsetree, rt_entry_relation, NULL);
+ rewriteTargetListIU(parsetree, rt_entry_relation);
}
}
else if (event == CMD_UPDATE)
{
- rewriteTargetListIU(parsetree, rt_entry_relation, NULL);
+ rewriteTargetListIU(parsetree, rt_entry_relation);
rewriteTargetListUD(parsetree, rt_entry, rt_entry_relation);
}
else if (event == CMD_DELETE)
@@ -3252,7 +3273,8 @@ RewriteQuery(Query *parsetree, List *rewrite_events)
RangeTblEntry *values_rte = rt_fetch(values_rte_index,
pt->rtable);
- rewriteValuesRTE(pt, values_rte, rt_entry_relation, attrnos,
+ rewriteValuesRTE(pt, values_rte, values_rte_index,
+ rt_entry_relation,
true); /* Force remaining defaults to NULL */
}
}
diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out
index 6b845a7530f..1393dbc5c5c 100644
--- a/src/test/regress/expected/updatable_views.out
+++ b/src/test/regress/expected/updatable_views.out
@@ -2420,6 +2420,7 @@ insert into base_tab_def_view values (12), (13);
insert into base_tab_def_view values (14, default, default, default, default);
insert into base_tab_def_view values (15, default, default, default, default),
(16, default, default, default, default);
+insert into base_tab_def_view values (17), (default);
select * from base_tab_def order by a;
a | b | c | d | e
----+---------------+---------------+--------------+---
@@ -2435,7 +2436,9 @@ select * from base_tab_def order by a;
14 | View default | Table default | View default |
15 | View default | Table default | View default |
16 | View default | Table default | View default |
-(12 rows)
+ 17 | View default | Table default | View default |
+ | View default | Table default | View default |
+(14 rows)
-- Adding an INSTEAD OF trigger should cause NULLs to be inserted instead of
-- table defaults, where there are no view defaults.
@@ -2461,6 +2464,7 @@ insert into base_tab_def_view values (12), (13);
insert into base_tab_def_view values (14, default, default, default, default);
insert into base_tab_def_view values (15, default, default, default, default),
(16, default, default, default, default);
+insert into base_tab_def_view values (17), (default);
select * from base_tab_def order by a;
a | b | c | d | e
----+---------------+---------------+--------------+---
@@ -2476,7 +2480,9 @@ select * from base_tab_def order by a;
14 | View default | | View default |
15 | View default | | View default |
16 | View default | | View default |
-(12 rows)
+ 17 | View default | | View default |
+ | View default | | View default |
+(14 rows)
-- Using an unconditional DO INSTEAD rule should also cause NULLs to be
-- inserted where there are no view defaults.
@@ -2495,6 +2501,7 @@ insert into base_tab_def_view values (12), (13);
insert into base_tab_def_view values (14, default, default, default, default);
insert into base_tab_def_view values (15, default, default, default, default),
(16, default, default, default, default);
+insert into base_tab_def_view values (17), (default);
select * from base_tab_def order by a;
a | b | c | d | e
----+---------------+---------------+--------------+---
@@ -2510,7 +2517,9 @@ select * from base_tab_def order by a;
14 | View default | | View default |
15 | View default | | View default |
16 | View default | | View default |
-(12 rows)
+ 17 | View default | | View default |
+ | View default | | View default |
+(14 rows)
-- A DO ALSO rule should cause each row to be inserted twice. The first
-- insert should behave the same as an auto-updatable view (using table
@@ -2531,6 +2540,7 @@ insert into base_tab_def_view values (12), (13);
insert into base_tab_def_view values (14, default, default, default, default);
insert into base_tab_def_view values (15, default, default, default, default),
(16, default, default, default, default);
+insert into base_tab_def_view values (17), (default);
select * from base_tab_def order by a, c NULLS LAST;
a | b | c | d | e
----+---------------+---------------+--------------+---
@@ -2552,7 +2562,26 @@ select * from base_tab_def order by a, c NULLS LAST;
15 | View default | | View default |
16 | View default | Table default | View default |
16 | View default | | View default |
-(18 rows)
+ 17 | View default | Table default | View default |
+ 17 | View default | | View default |
+ | View default | Table default | View default |
+ | View default | | View default |
+(22 rows)
drop view base_tab_def_view;
drop table base_tab_def;
+-- Test defaults with array assignments
+create table base_tab (a serial, b int[], c text, d text default 'Table default');
+create view base_tab_view as select c, a, b from base_tab;
+alter view base_tab_view alter column c set default 'View default';
+insert into base_tab_view (b[1], c, a)
+values (1, default, default), (10, 'C value', 100);
+select * from base_tab order by a;
+ a | b | c | d
+-----+------+--------------+---------------
+ 1 | {1} | View default | Table default
+ 100 | {10} | C value | Table default
+(2 rows)
+
+drop view base_tab_view;
+drop table base_tab;
diff --git a/src/test/regress/sql/updatable_views.sql b/src/test/regress/sql/updatable_views.sql
index 467306eebfe..86df54b9026 100644
--- a/src/test/regress/sql/updatable_views.sql
+++ b/src/test/regress/sql/updatable_views.sql
@@ -1121,6 +1121,7 @@ insert into base_tab_def_view values (12), (13);
insert into base_tab_def_view values (14, default, default, default, default);
insert into base_tab_def_view values (15, default, default, default, default),
(16, default, default, default, default);
+insert into base_tab_def_view values (17), (default);
select * from base_tab_def order by a;
-- Adding an INSTEAD OF trigger should cause NULLs to be inserted instead of
@@ -1147,6 +1148,7 @@ insert into base_tab_def_view values (12), (13);
insert into base_tab_def_view values (14, default, default, default, default);
insert into base_tab_def_view values (15, default, default, default, default),
(16, default, default, default, default);
+insert into base_tab_def_view values (17), (default);
select * from base_tab_def order by a;
-- Using an unconditional DO INSTEAD rule should also cause NULLs to be
@@ -1166,6 +1168,7 @@ insert into base_tab_def_view values (12), (13);
insert into base_tab_def_view values (14, default, default, default, default);
insert into base_tab_def_view values (15, default, default, default, default),
(16, default, default, default, default);
+insert into base_tab_def_view values (17), (default);
select * from base_tab_def order by a;
-- A DO ALSO rule should cause each row to be inserted twice. The first
@@ -1187,7 +1190,18 @@ insert into base_tab_def_view values (12), (13);
insert into base_tab_def_view values (14, default, default, default, default);
insert into base_tab_def_view values (15, default, default, default, default),
(16, default, default, default, default);
+insert into base_tab_def_view values (17), (default);
select * from base_tab_def order by a, c NULLS LAST;
drop view base_tab_def_view;
drop table base_tab_def;
+
+-- Test defaults with array assignments
+create table base_tab (a serial, b int[], c text, d text default 'Table default');
+create view base_tab_view as select c, a, b from base_tab;
+alter view base_tab_view alter column c set default 'View default';
+insert into base_tab_view (b[1], c, a)
+values (1, default, default), (10, 'C value', 100);
+select * from base_tab order by a;
+drop view base_tab_view;
+drop table base_tab;