On Tue, Jun 6, 2017 at 4:07 PM, Joe Conway <m...@joeconway.com> wrote:
> On 06/06/2017 11:57 AM, Mike Palmiotto wrote:
>> On Mon, Jun 5, 2017 at 10:36 AM, Robert Haas <robertmh...@gmail.com> wrote:
>>> On Mon, Jun 5, 2017 at 10:20 AM, Joe Conway <m...@joeconway.com> wrote:
>>>> Unless Robert objects, I'll work with Mike to get a fix posted and
>>>> committed in the next day or two.
>>>
>>> That would be great.  Thanks.
>>
>> I have the updated patch with rowsecurity regression tests and rebased
>> on master. I've run these and verified locally by feeding
>> rowsecurity.sql to psql, but have yet to get the full regression suite
>> passing -- it's failing on the constraints regtest and then gets stuck
>> in recovery. Undoubtedly something to do with my
>> configuration/environment over here. I'm working through those issues
>> right now. In the meantime, if you want to see the regression tests as
>> they stand, please see the attached patch.
>
> The constraints test passes here, so presumably something you borked
> locally. I only see a rowsecurity failure, which is not surprising since
> your patch does not include the changes to expected output ;-)
> Please resend with src/test/regress/expected/rowsecurity.out included.

It was indeed an issue on my end. Attached are the rowsecurity
regression tests and the expected out. Unsurprisingly, all tests pass,
because I said so. :)

Let me know if you want me to make any revisions.

Thanks,

--
Mike Palmiotto
Software Engineer
Crunchy Data Solutions
https://crunchydata.com
From 08432d93ed753a1e5cd4585ccf00e900abbd685f Mon Sep 17 00:00:00 2001
From: Mike Palmiotto <mike.palmio...@crunchydata.com>
Date: Wed, 24 May 2017 16:54:49 +0000
Subject: [PATCH] Add RLS support to partitioned tables

This is needed to get RLS policies to apply to the parent partitioned table.
Without this change partitioned tables are skipped.
---
 src/backend/rewrite/rewriteHandler.c      |   3 +-
 src/test/regress/expected/rowsecurity.out | 812 ++++++++++++++++++++++++++++++
 src/test/regress/sql/rowsecurity.sql      | 222 ++++++++
 3 files changed, 1036 insertions(+), 1 deletion(-)

diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index 35ff8bb..6cd73c1 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -1835,7 +1835,8 @@ fireRIRrules(Query *parsetree, List *activeRIRs, bool forUpdatePushedDown)
 
 		/* Only normal relations can have RLS policies */
 		if (rte->rtekind != RTE_RELATION ||
-			rte->relkind != RELKIND_RELATION)
+			(rte->relkind != RELKIND_RELATION &&
+			rte->relkind != RELKIND_PARTITIONED_TABLE))
 			continue;
 
 		rel = heap_open(rte->relid, NoLock);
diff --git a/src/test/regress/expected/rowsecurity.out b/src/test/regress/expected/rowsecurity.out
index 7bf2936..1e35498 100644
--- a/src/test/regress/expected/rowsecurity.out
+++ b/src/test/regress/expected/rowsecurity.out
@@ -899,6 +899,818 @@ EXPLAIN (COSTS OFF) SELECT * FROM t1 WHERE f_leak(b);
          Filter: f_leak(b)
 (7 rows)
 
+--
+-- Partitioned Tables
+--
+SET SESSION AUTHORIZATION regress_rls_alice;
+CREATE TABLE part_category (
+    cid        int primary key,
+    cname      text
+);
+GRANT ALL ON part_category TO public;
+INSERT INTO part_category VALUES
+    (11, 'fiction'),
+    (55, 'satire'),
+    (99, 'nonfiction');
+CREATE TABLE part_document (
+    did         int,
+    cid         int,
+    dlevel      int not null,
+    dauthor     name,
+    dtitle      text
+) PARTITION BY RANGE (cid);
+GRANT ALL ON part_document TO public;
+-- Create partitions for document categories
+CREATE TABLE part_document_fiction (
+	LIKE part_document INCLUDING ALL
+) PARTITION BY RANGE (dlevel);
+CREATE TABLE part_document_satire (
+	LIKE part_document INCLUDING ALL
+) PARTITION BY RANGE (dlevel);
+CREATE TABLE part_document_nonfiction (
+	LIKE part_document INCLUDING ALL
+) PARTITION BY RANGE (dlevel);
+ALTER TABLE part_document ATTACH PARTITION part_document_fiction FOR VALUES FROM ('11') TO ('12');
+ALTER TABLE part_document ATTACH PARTITION part_document_satire FOR VALUES FROM ('55') TO ('56');
+ALTER TABLE part_document ATTACH PARTITION part_document_nonfiction FOR VALUES FROM ('99') TO ('100');
+-- Create partitions for document levels
+CREATE TABLE part_document_fiction_1 (LIKE part_document_fiction INCLUDING ALL);
+CREATE TABLE part_document_fiction_2 (LIKE part_document_fiction INCLUDING ALL);
+CREATE TABLE part_document_satire_1 (LIKE part_document_satire INCLUDING ALL);
+CREATE TABLE part_document_satire_2 (LIKE part_document_satire INCLUDING ALL);
+CREATE TABLE part_document_nonfiction_1 (LIKE part_document_nonfiction INCLUDING ALL);
+CREATE TABLE part_document_nonfiction_2 (LIKE part_document_nonfiction INCLUDING ALL);
+GRANT ALL ON part_document_fiction_1 TO public;
+GRANT ALL ON part_document_fiction_2 TO public;
+GRANT ALL ON part_document_satire_1 TO public;
+GRANT ALL ON part_document_satire_2 TO public;
+GRANT ALL ON part_document_nonfiction_1 TO public;
+GRANT ALL ON part_document_nonfiction_2 TO public;
+ALTER TABLE part_document_fiction ATTACH PARTITION part_document_fiction_1 FOR VALUES FROM ('1') TO ('2');
+ALTER TABLE part_document_fiction ATTACH PARTITION part_document_fiction_2 FOR VALUES FROM ('2') TO ('3');
+ALTER TABLE part_document_satire ATTACH PARTITION part_document_satire_1 FOR VALUES FROM ('1') TO ('2');
+ALTER TABLE part_document_satire ATTACH PARTITION part_document_satire_2 FOR VALUES FROM ('2') TO ('3');
+ALTER TABLE part_document_nonfiction ATTACH PARTITION part_document_nonfiction_1 FOR VALUES FROM ('1') TO ('2');
+ALTER TABLE part_document_nonfiction ATTACH PARTITION part_document_nonfiction_2 FOR VALUES FROM ('2') TO ('3');
+INSERT INTO part_document VALUES
+    ( 1, 11, 1, 'regress_rls_bob', 'my first novel'),
+    ( 2, 11, 2, 'regress_rls_bob', 'my second novel'),
+    ( 3, 99, 2, 'regress_rls_bob', 'my science textbook'),
+    ( 4, 55, 1, 'regress_rls_bob', 'my first satire'),
+    ( 5, 99, 2, 'regress_rls_bob', 'my history book'),
+    ( 6, 11, 1, 'regress_rls_carol', 'great science fiction'),
+    ( 7, 99, 2, 'regress_rls_carol', 'great technology book'),
+    ( 8, 55, 2, 'regress_rls_carol', 'great satire'),
+    ( 9, 11, 1, 'regress_rls_dave', 'awesome science fiction'),
+    (10, 99, 2, 'regress_rls_dave', 'awesome technology book');
+ALTER TABLE part_document ENABLE ROW LEVEL SECURITY;
+ALTER TABLE part_document_fiction ENABLE ROW LEVEL SECURITY;
+ALTER TABLE part_document_fiction_1 ENABLE ROW LEVEL SECURITY;
+ALTER TABLE part_document_fiction_2 ENABLE ROW LEVEL SECURITY;
+ALTER TABLE part_document_satire ENABLE ROW LEVEL SECURITY;
+ALTER TABLE part_document_satire_1 ENABLE ROW LEVEL SECURITY;
+ALTER TABLE part_document_satire_2 ENABLE ROW LEVEL SECURITY;
+ALTER TABLE part_document_nonfiction ENABLE ROW LEVEL SECURITY;
+ALTER TABLE part_document_nonfiction_1 ENABLE ROW LEVEL SECURITY;
+ALTER TABLE part_document_nonfiction_2 ENABLE ROW LEVEL SECURITY;
+-- user's security level must be higher than or equal to document's
+CREATE POLICY pp1 ON part_document AS PERMISSIVE
+    USING (dlevel <= (SELECT seclv FROM uaccount WHERE pguser = current_user));
+CREATE POLICY pp1_fiction ON part_document_fiction AS PERMISSIVE
+    USING (dlevel <= (SELECT seclv FROM uaccount WHERE pguser = current_user));
+CREATE POLICY pp1_satire ON part_document_satire AS PERMISSIVE
+    USING (dlevel <= (SELECT seclv FROM uaccount WHERE pguser = current_user));
+CREATE POLICY pp1_nonfiction ON part_document_nonfiction AS PERMISSIVE
+    USING (dlevel <= (SELECT seclv FROM uaccount WHERE pguser = current_user));
+-- try to create a policy of bogus type
+CREATE POLICY pp1 ON part_document AS UGLY
+    USING (dlevel <= (SELECT seclv FROM uaccount WHERE pguser = current_user));
+ERROR:  unrecognized row security option "ugly"
+LINE 1: CREATE POLICY pp1 ON part_document AS UGLY
+                                              ^
+HINT:  Only PERMISSIVE or RESTRICTIVE policies are supported currently.
+CREATE POLICY pp1_fiction ON part_document_fiction AS UGLY
+    USING (dlevel <= (SELECT seclv FROM uaccount WHERE pguser = current_user));
+ERROR:  unrecognized row security option "ugly"
+LINE 1: CREATE POLICY pp1_fiction ON part_document_fiction AS UGLY
+                                                              ^
+HINT:  Only PERMISSIVE or RESTRICTIVE policies are supported currently.
+CREATE POLICY pp1_satire ON part_document_satire AS UGLY
+    USING (dlevel <= (SELECT seclv FROM uaccount WHERE pguser = current_user));
+ERROR:  unrecognized row security option "ugly"
+LINE 1: CREATE POLICY pp1_satire ON part_document_satire AS UGLY
+                                                            ^
+HINT:  Only PERMISSIVE or RESTRICTIVE policies are supported currently.
+CREATE POLICY pp1_nonfiction ON part_document_nonfiction AS UGLY
+    USING (dlevel <= (SELECT seclv FROM uaccount WHERE pguser = current_user));
+ERROR:  unrecognized row security option "ugly"
+LINE 1: ...TE POLICY pp1_nonfiction ON part_document_nonfiction AS UGLY
+                                                                   ^
+HINT:  Only PERMISSIVE or RESTRICTIVE policies are supported currently.
+-- but Dave isn't allowed to read any documents with cid 55
+-- this is to make sure that we sort the policies by name first
+-- when applying WITH CHECK, a later INSERT by Dave should fail due
+-- to pp1r first
+CREATE POLICY pp2r ON part_document AS RESTRICTIVE TO regress_rls_dave
+    USING (cid <> 55 AND cid < 99);
+CREATE POLICY pp2r_fiction ON part_document_fiction AS RESTRICTIVE TO regress_rls_dave
+    USING (cid <> 55 AND cid < 99);
+CREATE POLICY pp2r_satire ON part_document_satire AS RESTRICTIVE TO regress_rls_dave
+    USING (cid <> 55 AND cid < 99);
+CREATE POLICY pp2r_nonfiction ON part_document_nonfiction AS RESTRICTIVE TO regress_rls_dave
+    USING (cid <> 55 AND cid < 99);
+-- and Dave is only allowed to see nonfiction.
+-- this should fail before partition constraints
+CREATE POLICY pp1r ON part_document AS RESTRICTIVE TO regress_rls_dave
+    USING (cid <> 55);
+CREATE POLICY pp1r_fiction ON part_document_fiction AS RESTRICTIVE TO regress_rls_dave
+    USING (cid <> 55);
+CREATE POLICY pp1r_satire ON part_document_satire AS RESTRICTIVE TO regress_rls_dave
+    USING (cid <> 55);
+CREATE POLICY pp1r_nonfiction ON part_document_nonfiction AS RESTRICTIVE TO regress_rls_dave
+    USING (cid <> 55);
+\dp
+                                                                                                Access privileges
+       Schema       |            Name            | Type  |              Access privileges              | Column privileges |                                       Policies                                       
+--------------------+----------------------------+-------+---------------------------------------------+-------------------+--------------------------------------------------------------------------------------
+ regress_rls_schema | category                   | table | regress_rls_alice=arwdDxt/regress_rls_alice+|                   | p2:                                                                                 +
+                    |                            |       | =arwdDxt/regress_rls_alice                  |                   |   (u):                                                                              +
+                    |                            |       |                                             |                   | CASE                                                                                +
+                    |                            |       |                                             |                   |     WHEN (CURRENT_USER = 'regress_rls_bob'::name) THEN (cid = ANY (ARRAY[11, 33]))  +
+                    |                            |       |                                             |                   |     WHEN (CURRENT_USER = 'regress_rls_carol'::name) THEN (cid = ANY (ARRAY[22, 44]))+
+                    |                            |       |                                             |                   |     ELSE false                                                                      +
+                    |                            |       |                                             |                   | END
+ regress_rls_schema | document                   | table | regress_rls_alice=arwdDxt/regress_rls_alice+|                   | p1:                                                                                 +
+                    |                            |       | =arwdDxt/regress_rls_alice                  |                   |   (u): (dauthor = CURRENT_USER)                                                     +
+                    |                            |       |                                             |                   | p2r (RESTRICTIVE):                                                                  +
+                    |                            |       |                                             |                   |   (u): ((cid <> 44) AND (cid < 50))                                                 +
+                    |                            |       |                                             |                   |   to: regress_rls_dave                                                              +
+                    |                            |       |                                             |                   | p1r (RESTRICTIVE):                                                                  +
+                    |                            |       |                                             |                   |   (u): (cid <> 44)                                                                  +
+                    |                            |       |                                             |                   |   to: regress_rls_dave
+ regress_rls_schema | part_category              | table | regress_rls_alice=arwdDxt/regress_rls_alice+|                   | 
+                    |                            |       | =arwdDxt/regress_rls_alice                  |                   | 
+ regress_rls_schema | part_document              | table | regress_rls_alice=arwdDxt/regress_rls_alice+|                   | pp1:                                                                                +
+                    |                            |       | =arwdDxt/regress_rls_alice                  |                   |   (u): (dlevel <= ( SELECT uaccount.seclv                                           +
+                    |                            |       |                                             |                   |    FROM uaccount                                                                    +
+                    |                            |       |                                             |                   |   WHERE (uaccount.pguser = CURRENT_USER)))                                          +
+                    |                            |       |                                             |                   | pp2r (RESTRICTIVE):                                                                 +
+                    |                            |       |                                             |                   |   (u): ((cid <> 55) AND (cid < 99))                                                 +
+                    |                            |       |                                             |                   |   to: regress_rls_dave                                                              +
+                    |                            |       |                                             |                   | pp1r (RESTRICTIVE):                                                                 +
+                    |                            |       |                                             |                   |   (u): (cid <> 55)                                                                  +
+                    |                            |       |                                             |                   |   to: regress_rls_dave
+ regress_rls_schema | part_document_fiction      | table |                                             |                   | pp1_fiction:                                                                        +
+                    |                            |       |                                             |                   |   (u): (dlevel <= ( SELECT uaccount.seclv                                           +
+                    |                            |       |                                             |                   |    FROM uaccount                                                                    +
+                    |                            |       |                                             |                   |   WHERE (uaccount.pguser = CURRENT_USER)))                                          +
+                    |                            |       |                                             |                   | pp2r_fiction (RESTRICTIVE):                                                         +
+                    |                            |       |                                             |                   |   (u): ((cid <> 55) AND (cid < 99))                                                 +
+                    |                            |       |                                             |                   |   to: regress_rls_dave                                                              +
+                    |                            |       |                                             |                   | pp1r_fiction (RESTRICTIVE):                                                         +
+                    |                            |       |                                             |                   |   (u): (cid <> 55)                                                                  +
+                    |                            |       |                                             |                   |   to: regress_rls_dave
+ regress_rls_schema | part_document_fiction_1    | table | regress_rls_alice=arwdDxt/regress_rls_alice+|                   | 
+                    |                            |       | =arwdDxt/regress_rls_alice                  |                   | 
+ regress_rls_schema | part_document_fiction_2    | table | regress_rls_alice=arwdDxt/regress_rls_alice+|                   | 
+                    |                            |       | =arwdDxt/regress_rls_alice                  |                   | 
+ regress_rls_schema | part_document_nonfiction   | table |                                             |                   | pp1_nonfiction:                                                                     +
+                    |                            |       |                                             |                   |   (u): (dlevel <= ( SELECT uaccount.seclv                                           +
+                    |                            |       |                                             |                   |    FROM uaccount                                                                    +
+                    |                            |       |                                             |                   |   WHERE (uaccount.pguser = CURRENT_USER)))                                          +
+                    |                            |       |                                             |                   | pp2r_nonfiction (RESTRICTIVE):                                                      +
+                    |                            |       |                                             |                   |   (u): ((cid <> 55) AND (cid < 99))                                                 +
+                    |                            |       |                                             |                   |   to: regress_rls_dave                                                              +
+                    |                            |       |                                             |                   | pp1r_nonfiction (RESTRICTIVE):                                                      +
+                    |                            |       |                                             |                   |   (u): (cid <> 55)                                                                  +
+                    |                            |       |                                             |                   |   to: regress_rls_dave
+ regress_rls_schema | part_document_nonfiction_1 | table | regress_rls_alice=arwdDxt/regress_rls_alice+|                   | 
+                    |                            |       | =arwdDxt/regress_rls_alice                  |                   | 
+ regress_rls_schema | part_document_nonfiction_2 | table | regress_rls_alice=arwdDxt/regress_rls_alice+|                   | 
+                    |                            |       | =arwdDxt/regress_rls_alice                  |                   | 
+ regress_rls_schema | part_document_satire       | table |                                             |                   | pp1_satire:                                                                         +
+                    |                            |       |                                             |                   |   (u): (dlevel <= ( SELECT uaccount.seclv                                           +
+                    |                            |       |                                             |                   |    FROM uaccount                                                                    +
+                    |                            |       |                                             |                   |   WHERE (uaccount.pguser = CURRENT_USER)))                                          +
+                    |                            |       |                                             |                   | pp2r_satire (RESTRICTIVE):                                                          +
+                    |                            |       |                                             |                   |   (u): ((cid <> 55) AND (cid < 99))                                                 +
+                    |                            |       |                                             |                   |   to: regress_rls_dave                                                              +
+                    |                            |       |                                             |                   | pp1r_satire (RESTRICTIVE):                                                          +
+                    |                            |       |                                             |                   |   (u): (cid <> 55)                                                                  +
+                    |                            |       |                                             |                   |   to: regress_rls_dave
+ regress_rls_schema | part_document_satire_1     | table | regress_rls_alice=arwdDxt/regress_rls_alice+|                   | 
+                    |                            |       | =arwdDxt/regress_rls_alice                  |                   | 
+ regress_rls_schema | part_document_satire_2     | table | regress_rls_alice=arwdDxt/regress_rls_alice+|                   | 
+                    |                            |       | =arwdDxt/regress_rls_alice                  |                   | 
+ regress_rls_schema | t1                         | table | regress_rls_alice=arwdDxt/regress_rls_alice+|                   | p1:                                                                                 +
+                    |                            |       | =arwdDxt/regress_rls_alice                  |                   |   (u): ((a % 2) = 0)
+ regress_rls_schema | t2                         | table | regress_rls_alice=arwdDxt/regress_rls_alice+|                   | p2:                                                                                 +
+                    |                            |       | =arwdDxt/regress_rls_alice                  |                   |   (u): ((a % 2) = 1)
+ regress_rls_schema | t3                         | table | regress_rls_alice=arwdDxt/regress_rls_alice+|                   | 
+                    |                            |       | =arwdDxt/regress_rls_alice                  |                   | 
+ regress_rls_schema | uaccount                   | table | regress_rls_alice=arwdDxt/regress_rls_alice+|                   | 
+                    |                            |       | =r/regress_rls_alice                        |                   | 
+(17 rows)
+
+\d part_document*
+      Table "regress_rls_schema.part_document"
+ Column  |  Type   | Collation | Nullable | Default 
+---------+---------+-----------+----------+---------
+ did     | integer |           |          | 
+ cid     | integer |           |          | 
+ dlevel  | integer |           | not null | 
+ dauthor | name    |           |          | 
+ dtitle  | text    |           |          | 
+Partition key: RANGE (cid)
+Policies:
+    POLICY "pp1"
+      USING ((dlevel <= ( SELECT uaccount.seclv
+   FROM uaccount
+  WHERE (uaccount.pguser = CURRENT_USER))))
+    POLICY "pp1r" AS RESTRICTIVE
+      TO regress_rls_dave
+      USING ((cid <> 55))
+    POLICY "pp2r" AS RESTRICTIVE
+      TO regress_rls_dave
+      USING (((cid <> 55) AND (cid < 99)))
+Number of partitions: 3 (Use \d+ to list them.)
+
+  Table "regress_rls_schema.part_document_fiction"
+ Column  |  Type   | Collation | Nullable | Default 
+---------+---------+-----------+----------+---------
+ did     | integer |           |          | 
+ cid     | integer |           |          | 
+ dlevel  | integer |           | not null | 
+ dauthor | name    |           |          | 
+ dtitle  | text    |           |          | 
+Partition of: part_document FOR VALUES FROM (11) TO (12)
+Partition key: RANGE (dlevel)
+Policies:
+    POLICY "pp1_fiction"
+      USING ((dlevel <= ( SELECT uaccount.seclv
+   FROM uaccount
+  WHERE (uaccount.pguser = CURRENT_USER))))
+    POLICY "pp1r_fiction" AS RESTRICTIVE
+      TO regress_rls_dave
+      USING ((cid <> 55))
+    POLICY "pp2r_fiction" AS RESTRICTIVE
+      TO regress_rls_dave
+      USING (((cid <> 55) AND (cid < 99)))
+Number of partitions: 2 (Use \d+ to list them.)
+
+ Table "regress_rls_schema.part_document_fiction_1"
+ Column  |  Type   | Collation | Nullable | Default 
+---------+---------+-----------+----------+---------
+ did     | integer |           |          | 
+ cid     | integer |           |          | 
+ dlevel  | integer |           | not null | 
+ dauthor | name    |           |          | 
+ dtitle  | text    |           |          | 
+Partition of: part_document_fiction FOR VALUES FROM (1) TO (2)
+Policies (row security enabled): (none)
+
+ Table "regress_rls_schema.part_document_fiction_2"
+ Column  |  Type   | Collation | Nullable | Default 
+---------+---------+-----------+----------+---------
+ did     | integer |           |          | 
+ cid     | integer |           |          | 
+ dlevel  | integer |           | not null | 
+ dauthor | name    |           |          | 
+ dtitle  | text    |           |          | 
+Partition of: part_document_fiction FOR VALUES FROM (2) TO (3)
+Policies (row security enabled): (none)
+
+Table "regress_rls_schema.part_document_nonfiction"
+ Column  |  Type   | Collation | Nullable | Default 
+---------+---------+-----------+----------+---------
+ did     | integer |           |          | 
+ cid     | integer |           |          | 
+ dlevel  | integer |           | not null | 
+ dauthor | name    |           |          | 
+ dtitle  | text    |           |          | 
+Partition of: part_document FOR VALUES FROM (99) TO (100)
+Partition key: RANGE (dlevel)
+Policies:
+    POLICY "pp1_nonfiction"
+      USING ((dlevel <= ( SELECT uaccount.seclv
+   FROM uaccount
+  WHERE (uaccount.pguser = CURRENT_USER))))
+    POLICY "pp1r_nonfiction" AS RESTRICTIVE
+      TO regress_rls_dave
+      USING ((cid <> 55))
+    POLICY "pp2r_nonfiction" AS RESTRICTIVE
+      TO regress_rls_dave
+      USING (((cid <> 55) AND (cid < 99)))
+Number of partitions: 2 (Use \d+ to list them.)
+
+Table "regress_rls_schema.part_document_nonfiction_1"
+ Column  |  Type   | Collation | Nullable | Default 
+---------+---------+-----------+----------+---------
+ did     | integer |           |          | 
+ cid     | integer |           |          | 
+ dlevel  | integer |           | not null | 
+ dauthor | name    |           |          | 
+ dtitle  | text    |           |          | 
+Partition of: part_document_nonfiction FOR VALUES FROM (1) TO (2)
+Policies (row security enabled): (none)
+
+Table "regress_rls_schema.part_document_nonfiction_2"
+ Column  |  Type   | Collation | Nullable | Default 
+---------+---------+-----------+----------+---------
+ did     | integer |           |          | 
+ cid     | integer |           |          | 
+ dlevel  | integer |           | not null | 
+ dauthor | name    |           |          | 
+ dtitle  | text    |           |          | 
+Partition of: part_document_nonfiction FOR VALUES FROM (2) TO (3)
+Policies (row security enabled): (none)
+
+  Table "regress_rls_schema.part_document_satire"
+ Column  |  Type   | Collation | Nullable | Default 
+---------+---------+-----------+----------+---------
+ did     | integer |           |          | 
+ cid     | integer |           |          | 
+ dlevel  | integer |           | not null | 
+ dauthor | name    |           |          | 
+ dtitle  | text    |           |          | 
+Partition of: part_document FOR VALUES FROM (55) TO (56)
+Partition key: RANGE (dlevel)
+Policies:
+    POLICY "pp1_satire"
+      USING ((dlevel <= ( SELECT uaccount.seclv
+   FROM uaccount
+  WHERE (uaccount.pguser = CURRENT_USER))))
+    POLICY "pp1r_satire" AS RESTRICTIVE
+      TO regress_rls_dave
+      USING ((cid <> 55))
+    POLICY "pp2r_satire" AS RESTRICTIVE
+      TO regress_rls_dave
+      USING (((cid <> 55) AND (cid < 99)))
+Number of partitions: 2 (Use \d+ to list them.)
+
+ Table "regress_rls_schema.part_document_satire_1"
+ Column  |  Type   | Collation | Nullable | Default 
+---------+---------+-----------+----------+---------
+ did     | integer |           |          | 
+ cid     | integer |           |          | 
+ dlevel  | integer |           | not null | 
+ dauthor | name    |           |          | 
+ dtitle  | text    |           |          | 
+Partition of: part_document_satire FOR VALUES FROM (1) TO (2)
+Policies (row security enabled): (none)
+
+ Table "regress_rls_schema.part_document_satire_2"
+ Column  |  Type   | Collation | Nullable | Default 
+---------+---------+-----------+----------+---------
+ did     | integer |           |          | 
+ cid     | integer |           |          | 
+ dlevel  | integer |           | not null | 
+ dauthor | name    |           |          | 
+ dtitle  | text    |           |          | 
+Partition of: part_document_satire FOR VALUES FROM (2) TO (3)
+Policies (row security enabled): (none)
+
+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 
+--------------------+--------------------------+-----------------+-------------+--------------------+-----+--------------------------------------------+------------
+ regress_rls_schema | part_document            | pp1             | PERMISSIVE  | {public}           | ALL | (dlevel <= ( SELECT uaccount.seclv        +| 
+                    |                          |                 |             |                    |     |    FROM uaccount                          +| 
+                    |                          |                 |             |                    |     |   WHERE (uaccount.pguser = CURRENT_USER))) | 
+ regress_rls_schema | part_document_fiction    | pp1_fiction     | PERMISSIVE  | {public}           | ALL | (dlevel <= ( SELECT uaccount.seclv        +| 
+                    |                          |                 |             |                    |     |    FROM uaccount                          +| 
+                    |                          |                 |             |                    |     |   WHERE (uaccount.pguser = CURRENT_USER))) | 
+ regress_rls_schema | part_document_nonfiction | pp1_nonfiction  | PERMISSIVE  | {public}           | ALL | (dlevel <= ( SELECT uaccount.seclv        +| 
+                    |                          |                 |             |                    |     |    FROM uaccount                          +| 
+                    |                          |                 |             |                    |     |   WHERE (uaccount.pguser = CURRENT_USER))) | 
+ regress_rls_schema | part_document_satire     | pp1_satire      | PERMISSIVE  | {public}           | ALL | (dlevel <= ( SELECT uaccount.seclv        +| 
+                    |                          |                 |             |                    |     |    FROM uaccount                          +| 
+                    |                          |                 |             |                    |     |   WHERE (uaccount.pguser = CURRENT_USER))) | 
+ regress_rls_schema | part_document            | pp1r            | RESTRICTIVE | {regress_rls_dave} | ALL | (cid <> 55)                                | 
+ regress_rls_schema | part_document_fiction    | pp1r_fiction    | RESTRICTIVE | {regress_rls_dave} | ALL | (cid <> 55)                                | 
+ regress_rls_schema | part_document_nonfiction | pp1r_nonfiction | RESTRICTIVE | {regress_rls_dave} | ALL | (cid <> 55)                                | 
+ regress_rls_schema | part_document_satire     | pp1r_satire     | RESTRICTIVE | {regress_rls_dave} | ALL | (cid <> 55)                                | 
+ regress_rls_schema | part_document            | pp2r            | RESTRICTIVE | {regress_rls_dave} | ALL | ((cid <> 55) AND (cid < 99))               | 
+ regress_rls_schema | part_document_fiction    | pp2r_fiction    | RESTRICTIVE | {regress_rls_dave} | ALL | ((cid <> 55) AND (cid < 99))               | 
+ regress_rls_schema | part_document_nonfiction | pp2r_nonfiction | RESTRICTIVE | {regress_rls_dave} | ALL | ((cid <> 55) AND (cid < 99))               | 
+ regress_rls_schema | part_document_satire     | pp2r_satire     | RESTRICTIVE | {regress_rls_dave} | ALL | ((cid <> 55) AND (cid < 99))               | 
+(12 rows)
+
+-- viewpoint from regress_rls_bob
+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
+   4 |  55 |      1 | regress_rls_bob   | my first satire
+   6 |  11 |      1 | regress_rls_carol | great science fiction
+   9 |  11 |      1 | regress_rls_dave  | awesome science fiction
+(4 rows)
+
+SELECT * FROM part_document NATURAL JOIN part_category 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
+ cid | did | dlevel |      dauthor      |         dtitle          |  cname  
+-----+-----+--------+-------------------+-------------------------+---------
+  11 |   1 |      1 | regress_rls_bob   | my first novel          | fiction
+  55 |   4 |      1 | regress_rls_bob   | my first satire         | satire
+  11 |   6 |      1 | regress_rls_carol | great science fiction   | fiction
+  11 |   9 |      1 | regress_rls_dave  | awesome science fiction | fiction
+(4 rows)
+
+-- try a sampled version
+SELECT * FROM part_document TABLESAMPLE BERNOULLI(50) REPEATABLE(0)
+  WHERE f_leak(dtitle) ORDER BY did;
+ did | cid | dlevel | dauthor | dtitle 
+-----+-----+--------+---------+--------
+(0 rows)
+
+-- 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 => great science fiction
+NOTICE:  f_leak => awesome science fiction
+NOTICE:  f_leak => my second novel
+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
+   2 |  11 |      2 | regress_rls_bob   | my second novel
+   3 |  99 |      2 | regress_rls_bob   | my science textbook
+   4 |  55 |      1 | regress_rls_bob   | my first satire
+   5 |  99 |      2 | regress_rls_bob   | my history book
+   6 |  11 |      1 | regress_rls_carol | great science fiction
+   7 |  99 |      2 | regress_rls_carol | great technology book
+   8 |  55 |      2 | regress_rls_carol | great satire
+   9 |  11 |      1 | regress_rls_dave  | awesome science fiction
+  10 |  99 |      2 | regress_rls_dave  | awesome technology book
+(10 rows)
+
+SELECT * FROM part_document NATURAL JOIN part_category 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 second novel
+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
+ cid | did | dlevel |      dauthor      |         dtitle          |   cname    
+-----+-----+--------+-------------------+-------------------------+------------
+  11 |   1 |      1 | regress_rls_bob   | my first novel          | fiction
+  11 |   2 |      2 | regress_rls_bob   | my second novel         | fiction
+  99 |   3 |      2 | regress_rls_bob   | my science textbook     | nonfiction
+  55 |   4 |      1 | regress_rls_bob   | my first satire         | satire
+  99 |   5 |      2 | regress_rls_bob   | my history book         | nonfiction
+  11 |   6 |      1 | regress_rls_carol | great science fiction   | fiction
+  99 |   7 |      2 | regress_rls_carol | great technology book   | nonfiction
+  55 |   8 |      2 | regress_rls_carol | great satire            | satire
+  11 |   9 |      1 | regress_rls_dave  | awesome science fiction | fiction
+  99 |  10 |      2 | regress_rls_dave  | awesome technology book | nonfiction
+(10 rows)
+
+-- try a sampled version
+SELECT * FROM part_document TABLESAMPLE BERNOULLI(50) REPEATABLE(0)
+  WHERE f_leak(dtitle) ORDER BY did;
+NOTICE:  f_leak => awesome technology book
+ did | cid | dlevel |     dauthor      |         dtitle          
+-----+-----+--------+------------------+-------------------------
+  10 |  99 |      2 | regress_rls_dave | awesome technology book
+(1 row)
+
+EXPLAIN (COSTS OFF) SELECT * FROM part_document WHERE f_leak(dtitle);
+                     QUERY PLAN                      
+-----------------------------------------------------
+ Append
+   InitPlan 1 (returns $0)
+     ->  Index Scan using uaccount_pkey on uaccount
+           Index Cond: (pguser = CURRENT_USER)
+   ->  Seq Scan on part_document_fiction_1
+         Filter: ((dlevel <= $0) AND f_leak(dtitle))
+   ->  Seq Scan on part_document_fiction_2
+         Filter: ((dlevel <= $0) AND f_leak(dtitle))
+   ->  Seq Scan on part_document_satire_1
+         Filter: ((dlevel <= $0) AND f_leak(dtitle))
+   ->  Seq Scan on part_document_satire_2
+         Filter: ((dlevel <= $0) AND f_leak(dtitle))
+   ->  Seq Scan on part_document_nonfiction_1
+         Filter: ((dlevel <= $0) AND f_leak(dtitle))
+   ->  Seq Scan on part_document_nonfiction_2
+         Filter: ((dlevel <= $0) AND f_leak(dtitle))
+(16 rows)
+
+EXPLAIN (COSTS OFF) SELECT * FROM part_document NATURAL JOIN part_category WHERE f_leak(dtitle);
+                           QUERY PLAN                           
+----------------------------------------------------------------
+ Hash Join
+   Hash Cond: (part_document_fiction_1.cid = part_category.cid)
+   InitPlan 1 (returns $0)
+     ->  Index Scan using uaccount_pkey on uaccount
+           Index Cond: (pguser = CURRENT_USER)
+   ->  Append
+         ->  Seq Scan on part_document_fiction_1
+               Filter: ((dlevel <= $0) AND f_leak(dtitle))
+         ->  Seq Scan on part_document_fiction_2
+               Filter: ((dlevel <= $0) AND f_leak(dtitle))
+         ->  Seq Scan on part_document_satire_1
+               Filter: ((dlevel <= $0) AND f_leak(dtitle))
+         ->  Seq Scan on part_document_satire_2
+               Filter: ((dlevel <= $0) AND f_leak(dtitle))
+         ->  Seq Scan on part_document_nonfiction_1
+               Filter: ((dlevel <= $0) AND f_leak(dtitle))
+         ->  Seq Scan on part_document_nonfiction_2
+               Filter: ((dlevel <= $0) AND f_leak(dtitle))
+   ->  Hash
+         ->  Seq Scan on part_category
+(20 rows)
+
+-- 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 => great science fiction
+NOTICE:  f_leak => awesome science fiction
+NOTICE:  f_leak => my second novel
+ did | cid | dlevel |      dauthor      |         dtitle          
+-----+-----+--------+-------------------+-------------------------
+   1 |  11 |      1 | regress_rls_bob   | my first novel
+   2 |  11 |      2 | regress_rls_bob   | my second novel
+   6 |  11 |      1 | regress_rls_carol | great science fiction
+   9 |  11 |      1 | regress_rls_dave  | awesome science fiction
+(4 rows)
+
+SELECT * FROM part_document NATURAL JOIN part_category 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 second novel
+ cid | did | dlevel |      dauthor      |         dtitle          |  cname  
+-----+-----+--------+-------------------+-------------------------+---------
+  11 |   1 |      1 | regress_rls_bob   | my first novel          | fiction
+  11 |   2 |      2 | regress_rls_bob   | my second novel         | fiction
+  11 |   6 |      1 | regress_rls_carol | great science fiction   | fiction
+  11 |   9 |      1 | regress_rls_dave  | awesome science fiction | fiction
+(4 rows)
+
+EXPLAIN (COSTS OFF) SELECT * FROM part_document WHERE f_leak(dtitle);
+                                             QUERY PLAN                                             
+----------------------------------------------------------------------------------------------------
+ Append
+   InitPlan 1 (returns $0)
+     ->  Index Scan using uaccount_pkey on uaccount
+           Index Cond: (pguser = CURRENT_USER)
+   ->  Seq Scan on part_document_fiction_1
+         Filter: ((cid <> 55) AND (cid <> 55) AND (cid < 99) AND (dlevel <= $0) AND f_leak(dtitle))
+   ->  Seq Scan on part_document_fiction_2
+         Filter: ((cid <> 55) AND (cid <> 55) AND (cid < 99) AND (dlevel <= $0) AND f_leak(dtitle))
+   ->  Seq Scan on part_document_satire_1
+         Filter: ((cid <> 55) AND (cid <> 55) AND (cid < 99) AND (dlevel <= $0) AND f_leak(dtitle))
+   ->  Seq Scan on part_document_satire_2
+         Filter: ((cid <> 55) AND (cid <> 55) AND (cid < 99) AND (dlevel <= $0) AND f_leak(dtitle))
+(12 rows)
+
+EXPLAIN (COSTS OFF) SELECT * FROM part_document NATURAL JOIN part_category WHERE f_leak(dtitle);
+                                                   QUERY PLAN                                                   
+----------------------------------------------------------------------------------------------------------------
+ Hash Join
+   Hash Cond: (part_category.cid = part_document_fiction_1.cid)
+   InitPlan 1 (returns $0)
+     ->  Index Scan using uaccount_pkey on uaccount
+           Index Cond: (pguser = CURRENT_USER)
+   ->  Seq Scan on part_category
+   ->  Hash
+         ->  Append
+               ->  Seq Scan on part_document_fiction_1
+                     Filter: ((cid <> 55) AND (cid <> 55) AND (cid < 99) AND (dlevel <= $0) AND f_leak(dtitle))
+               ->  Seq Scan on part_document_fiction_2
+                     Filter: ((cid <> 55) AND (cid <> 55) AND (cid < 99) AND (dlevel <= $0) AND f_leak(dtitle))
+               ->  Seq Scan on part_document_satire_1
+                     Filter: ((cid <> 55) AND (cid <> 55) AND (cid < 99) AND (dlevel <= $0) AND f_leak(dtitle))
+               ->  Seq Scan on part_document_satire_2
+                     Filter: ((cid <> 55) AND (cid <> 55) AND (cid < 99) AND (dlevel <= $0) AND f_leak(dtitle))
+(16 rows)
+
+-- 99 would technically fail for both pp2r and pp1r, but we should get an error
+-- back from pp1r for this because it sorts first
+INSERT INTO part_document VALUES (100, 55, 1, 'regress_rls_dave', 'testing sorting of policies'); -- fail
+ERROR:  new row violates row-level security policy "pp1r" for table "part_document"
+-- Just to see a pp2r error
+INSERT INTO part_document VALUES (100, 99, 1, 'regress_rls_dave', 'testing sorting of policies'); -- fail
+ERROR:  new row violates row-level security policy "pp2r" for table "part_document"
+-- We should get an RLS error here even though inserting documents
+-- with cid 55 into fiction/nonfiction partitions is a constraint violation.
+-- RLS policies are checked before constraints.
+INSERT INTO part_document_fiction VALUES (100, 55, 1, 'regress_rls_dave', 'testing sorting of policies'); -- fail
+ERROR:  permission denied for relation part_document_fiction
+INSERT INTO part_document_nonfiction VALUES (100, 55, 1, 'regress_rls_dave', 'testing sorting of policies'); -- fail
+ERROR:  permission denied for relation part_document_nonfiction
+-- only owner can change policies
+ALTER POLICY pp1 ON part_document USING (true);    --fail
+ERROR:  must be owner of relation part_document
+DROP POLICY pp1 ON part_document;                  --fail
+ERROR:  must be owner of relation part_document
+SET SESSION AUTHORIZATION regress_rls_alice;
+ALTER POLICY pp1 ON part_document USING (dauthor = current_user);
+-- 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
+   2 |  11 |      2 | regress_rls_bob | my second novel
+   3 |  99 |      2 | regress_rls_bob | my science textbook
+   4 |  55 |      1 | regress_rls_bob | my first satire
+   5 |  99 |      2 | regress_rls_bob | my history book
+(5 rows)
+
+SELECT * FROM part_document NATURAL JOIN part_category 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
+ cid | did | dlevel |     dauthor     |       dtitle        |   cname    
+-----+-----+--------+-----------------+---------------------+------------
+  11 |   1 |      1 | regress_rls_bob | my first novel      | fiction
+  11 |   2 |      2 | regress_rls_bob | my second novel     | fiction
+  99 |   3 |      2 | regress_rls_bob | my science textbook | nonfiction
+  55 |   4 |      1 | regress_rls_bob | my first satire     | satire
+  99 |   5 |      2 | regress_rls_bob | my history book     | nonfiction
+(5 rows)
+
+-- 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
+   7 |  99 |      2 | regress_rls_carol | great technology book
+   8 |  55 |      2 | regress_rls_carol | great satire
+(3 rows)
+
+SELECT * FROM part_document NATURAL JOIN part_category WHERE f_leak(dtitle) ORDER by did;
+NOTICE:  f_leak => great science fiction
+NOTICE:  f_leak => great satire
+NOTICE:  f_leak => great technology book
+ cid | did | dlevel |      dauthor      |        dtitle         |   cname    
+-----+-----+--------+-------------------+-----------------------+------------
+  11 |   6 |      1 | regress_rls_carol | great science fiction | fiction
+  99 |   7 |      2 | regress_rls_carol | great technology book | nonfiction
+  55 |   8 |      2 | regress_rls_carol | great satire          | satire
+(3 rows)
+
+EXPLAIN (COSTS OFF) SELECT * FROM part_document WHERE f_leak(dtitle);
+                          QUERY PLAN                           
+---------------------------------------------------------------
+ Append
+   ->  Seq Scan on part_document_fiction_1
+         Filter: ((dauthor = CURRENT_USER) AND f_leak(dtitle))
+   ->  Seq Scan on part_document_fiction_2
+         Filter: ((dauthor = CURRENT_USER) AND f_leak(dtitle))
+   ->  Seq Scan on part_document_satire_1
+         Filter: ((dauthor = CURRENT_USER) AND f_leak(dtitle))
+   ->  Seq Scan on part_document_satire_2
+         Filter: ((dauthor = CURRENT_USER) AND f_leak(dtitle))
+   ->  Seq Scan on part_document_nonfiction_1
+         Filter: ((dauthor = CURRENT_USER) AND f_leak(dtitle))
+   ->  Seq Scan on part_document_nonfiction_2
+         Filter: ((dauthor = CURRENT_USER) AND f_leak(dtitle))
+(13 rows)
+
+EXPLAIN (COSTS OFF) SELECT * FROM part_document NATURAL JOIN part_category WHERE f_leak(dtitle);
+                                QUERY PLAN                                 
+---------------------------------------------------------------------------
+ Hash Join
+   Hash Cond: (part_category.cid = part_document_fiction_1.cid)
+   ->  Seq Scan on part_category
+   ->  Hash
+         ->  Append
+               ->  Seq Scan on part_document_fiction_1
+                     Filter: ((dauthor = CURRENT_USER) AND f_leak(dtitle))
+               ->  Seq Scan on part_document_fiction_2
+                     Filter: ((dauthor = CURRENT_USER) AND f_leak(dtitle))
+               ->  Seq Scan on part_document_satire_1
+                     Filter: ((dauthor = CURRENT_USER) AND f_leak(dtitle))
+               ->  Seq Scan on part_document_satire_2
+                     Filter: ((dauthor = CURRENT_USER) AND f_leak(dtitle))
+               ->  Seq Scan on part_document_nonfiction_1
+                     Filter: ((dauthor = CURRENT_USER) AND f_leak(dtitle))
+               ->  Seq Scan on part_document_nonfiction_2
+                     Filter: ((dauthor = CURRENT_USER) AND f_leak(dtitle))
+(17 rows)
+
+-- database superuser does bypass RLS policy when enabled
+RESET SESSION AUTHORIZATION;
+SET row_security TO ON;
+SELECT * FROM part_document;
+ did | cid | dlevel |      dauthor      |         dtitle          
+-----+-----+--------+-------------------+-------------------------
+   1 |  11 |      1 | regress_rls_bob   | my first novel
+   6 |  11 |      1 | regress_rls_carol | great science fiction
+   9 |  11 |      1 | regress_rls_dave  | awesome science fiction
+   2 |  11 |      2 | regress_rls_bob   | my second novel
+   4 |  55 |      1 | regress_rls_bob   | my first satire
+   8 |  55 |      2 | regress_rls_carol | great satire
+   3 |  99 |      2 | regress_rls_bob   | my science textbook
+   5 |  99 |      2 | regress_rls_bob   | my history book
+   7 |  99 |      2 | regress_rls_carol | great technology book
+  10 |  99 |      2 | regress_rls_dave  | awesome technology book
+(10 rows)
+
+-- database superuser does bypass RLS policy when disabled
+RESET SESSION AUTHORIZATION;
+SET row_security TO OFF;
+SELECT * FROM part_document;
+ did | cid | dlevel |      dauthor      |         dtitle          
+-----+-----+--------+-------------------+-------------------------
+   1 |  11 |      1 | regress_rls_bob   | my first novel
+   6 |  11 |      1 | regress_rls_carol | great science fiction
+   9 |  11 |      1 | regress_rls_dave  | awesome science fiction
+   2 |  11 |      2 | regress_rls_bob   | my second novel
+   4 |  55 |      1 | regress_rls_bob   | my first satire
+   8 |  55 |      2 | regress_rls_carol | great satire
+   3 |  99 |      2 | regress_rls_bob   | my science textbook
+   5 |  99 |      2 | regress_rls_bob   | my history book
+   7 |  99 |      2 | regress_rls_carol | great technology book
+  10 |  99 |      2 | regress_rls_dave  | awesome technology book
+(10 rows)
+
+-- database 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 part_document;
+ did | cid | dlevel |      dauthor      |         dtitle          
+-----+-----+--------+-------------------+-------------------------
+   1 |  11 |      1 | regress_rls_bob   | my first novel
+   6 |  11 |      1 | regress_rls_carol | great science fiction
+   9 |  11 |      1 | regress_rls_dave  | awesome science fiction
+   2 |  11 |      2 | regress_rls_bob   | my second novel
+   4 |  55 |      1 | regress_rls_bob   | my first satire
+   8 |  55 |      2 | regress_rls_carol | great satire
+   3 |  99 |      2 | regress_rls_bob   | my science textbook
+   5 |  99 |      2 | regress_rls_bob   | my history book
+   7 |  99 |      2 | regress_rls_carol | great technology book
+  10 |  99 |      2 | regress_rls_dave  | awesome technology book
+(10 rows)
+
+-- RLS policy does not apply to table owner when RLS enabled.
+SET SESSION AUTHORIZATION regress_rls_alice;
+SET row_security TO ON;
+SELECT * FROM part_document;
+ did | cid | dlevel |      dauthor      |         dtitle          
+-----+-----+--------+-------------------+-------------------------
+   1 |  11 |      1 | regress_rls_bob   | my first novel
+   6 |  11 |      1 | regress_rls_carol | great science fiction
+   9 |  11 |      1 | regress_rls_dave  | awesome science fiction
+   2 |  11 |      2 | regress_rls_bob   | my second novel
+   4 |  55 |      1 | regress_rls_bob   | my first satire
+   8 |  55 |      2 | regress_rls_carol | great satire
+   3 |  99 |      2 | regress_rls_bob   | my science textbook
+   5 |  99 |      2 | regress_rls_bob   | my history book
+   7 |  99 |      2 | regress_rls_carol | great technology book
+  10 |  99 |      2 | regress_rls_dave  | awesome technology book
+(10 rows)
+
+-- RLS policy does not apply to table owner when RLS disabled.
+SET SESSION AUTHORIZATION regress_rls_alice;
+SET row_security TO OFF;
+SELECT * FROM part_document;
+ did | cid | dlevel |      dauthor      |         dtitle          
+-----+-----+--------+-------------------+-------------------------
+   1 |  11 |      1 | regress_rls_bob   | my first novel
+   6 |  11 |      1 | regress_rls_carol | great science fiction
+   9 |  11 |      1 | regress_rls_dave  | awesome science fiction
+   2 |  11 |      2 | regress_rls_bob   | my second novel
+   4 |  55 |      1 | regress_rls_bob   | my first satire
+   8 |  55 |      2 | regress_rls_carol | great satire
+   3 |  99 |      2 | regress_rls_bob   | my science textbook
+   5 |  99 |      2 | regress_rls_bob   | my history book
+   7 |  99 |      2 | regress_rls_carol | great technology book
+  10 |  99 |      2 | regress_rls_dave  | awesome technology book
+(10 rows)
+
 ----- Dependencies -----
 SET SESSION AUTHORIZATION regress_rls_alice;
 SET row_security TO ON;
diff --git a/src/test/regress/sql/rowsecurity.sql b/src/test/regress/sql/rowsecurity.sql
index 1b6896e..372db25 100644
--- a/src/test/regress/sql/rowsecurity.sql
+++ b/src/test/regress/sql/rowsecurity.sql
@@ -308,6 +308,228 @@ SET row_security TO OFF;
 SELECT * FROM t1 WHERE f_leak(b);
 EXPLAIN (COSTS OFF) SELECT * FROM t1 WHERE f_leak(b);
 
+--
+-- Partitioned Tables
+--
+
+SET SESSION AUTHORIZATION regress_rls_alice;
+
+CREATE TABLE part_category (
+    cid        int primary key,
+    cname      text
+);
+GRANT ALL ON part_category TO public;
+INSERT INTO part_category VALUES
+    (11, 'fiction'),
+    (55, 'satire'),
+    (99, 'nonfiction');
+
+CREATE TABLE part_document (
+    did         int,
+    cid         int,
+    dlevel      int not null,
+    dauthor     name,
+    dtitle      text
+) PARTITION BY RANGE (cid);
+GRANT ALL ON part_document TO public;
+
+-- Create partitions for document categories
+CREATE TABLE part_document_fiction (
+	LIKE part_document INCLUDING ALL
+) PARTITION BY RANGE (dlevel);
+
+CREATE TABLE part_document_satire (
+	LIKE part_document INCLUDING ALL
+) PARTITION BY RANGE (dlevel);
+
+CREATE TABLE part_document_nonfiction (
+	LIKE part_document INCLUDING ALL
+) PARTITION BY RANGE (dlevel);
+
+ALTER TABLE part_document ATTACH PARTITION part_document_fiction FOR VALUES FROM ('11') TO ('12');
+ALTER TABLE part_document ATTACH PARTITION part_document_satire FOR VALUES FROM ('55') TO ('56');
+ALTER TABLE part_document ATTACH PARTITION part_document_nonfiction FOR VALUES FROM ('99') TO ('100');
+
+-- Create partitions for document levels
+CREATE TABLE part_document_fiction_1 (LIKE part_document_fiction INCLUDING ALL);
+CREATE TABLE part_document_fiction_2 (LIKE part_document_fiction INCLUDING ALL);
+CREATE TABLE part_document_satire_1 (LIKE part_document_satire INCLUDING ALL);
+CREATE TABLE part_document_satire_2 (LIKE part_document_satire INCLUDING ALL);
+CREATE TABLE part_document_nonfiction_1 (LIKE part_document_nonfiction INCLUDING ALL);
+CREATE TABLE part_document_nonfiction_2 (LIKE part_document_nonfiction INCLUDING ALL);
+
+GRANT ALL ON part_document_fiction_1 TO public;
+GRANT ALL ON part_document_fiction_2 TO public;
+GRANT ALL ON part_document_satire_1 TO public;
+GRANT ALL ON part_document_satire_2 TO public;
+GRANT ALL ON part_document_nonfiction_1 TO public;
+GRANT ALL ON part_document_nonfiction_2 TO public;
+
+ALTER TABLE part_document_fiction ATTACH PARTITION part_document_fiction_1 FOR VALUES FROM ('1') TO ('2');
+ALTER TABLE part_document_fiction ATTACH PARTITION part_document_fiction_2 FOR VALUES FROM ('2') TO ('3');
+ALTER TABLE part_document_satire ATTACH PARTITION part_document_satire_1 FOR VALUES FROM ('1') TO ('2');
+ALTER TABLE part_document_satire ATTACH PARTITION part_document_satire_2 FOR VALUES FROM ('2') TO ('3');
+ALTER TABLE part_document_nonfiction ATTACH PARTITION part_document_nonfiction_1 FOR VALUES FROM ('1') TO ('2');
+ALTER TABLE part_document_nonfiction ATTACH PARTITION part_document_nonfiction_2 FOR VALUES FROM ('2') TO ('3');
+
+INSERT INTO part_document VALUES
+    ( 1, 11, 1, 'regress_rls_bob', 'my first novel'),
+    ( 2, 11, 2, 'regress_rls_bob', 'my second novel'),
+    ( 3, 99, 2, 'regress_rls_bob', 'my science textbook'),
+    ( 4, 55, 1, 'regress_rls_bob', 'my first satire'),
+    ( 5, 99, 2, 'regress_rls_bob', 'my history book'),
+    ( 6, 11, 1, 'regress_rls_carol', 'great science fiction'),
+    ( 7, 99, 2, 'regress_rls_carol', 'great technology book'),
+    ( 8, 55, 2, 'regress_rls_carol', 'great satire'),
+    ( 9, 11, 1, 'regress_rls_dave', 'awesome science fiction'),
+    (10, 99, 2, 'regress_rls_dave', 'awesome technology book');
+
+ALTER TABLE part_document ENABLE ROW LEVEL SECURITY;
+ALTER TABLE part_document_fiction ENABLE ROW LEVEL SECURITY;
+ALTER TABLE part_document_fiction_1 ENABLE ROW LEVEL SECURITY;
+ALTER TABLE part_document_fiction_2 ENABLE ROW LEVEL SECURITY;
+ALTER TABLE part_document_satire ENABLE ROW LEVEL SECURITY;
+ALTER TABLE part_document_satire_1 ENABLE ROW LEVEL SECURITY;
+ALTER TABLE part_document_satire_2 ENABLE ROW LEVEL SECURITY;
+ALTER TABLE part_document_nonfiction ENABLE ROW LEVEL SECURITY;
+ALTER TABLE part_document_nonfiction_1 ENABLE ROW LEVEL SECURITY;
+ALTER TABLE part_document_nonfiction_2 ENABLE ROW LEVEL SECURITY;
+
+-- user's security level must be higher than or equal to document's
+CREATE POLICY pp1 ON part_document AS PERMISSIVE
+    USING (dlevel <= (SELECT seclv FROM uaccount WHERE pguser = current_user));
+CREATE POLICY pp1_fiction ON part_document_fiction AS PERMISSIVE
+    USING (dlevel <= (SELECT seclv FROM uaccount WHERE pguser = current_user));
+CREATE POLICY pp1_satire ON part_document_satire AS PERMISSIVE
+    USING (dlevel <= (SELECT seclv FROM uaccount WHERE pguser = current_user));
+CREATE POLICY pp1_nonfiction ON part_document_nonfiction AS PERMISSIVE
+    USING (dlevel <= (SELECT seclv FROM uaccount WHERE pguser = current_user));
+
+-- try to create a policy of bogus type
+CREATE POLICY pp1 ON part_document AS UGLY
+    USING (dlevel <= (SELECT seclv FROM uaccount WHERE pguser = current_user));
+CREATE POLICY pp1_fiction ON part_document_fiction AS UGLY
+    USING (dlevel <= (SELECT seclv FROM uaccount WHERE pguser = current_user));
+CREATE POLICY pp1_satire ON part_document_satire AS UGLY
+    USING (dlevel <= (SELECT seclv FROM uaccount WHERE pguser = current_user));
+CREATE POLICY pp1_nonfiction ON part_document_nonfiction AS UGLY
+    USING (dlevel <= (SELECT seclv FROM uaccount WHERE pguser = current_user));
+
+-- but Dave isn't allowed to read any documents with cid 55
+-- this is to make sure that we sort the policies by name first
+-- when applying WITH CHECK, a later INSERT by Dave should fail due
+-- to pp1r first
+CREATE POLICY pp2r ON part_document AS RESTRICTIVE TO regress_rls_dave
+    USING (cid <> 55 AND cid < 99);
+CREATE POLICY pp2r_fiction ON part_document_fiction AS RESTRICTIVE TO regress_rls_dave
+    USING (cid <> 55 AND cid < 99);
+CREATE POLICY pp2r_satire ON part_document_satire AS RESTRICTIVE TO regress_rls_dave
+    USING (cid <> 55 AND cid < 99);
+CREATE POLICY pp2r_nonfiction ON part_document_nonfiction AS RESTRICTIVE TO regress_rls_dave
+    USING (cid <> 55 AND cid < 99);
+
+-- and Dave is only allowed to see nonfiction.
+-- this should fail before partition constraints
+CREATE POLICY pp1r ON part_document AS RESTRICTIVE TO regress_rls_dave
+    USING (cid <> 55);
+CREATE POLICY pp1r_fiction ON part_document_fiction AS RESTRICTIVE TO regress_rls_dave
+    USING (cid <> 55);
+CREATE POLICY pp1r_satire ON part_document_satire AS RESTRICTIVE TO regress_rls_dave
+    USING (cid <> 55);
+CREATE POLICY pp1r_nonfiction ON part_document_nonfiction AS RESTRICTIVE TO regress_rls_dave
+    USING (cid <> 55);
+
+\dp
+\d part_document*
+SELECT * FROM pg_policies WHERE schemaname = 'regress_rls_schema' AND tablename like '%part_document%' ORDER BY policyname;
+
+-- viewpoint from regress_rls_bob
+SET SESSION AUTHORIZATION regress_rls_bob;
+SET row_security TO ON;
+SELECT * FROM part_document WHERE f_leak(dtitle) ORDER BY did;
+SELECT * FROM part_document NATURAL JOIN part_category WHERE f_leak(dtitle) ORDER BY did;
+
+-- try a sampled version
+SELECT * FROM part_document TABLESAMPLE BERNOULLI(50) REPEATABLE(0)
+  WHERE f_leak(dtitle) ORDER BY did;
+
+-- viewpoint from regress_rls_carol
+SET SESSION AUTHORIZATION regress_rls_carol;
+SELECT * FROM part_document WHERE f_leak(dtitle) ORDER BY did;
+SELECT * FROM part_document NATURAL JOIN part_category WHERE f_leak(dtitle) ORDER BY did;
+
+-- try a sampled version
+SELECT * FROM part_document TABLESAMPLE BERNOULLI(50) REPEATABLE(0)
+  WHERE f_leak(dtitle) ORDER BY did;
+
+EXPLAIN (COSTS OFF) SELECT * FROM part_document WHERE f_leak(dtitle);
+EXPLAIN (COSTS OFF) SELECT * FROM part_document NATURAL JOIN part_category WHERE f_leak(dtitle);
+
+-- viewpoint from regress_rls_dave
+SET SESSION AUTHORIZATION regress_rls_dave;
+SELECT * FROM part_document WHERE f_leak(dtitle) ORDER BY did;
+SELECT * FROM part_document NATURAL JOIN part_category WHERE f_leak(dtitle) ORDER BY did;
+
+EXPLAIN (COSTS OFF) SELECT * FROM part_document WHERE f_leak(dtitle);
+EXPLAIN (COSTS OFF) SELECT * FROM part_document NATURAL JOIN part_category WHERE f_leak(dtitle);
+
+-- 99 would technically fail for both pp2r and pp1r, but we should get an error
+-- back from pp1r for this because it sorts first
+INSERT INTO part_document VALUES (100, 55, 1, 'regress_rls_dave', 'testing sorting of policies'); -- fail
+-- Just to see a pp2r error
+INSERT INTO part_document VALUES (100, 99, 1, 'regress_rls_dave', 'testing sorting of policies'); -- fail
+
+-- We should get an RLS error here even though inserting documents
+-- with cid 55 into fiction/nonfiction partitions is a constraint violation.
+-- RLS policies are checked before constraints.
+INSERT INTO part_document_fiction VALUES (100, 55, 1, 'regress_rls_dave', 'testing sorting of policies'); -- fail
+INSERT INTO part_document_nonfiction VALUES (100, 55, 1, 'regress_rls_dave', 'testing sorting of policies'); -- fail
+
+-- only owner can change policies
+ALTER POLICY pp1 ON part_document USING (true);    --fail
+DROP POLICY pp1 ON part_document;                  --fail
+
+SET SESSION AUTHORIZATION regress_rls_alice;
+ALTER POLICY pp1 ON part_document USING (dauthor = current_user);
+
+-- viewpoint from regress_rls_bob again
+SET SESSION AUTHORIZATION regress_rls_bob;
+SELECT * FROM part_document WHERE f_leak(dtitle) ORDER BY did;
+SELECT * FROM part_document NATURAL JOIN part_category WHERE f_leak(dtitle) ORDER by did;
+
+-- viewpoint from rls_regres_carol again
+SET SESSION AUTHORIZATION regress_rls_carol;
+SELECT * FROM part_document WHERE f_leak(dtitle) ORDER BY did;
+SELECT * FROM part_document NATURAL JOIN part_category WHERE f_leak(dtitle) ORDER by did;
+
+EXPLAIN (COSTS OFF) SELECT * FROM part_document WHERE f_leak(dtitle);
+EXPLAIN (COSTS OFF) SELECT * FROM part_document NATURAL JOIN part_category WHERE f_leak(dtitle);
+
+-- database superuser does bypass RLS policy when enabled
+RESET SESSION AUTHORIZATION;
+SET row_security TO ON;
+SELECT * FROM part_document;
+
+-- database superuser does bypass RLS policy when disabled
+RESET SESSION AUTHORIZATION;
+SET row_security TO OFF;
+SELECT * FROM part_document;
+
+-- database 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 part_document;
+
+-- RLS policy does not apply to table owner when RLS enabled.
+SET SESSION AUTHORIZATION regress_rls_alice;
+SET row_security TO ON;
+SELECT * FROM part_document;
+
+-- RLS policy does not apply to table owner when RLS disabled.
+SET SESSION AUTHORIZATION regress_rls_alice;
+SET row_security TO OFF;
+SELECT * FROM part_document;
+
 ----- Dependencies -----
 SET SESSION AUTHORIZATION regress_rls_alice;
 SET row_security TO ON;
-- 
2.7.4

-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to