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

Commit 354f138

Browse files
Modified files for MERGE
1 parent e6597dc commit 354f138

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

82 files changed

+2570
-165
lines changed

contrib/test_decoding/expected/ddl.out

+46
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,52 @@ SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'inc
192192
COMMIT
193193
(33 rows)
194194

195+
-- MERGE support
196+
BEGIN;
197+
MERGE INTO replication_example t
198+
USING (SELECT i as id, i as data, i as num FROM generate_series(-20, 5) i) s
199+
ON t.id = s.id
200+
WHEN MATCHED AND t.id < 0 THEN
201+
UPDATE SET somenum = somenum + 1
202+
WHEN MATCHED AND t.id >= 0 THEN
203+
DELETE
204+
WHEN NOT MATCHED THEN
205+
INSERT VALUES (s.*);
206+
COMMIT;
207+
/* display results */
208+
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
209+
data
210+
--------------------------------------------------------------------------------------------------------------------------------------------------
211+
BEGIN
212+
table public.replication_example: INSERT: id[integer]:-20 somedata[integer]:-20 somenum[integer]:-20 zaphod1[integer]:null zaphod2[integer]:null
213+
table public.replication_example: INSERT: id[integer]:-19 somedata[integer]:-19 somenum[integer]:-19 zaphod1[integer]:null zaphod2[integer]:null
214+
table public.replication_example: INSERT: id[integer]:-18 somedata[integer]:-18 somenum[integer]:-18 zaphod1[integer]:null zaphod2[integer]:null
215+
table public.replication_example: INSERT: id[integer]:-17 somedata[integer]:-17 somenum[integer]:-17 zaphod1[integer]:null zaphod2[integer]:null
216+
table public.replication_example: INSERT: id[integer]:-16 somedata[integer]:-16 somenum[integer]:-16 zaphod1[integer]:null zaphod2[integer]:null
217+
table public.replication_example: UPDATE: id[integer]:-15 somedata[integer]:-15 somenum[integer]:-14 zaphod1[integer]:null zaphod2[integer]:null
218+
table public.replication_example: UPDATE: id[integer]:-14 somedata[integer]:-14 somenum[integer]:-13 zaphod1[integer]:null zaphod2[integer]:null
219+
table public.replication_example: UPDATE: id[integer]:-13 somedata[integer]:-13 somenum[integer]:-12 zaphod1[integer]:null zaphod2[integer]:null
220+
table public.replication_example: UPDATE: id[integer]:-12 somedata[integer]:-12 somenum[integer]:-11 zaphod1[integer]:null zaphod2[integer]:null
221+
table public.replication_example: UPDATE: id[integer]:-11 somedata[integer]:-11 somenum[integer]:-10 zaphod1[integer]:null zaphod2[integer]:null
222+
table public.replication_example: UPDATE: id[integer]:-10 somedata[integer]:-10 somenum[integer]:-9 zaphod1[integer]:null zaphod2[integer]:null
223+
table public.replication_example: UPDATE: id[integer]:-9 somedata[integer]:-9 somenum[integer]:-8 zaphod1[integer]:null zaphod2[integer]:null
224+
table public.replication_example: UPDATE: id[integer]:-8 somedata[integer]:-8 somenum[integer]:-7 zaphod1[integer]:null zaphod2[integer]:null
225+
table public.replication_example: UPDATE: id[integer]:-7 somedata[integer]:-7 somenum[integer]:-6 zaphod1[integer]:null zaphod2[integer]:null
226+
table public.replication_example: UPDATE: id[integer]:-6 somedata[integer]:-6 somenum[integer]:-5 zaphod1[integer]:null zaphod2[integer]:null
227+
table public.replication_example: UPDATE: id[integer]:-5 somedata[integer]:-5 somenum[integer]:-4 zaphod1[integer]:null zaphod2[integer]:null
228+
table public.replication_example: UPDATE: id[integer]:-4 somedata[integer]:-4 somenum[integer]:-3 zaphod1[integer]:null zaphod2[integer]:null
229+
table public.replication_example: UPDATE: id[integer]:-3 somedata[integer]:-3 somenum[integer]:-2 zaphod1[integer]:null zaphod2[integer]:null
230+
table public.replication_example: UPDATE: id[integer]:-2 somedata[integer]:-2 somenum[integer]:-1 zaphod1[integer]:null zaphod2[integer]:null
231+
table public.replication_example: UPDATE: id[integer]:-1 somedata[integer]:-1 somenum[integer]:0 zaphod1[integer]:null zaphod2[integer]:null
232+
table public.replication_example: DELETE: id[integer]:0
233+
table public.replication_example: DELETE: id[integer]:1
234+
table public.replication_example: DELETE: id[integer]:2
235+
table public.replication_example: DELETE: id[integer]:3
236+
table public.replication_example: DELETE: id[integer]:4
237+
table public.replication_example: DELETE: id[integer]:5
238+
COMMIT
239+
(28 rows)
240+
195241
CREATE TABLE tr_unique(id2 serial unique NOT NULL, data int);
196242
INSERT INTO tr_unique(data) VALUES(10);
197243
ALTER TABLE tr_unique RENAME TO tr_pkey;

contrib/test_decoding/sql/ddl.sql

+16
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,22 @@ COMMIT;
9393
/* display results */
9494
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
9595

96+
-- MERGE support
97+
BEGIN;
98+
MERGE INTO replication_example t
99+
USING (SELECT i as id, i as data, i as num FROM generate_series(-20, 5) i) s
100+
ON t.id = s.id
101+
WHEN MATCHED AND t.id < 0 THEN
102+
UPDATE SET somenum = somenum + 1
103+
WHEN MATCHED AND t.id >= 0 THEN
104+
DELETE
105+
WHEN NOT MATCHED THEN
106+
INSERT VALUES (s.*);
107+
COMMIT;
108+
109+
/* display results */
110+
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
111+
96112
CREATE TABLE tr_unique(id2 serial unique NOT NULL, data int);
97113
INSERT INTO tr_unique(data) VALUES(10);
98114
ALTER TABLE tr_unique RENAME TO tr_pkey;

doc/src/sgml/libpq.sgml

+5-3
Original file line numberDiff line numberDiff line change
@@ -3917,9 +3917,11 @@ char *PQcmdTuples(PGresult *res);
39173917
<structname>PGresult</structname>. This function can only be used following
39183918
the execution of a <command>SELECT</command>, <command>CREATE TABLE AS</command>,
39193919
<command>INSERT</command>, <command>UPDATE</command>, <command>DELETE</command>,
3920-
<command>MOVE</command>, <command>FETCH</command>, or <command>COPY</command> statement,
3921-
or an <command>EXECUTE</command> of a prepared query that contains an
3922-
<command>INSERT</command>, <command>UPDATE</command>, or <command>DELETE</command> statement.
3920+
<command>MERGE</command>, <command>MOVE</command>, <command>FETCH</command>,
3921+
or <command>COPY</command> statement, or an <command>EXECUTE</command> of a
3922+
prepared query that contains an <command>INSERT</command>,
3923+
<command>UPDATE</command>, <command>DELETE</command>
3924+
or <command>MERGE</command> statement.
39233925
If the command that generated the <structname>PGresult</structname> was anything
39243926
else, <function>PQcmdTuples</function> returns an empty string. The caller
39253927
should not free the return value directly. It will be freed when

doc/src/sgml/mvcc.sgml

+27-1
Original file line numberDiff line numberDiff line change
@@ -422,6 +422,31 @@ COMMIT;
422422
<literal>11</literal>, which no longer matches the criteria.
423423
</para>
424424

425+
<para>
426+
The <command>MERGE</command> allows the user to specify various combinations
427+
of <command>INSERT</command>, <command>UPDATE</command> or
428+
<command>DELETE</command> subcommands. A <command>MERGE</command> command
429+
with both <command>INSERT</command> and <command>UPDATE</command>
430+
subcommands looks similar to <command>INSERT</command> with an
431+
<literal>ON CONFLICT DO UPDATE</literal> clause but does not guarantee
432+
that either <command>INSERT</command> and <command>UPDATE</command> will occur.
433+
434+
If MERGE attempts an UPDATE or DELETE and the row is concurrently updated
435+
but the join condition still passes for the current target and the current
436+
source tuple, then MERGE will behave the same as the UPDATE or DELETE commands
437+
and perform its action on the latest version of the row, using standard
438+
EvalPlanQual. MERGE actions can be conditional, so conditions must be
439+
re-evaluated on the latest row, starting from the first action.
440+
441+
On the other hand, if the row is concurrently updated or deleted so that
442+
the join condition fails, then MERGE will execute a NOT MATCHED action, if it
443+
exists and the AND WHEN qual evaluates to true.
444+
445+
If MERGE attempts an INSERT and a unique index is present and a duplicate
446+
row is concurrently inserted then a uniqueness violation is raised. MERGE
447+
does not attempt to avoid the ERROR by attempting an UPDATE.
448+
</para>
449+
425450
<para>
426451
Because Read Committed mode starts each command with a new snapshot
427452
that includes all transactions committed up to that instant,
@@ -900,7 +925,8 @@ ERROR: could not serialize access due to read/write dependencies among transact
900925

901926
<para>
902927
The commands <command>UPDATE</command>,
903-
<command>DELETE</command>, and <command>INSERT</command>
928+
<command>DELETE</command>, <command>INSERT</command> and
929+
<command>MERGE</command>
904930
acquire this lock mode on the target table (in addition to
905931
<literal>ACCESS SHARE</literal> locks on any other referenced
906932
tables). In general, this lock mode will be acquired by any

doc/src/sgml/plpgsql.sgml

+2-1
Original file line numberDiff line numberDiff line change
@@ -1246,7 +1246,7 @@ EXECUTE format('SELECT count(*) FROM %I '
12461246
</programlisting>
12471247
Another restriction on parameter symbols is that they only work in
12481248
<command>SELECT</command>, <command>INSERT</command>, <command>UPDATE</command>, and
1249-
<command>DELETE</command> commands. In other statement
1249+
<command>DELETE</command> and <command>MERGE</command> commands. In other statement
12501250
types (generically called utility statements), you must insert
12511251
values textually even if they are just data values.
12521252
</para>
@@ -1529,6 +1529,7 @@ GET DIAGNOSTICS integer_var = ROW_COUNT;
15291529
<listitem>
15301530
<para>
15311531
<command>UPDATE</command>, <command>INSERT</command>, and <command>DELETE</command>
1532+
and <command>MERGE</command>
15321533
statements set <literal>FOUND</literal> true if at least one
15331534
row is affected, false if no row is affected.
15341535
</para>

doc/src/sgml/ref/allfiles.sgml

+1
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ Complete list of usable sgml source files in this directory.
159159
<!ENTITY load SYSTEM "load.sgml">
160160
<!ENTITY lock SYSTEM "lock.sgml">
161161
<!ENTITY move SYSTEM "move.sgml">
162+
<!ENTITY merge SYSTEM "merge.sgml">
162163
<!ENTITY notify SYSTEM "notify.sgml">
163164
<!ENTITY prepare SYSTEM "prepare.sgml">
164165
<!ENTITY prepareTransaction SYSTEM "prepare_transaction.sgml">

doc/src/sgml/ref/create_policy.sgml

+7
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,13 @@ CREATE POLICY <replaceable class="parameter">name</replaceable> ON <replaceable
9494
exist, a <quote>default deny</quote> policy is assumed, so that no rows will
9595
be visible or updatable.
9696
</para>
97+
98+
<para>
99+
No separate policy exists for <command>MERGE</command>. Instead policies
100+
defined for <literal>SELECT</literal>, <literal>INSERT</literal>,
101+
<literal>UPDATE</literal> and <literal>DELETE</literal> are applied
102+
while executing MERGE, depending on the actions that are activated.
103+
</para>
97104
</refsect1>
98105

99106
<refsect1>

doc/src/sgml/ref/insert.sgml

+10-1
Original file line numberDiff line numberDiff line change
@@ -579,6 +579,13 @@ INSERT <replaceable>oid</replaceable> <replaceable class="parameter">count</repl
579579
is a partition, an error will occur if one of the input rows violates
580580
the partition constraint.
581581
</para>
582+
583+
<para>
584+
You may also wish to consider using <command>MERGE</command>, since that
585+
allows mixed <command>INSERT</command>, <command>UPDATE</command> and
586+
<command>DELETE</command> within a single statement.
587+
See <xref linkend="sql-merge"/>.
588+
</para>
582589
</refsect1>
583590

584591
<refsect1>
@@ -749,7 +756,9 @@ INSERT INTO distributors (did, dname) VALUES (10, 'Conrad International')
749756
Also, the case in
750757
which a column name list is omitted, but not all the columns are
751758
filled from the <literal>VALUES</literal> clause or <replaceable>query</replaceable>,
752-
is disallowed by the standard.
759+
is disallowed by the standard. If you prefer a more SQL Standard
760+
conforming statement than <literal>ON CONFLICT</literal>, see
761+
<xref linkend="sql-merge"/>.
753762
</para>
754763

755764
<para>

doc/src/sgml/reference.sgml

+1
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,7 @@
186186
&listen;
187187
&load;
188188
&lock;
189+
&merge;
189190
&move;
190191
&notify;
191192
&prepare;

doc/src/sgml/trigger.sgml

+20
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,26 @@
182182
will be fired.
183183
</para>
184184

185+
<para>
186+
No separate triggers are defined for <command>MERGE</command>. Instead,
187+
statement-level or row-level <command>UPDATE</command>,
188+
<command>DELETE</command> and <command>INSERT</command> triggers are fired
189+
depending on what actions are specified in the <command>MERGE</command> query
190+
and what actions are activated.
191+
</para>
192+
193+
<para>
194+
While running a <command>MERGE</command> command, statement-level
195+
<literal>BEFORE</literal> and <literal>AFTER</literal> triggers are fired for
196+
events specified in the actions of the <command>MERGE</command> command,
197+
irrespective of whether the action is finally activated or not. This is same as
198+
an <command>UPDATE</command> statement that updates no rows, yet
199+
statement-level triggers are fired. The row-level triggers are fired only
200+
when a row is actually updated, inserted or deleted. So it's perfectly legal
201+
that while statement-level triggers are fired for certain type of action, no
202+
row-level triggers are fired for the same kind of action.
203+
</para>
204+
185205
<para>
186206
Trigger functions invoked by per-statement triggers should always
187207
return <symbol>NULL</symbol>. Trigger functions invoked by per-row

src/backend/access/heap/heapam.c

+16-12
Original file line numberDiff line numberDiff line change
@@ -3245,6 +3245,7 @@ heap_delete(Relation relation, ItemPointer tid,
32453245
result == HeapTupleUpdated ||
32463246
result == HeapTupleBeingUpdated);
32473247
Assert(!(tp.t_data->t_infomask & HEAP_XMAX_INVALID));
3248+
hufd->result = result;
32483249
hufd->ctid = tp.t_data->t_ctid;
32493250
hufd->xmax = HeapTupleHeaderGetUpdateXid(tp.t_data);
32503251
if (result == HeapTupleSelfUpdated)
@@ -3507,7 +3508,7 @@ simple_heap_delete(Relation relation, ItemPointer tid)
35073508
HTSU_Result
35083509
heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
35093510
CommandId cid, Snapshot crosscheck, bool wait,
3510-
HeapUpdateFailureData *hufd, LockTupleMode *lockmode)
3511+
HeapUpdateFailureData *hufd)
35113512
{
35123513
HTSU_Result result;
35133514
TransactionId xid = GetCurrentTransactionId();
@@ -3547,8 +3548,10 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
35473548
infomask2_old_tuple,
35483549
infomask_new_tuple,
35493550
infomask2_new_tuple;
3551+
LockTupleMode lockmode;
35503552

35513553
Assert(ItemPointerIsValid(otid));
3554+
Assert(hufd != NULL);
35523555

35533556
/*
35543557
* Forbid this during a parallel operation, lest it allocate a combocid.
@@ -3664,7 +3667,7 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
36643667
*/
36653668
if (!bms_overlap(modified_attrs, key_attrs))
36663669
{
3667-
*lockmode = LockTupleNoKeyExclusive;
3670+
lockmode = hufd->lockmode = LockTupleNoKeyExclusive;
36683671
mxact_status = MultiXactStatusNoKeyUpdate;
36693672
key_intact = true;
36703673

@@ -3681,7 +3684,7 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
36813684
}
36823685
else
36833686
{
3684-
*lockmode = LockTupleExclusive;
3687+
lockmode = hufd->lockmode = LockTupleExclusive;
36853688
mxact_status = MultiXactStatusUpdate;
36863689
key_intact = false;
36873690
}
@@ -3759,12 +3762,12 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
37593762
int remain;
37603763

37613764
if (DoesMultiXactIdConflict((MultiXactId) xwait, infomask,
3762-
*lockmode))
3765+
lockmode))
37633766
{
37643767
LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
37653768

37663769
/* acquire tuple lock, if necessary */
3767-
heap_acquire_tuplock(relation, &(oldtup.t_self), *lockmode,
3770+
heap_acquire_tuplock(relation, &(oldtup.t_self), lockmode,
37683771
LockWaitBlock, &have_tuple_lock);
37693772

37703773
/* wait for multixact */
@@ -3848,7 +3851,7 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
38483851
* lock.
38493852
*/
38503853
LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
3851-
heap_acquire_tuplock(relation, &(oldtup.t_self), *lockmode,
3854+
heap_acquire_tuplock(relation, &(oldtup.t_self), lockmode,
38523855
LockWaitBlock, &have_tuple_lock);
38533856
XactLockTableWait(xwait, relation, &oldtup.t_self,
38543857
XLTW_Update);
@@ -3887,6 +3890,7 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
38873890
result == HeapTupleUpdated ||
38883891
result == HeapTupleBeingUpdated);
38893892
Assert(!(oldtup.t_data->t_infomask & HEAP_XMAX_INVALID));
3893+
hufd->result = result;
38903894
hufd->ctid = oldtup.t_data->t_ctid;
38913895
hufd->xmax = HeapTupleHeaderGetUpdateXid(oldtup.t_data);
38923896
if (result == HeapTupleSelfUpdated)
@@ -3895,7 +3899,7 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
38953899
hufd->cmax = InvalidCommandId;
38963900
UnlockReleaseBuffer(buffer);
38973901
if (have_tuple_lock)
3898-
UnlockTupleTuplock(relation, &(oldtup.t_self), *lockmode);
3902+
UnlockTupleTuplock(relation, &(oldtup.t_self), lockmode);
38993903
if (vmbuffer != InvalidBuffer)
39003904
ReleaseBuffer(vmbuffer);
39013905
bms_free(hot_attrs);
@@ -3933,7 +3937,7 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
39333937
compute_new_xmax_infomask(HeapTupleHeaderGetRawXmax(oldtup.t_data),
39343938
oldtup.t_data->t_infomask,
39353939
oldtup.t_data->t_infomask2,
3936-
xid, *lockmode, true,
3940+
xid, lockmode, true,
39373941
&xmax_old_tuple, &infomask_old_tuple,
39383942
&infomask2_old_tuple);
39393943

@@ -4050,7 +4054,7 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
40504054
compute_new_xmax_infomask(HeapTupleHeaderGetRawXmax(oldtup.t_data),
40514055
oldtup.t_data->t_infomask,
40524056
oldtup.t_data->t_infomask2,
4053-
xid, *lockmode, false,
4057+
xid, lockmode, false,
40544058
&xmax_lock_old_tuple, &infomask_lock_old_tuple,
40554059
&infomask2_lock_old_tuple);
40564060

@@ -4362,7 +4366,7 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
43624366
* Release the lmgr tuple lock, if we had it.
43634367
*/
43644368
if (have_tuple_lock)
4365-
UnlockTupleTuplock(relation, &(oldtup.t_self), *lockmode);
4369+
UnlockTupleTuplock(relation, &(oldtup.t_self), lockmode);
43664370

43674371
pgstat_count_heap_update(relation, use_hot_update);
43684372

@@ -4586,12 +4590,11 @@ simple_heap_update(Relation relation, ItemPointer otid, HeapTuple tup)
45864590
{
45874591
HTSU_Result result;
45884592
HeapUpdateFailureData hufd;
4589-
LockTupleMode lockmode;
45904593

45914594
result = heap_update(relation, otid, tup,
45924595
GetCurrentCommandId(true), InvalidSnapshot,
45934596
true /* wait for commit */ ,
4594-
&hufd, &lockmode);
4597+
&hufd);
45954598
switch (result)
45964599
{
45974600
case HeapTupleSelfUpdated:
@@ -5177,6 +5180,7 @@ heap_lock_tuple(Relation relation, HeapTuple tuple,
51775180
Assert(result == HeapTupleSelfUpdated || result == HeapTupleUpdated ||
51785181
result == HeapTupleWouldBlock);
51795182
Assert(!(tuple->t_data->t_infomask & HEAP_XMAX_INVALID));
5183+
hufd->result = result;
51805184
hufd->ctid = tuple->t_data->t_ctid;
51815185
hufd->xmax = HeapTupleHeaderGetUpdateXid(tuple->t_data);
51825186
if (result == HeapTupleSelfUpdated)

src/backend/catalog/sql_features.txt

+3-3
Original file line numberDiff line numberDiff line change
@@ -229,9 +229,9 @@ F311 Schema definition statement 02 CREATE TABLE for persistent base tables YES
229229
F311 Schema definition statement 03 CREATE VIEW YES
230230
F311 Schema definition statement 04 CREATE VIEW: WITH CHECK OPTION YES
231231
F311 Schema definition statement 05 GRANT statement YES
232-
F312 MERGE statement NO consider INSERT ... ON CONFLICT DO UPDATE
233-
F313 Enhanced MERGE statement NO
234-
F314 MERGE statement with DELETE branch NO
232+
F312 MERGE statement YES also consider INSERT ... ON CONFLICT DO UPDATE
233+
F313 Enhanced MERGE statement YES
234+
F314 MERGE statement with DELETE branch YES
235235
F321 User authorization YES
236236
F341 Usage tables NO no ROUTINE_*_USAGE tables
237237
F361 Subprogram support YES

0 commit comments

Comments
 (0)