5 CREATE USER regress_merge_privs;
6 CREATE USER regress_merge_no_privs;
7 CREATE USER regress_merge_none;
9 DROP TABLE IF EXISTS target;
10 DROP TABLE IF EXISTS source;
11 CREATE TABLE target (tid integer, balance integer)
12 WITH (autovacuum_enabled=off);
13 CREATE TABLE source (sid integer, delta integer) -- no index
14 WITH (autovacuum_enabled=off);
15 INSERT INTO target VALUES (1, 10);
16 INSERT INTO target VALUES (2, 20);
17 INSERT INTO target VALUES (3, 30);
18 SELECT t.ctid is not null as matched, t.*, s.* FROM source s FULL OUTER JOIN target t ON s.sid = t.tid ORDER BY t.tid, s.sid;
20 ALTER TABLE target OWNER TO regress_merge_privs;
21 ALTER TABLE source OWNER TO regress_merge_privs;
23 CREATE TABLE target2 (tid integer, balance integer)
24 WITH (autovacuum_enabled=off);
25 CREATE TABLE source2 (sid integer, delta integer)
26 WITH (autovacuum_enabled=off);
28 ALTER TABLE target2 OWNER TO regress_merge_no_privs;
29 ALTER TABLE source2 OWNER TO regress_merge_no_privs;
31 GRANT INSERT ON target TO regress_merge_no_privs;
33 SET SESSION AUTHORIZATION regress_merge_privs;
45 MERGE INTO target t RANDOMWORD
49 UPDATE SET balance = 0;
50 -- MATCHED/INSERT error
55 INSERT DEFAULT VALUES;
56 -- incorrectly specifying INTO target
61 INSERT INTO target DEFAULT VALUES;
62 -- Multiple VALUES clause
67 INSERT VALUES (1,1), (2,2);
68 -- SELECT query for INSERT
79 UPDATE SET balance = 0;
85 UPDATE target SET balance = 0;
86 -- source and target names the same
90 WHEN MATCHED THEN DO NOTHING;
93 MERGE INTO target USING source ON (true)
94 WHEN MATCHED THEN DELETE
98 MERGE INTO target USING source ON (true)
99 WHEN MATCHED THEN DELETE
102 -- unsupported relation types
104 CREATE VIEW tv AS SELECT * FROM target;
108 WHEN NOT MATCHED THEN
109 INSERT DEFAULT VALUES;
113 CREATE MATERIALIZED VIEW mv AS SELECT * FROM target;
117 WHEN NOT MATCHED THEN
118 INSERT DEFAULT VALUES;
119 DROP MATERIALIZED VIEW mv;
123 SET SESSION AUTHORIZATION regress_merge_none;
130 SET SESSION AUTHORIZATION regress_merge_privs;
133 ON target.tid = source2.sid
135 UPDATE SET balance = 0;
137 GRANT INSERT ON target TO regress_merge_no_privs;
138 SET SESSION AUTHORIZATION regress_merge_no_privs;
142 ON target.tid = source2.sid
144 UPDATE SET balance = 0;
146 GRANT UPDATE ON target2 TO regress_merge_privs;
147 SET SESSION AUTHORIZATION regress_merge_privs;
151 ON target2.tid = source.sid
157 ON target2.tid = source.sid
158 WHEN NOT MATCHED THEN
159 INSERT DEFAULT VALUES;
161 -- check if the target can be accessed from source relation subquery; we should
162 -- not be able to do so
164 USING (SELECT * FROM source WHERE t.tid > sid) s
166 WHEN NOT MATCHED THEN
167 INSERT DEFAULT VALUES;
172 -- zero rows in source has no effect
175 ON target.tid = source.sid
177 UPDATE SET balance = 0;
183 UPDATE SET balance = 0;
193 WHEN NOT MATCHED THEN
194 INSERT DEFAULT VALUES;
197 -- insert some non-matching source rows to work from
198 INSERT INTO source VALUES (4, 40);
199 SELECT * FROM source ORDER BY sid;
200 SELECT * FROM target ORDER BY tid;
205 WHEN NOT MATCHED THEN
211 UPDATE SET balance = 0;
221 WHEN NOT MATCHED THEN
222 INSERT DEFAULT VALUES;
223 SELECT * FROM target ORDER BY tid;
227 INSERT INTO target SELECT generate_series(1000,2500), 0;
228 ALTER TABLE target ADD PRIMARY KEY (tid);
236 UPDATE SET balance = 0;
247 WHEN NOT MATCHED THEN
248 INSERT VALUES (4, NULL);
249 DELETE FROM target WHERE tid > 100;
252 -- insert some matching source rows to work from
253 INSERT INTO source VALUES (2, 5);
254 INSERT INTO source VALUES (3, 20);
255 SELECT * FROM source ORDER BY sid;
256 SELECT * FROM target ORDER BY tid;
258 -- equivalent of an UPDATE join
264 UPDATE SET balance = 0;
265 SELECT * FROM target ORDER BY tid;
268 -- equivalent of a DELETE join
275 SELECT * FROM target ORDER BY tid;
284 SELECT * FROM target ORDER BY tid;
291 WHEN NOT MATCHED THEN
292 INSERT VALUES (4, NULL);
293 SELECT * FROM target ORDER BY tid;
296 -- duplicate source row causes multiple target row update ERROR
297 INSERT INTO source VALUES (2, 5);
298 SELECT * FROM source ORDER BY sid;
299 SELECT * FROM target ORDER BY tid;
305 UPDATE SET balance = 0;
316 -- remove duplicate MATCHED data from source data
317 DELETE FROM source WHERE sid = 2;
318 INSERT INTO source VALUES (2, 5);
319 SELECT * FROM source ORDER BY sid;
320 SELECT * FROM target ORDER BY tid;
322 -- duplicate source row on INSERT should fail because of target_pkey
323 INSERT INTO source VALUES (4, 40);
328 WHEN NOT MATCHED THEN
329 INSERT VALUES (4, NULL);
330 SELECT * FROM target ORDER BY tid;
333 -- remove duplicate NOT MATCHED data from source data
334 DELETE FROM source WHERE sid = 4;
335 INSERT INTO source VALUES (4, 40);
336 SELECT * FROM source ORDER BY sid;
337 SELECT * FROM target ORDER BY tid;
339 -- remove constraints
340 alter table target drop CONSTRAINT target_pkey;
341 alter table target alter column tid drop not null;
348 WHEN NOT MATCHED THEN
351 UPDATE SET balance = 0;
352 SELECT * FROM target ORDER BY tid;
355 -- should be equivalent
361 UPDATE SET balance = 0
362 WHEN NOT MATCHED THEN
363 INSERT VALUES (4, 4);
364 SELECT * FROM target ORDER BY tid;
368 -- do a simple equivalent of an UPDATE join
374 UPDATE SET balance = t.balance + s.delta;
375 SELECT * FROM target ORDER BY tid;
378 -- do a simple equivalent of an INSERT SELECT
383 WHEN NOT MATCHED THEN
384 INSERT VALUES (s.sid, s.delta);
385 SELECT * FROM target ORDER BY tid;
388 -- and again with duplicate source rows
389 INSERT INTO source VALUES (5, 50);
390 INSERT INTO source VALUES (5, 50);
392 -- do a simple equivalent of an INSERT SELECT
397 WHEN NOT MATCHED THEN
398 INSERT VALUES (s.sid, s.delta);
399 SELECT * FROM target ORDER BY tid;
402 -- removing duplicate source rows
403 DELETE FROM source WHERE sid = 5;
405 -- and again with explicitly identified column list
410 WHEN NOT MATCHED THEN
411 INSERT (tid, balance) VALUES (s.sid, s.delta);
412 SELECT * FROM target ORDER BY tid;
415 -- and again with a subtle error: referring to non-existent target row for NOT MATCHED
419 WHEN NOT MATCHED THEN
420 INSERT (tid, balance) VALUES (t.tid, s.delta);
422 -- and again with a constant ON clause
427 WHEN NOT MATCHED THEN
428 INSERT (tid, balance) VALUES (t.tid, s.delta);
429 SELECT * FROM target ORDER BY tid;
432 -- now the classic UPSERT
438 UPDATE SET balance = t.balance + s.delta
439 WHEN NOT MATCHED THEN
440 INSERT VALUES (s.sid, s.delta);
441 SELECT * FROM target ORDER BY tid;
444 -- unreachable WHEN clause should ERROR
449 WHEN MATCHED THEN /* Terminal WHEN clause for MATCHED */
452 UPDATE SET balance = t.balance - s.delta;
455 -- conditional WHEN clause
456 CREATE TABLE wq_target (tid integer not null, balance integer DEFAULT -1)
457 WITH (autovacuum_enabled=off);
458 CREATE TABLE wq_source (balance integer, sid integer)
459 WITH (autovacuum_enabled=off);
461 INSERT INTO wq_source (sid, balance) VALUES (1, 100);
464 -- try a simple INSERT with default values first
465 MERGE INTO wq_target t
466 USING wq_source s ON t.tid = s.sid
467 WHEN NOT MATCHED THEN
468 INSERT (tid) VALUES (s.sid);
469 SELECT * FROM wq_target;
472 -- this time with a FALSE condition
473 MERGE INTO wq_target t
474 USING wq_source s ON t.tid = s.sid
475 WHEN NOT MATCHED AND FALSE THEN
476 INSERT (tid) VALUES (s.sid);
477 SELECT * FROM wq_target;
479 -- this time with an actual condition which returns false
480 MERGE INTO wq_target t
481 USING wq_source s ON t.tid = s.sid
482 WHEN NOT MATCHED AND s.balance <> 100 THEN
483 INSERT (tid) VALUES (s.sid);
484 SELECT * FROM wq_target;
487 -- and now with a condition which returns true
488 MERGE INTO wq_target t
489 USING wq_source s ON t.tid = s.sid
490 WHEN NOT MATCHED AND s.balance = 100 THEN
491 INSERT (tid) VALUES (s.sid);
492 SELECT * FROM wq_target;
495 -- conditions in the NOT MATCHED clause can only refer to source columns
497 MERGE INTO wq_target t
498 USING wq_source s ON t.tid = s.sid
499 WHEN NOT MATCHED AND t.balance = 100 THEN
500 INSERT (tid) VALUES (s.sid);
501 SELECT * FROM wq_target;
504 MERGE INTO wq_target t
505 USING wq_source s ON t.tid = s.sid
506 WHEN NOT MATCHED AND s.balance = 100 THEN
507 INSERT (tid) VALUES (s.sid);
508 SELECT * FROM wq_target;
510 -- conditions in MATCHED clause can refer to both source and target
511 SELECT * FROM wq_source;
512 MERGE INTO wq_target t
513 USING wq_source s ON t.tid = s.sid
514 WHEN MATCHED AND s.balance = 100 THEN
515 UPDATE SET balance = t.balance + s.balance;
516 SELECT * FROM wq_target;
518 MERGE INTO wq_target t
519 USING wq_source s ON t.tid = s.sid
520 WHEN MATCHED AND t.balance = 100 THEN
521 UPDATE SET balance = t.balance + s.balance;
522 SELECT * FROM wq_target;
524 -- check if AND works
525 MERGE INTO wq_target t
526 USING wq_source s ON t.tid = s.sid
527 WHEN MATCHED AND t.balance = 99 AND s.balance > 100 THEN
528 UPDATE SET balance = t.balance + s.balance;
529 SELECT * FROM wq_target;
531 MERGE INTO wq_target t
532 USING wq_source s ON t.tid = s.sid
533 WHEN MATCHED AND t.balance = 99 AND s.balance = 100 THEN
534 UPDATE SET balance = t.balance + s.balance;
535 SELECT * FROM wq_target;
538 MERGE INTO wq_target t
539 USING wq_source s ON t.tid = s.sid
540 WHEN MATCHED AND t.balance = 99 OR s.balance > 100 THEN
541 UPDATE SET balance = t.balance + s.balance;
542 SELECT * FROM wq_target;
544 MERGE INTO wq_target t
545 USING wq_source s ON t.tid = s.sid
546 WHEN MATCHED AND t.balance = 199 OR s.balance > 100 THEN
547 UPDATE SET balance = t.balance + s.balance;
548 SELECT * FROM wq_target;
550 -- check source-side whole-row references
552 MERGE INTO wq_target t
553 USING wq_source s ON (t.tid = s.sid)
554 WHEN matched and t = s or t.tid = s.sid THEN
555 UPDATE SET balance = t.balance + s.balance;
556 SELECT * FROM wq_target;
559 -- check if subqueries work in the conditions?
560 MERGE INTO wq_target t
561 USING wq_source s ON t.tid = s.sid
562 WHEN MATCHED AND t.balance > (SELECT max(balance) FROM target) THEN
563 UPDATE SET balance = t.balance + s.balance;
565 -- check if we can access system columns in the conditions
566 MERGE INTO wq_target t
567 USING wq_source s ON t.tid = s.sid
568 WHEN MATCHED AND t.xmin = t.xmax THEN
569 UPDATE SET balance = t.balance + s.balance;
571 MERGE INTO wq_target t
572 USING wq_source s ON t.tid = s.sid
573 WHEN MATCHED AND t.tableoid >= 0 THEN
574 UPDATE SET balance = t.balance + s.balance;
575 SELECT * FROM wq_target;
577 DROP TABLE wq_target, wq_source;
580 create or replace function merge_trigfunc () returns trigger
586 SELECT INTO line format('%s %s %s trigger%s',
587 TG_WHEN, TG_OP, TG_LEVEL, CASE
588 WHEN TG_OP = 'INSERT' AND TG_LEVEL = 'ROW'
589 THEN format(' row: %s', NEW)
590 WHEN TG_OP = 'UPDATE' AND TG_LEVEL = 'ROW'
591 THEN format(' row: %s -> %s', OLD, NEW)
592 WHEN TG_OP = 'DELETE' AND TG_LEVEL = 'ROW'
593 THEN format(' row: %s', OLD)
596 RAISE NOTICE '%', line;
597 IF (TG_WHEN = 'BEFORE' AND TG_LEVEL = 'ROW') THEN
598 IF (TG_OP = 'DELETE') THEN
608 CREATE TRIGGER merge_bsi BEFORE INSERT ON target FOR EACH STATEMENT EXECUTE PROCEDURE merge_trigfunc ();
609 CREATE TRIGGER merge_bsu BEFORE UPDATE ON target FOR EACH STATEMENT EXECUTE PROCEDURE merge_trigfunc ();
610 CREATE TRIGGER merge_bsd BEFORE DELETE ON target FOR EACH STATEMENT EXECUTE PROCEDURE merge_trigfunc ();
611 CREATE TRIGGER merge_asi AFTER INSERT ON target FOR EACH STATEMENT EXECUTE PROCEDURE merge_trigfunc ();
612 CREATE TRIGGER merge_asu AFTER UPDATE ON target FOR EACH STATEMENT EXECUTE PROCEDURE merge_trigfunc ();
613 CREATE TRIGGER merge_asd AFTER DELETE ON target FOR EACH STATEMENT EXECUTE PROCEDURE merge_trigfunc ();
614 CREATE TRIGGER merge_bri BEFORE INSERT ON target FOR EACH ROW EXECUTE PROCEDURE merge_trigfunc ();
615 CREATE TRIGGER merge_bru BEFORE UPDATE ON target FOR EACH ROW EXECUTE PROCEDURE merge_trigfunc ();
616 CREATE TRIGGER merge_brd BEFORE DELETE ON target FOR EACH ROW EXECUTE PROCEDURE merge_trigfunc ();
617 CREATE TRIGGER merge_ari AFTER INSERT ON target FOR EACH ROW EXECUTE PROCEDURE merge_trigfunc ();
618 CREATE TRIGGER merge_aru AFTER UPDATE ON target FOR EACH ROW EXECUTE PROCEDURE merge_trigfunc ();
619 CREATE TRIGGER merge_ard AFTER DELETE ON target FOR EACH ROW EXECUTE PROCEDURE merge_trigfunc ();
621 -- now the classic UPSERT, with a DELETE
623 UPDATE target SET balance = 0 WHERE tid = 3;
624 --EXPLAIN (ANALYZE ON, COSTS OFF, SUMMARY OFF, TIMING OFF)
628 WHEN MATCHED AND t.balance > s.delta THEN
629 UPDATE SET balance = t.balance - s.delta
632 WHEN NOT MATCHED THEN
633 INSERT VALUES (s.sid, s.delta);
634 SELECT * FROM target ORDER BY tid;
637 -- Test behavior of triggers that turn UPDATE/DELETE into no-ops
638 create or replace function skip_merge_op() returns trigger
646 SELECT * FROM target full outer join source on (sid = tid);
647 create trigger merge_skip BEFORE INSERT OR UPDATE or DELETE
648 ON target FOR EACH ROW EXECUTE FUNCTION skip_merge_op();
656 WHEN MATCHED AND s.sid = 3 THEN UPDATE SET balance = t.balance + s.delta
657 WHEN MATCHED THEN DELETE
658 WHEN NOT MATCHED THEN INSERT VALUES (sid, delta);
660 RAISE NOTICE 'Found';
662 RAISE NOTICE 'Not found';
664 GET DIAGNOSTICS result := ROW_COUNT;
665 RAISE NOTICE 'ROW_COUNT = %', result;
668 SELECT * FROM target FULL OUTER JOIN source ON (sid = tid);
669 DROP TRIGGER merge_skip ON target;
670 DROP FUNCTION skip_merge_op();
672 -- test from PL/pgSQL
673 -- make sure MERGE INTO isn't interpreted to mean returning variables like SELECT INTO
675 DO LANGUAGE plpgsql $$
680 WHEN MATCHED AND t.balance > s.delta THEN
681 UPDATE SET balance = t.balance - s.delta;
689 USING (SELECT 9 AS sid, 57 AS delta) AS s
691 WHEN NOT MATCHED THEN
692 INSERT (tid, balance) VALUES (s.sid, s.delta);
693 SELECT * FROM target ORDER BY tid;
699 USING (SELECT sid, delta FROM source WHERE delta > 0) AS s
701 WHEN NOT MATCHED THEN
702 INSERT (tid, balance) VALUES (s.sid, s.delta);
703 SELECT * FROM target ORDER BY tid;
708 USING (SELECT sid, delta as newname FROM source WHERE delta > 0) AS s
710 WHEN NOT MATCHED THEN
711 INSERT (tid, balance) VALUES (s.sid, s.newname);
712 SELECT * FROM target ORDER BY tid;
721 UPDATE SET balance = t1.balance + t2.balance
722 WHEN NOT MATCHED THEN
723 INSERT VALUES (t2.tid, t2.balance);
724 SELECT * FROM target ORDER BY tid;
729 USING (SELECT tid as sid, balance as delta FROM target WHERE balance > 0) AS s
731 WHEN NOT MATCHED THEN
732 INSERT (tid, balance) VALUES (s.sid, s.delta);
733 SELECT * FROM target ORDER BY tid;
739 (SELECT sid, max(delta) AS delta
743 ORDER BY sid ASC) AS s
745 WHEN NOT MATCHED THEN
746 INSERT (tid, balance) VALUES (s.sid, s.delta);
747 SELECT * FROM target ORDER BY tid;
750 -- plpgsql parameters and results
752 CREATE FUNCTION merge_func (p_id integer, p_bal integer)
760 USING (SELECT p_id AS sid) AS s
763 UPDATE SET balance = t.balance - p_bal;
765 GET DIAGNOSTICS result := ROW_COUNT;
770 SELECT merge_func(3, 4);
771 SELECT * FROM target ORDER BY tid;
776 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;
778 SELECT * FROM target ORDER BY tid;
782 PREPARE foom2 (integer, integer) AS
787 UPDATE SET balance = $2;
788 --EXPLAIN (ANALYZE ON, COSTS OFF, SUMMARY OFF, TIMING OFF)
789 execute foom2 (1, 1);
790 SELECT * FROM target ORDER BY tid;
793 -- subqueries in source relation
795 CREATE TABLE sq_target (tid integer NOT NULL, balance integer)
796 WITH (autovacuum_enabled=off);
797 CREATE TABLE sq_source (delta integer, sid integer, balance integer DEFAULT 0)
798 WITH (autovacuum_enabled=off);
800 INSERT INTO sq_target(tid, balance) VALUES (1,100), (2,200), (3,300);
801 INSERT INTO sq_source(sid, delta) VALUES (1,10), (2,20), (4,40);
804 MERGE INTO sq_target t
805 USING (SELECT * FROM sq_source) s
807 WHEN MATCHED AND t.balance > delta THEN
808 UPDATE SET balance = t.balance + delta;
809 SELECT * FROM sq_target;
813 CREATE VIEW v AS SELECT * FROM sq_source WHERE sid < 2;
820 UPDATE SET balance = v.balance + delta;
821 SELECT * FROM sq_target;
824 -- ambiguous reference to a column
829 WHEN MATCHED AND tid > 2 THEN
830 UPDATE SET balance = balance + delta
831 WHEN NOT MATCHED THEN
832 INSERT (balance, tid) VALUES (balance + delta, sid)
833 WHEN MATCHED AND tid < 2 THEN
838 INSERT INTO sq_source (sid, balance, delta) VALUES (-1, -1, -10);
839 MERGE INTO sq_target t
842 WHEN MATCHED AND tid > 2 THEN
843 UPDATE SET balance = t.balance + delta
844 WHEN NOT MATCHED THEN
845 INSERT (balance, tid) VALUES (balance + delta, sid)
846 WHEN MATCHED AND tid < 2 THEN
848 SELECT * FROM sq_target;
853 INSERT INTO sq_source (sid, balance, delta) VALUES (-1, -1, -10);
857 MERGE INTO sq_target t
860 WHEN MATCHED AND tid > 2 THEN
861 UPDATE SET balance = t.balance + delta
862 WHEN NOT MATCHED THEN
863 INSERT (balance, tid) VALUES (balance + delta, sid)
864 WHEN MATCHED AND tid < 2 THEN
870 INSERT INTO sq_source (sid, balance, delta) VALUES (-1, -1, -10);
871 MERGE INTO sq_target t
874 WHEN MATCHED AND tid > 2 THEN
875 UPDATE SET balance = t.balance + delta
876 WHEN NOT MATCHED THEN
877 INSERT (balance, tid) VALUES (balance + delta, sid)
878 WHEN MATCHED AND tid < 2 THEN
884 CREATE TABLE ex_mtarget (a int, b int)
885 WITH (autovacuum_enabled=off);
886 CREATE TABLE ex_msource (a int, b int)
887 WITH (autovacuum_enabled=off);
888 INSERT INTO ex_mtarget SELECT i, i*10 FROM generate_series(1,100,2) i;
889 INSERT INTO ex_msource SELECT i, i*10 FROM generate_series(1,100,1) i;
891 CREATE FUNCTION explain_merge(query text) RETURNS SETOF text
897 EXECUTE 'explain (analyze, timing off, summary off, costs off) ' ||
900 ln := regexp_replace(ln, '(Memory( Usage)?|Buckets|Batches): \S*', '\1: xxx', 'g');
907 SELECT explain_merge('
908 MERGE INTO ex_mtarget t USING ex_msource s ON t.a = s.a
910 UPDATE SET b = t.b + 1');
912 -- only updates to selected tuples
913 SELECT explain_merge('
914 MERGE INTO ex_mtarget t USING ex_msource s ON t.a = s.a
915 WHEN MATCHED AND t.a < 10 THEN
916 UPDATE SET b = t.b + 1');
919 SELECT explain_merge('
920 MERGE INTO ex_mtarget t USING ex_msource s ON t.a = s.a
921 WHEN MATCHED AND t.a < 10 THEN
922 UPDATE SET b = t.b + 1
923 WHEN MATCHED AND t.a >= 10 AND t.a <= 20 THEN
927 SELECT explain_merge('
928 MERGE INTO ex_mtarget t USING ex_msource s ON t.a = s.a
929 WHEN NOT MATCHED AND s.a < 10 THEN
930 INSERT VALUES (a, b)');
933 SELECT explain_merge('
934 MERGE INTO ex_mtarget t USING ex_msource s ON t.a = s.a
935 WHEN MATCHED AND t.a < 10 THEN
936 UPDATE SET b = t.b + 1
937 WHEN MATCHED AND t.a >= 30 AND t.a <= 40 THEN
939 WHEN NOT MATCHED AND s.a < 20 THEN
940 INSERT VALUES (a, b)');
943 SELECT explain_merge('
944 MERGE INTO ex_mtarget t USING ex_msource s ON t.a = s.a AND t.a < -1000
945 WHEN MATCHED AND t.a < 10 THEN
948 DROP TABLE ex_msource, ex_mtarget;
949 DROP FUNCTION explain_merge(text);
953 MERGE INTO sq_target t
957 UPDATE SET balance = (SELECT count(*) FROM sq_target);
958 SELECT * FROM sq_target WHERE tid = 1;
962 MERGE INTO sq_target t
965 WHEN MATCHED AND (SELECT count(*) > 0 FROM sq_target) THEN
966 UPDATE SET balance = 42;
967 SELECT * FROM sq_target WHERE tid = 1;
971 MERGE INTO sq_target t
973 ON tid = sid AND (SELECT count(*) > 0 FROM sq_target)
975 UPDATE SET balance = 42;
976 SELECT * FROM sq_target WHERE tid = 1;
979 DROP TABLE sq_target, sq_source CASCADE;
981 CREATE TABLE pa_target (tid integer, balance float, val text)
982 PARTITION BY LIST (tid);
984 CREATE TABLE part1 PARTITION OF pa_target FOR VALUES IN (1,4)
985 WITH (autovacuum_enabled=off);
986 CREATE TABLE part2 PARTITION OF pa_target FOR VALUES IN (2,5,6)
987 WITH (autovacuum_enabled=off);
988 CREATE TABLE part3 PARTITION OF pa_target FOR VALUES IN (3,8,9)
989 WITH (autovacuum_enabled=off);
990 CREATE TABLE part4 PARTITION OF pa_target DEFAULT
991 WITH (autovacuum_enabled=off);
993 CREATE TABLE pa_source (sid integer, delta float);
994 -- insert many rows to the source table
995 INSERT INTO pa_source SELECT id, id * 10 FROM generate_series(1,14) AS id;
996 -- insert a few rows in the target table (odd numbered tid)
997 INSERT INTO pa_target SELECT id, id * 100, 'initial' FROM generate_series(1,14,2) AS id;
1001 MERGE INTO pa_target t
1005 UPDATE SET balance = balance + delta, val = val || ' updated by merge'
1006 WHEN NOT MATCHED THEN
1007 INSERT VALUES (sid, delta, 'inserted by merge');
1008 SELECT * FROM pa_target ORDER BY tid;
1011 -- same with a constant qual
1013 MERGE INTO pa_target t
1015 ON t.tid = s.sid AND tid = 1
1017 UPDATE SET balance = balance + delta, val = val || ' updated by merge'
1018 WHEN NOT MATCHED THEN
1019 INSERT VALUES (sid, delta, 'inserted by merge');
1020 SELECT * FROM pa_target ORDER BY tid;
1023 -- try updating the partition key column
1025 CREATE FUNCTION merge_func() RETURNS integer LANGUAGE plpgsql AS $$
1029 MERGE INTO pa_target t
1033 UPDATE SET tid = tid + 1, balance = balance + delta, val = val || ' updated by merge'
1034 WHEN NOT MATCHED THEN
1035 INSERT VALUES (sid, delta, 'inserted by merge');
1037 GET DIAGNOSTICS result := ROW_COUNT;
1042 SELECT merge_func();
1043 SELECT * FROM pa_target ORDER BY tid;
1046 DROP TABLE pa_target CASCADE;
1048 -- The target table is partitioned in the same way, but this time by attaching
1049 -- partitions which have columns in different order, dropped columns etc.
1050 CREATE TABLE pa_target (tid integer, balance float, val text)
1051 PARTITION BY LIST (tid);
1053 CREATE TABLE part1 (tid integer, balance float, val text)
1054 WITH (autovacuum_enabled=off);
1055 CREATE TABLE part2 (balance float, tid integer, val text)
1056 WITH (autovacuum_enabled=off);
1057 CREATE TABLE part3 (tid integer, balance float, val text)
1058 WITH (autovacuum_enabled=off);
1059 CREATE TABLE part4 (extraid text, tid integer, balance float, val text)
1060 WITH (autovacuum_enabled=off);
1061 ALTER TABLE part4 DROP COLUMN extraid;
1063 ALTER TABLE pa_target ATTACH PARTITION part1 FOR VALUES IN (1,4);
1064 ALTER TABLE pa_target ATTACH PARTITION part2 FOR VALUES IN (2,5,6);
1065 ALTER TABLE pa_target ATTACH PARTITION part3 FOR VALUES IN (3,8,9);
1066 ALTER TABLE pa_target ATTACH PARTITION part4 DEFAULT;
1068 -- insert a few rows in the target table (odd numbered tid)
1069 INSERT INTO pa_target SELECT id, id * 100, 'initial' FROM generate_series(1,14,2) AS id;
1077 MERGE INTO pa_target t
1081 UPDATE SET balance = balance + delta, val = val || ' updated by merge'
1082 WHEN NOT MATCHED THEN
1083 INSERT VALUES (sid, delta, 'inserted by merge');
1084 GET DIAGNOSTICS result := ROW_COUNT;
1085 RAISE NOTICE 'ROW_COUNT = %', result;
1088 SELECT * FROM pa_target ORDER BY tid;
1091 -- same with a constant qual
1093 MERGE INTO pa_target t
1095 ON t.tid = s.sid AND tid IN (1, 5)
1096 WHEN MATCHED AND tid % 5 = 0 THEN DELETE
1098 UPDATE SET balance = balance + delta, val = val || ' updated by merge'
1099 WHEN NOT MATCHED THEN
1100 INSERT VALUES (sid, delta, 'inserted by merge');
1101 SELECT * FROM pa_target ORDER BY tid;
1104 -- try updating the partition key column
1110 MERGE INTO pa_target t
1114 UPDATE SET tid = tid + 1, balance = balance + delta, val = val || ' updated by merge'
1115 WHEN NOT MATCHED THEN
1116 INSERT VALUES (sid, delta, 'inserted by merge');
1117 GET DIAGNOSTICS result := ROW_COUNT;
1118 RAISE NOTICE 'ROW_COUNT = %', result;
1121 SELECT * FROM pa_target ORDER BY tid;
1124 -- as above, but blocked by BEFORE DELETE ROW trigger
1126 CREATE FUNCTION trig_fn() RETURNS trigger LANGUAGE plpgsql AS
1127 $$ BEGIN RETURN NULL; END; $$;
1128 CREATE TRIGGER del_trig BEFORE DELETE ON pa_target
1129 FOR EACH ROW EXECUTE PROCEDURE trig_fn();
1134 MERGE INTO pa_target t
1138 UPDATE SET tid = tid + 1, balance = balance + delta, val = val || ' updated by merge'
1139 WHEN NOT MATCHED THEN
1140 INSERT VALUES (sid, delta, 'inserted by merge');
1141 GET DIAGNOSTICS result := ROW_COUNT;
1142 RAISE NOTICE 'ROW_COUNT = %', result;
1145 SELECT * FROM pa_target ORDER BY tid;
1148 -- as above, but blocked by BEFORE INSERT ROW trigger
1150 CREATE FUNCTION trig_fn() RETURNS trigger LANGUAGE plpgsql AS
1151 $$ BEGIN RETURN NULL; END; $$;
1152 CREATE TRIGGER ins_trig BEFORE INSERT ON pa_target
1153 FOR EACH ROW EXECUTE PROCEDURE trig_fn();
1158 MERGE INTO pa_target t
1162 UPDATE SET tid = tid + 1, balance = balance + delta, val = val || ' updated by merge'
1163 WHEN NOT MATCHED THEN
1164 INSERT VALUES (sid, delta, 'inserted by merge');
1165 GET DIAGNOSTICS result := ROW_COUNT;
1166 RAISE NOTICE 'ROW_COUNT = %', result;
1169 SELECT * FROM pa_target ORDER BY tid;
1172 -- test RLS enforcement
1174 ALTER TABLE pa_target ENABLE ROW LEVEL SECURITY;
1175 ALTER TABLE pa_target FORCE ROW LEVEL SECURITY;
1176 CREATE POLICY pa_target_pol ON pa_target USING (tid != 0);
1177 MERGE INTO pa_target t
1179 ON t.tid = s.sid AND t.tid IN (1,2,3,4)
1181 UPDATE SET tid = tid - 1;
1184 DROP TABLE pa_source;
1185 DROP TABLE pa_target CASCADE;
1188 CREATE TABLE pa_target (logts timestamp, tid integer, balance float, val text)
1189 PARTITION BY RANGE (logts);
1191 CREATE TABLE part_m01 PARTITION OF pa_target
1192 FOR VALUES FROM ('2017-01-01') TO ('2017-02-01')
1193 PARTITION BY LIST (tid);
1194 CREATE TABLE part_m01_odd PARTITION OF part_m01
1195 FOR VALUES IN (1,3,5,7,9) WITH (autovacuum_enabled=off);
1196 CREATE TABLE part_m01_even PARTITION OF part_m01
1197 FOR VALUES IN (2,4,6,8) WITH (autovacuum_enabled=off);
1198 CREATE TABLE part_m02 PARTITION OF pa_target
1199 FOR VALUES FROM ('2017-02-01') TO ('2017-03-01')
1200 PARTITION BY LIST (tid);
1201 CREATE TABLE part_m02_odd PARTITION OF part_m02
1202 FOR VALUES IN (1,3,5,7,9) WITH (autovacuum_enabled=off);
1203 CREATE TABLE part_m02_even PARTITION OF part_m02
1204 FOR VALUES IN (2,4,6,8) WITH (autovacuum_enabled=off);
1206 CREATE TABLE pa_source (sid integer, delta float)
1207 WITH (autovacuum_enabled=off);
1208 -- insert many rows to the source table
1209 INSERT INTO pa_source SELECT id, id * 10 FROM generate_series(1,14) AS id;
1210 -- insert a few rows in the target table (odd numbered tid)
1211 INSERT INTO pa_target SELECT '2017-01-31', id, id * 100, 'initial' FROM generate_series(1,9,3) AS id;
1212 INSERT INTO pa_target SELECT '2017-02-28', id, id * 100, 'initial' FROM generate_series(2,9,3) AS id;
1216 MERGE INTO pa_target t
1217 USING (SELECT '2017-01-15' AS slogts, * FROM pa_source WHERE sid < 10) s
1220 UPDATE SET balance = balance + delta, val = val || ' updated by merge'
1221 WHEN NOT MATCHED THEN
1222 INSERT VALUES (slogts::timestamp, sid, delta, 'inserted by merge');
1223 SELECT * FROM pa_target ORDER BY tid;
1226 DROP TABLE pa_source;
1227 DROP TABLE pa_target CASCADE;
1229 -- Partitioned table with primary key
1231 CREATE TABLE pa_target (tid integer PRIMARY KEY) PARTITION BY LIST (tid);
1232 CREATE TABLE pa_targetp PARTITION OF pa_target DEFAULT;
1233 CREATE TABLE pa_source (sid integer);
1235 INSERT INTO pa_source VALUES (1), (2);
1237 EXPLAIN (VERBOSE, COSTS OFF)
1238 MERGE INTO pa_target t USING pa_source s ON t.tid = s.sid
1239 WHEN NOT MATCHED THEN INSERT VALUES (s.sid);
1241 MERGE INTO pa_target t USING pa_source s ON t.tid = s.sid
1242 WHEN NOT MATCHED THEN INSERT VALUES (s.sid);
1246 -- Partition-less partitioned table
1247 -- (the bug we are checking for appeared only if table had partitions before)
1249 DROP TABLE pa_targetp;
1251 EXPLAIN (VERBOSE, COSTS OFF)
1252 MERGE INTO pa_target t USING pa_source s ON t.tid = s.sid
1253 WHEN NOT MATCHED THEN INSERT VALUES (s.sid);
1255 MERGE INTO pa_target t USING pa_source s ON t.tid = s.sid
1256 WHEN NOT MATCHED THEN INSERT VALUES (s.sid);
1258 DROP TABLE pa_source;
1259 DROP TABLE pa_target CASCADE;
1261 -- some complex joins on the source side
1263 CREATE TABLE cj_target (tid integer, balance float, val text)
1264 WITH (autovacuum_enabled=off);
1265 CREATE TABLE cj_source1 (sid1 integer, scat integer, delta integer)
1266 WITH (autovacuum_enabled=off);
1267 CREATE TABLE cj_source2 (sid2 integer, sval text)
1268 WITH (autovacuum_enabled=off);
1269 INSERT INTO cj_source1 VALUES (1, 10, 100);
1270 INSERT INTO cj_source1 VALUES (1, 20, 200);
1271 INSERT INTO cj_source1 VALUES (2, 20, 300);
1272 INSERT INTO cj_source1 VALUES (3, 10, 400);
1273 INSERT INTO cj_source2 VALUES (1, 'initial source2');
1274 INSERT INTO cj_source2 VALUES (2, 'initial source2');
1275 INSERT INTO cj_source2 VALUES (3, 'initial source2');
1277 -- source relation is an unaliased join
1278 MERGE INTO cj_target t
1280 INNER JOIN cj_source2 s2 ON sid1 = sid2
1282 WHEN NOT MATCHED THEN
1283 INSERT VALUES (sid1, delta, sval);
1285 -- try accessing columns from either side of the source join
1286 MERGE INTO cj_target t
1288 INNER JOIN cj_source1 s1 ON sid1 = sid2 AND scat = 20
1290 WHEN NOT MATCHED THEN
1291 INSERT VALUES (sid2, delta, sval)
1295 -- some simple expressions in INSERT targetlist
1296 MERGE INTO cj_target t
1298 INNER JOIN cj_source1 s1 ON sid1 = sid2
1300 WHEN NOT MATCHED THEN
1301 INSERT VALUES (sid2, delta + scat, sval)
1303 UPDATE SET val = val || ' updated by merge';
1305 MERGE INTO cj_target t
1307 INNER JOIN cj_source1 s1 ON sid1 = sid2 AND scat = 20
1310 UPDATE SET val = val || ' ' || delta::text;
1312 SELECT * FROM cj_target;
1314 -- try it with an outer join and PlaceHolderVar
1315 MERGE INTO cj_target t
1316 USING (SELECT *, 'join input'::text AS phv FROM cj_source1) fj
1317 FULL JOIN cj_source2 fj2 ON fj.scat = fj2.sid2 * 10
1319 WHEN NOT MATCHED THEN
1320 INSERT (tid, balance, val) VALUES (fj.scat, fj.delta, fj.phv);
1322 SELECT * FROM cj_target;
1324 ALTER TABLE cj_source1 RENAME COLUMN sid1 TO sid;
1325 ALTER TABLE cj_source2 RENAME COLUMN sid2 TO sid;
1329 MERGE INTO cj_target t
1331 INNER JOIN cj_source2 s2 ON s1.sid = s2.sid
1333 WHEN NOT MATCHED THEN
1334 INSERT VALUES (s2.sid, delta, sval);
1336 DROP TABLE cj_source2, cj_source1, cj_target;
1339 CREATE TABLE fs_target (a int, b int, c text)
1340 WITH (autovacuum_enabled=off);
1341 MERGE INTO fs_target t
1342 USING generate_series(1,100,1) AS id
1345 UPDATE SET b = b + id
1346 WHEN NOT MATCHED THEN
1347 INSERT VALUES (id, -1);
1349 MERGE INTO fs_target t
1350 USING generate_series(1,100,2) AS id
1353 UPDATE SET b = b + id, c = 'updated '|| id.*::text
1354 WHEN NOT MATCHED THEN
1355 INSERT VALUES (id, -1, 'inserted ' || id.*::text);
1357 SELECT count(*) FROM fs_target;
1358 DROP TABLE fs_target;
1360 -- SERIALIZABLE test
1361 -- handled in isolation tests
1363 -- Inheritance-based partitioning
1364 CREATE TABLE measurement (
1365 city_id int not null,
1366 logdate date not null,
1369 ) WITH (autovacuum_enabled=off);
1370 CREATE TABLE measurement_y2006m02 (
1371 CHECK ( logdate >= DATE '2006-02-01' AND logdate < DATE '2006-03-01' )
1372 ) INHERITS (measurement) WITH (autovacuum_enabled=off);
1373 CREATE TABLE measurement_y2006m03 (
1374 CHECK ( logdate >= DATE '2006-03-01' AND logdate < DATE '2006-04-01' )
1375 ) INHERITS (measurement) WITH (autovacuum_enabled=off);
1376 CREATE TABLE measurement_y2007m01 (
1379 logdate date not null,
1380 city_id int not null,
1382 CHECK ( logdate >= DATE '2007-01-01' AND logdate < DATE '2007-02-01')
1383 ) WITH (autovacuum_enabled=off);
1384 ALTER TABLE measurement_y2007m01 DROP COLUMN filler;
1385 ALTER TABLE measurement_y2007m01 INHERIT measurement;
1386 INSERT INTO measurement VALUES (0, '2005-07-21', 5, 15);
1388 CREATE OR REPLACE FUNCTION measurement_insert_trigger()
1389 RETURNS TRIGGER AS $$
1391 IF ( NEW.logdate >= DATE '2006-02-01' AND
1392 NEW.logdate < DATE '2006-03-01' ) THEN
1393 INSERT INTO measurement_y2006m02 VALUES (NEW.*);
1394 ELSIF ( NEW.logdate >= DATE '2006-03-01' AND
1395 NEW.logdate < DATE '2006-04-01' ) THEN
1396 INSERT INTO measurement_y2006m03 VALUES (NEW.*);
1397 ELSIF ( NEW.logdate >= DATE '2007-01-01' AND
1398 NEW.logdate < DATE '2007-02-01' ) THEN
1399 INSERT INTO measurement_y2007m01 (city_id, logdate, peaktemp, unitsales)
1402 RAISE EXCEPTION 'Date out of range. Fix the measurement_insert_trigger() function!';
1406 $$ LANGUAGE plpgsql ;
1407 CREATE TRIGGER insert_measurement_trigger
1408 BEFORE INSERT ON measurement
1409 FOR EACH ROW EXECUTE PROCEDURE measurement_insert_trigger();
1410 INSERT INTO measurement VALUES (1, '2006-02-10', 35, 10);
1411 INSERT INTO measurement VALUES (1, '2006-02-16', 45, 20);
1412 INSERT INTO measurement VALUES (1, '2006-03-17', 25, 10);
1413 INSERT INTO measurement VALUES (1, '2006-03-27', 15, 40);
1414 INSERT INTO measurement VALUES (1, '2007-01-15', 10, 10);
1415 INSERT INTO measurement VALUES (1, '2007-01-17', 10, 10);
1417 SELECT tableoid::regclass, * FROM measurement ORDER BY city_id, logdate;
1419 CREATE TABLE new_measurement (LIKE measurement) WITH (autovacuum_enabled=off);
1420 INSERT INTO new_measurement VALUES (0, '2005-07-21', 25, 20);
1421 INSERT INTO new_measurement VALUES (1, '2006-03-01', 20, 10);
1422 INSERT INTO new_measurement VALUES (1, '2006-02-16', 50, 10);
1423 INSERT INTO new_measurement VALUES (2, '2006-02-10', 20, 20);
1424 INSERT INTO new_measurement VALUES (1, '2006-03-27', NULL, NULL);
1425 INSERT INTO new_measurement VALUES (1, '2007-01-17', NULL, NULL);
1426 INSERT INTO new_measurement VALUES (1, '2007-01-15', 5, NULL);
1427 INSERT INTO new_measurement VALUES (1, '2007-01-16', 10, 10);
1430 MERGE INTO ONLY measurement m
1431 USING new_measurement nm ON
1432 (m.city_id = nm.city_id and m.logdate=nm.logdate)
1433 WHEN MATCHED AND nm.peaktemp IS NULL THEN DELETE
1434 WHEN MATCHED THEN UPDATE
1435 SET peaktemp = greatest(m.peaktemp, nm.peaktemp),
1436 unitsales = m.unitsales + coalesce(nm.unitsales, 0)
1437 WHEN NOT MATCHED THEN INSERT
1438 (city_id, logdate, peaktemp, unitsales)
1439 VALUES (city_id, logdate, peaktemp, unitsales);
1441 SELECT tableoid::regclass, * FROM measurement ORDER BY city_id, logdate, peaktemp;
1444 MERGE into measurement m
1445 USING new_measurement nm ON
1446 (m.city_id = nm.city_id and m.logdate=nm.logdate)
1447 WHEN MATCHED AND nm.peaktemp IS NULL THEN DELETE
1448 WHEN MATCHED THEN UPDATE
1449 SET peaktemp = greatest(m.peaktemp, nm.peaktemp),
1450 unitsales = m.unitsales + coalesce(nm.unitsales, 0)
1451 WHEN NOT MATCHED THEN INSERT
1452 (city_id, logdate, peaktemp, unitsales)
1453 VALUES (city_id, logdate, peaktemp, unitsales);
1455 SELECT tableoid::regclass, * FROM measurement ORDER BY city_id, logdate;
1458 MERGE INTO new_measurement nm
1459 USING ONLY measurement m ON
1460 (nm.city_id = m.city_id and nm.logdate=m.logdate)
1461 WHEN MATCHED THEN DELETE;
1463 SELECT * FROM new_measurement ORDER BY city_id, logdate;
1466 MERGE INTO new_measurement nm
1467 USING measurement m ON
1468 (nm.city_id = m.city_id and nm.logdate=m.logdate)
1469 WHEN MATCHED THEN DELETE;
1471 SELECT * FROM new_measurement ORDER BY city_id, logdate;
1473 DROP TABLE measurement, new_measurement CASCADE;
1474 DROP FUNCTION measurement_insert_trigger();
1478 RESET SESSION AUTHORIZATION;
1479 DROP TABLE target, target2;
1480 DROP TABLE source, source2;
1481 DROP FUNCTION merge_trigfunc();
1482 DROP USER regress_merge_privs;
1483 DROP USER regress_merge_no_privs;
1484 DROP USER regress_merge_none;