1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-05 07:21:24 +03:00

Make RLS work with UPDATE ... WHERE CURRENT OF

UPDATE ... WHERE CURRENT OF would not work in conjunction with
RLS. Arrange to allow the CURRENT OF expression to be pushed down.
Issue noted by Peter Geoghegan. Patch by Dean Rasheed. Back patch
to 9.5 where RLS was introduced.
This commit is contained in:
Joe Conway
2015-07-24 12:55:30 -07:00
parent d9a356ff2e
commit b26e3d660d
4 changed files with 206 additions and 0 deletions

View File

@ -2729,6 +2729,113 @@ COPY copy_t FROM STDIN; --fail - permission denied.
ERROR: permission denied for relation copy_t
RESET SESSION AUTHORIZATION;
DROP TABLE copy_t;
-- Check WHERE CURRENT OF
SET SESSION AUTHORIZATION rls_regress_user0;
CREATE TABLE current_check (currentid int, payload text, rlsuser text);
GRANT ALL ON current_check TO PUBLIC;
INSERT INTO current_check VALUES
(1, 'abc', 'rls_regress_user1'),
(2, 'bcd', 'rls_regress_user1'),
(3, 'cde', 'rls_regress_user1'),
(4, 'def', 'rls_regress_user1');
CREATE POLICY p1 ON current_check FOR SELECT USING (currentid % 2 = 0);
CREATE POLICY p2 ON current_check FOR DELETE USING (currentid = 4 AND rlsuser = current_user);
CREATE POLICY p3 ON current_check FOR UPDATE USING (currentid = 4) WITH CHECK (rlsuser = current_user);
ALTER TABLE current_check ENABLE ROW LEVEL SECURITY;
SET SESSION AUTHORIZATION rls_regress_user1;
-- Can SELECT even rows
SELECT * FROM current_check;
currentid | payload | rlsuser
-----------+---------+-------------------
2 | bcd | rls_regress_user1
4 | def | rls_regress_user1
(2 rows)
-- Cannot UPDATE row 2
UPDATE current_check SET payload = payload || '_new' WHERE currentid = 2 RETURNING *;
currentid | payload | rlsuser
-----------+---------+---------
(0 rows)
BEGIN;
DECLARE current_check_cursor SCROLL CURSOR FOR SELECT * FROM current_check;
-- Returns rows that can be seen according to SELECT policy, like plain SELECT
-- above (even rows)
FETCH ABSOLUTE 1 FROM current_check_cursor;
currentid | payload | rlsuser
-----------+---------+-------------------
2 | bcd | rls_regress_user1
(1 row)
-- Still cannot UPDATE row 2 through cursor
UPDATE current_check SET payload = payload || '_new' WHERE CURRENT OF current_check_cursor RETURNING *;
currentid | payload | rlsuser
-----------+---------+---------
(0 rows)
-- Can update row 4 through cursor, which is the next visible row
FETCH RELATIVE 1 FROM current_check_cursor;
currentid | payload | rlsuser
-----------+---------+-------------------
4 | def | rls_regress_user1
(1 row)
UPDATE current_check SET payload = payload || '_new' WHERE CURRENT OF current_check_cursor RETURNING *;
currentid | payload | rlsuser
-----------+---------+-------------------
4 | def_new | rls_regress_user1
(1 row)
SELECT * FROM current_check;
currentid | payload | rlsuser
-----------+---------+-------------------
2 | bcd | rls_regress_user1
4 | def_new | rls_regress_user1
(2 rows)
-- Plan should be a subquery TID scan
EXPLAIN (COSTS OFF) UPDATE current_check SET payload = payload WHERE CURRENT OF current_check_cursor;
QUERY PLAN
---------------------------------------------------------------
Update on current_check current_check_1
-> Subquery Scan on current_check
-> LockRows
-> Tid Scan on current_check current_check_2
TID Cond: CURRENT OF current_check_cursor
Filter: (currentid = 4)
(6 rows)
-- Similarly can only delete row 4
FETCH ABSOLUTE 1 FROM current_check_cursor;
currentid | payload | rlsuser
-----------+---------+-------------------
2 | bcd | rls_regress_user1
(1 row)
DELETE FROM current_check WHERE CURRENT OF current_check_cursor RETURNING *;
currentid | payload | rlsuser
-----------+---------+---------
(0 rows)
FETCH RELATIVE 1 FROM current_check_cursor;
currentid | payload | rlsuser
-----------+---------+-------------------
4 | def | rls_regress_user1
(1 row)
DELETE FROM current_check WHERE CURRENT OF current_check_cursor RETURNING *;
currentid | payload | rlsuser
-----------+---------+-------------------
4 | def_new | rls_regress_user1
(1 row)
SELECT * FROM current_check;
currentid | payload | rlsuser
-----------+---------+-------------------
2 | bcd | rls_regress_user1
(1 row)
COMMIT;
--
-- Collation support
--

View File

@ -1087,6 +1087,55 @@ COPY copy_t FROM STDIN; --fail - permission denied.
RESET SESSION AUTHORIZATION;
DROP TABLE copy_t;
-- Check WHERE CURRENT OF
SET SESSION AUTHORIZATION rls_regress_user0;
CREATE TABLE current_check (currentid int, payload text, rlsuser text);
GRANT ALL ON current_check TO PUBLIC;
INSERT INTO current_check VALUES
(1, 'abc', 'rls_regress_user1'),
(2, 'bcd', 'rls_regress_user1'),
(3, 'cde', 'rls_regress_user1'),
(4, 'def', 'rls_regress_user1');
CREATE POLICY p1 ON current_check FOR SELECT USING (currentid % 2 = 0);
CREATE POLICY p2 ON current_check FOR DELETE USING (currentid = 4 AND rlsuser = current_user);
CREATE POLICY p3 ON current_check FOR UPDATE USING (currentid = 4) WITH CHECK (rlsuser = current_user);
ALTER TABLE current_check ENABLE ROW LEVEL SECURITY;
SET SESSION AUTHORIZATION rls_regress_user1;
-- Can SELECT even rows
SELECT * FROM current_check;
-- Cannot UPDATE row 2
UPDATE current_check SET payload = payload || '_new' WHERE currentid = 2 RETURNING *;
BEGIN;
DECLARE current_check_cursor SCROLL CURSOR FOR SELECT * FROM current_check;
-- Returns rows that can be seen according to SELECT policy, like plain SELECT
-- above (even rows)
FETCH ABSOLUTE 1 FROM current_check_cursor;
-- Still cannot UPDATE row 2 through cursor
UPDATE current_check SET payload = payload || '_new' WHERE CURRENT OF current_check_cursor RETURNING *;
-- Can update row 4 through cursor, which is the next visible row
FETCH RELATIVE 1 FROM current_check_cursor;
UPDATE current_check SET payload = payload || '_new' WHERE CURRENT OF current_check_cursor RETURNING *;
SELECT * FROM current_check;
-- Plan should be a subquery TID scan
EXPLAIN (COSTS OFF) UPDATE current_check SET payload = payload WHERE CURRENT OF current_check_cursor;
-- Similarly can only delete row 4
FETCH ABSOLUTE 1 FROM current_check_cursor;
DELETE FROM current_check WHERE CURRENT OF current_check_cursor RETURNING *;
FETCH RELATIVE 1 FROM current_check_cursor;
DELETE FROM current_check WHERE CURRENT OF current_check_cursor RETURNING *;
SELECT * FROM current_check;
COMMIT;
--
-- Collation support
--