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=arwdDxt/regress_rls_alice+| |
100 | | | =arwdDxt/regress_rls_alice | |
101 regress_rls_schema | document | table | regress_rls_alice=arwdDxt/regress_rls_alice+| | p1: +
102 | | | =arwdDxt/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=arwdDxt/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 <= $0) AND f_leak(dtitle))
272 InitPlan 1 (returns $0)
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)
282 InitPlan 1 (returns $0)
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 <= $0) 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 <= $0) AND f_leak(dtitle))
336 InitPlan 1 (returns $0)
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)
346 InitPlan 1 (returns $0)
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 <= $0) 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 Partitions: part_document_fiction FOR VALUES FROM (11) TO (12),
959 part_document_nonfiction FOR VALUES FROM (99) TO (100),
960 part_document_satire FOR VALUES FROM (55) TO (56)
962 SELECT * FROM pg_policies WHERE schemaname = 'regress_rls_schema' AND tablename like '%part_document%' ORDER BY policyname;
963 schemaname | tablename | policyname | permissive | roles | cmd | qual | with_check
964 --------------------+---------------+------------+-------------+--------------------+-----+--------------------------------------------+------------
965 regress_rls_schema | part_document | pp1 | PERMISSIVE | {public} | ALL | (dlevel <= ( SELECT uaccount.seclv +|
966 | | | | | | FROM uaccount +|
967 | | | | | | WHERE (uaccount.pguser = CURRENT_USER))) |
968 regress_rls_schema | part_document | pp1r | RESTRICTIVE | {regress_rls_dave} | ALL | (cid < 55) |
971 -- viewpoint from regress_rls_bob
972 SET SESSION AUTHORIZATION regress_rls_bob;
973 SET row_security TO ON;
974 SELECT * FROM part_document WHERE f_leak(dtitle) ORDER BY did;
975 NOTICE: f_leak => my first novel
976 NOTICE: f_leak => great science fiction
977 NOTICE: f_leak => awesome science fiction
978 NOTICE: f_leak => my first satire
979 did | cid | dlevel | dauthor | dtitle
980 -----+-----+--------+-------------------+-------------------------
981 1 | 11 | 1 | regress_rls_bob | my first novel
982 4 | 55 | 1 | regress_rls_bob | my first satire
983 6 | 11 | 1 | regress_rls_carol | great science fiction
984 9 | 11 | 1 | regress_rls_dave | awesome science fiction
987 EXPLAIN (COSTS OFF) SELECT * FROM part_document WHERE f_leak(dtitle);
989 ------------------------------------------------------------
991 InitPlan 1 (returns $0)
992 -> Index Scan using uaccount_pkey on uaccount
993 Index Cond: (pguser = CURRENT_USER)
994 -> Seq Scan on part_document_fiction part_document_1
995 Filter: ((dlevel <= $0) AND f_leak(dtitle))
996 -> Seq Scan on part_document_satire part_document_2
997 Filter: ((dlevel <= $0) AND f_leak(dtitle))
998 -> Seq Scan on part_document_nonfiction part_document_3
999 Filter: ((dlevel <= $0) AND f_leak(dtitle))
1002 -- viewpoint from regress_rls_carol
1003 SET SESSION AUTHORIZATION regress_rls_carol;
1004 SELECT * FROM part_document WHERE f_leak(dtitle) ORDER BY did;
1005 NOTICE: f_leak => my first novel
1006 NOTICE: f_leak => my second novel
1007 NOTICE: f_leak => great science fiction
1008 NOTICE: f_leak => awesome science fiction
1009 NOTICE: f_leak => my first satire
1010 NOTICE: f_leak => great satire
1011 NOTICE: f_leak => my science textbook
1012 NOTICE: f_leak => my history book
1013 NOTICE: f_leak => great technology book
1014 NOTICE: f_leak => awesome technology book
1015 did | cid | dlevel | dauthor | dtitle
1016 -----+-----+--------+-------------------+-------------------------
1017 1 | 11 | 1 | regress_rls_bob | my first novel
1018 2 | 11 | 2 | regress_rls_bob | my second novel
1019 3 | 99 | 2 | regress_rls_bob | my science textbook
1020 4 | 55 | 1 | regress_rls_bob | my first satire
1021 5 | 99 | 2 | regress_rls_bob | my history book
1022 6 | 11 | 1 | regress_rls_carol | great science fiction
1023 7 | 99 | 2 | regress_rls_carol | great technology book
1024 8 | 55 | 2 | regress_rls_carol | great satire
1025 9 | 11 | 1 | regress_rls_dave | awesome science fiction
1026 10 | 99 | 2 | regress_rls_dave | awesome technology book
1029 EXPLAIN (COSTS OFF) SELECT * FROM part_document WHERE f_leak(dtitle);
1031 ------------------------------------------------------------
1033 InitPlan 1 (returns $0)
1034 -> Index Scan using uaccount_pkey on uaccount
1035 Index Cond: (pguser = CURRENT_USER)
1036 -> Seq Scan on part_document_fiction part_document_1
1037 Filter: ((dlevel <= $0) AND f_leak(dtitle))
1038 -> Seq Scan on part_document_satire part_document_2
1039 Filter: ((dlevel <= $0) AND f_leak(dtitle))
1040 -> Seq Scan on part_document_nonfiction part_document_3
1041 Filter: ((dlevel <= $0) AND f_leak(dtitle))
1044 -- viewpoint from regress_rls_dave
1045 SET SESSION AUTHORIZATION regress_rls_dave;
1046 SELECT * FROM part_document WHERE f_leak(dtitle) ORDER BY did;
1047 NOTICE: f_leak => my first novel
1048 NOTICE: f_leak => my second novel
1049 NOTICE: f_leak => great science fiction
1050 NOTICE: f_leak => awesome science fiction
1051 did | cid | dlevel | dauthor | dtitle
1052 -----+-----+--------+-------------------+-------------------------
1053 1 | 11 | 1 | regress_rls_bob | my first novel
1054 2 | 11 | 2 | regress_rls_bob | my second novel
1055 6 | 11 | 1 | regress_rls_carol | great science fiction
1056 9 | 11 | 1 | regress_rls_dave | awesome science fiction
1059 EXPLAIN (COSTS OFF) SELECT * FROM part_document WHERE f_leak(dtitle);
1061 --------------------------------------------------------------
1062 Seq Scan on part_document_fiction part_document
1063 Filter: ((cid < 55) AND (dlevel <= $0) AND f_leak(dtitle))
1064 InitPlan 1 (returns $0)
1065 -> Index Scan using uaccount_pkey on uaccount
1066 Index Cond: (pguser = CURRENT_USER)
1070 INSERT INTO part_document VALUES (100, 11, 5, 'regress_rls_dave', 'testing pp1'); -- fail
1071 ERROR: new row violates row-level security policy for table "part_document"
1073 INSERT INTO part_document VALUES (100, 99, 1, 'regress_rls_dave', 'testing pp1r'); -- fail
1074 ERROR: new row violates row-level security policy "pp1r" for table "part_document"
1075 -- Show that RLS policy does not apply for direct inserts to children
1076 -- This should fail with RLS POLICY pp1r violation.
1077 INSERT INTO part_document VALUES (100, 55, 1, 'regress_rls_dave', 'testing RLS with partitions'); -- fail
1078 ERROR: new row violates row-level security policy "pp1r" for table "part_document"
1079 -- But this should succeed.
1080 INSERT INTO part_document_satire VALUES (100, 55, 1, 'regress_rls_dave', 'testing RLS with partitions'); -- success
1081 -- We still cannot see the row using the parent
1082 SELECT * FROM part_document WHERE f_leak(dtitle) ORDER BY did;
1083 NOTICE: f_leak => my first novel
1084 NOTICE: f_leak => my second novel
1085 NOTICE: f_leak => great science fiction
1086 NOTICE: f_leak => awesome science fiction
1087 did | cid | dlevel | dauthor | dtitle
1088 -----+-----+--------+-------------------+-------------------------
1089 1 | 11 | 1 | regress_rls_bob | my first novel
1090 2 | 11 | 2 | regress_rls_bob | my second novel
1091 6 | 11 | 1 | regress_rls_carol | great science fiction
1092 9 | 11 | 1 | regress_rls_dave | awesome science fiction
1095 -- But we can if we look directly
1096 SELECT * FROM part_document_satire WHERE f_leak(dtitle) ORDER BY did;
1097 NOTICE: f_leak => my first satire
1098 NOTICE: f_leak => great satire
1099 NOTICE: f_leak => testing RLS with partitions
1100 did | cid | dlevel | dauthor | dtitle
1101 -----+-----+--------+-------------------+-----------------------------
1102 4 | 55 | 1 | regress_rls_bob | my first satire
1103 8 | 55 | 2 | regress_rls_carol | great satire
1104 100 | 55 | 1 | regress_rls_dave | testing RLS with partitions
1107 -- Turn on RLS and create policy on child to show RLS is checked before constraints
1108 SET SESSION AUTHORIZATION regress_rls_alice;
1109 ALTER TABLE part_document_satire ENABLE ROW LEVEL SECURITY;
1110 CREATE POLICY pp3 ON part_document_satire AS RESTRICTIVE
1112 -- This should fail with RLS violation now.
1113 SET SESSION AUTHORIZATION regress_rls_dave;
1114 INSERT INTO part_document_satire VALUES (101, 55, 1, 'regress_rls_dave', 'testing RLS with partitions'); -- fail
1115 ERROR: new row violates row-level security policy for table "part_document_satire"
1116 -- And now we cannot see directly into the partition either, due to RLS
1117 SELECT * FROM part_document_satire WHERE f_leak(dtitle) ORDER BY did;
1118 did | cid | dlevel | dauthor | dtitle
1119 -----+-----+--------+---------+--------
1122 -- The parent looks same as before
1123 -- viewpoint from regress_rls_dave
1124 SELECT * FROM part_document WHERE f_leak(dtitle) ORDER BY did;
1125 NOTICE: f_leak => my first novel
1126 NOTICE: f_leak => my second novel
1127 NOTICE: f_leak => great science fiction
1128 NOTICE: f_leak => awesome science fiction
1129 did | cid | dlevel | dauthor | dtitle
1130 -----+-----+--------+-------------------+-------------------------
1131 1 | 11 | 1 | regress_rls_bob | my first novel
1132 2 | 11 | 2 | regress_rls_bob | my second novel
1133 6 | 11 | 1 | regress_rls_carol | great science fiction
1134 9 | 11 | 1 | regress_rls_dave | awesome science fiction
1137 EXPLAIN (COSTS OFF) SELECT * FROM part_document WHERE f_leak(dtitle);
1139 --------------------------------------------------------------
1140 Seq Scan on part_document_fiction part_document
1141 Filter: ((cid < 55) AND (dlevel <= $0) AND f_leak(dtitle))
1142 InitPlan 1 (returns $0)
1143 -> Index Scan using uaccount_pkey on uaccount
1144 Index Cond: (pguser = CURRENT_USER)
1147 -- viewpoint from regress_rls_carol
1148 SET SESSION AUTHORIZATION regress_rls_carol;
1149 SELECT * FROM part_document WHERE f_leak(dtitle) ORDER BY did;
1150 NOTICE: f_leak => my first novel
1151 NOTICE: f_leak => my second novel
1152 NOTICE: f_leak => great science fiction
1153 NOTICE: f_leak => awesome science fiction
1154 NOTICE: f_leak => my first satire
1155 NOTICE: f_leak => great satire
1156 NOTICE: f_leak => testing RLS with partitions
1157 NOTICE: f_leak => my science textbook
1158 NOTICE: f_leak => my history book
1159 NOTICE: f_leak => great technology book
1160 NOTICE: f_leak => awesome technology book
1161 did | cid | dlevel | dauthor | dtitle
1162 -----+-----+--------+-------------------+-----------------------------
1163 1 | 11 | 1 | regress_rls_bob | my first novel
1164 2 | 11 | 2 | regress_rls_bob | my second novel
1165 3 | 99 | 2 | regress_rls_bob | my science textbook
1166 4 | 55 | 1 | regress_rls_bob | my first satire
1167 5 | 99 | 2 | regress_rls_bob | my history book
1168 6 | 11 | 1 | regress_rls_carol | great science fiction
1169 7 | 99 | 2 | regress_rls_carol | great technology book
1170 8 | 55 | 2 | regress_rls_carol | great satire
1171 9 | 11 | 1 | regress_rls_dave | awesome science fiction
1172 10 | 99 | 2 | regress_rls_dave | awesome technology book
1173 100 | 55 | 1 | regress_rls_dave | testing RLS with partitions
1176 EXPLAIN (COSTS OFF) SELECT * FROM part_document WHERE f_leak(dtitle);
1178 ------------------------------------------------------------
1180 InitPlan 1 (returns $0)
1181 -> Index Scan using uaccount_pkey on uaccount
1182 Index Cond: (pguser = CURRENT_USER)
1183 -> Seq Scan on part_document_fiction part_document_1
1184 Filter: ((dlevel <= $0) AND f_leak(dtitle))
1185 -> Seq Scan on part_document_satire part_document_2
1186 Filter: ((dlevel <= $0) AND f_leak(dtitle))
1187 -> Seq Scan on part_document_nonfiction part_document_3
1188 Filter: ((dlevel <= $0) AND f_leak(dtitle))
1191 -- only owner can change policies
1192 ALTER POLICY pp1 ON part_document USING (true); --fail
1193 ERROR: must be owner of table part_document
1194 DROP POLICY pp1 ON part_document; --fail
1195 ERROR: must be owner of relation part_document
1196 SET SESSION AUTHORIZATION regress_rls_alice;
1197 ALTER POLICY pp1 ON part_document USING (dauthor = current_user);
1198 -- viewpoint from regress_rls_bob again
1199 SET SESSION AUTHORIZATION regress_rls_bob;
1200 SELECT * FROM part_document WHERE f_leak(dtitle) ORDER BY did;
1201 NOTICE: f_leak => my first novel
1202 NOTICE: f_leak => my second novel
1203 NOTICE: f_leak => my first satire
1204 NOTICE: f_leak => my science textbook
1205 NOTICE: f_leak => my history book
1206 did | cid | dlevel | dauthor | dtitle
1207 -----+-----+--------+-----------------+---------------------
1208 1 | 11 | 1 | regress_rls_bob | my first novel
1209 2 | 11 | 2 | regress_rls_bob | my second novel
1210 3 | 99 | 2 | regress_rls_bob | my science textbook
1211 4 | 55 | 1 | regress_rls_bob | my first satire
1212 5 | 99 | 2 | regress_rls_bob | my history book
1215 -- viewpoint from rls_regres_carol again
1216 SET SESSION AUTHORIZATION regress_rls_carol;
1217 SELECT * FROM part_document WHERE f_leak(dtitle) ORDER BY did;
1218 NOTICE: f_leak => great science fiction
1219 NOTICE: f_leak => great satire
1220 NOTICE: f_leak => great technology book
1221 did | cid | dlevel | dauthor | dtitle
1222 -----+-----+--------+-------------------+-----------------------
1223 6 | 11 | 1 | regress_rls_carol | great science fiction
1224 7 | 99 | 2 | regress_rls_carol | great technology book
1225 8 | 55 | 2 | regress_rls_carol | great satire
1228 EXPLAIN (COSTS OFF) SELECT * FROM part_document WHERE f_leak(dtitle);
1230 ---------------------------------------------------------------
1232 -> Seq Scan on part_document_fiction part_document_1
1233 Filter: ((dauthor = CURRENT_USER) AND f_leak(dtitle))
1234 -> Seq Scan on part_document_satire part_document_2
1235 Filter: ((dauthor = CURRENT_USER) AND f_leak(dtitle))
1236 -> Seq Scan on part_document_nonfiction part_document_3
1237 Filter: ((dauthor = CURRENT_USER) AND f_leak(dtitle))
1240 -- database superuser does bypass RLS policy when enabled
1241 RESET SESSION AUTHORIZATION;
1242 SET row_security TO ON;
1243 SELECT * FROM part_document ORDER BY did;
1244 did | cid | dlevel | dauthor | dtitle
1245 -----+-----+--------+-------------------+-----------------------------
1246 1 | 11 | 1 | regress_rls_bob | my first novel
1247 2 | 11 | 2 | regress_rls_bob | my second novel
1248 3 | 99 | 2 | regress_rls_bob | my science textbook
1249 4 | 55 | 1 | regress_rls_bob | my first satire
1250 5 | 99 | 2 | regress_rls_bob | my history book
1251 6 | 11 | 1 | regress_rls_carol | great science fiction
1252 7 | 99 | 2 | regress_rls_carol | great technology book
1253 8 | 55 | 2 | regress_rls_carol | great satire
1254 9 | 11 | 1 | regress_rls_dave | awesome science fiction
1255 10 | 99 | 2 | regress_rls_dave | awesome technology book
1256 100 | 55 | 1 | regress_rls_dave | testing RLS with partitions
1259 SELECT * FROM part_document_satire ORDER by did;
1260 did | cid | dlevel | dauthor | dtitle
1261 -----+-----+--------+-------------------+-----------------------------
1262 4 | 55 | 1 | regress_rls_bob | my first satire
1263 8 | 55 | 2 | regress_rls_carol | great satire
1264 100 | 55 | 1 | regress_rls_dave | testing RLS with partitions
1267 -- database non-superuser with bypass privilege can bypass RLS policy when disabled
1268 SET SESSION AUTHORIZATION regress_rls_exempt_user;
1269 SET row_security TO OFF;
1270 SELECT * FROM part_document ORDER BY did;
1271 did | cid | dlevel | dauthor | dtitle
1272 -----+-----+--------+-------------------+-----------------------------
1273 1 | 11 | 1 | regress_rls_bob | my first novel
1274 2 | 11 | 2 | regress_rls_bob | my second novel
1275 3 | 99 | 2 | regress_rls_bob | my science textbook
1276 4 | 55 | 1 | regress_rls_bob | my first satire
1277 5 | 99 | 2 | regress_rls_bob | my history book
1278 6 | 11 | 1 | regress_rls_carol | great science fiction
1279 7 | 99 | 2 | regress_rls_carol | great technology book
1280 8 | 55 | 2 | regress_rls_carol | great satire
1281 9 | 11 | 1 | regress_rls_dave | awesome science fiction
1282 10 | 99 | 2 | regress_rls_dave | awesome technology book
1283 100 | 55 | 1 | regress_rls_dave | testing RLS with partitions
1286 SELECT * FROM part_document_satire ORDER by did;
1287 did | cid | dlevel | dauthor | dtitle
1288 -----+-----+--------+-------------------+-----------------------------
1289 4 | 55 | 1 | regress_rls_bob | my first satire
1290 8 | 55 | 2 | regress_rls_carol | great satire
1291 100 | 55 | 1 | regress_rls_dave | testing RLS with partitions
1294 -- RLS policy does not apply to table owner when RLS enabled.
1295 SET SESSION AUTHORIZATION regress_rls_alice;
1296 SET row_security TO ON;
1297 SELECT * FROM part_document ORDER by did;
1298 did | cid | dlevel | dauthor | dtitle
1299 -----+-----+--------+-------------------+-----------------------------
1300 1 | 11 | 1 | regress_rls_bob | my first novel
1301 2 | 11 | 2 | regress_rls_bob | my second novel
1302 3 | 99 | 2 | regress_rls_bob | my science textbook
1303 4 | 55 | 1 | regress_rls_bob | my first satire
1304 5 | 99 | 2 | regress_rls_bob | my history book
1305 6 | 11 | 1 | regress_rls_carol | great science fiction
1306 7 | 99 | 2 | regress_rls_carol | great technology book
1307 8 | 55 | 2 | regress_rls_carol | great satire
1308 9 | 11 | 1 | regress_rls_dave | awesome science fiction
1309 10 | 99 | 2 | regress_rls_dave | awesome technology book
1310 100 | 55 | 1 | regress_rls_dave | testing RLS with partitions
1313 SELECT * FROM part_document_satire ORDER by did;
1314 did | cid | dlevel | dauthor | dtitle
1315 -----+-----+--------+-------------------+-----------------------------
1316 4 | 55 | 1 | regress_rls_bob | my first satire
1317 8 | 55 | 2 | regress_rls_carol | great satire
1318 100 | 55 | 1 | regress_rls_dave | testing RLS with partitions
1321 -- When RLS disabled, other users get ERROR.
1322 SET SESSION AUTHORIZATION regress_rls_dave;
1323 SET row_security TO OFF;
1324 SELECT * FROM part_document ORDER by did;
1325 ERROR: query would be affected by row-level security policy for table "part_document"
1326 SELECT * FROM part_document_satire ORDER by did;
1327 ERROR: query would be affected by row-level security policy for table "part_document_satire"
1328 -- Check behavior with a policy that uses a SubPlan not an InitPlan.
1329 SET SESSION AUTHORIZATION regress_rls_alice;
1330 SET row_security TO ON;
1331 CREATE POLICY pp3 ON part_document AS RESTRICTIVE
1332 USING ((SELECT dlevel <= seclv FROM uaccount WHERE pguser = current_user));
1333 SET SESSION AUTHORIZATION regress_rls_carol;
1334 INSERT INTO part_document VALUES (100, 11, 5, 'regress_rls_carol', 'testing pp3'); -- fail
1335 ERROR: new row violates row-level security policy "pp3" for table "part_document"
1336 ----- Dependencies -----
1337 SET SESSION AUTHORIZATION regress_rls_alice;
1338 SET row_security TO ON;
1339 CREATE TABLE dependee (x integer, y integer);
1340 CREATE TABLE dependent (x integer, y integer);
1341 CREATE POLICY d1 ON dependent FOR ALL
1343 USING (x = (SELECT d.x FROM dependee d WHERE d.y = y));
1344 DROP TABLE dependee; -- Should fail without CASCADE due to dependency on row security qual?
1345 ERROR: cannot drop table dependee because other objects depend on it
1346 DETAIL: policy d1 on table dependent depends on table dependee
1347 HINT: Use DROP ... CASCADE to drop the dependent objects too.
1348 DROP TABLE dependee CASCADE;
1349 NOTICE: drop cascades to policy d1 on table dependent
1350 EXPLAIN (COSTS OFF) SELECT * FROM dependent; -- After drop, should be unqualified
1352 -----------------------
1353 Seq Scan on dependent
1356 ----- RECURSION ----
1360 SET SESSION AUTHORIZATION regress_rls_alice;
1361 CREATE TABLE rec1 (x integer, y integer);
1362 CREATE POLICY r1 ON rec1 USING (x = (SELECT r.x FROM rec1 r WHERE y = r.y));
1363 ALTER TABLE rec1 ENABLE ROW LEVEL SECURITY;
1364 SET SESSION AUTHORIZATION regress_rls_bob;
1365 SELECT * FROM rec1; -- fail, direct recursion
1366 ERROR: infinite recursion detected in policy for relation "rec1"
1370 SET SESSION AUTHORIZATION regress_rls_alice;
1371 CREATE TABLE rec2 (a integer, b integer);
1372 ALTER POLICY r1 ON rec1 USING (x = (SELECT a FROM rec2 WHERE b = y));
1373 CREATE POLICY r2 ON rec2 USING (a = (SELECT x FROM rec1 WHERE y = b));
1374 ALTER TABLE rec2 ENABLE ROW LEVEL SECURITY;
1375 SET SESSION AUTHORIZATION regress_rls_bob;
1376 SELECT * FROM rec1; -- fail, mutual recursion
1377 ERROR: infinite recursion detected in policy for relation "rec1"
1379 -- Mutual recursion via views
1381 SET SESSION AUTHORIZATION regress_rls_bob;
1382 CREATE VIEW rec1v AS SELECT * FROM rec1;
1383 CREATE VIEW rec2v AS SELECT * FROM rec2;
1384 SET SESSION AUTHORIZATION regress_rls_alice;
1385 ALTER POLICY r1 ON rec1 USING (x = (SELECT a FROM rec2v WHERE b = y));
1386 ALTER POLICY r2 ON rec2 USING (a = (SELECT x FROM rec1v WHERE y = b));
1387 SET SESSION AUTHORIZATION regress_rls_bob;
1388 SELECT * FROM rec1; -- fail, mutual recursion via views
1389 ERROR: infinite recursion detected in policy for relation "rec1"
1391 -- Mutual recursion via .s.b views
1393 SET SESSION AUTHORIZATION regress_rls_bob;
1394 DROP VIEW rec1v, rec2v CASCADE;
1395 NOTICE: drop cascades to 2 other objects
1396 DETAIL: drop cascades to policy r1 on table rec1
1397 drop cascades to policy r2 on table rec2
1398 CREATE VIEW rec1v WITH (security_barrier) AS SELECT * FROM rec1;
1399 CREATE VIEW rec2v WITH (security_barrier) AS SELECT * FROM rec2;
1400 SET SESSION AUTHORIZATION regress_rls_alice;
1401 CREATE POLICY r1 ON rec1 USING (x = (SELECT a FROM rec2v WHERE b = y));
1402 CREATE POLICY r2 ON rec2 USING (a = (SELECT x FROM rec1v WHERE y = b));
1403 SET SESSION AUTHORIZATION regress_rls_bob;
1404 SELECT * FROM rec1; -- fail, mutual recursion via s.b. views
1405 ERROR: infinite recursion detected in policy for relation "rec1"
1407 -- recursive RLS and VIEWs in policy
1409 SET SESSION AUTHORIZATION regress_rls_alice;
1410 CREATE TABLE s1 (a int, b text);
1411 INSERT INTO s1 (SELECT x, md5(x::text) FROM generate_series(-10,10) x);
1412 CREATE TABLE s2 (x int, y text);
1413 INSERT INTO s2 (SELECT x, md5(x::text) FROM generate_series(-6,6) x);
1414 GRANT SELECT ON s1, s2 TO regress_rls_bob;
1415 CREATE POLICY p1 ON s1 USING (a in (select x from s2 where y like '%2f%'));
1416 CREATE POLICY p2 ON s2 USING (x in (select a from s1 where b like '%22%'));
1417 CREATE POLICY p3 ON s1 FOR INSERT WITH CHECK (a = (SELECT a FROM s1));
1418 ALTER TABLE s1 ENABLE ROW LEVEL SECURITY;
1419 ALTER TABLE s2 ENABLE ROW LEVEL SECURITY;
1420 SET SESSION AUTHORIZATION regress_rls_bob;
1421 CREATE VIEW v2 AS SELECT * FROM s2 WHERE y like '%af%';
1422 SELECT * FROM s1 WHERE f_leak(b); -- fail (infinite recursion)
1423 ERROR: infinite recursion detected in policy for relation "s1"
1424 INSERT INTO s1 VALUES (1, 'foo'); -- fail (infinite recursion)
1425 ERROR: infinite recursion detected in policy for relation "s1"
1426 SET SESSION AUTHORIZATION regress_rls_alice;
1427 DROP POLICY p3 on s1;
1428 ALTER POLICY p2 ON s2 USING (x % 2 = 0);
1429 SET SESSION AUTHORIZATION regress_rls_bob;
1430 SELECT * FROM s1 WHERE f_leak(b); -- OK
1431 NOTICE: f_leak => c81e728d9d4c2f636f067f89cc14862c
1432 NOTICE: f_leak => a87ff679a2f3e71d9181a67b7542122c
1434 ---+----------------------------------
1435 2 | c81e728d9d4c2f636f067f89cc14862c
1436 4 | a87ff679a2f3e71d9181a67b7542122c
1439 EXPLAIN (COSTS OFF) SELECT * FROM only s1 WHERE f_leak(b);
1441 -----------------------------------------------------------
1443 Filter: ((hashed SubPlan 1) 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 => 0267aaf632e87a63288a08331f22c7c3
1454 NOTICE: f_leak => 1679091c5a880faf6fb5e6087eb1b2dc
1456 ----+----------------------------------
1457 -4 | 0267aaf632e87a63288a08331f22c7c3
1458 6 | 1679091c5a880faf6fb5e6087eb1b2dc
1461 EXPLAIN (COSTS OFF) SELECT * FROM s1 WHERE f_leak(b);
1463 -----------------------------------------------------------
1465 Filter: ((hashed SubPlan 1) AND f_leak(b))
1468 Filter: (((x % 2) = 0) AND (y ~~ '%af%'::text))
1471 SELECT (SELECT x FROM s1 LIMIT 1) xx, * FROM s2 WHERE y like '%28%';
1473 ----+----+----------------------------------
1474 -6 | -6 | 596a3d04481816330f07e4f97510c28f
1475 -4 | -4 | 0267aaf632e87a63288a08331f22c7c3
1476 2 | 2 | c81e728d9d4c2f636f067f89cc14862c
1479 EXPLAIN (COSTS OFF) SELECT (SELECT x FROM s1 LIMIT 1) xx, * FROM s2 WHERE y like '%28%';
1481 -------------------------------------------------------------------------
1483 Filter: (((x % 2) = 0) AND (y ~~ '%28%'::text))
1487 Filter: (hashed SubPlan 1)
1489 -> Seq Scan on s2 s2_1
1490 Filter: (((x % 2) = 0) AND (y ~~ '%af%'::text))
1493 SET SESSION AUTHORIZATION regress_rls_alice;
1494 ALTER POLICY p2 ON s2 USING (x in (select a from s1 where b like '%d2%'));
1495 SET SESSION AUTHORIZATION regress_rls_bob;
1496 SELECT * FROM s1 WHERE f_leak(b); -- fail (infinite recursion via view)
1497 ERROR: infinite recursion detected in policy for relation "s1"
1498 -- prepared statement with regress_rls_alice privilege
1499 PREPARE p1(int) AS SELECT * FROM t1 WHERE a <= $1;
1508 EXPLAIN (COSTS OFF) EXECUTE p1(2);
1510 ----------------------------------------------
1512 -> Seq Scan on t1 t1_1
1513 Filter: ((a <= 2) AND ((a % 2) = 0))
1514 -> Seq Scan on t2 t1_2
1515 Filter: ((a <= 2) AND ((a % 2) = 0))
1516 -> Seq Scan on t3 t1_3
1517 Filter: ((a <= 2) AND ((a % 2) = 0))
1520 -- superuser is allowed to bypass RLS checks
1521 RESET SESSION AUTHORIZATION;
1522 SET row_security TO OFF;
1523 SELECT * FROM t1 WHERE f_leak(b);
1524 NOTICE: f_leak => aba
1525 NOTICE: f_leak => bbb
1526 NOTICE: f_leak => ccc
1527 NOTICE: f_leak => dad
1528 NOTICE: f_leak => abc
1529 NOTICE: f_leak => bcd
1530 NOTICE: f_leak => cde
1531 NOTICE: f_leak => def
1532 NOTICE: f_leak => xxx
1533 NOTICE: f_leak => yyy
1534 NOTICE: f_leak => zzz
1550 EXPLAIN (COSTS OFF) SELECT * FROM t1 WHERE f_leak(b);
1552 ---------------------------
1554 -> Seq Scan on t1 t1_1
1556 -> Seq Scan on t2 t1_2
1558 -> Seq Scan on t3 t1_3
1562 -- plan cache should be invalidated
1574 EXPLAIN (COSTS OFF) EXECUTE p1(2);
1576 ---------------------------
1578 -> Seq Scan on t1 t1_1
1580 -> Seq Scan on t2 t1_2
1582 -> Seq Scan on t3 t1_3
1586 PREPARE p2(int) AS SELECT * FROM t1 WHERE a = $1;
1595 EXPLAIN (COSTS OFF) EXECUTE p2(2);
1597 ---------------------------
1599 -> Seq Scan on t1 t1_1
1601 -> Seq Scan on t2 t1_2
1603 -> Seq Scan on t3 t1_3
1607 -- also, case when privilege switch from superuser
1608 SET SESSION AUTHORIZATION regress_rls_bob;
1609 SET row_security TO ON;
1618 EXPLAIN (COSTS OFF) EXECUTE p2(2);
1620 ---------------------------------------------
1622 -> Seq Scan on t1 t1_1
1623 Filter: ((a = 2) AND ((a % 2) = 0))
1624 -> Seq Scan on t2 t1_2
1625 Filter: ((a = 2) AND ((a % 2) = 0))
1626 -> Seq Scan on t3 t1_3
1627 Filter: ((a = 2) AND ((a % 2) = 0))
1631 -- UPDATE / DELETE and Row-level security
1633 SET SESSION AUTHORIZATION regress_rls_bob;
1634 EXPLAIN (COSTS OFF) UPDATE t1 SET b = b || b WHERE f_leak(b);
1636 -----------------------------------------------------------
1643 -> Seq Scan on t1 t1_1
1644 Filter: (((a % 2) = 0) AND f_leak(b))
1645 -> Seq Scan on t2 t1_2
1646 Filter: (((a % 2) = 0) AND f_leak(b))
1647 -> Seq Scan on t3 t1_3
1648 Filter: (((a % 2) = 0) AND f_leak(b))
1651 UPDATE t1 SET b = b || b WHERE f_leak(b);
1652 NOTICE: f_leak => bbb
1653 NOTICE: f_leak => dad
1654 NOTICE: f_leak => bcd
1655 NOTICE: f_leak => def
1656 NOTICE: f_leak => yyy
1657 EXPLAIN (COSTS OFF) UPDATE only t1 SET b = b || '_updt' WHERE f_leak(b);
1659 -----------------------------------------------
1662 Filter: (((a % 2) = 0) AND f_leak(b))
1665 UPDATE only t1 SET b = b || '_updt' WHERE f_leak(b);
1666 NOTICE: f_leak => bbbbbb
1667 NOTICE: f_leak => daddad
1668 -- returning clause with system column
1669 UPDATE only t1 SET b = b WHERE f_leak(b) RETURNING tableoid::regclass, *, t1;
1670 NOTICE: f_leak => bbbbbb_updt
1671 NOTICE: f_leak => daddad_updt
1672 tableoid | id | a | b | t1
1673 ----------+-----+---+-------------+---------------------
1674 t1 | 102 | 2 | bbbbbb_updt | (102,2,bbbbbb_updt)
1675 t1 | 104 | 4 | daddad_updt | (104,4,daddad_updt)
1678 UPDATE t1 SET b = b WHERE f_leak(b) RETURNING *;
1679 NOTICE: f_leak => bbbbbb_updt
1680 NOTICE: f_leak => daddad_updt
1681 NOTICE: f_leak => bcdbcd
1682 NOTICE: f_leak => defdef
1683 NOTICE: f_leak => yyyyyy
1685 -----+---+-------------
1686 102 | 2 | bbbbbb_updt
1687 104 | 4 | daddad_updt
1693 UPDATE t1 SET b = b WHERE f_leak(b) RETURNING tableoid::regclass, *, t1;
1694 NOTICE: f_leak => bbbbbb_updt
1695 NOTICE: f_leak => daddad_updt
1696 NOTICE: f_leak => bcdbcd
1697 NOTICE: f_leak => defdef
1698 NOTICE: f_leak => yyyyyy
1699 tableoid | id | a | b | t1
1700 ----------+-----+---+-------------+---------------------
1701 t1 | 102 | 2 | bbbbbb_updt | (102,2,bbbbbb_updt)
1702 t1 | 104 | 4 | daddad_updt | (104,4,daddad_updt)
1703 t2 | 202 | 2 | bcdbcd | (202,2,bcdbcd)
1704 t2 | 204 | 4 | defdef | (204,4,defdef)
1705 t3 | 302 | 2 | yyyyyy | (302,2,yyyyyy)
1708 -- updates with from clause
1709 EXPLAIN (COSTS OFF) UPDATE t2 SET b=t2.b FROM t3
1710 WHERE t2.a = 3 and t3.a = 2 AND f_leak(t2.b) AND f_leak(t3.b);
1712 -----------------------------------------------------------------
1716 Filter: ((a = 3) AND ((a % 2) = 1) AND f_leak(b))
1718 Filter: ((a = 2) AND f_leak(b))
1721 UPDATE t2 SET b=t2.b FROM t3
1722 WHERE t2.a = 3 and t3.a = 2 AND f_leak(t2.b) AND f_leak(t3.b);
1723 NOTICE: f_leak => cde
1724 NOTICE: f_leak => yyyyyy
1725 EXPLAIN (COSTS OFF) UPDATE t1 SET b=t1.b FROM t2
1726 WHERE t1.a = 3 and t2.a = 3 AND f_leak(t1.b) AND f_leak(t2.b);
1728 -----------------------------------------------------------------------
1735 Filter: ((a = 3) AND ((a % 2) = 1) AND f_leak(b))
1737 -> Seq Scan on t1 t1_1
1738 Filter: ((a = 3) AND ((a % 2) = 0) AND f_leak(b))
1739 -> Seq Scan on t2 t1_2
1740 Filter: ((a = 3) AND ((a % 2) = 0) AND f_leak(b))
1741 -> Seq Scan on t3 t1_3
1742 Filter: ((a = 3) AND ((a % 2) = 0) AND f_leak(b))
1745 UPDATE t1 SET b=t1.b FROM t2
1746 WHERE t1.a = 3 and t2.a = 3 AND f_leak(t1.b) AND f_leak(t2.b);
1747 NOTICE: f_leak => cde
1748 EXPLAIN (COSTS OFF) UPDATE t2 SET b=t2.b FROM t1
1749 WHERE t1.a = 3 and t2.a = 3 AND f_leak(t1.b) AND f_leak(t2.b);
1751 -----------------------------------------------------------------------
1755 Filter: ((a = 3) AND ((a % 2) = 1) AND f_leak(b))
1757 -> Seq Scan on t1 t1_1
1758 Filter: ((a = 3) AND ((a % 2) = 0) AND f_leak(b))
1759 -> Seq Scan on t2 t1_2
1760 Filter: ((a = 3) AND ((a % 2) = 0) AND f_leak(b))
1761 -> Seq Scan on t3 t1_3
1762 Filter: ((a = 3) AND ((a % 2) = 0) AND f_leak(b))
1765 UPDATE t2 SET b=t2.b FROM t1
1766 WHERE t1.a = 3 and t2.a = 3 AND f_leak(t1.b) AND f_leak(t2.b);
1767 NOTICE: f_leak => cde
1768 -- updates with from clause self join
1769 EXPLAIN (COSTS OFF) UPDATE t2 t2_1 SET b = t2_2.b FROM t2 t2_2
1770 WHERE t2_1.a = 3 AND t2_2.a = t2_1.a AND t2_2.b = t2_1.b
1771 AND f_leak(t2_1.b) AND f_leak(t2_2.b) RETURNING *, t2_1, t2_2;
1773 -----------------------------------------------------------------
1776 Join Filter: (t2_1.b = t2_2.b)
1777 -> Seq Scan on t2 t2_1
1778 Filter: ((a = 3) AND ((a % 2) = 1) AND f_leak(b))
1779 -> Seq Scan on t2 t2_2
1780 Filter: ((a = 3) AND ((a % 2) = 1) AND f_leak(b))
1783 UPDATE t2 t2_1 SET b = t2_2.b FROM t2 t2_2
1784 WHERE t2_1.a = 3 AND t2_2.a = t2_1.a AND t2_2.b = t2_1.b
1785 AND f_leak(t2_1.b) AND f_leak(t2_2.b) RETURNING *, t2_1, t2_2;
1786 NOTICE: f_leak => cde
1787 NOTICE: f_leak => cde
1788 id | a | b | c | id | a | b | c | t2_1 | t2_2
1789 -----+---+-----+-----+-----+---+-----+-----+-----------------+-----------------
1790 203 | 3 | cde | 3.3 | 203 | 3 | cde | 3.3 | (203,3,cde,3.3) | (203,3,cde,3.3)
1793 EXPLAIN (COSTS OFF) UPDATE t1 t1_1 SET b = t1_2.b FROM t1 t1_2
1794 WHERE t1_1.a = 4 AND t1_2.a = t1_1.a AND t1_2.b = t1_1.b
1795 AND f_leak(t1_1.b) AND f_leak(t1_2.b) RETURNING *, t1_1, t1_2;
1797 -----------------------------------------------------------------------------
1803 Join Filter: (t1_1.b = t1_2.b)
1805 -> Seq Scan on t1 t1_1_1
1806 Filter: ((a = 4) AND ((a % 2) = 0) AND f_leak(b))
1807 -> Seq Scan on t2 t1_1_2
1808 Filter: ((a = 4) AND ((a % 2) = 0) AND f_leak(b))
1809 -> Seq Scan on t3 t1_1_3
1810 Filter: ((a = 4) AND ((a % 2) = 0) AND f_leak(b))
1813 -> Seq Scan on t1 t1_2_1
1814 Filter: ((a = 4) AND ((a % 2) = 0) AND f_leak(b))
1815 -> Seq Scan on t2 t1_2_2
1816 Filter: ((a = 4) AND ((a % 2) = 0) AND f_leak(b))
1817 -> Seq Scan on t3 t1_2_3
1818 Filter: ((a = 4) AND ((a % 2) = 0) AND f_leak(b))
1821 UPDATE t1 t1_1 SET b = t1_2.b FROM t1 t1_2
1822 WHERE t1_1.a = 4 AND t1_2.a = t1_1.a AND t1_2.b = t1_1.b
1823 AND f_leak(t1_1.b) AND f_leak(t1_2.b) RETURNING *, t1_1, t1_2;
1824 NOTICE: f_leak => daddad_updt
1825 NOTICE: f_leak => daddad_updt
1826 NOTICE: f_leak => defdef
1827 NOTICE: f_leak => defdef
1828 id | a | b | id | a | b | t1_1 | t1_2
1829 -----+---+-------------+-----+---+-------------+---------------------+---------------------
1830 104 | 4 | daddad_updt | 104 | 4 | daddad_updt | (104,4,daddad_updt) | (104,4,daddad_updt)
1831 204 | 4 | defdef | 204 | 4 | defdef | (204,4,defdef) | (204,4,defdef)
1834 RESET SESSION AUTHORIZATION;
1835 SET row_security TO OFF;
1836 SELECT * FROM t1 ORDER BY a,b;
1838 -----+---+-------------
1842 102 | 2 | bbbbbb_updt
1848 104 | 4 | daddad_updt
1852 SET SESSION AUTHORIZATION regress_rls_bob;
1853 SET row_security TO ON;
1854 EXPLAIN (COSTS OFF) DELETE FROM only t1 WHERE f_leak(b);
1856 -----------------------------------------------
1859 Filter: (((a % 2) = 0) AND f_leak(b))
1862 EXPLAIN (COSTS OFF) DELETE FROM t1 WHERE f_leak(b);
1864 -----------------------------------------------------
1870 -> Seq Scan on t1 t1_1
1871 Filter: (((a % 2) = 0) AND f_leak(b))
1872 -> Seq Scan on t2 t1_2
1873 Filter: (((a % 2) = 0) AND f_leak(b))
1874 -> Seq Scan on t3 t1_3
1875 Filter: (((a % 2) = 0) AND f_leak(b))
1878 DELETE FROM only t1 WHERE f_leak(b) RETURNING tableoid::regclass, *, t1;
1879 NOTICE: f_leak => bbbbbb_updt
1880 NOTICE: f_leak => daddad_updt
1881 tableoid | id | a | b | t1
1882 ----------+-----+---+-------------+---------------------
1883 t1 | 102 | 2 | bbbbbb_updt | (102,2,bbbbbb_updt)
1884 t1 | 104 | 4 | daddad_updt | (104,4,daddad_updt)
1887 DELETE FROM t1 WHERE f_leak(b) RETURNING tableoid::regclass, *, t1;
1888 NOTICE: f_leak => bcdbcd
1889 NOTICE: f_leak => defdef
1890 NOTICE: f_leak => yyyyyy
1891 tableoid | id | a | b | t1
1892 ----------+-----+---+--------+----------------
1893 t2 | 202 | 2 | bcdbcd | (202,2,bcdbcd)
1894 t2 | 204 | 4 | defdef | (204,4,defdef)
1895 t3 | 302 | 2 | yyyyyy | (302,2,yyyyyy)
1899 -- S.b. view on top of Row-level security
1901 SET SESSION AUTHORIZATION regress_rls_alice;
1902 CREATE TABLE b1 (a int, b text);
1903 INSERT INTO b1 (SELECT x, md5(x::text) FROM generate_series(-10,10) x);
1904 CREATE POLICY p1 ON b1 USING (a % 2 = 0);
1905 ALTER TABLE b1 ENABLE ROW LEVEL SECURITY;
1906 GRANT ALL ON b1 TO regress_rls_bob;
1907 SET SESSION AUTHORIZATION regress_rls_bob;
1908 CREATE VIEW bv1 WITH (security_barrier) AS SELECT * FROM b1 WHERE a > 0 WITH CHECK OPTION;
1909 GRANT ALL ON bv1 TO regress_rls_carol;
1910 SET SESSION AUTHORIZATION regress_rls_carol;
1911 EXPLAIN (COSTS OFF) SELECT * FROM bv1 WHERE f_leak(b);
1913 ---------------------------------------------
1914 Subquery Scan on bv1
1915 Filter: f_leak(bv1.b)
1917 Filter: ((a > 0) AND ((a % 2) = 0))
1920 SELECT * FROM bv1 WHERE f_leak(b);
1921 NOTICE: f_leak => c81e728d9d4c2f636f067f89cc14862c
1922 NOTICE: f_leak => a87ff679a2f3e71d9181a67b7542122c
1923 NOTICE: f_leak => 1679091c5a880faf6fb5e6087eb1b2dc
1924 NOTICE: f_leak => c9f0f895fb98ab9159f51fd0297e236d
1925 NOTICE: f_leak => d3d9446802a44259755d38e6d163e820
1927 ----+----------------------------------
1928 2 | c81e728d9d4c2f636f067f89cc14862c
1929 4 | a87ff679a2f3e71d9181a67b7542122c
1930 6 | 1679091c5a880faf6fb5e6087eb1b2dc
1931 8 | c9f0f895fb98ab9159f51fd0297e236d
1932 10 | d3d9446802a44259755d38e6d163e820
1935 INSERT INTO bv1 VALUES (-1, 'xxx'); -- should fail view WCO
1936 ERROR: new row violates row-level security policy for table "b1"
1937 INSERT INTO bv1 VALUES (11, 'xxx'); -- should fail RLS check
1938 ERROR: new row violates row-level security policy for table "b1"
1939 INSERT INTO bv1 VALUES (12, 'xxx'); -- ok
1940 EXPLAIN (COSTS OFF) UPDATE bv1 SET b = 'yyy' WHERE a = 4 AND f_leak(b);
1942 -----------------------------------------------------------------------
1945 Filter: ((a > 0) AND (a = 4) AND ((a % 2) = 0) AND f_leak(b))
1948 UPDATE bv1 SET b = 'yyy' WHERE a = 4 AND f_leak(b);
1949 NOTICE: f_leak => a87ff679a2f3e71d9181a67b7542122c
1950 EXPLAIN (COSTS OFF) DELETE FROM bv1 WHERE a = 6 AND f_leak(b);
1952 -----------------------------------------------------------------------
1955 Filter: ((a > 0) AND (a = 6) AND ((a % 2) = 0) AND f_leak(b))
1958 DELETE FROM bv1 WHERE a = 6 AND f_leak(b);
1959 NOTICE: f_leak => 1679091c5a880faf6fb5e6087eb1b2dc
1960 SET SESSION AUTHORIZATION regress_rls_alice;
1963 -----+----------------------------------
1964 -10 | 1b0fd9efa5279c4203b7c70233f86dbf
1965 -9 | 252e691406782824eec43d7eadc3d256
1966 -8 | a8d2ec85eaf98407310b72eb73dda247
1967 -7 | 74687a12d3915d3c4d83f1af7b3683d5
1968 -6 | 596a3d04481816330f07e4f97510c28f
1969 -5 | 47c1b025fa18ea96c33fbb6718688c0f
1970 -4 | 0267aaf632e87a63288a08331f22c7c3
1971 -3 | b3149ecea4628efd23d2f86e5a723472
1972 -2 | 5d7b9adcbe1c629ec722529dd12e5129
1973 -1 | 6bb61e3b7bce0931da574d19d1d82c88
1974 0 | cfcd208495d565ef66e7dff9f98764da
1975 1 | c4ca4238a0b923820dcc509a6f75849b
1976 2 | c81e728d9d4c2f636f067f89cc14862c
1977 3 | eccbc87e4b5ce2fe28308fd9f2a7baf3
1978 5 | e4da3b7fbbce2345d7772b0674a318d5
1979 7 | 8f14e45fceea167a5a36dedd4bea2543
1980 8 | c9f0f895fb98ab9159f51fd0297e236d
1981 9 | 45c48cce2e2d7fbdea1afc51c7c6ad26
1982 10 | d3d9446802a44259755d38e6d163e820
1988 -- INSERT ... ON CONFLICT DO UPDATE and Row-level security
1990 SET SESSION AUTHORIZATION regress_rls_alice;
1991 DROP POLICY p1 ON document;
1992 DROP POLICY p1r ON document;
1993 CREATE POLICY p1 ON document FOR SELECT USING (true);
1994 CREATE POLICY p2 ON document FOR INSERT WITH CHECK (dauthor = current_user);
1995 CREATE POLICY p3 ON document FOR UPDATE
1996 USING (cid = (SELECT cid from category WHERE cname = 'novel'))
1997 WITH CHECK (dauthor = current_user);
1998 SET SESSION AUTHORIZATION regress_rls_bob;
2000 SELECT * FROM document WHERE did = 2;
2001 did | cid | dlevel | dauthor | dtitle
2002 -----+-----+--------+-----------------+-----------------
2003 2 | 11 | 2 | regress_rls_bob | my second novel
2006 -- ...so violates actual WITH CHECK OPTION within UPDATE (not INSERT, since
2007 -- alternative UPDATE path happens to be taken):
2008 INSERT INTO document VALUES (2, (SELECT cid from category WHERE cname = 'novel'), 1, 'regress_rls_carol', 'my first novel')
2009 ON CONFLICT (did) DO UPDATE SET dtitle = EXCLUDED.dtitle, dauthor = EXCLUDED.dauthor;
2010 ERROR: new row violates row-level security policy for table "document"
2011 -- Violates USING qual for UPDATE policy p3.
2013 -- UPDATE path is taken, but UPDATE fails purely because *existing* row to be
2014 -- updated is not a "novel"/cid 11 (row is not leaked, even though we have
2015 -- SELECT privileges sufficient to see the row in this instance):
2016 INSERT INTO document VALUES (33, 22, 1, 'regress_rls_bob', 'okay science fiction'); -- preparation for next statement
2017 INSERT INTO document VALUES (33, (SELECT cid from category WHERE cname = 'novel'), 1, 'regress_rls_bob', 'Some novel, replaces sci-fi') -- takes UPDATE path
2018 ON CONFLICT (did) DO UPDATE SET dtitle = EXCLUDED.dtitle;
2019 ERROR: new row violates row-level security policy (USING expression) for table "document"
2020 -- Fine (we UPDATE, since INSERT WCOs and UPDATE security barrier quals + WCOs
2022 INSERT INTO document VALUES (2, (SELECT cid from category WHERE cname = 'novel'), 1, 'regress_rls_bob', 'my first novel')
2023 ON CONFLICT (did) DO UPDATE SET dtitle = EXCLUDED.dtitle RETURNING *;
2024 did | cid | dlevel | dauthor | dtitle
2025 -----+-----+--------+-----------------+----------------
2026 2 | 11 | 2 | regress_rls_bob | my first novel
2029 -- Fine (we INSERT, so "cid = 33" ("technology") isn't evaluated):
2030 INSERT INTO document VALUES (78, (SELECT cid from category WHERE cname = 'novel'), 1, 'regress_rls_bob', 'some technology novel')
2031 ON CONFLICT (did) DO UPDATE SET dtitle = EXCLUDED.dtitle, cid = 33 RETURNING *;
2032 did | cid | dlevel | dauthor | dtitle
2033 -----+-----+--------+-----------------+-----------------------
2034 78 | 11 | 1 | regress_rls_bob | some technology novel
2037 -- Fine (same query, but we UPDATE, so "cid = 33", ("technology") is not the
2038 -- case in respect of *existing* tuple):
2039 INSERT INTO document VALUES (78, (SELECT cid from category WHERE cname = 'novel'), 1, 'regress_rls_bob', 'some technology novel')
2040 ON CONFLICT (did) DO UPDATE SET dtitle = EXCLUDED.dtitle, cid = 33 RETURNING *;
2041 did | cid | dlevel | dauthor | dtitle
2042 -----+-----+--------+-----------------+-----------------------
2043 78 | 33 | 1 | regress_rls_bob | some technology novel
2046 -- Same query a third time, but now fails due to existing tuple finally not
2048 INSERT INTO document VALUES (78, (SELECT cid from category WHERE cname = 'novel'), 1, 'regress_rls_bob', 'some technology novel')
2049 ON CONFLICT (did) DO UPDATE SET dtitle = EXCLUDED.dtitle, cid = 33 RETURNING *;
2050 ERROR: new row violates row-level security policy (USING expression) for table "document"
2051 -- Don't fail just because INSERT doesn't satisfy WITH CHECK option that
2052 -- originated as a barrier/USING() qual from the UPDATE. Note that the UPDATE
2053 -- path *isn't* taken, and so UPDATE-related policy does not apply:
2054 INSERT INTO document VALUES (79, (SELECT cid from category WHERE cname = 'technology'), 1, 'regress_rls_bob', 'technology book, can only insert')
2055 ON CONFLICT (did) DO UPDATE SET dtitle = EXCLUDED.dtitle RETURNING *;
2056 did | cid | dlevel | dauthor | dtitle
2057 -----+-----+--------+-----------------+----------------------------------
2058 79 | 33 | 1 | regress_rls_bob | technology book, can only insert
2061 -- But this time, the same statement fails, because the UPDATE path is taken,
2062 -- and updating the row just inserted falls afoul of security barrier qual
2063 -- (enforced as WCO) -- what we might have updated target tuple to is
2064 -- irrelevant, in fact.
2065 INSERT INTO document VALUES (79, (SELECT cid from category WHERE cname = 'technology'), 1, 'regress_rls_bob', 'technology book, can only insert')
2066 ON CONFLICT (did) DO UPDATE SET dtitle = EXCLUDED.dtitle RETURNING *;
2067 ERROR: new row violates row-level security policy (USING expression) for table "document"
2068 -- Test default USING qual enforced as WCO
2069 SET SESSION AUTHORIZATION regress_rls_alice;
2070 DROP POLICY p1 ON document;
2071 DROP POLICY p2 ON document;
2072 DROP POLICY p3 ON document;
2073 CREATE POLICY p3_with_default ON document FOR UPDATE
2074 USING (cid = (SELECT cid from category WHERE cname = 'novel'));
2075 SET SESSION AUTHORIZATION regress_rls_bob;
2076 -- Just because WCO-style enforcement of USING quals occurs with
2077 -- existing/target tuple does not mean that the implementation can be allowed
2078 -- to fail to also enforce this qual against the final tuple appended to
2079 -- relation (since in the absence of an explicit WCO, this is also interpreted
2080 -- as an UPDATE/ALL WCO in general).
2082 -- UPDATE path is taken here (fails due to existing tuple). Note that this is
2083 -- not reported as a "USING expression", because it's an RLS UPDATE check that originated as
2084 -- a USING qual for the purposes of RLS in general, as opposed to an explicit
2085 -- USING qual that is ordinarily a security barrier. We leave it up to the
2086 -- UPDATE to make this fail:
2087 INSERT INTO document VALUES (79, (SELECT cid from category WHERE cname = 'technology'), 1, 'regress_rls_bob', 'technology book, can only insert')
2088 ON CONFLICT (did) DO UPDATE SET dtitle = EXCLUDED.dtitle RETURNING *;
2089 ERROR: new row violates row-level security policy for table "document"
2090 -- UPDATE path is taken here. Existing tuple passes, since its cid
2091 -- corresponds to "novel", but default USING qual is enforced against
2092 -- post-UPDATE tuple too (as always when updating with a policy that lacks an
2093 -- explicit WCO), and so this fails:
2094 INSERT INTO document VALUES (2, (SELECT cid from category WHERE cname = 'technology'), 1, 'regress_rls_bob', 'my first novel')
2095 ON CONFLICT (did) DO UPDATE SET cid = EXCLUDED.cid, dtitle = EXCLUDED.dtitle RETURNING *;
2096 ERROR: new row violates row-level security policy for table "document"
2097 SET SESSION AUTHORIZATION regress_rls_alice;
2098 DROP POLICY p3_with_default ON document;
2100 -- Test ALL policies with ON CONFLICT DO UPDATE (much the same as existing UPDATE
2103 CREATE POLICY p3_with_all ON document FOR ALL
2104 USING (cid = (SELECT cid from category WHERE cname = 'novel'))
2105 WITH CHECK (dauthor = current_user);
2106 SET SESSION AUTHORIZATION regress_rls_bob;
2107 -- Fails, since ALL WCO is enforced in insert path:
2108 INSERT INTO document VALUES (80, (SELECT cid from category WHERE cname = 'novel'), 1, 'regress_rls_carol', 'my first novel')
2109 ON CONFLICT (did) DO UPDATE SET dtitle = EXCLUDED.dtitle, cid = 33;
2110 ERROR: new row violates row-level security policy for table "document"
2111 -- Fails, since ALL policy USING qual is enforced (existing, target tuple is in
2112 -- violation, since it has the "manga" cid):
2113 INSERT INTO document VALUES (4, (SELECT cid from category WHERE cname = 'novel'), 1, 'regress_rls_bob', 'my first novel')
2114 ON CONFLICT (did) DO UPDATE SET dtitle = EXCLUDED.dtitle;
2115 ERROR: new row violates row-level security policy (USING expression) for table "document"
2116 -- Fails, since ALL WCO are enforced:
2117 INSERT INTO document VALUES (1, (SELECT cid from category WHERE cname = 'novel'), 1, 'regress_rls_bob', 'my first novel')
2118 ON CONFLICT (did) DO UPDATE SET dauthor = 'regress_rls_carol';
2119 ERROR: new row violates row-level security policy for table "document"
2123 SET SESSION AUTHORIZATION regress_rls_alice;
2124 CREATE TABLE z1 (a int, b text);
2125 CREATE TABLE z2 (a int, b text);
2126 GRANT SELECT ON z1,z2 TO regress_rls_group1, regress_rls_group2,
2127 regress_rls_bob, regress_rls_carol;
2128 INSERT INTO z1 VALUES
2133 CREATE POLICY p1 ON z1 TO regress_rls_group1 USING (a % 2 = 0);
2134 CREATE POLICY p2 ON z1 TO regress_rls_group2 USING (a % 2 = 1);
2135 ALTER TABLE z1 ENABLE ROW LEVEL SECURITY;
2136 SET SESSION AUTHORIZATION regress_rls_bob;
2137 SELECT * FROM z1 WHERE f_leak(b);
2138 NOTICE: f_leak => bbb
2139 NOTICE: f_leak => dad
2146 EXPLAIN (COSTS OFF) SELECT * FROM z1 WHERE f_leak(b);
2148 -----------------------------------------
2150 Filter: (((a % 2) = 0) AND f_leak(b))
2153 PREPARE plancache_test AS SELECT * FROM z1 WHERE f_leak(b);
2154 EXPLAIN (COSTS OFF) EXECUTE plancache_test;
2156 -----------------------------------------
2158 Filter: (((a % 2) = 0) AND f_leak(b))
2161 PREPARE plancache_test2 AS WITH q AS MATERIALIZED (SELECT * FROM z1 WHERE f_leak(b)) SELECT * FROM q,z2;
2162 EXPLAIN (COSTS OFF) EXECUTE plancache_test2;
2164 -------------------------------------------------
2168 Filter: (((a % 2) = 0) AND f_leak(b))
2174 PREPARE plancache_test3 AS WITH q AS MATERIALIZED (SELECT * FROM z2) SELECT * FROM q,z1 WHERE f_leak(z1.b);
2175 EXPLAIN (COSTS OFF) EXECUTE plancache_test3;
2177 -----------------------------------------------------
2184 Filter: (((a % 2) = 0) AND f_leak(b))
2187 SET ROLE regress_rls_group1;
2188 SELECT * FROM z1 WHERE f_leak(b);
2189 NOTICE: f_leak => bbb
2190 NOTICE: f_leak => dad
2197 EXPLAIN (COSTS OFF) SELECT * FROM z1 WHERE f_leak(b);
2199 -----------------------------------------
2201 Filter: (((a % 2) = 0) AND f_leak(b))
2204 EXPLAIN (COSTS OFF) EXECUTE plancache_test;
2206 -----------------------------------------
2208 Filter: (((a % 2) = 0) AND f_leak(b))
2211 EXPLAIN (COSTS OFF) EXECUTE plancache_test2;
2213 -------------------------------------------------
2217 Filter: (((a % 2) = 0) AND f_leak(b))
2223 EXPLAIN (COSTS OFF) EXECUTE plancache_test3;
2225 -----------------------------------------------------
2232 Filter: (((a % 2) = 0) AND f_leak(b))
2235 SET SESSION AUTHORIZATION regress_rls_carol;
2236 SELECT * FROM z1 WHERE f_leak(b);
2237 NOTICE: f_leak => aba
2238 NOTICE: f_leak => ccc
2245 EXPLAIN (COSTS OFF) SELECT * FROM z1 WHERE f_leak(b);
2247 -----------------------------------------
2249 Filter: (((a % 2) = 1) AND f_leak(b))
2252 EXPLAIN (COSTS OFF) EXECUTE plancache_test;
2254 -----------------------------------------
2256 Filter: (((a % 2) = 1) AND f_leak(b))
2259 EXPLAIN (COSTS OFF) EXECUTE plancache_test2;
2261 -------------------------------------------------
2265 Filter: (((a % 2) = 1) AND f_leak(b))
2271 EXPLAIN (COSTS OFF) EXECUTE plancache_test3;
2273 -----------------------------------------------------
2280 Filter: (((a % 2) = 1) AND f_leak(b))
2283 SET ROLE regress_rls_group2;
2284 SELECT * FROM z1 WHERE f_leak(b);
2285 NOTICE: f_leak => aba
2286 NOTICE: f_leak => ccc
2293 EXPLAIN (COSTS OFF) SELECT * FROM z1 WHERE f_leak(b);
2295 -----------------------------------------
2297 Filter: (((a % 2) = 1) AND f_leak(b))
2300 EXPLAIN (COSTS OFF) EXECUTE plancache_test;
2302 -----------------------------------------
2304 Filter: (((a % 2) = 1) AND f_leak(b))
2307 EXPLAIN (COSTS OFF) EXECUTE plancache_test2;
2309 -------------------------------------------------
2313 Filter: (((a % 2) = 1) AND f_leak(b))
2319 EXPLAIN (COSTS OFF) EXECUTE plancache_test3;
2321 -----------------------------------------------------
2328 Filter: (((a % 2) = 1) AND f_leak(b))
2332 -- Views should follow policy for view owner.
2334 -- View and Table owner are the same.
2335 SET SESSION AUTHORIZATION regress_rls_alice;
2336 CREATE VIEW rls_view AS SELECT * FROM z1 WHERE f_leak(b);
2337 GRANT SELECT ON rls_view TO regress_rls_bob;
2338 -- Query as role that is not owner of view or table. Should return all records.
2339 SET SESSION AUTHORIZATION regress_rls_bob;
2340 SELECT * FROM rls_view;
2341 NOTICE: f_leak => aba
2342 NOTICE: f_leak => bbb
2343 NOTICE: f_leak => ccc
2344 NOTICE: f_leak => dad
2353 EXPLAIN (COSTS OFF) SELECT * FROM rls_view;
2355 ---------------------
2360 -- Query as view/table owner. Should return all records.
2361 SET SESSION AUTHORIZATION regress_rls_alice;
2362 SELECT * FROM rls_view;
2363 NOTICE: f_leak => aba
2364 NOTICE: f_leak => bbb
2365 NOTICE: f_leak => ccc
2366 NOTICE: f_leak => dad
2375 EXPLAIN (COSTS OFF) SELECT * FROM rls_view;
2377 ---------------------
2383 -- View and Table owners are different.
2384 SET SESSION AUTHORIZATION regress_rls_bob;
2385 CREATE VIEW rls_view AS SELECT * FROM z1 WHERE f_leak(b);
2386 GRANT SELECT ON rls_view TO regress_rls_alice;
2387 -- Query as role that is not owner of view but is owner of table.
2388 -- Should return records based on view owner policies.
2389 SET SESSION AUTHORIZATION regress_rls_alice;
2390 SELECT * FROM rls_view;
2391 NOTICE: f_leak => bbb
2392 NOTICE: f_leak => dad
2399 EXPLAIN (COSTS OFF) SELECT * FROM rls_view;
2401 -----------------------------------------
2403 Filter: (((a % 2) = 0) AND f_leak(b))
2406 -- Query as role that is not owner of table but is owner of view.
2407 -- Should return records based on view owner policies.
2408 SET SESSION AUTHORIZATION regress_rls_bob;
2409 SELECT * FROM rls_view;
2410 NOTICE: f_leak => bbb
2411 NOTICE: f_leak => dad
2418 EXPLAIN (COSTS OFF) SELECT * FROM rls_view;
2420 -----------------------------------------
2422 Filter: (((a % 2) = 0) AND f_leak(b))
2425 -- Query as role that is not the owner of the table or view without permissions.
2426 SET SESSION AUTHORIZATION regress_rls_carol;
2427 SELECT * FROM rls_view; --fail - permission denied.
2428 ERROR: permission denied for view rls_view
2429 EXPLAIN (COSTS OFF) SELECT * FROM rls_view; --fail - permission denied.
2430 ERROR: permission denied for view rls_view
2431 -- Query as role that is not the owner of the table or view with permissions.
2432 SET SESSION AUTHORIZATION regress_rls_bob;
2433 GRANT SELECT ON rls_view TO regress_rls_carol;
2434 SELECT * FROM rls_view;
2435 NOTICE: f_leak => bbb
2436 NOTICE: f_leak => dad
2443 EXPLAIN (COSTS OFF) SELECT * FROM rls_view;
2445 -----------------------------------------
2447 Filter: (((a % 2) = 0) AND f_leak(b))
2450 SET SESSION AUTHORIZATION regress_rls_bob;
2455 SET SESSION AUTHORIZATION regress_rls_alice;
2456 CREATE TABLE x1 (a int, b text, c text);
2457 GRANT ALL ON x1 TO PUBLIC;
2458 INSERT INTO x1 VALUES
2459 (1, 'abc', 'regress_rls_bob'),
2460 (2, 'bcd', 'regress_rls_bob'),
2461 (3, 'cde', 'regress_rls_carol'),
2462 (4, 'def', 'regress_rls_carol'),
2463 (5, 'efg', 'regress_rls_bob'),
2464 (6, 'fgh', 'regress_rls_bob'),
2465 (7, 'fgh', 'regress_rls_carol'),
2466 (8, 'fgh', 'regress_rls_carol');
2467 CREATE POLICY p0 ON x1 FOR ALL USING (c = current_user);
2468 CREATE POLICY p1 ON x1 FOR SELECT USING (a % 2 = 0);
2469 CREATE POLICY p2 ON x1 FOR INSERT WITH CHECK (a % 2 = 1);
2470 CREATE POLICY p3 ON x1 FOR UPDATE USING (a % 2 = 0);
2471 CREATE POLICY p4 ON x1 FOR DELETE USING (a < 8);
2472 ALTER TABLE x1 ENABLE ROW LEVEL SECURITY;
2473 SET SESSION AUTHORIZATION regress_rls_bob;
2474 SELECT * FROM x1 WHERE f_leak(b) ORDER BY a ASC;
2475 NOTICE: f_leak => abc
2476 NOTICE: f_leak => bcd
2477 NOTICE: f_leak => def
2478 NOTICE: f_leak => efg
2479 NOTICE: f_leak => fgh
2480 NOTICE: f_leak => fgh
2482 ---+-----+-------------------
2483 1 | abc | regress_rls_bob
2484 2 | bcd | regress_rls_bob
2485 4 | def | regress_rls_carol
2486 5 | efg | regress_rls_bob
2487 6 | fgh | regress_rls_bob
2488 8 | fgh | regress_rls_carol
2491 UPDATE x1 SET b = b || '_updt' WHERE f_leak(b) RETURNING *;
2492 NOTICE: f_leak => abc
2493 NOTICE: f_leak => bcd
2494 NOTICE: f_leak => def
2495 NOTICE: f_leak => efg
2496 NOTICE: f_leak => fgh
2497 NOTICE: f_leak => fgh
2499 ---+----------+-------------------
2500 1 | abc_updt | regress_rls_bob
2501 2 | bcd_updt | regress_rls_bob
2502 4 | def_updt | regress_rls_carol
2503 5 | efg_updt | regress_rls_bob
2504 6 | fgh_updt | regress_rls_bob
2505 8 | fgh_updt | regress_rls_carol
2508 SET SESSION AUTHORIZATION regress_rls_carol;
2509 SELECT * FROM x1 WHERE f_leak(b) ORDER BY a ASC;
2510 NOTICE: f_leak => cde
2511 NOTICE: f_leak => fgh
2512 NOTICE: f_leak => bcd_updt
2513 NOTICE: f_leak => def_updt
2514 NOTICE: f_leak => fgh_updt
2515 NOTICE: f_leak => fgh_updt
2517 ---+----------+-------------------
2518 2 | bcd_updt | regress_rls_bob
2519 3 | cde | regress_rls_carol
2520 4 | def_updt | regress_rls_carol
2521 6 | fgh_updt | regress_rls_bob
2522 7 | fgh | regress_rls_carol
2523 8 | fgh_updt | regress_rls_carol
2526 UPDATE x1 SET b = b || '_updt' WHERE f_leak(b) RETURNING *;
2527 NOTICE: f_leak => cde
2528 NOTICE: f_leak => fgh
2529 NOTICE: f_leak => bcd_updt
2530 NOTICE: f_leak => def_updt
2531 NOTICE: f_leak => fgh_updt
2532 NOTICE: f_leak => fgh_updt
2534 ---+---------------+-------------------
2535 3 | cde_updt | regress_rls_carol
2536 7 | fgh_updt | regress_rls_carol
2537 2 | bcd_updt_updt | regress_rls_bob
2538 4 | def_updt_updt | regress_rls_carol
2539 6 | fgh_updt_updt | regress_rls_bob
2540 8 | fgh_updt_updt | regress_rls_carol
2543 DELETE FROM x1 WHERE f_leak(b) RETURNING *;
2544 NOTICE: f_leak => cde_updt
2545 NOTICE: f_leak => fgh_updt
2546 NOTICE: f_leak => bcd_updt_updt
2547 NOTICE: f_leak => def_updt_updt
2548 NOTICE: f_leak => fgh_updt_updt
2549 NOTICE: f_leak => fgh_updt_updt
2551 ---+---------------+-------------------
2552 3 | cde_updt | regress_rls_carol
2553 7 | fgh_updt | regress_rls_carol
2554 2 | bcd_updt_updt | regress_rls_bob
2555 4 | def_updt_updt | regress_rls_carol
2556 6 | fgh_updt_updt | regress_rls_bob
2557 8 | fgh_updt_updt | regress_rls_carol
2561 -- Duplicate Policy Names
2563 SET SESSION AUTHORIZATION regress_rls_alice;
2564 CREATE TABLE y1 (a int, b text);
2565 CREATE TABLE y2 (a int, b text);
2566 GRANT ALL ON y1, y2 TO regress_rls_bob;
2567 CREATE POLICY p1 ON y1 FOR ALL USING (a % 2 = 0);
2568 CREATE POLICY p2 ON y1 FOR SELECT USING (a > 2);
2569 CREATE POLICY p1 ON y1 FOR SELECT USING (a % 2 = 1); --fail
2570 ERROR: policy "p1" for table "y1" already exists
2571 CREATE POLICY p1 ON y2 FOR ALL USING (a % 2 = 0); --OK
2572 ALTER TABLE y1 ENABLE ROW LEVEL SECURITY;
2573 ALTER TABLE y2 ENABLE ROW LEVEL SECURITY;
2575 -- Expression structure with SBV
2577 -- Create view as table owner. RLS should NOT be applied.
2578 SET SESSION AUTHORIZATION regress_rls_alice;
2579 CREATE VIEW rls_sbv WITH (security_barrier) AS
2580 SELECT * FROM y1 WHERE f_leak(b);
2581 EXPLAIN (COSTS OFF) SELECT * FROM rls_sbv WHERE (a = 1);
2583 -----------------------------------
2585 Filter: (f_leak(b) AND (a = 1))
2589 -- Create view as role that does not own table. RLS should be applied.
2590 SET SESSION AUTHORIZATION regress_rls_bob;
2591 CREATE VIEW rls_sbv WITH (security_barrier) AS
2592 SELECT * FROM y1 WHERE f_leak(b);
2593 EXPLAIN (COSTS OFF) SELECT * FROM rls_sbv WHERE (a = 1);
2595 ------------------------------------------------------------------
2597 Filter: ((a = 1) AND ((a > 2) OR ((a % 2) = 0)) AND f_leak(b))
2602 -- Expression structure
2604 SET SESSION AUTHORIZATION regress_rls_alice;
2605 INSERT INTO y2 (SELECT x, md5(x::text) FROM generate_series(0,20) x);
2606 CREATE POLICY p2 ON y2 USING (a % 3 = 0);
2607 CREATE POLICY p3 ON y2 USING (a % 4 = 0);
2608 SET SESSION AUTHORIZATION regress_rls_bob;
2609 SELECT * FROM y2 WHERE f_leak(b);
2610 NOTICE: f_leak => cfcd208495d565ef66e7dff9f98764da
2611 NOTICE: f_leak => c81e728d9d4c2f636f067f89cc14862c
2612 NOTICE: f_leak => eccbc87e4b5ce2fe28308fd9f2a7baf3
2613 NOTICE: f_leak => a87ff679a2f3e71d9181a67b7542122c
2614 NOTICE: f_leak => 1679091c5a880faf6fb5e6087eb1b2dc
2615 NOTICE: f_leak => c9f0f895fb98ab9159f51fd0297e236d
2616 NOTICE: f_leak => 45c48cce2e2d7fbdea1afc51c7c6ad26
2617 NOTICE: f_leak => d3d9446802a44259755d38e6d163e820
2618 NOTICE: f_leak => c20ad4d76fe97759aa27a0c99bff6710
2619 NOTICE: f_leak => aab3238922bcc25a6f606eb525ffdc56
2620 NOTICE: f_leak => 9bf31c7ff062936a96d3c8bd1f8f2ff3
2621 NOTICE: f_leak => c74d97b01eae257e44aa9d5bade97baf
2622 NOTICE: f_leak => 6f4922f45568161a8cdf4ad2299f6d23
2623 NOTICE: f_leak => 98f13708210194c475687be6106a3b84
2625 ----+----------------------------------
2626 0 | cfcd208495d565ef66e7dff9f98764da
2627 2 | c81e728d9d4c2f636f067f89cc14862c
2628 3 | eccbc87e4b5ce2fe28308fd9f2a7baf3
2629 4 | a87ff679a2f3e71d9181a67b7542122c
2630 6 | 1679091c5a880faf6fb5e6087eb1b2dc
2631 8 | c9f0f895fb98ab9159f51fd0297e236d
2632 9 | 45c48cce2e2d7fbdea1afc51c7c6ad26
2633 10 | d3d9446802a44259755d38e6d163e820
2634 12 | c20ad4d76fe97759aa27a0c99bff6710
2635 14 | aab3238922bcc25a6f606eb525ffdc56
2636 15 | 9bf31c7ff062936a96d3c8bd1f8f2ff3
2637 16 | c74d97b01eae257e44aa9d5bade97baf
2638 18 | 6f4922f45568161a8cdf4ad2299f6d23
2639 20 | 98f13708210194c475687be6106a3b84
2642 EXPLAIN (COSTS OFF) SELECT * FROM y2 WHERE f_leak(b);
2644 -----------------------------------------------------------------------------
2646 Filter: ((((a % 4) = 0) OR ((a % 3) = 0) OR ((a % 2) = 0)) AND f_leak(b))
2650 -- Qual push-down of leaky functions, when not referring to table
2652 SELECT * FROM y2 WHERE f_leak('abc');
2653 NOTICE: f_leak => abc
2654 NOTICE: f_leak => abc
2655 NOTICE: f_leak => abc
2656 NOTICE: f_leak => abc
2657 NOTICE: f_leak => abc
2658 NOTICE: f_leak => abc
2659 NOTICE: f_leak => abc
2660 NOTICE: f_leak => abc
2661 NOTICE: f_leak => abc
2662 NOTICE: f_leak => abc
2663 NOTICE: f_leak => abc
2664 NOTICE: f_leak => abc
2665 NOTICE: f_leak => abc
2666 NOTICE: f_leak => abc
2667 NOTICE: f_leak => abc
2668 NOTICE: f_leak => abc
2669 NOTICE: f_leak => abc
2670 NOTICE: f_leak => abc
2671 NOTICE: f_leak => abc
2672 NOTICE: f_leak => abc
2673 NOTICE: f_leak => abc
2675 ----+----------------------------------
2676 0 | cfcd208495d565ef66e7dff9f98764da
2677 2 | c81e728d9d4c2f636f067f89cc14862c
2678 3 | eccbc87e4b5ce2fe28308fd9f2a7baf3
2679 4 | a87ff679a2f3e71d9181a67b7542122c
2680 6 | 1679091c5a880faf6fb5e6087eb1b2dc
2681 8 | c9f0f895fb98ab9159f51fd0297e236d
2682 9 | 45c48cce2e2d7fbdea1afc51c7c6ad26
2683 10 | d3d9446802a44259755d38e6d163e820
2684 12 | c20ad4d76fe97759aa27a0c99bff6710
2685 14 | aab3238922bcc25a6f606eb525ffdc56
2686 15 | 9bf31c7ff062936a96d3c8bd1f8f2ff3
2687 16 | c74d97b01eae257e44aa9d5bade97baf
2688 18 | 6f4922f45568161a8cdf4ad2299f6d23
2689 20 | 98f13708210194c475687be6106a3b84
2692 EXPLAIN (COSTS OFF) SELECT * FROM y2 WHERE f_leak('abc');
2694 ---------------------------------------------------------------------------------------
2696 Filter: (f_leak('abc'::text) AND (((a % 4) = 0) OR ((a % 3) = 0) OR ((a % 2) = 0)))
2699 CREATE TABLE test_qual_pushdown (
2702 INSERT INTO test_qual_pushdown VALUES ('abc'),('def');
2703 SELECT * FROM y2 JOIN test_qual_pushdown ON (b = abc) WHERE f_leak(abc);
2704 NOTICE: f_leak => abc
2705 NOTICE: f_leak => def
2710 EXPLAIN (COSTS OFF) SELECT * FROM y2 JOIN test_qual_pushdown ON (b = abc) WHERE f_leak(abc);
2712 -------------------------------------------------------------------------
2714 Hash Cond: (test_qual_pushdown.abc = y2.b)
2715 -> Seq Scan on test_qual_pushdown
2719 Filter: (((a % 4) = 0) OR ((a % 3) = 0) OR ((a % 2) = 0))
2722 SELECT * FROM y2 JOIN test_qual_pushdown ON (b = abc) WHERE f_leak(b);
2723 NOTICE: f_leak => cfcd208495d565ef66e7dff9f98764da
2724 NOTICE: f_leak => c81e728d9d4c2f636f067f89cc14862c
2725 NOTICE: f_leak => eccbc87e4b5ce2fe28308fd9f2a7baf3
2726 NOTICE: f_leak => a87ff679a2f3e71d9181a67b7542122c
2727 NOTICE: f_leak => 1679091c5a880faf6fb5e6087eb1b2dc
2728 NOTICE: f_leak => c9f0f895fb98ab9159f51fd0297e236d
2729 NOTICE: f_leak => 45c48cce2e2d7fbdea1afc51c7c6ad26
2730 NOTICE: f_leak => d3d9446802a44259755d38e6d163e820
2731 NOTICE: f_leak => c20ad4d76fe97759aa27a0c99bff6710
2732 NOTICE: f_leak => aab3238922bcc25a6f606eb525ffdc56
2733 NOTICE: f_leak => 9bf31c7ff062936a96d3c8bd1f8f2ff3
2734 NOTICE: f_leak => c74d97b01eae257e44aa9d5bade97baf
2735 NOTICE: f_leak => 6f4922f45568161a8cdf4ad2299f6d23
2736 NOTICE: f_leak => 98f13708210194c475687be6106a3b84
2741 EXPLAIN (COSTS OFF) SELECT * FROM y2 JOIN test_qual_pushdown ON (b = abc) WHERE f_leak(b);
2743 -----------------------------------------------------------------------------------------
2745 Hash Cond: (test_qual_pushdown.abc = y2.b)
2746 -> Seq Scan on test_qual_pushdown
2749 Filter: ((((a % 4) = 0) OR ((a % 3) = 0) OR ((a % 2) = 0)) AND f_leak(b))
2752 DROP TABLE test_qual_pushdown;
2754 -- Plancache invalidate on user change.
2756 RESET SESSION AUTHORIZATION;
2757 DROP TABLE t1 CASCADE;
2758 NOTICE: drop cascades to 2 other objects
2759 DETAIL: drop cascades to table t2
2760 drop cascades to table t3
2761 CREATE TABLE t1 (a integer);
2762 GRANT SELECT ON t1 TO regress_rls_bob, regress_rls_carol;
2763 CREATE POLICY p1 ON t1 TO regress_rls_bob USING ((a % 2) = 0);
2764 CREATE POLICY p2 ON t1 TO regress_rls_carol USING ((a % 4) = 0);
2765 ALTER TABLE t1 ENABLE ROW LEVEL SECURITY;
2766 -- Prepare as regress_rls_bob
2767 SET ROLE regress_rls_bob;
2768 PREPARE role_inval AS SELECT * FROM t1;
2770 EXPLAIN (COSTS OFF) EXECUTE role_inval;
2772 -------------------------
2774 Filter: ((a % 2) = 0)
2777 -- Change to regress_rls_carol
2778 SET ROLE regress_rls_carol;
2779 -- Check plan- should be different
2780 EXPLAIN (COSTS OFF) EXECUTE role_inval;
2782 -------------------------
2784 Filter: ((a % 4) = 0)
2787 -- Change back to regress_rls_bob
2788 SET ROLE regress_rls_bob;
2789 -- Check plan- should be back to original
2790 EXPLAIN (COSTS OFF) EXECUTE role_inval;
2792 -------------------------
2794 Filter: ((a % 2) = 0)
2800 RESET SESSION AUTHORIZATION;
2801 DROP TABLE t1 CASCADE;
2802 CREATE TABLE t1 (a integer, b text);
2803 CREATE POLICY p1 ON t1 USING (a % 2 = 0);
2804 ALTER TABLE t1 ENABLE ROW LEVEL SECURITY;
2805 GRANT ALL ON t1 TO regress_rls_bob;
2806 INSERT INTO t1 (SELECT x, md5(x::text) FROM generate_series(0,20) x);
2807 SET SESSION AUTHORIZATION regress_rls_bob;
2808 WITH cte1 AS MATERIALIZED (SELECT * FROM t1 WHERE f_leak(b)) SELECT * FROM cte1;
2809 NOTICE: f_leak => cfcd208495d565ef66e7dff9f98764da
2810 NOTICE: f_leak => c81e728d9d4c2f636f067f89cc14862c
2811 NOTICE: f_leak => a87ff679a2f3e71d9181a67b7542122c
2812 NOTICE: f_leak => 1679091c5a880faf6fb5e6087eb1b2dc
2813 NOTICE: f_leak => c9f0f895fb98ab9159f51fd0297e236d
2814 NOTICE: f_leak => d3d9446802a44259755d38e6d163e820
2815 NOTICE: f_leak => c20ad4d76fe97759aa27a0c99bff6710
2816 NOTICE: f_leak => aab3238922bcc25a6f606eb525ffdc56
2817 NOTICE: f_leak => c74d97b01eae257e44aa9d5bade97baf
2818 NOTICE: f_leak => 6f4922f45568161a8cdf4ad2299f6d23
2819 NOTICE: f_leak => 98f13708210194c475687be6106a3b84
2821 ----+----------------------------------
2822 0 | cfcd208495d565ef66e7dff9f98764da
2823 2 | c81e728d9d4c2f636f067f89cc14862c
2824 4 | a87ff679a2f3e71d9181a67b7542122c
2825 6 | 1679091c5a880faf6fb5e6087eb1b2dc
2826 8 | c9f0f895fb98ab9159f51fd0297e236d
2827 10 | d3d9446802a44259755d38e6d163e820
2828 12 | c20ad4d76fe97759aa27a0c99bff6710
2829 14 | aab3238922bcc25a6f606eb525ffdc56
2830 16 | c74d97b01eae257e44aa9d5bade97baf
2831 18 | 6f4922f45568161a8cdf4ad2299f6d23
2832 20 | 98f13708210194c475687be6106a3b84
2836 WITH cte1 AS MATERIALIZED (SELECT * FROM t1 WHERE f_leak(b)) SELECT * FROM cte1;
2838 -------------------------------------------------
2842 Filter: (((a % 2) = 0) AND f_leak(b))
2845 WITH cte1 AS (UPDATE t1 SET a = a + 1 RETURNING *) SELECT * FROM cte1; --fail
2846 ERROR: new row violates row-level security policy for table "t1"
2847 WITH cte1 AS (UPDATE t1 SET a = a RETURNING *) SELECT * FROM cte1; --ok
2849 ----+----------------------------------
2850 0 | cfcd208495d565ef66e7dff9f98764da
2851 2 | c81e728d9d4c2f636f067f89cc14862c
2852 4 | a87ff679a2f3e71d9181a67b7542122c
2853 6 | 1679091c5a880faf6fb5e6087eb1b2dc
2854 8 | c9f0f895fb98ab9159f51fd0297e236d
2855 10 | d3d9446802a44259755d38e6d163e820
2856 12 | c20ad4d76fe97759aa27a0c99bff6710
2857 14 | aab3238922bcc25a6f606eb525ffdc56
2858 16 | c74d97b01eae257e44aa9d5bade97baf
2859 18 | 6f4922f45568161a8cdf4ad2299f6d23
2860 20 | 98f13708210194c475687be6106a3b84
2863 WITH cte1 AS (INSERT INTO t1 VALUES (21, 'Fail') RETURNING *) SELECT * FROM cte1; --fail
2864 ERROR: new row violates row-level security policy for table "t1"
2865 WITH cte1 AS (INSERT INTO t1 VALUES (20, 'Success') RETURNING *) SELECT * FROM cte1; --ok
2874 RESET SESSION AUTHORIZATION;
2875 ALTER POLICY p1 ON t1 RENAME TO p1; --fail
2876 ERROR: policy "p1" for table "t1" already exists
2877 SELECT polname, relname
2879 JOIN pg_class pc ON (pc.oid = pol.polrelid)
2880 WHERE relname = 't1';
2886 ALTER POLICY p1 ON t1 RENAME TO p2; --ok
2887 SELECT polname, relname
2889 JOIN pg_class pc ON (pc.oid = pol.polrelid)
2890 WHERE relname = 't1';
2897 -- Check INSERT SELECT
2899 SET SESSION AUTHORIZATION regress_rls_bob;
2900 CREATE TABLE t2 (a integer, b text);
2901 INSERT INTO t2 (SELECT * FROM t1);
2902 EXPLAIN (COSTS OFF) INSERT INTO t2 (SELECT * FROM t1);
2904 -------------------------------
2907 Filter: ((a % 2) = 0)
2912 ----+----------------------------------
2913 0 | cfcd208495d565ef66e7dff9f98764da
2914 2 | c81e728d9d4c2f636f067f89cc14862c
2915 4 | a87ff679a2f3e71d9181a67b7542122c
2916 6 | 1679091c5a880faf6fb5e6087eb1b2dc
2917 8 | c9f0f895fb98ab9159f51fd0297e236d
2918 10 | d3d9446802a44259755d38e6d163e820
2919 12 | c20ad4d76fe97759aa27a0c99bff6710
2920 14 | aab3238922bcc25a6f606eb525ffdc56
2921 16 | c74d97b01eae257e44aa9d5bade97baf
2922 18 | 6f4922f45568161a8cdf4ad2299f6d23
2923 20 | 98f13708210194c475687be6106a3b84
2927 EXPLAIN (COSTS OFF) SELECT * FROM t2;
2933 CREATE TABLE t3 AS SELECT * FROM t1;
2936 ----+----------------------------------
2937 0 | cfcd208495d565ef66e7dff9f98764da
2938 2 | c81e728d9d4c2f636f067f89cc14862c
2939 4 | a87ff679a2f3e71d9181a67b7542122c
2940 6 | 1679091c5a880faf6fb5e6087eb1b2dc
2941 8 | c9f0f895fb98ab9159f51fd0297e236d
2942 10 | d3d9446802a44259755d38e6d163e820
2943 12 | c20ad4d76fe97759aa27a0c99bff6710
2944 14 | aab3238922bcc25a6f606eb525ffdc56
2945 16 | c74d97b01eae257e44aa9d5bade97baf
2946 18 | 6f4922f45568161a8cdf4ad2299f6d23
2947 20 | 98f13708210194c475687be6106a3b84
2951 SELECT * INTO t4 FROM t1;
2954 ----+----------------------------------
2955 0 | cfcd208495d565ef66e7dff9f98764da
2956 2 | c81e728d9d4c2f636f067f89cc14862c
2957 4 | a87ff679a2f3e71d9181a67b7542122c
2958 6 | 1679091c5a880faf6fb5e6087eb1b2dc
2959 8 | c9f0f895fb98ab9159f51fd0297e236d
2960 10 | d3d9446802a44259755d38e6d163e820
2961 12 | c20ad4d76fe97759aa27a0c99bff6710
2962 14 | aab3238922bcc25a6f606eb525ffdc56
2963 16 | c74d97b01eae257e44aa9d5bade97baf
2964 18 | 6f4922f45568161a8cdf4ad2299f6d23
2965 20 | 98f13708210194c475687be6106a3b84
2972 SET SESSION AUTHORIZATION regress_rls_alice;
2973 CREATE TABLE blog (id integer, author text, post text);
2974 CREATE TABLE comment (blog_id integer, message text);
2975 GRANT ALL ON blog, comment TO regress_rls_bob;
2976 CREATE POLICY blog_1 ON blog USING (id % 2 = 0);
2977 ALTER TABLE blog ENABLE ROW LEVEL SECURITY;
2978 INSERT INTO blog VALUES
2979 (1, 'alice', 'blog #1'),
2980 (2, 'bob', 'blog #1'),
2981 (3, 'alice', 'blog #2'),
2982 (4, 'alice', 'blog #3'),
2983 (5, 'john', 'blog #1');
2984 INSERT INTO comment VALUES
2991 SET SESSION AUTHORIZATION regress_rls_bob;
2992 -- Check RLS JOIN with Non-RLS.
2993 SELECT id, author, message FROM blog JOIN comment ON id = blog_id;
2994 id | author | message
2995 ----+--------+-------------
2997 2 | bob | who did it?
3000 -- Check Non-RLS JOIN with RLS.
3001 SELECT id, author, message FROM comment JOIN blog ON id = blog_id;
3002 id | author | message
3003 ----+--------+-------------
3005 2 | bob | who did it?
3008 SET SESSION AUTHORIZATION regress_rls_alice;
3009 CREATE POLICY comment_1 ON comment USING (blog_id < 4);
3010 ALTER TABLE comment ENABLE ROW LEVEL SECURITY;
3011 SET SESSION AUTHORIZATION regress_rls_bob;
3012 -- Check RLS JOIN RLS
3013 SELECT id, author, message FROM blog JOIN comment ON id = blog_id;
3014 id | author | message
3015 ----+--------+-------------
3016 2 | bob | who did it?
3019 SELECT id, author, message FROM comment JOIN blog ON id = blog_id;
3020 id | author | message
3021 ----+--------+-------------
3022 2 | bob | who did it?
3025 SET SESSION AUTHORIZATION regress_rls_alice;
3026 DROP TABLE blog, comment;
3028 -- Default Deny Policy
3030 RESET SESSION AUTHORIZATION;
3031 DROP POLICY p2 ON t1;
3032 ALTER TABLE t1 OWNER TO regress_rls_alice;
3033 -- Check that default deny does not apply to superuser.
3034 RESET SESSION AUTHORIZATION;
3037 ----+----------------------------------
3038 1 | c4ca4238a0b923820dcc509a6f75849b
3039 3 | eccbc87e4b5ce2fe28308fd9f2a7baf3
3040 5 | e4da3b7fbbce2345d7772b0674a318d5
3041 7 | 8f14e45fceea167a5a36dedd4bea2543
3042 9 | 45c48cce2e2d7fbdea1afc51c7c6ad26
3043 11 | 6512bd43d9caa6e02c990b0a82652dca
3044 13 | c51ce410c124a10e0db5e4b97fc2af39
3045 15 | 9bf31c7ff062936a96d3c8bd1f8f2ff3
3046 17 | 70efdf2ec9b086079795c442636b55fb
3047 19 | 1f0e3dad99908345f7439f8ffabdffc4
3048 0 | cfcd208495d565ef66e7dff9f98764da
3049 2 | c81e728d9d4c2f636f067f89cc14862c
3050 4 | a87ff679a2f3e71d9181a67b7542122c
3051 6 | 1679091c5a880faf6fb5e6087eb1b2dc
3052 8 | c9f0f895fb98ab9159f51fd0297e236d
3053 10 | d3d9446802a44259755d38e6d163e820
3054 12 | c20ad4d76fe97759aa27a0c99bff6710
3055 14 | aab3238922bcc25a6f606eb525ffdc56
3056 16 | c74d97b01eae257e44aa9d5bade97baf
3057 18 | 6f4922f45568161a8cdf4ad2299f6d23
3058 20 | 98f13708210194c475687be6106a3b84
3062 EXPLAIN (COSTS OFF) SELECT * FROM t1;
3068 -- Check that default deny does not apply to table owner.
3069 SET SESSION AUTHORIZATION regress_rls_alice;
3072 ----+----------------------------------
3073 1 | c4ca4238a0b923820dcc509a6f75849b
3074 3 | eccbc87e4b5ce2fe28308fd9f2a7baf3
3075 5 | e4da3b7fbbce2345d7772b0674a318d5
3076 7 | 8f14e45fceea167a5a36dedd4bea2543
3077 9 | 45c48cce2e2d7fbdea1afc51c7c6ad26
3078 11 | 6512bd43d9caa6e02c990b0a82652dca
3079 13 | c51ce410c124a10e0db5e4b97fc2af39
3080 15 | 9bf31c7ff062936a96d3c8bd1f8f2ff3
3081 17 | 70efdf2ec9b086079795c442636b55fb
3082 19 | 1f0e3dad99908345f7439f8ffabdffc4
3083 0 | cfcd208495d565ef66e7dff9f98764da
3084 2 | c81e728d9d4c2f636f067f89cc14862c
3085 4 | a87ff679a2f3e71d9181a67b7542122c
3086 6 | 1679091c5a880faf6fb5e6087eb1b2dc
3087 8 | c9f0f895fb98ab9159f51fd0297e236d
3088 10 | d3d9446802a44259755d38e6d163e820
3089 12 | c20ad4d76fe97759aa27a0c99bff6710
3090 14 | aab3238922bcc25a6f606eb525ffdc56
3091 16 | c74d97b01eae257e44aa9d5bade97baf
3092 18 | 6f4922f45568161a8cdf4ad2299f6d23
3093 20 | 98f13708210194c475687be6106a3b84
3097 EXPLAIN (COSTS OFF) SELECT * FROM t1;
3103 -- Check that default deny applies to non-owner/non-superuser when RLS on.
3104 SET SESSION AUTHORIZATION regress_rls_bob;
3105 SET row_security TO ON;
3111 EXPLAIN (COSTS OFF) SELECT * FROM t1;
3113 --------------------------
3115 One-Time Filter: false
3118 SET SESSION AUTHORIZATION regress_rls_bob;
3124 EXPLAIN (COSTS OFF) SELECT * FROM t1;
3126 --------------------------
3128 One-Time Filter: false
3134 RESET SESSION AUTHORIZATION;
3135 DROP TABLE copy_t CASCADE;
3136 ERROR: table "copy_t" does not exist
3137 CREATE TABLE copy_t (a integer, b text);
3138 CREATE POLICY p1 ON copy_t USING (a % 2 = 0);
3139 ALTER TABLE copy_t ENABLE ROW LEVEL SECURITY;
3140 GRANT ALL ON copy_t TO regress_rls_bob, regress_rls_exempt_user;
3141 INSERT INTO copy_t (SELECT x, md5(x::text) FROM generate_series(0,10) x);
3142 -- Check COPY TO as Superuser/owner.
3143 RESET SESSION AUTHORIZATION;
3144 SET row_security TO OFF;
3145 COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ',';
3146 0,cfcd208495d565ef66e7dff9f98764da
3147 1,c4ca4238a0b923820dcc509a6f75849b
3148 2,c81e728d9d4c2f636f067f89cc14862c
3149 3,eccbc87e4b5ce2fe28308fd9f2a7baf3
3150 4,a87ff679a2f3e71d9181a67b7542122c
3151 5,e4da3b7fbbce2345d7772b0674a318d5
3152 6,1679091c5a880faf6fb5e6087eb1b2dc
3153 7,8f14e45fceea167a5a36dedd4bea2543
3154 8,c9f0f895fb98ab9159f51fd0297e236d
3155 9,45c48cce2e2d7fbdea1afc51c7c6ad26
3156 10,d3d9446802a44259755d38e6d163e820
3157 SET row_security TO ON;
3158 COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ',';
3159 0,cfcd208495d565ef66e7dff9f98764da
3160 1,c4ca4238a0b923820dcc509a6f75849b
3161 2,c81e728d9d4c2f636f067f89cc14862c
3162 3,eccbc87e4b5ce2fe28308fd9f2a7baf3
3163 4,a87ff679a2f3e71d9181a67b7542122c
3164 5,e4da3b7fbbce2345d7772b0674a318d5
3165 6,1679091c5a880faf6fb5e6087eb1b2dc
3166 7,8f14e45fceea167a5a36dedd4bea2543
3167 8,c9f0f895fb98ab9159f51fd0297e236d
3168 9,45c48cce2e2d7fbdea1afc51c7c6ad26
3169 10,d3d9446802a44259755d38e6d163e820
3170 -- Check COPY TO as user with permissions.
3171 SET SESSION AUTHORIZATION regress_rls_bob;
3172 SET row_security TO OFF;
3173 COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; --fail - would be affected by RLS
3174 ERROR: query would be affected by row-level security policy for table "copy_t"
3175 SET row_security TO ON;
3176 COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; --ok
3177 0,cfcd208495d565ef66e7dff9f98764da
3178 2,c81e728d9d4c2f636f067f89cc14862c
3179 4,a87ff679a2f3e71d9181a67b7542122c
3180 6,1679091c5a880faf6fb5e6087eb1b2dc
3181 8,c9f0f895fb98ab9159f51fd0297e236d
3182 10,d3d9446802a44259755d38e6d163e820
3183 -- Check COPY TO as user with permissions and BYPASSRLS
3184 SET SESSION AUTHORIZATION regress_rls_exempt_user;
3185 SET row_security TO OFF;
3186 COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; --ok
3187 0,cfcd208495d565ef66e7dff9f98764da
3188 1,c4ca4238a0b923820dcc509a6f75849b
3189 2,c81e728d9d4c2f636f067f89cc14862c
3190 3,eccbc87e4b5ce2fe28308fd9f2a7baf3
3191 4,a87ff679a2f3e71d9181a67b7542122c
3192 5,e4da3b7fbbce2345d7772b0674a318d5
3193 6,1679091c5a880faf6fb5e6087eb1b2dc
3194 7,8f14e45fceea167a5a36dedd4bea2543
3195 8,c9f0f895fb98ab9159f51fd0297e236d
3196 9,45c48cce2e2d7fbdea1afc51c7c6ad26
3197 10,d3d9446802a44259755d38e6d163e820
3198 SET row_security TO ON;
3199 COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; --ok
3200 0,cfcd208495d565ef66e7dff9f98764da
3201 1,c4ca4238a0b923820dcc509a6f75849b
3202 2,c81e728d9d4c2f636f067f89cc14862c
3203 3,eccbc87e4b5ce2fe28308fd9f2a7baf3
3204 4,a87ff679a2f3e71d9181a67b7542122c
3205 5,e4da3b7fbbce2345d7772b0674a318d5
3206 6,1679091c5a880faf6fb5e6087eb1b2dc
3207 7,8f14e45fceea167a5a36dedd4bea2543
3208 8,c9f0f895fb98ab9159f51fd0297e236d
3209 9,45c48cce2e2d7fbdea1afc51c7c6ad26
3210 10,d3d9446802a44259755d38e6d163e820
3211 -- Check COPY TO as user without permissions. SET row_security TO OFF;
3212 SET SESSION AUTHORIZATION regress_rls_carol;
3213 SET row_security TO OFF;
3214 COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; --fail - would be affected by RLS
3215 ERROR: query would be affected by row-level security policy for table "copy_t"
3216 SET row_security TO ON;
3217 COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; --fail - permission denied
3218 ERROR: permission denied for table copy_t
3219 -- Check COPY relation TO; keep it just one row to avoid reordering issues
3220 RESET SESSION AUTHORIZATION;
3221 SET row_security TO ON;
3222 CREATE TABLE copy_rel_to (a integer, b text);
3223 CREATE POLICY p1 ON copy_rel_to USING (a % 2 = 0);
3224 ALTER TABLE copy_rel_to ENABLE ROW LEVEL SECURITY;
3225 GRANT ALL ON copy_rel_to TO regress_rls_bob, regress_rls_exempt_user;
3226 INSERT INTO copy_rel_to VALUES (1, md5('1'));
3227 -- Check COPY TO as Superuser/owner.
3228 RESET SESSION AUTHORIZATION;
3229 SET row_security TO OFF;
3230 COPY copy_rel_to TO STDOUT WITH DELIMITER ',';
3231 1,c4ca4238a0b923820dcc509a6f75849b
3232 SET row_security TO ON;
3233 COPY copy_rel_to TO STDOUT WITH DELIMITER ',';
3234 1,c4ca4238a0b923820dcc509a6f75849b
3235 -- Check COPY TO as user with permissions.
3236 SET SESSION AUTHORIZATION regress_rls_bob;
3237 SET row_security TO OFF;
3238 COPY copy_rel_to TO STDOUT WITH DELIMITER ','; --fail - would be affected by RLS
3239 ERROR: query would be affected by row-level security policy for table "copy_rel_to"
3240 SET row_security TO ON;
3241 COPY copy_rel_to TO STDOUT WITH DELIMITER ','; --ok
3242 -- Check COPY TO as user with permissions and BYPASSRLS
3243 SET SESSION AUTHORIZATION regress_rls_exempt_user;
3244 SET row_security TO OFF;
3245 COPY copy_rel_to TO STDOUT WITH DELIMITER ','; --ok
3246 1,c4ca4238a0b923820dcc509a6f75849b
3247 SET row_security TO ON;
3248 COPY copy_rel_to TO STDOUT WITH DELIMITER ','; --ok
3249 1,c4ca4238a0b923820dcc509a6f75849b
3250 -- Check COPY TO as user without permissions. SET row_security TO OFF;
3251 SET SESSION AUTHORIZATION regress_rls_carol;
3252 SET row_security TO OFF;
3253 COPY copy_rel_to TO STDOUT WITH DELIMITER ','; --fail - permission denied
3254 ERROR: permission denied for table copy_rel_to
3255 SET row_security TO ON;
3256 COPY copy_rel_to TO STDOUT WITH DELIMITER ','; --fail - permission denied
3257 ERROR: permission denied for table copy_rel_to
3258 -- Check COPY FROM as Superuser/owner.
3259 RESET SESSION AUTHORIZATION;
3260 SET row_security TO OFF;
3261 COPY copy_t FROM STDIN; --ok
3262 SET row_security TO ON;
3263 COPY copy_t FROM STDIN; --ok
3264 -- Check COPY FROM as user with permissions.
3265 SET SESSION AUTHORIZATION regress_rls_bob;
3266 SET row_security TO OFF;
3267 COPY copy_t FROM STDIN; --fail - would be affected by RLS.
3268 ERROR: query would be affected by row-level security policy for table "copy_t"
3269 SET row_security TO ON;
3270 COPY copy_t FROM STDIN; --fail - COPY FROM not supported by RLS.
3271 ERROR: COPY FROM not supported with row-level security
3272 HINT: Use INSERT statements instead.
3273 -- Check COPY FROM as user with permissions and BYPASSRLS
3274 SET SESSION AUTHORIZATION regress_rls_exempt_user;
3275 SET row_security TO ON;
3276 COPY copy_t FROM STDIN; --ok
3277 -- Check COPY FROM as user without permissions.
3278 SET SESSION AUTHORIZATION regress_rls_carol;
3279 SET row_security TO OFF;
3280 COPY copy_t FROM STDIN; --fail - permission denied.
3281 ERROR: permission denied for table copy_t
3282 SET row_security TO ON;
3283 COPY copy_t FROM STDIN; --fail - permission denied.
3284 ERROR: permission denied for table copy_t
3285 RESET SESSION AUTHORIZATION;
3287 DROP TABLE copy_rel_to CASCADE;
3288 -- Check WHERE CURRENT OF
3289 SET SESSION AUTHORIZATION regress_rls_alice;
3290 CREATE TABLE current_check (currentid int, payload text, rlsuser text);
3291 GRANT ALL ON current_check TO PUBLIC;
3292 INSERT INTO current_check VALUES
3293 (1, 'abc', 'regress_rls_bob'),
3294 (2, 'bcd', 'regress_rls_bob'),
3295 (3, 'cde', 'regress_rls_bob'),
3296 (4, 'def', 'regress_rls_bob');
3297 CREATE POLICY p1 ON current_check FOR SELECT USING (currentid % 2 = 0);
3298 CREATE POLICY p2 ON current_check FOR DELETE USING (currentid = 4 AND rlsuser = current_user);
3299 CREATE POLICY p3 ON current_check FOR UPDATE USING (currentid = 4) WITH CHECK (rlsuser = current_user);
3300 ALTER TABLE current_check ENABLE ROW LEVEL SECURITY;
3301 SET SESSION AUTHORIZATION regress_rls_bob;
3302 -- Can SELECT even rows
3303 SELECT * FROM current_check;
3304 currentid | payload | rlsuser
3305 -----------+---------+-----------------
3306 2 | bcd | regress_rls_bob
3307 4 | def | regress_rls_bob
3310 -- Cannot UPDATE row 2
3311 UPDATE current_check SET payload = payload || '_new' WHERE currentid = 2 RETURNING *;
3312 currentid | payload | rlsuser
3313 -----------+---------+---------
3317 DECLARE current_check_cursor SCROLL CURSOR FOR SELECT * FROM current_check;
3318 -- Returns rows that can be seen according to SELECT policy, like plain SELECT
3319 -- above (even rows)
3320 FETCH ABSOLUTE 1 FROM current_check_cursor;
3321 currentid | payload | rlsuser
3322 -----------+---------+-----------------
3323 2 | bcd | regress_rls_bob
3326 -- Still cannot UPDATE row 2 through cursor
3327 UPDATE current_check SET payload = payload || '_new' WHERE CURRENT OF current_check_cursor RETURNING *;
3328 currentid | payload | rlsuser
3329 -----------+---------+---------
3332 -- Can update row 4 through cursor, which is the next visible row
3333 FETCH RELATIVE 1 FROM current_check_cursor;
3334 currentid | payload | rlsuser
3335 -----------+---------+-----------------
3336 4 | def | regress_rls_bob
3339 UPDATE current_check SET payload = payload || '_new' WHERE CURRENT OF current_check_cursor RETURNING *;
3340 currentid | payload | rlsuser
3341 -----------+---------+-----------------
3342 4 | def_new | regress_rls_bob
3345 SELECT * FROM current_check;
3346 currentid | payload | rlsuser
3347 -----------+---------+-----------------
3348 2 | bcd | regress_rls_bob
3349 4 | def_new | regress_rls_bob
3352 -- Plan should be a subquery TID scan
3353 EXPLAIN (COSTS OFF) UPDATE current_check SET payload = payload WHERE CURRENT OF current_check_cursor;
3355 -------------------------------------------------------------
3356 Update on current_check
3357 -> Tid Scan on current_check
3358 TID Cond: CURRENT OF current_check_cursor
3359 Filter: ((currentid = 4) AND ((currentid % 2) = 0))
3362 -- Similarly can only delete row 4
3363 FETCH ABSOLUTE 1 FROM current_check_cursor;
3364 currentid | payload | rlsuser
3365 -----------+---------+-----------------
3366 2 | bcd | regress_rls_bob
3369 DELETE FROM current_check WHERE CURRENT OF current_check_cursor RETURNING *;
3370 currentid | payload | rlsuser
3371 -----------+---------+---------
3374 FETCH RELATIVE 1 FROM current_check_cursor;
3375 currentid | payload | rlsuser
3376 -----------+---------+-----------------
3377 4 | def | regress_rls_bob
3380 DELETE FROM current_check WHERE CURRENT OF current_check_cursor RETURNING *;
3381 currentid | payload | rlsuser
3382 -----------+---------+-----------------
3383 4 | def_new | regress_rls_bob
3386 SELECT * FROM current_check;
3387 currentid | payload | rlsuser
3388 -----------+---------+-----------------
3389 2 | bcd | regress_rls_bob
3394 -- check pg_stats view filtering
3396 SET row_security TO ON;
3397 SET SESSION AUTHORIZATION regress_rls_alice;
3398 ANALYZE current_check;
3400 SELECT row_security_active('current_check');
3402 ---------------------
3406 SELECT attname, most_common_vals FROM pg_stats
3407 WHERE tablename = 'current_check'
3409 attname | most_common_vals
3410 -----------+-------------------
3413 rlsuser | {regress_rls_bob}
3416 SET SESSION AUTHORIZATION regress_rls_bob;
3417 -- Stats not visible
3418 SELECT row_security_active('current_check');
3420 ---------------------
3424 SELECT attname, most_common_vals FROM pg_stats
3425 WHERE tablename = 'current_check'
3427 attname | most_common_vals
3428 ---------+------------------
3432 -- Collation support
3435 CREATE TABLE coll_t (c) AS VALUES ('bar'::text);
3436 CREATE POLICY coll_p ON coll_t USING (c < ('foo'::text COLLATE "C"));
3437 ALTER TABLE coll_t ENABLE ROW LEVEL SECURITY;
3438 GRANT SELECT ON coll_t TO regress_rls_alice;
3439 SELECT (string_to_array(polqual, ':'))[7] AS inputcollid FROM pg_policy WHERE polrelid = 'coll_t'::regclass;
3445 SET SESSION AUTHORIZATION regress_rls_alice;
3446 SELECT * FROM coll_t;
3454 -- Shared Object Dependencies
3456 RESET SESSION AUTHORIZATION;
3458 CREATE ROLE regress_rls_eve;
3459 CREATE ROLE regress_rls_frank;
3460 CREATE TABLE tbl1 (c) AS VALUES ('bar'::text);
3461 GRANT SELECT ON TABLE tbl1 TO regress_rls_eve;
3462 CREATE POLICY P ON tbl1 TO regress_rls_eve, regress_rls_frank USING (true);
3463 SELECT refclassid::regclass, deptype
3465 WHERE classid = 'pg_policy'::regclass
3466 AND refobjid = 'tbl1'::regclass;
3467 refclassid | deptype
3468 ------------+---------
3472 SELECT refclassid::regclass, deptype
3474 WHERE classid = 'pg_policy'::regclass
3475 AND refobjid IN ('regress_rls_eve'::regrole, 'regress_rls_frank'::regrole);
3476 refclassid | deptype
3477 ------------+---------
3483 DROP ROLE regress_rls_eve; --fails due to dependency on POLICY p
3484 ERROR: role "regress_rls_eve" cannot be dropped because some objects depend on it
3485 DETAIL: privileges for table tbl1
3486 target of policy p on table tbl1
3488 ALTER POLICY p ON tbl1 TO regress_rls_frank USING (true);
3490 DROP ROLE regress_rls_eve; --fails due to dependency on GRANT SELECT
3491 ERROR: role "regress_rls_eve" cannot be dropped because some objects depend on it
3492 DETAIL: privileges for table tbl1
3494 REVOKE ALL ON TABLE tbl1 FROM regress_rls_eve;
3496 DROP ROLE regress_rls_eve; --succeeds
3499 DROP ROLE regress_rls_frank; --fails due to dependency on POLICY p
3500 ERROR: role "regress_rls_frank" cannot be dropped because some objects depend on it
3501 DETAIL: target of policy p on table tbl1
3503 DROP POLICY p ON tbl1;
3505 DROP ROLE regress_rls_frank; -- succeeds
3507 ROLLBACK; -- cleanup
3509 -- Converting table to view
3512 CREATE TABLE t (c int);
3513 CREATE POLICY p ON t USING (c % 2 = 1);
3514 ALTER TABLE t ENABLE ROW LEVEL SECURITY;
3516 CREATE RULE "_RETURN" AS ON SELECT TO t DO INSTEAD
3517 SELECT * FROM generate_series(1,5) t0(c); -- fails due to row-level security enabled
3518 ERROR: could not convert table "t" to a view because it has row security enabled
3520 ALTER TABLE t DISABLE ROW LEVEL SECURITY;
3522 CREATE RULE "_RETURN" AS ON SELECT TO t DO INSTEAD
3523 SELECT * FROM generate_series(1,5) t0(c); -- fails due to policy p on t
3524 ERROR: could not convert table "t" to a view because it has row security policies
3527 CREATE RULE "_RETURN" AS ON SELECT TO t DO INSTEAD
3528 SELECT * FROM generate_series(1,5) t0(c); -- succeeds
3531 -- Policy expression handling
3534 CREATE TABLE t (c) AS VALUES ('bar'::text);
3535 CREATE POLICY p ON t USING (max(c)); -- fails: aggregate functions are not allowed in policy expressions
3536 ERROR: aggregate functions are not allowed in policy expressions
3539 -- Non-target relations are only subject to SELECT policies
3541 SET SESSION AUTHORIZATION regress_rls_alice;
3542 CREATE TABLE r1 (a int);
3543 CREATE TABLE r2 (a int);
3544 INSERT INTO r1 VALUES (10), (20);
3545 INSERT INTO r2 VALUES (10), (20);
3546 GRANT ALL ON r1, r2 TO regress_rls_bob;
3547 CREATE POLICY p1 ON r1 USING (true);
3548 ALTER TABLE r1 ENABLE ROW LEVEL SECURITY;
3549 CREATE POLICY p1 ON r2 FOR SELECT USING (true);
3550 CREATE POLICY p2 ON r2 FOR INSERT WITH CHECK (false);
3551 CREATE POLICY p3 ON r2 FOR UPDATE USING (false);
3552 CREATE POLICY p4 ON r2 FOR DELETE USING (false);
3553 ALTER TABLE r2 ENABLE ROW LEVEL SECURITY;
3554 SET SESSION AUTHORIZATION regress_rls_bob;
3570 INSERT INTO r2 VALUES (2); -- Not allowed
3571 ERROR: new row violates row-level security policy for table "r2"
3572 UPDATE r2 SET a = 2 RETURNING *; -- Updates nothing
3577 DELETE FROM r2 RETURNING *; -- Deletes nothing
3582 -- r2 can be used as a non-target relation in DML
3583 INSERT INTO r1 SELECT a + 1 FROM r2 RETURNING *; -- OK
3590 UPDATE r1 SET a = r2.a + 2 FROM r2 WHERE r1.a = r2.a RETURNING *; -- OK
3597 DELETE FROM r1 USING r2 WHERE r1.a = r2.a + 2 RETURNING *; -- OK
3618 SET SESSION AUTHORIZATION regress_rls_alice;
3622 -- FORCE ROW LEVEL SECURITY applies RLS to owners too
3624 SET SESSION AUTHORIZATION regress_rls_alice;
3625 SET row_security = on;
3626 CREATE TABLE r1 (a int);
3627 INSERT INTO r1 VALUES (10), (20);
3628 CREATE POLICY p1 ON r1 USING (false);
3629 ALTER TABLE r1 ENABLE ROW LEVEL SECURITY;
3630 ALTER TABLE r1 FORCE ROW LEVEL SECURITY;
3631 -- No error, but no rows
3638 INSERT INTO r1 VALUES (1);
3639 ERROR: new row violates row-level security policy for table "r1"
3640 -- No error (unable to see any rows to update)
3641 UPDATE r1 SET a = 1;
3647 -- No error (unable to see any rows to delete)
3654 SET row_security = off;
3655 -- these all fail, would be affected by RLS
3657 ERROR: query would be affected by row-level security policy for table "r1"
3658 HINT: To disable the policy for the table's owner, use ALTER TABLE NO FORCE ROW LEVEL SECURITY.
3659 UPDATE r1 SET a = 1;
3660 ERROR: query would be affected by row-level security policy for table "r1"
3661 HINT: To disable the policy for the table's owner, use ALTER TABLE NO FORCE ROW LEVEL SECURITY.
3663 ERROR: query would be affected by row-level security policy for table "r1"
3664 HINT: To disable the policy for the table's owner, use ALTER TABLE NO FORCE ROW LEVEL SECURITY.
3667 -- FORCE ROW LEVEL SECURITY does not break RI
3669 SET SESSION AUTHORIZATION regress_rls_alice;
3670 SET row_security = on;
3671 CREATE TABLE r1 (a int PRIMARY KEY);
3672 CREATE TABLE r2 (a int REFERENCES r1);
3673 INSERT INTO r1 VALUES (10), (20);
3674 INSERT INTO r2 VALUES (10), (20);
3675 -- Create policies on r2 which prevent the
3676 -- owner from seeing any rows, but RI should
3678 CREATE POLICY p1 ON r2 USING (false);
3679 ALTER TABLE r2 ENABLE ROW LEVEL SECURITY;
3680 ALTER TABLE r2 FORCE ROW LEVEL SECURITY;
3681 -- Errors due to rows in r2
3683 ERROR: update or delete on table "r1" violates foreign key constraint "r2_a_fkey" on table "r2"
3684 DETAIL: Key (a)=(10) is still referenced from table "r2".
3685 -- Reset r2 to no-RLS
3686 DROP POLICY p1 ON r2;
3687 ALTER TABLE r2 NO FORCE ROW LEVEL SECURITY;
3688 ALTER TABLE r2 DISABLE ROW LEVEL SECURITY;
3689 -- clean out r2 for INSERT test below
3691 -- Change r1 to not allow rows to be seen
3692 CREATE POLICY p1 ON r1 USING (false);
3693 ALTER TABLE r1 ENABLE ROW LEVEL SECURITY;
3694 ALTER TABLE r1 FORCE ROW LEVEL SECURITY;
3701 -- No error, RI still sees that row exists in r1
3702 INSERT INTO r2 VALUES (10);
3705 -- Ensure cascaded DELETE works
3706 CREATE TABLE r1 (a int PRIMARY KEY);
3707 CREATE TABLE r2 (a int REFERENCES r1 ON DELETE CASCADE);
3708 INSERT INTO r1 VALUES (10), (20);
3709 INSERT INTO r2 VALUES (10), (20);
3710 -- Create policies on r2 which prevent the
3711 -- owner from seeing any rows, but RI should
3713 CREATE POLICY p1 ON r2 USING (false);
3714 ALTER TABLE r2 ENABLE ROW LEVEL SECURITY;
3715 ALTER TABLE r2 FORCE ROW LEVEL SECURITY;
3716 -- Deletes all records from both
3718 -- Remove FORCE from r2
3719 ALTER TABLE r2 NO FORCE ROW LEVEL SECURITY;
3720 -- As owner, we now bypass RLS
3721 -- verify no rows in r2 now
3729 -- Ensure cascaded UPDATE works
3730 CREATE TABLE r1 (a int PRIMARY KEY);
3731 CREATE TABLE r2 (a int REFERENCES r1 ON UPDATE CASCADE);
3732 INSERT INTO r1 VALUES (10), (20);
3733 INSERT INTO r2 VALUES (10), (20);
3734 -- Create policies on r2 which prevent the
3735 -- owner from seeing any rows, but RI should
3737 CREATE POLICY p1 ON r2 USING (false);
3738 ALTER TABLE r2 ENABLE ROW LEVEL SECURITY;
3739 ALTER TABLE r2 FORCE ROW LEVEL SECURITY;
3740 -- Updates records in both
3741 UPDATE r1 SET a = a+5;
3742 -- Remove FORCE from r2
3743 ALTER TABLE r2 NO FORCE ROW LEVEL SECURITY;
3744 -- As owner, we now bypass RLS
3745 -- verify records in r2 updated
3756 -- Test INSERT+RETURNING applies SELECT policies as
3757 -- WithCheckOptions (meaning an error is thrown)
3759 SET SESSION AUTHORIZATION regress_rls_alice;
3760 SET row_security = on;
3761 CREATE TABLE r1 (a int);
3762 CREATE POLICY p1 ON r1 FOR SELECT USING (false);
3763 CREATE POLICY p2 ON r1 FOR INSERT WITH CHECK (true);
3764 ALTER TABLE r1 ENABLE ROW LEVEL SECURITY;
3765 ALTER TABLE r1 FORCE ROW LEVEL SECURITY;
3767 INSERT INTO r1 VALUES (10), (20);
3768 -- No error, but no rows
3774 SET row_security = off;
3775 -- fail, would be affected by RLS
3777 ERROR: query would be affected by row-level security policy for table "r1"
3778 HINT: To disable the policy for the table's owner, use ALTER TABLE NO FORCE ROW LEVEL SECURITY.
3779 SET row_security = on;
3781 INSERT INTO r1 VALUES (10), (20) RETURNING *;
3782 ERROR: new row violates row-level security policy for table "r1"
3785 -- Test UPDATE+RETURNING applies SELECT policies as
3786 -- WithCheckOptions (meaning an error is thrown)
3788 SET SESSION AUTHORIZATION regress_rls_alice;
3789 SET row_security = on;
3790 CREATE TABLE r1 (a int PRIMARY KEY);
3791 CREATE POLICY p1 ON r1 FOR SELECT USING (a < 20);
3792 CREATE POLICY p2 ON r1 FOR UPDATE USING (a < 20) WITH CHECK (true);
3793 CREATE POLICY p3 ON r1 FOR INSERT WITH CHECK (true);
3794 INSERT INTO r1 VALUES (10);
3795 ALTER TABLE r1 ENABLE ROW LEVEL SECURITY;
3796 ALTER TABLE r1 FORCE ROW LEVEL SECURITY;
3798 UPDATE r1 SET a = 30;
3799 -- Show updated rows
3800 ALTER TABLE r1 NO FORCE ROW LEVEL SECURITY;
3807 -- reset value in r1 for test with RETURNING
3808 UPDATE r1 SET a = 10;
3816 ALTER TABLE r1 FORCE ROW LEVEL SECURITY;
3818 UPDATE r1 SET a = 30 RETURNING *;
3819 ERROR: new row violates row-level security policy for table "r1"
3820 -- UPDATE path of INSERT ... ON CONFLICT DO UPDATE should also error out
3821 INSERT INTO r1 VALUES (10)
3822 ON CONFLICT (a) DO UPDATE SET a = 30 RETURNING *;
3823 ERROR: new row violates row-level security policy for table "r1"
3824 -- Should still error out without RETURNING (use of arbiter always requires
3825 -- SELECT permissions)
3826 INSERT INTO r1 VALUES (10)
3827 ON CONFLICT (a) DO UPDATE SET a = 30;
3828 ERROR: new row violates row-level security policy for table "r1"
3829 INSERT INTO r1 VALUES (10)
3830 ON CONFLICT ON CONSTRAINT r1_pkey DO UPDATE SET a = 30;
3831 ERROR: new row violates row-level security policy for table "r1"
3833 -- Check dependency handling
3834 RESET SESSION AUTHORIZATION;
3835 CREATE TABLE dep1 (c1 int);
3836 CREATE TABLE dep2 (c1 int);
3837 CREATE POLICY dep_p1 ON dep1 TO regress_rls_bob USING (c1 > (select max(dep2.c1) from dep2));
3838 ALTER POLICY dep_p1 ON dep1 TO regress_rls_bob,regress_rls_carol;
3839 -- Should return one
3840 SELECT count(*) = 1 FROM pg_depend
3841 WHERE objid = (SELECT oid FROM pg_policy WHERE polname = 'dep_p1')
3842 AND refobjid = (SELECT oid FROM pg_class WHERE relname = 'dep2');
3848 ALTER POLICY dep_p1 ON dep1 USING (true);
3849 -- Should return one
3850 SELECT count(*) = 1 FROM pg_shdepend
3851 WHERE objid = (SELECT oid FROM pg_policy WHERE polname = 'dep_p1')
3852 AND refobjid = (SELECT oid FROM pg_authid WHERE rolname = 'regress_rls_bob');
3858 -- Should return one
3859 SELECT count(*) = 1 FROM pg_shdepend
3860 WHERE objid = (SELECT oid FROM pg_policy WHERE polname = 'dep_p1')
3861 AND refobjid = (SELECT oid FROM pg_authid WHERE rolname = 'regress_rls_carol');
3867 -- Should return zero
3868 SELECT count(*) = 0 FROM pg_depend
3869 WHERE objid = (SELECT oid FROM pg_policy WHERE polname = 'dep_p1')
3870 AND refobjid = (SELECT oid FROM pg_class WHERE relname = 'dep2');
3876 -- DROP OWNED BY testing
3877 RESET SESSION AUTHORIZATION;
3878 CREATE ROLE regress_rls_dob_role1;
3879 CREATE ROLE regress_rls_dob_role2;
3880 CREATE TABLE dob_t1 (c1 int);
3881 CREATE TABLE dob_t2 (c1 int) PARTITION BY RANGE (c1);
3882 CREATE POLICY p1 ON dob_t1 TO regress_rls_dob_role1 USING (true);
3883 DROP OWNED BY regress_rls_dob_role1;
3884 DROP POLICY p1 ON dob_t1; -- should fail, already gone
3885 ERROR: policy "p1" for table "dob_t1" does not exist
3886 CREATE POLICY p1 ON dob_t1 TO regress_rls_dob_role1,regress_rls_dob_role2 USING (true);
3887 DROP OWNED BY regress_rls_dob_role1;
3888 DROP POLICY p1 ON dob_t1; -- should succeed
3889 -- same cases with duplicate polroles entries
3890 CREATE POLICY p1 ON dob_t1 TO regress_rls_dob_role1,regress_rls_dob_role1 USING (true);
3891 DROP OWNED BY regress_rls_dob_role1;
3892 DROP POLICY p1 ON dob_t1; -- should fail, already gone
3893 ERROR: policy "p1" for table "dob_t1" does not exist
3894 CREATE POLICY p1 ON dob_t1 TO regress_rls_dob_role1,regress_rls_dob_role1,regress_rls_dob_role2 USING (true);
3895 DROP OWNED BY regress_rls_dob_role1;
3896 DROP POLICY p1 ON dob_t1; -- should succeed
3897 -- partitioned target
3898 CREATE POLICY p1 ON dob_t2 TO regress_rls_dob_role1,regress_rls_dob_role2 USING (true);
3899 DROP OWNED BY regress_rls_dob_role1;
3900 DROP POLICY p1 ON dob_t2; -- should succeed
3901 DROP USER regress_rls_dob_role1;
3902 DROP USER regress_rls_dob_role2;
3903 -- Bug #15708: view + table with RLS should check policies as view owner
3904 CREATE TABLE ref_tbl (a int);
3905 INSERT INTO ref_tbl VALUES (1);
3906 CREATE TABLE rls_tbl (a int);
3907 INSERT INTO rls_tbl VALUES (10);
3908 ALTER TABLE rls_tbl ENABLE ROW LEVEL SECURITY;
3909 CREATE POLICY p1 ON rls_tbl USING (EXISTS (SELECT 1 FROM ref_tbl));
3910 GRANT SELECT ON ref_tbl TO regress_rls_bob;
3911 GRANT SELECT ON rls_tbl TO regress_rls_bob;
3912 CREATE VIEW rls_view AS SELECT * FROM rls_tbl;
3913 ALTER VIEW rls_view OWNER TO regress_rls_bob;
3914 GRANT SELECT ON rls_view TO regress_rls_alice;
3915 SET SESSION AUTHORIZATION regress_rls_alice;
3916 SELECT * FROM ref_tbl; -- Permission denied
3917 ERROR: permission denied for table ref_tbl
3918 SELECT * FROM rls_tbl; -- Permission denied
3919 ERROR: permission denied for table rls_tbl
3920 SELECT * FROM rls_view; -- OK
3926 RESET SESSION AUTHORIZATION;
3930 -- Leaky operator test
3931 CREATE TABLE rls_tbl (a int);
3932 INSERT INTO rls_tbl SELECT x/10 FROM generate_series(1, 100) x;
3934 ALTER TABLE rls_tbl ENABLE ROW LEVEL SECURITY;
3935 GRANT SELECT ON rls_tbl TO regress_rls_alice;
3936 SET SESSION AUTHORIZATION regress_rls_alice;
3937 CREATE FUNCTION op_leak(int, int) RETURNS bool
3938 AS 'BEGIN RAISE NOTICE ''op_leak => %, %'', $1, $2; RETURN $1 < $2; END'
3940 CREATE OPERATOR <<< (procedure = op_leak, leftarg = int, rightarg = int,
3941 restrict = scalarltsel);
3942 SELECT * FROM rls_tbl WHERE a <<< 1000;
3947 DROP OPERATOR <<< (int, int);
3948 DROP FUNCTION op_leak(int, int);
3949 RESET SESSION AUTHORIZATION;
3951 -- Bug #16006: whole-row Vars in a policy don't play nice with sub-selects
3952 SET SESSION AUTHORIZATION regress_rls_alice;
3953 CREATE TABLE rls_tbl (a int, b int, c int);
3954 CREATE POLICY p1 ON rls_tbl USING (rls_tbl >= ROW(1,1,1));
3955 ALTER TABLE rls_tbl ENABLE ROW LEVEL SECURITY;
3956 ALTER TABLE rls_tbl FORCE ROW LEVEL SECURITY;
3957 INSERT INTO rls_tbl SELECT 10, 20, 30;
3958 EXPLAIN (VERBOSE, COSTS OFF)
3960 SELECT * FROM (SELECT b, c FROM rls_tbl ORDER BY a) ss;
3962 --------------------------------------------------------------------
3963 Insert on regress_rls_schema.rls_tbl
3964 -> Subquery Scan on ss
3965 Output: ss.b, ss.c, NULL::integer
3967 Output: rls_tbl_1.b, rls_tbl_1.c, rls_tbl_1.a
3968 Sort Key: rls_tbl_1.a
3969 -> Seq Scan on regress_rls_schema.rls_tbl rls_tbl_1
3970 Output: rls_tbl_1.b, rls_tbl_1.c, rls_tbl_1.a
3971 Filter: (rls_tbl_1.* >= '(1,1,1)'::record)
3975 SELECT * FROM (SELECT b, c FROM rls_tbl ORDER BY a) ss;
3976 SELECT * FROM rls_tbl;
3984 RESET SESSION AUTHORIZATION;
3988 RESET SESSION AUTHORIZATION;
3989 DROP SCHEMA regress_rls_schema CASCADE;
3990 NOTICE: drop cascades to 29 other objects
3991 DETAIL: drop cascades to function f_leak(text)
3992 drop cascades to table uaccount
3993 drop cascades to table category
3994 drop cascades to table document
3995 drop cascades to table part_document
3996 drop cascades to table dependent
3997 drop cascades to table rec1
3998 drop cascades to table rec2
3999 drop cascades to view rec1v
4000 drop cascades to view rec2v
4001 drop cascades to table s1
4002 drop cascades to table s2
4003 drop cascades to view v2
4004 drop cascades to table b1
4005 drop cascades to view bv1
4006 drop cascades to table z1
4007 drop cascades to table z2
4008 drop cascades to table x1
4009 drop cascades to table y1
4010 drop cascades to table y2
4011 drop cascades to table t1
4012 drop cascades to table t2
4013 drop cascades to table t3
4014 drop cascades to table t4
4015 drop cascades to table current_check
4016 drop cascades to table dep1
4017 drop cascades to table dep2
4018 drop cascades to table dob_t1
4019 drop cascades to table dob_t2
4020 DROP USER regress_rls_alice;
4021 DROP USER regress_rls_bob;
4022 DROP USER regress_rls_carol;
4023 DROP USER regress_rls_dave;
4024 DROP USER regress_rls_exempt_user;
4025 DROP ROLE regress_rls_group1;
4026 DROP ROLE regress_rls_group2;
4027 -- Arrange to have a few policies left over, for testing
4028 -- pg_dump/pg_restore
4029 CREATE SCHEMA regress_rls_schema;
4030 CREATE TABLE rls_tbl (c1 int);
4031 ALTER TABLE rls_tbl ENABLE ROW LEVEL SECURITY;
4032 CREATE POLICY p1 ON rls_tbl USING (c1 > 5);
4033 CREATE POLICY p2 ON rls_tbl FOR SELECT USING (c1 <= 3);
4034 CREATE POLICY p3 ON rls_tbl FOR UPDATE USING (c1 <= 3) WITH CHECK (c1 > 5);
4035 CREATE POLICY p4 ON rls_tbl FOR DELETE USING (c1 <= 3);
4036 CREATE TABLE rls_tbl_force (c1 int);
4037 ALTER TABLE rls_tbl_force ENABLE ROW LEVEL SECURITY;
4038 ALTER TABLE rls_tbl_force FORCE ROW LEVEL SECURITY;
4039 CREATE POLICY p1 ON rls_tbl_force USING (c1 = 5) WITH CHECK (c1 < 5);
4040 CREATE POLICY p2 ON rls_tbl_force FOR SELECT USING (c1 = 8);
4041 CREATE POLICY p3 ON rls_tbl_force FOR UPDATE USING (c1 = 8) WITH CHECK (c1 >= 5);
4042 CREATE POLICY p4 ON rls_tbl_force FOR DELETE USING (c1 = 8);