Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 915de70

Browse files
committed
Fix infer_arbiter_indexes() to not assume resultRelation is 1.
infer_arbiter_indexes failed to renumber varnos in index expressions or predicates that it got from the catalogs. This escaped detection up to now because the stored varnos in such trees will be 1, and an INSERT's result relation is usually the first rangetable entry, so that that was fine. However, in cases such as inserting through an updatable view, it's not fine, leading to failure to match the expressions to the query with ensuing "there is no unique or exclusion constraint matching the ON CONFLICT specification" errors. Fix by copy-and-paste from get_relation_info(). Per bug #18502 from Michael Wang. Back-patch to all supported versions. Discussion: https://postgr.es/m/18502-545b53f5b81e54e0@postgresql.org
1 parent c2fab70 commit 915de70

File tree

3 files changed

+27
-1
lines changed

3 files changed

+27
-1
lines changed

src/backend/optimizer/util/plancat.c

+8-1
Original file line numberDiff line numberDiff line change
@@ -695,6 +695,7 @@ infer_arbiter_indexes(PlannerInfo *root)
695695
OnConflictExpr *onconflict = root->parse->onConflict;
696696

697697
/* Iteration state */
698+
Index varno;
698699
RangeTblEntry *rte;
699700
Relation relation;
700701
Oid indexOidFromConstraint = InvalidOid;
@@ -723,7 +724,8 @@ infer_arbiter_indexes(PlannerInfo *root)
723724
* the rewriter or when expand_inherited_rtentry() added it to the query's
724725
* rangetable.
725726
*/
726-
rte = rt_fetch(root->parse->resultRelation, root->parse->rtable);
727+
varno = root->parse->resultRelation;
728+
rte = rt_fetch(varno, root->parse->rtable);
727729

728730
relation = table_open(rte->relid, NoLock);
729731

@@ -857,6 +859,9 @@ infer_arbiter_indexes(PlannerInfo *root)
857859

858860
/* Expression attributes (if any) must match */
859861
idxExprs = RelationGetIndexExpressions(idxRel);
862+
if (idxExprs && varno != 1)
863+
ChangeVarNodes((Node *) idxExprs, 1, varno, 0);
864+
860865
foreach(el, onconflict->arbiterElems)
861866
{
862867
InferenceElem *elem = (InferenceElem *) lfirst(el);
@@ -908,6 +913,8 @@ infer_arbiter_indexes(PlannerInfo *root)
908913
* CONFLICT's WHERE clause.
909914
*/
910915
predExprs = RelationGetIndexPredicate(idxRel);
916+
if (predExprs && varno != 1)
917+
ChangeVarNodes((Node *) predExprs, 1, varno, 0);
911918

912919
if (!predicate_implied_by(predExprs, (List *) onconflict->arbiterWhere, false))
913920
goto next;

src/test/regress/expected/insert_conflict.out

+13
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
-- insert...on conflict do unique index inference
33
--
44
create table insertconflicttest(key int4, fruit text);
5+
-- These things should work through a view, as well
6+
create view insertconflictview as select * from insertconflicttest;
57
--
68
-- Test unique index inference with operator class specifications and
79
-- named collations
@@ -43,6 +45,15 @@ explain (costs off) insert into insertconflicttest values(0, 'Crowberry') on con
4345
-> Result
4446
(4 rows)
4547

48+
explain (costs off) insert into insertconflictview values(0, 'Crowberry') on conflict (lower(fruit), key, lower(fruit), key) do nothing;
49+
QUERY PLAN
50+
-------------------------------------------------
51+
Insert on insertconflicttest
52+
Conflict Resolution: NOTHING
53+
Conflict Arbiter Indexes: both_index_expr_key
54+
-> Result
55+
(4 rows)
56+
4657
explain (costs off) insert into insertconflicttest values(0, 'Crowberry') on conflict (key, fruit) do update set fruit = excluded.fruit
4758
where exists (select 1 from insertconflicttest ii where ii.key = excluded.key);
4859
QUERY PLAN
@@ -380,6 +391,7 @@ create unique index partial_key_index on insertconflicttest(key) where fruit lik
380391
-- Succeeds
381392
insert into insertconflicttest values (23, 'Blackberry') on conflict (key) where fruit like '%berry' do update set fruit = excluded.fruit;
382393
insert into insertconflicttest as t values (23, 'Blackberry') on conflict (key) where fruit like '%berry' and t.fruit = 'inconsequential' do nothing;
394+
insert into insertconflictview as t values (23, 'Blackberry') on conflict (key) where fruit like '%berry' and t.fruit = 'inconsequential' do nothing;
383395
-- fails
384396
insert into insertconflicttest values (23, 'Blackberry') on conflict (key) do update set fruit = excluded.fruit;
385397
ERROR: there is no unique or exclusion constraint matching the ON CONFLICT specification
@@ -445,6 +457,7 @@ explain (costs off) insert into insertconflicttest as i values (23, 'Avocado') o
445457

446458
drop index plain;
447459
-- Cleanup
460+
drop view insertconflictview;
448461
drop table insertconflicttest;
449462
--
450463
-- Verify that EXCLUDED does not allow system column references. These

src/test/regress/sql/insert_conflict.sql

+6
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
--
44
create table insertconflicttest(key int4, fruit text);
55

6+
-- These things should work through a view, as well
7+
create view insertconflictview as select * from insertconflicttest;
8+
69
--
710
-- Test unique index inference with operator class specifications and
811
-- named collations
@@ -20,6 +23,7 @@ explain (costs off) insert into insertconflicttest values(0, 'Crowberry') on con
2023
explain (costs off) insert into insertconflicttest values(0, 'Crowberry') on conflict (key, fruit) do nothing;
2124
explain (costs off) insert into insertconflicttest values(0, 'Crowberry') on conflict (fruit, key, fruit, key) do nothing;
2225
explain (costs off) insert into insertconflicttest values(0, 'Crowberry') on conflict (lower(fruit), key, lower(fruit), key) do nothing;
26+
explain (costs off) insert into insertconflictview values(0, 'Crowberry') on conflict (lower(fruit), key, lower(fruit), key) do nothing;
2327
explain (costs off) insert into insertconflicttest values(0, 'Crowberry') on conflict (key, fruit) do update set fruit = excluded.fruit
2428
where exists (select 1 from insertconflicttest ii where ii.key = excluded.key);
2529
-- Neither collation nor operator class specifications are required --
@@ -218,6 +222,7 @@ create unique index partial_key_index on insertconflicttest(key) where fruit lik
218222
-- Succeeds
219223
insert into insertconflicttest values (23, 'Blackberry') on conflict (key) where fruit like '%berry' do update set fruit = excluded.fruit;
220224
insert into insertconflicttest as t values (23, 'Blackberry') on conflict (key) where fruit like '%berry' and t.fruit = 'inconsequential' do nothing;
225+
insert into insertconflictview as t values (23, 'Blackberry') on conflict (key) where fruit like '%berry' and t.fruit = 'inconsequential' do nothing;
221226

222227
-- fails
223228
insert into insertconflicttest values (23, 'Blackberry') on conflict (key) do update set fruit = excluded.fruit;
@@ -250,6 +255,7 @@ explain (costs off) insert into insertconflicttest as i values (23, 'Avocado') o
250255
drop index plain;
251256

252257
-- Cleanup
258+
drop view insertconflictview;
253259
drop table insertconflicttest;
254260

255261

0 commit comments

Comments
 (0)