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

Commit 555ee77

Browse files
committed
Handle INSERT .. ON CONFLICT with partitioned tables
Commit eb7ed3f enabled unique constraints on partitioned tables, but one thing that was not working properly is INSERT/ON CONFLICT. This commit introduces a new node keeps state related to the ON CONFLICT clause per partition, and fills it when that partition is about to be used for tuple routing. Author: Amit Langote, Álvaro Herrera Reviewed-by: Etsuro Fujita, Pavan Deolasee Discussion: https://postgr.es/m/20180228004602.cwdyralmg5ejdqkq@alvherre.pgsql
1 parent 1b89c21 commit 555ee77

File tree

14 files changed

+638
-84
lines changed

14 files changed

+638
-84
lines changed

doc/src/sgml/ddl.sgml

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3322,21 +3322,6 @@ ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
33223322
</para>
33233323
</listitem>
33243324

3325-
<listitem>
3326-
<para>
3327-
Using the <literal>ON CONFLICT</literal> clause with partitioned tables
3328-
will cause an error if the conflict target is specified (see
3329-
<xref linkend="sql-on-conflict" /> for more details on how the clause
3330-
works). Therefore, it is not possible to specify
3331-
<literal>DO UPDATE</literal> as the alternative action, because
3332-
specifying the conflict target is mandatory in that case. On the other
3333-
hand, specifying <literal>DO NOTHING</literal> as the alternative action
3334-
works fine provided the conflict target is not specified. In that case,
3335-
unique constraints (or exclusion constraints) of the individual leaf
3336-
partitions are considered.
3337-
</para>
3338-
</listitem>
3339-
33403325
<listitem>
33413326
<para>
33423327
When an <command>UPDATE</command> causes a row to move from one

doc/src/sgml/ref/insert.sgml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -518,6 +518,14 @@ INSERT INTO <replaceable class="parameter">table_name</replaceable> [ AS <replac
518518
not duplicate each other in terms of attributes constrained by an
519519
arbiter index or constraint.
520520
</para>
521+
522+
<para>
523+
Note that it is currently not supported for the
524+
<literal>ON CONFLICT DO UPDATE</literal> clause of an
525+
<command>INSERT</command> applied to a partitioned table to update the
526+
partition key of a conflicting row such that it requires the row be moved
527+
to a new partition.
528+
</para>
521529
<tip>
522530
<para>
523531
It is often preferable to use unique index inference rather than

src/backend/catalog/partition.c

Lines changed: 77 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,10 @@ typedef struct PartitionRangeBound
138138
bool lower; /* this is the lower (vs upper) bound */
139139
} PartitionRangeBound;
140140

141+
142+
static Oid get_partition_parent_worker(Relation inhRel, Oid relid);
143+
static void get_partition_ancestors_worker(Relation inhRel, Oid relid,
144+
List **ancestors);
141145
static int32 qsort_partition_hbound_cmp(const void *a, const void *b);
142146
static int32 qsort_partition_list_value_cmp(const void *a, const void *b,
143147
void *arg);
@@ -1377,6 +1381,7 @@ check_default_allows_bound(Relation parent, Relation default_rel,
13771381

13781382
/*
13791383
* get_partition_parent
1384+
* Obtain direct parent of given relation
13801385
*
13811386
* Returns inheritance parent of a partition by scanning pg_inherits
13821387
*
@@ -1387,15 +1392,34 @@ check_default_allows_bound(Relation parent, Relation default_rel,
13871392
Oid
13881393
get_partition_parent(Oid relid)
13891394
{
1390-
Form_pg_inherits form;
13911395
Relation catalogRelation;
1392-
SysScanDesc scan;
1393-
ScanKeyData key[2];
1394-
HeapTuple tuple;
13951396
Oid result;
13961397

13971398
catalogRelation = heap_open(InheritsRelationId, AccessShareLock);
13981399

1400+
result = get_partition_parent_worker(catalogRelation, relid);
1401+
1402+
if (!OidIsValid(result))
1403+
elog(ERROR, "could not find tuple for parent of relation %u", relid);
1404+
1405+
heap_close(catalogRelation, AccessShareLock);
1406+
1407+
return result;
1408+
}
1409+
1410+
/*
1411+
* get_partition_parent_worker
1412+
* Scan the pg_inherits relation to return the OID of the parent of the
1413+
* given relation
1414+
*/
1415+
static Oid
1416+
get_partition_parent_worker(Relation inhRel, Oid relid)
1417+
{
1418+
SysScanDesc scan;
1419+
ScanKeyData key[2];
1420+
Oid result = InvalidOid;
1421+
HeapTuple tuple;
1422+
13991423
ScanKeyInit(&key[0],
14001424
Anum_pg_inherits_inhrelid,
14011425
BTEqualStrategyNumber, F_OIDEQ,
@@ -1405,22 +1429,64 @@ get_partition_parent(Oid relid)
14051429
BTEqualStrategyNumber, F_INT4EQ,
14061430
Int32GetDatum(1));
14071431

1408-
scan = systable_beginscan(catalogRelation, InheritsRelidSeqnoIndexId, true,
1432+
scan = systable_beginscan(inhRel, InheritsRelidSeqnoIndexId, true,
14091433
NULL, 2, key);
1410-
14111434
tuple = systable_getnext(scan);
1412-
if (!HeapTupleIsValid(tuple))
1413-
elog(ERROR, "could not find tuple for parent of relation %u", relid);
1435+
if (HeapTupleIsValid(tuple))
1436+
{
1437+
Form_pg_inherits form = (Form_pg_inherits) GETSTRUCT(tuple);
14141438

1415-
form = (Form_pg_inherits) GETSTRUCT(tuple);
1416-
result = form->inhparent;
1439+
result = form->inhparent;
1440+
}
14171441

14181442
systable_endscan(scan);
1419-
heap_close(catalogRelation, AccessShareLock);
14201443

14211444
return result;
14221445
}
14231446

1447+
/*
1448+
* get_partition_ancestors
1449+
* Obtain ancestors of given relation
1450+
*
1451+
* Returns a list of ancestors of the given relation.
1452+
*
1453+
* Note: Because this function assumes that the relation whose OID is passed
1454+
* as an argument and each ancestor will have precisely one parent, it should
1455+
* only be called when it is known that the relation is a partition.
1456+
*/
1457+
List *
1458+
get_partition_ancestors(Oid relid)
1459+
{
1460+
List *result = NIL;
1461+
Relation inhRel;
1462+
1463+
inhRel = heap_open(InheritsRelationId, AccessShareLock);
1464+
1465+
get_partition_ancestors_worker(inhRel, relid, &result);
1466+
1467+
heap_close(inhRel, AccessShareLock);
1468+
1469+
return result;
1470+
}
1471+
1472+
/*
1473+
* get_partition_ancestors_worker
1474+
* recursive worker for get_partition_ancestors
1475+
*/
1476+
static void
1477+
get_partition_ancestors_worker(Relation inhRel, Oid relid, List **ancestors)
1478+
{
1479+
Oid parentOid;
1480+
1481+
/* Recursion ends at the topmost level, ie., when there's no parent */
1482+
parentOid = get_partition_parent_worker(inhRel, relid);
1483+
if (parentOid == InvalidOid)
1484+
return;
1485+
1486+
*ancestors = lappend_oid(*ancestors, parentOid);
1487+
get_partition_ancestors_worker(inhRel, parentOid, ancestors);
1488+
}
1489+
14241490
/*
14251491
* get_qual_from_partbound
14261492
* Given a parser node for partition bound, return the list of executable

src/backend/executor/execMain.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1347,11 +1347,15 @@ InitResultRelInfo(ResultRelInfo *resultRelInfo,
13471347
resultRelInfo->ri_FdwRoutine = GetFdwRoutineForRelation(resultRelationDesc, true);
13481348
else
13491349
resultRelInfo->ri_FdwRoutine = NULL;
1350+
1351+
/* The following fields are set later if needed */
13501352
resultRelInfo->ri_FdwState = NULL;
13511353
resultRelInfo->ri_usesFdwDirectModify = false;
13521354
resultRelInfo->ri_ConstraintExprs = NULL;
13531355
resultRelInfo->ri_junkFilter = NULL;
13541356
resultRelInfo->ri_projectReturning = NULL;
1357+
resultRelInfo->ri_onConflictArbiterIndexes = NIL;
1358+
resultRelInfo->ri_onConflict = NULL;
13551359

13561360
/*
13571361
* Partition constraint, which also includes the partition constraint of

0 commit comments

Comments
 (0)