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

Commit 0294df2

Browse files
committed
Add support for MERGE ... WHEN NOT MATCHED BY SOURCE.
This allows MERGE commands to include WHEN NOT MATCHED BY SOURCE actions, which operate on rows that exist in the target relation, but not in the data source. These actions can execute UPDATE, DELETE, or DO NOTHING sub-commands. This is in contrast to already-supported WHEN NOT MATCHED actions, which operate on rows that exist in the data source, but not in the target relation. To make this distinction clearer, such actions may now be written as WHEN NOT MATCHED BY TARGET. Writing WHEN NOT MATCHED without specifying BY SOURCE or BY TARGET is equivalent to writing WHEN NOT MATCHED BY TARGET. Dean Rasheed, reviewed by Alvaro Herrera, Ted Yu and Vik Fearing. Discussion: https://postgr.es/m/CAEZATCWqnKGc57Y_JanUBHQXNKcXd7r=0R4NEZUVwP+syRkWbA@mail.gmail.com
1 parent 46e5441 commit 0294df2

33 files changed

+1226
-360
lines changed

doc/src/sgml/mvcc.sgml

+8-4
Original file line numberDiff line numberDiff line change
@@ -394,10 +394,14 @@
394394
conditions for each action are re-evaluated on the updated version of
395395
the row, starting from the first action, even if the action that had
396396
originally matched appears later in the list of actions.
397-
On the other hand, if the row is concurrently updated or deleted so
398-
that the join condition fails, then <command>MERGE</command> will
399-
evaluate the condition's <literal>NOT MATCHED</literal> actions next,
400-
and execute the first one that succeeds.
397+
On the other hand, if the row is concurrently updated so that the join
398+
condition fails, then <command>MERGE</command> will evaluate the
399+
command's <literal>NOT MATCHED BY SOURCE</literal> and
400+
<literal>NOT MATCHED [BY TARGET]</literal> actions next, and execute
401+
the first one of each kind that succeeds.
402+
If the row is concurrently deleted, then <command>MERGE</command>
403+
will evaluate the command's <literal>NOT MATCHED [BY TARGET]</literal>
404+
actions, and execute the first one that succeeds.
401405
If <command>MERGE</command> attempts an <command>INSERT</command>
402406
and a unique index is present and a duplicate row is concurrently
403407
inserted, then a uniqueness violation error is raised;

doc/src/sgml/ref/merge.sgml

+82-18
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@ USING <replaceable class="parameter">data_source</replaceable> ON <replaceable c
3434
<phrase>and <replaceable class="parameter">when_clause</replaceable> is:</phrase>
3535

3636
{ WHEN MATCHED [ AND <replaceable class="parameter">condition</replaceable> ] THEN { <replaceable class="parameter">merge_update</replaceable> | <replaceable class="parameter">merge_delete</replaceable> | DO NOTHING } |
37-
WHEN NOT MATCHED [ AND <replaceable class="parameter">condition</replaceable> ] THEN { <replaceable class="parameter">merge_insert</replaceable> | DO NOTHING } }
37+
WHEN NOT MATCHED BY SOURCE [ AND <replaceable class="parameter">condition</replaceable> ] THEN { <replaceable class="parameter">merge_update</replaceable> | <replaceable class="parameter">merge_delete</replaceable> | DO NOTHING } |
38+
WHEN NOT MATCHED [ BY TARGET ] [ AND <replaceable class="parameter">condition</replaceable> ] THEN { <replaceable class="parameter">merge_insert</replaceable> | DO NOTHING } }
3839

3940
<phrase>and <replaceable class="parameter">merge_insert</replaceable> is:</phrase>
4041

@@ -73,7 +74,9 @@ DELETE
7374
from <replaceable class="parameter">data_source</replaceable> to
7475
the target table
7576
producing zero or more candidate change rows. For each candidate change
76-
row, the status of <literal>MATCHED</literal> or <literal>NOT MATCHED</literal>
77+
row, the status of <literal>MATCHED</literal>,
78+
<literal>NOT MATCHED BY SOURCE</literal>,
79+
or <literal>NOT MATCHED [BY TARGET]</literal>
7780
is set just once, after which <literal>WHEN</literal> clauses are evaluated
7881
in the order specified. For each candidate change row, the first clause to
7982
evaluate as true is executed. No more than one <literal>WHEN</literal>
@@ -257,6 +260,16 @@ DELETE
257260
only reference the target table's
258261
columns can affect which action is taken, often in surprising ways.
259262
</para>
263+
<para>
264+
If both <literal>WHEN NOT MATCHED BY SOURCE</literal> and
265+
<literal>WHEN NOT MATCHED [BY TARGET]</literal> clauses are specified,
266+
the <command>MERGE</command> command will perform a <literal>FULL</literal>
267+
join between <replaceable class="parameter">data_source</replaceable>
268+
and the target table. For this to work, at least one
269+
<replaceable class="parameter">join_condition</replaceable> subexpression
270+
must use an operator that can support a hash join, or all of the
271+
subexpressions must use operators that can support a merge join.
272+
</para>
260273
</warning>
261274
</listitem>
262275
</varlistentry>
@@ -267,19 +280,41 @@ DELETE
267280
<para>
268281
At least one <literal>WHEN</literal> clause is required.
269282
</para>
283+
<para>
284+
The <literal>WHEN</literal> clause may specify <literal>WHEN MATCHED</literal>,
285+
<literal>WHEN NOT MATCHED BY SOURCE</literal>, or
286+
<literal>WHEN NOT MATCHED [BY TARGET]</literal>.
287+
Note that the <acronym>SQL</acronym> standard only defines
288+
<literal>WHEN MATCHED</literal> and <literal>WHEN NOT MATCHED</literal>
289+
(which is defined to mean no matching target row).
290+
<literal>WHEN NOT MATCHED BY SOURCE</literal> is an extension to the
291+
<acronym>SQL</acronym> standard, as is the option to append
292+
<literal>BY TARGET</literal> to <literal>WHEN NOT MATCHED</literal>, to
293+
make its meaning more explicit.
294+
</para>
270295
<para>
271296
If the <literal>WHEN</literal> clause specifies <literal>WHEN MATCHED</literal>
272297
and the candidate change row matches a row in the
273-
target table,
274-
the <literal>WHEN</literal> clause is executed if the
298+
<replaceable class="parameter">data_source</replaceable> to a row in the
299+
target table, the <literal>WHEN</literal> clause is executed if the
300+
<replaceable class="parameter">condition</replaceable> is
301+
absent or it evaluates to <literal>true</literal>.
302+
</para>
303+
<para>
304+
If the <literal>WHEN</literal> clause specifies
305+
<literal>WHEN NOT MATCHED BY SOURCE</literal> and the candidate change
306+
row represents a row in the target table that does not match a row in the
307+
<replaceable class="parameter">data_source</replaceable>, the
308+
<literal>WHEN</literal> clause is executed if the
275309
<replaceable class="parameter">condition</replaceable> is
276310
absent or it evaluates to <literal>true</literal>.
277311
</para>
278312
<para>
279-
Conversely, if the <literal>WHEN</literal> clause specifies
280-
<literal>WHEN NOT MATCHED</literal>
281-
and the candidate change row does not match a row in the
282-
target table,
313+
If the <literal>WHEN</literal> clause specifies
314+
<literal>WHEN NOT MATCHED [BY TARGET]</literal> and the candidate change
315+
row represents a row in the
316+
<replaceable class="parameter">data_source</replaceable> that does not
317+
match a row in the target table,
283318
the <literal>WHEN</literal> clause is executed if the
284319
<replaceable class="parameter">condition</replaceable> is
285320
absent or it evaluates to <literal>true</literal>.
@@ -299,7 +334,10 @@ DELETE
299334
<para>
300335
A condition on a <literal>WHEN MATCHED</literal> clause can refer to columns
301336
in both the source and the target relations. A condition on a
302-
<literal>WHEN NOT MATCHED</literal> clause can only refer to columns from
337+
<literal>WHEN NOT MATCHED BY SOURCE</literal> clause can only refer to
338+
columns from the target relation, since by definition there is no matching
339+
source row. A condition on a <literal>WHEN NOT MATCHED [BY TARGET]</literal>
340+
clause can only refer to columns from
303341
the source relation, since by definition there is no matching target row.
304342
Only the system attributes from the target table are accessible.
305343
</para>
@@ -423,8 +461,10 @@ DELETE
423461
<literal>WHEN MATCHED</literal> clause, the expression can use values
424462
from the original row in the target table, and values from the
425463
<replaceable class="parameter">data_source</replaceable> row.
426-
If used in a <literal>WHEN NOT MATCHED</literal> clause, the
427-
expression can use values from the
464+
If used in a <literal>WHEN NOT MATCHED BY SOURCE</literal> clause, the
465+
expression can only use values from the original row in the target table.
466+
If used in a <literal>WHEN NOT MATCHED [BY TARGET]</literal> clause, the
467+
expression can only use values from the
428468
<replaceable class="parameter">data_source</replaceable> row.
429469
</para>
430470
</listitem>
@@ -449,9 +489,12 @@ DELETE
449489
sub-query must yield no more than one row when executed. If it
450490
yields one row, its column values are assigned to the target columns;
451491
if it yields no rows, NULL values are assigned to the target columns.
452-
The sub-query can refer to values from the original row in the target table,
453-
and values from the <replaceable class="parameter">data_source</replaceable>
454-
row.
492+
If used in a <literal>WHEN MATCHED</literal> clause, the sub-query can
493+
refer to values from the original row in the target table, and values
494+
from the <replaceable class="parameter">data_source</replaceable> row.
495+
If used in a <literal>WHEN NOT MATCHED BY SOURCE</literal> clause, the
496+
sub-query can only refer to values from the original row in the target
497+
table.
455498
</para>
456499
</listitem>
457500
</varlistentry>
@@ -535,8 +578,9 @@ MERGE <replaceable class="parameter">total_count</replaceable>
535578
<orderedlist>
536579
<listitem>
537580
<para>
538-
Evaluate whether each row is <literal>MATCHED</literal> or
539-
<literal>NOT MATCHED</literal>.
581+
Evaluate whether each row is <literal>MATCHED</literal>,
582+
<literal>NOT MATCHED BY SOURCE</literal>, or
583+
<literal>NOT MATCHED [BY TARGET]</literal>.
540584
</para>
541585
</listitem>
542586
<listitem>
@@ -615,7 +659,8 @@ MERGE <replaceable class="parameter">total_count</replaceable>
615659
<para>
616660
If a <literal>WHEN</literal> clause omits an <literal>AND</literal>
617661
sub-clause, it becomes the final reachable clause of that
618-
kind (<literal>MATCHED</literal> or <literal>NOT MATCHED</literal>).
662+
kind (<literal>MATCHED</literal>, <literal>NOT MATCHED BY SOURCE</literal>,
663+
or <literal>NOT MATCHED [BY TARGET]</literal>).
619664
If a later <literal>WHEN</literal> clause of that kind
620665
is specified it would be provably unreachable and an error is raised.
621666
If no final reachable clause is specified of either kind, it is
@@ -701,6 +746,23 @@ RETURNING merge_action(), w.*;
701746
temporary table recently loaded into the database.
702747
</para>
703748

749+
<para>
750+
Update <literal>wines</literal> based on a replacement wine list, inserting
751+
rows for any new stock, updating modified stock entries, and deleting any
752+
wines not present in the new list.
753+
<programlisting>
754+
MERGE INTO wines w
755+
USING new_wine_list s
756+
ON s.winename = w.winename
757+
WHEN NOT MATCHED BY TARGET THEN
758+
INSERT VALUES(s.winename, s.stock)
759+
WHEN MATCHED AND w.stock != s.stock THEN
760+
UPDATE SET stock = s.stock
761+
WHEN NOT MATCHED BY SOURCE THEN
762+
DELETE;
763+
</programlisting>
764+
</para>
765+
704766
</refsect1>
705767

706768
<refsect1>
@@ -709,7 +771,9 @@ RETURNING merge_action(), w.*;
709771
This command conforms to the <acronym>SQL</acronym> standard.
710772
</para>
711773
<para>
712-
The <literal>WITH</literal> clause, <literal>DO NOTHING</literal> action,
774+
The <literal>WITH</literal> clause, <literal>BY SOURCE</literal> and
775+
<literal>BY TARGET</literal> qualifiers to
776+
<literal>WHEN NOT MATCHED</literal>, <literal>DO NOTHING</literal> action,
713777
and <literal>RETURNING</literal> clause are extensions to the
714778
<acronym>SQL</acronym> standard.
715779
</para>

src/backend/executor/execMain.c

+4-2
Original file line numberDiff line numberDiff line change
@@ -1251,8 +1251,10 @@ InitResultRelInfo(ResultRelInfo *resultRelInfo,
12511251
resultRelInfo->ri_ReturningSlot = NULL;
12521252
resultRelInfo->ri_TrigOldSlot = NULL;
12531253
resultRelInfo->ri_TrigNewSlot = NULL;
1254-
resultRelInfo->ri_matchedMergeAction = NIL;
1255-
resultRelInfo->ri_notMatchedMergeAction = NIL;
1254+
resultRelInfo->ri_MergeActions[MERGE_WHEN_MATCHED] = NIL;
1255+
resultRelInfo->ri_MergeActions[MERGE_WHEN_NOT_MATCHED_BY_SOURCE] = NIL;
1256+
resultRelInfo->ri_MergeActions[MERGE_WHEN_NOT_MATCHED_BY_TARGET] = NIL;
1257+
resultRelInfo->ri_MergeJoinCondition = NULL;
12561258

12571259
/*
12581260
* Only ExecInitPartitionInfo() and ExecInitPartitionDispatchInfo() pass

src/backend/executor/execPartition.c

+15-6
Original file line numberDiff line numberDiff line change
@@ -880,6 +880,7 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate,
880880
List *firstMergeActionList = linitial(node->mergeActionLists);
881881
ListCell *lc;
882882
ExprContext *econtext = mtstate->ps.ps_ExprContext;
883+
Node *joinCondition;
883884

884885
if (part_attmap == NULL)
885886
part_attmap =
@@ -890,23 +891,31 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate,
890891
if (unlikely(!leaf_part_rri->ri_projectNewInfoValid))
891892
ExecInitMergeTupleSlots(mtstate, leaf_part_rri);
892893

894+
/* Initialize state for join condition checking. */
895+
joinCondition =
896+
map_variable_attnos(linitial(node->mergeJoinConditions),
897+
firstVarno, 0,
898+
part_attmap,
899+
RelationGetForm(partrel)->reltype,
900+
&found_whole_row);
901+
/* We ignore the value of found_whole_row. */
902+
leaf_part_rri->ri_MergeJoinCondition =
903+
ExecInitQual((List *) joinCondition, &mtstate->ps);
904+
893905
foreach(lc, firstMergeActionList)
894906
{
895907
/* Make a copy for this relation to be safe. */
896908
MergeAction *action = copyObject(lfirst(lc));
897909
MergeActionState *action_state;
898-
List **list;
899910

900911
/* Generate the action's state for this relation */
901912
action_state = makeNode(MergeActionState);
902913
action_state->mas_action = action;
903914

904915
/* And put the action in the appropriate list */
905-
if (action->matched)
906-
list = &leaf_part_rri->ri_matchedMergeAction;
907-
else
908-
list = &leaf_part_rri->ri_notMatchedMergeAction;
909-
*list = lappend(*list, action_state);
916+
leaf_part_rri->ri_MergeActions[action->matchKind] =
917+
lappend(leaf_part_rri->ri_MergeActions[action->matchKind],
918+
action_state);
910919

911920
switch (action->commandType)
912921
{

0 commit comments

Comments
 (0)