mirror of
https://github.com/postgres/postgres.git
synced 2025-07-18 17:42:25 +03:00
Revert MERGE patch
This reverts commitsd204ef6377
,83454e3c2b
and a few more commits thereafter (complete list at the end) related to MERGE feature. While the feature was fully functional, with sufficient test coverage and necessary documentation, it was felt that some parts of the executor and parse-analyzer can use a different design and it wasn't possible to do that in the available time. So it was decided to revert the patch for PG11 and retry again in the future. Thanks again to all reviewers and bug reporters. List of commits reverted, in reverse chronological order:f1464c5380
Improve parse representation for MERGEddb4158579
MERGE syntax diagram correction530e69e59b
Allow cpluspluscheck to pass by renaming variable01b88b4df5
MERGE minor errata3af7b2b0d4
MERGE fix variable warning in non-assert buildsa5d86181ec
MERGE INSERT allows only one VALUES clause4b2d44031f
MERGE post-commit review4923550c20
Tab completion for MERGEaa3faa3c7a
WITH support in MERGE83454e3c2b
New files for MERGEd204ef6377
MERGE SQL Command following SQL:2016 Author: Pavan Deolasee Reviewed-by: Michael Paquier
This commit is contained in:
@ -1,97 +0,0 @@
|
||||
Parsed test spec with 2 sessions
|
||||
|
||||
starting permutation: delete c1 select2 c2
|
||||
step delete: DELETE FROM target t WHERE t.key = 1;
|
||||
step c1: COMMIT;
|
||||
step select2: SELECT * FROM target;
|
||||
key val
|
||||
|
||||
step c2: COMMIT;
|
||||
|
||||
starting permutation: merge_delete c1 select2 c2
|
||||
step merge_delete: MERGE INTO target t USING (SELECT 1 as key) s ON s.key = t.key WHEN MATCHED THEN DELETE;
|
||||
step c1: COMMIT;
|
||||
step select2: SELECT * FROM target;
|
||||
key val
|
||||
|
||||
step c2: COMMIT;
|
||||
|
||||
starting permutation: delete c1 update1 select2 c2
|
||||
step delete: DELETE FROM target t WHERE t.key = 1;
|
||||
step c1: COMMIT;
|
||||
step update1: UPDATE target t SET val = t.val || ' updated by update1' WHERE t.key = 1;
|
||||
step select2: SELECT * FROM target;
|
||||
key val
|
||||
|
||||
step c2: COMMIT;
|
||||
|
||||
starting permutation: merge_delete c1 update1 select2 c2
|
||||
step merge_delete: MERGE INTO target t USING (SELECT 1 as key) s ON s.key = t.key WHEN MATCHED THEN DELETE;
|
||||
step c1: COMMIT;
|
||||
step update1: UPDATE target t SET val = t.val || ' updated by update1' WHERE t.key = 1;
|
||||
step select2: SELECT * FROM target;
|
||||
key val
|
||||
|
||||
step c2: COMMIT;
|
||||
|
||||
starting permutation: delete c1 merge2 select2 c2
|
||||
step delete: DELETE FROM target t WHERE t.key = 1;
|
||||
step c1: COMMIT;
|
||||
step merge2: MERGE INTO target t USING (SELECT 1 as key, 'merge2a' as val) s ON s.key = t.key WHEN NOT MATCHED THEN INSERT VALUES (s.key, s.val) WHEN MATCHED THEN UPDATE set key = t.key + 1, val = t.val || ' updated by ' || s.val;
|
||||
step select2: SELECT * FROM target;
|
||||
key val
|
||||
|
||||
1 merge2a
|
||||
step c2: COMMIT;
|
||||
|
||||
starting permutation: merge_delete c1 merge2 select2 c2
|
||||
step merge_delete: MERGE INTO target t USING (SELECT 1 as key) s ON s.key = t.key WHEN MATCHED THEN DELETE;
|
||||
step c1: COMMIT;
|
||||
step merge2: MERGE INTO target t USING (SELECT 1 as key, 'merge2a' as val) s ON s.key = t.key WHEN NOT MATCHED THEN INSERT VALUES (s.key, s.val) WHEN MATCHED THEN UPDATE set key = t.key + 1, val = t.val || ' updated by ' || s.val;
|
||||
step select2: SELECT * FROM target;
|
||||
key val
|
||||
|
||||
1 merge2a
|
||||
step c2: COMMIT;
|
||||
|
||||
starting permutation: delete update1 c1 select2 c2
|
||||
step delete: DELETE FROM target t WHERE t.key = 1;
|
||||
step update1: UPDATE target t SET val = t.val || ' updated by update1' WHERE t.key = 1; <waiting ...>
|
||||
step c1: COMMIT;
|
||||
step update1: <... completed>
|
||||
step select2: SELECT * FROM target;
|
||||
key val
|
||||
|
||||
step c2: COMMIT;
|
||||
|
||||
starting permutation: merge_delete update1 c1 select2 c2
|
||||
step merge_delete: MERGE INTO target t USING (SELECT 1 as key) s ON s.key = t.key WHEN MATCHED THEN DELETE;
|
||||
step update1: UPDATE target t SET val = t.val || ' updated by update1' WHERE t.key = 1; <waiting ...>
|
||||
step c1: COMMIT;
|
||||
step update1: <... completed>
|
||||
step select2: SELECT * FROM target;
|
||||
key val
|
||||
|
||||
step c2: COMMIT;
|
||||
|
||||
starting permutation: delete merge2 c1 select2 c2
|
||||
step delete: DELETE FROM target t WHERE t.key = 1;
|
||||
step merge2: MERGE INTO target t USING (SELECT 1 as key, 'merge2a' as val) s ON s.key = t.key WHEN NOT MATCHED THEN INSERT VALUES (s.key, s.val) WHEN MATCHED THEN UPDATE set key = t.key + 1, val = t.val || ' updated by ' || s.val; <waiting ...>
|
||||
step c1: COMMIT;
|
||||
step merge2: <... completed>
|
||||
step select2: SELECT * FROM target;
|
||||
key val
|
||||
|
||||
1 merge2a
|
||||
step c2: COMMIT;
|
||||
|
||||
starting permutation: merge_delete merge2 c1 select2 c2
|
||||
step merge_delete: MERGE INTO target t USING (SELECT 1 as key) s ON s.key = t.key WHEN MATCHED THEN DELETE;
|
||||
step merge2: MERGE INTO target t USING (SELECT 1 as key, 'merge2a' as val) s ON s.key = t.key WHEN NOT MATCHED THEN INSERT VALUES (s.key, s.val) WHEN MATCHED THEN UPDATE set key = t.key + 1, val = t.val || ' updated by ' || s.val; <waiting ...>
|
||||
step c1: COMMIT;
|
||||
step merge2: <... completed>
|
||||
step select2: SELECT * FROM target;
|
||||
key val
|
||||
|
||||
1 merge2a
|
||||
step c2: COMMIT;
|
@ -1,84 +0,0 @@
|
||||
Parsed test spec with 2 sessions
|
||||
|
||||
starting permutation: merge1 c1 select2 c2
|
||||
step merge1: MERGE INTO target t USING (SELECT 1 as key, 'merge1' as val) s ON s.key = t.key WHEN NOT MATCHED THEN INSERT VALUES (s.key, s.val) WHEN MATCHED THEN UPDATE set val = t.val || ' updated by merge1';
|
||||
step c1: COMMIT;
|
||||
step select2: SELECT * FROM target;
|
||||
key val
|
||||
|
||||
1 merge1
|
||||
step c2: COMMIT;
|
||||
|
||||
starting permutation: merge1 c1 merge2 select2 c2
|
||||
step merge1: MERGE INTO target t USING (SELECT 1 as key, 'merge1' as val) s ON s.key = t.key WHEN NOT MATCHED THEN INSERT VALUES (s.key, s.val) WHEN MATCHED THEN UPDATE set val = t.val || ' updated by merge1';
|
||||
step c1: COMMIT;
|
||||
step merge2: MERGE INTO target t USING (SELECT 1 as key, 'merge2' as val) s ON s.key = t.key WHEN NOT MATCHED THEN INSERT VALUES (s.key, s.val) WHEN MATCHED THEN UPDATE set val = t.val || ' updated by merge2';
|
||||
step select2: SELECT * FROM target;
|
||||
key val
|
||||
|
||||
1 merge1 updated by merge2
|
||||
step c2: COMMIT;
|
||||
|
||||
starting permutation: insert1 merge2 c1 select2 c2
|
||||
step insert1: INSERT INTO target VALUES (1, 'insert1');
|
||||
step merge2: MERGE INTO target t USING (SELECT 1 as key, 'merge2' as val) s ON s.key = t.key WHEN NOT MATCHED THEN INSERT VALUES (s.key, s.val) WHEN MATCHED THEN UPDATE set val = t.val || ' updated by merge2'; <waiting ...>
|
||||
step c1: COMMIT;
|
||||
step merge2: <... completed>
|
||||
error in steps c1 merge2: ERROR: duplicate key value violates unique constraint "target_pkey"
|
||||
step select2: SELECT * FROM target;
|
||||
ERROR: current transaction is aborted, commands ignored until end of transaction block
|
||||
step c2: COMMIT;
|
||||
|
||||
starting permutation: merge1 merge2 c1 select2 c2
|
||||
step merge1: MERGE INTO target t USING (SELECT 1 as key, 'merge1' as val) s ON s.key = t.key WHEN NOT MATCHED THEN INSERT VALUES (s.key, s.val) WHEN MATCHED THEN UPDATE set val = t.val || ' updated by merge1';
|
||||
step merge2: MERGE INTO target t USING (SELECT 1 as key, 'merge2' as val) s ON s.key = t.key WHEN NOT MATCHED THEN INSERT VALUES (s.key, s.val) WHEN MATCHED THEN UPDATE set val = t.val || ' updated by merge2'; <waiting ...>
|
||||
step c1: COMMIT;
|
||||
step merge2: <... completed>
|
||||
error in steps c1 merge2: ERROR: duplicate key value violates unique constraint "target_pkey"
|
||||
step select2: SELECT * FROM target;
|
||||
ERROR: current transaction is aborted, commands ignored until end of transaction block
|
||||
step c2: COMMIT;
|
||||
|
||||
starting permutation: merge1 merge2 a1 select2 c2
|
||||
step merge1: MERGE INTO target t USING (SELECT 1 as key, 'merge1' as val) s ON s.key = t.key WHEN NOT MATCHED THEN INSERT VALUES (s.key, s.val) WHEN MATCHED THEN UPDATE set val = t.val || ' updated by merge1';
|
||||
step merge2: MERGE INTO target t USING (SELECT 1 as key, 'merge2' as val) s ON s.key = t.key WHEN NOT MATCHED THEN INSERT VALUES (s.key, s.val) WHEN MATCHED THEN UPDATE set val = t.val || ' updated by merge2'; <waiting ...>
|
||||
step a1: ABORT;
|
||||
step merge2: <... completed>
|
||||
step select2: SELECT * FROM target;
|
||||
key val
|
||||
|
||||
1 merge2
|
||||
step c2: COMMIT;
|
||||
|
||||
starting permutation: delete1 insert1 c1 merge2 select2 c2
|
||||
step delete1: DELETE FROM target WHERE key = 1;
|
||||
step insert1: INSERT INTO target VALUES (1, 'insert1');
|
||||
step c1: COMMIT;
|
||||
step merge2: MERGE INTO target t USING (SELECT 1 as key, 'merge2' as val) s ON s.key = t.key WHEN NOT MATCHED THEN INSERT VALUES (s.key, s.val) WHEN MATCHED THEN UPDATE set val = t.val || ' updated by merge2';
|
||||
step select2: SELECT * FROM target;
|
||||
key val
|
||||
|
||||
1 insert1 updated by merge2
|
||||
step c2: COMMIT;
|
||||
|
||||
starting permutation: delete1 insert1 merge2 c1 select2 c2
|
||||
step delete1: DELETE FROM target WHERE key = 1;
|
||||
step insert1: INSERT INTO target VALUES (1, 'insert1');
|
||||
step merge2: MERGE INTO target t USING (SELECT 1 as key, 'merge2' as val) s ON s.key = t.key WHEN NOT MATCHED THEN INSERT VALUES (s.key, s.val) WHEN MATCHED THEN UPDATE set val = t.val || ' updated by merge2'; <waiting ...>
|
||||
step c1: COMMIT;
|
||||
step merge2: <... completed>
|
||||
error in steps c1 merge2: ERROR: duplicate key value violates unique constraint "target_pkey"
|
||||
step select2: SELECT * FROM target;
|
||||
ERROR: current transaction is aborted, commands ignored until end of transaction block
|
||||
step c2: COMMIT;
|
||||
|
||||
starting permutation: delete1 insert1 merge2i c1 select2 c2
|
||||
step delete1: DELETE FROM target WHERE key = 1;
|
||||
step insert1: INSERT INTO target VALUES (1, 'insert1');
|
||||
step merge2i: MERGE INTO target t USING (SELECT 1 as key, 'merge2' as val) s ON s.key = t.key WHEN MATCHED THEN UPDATE set val = t.val || ' updated by merge2';
|
||||
step c1: COMMIT;
|
||||
step select2: SELECT * FROM target;
|
||||
key val
|
||||
|
||||
1 insert1
|
||||
step c2: COMMIT;
|
@ -1,106 +0,0 @@
|
||||
Parsed test spec with 2 sessions
|
||||
|
||||
starting permutation: update1 merge_status c2 select1 c1
|
||||
step update1: UPDATE target t SET balance = balance + 10, val = t.val || ' updated by update1' WHERE t.key = 1;
|
||||
step merge_status:
|
||||
MERGE INTO target t
|
||||
USING (SELECT 1 as key) s
|
||||
ON s.key = t.key
|
||||
WHEN MATCHED AND status = 's1' THEN
|
||||
UPDATE SET status = 's2', val = t.val || ' when1'
|
||||
WHEN MATCHED AND status = 's2' THEN
|
||||
UPDATE SET status = 's3', val = t.val || ' when2'
|
||||
WHEN MATCHED AND status = 's3' THEN
|
||||
UPDATE SET status = 's4', val = t.val || ' when3';
|
||||
<waiting ...>
|
||||
step c2: COMMIT;
|
||||
step merge_status: <... completed>
|
||||
step select1: SELECT * FROM target;
|
||||
key balance status val
|
||||
|
||||
1 170 s2 setup updated by update1 when1
|
||||
step c1: COMMIT;
|
||||
|
||||
starting permutation: update2 merge_status c2 select1 c1
|
||||
step update2: UPDATE target t SET status = 's2', val = t.val || ' updated by update2' WHERE t.key = 1;
|
||||
step merge_status:
|
||||
MERGE INTO target t
|
||||
USING (SELECT 1 as key) s
|
||||
ON s.key = t.key
|
||||
WHEN MATCHED AND status = 's1' THEN
|
||||
UPDATE SET status = 's2', val = t.val || ' when1'
|
||||
WHEN MATCHED AND status = 's2' THEN
|
||||
UPDATE SET status = 's3', val = t.val || ' when2'
|
||||
WHEN MATCHED AND status = 's3' THEN
|
||||
UPDATE SET status = 's4', val = t.val || ' when3';
|
||||
<waiting ...>
|
||||
step c2: COMMIT;
|
||||
step merge_status: <... completed>
|
||||
step select1: SELECT * FROM target;
|
||||
key balance status val
|
||||
|
||||
1 160 s3 setup updated by update2 when2
|
||||
step c1: COMMIT;
|
||||
|
||||
starting permutation: update3 merge_status c2 select1 c1
|
||||
step update3: UPDATE target t SET status = 's3', val = t.val || ' updated by update3' WHERE t.key = 1;
|
||||
step merge_status:
|
||||
MERGE INTO target t
|
||||
USING (SELECT 1 as key) s
|
||||
ON s.key = t.key
|
||||
WHEN MATCHED AND status = 's1' THEN
|
||||
UPDATE SET status = 's2', val = t.val || ' when1'
|
||||
WHEN MATCHED AND status = 's2' THEN
|
||||
UPDATE SET status = 's3', val = t.val || ' when2'
|
||||
WHEN MATCHED AND status = 's3' THEN
|
||||
UPDATE SET status = 's4', val = t.val || ' when3';
|
||||
<waiting ...>
|
||||
step c2: COMMIT;
|
||||
step merge_status: <... completed>
|
||||
step select1: SELECT * FROM target;
|
||||
key balance status val
|
||||
|
||||
1 160 s4 setup updated by update3 when3
|
||||
step c1: COMMIT;
|
||||
|
||||
starting permutation: update5 merge_status c2 select1 c1
|
||||
step update5: UPDATE target t SET status = 's5', val = t.val || ' updated by update5' WHERE t.key = 1;
|
||||
step merge_status:
|
||||
MERGE INTO target t
|
||||
USING (SELECT 1 as key) s
|
||||
ON s.key = t.key
|
||||
WHEN MATCHED AND status = 's1' THEN
|
||||
UPDATE SET status = 's2', val = t.val || ' when1'
|
||||
WHEN MATCHED AND status = 's2' THEN
|
||||
UPDATE SET status = 's3', val = t.val || ' when2'
|
||||
WHEN MATCHED AND status = 's3' THEN
|
||||
UPDATE SET status = 's4', val = t.val || ' when3';
|
||||
<waiting ...>
|
||||
step c2: COMMIT;
|
||||
step merge_status: <... completed>
|
||||
step select1: SELECT * FROM target;
|
||||
key balance status val
|
||||
|
||||
1 160 s5 setup updated by update5
|
||||
step c1: COMMIT;
|
||||
|
||||
starting permutation: update_bal1 merge_bal c2 select1 c1
|
||||
step update_bal1: UPDATE target t SET balance = 50, val = t.val || ' updated by update_bal1' WHERE t.key = 1;
|
||||
step merge_bal:
|
||||
MERGE INTO target t
|
||||
USING (SELECT 1 as key) s
|
||||
ON s.key = t.key
|
||||
WHEN MATCHED AND balance < 100 THEN
|
||||
UPDATE SET balance = balance * 2, val = t.val || ' when1'
|
||||
WHEN MATCHED AND balance < 200 THEN
|
||||
UPDATE SET balance = balance * 4, val = t.val || ' when2'
|
||||
WHEN MATCHED AND balance < 300 THEN
|
||||
UPDATE SET balance = balance * 8, val = t.val || ' when3';
|
||||
<waiting ...>
|
||||
step c2: COMMIT;
|
||||
step merge_bal: <... completed>
|
||||
step select1: SELECT * FROM target;
|
||||
key balance status val
|
||||
|
||||
1 100 s1 setup updated by update_bal1 when1
|
||||
step c1: COMMIT;
|
@ -1,238 +0,0 @@
|
||||
Parsed test spec with 2 sessions
|
||||
|
||||
starting permutation: merge1 c1 select2 c2
|
||||
step merge1:
|
||||
MERGE INTO target t
|
||||
USING (SELECT 1 as key, 'merge1' as val) s
|
||||
ON s.key = t.key
|
||||
WHEN NOT MATCHED THEN
|
||||
INSERT VALUES (s.key, s.val)
|
||||
WHEN MATCHED THEN
|
||||
UPDATE set key = t.key + 1, val = t.val || ' updated by ' || s.val;
|
||||
|
||||
step c1: COMMIT;
|
||||
step select2: SELECT * FROM target;
|
||||
key val
|
||||
|
||||
2 setup1 updated by merge1
|
||||
step c2: COMMIT;
|
||||
|
||||
starting permutation: merge1 c1 merge2a select2 c2
|
||||
step merge1:
|
||||
MERGE INTO target t
|
||||
USING (SELECT 1 as key, 'merge1' as val) s
|
||||
ON s.key = t.key
|
||||
WHEN NOT MATCHED THEN
|
||||
INSERT VALUES (s.key, s.val)
|
||||
WHEN MATCHED THEN
|
||||
UPDATE set key = t.key + 1, val = t.val || ' updated by ' || s.val;
|
||||
|
||||
step c1: COMMIT;
|
||||
step merge2a:
|
||||
MERGE INTO target t
|
||||
USING (SELECT 1 as key, 'merge2a' as val) s
|
||||
ON s.key = t.key
|
||||
WHEN NOT MATCHED THEN
|
||||
INSERT VALUES (s.key, s.val)
|
||||
WHEN MATCHED THEN
|
||||
UPDATE set key = t.key + 1, val = t.val || ' updated by ' || s.val;
|
||||
|
||||
step select2: SELECT * FROM target;
|
||||
key val
|
||||
|
||||
2 setup1 updated by merge1
|
||||
1 merge2a
|
||||
step c2: COMMIT;
|
||||
|
||||
starting permutation: merge1 merge2a c1 select2 c2
|
||||
step merge1:
|
||||
MERGE INTO target t
|
||||
USING (SELECT 1 as key, 'merge1' as val) s
|
||||
ON s.key = t.key
|
||||
WHEN NOT MATCHED THEN
|
||||
INSERT VALUES (s.key, s.val)
|
||||
WHEN MATCHED THEN
|
||||
UPDATE set key = t.key + 1, val = t.val || ' updated by ' || s.val;
|
||||
|
||||
step merge2a:
|
||||
MERGE INTO target t
|
||||
USING (SELECT 1 as key, 'merge2a' as val) s
|
||||
ON s.key = t.key
|
||||
WHEN NOT MATCHED THEN
|
||||
INSERT VALUES (s.key, s.val)
|
||||
WHEN MATCHED THEN
|
||||
UPDATE set key = t.key + 1, val = t.val || ' updated by ' || s.val;
|
||||
<waiting ...>
|
||||
step c1: COMMIT;
|
||||
step merge2a: <... completed>
|
||||
step select2: SELECT * FROM target;
|
||||
key val
|
||||
|
||||
2 setup1 updated by merge1
|
||||
1 merge2a
|
||||
step c2: COMMIT;
|
||||
|
||||
starting permutation: merge1 merge2a a1 select2 c2
|
||||
step merge1:
|
||||
MERGE INTO target t
|
||||
USING (SELECT 1 as key, 'merge1' as val) s
|
||||
ON s.key = t.key
|
||||
WHEN NOT MATCHED THEN
|
||||
INSERT VALUES (s.key, s.val)
|
||||
WHEN MATCHED THEN
|
||||
UPDATE set key = t.key + 1, val = t.val || ' updated by ' || s.val;
|
||||
|
||||
step merge2a:
|
||||
MERGE INTO target t
|
||||
USING (SELECT 1 as key, 'merge2a' as val) s
|
||||
ON s.key = t.key
|
||||
WHEN NOT MATCHED THEN
|
||||
INSERT VALUES (s.key, s.val)
|
||||
WHEN MATCHED THEN
|
||||
UPDATE set key = t.key + 1, val = t.val || ' updated by ' || s.val;
|
||||
<waiting ...>
|
||||
step a1: ABORT;
|
||||
step merge2a: <... completed>
|
||||
step select2: SELECT * FROM target;
|
||||
key val
|
||||
|
||||
2 setup1 updated by merge2a
|
||||
step c2: COMMIT;
|
||||
|
||||
starting permutation: merge1 merge2b c1 select2 c2
|
||||
step merge1:
|
||||
MERGE INTO target t
|
||||
USING (SELECT 1 as key, 'merge1' as val) s
|
||||
ON s.key = t.key
|
||||
WHEN NOT MATCHED THEN
|
||||
INSERT VALUES (s.key, s.val)
|
||||
WHEN MATCHED THEN
|
||||
UPDATE set key = t.key + 1, val = t.val || ' updated by ' || s.val;
|
||||
|
||||
step merge2b:
|
||||
MERGE INTO target t
|
||||
USING (SELECT 1 as key, 'merge2b' as val) s
|
||||
ON s.key = t.key
|
||||
WHEN NOT MATCHED THEN
|
||||
INSERT VALUES (s.key, s.val)
|
||||
WHEN MATCHED AND t.key < 2 THEN
|
||||
UPDATE set key = t.key + 1, val = t.val || ' updated by ' || s.val;
|
||||
<waiting ...>
|
||||
step c1: COMMIT;
|
||||
step merge2b: <... completed>
|
||||
step select2: SELECT * FROM target;
|
||||
key val
|
||||
|
||||
2 setup1 updated by merge1
|
||||
1 merge2b
|
||||
step c2: COMMIT;
|
||||
|
||||
starting permutation: merge1 merge2c c1 select2 c2
|
||||
step merge1:
|
||||
MERGE INTO target t
|
||||
USING (SELECT 1 as key, 'merge1' as val) s
|
||||
ON s.key = t.key
|
||||
WHEN NOT MATCHED THEN
|
||||
INSERT VALUES (s.key, s.val)
|
||||
WHEN MATCHED THEN
|
||||
UPDATE set key = t.key + 1, val = t.val || ' updated by ' || s.val;
|
||||
|
||||
step merge2c:
|
||||
MERGE INTO target t
|
||||
USING (SELECT 1 as key, 'merge2c' as val) s
|
||||
ON s.key = t.key AND t.key < 2
|
||||
WHEN NOT MATCHED THEN
|
||||
INSERT VALUES (s.key, s.val)
|
||||
WHEN MATCHED THEN
|
||||
UPDATE set key = t.key + 1, val = t.val || ' updated by ' || s.val;
|
||||
<waiting ...>
|
||||
step c1: COMMIT;
|
||||
step merge2c: <... completed>
|
||||
step select2: SELECT * FROM target;
|
||||
key val
|
||||
|
||||
2 setup1 updated by merge1
|
||||
1 merge2c
|
||||
step c2: COMMIT;
|
||||
|
||||
starting permutation: pa_merge1 pa_merge2a c1 pa_select2 c2
|
||||
step pa_merge1:
|
||||
MERGE INTO pa_target t
|
||||
USING (SELECT 1 as key, 'pa_merge1' as val) s
|
||||
ON s.key = t.key
|
||||
WHEN NOT MATCHED THEN
|
||||
INSERT VALUES (s.key, s.val)
|
||||
WHEN MATCHED THEN
|
||||
UPDATE set val = t.val || ' updated by ' || s.val;
|
||||
|
||||
step pa_merge2a:
|
||||
MERGE INTO pa_target t
|
||||
USING (SELECT 1 as key, 'pa_merge2a' as val) s
|
||||
ON s.key = t.key
|
||||
WHEN NOT MATCHED THEN
|
||||
INSERT VALUES (s.key, s.val)
|
||||
WHEN MATCHED THEN
|
||||
UPDATE set key = t.key + 1, val = t.val || ' updated by ' || s.val;
|
||||
<waiting ...>
|
||||
step c1: COMMIT;
|
||||
step pa_merge2a: <... completed>
|
||||
step pa_select2: SELECT * FROM pa_target;
|
||||
key val
|
||||
|
||||
2 initial
|
||||
2 initial updated by pa_merge2a
|
||||
step c2: COMMIT;
|
||||
|
||||
starting permutation: pa_merge2 pa_merge2a c1 pa_select2 c2
|
||||
step pa_merge2:
|
||||
MERGE INTO pa_target t
|
||||
USING (SELECT 1 as key, 'pa_merge1' as val) s
|
||||
ON s.key = t.key
|
||||
WHEN NOT MATCHED THEN
|
||||
INSERT VALUES (s.key, s.val)
|
||||
WHEN MATCHED THEN
|
||||
UPDATE set key = t.key + 1, val = t.val || ' updated by ' || s.val;
|
||||
|
||||
step pa_merge2a:
|
||||
MERGE INTO pa_target t
|
||||
USING (SELECT 1 as key, 'pa_merge2a' as val) s
|
||||
ON s.key = t.key
|
||||
WHEN NOT MATCHED THEN
|
||||
INSERT VALUES (s.key, s.val)
|
||||
WHEN MATCHED THEN
|
||||
UPDATE set key = t.key + 1, val = t.val || ' updated by ' || s.val;
|
||||
<waiting ...>
|
||||
step c1: COMMIT;
|
||||
step pa_merge2a: <... completed>
|
||||
error in steps c1 pa_merge2a: ERROR: tuple to be deleted was already moved to another partition due to concurrent update
|
||||
step pa_select2: SELECT * FROM pa_target;
|
||||
ERROR: current transaction is aborted, commands ignored until end of transaction block
|
||||
step c2: COMMIT;
|
||||
|
||||
starting permutation: pa_merge2 c1 pa_merge2a pa_select2 c2
|
||||
step pa_merge2:
|
||||
MERGE INTO pa_target t
|
||||
USING (SELECT 1 as key, 'pa_merge1' as val) s
|
||||
ON s.key = t.key
|
||||
WHEN NOT MATCHED THEN
|
||||
INSERT VALUES (s.key, s.val)
|
||||
WHEN MATCHED THEN
|
||||
UPDATE set key = t.key + 1, val = t.val || ' updated by ' || s.val;
|
||||
|
||||
step c1: COMMIT;
|
||||
step pa_merge2a:
|
||||
MERGE INTO pa_target t
|
||||
USING (SELECT 1 as key, 'pa_merge2a' as val) s
|
||||
ON s.key = t.key
|
||||
WHEN NOT MATCHED THEN
|
||||
INSERT VALUES (s.key, s.val)
|
||||
WHEN MATCHED THEN
|
||||
UPDATE set key = t.key + 1, val = t.val || ' updated by ' || s.val;
|
||||
|
||||
step pa_select2: SELECT * FROM pa_target;
|
||||
key val
|
||||
|
||||
1 pa_merge2a
|
||||
2 initial
|
||||
2 initial updated by pa_merge1
|
||||
step c2: COMMIT;
|
@ -33,10 +33,6 @@ test: insert-conflict-do-update
|
||||
test: insert-conflict-do-update-2
|
||||
test: insert-conflict-do-update-3
|
||||
test: insert-conflict-toast
|
||||
test: merge-insert-update
|
||||
test: merge-delete
|
||||
test: merge-update
|
||||
test: merge-match-recheck
|
||||
test: delete-abort-savept
|
||||
test: delete-abort-savept-2
|
||||
test: aborted-keyrevoke
|
||||
|
@ -1,51 +0,0 @@
|
||||
# MERGE DELETE
|
||||
#
|
||||
# This test looks at the interactions involving concurrent deletes
|
||||
# comparing the behavior of MERGE, DELETE and UPDATE
|
||||
|
||||
setup
|
||||
{
|
||||
CREATE TABLE target (key int primary key, val text);
|
||||
INSERT INTO target VALUES (1, 'setup1');
|
||||
}
|
||||
|
||||
teardown
|
||||
{
|
||||
DROP TABLE target;
|
||||
}
|
||||
|
||||
session "s1"
|
||||
setup
|
||||
{
|
||||
BEGIN ISOLATION LEVEL READ COMMITTED;
|
||||
}
|
||||
step "delete" { DELETE FROM target t WHERE t.key = 1; }
|
||||
step "merge_delete" { MERGE INTO target t USING (SELECT 1 as key) s ON s.key = t.key WHEN MATCHED THEN DELETE; }
|
||||
step "c1" { COMMIT; }
|
||||
step "a1" { ABORT; }
|
||||
|
||||
session "s2"
|
||||
setup
|
||||
{
|
||||
BEGIN ISOLATION LEVEL READ COMMITTED;
|
||||
}
|
||||
step "update1" { UPDATE target t SET val = t.val || ' updated by update1' WHERE t.key = 1; }
|
||||
step "merge2" { MERGE INTO target t USING (SELECT 1 as key, 'merge2a' as val) s ON s.key = t.key WHEN NOT MATCHED THEN INSERT VALUES (s.key, s.val) WHEN MATCHED THEN UPDATE set key = t.key + 1, val = t.val || ' updated by ' || s.val; }
|
||||
step "select2" { SELECT * FROM target; }
|
||||
step "c2" { COMMIT; }
|
||||
|
||||
# Basic effects
|
||||
permutation "delete" "c1" "select2" "c2"
|
||||
permutation "merge_delete" "c1" "select2" "c2"
|
||||
|
||||
# One after the other, no concurrency
|
||||
permutation "delete" "c1" "update1" "select2" "c2"
|
||||
permutation "merge_delete" "c1" "update1" "select2" "c2"
|
||||
permutation "delete" "c1" "merge2" "select2" "c2"
|
||||
permutation "merge_delete" "c1" "merge2" "select2" "c2"
|
||||
|
||||
# Now with concurrency
|
||||
permutation "delete" "update1" "c1" "select2" "c2"
|
||||
permutation "merge_delete" "update1" "c1" "select2" "c2"
|
||||
permutation "delete" "merge2" "c1" "select2" "c2"
|
||||
permutation "merge_delete" "merge2" "c1" "select2" "c2"
|
@ -1,52 +0,0 @@
|
||||
# MERGE INSERT UPDATE
|
||||
#
|
||||
# This looks at how we handle concurrent INSERTs, illustrating how the
|
||||
# behavior differs from INSERT ... ON CONFLICT
|
||||
|
||||
setup
|
||||
{
|
||||
CREATE TABLE target (key int primary key, val text);
|
||||
}
|
||||
|
||||
teardown
|
||||
{
|
||||
DROP TABLE target;
|
||||
}
|
||||
|
||||
session "s1"
|
||||
setup
|
||||
{
|
||||
BEGIN ISOLATION LEVEL READ COMMITTED;
|
||||
}
|
||||
step "merge1" { MERGE INTO target t USING (SELECT 1 as key, 'merge1' as val) s ON s.key = t.key WHEN NOT MATCHED THEN INSERT VALUES (s.key, s.val) WHEN MATCHED THEN UPDATE set val = t.val || ' updated by merge1'; }
|
||||
step "delete1" { DELETE FROM target WHERE key = 1; }
|
||||
step "insert1" { INSERT INTO target VALUES (1, 'insert1'); }
|
||||
step "c1" { COMMIT; }
|
||||
step "a1" { ABORT; }
|
||||
|
||||
session "s2"
|
||||
setup
|
||||
{
|
||||
BEGIN ISOLATION LEVEL READ COMMITTED;
|
||||
}
|
||||
step "merge2" { MERGE INTO target t USING (SELECT 1 as key, 'merge2' as val) s ON s.key = t.key WHEN NOT MATCHED THEN INSERT VALUES (s.key, s.val) WHEN MATCHED THEN UPDATE set val = t.val || ' updated by merge2'; }
|
||||
|
||||
step "merge2i" { MERGE INTO target t USING (SELECT 1 as key, 'merge2' as val) s ON s.key = t.key WHEN MATCHED THEN UPDATE set val = t.val || ' updated by merge2'; }
|
||||
|
||||
step "select2" { SELECT * FROM target; }
|
||||
step "c2" { COMMIT; }
|
||||
step "a2" { ABORT; }
|
||||
|
||||
# Basic effects
|
||||
permutation "merge1" "c1" "select2" "c2"
|
||||
permutation "merge1" "c1" "merge2" "select2" "c2"
|
||||
|
||||
# check concurrent inserts
|
||||
permutation "insert1" "merge2" "c1" "select2" "c2"
|
||||
permutation "merge1" "merge2" "c1" "select2" "c2"
|
||||
permutation "merge1" "merge2" "a1" "select2" "c2"
|
||||
|
||||
# check how we handle when visible row has been concurrently deleted, then same key re-inserted
|
||||
permutation "delete1" "insert1" "c1" "merge2" "select2" "c2"
|
||||
permutation "delete1" "insert1" "merge2" "c1" "select2" "c2"
|
||||
permutation "delete1" "insert1" "merge2i" "c1" "select2" "c2"
|
@ -1,79 +0,0 @@
|
||||
# MERGE MATCHED RECHECK
|
||||
#
|
||||
# This test looks at what happens when we have complex
|
||||
# WHEN MATCHED AND conditions and a concurrent UPDATE causes a
|
||||
# recheck of the AND condition on the new row
|
||||
|
||||
setup
|
||||
{
|
||||
CREATE TABLE target (key int primary key, balance integer, status text, val text);
|
||||
INSERT INTO target VALUES (1, 160, 's1', 'setup');
|
||||
}
|
||||
|
||||
teardown
|
||||
{
|
||||
DROP TABLE target;
|
||||
}
|
||||
|
||||
session "s1"
|
||||
setup
|
||||
{
|
||||
BEGIN ISOLATION LEVEL READ COMMITTED;
|
||||
}
|
||||
step "merge_status"
|
||||
{
|
||||
MERGE INTO target t
|
||||
USING (SELECT 1 as key) s
|
||||
ON s.key = t.key
|
||||
WHEN MATCHED AND status = 's1' THEN
|
||||
UPDATE SET status = 's2', val = t.val || ' when1'
|
||||
WHEN MATCHED AND status = 's2' THEN
|
||||
UPDATE SET status = 's3', val = t.val || ' when2'
|
||||
WHEN MATCHED AND status = 's3' THEN
|
||||
UPDATE SET status = 's4', val = t.val || ' when3';
|
||||
}
|
||||
|
||||
step "merge_bal"
|
||||
{
|
||||
MERGE INTO target t
|
||||
USING (SELECT 1 as key) s
|
||||
ON s.key = t.key
|
||||
WHEN MATCHED AND balance < 100 THEN
|
||||
UPDATE SET balance = balance * 2, val = t.val || ' when1'
|
||||
WHEN MATCHED AND balance < 200 THEN
|
||||
UPDATE SET balance = balance * 4, val = t.val || ' when2'
|
||||
WHEN MATCHED AND balance < 300 THEN
|
||||
UPDATE SET balance = balance * 8, val = t.val || ' when3';
|
||||
}
|
||||
|
||||
step "select1" { SELECT * FROM target; }
|
||||
step "c1" { COMMIT; }
|
||||
step "a1" { ABORT; }
|
||||
|
||||
session "s2"
|
||||
setup
|
||||
{
|
||||
BEGIN ISOLATION LEVEL READ COMMITTED;
|
||||
}
|
||||
step "update1" { UPDATE target t SET balance = balance + 10, val = t.val || ' updated by update1' WHERE t.key = 1; }
|
||||
step "update2" { UPDATE target t SET status = 's2', val = t.val || ' updated by update2' WHERE t.key = 1; }
|
||||
step "update3" { UPDATE target t SET status = 's3', val = t.val || ' updated by update3' WHERE t.key = 1; }
|
||||
step "update5" { UPDATE target t SET status = 's5', val = t.val || ' updated by update5' WHERE t.key = 1; }
|
||||
step "update_bal1" { UPDATE target t SET balance = 50, val = t.val || ' updated by update_bal1' WHERE t.key = 1; }
|
||||
step "select2" { SELECT * FROM target; }
|
||||
step "c2" { COMMIT; }
|
||||
|
||||
# merge_status sees concurrently updated row and rechecks WHEN conditions, but recheck passes and final status = 's2'
|
||||
permutation "update1" "merge_status" "c2" "select1" "c1"
|
||||
|
||||
# merge_status sees concurrently updated row and rechecks WHEN conditions, recheck fails, so final status = 's3' not 's2'
|
||||
permutation "update2" "merge_status" "c2" "select1" "c1"
|
||||
|
||||
# merge_status sees concurrently updated row and rechecks WHEN conditions, recheck fails, so final status = 's4' not 's2'
|
||||
permutation "update3" "merge_status" "c2" "select1" "c1"
|
||||
|
||||
# merge_status sees concurrently updated row and rechecks WHEN conditions, recheck fails, but we skip update and MERGE does nothing
|
||||
permutation "update5" "merge_status" "c2" "select1" "c1"
|
||||
|
||||
# merge_bal sees concurrently updated row and rechecks WHEN conditions, recheck fails, so final balance = 100 not 640
|
||||
permutation "update_bal1" "merge_bal" "c2" "select1" "c1"
|
@ -1,133 +0,0 @@
|
||||
# MERGE UPDATE
|
||||
#
|
||||
# This test exercises atypical cases
|
||||
# 1. UPDATEs of PKs that change the join in the ON clause
|
||||
# 2. UPDATEs with WHEN AND conditions that would fail after concurrent update
|
||||
# 3. UPDATEs with extra ON conditions that would fail after concurrent update
|
||||
|
||||
setup
|
||||
{
|
||||
CREATE TABLE target (key int primary key, val text);
|
||||
INSERT INTO target VALUES (1, 'setup1');
|
||||
|
||||
CREATE TABLE pa_target (key integer, val text)
|
||||
PARTITION BY LIST (key);
|
||||
CREATE TABLE part1 (key integer, val text);
|
||||
CREATE TABLE part2 (val text, key integer);
|
||||
CREATE TABLE part3 (key integer, val text);
|
||||
|
||||
ALTER TABLE pa_target ATTACH PARTITION part1 FOR VALUES IN (1,4);
|
||||
ALTER TABLE pa_target ATTACH PARTITION part2 FOR VALUES IN (2,5,6);
|
||||
ALTER TABLE pa_target ATTACH PARTITION part3 DEFAULT;
|
||||
|
||||
INSERT INTO pa_target VALUES (1, 'initial');
|
||||
INSERT INTO pa_target VALUES (2, 'initial');
|
||||
}
|
||||
|
||||
teardown
|
||||
{
|
||||
DROP TABLE target;
|
||||
DROP TABLE pa_target CASCADE;
|
||||
}
|
||||
|
||||
session "s1"
|
||||
setup
|
||||
{
|
||||
BEGIN ISOLATION LEVEL READ COMMITTED;
|
||||
}
|
||||
step "merge1"
|
||||
{
|
||||
MERGE INTO target t
|
||||
USING (SELECT 1 as key, 'merge1' as val) s
|
||||
ON s.key = t.key
|
||||
WHEN NOT MATCHED THEN
|
||||
INSERT VALUES (s.key, s.val)
|
||||
WHEN MATCHED THEN
|
||||
UPDATE set key = t.key + 1, val = t.val || ' updated by ' || s.val;
|
||||
}
|
||||
step "pa_merge1"
|
||||
{
|
||||
MERGE INTO pa_target t
|
||||
USING (SELECT 1 as key, 'pa_merge1' as val) s
|
||||
ON s.key = t.key
|
||||
WHEN NOT MATCHED THEN
|
||||
INSERT VALUES (s.key, s.val)
|
||||
WHEN MATCHED THEN
|
||||
UPDATE set val = t.val || ' updated by ' || s.val;
|
||||
}
|
||||
step "pa_merge2"
|
||||
{
|
||||
MERGE INTO pa_target t
|
||||
USING (SELECT 1 as key, 'pa_merge1' as val) s
|
||||
ON s.key = t.key
|
||||
WHEN NOT MATCHED THEN
|
||||
INSERT VALUES (s.key, s.val)
|
||||
WHEN MATCHED THEN
|
||||
UPDATE set key = t.key + 1, val = t.val || ' updated by ' || s.val;
|
||||
}
|
||||
step "c1" { COMMIT; }
|
||||
step "a1" { ABORT; }
|
||||
|
||||
session "s2"
|
||||
setup
|
||||
{
|
||||
BEGIN ISOLATION LEVEL READ COMMITTED;
|
||||
}
|
||||
step "merge2a"
|
||||
{
|
||||
MERGE INTO target t
|
||||
USING (SELECT 1 as key, 'merge2a' as val) s
|
||||
ON s.key = t.key
|
||||
WHEN NOT MATCHED THEN
|
||||
INSERT VALUES (s.key, s.val)
|
||||
WHEN MATCHED THEN
|
||||
UPDATE set key = t.key + 1, val = t.val || ' updated by ' || s.val;
|
||||
}
|
||||
step "merge2b"
|
||||
{
|
||||
MERGE INTO target t
|
||||
USING (SELECT 1 as key, 'merge2b' as val) s
|
||||
ON s.key = t.key
|
||||
WHEN NOT MATCHED THEN
|
||||
INSERT VALUES (s.key, s.val)
|
||||
WHEN MATCHED AND t.key < 2 THEN
|
||||
UPDATE set key = t.key + 1, val = t.val || ' updated by ' || s.val;
|
||||
}
|
||||
step "merge2c"
|
||||
{
|
||||
MERGE INTO target t
|
||||
USING (SELECT 1 as key, 'merge2c' as val) s
|
||||
ON s.key = t.key AND t.key < 2
|
||||
WHEN NOT MATCHED THEN
|
||||
INSERT VALUES (s.key, s.val)
|
||||
WHEN MATCHED THEN
|
||||
UPDATE set key = t.key + 1, val = t.val || ' updated by ' || s.val;
|
||||
}
|
||||
step "pa_merge2a"
|
||||
{
|
||||
MERGE INTO pa_target t
|
||||
USING (SELECT 1 as key, 'pa_merge2a' as val) s
|
||||
ON s.key = t.key
|
||||
WHEN NOT MATCHED THEN
|
||||
INSERT VALUES (s.key, s.val)
|
||||
WHEN MATCHED THEN
|
||||
UPDATE set key = t.key + 1, val = t.val || ' updated by ' || s.val;
|
||||
}
|
||||
step "select2" { SELECT * FROM target; }
|
||||
step "pa_select2" { SELECT * FROM pa_target; }
|
||||
step "c2" { COMMIT; }
|
||||
|
||||
# Basic effects
|
||||
permutation "merge1" "c1" "select2" "c2"
|
||||
|
||||
# One after the other, no concurrency
|
||||
permutation "merge1" "c1" "merge2a" "select2" "c2"
|
||||
|
||||
# Now with concurrency
|
||||
permutation "merge1" "merge2a" "c1" "select2" "c2"
|
||||
permutation "merge1" "merge2a" "a1" "select2" "c2"
|
||||
permutation "merge1" "merge2b" "c1" "select2" "c2"
|
||||
permutation "merge1" "merge2c" "c1" "select2" "c2"
|
||||
permutation "pa_merge1" "pa_merge2a" "c1" "pa_select2" "c2"
|
||||
permutation "pa_merge2" "pa_merge2a" "c1" "pa_select2" "c2" # fails
|
||||
permutation "pa_merge2" "c1" "pa_merge2a" "pa_select2" "c2" # succeeds
|
@ -386,58 +386,3 @@ CREATE TABLE itest_child PARTITION OF itest_parent (
|
||||
) FOR VALUES FROM ('2016-07-01') TO ('2016-08-01'); -- error
|
||||
ERROR: identity columns are not supported on partitions
|
||||
DROP TABLE itest_parent;
|
||||
-- MERGE tests
|
||||
CREATE TABLE itest14 (a int GENERATED ALWAYS AS IDENTITY, b text);
|
||||
CREATE TABLE itest15 (a int GENERATED BY DEFAULT AS IDENTITY, b text);
|
||||
MERGE INTO itest14 t
|
||||
USING (SELECT 10 AS s_a, 'inserted by merge' AS s_b) s
|
||||
ON t.a = s.s_a
|
||||
WHEN NOT MATCHED THEN
|
||||
INSERT (a, b) VALUES (s.s_a, s.s_b);
|
||||
ERROR: cannot insert into column "a"
|
||||
DETAIL: Column "a" is an identity column defined as GENERATED ALWAYS.
|
||||
HINT: Use OVERRIDING SYSTEM VALUE to override.
|
||||
MERGE INTO itest14 t
|
||||
USING (SELECT 20 AS s_a, 'inserted by merge' AS s_b) s
|
||||
ON t.a = s.s_a
|
||||
WHEN NOT MATCHED THEN
|
||||
INSERT (a, b) OVERRIDING USER VALUE VALUES (s.s_a, s.s_b);
|
||||
ERROR: cannot insert into column "a"
|
||||
DETAIL: Column "a" is an identity column defined as GENERATED ALWAYS.
|
||||
HINT: Use OVERRIDING SYSTEM VALUE to override.
|
||||
MERGE INTO itest14 t
|
||||
USING (SELECT 30 AS s_a, 'inserted by merge' AS s_b) s
|
||||
ON t.a = s.s_a
|
||||
WHEN NOT MATCHED THEN
|
||||
INSERT (a, b) OVERRIDING SYSTEM VALUE VALUES (s.s_a, s.s_b);
|
||||
MERGE INTO itest15 t
|
||||
USING (SELECT 10 AS s_a, 'inserted by merge' AS s_b) s
|
||||
ON t.a = s.s_a
|
||||
WHEN NOT MATCHED THEN
|
||||
INSERT (a, b) VALUES (s.s_a, s.s_b);
|
||||
MERGE INTO itest15 t
|
||||
USING (SELECT 20 AS s_a, 'inserted by merge' AS s_b) s
|
||||
ON t.a = s.s_a
|
||||
WHEN NOT MATCHED THEN
|
||||
INSERT (a, b) OVERRIDING USER VALUE VALUES (s.s_a, s.s_b);
|
||||
MERGE INTO itest15 t
|
||||
USING (SELECT 30 AS s_a, 'inserted by merge' AS s_b) s
|
||||
ON t.a = s.s_a
|
||||
WHEN NOT MATCHED THEN
|
||||
INSERT (a, b) OVERRIDING SYSTEM VALUE VALUES (s.s_a, s.s_b);
|
||||
SELECT * FROM itest14;
|
||||
a | b
|
||||
----+-------------------
|
||||
30 | inserted by merge
|
||||
(1 row)
|
||||
|
||||
SELECT * FROM itest15;
|
||||
a | b
|
||||
----+-------------------
|
||||
10 | inserted by merge
|
||||
1 | inserted by merge
|
||||
30 | inserted by merge
|
||||
(3 rows)
|
||||
|
||||
DROP TABLE itest14;
|
||||
DROP TABLE itest15;
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -517,104 +517,6 @@ SELECT atest6 FROM atest6; -- ok
|
||||
(0 rows)
|
||||
|
||||
COPY atest6 TO stdout; -- ok
|
||||
-- test column privileges with MERGE
|
||||
SET SESSION AUTHORIZATION regress_priv_user1;
|
||||
CREATE TABLE mtarget (a int, b text);
|
||||
CREATE TABLE msource (a int, b text);
|
||||
INSERT INTO mtarget VALUES (1, 'init1'), (2, 'init2');
|
||||
INSERT INTO msource VALUES (1, 'source1'), (2, 'source2'), (3, 'source3');
|
||||
GRANT SELECT (a) ON msource TO regress_priv_user4;
|
||||
GRANT SELECT (a) ON mtarget TO regress_priv_user4;
|
||||
GRANT INSERT (a,b) ON mtarget TO regress_priv_user4;
|
||||
GRANT UPDATE (b) ON mtarget TO regress_priv_user4;
|
||||
SET SESSION AUTHORIZATION regress_priv_user4;
|
||||
--
|
||||
-- test source privileges
|
||||
--
|
||||
-- fail (no SELECT priv on s.b)
|
||||
MERGE INTO mtarget t USING msource s ON t.a = s.a
|
||||
WHEN MATCHED THEN
|
||||
UPDATE SET b = s.b
|
||||
WHEN NOT MATCHED THEN
|
||||
INSERT VALUES (a, NULL);
|
||||
ERROR: permission denied for table msource
|
||||
-- fail (s.b used in the INSERTed values)
|
||||
MERGE INTO mtarget t USING msource s ON t.a = s.a
|
||||
WHEN MATCHED THEN
|
||||
UPDATE SET b = 'x'
|
||||
WHEN NOT MATCHED THEN
|
||||
INSERT VALUES (a, b);
|
||||
ERROR: permission denied for table msource
|
||||
-- fail (s.b used in the WHEN quals)
|
||||
MERGE INTO mtarget t USING msource s ON t.a = s.a
|
||||
WHEN MATCHED AND s.b = 'x' THEN
|
||||
UPDATE SET b = 'x'
|
||||
WHEN NOT MATCHED THEN
|
||||
INSERT VALUES (a, NULL);
|
||||
ERROR: permission denied for table msource
|
||||
-- this should be ok since only s.a is accessed
|
||||
BEGIN;
|
||||
MERGE INTO mtarget t USING msource s ON t.a = s.a
|
||||
WHEN MATCHED THEN
|
||||
UPDATE SET b = 'ok'
|
||||
WHEN NOT MATCHED THEN
|
||||
INSERT VALUES (a, NULL);
|
||||
ROLLBACK;
|
||||
SET SESSION AUTHORIZATION regress_priv_user1;
|
||||
GRANT SELECT (b) ON msource TO regress_priv_user4;
|
||||
SET SESSION AUTHORIZATION regress_priv_user4;
|
||||
-- should now be ok
|
||||
BEGIN;
|
||||
MERGE INTO mtarget t USING msource s ON t.a = s.a
|
||||
WHEN MATCHED THEN
|
||||
UPDATE SET b = s.b
|
||||
WHEN NOT MATCHED THEN
|
||||
INSERT VALUES (a, b);
|
||||
ROLLBACK;
|
||||
--
|
||||
-- test target privileges
|
||||
--
|
||||
-- fail (no SELECT priv on t.b)
|
||||
MERGE INTO mtarget t USING msource s ON t.a = s.a
|
||||
WHEN MATCHED THEN
|
||||
UPDATE SET b = t.b
|
||||
WHEN NOT MATCHED THEN
|
||||
INSERT VALUES (a, NULL);
|
||||
ERROR: permission denied for table mtarget
|
||||
-- fail (no UPDATE on t.a)
|
||||
MERGE INTO mtarget t USING msource s ON t.a = s.a
|
||||
WHEN MATCHED THEN
|
||||
UPDATE SET b = s.b, a = t.a + 1
|
||||
WHEN NOT MATCHED THEN
|
||||
INSERT VALUES (a, b);
|
||||
ERROR: permission denied for table mtarget
|
||||
-- fail (no SELECT on t.b)
|
||||
MERGE INTO mtarget t USING msource s ON t.a = s.a
|
||||
WHEN MATCHED AND t.b IS NOT NULL THEN
|
||||
UPDATE SET b = s.b
|
||||
WHEN NOT MATCHED THEN
|
||||
INSERT VALUES (a, b);
|
||||
ERROR: permission denied for table mtarget
|
||||
-- ok
|
||||
BEGIN;
|
||||
MERGE INTO mtarget t USING msource s ON t.a = s.a
|
||||
WHEN MATCHED THEN
|
||||
UPDATE SET b = s.b;
|
||||
ROLLBACK;
|
||||
-- fail (no DELETE)
|
||||
MERGE INTO mtarget t USING msource s ON t.a = s.a
|
||||
WHEN MATCHED AND t.b IS NOT NULL THEN
|
||||
DELETE;
|
||||
ERROR: permission denied for table mtarget
|
||||
-- grant delete privileges
|
||||
SET SESSION AUTHORIZATION regress_priv_user1;
|
||||
GRANT DELETE ON mtarget TO regress_priv_user4;
|
||||
-- should be ok now
|
||||
BEGIN;
|
||||
MERGE INTO mtarget t USING msource s ON t.a = s.a
|
||||
WHEN MATCHED AND t.b IS NOT NULL THEN
|
||||
DELETE;
|
||||
ROLLBACK;
|
||||
-- check error reporting with column privs
|
||||
SET SESSION AUTHORIZATION regress_priv_user1;
|
||||
CREATE TABLE t1 (c1 int, c2 int, c3 int check (c3 < 5), primary key (c1, c2));
|
||||
|
@ -2138,188 +2138,6 @@ ERROR: new row violates row-level security policy (USING expression) for table
|
||||
INSERT INTO document VALUES (1, (SELECT cid from category WHERE cname = 'novel'), 1, 'regress_rls_bob', 'my first novel')
|
||||
ON CONFLICT (did) DO UPDATE SET dauthor = 'regress_rls_carol';
|
||||
ERROR: new row violates row-level security policy for table "document"
|
||||
--
|
||||
-- MERGE
|
||||
--
|
||||
RESET SESSION AUTHORIZATION;
|
||||
DROP POLICY p3_with_all ON document;
|
||||
ALTER TABLE document ADD COLUMN dnotes text DEFAULT '';
|
||||
-- all documents are readable
|
||||
CREATE POLICY p1 ON document FOR SELECT USING (true);
|
||||
-- one may insert documents only authored by them
|
||||
CREATE POLICY p2 ON document FOR INSERT WITH CHECK (dauthor = current_user);
|
||||
-- one may only update documents in 'novel' category
|
||||
CREATE POLICY p3 ON document FOR UPDATE
|
||||
USING (cid = (SELECT cid from category WHERE cname = 'novel'))
|
||||
WITH CHECK (dauthor = current_user);
|
||||
-- one may only delete documents in 'manga' category
|
||||
CREATE POLICY p4 ON document FOR DELETE
|
||||
USING (cid = (SELECT cid from category WHERE cname = 'manga'));
|
||||
SELECT * FROM document;
|
||||
did | cid | dlevel | dauthor | dtitle | dnotes
|
||||
-----+-----+--------+-------------------+----------------------------------+--------
|
||||
1 | 11 | 1 | regress_rls_bob | my first novel |
|
||||
3 | 22 | 2 | regress_rls_bob | my science fiction |
|
||||
4 | 44 | 1 | regress_rls_bob | my first manga |
|
||||
5 | 44 | 2 | regress_rls_bob | my second manga |
|
||||
6 | 22 | 1 | regress_rls_carol | great science fiction |
|
||||
7 | 33 | 2 | regress_rls_carol | great technology book |
|
||||
8 | 44 | 1 | regress_rls_carol | great manga |
|
||||
9 | 22 | 1 | regress_rls_dave | awesome science fiction |
|
||||
10 | 33 | 2 | regress_rls_dave | awesome technology book |
|
||||
11 | 33 | 1 | regress_rls_carol | hoge |
|
||||
33 | 22 | 1 | regress_rls_bob | okay science fiction |
|
||||
2 | 11 | 2 | regress_rls_bob | my first novel |
|
||||
78 | 33 | 1 | regress_rls_bob | some technology novel |
|
||||
79 | 33 | 1 | regress_rls_bob | technology book, can only insert |
|
||||
(14 rows)
|
||||
|
||||
SET SESSION AUTHORIZATION regress_rls_bob;
|
||||
-- Fails, since update violates WITH CHECK qual on dauthor
|
||||
MERGE INTO document d
|
||||
USING (SELECT 1 as sdid) s
|
||||
ON did = s.sdid
|
||||
WHEN MATCHED THEN
|
||||
UPDATE SET dnotes = dnotes || ' notes added by merge1 ', dauthor = 'regress_rls_alice';
|
||||
ERROR: new row violates row-level security policy for table "document"
|
||||
-- Should be OK since USING and WITH CHECK quals pass
|
||||
MERGE INTO document d
|
||||
USING (SELECT 1 as sdid) s
|
||||
ON did = s.sdid
|
||||
WHEN MATCHED THEN
|
||||
UPDATE SET dnotes = dnotes || ' notes added by merge2 ';
|
||||
-- Even when dauthor is updated explicitly, but to the existing value
|
||||
MERGE INTO document d
|
||||
USING (SELECT 1 as sdid) s
|
||||
ON did = s.sdid
|
||||
WHEN MATCHED THEN
|
||||
UPDATE SET dnotes = dnotes || ' notes added by merge3 ', dauthor = 'regress_rls_bob';
|
||||
-- There is a MATCH for did = 3, but UPDATE's USING qual does not allow
|
||||
-- updating an item in category 'science fiction'
|
||||
MERGE INTO document d
|
||||
USING (SELECT 3 as sdid) s
|
||||
ON did = s.sdid
|
||||
WHEN MATCHED THEN
|
||||
UPDATE SET dnotes = dnotes || ' notes added by merge ';
|
||||
ERROR: target row violates row-level security policy (USING expression) for table "document"
|
||||
-- The same thing with DELETE action, but fails again because no permissions
|
||||
-- to delete items in 'science fiction' category that did 3 belongs to.
|
||||
MERGE INTO document d
|
||||
USING (SELECT 3 as sdid) s
|
||||
ON did = s.sdid
|
||||
WHEN MATCHED THEN
|
||||
DELETE;
|
||||
ERROR: target row violates row-level security policy (USING expression) for table "document"
|
||||
-- Document with did 4 belongs to 'manga' category which is allowed for
|
||||
-- deletion. But this fails because the UPDATE action is matched first and
|
||||
-- UPDATE policy does not allow updation in the category.
|
||||
MERGE INTO document d
|
||||
USING (SELECT 4 as sdid) s
|
||||
ON did = s.sdid
|
||||
WHEN MATCHED AND dnotes = '' THEN
|
||||
UPDATE SET dnotes = dnotes || ' notes added by merge '
|
||||
WHEN MATCHED THEN
|
||||
DELETE;
|
||||
ERROR: target row violates row-level security policy (USING expression) for table "document"
|
||||
-- UPDATE action is not matched this time because of the WHEN AND qual.
|
||||
-- DELETE still fails because role regress_rls_bob does not have SELECT
|
||||
-- privileges on 'manga' category row in the category table.
|
||||
MERGE INTO document d
|
||||
USING (SELECT 4 as sdid) s
|
||||
ON did = s.sdid
|
||||
WHEN MATCHED AND dnotes <> '' THEN
|
||||
UPDATE SET dnotes = dnotes || ' notes added by merge '
|
||||
WHEN MATCHED THEN
|
||||
DELETE;
|
||||
ERROR: target row violates row-level security policy (USING expression) for table "document"
|
||||
SELECT * FROM document WHERE did = 4;
|
||||
did | cid | dlevel | dauthor | dtitle | dnotes
|
||||
-----+-----+--------+-----------------+----------------+--------
|
||||
4 | 44 | 1 | regress_rls_bob | my first manga |
|
||||
(1 row)
|
||||
|
||||
-- Switch to regress_rls_carol role and try the DELETE again. It should succeed
|
||||
-- this time
|
||||
RESET SESSION AUTHORIZATION;
|
||||
SET SESSION AUTHORIZATION regress_rls_carol;
|
||||
MERGE INTO document d
|
||||
USING (SELECT 4 as sdid) s
|
||||
ON did = s.sdid
|
||||
WHEN MATCHED AND dnotes <> '' THEN
|
||||
UPDATE SET dnotes = dnotes || ' notes added by merge '
|
||||
WHEN MATCHED THEN
|
||||
DELETE;
|
||||
-- Switch back to regress_rls_bob role
|
||||
RESET SESSION AUTHORIZATION;
|
||||
SET SESSION AUTHORIZATION regress_rls_bob;
|
||||
-- Try INSERT action. This fails because we are trying to insert
|
||||
-- dauthor = regress_rls_dave and INSERT's WITH CHECK does not allow
|
||||
-- that
|
||||
MERGE INTO document d
|
||||
USING (SELECT 12 as sdid) s
|
||||
ON did = s.sdid
|
||||
WHEN MATCHED THEN
|
||||
DELETE
|
||||
WHEN NOT MATCHED THEN
|
||||
INSERT VALUES (12, 11, 1, 'regress_rls_dave', 'another novel');
|
||||
ERROR: new row violates row-level security policy for table "document"
|
||||
-- This should be fine
|
||||
MERGE INTO document d
|
||||
USING (SELECT 12 as sdid) s
|
||||
ON did = s.sdid
|
||||
WHEN MATCHED THEN
|
||||
DELETE
|
||||
WHEN NOT MATCHED THEN
|
||||
INSERT VALUES (12, 11, 1, 'regress_rls_bob', 'another novel');
|
||||
-- ok
|
||||
MERGE INTO document d
|
||||
USING (SELECT 1 as sdid) s
|
||||
ON did = s.sdid
|
||||
WHEN MATCHED THEN
|
||||
UPDATE SET dnotes = dnotes || ' notes added by merge4 '
|
||||
WHEN NOT MATCHED THEN
|
||||
INSERT VALUES (12, 11, 1, 'regress_rls_bob', 'another novel');
|
||||
-- drop and create a new SELECT policy which prevents us from reading
|
||||
-- any document except with category 'magna'
|
||||
RESET SESSION AUTHORIZATION;
|
||||
DROP POLICY p1 ON document;
|
||||
CREATE POLICY p1 ON document FOR SELECT
|
||||
USING (cid = (SELECT cid from category WHERE cname = 'manga'));
|
||||
SET SESSION AUTHORIZATION regress_rls_bob;
|
||||
-- MERGE can no longer see the matching row and hence attempts the
|
||||
-- NOT MATCHED action, which results in unique key violation
|
||||
MERGE INTO document d
|
||||
USING (SELECT 1 as sdid) s
|
||||
ON did = s.sdid
|
||||
WHEN MATCHED THEN
|
||||
UPDATE SET dnotes = dnotes || ' notes added by merge5 '
|
||||
WHEN NOT MATCHED THEN
|
||||
INSERT VALUES (12, 11, 1, 'regress_rls_bob', 'another novel');
|
||||
ERROR: duplicate key value violates unique constraint "document_pkey"
|
||||
RESET SESSION AUTHORIZATION;
|
||||
-- drop the restrictive SELECT policy so that we can look at the
|
||||
-- final state of the table
|
||||
DROP POLICY p1 ON document;
|
||||
-- Just check everything went per plan
|
||||
SELECT * FROM document;
|
||||
did | cid | dlevel | dauthor | dtitle | dnotes
|
||||
-----+-----+--------+-------------------+----------------------------------+-----------------------------------------------------------------------
|
||||
3 | 22 | 2 | regress_rls_bob | my science fiction |
|
||||
5 | 44 | 2 | regress_rls_bob | my second manga |
|
||||
6 | 22 | 1 | regress_rls_carol | great science fiction |
|
||||
7 | 33 | 2 | regress_rls_carol | great technology book |
|
||||
8 | 44 | 1 | regress_rls_carol | great manga |
|
||||
9 | 22 | 1 | regress_rls_dave | awesome science fiction |
|
||||
10 | 33 | 2 | regress_rls_dave | awesome technology book |
|
||||
11 | 33 | 1 | regress_rls_carol | hoge |
|
||||
33 | 22 | 1 | regress_rls_bob | okay science fiction |
|
||||
2 | 11 | 2 | regress_rls_bob | my first novel |
|
||||
78 | 33 | 1 | regress_rls_bob | some technology novel |
|
||||
79 | 33 | 1 | regress_rls_bob | technology book, can only insert |
|
||||
12 | 11 | 1 | regress_rls_bob | another novel |
|
||||
1 | 11 | 1 | regress_rls_bob | my first novel | notes added by merge2 notes added by merge3 notes added by merge4
|
||||
(14 rows)
|
||||
|
||||
--
|
||||
-- ROLE/GROUP
|
||||
--
|
||||
|
@ -3265,37 +3265,6 @@ CREATE RULE rules_parted_table_insert AS ON INSERT to rules_parted_table
|
||||
ALTER RULE rules_parted_table_insert ON rules_parted_table RENAME TO rules_parted_table_insert_redirect;
|
||||
DROP TABLE rules_parted_table;
|
||||
--
|
||||
-- test MERGE
|
||||
--
|
||||
CREATE TABLE rule_merge1 (a int, b text);
|
||||
CREATE TABLE rule_merge2 (a int, b text);
|
||||
CREATE RULE rule1 AS ON INSERT TO rule_merge1
|
||||
DO INSTEAD INSERT INTO rule_merge2 VALUES (NEW.*);
|
||||
CREATE RULE rule2 AS ON UPDATE TO rule_merge1
|
||||
DO INSTEAD UPDATE rule_merge2 SET a = NEW.a, b = NEW.b
|
||||
WHERE a = OLD.a;
|
||||
CREATE RULE rule3 AS ON DELETE TO rule_merge1
|
||||
DO INSTEAD DELETE FROM rule_merge2 WHERE a = OLD.a;
|
||||
-- MERGE not supported for table with rules
|
||||
MERGE INTO rule_merge1 t USING (SELECT 1 AS a) s
|
||||
ON t.a = s.a
|
||||
WHEN MATCHED AND t.a < 2 THEN
|
||||
UPDATE SET b = b || ' updated by merge'
|
||||
WHEN MATCHED AND t.a > 2 THEN
|
||||
DELETE
|
||||
WHEN NOT MATCHED THEN
|
||||
INSERT VALUES (s.a, '');
|
||||
ERROR: MERGE is not supported for relations with rules
|
||||
-- should be ok with the other table though
|
||||
MERGE INTO rule_merge2 t USING (SELECT 1 AS a) s
|
||||
ON t.a = s.a
|
||||
WHEN MATCHED AND t.a < 2 THEN
|
||||
UPDATE SET b = b || ' updated by merge'
|
||||
WHEN MATCHED AND t.a > 2 THEN
|
||||
DELETE
|
||||
WHEN NOT MATCHED THEN
|
||||
INSERT VALUES (s.a, '');
|
||||
--
|
||||
-- Test enabling/disabling
|
||||
--
|
||||
CREATE TABLE ruletest1 (a int);
|
||||
|
@ -2784,54 +2784,6 @@ delete from self_ref where a = 1;
|
||||
NOTICE: trigger_func(self_ref) called: action = DELETE, when = BEFORE, level = STATEMENT
|
||||
NOTICE: trigger = self_ref_s_trig, old table = (1,), (2,1), (3,2), (4,3)
|
||||
drop table self_ref;
|
||||
--
|
||||
-- test transition tables with MERGE
|
||||
--
|
||||
create table merge_target_table (a int primary key, b text);
|
||||
create trigger merge_target_table_insert_trig
|
||||
after insert on merge_target_table referencing new table as new_table
|
||||
for each statement execute procedure dump_insert();
|
||||
create trigger merge_target_table_update_trig
|
||||
after update on merge_target_table referencing old table as old_table new table as new_table
|
||||
for each statement execute procedure dump_update();
|
||||
create trigger merge_target_table_delete_trig
|
||||
after delete on merge_target_table referencing old table as old_table
|
||||
for each statement execute procedure dump_delete();
|
||||
create table merge_source_table (a int, b text);
|
||||
insert into merge_source_table
|
||||
values (1, 'initial1'), (2, 'initial2'),
|
||||
(3, 'initial3'), (4, 'initial4');
|
||||
merge into merge_target_table t
|
||||
using merge_source_table s
|
||||
on t.a = s.a
|
||||
when not matched then
|
||||
insert values (a, b);
|
||||
NOTICE: trigger = merge_target_table_insert_trig, new table = (1,initial1), (2,initial2), (3,initial3), (4,initial4)
|
||||
merge into merge_target_table t
|
||||
using merge_source_table s
|
||||
on t.a = s.a
|
||||
when matched and s.a <= 2 then
|
||||
update set b = t.b || ' updated by merge'
|
||||
when matched and s.a > 2 then
|
||||
delete
|
||||
when not matched then
|
||||
insert values (a, b);
|
||||
NOTICE: trigger = merge_target_table_delete_trig, old table = (3,initial3), (4,initial4)
|
||||
NOTICE: trigger = merge_target_table_update_trig, old table = (1,initial1), (2,initial2), new table = (1,"initial1 updated by merge"), (2,"initial2 updated by merge")
|
||||
NOTICE: trigger = merge_target_table_insert_trig, new table = <NULL>
|
||||
merge into merge_target_table t
|
||||
using merge_source_table s
|
||||
on t.a = s.a
|
||||
when matched and s.a <= 2 then
|
||||
update set b = t.b || ' updated again by merge'
|
||||
when matched and s.a > 2 then
|
||||
delete
|
||||
when not matched then
|
||||
insert values (a, b);
|
||||
NOTICE: trigger = merge_target_table_delete_trig, old table = <NULL>
|
||||
NOTICE: trigger = merge_target_table_update_trig, old table = (1,"initial1 updated by merge"), (2,"initial2 updated by merge"), new table = (1,"initial1 updated by merge updated again by merge"), (2,"initial2 updated by merge updated again by merge")
|
||||
NOTICE: trigger = merge_target_table_insert_trig, new table = (3,initial3), (4,initial4)
|
||||
drop table merge_source_table, merge_target_table;
|
||||
-- cleanup
|
||||
drop function dump_insert();
|
||||
drop function dump_update();
|
||||
|
@ -1904,143 +1904,6 @@ RETURNING k, v;
|
||||
(0 rows)
|
||||
|
||||
DROP TABLE withz;
|
||||
-- WITH referenced by MERGE statement
|
||||
CREATE TABLE m AS SELECT i AS k, (i || ' v')::text v FROM generate_series(1, 16, 3) i;
|
||||
ALTER TABLE m ADD UNIQUE (k);
|
||||
WITH RECURSIVE cte_basic AS (SELECT 1 a, 'cte_basic val' b)
|
||||
MERGE INTO m USING (select 0 k, 'merge source SubPlan' v) o ON m.k=o.k
|
||||
WHEN MATCHED THEN UPDATE SET v = (SELECT b || ' merge update' FROM cte_basic WHERE cte_basic.a = m.k LIMIT 1)
|
||||
WHEN NOT MATCHED THEN INSERT VALUES(o.k, o.v);
|
||||
ERROR: WITH RECURSIVE is not supported for MERGE statement
|
||||
-- Basic:
|
||||
WITH cte_basic AS (SELECT 1 a, 'cte_basic val' b)
|
||||
MERGE INTO m USING (select 0 k, 'merge source SubPlan' v) o ON m.k=o.k
|
||||
WHEN MATCHED THEN UPDATE SET v = (SELECT b || ' merge update' FROM cte_basic WHERE cte_basic.a = m.k LIMIT 1)
|
||||
WHEN NOT MATCHED THEN INSERT VALUES(o.k, o.v);
|
||||
-- Examine
|
||||
SELECT * FROM m where k = 0;
|
||||
k | v
|
||||
---+----------------------
|
||||
0 | merge source SubPlan
|
||||
(1 row)
|
||||
|
||||
-- See EXPLAIN output for same query:
|
||||
EXPLAIN (VERBOSE, COSTS OFF)
|
||||
WITH cte_basic AS (SELECT 1 a, 'cte_basic val' b)
|
||||
MERGE INTO m USING (select 0 k, 'merge source SubPlan' v) o ON m.k=o.k
|
||||
WHEN MATCHED THEN UPDATE SET v = (SELECT b || ' merge update' FROM cte_basic WHERE cte_basic.a = m.k LIMIT 1)
|
||||
WHEN NOT MATCHED THEN INSERT VALUES(o.k, o.v);
|
||||
QUERY PLAN
|
||||
-------------------------------------------------------------------
|
||||
Merge on public.m
|
||||
CTE cte_basic
|
||||
-> Result
|
||||
Output: 1, 'cte_basic val'::text
|
||||
-> Hash Right Join
|
||||
Output: o.k, o.v, o.*, m_1.ctid
|
||||
Hash Cond: (m_1.k = o.k)
|
||||
-> Seq Scan on public.m m_1
|
||||
Output: m_1.ctid, m_1.k
|
||||
-> Hash
|
||||
Output: o.k, o.v, o.*
|
||||
-> Subquery Scan on o
|
||||
Output: o.k, o.v, o.*
|
||||
-> Result
|
||||
Output: 0, 'merge source SubPlan'::text
|
||||
SubPlan 2
|
||||
-> Limit
|
||||
Output: ((cte_basic.b || ' merge update'::text))
|
||||
-> CTE Scan on cte_basic
|
||||
Output: (cte_basic.b || ' merge update'::text)
|
||||
Filter: (cte_basic.a = m.k)
|
||||
(21 rows)
|
||||
|
||||
-- InitPlan
|
||||
WITH cte_init AS (SELECT 1 a, 'cte_init val' b)
|
||||
MERGE INTO m USING (select 1 k, 'merge source InitPlan' v) o ON m.k=o.k
|
||||
WHEN MATCHED THEN UPDATE SET v = (SELECT b || ' merge update' FROM cte_init WHERE a = 1 LIMIT 1)
|
||||
WHEN NOT MATCHED THEN INSERT VALUES(o.k, o.v);
|
||||
-- Examine
|
||||
SELECT * FROM m where k = 1;
|
||||
k | v
|
||||
---+---------------------------
|
||||
1 | cte_init val merge update
|
||||
(1 row)
|
||||
|
||||
-- See EXPLAIN output for same query:
|
||||
EXPLAIN (VERBOSE, COSTS OFF)
|
||||
WITH cte_init AS (SELECT 1 a, 'cte_init val' b)
|
||||
MERGE INTO m USING (select 1 k, 'merge source InitPlan' v) o ON m.k=o.k
|
||||
WHEN MATCHED THEN UPDATE SET v = (SELECT b || ' merge update' FROM cte_init WHERE a = 1 LIMIT 1)
|
||||
WHEN NOT MATCHED THEN INSERT VALUES(o.k, o.v);
|
||||
QUERY PLAN
|
||||
--------------------------------------------------------------------
|
||||
Merge on public.m
|
||||
CTE cte_init
|
||||
-> Result
|
||||
Output: 1, 'cte_init val'::text
|
||||
InitPlan 2 (returns $1)
|
||||
-> Limit
|
||||
Output: ((cte_init.b || ' merge update'::text))
|
||||
-> CTE Scan on cte_init
|
||||
Output: (cte_init.b || ' merge update'::text)
|
||||
Filter: (cte_init.a = 1)
|
||||
-> Hash Right Join
|
||||
Output: o.k, o.v, o.*, m_1.ctid
|
||||
Hash Cond: (m_1.k = o.k)
|
||||
-> Seq Scan on public.m m_1
|
||||
Output: m_1.ctid, m_1.k
|
||||
-> Hash
|
||||
Output: o.k, o.v, o.*
|
||||
-> Subquery Scan on o
|
||||
Output: o.k, o.v, o.*
|
||||
-> Result
|
||||
Output: 1, 'merge source InitPlan'::text
|
||||
(21 rows)
|
||||
|
||||
-- MERGE source comes from CTE:
|
||||
WITH merge_source_cte AS (SELECT 15 a, 'merge_source_cte val' b)
|
||||
MERGE INTO m USING (select * from merge_source_cte) o ON m.k=o.a
|
||||
WHEN MATCHED THEN UPDATE SET v = (SELECT b || merge_source_cte.*::text || ' merge update' FROM merge_source_cte WHERE a = 15)
|
||||
WHEN NOT MATCHED THEN INSERT VALUES(o.a, o.b || (SELECT merge_source_cte.*::text || ' merge insert' FROM merge_source_cte));
|
||||
-- Examine
|
||||
SELECT * FROM m where k = 15;
|
||||
k | v
|
||||
----+--------------------------------------------------------------
|
||||
15 | merge_source_cte val(15,"merge_source_cte val") merge insert
|
||||
(1 row)
|
||||
|
||||
-- See EXPLAIN output for same query:
|
||||
EXPLAIN (VERBOSE, COSTS OFF)
|
||||
WITH merge_source_cte AS (SELECT 15 a, 'merge_source_cte val' b)
|
||||
MERGE INTO m USING (select * from merge_source_cte) o ON m.k=o.a
|
||||
WHEN MATCHED THEN UPDATE SET v = (SELECT b || merge_source_cte.*::text || ' merge update' FROM merge_source_cte WHERE a = 15)
|
||||
WHEN NOT MATCHED THEN INSERT VALUES(o.a, o.b || (SELECT merge_source_cte.*::text || ' merge insert' FROM merge_source_cte));
|
||||
QUERY PLAN
|
||||
---------------------------------------------------------------------------------------------------------------
|
||||
Merge on public.m
|
||||
CTE merge_source_cte
|
||||
-> Result
|
||||
Output: 15, 'merge_source_cte val'::text
|
||||
InitPlan 2 (returns $1)
|
||||
-> CTE Scan on merge_source_cte merge_source_cte_1
|
||||
Output: ((merge_source_cte_1.b || (merge_source_cte_1.*)::text) || ' merge update'::text)
|
||||
Filter: (merge_source_cte_1.a = 15)
|
||||
InitPlan 3 (returns $2)
|
||||
-> CTE Scan on merge_source_cte merge_source_cte_2
|
||||
Output: ((merge_source_cte_2.*)::text || ' merge insert'::text)
|
||||
-> Hash Right Join
|
||||
Output: merge_source_cte.a, merge_source_cte.b, ROW(merge_source_cte.a, merge_source_cte.b), m_1.ctid
|
||||
Hash Cond: (m_1.k = merge_source_cte.a)
|
||||
-> Seq Scan on public.m m_1
|
||||
Output: m_1.ctid, m_1.k
|
||||
-> Hash
|
||||
Output: merge_source_cte.a, merge_source_cte.b
|
||||
-> CTE Scan on merge_source_cte
|
||||
Output: merge_source_cte.a, merge_source_cte.b
|
||||
(20 rows)
|
||||
|
||||
DROP TABLE m;
|
||||
-- check that run to completion happens in proper ordering
|
||||
TRUNCATE TABLE y;
|
||||
INSERT INTO y SELECT generate_series(1, 3);
|
||||
|
@ -84,7 +84,7 @@ test: select_into select_distinct select_distinct_on select_implicit select_havi
|
||||
# ----------
|
||||
# Another group of parallel tests
|
||||
# ----------
|
||||
test: brin gin gist spgist privileges init_privs security_label collate matview lock replica_identity rowsecurity object_address tablesample groupingsets drop_operator password func_index merge
|
||||
test: brin gin gist spgist privileges init_privs security_label collate matview lock replica_identity rowsecurity object_address tablesample groupingsets drop_operator password func_index
|
||||
|
||||
# ----------
|
||||
# Another group of parallel tests
|
||||
|
@ -124,7 +124,6 @@ test: tablesample
|
||||
test: groupingsets
|
||||
test: drop_operator
|
||||
test: password
|
||||
test: merge
|
||||
test: alter_generic
|
||||
test: alter_operator
|
||||
test: misc
|
||||
|
@ -246,48 +246,3 @@ CREATE TABLE itest_child PARTITION OF itest_parent (
|
||||
f3 WITH OPTIONS GENERATED ALWAYS AS IDENTITY
|
||||
) FOR VALUES FROM ('2016-07-01') TO ('2016-08-01'); -- error
|
||||
DROP TABLE itest_parent;
|
||||
|
||||
-- MERGE tests
|
||||
CREATE TABLE itest14 (a int GENERATED ALWAYS AS IDENTITY, b text);
|
||||
CREATE TABLE itest15 (a int GENERATED BY DEFAULT AS IDENTITY, b text);
|
||||
|
||||
MERGE INTO itest14 t
|
||||
USING (SELECT 10 AS s_a, 'inserted by merge' AS s_b) s
|
||||
ON t.a = s.s_a
|
||||
WHEN NOT MATCHED THEN
|
||||
INSERT (a, b) VALUES (s.s_a, s.s_b);
|
||||
|
||||
MERGE INTO itest14 t
|
||||
USING (SELECT 20 AS s_a, 'inserted by merge' AS s_b) s
|
||||
ON t.a = s.s_a
|
||||
WHEN NOT MATCHED THEN
|
||||
INSERT (a, b) OVERRIDING USER VALUE VALUES (s.s_a, s.s_b);
|
||||
|
||||
MERGE INTO itest14 t
|
||||
USING (SELECT 30 AS s_a, 'inserted by merge' AS s_b) s
|
||||
ON t.a = s.s_a
|
||||
WHEN NOT MATCHED THEN
|
||||
INSERT (a, b) OVERRIDING SYSTEM VALUE VALUES (s.s_a, s.s_b);
|
||||
|
||||
MERGE INTO itest15 t
|
||||
USING (SELECT 10 AS s_a, 'inserted by merge' AS s_b) s
|
||||
ON t.a = s.s_a
|
||||
WHEN NOT MATCHED THEN
|
||||
INSERT (a, b) VALUES (s.s_a, s.s_b);
|
||||
|
||||
MERGE INTO itest15 t
|
||||
USING (SELECT 20 AS s_a, 'inserted by merge' AS s_b) s
|
||||
ON t.a = s.s_a
|
||||
WHEN NOT MATCHED THEN
|
||||
INSERT (a, b) OVERRIDING USER VALUE VALUES (s.s_a, s.s_b);
|
||||
|
||||
MERGE INTO itest15 t
|
||||
USING (SELECT 30 AS s_a, 'inserted by merge' AS s_b) s
|
||||
ON t.a = s.s_a
|
||||
WHEN NOT MATCHED THEN
|
||||
INSERT (a, b) OVERRIDING SYSTEM VALUE VALUES (s.s_a, s.s_b);
|
||||
|
||||
SELECT * FROM itest14;
|
||||
SELECT * FROM itest15;
|
||||
DROP TABLE itest14;
|
||||
DROP TABLE itest15;
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -349,114 +349,6 @@ UPDATE atest5 SET one = 1; -- fail
|
||||
SELECT atest6 FROM atest6; -- ok
|
||||
COPY atest6 TO stdout; -- ok
|
||||
|
||||
-- test column privileges with MERGE
|
||||
SET SESSION AUTHORIZATION regress_priv_user1;
|
||||
CREATE TABLE mtarget (a int, b text);
|
||||
CREATE TABLE msource (a int, b text);
|
||||
INSERT INTO mtarget VALUES (1, 'init1'), (2, 'init2');
|
||||
INSERT INTO msource VALUES (1, 'source1'), (2, 'source2'), (3, 'source3');
|
||||
|
||||
GRANT SELECT (a) ON msource TO regress_priv_user4;
|
||||
GRANT SELECT (a) ON mtarget TO regress_priv_user4;
|
||||
GRANT INSERT (a,b) ON mtarget TO regress_priv_user4;
|
||||
GRANT UPDATE (b) ON mtarget TO regress_priv_user4;
|
||||
|
||||
SET SESSION AUTHORIZATION regress_priv_user4;
|
||||
|
||||
--
|
||||
-- test source privileges
|
||||
--
|
||||
|
||||
-- fail (no SELECT priv on s.b)
|
||||
MERGE INTO mtarget t USING msource s ON t.a = s.a
|
||||
WHEN MATCHED THEN
|
||||
UPDATE SET b = s.b
|
||||
WHEN NOT MATCHED THEN
|
||||
INSERT VALUES (a, NULL);
|
||||
|
||||
-- fail (s.b used in the INSERTed values)
|
||||
MERGE INTO mtarget t USING msource s ON t.a = s.a
|
||||
WHEN MATCHED THEN
|
||||
UPDATE SET b = 'x'
|
||||
WHEN NOT MATCHED THEN
|
||||
INSERT VALUES (a, b);
|
||||
|
||||
-- fail (s.b used in the WHEN quals)
|
||||
MERGE INTO mtarget t USING msource s ON t.a = s.a
|
||||
WHEN MATCHED AND s.b = 'x' THEN
|
||||
UPDATE SET b = 'x'
|
||||
WHEN NOT MATCHED THEN
|
||||
INSERT VALUES (a, NULL);
|
||||
|
||||
-- this should be ok since only s.a is accessed
|
||||
BEGIN;
|
||||
MERGE INTO mtarget t USING msource s ON t.a = s.a
|
||||
WHEN MATCHED THEN
|
||||
UPDATE SET b = 'ok'
|
||||
WHEN NOT MATCHED THEN
|
||||
INSERT VALUES (a, NULL);
|
||||
ROLLBACK;
|
||||
|
||||
SET SESSION AUTHORIZATION regress_priv_user1;
|
||||
GRANT SELECT (b) ON msource TO regress_priv_user4;
|
||||
SET SESSION AUTHORIZATION regress_priv_user4;
|
||||
|
||||
-- should now be ok
|
||||
BEGIN;
|
||||
MERGE INTO mtarget t USING msource s ON t.a = s.a
|
||||
WHEN MATCHED THEN
|
||||
UPDATE SET b = s.b
|
||||
WHEN NOT MATCHED THEN
|
||||
INSERT VALUES (a, b);
|
||||
ROLLBACK;
|
||||
|
||||
--
|
||||
-- test target privileges
|
||||
--
|
||||
|
||||
-- fail (no SELECT priv on t.b)
|
||||
MERGE INTO mtarget t USING msource s ON t.a = s.a
|
||||
WHEN MATCHED THEN
|
||||
UPDATE SET b = t.b
|
||||
WHEN NOT MATCHED THEN
|
||||
INSERT VALUES (a, NULL);
|
||||
|
||||
-- fail (no UPDATE on t.a)
|
||||
MERGE INTO mtarget t USING msource s ON t.a = s.a
|
||||
WHEN MATCHED THEN
|
||||
UPDATE SET b = s.b, a = t.a + 1
|
||||
WHEN NOT MATCHED THEN
|
||||
INSERT VALUES (a, b);
|
||||
|
||||
-- fail (no SELECT on t.b)
|
||||
MERGE INTO mtarget t USING msource s ON t.a = s.a
|
||||
WHEN MATCHED AND t.b IS NOT NULL THEN
|
||||
UPDATE SET b = s.b
|
||||
WHEN NOT MATCHED THEN
|
||||
INSERT VALUES (a, b);
|
||||
|
||||
-- ok
|
||||
BEGIN;
|
||||
MERGE INTO mtarget t USING msource s ON t.a = s.a
|
||||
WHEN MATCHED THEN
|
||||
UPDATE SET b = s.b;
|
||||
ROLLBACK;
|
||||
|
||||
-- fail (no DELETE)
|
||||
MERGE INTO mtarget t USING msource s ON t.a = s.a
|
||||
WHEN MATCHED AND t.b IS NOT NULL THEN
|
||||
DELETE;
|
||||
|
||||
-- grant delete privileges
|
||||
SET SESSION AUTHORIZATION regress_priv_user1;
|
||||
GRANT DELETE ON mtarget TO regress_priv_user4;
|
||||
-- should be ok now
|
||||
BEGIN;
|
||||
MERGE INTO mtarget t USING msource s ON t.a = s.a
|
||||
WHEN MATCHED AND t.b IS NOT NULL THEN
|
||||
DELETE;
|
||||
ROLLBACK;
|
||||
|
||||
-- check error reporting with column privs
|
||||
SET SESSION AUTHORIZATION regress_priv_user1;
|
||||
CREATE TABLE t1 (c1 int, c2 int, c3 int check (c3 < 5), primary key (c1, c2));
|
||||
|
@ -812,162 +812,6 @@ INSERT INTO document VALUES (4, (SELECT cid from category WHERE cname = 'novel')
|
||||
INSERT INTO document VALUES (1, (SELECT cid from category WHERE cname = 'novel'), 1, 'regress_rls_bob', 'my first novel')
|
||||
ON CONFLICT (did) DO UPDATE SET dauthor = 'regress_rls_carol';
|
||||
|
||||
--
|
||||
-- MERGE
|
||||
--
|
||||
RESET SESSION AUTHORIZATION;
|
||||
DROP POLICY p3_with_all ON document;
|
||||
|
||||
ALTER TABLE document ADD COLUMN dnotes text DEFAULT '';
|
||||
-- all documents are readable
|
||||
CREATE POLICY p1 ON document FOR SELECT USING (true);
|
||||
-- one may insert documents only authored by them
|
||||
CREATE POLICY p2 ON document FOR INSERT WITH CHECK (dauthor = current_user);
|
||||
-- one may only update documents in 'novel' category
|
||||
CREATE POLICY p3 ON document FOR UPDATE
|
||||
USING (cid = (SELECT cid from category WHERE cname = 'novel'))
|
||||
WITH CHECK (dauthor = current_user);
|
||||
-- one may only delete documents in 'manga' category
|
||||
CREATE POLICY p4 ON document FOR DELETE
|
||||
USING (cid = (SELECT cid from category WHERE cname = 'manga'));
|
||||
|
||||
SELECT * FROM document;
|
||||
|
||||
SET SESSION AUTHORIZATION regress_rls_bob;
|
||||
|
||||
-- Fails, since update violates WITH CHECK qual on dauthor
|
||||
MERGE INTO document d
|
||||
USING (SELECT 1 as sdid) s
|
||||
ON did = s.sdid
|
||||
WHEN MATCHED THEN
|
||||
UPDATE SET dnotes = dnotes || ' notes added by merge1 ', dauthor = 'regress_rls_alice';
|
||||
|
||||
-- Should be OK since USING and WITH CHECK quals pass
|
||||
MERGE INTO document d
|
||||
USING (SELECT 1 as sdid) s
|
||||
ON did = s.sdid
|
||||
WHEN MATCHED THEN
|
||||
UPDATE SET dnotes = dnotes || ' notes added by merge2 ';
|
||||
|
||||
-- Even when dauthor is updated explicitly, but to the existing value
|
||||
MERGE INTO document d
|
||||
USING (SELECT 1 as sdid) s
|
||||
ON did = s.sdid
|
||||
WHEN MATCHED THEN
|
||||
UPDATE SET dnotes = dnotes || ' notes added by merge3 ', dauthor = 'regress_rls_bob';
|
||||
|
||||
-- There is a MATCH for did = 3, but UPDATE's USING qual does not allow
|
||||
-- updating an item in category 'science fiction'
|
||||
MERGE INTO document d
|
||||
USING (SELECT 3 as sdid) s
|
||||
ON did = s.sdid
|
||||
WHEN MATCHED THEN
|
||||
UPDATE SET dnotes = dnotes || ' notes added by merge ';
|
||||
|
||||
-- The same thing with DELETE action, but fails again because no permissions
|
||||
-- to delete items in 'science fiction' category that did 3 belongs to.
|
||||
MERGE INTO document d
|
||||
USING (SELECT 3 as sdid) s
|
||||
ON did = s.sdid
|
||||
WHEN MATCHED THEN
|
||||
DELETE;
|
||||
|
||||
-- Document with did 4 belongs to 'manga' category which is allowed for
|
||||
-- deletion. But this fails because the UPDATE action is matched first and
|
||||
-- UPDATE policy does not allow updation in the category.
|
||||
MERGE INTO document d
|
||||
USING (SELECT 4 as sdid) s
|
||||
ON did = s.sdid
|
||||
WHEN MATCHED AND dnotes = '' THEN
|
||||
UPDATE SET dnotes = dnotes || ' notes added by merge '
|
||||
WHEN MATCHED THEN
|
||||
DELETE;
|
||||
|
||||
-- UPDATE action is not matched this time because of the WHEN AND qual.
|
||||
-- DELETE still fails because role regress_rls_bob does not have SELECT
|
||||
-- privileges on 'manga' category row in the category table.
|
||||
MERGE INTO document d
|
||||
USING (SELECT 4 as sdid) s
|
||||
ON did = s.sdid
|
||||
WHEN MATCHED AND dnotes <> '' THEN
|
||||
UPDATE SET dnotes = dnotes || ' notes added by merge '
|
||||
WHEN MATCHED THEN
|
||||
DELETE;
|
||||
|
||||
SELECT * FROM document WHERE did = 4;
|
||||
|
||||
-- Switch to regress_rls_carol role and try the DELETE again. It should succeed
|
||||
-- this time
|
||||
RESET SESSION AUTHORIZATION;
|
||||
SET SESSION AUTHORIZATION regress_rls_carol;
|
||||
|
||||
MERGE INTO document d
|
||||
USING (SELECT 4 as sdid) s
|
||||
ON did = s.sdid
|
||||
WHEN MATCHED AND dnotes <> '' THEN
|
||||
UPDATE SET dnotes = dnotes || ' notes added by merge '
|
||||
WHEN MATCHED THEN
|
||||
DELETE;
|
||||
|
||||
-- Switch back to regress_rls_bob role
|
||||
RESET SESSION AUTHORIZATION;
|
||||
SET SESSION AUTHORIZATION regress_rls_bob;
|
||||
|
||||
-- Try INSERT action. This fails because we are trying to insert
|
||||
-- dauthor = regress_rls_dave and INSERT's WITH CHECK does not allow
|
||||
-- that
|
||||
MERGE INTO document d
|
||||
USING (SELECT 12 as sdid) s
|
||||
ON did = s.sdid
|
||||
WHEN MATCHED THEN
|
||||
DELETE
|
||||
WHEN NOT MATCHED THEN
|
||||
INSERT VALUES (12, 11, 1, 'regress_rls_dave', 'another novel');
|
||||
|
||||
-- This should be fine
|
||||
MERGE INTO document d
|
||||
USING (SELECT 12 as sdid) s
|
||||
ON did = s.sdid
|
||||
WHEN MATCHED THEN
|
||||
DELETE
|
||||
WHEN NOT MATCHED THEN
|
||||
INSERT VALUES (12, 11, 1, 'regress_rls_bob', 'another novel');
|
||||
|
||||
-- ok
|
||||
MERGE INTO document d
|
||||
USING (SELECT 1 as sdid) s
|
||||
ON did = s.sdid
|
||||
WHEN MATCHED THEN
|
||||
UPDATE SET dnotes = dnotes || ' notes added by merge4 '
|
||||
WHEN NOT MATCHED THEN
|
||||
INSERT VALUES (12, 11, 1, 'regress_rls_bob', 'another novel');
|
||||
|
||||
-- drop and create a new SELECT policy which prevents us from reading
|
||||
-- any document except with category 'magna'
|
||||
RESET SESSION AUTHORIZATION;
|
||||
DROP POLICY p1 ON document;
|
||||
CREATE POLICY p1 ON document FOR SELECT
|
||||
USING (cid = (SELECT cid from category WHERE cname = 'manga'));
|
||||
|
||||
SET SESSION AUTHORIZATION regress_rls_bob;
|
||||
|
||||
-- MERGE can no longer see the matching row and hence attempts the
|
||||
-- NOT MATCHED action, which results in unique key violation
|
||||
MERGE INTO document d
|
||||
USING (SELECT 1 as sdid) s
|
||||
ON did = s.sdid
|
||||
WHEN MATCHED THEN
|
||||
UPDATE SET dnotes = dnotes || ' notes added by merge5 '
|
||||
WHEN NOT MATCHED THEN
|
||||
INSERT VALUES (12, 11, 1, 'regress_rls_bob', 'another novel');
|
||||
|
||||
RESET SESSION AUTHORIZATION;
|
||||
-- drop the restrictive SELECT policy so that we can look at the
|
||||
-- final state of the table
|
||||
DROP POLICY p1 ON document;
|
||||
-- Just check everything went per plan
|
||||
SELECT * FROM document;
|
||||
|
||||
--
|
||||
-- ROLE/GROUP
|
||||
--
|
||||
|
@ -1191,39 +1191,6 @@ CREATE RULE rules_parted_table_insert AS ON INSERT to rules_parted_table
|
||||
ALTER RULE rules_parted_table_insert ON rules_parted_table RENAME TO rules_parted_table_insert_redirect;
|
||||
DROP TABLE rules_parted_table;
|
||||
|
||||
--
|
||||
-- test MERGE
|
||||
--
|
||||
CREATE TABLE rule_merge1 (a int, b text);
|
||||
CREATE TABLE rule_merge2 (a int, b text);
|
||||
CREATE RULE rule1 AS ON INSERT TO rule_merge1
|
||||
DO INSTEAD INSERT INTO rule_merge2 VALUES (NEW.*);
|
||||
CREATE RULE rule2 AS ON UPDATE TO rule_merge1
|
||||
DO INSTEAD UPDATE rule_merge2 SET a = NEW.a, b = NEW.b
|
||||
WHERE a = OLD.a;
|
||||
CREATE RULE rule3 AS ON DELETE TO rule_merge1
|
||||
DO INSTEAD DELETE FROM rule_merge2 WHERE a = OLD.a;
|
||||
|
||||
-- MERGE not supported for table with rules
|
||||
MERGE INTO rule_merge1 t USING (SELECT 1 AS a) s
|
||||
ON t.a = s.a
|
||||
WHEN MATCHED AND t.a < 2 THEN
|
||||
UPDATE SET b = b || ' updated by merge'
|
||||
WHEN MATCHED AND t.a > 2 THEN
|
||||
DELETE
|
||||
WHEN NOT MATCHED THEN
|
||||
INSERT VALUES (s.a, '');
|
||||
|
||||
-- should be ok with the other table though
|
||||
MERGE INTO rule_merge2 t USING (SELECT 1 AS a) s
|
||||
ON t.a = s.a
|
||||
WHEN MATCHED AND t.a < 2 THEN
|
||||
UPDATE SET b = b || ' updated by merge'
|
||||
WHEN MATCHED AND t.a > 2 THEN
|
||||
DELETE
|
||||
WHEN NOT MATCHED THEN
|
||||
INSERT VALUES (s.a, '');
|
||||
|
||||
--
|
||||
-- Test enabling/disabling
|
||||
--
|
||||
|
@ -2125,53 +2125,6 @@ delete from self_ref where a = 1;
|
||||
|
||||
drop table self_ref;
|
||||
|
||||
--
|
||||
-- test transition tables with MERGE
|
||||
--
|
||||
create table merge_target_table (a int primary key, b text);
|
||||
create trigger merge_target_table_insert_trig
|
||||
after insert on merge_target_table referencing new table as new_table
|
||||
for each statement execute procedure dump_insert();
|
||||
create trigger merge_target_table_update_trig
|
||||
after update on merge_target_table referencing old table as old_table new table as new_table
|
||||
for each statement execute procedure dump_update();
|
||||
create trigger merge_target_table_delete_trig
|
||||
after delete on merge_target_table referencing old table as old_table
|
||||
for each statement execute procedure dump_delete();
|
||||
|
||||
create table merge_source_table (a int, b text);
|
||||
insert into merge_source_table
|
||||
values (1, 'initial1'), (2, 'initial2'),
|
||||
(3, 'initial3'), (4, 'initial4');
|
||||
|
||||
merge into merge_target_table t
|
||||
using merge_source_table s
|
||||
on t.a = s.a
|
||||
when not matched then
|
||||
insert values (a, b);
|
||||
|
||||
merge into merge_target_table t
|
||||
using merge_source_table s
|
||||
on t.a = s.a
|
||||
when matched and s.a <= 2 then
|
||||
update set b = t.b || ' updated by merge'
|
||||
when matched and s.a > 2 then
|
||||
delete
|
||||
when not matched then
|
||||
insert values (a, b);
|
||||
|
||||
merge into merge_target_table t
|
||||
using merge_source_table s
|
||||
on t.a = s.a
|
||||
when matched and s.a <= 2 then
|
||||
update set b = t.b || ' updated again by merge'
|
||||
when matched and s.a > 2 then
|
||||
delete
|
||||
when not matched then
|
||||
insert values (a, b);
|
||||
|
||||
drop table merge_source_table, merge_target_table;
|
||||
|
||||
-- cleanup
|
||||
drop function dump_insert();
|
||||
drop function dump_update();
|
||||
|
@ -862,62 +862,6 @@ RETURNING k, v;
|
||||
|
||||
DROP TABLE withz;
|
||||
|
||||
-- WITH referenced by MERGE statement
|
||||
CREATE TABLE m AS SELECT i AS k, (i || ' v')::text v FROM generate_series(1, 16, 3) i;
|
||||
ALTER TABLE m ADD UNIQUE (k);
|
||||
|
||||
WITH RECURSIVE cte_basic AS (SELECT 1 a, 'cte_basic val' b)
|
||||
MERGE INTO m USING (select 0 k, 'merge source SubPlan' v) o ON m.k=o.k
|
||||
WHEN MATCHED THEN UPDATE SET v = (SELECT b || ' merge update' FROM cte_basic WHERE cte_basic.a = m.k LIMIT 1)
|
||||
WHEN NOT MATCHED THEN INSERT VALUES(o.k, o.v);
|
||||
|
||||
-- Basic:
|
||||
WITH cte_basic AS (SELECT 1 a, 'cte_basic val' b)
|
||||
MERGE INTO m USING (select 0 k, 'merge source SubPlan' v) o ON m.k=o.k
|
||||
WHEN MATCHED THEN UPDATE SET v = (SELECT b || ' merge update' FROM cte_basic WHERE cte_basic.a = m.k LIMIT 1)
|
||||
WHEN NOT MATCHED THEN INSERT VALUES(o.k, o.v);
|
||||
-- Examine
|
||||
SELECT * FROM m where k = 0;
|
||||
|
||||
-- See EXPLAIN output for same query:
|
||||
EXPLAIN (VERBOSE, COSTS OFF)
|
||||
WITH cte_basic AS (SELECT 1 a, 'cte_basic val' b)
|
||||
MERGE INTO m USING (select 0 k, 'merge source SubPlan' v) o ON m.k=o.k
|
||||
WHEN MATCHED THEN UPDATE SET v = (SELECT b || ' merge update' FROM cte_basic WHERE cte_basic.a = m.k LIMIT 1)
|
||||
WHEN NOT MATCHED THEN INSERT VALUES(o.k, o.v);
|
||||
|
||||
-- InitPlan
|
||||
WITH cte_init AS (SELECT 1 a, 'cte_init val' b)
|
||||
MERGE INTO m USING (select 1 k, 'merge source InitPlan' v) o ON m.k=o.k
|
||||
WHEN MATCHED THEN UPDATE SET v = (SELECT b || ' merge update' FROM cte_init WHERE a = 1 LIMIT 1)
|
||||
WHEN NOT MATCHED THEN INSERT VALUES(o.k, o.v);
|
||||
-- Examine
|
||||
SELECT * FROM m where k = 1;
|
||||
|
||||
-- See EXPLAIN output for same query:
|
||||
EXPLAIN (VERBOSE, COSTS OFF)
|
||||
WITH cte_init AS (SELECT 1 a, 'cte_init val' b)
|
||||
MERGE INTO m USING (select 1 k, 'merge source InitPlan' v) o ON m.k=o.k
|
||||
WHEN MATCHED THEN UPDATE SET v = (SELECT b || ' merge update' FROM cte_init WHERE a = 1 LIMIT 1)
|
||||
WHEN NOT MATCHED THEN INSERT VALUES(o.k, o.v);
|
||||
|
||||
-- MERGE source comes from CTE:
|
||||
WITH merge_source_cte AS (SELECT 15 a, 'merge_source_cte val' b)
|
||||
MERGE INTO m USING (select * from merge_source_cte) o ON m.k=o.a
|
||||
WHEN MATCHED THEN UPDATE SET v = (SELECT b || merge_source_cte.*::text || ' merge update' FROM merge_source_cte WHERE a = 15)
|
||||
WHEN NOT MATCHED THEN INSERT VALUES(o.a, o.b || (SELECT merge_source_cte.*::text || ' merge insert' FROM merge_source_cte));
|
||||
-- Examine
|
||||
SELECT * FROM m where k = 15;
|
||||
|
||||
-- See EXPLAIN output for same query:
|
||||
EXPLAIN (VERBOSE, COSTS OFF)
|
||||
WITH merge_source_cte AS (SELECT 15 a, 'merge_source_cte val' b)
|
||||
MERGE INTO m USING (select * from merge_source_cte) o ON m.k=o.a
|
||||
WHEN MATCHED THEN UPDATE SET v = (SELECT b || merge_source_cte.*::text || ' merge update' FROM merge_source_cte WHERE a = 15)
|
||||
WHEN NOT MATCHED THEN INSERT VALUES(o.a, o.b || (SELECT merge_source_cte.*::text || ' merge insert' FROM merge_source_cte));
|
||||
|
||||
DROP TABLE m;
|
||||
|
||||
-- check that run to completion happens in proper ordering
|
||||
|
||||
TRUNCATE TABLE y;
|
||||
|
Reference in New Issue
Block a user