Collect the global OR of hasRowSecurity flags for plancache

We carry around information about if a given query has row security or
not to allow the plancache to use that information to invalidate a
planned query in the event that the environment changes.

Previously, the flag of one of the subqueries was simply being copied
into place to indicate if the query overall included RLS components.
That's wrong as we need the global OR of all subqueries.  Fix by
changing the code to match how fireRIRules works, which is results
in OR'ing all of the flags.

Noted by Tom.

Back-patch to 9.5 where RLS was introduced.
This commit is contained in:
Stephen Frost 2015-12-14 20:05:43 -05:00
parent db81329eed
commit e5e11c8cca
4 changed files with 180 additions and 4 deletions

View File

@ -1529,7 +1529,8 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
* This may add new security barrier subquery RTEs to the rangetable.
*/
expand_security_quals(root, tlist);
root->glob->hasRowSecurity = parse->hasRowSecurity;
if (parse->hasRowSecurity)
root->glob->hasRowSecurity = true;
/*
* Locate any window functions in the tlist. (We don't need to look

View File

@ -2401,7 +2401,8 @@ extract_query_dependencies_walker(Node *node, PlannerInfo *context)
ListCell *lc;
/* Collect row security information */
context->glob->hasRowSecurity = query->hasRowSecurity;
if (query->hasRowSecurity)
context->glob->hasRowSecurity = true;
if (query->commandType == CMD_UTILITY)
{

View File

@ -1649,7 +1649,8 @@ ERROR: new row violates row-level security policy for table "document"
--
SET SESSION AUTHORIZATION rls_regress_user0;
CREATE TABLE z1 (a int, b text);
GRANT SELECT ON z1 TO rls_regress_group1, rls_regress_group2,
CREATE TABLE z2 (a int, b text);
GRANT SELECT ON z1,z2 TO rls_regress_group1, rls_regress_group2,
rls_regress_user1, rls_regress_user2;
INSERT INTO z1 VALUES
(1, 'aaa'),
@ -1678,6 +1679,46 @@ EXPLAIN (COSTS OFF) SELECT * FROM z1 WHERE f_leak(b);
Filter: ((a % 2) = 0)
(4 rows)
PREPARE plancache_test AS SELECT * FROM z1 WHERE f_leak(b);
EXPLAIN EXECUTE plancache_test;
QUERY PLAN
---------------------------------------------------------------
Subquery Scan on z1 (cost=0.00..29.11 rows=2 width=36)
Filter: f_leak(z1.b)
-> Seq Scan on z1 z1_1 (cost=0.00..29.05 rows=6 width=36)
Filter: ((a % 2) = 0)
(4 rows)
PREPARE plancache_test2 AS WITH q AS (SELECT * FROM z1 WHERE f_leak(b)) SELECT * FROM q,z2;
EXPLAIN EXECUTE plancache_test2;
QUERY PLAN
-----------------------------------------------------------------------
Nested Loop (cost=29.11..86.78 rows=2540 width=72)
CTE q
-> Subquery Scan on z1 (cost=0.00..29.11 rows=2 width=36)
Filter: f_leak(z1.b)
-> Seq Scan on z1 z1_1 (cost=0.00..29.05 rows=6 width=36)
Filter: ((a % 2) = 0)
-> CTE Scan on q (cost=0.00..0.04 rows=2 width=36)
-> Materialize (cost=0.00..29.05 rows=1270 width=36)
-> Seq Scan on z2 (cost=0.00..22.70 rows=1270 width=36)
(9 rows)
PREPARE plancache_test3 AS WITH q AS (SELECT * FROM z2) SELECT * FROM q,z1 WHERE f_leak(z1.b);
EXPLAIN EXECUTE plancache_test3;
QUERY PLAN
---------------------------------------------------------------------------
Nested Loop (cost=22.70..108.97 rows=2540 width=72)
CTE q
-> Seq Scan on z2 (cost=0.00..22.70 rows=1270 width=36)
-> CTE Scan on q (cost=0.00..25.40 rows=1270 width=36)
-> Materialize (cost=0.00..29.12 rows=2 width=36)
-> Subquery Scan on z1 (cost=0.00..29.11 rows=2 width=36)
Filter: f_leak(z1.b)
-> Seq Scan on z1 z1_1 (cost=0.00..29.05 rows=6 width=36)
Filter: ((a % 2) = 0)
(9 rows)
SET ROLE rls_regress_group1;
SELECT * FROM z1 WHERE f_leak(b);
NOTICE: f_leak => bbb
@ -1697,6 +1738,43 @@ EXPLAIN (COSTS OFF) SELECT * FROM z1 WHERE f_leak(b);
Filter: ((a % 2) = 0)
(4 rows)
EXPLAIN EXECUTE plancache_test;
QUERY PLAN
---------------------------------------------------------------
Subquery Scan on z1 (cost=0.00..29.11 rows=2 width=36)
Filter: f_leak(z1.b)
-> Seq Scan on z1 z1_1 (cost=0.00..29.05 rows=6 width=36)
Filter: ((a % 2) = 0)
(4 rows)
EXPLAIN EXECUTE plancache_test2;
QUERY PLAN
-----------------------------------------------------------------------
Nested Loop (cost=29.11..86.78 rows=2540 width=72)
CTE q
-> Subquery Scan on z1 (cost=0.00..29.11 rows=2 width=36)
Filter: f_leak(z1.b)
-> Seq Scan on z1 z1_1 (cost=0.00..29.05 rows=6 width=36)
Filter: ((a % 2) = 0)
-> CTE Scan on q (cost=0.00..0.04 rows=2 width=36)
-> Materialize (cost=0.00..29.05 rows=1270 width=36)
-> Seq Scan on z2 (cost=0.00..22.70 rows=1270 width=36)
(9 rows)
EXPLAIN EXECUTE plancache_test3;
QUERY PLAN
---------------------------------------------------------------------------
Nested Loop (cost=22.70..108.97 rows=2540 width=72)
CTE q
-> Seq Scan on z2 (cost=0.00..22.70 rows=1270 width=36)
-> CTE Scan on q (cost=0.00..25.40 rows=1270 width=36)
-> Materialize (cost=0.00..29.12 rows=2 width=36)
-> Subquery Scan on z1 (cost=0.00..29.11 rows=2 width=36)
Filter: f_leak(z1.b)
-> Seq Scan on z1 z1_1 (cost=0.00..29.05 rows=6 width=36)
Filter: ((a % 2) = 0)
(9 rows)
SET SESSION AUTHORIZATION rls_regress_user2;
SELECT * FROM z1 WHERE f_leak(b);
NOTICE: f_leak => aaa
@ -1716,6 +1794,43 @@ EXPLAIN (COSTS OFF) SELECT * FROM z1 WHERE f_leak(b);
Filter: ((a % 2) = 1)
(4 rows)
EXPLAIN EXECUTE plancache_test;
QUERY PLAN
---------------------------------------------------------------
Subquery Scan on z1 (cost=0.00..29.11 rows=2 width=36)
Filter: f_leak(z1.b)
-> Seq Scan on z1 z1_1 (cost=0.00..29.05 rows=6 width=36)
Filter: ((a % 2) = 1)
(4 rows)
EXPLAIN EXECUTE plancache_test2;
QUERY PLAN
-----------------------------------------------------------------------
Nested Loop (cost=29.11..86.78 rows=2540 width=72)
CTE q
-> Subquery Scan on z1 (cost=0.00..29.11 rows=2 width=36)
Filter: f_leak(z1.b)
-> Seq Scan on z1 z1_1 (cost=0.00..29.05 rows=6 width=36)
Filter: ((a % 2) = 1)
-> CTE Scan on q (cost=0.00..0.04 rows=2 width=36)
-> Materialize (cost=0.00..29.05 rows=1270 width=36)
-> Seq Scan on z2 (cost=0.00..22.70 rows=1270 width=36)
(9 rows)
EXPLAIN EXECUTE plancache_test3;
QUERY PLAN
---------------------------------------------------------------------------
Nested Loop (cost=22.70..108.97 rows=2540 width=72)
CTE q
-> Seq Scan on z2 (cost=0.00..22.70 rows=1270 width=36)
-> CTE Scan on q (cost=0.00..25.40 rows=1270 width=36)
-> Materialize (cost=0.00..29.12 rows=2 width=36)
-> Subquery Scan on z1 (cost=0.00..29.11 rows=2 width=36)
Filter: f_leak(z1.b)
-> Seq Scan on z1 z1_1 (cost=0.00..29.05 rows=6 width=36)
Filter: ((a % 2) = 1)
(9 rows)
SET ROLE rls_regress_group2;
SELECT * FROM z1 WHERE f_leak(b);
NOTICE: f_leak => aaa
@ -1735,6 +1850,43 @@ EXPLAIN (COSTS OFF) SELECT * FROM z1 WHERE f_leak(b);
Filter: ((a % 2) = 1)
(4 rows)
EXPLAIN EXECUTE plancache_test;
QUERY PLAN
---------------------------------------------------------------
Subquery Scan on z1 (cost=0.00..29.11 rows=2 width=36)
Filter: f_leak(z1.b)
-> Seq Scan on z1 z1_1 (cost=0.00..29.05 rows=6 width=36)
Filter: ((a % 2) = 1)
(4 rows)
EXPLAIN EXECUTE plancache_test2;
QUERY PLAN
-----------------------------------------------------------------------
Nested Loop (cost=29.11..86.78 rows=2540 width=72)
CTE q
-> Subquery Scan on z1 (cost=0.00..29.11 rows=2 width=36)
Filter: f_leak(z1.b)
-> Seq Scan on z1 z1_1 (cost=0.00..29.05 rows=6 width=36)
Filter: ((a % 2) = 1)
-> CTE Scan on q (cost=0.00..0.04 rows=2 width=36)
-> Materialize (cost=0.00..29.05 rows=1270 width=36)
-> Seq Scan on z2 (cost=0.00..22.70 rows=1270 width=36)
(9 rows)
EXPLAIN EXECUTE plancache_test3;
QUERY PLAN
---------------------------------------------------------------------------
Nested Loop (cost=22.70..108.97 rows=2540 width=72)
CTE q
-> Seq Scan on z2 (cost=0.00..22.70 rows=1270 width=36)
-> CTE Scan on q (cost=0.00..25.40 rows=1270 width=36)
-> Materialize (cost=0.00..29.12 rows=2 width=36)
-> Subquery Scan on z1 (cost=0.00..29.11 rows=2 width=36)
Filter: f_leak(z1.b)
-> Seq Scan on z1 z1_1 (cost=0.00..29.05 rows=6 width=36)
Filter: ((a % 2) = 1)
(9 rows)
--
-- Views should follow policy for view owner.
--

View File

@ -629,8 +629,9 @@ INSERT INTO document VALUES (1, (SELECT cid from category WHERE cname = 'novel')
--
SET SESSION AUTHORIZATION rls_regress_user0;
CREATE TABLE z1 (a int, b text);
CREATE TABLE z2 (a int, b text);
GRANT SELECT ON z1 TO rls_regress_group1, rls_regress_group2,
GRANT SELECT ON z1,z2 TO rls_regress_group1, rls_regress_group2,
rls_regress_user1, rls_regress_user2;
INSERT INTO z1 VALUES
@ -648,18 +649,39 @@ SET SESSION AUTHORIZATION rls_regress_user1;
SELECT * FROM z1 WHERE f_leak(b);
EXPLAIN (COSTS OFF) SELECT * FROM z1 WHERE f_leak(b);
PREPARE plancache_test AS SELECT * FROM z1 WHERE f_leak(b);
EXPLAIN EXECUTE plancache_test;
PREPARE plancache_test2 AS WITH q AS (SELECT * FROM z1 WHERE f_leak(b)) SELECT * FROM q,z2;
EXPLAIN EXECUTE plancache_test2;
PREPARE plancache_test3 AS WITH q AS (SELECT * FROM z2) SELECT * FROM q,z1 WHERE f_leak(z1.b);
EXPLAIN EXECUTE plancache_test3;
SET ROLE rls_regress_group1;
SELECT * FROM z1 WHERE f_leak(b);
EXPLAIN (COSTS OFF) SELECT * FROM z1 WHERE f_leak(b);
EXPLAIN EXECUTE plancache_test;
EXPLAIN EXECUTE plancache_test2;
EXPLAIN EXECUTE plancache_test3;
SET SESSION AUTHORIZATION rls_regress_user2;
SELECT * FROM z1 WHERE f_leak(b);
EXPLAIN (COSTS OFF) SELECT * FROM z1 WHERE f_leak(b);
EXPLAIN EXECUTE plancache_test;
EXPLAIN EXECUTE plancache_test2;
EXPLAIN EXECUTE plancache_test3;
SET ROLE rls_regress_group2;
SELECT * FROM z1 WHERE f_leak(b);
EXPLAIN (COSTS OFF) SELECT * FROM z1 WHERE f_leak(b);
EXPLAIN EXECUTE plancache_test;
EXPLAIN EXECUTE plancache_test2;
EXPLAIN EXECUTE plancache_test3;
--
-- Views should follow policy for view owner.
--