2 -- Test of Row-level security feature
4 -- Clean up in case a prior regression run failed
5 -- Suppress NOTICE messages when users/groups don't exist
6 SET client_min_messages TO 'warning';
7 DROP USER IF EXISTS regress_rls_alice;
8 DROP USER IF EXISTS regress_rls_bob;
9 DROP USER IF EXISTS regress_rls_carol;
10 DROP USER IF EXISTS regress_rls_dave;
11 DROP USER IF EXISTS regress_rls_exempt_user;
12 DROP ROLE IF EXISTS regress_rls_group1;
13 DROP ROLE IF EXISTS regress_rls_group2;
14 DROP SCHEMA IF EXISTS regress_rls_schema CASCADE;
15 RESET client_min_messages;
17 CREATE USER regress_rls_alice NOLOGIN;
18 CREATE USER regress_rls_bob NOLOGIN;
19 CREATE USER regress_rls_carol NOLOGIN;
20 CREATE USER regress_rls_dave NOLOGIN;
21 CREATE USER regress_rls_exempt_user BYPASSRLS NOLOGIN;
22 CREATE ROLE regress_rls_group1 NOLOGIN;
23 CREATE ROLE regress_rls_group2 NOLOGIN;
24 GRANT regress_rls_group1 TO regress_rls_bob;
25 GRANT regress_rls_group2 TO regress_rls_carol;
26 CREATE SCHEMA regress_rls_schema;
27 GRANT ALL ON SCHEMA regress_rls_schema to public;
28 SET search_path = regress_rls_schema;
29 -- setup of malicious function
30 CREATE OR REPLACE FUNCTION f_leak(text) RETURNS bool
31 COST 0.0000001 LANGUAGE plpgsql
32 AS 'BEGIN RAISE NOTICE ''f_leak => %'', $1; RETURN true; END';
33 GRANT EXECUTE ON FUNCTION f_leak(text) TO public;
34 -- BASIC Row-Level Security Scenario
35 SET SESSION AUTHORIZATION regress_rls_alice;
36 CREATE TABLE uaccount (
37 pguser name primary key,
40 GRANT SELECT ON uaccount TO public;
41 INSERT INTO uaccount VALUES
42 ('regress_rls_alice', 99),
43 ('regress_rls_bob', 1),
44 ('regress_rls_carol', 2),
45 ('regress_rls_dave', 3);
46 CREATE TABLE category (
50 GRANT ALL ON category TO public;
51 INSERT INTO category VALUES
53 (22, 'science fiction'),
56 CREATE TABLE document (
58 cid int references category(cid),
63 GRANT ALL ON document TO public;
64 INSERT INTO document VALUES
65 ( 1, 11, 1, 'regress_rls_bob', 'my first novel'),
66 ( 2, 11, 2, 'regress_rls_bob', 'my second novel'),
67 ( 3, 22, 2, 'regress_rls_bob', 'my science fiction'),
68 ( 4, 44, 1, 'regress_rls_bob', 'my first manga'),
69 ( 5, 44, 2, 'regress_rls_bob', 'my second manga'),
70 ( 6, 22, 1, 'regress_rls_carol', 'great science fiction'),
71 ( 7, 33, 2, 'regress_rls_carol', 'great technology book'),
72 ( 8, 44, 1, 'regress_rls_carol', 'great manga'),
73 ( 9, 22, 1, 'regress_rls_dave', 'awesome science fiction'),
74 (10, 33, 2, 'regress_rls_dave', 'awesome technology book');
75 ALTER TABLE document ENABLE ROW LEVEL SECURITY;
76 -- user's security level must be higher than or equal to document's
77 CREATE POLICY p1 ON document AS PERMISSIVE
78 USING (dlevel <= (SELECT seclv FROM uaccount WHERE pguser = current_user));
79 -- try to create a policy of bogus type
80 CREATE POLICY p1 ON document AS UGLY
81 USING (dlevel <= (SELECT seclv FROM uaccount WHERE pguser = current_user));
82 ERROR: unrecognized row security option "ugly"
83 LINE 1: CREATE POLICY p1 ON document AS UGLY
85 HINT: Only PERMISSIVE or RESTRICTIVE policies are supported currently.
86 -- but Dave isn't allowed to anything at cid 50 or above
87 -- this is to make sure that we sort the policies by name first
88 -- when applying WITH CHECK, a later INSERT by Dave should fail due
90 CREATE POLICY p2r ON document AS RESTRICTIVE TO regress_rls_dave
91 USING (cid <> 44 AND cid < 50);
92 -- and Dave isn't allowed to see manga documents
93 CREATE POLICY p1r ON document AS RESTRICTIVE TO regress_rls_dave
97 Schema | Name | Type | Access privileges | Column privileges | Policies
98 --------------------+----------+-------+----------------------------------------------+-------------------+--------------------------------------------
99 regress_rls_schema | category | table | regress_rls_alice=arwdDxtm/regress_rls_alice+| |
100 | | | =arwdDxtm/regress_rls_alice | |
101 regress_rls_schema | document | table | regress_rls_alice=arwdDxtm/regress_rls_alice+| | p1: +
102 | | | =arwdDxtm/regress_rls_alice | | (u): (dlevel <= ( SELECT uaccount.seclv +
103 | | | | | FROM uaccount +
104 | | | | | WHERE (uaccount.pguser = CURRENT_USER)))+
105 | | | | | p2r (RESTRICTIVE): +
106 | | | | | (u): ((cid <> 44) AND (cid < 50)) +
107 | | | | | to: regress_rls_dave +
108 | | | | | p1r (RESTRICTIVE): +
109 | | | | | (u): (cid <> 44) +
110 | | | | | to: regress_rls_dave
111 regress_rls_schema | uaccount | table | regress_rls_alice=arwdDxtm/regress_rls_alice+| |
112 | | | =r/regress_rls_alice | |
116 Table "regress_rls_schema.document"
117 Column | Type | Collation | Nullable | Default
118 ---------+---------+-----------+----------+---------
119 did | integer | | not null |
121 dlevel | integer | | not null |
125 "document_pkey" PRIMARY KEY, btree (did)
126 Foreign-key constraints:
127 "document_cid_fkey" FOREIGN KEY (cid) REFERENCES category(cid)
130 USING ((dlevel <= ( SELECT uaccount.seclv
132 WHERE (uaccount.pguser = CURRENT_USER))))
133 POLICY "p1r" AS RESTRICTIVE
136 POLICY "p2r" AS RESTRICTIVE
138 USING (((cid <> 44) AND (cid < 50)))
140 SELECT * FROM pg_policies WHERE schemaname = 'regress_rls_schema' AND tablename = 'document' ORDER BY policyname;
141 schemaname | tablename | policyname | permissive | roles | cmd | qual | with_check
142 --------------------+-----------+------------+-------------+--------------------+-----+--------------------------------------------+------------
143 regress_rls_schema | document | p1 | PERMISSIVE | {public} | ALL | (dlevel <= ( SELECT uaccount.seclv +|
144 | | | | | | FROM uaccount +|
145 | | | | | | WHERE (uaccount.pguser = CURRENT_USER))) |
146 regress_rls_schema | document | p1r | RESTRICTIVE | {regress_rls_dave} | ALL | (cid <> 44) |
147 regress_rls_schema | document | p2r | RESTRICTIVE | {regress_rls_dave} | ALL | ((cid <> 44) AND (cid < 50)) |
150 -- viewpoint from regress_rls_bob
151 SET SESSION AUTHORIZATION regress_rls_bob;
152 SET row_security TO ON;
153 SELECT * FROM document WHERE f_leak(dtitle) ORDER BY did;
154 NOTICE: f_leak => my first novel
155 NOTICE: f_leak => my first manga
156 NOTICE: f_leak => great science fiction
157 NOTICE: f_leak => great manga
158 NOTICE: f_leak => awesome science fiction
159 did | cid | dlevel | dauthor | dtitle
160 -----+-----+--------+-------------------+-------------------------
161 1 | 11 | 1 | regress_rls_bob | my first novel
162 4 | 44 | 1 | regress_rls_bob | my first manga
163 6 | 22 | 1 | regress_rls_carol | great science fiction
164 8 | 44 | 1 | regress_rls_carol | great manga
165 9 | 22 | 1 | regress_rls_dave | awesome science fiction
168 SELECT * FROM document NATURAL JOIN category WHERE f_leak(dtitle) ORDER BY did;
169 NOTICE: f_leak => my first novel
170 NOTICE: f_leak => my first manga
171 NOTICE: f_leak => great science fiction
172 NOTICE: f_leak => great manga
173 NOTICE: f_leak => awesome science fiction
174 cid | did | dlevel | dauthor | dtitle | cname
175 -----+-----+--------+-------------------+-------------------------+-----------------
176 11 | 1 | 1 | regress_rls_bob | my first novel | novel
177 44 | 4 | 1 | regress_rls_bob | my first manga | manga
178 22 | 6 | 1 | regress_rls_carol | great science fiction | science fiction
179 44 | 8 | 1 | regress_rls_carol | great manga | manga
180 22 | 9 | 1 | regress_rls_dave | awesome science fiction | science fiction
183 -- try a sampled version
184 SELECT * FROM document TABLESAMPLE BERNOULLI(50) REPEATABLE(0)
185 WHERE f_leak(dtitle) ORDER BY did;
186 NOTICE: f_leak => my first manga
187 NOTICE: f_leak => great science fiction
188 NOTICE: f_leak => great manga
189 NOTICE: f_leak => awesome science fiction
190 did | cid | dlevel | dauthor | dtitle
191 -----+-----+--------+-------------------+-------------------------
192 4 | 44 | 1 | regress_rls_bob | my first manga
193 6 | 22 | 1 | regress_rls_carol | great science fiction
194 8 | 44 | 1 | regress_rls_carol | great manga
195 9 | 22 | 1 | regress_rls_dave | awesome science fiction
198 -- viewpoint from regress_rls_carol
199 SET SESSION AUTHORIZATION regress_rls_carol;
200 SELECT * FROM document WHERE f_leak(dtitle) ORDER BY did;
201 NOTICE: f_leak => my first novel
202 NOTICE: f_leak => my second novel
203 NOTICE: f_leak => my science fiction
204 NOTICE: f_leak => my first manga
205 NOTICE: f_leak => my second manga
206 NOTICE: f_leak => great science fiction
207 NOTICE: f_leak => great technology book
208 NOTICE: f_leak => great manga
209 NOTICE: f_leak => awesome science fiction
210 NOTICE: f_leak => awesome technology book
211 did | cid | dlevel | dauthor | dtitle
212 -----+-----+--------+-------------------+-------------------------
213 1 | 11 | 1 | regress_rls_bob | my first novel
214 2 | 11 | 2 | regress_rls_bob | my second novel
215 3 | 22 | 2 | regress_rls_bob | my science fiction
216 4 | 44 | 1 | regress_rls_bob | my first manga
217 5 | 44 | 2 | regress_rls_bob | my second manga
218 6 | 22 | 1 | regress_rls_carol | great science fiction
219 7 | 33 | 2 | regress_rls_carol | great technology book
220 8 | 44 | 1 | regress_rls_carol | great manga
221 9 | 22 | 1 | regress_rls_dave | awesome science fiction
222 10 | 33 | 2 | regress_rls_dave | awesome technology book
225 SELECT * FROM document NATURAL JOIN category WHERE f_leak(dtitle) ORDER BY did;
226 NOTICE: f_leak => my first novel
227 NOTICE: f_leak => my second novel
228 NOTICE: f_leak => my science fiction
229 NOTICE: f_leak => my first manga
230 NOTICE: f_leak => my second manga
231 NOTICE: f_leak => great science fiction
232 NOTICE: f_leak => great technology book
233 NOTICE: f_leak => great manga
234 NOTICE: f_leak => awesome science fiction
235 NOTICE: f_leak => awesome technology book
236 cid | did | dlevel | dauthor | dtitle | cname
237 -----+-----+--------+-------------------+-------------------------+-----------------
238 11 | 1 | 1 | regress_rls_bob | my first novel | novel
239 11 | 2 | 2 | regress_rls_bob | my second novel | novel
240 22 | 3 | 2 | regress_rls_bob | my science fiction | science fiction
241 44 | 4 | 1 | regress_rls_bob | my first manga | manga
242 44 | 5 | 2 | regress_rls_bob | my second manga | manga
243 22 | 6 | 1 | regress_rls_carol | great science fiction | science fiction
244 33 | 7 | 2 | regress_rls_carol | great technology book | technology
245 44 | 8 | 1 | regress_rls_carol | great manga | manga
246 22 | 9 | 1 | regress_rls_dave | awesome science fiction | science fiction
247 33 | 10 | 2 | regress_rls_dave | awesome technology book | technology
250 -- try a sampled version
251 SELECT * FROM document TABLESAMPLE BERNOULLI(50) REPEATABLE(0)
252 WHERE f_leak(dtitle) ORDER BY did;
253 NOTICE: f_leak => my first manga
254 NOTICE: f_leak => my second manga
255 NOTICE: f_leak => great science fiction
256 NOTICE: f_leak => great manga
257 NOTICE: f_leak => awesome science fiction
258 did | cid | dlevel | dauthor | dtitle
259 -----+-----+--------+-------------------+-------------------------
260 4 | 44 | 1 | regress_rls_bob | my first manga
261 5 | 44 | 2 | regress_rls_bob | my second manga
262 6 | 22 | 1 | regress_rls_carol | great science fiction
263 8 | 44 | 1 | regress_rls_carol | great manga
264 9 | 22 | 1 | regress_rls_dave | awesome science fiction
267 EXPLAIN (COSTS OFF) SELECT * FROM document WHERE f_leak(dtitle);
269 --------------------------------------------------------------
271 Filter: ((dlevel <= (InitPlan 1).col1) AND f_leak(dtitle))
273 -> Index Scan using uaccount_pkey on uaccount
274 Index Cond: (pguser = CURRENT_USER)
277 EXPLAIN (COSTS OFF) SELECT * FROM document NATURAL JOIN category WHERE f_leak(dtitle);
279 --------------------------------------------------------------------------
281 Hash Cond: (category.cid = document.cid)
283 -> Index Scan using uaccount_pkey on uaccount
284 Index Cond: (pguser = CURRENT_USER)
285 -> Seq Scan on category
287 -> Seq Scan on document
288 Filter: ((dlevel <= (InitPlan 1).col1) AND f_leak(dtitle))
291 -- viewpoint from regress_rls_dave
292 SET SESSION AUTHORIZATION regress_rls_dave;
293 SELECT * FROM document WHERE f_leak(dtitle) ORDER BY did;
294 NOTICE: f_leak => my first novel
295 NOTICE: f_leak => my second novel
296 NOTICE: f_leak => my science fiction
297 NOTICE: f_leak => great science fiction
298 NOTICE: f_leak => great technology book
299 NOTICE: f_leak => awesome science fiction
300 NOTICE: f_leak => awesome technology book
301 did | cid | dlevel | dauthor | dtitle
302 -----+-----+--------+-------------------+-------------------------
303 1 | 11 | 1 | regress_rls_bob | my first novel
304 2 | 11 | 2 | regress_rls_bob | my second novel
305 3 | 22 | 2 | regress_rls_bob | my science fiction
306 6 | 22 | 1 | regress_rls_carol | great science fiction
307 7 | 33 | 2 | regress_rls_carol | great technology book
308 9 | 22 | 1 | regress_rls_dave | awesome science fiction
309 10 | 33 | 2 | regress_rls_dave | awesome technology book
312 SELECT * FROM document NATURAL JOIN category WHERE f_leak(dtitle) ORDER BY did;
313 NOTICE: f_leak => my first novel
314 NOTICE: f_leak => my second novel
315 NOTICE: f_leak => my science fiction
316 NOTICE: f_leak => great science fiction
317 NOTICE: f_leak => great technology book
318 NOTICE: f_leak => awesome science fiction
319 NOTICE: f_leak => awesome technology book
320 cid | did | dlevel | dauthor | dtitle | cname
321 -----+-----+--------+-------------------+-------------------------+-----------------
322 11 | 1 | 1 | regress_rls_bob | my first novel | novel
323 11 | 2 | 2 | regress_rls_bob | my second novel | novel
324 22 | 3 | 2 | regress_rls_bob | my science fiction | science fiction
325 22 | 6 | 1 | regress_rls_carol | great science fiction | science fiction
326 33 | 7 | 2 | regress_rls_carol | great technology book | technology
327 22 | 9 | 1 | regress_rls_dave | awesome science fiction | science fiction
328 33 | 10 | 2 | regress_rls_dave | awesome technology book | technology
331 EXPLAIN (COSTS OFF) SELECT * FROM document WHERE f_leak(dtitle);
333 -------------------------------------------------------------------------------------------------------------
335 Filter: ((cid <> 44) AND (cid <> 44) AND (cid < 50) AND (dlevel <= (InitPlan 1).col1) AND f_leak(dtitle))
337 -> Index Scan using uaccount_pkey on uaccount
338 Index Cond: (pguser = CURRENT_USER)
341 EXPLAIN (COSTS OFF) SELECT * FROM document NATURAL JOIN category WHERE f_leak(dtitle);
343 -------------------------------------------------------------------------------------------------------------------------
345 Hash Cond: (category.cid = document.cid)
347 -> Index Scan using uaccount_pkey on uaccount
348 Index Cond: (pguser = CURRENT_USER)
349 -> Seq Scan on category
351 -> Seq Scan on document
352 Filter: ((cid <> 44) AND (cid <> 44) AND (cid < 50) AND (dlevel <= (InitPlan 1).col1) AND f_leak(dtitle))
355 -- 44 would technically fail for both p2r and p1r, but we should get an error
356 -- back from p1r for this because it sorts first
357 INSERT INTO document VALUES (100, 44, 1, 'regress_rls_dave', 'testing sorting of policies'); -- fail
358 ERROR: new row violates row-level security policy "p1r" for table "document"
359 -- Just to see a p2r error
360 INSERT INTO document VALUES (100, 55, 1, 'regress_rls_dave', 'testing sorting of policies'); -- fail
361 ERROR: new row violates row-level security policy "p2r" for table "document"
362 -- only owner can change policies
363 ALTER POLICY p1 ON document USING (true); --fail
364 ERROR: must be owner of table document
365 DROP POLICY p1 ON document; --fail
366 ERROR: must be owner of relation document
367 SET SESSION AUTHORIZATION regress_rls_alice;
368 ALTER POLICY p1 ON document USING (dauthor = current_user);
369 -- viewpoint from regress_rls_bob again
370 SET SESSION AUTHORIZATION regress_rls_bob;
371 SELECT * FROM document WHERE f_leak(dtitle) ORDER BY did;
372 NOTICE: f_leak => my first novel
373 NOTICE: f_leak => my second novel
374 NOTICE: f_leak => my science fiction
375 NOTICE: f_leak => my first manga
376 NOTICE: f_leak => my second manga
377 did | cid | dlevel | dauthor | dtitle
378 -----+-----+--------+-----------------+--------------------
379 1 | 11 | 1 | regress_rls_bob | my first novel
380 2 | 11 | 2 | regress_rls_bob | my second novel
381 3 | 22 | 2 | regress_rls_bob | my science fiction
382 4 | 44 | 1 | regress_rls_bob | my first manga
383 5 | 44 | 2 | regress_rls_bob | my second manga
386 SELECT * FROM document NATURAL JOIN category WHERE f_leak(dtitle) ORDER by did;
387 NOTICE: f_leak => my first novel
388 NOTICE: f_leak => my second novel
389 NOTICE: f_leak => my science fiction
390 NOTICE: f_leak => my first manga
391 NOTICE: f_leak => my second manga
392 cid | did | dlevel | dauthor | dtitle | cname
393 -----+-----+--------+-----------------+--------------------+-----------------
394 11 | 1 | 1 | regress_rls_bob | my first novel | novel
395 11 | 2 | 2 | regress_rls_bob | my second novel | novel
396 22 | 3 | 2 | regress_rls_bob | my science fiction | science fiction
397 44 | 4 | 1 | regress_rls_bob | my first manga | manga
398 44 | 5 | 2 | regress_rls_bob | my second manga | manga
401 -- viewpoint from rls_regres_carol again
402 SET SESSION AUTHORIZATION regress_rls_carol;
403 SELECT * FROM document WHERE f_leak(dtitle) ORDER BY did;
404 NOTICE: f_leak => great science fiction
405 NOTICE: f_leak => great technology book
406 NOTICE: f_leak => great manga
407 did | cid | dlevel | dauthor | dtitle
408 -----+-----+--------+-------------------+-----------------------
409 6 | 22 | 1 | regress_rls_carol | great science fiction
410 7 | 33 | 2 | regress_rls_carol | great technology book
411 8 | 44 | 1 | regress_rls_carol | great manga
414 SELECT * FROM document NATURAL JOIN category WHERE f_leak(dtitle) ORDER by did;
415 NOTICE: f_leak => great science fiction
416 NOTICE: f_leak => great technology book
417 NOTICE: f_leak => great manga
418 cid | did | dlevel | dauthor | dtitle | cname
419 -----+-----+--------+-------------------+-----------------------+-----------------
420 22 | 6 | 1 | regress_rls_carol | great science fiction | science fiction
421 33 | 7 | 2 | regress_rls_carol | great technology book | technology
422 44 | 8 | 1 | regress_rls_carol | great manga | manga
425 EXPLAIN (COSTS OFF) SELECT * FROM document WHERE f_leak(dtitle);
427 ---------------------------------------------------------
429 Filter: ((dauthor = CURRENT_USER) AND f_leak(dtitle))
432 EXPLAIN (COSTS OFF) SELECT * FROM document NATURAL JOIN category WHERE f_leak(dtitle);
434 ---------------------------------------------------------------
436 -> Seq Scan on document
437 Filter: ((dauthor = CURRENT_USER) AND f_leak(dtitle))
438 -> Index Scan using category_pkey on category
439 Index Cond: (cid = document.cid)
442 -- interaction of FK/PK constraints
443 SET SESSION AUTHORIZATION regress_rls_alice;
444 CREATE POLICY p2 ON category
445 USING (CASE WHEN current_user = 'regress_rls_bob' THEN cid IN (11, 33)
446 WHEN current_user = 'regress_rls_carol' THEN cid IN (22, 44)
448 ALTER TABLE category ENABLE ROW LEVEL SECURITY;
449 -- cannot delete PK referenced by invisible FK
450 SET SESSION AUTHORIZATION regress_rls_bob;
451 SELECT * FROM document d FULL OUTER JOIN category c on d.cid = c.cid ORDER BY d.did, c.cid;
452 did | cid | dlevel | dauthor | dtitle | cid | cname
453 -----+-----+--------+-----------------+--------------------+-----+------------
454 1 | 11 | 1 | regress_rls_bob | my first novel | 11 | novel
455 2 | 11 | 2 | regress_rls_bob | my second novel | 11 | novel
456 3 | 22 | 2 | regress_rls_bob | my science fiction | |
457 4 | 44 | 1 | regress_rls_bob | my first manga | |
458 5 | 44 | 2 | regress_rls_bob | my second manga | |
459 | | | | | 33 | technology
462 DELETE FROM category WHERE cid = 33; -- fails with FK violation
463 ERROR: update or delete on table "category" violates foreign key constraint "document_cid_fkey" on table "document"
464 DETAIL: Key is still referenced from table "document".
465 -- can insert FK referencing invisible PK
466 SET SESSION AUTHORIZATION regress_rls_carol;
467 SELECT * FROM document d FULL OUTER JOIN category c on d.cid = c.cid ORDER BY d.did, c.cid;
468 did | cid | dlevel | dauthor | dtitle | cid | cname
469 -----+-----+--------+-------------------+-----------------------+-----+-----------------
470 6 | 22 | 1 | regress_rls_carol | great science fiction | 22 | science fiction
471 7 | 33 | 2 | regress_rls_carol | great technology book | |
472 8 | 44 | 1 | regress_rls_carol | great manga | 44 | manga
475 INSERT INTO document VALUES (11, 33, 1, current_user, 'hoge');
476 -- UNIQUE or PRIMARY KEY constraint violation DOES reveal presence of row
477 SET SESSION AUTHORIZATION regress_rls_bob;
478 INSERT INTO document VALUES (8, 44, 1, 'regress_rls_bob', 'my third manga'); -- Must fail with unique violation, revealing presence of did we can't see
479 ERROR: duplicate key value violates unique constraint "document_pkey"
480 SELECT * FROM document WHERE did = 8; -- and confirm we can't see it
481 did | cid | dlevel | dauthor | dtitle
482 -----+-----+--------+---------+--------
485 -- RLS policies are checked before constraints
486 INSERT INTO document VALUES (8, 44, 1, 'regress_rls_carol', 'my third manga'); -- Should fail with RLS check violation, not duplicate key violation
487 ERROR: new row violates row-level security policy for table "document"
488 UPDATE document SET did = 8, dauthor = 'regress_rls_carol' WHERE did = 5; -- Should fail with RLS check violation, not duplicate key violation
489 ERROR: new row violates row-level security policy for table "document"
490 -- database superuser does bypass RLS policy when enabled
491 RESET SESSION AUTHORIZATION;
492 SET row_security TO ON;
493 SELECT * FROM document;
494 did | cid | dlevel | dauthor | dtitle
495 -----+-----+--------+-------------------+-------------------------
496 1 | 11 | 1 | regress_rls_bob | my first novel
497 2 | 11 | 2 | regress_rls_bob | my second novel
498 3 | 22 | 2 | regress_rls_bob | my science fiction
499 4 | 44 | 1 | regress_rls_bob | my first manga
500 5 | 44 | 2 | regress_rls_bob | my second manga
501 6 | 22 | 1 | regress_rls_carol | great science fiction
502 7 | 33 | 2 | regress_rls_carol | great technology book
503 8 | 44 | 1 | regress_rls_carol | great manga
504 9 | 22 | 1 | regress_rls_dave | awesome science fiction
505 10 | 33 | 2 | regress_rls_dave | awesome technology book
506 11 | 33 | 1 | regress_rls_carol | hoge
509 SELECT * FROM category;
511 -----+-----------------
518 -- database superuser does bypass RLS policy when disabled
519 RESET SESSION AUTHORIZATION;
520 SET row_security TO OFF;
521 SELECT * FROM document;
522 did | cid | dlevel | dauthor | dtitle
523 -----+-----+--------+-------------------+-------------------------
524 1 | 11 | 1 | regress_rls_bob | my first novel
525 2 | 11 | 2 | regress_rls_bob | my second novel
526 3 | 22 | 2 | regress_rls_bob | my science fiction
527 4 | 44 | 1 | regress_rls_bob | my first manga
528 5 | 44 | 2 | regress_rls_bob | my second manga
529 6 | 22 | 1 | regress_rls_carol | great science fiction
530 7 | 33 | 2 | regress_rls_carol | great technology book
531 8 | 44 | 1 | regress_rls_carol | great manga
532 9 | 22 | 1 | regress_rls_dave | awesome science fiction
533 10 | 33 | 2 | regress_rls_dave | awesome technology book
534 11 | 33 | 1 | regress_rls_carol | hoge
537 SELECT * FROM category;
539 -----+-----------------
546 -- database non-superuser with bypass privilege can bypass RLS policy when disabled
547 SET SESSION AUTHORIZATION regress_rls_exempt_user;
548 SET row_security TO OFF;
549 SELECT * FROM document;
550 did | cid | dlevel | dauthor | dtitle
551 -----+-----+--------+-------------------+-------------------------
552 1 | 11 | 1 | regress_rls_bob | my first novel
553 2 | 11 | 2 | regress_rls_bob | my second novel
554 3 | 22 | 2 | regress_rls_bob | my science fiction
555 4 | 44 | 1 | regress_rls_bob | my first manga
556 5 | 44 | 2 | regress_rls_bob | my second manga
557 6 | 22 | 1 | regress_rls_carol | great science fiction
558 7 | 33 | 2 | regress_rls_carol | great technology book
559 8 | 44 | 1 | regress_rls_carol | great manga
560 9 | 22 | 1 | regress_rls_dave | awesome science fiction
561 10 | 33 | 2 | regress_rls_dave | awesome technology book
562 11 | 33 | 1 | regress_rls_carol | hoge
565 SELECT * FROM category;
567 -----+-----------------
574 -- RLS policy does not apply to table owner when RLS enabled.
575 SET SESSION AUTHORIZATION regress_rls_alice;
576 SET row_security TO ON;
577 SELECT * FROM document;
578 did | cid | dlevel | dauthor | dtitle
579 -----+-----+--------+-------------------+-------------------------
580 1 | 11 | 1 | regress_rls_bob | my first novel
581 2 | 11 | 2 | regress_rls_bob | my second novel
582 3 | 22 | 2 | regress_rls_bob | my science fiction
583 4 | 44 | 1 | regress_rls_bob | my first manga
584 5 | 44 | 2 | regress_rls_bob | my second manga
585 6 | 22 | 1 | regress_rls_carol | great science fiction
586 7 | 33 | 2 | regress_rls_carol | great technology book
587 8 | 44 | 1 | regress_rls_carol | great manga
588 9 | 22 | 1 | regress_rls_dave | awesome science fiction
589 10 | 33 | 2 | regress_rls_dave | awesome technology book
590 11 | 33 | 1 | regress_rls_carol | hoge
593 SELECT * FROM category;
595 -----+-----------------
602 -- RLS policy does not apply to table owner when RLS disabled.
603 SET SESSION AUTHORIZATION regress_rls_alice;
604 SET row_security TO OFF;
605 SELECT * FROM document;
606 did | cid | dlevel | dauthor | dtitle
607 -----+-----+--------+-------------------+-------------------------
608 1 | 11 | 1 | regress_rls_bob | my first novel
609 2 | 11 | 2 | regress_rls_bob | my second novel
610 3 | 22 | 2 | regress_rls_bob | my science fiction
611 4 | 44 | 1 | regress_rls_bob | my first manga
612 5 | 44 | 2 | regress_rls_bob | my second manga
613 6 | 22 | 1 | regress_rls_carol | great science fiction
614 7 | 33 | 2 | regress_rls_carol | great technology book
615 8 | 44 | 1 | regress_rls_carol | great manga
616 9 | 22 | 1 | regress_rls_dave | awesome science fiction
617 10 | 33 | 2 | regress_rls_dave | awesome technology book
618 11 | 33 | 1 | regress_rls_carol | hoge
621 SELECT * FROM category;
623 -----+-----------------
631 -- Table inheritance and RLS policy
633 SET SESSION AUTHORIZATION regress_rls_alice;
634 SET row_security TO ON;
635 CREATE TABLE t1 (id int not null primary key, a int, junk1 text, b text);
636 ALTER TABLE t1 DROP COLUMN junk1; -- just a disturbing factor
637 GRANT ALL ON t1 TO public;
638 COPY t1 FROM stdin WITH ;
639 CREATE TABLE t2 (c float) INHERITS (t1);
640 GRANT ALL ON t2 TO public;
642 CREATE TABLE t3 (id int not null primary key, c text, b text, a int);
643 ALTER TABLE t3 INHERIT t1;
644 GRANT ALL ON t3 TO public;
645 COPY t3(id, a,b,c) FROM stdin;
646 CREATE POLICY p1 ON t1 FOR ALL TO PUBLIC USING (a % 2 = 0); -- be even number
647 CREATE POLICY p2 ON t2 FOR ALL TO PUBLIC USING (a % 2 = 1); -- be odd number
648 ALTER TABLE t1 ENABLE ROW LEVEL SECURITY;
649 ALTER TABLE t2 ENABLE ROW LEVEL SECURITY;
650 SET SESSION AUTHORIZATION regress_rls_bob;
661 EXPLAIN (COSTS OFF) SELECT * FROM t1;
663 -------------------------------
665 -> Seq Scan on t1 t1_1
666 Filter: ((a % 2) = 0)
667 -> Seq Scan on t2 t1_2
668 Filter: ((a % 2) = 0)
669 -> Seq Scan on t3 t1_3
670 Filter: ((a % 2) = 0)
673 SELECT * FROM t1 WHERE f_leak(b);
674 NOTICE: f_leak => bbb
675 NOTICE: f_leak => dad
676 NOTICE: f_leak => bcd
677 NOTICE: f_leak => def
678 NOTICE: f_leak => yyy
688 EXPLAIN (COSTS OFF) SELECT * FROM t1 WHERE f_leak(b);
690 -----------------------------------------------
692 -> Seq Scan on t1 t1_1
693 Filter: (((a % 2) = 0) AND f_leak(b))
694 -> Seq Scan on t2 t1_2
695 Filter: (((a % 2) = 0) AND f_leak(b))
696 -> Seq Scan on t3 t1_3
697 Filter: (((a % 2) = 0) AND f_leak(b))
700 -- reference to system column
701 SELECT tableoid::regclass, * FROM t1;
702 tableoid | id | a | b
703 ----------+-----+---+-----
711 EXPLAIN (COSTS OFF) SELECT *, t1 FROM t1;
713 -------------------------------
715 -> Seq Scan on t1 t1_1
716 Filter: ((a % 2) = 0)
717 -> Seq Scan on t2 t1_2
718 Filter: ((a % 2) = 0)
719 -> Seq Scan on t3 t1_3
720 Filter: ((a % 2) = 0)
723 -- reference to whole-row reference
724 SELECT *, t1 FROM t1;
726 -----+---+-----+-------------
727 102 | 2 | bbb | (102,2,bbb)
728 104 | 4 | dad | (104,4,dad)
729 202 | 2 | bcd | (202,2,bcd)
730 204 | 4 | def | (204,4,def)
731 302 | 2 | yyy | (302,2,yyy)
734 EXPLAIN (COSTS OFF) SELECT *, t1 FROM t1;
736 -------------------------------
738 -> Seq Scan on t1 t1_1
739 Filter: ((a % 2) = 0)
740 -> Seq Scan on t2 t1_2
741 Filter: ((a % 2) = 0)
742 -> Seq Scan on t3 t1_3
743 Filter: ((a % 2) = 0)
746 -- for share/update lock
747 SELECT * FROM t1 FOR SHARE;
757 EXPLAIN (COSTS OFF) SELECT * FROM t1 FOR SHARE;
759 -------------------------------------
762 -> Seq Scan on t1 t1_1
763 Filter: ((a % 2) = 0)
764 -> Seq Scan on t2 t1_2
765 Filter: ((a % 2) = 0)
766 -> Seq Scan on t3 t1_3
767 Filter: ((a % 2) = 0)
770 SELECT * FROM t1 WHERE f_leak(b) FOR SHARE;
771 NOTICE: f_leak => bbb
772 NOTICE: f_leak => dad
773 NOTICE: f_leak => bcd
774 NOTICE: f_leak => def
775 NOTICE: f_leak => yyy
785 EXPLAIN (COSTS OFF) SELECT * FROM t1 WHERE f_leak(b) FOR SHARE;
787 -----------------------------------------------------
790 -> Seq Scan on t1 t1_1
791 Filter: (((a % 2) = 0) AND f_leak(b))
792 -> Seq Scan on t2 t1_2
793 Filter: (((a % 2) = 0) AND f_leak(b))
794 -> Seq Scan on t3 t1_3
795 Filter: (((a % 2) = 0) AND f_leak(b))
799 SELECT a, b, tableoid::regclass FROM t2 UNION ALL SELECT a, b, tableoid::regclass FROM t3;
809 EXPLAIN (COSTS OFF) SELECT a, b, tableoid::regclass FROM t2 UNION ALL SELECT a, b, tableoid::regclass FROM t3;
811 -------------------------------
814 Filter: ((a % 2) = 1)
818 -- superuser is allowed to bypass RLS checks
819 RESET SESSION AUTHORIZATION;
820 SET row_security TO OFF;
821 SELECT * FROM t1 WHERE f_leak(b);
822 NOTICE: f_leak => aba
823 NOTICE: f_leak => bbb
824 NOTICE: f_leak => ccc
825 NOTICE: f_leak => dad
826 NOTICE: f_leak => abc
827 NOTICE: f_leak => bcd
828 NOTICE: f_leak => cde
829 NOTICE: f_leak => def
830 NOTICE: f_leak => xxx
831 NOTICE: f_leak => yyy
832 NOTICE: f_leak => zzz
848 EXPLAIN (COSTS OFF) SELECT * FROM t1 WHERE f_leak(b);
850 ---------------------------
852 -> Seq Scan on t1 t1_1
854 -> Seq Scan on t2 t1_2
856 -> Seq Scan on t3 t1_3
860 -- non-superuser with bypass privilege can bypass RLS policy when disabled
861 SET SESSION AUTHORIZATION regress_rls_exempt_user;
862 SET row_security TO OFF;
863 SELECT * FROM t1 WHERE f_leak(b);
864 NOTICE: f_leak => aba
865 NOTICE: f_leak => bbb
866 NOTICE: f_leak => ccc
867 NOTICE: f_leak => dad
868 NOTICE: f_leak => abc
869 NOTICE: f_leak => bcd
870 NOTICE: f_leak => cde
871 NOTICE: f_leak => def
872 NOTICE: f_leak => xxx
873 NOTICE: f_leak => yyy
874 NOTICE: f_leak => zzz
890 EXPLAIN (COSTS OFF) SELECT * FROM t1 WHERE f_leak(b);
892 ---------------------------
894 -> Seq Scan on t1 t1_1
896 -> Seq Scan on t2 t1_2
898 -> Seq Scan on t3 t1_3
903 -- Partitioned Tables
905 SET SESSION AUTHORIZATION regress_rls_alice;
906 CREATE TABLE part_document (
912 ) PARTITION BY RANGE (cid);
913 GRANT ALL ON part_document TO public;
914 -- Create partitions for document categories
915 CREATE TABLE part_document_fiction PARTITION OF part_document FOR VALUES FROM (11) to (12);
916 CREATE TABLE part_document_satire PARTITION OF part_document FOR VALUES FROM (55) to (56);
917 CREATE TABLE part_document_nonfiction PARTITION OF part_document FOR VALUES FROM (99) to (100);
918 GRANT ALL ON part_document_fiction TO public;
919 GRANT ALL ON part_document_satire TO public;
920 GRANT ALL ON part_document_nonfiction TO public;
921 INSERT INTO part_document VALUES
922 ( 1, 11, 1, 'regress_rls_bob', 'my first novel'),
923 ( 2, 11, 2, 'regress_rls_bob', 'my second novel'),
924 ( 3, 99, 2, 'regress_rls_bob', 'my science textbook'),
925 ( 4, 55, 1, 'regress_rls_bob', 'my first satire'),
926 ( 5, 99, 2, 'regress_rls_bob', 'my history book'),
927 ( 6, 11, 1, 'regress_rls_carol', 'great science fiction'),
928 ( 7, 99, 2, 'regress_rls_carol', 'great technology book'),
929 ( 8, 55, 2, 'regress_rls_carol', 'great satire'),
930 ( 9, 11, 1, 'regress_rls_dave', 'awesome science fiction'),
931 (10, 99, 2, 'regress_rls_dave', 'awesome technology book');
932 ALTER TABLE part_document ENABLE ROW LEVEL SECURITY;
933 -- Create policy on parent
934 -- user's security level must be higher than or equal to document's
935 CREATE POLICY pp1 ON part_document AS PERMISSIVE
936 USING (dlevel <= (SELECT seclv FROM uaccount WHERE pguser = current_user));
937 -- Dave is only allowed to see cid < 55
938 CREATE POLICY pp1r ON part_document AS RESTRICTIVE TO regress_rls_dave
941 Partitioned table "regress_rls_schema.part_document"
942 Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
943 ---------+---------+-----------+----------+---------+----------+--------------+-------------
944 did | integer | | | | plain | |
945 cid | integer | | | | plain | |
946 dlevel | integer | | not null | | plain | |
947 dauthor | name | | | | plain | |
948 dtitle | text | | | | extended | |
949 Partition key: RANGE (cid)
952 USING ((dlevel <= ( SELECT uaccount.seclv
954 WHERE (uaccount.pguser = CURRENT_USER))))
955 POLICY "pp1r" AS RESTRICTIVE
958 Not-null constraints:
959 "part_document_dlevel_not_null" NOT NULL "dlevel"
960 Partitions: part_document_fiction FOR VALUES FROM (11) TO (12),
961 part_document_nonfiction FOR VALUES FROM (99) TO (100),
962 part_document_satire FOR VALUES FROM (55) TO (56)
964 SELECT * FROM pg_policies WHERE schemaname = 'regress_rls_schema' AND tablename like '%part_document%' ORDER BY policyname;
965 schemaname | tablename | policyname | permissive | roles | cmd | qual | with_check
966 --------------------+---------------+------------+-------------+--------------------+-----+--------------------------------------------+------------
967 regress_rls_schema | part_document | pp1 | PERMISSIVE | {public} | ALL | (dlevel <= ( SELECT uaccount.seclv +|
968 | | | | | | FROM uaccount +|
969 | | | | | | WHERE (uaccount.pguser = CURRENT_USER))) |
970 regress_rls_schema | part_document | pp1r | RESTRICTIVE | {regress_rls_dave} | ALL | (cid < 55) |
973 -- viewpoint from regress_rls_bob
974 SET SESSION AUTHORIZATION regress_rls_bob;
975 SET row_security TO ON;
976 SELECT * FROM part_document WHERE f_leak(dtitle) ORDER BY did;
977 NOTICE: f_leak => my first novel
978 NOTICE: f_leak => great science fiction
979 NOTICE: f_leak => awesome science fiction
980 NOTICE: f_leak => my first satire
981 did | cid | dlevel | dauthor | dtitle
982 -----+-----+--------+-------------------+-------------------------
983 1 | 11 | 1 | regress_rls_bob | my first novel
984 4 | 55 | 1 | regress_rls_bob | my first satire
985 6 | 11 | 1 | regress_rls_carol | great science fiction
986 9 | 11 | 1 | regress_rls_dave | awesome science fiction
989 EXPLAIN (COSTS OFF) SELECT * FROM part_document WHERE f_leak(dtitle);
991 --------------------------------------------------------------------
994 -> Index Scan using uaccount_pkey on uaccount
995 Index Cond: (pguser = CURRENT_USER)
996 -> Seq Scan on part_document_fiction part_document_1
997 Filter: ((dlevel <= (InitPlan 1).col1) AND f_leak(dtitle))
998 -> Seq Scan on part_document_satire part_document_2
999 Filter: ((dlevel <= (InitPlan 1).col1) AND f_leak(dtitle))
1000 -> Seq Scan on part_document_nonfiction part_document_3
1001 Filter: ((dlevel <= (InitPlan 1).col1) AND f_leak(dtitle))
1004 -- viewpoint from regress_rls_carol
1005 SET SESSION AUTHORIZATION regress_rls_carol;
1006 SELECT * FROM part_document WHERE f_leak(dtitle) ORDER BY did;
1007 NOTICE: f_leak => my first novel
1008 NOTICE: f_leak => my second novel
1009 NOTICE: f_leak => great science fiction
1010 NOTICE: f_leak => awesome science fiction
1011 NOTICE: f_leak => my first satire
1012 NOTICE: f_leak => great satire
1013 NOTICE: f_leak => my science textbook
1014 NOTICE: f_leak => my history book
1015 NOTICE: f_leak => great technology book
1016 NOTICE: f_leak => awesome technology book
1017 did | cid | dlevel | dauthor | dtitle
1018 -----+-----+--------+-------------------+-------------------------
1019 1 | 11 | 1 | regress_rls_bob | my first novel
1020 2 | 11 | 2 | regress_rls_bob | my second novel
1021 3 | 99 | 2 | regress_rls_bob | my science textbook
1022 4 | 55 | 1 | regress_rls_bob | my first satire
1023 5 | 99 | 2 | regress_rls_bob | my history book
1024 6 | 11 | 1 | regress_rls_carol | great science fiction
1025 7 | 99 | 2 | regress_rls_carol | great technology book
1026 8 | 55 | 2 | regress_rls_carol | great satire
1027 9 | 11 | 1 | regress_rls_dave | awesome science fiction
1028 10 | 99 | 2 | regress_rls_dave | awesome technology book
1031 EXPLAIN (COSTS OFF) SELECT * FROM part_document WHERE f_leak(dtitle);
1033 --------------------------------------------------------------------
1036 -> Index Scan using uaccount_pkey on uaccount
1037 Index Cond: (pguser = CURRENT_USER)
1038 -> Seq Scan on part_document_fiction part_document_1
1039 Filter: ((dlevel <= (InitPlan 1).col1) AND f_leak(dtitle))
1040 -> Seq Scan on part_document_satire part_document_2
1041 Filter: ((dlevel <= (InitPlan 1).col1) AND f_leak(dtitle))
1042 -> Seq Scan on part_document_nonfiction part_document_3
1043 Filter: ((dlevel <= (InitPlan 1).col1) AND f_leak(dtitle))
1046 -- viewpoint from regress_rls_dave
1047 SET SESSION AUTHORIZATION regress_rls_dave;
1048 SELECT * FROM part_document WHERE f_leak(dtitle) ORDER BY did;
1049 NOTICE: f_leak => my first novel
1050 NOTICE: f_leak => my second novel
1051 NOTICE: f_leak => great science fiction
1052 NOTICE: f_leak => awesome science fiction
1053 did | cid | dlevel | dauthor | dtitle
1054 -----+-----+--------+-------------------+-------------------------
1055 1 | 11 | 1 | regress_rls_bob | my first novel
1056 2 | 11 | 2 | regress_rls_bob | my second novel
1057 6 | 11 | 1 | regress_rls_carol | great science fiction
1058 9 | 11 | 1 | regress_rls_dave | awesome science fiction
1061 EXPLAIN (COSTS OFF) SELECT * FROM part_document WHERE f_leak(dtitle);
1063 -----------------------------------------------------------------------------
1064 Seq Scan on part_document_fiction part_document
1065 Filter: ((cid < 55) AND (dlevel <= (InitPlan 1).col1) AND f_leak(dtitle))
1067 -> Index Scan using uaccount_pkey on uaccount
1068 Index Cond: (pguser = CURRENT_USER)
1072 INSERT INTO part_document VALUES (100, 11, 5, 'regress_rls_dave', 'testing pp1'); -- fail
1073 ERROR: new row violates row-level security policy for table "part_document"
1075 INSERT INTO part_document VALUES (100, 99, 1, 'regress_rls_dave', 'testing pp1r'); -- fail
1076 ERROR: new row violates row-level security policy "pp1r" for table "part_document"
1077 -- Show that RLS policy does not apply for direct inserts to children
1078 -- This should fail with RLS POLICY pp1r violation.
1079 INSERT INTO part_document VALUES (100, 55, 1, 'regress_rls_dave', 'testing RLS with partitions'); -- fail
1080 ERROR: new row violates row-level security policy "pp1r" for table "part_document"
1081 -- But this should succeed.
1082 INSERT INTO part_document_satire VALUES (100, 55, 1, 'regress_rls_dave', 'testing RLS with partitions'); -- success
1083 -- We still cannot see the row using the parent
1084 SELECT * FROM part_document WHERE f_leak(dtitle) ORDER BY did;
1085 NOTICE: f_leak => my first novel
1086 NOTICE: f_leak => my second novel
1087 NOTICE: f_leak => great science fiction
1088 NOTICE: f_leak => awesome science fiction
1089 did | cid | dlevel | dauthor | dtitle
1090 -----+-----+--------+-------------------+-------------------------
1091 1 | 11 | 1 | regress_rls_bob | my first novel
1092 2 | 11 | 2 | regress_rls_bob | my second novel
1093 6 | 11 | 1 | regress_rls_carol | great science fiction
1094 9 | 11 | 1 | regress_rls_dave | awesome science fiction
1097 -- But we can if we look directly
1098 SELECT * FROM part_document_satire WHERE f_leak(dtitle) ORDER BY did;
1099 NOTICE: f_leak => my first satire
1100 NOTICE: f_leak => great satire
1101 NOTICE: f_leak => testing RLS with partitions
1102 did | cid | dlevel | dauthor | dtitle
1103 -----+-----+--------+-------------------+-----------------------------
1104 4 | 55 | 1 | regress_rls_bob | my first satire
1105 8 | 55 | 2 | regress_rls_carol | great satire
1106 100 | 55 | 1 | regress_rls_dave | testing RLS with partitions
1109 -- Turn on RLS and create policy on child to show RLS is checked before constraints
1110 SET SESSION AUTHORIZATION regress_rls_alice;
1111 ALTER TABLE part_document_satire ENABLE ROW LEVEL SECURITY;
1112 CREATE POLICY pp3 ON part_document_satire AS RESTRICTIVE
1114 -- This should fail with RLS violation now.
1115 SET SESSION AUTHORIZATION regress_rls_dave;
1116 INSERT INTO part_document_satire VALUES (101, 55, 1, 'regress_rls_dave', 'testing RLS with partitions'); -- fail
1117 ERROR: new row violates row-level security policy for table "part_document_satire"
1118 -- And now we cannot see directly into the partition either, due to RLS
1119 SELECT * FROM part_document_satire WHERE f_leak(dtitle) ORDER BY did;
1120 did | cid | dlevel | dauthor | dtitle
1121 -----+-----+--------+---------+--------
1124 -- The parent looks same as before
1125 -- viewpoint from regress_rls_dave
1126 SELECT * FROM part_document WHERE f_leak(dtitle) ORDER BY did;
1127 NOTICE: f_leak => my first novel
1128 NOTICE: f_leak => my second novel
1129 NOTICE: f_leak => great science fiction
1130 NOTICE: f_leak => awesome science fiction
1131 did | cid | dlevel | dauthor | dtitle
1132 -----+-----+--------+-------------------+-------------------------
1133 1 | 11 | 1 | regress_rls_bob | my first novel
1134 2 | 11 | 2 | regress_rls_bob | my second novel
1135 6 | 11 | 1 | regress_rls_carol | great science fiction
1136 9 | 11 | 1 | regress_rls_dave | awesome science fiction
1139 EXPLAIN (COSTS OFF) SELECT * FROM part_document WHERE f_leak(dtitle);
1141 -----------------------------------------------------------------------------
1142 Seq Scan on part_document_fiction part_document
1143 Filter: ((cid < 55) AND (dlevel <= (InitPlan 1).col1) AND f_leak(dtitle))
1145 -> Index Scan using uaccount_pkey on uaccount
1146 Index Cond: (pguser = CURRENT_USER)
1149 -- viewpoint from regress_rls_carol
1150 SET SESSION AUTHORIZATION regress_rls_carol;
1151 SELECT * FROM part_document WHERE f_leak(dtitle) ORDER BY did;
1152 NOTICE: f_leak => my first novel
1153 NOTICE: f_leak => my second novel
1154 NOTICE: f_leak => great science fiction
1155 NOTICE: f_leak => awesome science fiction
1156 NOTICE: f_leak => my first satire
1157 NOTICE: f_leak => great satire
1158 NOTICE: f_leak => testing RLS with partitions
1159 NOTICE: f_leak => my science textbook
1160 NOTICE: f_leak => my history book
1161 NOTICE: f_leak => great technology book
1162 NOTICE: f_leak => awesome technology book
1163 did | cid | dlevel | dauthor | dtitle
1164 -----+-----+--------+-------------------+-----------------------------
1165 1 | 11 | 1 | regress_rls_bob | my first novel
1166 2 | 11 | 2 | regress_rls_bob | my second novel
1167 3 | 99 | 2 | regress_rls_bob | my science textbook
1168 4 | 55 | 1 | regress_rls_bob | my first satire
1169 5 | 99 | 2 | regress_rls_bob | my history book
1170 6 | 11 | 1 | regress_rls_carol | great science fiction
1171 7 | 99 | 2 | regress_rls_carol | great technology book
1172 8 | 55 | 2 | regress_rls_carol | great satire
1173 9 | 11 | 1 | regress_rls_dave | awesome science fiction
1174 10 | 99 | 2 | regress_rls_dave | awesome technology book
1175 100 | 55 | 1 | regress_rls_dave | testing RLS with partitions
1178 EXPLAIN (COSTS OFF) SELECT * FROM part_document WHERE f_leak(dtitle);
1180 --------------------------------------------------------------------
1183 -> Index Scan using uaccount_pkey on uaccount
1184 Index Cond: (pguser = CURRENT_USER)
1185 -> Seq Scan on part_document_fiction part_document_1
1186 Filter: ((dlevel <= (InitPlan 1).col1) AND f_leak(dtitle))
1187 -> Seq Scan on part_document_satire part_document_2
1188 Filter: ((dlevel <= (InitPlan 1).col1) AND f_leak(dtitle))
1189 -> Seq Scan on part_document_nonfiction part_document_3
1190 Filter: ((dlevel <= (InitPlan 1).col1) AND f_leak(dtitle))
1193 -- only owner can change policies
1194 ALTER POLICY pp1 ON part_document USING (true); --fail
1195 ERROR: must be owner of table part_document
1196 DROP POLICY pp1 ON part_document; --fail
1197 ERROR: must be owner of relation part_document
1198 SET SESSION AUTHORIZATION regress_rls_alice;
1199 ALTER POLICY pp1 ON part_document USING (dauthor = current_user);
1200 -- viewpoint from regress_rls_bob again
1201 SET SESSION AUTHORIZATION regress_rls_bob;
1202 SELECT * FROM part_document WHERE f_leak(dtitle) ORDER BY did;
1203 NOTICE: f_leak => my first novel
1204 NOTICE: f_leak => my second novel
1205 NOTICE: f_leak => my first satire
1206 NOTICE: f_leak => my science textbook
1207 NOTICE: f_leak => my history book
1208 did | cid | dlevel | dauthor | dtitle
1209 -----+-----+--------+-----------------+---------------------
1210 1 | 11 | 1 | regress_rls_bob | my first novel
1211 2 | 11 | 2 | regress_rls_bob | my second novel
1212 3 | 99 | 2 | regress_rls_bob | my science textbook
1213 4 | 55 | 1 | regress_rls_bob | my first satire
1214 5 | 99 | 2 | regress_rls_bob | my history book
1217 -- viewpoint from rls_regres_carol again
1218 SET SESSION AUTHORIZATION regress_rls_carol;
1219 SELECT * FROM part_document WHERE f_leak(dtitle) ORDER BY did;
1220 NOTICE: f_leak => great science fiction
1221 NOTICE: f_leak => great satire
1222 NOTICE: f_leak => great technology book
1223 did | cid | dlevel | dauthor | dtitle
1224 -----+-----+--------+-------------------+-----------------------
1225 6 | 11 | 1 | regress_rls_carol | great science fiction
1226 7 | 99 | 2 | regress_rls_carol | great technology book
1227 8 | 55 | 2 | regress_rls_carol | great satire
1230 EXPLAIN (COSTS OFF) SELECT * FROM part_document WHERE f_leak(dtitle);
1232 ---------------------------------------------------------------
1234 -> Seq Scan on part_document_fiction part_document_1
1235 Filter: ((dauthor = CURRENT_USER) AND f_leak(dtitle))
1236 -> Seq Scan on part_document_satire part_document_2
1237 Filter: ((dauthor = CURRENT_USER) AND f_leak(dtitle))
1238 -> Seq Scan on part_document_nonfiction part_document_3
1239 Filter: ((dauthor = CURRENT_USER) AND f_leak(dtitle))
1242 -- database superuser does bypass RLS policy when enabled
1243 RESET SESSION AUTHORIZATION;
1244 SET row_security TO ON;
1245 SELECT * FROM part_document ORDER BY did;
1246 did | cid | dlevel | dauthor | dtitle
1247 -----+-----+--------+-------------------+-----------------------------
1248 1 | 11 | 1 | regress_rls_bob | my first novel
1249 2 | 11 | 2 | regress_rls_bob | my second novel
1250 3 | 99 | 2 | regress_rls_bob | my science textbook
1251 4 | 55 | 1 | regress_rls_bob | my first satire
1252 5 | 99 | 2 | regress_rls_bob | my history book
1253 6 | 11 | 1 | regress_rls_carol | great science fiction
1254 7 | 99 | 2 | regress_rls_carol | great technology book
1255 8 | 55 | 2 | regress_rls_carol | great satire
1256 9 | 11 | 1 | regress_rls_dave | awesome science fiction
1257 10 | 99 | 2 | regress_rls_dave | awesome technology book
1258 100 | 55 | 1 | regress_rls_dave | testing RLS with partitions
1261 SELECT * FROM part_document_satire ORDER by did;
1262 did | cid | dlevel | dauthor | dtitle
1263 -----+-----+--------+-------------------+-----------------------------
1264 4 | 55 | 1 | regress_rls_bob | my first satire
1265 8 | 55 | 2 | regress_rls_carol | great satire
1266 100 | 55 | 1 | regress_rls_dave | testing RLS with partitions
1269 -- database non-superuser with bypass privilege can bypass RLS policy when disabled
1270 SET SESSION AUTHORIZATION regress_rls_exempt_user;
1271 SET row_security TO OFF;
1272 SELECT * FROM part_document ORDER BY did;
1273 did | cid | dlevel | dauthor | dtitle
1274 -----+-----+--------+-------------------+-----------------------------
1275 1 | 11 | 1 | regress_rls_bob | my first novel
1276 2 | 11 | 2 | regress_rls_bob | my second novel
1277 3 | 99 | 2 | regress_rls_bob | my science textbook
1278 4 | 55 | 1 | regress_rls_bob | my first satire
1279 5 | 99 | 2 | regress_rls_bob | my history book
1280 6 | 11 | 1 | regress_rls_carol | great science fiction
1281 7 | 99 | 2 | regress_rls_carol | great technology book
1282 8 | 55 | 2 | regress_rls_carol | great satire
1283 9 | 11 | 1 | regress_rls_dave | awesome science fiction
1284 10 | 99 | 2 | regress_rls_dave | awesome technology book
1285 100 | 55 | 1 | regress_rls_dave | testing RLS with partitions
1288 SELECT * FROM part_document_satire ORDER by did;
1289 did | cid | dlevel | dauthor | dtitle
1290 -----+-----+--------+-------------------+-----------------------------
1291 4 | 55 | 1 | regress_rls_bob | my first satire
1292 8 | 55 | 2 | regress_rls_carol | great satire
1293 100 | 55 | 1 | regress_rls_dave | testing RLS with partitions
1296 -- RLS policy does not apply to table owner when RLS enabled.
1297 SET SESSION AUTHORIZATION regress_rls_alice;
1298 SET row_security TO ON;
1299 SELECT * FROM part_document ORDER by did;
1300 did | cid | dlevel | dauthor | dtitle
1301 -----+-----+--------+-------------------+-----------------------------
1302 1 | 11 | 1 | regress_rls_bob | my first novel
1303 2 | 11 | 2 | regress_rls_bob | my second novel
1304 3 | 99 | 2 | regress_rls_bob | my science textbook
1305 4 | 55 | 1 | regress_rls_bob | my first satire
1306 5 | 99 | 2 | regress_rls_bob | my history book
1307 6 | 11 | 1 | regress_rls_carol | great science fiction
1308 7 | 99 | 2 | regress_rls_carol | great technology book
1309 8 | 55 | 2 | regress_rls_carol | great satire
1310 9 | 11 | 1 | regress_rls_dave | awesome science fiction
1311 10 | 99 | 2 | regress_rls_dave | awesome technology book
1312 100 | 55 | 1 | regress_rls_dave | testing RLS with partitions
1315 SELECT * FROM part_document_satire ORDER by did;
1316 did | cid | dlevel | dauthor | dtitle
1317 -----+-----+--------+-------------------+-----------------------------
1318 4 | 55 | 1 | regress_rls_bob | my first satire
1319 8 | 55 | 2 | regress_rls_carol | great satire
1320 100 | 55 | 1 | regress_rls_dave | testing RLS with partitions
1323 -- When RLS disabled, other users get ERROR.
1324 SET SESSION AUTHORIZATION regress_rls_dave;
1325 SET row_security TO OFF;
1326 SELECT * FROM part_document ORDER by did;
1327 ERROR: query would be affected by row-level security policy for table "part_document"
1328 SELECT * FROM part_document_satire ORDER by did;
1329 ERROR: query would be affected by row-level security policy for table "part_document_satire"
1330 -- Check behavior with a policy that uses a SubPlan not an InitPlan.
1331 SET SESSION AUTHORIZATION regress_rls_alice;
1332 SET row_security TO ON;
1333 CREATE POLICY pp3 ON part_document AS RESTRICTIVE
1334 USING ((SELECT dlevel <= seclv FROM uaccount WHERE pguser = current_user));
1335 SET SESSION AUTHORIZATION regress_rls_carol;
1336 INSERT INTO part_document VALUES (100, 11, 5, 'regress_rls_carol', 'testing pp3'); -- fail
1337 ERROR: new row violates row-level security policy "pp3" for table "part_document"
1338 ----- Dependencies -----
1339 SET SESSION AUTHORIZATION regress_rls_alice;
1340 SET row_security TO ON;
1341 CREATE TABLE dependee (x integer, y integer);
1342 CREATE TABLE dependent (x integer, y integer);
1343 CREATE POLICY d1 ON dependent FOR ALL
1345 USING (x = (SELECT d.x FROM dependee d WHERE d.y = y));
1346 DROP TABLE dependee; -- Should fail without CASCADE due to dependency on row security qual?
1347 ERROR: cannot drop table dependee because other objects depend on it
1348 DETAIL: policy d1 on table dependent depends on table dependee
1349 HINT: Use DROP ... CASCADE to drop the dependent objects too.
1350 DROP TABLE dependee CASCADE;
1351 NOTICE: drop cascades to policy d1 on table dependent
1352 EXPLAIN (COSTS OFF) SELECT * FROM dependent; -- After drop, should be unqualified
1354 -----------------------
1355 Seq Scan on dependent
1358 ----- RECURSION ----
1362 SET SESSION AUTHORIZATION regress_rls_alice;
1363 CREATE TABLE rec1 (x integer, y integer);
1364 CREATE POLICY r1 ON rec1 USING (x = (SELECT r.x FROM rec1 r WHERE y = r.y));
1365 ALTER TABLE rec1 ENABLE ROW LEVEL SECURITY;
1366 SET SESSION AUTHORIZATION regress_rls_bob;
1367 SELECT * FROM rec1; -- fail, direct recursion
1368 ERROR: infinite recursion detected in policy for relation "rec1"
1372 SET SESSION AUTHORIZATION regress_rls_alice;
1373 CREATE TABLE rec2 (a integer, b integer);
1374 ALTER POLICY r1 ON rec1 USING (x = (SELECT a FROM rec2 WHERE b = y));
1375 CREATE POLICY r2 ON rec2 USING (a = (SELECT x FROM rec1 WHERE y = b));
1376 ALTER TABLE rec2 ENABLE ROW LEVEL SECURITY;
1377 SET SESSION AUTHORIZATION regress_rls_bob;
1378 SELECT * FROM rec1; -- fail, mutual recursion
1379 ERROR: infinite recursion detected in policy for relation "rec1"
1381 -- Mutual recursion via views
1383 SET SESSION AUTHORIZATION regress_rls_bob;
1384 CREATE VIEW rec1v AS SELECT * FROM rec1;
1385 CREATE VIEW rec2v AS SELECT * FROM rec2;
1386 SET SESSION AUTHORIZATION regress_rls_alice;
1387 ALTER POLICY r1 ON rec1 USING (x = (SELECT a FROM rec2v WHERE b = y));
1388 ALTER POLICY r2 ON rec2 USING (a = (SELECT x FROM rec1v WHERE y = b));
1389 SET SESSION AUTHORIZATION regress_rls_bob;
1390 SELECT * FROM rec1; -- fail, mutual recursion via views
1391 ERROR: infinite recursion detected in policy for relation "rec1"
1393 -- Mutual recursion via .s.b views
1395 SET SESSION AUTHORIZATION regress_rls_bob;
1396 DROP VIEW rec1v, rec2v CASCADE;
1397 NOTICE: drop cascades to 2 other objects
1398 DETAIL: drop cascades to policy r1 on table rec1
1399 drop cascades to policy r2 on table rec2
1400 CREATE VIEW rec1v WITH (security_barrier) AS SELECT * FROM rec1;
1401 CREATE VIEW rec2v WITH (security_barrier) AS SELECT * FROM rec2;
1402 SET SESSION AUTHORIZATION regress_rls_alice;
1403 CREATE POLICY r1 ON rec1 USING (x = (SELECT a FROM rec2v WHERE b = y));
1404 CREATE POLICY r2 ON rec2 USING (a = (SELECT x FROM rec1v WHERE y = b));
1405 SET SESSION AUTHORIZATION regress_rls_bob;
1406 SELECT * FROM rec1; -- fail, mutual recursion via s.b. views
1407 ERROR: infinite recursion detected in policy for relation "rec1"
1409 -- recursive RLS and VIEWs in policy
1411 SET SESSION AUTHORIZATION regress_rls_alice;
1412 CREATE TABLE s1 (a int, b text);
1413 INSERT INTO s1 (SELECT x, public.fipshash(x::text) FROM generate_series(-10,10) x);
1414 CREATE TABLE s2 (x int, y text);
1415 INSERT INTO s2 (SELECT x, public.fipshash(x::text) FROM generate_series(-6,6) x);
1416 GRANT SELECT ON s1, s2 TO regress_rls_bob;
1417 CREATE POLICY p1 ON s1 USING (a in (select x from s2 where y like '%2f%'));
1418 CREATE POLICY p2 ON s2 USING (x in (select a from s1 where b like '%22%'));
1419 CREATE POLICY p3 ON s1 FOR INSERT WITH CHECK (a = (SELECT a FROM s1));
1420 ALTER TABLE s1 ENABLE ROW LEVEL SECURITY;
1421 ALTER TABLE s2 ENABLE ROW LEVEL SECURITY;
1422 SET SESSION AUTHORIZATION regress_rls_bob;
1423 CREATE VIEW v2 AS SELECT * FROM s2 WHERE y like '%af%';
1424 SELECT * FROM s1 WHERE f_leak(b); -- fail (infinite recursion)
1425 ERROR: infinite recursion detected in policy for relation "s1"
1426 INSERT INTO s1 VALUES (1, 'foo'); -- fail (infinite recursion)
1427 ERROR: infinite recursion detected in policy for relation "s1"
1428 SET SESSION AUTHORIZATION regress_rls_alice;
1429 DROP POLICY p3 on s1;
1430 ALTER POLICY p2 ON s2 USING (x % 2 = 0);
1431 SET SESSION AUTHORIZATION regress_rls_bob;
1432 SELECT * FROM s1 WHERE f_leak(b); -- OK
1433 NOTICE: f_leak => 03b26944890929ff751653acb2f2af79
1435 ----+----------------------------------
1436 -6 | 03b26944890929ff751653acb2f2af79
1439 EXPLAIN (COSTS OFF) SELECT * FROM only s1 WHERE f_leak(b);
1441 ---------------------------------------------------------------
1443 Filter: ((ANY (a = (hashed SubPlan 1).col1)) AND f_leak(b))
1446 Filter: (((x % 2) = 0) AND (y ~~ '%2f%'::text))
1449 SET SESSION AUTHORIZATION regress_rls_alice;
1450 ALTER POLICY p1 ON s1 USING (a in (select x from v2)); -- using VIEW in RLS policy
1451 SET SESSION AUTHORIZATION regress_rls_bob;
1452 SELECT * FROM s1 WHERE f_leak(b); -- OK
1453 NOTICE: f_leak => 03b26944890929ff751653acb2f2af79
1455 ----+----------------------------------
1456 -6 | 03b26944890929ff751653acb2f2af79
1459 EXPLAIN (COSTS OFF) SELECT * FROM s1 WHERE f_leak(b);
1461 ---------------------------------------------------------------
1463 Filter: ((ANY (a = (hashed SubPlan 1).col1)) AND f_leak(b))
1466 Filter: (((x % 2) = 0) AND (y ~~ '%af%'::text))
1469 SELECT (SELECT x FROM s1 LIMIT 1) xx, * FROM s2 WHERE y like '%28%';
1471 ----+----+----------------------------------
1472 -4 | -4 | e5e0093f285a4fb94c3fcc2ad7fd04ed
1475 EXPLAIN (COSTS OFF) SELECT (SELECT x FROM s1 LIMIT 1) xx, * FROM s2 WHERE y like '%28%';
1477 -------------------------------------------------------------------------
1479 Filter: (((x % 2) = 0) AND (y ~~ '%28%'::text))
1483 Filter: (ANY (a = (hashed SubPlan 1).col1))
1485 -> Seq Scan on s2 s2_1
1486 Filter: (((x % 2) = 0) AND (y ~~ '%af%'::text))
1489 SET SESSION AUTHORIZATION regress_rls_alice;
1490 ALTER POLICY p2 ON s2 USING (x in (select a from s1 where b like '%d2%'));
1491 SET SESSION AUTHORIZATION regress_rls_bob;
1492 SELECT * FROM s1 WHERE f_leak(b); -- fail (infinite recursion via view)
1493 ERROR: infinite recursion detected in policy for relation "s1"
1494 -- prepared statement with regress_rls_alice privilege
1495 PREPARE p1(int) AS SELECT * FROM t1 WHERE a <= $1;
1504 EXPLAIN (COSTS OFF) EXECUTE p1(2);
1506 ----------------------------------------------
1508 -> Seq Scan on t1 t1_1
1509 Filter: ((a <= 2) AND ((a % 2) = 0))
1510 -> Seq Scan on t2 t1_2
1511 Filter: ((a <= 2) AND ((a % 2) = 0))
1512 -> Seq Scan on t3 t1_3
1513 Filter: ((a <= 2) AND ((a % 2) = 0))
1516 -- superuser is allowed to bypass RLS checks
1517 RESET SESSION AUTHORIZATION;
1518 SET row_security TO OFF;
1519 SELECT * FROM t1 WHERE f_leak(b);
1520 NOTICE: f_leak => aba
1521 NOTICE: f_leak => bbb
1522 NOTICE: f_leak => ccc
1523 NOTICE: f_leak => dad
1524 NOTICE: f_leak => abc
1525 NOTICE: f_leak => bcd
1526 NOTICE: f_leak => cde
1527 NOTICE: f_leak => def
1528 NOTICE: f_leak => xxx
1529 NOTICE: f_leak => yyy
1530 NOTICE: f_leak => zzz
1546 EXPLAIN (COSTS OFF) SELECT * FROM t1 WHERE f_leak(b);
1548 ---------------------------
1550 -> Seq Scan on t1 t1_1
1552 -> Seq Scan on t2 t1_2
1554 -> Seq Scan on t3 t1_3
1558 -- plan cache should be invalidated
1570 EXPLAIN (COSTS OFF) EXECUTE p1(2);
1572 ---------------------------
1574 -> Seq Scan on t1 t1_1
1576 -> Seq Scan on t2 t1_2
1578 -> Seq Scan on t3 t1_3
1582 PREPARE p2(int) AS SELECT * FROM t1 WHERE a = $1;
1591 EXPLAIN (COSTS OFF) EXECUTE p2(2);
1593 ---------------------------
1595 -> Seq Scan on t1 t1_1
1597 -> Seq Scan on t2 t1_2
1599 -> Seq Scan on t3 t1_3
1603 -- also, case when privilege switch from superuser
1604 SET SESSION AUTHORIZATION regress_rls_bob;
1605 SET row_security TO ON;
1614 EXPLAIN (COSTS OFF) EXECUTE p2(2);
1616 ---------------------------------------------
1618 -> Seq Scan on t1 t1_1
1619 Filter: ((a = 2) AND ((a % 2) = 0))
1620 -> Seq Scan on t2 t1_2
1621 Filter: ((a = 2) AND ((a % 2) = 0))
1622 -> Seq Scan on t3 t1_3
1623 Filter: ((a = 2) AND ((a % 2) = 0))
1627 -- UPDATE / DELETE and Row-level security
1629 SET SESSION AUTHORIZATION regress_rls_bob;
1630 EXPLAIN (COSTS OFF) UPDATE t1 SET b = b || b WHERE f_leak(b);
1632 -----------------------------------------------------------
1639 -> Seq Scan on t1 t1_1
1640 Filter: (((a % 2) = 0) AND f_leak(b))
1641 -> Seq Scan on t2 t1_2
1642 Filter: (((a % 2) = 0) AND f_leak(b))
1643 -> Seq Scan on t3 t1_3
1644 Filter: (((a % 2) = 0) AND f_leak(b))
1647 UPDATE t1 SET b = b || b WHERE f_leak(b);
1648 NOTICE: f_leak => bbb
1649 NOTICE: f_leak => dad
1650 NOTICE: f_leak => bcd
1651 NOTICE: f_leak => def
1652 NOTICE: f_leak => yyy
1653 EXPLAIN (COSTS OFF) UPDATE only t1 SET b = b || '_updt' WHERE f_leak(b);
1655 -----------------------------------------------
1658 Filter: (((a % 2) = 0) AND f_leak(b))
1661 UPDATE only t1 SET b = b || '_updt' WHERE f_leak(b);
1662 NOTICE: f_leak => bbbbbb
1663 NOTICE: f_leak => daddad
1664 -- returning clause with system column
1665 UPDATE only t1 SET b = b WHERE f_leak(b) RETURNING tableoid::regclass, *, t1;
1666 NOTICE: f_leak => bbbbbb_updt
1667 NOTICE: f_leak => daddad_updt
1668 tableoid | id | a | b | t1
1669 ----------+-----+---+-------------+---------------------
1670 t1 | 102 | 2 | bbbbbb_updt | (102,2,bbbbbb_updt)
1671 t1 | 104 | 4 | daddad_updt | (104,4,daddad_updt)
1674 UPDATE t1 SET b = b WHERE f_leak(b) RETURNING *;
1675 NOTICE: f_leak => bbbbbb_updt
1676 NOTICE: f_leak => daddad_updt
1677 NOTICE: f_leak => bcdbcd
1678 NOTICE: f_leak => defdef
1679 NOTICE: f_leak => yyyyyy
1681 -----+---+-------------
1682 102 | 2 | bbbbbb_updt
1683 104 | 4 | daddad_updt
1689 UPDATE t1 SET b = b WHERE f_leak(b) RETURNING tableoid::regclass, *, t1;
1690 NOTICE: f_leak => bbbbbb_updt
1691 NOTICE: f_leak => daddad_updt
1692 NOTICE: f_leak => bcdbcd
1693 NOTICE: f_leak => defdef
1694 NOTICE: f_leak => yyyyyy
1695 tableoid | id | a | b | t1
1696 ----------+-----+---+-------------+---------------------
1697 t1 | 102 | 2 | bbbbbb_updt | (102,2,bbbbbb_updt)
1698 t1 | 104 | 4 | daddad_updt | (104,4,daddad_updt)
1699 t2 | 202 | 2 | bcdbcd | (202,2,bcdbcd)
1700 t2 | 204 | 4 | defdef | (204,4,defdef)
1701 t3 | 302 | 2 | yyyyyy | (302,2,yyyyyy)
1704 -- updates with from clause
1705 EXPLAIN (COSTS OFF) UPDATE t2 SET b=t2.b FROM t3
1706 WHERE t2.a = 3 and t3.a = 2 AND f_leak(t2.b) AND f_leak(t3.b);
1708 -----------------------------------------------------------------
1712 Filter: ((a = 3) AND ((a % 2) = 1) AND f_leak(b))
1714 Filter: ((a = 2) AND f_leak(b))
1717 UPDATE t2 SET b=t2.b FROM t3
1718 WHERE t2.a = 3 and t3.a = 2 AND f_leak(t2.b) AND f_leak(t3.b);
1719 NOTICE: f_leak => cde
1720 NOTICE: f_leak => yyyyyy
1721 EXPLAIN (COSTS OFF) UPDATE t1 SET b=t1.b FROM t2
1722 WHERE t1.a = 3 and t2.a = 3 AND f_leak(t1.b) AND f_leak(t2.b);
1724 -----------------------------------------------------------------------
1731 Filter: ((a = 3) AND ((a % 2) = 1) AND f_leak(b))
1733 -> Seq Scan on t1 t1_1
1734 Filter: ((a = 3) AND ((a % 2) = 0) AND f_leak(b))
1735 -> Seq Scan on t2 t1_2
1736 Filter: ((a = 3) AND ((a % 2) = 0) AND f_leak(b))
1737 -> Seq Scan on t3 t1_3
1738 Filter: ((a = 3) AND ((a % 2) = 0) AND f_leak(b))
1741 UPDATE t1 SET b=t1.b FROM t2
1742 WHERE t1.a = 3 and t2.a = 3 AND f_leak(t1.b) AND f_leak(t2.b);
1743 NOTICE: f_leak => cde
1744 EXPLAIN (COSTS OFF) UPDATE t2 SET b=t2.b FROM t1
1745 WHERE t1.a = 3 and t2.a = 3 AND f_leak(t1.b) AND f_leak(t2.b);
1747 -----------------------------------------------------------------------
1751 Filter: ((a = 3) AND ((a % 2) = 1) AND f_leak(b))
1753 -> Seq Scan on t1 t1_1
1754 Filter: ((a = 3) AND ((a % 2) = 0) AND f_leak(b))
1755 -> Seq Scan on t2 t1_2
1756 Filter: ((a = 3) AND ((a % 2) = 0) AND f_leak(b))
1757 -> Seq Scan on t3 t1_3
1758 Filter: ((a = 3) AND ((a % 2) = 0) AND f_leak(b))
1761 UPDATE t2 SET b=t2.b FROM t1
1762 WHERE t1.a = 3 and t2.a = 3 AND f_leak(t1.b) AND f_leak(t2.b);
1763 NOTICE: f_leak => cde
1764 -- updates with from clause self join
1765 EXPLAIN (COSTS OFF) UPDATE t2 t2_1 SET b = t2_2.b FROM t2 t2_2
1766 WHERE t2_1.a = 3 AND t2_2.a = t2_1.a AND t2_2.b = t2_1.b
1767 AND f_leak(t2_1.b) AND f_leak(t2_2.b) RETURNING *, t2_1, t2_2;
1769 -----------------------------------------------------------------
1772 Join Filter: (t2_1.b = t2_2.b)
1773 -> Seq Scan on t2 t2_1
1774 Filter: ((a = 3) AND ((a % 2) = 1) AND f_leak(b))
1775 -> Seq Scan on t2 t2_2
1776 Filter: ((a = 3) AND ((a % 2) = 1) AND f_leak(b))
1779 UPDATE t2 t2_1 SET b = t2_2.b FROM t2 t2_2
1780 WHERE t2_1.a = 3 AND t2_2.a = t2_1.a AND t2_2.b = t2_1.b
1781 AND f_leak(t2_1.b) AND f_leak(t2_2.b) RETURNING *, t2_1, t2_2;
1782 NOTICE: f_leak => cde
1783 NOTICE: f_leak => cde
1784 id | a | b | c | id | a | b | c | t2_1 | t2_2
1785 -----+---+-----+-----+-----+---+-----+-----+-----------------+-----------------
1786 203 | 3 | cde | 3.3 | 203 | 3 | cde | 3.3 | (203,3,cde,3.3) | (203,3,cde,3.3)
1789 EXPLAIN (COSTS OFF) UPDATE t1 t1_1 SET b = t1_2.b FROM t1 t1_2
1790 WHERE t1_1.a = 4 AND t1_2.a = t1_1.a AND t1_2.b = t1_1.b
1791 AND f_leak(t1_1.b) AND f_leak(t1_2.b) RETURNING *, t1_1, t1_2;
1793 -----------------------------------------------------------------------------
1799 Join Filter: (t1_1.b = t1_2.b)
1801 -> Seq Scan on t1 t1_1_1
1802 Filter: ((a = 4) AND ((a % 2) = 0) AND f_leak(b))
1803 -> Seq Scan on t2 t1_1_2
1804 Filter: ((a = 4) AND ((a % 2) = 0) AND f_leak(b))
1805 -> Seq Scan on t3 t1_1_3
1806 Filter: ((a = 4) AND ((a % 2) = 0) AND f_leak(b))
1809 -> Seq Scan on t1 t1_2_1
1810 Filter: ((a = 4) AND ((a % 2) = 0) AND f_leak(b))
1811 -> Seq Scan on t2 t1_2_2
1812 Filter: ((a = 4) AND ((a % 2) = 0) AND f_leak(b))
1813 -> Seq Scan on t3 t1_2_3
1814 Filter: ((a = 4) AND ((a % 2) = 0) AND f_leak(b))
1817 UPDATE t1 t1_1 SET b = t1_2.b FROM t1 t1_2
1818 WHERE t1_1.a = 4 AND t1_2.a = t1_1.a AND t1_2.b = t1_1.b
1819 AND f_leak(t1_1.b) AND f_leak(t1_2.b) RETURNING *, t1_1, t1_2;
1820 NOTICE: f_leak => daddad_updt
1821 NOTICE: f_leak => daddad_updt
1822 NOTICE: f_leak => defdef
1823 NOTICE: f_leak => defdef
1824 id | a | b | id | a | b | t1_1 | t1_2
1825 -----+---+-------------+-----+---+-------------+---------------------+---------------------
1826 104 | 4 | daddad_updt | 104 | 4 | daddad_updt | (104,4,daddad_updt) | (104,4,daddad_updt)
1827 204 | 4 | defdef | 204 | 4 | defdef | (204,4,defdef) | (204,4,defdef)
1830 RESET SESSION AUTHORIZATION;
1831 SET row_security TO OFF;
1832 SELECT * FROM t1 ORDER BY a,b;
1834 -----+---+-------------
1838 102 | 2 | bbbbbb_updt
1844 104 | 4 | daddad_updt
1848 SET SESSION AUTHORIZATION regress_rls_bob;
1849 SET row_security TO ON;
1850 EXPLAIN (COSTS OFF) DELETE FROM only t1 WHERE f_leak(b);
1852 -----------------------------------------------
1855 Filter: (((a % 2) = 0) AND f_leak(b))
1858 EXPLAIN (COSTS OFF) DELETE FROM t1 WHERE f_leak(b);
1860 -----------------------------------------------------
1866 -> Seq Scan on t1 t1_1
1867 Filter: (((a % 2) = 0) AND f_leak(b))
1868 -> Seq Scan on t2 t1_2
1869 Filter: (((a % 2) = 0) AND f_leak(b))
1870 -> Seq Scan on t3 t1_3
1871 Filter: (((a % 2) = 0) AND f_leak(b))
1874 DELETE FROM only t1 WHERE f_leak(b) RETURNING tableoid::regclass, *, t1;
1875 NOTICE: f_leak => bbbbbb_updt
1876 NOTICE: f_leak => daddad_updt
1877 tableoid | id | a | b | t1
1878 ----------+-----+---+-------------+---------------------
1879 t1 | 102 | 2 | bbbbbb_updt | (102,2,bbbbbb_updt)
1880 t1 | 104 | 4 | daddad_updt | (104,4,daddad_updt)
1883 DELETE FROM t1 WHERE f_leak(b) RETURNING tableoid::regclass, *, t1;
1884 NOTICE: f_leak => bcdbcd
1885 NOTICE: f_leak => defdef
1886 NOTICE: f_leak => yyyyyy
1887 tableoid | id | a | b | t1
1888 ----------+-----+---+--------+----------------
1889 t2 | 202 | 2 | bcdbcd | (202,2,bcdbcd)
1890 t2 | 204 | 4 | defdef | (204,4,defdef)
1891 t3 | 302 | 2 | yyyyyy | (302,2,yyyyyy)
1895 -- S.b. view on top of Row-level security
1897 SET SESSION AUTHORIZATION regress_rls_alice;
1898 CREATE TABLE b1 (a int, b text);
1899 INSERT INTO b1 (SELECT x, public.fipshash(x::text) FROM generate_series(-10,10) x);
1900 CREATE POLICY p1 ON b1 USING (a % 2 = 0);
1901 ALTER TABLE b1 ENABLE ROW LEVEL SECURITY;
1902 GRANT ALL ON b1 TO regress_rls_bob;
1903 SET SESSION AUTHORIZATION regress_rls_bob;
1904 CREATE VIEW bv1 WITH (security_barrier) AS SELECT * FROM b1 WHERE a > 0 WITH CHECK OPTION;
1905 GRANT ALL ON bv1 TO regress_rls_carol;
1906 SET SESSION AUTHORIZATION regress_rls_carol;
1907 EXPLAIN (COSTS OFF) SELECT * FROM bv1 WHERE f_leak(b);
1909 ---------------------------------------------
1910 Subquery Scan on bv1
1911 Filter: f_leak(bv1.b)
1913 Filter: ((a > 0) AND ((a % 2) = 0))
1916 SELECT * FROM bv1 WHERE f_leak(b);
1917 NOTICE: f_leak => d4735e3a265e16eee03f59718b9b5d03
1918 NOTICE: f_leak => 4b227777d4dd1fc61c6f884f48641d02
1919 NOTICE: f_leak => e7f6c011776e8db7cd330b54174fd76f
1920 NOTICE: f_leak => 2c624232cdd221771294dfbb310aca00
1921 NOTICE: f_leak => 4a44dc15364204a80fe80e9039455cc1
1923 ----+----------------------------------
1924 2 | d4735e3a265e16eee03f59718b9b5d03
1925 4 | 4b227777d4dd1fc61c6f884f48641d02
1926 6 | e7f6c011776e8db7cd330b54174fd76f
1927 8 | 2c624232cdd221771294dfbb310aca00
1928 10 | 4a44dc15364204a80fe80e9039455cc1
1931 INSERT INTO bv1 VALUES (-1, 'xxx'); -- should fail view WCO
1932 ERROR: new row violates row-level security policy for table "b1"
1933 INSERT INTO bv1 VALUES (11, 'xxx'); -- should fail RLS check
1934 ERROR: new row violates row-level security policy for table "b1"
1935 INSERT INTO bv1 VALUES (12, 'xxx'); -- ok
1936 EXPLAIN (COSTS OFF) UPDATE bv1 SET b = 'yyy' WHERE a = 4 AND f_leak(b);
1938 -----------------------------------------------------------------------
1941 Filter: ((a > 0) AND (a = 4) AND ((a % 2) = 0) AND f_leak(b))
1944 UPDATE bv1 SET b = 'yyy' WHERE a = 4 AND f_leak(b);
1945 NOTICE: f_leak => 4b227777d4dd1fc61c6f884f48641d02
1946 EXPLAIN (COSTS OFF) DELETE FROM bv1 WHERE a = 6 AND f_leak(b);
1948 -----------------------------------------------------------------------
1951 Filter: ((a > 0) AND (a = 6) AND ((a % 2) = 0) AND f_leak(b))
1954 DELETE FROM bv1 WHERE a = 6 AND f_leak(b);
1955 NOTICE: f_leak => e7f6c011776e8db7cd330b54174fd76f
1956 SET SESSION AUTHORIZATION regress_rls_alice;
1959 -----+----------------------------------
1960 -10 | c171d4ec282b23db89a99880cd624e9b
1961 -9 | d5c534fde62beb89c745a59952c8efed
1962 -8 | e91592205d3881e3ea35d66973bb4898
1963 -7 | a770d3270c9dcdedf12ed9fd70444f7c
1964 -6 | 03b26944890929ff751653acb2f2af79
1965 -5 | 37aa1ccf80e481832b2db282d4d4f895
1966 -4 | e5e0093f285a4fb94c3fcc2ad7fd04ed
1967 -3 | 615bdd17c2556f82f384392ea8557f8c
1968 -2 | cf3bae39dd692048a8bf961182e6a34d
1969 -1 | 1bad6b8cf97131fceab8543e81f77571
1970 0 | 5feceb66ffc86f38d952786c6d696c79
1971 1 | 6b86b273ff34fce19d6b804eff5a3f57
1972 2 | d4735e3a265e16eee03f59718b9b5d03
1973 3 | 4e07408562bedb8b60ce05c1decfe3ad
1974 5 | ef2d127de37b942baad06145e54b0c61
1975 7 | 7902699be42c8a8e46fbbb4501726517
1976 8 | 2c624232cdd221771294dfbb310aca00
1977 9 | 19581e27de7ced00ff1ce50b2047e7a5
1978 10 | 4a44dc15364204a80fe80e9039455cc1
1984 -- INSERT ... ON CONFLICT DO UPDATE and Row-level security
1986 SET SESSION AUTHORIZATION regress_rls_alice;
1987 DROP POLICY p1 ON document;
1988 DROP POLICY p1r ON document;
1989 CREATE POLICY p1 ON document FOR SELECT USING (true);
1990 CREATE POLICY p2 ON document FOR INSERT WITH CHECK (dauthor = current_user);
1991 CREATE POLICY p3 ON document FOR UPDATE
1992 USING (cid = (SELECT cid from category WHERE cname = 'novel'))
1993 WITH CHECK (dauthor = current_user);
1994 SET SESSION AUTHORIZATION regress_rls_bob;
1996 SELECT * FROM document WHERE did = 2;
1997 did | cid | dlevel | dauthor | dtitle
1998 -----+-----+--------+-----------------+-----------------
1999 2 | 11 | 2 | regress_rls_bob | my second novel
2002 -- ...so violates actual WITH CHECK OPTION within UPDATE (not INSERT, since
2003 -- alternative UPDATE path happens to be taken):
2004 INSERT INTO document VALUES (2, (SELECT cid from category WHERE cname = 'novel'), 1, 'regress_rls_carol', 'my first novel')
2005 ON CONFLICT (did) DO UPDATE SET dtitle = EXCLUDED.dtitle, dauthor = EXCLUDED.dauthor;
2006 ERROR: new row violates row-level security policy for table "document"
2007 -- Violates USING qual for UPDATE policy p3.
2009 -- UPDATE path is taken, but UPDATE fails purely because *existing* row to be
2010 -- updated is not a "novel"/cid 11 (row is not leaked, even though we have
2011 -- SELECT privileges sufficient to see the row in this instance):
2012 INSERT INTO document VALUES (33, 22, 1, 'regress_rls_bob', 'okay science fiction'); -- preparation for next statement
2013 INSERT INTO document VALUES (33, (SELECT cid from category WHERE cname = 'novel'), 1, 'regress_rls_bob', 'Some novel, replaces sci-fi') -- takes UPDATE path
2014 ON CONFLICT (did) DO UPDATE SET dtitle = EXCLUDED.dtitle;
2015 ERROR: new row violates row-level security policy (USING expression) for table "document"
2016 -- Fine (we UPDATE, since INSERT WCOs and UPDATE security barrier quals + WCOs
2018 INSERT INTO document VALUES (2, (SELECT cid from category WHERE cname = 'novel'), 1, 'regress_rls_bob', 'my first novel')
2019 ON CONFLICT (did) DO UPDATE SET dtitle = EXCLUDED.dtitle RETURNING *;
2020 did | cid | dlevel | dauthor | dtitle
2021 -----+-----+--------+-----------------+----------------
2022 2 | 11 | 2 | regress_rls_bob | my first novel
2025 -- Fine (we INSERT, so "cid = 33" ("technology") isn't evaluated):
2026 INSERT INTO document VALUES (78, (SELECT cid from category WHERE cname = 'novel'), 1, 'regress_rls_bob', 'some technology novel')
2027 ON CONFLICT (did) DO UPDATE SET dtitle = EXCLUDED.dtitle, cid = 33 RETURNING *;
2028 did | cid | dlevel | dauthor | dtitle
2029 -----+-----+--------+-----------------+-----------------------
2030 78 | 11 | 1 | regress_rls_bob | some technology novel
2033 -- Fine (same query, but we UPDATE, so "cid = 33", ("technology") is not the
2034 -- case in respect of *existing* tuple):
2035 INSERT INTO document VALUES (78, (SELECT cid from category WHERE cname = 'novel'), 1, 'regress_rls_bob', 'some technology novel')
2036 ON CONFLICT (did) DO UPDATE SET dtitle = EXCLUDED.dtitle, cid = 33 RETURNING *;
2037 did | cid | dlevel | dauthor | dtitle
2038 -----+-----+--------+-----------------+-----------------------
2039 78 | 33 | 1 | regress_rls_bob | some technology novel
2042 -- Same query a third time, but now fails due to existing tuple finally not
2044 INSERT INTO document VALUES (78, (SELECT cid from category WHERE cname = 'novel'), 1, 'regress_rls_bob', 'some technology novel')
2045 ON CONFLICT (did) DO UPDATE SET dtitle = EXCLUDED.dtitle, cid = 33 RETURNING *;
2046 ERROR: new row violates row-level security policy (USING expression) for table "document"
2047 -- Don't fail just because INSERT doesn't satisfy WITH CHECK option that
2048 -- originated as a barrier/USING() qual from the UPDATE. Note that the UPDATE
2049 -- path *isn't* taken, and so UPDATE-related policy does not apply:
2050 INSERT INTO document VALUES (79, (SELECT cid from category WHERE cname = 'technology'), 1, 'regress_rls_bob', 'technology book, can only insert')
2051 ON CONFLICT (did) DO UPDATE SET dtitle = EXCLUDED.dtitle RETURNING *;
2052 did | cid | dlevel | dauthor | dtitle
2053 -----+-----+--------+-----------------+----------------------------------
2054 79 | 33 | 1 | regress_rls_bob | technology book, can only insert
2057 -- But this time, the same statement fails, because the UPDATE path is taken,
2058 -- and updating the row just inserted falls afoul of security barrier qual
2059 -- (enforced as WCO) -- what we might have updated target tuple to is
2060 -- irrelevant, in fact.
2061 INSERT INTO document VALUES (79, (SELECT cid from category WHERE cname = 'technology'), 1, 'regress_rls_bob', 'technology book, can only insert')
2062 ON CONFLICT (did) DO UPDATE SET dtitle = EXCLUDED.dtitle RETURNING *;
2063 ERROR: new row violates row-level security policy (USING expression) for table "document"
2064 -- Test default USING qual enforced as WCO
2065 SET SESSION AUTHORIZATION regress_rls_alice;
2066 DROP POLICY p1 ON document;
2067 DROP POLICY p2 ON document;
2068 DROP POLICY p3 ON document;
2069 CREATE POLICY p3_with_default ON document FOR UPDATE
2070 USING (cid = (SELECT cid from category WHERE cname = 'novel'));
2071 SET SESSION AUTHORIZATION regress_rls_bob;
2072 -- Just because WCO-style enforcement of USING quals occurs with
2073 -- existing/target tuple does not mean that the implementation can be allowed
2074 -- to fail to also enforce this qual against the final tuple appended to
2075 -- relation (since in the absence of an explicit WCO, this is also interpreted
2076 -- as an UPDATE/ALL WCO in general).
2078 -- UPDATE path is taken here (fails due to existing tuple). Note that this is
2079 -- not reported as a "USING expression", because it's an RLS UPDATE check that originated as
2080 -- a USING qual for the purposes of RLS in general, as opposed to an explicit
2081 -- USING qual that is ordinarily a security barrier. We leave it up to the
2082 -- UPDATE to make this fail:
2083 INSERT INTO document VALUES (79, (SELECT cid from category WHERE cname = 'technology'), 1, 'regress_rls_bob', 'technology book, can only insert')
2084 ON CONFLICT (did) DO UPDATE SET dtitle = EXCLUDED.dtitle RETURNING *;
2085 ERROR: new row violates row-level security policy for table "document"
2086 -- UPDATE path is taken here. Existing tuple passes, since its cid
2087 -- corresponds to "novel", but default USING qual is enforced against
2088 -- post-UPDATE tuple too (as always when updating with a policy that lacks an
2089 -- explicit WCO), and so this fails:
2090 INSERT INTO document VALUES (2, (SELECT cid from category WHERE cname = 'technology'), 1, 'regress_rls_bob', 'my first novel')
2091 ON CONFLICT (did) DO UPDATE SET cid = EXCLUDED.cid, dtitle = EXCLUDED.dtitle RETURNING *;
2092 ERROR: new row violates row-level security policy for table "document"
2093 SET SESSION AUTHORIZATION regress_rls_alice;
2094 DROP POLICY p3_with_default ON document;
2096 -- Test ALL policies with ON CONFLICT DO UPDATE (much the same as existing UPDATE
2099 CREATE POLICY p3_with_all ON document FOR ALL
2100 USING (cid = (SELECT cid from category WHERE cname = 'novel'))
2101 WITH CHECK (dauthor = current_user);
2102 SET SESSION AUTHORIZATION regress_rls_bob;
2103 -- Fails, since ALL WCO is enforced in insert path:
2104 INSERT INTO document VALUES (80, (SELECT cid from category WHERE cname = 'novel'), 1, 'regress_rls_carol', 'my first novel')
2105 ON CONFLICT (did) DO UPDATE SET dtitle = EXCLUDED.dtitle, cid = 33;
2106 ERROR: new row violates row-level security policy for table "document"
2107 -- Fails, since ALL policy USING qual is enforced (existing, target tuple is in
2108 -- violation, since it has the "manga" cid):
2109 INSERT INTO document VALUES (4, (SELECT cid from category WHERE cname = 'novel'), 1, 'regress_rls_bob', 'my first novel')
2110 ON CONFLICT (did) DO UPDATE SET dtitle = EXCLUDED.dtitle;
2111 ERROR: new row violates row-level security policy (USING expression) for table "document"
2112 -- Fails, since ALL WCO are enforced:
2113 INSERT INTO document VALUES (1, (SELECT cid from category WHERE cname = 'novel'), 1, 'regress_rls_bob', 'my first novel')
2114 ON CONFLICT (did) DO UPDATE SET dauthor = 'regress_rls_carol';
2115 ERROR: new row violates row-level security policy for table "document"
2119 RESET SESSION AUTHORIZATION;
2120 DROP POLICY p3_with_all ON document;
2121 ALTER TABLE document ADD COLUMN dnotes text DEFAULT '';
2122 -- all documents are readable
2123 CREATE POLICY p1 ON document FOR SELECT USING (true);
2124 -- one may insert documents only authored by them
2125 CREATE POLICY p2 ON document FOR INSERT WITH CHECK (dauthor = current_user);
2126 -- one may only update documents in 'novel' category and new dlevel must be > 0
2127 CREATE POLICY p3 ON document FOR UPDATE
2128 USING (cid = (SELECT cid from category WHERE cname = 'novel'))
2129 WITH CHECK (dlevel > 0);
2130 -- one may only delete documents in 'manga' category
2131 CREATE POLICY p4 ON document FOR DELETE
2132 USING (cid = (SELECT cid from category WHERE cname = 'manga'));
2133 SELECT * FROM document;
2134 did | cid | dlevel | dauthor | dtitle | dnotes
2135 -----+-----+--------+-------------------+----------------------------------+--------
2136 1 | 11 | 1 | regress_rls_bob | my first novel |
2137 3 | 22 | 2 | regress_rls_bob | my science fiction |
2138 4 | 44 | 1 | regress_rls_bob | my first manga |
2139 5 | 44 | 2 | regress_rls_bob | my second manga |
2140 6 | 22 | 1 | regress_rls_carol | great science fiction |
2141 7 | 33 | 2 | regress_rls_carol | great technology book |
2142 8 | 44 | 1 | regress_rls_carol | great manga |
2143 9 | 22 | 1 | regress_rls_dave | awesome science fiction |
2144 10 | 33 | 2 | regress_rls_dave | awesome technology book |
2145 11 | 33 | 1 | regress_rls_carol | hoge |
2146 33 | 22 | 1 | regress_rls_bob | okay science fiction |
2147 2 | 11 | 2 | regress_rls_bob | my first novel |
2148 78 | 33 | 1 | regress_rls_bob | some technology novel |
2149 79 | 33 | 1 | regress_rls_bob | technology book, can only insert |
2152 SET SESSION AUTHORIZATION regress_rls_bob;
2153 -- Fails, since update violates WITH CHECK qual on dlevel
2154 MERGE INTO document d
2155 USING (SELECT 1 as sdid) s
2158 UPDATE SET dnotes = dnotes || ' notes added by merge1 ', dlevel = 0;
2159 ERROR: new row violates row-level security policy for table "document"
2160 -- Should be OK since USING and WITH CHECK quals pass
2161 MERGE INTO document d
2162 USING (SELECT 1 as sdid) s
2165 UPDATE SET dnotes = dnotes || ' notes added by merge2 ';
2166 -- Even when dlevel is updated explicitly, but to the existing value
2167 MERGE INTO document d
2168 USING (SELECT 1 as sdid) s
2171 UPDATE SET dnotes = dnotes || ' notes added by merge3 ', dlevel = 1;
2172 -- There is a MATCH for did = 3, but UPDATE's USING qual does not allow
2173 -- updating an item in category 'science fiction'
2174 MERGE INTO document d
2175 USING (SELECT 3 as sdid) s
2178 UPDATE SET dnotes = dnotes || ' notes added by merge ';
2179 ERROR: target row violates row-level security policy (USING expression) for table "document"
2180 -- The same thing with DELETE action, but fails again because no permissions
2181 -- to delete items in 'science fiction' category that did 3 belongs to.
2182 MERGE INTO document d
2183 USING (SELECT 3 as sdid) s
2187 ERROR: target row violates row-level security policy (USING expression) for table "document"
2188 -- Document with did 4 belongs to 'manga' category which is allowed for
2189 -- deletion. But this fails because the UPDATE action is matched first and
2190 -- UPDATE policy does not allow updation in the category.
2191 MERGE INTO document d
2192 USING (SELECT 4 as sdid) s
2194 WHEN MATCHED AND dnotes = '' THEN
2195 UPDATE SET dnotes = dnotes || ' notes added by merge '
2198 ERROR: target row violates row-level security policy (USING expression) for table "document"
2199 -- UPDATE action is not matched this time because of the WHEN qual.
2200 -- DELETE still fails because role regress_rls_bob does not have SELECT
2201 -- privileges on 'manga' category row in the category table.
2202 MERGE INTO document d
2203 USING (SELECT 4 as sdid) s
2205 WHEN MATCHED AND dnotes <> '' THEN
2206 UPDATE SET dnotes = dnotes || ' notes added by merge '
2209 ERROR: target row violates row-level security policy (USING expression) for table "document"
2210 -- OK if DELETE is replaced with DO NOTHING
2211 MERGE INTO document d
2212 USING (SELECT 4 as sdid) s
2214 WHEN MATCHED AND dnotes <> '' THEN
2215 UPDATE SET dnotes = dnotes || ' notes added by merge '
2218 SELECT * FROM document WHERE did = 4;
2219 did | cid | dlevel | dauthor | dtitle | dnotes
2220 -----+-----+--------+-----------------+----------------+--------
2221 4 | 44 | 1 | regress_rls_bob | my first manga |
2224 -- Switch to regress_rls_carol role and try the DELETE again. It should succeed
2226 RESET SESSION AUTHORIZATION;
2227 SET SESSION AUTHORIZATION regress_rls_carol;
2228 MERGE INTO document d
2229 USING (SELECT 4 as sdid) s
2231 WHEN MATCHED AND dnotes <> '' THEN
2232 UPDATE SET dnotes = dnotes || ' notes added by merge '
2235 -- Switch back to regress_rls_bob role
2236 RESET SESSION AUTHORIZATION;
2237 SET SESSION AUTHORIZATION regress_rls_bob;
2238 -- Try INSERT action. This fails because we are trying to insert
2239 -- dauthor = regress_rls_dave and INSERT's WITH CHECK does not allow
2241 MERGE INTO document d
2242 USING (SELECT 12 as sdid) s
2246 WHEN NOT MATCHED THEN
2247 INSERT VALUES (12, 11, 1, 'regress_rls_dave', 'another novel');
2248 ERROR: new row violates row-level security policy for table "document"
2249 -- This should be fine
2250 MERGE INTO document d
2251 USING (SELECT 12 as sdid) s
2255 WHEN NOT MATCHED THEN
2256 INSERT VALUES (12, 11, 1, 'regress_rls_bob', 'another novel');
2258 MERGE INTO document d
2259 USING (SELECT 1 as sdid) s
2262 UPDATE SET dnotes = dnotes || ' notes added by merge4 '
2263 WHEN NOT MATCHED THEN
2264 INSERT VALUES (12, 11, 1, 'regress_rls_bob', 'another novel');
2265 -- drop and create a new SELECT policy which prevents us from reading
2266 -- any document except with category 'novel'
2267 RESET SESSION AUTHORIZATION;
2268 DROP POLICY p1 ON document;
2269 CREATE POLICY p1 ON document FOR SELECT
2270 USING (cid = (SELECT cid from category WHERE cname = 'novel'));
2271 SET SESSION AUTHORIZATION regress_rls_bob;
2272 -- MERGE can no longer see the matching row and hence attempts the
2273 -- NOT MATCHED action, which results in unique key violation
2274 MERGE INTO document d
2275 USING (SELECT 7 as sdid) s
2278 UPDATE SET dnotes = dnotes || ' notes added by merge5 '
2279 WHEN NOT MATCHED THEN
2280 INSERT VALUES (12, 11, 1, 'regress_rls_bob', 'another novel');
2281 ERROR: duplicate key value violates unique constraint "document_pkey"
2282 -- UPDATE action fails if new row is not visible
2283 MERGE INTO document d
2284 USING (SELECT 1 as sdid) s
2287 UPDATE SET dnotes = dnotes || ' notes added by merge6 ',
2288 cid = (SELECT cid from category WHERE cname = 'technology');
2289 ERROR: new row violates row-level security policy for table "document"
2290 -- but OK if new row is visible
2291 MERGE INTO document d
2292 USING (SELECT 1 as sdid) s
2295 UPDATE SET dnotes = dnotes || ' notes added by merge7 ',
2296 cid = (SELECT cid from category WHERE cname = 'novel');
2297 -- OK to insert a new row that is not visible
2298 MERGE INTO document d
2299 USING (SELECT 13 as sdid) s
2302 UPDATE SET dnotes = dnotes || ' notes added by merge8 '
2303 WHEN NOT MATCHED THEN
2304 INSERT VALUES (13, 44, 1, 'regress_rls_bob', 'new manga');
2305 SELECT * FROM document WHERE did = 13;
2306 did | cid | dlevel | dauthor | dtitle | dnotes
2307 -----+-----+--------+---------+--------+--------
2310 -- but not OK if RETURNING is used
2311 MERGE INTO document d
2312 USING (SELECT 14 as sdid) s
2315 UPDATE SET dnotes = dnotes || ' notes added by merge9 '
2316 WHEN NOT MATCHED THEN
2317 INSERT VALUES (14, 44, 1, 'regress_rls_bob', 'new manga')
2319 ERROR: new row violates row-level security policy for table "document"
2320 -- but OK if new row is visible
2321 MERGE INTO document d
2322 USING (SELECT 14 as sdid) s
2325 UPDATE SET dnotes = dnotes || ' notes added by merge10 '
2326 WHEN NOT MATCHED THEN
2327 INSERT VALUES (14, 11, 1, 'regress_rls_bob', 'new novel')
2329 sdid | did | cid | dlevel | dauthor | dtitle | dnotes
2330 ------+-----+-----+--------+-----------------+-----------+--------
2331 14 | 14 | 11 | 1 | regress_rls_bob | new novel |
2334 RESET SESSION AUTHORIZATION;
2335 -- drop the restrictive SELECT policy so that we can look at the
2336 -- final state of the table
2337 DROP POLICY p1 ON document;
2338 -- Just check everything went per plan
2339 SELECT * FROM document;
2340 did | cid | dlevel | dauthor | dtitle | dnotes
2341 -----+-----+--------+-------------------+----------------------------------+----------------------------------------------------------------------------------------------
2342 3 | 22 | 2 | regress_rls_bob | my science fiction |
2343 5 | 44 | 2 | regress_rls_bob | my second manga |
2344 6 | 22 | 1 | regress_rls_carol | great science fiction |
2345 7 | 33 | 2 | regress_rls_carol | great technology book |
2346 8 | 44 | 1 | regress_rls_carol | great manga |
2347 9 | 22 | 1 | regress_rls_dave | awesome science fiction |
2348 10 | 33 | 2 | regress_rls_dave | awesome technology book |
2349 11 | 33 | 1 | regress_rls_carol | hoge |
2350 33 | 22 | 1 | regress_rls_bob | okay science fiction |
2351 2 | 11 | 2 | regress_rls_bob | my first novel |
2352 78 | 33 | 1 | regress_rls_bob | some technology novel |
2353 79 | 33 | 1 | regress_rls_bob | technology book, can only insert |
2354 12 | 11 | 1 | regress_rls_bob | another novel |
2355 1 | 11 | 1 | regress_rls_bob | my first novel | notes added by merge2 notes added by merge3 notes added by merge4 notes added by merge7
2356 13 | 44 | 1 | regress_rls_bob | new manga |
2357 14 | 11 | 1 | regress_rls_bob | new novel |
2363 SET SESSION AUTHORIZATION regress_rls_alice;
2364 CREATE TABLE z1 (a int, b text);
2365 CREATE TABLE z2 (a int, b text);
2366 GRANT SELECT ON z1,z2 TO regress_rls_group1, regress_rls_group2,
2367 regress_rls_bob, regress_rls_carol;
2368 INSERT INTO z1 VALUES
2373 CREATE POLICY p1 ON z1 TO regress_rls_group1 USING (a % 2 = 0);
2374 CREATE POLICY p2 ON z1 TO regress_rls_group2 USING (a % 2 = 1);
2375 ALTER TABLE z1 ENABLE ROW LEVEL SECURITY;
2376 SET SESSION AUTHORIZATION regress_rls_bob;
2377 SELECT * FROM z1 WHERE f_leak(b);
2378 NOTICE: f_leak => bbb
2379 NOTICE: f_leak => dad
2386 EXPLAIN (COSTS OFF) SELECT * FROM z1 WHERE f_leak(b);
2388 -----------------------------------------
2390 Filter: (((a % 2) = 0) AND f_leak(b))
2393 PREPARE plancache_test AS SELECT * FROM z1 WHERE f_leak(b);
2394 EXPLAIN (COSTS OFF) EXECUTE plancache_test;
2396 -----------------------------------------
2398 Filter: (((a % 2) = 0) AND f_leak(b))
2401 PREPARE plancache_test2 AS WITH q AS MATERIALIZED (SELECT * FROM z1 WHERE f_leak(b)) SELECT * FROM q,z2;
2402 EXPLAIN (COSTS OFF) EXECUTE plancache_test2;
2404 -------------------------------------------------
2408 Filter: (((a % 2) = 0) AND f_leak(b))
2414 PREPARE plancache_test3 AS WITH q AS MATERIALIZED (SELECT * FROM z2) SELECT * FROM q,z1 WHERE f_leak(z1.b);
2415 EXPLAIN (COSTS OFF) EXECUTE plancache_test3;
2417 -----------------------------------------------------
2424 Filter: (((a % 2) = 0) AND f_leak(b))
2427 SET ROLE regress_rls_group1;
2428 SELECT * FROM z1 WHERE f_leak(b);
2429 NOTICE: f_leak => bbb
2430 NOTICE: f_leak => dad
2437 EXPLAIN (COSTS OFF) SELECT * FROM z1 WHERE f_leak(b);
2439 -----------------------------------------
2441 Filter: (((a % 2) = 0) AND f_leak(b))
2444 EXPLAIN (COSTS OFF) EXECUTE plancache_test;
2446 -----------------------------------------
2448 Filter: (((a % 2) = 0) AND f_leak(b))
2451 EXPLAIN (COSTS OFF) EXECUTE plancache_test2;
2453 -------------------------------------------------
2457 Filter: (((a % 2) = 0) AND f_leak(b))
2463 EXPLAIN (COSTS OFF) EXECUTE plancache_test3;
2465 -----------------------------------------------------
2472 Filter: (((a % 2) = 0) AND f_leak(b))
2475 SET SESSION AUTHORIZATION regress_rls_carol;
2476 SELECT * FROM z1 WHERE f_leak(b);
2477 NOTICE: f_leak => aba
2478 NOTICE: f_leak => ccc
2485 EXPLAIN (COSTS OFF) SELECT * FROM z1 WHERE f_leak(b);
2487 -----------------------------------------
2489 Filter: (((a % 2) = 1) AND f_leak(b))
2492 EXPLAIN (COSTS OFF) EXECUTE plancache_test;
2494 -----------------------------------------
2496 Filter: (((a % 2) = 1) AND f_leak(b))
2499 EXPLAIN (COSTS OFF) EXECUTE plancache_test2;
2501 -------------------------------------------------
2505 Filter: (((a % 2) = 1) AND f_leak(b))
2511 EXPLAIN (COSTS OFF) EXECUTE plancache_test3;
2513 -----------------------------------------------------
2520 Filter: (((a % 2) = 1) AND f_leak(b))
2523 SET ROLE regress_rls_group2;
2524 SELECT * FROM z1 WHERE f_leak(b);
2525 NOTICE: f_leak => aba
2526 NOTICE: f_leak => ccc
2533 EXPLAIN (COSTS OFF) SELECT * FROM z1 WHERE f_leak(b);
2535 -----------------------------------------
2537 Filter: (((a % 2) = 1) AND f_leak(b))
2540 EXPLAIN (COSTS OFF) EXECUTE plancache_test;
2542 -----------------------------------------
2544 Filter: (((a % 2) = 1) AND f_leak(b))
2547 EXPLAIN (COSTS OFF) EXECUTE plancache_test2;
2549 -------------------------------------------------
2553 Filter: (((a % 2) = 1) AND f_leak(b))
2559 EXPLAIN (COSTS OFF) EXECUTE plancache_test3;
2561 -----------------------------------------------------
2568 Filter: (((a % 2) = 1) AND f_leak(b))
2572 -- Views should follow policy for view owner.
2574 -- View and Table owner are the same.
2575 SET SESSION AUTHORIZATION regress_rls_alice;
2576 CREATE VIEW rls_view AS SELECT * FROM z1 WHERE f_leak(b);
2577 GRANT SELECT ON rls_view TO regress_rls_bob;
2578 -- Query as role that is not owner of view or table. Should return all records.
2579 SET SESSION AUTHORIZATION regress_rls_bob;
2580 SELECT * FROM rls_view;
2581 NOTICE: f_leak => aba
2582 NOTICE: f_leak => bbb
2583 NOTICE: f_leak => ccc
2584 NOTICE: f_leak => dad
2593 EXPLAIN (COSTS OFF) SELECT * FROM rls_view;
2595 ---------------------
2600 -- Query as view/table owner. Should return all records.
2601 SET SESSION AUTHORIZATION regress_rls_alice;
2602 SELECT * FROM rls_view;
2603 NOTICE: f_leak => aba
2604 NOTICE: f_leak => bbb
2605 NOTICE: f_leak => ccc
2606 NOTICE: f_leak => dad
2615 EXPLAIN (COSTS OFF) SELECT * FROM rls_view;
2617 ---------------------
2623 -- View and Table owners are different.
2624 SET SESSION AUTHORIZATION regress_rls_bob;
2625 CREATE VIEW rls_view AS SELECT * FROM z1 WHERE f_leak(b);
2626 GRANT SELECT ON rls_view TO regress_rls_alice;
2627 -- Query as role that is not owner of view but is owner of table.
2628 -- Should return records based on view owner policies.
2629 SET SESSION AUTHORIZATION regress_rls_alice;
2630 SELECT * FROM rls_view;
2631 NOTICE: f_leak => bbb
2632 NOTICE: f_leak => dad
2639 EXPLAIN (COSTS OFF) SELECT * FROM rls_view;
2641 -----------------------------------------
2643 Filter: (((a % 2) = 0) AND f_leak(b))
2646 -- Query as role that is not owner of table but is owner of view.
2647 -- Should return records based on view owner policies.
2648 SET SESSION AUTHORIZATION regress_rls_bob;
2649 SELECT * FROM rls_view;
2650 NOTICE: f_leak => bbb
2651 NOTICE: f_leak => dad
2658 EXPLAIN (COSTS OFF) SELECT * FROM rls_view;
2660 -----------------------------------------
2662 Filter: (((a % 2) = 0) AND f_leak(b))
2665 -- Query as role that is not the owner of the table or view without permissions.
2666 SET SESSION AUTHORIZATION regress_rls_carol;
2667 SELECT * FROM rls_view; --fail - permission denied.
2668 ERROR: permission denied for view rls_view
2669 EXPLAIN (COSTS OFF) SELECT * FROM rls_view; --fail - permission denied.
2670 ERROR: permission denied for view rls_view
2671 -- Query as role that is not the owner of the table or view with permissions.
2672 SET SESSION AUTHORIZATION regress_rls_bob;
2673 GRANT SELECT ON rls_view TO regress_rls_carol;
2674 SET SESSION AUTHORIZATION regress_rls_carol;
2675 SELECT * FROM rls_view;
2676 NOTICE: f_leak => bbb
2677 NOTICE: f_leak => dad
2684 EXPLAIN (COSTS OFF) SELECT * FROM rls_view;
2686 -----------------------------------------
2688 Filter: (((a % 2) = 0) AND f_leak(b))
2691 -- Policy requiring access to another table.
2692 SET SESSION AUTHORIZATION regress_rls_alice;
2693 CREATE TABLE z1_blacklist (a int);
2694 INSERT INTO z1_blacklist VALUES (3), (4);
2695 CREATE POLICY p3 ON z1 AS RESTRICTIVE USING (a NOT IN (SELECT a FROM z1_blacklist));
2696 -- Query as role that is not owner of table but is owner of view without permissions.
2697 SET SESSION AUTHORIZATION regress_rls_bob;
2698 SELECT * FROM rls_view; --fail - permission denied.
2699 ERROR: permission denied for table z1_blacklist
2700 EXPLAIN (COSTS OFF) SELECT * FROM rls_view; --fail - permission denied.
2701 ERROR: permission denied for table z1_blacklist
2702 -- Query as role that is not the owner of the table or view without permissions.
2703 SET SESSION AUTHORIZATION regress_rls_carol;
2704 SELECT * FROM rls_view; --fail - permission denied.
2705 ERROR: permission denied for table z1_blacklist
2706 EXPLAIN (COSTS OFF) SELECT * FROM rls_view; --fail - permission denied.
2707 ERROR: permission denied for table z1_blacklist
2708 -- Query as role that is not owner of table but is owner of view with permissions.
2709 SET SESSION AUTHORIZATION regress_rls_alice;
2710 GRANT SELECT ON z1_blacklist TO regress_rls_bob;
2711 SET SESSION AUTHORIZATION regress_rls_bob;
2712 SELECT * FROM rls_view;
2713 NOTICE: f_leak => bbb
2719 EXPLAIN (COSTS OFF) SELECT * FROM rls_view;
2721 ---------------------------------------------------------------------------------------
2723 Filter: ((NOT (ANY (a = (hashed SubPlan 1).col1))) AND ((a % 2) = 0) AND f_leak(b))
2725 -> Seq Scan on z1_blacklist
2728 -- Query as role that is not the owner of the table or view with permissions.
2729 SET SESSION AUTHORIZATION regress_rls_carol;
2730 SELECT * FROM rls_view;
2731 NOTICE: f_leak => bbb
2737 EXPLAIN (COSTS OFF) SELECT * FROM rls_view;
2739 ---------------------------------------------------------------------------------------
2741 Filter: ((NOT (ANY (a = (hashed SubPlan 1).col1))) AND ((a % 2) = 0) AND f_leak(b))
2743 -> Seq Scan on z1_blacklist
2746 SET SESSION AUTHORIZATION regress_rls_alice;
2747 REVOKE SELECT ON z1_blacklist FROM regress_rls_bob;
2748 DROP POLICY p3 ON z1;
2749 SET SESSION AUTHORIZATION regress_rls_bob;
2752 -- Security invoker views should follow policy for current user.
2754 -- View and table owner are the same.
2755 SET SESSION AUTHORIZATION regress_rls_alice;
2756 CREATE VIEW rls_view WITH (security_invoker) AS
2757 SELECT * FROM z1 WHERE f_leak(b);
2758 GRANT SELECT ON rls_view TO regress_rls_bob;
2759 GRANT SELECT ON rls_view TO regress_rls_carol;
2760 -- Query as table owner. Should return all records.
2761 SELECT * FROM rls_view;
2762 NOTICE: f_leak => aba
2763 NOTICE: f_leak => bbb
2764 NOTICE: f_leak => ccc
2765 NOTICE: f_leak => dad
2774 EXPLAIN (COSTS OFF) SELECT * FROM rls_view;
2776 ---------------------
2781 -- Queries as other users.
2782 -- Should return records based on current user's policies.
2783 SET SESSION AUTHORIZATION regress_rls_bob;
2784 SELECT * FROM rls_view;
2785 NOTICE: f_leak => bbb
2786 NOTICE: f_leak => dad
2793 EXPLAIN (COSTS OFF) SELECT * FROM rls_view;
2795 -----------------------------------------
2797 Filter: (((a % 2) = 0) AND f_leak(b))
2800 SET SESSION AUTHORIZATION regress_rls_carol;
2801 SELECT * FROM rls_view;
2802 NOTICE: f_leak => aba
2803 NOTICE: f_leak => ccc
2810 EXPLAIN (COSTS OFF) SELECT * FROM rls_view;
2812 -----------------------------------------
2814 Filter: (((a % 2) = 1) AND f_leak(b))
2817 -- View and table owners are different.
2818 SET SESSION AUTHORIZATION regress_rls_alice;
2820 SET SESSION AUTHORIZATION regress_rls_bob;
2821 CREATE VIEW rls_view WITH (security_invoker) AS
2822 SELECT * FROM z1 WHERE f_leak(b);
2823 GRANT SELECT ON rls_view TO regress_rls_alice;
2824 GRANT SELECT ON rls_view TO regress_rls_carol;
2825 -- Query as table owner. Should return all records.
2826 SET SESSION AUTHORIZATION regress_rls_alice;
2827 SELECT * FROM rls_view;
2828 NOTICE: f_leak => aba
2829 NOTICE: f_leak => bbb
2830 NOTICE: f_leak => ccc
2831 NOTICE: f_leak => dad
2840 EXPLAIN (COSTS OFF) SELECT * FROM rls_view;
2842 ---------------------
2847 -- Queries as other users.
2848 -- Should return records based on current user's policies.
2849 SET SESSION AUTHORIZATION regress_rls_bob;
2850 SELECT * FROM rls_view;
2851 NOTICE: f_leak => bbb
2852 NOTICE: f_leak => dad
2859 EXPLAIN (COSTS OFF) SELECT * FROM rls_view;
2861 -----------------------------------------
2863 Filter: (((a % 2) = 0) AND f_leak(b))
2866 SET SESSION AUTHORIZATION regress_rls_carol;
2867 SELECT * FROM rls_view;
2868 NOTICE: f_leak => aba
2869 NOTICE: f_leak => ccc
2876 EXPLAIN (COSTS OFF) SELECT * FROM rls_view;
2878 -----------------------------------------
2880 Filter: (((a % 2) = 1) AND f_leak(b))
2883 -- Policy requiring access to another table.
2884 SET SESSION AUTHORIZATION regress_rls_alice;
2885 CREATE POLICY p3 ON z1 AS RESTRICTIVE USING (a NOT IN (SELECT a FROM z1_blacklist));
2886 -- Query as role that is not owner of table but is owner of view without permissions.
2887 SET SESSION AUTHORIZATION regress_rls_bob;
2888 SELECT * FROM rls_view; --fail - permission denied.
2889 ERROR: permission denied for table z1_blacklist
2890 EXPLAIN (COSTS OFF) SELECT * FROM rls_view; --fail - permission denied.
2891 ERROR: permission denied for table z1_blacklist
2892 -- Query as role that is not the owner of the table or view without permissions.
2893 SET SESSION AUTHORIZATION regress_rls_carol;
2894 SELECT * FROM rls_view; --fail - permission denied.
2895 ERROR: permission denied for table z1_blacklist
2896 EXPLAIN (COSTS OFF) SELECT * FROM rls_view; --fail - permission denied.
2897 ERROR: permission denied for table z1_blacklist
2898 -- Query as role that is not owner of table but is owner of view with permissions.
2899 SET SESSION AUTHORIZATION regress_rls_alice;
2900 GRANT SELECT ON z1_blacklist TO regress_rls_bob;
2901 SET SESSION AUTHORIZATION regress_rls_bob;
2902 SELECT * FROM rls_view;
2903 NOTICE: f_leak => bbb
2909 EXPLAIN (COSTS OFF) SELECT * FROM rls_view;
2911 ---------------------------------------------------------------------------------------
2913 Filter: ((NOT (ANY (a = (hashed SubPlan 1).col1))) AND ((a % 2) = 0) AND f_leak(b))
2915 -> Seq Scan on z1_blacklist
2918 -- Query as role that is not the owner of the table or view without permissions.
2919 SET SESSION AUTHORIZATION regress_rls_carol;
2920 SELECT * FROM rls_view; --fail - permission denied.
2921 ERROR: permission denied for table z1_blacklist
2922 EXPLAIN (COSTS OFF) SELECT * FROM rls_view; --fail - permission denied.
2923 ERROR: permission denied for table z1_blacklist
2924 -- Query as role that is not the owner of the table or view with permissions.
2925 SET SESSION AUTHORIZATION regress_rls_alice;
2926 GRANT SELECT ON z1_blacklist TO regress_rls_carol;
2927 SET SESSION AUTHORIZATION regress_rls_carol;
2928 SELECT * FROM rls_view;
2929 NOTICE: f_leak => aba
2935 EXPLAIN (COSTS OFF) SELECT * FROM rls_view;
2937 ---------------------------------------------------------------------------------------
2939 Filter: ((NOT (ANY (a = (hashed SubPlan 1).col1))) AND ((a % 2) = 1) AND f_leak(b))
2941 -> Seq Scan on z1_blacklist
2944 SET SESSION AUTHORIZATION regress_rls_bob;
2949 SET SESSION AUTHORIZATION regress_rls_alice;
2950 CREATE TABLE x1 (a int, b text, c text);
2951 GRANT ALL ON x1 TO PUBLIC;
2952 INSERT INTO x1 VALUES
2953 (1, 'abc', 'regress_rls_bob'),
2954 (2, 'bcd', 'regress_rls_bob'),
2955 (3, 'cde', 'regress_rls_carol'),
2956 (4, 'def', 'regress_rls_carol'),
2957 (5, 'efg', 'regress_rls_bob'),
2958 (6, 'fgh', 'regress_rls_bob'),
2959 (7, 'fgh', 'regress_rls_carol'),
2960 (8, 'fgh', 'regress_rls_carol');
2961 CREATE POLICY p0 ON x1 FOR ALL USING (c = current_user);
2962 CREATE POLICY p1 ON x1 FOR SELECT USING (a % 2 = 0);
2963 CREATE POLICY p2 ON x1 FOR INSERT WITH CHECK (a % 2 = 1);
2964 CREATE POLICY p3 ON x1 FOR UPDATE USING (a % 2 = 0);
2965 CREATE POLICY p4 ON x1 FOR DELETE USING (a < 8);
2966 ALTER TABLE x1 ENABLE ROW LEVEL SECURITY;
2967 SET SESSION AUTHORIZATION regress_rls_bob;
2968 SELECT * FROM x1 WHERE f_leak(b) ORDER BY a ASC;
2969 NOTICE: f_leak => abc
2970 NOTICE: f_leak => bcd
2971 NOTICE: f_leak => def
2972 NOTICE: f_leak => efg
2973 NOTICE: f_leak => fgh
2974 NOTICE: f_leak => fgh
2976 ---+-----+-------------------
2977 1 | abc | regress_rls_bob
2978 2 | bcd | regress_rls_bob
2979 4 | def | regress_rls_carol
2980 5 | efg | regress_rls_bob
2981 6 | fgh | regress_rls_bob
2982 8 | fgh | regress_rls_carol
2985 UPDATE x1 SET b = b || '_updt' WHERE f_leak(b) RETURNING *;
2986 NOTICE: f_leak => abc
2987 NOTICE: f_leak => bcd
2988 NOTICE: f_leak => def
2989 NOTICE: f_leak => efg
2990 NOTICE: f_leak => fgh
2991 NOTICE: f_leak => fgh
2993 ---+----------+-------------------
2994 1 | abc_updt | regress_rls_bob
2995 2 | bcd_updt | regress_rls_bob
2996 4 | def_updt | regress_rls_carol
2997 5 | efg_updt | regress_rls_bob
2998 6 | fgh_updt | regress_rls_bob
2999 8 | fgh_updt | regress_rls_carol
3002 SET SESSION AUTHORIZATION regress_rls_carol;
3003 SELECT * FROM x1 WHERE f_leak(b) ORDER BY a ASC;
3004 NOTICE: f_leak => cde
3005 NOTICE: f_leak => fgh
3006 NOTICE: f_leak => bcd_updt
3007 NOTICE: f_leak => def_updt
3008 NOTICE: f_leak => fgh_updt
3009 NOTICE: f_leak => fgh_updt
3011 ---+----------+-------------------
3012 2 | bcd_updt | regress_rls_bob
3013 3 | cde | regress_rls_carol
3014 4 | def_updt | regress_rls_carol
3015 6 | fgh_updt | regress_rls_bob
3016 7 | fgh | regress_rls_carol
3017 8 | fgh_updt | regress_rls_carol
3020 UPDATE x1 SET b = b || '_updt' WHERE f_leak(b) RETURNING *;
3021 NOTICE: f_leak => cde
3022 NOTICE: f_leak => fgh
3023 NOTICE: f_leak => bcd_updt
3024 NOTICE: f_leak => def_updt
3025 NOTICE: f_leak => fgh_updt
3026 NOTICE: f_leak => fgh_updt
3028 ---+---------------+-------------------
3029 3 | cde_updt | regress_rls_carol
3030 7 | fgh_updt | regress_rls_carol
3031 2 | bcd_updt_updt | regress_rls_bob
3032 4 | def_updt_updt | regress_rls_carol
3033 6 | fgh_updt_updt | regress_rls_bob
3034 8 | fgh_updt_updt | regress_rls_carol
3037 DELETE FROM x1 WHERE f_leak(b) RETURNING *;
3038 NOTICE: f_leak => cde_updt
3039 NOTICE: f_leak => fgh_updt
3040 NOTICE: f_leak => bcd_updt_updt
3041 NOTICE: f_leak => def_updt_updt
3042 NOTICE: f_leak => fgh_updt_updt
3043 NOTICE: f_leak => fgh_updt_updt
3045 ---+---------------+-------------------
3046 3 | cde_updt | regress_rls_carol
3047 7 | fgh_updt | regress_rls_carol
3048 2 | bcd_updt_updt | regress_rls_bob
3049 4 | def_updt_updt | regress_rls_carol
3050 6 | fgh_updt_updt | regress_rls_bob
3051 8 | fgh_updt_updt | regress_rls_carol
3055 -- Duplicate Policy Names
3057 SET SESSION AUTHORIZATION regress_rls_alice;
3058 CREATE TABLE y1 (a int, b text);
3059 CREATE TABLE y2 (a int, b text);
3060 GRANT ALL ON y1, y2 TO regress_rls_bob;
3061 CREATE POLICY p1 ON y1 FOR ALL USING (a % 2 = 0);
3062 CREATE POLICY p2 ON y1 FOR SELECT USING (a > 2);
3063 CREATE POLICY p1 ON y1 FOR SELECT USING (a % 2 = 1); --fail
3064 ERROR: policy "p1" for table "y1" already exists
3065 CREATE POLICY p1 ON y2 FOR ALL USING (a % 2 = 0); --OK
3066 ALTER TABLE y1 ENABLE ROW LEVEL SECURITY;
3067 ALTER TABLE y2 ENABLE ROW LEVEL SECURITY;
3069 -- Expression structure with SBV
3071 -- Create view as table owner. RLS should NOT be applied.
3072 SET SESSION AUTHORIZATION regress_rls_alice;
3073 CREATE VIEW rls_sbv WITH (security_barrier) AS
3074 SELECT * FROM y1 WHERE f_leak(b);
3075 EXPLAIN (COSTS OFF) SELECT * FROM rls_sbv WHERE (a = 1);
3077 -----------------------------------
3079 Filter: (f_leak(b) AND (a = 1))
3083 -- Create view as role that does not own table. RLS should be applied.
3084 SET SESSION AUTHORIZATION regress_rls_bob;
3085 CREATE VIEW rls_sbv WITH (security_barrier) AS
3086 SELECT * FROM y1 WHERE f_leak(b);
3087 EXPLAIN (COSTS OFF) SELECT * FROM rls_sbv WHERE (a = 1);
3089 ------------------------------------------------------------------
3091 Filter: ((a = 1) AND ((a > 2) OR ((a % 2) = 0)) AND f_leak(b))
3096 -- Expression structure
3098 SET SESSION AUTHORIZATION regress_rls_alice;
3099 INSERT INTO y2 (SELECT x, public.fipshash(x::text) FROM generate_series(0,20) x);
3100 CREATE POLICY p2 ON y2 USING (a % 3 = 0);
3101 CREATE POLICY p3 ON y2 USING (a % 4 = 0);
3102 SET SESSION AUTHORIZATION regress_rls_bob;
3103 SELECT * FROM y2 WHERE f_leak(b);
3104 NOTICE: f_leak => 5feceb66ffc86f38d952786c6d696c79
3105 NOTICE: f_leak => d4735e3a265e16eee03f59718b9b5d03
3106 NOTICE: f_leak => 4e07408562bedb8b60ce05c1decfe3ad
3107 NOTICE: f_leak => 4b227777d4dd1fc61c6f884f48641d02
3108 NOTICE: f_leak => e7f6c011776e8db7cd330b54174fd76f
3109 NOTICE: f_leak => 2c624232cdd221771294dfbb310aca00
3110 NOTICE: f_leak => 19581e27de7ced00ff1ce50b2047e7a5
3111 NOTICE: f_leak => 4a44dc15364204a80fe80e9039455cc1
3112 NOTICE: f_leak => 6b51d431df5d7f141cbececcf79edf3d
3113 NOTICE: f_leak => 8527a891e224136950ff32ca212b45bc
3114 NOTICE: f_leak => e629fa6598d732768f7c726b4b621285
3115 NOTICE: f_leak => b17ef6d19c7a5b1ee83b907c595526dc
3116 NOTICE: f_leak => 4ec9599fc203d176a301536c2e091a19
3117 NOTICE: f_leak => f5ca38f748a1d6eaf726b8a42fb575c3
3119 ----+----------------------------------
3120 0 | 5feceb66ffc86f38d952786c6d696c79
3121 2 | d4735e3a265e16eee03f59718b9b5d03
3122 3 | 4e07408562bedb8b60ce05c1decfe3ad
3123 4 | 4b227777d4dd1fc61c6f884f48641d02
3124 6 | e7f6c011776e8db7cd330b54174fd76f
3125 8 | 2c624232cdd221771294dfbb310aca00
3126 9 | 19581e27de7ced00ff1ce50b2047e7a5
3127 10 | 4a44dc15364204a80fe80e9039455cc1
3128 12 | 6b51d431df5d7f141cbececcf79edf3d
3129 14 | 8527a891e224136950ff32ca212b45bc
3130 15 | e629fa6598d732768f7c726b4b621285
3131 16 | b17ef6d19c7a5b1ee83b907c595526dc
3132 18 | 4ec9599fc203d176a301536c2e091a19
3133 20 | f5ca38f748a1d6eaf726b8a42fb575c3
3136 EXPLAIN (COSTS OFF) SELECT * FROM y2 WHERE f_leak(b);
3138 -----------------------------------------------------------------------------
3140 Filter: ((((a % 4) = 0) OR ((a % 3) = 0) OR ((a % 2) = 0)) AND f_leak(b))
3144 -- Qual push-down of leaky functions, when not referring to table
3146 SELECT * FROM y2 WHERE f_leak('abc');
3147 NOTICE: f_leak => abc
3148 NOTICE: f_leak => abc
3149 NOTICE: f_leak => abc
3150 NOTICE: f_leak => abc
3151 NOTICE: f_leak => abc
3152 NOTICE: f_leak => abc
3153 NOTICE: f_leak => abc
3154 NOTICE: f_leak => abc
3155 NOTICE: f_leak => abc
3156 NOTICE: f_leak => abc
3157 NOTICE: f_leak => abc
3158 NOTICE: f_leak => abc
3159 NOTICE: f_leak => abc
3160 NOTICE: f_leak => abc
3161 NOTICE: f_leak => abc
3162 NOTICE: f_leak => abc
3163 NOTICE: f_leak => abc
3164 NOTICE: f_leak => abc
3165 NOTICE: f_leak => abc
3166 NOTICE: f_leak => abc
3167 NOTICE: f_leak => abc
3169 ----+----------------------------------
3170 0 | 5feceb66ffc86f38d952786c6d696c79
3171 2 | d4735e3a265e16eee03f59718b9b5d03
3172 3 | 4e07408562bedb8b60ce05c1decfe3ad
3173 4 | 4b227777d4dd1fc61c6f884f48641d02
3174 6 | e7f6c011776e8db7cd330b54174fd76f
3175 8 | 2c624232cdd221771294dfbb310aca00
3176 9 | 19581e27de7ced00ff1ce50b2047e7a5
3177 10 | 4a44dc15364204a80fe80e9039455cc1
3178 12 | 6b51d431df5d7f141cbececcf79edf3d
3179 14 | 8527a891e224136950ff32ca212b45bc
3180 15 | e629fa6598d732768f7c726b4b621285
3181 16 | b17ef6d19c7a5b1ee83b907c595526dc
3182 18 | 4ec9599fc203d176a301536c2e091a19
3183 20 | f5ca38f748a1d6eaf726b8a42fb575c3
3186 EXPLAIN (COSTS OFF) SELECT * FROM y2 WHERE f_leak('abc');
3188 ---------------------------------------------------------------------------------------
3190 Filter: (f_leak('abc'::text) AND (((a % 4) = 0) OR ((a % 3) = 0) OR ((a % 2) = 0)))
3193 CREATE TABLE test_qual_pushdown (
3196 INSERT INTO test_qual_pushdown VALUES ('abc'),('def');
3197 SELECT * FROM y2 JOIN test_qual_pushdown ON (b = abc) WHERE f_leak(abc);
3198 NOTICE: f_leak => abc
3199 NOTICE: f_leak => def
3204 EXPLAIN (COSTS OFF) SELECT * FROM y2 JOIN test_qual_pushdown ON (b = abc) WHERE f_leak(abc);
3206 -------------------------------------------------------------------------
3208 Hash Cond: (test_qual_pushdown.abc = y2.b)
3209 -> Seq Scan on test_qual_pushdown
3213 Filter: (((a % 4) = 0) OR ((a % 3) = 0) OR ((a % 2) = 0))
3216 SELECT * FROM y2 JOIN test_qual_pushdown ON (b = abc) WHERE f_leak(b);
3217 NOTICE: f_leak => 5feceb66ffc86f38d952786c6d696c79
3218 NOTICE: f_leak => d4735e3a265e16eee03f59718b9b5d03
3219 NOTICE: f_leak => 4e07408562bedb8b60ce05c1decfe3ad
3220 NOTICE: f_leak => 4b227777d4dd1fc61c6f884f48641d02
3221 NOTICE: f_leak => e7f6c011776e8db7cd330b54174fd76f
3222 NOTICE: f_leak => 2c624232cdd221771294dfbb310aca00
3223 NOTICE: f_leak => 19581e27de7ced00ff1ce50b2047e7a5
3224 NOTICE: f_leak => 4a44dc15364204a80fe80e9039455cc1
3225 NOTICE: f_leak => 6b51d431df5d7f141cbececcf79edf3d
3226 NOTICE: f_leak => 8527a891e224136950ff32ca212b45bc
3227 NOTICE: f_leak => e629fa6598d732768f7c726b4b621285
3228 NOTICE: f_leak => b17ef6d19c7a5b1ee83b907c595526dc
3229 NOTICE: f_leak => 4ec9599fc203d176a301536c2e091a19
3230 NOTICE: f_leak => f5ca38f748a1d6eaf726b8a42fb575c3
3235 EXPLAIN (COSTS OFF) SELECT * FROM y2 JOIN test_qual_pushdown ON (b = abc) WHERE f_leak(b);
3237 -----------------------------------------------------------------------------------------
3239 Hash Cond: (test_qual_pushdown.abc = y2.b)
3240 -> Seq Scan on test_qual_pushdown
3243 Filter: ((((a % 4) = 0) OR ((a % 3) = 0) OR ((a % 2) = 0)) AND f_leak(b))
3246 DROP TABLE test_qual_pushdown;
3248 -- Plancache invalidate on user change.
3250 RESET SESSION AUTHORIZATION;
3251 DROP TABLE t1 CASCADE;
3252 NOTICE: drop cascades to 2 other objects
3253 DETAIL: drop cascades to table t2
3254 drop cascades to table t3
3255 CREATE TABLE t1 (a integer);
3256 GRANT SELECT ON t1 TO regress_rls_bob, regress_rls_carol;
3257 CREATE POLICY p1 ON t1 TO regress_rls_bob USING ((a % 2) = 0);
3258 CREATE POLICY p2 ON t1 TO regress_rls_carol USING ((a % 4) = 0);
3259 ALTER TABLE t1 ENABLE ROW LEVEL SECURITY;
3260 -- Prepare as regress_rls_bob
3261 SET ROLE regress_rls_bob;
3262 PREPARE role_inval AS SELECT * FROM t1;
3264 EXPLAIN (COSTS OFF) EXECUTE role_inval;
3266 -------------------------
3268 Filter: ((a % 2) = 0)
3271 -- Change to regress_rls_carol
3272 SET ROLE regress_rls_carol;
3273 -- Check plan- should be different
3274 EXPLAIN (COSTS OFF) EXECUTE role_inval;
3276 -------------------------
3278 Filter: ((a % 4) = 0)
3281 -- Change back to regress_rls_bob
3282 SET ROLE regress_rls_bob;
3283 -- Check plan- should be back to original
3284 EXPLAIN (COSTS OFF) EXECUTE role_inval;
3286 -------------------------
3288 Filter: ((a % 2) = 0)
3294 RESET SESSION AUTHORIZATION;
3295 DROP TABLE t1 CASCADE;
3296 CREATE TABLE t1 (a integer, b text);
3297 CREATE POLICY p1 ON t1 USING (a % 2 = 0);
3298 ALTER TABLE t1 ENABLE ROW LEVEL SECURITY;
3299 GRANT ALL ON t1 TO regress_rls_bob;
3300 INSERT INTO t1 (SELECT x, public.fipshash(x::text) FROM generate_series(0,20) x);
3301 SET SESSION AUTHORIZATION regress_rls_bob;
3302 WITH cte1 AS MATERIALIZED (SELECT * FROM t1 WHERE f_leak(b)) SELECT * FROM cte1;
3303 NOTICE: f_leak => 5feceb66ffc86f38d952786c6d696c79
3304 NOTICE: f_leak => d4735e3a265e16eee03f59718b9b5d03
3305 NOTICE: f_leak => 4b227777d4dd1fc61c6f884f48641d02
3306 NOTICE: f_leak => e7f6c011776e8db7cd330b54174fd76f
3307 NOTICE: f_leak => 2c624232cdd221771294dfbb310aca00
3308 NOTICE: f_leak => 4a44dc15364204a80fe80e9039455cc1
3309 NOTICE: f_leak => 6b51d431df5d7f141cbececcf79edf3d
3310 NOTICE: f_leak => 8527a891e224136950ff32ca212b45bc
3311 NOTICE: f_leak => b17ef6d19c7a5b1ee83b907c595526dc
3312 NOTICE: f_leak => 4ec9599fc203d176a301536c2e091a19
3313 NOTICE: f_leak => f5ca38f748a1d6eaf726b8a42fb575c3
3315 ----+----------------------------------
3316 0 | 5feceb66ffc86f38d952786c6d696c79
3317 2 | d4735e3a265e16eee03f59718b9b5d03
3318 4 | 4b227777d4dd1fc61c6f884f48641d02
3319 6 | e7f6c011776e8db7cd330b54174fd76f
3320 8 | 2c624232cdd221771294dfbb310aca00
3321 10 | 4a44dc15364204a80fe80e9039455cc1
3322 12 | 6b51d431df5d7f141cbececcf79edf3d
3323 14 | 8527a891e224136950ff32ca212b45bc
3324 16 | b17ef6d19c7a5b1ee83b907c595526dc
3325 18 | 4ec9599fc203d176a301536c2e091a19
3326 20 | f5ca38f748a1d6eaf726b8a42fb575c3
3330 WITH cte1 AS MATERIALIZED (SELECT * FROM t1 WHERE f_leak(b)) SELECT * FROM cte1;
3332 -------------------------------------------------
3336 Filter: (((a % 2) = 0) AND f_leak(b))
3339 WITH cte1 AS (UPDATE t1 SET a = a + 1 RETURNING *) SELECT * FROM cte1; --fail
3340 ERROR: new row violates row-level security policy for table "t1"
3341 WITH cte1 AS (UPDATE t1 SET a = a RETURNING *) SELECT * FROM cte1; --ok
3343 ----+----------------------------------
3344 0 | 5feceb66ffc86f38d952786c6d696c79
3345 2 | d4735e3a265e16eee03f59718b9b5d03
3346 4 | 4b227777d4dd1fc61c6f884f48641d02
3347 6 | e7f6c011776e8db7cd330b54174fd76f
3348 8 | 2c624232cdd221771294dfbb310aca00
3349 10 | 4a44dc15364204a80fe80e9039455cc1
3350 12 | 6b51d431df5d7f141cbececcf79edf3d
3351 14 | 8527a891e224136950ff32ca212b45bc
3352 16 | b17ef6d19c7a5b1ee83b907c595526dc
3353 18 | 4ec9599fc203d176a301536c2e091a19
3354 20 | f5ca38f748a1d6eaf726b8a42fb575c3
3357 WITH cte1 AS (INSERT INTO t1 VALUES (21, 'Fail') RETURNING *) SELECT * FROM cte1; --fail
3358 ERROR: new row violates row-level security policy for table "t1"
3359 WITH cte1 AS (INSERT INTO t1 VALUES (20, 'Success') RETURNING *) SELECT * FROM cte1; --ok
3368 RESET SESSION AUTHORIZATION;
3369 ALTER POLICY p1 ON t1 RENAME TO p1; --fail
3370 ERROR: policy "p1" for table "t1" already exists
3371 SELECT polname, relname
3373 JOIN pg_class pc ON (pc.oid = pol.polrelid)
3374 WHERE relname = 't1';
3380 ALTER POLICY p1 ON t1 RENAME TO p2; --ok
3381 SELECT polname, relname
3383 JOIN pg_class pc ON (pc.oid = pol.polrelid)
3384 WHERE relname = 't1';
3391 -- Check INSERT SELECT
3393 SET SESSION AUTHORIZATION regress_rls_bob;
3394 CREATE TABLE t2 (a integer, b text);
3395 INSERT INTO t2 (SELECT * FROM t1);
3396 EXPLAIN (COSTS OFF) INSERT INTO t2 (SELECT * FROM t1);
3398 -------------------------------
3401 Filter: ((a % 2) = 0)
3406 ----+----------------------------------
3407 0 | 5feceb66ffc86f38d952786c6d696c79
3408 2 | d4735e3a265e16eee03f59718b9b5d03
3409 4 | 4b227777d4dd1fc61c6f884f48641d02
3410 6 | e7f6c011776e8db7cd330b54174fd76f
3411 8 | 2c624232cdd221771294dfbb310aca00
3412 10 | 4a44dc15364204a80fe80e9039455cc1
3413 12 | 6b51d431df5d7f141cbececcf79edf3d
3414 14 | 8527a891e224136950ff32ca212b45bc
3415 16 | b17ef6d19c7a5b1ee83b907c595526dc
3416 18 | 4ec9599fc203d176a301536c2e091a19
3417 20 | f5ca38f748a1d6eaf726b8a42fb575c3
3421 EXPLAIN (COSTS OFF) SELECT * FROM t2;
3427 CREATE TABLE t3 AS SELECT * FROM t1;
3430 ----+----------------------------------
3431 0 | 5feceb66ffc86f38d952786c6d696c79
3432 2 | d4735e3a265e16eee03f59718b9b5d03
3433 4 | 4b227777d4dd1fc61c6f884f48641d02
3434 6 | e7f6c011776e8db7cd330b54174fd76f
3435 8 | 2c624232cdd221771294dfbb310aca00
3436 10 | 4a44dc15364204a80fe80e9039455cc1
3437 12 | 6b51d431df5d7f141cbececcf79edf3d
3438 14 | 8527a891e224136950ff32ca212b45bc
3439 16 | b17ef6d19c7a5b1ee83b907c595526dc
3440 18 | 4ec9599fc203d176a301536c2e091a19
3441 20 | f5ca38f748a1d6eaf726b8a42fb575c3
3445 SELECT * INTO t4 FROM t1;
3448 ----+----------------------------------
3449 0 | 5feceb66ffc86f38d952786c6d696c79
3450 2 | d4735e3a265e16eee03f59718b9b5d03
3451 4 | 4b227777d4dd1fc61c6f884f48641d02
3452 6 | e7f6c011776e8db7cd330b54174fd76f
3453 8 | 2c624232cdd221771294dfbb310aca00
3454 10 | 4a44dc15364204a80fe80e9039455cc1
3455 12 | 6b51d431df5d7f141cbececcf79edf3d
3456 14 | 8527a891e224136950ff32ca212b45bc
3457 16 | b17ef6d19c7a5b1ee83b907c595526dc
3458 18 | 4ec9599fc203d176a301536c2e091a19
3459 20 | f5ca38f748a1d6eaf726b8a42fb575c3
3466 SET SESSION AUTHORIZATION regress_rls_alice;
3467 CREATE TABLE blog (id integer, author text, post text);
3468 CREATE TABLE comment (blog_id integer, message text);
3469 GRANT ALL ON blog, comment TO regress_rls_bob;
3470 CREATE POLICY blog_1 ON blog USING (id % 2 = 0);
3471 ALTER TABLE blog ENABLE ROW LEVEL SECURITY;
3472 INSERT INTO blog VALUES
3473 (1, 'alice', 'blog #1'),
3474 (2, 'bob', 'blog #1'),
3475 (3, 'alice', 'blog #2'),
3476 (4, 'alice', 'blog #3'),
3477 (5, 'john', 'blog #1');
3478 INSERT INTO comment VALUES
3485 SET SESSION AUTHORIZATION regress_rls_bob;
3486 -- Check RLS JOIN with Non-RLS.
3487 SELECT id, author, message FROM blog JOIN comment ON id = blog_id;
3488 id | author | message
3489 ----+--------+-------------
3491 2 | bob | who did it?
3494 -- Check Non-RLS JOIN with RLS.
3495 SELECT id, author, message FROM comment JOIN blog ON id = blog_id;
3496 id | author | message
3497 ----+--------+-------------
3499 2 | bob | who did it?
3502 SET SESSION AUTHORIZATION regress_rls_alice;
3503 CREATE POLICY comment_1 ON comment USING (blog_id < 4);
3504 ALTER TABLE comment ENABLE ROW LEVEL SECURITY;
3505 SET SESSION AUTHORIZATION regress_rls_bob;
3506 -- Check RLS JOIN RLS
3507 SELECT id, author, message FROM blog JOIN comment ON id = blog_id;
3508 id | author | message
3509 ----+--------+-------------
3510 2 | bob | who did it?
3513 SELECT id, author, message FROM comment JOIN blog ON id = blog_id;
3514 id | author | message
3515 ----+--------+-------------
3516 2 | bob | who did it?
3519 SET SESSION AUTHORIZATION regress_rls_alice;
3520 DROP TABLE blog, comment;
3522 -- Default Deny Policy
3524 RESET SESSION AUTHORIZATION;
3525 DROP POLICY p2 ON t1;
3526 ALTER TABLE t1 OWNER TO regress_rls_alice;
3527 -- Check that default deny does not apply to superuser.
3528 RESET SESSION AUTHORIZATION;
3531 ----+----------------------------------
3532 1 | 6b86b273ff34fce19d6b804eff5a3f57
3533 3 | 4e07408562bedb8b60ce05c1decfe3ad
3534 5 | ef2d127de37b942baad06145e54b0c61
3535 7 | 7902699be42c8a8e46fbbb4501726517
3536 9 | 19581e27de7ced00ff1ce50b2047e7a5
3537 11 | 4fc82b26aecb47d2868c4efbe3581732
3538 13 | 3fdba35f04dc8c462986c992bcf87554
3539 15 | e629fa6598d732768f7c726b4b621285
3540 17 | 4523540f1504cd17100c4835e85b7eef
3541 19 | 9400f1b21cb527d7fa3d3eabba93557a
3542 0 | 5feceb66ffc86f38d952786c6d696c79
3543 2 | d4735e3a265e16eee03f59718b9b5d03
3544 4 | 4b227777d4dd1fc61c6f884f48641d02
3545 6 | e7f6c011776e8db7cd330b54174fd76f
3546 8 | 2c624232cdd221771294dfbb310aca00
3547 10 | 4a44dc15364204a80fe80e9039455cc1
3548 12 | 6b51d431df5d7f141cbececcf79edf3d
3549 14 | 8527a891e224136950ff32ca212b45bc
3550 16 | b17ef6d19c7a5b1ee83b907c595526dc
3551 18 | 4ec9599fc203d176a301536c2e091a19
3552 20 | f5ca38f748a1d6eaf726b8a42fb575c3
3556 EXPLAIN (COSTS OFF) SELECT * FROM t1;
3562 -- Check that default deny does not apply to table owner.
3563 SET SESSION AUTHORIZATION regress_rls_alice;
3566 ----+----------------------------------
3567 1 | 6b86b273ff34fce19d6b804eff5a3f57
3568 3 | 4e07408562bedb8b60ce05c1decfe3ad
3569 5 | ef2d127de37b942baad06145e54b0c61
3570 7 | 7902699be42c8a8e46fbbb4501726517
3571 9 | 19581e27de7ced00ff1ce50b2047e7a5
3572 11 | 4fc82b26aecb47d2868c4efbe3581732
3573 13 | 3fdba35f04dc8c462986c992bcf87554
3574 15 | e629fa6598d732768f7c726b4b621285
3575 17 | 4523540f1504cd17100c4835e85b7eef
3576 19 | 9400f1b21cb527d7fa3d3eabba93557a
3577 0 | 5feceb66ffc86f38d952786c6d696c79
3578 2 | d4735e3a265e16eee03f59718b9b5d03
3579 4 | 4b227777d4dd1fc61c6f884f48641d02
3580 6 | e7f6c011776e8db7cd330b54174fd76f
3581 8 | 2c624232cdd221771294dfbb310aca00
3582 10 | 4a44dc15364204a80fe80e9039455cc1
3583 12 | 6b51d431df5d7f141cbececcf79edf3d
3584 14 | 8527a891e224136950ff32ca212b45bc
3585 16 | b17ef6d19c7a5b1ee83b907c595526dc
3586 18 | 4ec9599fc203d176a301536c2e091a19
3587 20 | f5ca38f748a1d6eaf726b8a42fb575c3
3591 EXPLAIN (COSTS OFF) SELECT * FROM t1;
3597 -- Check that default deny applies to non-owner/non-superuser when RLS on.
3598 SET SESSION AUTHORIZATION regress_rls_bob;
3599 SET row_security TO ON;
3605 EXPLAIN (COSTS OFF) SELECT * FROM t1;
3607 --------------------------
3609 One-Time Filter: false
3612 SET SESSION AUTHORIZATION regress_rls_bob;
3618 EXPLAIN (COSTS OFF) SELECT * FROM t1;
3620 --------------------------
3622 One-Time Filter: false
3628 RESET SESSION AUTHORIZATION;
3629 DROP TABLE copy_t CASCADE;
3630 ERROR: table "copy_t" does not exist
3631 CREATE TABLE copy_t (a integer, b text);
3632 CREATE POLICY p1 ON copy_t USING (a % 2 = 0);
3633 ALTER TABLE copy_t ENABLE ROW LEVEL SECURITY;
3634 GRANT ALL ON copy_t TO regress_rls_bob, regress_rls_exempt_user;
3635 INSERT INTO copy_t (SELECT x, public.fipshash(x::text) FROM generate_series(0,10) x);
3636 -- Check COPY TO as Superuser/owner.
3637 RESET SESSION AUTHORIZATION;
3638 SET row_security TO OFF;
3639 COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ',';
3640 0,5feceb66ffc86f38d952786c6d696c79
3641 1,6b86b273ff34fce19d6b804eff5a3f57
3642 2,d4735e3a265e16eee03f59718b9b5d03
3643 3,4e07408562bedb8b60ce05c1decfe3ad
3644 4,4b227777d4dd1fc61c6f884f48641d02
3645 5,ef2d127de37b942baad06145e54b0c61
3646 6,e7f6c011776e8db7cd330b54174fd76f
3647 7,7902699be42c8a8e46fbbb4501726517
3648 8,2c624232cdd221771294dfbb310aca00
3649 9,19581e27de7ced00ff1ce50b2047e7a5
3650 10,4a44dc15364204a80fe80e9039455cc1
3651 SET row_security TO ON;
3652 COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ',';
3653 0,5feceb66ffc86f38d952786c6d696c79
3654 1,6b86b273ff34fce19d6b804eff5a3f57
3655 2,d4735e3a265e16eee03f59718b9b5d03
3656 3,4e07408562bedb8b60ce05c1decfe3ad
3657 4,4b227777d4dd1fc61c6f884f48641d02
3658 5,ef2d127de37b942baad06145e54b0c61
3659 6,e7f6c011776e8db7cd330b54174fd76f
3660 7,7902699be42c8a8e46fbbb4501726517
3661 8,2c624232cdd221771294dfbb310aca00
3662 9,19581e27de7ced00ff1ce50b2047e7a5
3663 10,4a44dc15364204a80fe80e9039455cc1
3664 -- Check COPY TO as user with permissions.
3665 SET SESSION AUTHORIZATION regress_rls_bob;
3666 SET row_security TO OFF;
3667 COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; --fail - would be affected by RLS
3668 ERROR: query would be affected by row-level security policy for table "copy_t"
3669 SET row_security TO ON;
3670 COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; --ok
3671 0,5feceb66ffc86f38d952786c6d696c79
3672 2,d4735e3a265e16eee03f59718b9b5d03
3673 4,4b227777d4dd1fc61c6f884f48641d02
3674 6,e7f6c011776e8db7cd330b54174fd76f
3675 8,2c624232cdd221771294dfbb310aca00
3676 10,4a44dc15364204a80fe80e9039455cc1
3677 -- Check COPY TO as user with permissions and BYPASSRLS
3678 SET SESSION AUTHORIZATION regress_rls_exempt_user;
3679 SET row_security TO OFF;
3680 COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; --ok
3681 0,5feceb66ffc86f38d952786c6d696c79
3682 1,6b86b273ff34fce19d6b804eff5a3f57
3683 2,d4735e3a265e16eee03f59718b9b5d03
3684 3,4e07408562bedb8b60ce05c1decfe3ad
3685 4,4b227777d4dd1fc61c6f884f48641d02
3686 5,ef2d127de37b942baad06145e54b0c61
3687 6,e7f6c011776e8db7cd330b54174fd76f
3688 7,7902699be42c8a8e46fbbb4501726517
3689 8,2c624232cdd221771294dfbb310aca00
3690 9,19581e27de7ced00ff1ce50b2047e7a5
3691 10,4a44dc15364204a80fe80e9039455cc1
3692 SET row_security TO ON;
3693 COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; --ok
3694 0,5feceb66ffc86f38d952786c6d696c79
3695 1,6b86b273ff34fce19d6b804eff5a3f57
3696 2,d4735e3a265e16eee03f59718b9b5d03
3697 3,4e07408562bedb8b60ce05c1decfe3ad
3698 4,4b227777d4dd1fc61c6f884f48641d02
3699 5,ef2d127de37b942baad06145e54b0c61
3700 6,e7f6c011776e8db7cd330b54174fd76f
3701 7,7902699be42c8a8e46fbbb4501726517
3702 8,2c624232cdd221771294dfbb310aca00
3703 9,19581e27de7ced00ff1ce50b2047e7a5
3704 10,4a44dc15364204a80fe80e9039455cc1
3705 -- Check COPY TO as user without permissions. SET row_security TO OFF;
3706 SET SESSION AUTHORIZATION regress_rls_carol;
3707 SET row_security TO OFF;
3708 COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; --fail - would be affected by RLS
3709 ERROR: query would be affected by row-level security policy for table "copy_t"
3710 SET row_security TO ON;
3711 COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; --fail - permission denied
3712 ERROR: permission denied for table copy_t
3713 -- Check COPY relation TO; keep it just one row to avoid reordering issues
3714 RESET SESSION AUTHORIZATION;
3715 SET row_security TO ON;
3716 CREATE TABLE copy_rel_to (a integer, b text);
3717 CREATE POLICY p1 ON copy_rel_to USING (a % 2 = 0);
3718 ALTER TABLE copy_rel_to ENABLE ROW LEVEL SECURITY;
3719 GRANT ALL ON copy_rel_to TO regress_rls_bob, regress_rls_exempt_user;
3720 INSERT INTO copy_rel_to VALUES (1, public.fipshash('1'));
3721 -- Check COPY TO as Superuser/owner.
3722 RESET SESSION AUTHORIZATION;
3723 SET row_security TO OFF;
3724 COPY copy_rel_to TO STDOUT WITH DELIMITER ',';
3725 1,6b86b273ff34fce19d6b804eff5a3f57
3726 SET row_security TO ON;
3727 COPY copy_rel_to TO STDOUT WITH DELIMITER ',';
3728 1,6b86b273ff34fce19d6b804eff5a3f57
3729 -- Check COPY TO as user with permissions.
3730 SET SESSION AUTHORIZATION regress_rls_bob;
3731 SET row_security TO OFF;
3732 COPY copy_rel_to TO STDOUT WITH DELIMITER ','; --fail - would be affected by RLS
3733 ERROR: query would be affected by row-level security policy for table "copy_rel_to"
3734 SET row_security TO ON;
3735 COPY copy_rel_to TO STDOUT WITH DELIMITER ','; --ok
3736 -- Check COPY TO as user with permissions and BYPASSRLS
3737 SET SESSION AUTHORIZATION regress_rls_exempt_user;
3738 SET row_security TO OFF;
3739 COPY copy_rel_to TO STDOUT WITH DELIMITER ','; --ok
3740 1,6b86b273ff34fce19d6b804eff5a3f57
3741 SET row_security TO ON;
3742 COPY copy_rel_to TO STDOUT WITH DELIMITER ','; --ok
3743 1,6b86b273ff34fce19d6b804eff5a3f57
3744 -- Check COPY TO as user without permissions. SET row_security TO OFF;
3745 SET SESSION AUTHORIZATION regress_rls_carol;
3746 SET row_security TO OFF;
3747 COPY copy_rel_to TO STDOUT WITH DELIMITER ','; --fail - permission denied
3748 ERROR: permission denied for table copy_rel_to
3749 SET row_security TO ON;
3750 COPY copy_rel_to TO STDOUT WITH DELIMITER ','; --fail - permission denied
3751 ERROR: permission denied for table copy_rel_to
3752 -- Check behavior with a child table.
3753 RESET SESSION AUTHORIZATION;
3754 SET row_security TO ON;
3755 CREATE TABLE copy_rel_to_child () INHERITS (copy_rel_to);
3756 INSERT INTO copy_rel_to_child VALUES (1, 'one'), (2, 'two');
3757 -- Check COPY TO as Superuser/owner.
3758 RESET SESSION AUTHORIZATION;
3759 SET row_security TO OFF;
3760 COPY copy_rel_to TO STDOUT WITH DELIMITER ',';
3761 1,6b86b273ff34fce19d6b804eff5a3f57
3762 SET row_security TO ON;
3763 COPY copy_rel_to TO STDOUT WITH DELIMITER ',';
3764 1,6b86b273ff34fce19d6b804eff5a3f57
3765 -- Check COPY TO as user with permissions.
3766 SET SESSION AUTHORIZATION regress_rls_bob;
3767 SET row_security TO OFF;
3768 COPY copy_rel_to TO STDOUT WITH DELIMITER ','; --fail - would be affected by RLS
3769 ERROR: query would be affected by row-level security policy for table "copy_rel_to"
3770 SET row_security TO ON;
3771 COPY copy_rel_to TO STDOUT WITH DELIMITER ','; --ok
3772 -- Check COPY TO as user with permissions and BYPASSRLS
3773 SET SESSION AUTHORIZATION regress_rls_exempt_user;
3774 SET row_security TO OFF;
3775 COPY copy_rel_to TO STDOUT WITH DELIMITER ','; --ok
3776 1,6b86b273ff34fce19d6b804eff5a3f57
3777 SET row_security TO ON;
3778 COPY copy_rel_to TO STDOUT WITH DELIMITER ','; --ok
3779 1,6b86b273ff34fce19d6b804eff5a3f57
3780 -- Check COPY TO as user without permissions. SET row_security TO OFF;
3781 SET SESSION AUTHORIZATION regress_rls_carol;
3782 SET row_security TO OFF;
3783 COPY copy_rel_to TO STDOUT WITH DELIMITER ','; --fail - permission denied
3784 ERROR: permission denied for table copy_rel_to
3785 SET row_security TO ON;
3786 COPY copy_rel_to TO STDOUT WITH DELIMITER ','; --fail - permission denied
3787 ERROR: permission denied for table copy_rel_to
3788 -- Check COPY FROM as Superuser/owner.
3789 RESET SESSION AUTHORIZATION;
3790 SET row_security TO OFF;
3791 COPY copy_t FROM STDIN; --ok
3792 SET row_security TO ON;
3793 COPY copy_t FROM STDIN; --ok
3794 -- Check COPY FROM as user with permissions.
3795 SET SESSION AUTHORIZATION regress_rls_bob;
3796 SET row_security TO OFF;
3797 COPY copy_t FROM STDIN; --fail - would be affected by RLS.
3798 ERROR: query would be affected by row-level security policy for table "copy_t"
3799 SET row_security TO ON;
3800 COPY copy_t FROM STDIN; --fail - COPY FROM not supported by RLS.
3801 ERROR: COPY FROM not supported with row-level security
3802 HINT: Use INSERT statements instead.
3803 -- Check COPY FROM as user with permissions and BYPASSRLS
3804 SET SESSION AUTHORIZATION regress_rls_exempt_user;
3805 SET row_security TO ON;
3806 COPY copy_t FROM STDIN; --ok
3807 -- Check COPY FROM as user without permissions.
3808 SET SESSION AUTHORIZATION regress_rls_carol;
3809 SET row_security TO OFF;
3810 COPY copy_t FROM STDIN; --fail - permission denied.
3811 ERROR: permission denied for table copy_t
3812 SET row_security TO ON;
3813 COPY copy_t FROM STDIN; --fail - permission denied.
3814 ERROR: permission denied for table copy_t
3815 RESET SESSION AUTHORIZATION;
3817 DROP TABLE copy_rel_to CASCADE;
3818 NOTICE: drop cascades to table copy_rel_to_child
3819 -- Check WHERE CURRENT OF
3820 SET SESSION AUTHORIZATION regress_rls_alice;
3821 CREATE TABLE current_check (currentid int, payload text, rlsuser text);
3822 GRANT ALL ON current_check TO PUBLIC;
3823 INSERT INTO current_check VALUES
3824 (1, 'abc', 'regress_rls_bob'),
3825 (2, 'bcd', 'regress_rls_bob'),
3826 (3, 'cde', 'regress_rls_bob'),
3827 (4, 'def', 'regress_rls_bob');
3828 CREATE POLICY p1 ON current_check FOR SELECT USING (currentid % 2 = 0);
3829 CREATE POLICY p2 ON current_check FOR DELETE USING (currentid = 4 AND rlsuser = current_user);
3830 CREATE POLICY p3 ON current_check FOR UPDATE USING (currentid = 4) WITH CHECK (rlsuser = current_user);
3831 ALTER TABLE current_check ENABLE ROW LEVEL SECURITY;
3832 SET SESSION AUTHORIZATION regress_rls_bob;
3833 -- Can SELECT even rows
3834 SELECT * FROM current_check;
3835 currentid | payload | rlsuser
3836 -----------+---------+-----------------
3837 2 | bcd | regress_rls_bob
3838 4 | def | regress_rls_bob
3841 -- Cannot UPDATE row 2
3842 UPDATE current_check SET payload = payload || '_new' WHERE currentid = 2 RETURNING *;
3843 currentid | payload | rlsuser
3844 -----------+---------+---------
3848 DECLARE current_check_cursor SCROLL CURSOR FOR SELECT * FROM current_check;
3849 -- Returns rows that can be seen according to SELECT policy, like plain SELECT
3850 -- above (even rows)
3851 FETCH ABSOLUTE 1 FROM current_check_cursor;
3852 currentid | payload | rlsuser
3853 -----------+---------+-----------------
3854 2 | bcd | regress_rls_bob
3857 -- Still cannot UPDATE row 2 through cursor
3858 UPDATE current_check SET payload = payload || '_new' WHERE CURRENT OF current_check_cursor RETURNING *;
3859 currentid | payload | rlsuser
3860 -----------+---------+---------
3863 -- Can update row 4 through cursor, which is the next visible row
3864 FETCH RELATIVE 1 FROM current_check_cursor;
3865 currentid | payload | rlsuser
3866 -----------+---------+-----------------
3867 4 | def | regress_rls_bob
3870 UPDATE current_check SET payload = payload || '_new' WHERE CURRENT OF current_check_cursor RETURNING *;
3871 currentid | payload | rlsuser
3872 -----------+---------+-----------------
3873 4 | def_new | regress_rls_bob
3876 SELECT * FROM current_check;
3877 currentid | payload | rlsuser
3878 -----------+---------+-----------------
3879 2 | bcd | regress_rls_bob
3880 4 | def_new | regress_rls_bob
3883 -- Plan should be a subquery TID scan
3884 EXPLAIN (COSTS OFF) UPDATE current_check SET payload = payload WHERE CURRENT OF current_check_cursor;
3886 -------------------------------------------------------------
3887 Update on current_check
3888 -> Tid Scan on current_check
3889 TID Cond: CURRENT OF current_check_cursor
3890 Filter: ((currentid = 4) AND ((currentid % 2) = 0))
3893 -- Similarly can only delete row 4
3894 FETCH ABSOLUTE 1 FROM current_check_cursor;
3895 currentid | payload | rlsuser
3896 -----------+---------+-----------------
3897 2 | bcd | regress_rls_bob
3900 DELETE FROM current_check WHERE CURRENT OF current_check_cursor RETURNING *;
3901 currentid | payload | rlsuser
3902 -----------+---------+---------
3905 FETCH RELATIVE 1 FROM current_check_cursor;
3906 currentid | payload | rlsuser
3907 -----------+---------+-----------------
3908 4 | def | regress_rls_bob
3911 DELETE FROM current_check WHERE CURRENT OF current_check_cursor RETURNING *;
3912 currentid | payload | rlsuser
3913 -----------+---------+-----------------
3914 4 | def_new | regress_rls_bob
3917 SELECT * FROM current_check;
3918 currentid | payload | rlsuser
3919 -----------+---------+-----------------
3920 2 | bcd | regress_rls_bob
3924 -- Check that RLS filters that are tidquals don't override WHERE CURRENT OF
3926 CREATE TABLE current_check_2 (a int, b text);
3927 INSERT INTO current_check_2 VALUES (1, 'Apple');
3928 ALTER TABLE current_check_2 ENABLE ROW LEVEL SECURITY;
3929 ALTER TABLE current_check_2 FORCE ROW LEVEL SECURITY;
3930 -- policy must accept ctid = (InvalidBlockNumber,0) since updates check it
3931 -- before assigning a ctid to the new row
3932 CREATE POLICY p1 ON current_check_2 AS PERMISSIVE
3933 USING (ctid IN ('(0,1)', '(0,2)', '(4294967295,0)'));
3934 SELECT ctid, * FROM current_check_2;
3940 DECLARE current_check_cursor CURSOR FOR SELECT * FROM current_check_2;
3941 FETCH FROM current_check_cursor;
3948 UPDATE current_check_2 SET b = 'Manzana' WHERE CURRENT OF current_check_cursor;
3950 ----------------------------------------------------------------------------
3951 Update on current_check_2
3952 -> Tid Scan on current_check_2
3953 TID Cond: CURRENT OF current_check_cursor
3954 Filter: (ctid = ANY ('{"(0,1)","(0,2)","(4294967295,0)"}'::tid[]))
3957 UPDATE current_check_2 SET b = 'Manzana' WHERE CURRENT OF current_check_cursor;
3958 SELECT ctid, * FROM current_check_2;
3960 -------+---+---------
3966 -- check pg_stats view filtering
3968 SET row_security TO ON;
3969 SET SESSION AUTHORIZATION regress_rls_alice;
3970 ANALYZE current_check;
3972 SELECT row_security_active('current_check');
3974 ---------------------
3978 SELECT attname, most_common_vals FROM pg_stats
3979 WHERE tablename = 'current_check'
3981 attname | most_common_vals
3982 -----------+-------------------
3985 rlsuser | {regress_rls_bob}
3988 SET SESSION AUTHORIZATION regress_rls_bob;
3989 -- Stats not visible
3990 SELECT row_security_active('current_check');
3992 ---------------------
3996 SELECT attname, most_common_vals FROM pg_stats
3997 WHERE tablename = 'current_check'
3999 attname | most_common_vals
4000 ---------+------------------
4004 -- Collation support
4007 CREATE TABLE coll_t (c) AS VALUES ('bar'::text);
4008 CREATE POLICY coll_p ON coll_t USING (c < ('foo'::text COLLATE "C"));
4009 ALTER TABLE coll_t ENABLE ROW LEVEL SECURITY;
4010 GRANT SELECT ON coll_t TO regress_rls_alice;
4011 SELECT (string_to_array(polqual, ':'))[7] AS inputcollid FROM pg_policy WHERE polrelid = 'coll_t'::regclass;
4017 SET SESSION AUTHORIZATION regress_rls_alice;
4018 SELECT * FROM coll_t;
4026 -- Shared Object Dependencies
4028 RESET SESSION AUTHORIZATION;
4030 CREATE ROLE regress_rls_eve;
4031 CREATE ROLE regress_rls_frank;
4032 CREATE TABLE tbl1 (c) AS VALUES ('bar'::text);
4033 GRANT SELECT ON TABLE tbl1 TO regress_rls_eve;
4034 CREATE POLICY P ON tbl1 TO regress_rls_eve, regress_rls_frank USING (true);
4035 SELECT refclassid::regclass, deptype
4037 WHERE classid = 'pg_policy'::regclass
4038 AND refobjid = 'tbl1'::regclass;
4039 refclassid | deptype
4040 ------------+---------
4044 SELECT refclassid::regclass, deptype
4046 WHERE classid = 'pg_policy'::regclass
4047 AND refobjid IN ('regress_rls_eve'::regrole, 'regress_rls_frank'::regrole);
4048 refclassid | deptype
4049 ------------+---------
4055 DROP ROLE regress_rls_eve; --fails due to dependency on POLICY p
4056 ERROR: role "regress_rls_eve" cannot be dropped because some objects depend on it
4057 DETAIL: privileges for table tbl1
4058 target of policy p on table tbl1
4060 ALTER POLICY p ON tbl1 TO regress_rls_frank USING (true);
4062 DROP ROLE regress_rls_eve; --fails due to dependency on GRANT SELECT
4063 ERROR: role "regress_rls_eve" cannot be dropped because some objects depend on it
4064 DETAIL: privileges for table tbl1
4066 REVOKE ALL ON TABLE tbl1 FROM regress_rls_eve;
4068 DROP ROLE regress_rls_eve; --succeeds
4071 DROP ROLE regress_rls_frank; --fails due to dependency on POLICY p
4072 ERROR: role "regress_rls_frank" cannot be dropped because some objects depend on it
4073 DETAIL: target of policy p on table tbl1
4075 DROP POLICY p ON tbl1;
4077 DROP ROLE regress_rls_frank; -- succeeds
4079 ROLLBACK; -- cleanup
4081 -- Policy expression handling
4084 CREATE TABLE t (c) AS VALUES ('bar'::text);
4085 CREATE POLICY p ON t USING (max(c)); -- fails: aggregate functions are not allowed in policy expressions
4086 ERROR: aggregate functions are not allowed in policy expressions
4089 -- Non-target relations are only subject to SELECT policies
4091 SET SESSION AUTHORIZATION regress_rls_alice;
4092 CREATE TABLE r1 (a int);
4093 CREATE TABLE r2 (a int);
4094 INSERT INTO r1 VALUES (10), (20);
4095 INSERT INTO r2 VALUES (10), (20);
4096 GRANT ALL ON r1, r2 TO regress_rls_bob;
4097 CREATE POLICY p1 ON r1 USING (true);
4098 ALTER TABLE r1 ENABLE ROW LEVEL SECURITY;
4099 CREATE POLICY p1 ON r2 FOR SELECT USING (true);
4100 CREATE POLICY p2 ON r2 FOR INSERT WITH CHECK (false);
4101 CREATE POLICY p3 ON r2 FOR UPDATE USING (false);
4102 CREATE POLICY p4 ON r2 FOR DELETE USING (false);
4103 ALTER TABLE r2 ENABLE ROW LEVEL SECURITY;
4104 SET SESSION AUTHORIZATION regress_rls_bob;
4120 INSERT INTO r2 VALUES (2); -- Not allowed
4121 ERROR: new row violates row-level security policy for table "r2"
4122 UPDATE r2 SET a = 2 RETURNING *; -- Updates nothing
4127 DELETE FROM r2 RETURNING *; -- Deletes nothing
4132 -- r2 can be used as a non-target relation in DML
4133 INSERT INTO r1 SELECT a + 1 FROM r2 RETURNING *; -- OK
4140 UPDATE r1 SET a = r2.a + 2 FROM r2 WHERE r1.a = r2.a RETURNING *; -- OK
4147 DELETE FROM r1 USING r2 WHERE r1.a = r2.a + 2 RETURNING *; -- OK
4168 SET SESSION AUTHORIZATION regress_rls_alice;
4172 -- FORCE ROW LEVEL SECURITY applies RLS to owners too
4174 SET SESSION AUTHORIZATION regress_rls_alice;
4175 SET row_security = on;
4176 CREATE TABLE r1 (a int);
4177 INSERT INTO r1 VALUES (10), (20);
4178 CREATE POLICY p1 ON r1 USING (false);
4179 ALTER TABLE r1 ENABLE ROW LEVEL SECURITY;
4180 ALTER TABLE r1 FORCE ROW LEVEL SECURITY;
4181 -- No error, but no rows
4188 INSERT INTO r1 VALUES (1);
4189 ERROR: new row violates row-level security policy for table "r1"
4190 -- No error (unable to see any rows to update)
4191 UPDATE r1 SET a = 1;
4197 -- No error (unable to see any rows to delete)
4204 SET row_security = off;
4205 -- these all fail, would be affected by RLS
4207 ERROR: query would be affected by row-level security policy for table "r1"
4208 HINT: To disable the policy for the table's owner, use ALTER TABLE NO FORCE ROW LEVEL SECURITY.
4209 UPDATE r1 SET a = 1;
4210 ERROR: query would be affected by row-level security policy for table "r1"
4211 HINT: To disable the policy for the table's owner, use ALTER TABLE NO FORCE ROW LEVEL SECURITY.
4213 ERROR: query would be affected by row-level security policy for table "r1"
4214 HINT: To disable the policy for the table's owner, use ALTER TABLE NO FORCE ROW LEVEL SECURITY.
4217 -- FORCE ROW LEVEL SECURITY does not break RI
4219 SET SESSION AUTHORIZATION regress_rls_alice;
4220 SET row_security = on;
4221 CREATE TABLE r1 (a int PRIMARY KEY);
4222 CREATE TABLE r2 (a int REFERENCES r1);
4223 INSERT INTO r1 VALUES (10), (20);
4224 INSERT INTO r2 VALUES (10), (20);
4225 -- Create policies on r2 which prevent the
4226 -- owner from seeing any rows, but RI should
4228 CREATE POLICY p1 ON r2 USING (false);
4229 ALTER TABLE r2 ENABLE ROW LEVEL SECURITY;
4230 ALTER TABLE r2 FORCE ROW LEVEL SECURITY;
4231 -- Errors due to rows in r2
4233 ERROR: update or delete on table "r1" violates foreign key constraint "r2_a_fkey" on table "r2"
4234 DETAIL: Key (a)=(10) is still referenced from table "r2".
4235 -- Reset r2 to no-RLS
4236 DROP POLICY p1 ON r2;
4237 ALTER TABLE r2 NO FORCE ROW LEVEL SECURITY;
4238 ALTER TABLE r2 DISABLE ROW LEVEL SECURITY;
4239 -- clean out r2 for INSERT test below
4241 -- Change r1 to not allow rows to be seen
4242 CREATE POLICY p1 ON r1 USING (false);
4243 ALTER TABLE r1 ENABLE ROW LEVEL SECURITY;
4244 ALTER TABLE r1 FORCE ROW LEVEL SECURITY;
4251 -- No error, RI still sees that row exists in r1
4252 INSERT INTO r2 VALUES (10);
4255 -- Ensure cascaded DELETE works
4256 CREATE TABLE r1 (a int PRIMARY KEY);
4257 CREATE TABLE r2 (a int REFERENCES r1 ON DELETE CASCADE);
4258 INSERT INTO r1 VALUES (10), (20);
4259 INSERT INTO r2 VALUES (10), (20);
4260 -- Create policies on r2 which prevent the
4261 -- owner from seeing any rows, but RI should
4263 CREATE POLICY p1 ON r2 USING (false);
4264 ALTER TABLE r2 ENABLE ROW LEVEL SECURITY;
4265 ALTER TABLE r2 FORCE ROW LEVEL SECURITY;
4266 -- Deletes all records from both
4268 -- Remove FORCE from r2
4269 ALTER TABLE r2 NO FORCE ROW LEVEL SECURITY;
4270 -- As owner, we now bypass RLS
4271 -- verify no rows in r2 now
4279 -- Ensure cascaded UPDATE works
4280 CREATE TABLE r1 (a int PRIMARY KEY);
4281 CREATE TABLE r2 (a int REFERENCES r1 ON UPDATE CASCADE);
4282 INSERT INTO r1 VALUES (10), (20);
4283 INSERT INTO r2 VALUES (10), (20);
4284 -- Create policies on r2 which prevent the
4285 -- owner from seeing any rows, but RI should
4287 CREATE POLICY p1 ON r2 USING (false);
4288 ALTER TABLE r2 ENABLE ROW LEVEL SECURITY;
4289 ALTER TABLE r2 FORCE ROW LEVEL SECURITY;
4290 -- Updates records in both
4291 UPDATE r1 SET a = a+5;
4292 -- Remove FORCE from r2
4293 ALTER TABLE r2 NO FORCE ROW LEVEL SECURITY;
4294 -- As owner, we now bypass RLS
4295 -- verify records in r2 updated
4306 -- Test INSERT+RETURNING applies SELECT policies as
4307 -- WithCheckOptions (meaning an error is thrown)
4309 SET SESSION AUTHORIZATION regress_rls_alice;
4310 SET row_security = on;
4311 CREATE TABLE r1 (a int);
4312 CREATE POLICY p1 ON r1 FOR SELECT USING (false);
4313 CREATE POLICY p2 ON r1 FOR INSERT WITH CHECK (true);
4314 ALTER TABLE r1 ENABLE ROW LEVEL SECURITY;
4315 ALTER TABLE r1 FORCE ROW LEVEL SECURITY;
4317 INSERT INTO r1 VALUES (10), (20);
4318 -- No error, but no rows
4324 SET row_security = off;
4325 -- fail, would be affected by RLS
4327 ERROR: query would be affected by row-level security policy for table "r1"
4328 HINT: To disable the policy for the table's owner, use ALTER TABLE NO FORCE ROW LEVEL SECURITY.
4329 SET row_security = on;
4331 INSERT INTO r1 VALUES (10), (20) RETURNING *;
4332 ERROR: new row violates row-level security policy for table "r1"
4335 -- Test UPDATE+RETURNING applies SELECT policies as
4336 -- WithCheckOptions (meaning an error is thrown)
4338 SET SESSION AUTHORIZATION regress_rls_alice;
4339 SET row_security = on;
4340 CREATE TABLE r1 (a int PRIMARY KEY);
4341 CREATE POLICY p1 ON r1 FOR SELECT USING (a < 20);
4342 CREATE POLICY p2 ON r1 FOR UPDATE USING (a < 20) WITH CHECK (true);
4343 CREATE POLICY p3 ON r1 FOR INSERT WITH CHECK (true);
4344 INSERT INTO r1 VALUES (10);
4345 ALTER TABLE r1 ENABLE ROW LEVEL SECURITY;
4346 ALTER TABLE r1 FORCE ROW LEVEL SECURITY;
4348 UPDATE r1 SET a = 30;
4349 -- Show updated rows
4350 ALTER TABLE r1 NO FORCE ROW LEVEL SECURITY;
4357 -- reset value in r1 for test with RETURNING
4358 UPDATE r1 SET a = 10;
4366 ALTER TABLE r1 FORCE ROW LEVEL SECURITY;
4368 UPDATE r1 SET a = 30 RETURNING *;
4369 ERROR: new row violates row-level security policy for table "r1"
4370 -- UPDATE path of INSERT ... ON CONFLICT DO UPDATE should also error out
4371 INSERT INTO r1 VALUES (10)
4372 ON CONFLICT (a) DO UPDATE SET a = 30 RETURNING *;
4373 ERROR: new row violates row-level security policy for table "r1"
4374 -- Should still error out without RETURNING (use of arbiter always requires
4375 -- SELECT permissions)
4376 INSERT INTO r1 VALUES (10)
4377 ON CONFLICT (a) DO UPDATE SET a = 30;
4378 ERROR: new row violates row-level security policy for table "r1"
4379 INSERT INTO r1 VALUES (10)
4380 ON CONFLICT ON CONSTRAINT r1_pkey DO UPDATE SET a = 30;
4381 ERROR: new row violates row-level security policy for table "r1"
4383 -- Check dependency handling
4384 RESET SESSION AUTHORIZATION;
4385 CREATE TABLE dep1 (c1 int);
4386 CREATE TABLE dep2 (c1 int);
4387 CREATE POLICY dep_p1 ON dep1 TO regress_rls_bob USING (c1 > (select max(dep2.c1) from dep2));
4388 ALTER POLICY dep_p1 ON dep1 TO regress_rls_bob,regress_rls_carol;
4389 -- Should return one
4390 SELECT count(*) = 1 FROM pg_depend
4391 WHERE objid = (SELECT oid FROM pg_policy WHERE polname = 'dep_p1')
4392 AND refobjid = (SELECT oid FROM pg_class WHERE relname = 'dep2');
4398 ALTER POLICY dep_p1 ON dep1 USING (true);
4399 -- Should return one
4400 SELECT count(*) = 1 FROM pg_shdepend
4401 WHERE objid = (SELECT oid FROM pg_policy WHERE polname = 'dep_p1')
4402 AND refobjid = (SELECT oid FROM pg_authid WHERE rolname = 'regress_rls_bob');
4408 -- Should return one
4409 SELECT count(*) = 1 FROM pg_shdepend
4410 WHERE objid = (SELECT oid FROM pg_policy WHERE polname = 'dep_p1')
4411 AND refobjid = (SELECT oid FROM pg_authid WHERE rolname = 'regress_rls_carol');
4417 -- Should return zero
4418 SELECT count(*) = 0 FROM pg_depend
4419 WHERE objid = (SELECT oid FROM pg_policy WHERE polname = 'dep_p1')
4420 AND refobjid = (SELECT oid FROM pg_class WHERE relname = 'dep2');
4426 -- DROP OWNED BY testing
4427 RESET SESSION AUTHORIZATION;
4428 CREATE ROLE regress_rls_dob_role1;
4429 CREATE ROLE regress_rls_dob_role2;
4430 CREATE TABLE dob_t1 (c1 int);
4431 CREATE TABLE dob_t2 (c1 int) PARTITION BY RANGE (c1);
4432 CREATE POLICY p1 ON dob_t1 TO regress_rls_dob_role1 USING (true);
4433 DROP OWNED BY regress_rls_dob_role1;
4434 DROP POLICY p1 ON dob_t1; -- should fail, already gone
4435 ERROR: policy "p1" for table "dob_t1" does not exist
4436 CREATE POLICY p1 ON dob_t1 TO regress_rls_dob_role1,regress_rls_dob_role2 USING (true);
4437 DROP OWNED BY regress_rls_dob_role1;
4438 DROP POLICY p1 ON dob_t1; -- should succeed
4439 -- same cases with duplicate polroles entries
4440 CREATE POLICY p1 ON dob_t1 TO regress_rls_dob_role1,regress_rls_dob_role1 USING (true);
4441 DROP OWNED BY regress_rls_dob_role1;
4442 DROP POLICY p1 ON dob_t1; -- should fail, already gone
4443 ERROR: policy "p1" for table "dob_t1" does not exist
4444 CREATE POLICY p1 ON dob_t1 TO regress_rls_dob_role1,regress_rls_dob_role1,regress_rls_dob_role2 USING (true);
4445 DROP OWNED BY regress_rls_dob_role1;
4446 DROP POLICY p1 ON dob_t1; -- should succeed
4447 -- partitioned target
4448 CREATE POLICY p1 ON dob_t2 TO regress_rls_dob_role1,regress_rls_dob_role2 USING (true);
4449 DROP OWNED BY regress_rls_dob_role1;
4450 DROP POLICY p1 ON dob_t2; -- should succeed
4451 DROP USER regress_rls_dob_role1;
4452 DROP USER regress_rls_dob_role2;
4453 -- Bug #15708: view + table with RLS should check policies as view owner
4454 CREATE TABLE ref_tbl (a int);
4455 INSERT INTO ref_tbl VALUES (1);
4456 CREATE TABLE rls_tbl (a int);
4457 INSERT INTO rls_tbl VALUES (10);
4458 ALTER TABLE rls_tbl ENABLE ROW LEVEL SECURITY;
4459 CREATE POLICY p1 ON rls_tbl USING (EXISTS (SELECT 1 FROM ref_tbl));
4460 GRANT SELECT ON ref_tbl TO regress_rls_bob;
4461 GRANT SELECT ON rls_tbl TO regress_rls_bob;
4462 CREATE VIEW rls_view AS SELECT * FROM rls_tbl;
4463 ALTER VIEW rls_view OWNER TO regress_rls_bob;
4464 GRANT SELECT ON rls_view TO regress_rls_alice;
4465 SET SESSION AUTHORIZATION regress_rls_alice;
4466 SELECT * FROM ref_tbl; -- Permission denied
4467 ERROR: permission denied for table ref_tbl
4468 SELECT * FROM rls_tbl; -- Permission denied
4469 ERROR: permission denied for table rls_tbl
4470 SELECT * FROM rls_view; -- OK
4476 RESET SESSION AUTHORIZATION;
4480 -- Leaky operator test
4481 CREATE TABLE rls_tbl (a int);
4482 INSERT INTO rls_tbl SELECT x/10 FROM generate_series(1, 100) x;
4484 ALTER TABLE rls_tbl ENABLE ROW LEVEL SECURITY;
4485 GRANT SELECT ON rls_tbl TO regress_rls_alice;
4486 SET SESSION AUTHORIZATION regress_rls_alice;
4487 CREATE FUNCTION op_leak(int, int) RETURNS bool
4488 AS 'BEGIN RAISE NOTICE ''op_leak => %, %'', $1, $2; RETURN $1 < $2; END'
4490 CREATE OPERATOR <<< (procedure = op_leak, leftarg = int, rightarg = int,
4491 restrict = scalarltsel);
4492 SELECT * FROM rls_tbl WHERE a <<< 1000;
4497 EXPLAIN (COSTS OFF) SELECT * FROM rls_tbl WHERE a <<< 1000 or a <<< 900;
4499 --------------------------
4501 One-Time Filter: false
4504 DROP OPERATOR <<< (int, int);
4505 DROP FUNCTION op_leak(int, int);
4506 RESET SESSION AUTHORIZATION;
4508 -- Bug #16006: whole-row Vars in a policy don't play nice with sub-selects
4509 SET SESSION AUTHORIZATION regress_rls_alice;
4510 CREATE TABLE rls_tbl (a int, b int, c int);
4511 CREATE POLICY p1 ON rls_tbl USING (rls_tbl >= ROW(1,1,1));
4512 ALTER TABLE rls_tbl ENABLE ROW LEVEL SECURITY;
4513 ALTER TABLE rls_tbl FORCE ROW LEVEL SECURITY;
4514 INSERT INTO rls_tbl SELECT 10, 20, 30;
4515 EXPLAIN (VERBOSE, COSTS OFF)
4517 SELECT * FROM (SELECT b, c FROM rls_tbl ORDER BY a) ss;
4519 --------------------------------------------------------------------
4520 Insert on regress_rls_schema.rls_tbl
4521 -> Subquery Scan on ss
4522 Output: ss.b, ss.c, NULL::integer
4524 Output: rls_tbl_1.b, rls_tbl_1.c, rls_tbl_1.a
4525 Sort Key: rls_tbl_1.a
4526 -> Seq Scan on regress_rls_schema.rls_tbl rls_tbl_1
4527 Output: rls_tbl_1.b, rls_tbl_1.c, rls_tbl_1.a
4528 Filter: (rls_tbl_1.* >= '(1,1,1)'::record)
4532 SELECT * FROM (SELECT b, c FROM rls_tbl ORDER BY a) ss;
4533 SELECT * FROM rls_tbl;
4541 RESET SESSION AUTHORIZATION;
4542 -- CVE-2023-2455: inlining an SRF may introduce an RLS dependency
4543 create table rls_t (c text);
4544 insert into rls_t values ('invisible to bob');
4545 alter table rls_t enable row level security;
4546 grant select on rls_t to regress_rls_alice, regress_rls_bob;
4547 create policy p1 on rls_t for select to regress_rls_alice using (true);
4548 create policy p2 on rls_t for select to regress_rls_bob using (false);
4549 create function rls_f () returns setof rls_t
4551 as $$ select * from rls_t $$;
4552 prepare q as select current_user, * from rls_f();
4553 set role regress_rls_alice;
4556 -------------------+------------------
4557 regress_rls_alice | invisible to bob
4560 set role regress_rls_bob;
4566 -- make sure RLS dependencies in CTEs are handled
4568 create or replace function rls_f() returns setof rls_t
4570 as $$ with cte as (select * from rls_t) select * from cte $$;
4571 prepare r as select current_user, * from rls_f();
4572 set role regress_rls_alice;
4575 -------------------+------------------
4576 regress_rls_alice | invisible to bob
4579 set role regress_rls_bob;
4585 -- make sure RLS dependencies in subqueries are handled
4587 create or replace function rls_f() returns setof rls_t
4589 as $$ select * from (select * from rls_t) _ $$;
4590 prepare s as select current_user, * from rls_f();
4591 set role regress_rls_alice;
4594 -------------------+------------------
4595 regress_rls_alice | invisible to bob
4598 set role regress_rls_bob;
4604 -- make sure RLS dependencies in sublinks are handled
4606 create or replace function rls_f() returns setof rls_t
4608 as $$ select exists(select * from rls_t)::text $$;
4609 prepare t as select current_user, * from rls_f();
4610 set role regress_rls_alice;
4613 -------------------+------
4614 regress_rls_alice | true
4617 set role regress_rls_bob;
4620 -----------------+-------
4621 regress_rls_bob | false
4624 -- make sure RLS dependencies are handled when coercion projections are inserted
4626 create or replace function rls_f() returns setof rls_t
4628 as $$ select * from (select array_agg(c) as cs from rls_t) _ group by cs $$;
4629 prepare u as select current_user, * from rls_f();
4630 set role regress_rls_alice;
4633 -------------------+----------------------
4634 regress_rls_alice | {"invisible to bob"}
4637 set role regress_rls_bob;
4640 -----------------+---
4644 -- make sure RLS dependencies in security invoker views are handled
4646 create view rls_v with (security_invoker) as select * from rls_t;
4647 grant select on rls_v to regress_rls_alice, regress_rls_bob;
4648 create or replace function rls_f() returns setof rls_t
4650 as $$ select * from rls_v $$;
4651 prepare v as select current_user, * from rls_f();
4652 set role regress_rls_alice;
4655 -------------------+------------------
4656 regress_rls_alice | invisible to bob
4659 set role regress_rls_bob;
4666 DROP FUNCTION rls_f();
4672 RESET SESSION AUTHORIZATION;
4673 DROP SCHEMA regress_rls_schema CASCADE;
4674 NOTICE: drop cascades to 30 other objects
4675 DETAIL: drop cascades to function f_leak(text)
4676 drop cascades to table uaccount
4677 drop cascades to table category
4678 drop cascades to table document
4679 drop cascades to table part_document
4680 drop cascades to table dependent
4681 drop cascades to table rec1
4682 drop cascades to table rec2
4683 drop cascades to view rec1v
4684 drop cascades to view rec2v
4685 drop cascades to table s1
4686 drop cascades to table s2
4687 drop cascades to view v2
4688 drop cascades to table b1
4689 drop cascades to view bv1
4690 drop cascades to table z1
4691 drop cascades to table z2
4692 drop cascades to table z1_blacklist
4693 drop cascades to table x1
4694 drop cascades to table y1
4695 drop cascades to table y2
4696 drop cascades to table t1
4697 drop cascades to table t2
4698 drop cascades to table t3
4699 drop cascades to table t4
4700 drop cascades to table current_check
4701 drop cascades to table dep1
4702 drop cascades to table dep2
4703 drop cascades to table dob_t1
4704 drop cascades to table dob_t2
4705 DROP USER regress_rls_alice;
4706 DROP USER regress_rls_bob;
4707 DROP USER regress_rls_carol;
4708 DROP USER regress_rls_dave;
4709 DROP USER regress_rls_exempt_user;
4710 DROP ROLE regress_rls_group1;
4711 DROP ROLE regress_rls_group2;
4712 -- Arrange to have a few policies left over, for testing
4713 -- pg_dump/pg_restore
4714 CREATE SCHEMA regress_rls_schema;
4715 CREATE TABLE rls_tbl (c1 int);
4716 ALTER TABLE rls_tbl ENABLE ROW LEVEL SECURITY;
4717 CREATE POLICY p1 ON rls_tbl USING (c1 > 5);
4718 CREATE POLICY p2 ON rls_tbl FOR SELECT USING (c1 <= 3);
4719 CREATE POLICY p3 ON rls_tbl FOR UPDATE USING (c1 <= 3) WITH CHECK (c1 > 5);
4720 CREATE POLICY p4 ON rls_tbl FOR DELETE USING (c1 <= 3);
4721 CREATE TABLE rls_tbl_force (c1 int);
4722 ALTER TABLE rls_tbl_force ENABLE ROW LEVEL SECURITY;
4723 ALTER TABLE rls_tbl_force FORCE ROW LEVEL SECURITY;
4724 CREATE POLICY p1 ON rls_tbl_force USING (c1 = 5) WITH CHECK (c1 < 5);
4725 CREATE POLICY p2 ON rls_tbl_force FOR SELECT USING (c1 = 8);
4726 CREATE POLICY p3 ON rls_tbl_force FOR UPDATE USING (c1 = 8) WITH CHECK (c1 >= 5);
4727 CREATE POLICY p4 ON rls_tbl_force FOR DELETE USING (c1 = 8);