WHERE f_leak(dtitle) ORDER BY did;
did | cid | dlevel | dauthor | dtitle
-----+-----+--------+-------------------+-------------------------
- 4 | 44 | 1 | regress_rls_bob | my first manga
6 | 22 | 1 | regress_rls_carol | great science fiction
8 | 44 | 1 | regress_rls_carol | great manga
9 | 22 | 1 | regress_rls_dave | awesome science fiction
-(4 rows)
+(3 rows)
-- viewpoint from regress_rls_carol
SET SESSION AUTHORIZATION regress_rls_carol;
WHERE f_leak(dtitle) ORDER BY did;
did | cid | dlevel | dauthor | dtitle
-----+-----+--------+-------------------+-------------------------
- 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
8 | 44 | 1 | regress_rls_carol | great manga
9 | 22 | 1 | regress_rls_dave | awesome science fiction
-(5 rows)
+ 10 | 33 | 2 | regress_rls_dave | awesome technology book
+(4 rows)
EXPLAIN (COSTS OFF) SELECT * FROM document WHERE f_leak(dtitle);
QUERY PLAN
-> Seq Scan on document
Filter: ((dlevel <= $0) AND f_leak(dtitle))
(11 rows)
+
-- viewpoint from regress_rls_dave
SET SESSION AUTHORIZATION regress_rls_dave;
SELECT * FROM document WHERE f_leak(dtitle) ORDER BY did;
-NOTICE: f_leak => my first novel
-NOTICE: f_leak => my second novel
-NOTICE: f_leak => my science fiction
-NOTICE: f_leak => great science fiction
-NOTICE: f_leak => great technology book
-NOTICE: f_leak => awesome science fiction
-NOTICE: f_leak => awesome technology book
did | cid | dlevel | dauthor | dtitle
-----+-----+--------+-------------------+-------------------------
1 | 11 | 1 | regress_rls_bob | my first novel
(7 rows)
SELECT * FROM document NATURAL JOIN category WHERE f_leak(dtitle) ORDER BY did;
-NOTICE: f_leak => my first novel
-NOTICE: f_leak => my second novel
-NOTICE: f_leak => my science fiction
-NOTICE: f_leak => great science fiction
-NOTICE: f_leak => great technology book
-NOTICE: f_leak => awesome science fiction
-NOTICE: f_leak => awesome technology book
cid | did | dlevel | dauthor | dtitle | cname
-----+-----+--------+-------------------+-------------------------+-----------------
11 | 1 | 1 | regress_rls_bob | my first novel | novel
--
SET SESSION AUTHORIZATION regress_rls_alice;
SET row_security TO ON;
-CREATE TABLE t1 (a int, junk1 text, b text) WITH OIDS;
-ALTER TABLE t1 DROP COLUMN junk1; -- just a disturbing factor
+-- CREATE TABLE t1 (a int, junk1 text, b text) WITH OIDS;
+-- XL requires columns to be in the same order
+CREATE TABLE t1 (a int, b text) WITH OIDS;
+-- ALTER TABLE t1 DROP COLUMN junk1; -- just a disturbing factor
GRANT ALL ON t1 TO public;
COPY t1 FROM stdin WITH (oids);
CREATE TABLE t2 (c float) INHERITS (t1);
GRANT ALL ON t2 TO public;
COPY t2 FROM stdin WITH (oids);
-CREATE TABLE t3 (c text, b text, a int) WITH OIDS;
+CREATE TABLE t3 (a int, b text) WITH OIDS;
ALTER TABLE t3 INHERIT t1;
GRANT ALL ON t3 TO public;
-COPY t3(a,b,c) FROM stdin WITH (oids);
+COPY t3(a,b) FROM stdin WITH (oids);
CREATE POLICY p1 ON t1 FOR ALL TO PUBLIC USING (a % 2 = 0); -- be even number
CREATE POLICY p2 ON t2 FOR ALL TO PUBLIC USING (a % 2 = 1); -- be odd number
ALTER TABLE t1 ENABLE ROW LEVEL SECURITY;
ALTER TABLE t2 ENABLE ROW LEVEL SECURITY;
SET SESSION AUTHORIZATION regress_rls_bob;
-SELECT * FROM t1;
+SELECT * FROM t1 ORDER BY a;
a | b
---+-----
2 | bbb
- 4 | dad
2 | bcd
- 4 | def
2 | yyy
+ 4 | dad
+ 4 | def
(5 rows)
EXPLAIN (COSTS OFF) SELECT * FROM t1;
- QUERY PLAN
------------------------------------------------------------
- Append
- -> Remote Subquery Scan on all (datanode_1,datanode_2)
+ QUERY PLAN
+-----------------------------------------------------
+ Remote Subquery Scan on all (datanode_1,datanode_2)
+ -> Append
-> Seq Scan on t1
Filter: ((a % 2) = 0)
- -> Remote Subquery Scan on all (datanode_1,datanode_2)
-> Seq Scan on t2
Filter: ((a % 2) = 0)
- -> Remote Subquery Scan on all (datanode_1,datanode_2)
-> Seq Scan on t3
Filter: ((a % 2) = 0)
-(10 rows)
+(8 rows)
-SELECT * FROM t1 WHERE f_leak(b);
-NOTICE: f_leak => bbb
-NOTICE: f_leak => dad
-NOTICE: f_leak => bcd
-NOTICE: f_leak => def
-NOTICE: f_leak => yyy
+SELECT * FROM t1 WHERE f_leak(b) ORDER BY a;
a | b
---+-----
2 | bbb
- 4 | dad
2 | bcd
- 4 | def
2 | yyy
+ 4 | dad
+ 4 | def
(5 rows)
EXPLAIN (COSTS OFF) SELECT * FROM t1 WHERE f_leak(b);
- QUERY PLAN
------------------------------------------------------------------
- Subquery Scan on t1
- Filter: f_leak(t1.b)
+ QUERY PLAN
+-----------------------------------------------------
+ Remote Subquery Scan on all (datanode_1,datanode_2)
-> Append
- -> Remote Subquery Scan on all (datanode_1,datanode_2)
- -> Seq Scan on t1 t1_1
- Filter: ((a % 2) = 0)
- -> Remote Subquery Scan on all (datanode_1,datanode_2)
- -> Seq Scan on t2
- Filter: ((a % 2) = 0)
- -> Remote Subquery Scan on all (datanode_1,datanode_2)
- -> Seq Scan on t3
- Filter: ((a % 2) = 0)
-(12 rows)
+ -> Seq Scan on t1
+ Filter: (((a % 2) = 0) AND f_leak(b))
+ -> Seq Scan on t2
+ Filter: (((a % 2) = 0) AND f_leak(b))
+ -> Seq Scan on t3
+ Filter: (((a % 2) = 0) AND f_leak(b))
+(8 rows)
-- reference to system column
-SELECT oid, * FROM t1;
+SELECT oid, * FROM t1 ORDER BY a;
oid | a | b
-----+---+-----
102 | 2 | bbb
- 104 | 4 | dad
202 | 2 | bcd
- 204 | 4 | def
302 | 2 | yyy
+ 104 | 4 | dad
+ 204 | 4 | def
(5 rows)
EXPLAIN (COSTS OFF) SELECT *, t1 FROM t1;
- QUERY PLAN
------------------------------------------------------------
- Append
- -> Remote Subquery Scan on all (datanode_1,datanode_2)
+ QUERY PLAN
+-----------------------------------------------------
+ Remote Subquery Scan on all (datanode_1,datanode_2)
+ -> Append
-> Seq Scan on t1
Filter: ((a % 2) = 0)
- -> Remote Subquery Scan on all (datanode_1,datanode_2)
-> Seq Scan on t2
Filter: ((a % 2) = 0)
- -> Remote Subquery Scan on all (datanode_1,datanode_2)
-> Seq Scan on t3
Filter: ((a % 2) = 0)
-(10 rows)
+(8 rows)
-- reference to whole-row reference
-SELECT *, t1 FROM t1;
+SELECT *, t1 FROM t1 ORDER BY a;
a | b | t1
---+-----+---------
2 | bbb | (2,bbb)
- 4 | dad | (4,dad)
2 | bcd | (2,bcd)
- 4 | def | (4,def)
2 | yyy | (2,yyy)
+ 4 | dad | (4,dad)
+ 4 | def | (4,def)
(5 rows)
EXPLAIN (COSTS OFF) SELECT *, t1 FROM t1;
- QUERY PLAN
------------------------------------------------------------
- Append
- -> Remote Subquery Scan on all (datanode_1,datanode_2)
+ QUERY PLAN
+-----------------------------------------------------
+ Remote Subquery Scan on all (datanode_1,datanode_2)
+ -> Append
-> Seq Scan on t1
Filter: ((a % 2) = 0)
- -> Remote Subquery Scan on all (datanode_1,datanode_2)
-> Seq Scan on t2
Filter: ((a % 2) = 0)
- -> Remote Subquery Scan on all (datanode_1,datanode_2)
-> Seq Scan on t3
Filter: ((a % 2) = 0)
-(10 rows)
+(8 rows)
-- for share/update lock
-SELECT * FROM t1 FOR SHARE;
+SELECT * FROM t1 ORDER BY a FOR SHARE;
a | b
---+-----
2 | bbb
- 4 | dad
2 | bcd
- 4 | def
2 | yyy
+ 4 | dad
+ 4 | def
(5 rows)
EXPLAIN (COSTS OFF) SELECT * FROM t1 FOR SHARE;
- QUERY PLAN
------------------------------------------------------------------------------------
- LockRows
- -> Subquery Scan on t1
- -> LockRows
- -> Result
- -> Append
- -> Remote Subquery Scan on all (datanode_1,datanode_2)
- -> Seq Scan on t1 t1_1
- Filter: ((a % 2) = 0)
- -> Remote Subquery Scan on all (datanode_1,datanode_2)
- -> Seq Scan on t2
- Filter: ((a % 2) = 0)
- -> Remote Subquery Scan on all (datanode_1,datanode_2)
- -> Seq Scan on t3
- Filter: ((a % 2) = 0)
-(14 rows)
+ QUERY PLAN
+-----------------------------------------------------
+ Remote Subquery Scan on all (datanode_1,datanode_2)
+ -> LockRows
+ -> Append
+ -> Seq Scan on t1
+ Filter: ((a % 2) = 0)
+ -> Seq Scan on t2
+ Filter: ((a % 2) = 0)
+ -> Seq Scan on t3
+ Filter: ((a % 2) = 0)
+(9 rows)
-SELECT * FROM t1 WHERE f_leak(b) FOR SHARE;
-NOTICE: f_leak => bbb
-NOTICE: f_leak => dad
-NOTICE: f_leak => bcd
-NOTICE: f_leak => def
-NOTICE: f_leak => yyy
+SELECT * FROM t1 WHERE f_leak(b) ORDER BY a FOR SHARE;
a | b
---+-----
2 | bbb
- 4 | dad
2 | bcd
- 4 | def
2 | yyy
+ 4 | dad
+ 4 | def
(5 rows)
EXPLAIN (COSTS OFF) SELECT * FROM t1 WHERE f_leak(b) FOR SHARE;
- QUERY PLAN
------------------------------------------------------------------------------------
- LockRows
- -> Subquery Scan on t1
- Filter: f_leak(t1.b)
- -> LockRows
- -> Result
- -> Append
- -> Remote Subquery Scan on all (datanode_1,datanode_2)
- -> Seq Scan on t1 t1_1
- Filter: ((a % 2) = 0)
- -> Remote Subquery Scan on all (datanode_1,datanode_2)
- -> Seq Scan on t2
- Filter: ((a % 2) = 0)
- -> Remote Subquery Scan on all (datanode_1,datanode_2)
- -> Seq Scan on t3
- Filter: ((a % 2) = 0)
-(15 rows)
+ QUERY PLAN
+-----------------------------------------------------------
+ Remote Subquery Scan on all (datanode_1,datanode_2)
+ -> LockRows
+ -> Append
+ -> Seq Scan on t1
+ Filter: (((a % 2) = 0) AND f_leak(b))
+ -> Seq Scan on t2
+ Filter: (((a % 2) = 0) AND f_leak(b))
+ -> Seq Scan on t3
+ Filter: (((a % 2) = 0) AND f_leak(b))
+(9 rows)
-- union all query
SELECT a, b, oid FROM t2 UNION ALL SELECT a, b, oid FROM t3;
a | b | oid
---+-----+-----
1 | abc | 201
- 3 | cde | 203
1 | xxx | 301
2 | yyy | 302
+ 3 | cde | 203
3 | zzz | 303
(5 rows)
EXPLAIN (COSTS OFF) SELECT a, b, oid FROM t2 UNION ALL SELECT a, b, oid FROM t3;
- QUERY PLAN
------------------------------------------------------------
- Append
- -> Remote Subquery Scan on all (datanode_1,datanode_2)
+ QUERY PLAN
+-----------------------------------------------------
+ Remote Subquery Scan on all (datanode_1,datanode_2)
+ -> Append
-> Seq Scan on t2
Filter: ((a % 2) = 1)
- -> Remote Subquery Scan on all (datanode_1,datanode_2)
-> Seq Scan on t3
-(6 rows)
+(5 rows)
-- superuser is allowed to bypass RLS checks
RESET SESSION AUTHORIZATION;
SET row_security TO OFF;
-SELECT * FROM t1 WHERE f_leak(b);
+SELECT * FROM t1 WHERE f_leak(b) ORDER BY a;
a | b
---+-----
1 | aba
- 2 | bbb
- 3 | ccc
- 4 | dad
1 | abc
- 2 | bcd
- 3 | cde
- 4 | def
1 | xxx
+ 2 | bbb
+ 2 | bcd
2 | yyy
+ 3 | ccc
+ 3 | cde
3 | zzz
+ 4 | dad
+ 4 | def
(11 rows)
EXPLAIN (COSTS OFF) SELECT * FROM t1 WHERE f_leak(b);
- QUERY PLAN
------------------------------------------------------------
- Append
- -> Remote Subquery Scan on all (datanode_1,datanode_2)
+ QUERY PLAN
+-----------------------------------------------------
+ Remote Subquery Scan on all (datanode_1,datanode_2)
+ -> Append
-> Seq Scan on t1
Filter: f_leak(b)
- -> Remote Subquery Scan on all (datanode_1,datanode_2)
-> Seq Scan on t2
Filter: f_leak(b)
- -> Remote Subquery Scan on all (datanode_1,datanode_2)
-> Seq Scan on t3
Filter: f_leak(b)
-(10 rows)
+(8 rows)
-- non-superuser with bypass privilege can bypass RLS policy when disabled
SET SESSION AUTHORIZATION regress_rls_exempt_user;
SET row_security TO OFF;
-SELECT * FROM t1 WHERE f_leak(b);
+SELECT * FROM t1 WHERE f_leak(b) ORDER BY a;
a | b
---+-----
1 | aba
- 2 | bbb
- 3 | ccc
- 4 | dad
1 | abc
- 2 | bcd
- 3 | cde
- 4 | def
1 | xxx
+ 2 | bbb
+ 2 | bcd
2 | yyy
+ 3 | ccc
+ 3 | cde
3 | zzz
+ 4 | dad
+ 4 | def
(11 rows)
EXPLAIN (COSTS OFF) SELECT * FROM t1 WHERE f_leak(b);
- QUERY PLAN
------------------------------------------------------------
- Append
- -> Remote Subquery Scan on all (datanode_1,datanode_2)
+ QUERY PLAN
+-----------------------------------------------------
+ Remote Subquery Scan on all (datanode_1,datanode_2)
+ -> Append
-> Seq Scan on t1
Filter: f_leak(b)
- -> Remote Subquery Scan on all (datanode_1,datanode_2)
-> Seq Scan on t2
Filter: f_leak(b)
- -> Remote Subquery Scan on all (datanode_1,datanode_2)
-> Seq Scan on t3
Filter: f_leak(b)
-(10 rows)
+(8 rows)
--
-- Partitioned Tables
Partitions: part_document_fiction FOR VALUES FROM (11) TO (12),
part_document_nonfiction FOR VALUES FROM (99) TO (100),
part_document_satire FOR VALUES FROM (55) TO (56)
+Distribute By: HASH(did)
+Location Nodes: ALL DATANODES
SELECT * FROM pg_policies WHERE schemaname = 'regress_rls_schema' AND tablename like '%part_document%' ORDER BY policyname;
schemaname | tablename | policyname | permissive | roles | cmd | qual | with_check
SET SESSION AUTHORIZATION regress_rls_bob;
SET row_security TO ON;
SELECT * FROM part_document WHERE f_leak(dtitle) ORDER BY did;
-NOTICE: f_leak => my first novel
-NOTICE: f_leak => great science fiction
-NOTICE: f_leak => awesome science fiction
-NOTICE: f_leak => my first satire
did | cid | dlevel | dauthor | dtitle
-----+-----+--------+-------------------+-------------------------
1 | 11 | 1 | regress_rls_bob | my first novel
-- viewpoint from regress_rls_carol
SET SESSION AUTHORIZATION regress_rls_carol;
SELECT * FROM part_document WHERE f_leak(dtitle) ORDER BY did;
-NOTICE: f_leak => my first novel
-NOTICE: f_leak => my second novel
-NOTICE: f_leak => great science fiction
-NOTICE: f_leak => awesome science fiction
-NOTICE: f_leak => my first satire
-NOTICE: f_leak => great satire
-NOTICE: f_leak => my science textbook
-NOTICE: f_leak => my history book
-NOTICE: f_leak => great technology book
-NOTICE: f_leak => awesome technology book
did | cid | dlevel | dauthor | dtitle
-----+-----+--------+-------------------+-------------------------
1 | 11 | 1 | regress_rls_bob | my first novel
-- viewpoint from regress_rls_dave
SET SESSION AUTHORIZATION regress_rls_dave;
SELECT * FROM part_document WHERE f_leak(dtitle) ORDER BY did;
-NOTICE: f_leak => my first novel
-NOTICE: f_leak => my second novel
-NOTICE: f_leak => great science fiction
-NOTICE: f_leak => awesome science fiction
did | cid | dlevel | dauthor | dtitle
-----+-----+--------+-------------------+-------------------------
1 | 11 | 1 | regress_rls_bob | my first novel
INSERT INTO part_document_satire VALUES (100, 55, 1, 'regress_rls_dave', 'testing RLS with partitions'); -- success
-- We still cannot see the row using the parent
SELECT * FROM part_document WHERE f_leak(dtitle) ORDER BY did;
-NOTICE: f_leak => my first novel
-NOTICE: f_leak => my second novel
-NOTICE: f_leak => great science fiction
-NOTICE: f_leak => awesome science fiction
did | cid | dlevel | dauthor | dtitle
-----+-----+--------+-------------------+-------------------------
1 | 11 | 1 | regress_rls_bob | my first novel
-- But we can if we look directly
SELECT * FROM part_document_satire WHERE f_leak(dtitle) ORDER BY did;
-NOTICE: f_leak => my first satire
-NOTICE: f_leak => great satire
-NOTICE: f_leak => testing RLS with partitions
did | cid | dlevel | dauthor | dtitle
-----+-----+--------+-------------------+-----------------------------
4 | 55 | 1 | regress_rls_bob | my first satire
-- The parent looks same as before
-- viewpoint from regress_rls_dave
SELECT * FROM part_document WHERE f_leak(dtitle) ORDER BY did;
-NOTICE: f_leak => my first novel
-NOTICE: f_leak => my second novel
-NOTICE: f_leak => great science fiction
-NOTICE: f_leak => awesome science fiction
did | cid | dlevel | dauthor | dtitle
-----+-----+--------+-------------------+-------------------------
1 | 11 | 1 | regress_rls_bob | my first novel
-- viewpoint from regress_rls_carol
SET SESSION AUTHORIZATION regress_rls_carol;
SELECT * FROM part_document WHERE f_leak(dtitle) ORDER BY did;
-NOTICE: f_leak => my first novel
-NOTICE: f_leak => my second novel
-NOTICE: f_leak => great science fiction
-NOTICE: f_leak => awesome science fiction
-NOTICE: f_leak => my first satire
-NOTICE: f_leak => great satire
-NOTICE: f_leak => testing RLS with partitions
-NOTICE: f_leak => my science textbook
-NOTICE: f_leak => my history book
-NOTICE: f_leak => great technology book
-NOTICE: f_leak => awesome technology book
did | cid | dlevel | dauthor | dtitle
-----+-----+--------+-------------------+-----------------------------
1 | 11 | 1 | regress_rls_bob | my first novel
-- viewpoint from regress_rls_bob again
SET SESSION AUTHORIZATION regress_rls_bob;
SELECT * FROM part_document WHERE f_leak(dtitle) ORDER BY did;
-NOTICE: f_leak => my first novel
-NOTICE: f_leak => my second novel
-NOTICE: f_leak => my first satire
-NOTICE: f_leak => my science textbook
-NOTICE: f_leak => my history book
did | cid | dlevel | dauthor | dtitle
-----+-----+--------+-----------------+---------------------
1 | 11 | 1 | regress_rls_bob | my first novel
-- viewpoint from rls_regres_carol again
SET SESSION AUTHORIZATION regress_rls_carol;
SELECT * FROM part_document WHERE f_leak(dtitle) ORDER BY did;
-NOTICE: f_leak => great science fiction
-NOTICE: f_leak => great satire
-NOTICE: f_leak => great technology book
did | cid | dlevel | dauthor | dtitle
-----+-----+--------+-------------------+-----------------------
6 | 11 | 1 | regress_rls_carol | great science fiction
SET SESSION AUTHORIZATION regress_rls_bob;
\set VERBOSITY terse \\ -- suppress cascade details
DROP VIEW rec1v, rec2v CASCADE;
-NOTICE: drop cascades to 2 other objects
\set VERBOSITY default
CREATE VIEW rec1v WITH (security_barrier) AS SELECT * FROM rec1;
CREATE VIEW rec2v WITH (security_barrier) AS SELECT * FROM rec2;
(3 rows)
EXPLAIN (COSTS OFF) EXECUTE p1(2);
- QUERY PLAN
------------------------------------------------------------
- Append
- -> Remote Subquery Scan on all (datanode_1,datanode_2)
+ QUERY PLAN
+-----------------------------------------------------
+ Remote Subquery Scan on all (datanode_1,datanode_2)
+ -> Append
-> Seq Scan on t1
Filter: ((a <= 2) AND ((a % 2) = 0))
- -> Remote Subquery Scan on all (datanode_1,datanode_2)
-> Seq Scan on t2
Filter: ((a <= 2) AND ((a % 2) = 0))
- -> Remote Subquery Scan on all (datanode_1,datanode_2)
-> Seq Scan on t3
Filter: ((a <= 2) AND ((a % 2) = 0))
-(10 rows)
+(8 rows)
-- superuser is allowed to bypass RLS checks
RESET SESSION AUTHORIZATION;
SET row_security TO OFF;
-SELECT * FROM t1 WHERE f_leak(b);
+SELECT * FROM t1 WHERE f_leak(b) ORDER BY a;
a | b
---+-----
1 | aba
- 2 | bbb
- 3 | ccc
- 4 | dad
1 | abc
- 2 | bcd
- 3 | cde
- 4 | def
1 | xxx
+ 2 | bbb
+ 2 | bcd
2 | yyy
+ 3 | ccc
+ 3 | cde
3 | zzz
+ 4 | dad
+ 4 | def
(11 rows)
EXPLAIN (COSTS OFF) SELECT * FROM t1 WHERE f_leak(b);
- QUERY PLAN
------------------------------------------------------------
- Append
- -> Remote Subquery Scan on all (datanode_1,datanode_2)
+ QUERY PLAN
+-----------------------------------------------------
+ Remote Subquery Scan on all (datanode_1,datanode_2)
+ -> Append
-> Seq Scan on t1
Filter: f_leak(b)
- -> Remote Subquery Scan on all (datanode_1,datanode_2)
-> Seq Scan on t2
Filter: f_leak(b)
- -> Remote Subquery Scan on all (datanode_1,datanode_2)
-> Seq Scan on t3
Filter: f_leak(b)
-(10 rows)
+(8 rows)
-- plan cache should be invalidated
EXECUTE p1(2);
(6 rows)
EXPLAIN (COSTS OFF) EXECUTE p1(2);
- QUERY PLAN
------------------------------------------------------------
- Append
- -> Remote Subquery Scan on all (datanode_1,datanode_2)
+ QUERY PLAN
+-----------------------------------------------------
+ Remote Subquery Scan on all (datanode_1,datanode_2)
+ -> Append
-> Seq Scan on t1
Filter: (a <= 2)
- -> Remote Subquery Scan on all (datanode_1,datanode_2)
-> Seq Scan on t2
Filter: (a <= 2)
- -> Remote Subquery Scan on all (datanode_1,datanode_2)
-> Seq Scan on t3
Filter: (a <= 2)
-(10 rows)
+(8 rows)
PREPARE p2(int) AS SELECT * FROM t1 WHERE a = $1;
EXECUTE p2(2);
(3 rows)
EXPLAIN (COSTS OFF) EXECUTE p2(2);
- QUERY PLAN
------------------------------------------------------------
- Append
- -> Remote Subquery Scan on all (datanode_1)
+ QUERY PLAN
+------------------------------------------
+ Remote Subquery Scan on all (datanode_1)
+ -> Append
-> Seq Scan on t1
Filter: (a = 2)
- -> Remote Subquery Scan on all (datanode_1)
-> Seq Scan on t2
Filter: (a = 2)
- -> Remote Subquery Scan on all (datanode_1,datanode_2)
-> Seq Scan on t3
Filter: (a = 2)
-(10 rows)
+(8 rows)
-- also, case when privilege switch from superuser
SET SESSION AUTHORIZATION regress_rls_bob;
(3 rows)
EXPLAIN (COSTS OFF) EXECUTE p2(2);
- QUERY PLAN
------------------------------------------------------------
- Append
- -> Remote Subquery Scan on all (datanode_1)
+ QUERY PLAN
+---------------------------------------------------
+ Remote Subquery Scan on all (datanode_1)
+ -> Append
-> Seq Scan on t1
Filter: ((a = 2) AND ((a % 2) = 0))
- -> Remote Subquery Scan on all (datanode_1)
-> Seq Scan on t2
Filter: ((a = 2) AND ((a % 2) = 0))
- -> Remote Subquery Scan on all (datanode_1,datanode_2)
-> Seq Scan on t3
Filter: ((a = 2) AND ((a % 2) = 0))
-(10 rows)
+(8 rows)
--
-- UPDATE / DELETE and Row-level security
QUERY PLAN
-----------------------------------------------------
Remote Subquery Scan on all (datanode_1,datanode_2)
- -> Update on t1 t1_3
- Update on t1 t1_3
- Update on t2 t1
- Update on t3 t1
- -> Subquery Scan on t1
- Filter: f_leak(t1.b)
- -> LockRows
- -> Seq Scan on t1 t1_4
- Filter: ((a % 2) = 0)
- -> Subquery Scan on t1_1
- Filter: f_leak(t1_1.b)
- -> LockRows
- -> Seq Scan on t2
- Filter: ((a % 2) = 0)
- -> Subquery Scan on t1_2
- Filter: f_leak(t1_2.b)
- -> LockRows
- -> Seq Scan on t3
- Filter: ((a % 2) = 0)
-(20 rows)
+ -> Update on t1
+ Update on t1
+ Update on t2
+ Update on t3
+ -> Seq Scan on t1
+ Filter: (((a % 2) = 0) AND f_leak(b))
+ -> Seq Scan on t2
+ Filter: (((a % 2) = 0) AND f_leak(b))
+ -> Seq Scan on t3
+ Filter: (((a % 2) = 0) AND f_leak(b))
+(11 rows)
UPDATE t1 SET b = b || b WHERE f_leak(b);
EXPLAIN (COSTS OFF) UPDATE only t1 SET b = b || '_updt' WHERE f_leak(b);
QUERY PLAN
-----------------------------------------------------
Remote Subquery Scan on all (datanode_1,datanode_2)
- -> Update on t1 t1_1
- -> Subquery Scan on t1
- Filter: f_leak(t1.b)
- -> LockRows
- -> Seq Scan on t1 t1_2
- Filter: ((a % 2) = 0)
-(7 rows)
+ -> Update on t1
+ -> Seq Scan on t1
+ Filter: (((a % 2) = 0) AND f_leak(b))
+(4 rows)
UPDATE only t1 SET b = b || '_updt' WHERE f_leak(b);
-- returning clause with system column
QUERY PLAN
-----------------------------------------------------
Remote Subquery Scan on all (datanode_1,datanode_2)
- -> Delete on t1 t1_1
- -> Subquery Scan on t1
- Filter: f_leak(t1.b)
- -> LockRows
- -> Seq Scan on t1 t1_2
- Filter: ((a % 2) = 0)
-(7 rows)
+ -> Delete on t1
+ -> Seq Scan on t1
+ Filter: (((a % 2) = 0) AND f_leak(b))
+(4 rows)
EXPLAIN (COSTS OFF) DELETE FROM t1 WHERE f_leak(b);
QUERY PLAN
-----------------------------------------------------
Remote Subquery Scan on all (datanode_1,datanode_2)
- -> Delete on t1 t1_3
- Delete on t1 t1_3
- Delete on t2 t1
- Delete on t3 t1
- -> Subquery Scan on t1
- Filter: f_leak(t1.b)
- -> LockRows
- -> Seq Scan on t1 t1_4
- Filter: ((a % 2) = 0)
- -> Subquery Scan on t1_1
- Filter: f_leak(t1_1.b)
- -> LockRows
- -> Seq Scan on t2
- Filter: ((a % 2) = 0)
- -> Subquery Scan on t1_2
- Filter: f_leak(t1_2.b)
- -> LockRows
- -> Seq Scan on t3
- Filter: ((a % 2) = 0)
-(20 rows)
+ -> Delete on t1
+ Delete on t1
+ Delete on t2
+ Delete on t3
+ -> Seq Scan on t1
+ Filter: (((a % 2) = 0) AND f_leak(b))
+ -> Seq Scan on t2
+ Filter: (((a % 2) = 0) AND f_leak(b))
+ -> Seq Scan on t3
+ Filter: (((a % 2) = 0) AND f_leak(b))
+(11 rows)
DELETE FROM only t1 WHERE f_leak(b) RETURNING oid, *, t1;
oid | a | b | t1
ERROR: new row violates row-level security policy for table "b1"
INSERT INTO bv1 VALUES (12, 'xxx'); -- ok
EXPLAIN (COSTS OFF) UPDATE bv1 SET b = 'yyy' WHERE a = 4 AND f_leak(b);
- QUERY PLAN
----------------------------------------------------------------------------------
+ QUERY PLAN
+-----------------------------------------------------------------------------
Remote Subquery Scan on all (datanode_2)
- -> Update on b1 b1_1
- -> Subquery Scan on b1
- Filter: f_leak(b1.b)
- -> Subquery Scan on b1_2
- -> LockRows
- -> Seq Scan on b1 b1_3
- Filter: ((a > 0) AND (a = 4) AND ((a % 2) = 0))
-(8 rows)
+ -> Update on b1
+ -> Seq Scan on b1
+ Filter: ((a > 0) AND (a = 4) AND ((a % 2) = 0) AND f_leak(b))
+(4 rows)
UPDATE bv1 SET b = 'yyy' WHERE a = 4 AND f_leak(b);
EXPLAIN (COSTS OFF) DELETE FROM bv1 WHERE a = 6 AND f_leak(b);
- QUERY PLAN
----------------------------------------------------------------------------------
+ QUERY PLAN
+-----------------------------------------------------------------------------
Remote Subquery Scan on all (datanode_1)
- -> Delete on b1 b1_1
- -> Subquery Scan on b1
- Filter: f_leak(b1.b)
- -> Subquery Scan on b1_2
- -> LockRows
- -> Seq Scan on b1 b1_3
- Filter: ((a > 0) AND (a = 6) AND ((a % 2) = 0))
-(8 rows)
+ -> Delete on b1
+ -> Seq Scan on b1
+ Filter: ((a > 0) AND (a = 6) AND ((a % 2) = 0) AND f_leak(b))
+(4 rows)
DELETE FROM bv1 WHERE a = 6 AND f_leak(b);
SET SESSION AUTHORIZATION regress_rls_alice;
-- 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)
-
+ERROR: WHERE CURRENT OF clause not yet supported
-- Can update row 4 through cursor, which is the next visible row
FETCH RELATIVE 1 FROM current_check_cursor;
- currentid | payload | rlsuser
------------+---------+-----------------
- 4 | def | regress_rls_bob
-(1 row)
-
+ERROR: current transaction is aborted, commands ignored until end of transaction block
UPDATE current_check SET payload = payload || '_new' WHERE CURRENT OF current_check_cursor RETURNING *;
- currentid | payload | rlsuser
------------+---------+-----------------
- 4 | def_new | regress_rls_bob
-(1 row)
-
+ERROR: current transaction is aborted, commands ignored until end of transaction block
SELECT * FROM current_check;
- currentid | payload | rlsuser
------------+---------+-----------------
- 2 | bcd | regress_rls_bob
- 4 | def_new | regress_rls_bob
-(2 rows)
-
+ERROR: current transaction is aborted, commands ignored until end of transaction block
-- 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
- -> Tid Scan on current_check
- TID Cond: CURRENT OF current_check_cursor
- Filter: ((currentid = 4) AND ((currentid % 2) = 0))
-(4 rows)
-
+ERROR: current transaction is aborted, commands ignored until end of transaction block
-- Similarly can only delete row 4
FETCH ABSOLUTE 1 FROM current_check_cursor;
- currentid | payload | rlsuser
------------+---------+-----------------
- 2 | bcd | regress_rls_bob
-(1 row)
-
+ERROR: current transaction is aborted, commands ignored until end of transaction block
DELETE FROM current_check WHERE CURRENT OF current_check_cursor RETURNING *;
- currentid | payload | rlsuser
------------+---------+---------
-(0 rows)
-
+ERROR: current transaction is aborted, commands ignored until end of transaction block
FETCH RELATIVE 1 FROM current_check_cursor;
- currentid | payload | rlsuser
------------+---------+-----------------
- 4 | def | regress_rls_bob
-(1 row)
-
+ERROR: current transaction is aborted, commands ignored until end of transaction block
DELETE FROM current_check WHERE CURRENT OF current_check_cursor RETURNING *;
- currentid | payload | rlsuser
------------+---------+-----------------
- 4 | def_new | regress_rls_bob
-(1 row)
-
+ERROR: current transaction is aborted, commands ignored until end of transaction block
SELECT * FROM current_check;
- currentid | payload | rlsuser
------------+---------+-----------------
- 2 | bcd | regress_rls_bob
-(1 row)
-
+ERROR: current transaction is aborted, commands ignored until end of transaction block
COMMIT;
--
-- check pg_stats view filtering
pg_authid | r
(2 rows)
-SAVEPOINT q;
DROP ROLE regress_rls_eve; --fails due to dependency on POLICY p
ERROR: role "regress_rls_eve" cannot be dropped because some objects depend on it
DETAIL: target of policy p on table tbl1
privileges for table tbl1
-ROLLBACK TO q;
+ROLLBACK;
+BEGIN;
+CREATE ROLE regress_rls_eve;
+CREATE ROLE regress_rls_frank;
+CREATE TABLE tbl1 (c) AS VALUES ('bar'::text);
+GRANT SELECT ON TABLE tbl1 TO regress_rls_eve;
+CREATE POLICY P ON tbl1 TO regress_rls_eve, regress_rls_frank USING (true);
+SELECT refclassid::regclass, deptype
+ FROM pg_depend
+ WHERE classid = 'pg_policy'::regclass
+ AND refobjid = 'tbl1'::regclass;
+ refclassid | deptype
+------------+---------
+ pg_class | a
+(1 row)
+
+SELECT refclassid::regclass, deptype
+ FROM pg_shdepend
+ WHERE classid = 'pg_policy'::regclass
+ AND refobjid IN ('regress_rls_eve'::regrole, 'regress_rls_frank'::regrole);
+ refclassid | deptype
+------------+---------
+ pg_authid | r
+ pg_authid | r
+(2 rows)
+
ALTER POLICY p ON tbl1 TO regress_rls_frank USING (true);
-SAVEPOINT q;
DROP ROLE regress_rls_eve; --fails due to dependency on GRANT SELECT
ERROR: role "regress_rls_eve" cannot be dropped because some objects depend on it
DETAIL: privileges for table tbl1
-ROLLBACK TO q;
+ROLLBACK;
+BEGIN;
+CREATE ROLE regress_rls_eve;
+CREATE ROLE regress_rls_frank;
+CREATE TABLE tbl1 (c) AS VALUES ('bar'::text);
+GRANT SELECT ON TABLE tbl1 TO regress_rls_eve;
+CREATE POLICY P ON tbl1 TO regress_rls_eve, regress_rls_frank USING (true);
+SELECT refclassid::regclass, deptype
+ FROM pg_depend
+ WHERE classid = 'pg_policy'::regclass
+ AND refobjid = 'tbl1'::regclass;
+ refclassid | deptype
+------------+---------
+ pg_class | a
+(1 row)
+
+SELECT refclassid::regclass, deptype
+ FROM pg_shdepend
+ WHERE classid = 'pg_policy'::regclass
+ AND refobjid IN ('regress_rls_eve'::regrole, 'regress_rls_frank'::regrole);
+ refclassid | deptype
+------------+---------
+ pg_authid | r
+ pg_authid | r
+(2 rows)
+
+ALTER POLICY p ON tbl1 TO regress_rls_frank USING (true);
REVOKE ALL ON TABLE tbl1 FROM regress_rls_eve;
-SAVEPOINT q;
DROP ROLE regress_rls_eve; --succeeds
-ROLLBACK TO q;
-SAVEPOINT q;
+ROLLBACK;
+BEGIN;
+CREATE ROLE regress_rls_eve;
+CREATE ROLE regress_rls_frank;
+CREATE TABLE tbl1 (c) AS VALUES ('bar'::text);
+GRANT SELECT ON TABLE tbl1 TO regress_rls_eve;
+CREATE POLICY P ON tbl1 TO regress_rls_eve, regress_rls_frank USING (true);
+SELECT refclassid::regclass, deptype
+ FROM pg_depend
+ WHERE classid = 'pg_policy'::regclass
+ AND refobjid = 'tbl1'::regclass;
+ refclassid | deptype
+------------+---------
+ pg_class | a
+(1 row)
+
+SELECT refclassid::regclass, deptype
+ FROM pg_shdepend
+ WHERE classid = 'pg_policy'::regclass
+ AND refobjid IN ('regress_rls_eve'::regrole, 'regress_rls_frank'::regrole);
+ refclassid | deptype
+------------+---------
+ pg_authid | r
+ pg_authid | r
+(2 rows)
+
+ALTER POLICY p ON tbl1 TO regress_rls_frank USING (true);
+REVOKE ALL ON TABLE tbl1 FROM regress_rls_eve;
DROP ROLE regress_rls_frank; --fails due to dependency on POLICY p
ERROR: role "regress_rls_frank" cannot be dropped because some objects depend on it
DETAIL: target of policy p on table tbl1
-ROLLBACK TO q;
+ROLLBACK;
+BEGIN;
+CREATE ROLE regress_rls_eve;
+CREATE ROLE regress_rls_frank;
+CREATE TABLE tbl1 (c) AS VALUES ('bar'::text);
+GRANT SELECT ON TABLE tbl1 TO regress_rls_eve;
+CREATE POLICY P ON tbl1 TO regress_rls_eve, regress_rls_frank USING (true);
+SELECT refclassid::regclass, deptype
+ FROM pg_depend
+ WHERE classid = 'pg_policy'::regclass
+ AND refobjid = 'tbl1'::regclass;
+ refclassid | deptype
+------------+---------
+ pg_class | a
+(1 row)
+
+SELECT refclassid::regclass, deptype
+ FROM pg_shdepend
+ WHERE classid = 'pg_policy'::regclass
+ AND refobjid IN ('regress_rls_eve'::regrole, 'regress_rls_frank'::regrole);
+ refclassid | deptype
+------------+---------
+ pg_authid | r
+ pg_authid | r
+(2 rows)
+
+ALTER POLICY p ON tbl1 TO regress_rls_frank USING (true);
+REVOKE ALL ON TABLE tbl1 FROM regress_rls_eve;
DROP POLICY p ON tbl1;
-SAVEPOINT q;
DROP ROLE regress_rls_frank; -- succeeds
-ROLLBACK TO q;
ROLLBACK; -- cleanup
--
-- Converting table to view
CREATE TABLE t (c int);
CREATE POLICY p ON t USING (c % 2 = 1);
ALTER TABLE t ENABLE ROW LEVEL SECURITY;
-SAVEPOINT q;
-ERROR: SAVEPOINT is not yet supported.
CREATE RULE "_RETURN" AS ON SELECT TO t DO INSTEAD
SELECT * FROM generate_series(1,5) t0(c); -- fails due to row level security enabled
-ERROR: current transaction is aborted, commands ignored until end of transaction block
-ROLLBACK TO q;
-ERROR: no such savepoint
+ERROR: could not convert table "t" to a view because it has row security enabled
+ROLLBACK;
+BEGIN;
+CREATE TABLE t (c int);
+CREATE POLICY p ON t USING (c % 2 = 1);
+ALTER TABLE t ENABLE ROW LEVEL SECURITY;
ALTER TABLE t DISABLE ROW LEVEL SECURITY;
-ERROR: current transaction is aborted, commands ignored until end of transaction block
-SAVEPOINT q;
-ERROR: current transaction is aborted, commands ignored until end of transaction block
CREATE RULE "_RETURN" AS ON SELECT TO t DO INSTEAD
SELECT * FROM generate_series(1,5) t0(c); -- fails due to policy p on t
-ERROR: current transaction is aborted, commands ignored until end of transaction block
-ROLLBACK TO q;
-ERROR: no such savepoint
+ERROR: could not convert table "t" to a view because it has row security policies
+ROLLBACK;
+BEGIN;
+CREATE TABLE t (c int);
+CREATE POLICY p ON t USING (c % 2 = 1);
+ALTER TABLE t ENABLE ROW LEVEL SECURITY;
+ALTER TABLE t DISABLE ROW LEVEL SECURITY;
DROP POLICY p ON t;
-ERROR: current transaction is aborted, commands ignored until end of transaction block
CREATE RULE "_RETURN" AS ON SELECT TO t DO INSTEAD
SELECT * FROM generate_series(1,5) t0(c); -- succeeds
-ERROR: current transaction is aborted, commands ignored until end of transaction block
ROLLBACK;
--
-- Policy expression handling
RESET SESSION AUTHORIZATION;
SET row_security TO ON;
SELECT * FROM document ORDER BY did;
-SELECT * FROM category;
+SELECT * FROM category ORDER BY cid;
-- database superuser does bypass RLS policy when disabled
RESET SESSION AUTHORIZATION;
SET row_security TO ON;
-CREATE TABLE t1 (a int, junk1 text, b text) WITH OIDS;
-ALTER TABLE t1 DROP COLUMN junk1; -- just a disturbing factor
+-- CREATE TABLE t1 (a int, junk1 text, b text) WITH OIDS;
+-- XL requires columns to be in the same order
+CREATE TABLE t1 (a int, b text) WITH OIDS;
+-- ALTER TABLE t1 DROP COLUMN junk1; -- just a disturbing factor
GRANT ALL ON t1 TO public;
COPY t1 FROM stdin WITH (oids);
204 4 def 4.4
\.
-CREATE TABLE t3 (c text, b text, a int) WITH OIDS;
+CREATE TABLE t3 (a int, b text) WITH OIDS;
ALTER TABLE t3 INHERIT t1;
GRANT ALL ON t3 TO public;
-COPY t3(a,b,c) FROM stdin WITH (oids);
-301 1 xxx X
-302 2 yyy Y
-303 3 zzz Z
+COPY t3(a,b) FROM stdin WITH (oids);
+301 1 xxx
+302 2 yyy
+303 3 zzz
\.
CREATE POLICY p1 ON t1 FOR ALL TO PUBLIC USING (a % 2 = 0); -- be even number
SET SESSION AUTHORIZATION regress_rls_bob;
-SELECT * FROM t1;
+SELECT * FROM t1 ORDER BY a;
EXPLAIN (COSTS OFF) SELECT * FROM t1;
-SELECT * FROM t1 WHERE f_leak(b);
+SELECT * FROM t1 WHERE f_leak(b) ORDER BY a;
EXPLAIN (COSTS OFF) SELECT * FROM t1 WHERE f_leak(b);
-- reference to system column
-SELECT oid, * FROM t1;
+SELECT oid, * FROM t1 ORDER BY a;
EXPLAIN (COSTS OFF) SELECT *, t1 FROM t1;
-- reference to whole-row reference
-SELECT *, t1 FROM t1;
+SELECT *, t1 FROM t1 ORDER BY a;
EXPLAIN (COSTS OFF) SELECT *, t1 FROM t1;
-- for share/update lock
-SELECT * FROM t1 FOR SHARE;
+SELECT * FROM t1 ORDER BY a FOR SHARE;
EXPLAIN (COSTS OFF) SELECT * FROM t1 FOR SHARE;
-SELECT * FROM t1 WHERE f_leak(b) FOR SHARE;
+SELECT * FROM t1 WHERE f_leak(b) ORDER BY a FOR SHARE;
EXPLAIN (COSTS OFF) SELECT * FROM t1 WHERE f_leak(b) FOR SHARE;
-- union all query
-- superuser is allowed to bypass RLS checks
RESET SESSION AUTHORIZATION;
SET row_security TO OFF;
-SELECT * FROM t1 WHERE f_leak(b);
+SELECT * FROM t1 WHERE f_leak(b) ORDER BY a;
EXPLAIN (COSTS OFF) SELECT * FROM t1 WHERE f_leak(b);
-- non-superuser with bypass privilege can bypass RLS policy when disabled
SET SESSION AUTHORIZATION regress_rls_exempt_user;
SET row_security TO OFF;
-SELECT * FROM t1 WHERE f_leak(b);
+SELECT * FROM t1 WHERE f_leak(b) ORDER BY a;
EXPLAIN (COSTS OFF) SELECT * FROM t1 WHERE f_leak(b);
--
-- superuser is allowed to bypass RLS checks
RESET SESSION AUTHORIZATION;
SET row_security TO OFF;
-SELECT * FROM t1 WHERE f_leak(b);
+SELECT * FROM t1 WHERE f_leak(b) ORDER BY a;
EXPLAIN (COSTS OFF) SELECT * FROM t1 WHERE f_leak(b);
-- plan cache should be invalidated
WHERE classid = 'pg_policy'::regclass
AND refobjid IN ('regress_rls_eve'::regrole, 'regress_rls_frank'::regrole);
-SAVEPOINT q;
DROP ROLE regress_rls_eve; --fails due to dependency on POLICY p
-ROLLBACK TO q;
+ROLLBACK;
+
+BEGIN;
+CREATE ROLE regress_rls_eve;
+CREATE ROLE regress_rls_frank;
+CREATE TABLE tbl1 (c) AS VALUES ('bar'::text);
+GRANT SELECT ON TABLE tbl1 TO regress_rls_eve;
+CREATE POLICY P ON tbl1 TO regress_rls_eve, regress_rls_frank USING (true);
+SELECT refclassid::regclass, deptype
+ FROM pg_depend
+ WHERE classid = 'pg_policy'::regclass
+ AND refobjid = 'tbl1'::regclass;
+SELECT refclassid::regclass, deptype
+ FROM pg_shdepend
+ WHERE classid = 'pg_policy'::regclass
+ AND refobjid IN ('regress_rls_eve'::regrole, 'regress_rls_frank'::regrole);
ALTER POLICY p ON tbl1 TO regress_rls_frank USING (true);
-SAVEPOINT q;
DROP ROLE regress_rls_eve; --fails due to dependency on GRANT SELECT
-ROLLBACK TO q;
+ROLLBACK;
+
+BEGIN;
+CREATE ROLE regress_rls_eve;
+CREATE ROLE regress_rls_frank;
+CREATE TABLE tbl1 (c) AS VALUES ('bar'::text);
+GRANT SELECT ON TABLE tbl1 TO regress_rls_eve;
+CREATE POLICY P ON tbl1 TO regress_rls_eve, regress_rls_frank USING (true);
+SELECT refclassid::regclass, deptype
+ FROM pg_depend
+ WHERE classid = 'pg_policy'::regclass
+ AND refobjid = 'tbl1'::regclass;
+SELECT refclassid::regclass, deptype
+ FROM pg_shdepend
+ WHERE classid = 'pg_policy'::regclass
+ AND refobjid IN ('regress_rls_eve'::regrole, 'regress_rls_frank'::regrole);
+ALTER POLICY p ON tbl1 TO regress_rls_frank USING (true);
REVOKE ALL ON TABLE tbl1 FROM regress_rls_eve;
-SAVEPOINT q;
DROP ROLE regress_rls_eve; --succeeds
-ROLLBACK TO q;
+ROLLBACK;
+
+BEGIN;
+CREATE ROLE regress_rls_eve;
+CREATE ROLE regress_rls_frank;
+CREATE TABLE tbl1 (c) AS VALUES ('bar'::text);
+GRANT SELECT ON TABLE tbl1 TO regress_rls_eve;
+CREATE POLICY P ON tbl1 TO regress_rls_eve, regress_rls_frank USING (true);
+SELECT refclassid::regclass, deptype
+ FROM pg_depend
+ WHERE classid = 'pg_policy'::regclass
+ AND refobjid = 'tbl1'::regclass;
+SELECT refclassid::regclass, deptype
+ FROM pg_shdepend
+ WHERE classid = 'pg_policy'::regclass
+ AND refobjid IN ('regress_rls_eve'::regrole, 'regress_rls_frank'::regrole);
-SAVEPOINT q;
+ALTER POLICY p ON tbl1 TO regress_rls_frank USING (true);
+REVOKE ALL ON TABLE tbl1 FROM regress_rls_eve;
DROP ROLE regress_rls_frank; --fails due to dependency on POLICY p
-ROLLBACK TO q;
+ROLLBACK;
+
+BEGIN;
+CREATE ROLE regress_rls_eve;
+CREATE ROLE regress_rls_frank;
+CREATE TABLE tbl1 (c) AS VALUES ('bar'::text);
+GRANT SELECT ON TABLE tbl1 TO regress_rls_eve;
+CREATE POLICY P ON tbl1 TO regress_rls_eve, regress_rls_frank USING (true);
+SELECT refclassid::regclass, deptype
+ FROM pg_depend
+ WHERE classid = 'pg_policy'::regclass
+ AND refobjid = 'tbl1'::regclass;
+SELECT refclassid::regclass, deptype
+ FROM pg_shdepend
+ WHERE classid = 'pg_policy'::regclass
+ AND refobjid IN ('regress_rls_eve'::regrole, 'regress_rls_frank'::regrole);
+ALTER POLICY p ON tbl1 TO regress_rls_frank USING (true);
+REVOKE ALL ON TABLE tbl1 FROM regress_rls_eve;
DROP POLICY p ON tbl1;
-SAVEPOINT q;
DROP ROLE regress_rls_frank; -- succeeds
-ROLLBACK TO q;
-
ROLLBACK; -- cleanup
--
CREATE TABLE t (c int);
CREATE POLICY p ON t USING (c % 2 = 1);
ALTER TABLE t ENABLE ROW LEVEL SECURITY;
-
-SAVEPOINT q;
CREATE RULE "_RETURN" AS ON SELECT TO t DO INSTEAD
SELECT * FROM generate_series(1,5) t0(c); -- fails due to row level security enabled
-ROLLBACK TO q;
+ROLLBACK;
+BEGIN;
+CREATE TABLE t (c int);
+CREATE POLICY p ON t USING (c % 2 = 1);
+ALTER TABLE t ENABLE ROW LEVEL SECURITY;
ALTER TABLE t DISABLE ROW LEVEL SECURITY;
-SAVEPOINT q;
CREATE RULE "_RETURN" AS ON SELECT TO t DO INSTEAD
SELECT * FROM generate_series(1,5) t0(c); -- fails due to policy p on t
-ROLLBACK TO q;
+ROLLBACK;
+BEGIN;
+CREATE TABLE t (c int);
+CREATE POLICY p ON t USING (c % 2 = 1);
+ALTER TABLE t ENABLE ROW LEVEL SECURITY;
+ALTER TABLE t DISABLE ROW LEVEL SECURITY;
DROP POLICY p ON t;
CREATE RULE "_RETURN" AS ON SELECT TO t DO INSTEAD
SELECT * FROM generate_series(1,5) t0(c); -- succeeds