Commit 87c37e32 authored by Robert Haas's avatar Robert Haas

Re-allow INSERT .. ON CONFLICT DO NOTHING on partitioned tables.

Commit 8355a011 was reverted in
f0523075, but this attempt is
hopefully better-considered: we now pass the correct value to
ExecOpenIndices, which should avoid the crash that we hit before.

Amit Langote, reviewed by Simon Riggs and by me.  Some final
editing by me.

Discussion: http://postgr.es/m/7ff1e8ec-dc39-96b1-7f47-ff5965dceeac@lab.ntt.co.jp
parent 1cbc17aa
...@@ -3288,10 +3288,15 @@ ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02 ...@@ -3288,10 +3288,15 @@ ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
<listitem> <listitem>
<para> <para>
Using the <literal>ON CONFLICT</literal> clause with partitioned tables Using the <literal>ON CONFLICT</literal> clause with partitioned tables
will cause an error, because unique or exclusion constraints can only be will cause an error if the conflict target is specified (see
created on individual partitions. There is no support for enforcing <xref linkend="sql-on-conflict" /> for more details on how the clause
uniqueness (or an exclusion constraint) across an entire partitioning works). Therefore, it is not possible to specify
hierarchy. <literal>DO UPDATE</literal> as the alternative action, because
specifying the conflict target is mandatory in that case. On the other
hand, specifying <literal>DO NOTHING</literal> as the alternative action
works fine provided the conflict target is not specified. In that case,
unique constraints (or exclusion constraints) of the individual leaf
partitions are considered.
</para> </para>
</listitem> </listitem>
......
...@@ -2478,7 +2478,8 @@ CopyFrom(CopyState cstate) ...@@ -2478,7 +2478,8 @@ CopyFrom(CopyState cstate)
int num_parted, int num_parted,
num_partitions; num_partitions;
ExecSetupPartitionTupleRouting(cstate->rel, ExecSetupPartitionTupleRouting(NULL,
cstate->rel,
1, 1,
estate, estate,
&partition_dispatch_info, &partition_dispatch_info,
......
...@@ -63,7 +63,8 @@ static char *ExecBuildSlotPartitionKeyDescription(Relation rel, ...@@ -63,7 +63,8 @@ static char *ExecBuildSlotPartitionKeyDescription(Relation rel,
* RowExclusiveLock mode upon return from this function. * RowExclusiveLock mode upon return from this function.
*/ */
void void
ExecSetupPartitionTupleRouting(Relation rel, ExecSetupPartitionTupleRouting(ModifyTableState *mtstate,
Relation rel,
Index resultRTindex, Index resultRTindex,
EState *estate, EState *estate,
PartitionDispatch **pd, PartitionDispatch **pd,
...@@ -133,13 +134,17 @@ ExecSetupPartitionTupleRouting(Relation rel, ...@@ -133,13 +134,17 @@ ExecSetupPartitionTupleRouting(Relation rel,
CheckValidResultRel(leaf_part_rri, CMD_INSERT); CheckValidResultRel(leaf_part_rri, CMD_INSERT);
/* /*
* Open partition indices (remember we do not support ON CONFLICT in * Open partition indices. The user may have asked to check for
* case of partitioned tables, so we do not need support information * conflicts within this leaf partition and do "nothing" instead of
* for speculative insertion) * throwing an error. Be prepared in that case by initializing the
* index information needed by ExecInsert() to perform speculative
* insertions.
*/ */
if (leaf_part_rri->ri_RelationDesc->rd_rel->relhasindex && if (leaf_part_rri->ri_RelationDesc->rd_rel->relhasindex &&
leaf_part_rri->ri_IndexRelationDescs == NULL) leaf_part_rri->ri_IndexRelationDescs == NULL)
ExecOpenIndices(leaf_part_rri, false); ExecOpenIndices(leaf_part_rri,
mtstate != NULL &&
mtstate->mt_onconflict != ONCONFLICT_NONE);
estate->es_leaf_result_relations = estate->es_leaf_result_relations =
lappend(estate->es_leaf_result_relations, leaf_part_rri); lappend(estate->es_leaf_result_relations, leaf_part_rri);
......
...@@ -1953,7 +1953,8 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) ...@@ -1953,7 +1953,8 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
int num_parted, int num_parted,
num_partitions; num_partitions;
ExecSetupPartitionTupleRouting(rel, ExecSetupPartitionTupleRouting(mtstate,
rel,
node->nominalRelation, node->nominalRelation,
estate, estate,
&partition_dispatch_info, &partition_dispatch_info,
......
...@@ -847,16 +847,8 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) ...@@ -847,16 +847,8 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
/* Process ON CONFLICT, if any. */ /* Process ON CONFLICT, if any. */
if (stmt->onConflictClause) if (stmt->onConflictClause)
{
/* Bail out if target relation is partitioned table */
if (pstate->p_target_rangetblentry->relkind == RELKIND_PARTITIONED_TABLE)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("ON CONFLICT clause is not supported with partitioned tables")));
qry->onConflict = transformOnConflictClause(pstate, qry->onConflict = transformOnConflictClause(pstate,
stmt->onConflictClause); stmt->onConflictClause);
}
/* /*
* If we have a RETURNING clause, we need to add the target relation to * If we have a RETURNING clause, we need to add the target relation to
......
...@@ -49,7 +49,8 @@ typedef struct PartitionDispatchData ...@@ -49,7 +49,8 @@ typedef struct PartitionDispatchData
typedef struct PartitionDispatchData *PartitionDispatch; typedef struct PartitionDispatchData *PartitionDispatch;
extern void ExecSetupPartitionTupleRouting(Relation rel, extern void ExecSetupPartitionTupleRouting(ModifyTableState *mtstate,
Relation rel,
Index resultRTindex, Index resultRTindex,
EState *estate, EState *estate,
PartitionDispatch **pd, PartitionDispatch **pd,
......
...@@ -786,3 +786,16 @@ select * from selfconflict; ...@@ -786,3 +786,16 @@ select * from selfconflict;
(3 rows) (3 rows)
drop table 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;
-- 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;
ERROR: there is no unique or exclusion constraint matching the ON CONFLICT specification
-- 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;
drop table parted_conflict_test;
...@@ -471,3 +471,16 @@ commit; ...@@ -471,3 +471,16 @@ commit;
select * from selfconflict; select * from selfconflict;
drop table 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;
-- 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;
-- 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;
drop table parted_conflict_test;
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment