1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-31 22:04:40 +03:00

Allow UPDATE to move rows between partitions.

When an UPDATE causes a row to no longer match the partition
constraint, try to move it to a different partition where it does
match the partition constraint.  In essence, the UPDATE is split into
a DELETE from the old partition and an INSERT into the new one.  This
can lead to surprising behavior in concurrency scenarios because
EvalPlanQual rechecks won't work as they normally did; the known
problems are documented.  (There is a pending patch to improve the
situation further, but it needs more review.)

Amit Khandekar, reviewed and tested by Amit Langote, David Rowley,
Rajkumar Raghuwanshi, Dilip Kumar, Amul Sul, Thomas Munro, Álvaro
Herrera, Amit Kapila, and me.  A few final revisions by me.

Discussion: http://postgr.es/m/CAJ3gD9do9o2ccQ7j7+tSgiE1REY65XRiMb=yJO3u3QhyP8EEPQ@mail.gmail.com
This commit is contained in:
Robert Haas
2018-01-19 15:33:06 -05:00
parent 7f17fd6fc7
commit 2f17844104
27 changed files with 1959 additions and 276 deletions

View File

@ -3005,6 +3005,11 @@ VALUES ('Albany', NULL, NULL, 'NY');
foreign table partitions.
</para>
<para>
Updating the partition key of a row might cause it to be moved into a
different partition where this row satisfies its partition constraint.
</para>
<sect3 id="ddl-partitioning-declarative-example">
<title>Example</title>
@ -3302,9 +3307,22 @@ ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
<listitem>
<para>
An <command>UPDATE</command> that causes a row to move from one partition to
another fails, because the new value of the row fails to satisfy the
implicit partition constraint of the original partition.
When an <command>UPDATE</command> causes a row to move from one
partition to another, there is a chance that another concurrent
<command>UPDATE</command> or <command>DELETE</command> misses this row.
Suppose session 1 is performing an <command>UPDATE</command> on a
partition key, and meanwhile a concurrent session 2 for which this row
is visible performs an <command>UPDATE</command> or
<command>DELETE</command> operation on this row. Session 2 can silently
miss the row if the row is deleted from the partition due to session
1's activity. In such case, session 2's
<command>UPDATE</command> or <command>DELETE</command>, being unaware of
the row movement thinks that the row has just been deleted and concludes
that there is nothing to be done for this row. In the usual case where
the table is not partitioned, or where there is no row movement,
session 2 would have identified the newly updated row and carried out
the <command>UPDATE</command>/<command>DELETE</command> on this new row
version.
</para>
</listitem>

View File

@ -282,10 +282,15 @@ UPDATE <replaceable class="parameter">count</replaceable>
<para>
In the case of a partitioned table, updating a row might cause it to no
longer satisfy the partition constraint. Since there is no provision to
move the row to the partition appropriate to the new value of its
partitioning key, an error will occur in this case. This can also happen
when updating a partition directly.
longer satisfy the partition constraint of the containing partition. In that
case, if there is some other partition in the partition tree for which this
row satisfies its partition constraint, then the row is moved to that
partition. If there is no such partition, an error will occur. Behind the
scenes, the row movement is actually a <command>DELETE</command> and
<command>INSERT</command> operation. However, there is a possibility that a
concurrent <command>UPDATE</command> or <command>DELETE</command> on the
same row may miss this row. For details see the section
<xref linkend="ddl-partitioning-declarative-limitations"/>.
</para>
</refsect1>

View File

@ -153,6 +153,29 @@
triggers.
</para>
<para>
If an <command>UPDATE</command> on a partitioned table causes a row to move
to another partition, it will be performed as a <command>DELETE</command>
from the original partition followed by an <command>INSERT</command> into
the new partition. In this case, all row-level <literal>BEFORE</literal>
<command>UPDATE</command> triggers and all row-level
<literal>BEFORE</literal> <command>DELETE</command> triggers are fired on
the original partition. Then all row-level <literal>BEFORE</literal>
<command>INSERT</command> triggers are fired on the destination partition.
The possibility of surprising outcomes should be considered when all these
triggers affect the row being moved. As far as <literal>AFTER ROW</literal>
triggers are concerned, <literal>AFTER</literal> <command>DELETE</command>
and <literal>AFTER</literal> <command>INSERT</command> triggers are
applied; but <literal>AFTER</literal> <command>UPDATE</command> triggers
are not applied because the <command>UPDATE</command> has been converted to
a <command>DELETE</command> and an <command>INSERT</command>. As far as
statement-level triggers are concerned, none of the
<command>DELETE</command> or <command>INSERT</command> triggers are fired,
even if row movement occurs; only the <command>UPDATE</command> triggers
defined on the target table used in the <command>UPDATE</command> statement
will be fired.
</para>
<para>
Trigger functions invoked by per-statement triggers should always
return <symbol>NULL</symbol>. Trigger functions invoked by per-row