Hi.

On 2018/02/20 5:45, Alvaro Herrera wrote:
> I pushed this now, with fixes for the last few comments there were.

I noticed with the commit that, while ON CONFLICT (conflict_target) DO
UPDATE gives a less surprising error message by catching it in the parser,
ON CONFLICT (conflict_target) DO NOTHING will go into the executor without
the necessary code to handle the case.  Example:

create table p (a int primary key, b text) partition by list (a);
create table p12 partition of p for values in (1, 2);
create table p3 partition of p (a unique) for values in (3);

insert into p values (1, 'a') on conflict (a) do nothing;
ERROR:  unexpected failure to find arbiter index

Attached is a patch to fix that.  Actually, there are two -- one that
adjusts the partitioned table tests in insert_conflict.sql to have a
partitioned unique index and another that fixes the code.

I suppose we'd need to apply this temporarily until we fix the ON CONFLICT
(conflict_target) case to be able to use partitioned indexes.

Thanks,
Amit
From c5536f89c6723db7b2af8d93ab544254763b522a Mon Sep 17 00:00:00 2001
From: amit <amitlangot...@gmail.com>
Date: Tue, 20 Feb 2018 18:02:30 +0900
Subject: [PATCH v1 1/2] Adjust partitioned table tests in insert_conflict.sql

---
 src/test/regress/expected/insert_conflict.out | 24 +++++++++++++++++-------
 src/test/regress/sql/insert_conflict.sql      | 16 +++++++++-------
 2 files changed, 26 insertions(+), 14 deletions(-)

diff --git a/src/test/regress/expected/insert_conflict.out 
b/src/test/regress/expected/insert_conflict.out
index 2650faedee..a46fe7ec60 100644
--- a/src/test/regress/expected/insert_conflict.out
+++ b/src/test/regress/expected/insert_conflict.out
@@ -788,14 +788,24 @@ select * from selfconflict;
 drop table selfconflict;
 -- check that the following works:
 -- insert into partitioned_table on conflict do nothing
-create table parted_conflict_test (a int, b char) partition by list (a);
-create table parted_conflict_test_1 partition of parted_conflict_test (b 
unique) for values in (1);
-insert into parted_conflict_test values (1, 'a') on conflict do nothing;
-insert into parted_conflict_test values (1, 'a') on conflict do nothing;
+create table parted_conflict_test (a int unique, b char) partition by list (a);
+create table parted_conflict_test_1 partition of parted_conflict_test (b 
unique) for values in (1, 2);
+insert into parted_conflict_test values (1, 'a') on conflict (a) do nothing;
+ERROR:  unexpected failure to find arbiter index
+insert into parted_conflict_test values (1, 'a') on conflict (a) do nothing;
+ERROR:  unexpected failure to find arbiter index
 -- however, on conflict do update is not supported yet
-insert into parted_conflict_test values (1) on conflict (b) do update set a = 
excluded.a;
+insert into parted_conflict_test values (1, 'a') on conflict (b) do update set 
a = excluded.a;
+ERROR:  ON CONFLICT DO UPDATE cannot be applied to partitioned table 
"parted_conflict_test"
+insert into parted_conflict_test values (1, 'a') on conflict (a) do update set 
b = excluded.b;
 ERROR:  ON CONFLICT DO UPDATE cannot be applied to partitioned table 
"parted_conflict_test"
 -- but it works OK if we target the partition directly
-insert into parted_conflict_test_1 values (1) on conflict (b) do
-update set a = excluded.a;
+insert into parted_conflict_test_1 values (2, 'a') on conflict (b) do update 
set a = excluded.a;
+insert into parted_conflict_test_1 values (2, 'b') on conflict (a) do update 
set b = excluded.b;
+select * from parted_conflict_test order by a;
+ a | b 
+---+---
+ 2 | b
+(1 row)
+
 drop table parted_conflict_test;
diff --git a/src/test/regress/sql/insert_conflict.sql 
b/src/test/regress/sql/insert_conflict.sql
index 32c647e3f8..97affc3726 100644
--- a/src/test/regress/sql/insert_conflict.sql
+++ b/src/test/regress/sql/insert_conflict.sql
@@ -474,13 +474,15 @@ drop table selfconflict;
 
 -- check that the following works:
 -- insert into partitioned_table on conflict do nothing
-create table parted_conflict_test (a int, b char) partition by list (a);
-create table parted_conflict_test_1 partition of parted_conflict_test (b 
unique) for values in (1);
-insert into parted_conflict_test values (1, 'a') on conflict do nothing;
-insert into parted_conflict_test values (1, 'a') on conflict do nothing;
+create table parted_conflict_test (a int unique, b char) partition by list (a);
+create table parted_conflict_test_1 partition of parted_conflict_test (b 
unique) for values in (1, 2);
+insert into parted_conflict_test values (1, 'a') on conflict (a) do nothing;
+insert into parted_conflict_test values (1, 'a') on conflict (a) do nothing;
 -- however, on conflict do update is not supported yet
-insert into parted_conflict_test values (1) on conflict (b) do update set a = 
excluded.a;
+insert into parted_conflict_test values (1, 'a') on conflict (b) do update set 
a = excluded.a;
+insert into parted_conflict_test values (1, 'a') on conflict (a) do update set 
b = excluded.b;
 -- but it works OK if we target the partition directly
-insert into parted_conflict_test_1 values (1) on conflict (b) do
-update set a = excluded.a;
+insert into parted_conflict_test_1 values (2, 'a') on conflict (b) do update 
set a = excluded.a;
+insert into parted_conflict_test_1 values (2, 'b') on conflict (a) do update 
set b = excluded.b;
+select * from parted_conflict_test order by a;
 drop table parted_conflict_test;
-- 
2.11.0

From e88b485ec4aab08b80061c1899b38ad12736d8f9 Mon Sep 17 00:00:00 2001
From: amit <amitlangot...@gmail.com>
Date: Tue, 20 Feb 2018 18:02:59 +0900
Subject: [PATCH v1 2/2] Fix ON CONFLICT DO NOTHING with partitioned indexes

---
 src/backend/optimizer/plan/createplan.c       | 41 +++++++++++++++++++++------
 src/test/regress/expected/insert_conflict.out |  2 --
 2 files changed, 32 insertions(+), 11 deletions(-)

diff --git a/src/backend/optimizer/plan/createplan.c 
b/src/backend/optimizer/plan/createplan.c
index da0cc7f266..98825b0b27 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -6504,20 +6504,43 @@ make_modifytable(PlannerInfo *root,
        }
        else
        {
+               RangeTblEntry *rte;
+
                node->onConflictAction = onconflict->action;
-               node->onConflictSet = onconflict->onConflictSet;
-               node->onConflictWhere = onconflict->onConflictWhere;
 
                /*
-                * If a set of unique index inference elements was provided (an
-                * INSERT...ON CONFLICT "inference specification"), then infer
-                * appropriate unique indexes (or throw an error if none are
-                * available).
+                * Partitioned tables don't yet have the executor support needed
+                * to handle ON CONFLICT actions that rely on partitioned 
indexes,
+                * so leave the following fields unset for them.
                 */
-               node->arbiterIndexes = infer_arbiter_indexes(root);
 
-               node->exclRelRTI = onconflict->exclRelIndex;
-               node->exclRelTlist = onconflict->exclRelTlist;
+                /* Must have only one result relation in the case of INSERT. */
+               Assert(list_length(node->resultRelations) == 1);
+               rte = planner_rt_fetch(linitial_int(node->resultRelations), 
root);
+               Assert(rte->rtekind == RTE_RELATION);
+               if (rte->relkind != RELKIND_PARTITIONED_TABLE)
+               {
+                       node->onConflictSet = onconflict->onConflictSet;
+                       node->onConflictWhere = onconflict->onConflictWhere;
+
+                       /*
+                        * If a set of unique index inference elements was 
provided (an
+                        * INSERT...ON CONFLICT "inference specification"), 
then infer
+                        * appropriate unique indexes (or throw an error if 
none are
+                        * available).
+                        */
+                       node->arbiterIndexes = infer_arbiter_indexes(root);
+                       node->exclRelRTI = onconflict->exclRelIndex;
+                       node->exclRelTlist = onconflict->exclRelTlist;
+               }
+               else
+               {
+                       node->onConflictSet = NIL;
+                       node->onConflictWhere = NULL;
+                       node->arbiterIndexes = NIL;
+                       node->exclRelRTI = 0;
+                       node->exclRelTlist = NIL;
+               }
        }
        node->withCheckOptionLists = withCheckOptionLists;
        node->returningLists = returningLists;
diff --git a/src/test/regress/expected/insert_conflict.out 
b/src/test/regress/expected/insert_conflict.out
index a46fe7ec60..7fa4282e0d 100644
--- a/src/test/regress/expected/insert_conflict.out
+++ b/src/test/regress/expected/insert_conflict.out
@@ -791,9 +791,7 @@ drop table selfconflict;
 create table parted_conflict_test (a int unique, b char) partition by list (a);
 create table parted_conflict_test_1 partition of parted_conflict_test (b 
unique) for values in (1, 2);
 insert into parted_conflict_test values (1, 'a') on conflict (a) do nothing;
-ERROR:  unexpected failure to find arbiter index
 insert into parted_conflict_test values (1, 'a') on conflict (a) do nothing;
-ERROR:  unexpected failure to find arbiter index
 -- however, on conflict do update is not supported yet
 insert into parted_conflict_test values (1, 'a') on conflict (b) do update set 
a = excluded.a;
 ERROR:  ON CONFLICT DO UPDATE cannot be applied to partitioned table 
"parted_conflict_test"
-- 
2.11.0

Reply via email to