mirror of
https://github.com/postgres/postgres.git
synced 2025-07-20 05:03:10 +03:00
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -2761,54 +2761,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();
|
||||
|
@ -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
|
||||
|
@ -123,7 +123,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;
|
||||
|
@ -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
|
||||
--
|
||||
|
@ -2110,53 +2110,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();
|
||||
|
Reference in New Issue
Block a user