diff --git a/src/backend/rewrite/rowsecurity.c b/src/backend/rewrite/rowsecurity.c index 2386cf0..562dbc9 100644 *** a/src/backend/rewrite/rowsecurity.c --- b/src/backend/rewrite/rowsecurity.c *************** get_row_security_policies(Query *root, C *** 147,154 **** return; } ! /* Grab the built-in policies which should be applied to this relation. */ rel = heap_open(rte->relid, NoLock); rowsec_policies = pull_row_security_policies(commandType, rel, user_id); --- 147,164 ---- return; } ! /* ! * RLS is enabled for this relation. ! * ! * Get the security policies that should be applied, based on the command ! * type. Note that if this isn't the target relation, we actually want ! * the relation's SELECT policies, regardless of the query command type, ! * for example in UPDATE t1 ... FROM t2 we need to apply t1's UPDATE ! * policies and t2's SELECT policies. ! */ rel = heap_open(rte->relid, NoLock); + if (rt_index != root->resultRelation) + commandType = CMD_SELECT; rowsec_policies = pull_row_security_policies(commandType, rel, user_id); diff --git a/src/test/regress/expected/rowsecurity.out b/src/test/regress/expected/rowsecurity.out index b0556c2..6fc80af 100644 *** a/src/test/regress/expected/rowsecurity.out --- b/src/test/regress/expected/rowsecurity.out *************** CREATE POLICY p ON t USING (max(c)); -- *** 3033,3038 **** --- 3033,3121 ---- ERROR: aggregate functions are not allowed in policy expressions ROLLBACK; -- + -- Non-target relations are only subject to SELECT policies + -- + SET SESSION AUTHORIZATION rls_regress_user0; + CREATE TABLE r1 (a int); + CREATE TABLE r2 (a int); + INSERT INTO r1 VALUES (10), (20); + INSERT INTO r2 VALUES (10), (20); + GRANT ALL ON r1, r2 TO rls_regress_user1; + CREATE POLICY p1 ON r1 USING (true); + ALTER TABLE r1 ENABLE ROW LEVEL SECURITY; + CREATE POLICY p1 ON r2 FOR SELECT USING (true); + CREATE POLICY p2 ON r2 FOR INSERT WITH CHECK (false); + CREATE POLICY p3 ON r2 FOR UPDATE USING (false); + CREATE POLICY p4 ON r2 FOR DELETE USING (false); + ALTER TABLE r2 ENABLE ROW LEVEL SECURITY; + SET SESSION AUTHORIZATION rls_regress_user1; + SELECT * FROM r1; + a + ---- + 10 + 20 + (2 rows) + + SELECT * FROM r2; + a + ---- + 10 + 20 + (2 rows) + + -- r2 is read-only + INSERT INTO r2 VALUES (2); -- Not allowed + ERROR: new row violates row level security policy for "r2" + UPDATE r2 SET a = 2 RETURNING *; -- Updates nothing + a + --- + (0 rows) + + DELETE FROM r2 RETURNING *; -- Deletes nothing + a + --- + (0 rows) + + -- r2 can be used as a non-target relation in DML + INSERT INTO r1 SELECT a + 1 FROM r2 RETURNING *; -- OK + a + ---- + 11 + 21 + (2 rows) + + UPDATE r1 SET a = r2.a + 2 FROM r2 WHERE r1.a = r2.a RETURNING *; -- OK + a | a + ----+---- + 12 | 10 + 22 | 20 + (2 rows) + + DELETE FROM r1 USING r2 WHERE r1.a = r2.a + 2 RETURNING *; -- OK + a | a + ----+---- + 12 | 10 + 22 | 20 + (2 rows) + + SELECT * FROM r1; + a + ---- + 11 + 21 + (2 rows) + + SELECT * FROM r2; + a + ---- + 10 + 20 + (2 rows) + + SET SESSION AUTHORIZATION rls_regress_user0; + DROP TABLE r1; + DROP TABLE r2; + -- -- Clean up objects -- RESET SESSION AUTHORIZATION; diff --git a/src/test/regress/sql/rowsecurity.sql b/src/test/regress/sql/rowsecurity.sql index 300f34a..e8c09e9 100644 *** a/src/test/regress/sql/rowsecurity.sql --- b/src/test/regress/sql/rowsecurity.sql *************** CREATE POLICY p ON t USING (max(c)); -- *** 1299,1304 **** --- 1299,1344 ---- ROLLBACK; -- + -- Non-target relations are only subject to SELECT policies + -- + SET SESSION AUTHORIZATION rls_regress_user0; + CREATE TABLE r1 (a int); + CREATE TABLE r2 (a int); + INSERT INTO r1 VALUES (10), (20); + INSERT INTO r2 VALUES (10), (20); + + GRANT ALL ON r1, r2 TO rls_regress_user1; + + CREATE POLICY p1 ON r1 USING (true); + ALTER TABLE r1 ENABLE ROW LEVEL SECURITY; + + CREATE POLICY p1 ON r2 FOR SELECT USING (true); + CREATE POLICY p2 ON r2 FOR INSERT WITH CHECK (false); + CREATE POLICY p3 ON r2 FOR UPDATE USING (false); + CREATE POLICY p4 ON r2 FOR DELETE USING (false); + ALTER TABLE r2 ENABLE ROW LEVEL SECURITY; + + SET SESSION AUTHORIZATION rls_regress_user1; + SELECT * FROM r1; + SELECT * FROM r2; + + -- r2 is read-only + INSERT INTO r2 VALUES (2); -- Not allowed + UPDATE r2 SET a = 2 RETURNING *; -- Updates nothing + DELETE FROM r2 RETURNING *; -- Deletes nothing + + -- r2 can be used as a non-target relation in DML + INSERT INTO r1 SELECT a + 1 FROM r2 RETURNING *; -- OK + UPDATE r1 SET a = r2.a + 2 FROM r2 WHERE r1.a = r2.a RETURNING *; -- OK + DELETE FROM r1 USING r2 WHERE r1.a = r2.a + 2 RETURNING *; -- OK + SELECT * FROM r1; + SELECT * FROM r2; + + SET SESSION AUTHORIZATION rls_regress_user0; + DROP TABLE r1; + DROP TABLE r2; + + -- -- Clean up objects -- RESET SESSION AUTHORIZATION;