On 2018/03/23 3:42, Pavan Deolasee wrote:
> A slightly improved version attached. Apart from doc cleanup based on
> earlier feedback, fixed one assertion failure based on Rahila's report.
> This was happening when target relation is referenced in the source
> subquery. Fixed that and added a test case to test that situation.
>
> Rebased on current master.
I tried these patches (applied 0002 on top of 0001). When applying 0002,
I got some apply errors:
The next patch would create the file
src/test/isolation/expected/merge-delete.out,
which already exists! Assume -R? [n]
I managed to apply it by ignoring the errors, but couldn't get make check
to pass; attached regressions.diffs if you want to take a look.
Btw, is 0001 redundant with the latest patch on ON CONFLICT DO UPDATE
thread? Can I apply just 0002 on top of that patch? So, I tried that --
that is, skipped your 0001 and instead applied ON CONFLICT DO UPDATE
patch, and then applied your 0002. I had to fix a couple of places to get
MERGE working correctly for partitioned tables; attached find a delta
patch for the fixes I made, which were needed because I skipped 0001 in
favor of the ON CONFLICT DO UPDATE patch. But the regression test failure
I mentioned above didn't go away, so it seems to have nothing to do with
partitioning.
Thanks,
Amit
diff --git a/src/backend/executor/nodeModifyTable.c
b/src/backend/executor/nodeModifyTable.c
index 00d241f232..4927bfebfa 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -1552,7 +1552,6 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
ExecCheckHeapTupleVisible(estate, &tuple, buffer);
/* Store target's existing tuple in the state's dedicated slot */
- ExecSetSlotDescriptor(mtstate->mt_existing, relation->rd_att);
ExecStoreTuple(&tuple, mtstate->mt_existing, buffer, false);
/*
diff --git a/src/backend/optimizer/prep/preptlist.c
b/src/backend/optimizer/prep/preptlist.c
index 3ff8d86853..4a864b2340 100644
--- a/src/backend/optimizer/prep/preptlist.c
+++ b/src/backend/optimizer/prep/preptlist.c
@@ -142,7 +142,7 @@ preprocess_targetlist(PlannerInfo *root)
action->targetList =
expand_targetlist(action->targetList,
action->commandType,
result_relation,
-
RelationGetDescr(target_relation));
+
target_relation);
break;
case CMD_DELETE:
break;
*** /home/amit/pg/mygit/src/test/regress/expected/merge.out 2018-01-30
11:50:31.297108552 +0900
--- /home/amit/pg/mygit/src/test/regress/results/merge.out 2018-03-23
13:18:25.034107527 +0900
***************
*** 39,52 ****
WHEN MATCHED THEN
DELETE
;
! QUERY PLAN
! ----------------------------------------
Merge on target t
-> Merge Join
! Merge Cond: (t.tid = s.sid)
-> Sort
! Sort Key: t.tid
! -> Seq Scan on target t
-> Sort
Sort Key: s.sid
-> Seq Scan on source s
--- 39,52 ----
WHEN MATCHED THEN
DELETE
;
! QUERY PLAN
! ------------------------------------------
Merge on target t
-> Merge Join
! Merge Cond: (t_1.tid = s.sid)
-> Sort
! Sort Key: t_1.tid
! -> Seq Scan on target t_1
-> Sort
Sort Key: s.sid
-> Seq Scan on source s
***************
*** 137,142 ****
--- 137,154 ----
INSERT DEFAULT VALUES
;
ERROR: permission denied for table target2
+ -- check if the target can be accessed from source relation subquery; we
should
+ -- not be able to do so
+ MERGE INTO target t
+ USING (SELECT * FROM source WHERE t.tid > sid) s
+ ON t.tid = s.sid
+ WHEN NOT MATCHED THEN
+ INSERT DEFAULT VALUES
+ ;
+ ERROR: invalid reference to FROM-clause entry for table "t"
+ LINE 2: USING (SELECT * FROM source WHERE t.tid > sid) s
+ ^
+ HINT: There is an entry for table "t", but it cannot be referenced from this
part of the query.
--
-- initial tests
--
***************
*** 229,242 ****
WHEN MATCHED THEN
UPDATE SET balance = 0
;
! QUERY PLAN
! ----------------------------------------
Merge on target t
-> Hash Join
! Hash Cond: (s.sid = t.tid)
-> Seq Scan on source s
-> Hash
! -> Seq Scan on target t
(6 rows)
EXPLAIN (COSTS OFF)
--- 241,254 ----
WHEN MATCHED THEN
UPDATE SET balance = 0
;
! QUERY PLAN
! ------------------------------------------
Merge on target t
-> Hash Join
! Hash Cond: (s.sid = t_1.tid)
-> Seq Scan on source s
-> Hash
! -> Seq Scan on target t_1
(6 rows)
EXPLAIN (COSTS OFF)
***************
*** 246,259 ****
WHEN MATCHED THEN
DELETE
;
! QUERY PLAN
! ----------------------------------------
Merge on target t
-> Hash Join
! Hash Cond: (s.sid = t.tid)
-> Seq Scan on source s
-> Hash
! -> Seq Scan on target t
(6 rows)
EXPLAIN (COSTS OFF)
--- 258,271 ----
WHEN MATCHED THEN
DELETE
;
! QUERY PLAN
! ------------------------------------------
Merge on target t
-> Hash Join
! Hash Cond: (s.sid = t_1.tid)
-> Seq Scan on source s
-> Hash
! -> Seq Scan on target t_1
(6 rows)
EXPLAIN (COSTS OFF)
***************
*** 262,275 ****
ON t.tid = s.sid
WHEN NOT MATCHED THEN
INSERT VALUES (4, NULL);
! QUERY PLAN
! ----------------------------------------
Merge on target t
-> Hash Left Join
! Hash Cond: (s.sid = t.tid)
-> Seq Scan on source s
-> Hash
! -> Seq Scan on target t
(6 rows)
;
--- 274,287 ----
ON t.tid = s.sid
WHEN NOT MATCHED THEN
INSERT VALUES (4, NULL);
! QUERY PLAN
! ------------------------------------------
Merge on target t
-> Hash Left Join
! Hash Cond: (s.sid = t_1.tid)
-> Seq Scan on source s
-> Hash
! -> Seq Scan on target t_1
(6 rows)
;
***************
*** 370,375 ****
--- 382,388 ----
UPDATE SET balance = 0
;
ERROR: MERGE command cannot affect row a second time
+ HINT: Ensure that not more than one source rows match any one target row
ROLLBACK;
BEGIN;
MERGE INTO target t
***************
*** 379,384 ****
--- 392,398 ----
DELETE
;
ERROR: MERGE command cannot affect row a second time
+ HINT: Ensure that not more than one source rows match any one target row
ROLLBACK;
-- correct source data
DELETE FROM source WHERE sid = 2;
***************
*** 696,701 ****
--- 710,720 ----
1 | 299
(1 row)
+ -- check if subqueries work in the conditions?
+ MERGE INTO wq_target t
+ USING wq_source s ON t.tid = s.sid
+ WHEN MATCHED AND t.balance > (SELECT max(balance) FROM target) THEN
+ UPDATE SET balance = t.balance + s.balance;
-- check if we can access system columns in the conditions
MERGE INTO wq_target t
USING wq_source s ON t.tid = s.sid
***************
*** 704,729 ****
ERROR: system column "xmin" reference in WHEN AND condition is invalid
LINE 3: WHEN MATCHED AND t.xmin = t.xmax THEN
^
SELECT * FROM wq_target;
tid | balance
-----+---------
! 1 | 299
(1 row)
- -- check if subqueries work in the conditions?
MERGE INTO wq_target t
USING wq_source s ON t.tid = s.sid
! WHEN MATCHED AND t.balance > (SELECT max(balance) FROM target) THEN
UPDATE SET balance = t.balance + s.balance;
- ERROR: cannot use subquery in WHEN AND condition
- LINE 3: WHEN MATCHED AND t.balance > (SELECT max(balance) FROM targe...
- ^
SELECT * FROM wq_target;
tid | balance
-----+---------
! 1 | 299
(1 row)
DROP TABLE wq_target, wq_source;
-- test triggers
create or replace function merge_trigfunc () returns trigger
--- 723,761 ----
ERROR: system column "xmin" reference in WHEN AND condition is invalid
LINE 3: WHEN MATCHED AND t.xmin = t.xmax THEN
^
+ ALTER TABLE wq_target SET WITH OIDS;
SELECT * FROM wq_target;
tid | balance
-----+---------
! 1 | 399
(1 row)
MERGE INTO wq_target t
USING wq_source s ON t.tid = s.sid
! WHEN MATCHED AND t.oid >= 0 THEN
UPDATE SET balance = t.balance + s.balance;
SELECT * FROM wq_target;
tid | balance
-----+---------
! 1 | 499
(1 row)
+ -- test preventing WHEN AND conditions from writing to the database
+ create or replace function merge_when_and_write() returns boolean
+ language plpgsql as
+ $$
+ BEGIN
+ INSERT INTO target VALUES (100, 100);
+ RETURN TRUE;
+ END;
+ $$;
+ BEGIN;
+ MERGE INTO wq_target t
+ USING wq_source s ON t.tid = s.sid
+ WHEN MATCHED AND (merge_when_and_write()) THEN
+ UPDATE SET balance = t.balance + s.balance;
+ ROLLBACK;
+ drop function merge_when_and_write();
DROP TABLE wq_target, wq_source;
-- test triggers
create or replace function merge_trigfunc () returns trigger
***************
*** 761,766 ****
--- 793,799 ----
NOTICE: BEFORE UPDATE ROW trigger
NOTICE: AFTER UPDATE ROW trigger
NOTICE: AFTER UPDATE STATEMENT trigger
+ EXPLAIN (ANALYZE ON, COSTS OFF, SUMMARY OFF, TIMING OFF)
MERGE INTO target t
USING source AS s
ON t.tid = s.sid
***************
*** 783,788 ****
--- 816,847 ----
NOTICE: AFTER DELETE STATEMENT trigger
NOTICE: AFTER UPDATE STATEMENT trigger
NOTICE: AFTER INSERT STATEMENT trigger
+ QUERY PLAN
+ ------------------------------------------------------------------
+ Merge on target t (actual rows=0 loops=1)
+ Tuples Inserted: 1
+ Tuples Updated: 1
+ Tuples Deleted: 1
+ -> Hash Left Join (actual rows=3 loops=1)
+ Hash Cond: (s.sid = t_1.tid)
+ -> Seq Scan on source s (actual rows=3 loops=1)
+ -> Hash (actual rows=3 loops=1)
+ Buckets: 1024 Batches: 1 Memory Usage: 9kB
+ -> Seq Scan on target t_1 (actual rows=3 loops=1)
+ Trigger merge_ard: calls=1
+ Trigger merge_ari: calls=1
+ Trigger merge_aru: calls=1
+ Trigger merge_asd: calls=1
+ Trigger merge_asi: calls=1
+ Trigger merge_asu: calls=1
+ Trigger merge_brd: calls=1
+ Trigger merge_bri: calls=1
+ Trigger merge_bru: calls=1
+ Trigger merge_bsd: calls=1
+ Trigger merge_bsi: calls=1
+ Trigger merge_bsu: calls=1
+ (22 rows)
+
SELECT * FROM target ORDER BY tid;
tid | balance
-----+---------
***************
*** 879,884 ****
--- 938,971 ----
ROLLBACK;
--self-merge
BEGIN;
+ MERGE INTO target t1
+ USING target t2
+ ON t1.tid = t2.tid
+ WHEN MATCHED THEN
+ UPDATE SET balance = t1.balance + t2.balance
+ WHEN NOT MATCHED THEN
+ INSERT VALUES (t2.tid, t2.balance)
+ ;
+ NOTICE: BEFORE INSERT STATEMENT trigger
+ NOTICE: BEFORE UPDATE STATEMENT trigger
+ NOTICE: BEFORE UPDATE ROW trigger
+ NOTICE: BEFORE UPDATE ROW trigger
+ NOTICE: BEFORE UPDATE ROW trigger
+ NOTICE: AFTER UPDATE ROW trigger
+ NOTICE: AFTER UPDATE ROW trigger
+ NOTICE: AFTER UPDATE ROW trigger
+ NOTICE: AFTER UPDATE STATEMENT trigger
+ NOTICE: AFTER INSERT STATEMENT trigger
+ SELECT * FROM target ORDER BY tid;
+ tid | balance
+ -----+---------
+ 1 | 20
+ 2 | 40
+ 3 | 60
+ (3 rows)
+
+ ROLLBACK;
+ BEGIN;
MERGE INTO target t
USING (SELECT tid as sid, balance as delta FROM target WHERE balance > 0) AS s
ON t.tid = s.sid
***************
*** 963,974 ****
--- 1050,1108 ----
ROLLBACK;
-- PREPARE
+ BEGIN;
prepare foom as merge into target t using (select 1 as sid) s on (t.tid =
s.sid) when matched then update set balance = 1;
execute foom;
NOTICE: BEFORE UPDATE STATEMENT trigger
NOTICE: BEFORE UPDATE ROW trigger
NOTICE: AFTER UPDATE ROW trigger
NOTICE: AFTER UPDATE STATEMENT trigger
+ SELECT * FROM target ORDER BY tid;
+ tid | balance
+ -----+---------
+ 1 | 1
+ 2 | 20
+ 3 | 30
+ (3 rows)
+
+ ROLLBACK;
+ BEGIN;
+ PREPARE foom2 (integer, integer) AS
+ MERGE INTO target t
+ USING (SELECT 1) s
+ ON t.tid = $1
+ WHEN MATCHED THEN
+ UPDATE SET balance = $2;
+ EXPLAIN (ANALYZE ON, COSTS OFF, SUMMARY OFF, TIMING OFF)
+ execute foom2 (1, 1);
+ NOTICE: BEFORE UPDATE STATEMENT trigger
+ NOTICE: BEFORE UPDATE ROW trigger
+ NOTICE: AFTER UPDATE ROW trigger
+ NOTICE: AFTER UPDATE STATEMENT trigger
+ QUERY PLAN
+ ------------------------------------------------------
+ Merge on target t (actual rows=0 loops=1)
+ Tuples Inserted: 0
+ Tuples Updated: 1
+ Tuples Deleted: 0
+ -> Seq Scan on target t_1 (actual rows=1 loops=1)
+ Filter: (tid = 1)
+ Rows Removed by Filter: 2
+ Trigger merge_aru: calls=1
+ Trigger merge_asu: calls=1
+ Trigger merge_bru: calls=1
+ Trigger merge_bsu: calls=1
+ (11 rows)
+
+ SELECT * FROM target ORDER BY tid;
+ tid | balance
+ -----+---------
+ 1 | 1
+ 2 | 20
+ 3 | 30
+ (3 rows)
+
+ ROLLBACK;
-- subqueries in source relation
CREATE TABLE sq_target (tid integer NOT NULL, balance integer);
CREATE TABLE sq_source (delta integer, sid integer, balance integer DEFAULT
0);
***************
*** 1020,1027 ****
ERROR: column reference "balance" is ambiguous
LINE 5: UPDATE SET balance = balance + delta
^
- SELECT * FROM sq_target;
- ERROR: current transaction is aborted, commands ignored until end of
transaction block
ROLLBACK;
BEGIN;
INSERT INTO sq_source (sid, balance, delta) VALUES (-1, -1, -10);
--- 1154,1159 ----
***************
*** 1043,1050 ****
--- 1175,1593 ----
(3 rows)
ROLLBACK;
+ -- CTEs
+ BEGIN;
+ INSERT INTO sq_source (sid, balance, delta) VALUES (-1, -1, -10);
+ WITH targq AS (
+ SELECT * FROM v
+ )
+ MERGE INTO sq_target t
+ USING v
+ ON tid = sid
+ WHEN MATCHED AND tid > 2 THEN
+ UPDATE SET balance = t.balance + delta
+ WHEN NOT MATCHED THEN
+ INSERT (balance, tid) VALUES (balance + delta, sid)
+ WHEN MATCHED AND tid < 2 THEN
+ DELETE
+ ;
+ ERROR: syntax error at or near "MERGE"
+ LINE 4: MERGE INTO sq_target t
+ ^
+ ROLLBACK;
+ -- RETURNING
+ BEGIN;
+ INSERT INTO sq_source (sid, balance, delta) VALUES (-1, -1, -10);
+ MERGE INTO sq_target t
+ USING v
+ ON tid = sid
+ WHEN MATCHED AND tid > 2 THEN
+ UPDATE SET balance = t.balance + delta
+ WHEN NOT MATCHED THEN
+ INSERT (balance, tid) VALUES (balance + delta, sid)
+ WHEN MATCHED AND tid < 2 THEN
+ DELETE
+ RETURNING *
+ ;
+ ERROR: syntax error at or near "RETURNING"
+ LINE 10: RETURNING *
+ ^
+ ROLLBACK;
+ -- Subqueries
+ BEGIN;
+ MERGE INTO sq_target t
+ USING v
+ ON tid = sid
+ WHEN MATCHED THEN
+ UPDATE SET balance = (SELECT count(*) FROM sq_target)
+ ;
+ ROLLBACK;
+ BEGIN;
+ MERGE INTO sq_target t
+ USING v
+ ON tid = sid
+ WHEN MATCHED AND (SELECT count(*) > 0 FROM sq_target) THEN
+ UPDATE SET balance = 42
+ ;
+ ROLLBACK;
+ BEGIN;
+ MERGE INTO sq_target t
+ USING v
+ ON tid = sid AND (SELECT count(*) > 0 FROM sq_target)
+ WHEN MATCHED THEN
+ UPDATE SET balance = 42
+ ;
+ ROLLBACK;
DROP TABLE sq_target, sq_source CASCADE;
NOTICE: drop cascades to view v
+ CREATE TABLE pa_target (tid integer, balance float, val text)
+ PARTITION BY LIST (tid);
+ CREATE TABLE part1 PARTITION OF pa_target FOR VALUES IN (1,4);
+ CREATE TABLE part2 PARTITION OF pa_target FOR VALUES IN (2,5,6);
+ CREATE TABLE part3 PARTITION OF pa_target FOR VALUES IN (3,8,9);
+ CREATE TABLE part4 PARTITION OF pa_target DEFAULT;
+ CREATE TABLE pa_source (sid integer, delta float);
+ -- insert many rows to the source table
+ INSERT INTO pa_source SELECT id, id * 10 FROM generate_series(1,14) AS id;
+ -- insert a few rows in the target table (odd numbered tid)
+ INSERT INTO pa_target SELECT id, id * 100, 'initial' FROM
generate_series(1,14,2) AS id;
+ -- try simple MERGE
+ BEGIN;
+ MERGE INTO pa_target t
+ USING pa_source s
+ ON t.tid = s.sid
+ WHEN MATCHED THEN
+ UPDATE SET balance = balance + delta, val = val || ' updated by merge'
+ WHEN NOT MATCHED THEN
+ INSERT VALUES (sid, delta, 'inserted by merge');
+ SELECT * FROM pa_target ORDER BY tid;
+ tid | balance | val
+ -----+---------+--------------------------
+ 1 | 110 | initial updated by merge
+ 2 | 20 | inserted by merge
+ 3 | 330 | initial updated by merge
+ 4 | 40 | inserted by merge
+ 5 | 550 | initial updated by merge
+ 6 | 60 | inserted by merge
+ 7 | 770 | initial updated by merge
+ 8 | 80 | inserted by merge
+ 9 | 990 | initial updated by merge
+ 10 | 100 | inserted by merge
+ 11 | 1210 | initial updated by merge
+ 12 | 120 | inserted by merge
+ 13 | 1430 | initial updated by merge
+ 14 | 140 | inserted by merge
+ (14 rows)
+
+ ROLLBACK;
+ -- same with a constant qual
+ BEGIN;
+ MERGE INTO pa_target t
+ USING pa_source s
+ ON t.tid = s.sid AND tid = 1
+ WHEN MATCHED THEN
+ UPDATE SET balance = balance + delta, val = val || ' updated by merge'
+ WHEN NOT MATCHED THEN
+ INSERT VALUES (sid, delta, 'inserted by merge');
+ SELECT * FROM pa_target ORDER BY tid;
+ tid | balance | val
+ -----+---------+--------------------------
+ 1 | 110 | initial updated by merge
+ 2 | 20 | inserted by merge
+ 3 | 30 | inserted by merge
+ 3 | 300 | initial
+ 4 | 40 | inserted by merge
+ 5 | 500 | initial
+ 5 | 50 | inserted by merge
+ 6 | 60 | inserted by merge
+ 7 | 700 | initial
+ 7 | 70 | inserted by merge
+ 8 | 80 | inserted by merge
+ 9 | 90 | inserted by merge
+ 9 | 900 | initial
+ 10 | 100 | inserted by merge
+ 11 | 1100 | initial
+ 11 | 110 | inserted by merge
+ 12 | 120 | inserted by merge
+ 13 | 1300 | initial
+ 13 | 130 | inserted by merge
+ 14 | 140 | inserted by merge
+ (20 rows)
+
+ ROLLBACK;
+ -- try updating the partition key column
+ BEGIN;
+ MERGE INTO pa_target t
+ USING pa_source s
+ ON t.tid = s.sid
+ WHEN MATCHED THEN
+ UPDATE SET tid = tid + 1, balance = balance + delta, val = val || '
updated by merge'
+ WHEN NOT MATCHED THEN
+ INSERT VALUES (sid, delta, 'inserted by merge');
+ SELECT * FROM pa_target ORDER BY tid;
+ tid | balance | val
+ -----+---------+--------------------------
+ 2 | 110 | initial updated by merge
+ 2 | 20 | inserted by merge
+ 4 | 40 | inserted by merge
+ 4 | 330 | initial updated by merge
+ 6 | 550 | initial updated by merge
+ 6 | 60 | inserted by merge
+ 8 | 80 | inserted by merge
+ 8 | 770 | initial updated by merge
+ 10 | 990 | initial updated by merge
+ 10 | 100 | inserted by merge
+ 12 | 1210 | initial updated by merge
+ 12 | 120 | inserted by merge
+ 14 | 1430 | initial updated by merge
+ 14 | 140 | inserted by merge
+ (14 rows)
+
+ ROLLBACK;
+ DROP TABLE pa_target CASCADE;
+ -- The target table is partitioned in the same way, but this time by attaching
+ -- partitions which have columns in different order, dropped columns etc.
+ CREATE TABLE pa_target (tid integer, balance float, val text)
+ PARTITION BY LIST (tid);
+ CREATE TABLE part1 (tid integer, balance float, val text);
+ CREATE TABLE part2 (balance float, tid integer, val text);
+ CREATE TABLE part3 (tid integer, balance float, val text);
+ CREATE TABLE part4 (extraid text, tid integer, balance float, val text);
+ ALTER TABLE part4 DROP COLUMN extraid;
+ ALTER TABLE pa_target ATTACH PARTITION part1 FOR VALUES IN (1,4);
+ ALTER TABLE pa_target ATTACH PARTITION part2 FOR VALUES IN (2,5,6);
+ ALTER TABLE pa_target ATTACH PARTITION part3 FOR VALUES IN (3,8,9);
+ ALTER TABLE pa_target ATTACH PARTITION part4 DEFAULT;
+ -- insert a few rows in the target table (odd numbered tid)
+ INSERT INTO pa_target SELECT id, id * 100, 'initial' FROM
generate_series(1,14,2) AS id;
+ -- try simple MERGE
+ BEGIN;
+ MERGE INTO pa_target t
+ USING pa_source s
+ ON t.tid = s.sid
+ WHEN MATCHED THEN
+ UPDATE SET balance = balance + delta, val = val || ' updated by merge'
+ WHEN NOT MATCHED THEN
+ INSERT VALUES (sid, delta, 'inserted by merge');
+ SELECT * FROM pa_target ORDER BY tid;
+ tid | balance | val
+ -----+---------+--------------------------
+ 1 | 110 | initial updated by merge
+ 2 | 20 | inserted by merge
+ 3 | 330 | initial updated by merge
+ 4 | 40 | inserted by merge
+ 5 | 550 | initial updated by merge
+ 6 | 60 | inserted by merge
+ 7 | 770 | initial updated by merge
+ 8 | 80 | inserted by merge
+ 9 | 990 | initial updated by merge
+ 10 | 100 | inserted by merge
+ 11 | 1210 | initial updated by merge
+ 12 | 120 | inserted by merge
+ 13 | 1430 | initial updated by merge
+ 14 | 140 | inserted by merge
+ (14 rows)
+
+ ROLLBACK;
+ -- same with a constant qual
+ BEGIN;
+ MERGE INTO pa_target t
+ USING pa_source s
+ ON t.tid = s.sid AND tid = 1
+ WHEN MATCHED THEN
+ UPDATE SET balance = balance + delta, val = val || ' updated by merge'
+ WHEN NOT MATCHED THEN
+ INSERT VALUES (sid, delta, 'inserted by merge');
+ SELECT * FROM pa_target ORDER BY tid;
+ tid | balance | val
+ -----+---------+--------------------------
+ 1 | 110 | initial updated by merge
+ 2 | 20 | inserted by merge
+ 3 | 30 | inserted by merge
+ 3 | 300 | initial
+ 4 | 40 | inserted by merge
+ 5 | 500 | initial
+ 5 | 50 | inserted by merge
+ 6 | 60 | inserted by merge
+ 7 | 700 | initial
+ 7 | 70 | inserted by merge
+ 8 | 80 | inserted by merge
+ 9 | 90 | inserted by merge
+ 9 | 900 | initial
+ 10 | 100 | inserted by merge
+ 11 | 1100 | initial
+ 11 | 110 | inserted by merge
+ 12 | 120 | inserted by merge
+ 13 | 1300 | initial
+ 13 | 130 | inserted by merge
+ 14 | 140 | inserted by merge
+ (20 rows)
+
+ ROLLBACK;
+ -- try updating the partition key column
+ BEGIN;
+ MERGE INTO pa_target t
+ USING pa_source s
+ ON t.tid = s.sid
+ WHEN MATCHED THEN
+ UPDATE SET tid = tid + 1, balance = balance + delta, val = val || '
updated by merge'
+ WHEN NOT MATCHED THEN
+ INSERT VALUES (sid, delta, 'inserted by merge');
+ SELECT * FROM pa_target ORDER BY tid;
+ tid | balance | val
+ -----+---------+--------------------------
+ 2 | 110 | initial updated by merge
+ 2 | 20 | inserted by merge
+ 4 | 40 | inserted by merge
+ 4 | 330 | initial updated by merge
+ 6 | 550 | initial updated by merge
+ 6 | 60 | inserted by merge
+ 8 | 80 | inserted by merge
+ 8 | 770 | initial updated by merge
+ 10 | 990 | initial updated by merge
+ 10 | 100 | inserted by merge
+ 12 | 1210 | initial updated by merge
+ 12 | 120 | inserted by merge
+ 14 | 1430 | initial updated by merge
+ 14 | 140 | inserted by merge
+ (14 rows)
+
+ ROLLBACK;
+ DROP TABLE pa_source;
+ DROP TABLE pa_target CASCADE;
+ -- Sub-partitionin
+ CREATE TABLE pa_target (logts timestamp, tid integer, balance float, val text)
+ PARTITION BY RANGE (logts);
+ CREATE TABLE part_m01 PARTITION OF pa_target
+ FOR VALUES FROM ('2017-01-01') TO ('2017-02-01')
+ PARTITION BY LIST (tid);
+ CREATE TABLE part_m01_odd PARTITION OF part_m01
+ FOR VALUES IN (1,3,5,7,9);
+ CREATE TABLE part_m01_even PARTITION OF part_m01
+ FOR VALUES IN (2,4,6,8);
+ CREATE TABLE part_m02 PARTITION OF pa_target
+ FOR VALUES FROM ('2017-02-01') TO ('2017-03-01')
+ PARTITION BY LIST (tid);
+ CREATE TABLE part_m02_odd PARTITION OF part_m02
+ FOR VALUES IN (1,3,5,7,9);
+ CREATE TABLE part_m02_even PARTITION OF part_m02
+ FOR VALUES IN (2,4,6,8);
+ CREATE TABLE pa_source (sid integer, delta float);
+ -- insert many rows to the source table
+ INSERT INTO pa_source SELECT id, id * 10 FROM generate_series(1,14) AS id;
+ -- insert a few rows in the target table (odd numbered tid)
+ INSERT INTO pa_target SELECT '2017-01-31', id, id * 100, 'initial' FROM
generate_series(1,9,3) AS id;
+ INSERT INTO pa_target SELECT '2017-02-28', id, id * 100, 'initial' FROM
generate_series(2,9,3) AS id;
+ -- try simple MERGE
+ BEGIN;
+ MERGE INTO pa_target t
+ USING (SELECT '2017-01-15' AS slogts, * FROM pa_source WHERE sid < 10) s
+ ON t.tid = s.sid
+ WHEN MATCHED THEN
+ UPDATE SET balance = balance + delta, val = val || ' updated by merge'
+ WHEN NOT MATCHED THEN
+ INSERT VALUES (slogts::timestamp, sid, delta, 'inserted by merge');
+ SELECT * FROM pa_target ORDER BY tid;
+ logts | tid | balance | val
+ --------------------------+-----+---------+--------------------------
+ Tue Jan 31 00:00:00 2017 | 1 | 110 | initial updated by merge
+ Tue Feb 28 00:00:00 2017 | 2 | 220 | initial updated by merge
+ Sun Jan 15 00:00:00 2017 | 3 | 30 | inserted by merge
+ Tue Jan 31 00:00:00 2017 | 4 | 440 | initial updated by merge
+ Tue Feb 28 00:00:00 2017 | 5 | 550 | initial updated by merge
+ Sun Jan 15 00:00:00 2017 | 6 | 60 | inserted by merge
+ Tue Jan 31 00:00:00 2017 | 7 | 770 | initial updated by merge
+ Tue Feb 28 00:00:00 2017 | 8 | 880 | initial updated by merge
+ Sun Jan 15 00:00:00 2017 | 9 | 90 | inserted by merge
+ (9 rows)
+
+ ROLLBACK;
+ DROP TABLE pa_source;
+ DROP TABLE pa_target CASCADE;
+ -- some complex joins on the source side
+ CREATE TABLE cj_target (tid integer, balance float, val text);
+ CREATE TABLE cj_source1 (sid1 integer, scat integer, delta integer);
+ CREATE TABLE cj_source2 (sid2 integer, sval text);
+ INSERT INTO cj_source1 VALUES (1, 10, 100);
+ INSERT INTO cj_source1 VALUES (1, 20, 200);
+ INSERT INTO cj_source1 VALUES (2, 20, 300);
+ INSERT INTO cj_source1 VALUES (3, 10, 400);
+ INSERT INTO cj_source2 VALUES (1, 'initial source2');
+ INSERT INTO cj_source2 VALUES (2, 'initial source2');
+ INSERT INTO cj_source2 VALUES (3, 'initial source2');
+ -- source relation is an unalised join
+ MERGE INTO cj_target t
+ USING cj_source1 s1
+ INNER JOIN cj_source2 s2 ON sid1 = sid2
+ ON t.tid = sid1
+ WHEN NOT MATCHED THEN
+ INSERT VALUES (sid1, delta, sval);
+ -- try accessing columns from either side of the source join
+ MERGE INTO cj_target t
+ USING cj_source2 s2
+ INNER JOIN cj_source1 s1 ON sid1 = sid2 AND scat = 20
+ ON t.tid = sid1
+ WHEN NOT MATCHED THEN
+ INSERT VALUES (sid2, delta, sval)
+ WHEN MATCHED THEN
+ DELETE;
+ -- some simple expressions in INSERT targetlist
+ MERGE INTO cj_target t
+ USING cj_source2 s2
+ INNER JOIN cj_source1 s1 ON sid1 = sid2
+ ON t.tid = sid1
+ WHEN NOT MATCHED THEN
+ INSERT VALUES (sid2, delta + scat, sval)
+ WHEN MATCHED THEN
+ UPDATE SET val = val || ' updated by merge';
+ MERGE INTO cj_target t
+ USING cj_source2 s2
+ INNER JOIN cj_source1 s1 ON sid1 = sid2 AND scat = 20
+ ON t.tid = sid1
+ WHEN MATCHED THEN
+ UPDATE SET val = val || ' ' || delta::text;
+ SELECT * FROM cj_target;
+ tid | balance | val
+ -----+---------+----------------------------------
+ 3 | 400 | initial source2 updated by merge
+ 1 | 220 | initial source2 200
+ 1 | 110 | initial source2 200
+ 2 | 320 | initial source2 300
+ (4 rows)
+
+ ALTER TABLE cj_source1 RENAME COLUMN sid1 TO sid;
+ ALTER TABLE cj_source2 RENAME COLUMN sid2 TO sid;
+ TRUNCATE cj_target;
+ MERGE INTO cj_target t
+ USING cj_source1 s1
+ INNER JOIN cj_source2 s2 ON s1.sid = s2.sid
+ ON t.tid = s1.sid
+ WHEN NOT MATCHED THEN
+ INSERT VALUES (s2.sid, delta, sval);
+ DROP TABLE cj_source2, cj_source1, cj_target;
+ -- Function scans
+ CREATE TABLE fs_target (a int, b int, c text);
+ MERGE INTO fs_target t
+ USING generate_series(1,100,1) AS id
+ ON t.a = id
+ WHEN MATCHED THEN
+ UPDATE SET b = b + id
+ WHEN NOT MATCHED THEN
+ INSERT VALUES (id, -1);
+ MERGE INTO fs_target t
+ USING generate_series(1,100,2) AS id
+ ON t.a = id
+ WHEN MATCHED THEN
+ UPDATE SET b = b + id, c = 'updated '|| id.*::text
+ WHEN NOT MATCHED THEN
+ INSERT VALUES (id, -1, 'inserted ' || id.*::text);
+ SELECT count(*) FROM fs_target;
+ count
+ -------
+ 100
+ (1 row)
+
+ DROP TABLE fs_target;
-- SERIALIZABLE test
-- handled in isolation tests
-- prepare
======================================================================