4 CREATE USER regress_merge_privs;
5 CREATE USER regress_merge_no_privs;
6 CREATE USER regress_merge_none;
7 DROP TABLE IF EXISTS target;
8 NOTICE: table "target" does not exist, skipping
9 DROP TABLE IF EXISTS source;
10 NOTICE: table "source" does not exist, skipping
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;
19 matched | tid | balance | sid | delta
20 ---------+-----+---------+-----+-------
26 ALTER TABLE target OWNER TO regress_merge_privs;
27 ALTER TABLE source OWNER TO regress_merge_privs;
28 CREATE TABLE target2 (tid integer, balance integer)
29 WITH (autovacuum_enabled=off);
30 CREATE TABLE source2 (sid integer, delta integer)
31 WITH (autovacuum_enabled=off);
32 ALTER TABLE target2 OWNER TO regress_merge_no_privs;
33 ALTER TABLE source2 OWNER TO regress_merge_no_privs;
34 GRANT INSERT ON target TO regress_merge_no_privs;
35 SET SESSION AUTHORIZATION regress_merge_privs;
43 ----------------------------------------
46 Merge Cond: (t.tid = s.sid)
49 -> Seq Scan on target t
52 -> Seq Scan on source s
58 MERGE INTO target t RANDOMWORD
62 UPDATE SET balance = 0;
63 ERROR: syntax error at or near "RANDOMWORD"
64 LINE 1: MERGE INTO target t RANDOMWORD
66 -- MATCHED/INSERT error
71 INSERT DEFAULT VALUES;
72 ERROR: syntax error at or near "INSERT"
73 LINE 5: INSERT DEFAULT VALUES;
75 -- NOT MATCHED BY SOURCE/INSERT error
79 WHEN NOT MATCHED BY SOURCE THEN
80 INSERT DEFAULT VALUES;
81 ERROR: syntax error at or near "INSERT"
82 LINE 5: INSERT DEFAULT VALUES;
84 -- incorrectly specifying INTO target
89 INSERT INTO target DEFAULT VALUES;
90 ERROR: syntax error at or near "INTO"
91 LINE 5: INSERT INTO target DEFAULT VALUES;
93 -- Multiple VALUES clause
98 INSERT VALUES (1,1), (2,2);
99 ERROR: syntax error at or near ","
100 LINE 5: INSERT VALUES (1,1), (2,2);
102 -- SELECT query for INSERT
106 WHEN NOT MATCHED THEN
107 INSERT SELECT (1, 1);
108 ERROR: syntax error at or near "SELECT"
109 LINE 5: INSERT SELECT (1, 1);
111 -- NOT MATCHED/UPDATE
115 WHEN NOT MATCHED THEN
116 UPDATE SET balance = 0;
117 ERROR: syntax error at or near "UPDATE"
118 LINE 5: UPDATE SET balance = 0;
120 -- NOT MATCHED BY TARGET/UPDATE
124 WHEN NOT MATCHED BY TARGET THEN
125 UPDATE SET balance = 0;
126 ERROR: syntax error at or near "UPDATE"
127 LINE 5: UPDATE SET balance = 0;
134 UPDATE target SET balance = 0;
135 ERROR: syntax error at or near "target"
136 LINE 5: UPDATE target SET balance = 0;
138 -- source and target names the same
142 WHEN MATCHED THEN DO NOTHING;
143 ERROR: name "target" specified more than once
144 DETAIL: The name is used both as MERGE target table and data source.
145 -- used in a CTE without RETURNING
147 MERGE INTO target USING source ON (true)
148 WHEN MATCHED THEN DELETE
150 ERROR: WITH query "foo" does not have a RETURNING clause
151 LINE 4: ) SELECT * FROM foo;
153 -- used in COPY without RETURNING
155 MERGE INTO target USING source ON (true)
156 WHEN MATCHED THEN DELETE
158 ERROR: COPY query must have a RETURNING clause
159 -- unsupported relation types
161 CREATE MATERIALIZED VIEW mv AS SELECT * FROM target;
165 WHEN NOT MATCHED THEN
166 INSERT DEFAULT VALUES;
167 ERROR: cannot execute MERGE on relation "mv"
168 DETAIL: This operation is not supported for materialized views.
169 DROP MATERIALIZED VIEW mv;
171 SET SESSION AUTHORIZATION regress_merge_none;
177 ERROR: permission denied for table target
178 SET SESSION AUTHORIZATION regress_merge_privs;
181 ON target.tid = source2.sid
183 UPDATE SET balance = 0;
184 ERROR: permission denied for table source2
185 GRANT INSERT ON target TO regress_merge_no_privs;
186 SET SESSION AUTHORIZATION regress_merge_no_privs;
189 ON target.tid = source2.sid
191 UPDATE SET balance = 0;
192 ERROR: permission denied for table target
193 GRANT UPDATE ON target2 TO regress_merge_privs;
194 SET SESSION AUTHORIZATION regress_merge_privs;
197 ON target2.tid = source.sid
200 ERROR: permission denied for table target2
203 ON target2.tid = source.sid
204 WHEN NOT MATCHED THEN
205 INSERT DEFAULT VALUES;
206 ERROR: permission denied for table target2
207 -- check if the target can be accessed from source relation subquery; we should
208 -- not be able to do so
210 USING (SELECT * FROM source WHERE t.tid > sid) s
212 WHEN NOT MATCHED THEN
213 INSERT DEFAULT VALUES;
214 ERROR: invalid reference to FROM-clause entry for table "t"
215 LINE 2: USING (SELECT * FROM source WHERE t.tid > sid) s
217 DETAIL: There is an entry for table "t", but it cannot be referenced from this part of the query.
221 -- zero rows in source has no effect
224 ON target.tid = source.sid
226 UPDATE SET balance = 0;
231 UPDATE SET balance = 0;
241 WHEN NOT MATCHED THEN
242 INSERT DEFAULT VALUES;
244 -- insert some non-matching source rows to work from
245 INSERT INTO source VALUES (4, 40);
246 SELECT * FROM source ORDER BY sid;
252 SELECT * FROM target ORDER BY tid;
263 WHEN NOT MATCHED THEN
269 UPDATE SET balance = 0;
279 WHEN NOT MATCHED THEN
280 INSERT DEFAULT VALUES;
281 SELECT * FROM target ORDER BY tid;
291 -- DELETE/INSERT not matched by source/target
296 WHEN NOT MATCHED BY SOURCE THEN
298 WHEN NOT MATCHED BY TARGET THEN
299 INSERT VALUES (s.sid, s.delta)
300 RETURNING merge_action(), t.*;
301 merge_action | tid | balance
302 --------------+-----+---------
309 SELECT * FROM target ORDER BY tid;
317 INSERT INTO target SELECT generate_series(1000,2500), 0;
318 ALTER TABLE target ADD PRIMARY KEY (tid);
325 UPDATE SET balance = 0;
327 ----------------------------------------
330 Hash Cond: (s.sid = t.tid)
331 -> Seq Scan on source s
333 -> Seq Scan on target t
343 ----------------------------------------
346 Hash Cond: (s.sid = t.tid)
347 -> Seq Scan on source s
349 -> Seq Scan on target t
356 WHEN NOT MATCHED THEN
357 INSERT VALUES (4, NULL);
359 ----------------------------------------
362 Hash Cond: (s.sid = t.tid)
363 -> Seq Scan on source s
365 -> Seq Scan on target t
368 DELETE FROM target WHERE tid > 100;
370 -- insert some matching source rows to work from
371 INSERT INTO source VALUES (2, 5);
372 INSERT INTO source VALUES (3, 20);
373 SELECT * FROM source ORDER BY sid;
381 SELECT * FROM target ORDER BY tid;
389 -- equivalent of an UPDATE join
395 UPDATE SET balance = 0;
396 SELECT * FROM target ORDER BY tid;
405 -- equivalent of a DELETE join
412 SELECT * FROM target ORDER BY tid;
425 SELECT * FROM target ORDER BY tid;
438 WHEN NOT MATCHED THEN
439 INSERT VALUES (4, NULL);
440 SELECT * FROM target ORDER BY tid;
450 -- duplicate source row causes multiple target row update ERROR
451 INSERT INTO source VALUES (2, 5);
452 SELECT * FROM source ORDER BY sid;
461 SELECT * FROM target ORDER BY tid;
474 UPDATE SET balance = 0;
475 ERROR: MERGE command cannot affect row a second time
476 HINT: Ensure that not more than one source row matches any one target row.
484 ERROR: MERGE command cannot affect row a second time
485 HINT: Ensure that not more than one source row matches any one target row.
487 -- remove duplicate MATCHED data from source data
488 DELETE FROM source WHERE sid = 2;
489 INSERT INTO source VALUES (2, 5);
490 SELECT * FROM source ORDER BY sid;
498 SELECT * FROM target ORDER BY tid;
506 -- duplicate source row on INSERT should fail because of target_pkey
507 INSERT INTO source VALUES (4, 40);
512 WHEN NOT MATCHED THEN
513 INSERT VALUES (4, NULL);
514 ERROR: duplicate key value violates unique constraint "target_pkey"
515 DETAIL: Key (tid)=(4) already exists.
516 SELECT * FROM target ORDER BY tid;
517 ERROR: current transaction is aborted, commands ignored until end of transaction block
519 -- remove duplicate NOT MATCHED data from source data
520 DELETE FROM source WHERE sid = 4;
521 INSERT INTO source VALUES (4, 40);
522 SELECT * FROM source ORDER BY sid;
530 SELECT * FROM target ORDER BY tid;
538 -- remove constraints
539 alter table target drop CONSTRAINT target_pkey;
540 alter table target alter column tid drop not null;
546 WHEN NOT MATCHED THEN
549 UPDATE SET balance = 0;
550 SELECT * FROM target ORDER BY tid;
560 -- should be equivalent
566 UPDATE SET balance = 0
567 WHEN NOT MATCHED THEN
568 INSERT VALUES (4, 4);
569 SELECT * FROM target ORDER BY tid;
580 -- do a simple equivalent of an UPDATE join
586 UPDATE SET balance = t.balance + s.delta;
587 SELECT * FROM target ORDER BY tid;
596 -- do a simple equivalent of an INSERT SELECT
601 WHEN NOT MATCHED THEN
602 INSERT VALUES (s.sid, s.delta);
603 SELECT * FROM target ORDER BY tid;
613 -- and again with duplicate source rows
614 INSERT INTO source VALUES (5, 50);
615 INSERT INTO source VALUES (5, 50);
616 -- do a simple equivalent of an INSERT SELECT
621 WHEN NOT MATCHED THEN
622 INSERT VALUES (s.sid, s.delta);
623 SELECT * FROM target ORDER BY tid;
635 -- removing duplicate source rows
636 DELETE FROM source WHERE sid = 5;
637 -- and again with explicitly identified column list
642 WHEN NOT MATCHED THEN
643 INSERT (tid, balance) VALUES (s.sid, s.delta);
644 SELECT * FROM target ORDER BY tid;
654 -- and again with a subtle error: referring to non-existent target row for NOT MATCHED
658 WHEN NOT MATCHED THEN
659 INSERT (tid, balance) VALUES (t.tid, s.delta);
660 ERROR: invalid reference to FROM-clause entry for table "t"
661 LINE 5: INSERT (tid, balance) VALUES (t.tid, s.delta);
663 DETAIL: There is an entry for table "t", but it cannot be referenced from this part of the query.
664 -- and again with a constant ON clause
669 WHEN NOT MATCHED THEN
670 INSERT (tid, balance) VALUES (t.tid, s.delta);
671 ERROR: invalid reference to FROM-clause entry for table "t"
672 LINE 5: INSERT (tid, balance) VALUES (t.tid, s.delta);
674 DETAIL: There is an entry for table "t", but it cannot be referenced from this part of the query.
675 SELECT * FROM target ORDER BY tid;
676 ERROR: current transaction is aborted, commands ignored until end of transaction block
678 -- now the classic UPSERT
684 UPDATE SET balance = t.balance + s.delta
685 WHEN NOT MATCHED THEN
686 INSERT VALUES (s.sid, s.delta);
687 SELECT * FROM target ORDER BY tid;
697 -- unreachable WHEN clause should ERROR
702 WHEN MATCHED THEN /* Terminal WHEN clause for MATCHED */
705 UPDATE SET balance = t.balance - s.delta;
706 ERROR: unreachable WHEN clause specified after unconditional WHEN clause
708 -- conditional WHEN clause
709 CREATE TABLE wq_target (tid integer not null, balance integer DEFAULT -1)
710 WITH (autovacuum_enabled=off);
711 CREATE TABLE wq_source (balance integer, sid integer)
712 WITH (autovacuum_enabled=off);
713 INSERT INTO wq_source (sid, balance) VALUES (1, 100);
715 -- try a simple INSERT with default values first
716 MERGE INTO wq_target t
717 USING wq_source s ON t.tid = s.sid
718 WHEN NOT MATCHED THEN
719 INSERT (tid) VALUES (s.sid);
720 SELECT * FROM wq_target;
727 -- this time with a FALSE condition
728 MERGE INTO wq_target t
729 USING wq_source s ON t.tid = s.sid
730 WHEN NOT MATCHED AND FALSE THEN
731 INSERT (tid) VALUES (s.sid);
732 SELECT * FROM wq_target;
737 -- this time with an actual condition which returns false
738 MERGE INTO wq_target t
739 USING wq_source s ON t.tid = s.sid
740 WHEN NOT MATCHED AND s.balance <> 100 THEN
741 INSERT (tid) VALUES (s.sid);
742 SELECT * FROM wq_target;
748 -- and now with a condition which returns true
749 MERGE INTO wq_target t
750 USING wq_source s ON t.tid = s.sid
751 WHEN NOT MATCHED AND s.balance = 100 THEN
752 INSERT (tid) VALUES (s.sid);
753 SELECT * FROM wq_target;
760 -- conditions in the NOT MATCHED clause can only refer to source columns
762 MERGE INTO wq_target t
763 USING wq_source s ON t.tid = s.sid
764 WHEN NOT MATCHED AND t.balance = 100 THEN
765 INSERT (tid) VALUES (s.sid);
766 ERROR: invalid reference to FROM-clause entry for table "t"
767 LINE 3: WHEN NOT MATCHED AND t.balance = 100 THEN
769 DETAIL: There is an entry for table "t", but it cannot be referenced from this part of the query.
770 SELECT * FROM wq_target;
771 ERROR: current transaction is aborted, commands ignored until end of transaction block
773 MERGE INTO wq_target t
774 USING wq_source s ON t.tid = s.sid
775 WHEN NOT MATCHED AND s.balance = 100 THEN
776 INSERT (tid) VALUES (s.sid);
777 SELECT * FROM wq_target;
783 -- conditions in NOT MATCHED BY SOURCE clause can only refer to target columns
784 MERGE INTO wq_target t
785 USING wq_source s ON t.tid = s.sid
786 WHEN NOT MATCHED BY SOURCE AND s.balance = 100 THEN
788 ERROR: invalid reference to FROM-clause entry for table "s"
789 LINE 3: WHEN NOT MATCHED BY SOURCE AND s.balance = 100 THEN
791 DETAIL: There is an entry for table "s", but it cannot be referenced from this part of the query.
792 MERGE INTO wq_target t
793 USING wq_source s ON t.tid = s.sid
794 WHEN NOT MATCHED BY SOURCE AND t.balance = 100 THEN
796 -- conditions in MATCHED clause can refer to both source and target
797 SELECT * FROM wq_source;
803 MERGE INTO wq_target t
804 USING wq_source s ON t.tid = s.sid
805 WHEN MATCHED AND s.balance = 100 THEN
806 UPDATE SET balance = t.balance + s.balance;
807 SELECT * FROM wq_target;
813 MERGE INTO wq_target t
814 USING wq_source s ON t.tid = s.sid
815 WHEN MATCHED AND t.balance = 100 THEN
816 UPDATE SET balance = t.balance + s.balance;
817 SELECT * FROM wq_target;
823 -- check if AND works
824 MERGE INTO wq_target t
825 USING wq_source s ON t.tid = s.sid
826 WHEN MATCHED AND t.balance = 99 AND s.balance > 100 THEN
827 UPDATE SET balance = t.balance + s.balance;
828 SELECT * FROM wq_target;
834 MERGE INTO wq_target t
835 USING wq_source s ON t.tid = s.sid
836 WHEN MATCHED AND t.balance = 99 AND s.balance = 100 THEN
837 UPDATE SET balance = t.balance + s.balance;
838 SELECT * FROM wq_target;
845 MERGE INTO wq_target t
846 USING wq_source s ON t.tid = s.sid
847 WHEN MATCHED AND t.balance = 99 OR s.balance > 100 THEN
848 UPDATE SET balance = t.balance + s.balance;
849 SELECT * FROM wq_target;
855 MERGE INTO wq_target t
856 USING wq_source s ON t.tid = s.sid
857 WHEN MATCHED AND t.balance = 199 OR s.balance > 100 THEN
858 UPDATE SET balance = t.balance + s.balance;
859 SELECT * FROM wq_target;
865 -- check source-side whole-row references
867 MERGE INTO wq_target t
868 USING wq_source s ON (t.tid = s.sid)
869 WHEN matched and t = s or t.tid = s.sid THEN
870 UPDATE SET balance = t.balance + s.balance;
871 SELECT * FROM wq_target;
878 -- check if subqueries work in the conditions?
879 MERGE INTO wq_target t
880 USING wq_source s ON t.tid = s.sid
881 WHEN MATCHED AND t.balance > (SELECT max(balance) FROM target) THEN
882 UPDATE SET balance = t.balance + s.balance;
883 -- check if we can access system columns in the conditions
884 MERGE INTO wq_target t
885 USING wq_source s ON t.tid = s.sid
886 WHEN MATCHED AND t.xmin = t.xmax THEN
887 UPDATE SET balance = t.balance + s.balance;
888 ERROR: cannot use system column "xmin" in MERGE WHEN condition
889 LINE 3: WHEN MATCHED AND t.xmin = t.xmax THEN
891 MERGE INTO wq_target t
892 USING wq_source s ON t.tid = s.sid
893 WHEN MATCHED AND t.tableoid >= 0 THEN
894 UPDATE SET balance = t.balance + s.balance;
895 SELECT * FROM wq_target;
901 DROP TABLE wq_target, wq_source;
903 create or replace function merge_trigfunc () returns trigger
909 SELECT INTO line format('%s %s %s trigger%s',
910 TG_WHEN, TG_OP, TG_LEVEL, CASE
911 WHEN TG_OP = 'INSERT' AND TG_LEVEL = 'ROW'
912 THEN format(' row: %s', NEW)
913 WHEN TG_OP = 'UPDATE' AND TG_LEVEL = 'ROW'
914 THEN format(' row: %s -> %s', OLD, NEW)
915 WHEN TG_OP = 'DELETE' AND TG_LEVEL = 'ROW'
916 THEN format(' row: %s', OLD)
919 RAISE NOTICE '%', line;
920 IF (TG_WHEN = 'BEFORE' AND TG_LEVEL = 'ROW') THEN
921 IF (TG_OP = 'DELETE') THEN
931 CREATE TRIGGER merge_bsi BEFORE INSERT ON target FOR EACH STATEMENT EXECUTE PROCEDURE merge_trigfunc ();
932 CREATE TRIGGER merge_bsu BEFORE UPDATE ON target FOR EACH STATEMENT EXECUTE PROCEDURE merge_trigfunc ();
933 CREATE TRIGGER merge_bsd BEFORE DELETE ON target FOR EACH STATEMENT EXECUTE PROCEDURE merge_trigfunc ();
934 CREATE TRIGGER merge_asi AFTER INSERT ON target FOR EACH STATEMENT EXECUTE PROCEDURE merge_trigfunc ();
935 CREATE TRIGGER merge_asu AFTER UPDATE ON target FOR EACH STATEMENT EXECUTE PROCEDURE merge_trigfunc ();
936 CREATE TRIGGER merge_asd AFTER DELETE ON target FOR EACH STATEMENT EXECUTE PROCEDURE merge_trigfunc ();
937 CREATE TRIGGER merge_bri BEFORE INSERT ON target FOR EACH ROW EXECUTE PROCEDURE merge_trigfunc ();
938 CREATE TRIGGER merge_bru BEFORE UPDATE ON target FOR EACH ROW EXECUTE PROCEDURE merge_trigfunc ();
939 CREATE TRIGGER merge_brd BEFORE DELETE ON target FOR EACH ROW EXECUTE PROCEDURE merge_trigfunc ();
940 CREATE TRIGGER merge_ari AFTER INSERT ON target FOR EACH ROW EXECUTE PROCEDURE merge_trigfunc ();
941 CREATE TRIGGER merge_aru AFTER UPDATE ON target FOR EACH ROW EXECUTE PROCEDURE merge_trigfunc ();
942 CREATE TRIGGER merge_ard AFTER DELETE ON target FOR EACH ROW EXECUTE PROCEDURE merge_trigfunc ();
943 -- now the classic UPSERT, with a DELETE
945 UPDATE target SET balance = 0 WHERE tid = 3;
946 NOTICE: BEFORE UPDATE STATEMENT trigger
947 NOTICE: BEFORE UPDATE ROW trigger row: (3,30) -> (3,0)
948 NOTICE: AFTER UPDATE ROW trigger row: (3,30) -> (3,0)
949 NOTICE: AFTER UPDATE STATEMENT trigger
950 --EXPLAIN (ANALYZE ON, COSTS OFF, SUMMARY OFF, TIMING OFF)
954 WHEN MATCHED AND t.balance > s.delta THEN
955 UPDATE SET balance = t.balance - s.delta
958 WHEN NOT MATCHED THEN
959 INSERT VALUES (s.sid, s.delta);
960 NOTICE: BEFORE INSERT STATEMENT trigger
961 NOTICE: BEFORE UPDATE STATEMENT trigger
962 NOTICE: BEFORE DELETE STATEMENT trigger
963 NOTICE: BEFORE DELETE ROW trigger row: (3,0)
964 NOTICE: BEFORE UPDATE ROW trigger row: (2,20) -> (2,15)
965 NOTICE: BEFORE INSERT ROW trigger row: (4,40)
966 NOTICE: AFTER DELETE ROW trigger row: (3,0)
967 NOTICE: AFTER UPDATE ROW trigger row: (2,20) -> (2,15)
968 NOTICE: AFTER INSERT ROW trigger row: (4,40)
969 NOTICE: AFTER DELETE STATEMENT trigger
970 NOTICE: AFTER UPDATE STATEMENT trigger
971 NOTICE: AFTER INSERT STATEMENT trigger
972 SELECT * FROM target ORDER BY tid;
981 -- UPSERT with UPDATE/DELETE when not matched by source
983 DELETE FROM SOURCE WHERE sid = 2;
987 WHEN MATCHED AND t.balance > s.delta THEN
988 UPDATE SET balance = t.balance - s.delta
990 UPDATE SET balance = 0
991 WHEN NOT MATCHED THEN
992 INSERT VALUES (s.sid, s.delta)
993 WHEN NOT MATCHED BY SOURCE AND tid = 1 THEN
994 UPDATE SET balance = 0
995 WHEN NOT MATCHED BY SOURCE THEN
997 RETURNING merge_action(), t.*;
998 NOTICE: BEFORE INSERT STATEMENT trigger
999 NOTICE: BEFORE UPDATE STATEMENT trigger
1000 NOTICE: BEFORE DELETE STATEMENT trigger
1001 NOTICE: BEFORE UPDATE ROW trigger row: (3,30) -> (3,10)
1002 NOTICE: BEFORE INSERT ROW trigger row: (4,40)
1003 NOTICE: BEFORE DELETE ROW trigger row: (2,20)
1004 NOTICE: BEFORE UPDATE ROW trigger row: (1,10) -> (1,0)
1005 NOTICE: AFTER UPDATE ROW trigger row: (3,30) -> (3,10)
1006 NOTICE: AFTER INSERT ROW trigger row: (4,40)
1007 NOTICE: AFTER DELETE ROW trigger row: (2,20)
1008 NOTICE: AFTER UPDATE ROW trigger row: (1,10) -> (1,0)
1009 NOTICE: AFTER DELETE STATEMENT trigger
1010 NOTICE: AFTER UPDATE STATEMENT trigger
1011 NOTICE: AFTER INSERT STATEMENT trigger
1012 merge_action | tid | balance
1013 --------------+-----+---------
1020 SELECT * FROM target ORDER BY tid;
1029 -- Test behavior of triggers that turn UPDATE/DELETE into no-ops
1030 create or replace function skip_merge_op() returns trigger
1037 SELECT * FROM target full outer join source on (sid = tid);
1038 tid | balance | sid | delta
1039 -----+---------+-----+-------
1046 create trigger merge_skip BEFORE INSERT OR UPDATE or DELETE
1047 ON target FOR EACH ROW EXECUTE FUNCTION skip_merge_op();
1055 WHEN MATCHED AND s.sid = 3 THEN UPDATE SET balance = t.balance + s.delta
1056 WHEN MATCHED THEN DELETE
1057 WHEN NOT MATCHED THEN INSERT VALUES (sid, delta);
1059 RAISE NOTICE 'Found';
1061 RAISE NOTICE 'Not found';
1063 GET DIAGNOSTICS result := ROW_COUNT;
1064 RAISE NOTICE 'ROW_COUNT = %', result;
1067 NOTICE: BEFORE INSERT STATEMENT trigger
1068 NOTICE: BEFORE UPDATE STATEMENT trigger
1069 NOTICE: BEFORE DELETE STATEMENT trigger
1070 NOTICE: BEFORE UPDATE ROW trigger row: (3,30) -> (3,50)
1071 NOTICE: BEFORE DELETE ROW trigger row: (2,20)
1072 NOTICE: BEFORE INSERT ROW trigger row: (4,40)
1073 NOTICE: AFTER DELETE STATEMENT trigger
1074 NOTICE: AFTER UPDATE STATEMENT trigger
1075 NOTICE: AFTER INSERT STATEMENT trigger
1077 NOTICE: ROW_COUNT = 0
1078 SELECT * FROM target FULL OUTER JOIN source ON (sid = tid);
1079 tid | balance | sid | delta
1080 -----+---------+-----+-------
1087 DROP TRIGGER merge_skip ON target;
1088 DROP FUNCTION skip_merge_op();
1089 -- test from PL/pgSQL
1090 -- make sure MERGE INTO isn't interpreted to mean returning variables like SELECT INTO
1092 DO LANGUAGE plpgsql $$
1097 WHEN MATCHED AND t.balance > s.delta THEN
1098 UPDATE SET balance = t.balance - s.delta;
1101 NOTICE: BEFORE UPDATE STATEMENT trigger
1102 NOTICE: BEFORE UPDATE ROW trigger row: (3,30) -> (3,10)
1103 NOTICE: BEFORE UPDATE ROW trigger row: (2,20) -> (2,15)
1104 NOTICE: AFTER UPDATE ROW trigger row: (3,30) -> (3,10)
1105 NOTICE: AFTER UPDATE ROW trigger row: (2,20) -> (2,15)
1106 NOTICE: AFTER UPDATE STATEMENT trigger
1111 USING (SELECT 9 AS sid, 57 AS delta) AS s
1113 WHEN NOT MATCHED THEN
1114 INSERT (tid, balance) VALUES (s.sid, s.delta);
1115 NOTICE: BEFORE INSERT STATEMENT trigger
1116 NOTICE: BEFORE INSERT ROW trigger row: (9,57)
1117 NOTICE: AFTER INSERT ROW trigger row: (9,57)
1118 NOTICE: AFTER INSERT STATEMENT trigger
1119 SELECT * FROM target ORDER BY tid;
1132 USING (SELECT sid, delta FROM source WHERE delta > 0) AS s
1134 WHEN NOT MATCHED THEN
1135 INSERT (tid, balance) VALUES (s.sid, s.delta);
1136 NOTICE: BEFORE INSERT STATEMENT trigger
1137 NOTICE: BEFORE INSERT ROW trigger row: (4,40)
1138 NOTICE: AFTER INSERT ROW trigger row: (4,40)
1139 NOTICE: AFTER INSERT STATEMENT trigger
1140 SELECT * FROM target ORDER BY tid;
1152 USING (SELECT sid, delta as newname FROM source WHERE delta > 0) AS s
1154 WHEN NOT MATCHED THEN
1155 INSERT (tid, balance) VALUES (s.sid, s.newname);
1156 NOTICE: BEFORE INSERT STATEMENT trigger
1157 NOTICE: BEFORE INSERT ROW trigger row: (4,40)
1158 NOTICE: AFTER INSERT ROW trigger row: (4,40)
1159 NOTICE: AFTER INSERT STATEMENT trigger
1160 SELECT * FROM target ORDER BY tid;
1172 MERGE INTO target t1
1176 UPDATE SET balance = t1.balance + t2.balance
1177 WHEN NOT MATCHED THEN
1178 INSERT VALUES (t2.tid, t2.balance);
1179 NOTICE: BEFORE INSERT STATEMENT trigger
1180 NOTICE: BEFORE UPDATE STATEMENT trigger
1181 NOTICE: BEFORE UPDATE ROW trigger row: (1,10) -> (1,20)
1182 NOTICE: BEFORE UPDATE ROW trigger row: (2,20) -> (2,40)
1183 NOTICE: BEFORE UPDATE ROW trigger row: (3,30) -> (3,60)
1184 NOTICE: AFTER UPDATE ROW trigger row: (1,10) -> (1,20)
1185 NOTICE: AFTER UPDATE ROW trigger row: (2,20) -> (2,40)
1186 NOTICE: AFTER UPDATE ROW trigger row: (3,30) -> (3,60)
1187 NOTICE: AFTER UPDATE STATEMENT trigger
1188 NOTICE: AFTER INSERT STATEMENT trigger
1189 SELECT * FROM target ORDER BY tid;
1200 USING (SELECT tid as sid, balance as delta FROM target WHERE balance > 0) AS s
1202 WHEN NOT MATCHED THEN
1203 INSERT (tid, balance) VALUES (s.sid, s.delta);
1204 NOTICE: BEFORE INSERT STATEMENT trigger
1205 NOTICE: AFTER INSERT STATEMENT trigger
1206 SELECT * FROM target ORDER BY tid;
1218 (SELECT sid, max(delta) AS delta
1222 ORDER BY sid ASC) AS s
1224 WHEN NOT MATCHED THEN
1225 INSERT (tid, balance) VALUES (s.sid, s.delta);
1226 NOTICE: BEFORE INSERT STATEMENT trigger
1227 NOTICE: BEFORE INSERT ROW trigger row: (4,40)
1228 NOTICE: AFTER INSERT ROW trigger row: (4,40)
1229 NOTICE: AFTER INSERT STATEMENT trigger
1230 SELECT * FROM target ORDER BY tid;
1240 -- plpgsql parameters and results
1242 CREATE FUNCTION merge_func (p_id integer, p_bal integer)
1250 USING (SELECT p_id AS sid) AS s
1253 UPDATE SET balance = t.balance - p_bal;
1255 GET DIAGNOSTICS result := ROW_COUNT;
1260 SELECT merge_func(3, 4);
1261 NOTICE: BEFORE UPDATE STATEMENT trigger
1262 NOTICE: BEFORE UPDATE ROW trigger row: (3,30) -> (3,26)
1263 NOTICE: AFTER UPDATE ROW trigger row: (3,30) -> (3,26)
1264 NOTICE: AFTER UPDATE STATEMENT trigger
1270 SELECT * FROM target ORDER BY tid;
1281 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;
1283 NOTICE: BEFORE UPDATE STATEMENT trigger
1284 NOTICE: BEFORE UPDATE ROW trigger row: (1,10) -> (1,1)
1285 NOTICE: AFTER UPDATE ROW trigger row: (1,10) -> (1,1)
1286 NOTICE: AFTER UPDATE STATEMENT trigger
1287 SELECT * FROM target ORDER BY tid;
1297 PREPARE foom2 (integer, integer) AS
1302 UPDATE SET balance = $2;
1303 --EXPLAIN (ANALYZE ON, COSTS OFF, SUMMARY OFF, TIMING OFF)
1304 execute foom2 (1, 1);
1305 NOTICE: BEFORE UPDATE STATEMENT trigger
1306 NOTICE: BEFORE UPDATE ROW trigger row: (1,10) -> (1,1)
1307 NOTICE: AFTER UPDATE ROW trigger row: (1,10) -> (1,1)
1308 NOTICE: AFTER UPDATE STATEMENT trigger
1309 SELECT * FROM target ORDER BY tid;
1318 -- subqueries in source relation
1319 CREATE TABLE sq_target (tid integer NOT NULL, balance integer)
1320 WITH (autovacuum_enabled=off);
1321 CREATE TABLE sq_source (delta integer, sid integer, balance integer DEFAULT 0)
1322 WITH (autovacuum_enabled=off);
1323 INSERT INTO sq_target(tid, balance) VALUES (1,100), (2,200), (3,300);
1324 INSERT INTO sq_source(sid, delta) VALUES (1,10), (2,20), (4,40);
1326 MERGE INTO sq_target t
1327 USING (SELECT * FROM sq_source) s
1329 WHEN MATCHED AND t.balance > delta THEN
1330 UPDATE SET balance = t.balance + delta;
1331 SELECT * FROM sq_target;
1341 CREATE VIEW v AS SELECT * FROM sq_source WHERE sid < 2;
1343 MERGE INTO sq_target
1347 UPDATE SET balance = v.balance + delta;
1348 SELECT * FROM sq_target;
1357 -- ambiguous reference to a column
1359 MERGE INTO sq_target
1362 WHEN MATCHED AND tid >= 2 THEN
1363 UPDATE SET balance = balance + delta
1364 WHEN NOT MATCHED THEN
1365 INSERT (balance, tid) VALUES (balance + delta, sid)
1366 WHEN MATCHED AND tid < 2 THEN
1368 ERROR: column reference "balance" is ambiguous
1369 LINE 5: UPDATE SET balance = balance + delta
1373 INSERT INTO sq_source (sid, balance, delta) VALUES (-1, -1, -10);
1374 MERGE INTO sq_target t
1377 WHEN MATCHED AND tid >= 2 THEN
1378 UPDATE SET balance = t.balance + delta
1379 WHEN NOT MATCHED THEN
1380 INSERT (balance, tid) VALUES (balance + delta, sid)
1381 WHEN MATCHED AND tid < 2 THEN
1383 SELECT * FROM sq_target;
1394 INSERT INTO sq_source (sid, balance, delta) VALUES (-1, -1, -10);
1398 MERGE INTO sq_target t
1401 WHEN MATCHED AND tid >= 2 THEN
1402 UPDATE SET balance = t.balance + delta
1403 WHEN NOT MATCHED THEN
1404 INSERT (balance, tid) VALUES (balance + delta, sid)
1405 WHEN MATCHED AND tid < 2 THEN
1409 SELECT * FROM sq_source ORDER BY sid;
1410 delta | sid | balance
1411 -------+-----+---------
1417 SELECT * FROM sq_target ORDER BY tid;
1426 CREATE TABLE merge_actions(action text, abbrev text);
1427 INSERT INTO merge_actions VALUES ('INSERT', 'ins'), ('UPDATE', 'upd'), ('DELETE', 'del');
1428 MERGE INTO sq_target t
1431 WHEN MATCHED AND tid >= 2 THEN
1432 UPDATE SET balance = t.balance + delta
1433 WHEN NOT MATCHED THEN
1434 INSERT (balance, tid) VALUES (balance + delta, sid)
1435 WHEN MATCHED AND tid < 2 THEN
1437 RETURNING (SELECT abbrev FROM merge_actions
1438 WHERE action = merge_action()) AS action,
1441 WHEN 'INSERT' THEN 'Inserted '||t
1442 WHEN 'UPDATE' THEN 'Added '||delta||' to balance'
1443 WHEN 'DELETE' THEN 'Removed '||t
1445 action | tid | balance | description
1446 --------+-----+---------+---------------------
1447 del | 1 | 100 | Removed (1,100)
1448 upd | 2 | 220 | Added 20 to balance
1449 ins | 4 | 40 | Inserted (4,40)
1453 -- error when using merge_action() outside MERGE
1454 SELECT merge_action() FROM sq_target;
1455 ERROR: MERGE_ACTION() can only be used in the RETURNING list of a MERGE command
1456 LINE 1: SELECT merge_action() FROM sq_target;
1458 UPDATE sq_target SET balance = balance + 1 RETURNING merge_action();
1459 ERROR: MERGE_ACTION() can only be used in the RETURNING list of a MERGE command
1460 LINE 1: ...ATE sq_target SET balance = balance + 1 RETURNING merge_acti...
1462 -- RETURNING in CTEs
1463 CREATE TABLE sq_target_merge_log (tid integer NOT NULL, last_change text);
1464 INSERT INTO sq_target_merge_log VALUES (1, 'Original value');
1467 MERGE INTO sq_target t
1470 WHEN MATCHED AND tid >= 2 THEN
1471 UPDATE SET balance = t.balance + delta
1472 WHEN NOT MATCHED THEN
1473 INSERT (balance, tid) VALUES (balance + delta, sid)
1474 WHEN MATCHED AND tid < 2 THEN
1476 RETURNING merge_action() AS action, t.*,
1478 WHEN 'INSERT' THEN 'Inserted '||t
1479 WHEN 'UPDATE' THEN 'Added '||delta||' to balance'
1480 WHEN 'DELETE' THEN 'Removed '||t
1483 MERGE INTO sq_target_merge_log l
1487 UPDATE SET last_change = description
1488 WHEN NOT MATCHED THEN
1489 INSERT VALUES (m.tid, description)
1490 RETURNING action, merge_action() AS log_action, l.*
1493 action | log_action | tid | last_change
1494 --------+------------+-----+---------------------
1495 DELETE | UPDATE | 1 | Removed (1,100)
1496 UPDATE | INSERT | 2 | Added 20 to balance
1497 INSERT | INSERT | 4 | Inserted (4,40)
1500 SELECT * FROM sq_target_merge_log ORDER BY tid;
1502 -----+---------------------
1504 2 | Added 20 to balance
1509 -- COPY (MERGE ... RETURNING) TO ...
1512 MERGE INTO sq_target t
1515 WHEN MATCHED AND tid >= 2 THEN
1516 UPDATE SET balance = t.balance + delta
1517 WHEN NOT MATCHED THEN
1518 INSERT (balance, tid) VALUES (balance + delta, sid)
1519 WHEN MATCHED AND tid < 2 THEN
1521 RETURNING merge_action(), t.*
1527 -- SQL function with MERGE ... RETURNING
1529 CREATE FUNCTION merge_into_sq_target(sid int, balance int, delta int,
1530 OUT action text, OUT tid int, OUT new_balance int)
1533 MERGE INTO sq_target t
1534 USING (VALUES ($1, $2, $3)) AS v(sid, balance, delta)
1536 WHEN MATCHED AND tid >= 2 THEN
1537 UPDATE SET balance = t.balance + v.delta
1538 WHEN NOT MATCHED THEN
1539 INSERT (balance, tid) VALUES (v.balance + v.delta, v.sid)
1540 WHEN MATCHED AND tid < 2 THEN
1542 RETURNING merge_action(), t.*;
1545 FROM (VALUES (1, 0, 0), (3, 0, 20), (4, 100, 10)) AS v(sid, balance, delta),
1546 LATERAL (SELECT action, tid, new_balance FROM merge_into_sq_target(sid, balance, delta)) m;
1547 action | tid | new_balance
1548 --------+-----+-------------
1555 -- SQL SRF with MERGE ... RETURNING
1557 CREATE FUNCTION merge_sq_source_into_sq_target()
1558 RETURNS TABLE (action text, tid int, balance int)
1561 MERGE INTO sq_target t
1564 WHEN MATCHED AND tid >= 2 THEN
1565 UPDATE SET balance = t.balance + delta
1566 WHEN NOT MATCHED THEN
1567 INSERT (balance, tid) VALUES (balance + delta, sid)
1568 WHEN MATCHED AND tid < 2 THEN
1570 RETURNING merge_action(), t.*;
1572 SELECT * FROM merge_sq_source_into_sq_target();
1573 action | tid | balance
1574 --------+-----+---------
1581 -- PL/pgSQL function with MERGE ... RETURNING ... INTO
1583 CREATE FUNCTION merge_into_sq_target(sid int, balance int, delta int,
1584 OUT r_action text, OUT r_tid int, OUT r_balance int)
1588 MERGE INTO sq_target t
1589 USING (VALUES ($1, $2, $3)) AS v(sid, balance, delta)
1591 WHEN MATCHED AND tid >= 2 THEN
1592 UPDATE SET balance = t.balance + v.delta
1593 WHEN NOT MATCHED THEN
1594 INSERT (balance, tid) VALUES (v.balance + v.delta, v.sid)
1595 WHEN MATCHED AND tid < 2 THEN
1597 RETURNING merge_action(), t.* INTO r_action, r_tid, r_balance;
1601 FROM (VALUES (1, 0, 0), (3, 0, 20), (4, 100, 10)) AS v(sid, balance, delta),
1602 LATERAL (SELECT r_action, r_tid, r_balance FROM merge_into_sq_target(sid, balance, delta)) m;
1603 r_action | r_tid | r_balance
1604 ----------+-------+-----------
1612 CREATE TABLE ex_mtarget (a int, b int)
1613 WITH (autovacuum_enabled=off);
1614 CREATE TABLE ex_msource (a int, b int)
1615 WITH (autovacuum_enabled=off);
1616 INSERT INTO ex_mtarget SELECT i, i*10 FROM generate_series(1,100,2) i;
1617 INSERT INTO ex_msource SELECT i, i*10 FROM generate_series(1,100,1) i;
1618 CREATE FUNCTION explain_merge(query text) RETURNS SETOF text
1624 EXECUTE 'explain (analyze, timing off, summary off, costs off, buffers off) ' ||
1627 ln := regexp_replace(ln, '(Memory( Usage)?|Buckets|Batches): \S*', '\1: xxx', 'g');
1633 SELECT explain_merge('
1634 MERGE INTO ex_mtarget t USING ex_msource s ON t.a = s.a
1636 UPDATE SET b = t.b + 1');
1638 ----------------------------------------------------------------------
1639 Merge on ex_mtarget t (actual rows=0 loops=1)
1641 -> Merge Join (actual rows=50 loops=1)
1642 Merge Cond: (t.a = s.a)
1643 -> Sort (actual rows=50 loops=1)
1645 Sort Method: quicksort Memory: xxx
1646 -> Seq Scan on ex_mtarget t (actual rows=50 loops=1)
1647 -> Sort (actual rows=100 loops=1)
1649 Sort Method: quicksort Memory: xxx
1650 -> Seq Scan on ex_msource s (actual rows=100 loops=1)
1653 -- only updates to selected tuples
1654 SELECT explain_merge('
1655 MERGE INTO ex_mtarget t USING ex_msource s ON t.a = s.a
1656 WHEN MATCHED AND t.a < 10 THEN
1657 UPDATE SET b = t.b + 1');
1659 ----------------------------------------------------------------------
1660 Merge on ex_mtarget t (actual rows=0 loops=1)
1661 Tuples: updated=5 skipped=45
1662 -> Merge Join (actual rows=50 loops=1)
1663 Merge Cond: (t.a = s.a)
1664 -> Sort (actual rows=50 loops=1)
1666 Sort Method: quicksort Memory: xxx
1667 -> Seq Scan on ex_mtarget t (actual rows=50 loops=1)
1668 -> Sort (actual rows=100 loops=1)
1670 Sort Method: quicksort Memory: xxx
1671 -> Seq Scan on ex_msource s (actual rows=100 loops=1)
1674 -- updates + deletes
1675 SELECT explain_merge('
1676 MERGE INTO ex_mtarget t USING ex_msource s ON t.a = s.a
1677 WHEN MATCHED AND t.a < 10 THEN
1678 UPDATE SET b = t.b + 1
1679 WHEN MATCHED AND t.a >= 10 AND t.a <= 20 THEN
1682 ----------------------------------------------------------------------
1683 Merge on ex_mtarget t (actual rows=0 loops=1)
1684 Tuples: updated=5 deleted=5 skipped=40
1685 -> Merge Join (actual rows=50 loops=1)
1686 Merge Cond: (t.a = s.a)
1687 -> Sort (actual rows=50 loops=1)
1689 Sort Method: quicksort Memory: xxx
1690 -> Seq Scan on ex_mtarget t (actual rows=50 loops=1)
1691 -> Sort (actual rows=100 loops=1)
1693 Sort Method: quicksort Memory: xxx
1694 -> Seq Scan on ex_msource s (actual rows=100 loops=1)
1698 SELECT explain_merge('
1699 MERGE INTO ex_mtarget t USING ex_msource s ON t.a = s.a
1700 WHEN NOT MATCHED AND s.a < 10 THEN
1701 INSERT VALUES (a, b)');
1703 ----------------------------------------------------------------------
1704 Merge on ex_mtarget t (actual rows=0 loops=1)
1705 Tuples: inserted=4 skipped=96
1706 -> Merge Left Join (actual rows=100 loops=1)
1707 Merge Cond: (s.a = t.a)
1708 -> Sort (actual rows=100 loops=1)
1710 Sort Method: quicksort Memory: xxx
1711 -> Seq Scan on ex_msource s (actual rows=100 loops=1)
1712 -> Sort (actual rows=45 loops=1)
1714 Sort Method: quicksort Memory: xxx
1715 -> Seq Scan on ex_mtarget t (actual rows=45 loops=1)
1719 SELECT explain_merge('
1720 MERGE INTO ex_mtarget t USING ex_msource s ON t.a = s.a
1721 WHEN MATCHED AND t.a < 10 THEN
1722 UPDATE SET b = t.b + 1
1723 WHEN MATCHED AND t.a >= 30 AND t.a <= 40 THEN
1725 WHEN NOT MATCHED AND s.a < 20 THEN
1726 INSERT VALUES (a, b)');
1728 ----------------------------------------------------------------------
1729 Merge on ex_mtarget t (actual rows=0 loops=1)
1730 Tuples: inserted=10 updated=9 deleted=5 skipped=76
1731 -> Merge Left Join (actual rows=100 loops=1)
1732 Merge Cond: (s.a = t.a)
1733 -> Sort (actual rows=100 loops=1)
1735 Sort Method: quicksort Memory: xxx
1736 -> Seq Scan on ex_msource s (actual rows=100 loops=1)
1737 -> Sort (actual rows=49 loops=1)
1739 Sort Method: quicksort Memory: xxx
1740 -> Seq Scan on ex_mtarget t (actual rows=49 loops=1)
1743 -- not matched by source
1744 SELECT explain_merge('
1745 MERGE INTO ex_mtarget t USING ex_msource s ON t.a = s.a
1746 WHEN NOT MATCHED BY SOURCE and t.a < 10 THEN
1749 ----------------------------------------------------------------------
1750 Merge on ex_mtarget t (actual rows=0 loops=1)
1752 -> Merge Left Join (actual rows=54 loops=1)
1753 Merge Cond: (t.a = s.a)
1754 -> Sort (actual rows=54 loops=1)
1756 Sort Method: quicksort Memory: xxx
1757 -> Seq Scan on ex_mtarget t (actual rows=54 loops=1)
1758 -> Sort (actual rows=100 loops=1)
1760 Sort Method: quicksort Memory: xxx
1761 -> Seq Scan on ex_msource s (actual rows=100 loops=1)
1764 -- not matched by source and target
1765 SELECT explain_merge('
1766 MERGE INTO ex_mtarget t USING ex_msource s ON t.a = s.a
1767 WHEN NOT MATCHED BY SOURCE AND t.a < 10 THEN
1769 WHEN NOT MATCHED BY TARGET AND s.a < 20 THEN
1770 INSERT VALUES (a, b)');
1772 ----------------------------------------------------------------------
1773 Merge on ex_mtarget t (actual rows=0 loops=1)
1775 -> Merge Full Join (actual rows=100 loops=1)
1776 Merge Cond: (t.a = s.a)
1777 -> Sort (actual rows=54 loops=1)
1779 Sort Method: quicksort Memory: xxx
1780 -> Seq Scan on ex_mtarget t (actual rows=54 loops=1)
1781 -> Sort (actual rows=100 loops=1)
1783 Sort Method: quicksort Memory: xxx
1784 -> Seq Scan on ex_msource s (actual rows=100 loops=1)
1788 SELECT explain_merge('
1789 MERGE INTO ex_mtarget t USING ex_msource s ON t.a = s.a AND t.a < -1000
1790 WHEN MATCHED AND t.a < 10 THEN
1793 --------------------------------------------------------------------
1794 Merge on ex_mtarget t (actual rows=0 loops=1)
1795 -> Merge Join (actual rows=0 loops=1)
1796 Merge Cond: (t.a = s.a)
1797 -> Sort (actual rows=0 loops=1)
1799 Sort Method: quicksort Memory: xxx
1800 -> Seq Scan on ex_mtarget t (actual rows=0 loops=1)
1801 Filter: (a < '-1000'::integer)
1802 Rows Removed by Filter: 54
1803 -> Sort (never executed)
1805 -> Seq Scan on ex_msource s (never executed)
1808 DROP TABLE ex_msource, ex_mtarget;
1809 DROP FUNCTION explain_merge(text);
1810 -- EXPLAIN SubPlans and InitPlans
1811 CREATE TABLE src (a int, b int, c int, d int);
1812 CREATE TABLE tgt (a int, b int, c int, d int);
1813 CREATE TABLE ref (ab int, cd int);
1814 EXPLAIN (verbose, costs off)
1816 USING (SELECT *, (SELECT count(*) FROM ref r
1817 WHERE r.ab = s.a + s.b
1818 AND r.cd = s.c - s.d) cnt
1820 ON t.a = s.a AND t.b < s.cnt
1821 WHEN MATCHED AND t.c > s.cnt THEN
1822 UPDATE SET (b, c) = (SELECT s.b, s.cnt);
1824 -------------------------------------------------------------------------------------
1825 Merge on public.tgt t
1827 Output: t.ctid, s.a, s.b, s.c, s.d, s.ctid
1828 Hash Cond: (t.a = s.a)
1829 Join Filter: (t.b < (SubPlan 1))
1830 -> Seq Scan on public.tgt t
1831 Output: t.ctid, t.a, t.b
1833 Output: s.a, s.b, s.c, s.d, s.ctid
1834 -> Seq Scan on public.src s
1835 Output: s.a, s.b, s.c, s.d, s.ctid
1839 -> Seq Scan on public.ref r
1841 Filter: ((r.ab = (s.a + s.b)) AND (r.cd = (s.c - s.d)))
1845 -> Seq Scan on public.ref r_2
1846 Output: r_2.ab, r_2.cd
1847 Filter: ((r_2.ab = (s.a + s.b)) AND (r_2.cd = (s.c - s.d)))
1850 Output: s.b, (InitPlan 2).col1
1854 -> Seq Scan on public.ref r_1
1855 Output: r_1.ab, r_1.cd
1856 Filter: ((r_1.ab = (s.a + s.b)) AND (r_1.cd = (s.c - s.d)))
1859 DROP TABLE src, tgt, ref;
1862 MERGE INTO sq_target t
1866 UPDATE SET balance = (SELECT count(*) FROM sq_target);
1867 SELECT * FROM sq_target WHERE tid = 1;
1875 MERGE INTO sq_target t
1878 WHEN MATCHED AND (SELECT count(*) > 0 FROM sq_target) THEN
1879 UPDATE SET balance = 42;
1880 SELECT * FROM sq_target WHERE tid = 1;
1888 MERGE INTO sq_target t
1890 ON tid = sid AND (SELECT count(*) > 0 FROM sq_target)
1892 UPDATE SET balance = 42;
1893 SELECT * FROM sq_target WHERE tid = 1;
1900 DROP TABLE sq_target, sq_target_merge_log, sq_source CASCADE;
1901 NOTICE: drop cascades to view v
1902 CREATE TABLE pa_target (tid integer, balance float, val text)
1903 PARTITION BY LIST (tid);
1904 CREATE TABLE part1 PARTITION OF pa_target FOR VALUES IN (1,4)
1905 WITH (autovacuum_enabled=off);
1906 CREATE TABLE part2 PARTITION OF pa_target FOR VALUES IN (2,5,6)
1907 WITH (autovacuum_enabled=off);
1908 CREATE TABLE part3 PARTITION OF pa_target FOR VALUES IN (3,8,9)
1909 WITH (autovacuum_enabled=off);
1910 CREATE TABLE part4 PARTITION OF pa_target DEFAULT
1911 WITH (autovacuum_enabled=off);
1912 CREATE TABLE pa_source (sid integer, delta float);
1913 -- insert many rows to the source table
1914 INSERT INTO pa_source SELECT id, id * 10 FROM generate_series(1,14) AS id;
1915 -- insert a few rows in the target table (odd numbered tid)
1916 INSERT INTO pa_target SELECT id, id * 100, 'initial' FROM generate_series(1,15,2) AS id;
1919 MERGE INTO pa_target t
1923 UPDATE SET balance = balance + delta, val = val || ' updated by merge'
1924 WHEN NOT MATCHED THEN
1925 INSERT VALUES (sid, delta, 'inserted by merge')
1926 WHEN NOT MATCHED BY SOURCE THEN
1927 UPDATE SET val = val || ' not matched by source';
1928 SELECT * FROM pa_target ORDER BY tid, val;
1930 -----+---------+-------------------------------
1931 1 | 110 | initial updated by merge
1932 2 | 20 | inserted by merge
1933 3 | 330 | initial updated by merge
1934 4 | 40 | inserted by merge
1935 5 | 550 | initial updated by merge
1936 6 | 60 | inserted by merge
1937 7 | 770 | initial updated by merge
1938 8 | 80 | inserted by merge
1939 9 | 990 | initial updated by merge
1940 10 | 100 | inserted by merge
1941 11 | 1210 | initial updated by merge
1942 12 | 120 | inserted by merge
1943 13 | 1430 | initial updated by merge
1944 14 | 140 | inserted by merge
1945 15 | 1500 | initial not matched by source
1949 -- same with a constant qual
1951 MERGE INTO pa_target t
1953 ON t.tid = s.sid AND tid = 1
1955 UPDATE SET balance = balance + delta, val = val || ' updated by merge'
1956 WHEN NOT MATCHED THEN
1957 INSERT VALUES (sid, delta, 'inserted by merge')
1958 WHEN NOT MATCHED BY SOURCE THEN
1959 UPDATE SET val = val || ' not matched by source';
1960 SELECT * FROM pa_target ORDER BY tid, val;
1962 -----+---------+-------------------------------
1963 1 | 110 | initial updated by merge
1964 2 | 20 | inserted by merge
1965 3 | 300 | initial not matched by source
1966 3 | 30 | inserted by merge
1967 4 | 40 | inserted by merge
1968 5 | 500 | initial not matched by source
1969 5 | 50 | inserted by merge
1970 6 | 60 | inserted by merge
1971 7 | 700 | initial not matched by source
1972 7 | 70 | inserted by merge
1973 8 | 80 | inserted by merge
1974 9 | 900 | initial not matched by source
1975 9 | 90 | inserted by merge
1976 10 | 100 | inserted by merge
1977 11 | 1100 | initial not matched by source
1978 11 | 110 | inserted by merge
1979 12 | 120 | inserted by merge
1980 13 | 1300 | initial not matched by source
1981 13 | 130 | inserted by merge
1982 14 | 140 | inserted by merge
1983 15 | 1500 | initial not matched by source
1987 -- try updating the partition key column
1989 CREATE FUNCTION merge_func() RETURNS integer LANGUAGE plpgsql AS $$
1993 MERGE INTO pa_target t
1997 UPDATE SET tid = tid + 1, balance = balance + delta, val = val || ' updated by merge'
1998 WHEN NOT MATCHED THEN
1999 INSERT VALUES (sid, delta, 'inserted by merge')
2000 WHEN NOT MATCHED BY SOURCE THEN
2001 UPDATE SET tid = 1, val = val || ' not matched by source';
2003 GET DIAGNOSTICS result := ROW_COUNT;
2008 SELECT merge_func();
2014 SELECT * FROM pa_target ORDER BY tid, val;
2016 -----+---------+-------------------------------
2017 1 | 1500 | initial not matched by source
2018 2 | 110 | initial updated by merge
2019 2 | 20 | inserted by merge
2020 4 | 330 | initial updated by merge
2021 4 | 40 | inserted by merge
2022 6 | 550 | initial updated by merge
2023 6 | 60 | inserted by merge
2024 8 | 770 | initial updated by merge
2025 8 | 80 | inserted by merge
2026 10 | 990 | initial updated by merge
2027 10 | 100 | inserted by merge
2028 12 | 1210 | initial updated by merge
2029 12 | 120 | inserted by merge
2030 14 | 1430 | initial updated by merge
2031 14 | 140 | inserted by merge
2035 -- update partition key to partition not initially scanned
2037 MERGE INTO pa_target t
2039 ON t.tid = s.sid AND t.tid = 1
2041 UPDATE SET tid = tid + 1, balance = balance + delta, val = val || ' updated by merge'
2042 RETURNING merge_action(), t.*;
2043 merge_action | tid | balance | val
2044 --------------+-----+---------+--------------------------
2045 UPDATE | 2 | 110 | initial updated by merge
2048 SELECT * FROM pa_target ORDER BY tid;
2050 -----+---------+--------------------------
2051 2 | 110 | initial updated by merge
2062 DROP TABLE pa_target CASCADE;
2063 -- The target table is partitioned in the same way, but this time by attaching
2064 -- partitions which have columns in different order, dropped columns etc.
2065 CREATE TABLE pa_target (tid integer, balance float, val text)
2066 PARTITION BY LIST (tid);
2067 CREATE TABLE part1 (tid integer, balance float, val text)
2068 WITH (autovacuum_enabled=off);
2069 CREATE TABLE part2 (balance float, tid integer, val text)
2070 WITH (autovacuum_enabled=off);
2071 CREATE TABLE part3 (tid integer, balance float, val text)
2072 WITH (autovacuum_enabled=off);
2073 CREATE TABLE part4 (extraid text, tid integer, balance float, val text)
2074 WITH (autovacuum_enabled=off);
2075 ALTER TABLE part4 DROP COLUMN extraid;
2076 ALTER TABLE pa_target ATTACH PARTITION part1 FOR VALUES IN (1,4);
2077 ALTER TABLE pa_target ATTACH PARTITION part2 FOR VALUES IN (2,5,6);
2078 ALTER TABLE pa_target ATTACH PARTITION part3 FOR VALUES IN (3,8,9);
2079 ALTER TABLE pa_target ATTACH PARTITION part4 DEFAULT;
2080 -- insert a few rows in the target table (odd numbered tid)
2081 INSERT INTO pa_target SELECT id, id * 100, 'initial' FROM generate_series(1,15,2) AS id;
2088 MERGE INTO pa_target t
2092 UPDATE SET balance = balance + delta, val = val || ' updated by merge'
2093 WHEN NOT MATCHED THEN
2094 INSERT VALUES (sid, delta, 'inserted by merge')
2095 WHEN NOT MATCHED BY SOURCE THEN
2096 UPDATE SET val = val || ' not matched by source';
2097 GET DIAGNOSTICS result := ROW_COUNT;
2098 RAISE NOTICE 'ROW_COUNT = %', result;
2101 NOTICE: ROW_COUNT = 15
2102 SELECT * FROM pa_target ORDER BY tid, val;
2104 -----+---------+-------------------------------
2105 1 | 110 | initial updated by merge
2106 2 | 20 | inserted by merge
2107 3 | 330 | initial updated by merge
2108 4 | 40 | inserted by merge
2109 5 | 550 | initial updated by merge
2110 6 | 60 | inserted by merge
2111 7 | 770 | initial updated by merge
2112 8 | 80 | inserted by merge
2113 9 | 990 | initial updated by merge
2114 10 | 100 | inserted by merge
2115 11 | 1210 | initial updated by merge
2116 12 | 120 | inserted by merge
2117 13 | 1430 | initial updated by merge
2118 14 | 140 | inserted by merge
2119 15 | 1500 | initial not matched by source
2123 -- same with a constant qual
2125 MERGE INTO pa_target t
2127 ON t.tid = s.sid AND tid IN (1, 5)
2128 WHEN MATCHED AND tid % 5 = 0 THEN DELETE
2130 UPDATE SET balance = balance + delta, val = val || ' updated by merge'
2131 WHEN NOT MATCHED THEN
2132 INSERT VALUES (sid, delta, 'inserted by merge')
2133 WHEN NOT MATCHED BY SOURCE THEN
2134 UPDATE SET val = val || ' not matched by source';
2135 SELECT * FROM pa_target ORDER BY tid, val;
2137 -----+---------+-------------------------------
2138 1 | 110 | initial updated by merge
2139 2 | 20 | inserted by merge
2140 3 | 300 | initial not matched by source
2141 3 | 30 | inserted by merge
2142 4 | 40 | inserted by merge
2143 6 | 60 | inserted by merge
2144 7 | 700 | initial not matched by source
2145 7 | 70 | inserted by merge
2146 8 | 80 | inserted by merge
2147 9 | 900 | initial not matched by source
2148 9 | 90 | inserted by merge
2149 10 | 100 | inserted by merge
2150 11 | 1100 | initial not matched by source
2151 11 | 110 | inserted by merge
2152 12 | 120 | inserted by merge
2153 13 | 1300 | initial not matched by source
2154 13 | 130 | inserted by merge
2155 14 | 140 | inserted by merge
2156 15 | 1500 | initial not matched by source
2160 -- try updating the partition key column
2166 MERGE INTO pa_target t
2170 UPDATE SET tid = tid + 1, balance = balance + delta, val = val || ' updated by merge'
2171 WHEN NOT MATCHED THEN
2172 INSERT VALUES (sid, delta, 'inserted by merge')
2173 WHEN NOT MATCHED BY SOURCE THEN
2174 UPDATE SET tid = 1, val = val || ' not matched by source';
2175 GET DIAGNOSTICS result := ROW_COUNT;
2176 RAISE NOTICE 'ROW_COUNT = %', result;
2179 NOTICE: ROW_COUNT = 15
2180 SELECT * FROM pa_target ORDER BY tid, val;
2182 -----+---------+-------------------------------
2183 1 | 1500 | initial not matched by source
2184 2 | 110 | initial updated by merge
2185 2 | 20 | inserted by merge
2186 4 | 330 | initial updated by merge
2187 4 | 40 | inserted by merge
2188 6 | 550 | initial updated by merge
2189 6 | 60 | inserted by merge
2190 8 | 770 | initial updated by merge
2191 8 | 80 | inserted by merge
2192 10 | 990 | initial updated by merge
2193 10 | 100 | inserted by merge
2194 12 | 1210 | initial updated by merge
2195 12 | 120 | inserted by merge
2196 14 | 1430 | initial updated by merge
2197 14 | 140 | inserted by merge
2201 -- as above, but blocked by BEFORE DELETE ROW trigger
2203 CREATE FUNCTION trig_fn() RETURNS trigger LANGUAGE plpgsql AS
2204 $$ BEGIN RETURN NULL; END; $$;
2205 CREATE TRIGGER del_trig BEFORE DELETE ON pa_target
2206 FOR EACH ROW EXECUTE PROCEDURE trig_fn();
2211 MERGE INTO pa_target t
2215 UPDATE SET tid = tid + 1, balance = balance + delta, val = val || ' updated by merge'
2216 WHEN NOT MATCHED THEN
2217 INSERT VALUES (sid, delta, 'inserted by merge')
2218 WHEN NOT MATCHED BY SOURCE THEN
2219 UPDATE SET val = val || ' not matched by source';
2220 GET DIAGNOSTICS result := ROW_COUNT;
2221 RAISE NOTICE 'ROW_COUNT = %', result;
2224 NOTICE: ROW_COUNT = 11
2225 SELECT * FROM pa_target ORDER BY tid, val;
2227 -----+---------+-------------------------------
2229 2 | 20 | inserted by merge
2231 4 | 40 | inserted by merge
2232 6 | 550 | initial updated by merge
2233 6 | 60 | inserted by merge
2235 8 | 80 | inserted by merge
2237 10 | 100 | inserted by merge
2238 12 | 1210 | initial updated by merge
2239 12 | 120 | inserted by merge
2240 14 | 1430 | initial updated by merge
2241 14 | 140 | inserted by merge
2242 15 | 1500 | initial not matched by source
2246 -- as above, but blocked by BEFORE INSERT ROW trigger
2248 CREATE FUNCTION trig_fn() RETURNS trigger LANGUAGE plpgsql AS
2249 $$ BEGIN RETURN NULL; END; $$;
2250 CREATE TRIGGER ins_trig BEFORE INSERT ON pa_target
2251 FOR EACH ROW EXECUTE PROCEDURE trig_fn();
2256 MERGE INTO pa_target t
2260 UPDATE SET tid = tid + 1, balance = balance + delta, val = val || ' updated by merge'
2261 WHEN NOT MATCHED THEN
2262 INSERT VALUES (sid, delta, 'inserted by merge')
2263 WHEN NOT MATCHED BY SOURCE THEN
2264 UPDATE SET val = val || ' not matched by source';
2265 GET DIAGNOSTICS result := ROW_COUNT;
2266 RAISE NOTICE 'ROW_COUNT = %', result;
2269 NOTICE: ROW_COUNT = 4
2270 SELECT * FROM pa_target ORDER BY tid, val;
2272 -----+---------+-------------------------------
2273 6 | 550 | initial updated by merge
2274 12 | 1210 | initial updated by merge
2275 14 | 1430 | initial updated by merge
2276 15 | 1500 | initial not matched by source
2280 -- test RLS enforcement
2282 ALTER TABLE pa_target ENABLE ROW LEVEL SECURITY;
2283 ALTER TABLE pa_target FORCE ROW LEVEL SECURITY;
2284 CREATE POLICY pa_target_pol ON pa_target USING (tid != 0);
2285 MERGE INTO pa_target t
2287 ON t.tid = s.sid AND t.tid IN (1,2,3,4)
2289 UPDATE SET tid = tid - 1;
2290 ERROR: new row violates row-level security policy for table "pa_target"
2292 DROP TABLE pa_source;
2293 DROP TABLE pa_target CASCADE;
2295 CREATE TABLE pa_target (logts timestamp, tid integer, balance float, val text)
2296 PARTITION BY RANGE (logts);
2297 CREATE TABLE part_m01 PARTITION OF pa_target
2298 FOR VALUES FROM ('2017-01-01') TO ('2017-02-01')
2299 PARTITION BY LIST (tid);
2300 CREATE TABLE part_m01_odd PARTITION OF part_m01
2301 FOR VALUES IN (1,3,5,7,9) WITH (autovacuum_enabled=off);
2302 CREATE TABLE part_m01_even PARTITION OF part_m01
2303 FOR VALUES IN (2,4,6,8) WITH (autovacuum_enabled=off);
2304 CREATE TABLE part_m02 PARTITION OF pa_target
2305 FOR VALUES FROM ('2017-02-01') TO ('2017-03-01')
2306 PARTITION BY LIST (tid);
2307 CREATE TABLE part_m02_odd PARTITION OF part_m02
2308 FOR VALUES IN (1,3,5,7,9) WITH (autovacuum_enabled=off);
2309 CREATE TABLE part_m02_even PARTITION OF part_m02
2310 FOR VALUES IN (2,4,6,8) WITH (autovacuum_enabled=off);
2311 CREATE TABLE pa_source (sid integer, delta float)
2312 WITH (autovacuum_enabled=off);
2313 -- insert many rows to the source table
2314 INSERT INTO pa_source SELECT id, id * 10 FROM generate_series(1,14) AS id;
2315 -- insert a few rows in the target table (odd numbered tid)
2316 INSERT INTO pa_target SELECT '2017-01-31', id, id * 100, 'initial' FROM generate_series(1,9,3) AS id;
2317 INSERT INTO pa_target SELECT '2017-02-28', id, id * 100, 'initial' FROM generate_series(2,9,3) AS id;
2320 MERGE INTO pa_target t
2321 USING (SELECT '2017-01-15' AS slogts, * FROM pa_source WHERE sid < 10) s
2324 UPDATE SET balance = balance + delta, val = val || ' updated by merge'
2325 WHEN NOT MATCHED THEN
2326 INSERT VALUES (slogts::timestamp, sid, delta, 'inserted by merge')
2327 RETURNING merge_action(), t.*;
2328 merge_action | logts | tid | balance | val
2329 --------------+--------------------------+-----+---------+--------------------------
2330 UPDATE | Tue Jan 31 00:00:00 2017 | 1 | 110 | initial updated by merge
2331 UPDATE | Tue Feb 28 00:00:00 2017 | 2 | 220 | initial updated by merge
2332 INSERT | Sun Jan 15 00:00:00 2017 | 3 | 30 | inserted by merge
2333 UPDATE | Tue Jan 31 00:00:00 2017 | 4 | 440 | initial updated by merge
2334 UPDATE | Tue Feb 28 00:00:00 2017 | 5 | 550 | initial updated by merge
2335 INSERT | Sun Jan 15 00:00:00 2017 | 6 | 60 | inserted by merge
2336 UPDATE | Tue Jan 31 00:00:00 2017 | 7 | 770 | initial updated by merge
2337 UPDATE | Tue Feb 28 00:00:00 2017 | 8 | 880 | initial updated by merge
2338 INSERT | Sun Jan 15 00:00:00 2017 | 9 | 90 | inserted by merge
2341 SELECT * FROM pa_target ORDER BY tid;
2342 logts | tid | balance | val
2343 --------------------------+-----+---------+--------------------------
2344 Tue Jan 31 00:00:00 2017 | 1 | 110 | initial updated by merge
2345 Tue Feb 28 00:00:00 2017 | 2 | 220 | initial updated by merge
2346 Sun Jan 15 00:00:00 2017 | 3 | 30 | inserted by merge
2347 Tue Jan 31 00:00:00 2017 | 4 | 440 | initial updated by merge
2348 Tue Feb 28 00:00:00 2017 | 5 | 550 | initial updated by merge
2349 Sun Jan 15 00:00:00 2017 | 6 | 60 | inserted by merge
2350 Tue Jan 31 00:00:00 2017 | 7 | 770 | initial updated by merge
2351 Tue Feb 28 00:00:00 2017 | 8 | 880 | initial updated by merge
2352 Sun Jan 15 00:00:00 2017 | 9 | 90 | inserted by merge
2356 DROP TABLE pa_source;
2357 DROP TABLE pa_target CASCADE;
2358 -- Partitioned table with primary key
2359 CREATE TABLE pa_target (tid integer PRIMARY KEY) PARTITION BY LIST (tid);
2360 CREATE TABLE pa_targetp PARTITION OF pa_target DEFAULT;
2361 CREATE TABLE pa_source (sid integer);
2362 INSERT INTO pa_source VALUES (1), (2);
2363 EXPLAIN (VERBOSE, COSTS OFF)
2364 MERGE INTO pa_target t USING pa_source s ON t.tid = s.sid
2365 WHEN NOT MATCHED THEN INSERT VALUES (s.sid);
2367 -------------------------------------------------------------
2368 Merge on public.pa_target t
2369 Merge on public.pa_targetp t_1
2371 Output: s.sid, s.ctid, t_1.tableoid, t_1.ctid
2373 Hash Cond: (s.sid = t_1.tid)
2374 -> Seq Scan on public.pa_source s
2375 Output: s.sid, s.ctid
2377 Output: t_1.tid, t_1.tableoid, t_1.ctid
2378 -> Seq Scan on public.pa_targetp t_1
2379 Output: t_1.tid, t_1.tableoid, t_1.ctid
2382 MERGE INTO pa_target t USING pa_source s ON t.tid = s.sid
2383 WHEN NOT MATCHED THEN INSERT VALUES (s.sid);
2391 -- Partition-less partitioned table
2392 -- (the bug we are checking for appeared only if table had partitions before)
2393 DROP TABLE pa_targetp;
2394 EXPLAIN (VERBOSE, COSTS OFF)
2395 MERGE INTO pa_target t USING pa_source s ON t.tid = s.sid
2396 WHEN NOT MATCHED THEN INSERT VALUES (s.sid);
2398 --------------------------------------------
2399 Merge on public.pa_target t
2401 Output: s.sid, s.ctid, t.ctid
2403 Hash Cond: (s.sid = t.tid)
2404 -> Seq Scan on public.pa_source s
2405 Output: s.sid, s.ctid
2407 Output: t.tid, t.ctid
2409 Output: t.tid, t.ctid
2410 One-Time Filter: false
2413 MERGE INTO pa_target t USING pa_source s ON t.tid = s.sid
2414 WHEN NOT MATCHED THEN INSERT VALUES (s.sid);
2415 ERROR: no partition of relation "pa_target" found for row
2416 DETAIL: Partition key of the failing row contains (tid) = (1).
2417 DROP TABLE pa_source;
2418 DROP TABLE pa_target CASCADE;
2419 -- some complex joins on the source side
2420 CREATE TABLE cj_target (tid integer, balance float, val text)
2421 WITH (autovacuum_enabled=off);
2422 CREATE TABLE cj_source1 (sid1 integer, scat integer, delta integer)
2423 WITH (autovacuum_enabled=off);
2424 CREATE TABLE cj_source2 (sid2 integer, sval text)
2425 WITH (autovacuum_enabled=off);
2426 INSERT INTO cj_source1 VALUES (1, 10, 100);
2427 INSERT INTO cj_source1 VALUES (1, 20, 200);
2428 INSERT INTO cj_source1 VALUES (2, 20, 300);
2429 INSERT INTO cj_source1 VALUES (3, 10, 400);
2430 INSERT INTO cj_source2 VALUES (1, 'initial source2');
2431 INSERT INTO cj_source2 VALUES (2, 'initial source2');
2432 INSERT INTO cj_source2 VALUES (3, 'initial source2');
2433 -- source relation is an unaliased join
2434 MERGE INTO cj_target t
2436 INNER JOIN cj_source2 s2 ON sid1 = sid2
2438 WHEN NOT MATCHED THEN
2439 INSERT VALUES (sid1, delta, sval);
2440 -- try accessing columns from either side of the source join
2441 MERGE INTO cj_target t
2443 INNER JOIN cj_source1 s1 ON sid1 = sid2 AND scat = 20
2445 WHEN NOT MATCHED THEN
2446 INSERT VALUES (sid2, delta, sval)
2449 -- some simple expressions in INSERT targetlist
2450 MERGE INTO cj_target t
2452 INNER JOIN cj_source1 s1 ON sid1 = sid2
2454 WHEN NOT MATCHED THEN
2455 INSERT VALUES (sid2, delta + scat, sval)
2457 UPDATE SET val = val || ' updated by merge';
2458 MERGE INTO cj_target t
2460 INNER JOIN cj_source1 s1 ON sid1 = sid2 AND scat = 20
2463 UPDATE SET val = val || ' ' || delta::text;
2464 SELECT * FROM cj_target;
2466 -----+---------+----------------------------------
2467 3 | 400 | initial source2 updated by merge
2468 1 | 220 | initial source2 200
2469 1 | 110 | initial source2 200
2470 2 | 320 | initial source2 300
2473 -- try it with an outer join and PlaceHolderVar
2474 MERGE INTO cj_target t
2475 USING (SELECT *, 'join input'::text AS phv FROM cj_source1) fj
2476 FULL JOIN cj_source2 fj2 ON fj.scat = fj2.sid2 * 10
2478 WHEN NOT MATCHED THEN
2479 INSERT (tid, balance, val) VALUES (fj.scat, fj.delta, fj.phv);
2480 SELECT * FROM cj_target;
2482 -----+---------+----------------------------------
2483 3 | 400 | initial source2 updated by merge
2484 1 | 220 | initial source2 200
2485 1 | 110 | initial source2 200
2486 2 | 320 | initial source2 300
2487 10 | 100 | join input
2488 10 | 400 | join input
2489 20 | 200 | join input
2490 20 | 300 | join input
2494 ALTER TABLE cj_source1 RENAME COLUMN sid1 TO sid;
2495 ALTER TABLE cj_source2 RENAME COLUMN sid2 TO sid;
2497 MERGE INTO cj_target t
2499 INNER JOIN cj_source2 s2 ON s1.sid = s2.sid
2501 WHEN NOT MATCHED THEN
2502 INSERT VALUES (s2.sid, delta, sval);
2503 DROP TABLE cj_source2, cj_source1, cj_target;
2505 CREATE TABLE fs_target (a int, b int, c text)
2506 WITH (autovacuum_enabled=off);
2507 MERGE INTO fs_target t
2508 USING generate_series(1,100,1) AS id
2511 UPDATE SET b = b + id
2512 WHEN NOT MATCHED THEN
2513 INSERT VALUES (id, -1);
2514 MERGE INTO fs_target t
2515 USING generate_series(1,100,2) AS id
2518 UPDATE SET b = b + id, c = 'updated '|| id.*::text
2519 WHEN NOT MATCHED THEN
2520 INSERT VALUES (id, -1, 'inserted ' || id.*::text);
2521 SELECT count(*) FROM fs_target;
2527 DROP TABLE fs_target;
2528 -- SERIALIZABLE test
2529 -- handled in isolation tests
2530 -- Inheritance-based partitioning
2531 CREATE TABLE measurement (
2532 city_id int not null,
2533 logdate date not null,
2536 ) WITH (autovacuum_enabled=off);
2537 CREATE TABLE measurement_y2006m02 (
2538 CHECK ( logdate >= DATE '2006-02-01' AND logdate < DATE '2006-03-01' )
2539 ) INHERITS (measurement) WITH (autovacuum_enabled=off);
2540 CREATE TABLE measurement_y2006m03 (
2541 CHECK ( logdate >= DATE '2006-03-01' AND logdate < DATE '2006-04-01' )
2542 ) INHERITS (measurement) WITH (autovacuum_enabled=off);
2543 CREATE TABLE measurement_y2007m01 (
2546 logdate date not null,
2547 city_id int not null,
2549 CHECK ( logdate >= DATE '2007-01-01' AND logdate < DATE '2007-02-01')
2550 ) WITH (autovacuum_enabled=off);
2551 ALTER TABLE measurement_y2007m01 DROP COLUMN filler;
2552 ALTER TABLE measurement_y2007m01 INHERIT measurement;
2553 INSERT INTO measurement VALUES (0, '2005-07-21', 5, 15);
2554 CREATE OR REPLACE FUNCTION measurement_insert_trigger()
2555 RETURNS TRIGGER AS $$
2557 IF ( NEW.logdate >= DATE '2006-02-01' AND
2558 NEW.logdate < DATE '2006-03-01' ) THEN
2559 INSERT INTO measurement_y2006m02 VALUES (NEW.*);
2560 ELSIF ( NEW.logdate >= DATE '2006-03-01' AND
2561 NEW.logdate < DATE '2006-04-01' ) THEN
2562 INSERT INTO measurement_y2006m03 VALUES (NEW.*);
2563 ELSIF ( NEW.logdate >= DATE '2007-01-01' AND
2564 NEW.logdate < DATE '2007-02-01' ) THEN
2565 INSERT INTO measurement_y2007m01 (city_id, logdate, peaktemp, unitsales)
2568 RAISE EXCEPTION 'Date out of range. Fix the measurement_insert_trigger() function!';
2572 $$ LANGUAGE plpgsql ;
2573 CREATE TRIGGER insert_measurement_trigger
2574 BEFORE INSERT ON measurement
2575 FOR EACH ROW EXECUTE PROCEDURE measurement_insert_trigger();
2576 INSERT INTO measurement VALUES (1, '2006-02-10', 35, 10);
2577 INSERT INTO measurement VALUES (1, '2006-02-16', 45, 20);
2578 INSERT INTO measurement VALUES (1, '2006-03-17', 25, 10);
2579 INSERT INTO measurement VALUES (1, '2006-03-27', 15, 40);
2580 INSERT INTO measurement VALUES (1, '2007-01-15', 10, 10);
2581 INSERT INTO measurement VALUES (1, '2007-01-17', 10, 10);
2582 SELECT tableoid::regclass, * FROM measurement ORDER BY city_id, logdate;
2583 tableoid | city_id | logdate | peaktemp | unitsales
2584 ----------------------+---------+------------+----------+-----------
2585 measurement | 0 | 07-21-2005 | 5 | 15
2586 measurement_y2006m02 | 1 | 02-10-2006 | 35 | 10
2587 measurement_y2006m02 | 1 | 02-16-2006 | 45 | 20
2588 measurement_y2006m03 | 1 | 03-17-2006 | 25 | 10
2589 measurement_y2006m03 | 1 | 03-27-2006 | 15 | 40
2590 measurement_y2007m01 | 1 | 01-15-2007 | 10 | 10
2591 measurement_y2007m01 | 1 | 01-17-2007 | 10 | 10
2594 CREATE TABLE new_measurement (LIKE measurement) WITH (autovacuum_enabled=off);
2595 INSERT INTO new_measurement VALUES (0, '2005-07-21', 25, 20);
2596 INSERT INTO new_measurement VALUES (1, '2006-03-01', 20, 10);
2597 INSERT INTO new_measurement VALUES (1, '2006-02-16', 50, 10);
2598 INSERT INTO new_measurement VALUES (2, '2006-02-10', 20, 20);
2599 INSERT INTO new_measurement VALUES (1, '2006-03-27', NULL, NULL);
2600 INSERT INTO new_measurement VALUES (1, '2007-01-17', NULL, NULL);
2601 INSERT INTO new_measurement VALUES (1, '2007-01-15', 5, NULL);
2602 INSERT INTO new_measurement VALUES (1, '2007-01-16', 10, 10);
2604 MERGE INTO ONLY measurement m
2605 USING new_measurement nm ON
2606 (m.city_id = nm.city_id and m.logdate=nm.logdate)
2607 WHEN MATCHED AND nm.peaktemp IS NULL THEN DELETE
2608 WHEN MATCHED THEN UPDATE
2609 SET peaktemp = greatest(m.peaktemp, nm.peaktemp),
2610 unitsales = m.unitsales + coalesce(nm.unitsales, 0)
2611 WHEN NOT MATCHED THEN INSERT
2612 (city_id, logdate, peaktemp, unitsales)
2613 VALUES (city_id, logdate, peaktemp, unitsales);
2614 SELECT tableoid::regclass, * FROM measurement ORDER BY city_id, logdate, peaktemp;
2615 tableoid | city_id | logdate | peaktemp | unitsales
2616 ----------------------+---------+------------+----------+-----------
2617 measurement | 0 | 07-21-2005 | 25 | 35
2618 measurement_y2006m02 | 1 | 02-10-2006 | 35 | 10
2619 measurement_y2006m02 | 1 | 02-16-2006 | 45 | 20
2620 measurement_y2006m02 | 1 | 02-16-2006 | 50 | 10
2621 measurement_y2006m03 | 1 | 03-01-2006 | 20 | 10
2622 measurement_y2006m03 | 1 | 03-17-2006 | 25 | 10
2623 measurement_y2006m03 | 1 | 03-27-2006 | 15 | 40
2624 measurement_y2006m03 | 1 | 03-27-2006 | |
2625 measurement_y2007m01 | 1 | 01-15-2007 | 5 |
2626 measurement_y2007m01 | 1 | 01-15-2007 | 10 | 10
2627 measurement_y2007m01 | 1 | 01-16-2007 | 10 | 10
2628 measurement_y2007m01 | 1 | 01-17-2007 | 10 | 10
2629 measurement_y2007m01 | 1 | 01-17-2007 | |
2630 measurement_y2006m02 | 2 | 02-10-2006 | 20 | 20
2634 MERGE into measurement m
2635 USING new_measurement nm ON
2636 (m.city_id = nm.city_id and m.logdate=nm.logdate)
2637 WHEN MATCHED AND nm.peaktemp IS NULL THEN DELETE
2638 WHEN MATCHED THEN UPDATE
2639 SET peaktemp = greatest(m.peaktemp, nm.peaktemp),
2640 unitsales = m.unitsales + coalesce(nm.unitsales, 0)
2641 WHEN NOT MATCHED THEN INSERT
2642 (city_id, logdate, peaktemp, unitsales)
2643 VALUES (city_id, logdate, peaktemp, unitsales);
2644 SELECT tableoid::regclass, * FROM measurement ORDER BY city_id, logdate;
2645 tableoid | city_id | logdate | peaktemp | unitsales
2646 ----------------------+---------+------------+----------+-----------
2647 measurement | 0 | 07-21-2005 | 25 | 35
2648 measurement_y2006m02 | 1 | 02-10-2006 | 35 | 10
2649 measurement_y2006m02 | 1 | 02-16-2006 | 50 | 30
2650 measurement_y2006m03 | 1 | 03-01-2006 | 20 | 10
2651 measurement_y2006m03 | 1 | 03-17-2006 | 25 | 10
2652 measurement_y2007m01 | 1 | 01-15-2007 | 10 | 10
2653 measurement_y2007m01 | 1 | 01-16-2007 | 10 | 10
2654 measurement_y2006m02 | 2 | 02-10-2006 | 20 | 20
2658 MERGE INTO new_measurement nm
2659 USING ONLY measurement m ON
2660 (nm.city_id = m.city_id and nm.logdate=m.logdate)
2661 WHEN MATCHED THEN DELETE;
2662 SELECT * FROM new_measurement ORDER BY city_id, logdate;
2663 city_id | logdate | peaktemp | unitsales
2664 ---------+------------+----------+-----------
2665 1 | 02-16-2006 | 50 | 10
2666 1 | 03-01-2006 | 20 | 10
2668 1 | 01-15-2007 | 5 |
2669 1 | 01-16-2007 | 10 | 10
2671 2 | 02-10-2006 | 20 | 20
2675 MERGE INTO new_measurement nm
2676 USING measurement m ON
2677 (nm.city_id = m.city_id and nm.logdate=m.logdate)
2678 WHEN MATCHED THEN DELETE;
2679 SELECT * FROM new_measurement ORDER BY city_id, logdate;
2680 city_id | logdate | peaktemp | unitsales
2681 ---------+------------+----------+-----------
2686 DROP TABLE measurement, new_measurement CASCADE;
2687 NOTICE: drop cascades to 3 other objects
2688 DETAIL: drop cascades to table measurement_y2006m02
2689 drop cascades to table measurement_y2006m03
2690 drop cascades to table measurement_y2007m01
2691 DROP FUNCTION measurement_insert_trigger();
2693 -- test non-strict join clause
2695 CREATE TABLE src (a int, b text);
2696 INSERT INTO src VALUES (1, 'src row');
2697 CREATE TABLE tgt (a int, b text);
2698 INSERT INTO tgt VALUES (NULL, 'tgt row');
2699 MERGE INTO tgt USING src ON tgt.a IS NOT DISTINCT FROM src.a
2700 WHEN MATCHED THEN UPDATE SET a = src.a, b = src.b
2701 WHEN NOT MATCHED BY SOURCE THEN DELETE
2702 RETURNING merge_action(), src.*, tgt.*;
2703 merge_action | a | b | a | b
2704 --------------+---+---+---+---------
2705 DELETE | | | | tgt row
2713 DROP TABLE src, tgt;
2715 -- test for bug #18634 (wrong varnullingrels error)
2717 CREATE TABLE bug18634t (a int, b int, c text);
2718 INSERT INTO bug18634t VALUES(1, 10, 'tgt1'), (2, 20, 'tgt2');
2719 CREATE VIEW bug18634v AS
2720 SELECT * FROM bug18634t WHERE EXISTS (SELECT 1 FROM bug18634t);
2721 CREATE TABLE bug18634s (a int, b int, c text);
2722 INSERT INTO bug18634s VALUES (1, 2, 'src1');
2723 MERGE INTO bug18634v t USING bug18634s s ON s.a = t.a
2724 WHEN MATCHED THEN UPDATE SET b = s.b
2725 WHEN NOT MATCHED BY SOURCE THEN DELETE
2726 RETURNING merge_action(), s.c, t.*;
2727 merge_action | c | a | b | c
2728 --------------+------+---+----+------
2729 UPDATE | src1 | 1 | 2 | tgt1
2730 DELETE | | 2 | 20 | tgt2
2733 SELECT * FROM bug18634t;
2739 DROP TABLE bug18634t CASCADE;
2740 NOTICE: drop cascades to view bug18634v
2741 DROP TABLE bug18634s;
2743 RESET SESSION AUTHORIZATION;
2744 -- try a system catalog
2745 MERGE INTO pg_class c
2746 USING (SELECT 'pg_depend'::regclass AS oid) AS j
2749 UPDATE SET reltuples = reltuples + 1
2756 CREATE VIEW classv AS SELECT * FROM pg_class;
2758 USING pg_namespace n
2759 ON n.oid = c.relnamespace
2760 WHEN MATCHED AND c.oid = 'pg_depend'::regclass THEN
2761 UPDATE SET reltuples = reltuples - 1
2768 DROP TABLE target, target2;
2769 DROP TABLE source, source2;
2770 DROP FUNCTION merge_trigfunc();
2771 DROP USER regress_merge_privs;
2772 DROP USER regress_merge_no_privs;
2773 DROP USER regress_merge_none;