Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit23e2a06

Browse files
committed
Yet further fixes for multi-row VALUES lists for updatable views.
DEFAULT markers appearing in an INSERT on an updatable viewcould be mis-processed if they were in a multi-row VALUES clause.This would lead to strange errors such as "cache lookup failedfor type NNNN", or in older branches even to crashes.The cause is that commit41531e4 tried to re-use rewriteValuesRTE()to remove any SetToDefault nodes (that hadn't previously been replacedby the view's own default values) appearing in "product" queries,that is DO ALSO queries. That's fundamentally wrong because theDO ALSO queries might not even be INSERTs; and even if they are,their targetlists don't necessarily match the view's column list,so that almost all the logic in rewriteValuesRTE() is inapplicable.What we want is a narrow focus on replacing any such nodes with NULLconstants. (That is, in this context we are interpreting the defaultsas being strictly those of the view itself; and we already replacedany that aren't NULL.) We could add still more !force_nulls teststo further lobotomize rewriteValuesRTE(); but it seems cleaner tosplit out this case to a new function, restoring rewriteValuesRTE()to the charter it had before.Per bug #17633 from jiye_sw. Patch by me, but thanks toRichard Guo and Japin Li for initial investigation.Back-patch to all supported branches, as the previous fix was.Discussion:https://postgr.es/m/17633-98cc85e1fa91e905@postgresql.org
1 parent8bf4705 commit23e2a06

File tree

3 files changed

+97
-26
lines changed

3 files changed

+97
-26
lines changed

‎src/backend/rewrite/rewriteHandler.c

Lines changed: 58 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,8 @@ static TargetEntry *process_matched_tle(TargetEntry *src_tle,
7272
constchar*attrName);
7373
staticNode*get_assignment_input(Node*node);
7474
staticboolrewriteValuesRTE(Query*parsetree,RangeTblEntry*rte,intrti,
75-
Relationtarget_relation,boolforce_nulls);
75+
Relationtarget_relation);
76+
staticvoidrewriteValuesRTEToNulls(Query*parsetree,RangeTblEntry*rte);
7677
staticvoidmarkQueryForLocking(Query*qry,Node*jtnode,
7778
LockClauseStrengthstrength,LockWaitPolicywaitPolicy,
7879
boolpushedDown);
@@ -1233,17 +1234,6 @@ searchForDefault(RangeTblEntry *rte)
12331234
* all DEFAULT items are replaced, and if the target relation doesn't have a
12341235
* default, the value is explicitly set to NULL.
12351236
*
1236-
* Additionally, if force_nulls is true, the target relation's defaults are
1237-
* ignored and all DEFAULT items in the VALUES list are explicitly set to
1238-
* NULL, regardless of the target relation's type. This is used for the
1239-
* product queries generated by DO ALSO rules attached to an auto-updatable
1240-
* view, for which we will have already called this function with force_nulls
1241-
* false. For these product queries, we must then force any remaining DEFAULT
1242-
* items to NULL to provide concrete values for the rule actions.
1243-
* Essentially, this is a mix of the 2 cases above --- the original query is
1244-
* an insert into an auto-updatable view, and the product queries are inserts
1245-
* into a rule-updatable view.
1246-
*
12471237
* Note that we may have subscripted or field assignment targetlist entries,
12481238
* as well as more complex expressions from already-replaced DEFAULT items if
12491239
* we have recursed to here for an auto-updatable view. However, it ought to
@@ -1256,7 +1246,7 @@ searchForDefault(RangeTblEntry *rte)
12561246
*/
12571247
staticbool
12581248
rewriteValuesRTE(Query*parsetree,RangeTblEntry*rte,intrti,
1259-
Relationtarget_relation,boolforce_nulls)
1249+
Relationtarget_relation)
12601250
{
12611251
List*newValues;
12621252
ListCell*lc;
@@ -1265,15 +1255,16 @@ rewriteValuesRTE(Query *parsetree, RangeTblEntry *rte, int rti,
12651255
intnumattrs;
12661256
int*attrnos;
12671257

1258+
/* Steps below are not sensible for non-INSERT queries */
1259+
Assert(parsetree->commandType==CMD_INSERT);
1260+
Assert(rte->rtekind==RTE_VALUES);
1261+
12681262
/*
12691263
* Rebuilding all the lists is a pretty expensive proposition in a big
12701264
* VALUES list, and it's a waste of time if there aren't any DEFAULT
12711265
* placeholders. So first scan to see if there are any.
1272-
*
1273-
* We skip this check if force_nulls is true, because we know that there
1274-
* are DEFAULT items present in that case.
12751266
*/
1276-
if (!force_nulls&& !searchForDefault(rte))
1267+
if (!searchForDefault(rte))
12771268
return true;/* nothing to do */
12781269

12791270
/*
@@ -1307,12 +1298,10 @@ rewriteValuesRTE(Query *parsetree, RangeTblEntry *rte, int rti,
13071298
/*
13081299
* Check if the target relation is an auto-updatable view, in which case
13091300
* unresolved defaults will be left untouched rather than being set to
1310-
* NULL. If force_nulls is true, we always set DEFAULT items to NULL, so
1311-
* skip this check in that case --- it isn't an auto-updatable view.
1301+
* NULL.
13121302
*/
13131303
isAutoUpdatableView= false;
1314-
if (!force_nulls&&
1315-
target_relation->rd_rel->relkind==RELKIND_VIEW&&
1304+
if (target_relation->rd_rel->relkind==RELKIND_VIEW&&
13161305
!view_has_instead_trigger(target_relation,CMD_INSERT))
13171306
{
13181307
List*locks;
@@ -1370,9 +1359,10 @@ rewriteValuesRTE(Query *parsetree, RangeTblEntry *rte, int rti,
13701359

13711360
if (attrno==0)
13721361
elog(ERROR,"cannot set value in column %d to DEFAULT",i);
1362+
Assert(attrno>0&&attrno <=target_relation->rd_att->natts);
13731363
att_tup=target_relation->rd_att->attrs[attrno-1];
13741364

1375-
if (!force_nulls&& !att_tup->attisdropped)
1365+
if (!att_tup->attisdropped)
13761366
new_expr=build_column_default(target_relation,attrno);
13771367
else
13781368
new_expr=NULL;/* force a NULL if dropped */
@@ -1422,6 +1412,50 @@ rewriteValuesRTE(Query *parsetree, RangeTblEntry *rte, int rti,
14221412
returnallReplaced;
14231413
}
14241414

1415+
/*
1416+
* Mop up any remaining DEFAULT items in the given VALUES RTE by
1417+
* replacing them with NULL constants.
1418+
*
1419+
* This is used for the product queries generated by DO ALSO rules attached to
1420+
* an auto-updatable view. The action can't depend on the "target relation"
1421+
* since the product query might not have one (it needn't be an INSERT).
1422+
* Essentially, such queries are treated as being attached to a rule-updatable
1423+
* view.
1424+
*/
1425+
staticvoid
1426+
rewriteValuesRTEToNulls(Query*parsetree,RangeTblEntry*rte)
1427+
{
1428+
List*newValues;
1429+
ListCell*lc;
1430+
1431+
Assert(rte->rtekind==RTE_VALUES);
1432+
newValues=NIL;
1433+
foreach(lc,rte->values_lists)
1434+
{
1435+
List*sublist= (List*)lfirst(lc);
1436+
List*newList=NIL;
1437+
ListCell*lc2;
1438+
1439+
foreach(lc2,sublist)
1440+
{
1441+
Node*col= (Node*)lfirst(lc2);
1442+
1443+
if (IsA(col,SetToDefault))
1444+
{
1445+
SetToDefault*def= (SetToDefault*)col;
1446+
1447+
newList=lappend(newList,makeNullConst(def->typeId,
1448+
def->typeMod,
1449+
def->collation));
1450+
}
1451+
else
1452+
newList=lappend(newList,col);
1453+
}
1454+
newValues=lappend(newValues,newList);
1455+
}
1456+
rte->values_lists=newValues;
1457+
}
1458+
14251459

14261460
/*
14271461
* rewriteTargetListUD - rewrite UPDATE/DELETE targetlist as needed
@@ -3578,7 +3612,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events)
35783612
parsetree->resultRelation);
35793613
/* ... and the VALUES expression lists */
35803614
if (!rewriteValuesRTE(parsetree,values_rte,values_rte_index,
3581-
rt_entry_relation, false))
3615+
rt_entry_relation))
35823616
defaults_remaining= true;
35833617
}
35843618
else
@@ -3655,9 +3689,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events)
36553689
RangeTblEntry*values_rte=rt_fetch(values_rte_index,
36563690
pt->rtable);
36573691

3658-
rewriteValuesRTE(pt,values_rte,values_rte_index,
3659-
rt_entry_relation,
3660-
true);/* Force remaining defaults to NULL */
3692+
rewriteValuesRTEToNulls(pt,values_rte);
36613693
}
36623694
}
36633695

‎src/test/regress/expected/updatable_views.out

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -431,8 +431,30 @@ EXPLAIN (costs off) DELETE FROM rw_view1 WHERE a=5;
431431
Index Cond: ((a > 0) AND (a = 5))
432432
(3 rows)
433433

434+
-- it's still updatable if we add a DO ALSO rule
435+
CREATE TABLE base_tbl_hist(ts timestamptz default now(), a int, b text);
436+
CREATE RULE base_tbl_log AS ON INSERT TO rw_view1 DO ALSO
437+
INSERT INTO base_tbl_hist(a,b) VALUES(new.a, new.b);
438+
SELECT table_name, is_updatable, is_insertable_into
439+
FROM information_schema.views
440+
WHERE table_name = 'rw_view1';
441+
table_name | is_updatable | is_insertable_into
442+
------------+--------------+--------------------
443+
rw_view1 | YES | YES
444+
(1 row)
445+
446+
-- Check behavior with DEFAULTs (bug #17633)
447+
INSERT INTO rw_view1 VALUES (9, DEFAULT), (10, DEFAULT);
448+
SELECT a, b FROM base_tbl_hist;
449+
a | b
450+
----+---
451+
9 |
452+
10 |
453+
(2 rows)
454+
434455
DROP TABLE base_tbl CASCADE;
435456
NOTICE: drop cascades to view rw_view1
457+
DROP TABLE base_tbl_hist;
436458
-- view on top of view
437459
CREATE TABLE base_tbl (a int PRIMARY KEY, b text DEFAULT 'Unspecified');
438460
INSERT INTO base_tbl SELECT i, 'Row ' || i FROM generate_series(-2, 2) g(i);

‎src/test/regress/sql/updatable_views.sql

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,24 @@ SELECT * FROM base_tbl;
145145
EXPLAIN (costs off)UPDATE rw_view1SET a=6WHERE a=5;
146146
EXPLAIN (costs off)DELETEFROM rw_view1WHERE a=5;
147147

148+
-- it's still updatable if we add a DO ALSO rule
149+
150+
CREATETABLEbase_tbl_hist(tstimestamptz default now(), aint, btext);
151+
152+
CREATERULEbase_tbl_logASON INSERT TO rw_view1 DO ALSO
153+
INSERT INTO base_tbl_hist(a,b)VALUES(new.a,new.b);
154+
155+
SELECT table_name, is_updatable, is_insertable_into
156+
FROMinformation_schema.views
157+
WHERE table_name='rw_view1';
158+
159+
-- Check behavior with DEFAULTs (bug #17633)
160+
161+
INSERT INTO rw_view1VALUES (9, DEFAULT), (10, DEFAULT);
162+
SELECT a, bFROM base_tbl_hist;
163+
148164
DROPTABLE base_tbl CASCADE;
165+
DROPTABLE base_tbl_hist;
149166

150167
-- view on top of view
151168

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp