blob: 5f6b97d86e2815b826418851944d910b118a3c3d [file] [log] [blame]
--------------------------------------------------------------------------------
-- Current implementation only supports SELECT and DELETE queries.
-- Foreign Scan / Dynamic Foreign Scan with row level security is not supported.
-- Any sort of index plans will not be generated by ORCA
-- for a relation having RLS enabled.
-- If the security quals contains sublinks then ORCA will fall back to planner.
--------------------------------------------------------------------------------
-- NOTE:
-- The file rowsecurity.sql has a wider test coverage but the issue is most of
-- the queries in that file are falling back to planner because of not supported
-- features by ORCA such as non default collation, VIEW with WITH CHECK OPTION,
-- Queries on coordinator-only tables, Query has row level security enabled and
-- security quals contain sublinks etc. So rowsecurity.sql file is not providing
-- much help in testing ORCA side changes.
-- But on a high level some of the tests covered in rowsecurity.sql is
-- Table inheritance and RLS policy
-- There are tests related to partition relations but the test cases are
-- falling back due to various reasons like non default collation, VIEW with
-- WITH CHECK OPTION.
-- CTE with RLS
-- RLS with joins
-- View with WITH CHECK OPTION
-- Recursive Queries
-- Testing the output of queries by granting permissions to various roles
-- and groups and superuser database non-superuser with bypass privilege can
-- bypass RLS policy when disabled
--- RLS policy does not apply to table owner when RLS enabled.
-- RLS policy does not apply to table owner when RLS disabled.
-- only owner can change policies
-- FORCE ROW LEVEL SECURITY applies RLS to owners too
-- PREPARE statements
-- Duplicate policy names
-- Multiple policies on a table
-- INSERT in a table with no RLS by selecting from a relation having RLS
-- Default Deny policy
-- UPDATE/UPDATE with FROM clause/INSERT with Row-level security etc etc.
-- So the tests that have been covered in qp_rowsecurity are mostly similar to
-- that in rowsecurity.sql but since they are not falling back to planner, able
-- to verify the ORCA side changes.
--------------------------------------------------------------------------------
----- SELECT queries on relation having row level security enabled -------------
--------------------------------------------------------------------------------
set optimizer_trace_fallback to on;
create user new_user;
create table foo_rls ( a int, b int, c int) distributed by (a);
create table bar_rls ( p int, q int) distributed by (p);
insert into foo_rls select i,i*10,i*100 from generate_series(1,5)i;
insert into foo_rls values (NULL,50,500);;
insert into foo_rls values (NULL,60,NULL);
insert into foo_rls values (NULL,70,NULL);
insert into foo_rls values (NULL,NULL,600);
insert into foo_rls values (NULL,NULL,700);
vacuum analyze foo_rls;
grant select on foo_rls to new_user;
grant select on bar_rls to new_user;
alter table foo_rls enable row level security;
CREATE OR REPLACE FUNCTION leak_data(int) RETURNS boolean AS $$
BEGIN
RAISE NOTICE 'Value of column is: %',$1;
RETURN true;
END;
$$ LANGUAGE plpgsql;
-- Permissive policy
create policy p1 on foo_rls using (a * 10 = b and c > 300);
set session authorization new_user;
-- Query plan will have the filter defined in the policy [(a * 10) = b) AND (c > 300)]
-- before query filter [leak_data(a)]
explain (costs off ) select * from foo_rls where leak_data(a);
QUERY PLAN
-------------------------------------------------------------------
Gather Motion 3:1 (slice1; segments: 3)
-> Seq Scan on foo_rls
Filter: ((((a * 10) = b) AND (c > 300)) AND leak_data(a))
Optimizer: GPORCA
(4 rows)
select * from foo_rls where leak_data(a);
NOTICE: Value of column is: 4
NOTICE: Value of column is: 5
a | b | c
---+----+-----
4 | 40 | 400
5 | 50 | 500
(2 rows)
reset session authorization;
drop policy p1 on foo_rls;
-- All permissive policies which are applicable to a given query
-- will be combined together using the Boolean OR operator
create policy p1 on foo_rls as permissive for select using (a * 10 >= 30 );
create policy p2 on foo_rls as permissive for select
using (LEAST(a,b) >= 2);
set session authorization new_user;
-- The filter defined in the two permissive policies will be OR'ed together
explain (costs off )select * from foo_rls where leak_data(c);
QUERY PLAN
-----------------------------------------------------------------------------
Gather Motion 3:1 (slice1; segments: 3)
-> Seq Scan on foo_rls
Filter: (((LEAST(a, b) >= 2) OR ((a * 10) >= 30)) AND leak_data(c))
Optimizer: GPORCA
(4 rows)
select * from foo_rls where leak_data(c);
NOTICE: Value of column is: 200
NOTICE: Value of column is: 500
NOTICE: Value of column is: 300
NOTICE: Value of column is: 400
NOTICE: Value of column is: 500
NOTICE: Value of column is: <NULL>
NOTICE: Value of column is: <NULL>
a | b | c
---+----+-----
5 | 50 | 500
2 | 20 | 200
3 | 30 | 300
4 | 40 | 400
| 50 | 500
| 60 |
| 70 |
(7 rows)
reset session authorization;
drop policy p1 on foo_rls;
drop policy p2 on foo_rls;
-- Restrictive policies
-- There needs to be at least one permissive policy to grant access to records
create policy p1 on foo_rls as restrictive for select using (a > 3);
create policy p2 on foo_rls as restrictive for select using (b > 35);
set session authorization new_user;
-- No permissive policies defined on the relation, so no records will be
-- returned
explain (costs off ) select * from foo_rls where leak_data(a);
QUERY PLAN
------------------------------------------
Gather Motion 3:1 (slice1; segments: 3)
-> Seq Scan on foo_rls
Filter: (false AND leak_data(a))
Optimizer: GPORCA
(4 rows)
reset session authorization ;
create policy p3 on foo_rls as permissive for select using (c % 100 = 0);
set session authorization new_user;
-- Now after creating a permissive policy, the query will return records and the
-- restrictive policies will be AND'ed with permissive policies
explain (costs off ) select * from foo_rls where leak_data(a);
QUERY PLAN
-----------------------------------------------------------------------------
Gather Motion 3:1 (slice1; segments: 3)
-> Seq Scan on foo_rls
Filter: ((a > 3) AND (b > 35) AND ((c % 100) = 0) AND leak_data(a))
Optimizer: GPORCA
(4 rows)
select * from foo_rls where leak_data(a);
NOTICE: Value of column is: 4
NOTICE: Value of column is: 5
a | b | c
---+----+-----
5 | 50 | 500
4 | 40 | 400
(2 rows)
reset session authorization;
drop policy p1 on foo_rls;
drop policy p2 on foo_rls;
drop policy p3 on foo_rls;
-- The RTE of the relation containing security quals is present inside a
-- RTE_SUBQUERY rtekind
create policy p1 on foo_rls as permissive for select using (b + 1 = 4 );
create policy p2 on foo_rls as permissive for select using (b is distinct
from c);
set session authorization new_user;
explain (costs off ) select * from (select * from foo_rls) t1, bar_rls where
t1.a=bar_rls.p and bar_rls.q is null and leak_data(c);
QUERY PLAN
------------------------------------------------------------------------------------
Gather Motion 3:1 (slice1; segments: 3)
-> Hash Join
Hash Cond: (foo_rls.a = bar_rls.p)
-> Seq Scan on foo_rls
Filter: (((b IS DISTINCT FROM c) OR ((b + 1) = 4)) AND leak_data(c))
-> Hash
-> Seq Scan on bar_rls
Filter: (q IS NULL)
Optimizer: GPORCA
(9 rows)
-- The RTE of the relation containing security quals is present inside a
-- SUBLINK node
explain (costs off ) select * from bar_rls where bar_rls.p in (select a
from foo_rls where abs(a) >= 10 and leak_data(c)) and bar_rls.q is not null;
QUERY PLAN
---------------------------------------------------------------------------------------------------------------
Gather Motion 3:1 (slice1; segments: 3)
-> Hash Semi Join
Hash Cond: (bar_rls.p = foo_rls.a)
-> Seq Scan on bar_rls
Filter: (NOT (q IS NULL))
-> Hash
-> Seq Scan on foo_rls
Filter: ((("" IS DISTINCT FROM c) OR (("" + 1) = 4)) AND ((abs(a) >= 10) AND leak_data(c)))
Optimizer: GPORCA
(9 rows)
-- The RTE of the relation containing security quals is present inside a CTE
explain (costs off ) with cte as (select * from foo_rls where
a > LEAST(a,b)) select * from cte left join bar_rls on
cte.a=bar_rls.p and q = 10 and leak_data(cte.a);
QUERY PLAN
-----------------------------------------------------------------------------------------
Gather Motion 3:1 (slice1; segments: 3)
-> Hash Left Join
Hash Cond: (foo_rls.a = bar_rls.p)
Join Filter: leak_data(foo_rls.a)
-> Seq Scan on foo_rls
Filter: (((b IS DISTINCT FROM c) OR ((b + 1) = 4)) AND (a > LEAST(a, b)))
-> Hash
-> Seq Scan on bar_rls
Filter: (q = 10)
Optimizer: GPORCA
(10 rows)
reset session authorization;
drop policy p1 on foo_rls;
drop policy p2 on foo_rls;
-- ORCA will fallback to planner if SUBLINK is present inside a security qual
create policy p1 on foo_rls as permissive for select using (a in (select p
from bar_rls));
set session authorization new_user;
explain (costs off ) select * from foo_rls;
INFO: GPORCA failed to produce a plan, falling back to Postgres-based planner
DETAIL: Falling back to Postgres-based planner because GPORCA does not support the following feature: Query has row level security enabled and security quals contain sublinks
QUERY PLAN
-----------------------------------------------------------
Gather Motion 3:1 (slice1; segments: 3)
-> Seq Scan on foo_rls
Filter: (hashed SubPlan 1)
SubPlan 1
-> Broadcast Motion 3:3 (slice2; segments: 3)
-> Seq Scan on bar_rls
Optimizer: Postgres-based planner
(7 rows)
reset session authorization;
drop policy p1 on foo_rls;
-- ORCA will not generate any index plans when row level security is enabled
-- for a relation
create policy p1 on foo_rls as permissive for select using (b = 10);
create index btindex on foo_rls using btree(a);
-- Index Scan is generated by ORCA when the owner of the table runs the query
set enable_seqscan to off;
explain (costs off) select * from foo_rls where a is null and b = 10;
QUERY PLAN
-------------------------------------------
Gather Motion 1:1 (slice1; segments: 1)
-> Index Scan using btindex on foo_rls
Index Cond: (a IS NULL)
Filter: (b = 10)
Optimizer: GPORCA
(5 rows)
-- Index Scan is not generated by ORCA when RLS enabled by ORCA
set session authorization new_user;
explain (costs off) select * from foo_rls where a is null;
QUERY PLAN
--------------------------------------------
Gather Motion 1:1 (slice1; segments: 1)
-> Seq Scan on foo_rls
Filter: ((b = 10) AND (a IS NULL))
Optimizer: GPORCA
(4 rows)
reset session authorization;
-- Index Only Scan is generated by ORCA when the owner of the table runs the query
set optimizer_enable_indexscan to off;
explain (costs off) select a from foo_rls where a is null;
QUERY PLAN
------------------------------------------------
Gather Motion 1:1 (slice1; segments: 1)
-> Index Only Scan using btindex on foo_rls
Index Cond: (a IS NULL)
Optimizer: GPORCA
(4 rows)
-- Index Only Scan is not generated by ORCA when RLS enabled
set session authorization new_user;
explain (costs off) select a from foo_rls where a is null;
QUERY PLAN
--------------------------------------------
Gather Motion 1:1 (slice1; segments: 1)
-> Seq Scan on foo_rls
Filter: (("" = 10) AND (a IS NULL))
Optimizer: GPORCA
(4 rows)
reset session authorization;
set optimizer_enable_indexscan to on;
reset enable_seqscan;
drop policy p1 on foo_rls;
drop table foo_rls cascade;
drop table bar_rls cascade;
--------------------------------------------------------------------------------
----------- SELECT queries on partitioned tables with RLS enabled --------------
--------------------------------------------------------------------------------
create table foo_part_rls ( a int, b int, c int) distributed by (a)
partition by range(b) (start(0) end(100) every(20), default partition other_part);
create table bar_part_rls ( p int, q int) distributed by (p) partition by range(q)
(start(0) end(100) every(20));
insert into foo_part_rls select i,i*10,i*100 from generate_series(1,5)i;
insert into foo_part_rls values (NULL,50,500);;
insert into foo_part_rls values (NULL,60,NULL);
insert into foo_part_rls values (NULL,70,NULL);
insert into foo_part_rls values (NULL,NULL,600);
insert into foo_part_rls values (NULL,NULL,700);
analyze foo_part_rls;
grant select on foo_part_rls to new_user;
grant select on bar_part_rls to new_user;
alter table foo_part_rls enable row level security;
-- Permissive policy
create policy p1 on foo_part_rls using (a * 10 = b and c > 300);
set session authorization new_user;
-- Query plan will have the filter defined in the policy [(a * 10) = b) AND (c > 300)]
-- before query filter [leak_data(a)]
explain (costs off ) select * from foo_part_rls where leak_data(a);
QUERY PLAN
-------------------------------------------------------------------
Gather Motion 3:1 (slice1; segments: 3)
-> Dynamic Seq Scan on foo_part_rls
Number of partitions to scan: 6 (out of 6)
Filter: ((((a * 10) = b) AND (c > 300)) AND leak_data(a))
Optimizer: GPORCA
(5 rows)
select * from foo_part_rls where leak_data(a);
NOTICE: Value of column is: 4
NOTICE: Value of column is: 5
a | b | c
---+----+-----
5 | 50 | 500
4 | 40 | 400
(2 rows)
reset session authorization;
drop policy p1 on foo_part_rls;
-- All permissive policies which are applicable to a given query
-- will be combined together using the Boolean OR operator
create policy p1 on foo_part_rls as permissive for select using (a * 10 >= 30 );
create policy p2 on foo_part_rls as permissive for select
using (LEAST(a,b) >= 2);
set session authorization new_user;
-- The filter defined in the two permissive policies will be OR'ed together
explain (costs off )select * from foo_part_rls where leak_data(c);
QUERY PLAN
-----------------------------------------------------------------------------
Gather Motion 3:1 (slice1; segments: 3)
-> Dynamic Seq Scan on foo_part_rls
Number of partitions to scan: 6 (out of 6)
Filter: (((LEAST(a, b) >= 2) OR ((a * 10) >= 30)) AND leak_data(c))
Optimizer: GPORCA
(5 rows)
select * from foo_part_rls where leak_data(c);
NOTICE: Value of column is: 200
NOTICE: Value of column is: 300
NOTICE: Value of column is: 500
NOTICE: Value of column is: 400
NOTICE: Value of column is: 500
NOTICE: Value of column is: <NULL>
NOTICE: Value of column is: <NULL>
a | b | c
---+----+-----
5 | 50 | 500
2 | 20 | 200
3 | 30 | 300
4 | 40 | 400
| 50 | 500
| 60 |
| 70 |
(7 rows)
reset session authorization;
drop policy p1 on foo_part_rls;
drop policy p2 on foo_part_rls;
-- Restrictive policies
-- There needs to be at least one permissive policy to grant access to records
create policy p1 on foo_part_rls as restrictive for select using (a > 3);
create policy p2 on foo_part_rls as restrictive for select using (b > 35);
set session authorization new_user;
-- No permissive policies defined on the relation, so no records will be
-- returned
explain (costs off ) select * from foo_part_rls where leak_data(a);
QUERY PLAN
----------------------------------------------------
Gather Motion 3:1 (slice1; segments: 3)
-> Dynamic Seq Scan on foo_part_rls
Number of partitions to scan: 6 (out of 6)
Filter: (false AND leak_data(a))
Optimizer: GPORCA
(5 rows)
reset session authorization ;
create policy p3 on foo_part_rls as permissive for select using (c % 100 = 0);
set session authorization new_user;
-- Now after creating a permissive policy, the query will return records and the
-- restrictive policies will be AND'ed with permissive policies
explain (costs off ) select * from foo_part_rls where leak_data(a);
QUERY PLAN
-----------------------------------------------------------------------------
Gather Motion 3:1 (slice1; segments: 3)
-> Dynamic Seq Scan on foo_part_rls
Number of partitions to scan: 6 (out of 6)
Filter: ((a > 3) AND (b > 35) AND ((c % 100) = 0) AND leak_data(a))
Optimizer: GPORCA
(5 rows)
select * from foo_part_rls where leak_data(a);
NOTICE: Value of column is: 4
NOTICE: Value of column is: 5
a | b | c
---+----+-----
5 | 50 | 500
4 | 40 | 400
(2 rows)
reset session authorization;
drop policy p1 on foo_part_rls;
drop policy p2 on foo_part_rls;
drop policy p3 on foo_part_rls;
-- The RTE of the relation containing security quals is present inside a
-- RTE_SUBQUERY rtekind
create policy p1 on foo_part_rls as permissive for select using (b + 1 = 4 );
create policy p2 on foo_part_rls as permissive for select using (b is distinct
from c);
set session authorization new_user;
explain (costs off ) select * from (select * from foo_part_rls) t1, bar_part_rls
where t1.a=bar_part_rls.p and bar_part_rls.q is not null and leak_data(c);
QUERY PLAN
------------------------------------------------------------------------------------
Gather Motion 3:1 (slice1; segments: 3)
-> Hash Join
Hash Cond: (foo_part_rls.a = bar_part_rls.p)
-> Dynamic Seq Scan on foo_part_rls
Number of partitions to scan: 6 (out of 6)
Filter: (((b IS DISTINCT FROM c) OR ((b + 1) = 4)) AND leak_data(c))
-> Hash
-> Dynamic Seq Scan on bar_part_rls
Number of partitions to scan: 5 (out of 5)
Filter: (NOT (q IS NULL))
Optimizer: GPORCA
(11 rows)
-- The RTE of the relation containing security quals is present inside a
-- SUBLINK node
explain (costs off ) select * from bar_part_rls where bar_part_rls.p in
(select a from foo_part_rls where abs(a) >= 10 and leak_data(c)) and
bar_part_rls.q is not null;
QUERY PLAN
---------------------------------------------------------------------------------------------------------------
Gather Motion 3:1 (slice1; segments: 3)
-> Hash Semi Join
Hash Cond: (bar_part_rls.p = foo_part_rls.a)
-> Dynamic Seq Scan on bar_part_rls
Number of partitions to scan: 5 (out of 5)
Filter: (NOT (q IS NULL))
-> Hash
-> Dynamic Seq Scan on foo_part_rls
Number of partitions to scan: 6 (out of 6)
Filter: (((b IS DISTINCT FROM c) OR ((b + 1) = 4)) AND ((abs(a) >= 10) AND leak_data(c)))
Optimizer: GPORCA
(11 rows)
-- The RTE of the relation containing security quals is present inside a CTE
explain (costs off ) with cte as (select * from foo_part_rls where
a > LEAST(a,b)) select * from cte left join bar_part_rls on
cte.a=bar_part_rls.p and q = 10 and leak_data(cte.a);
QUERY PLAN
-----------------------------------------------------------------------------------------
Gather Motion 3:1 (slice1; segments: 3)
-> Hash Left Join
Hash Cond: (foo_part_rls.a = bar_part_rls.p)
Join Filter: leak_data(foo_part_rls.a)
-> Dynamic Seq Scan on foo_part_rls
Number of partitions to scan: 6 (out of 6)
Filter: (((b IS DISTINCT FROM c) OR ((b + 1) = 4)) AND (a > LEAST(a, b)))
-> Hash
-> Dynamic Seq Scan on bar_part_rls
Number of partitions to scan: 1 (out of 5)
Filter: (q = 10)
Optimizer: GPORCA
(12 rows)
reset session authorization;
drop policy p1 on foo_part_rls;
drop policy p2 on foo_part_rls;
-- ORCA will fallback to planner if SUBLINK is present inside a security qual
create policy p1 on foo_part_rls as permissive for select using (a in
(select p from bar_part_rls));
set session authorization new_user;
explain (costs off ) select * from foo_part_rls where foo_part_rls.b <15;
INFO: GPORCA failed to produce a plan, falling back to Postgres-based planner
DETAIL: Falling back to Postgres-based planner because GPORCA does not support the following feature: Query has row level security enabled and security quals contain sublinks
QUERY PLAN
----------------------------------------------------------------------------------
Gather Motion 3:1 (slice1; segments: 3)
-> Append
-> Seq Scan on foo_part_rls_1_prt_2 foo_part_rls_1
Filter: ((hashed SubPlan 1 (copy 2)) AND (b < 15))
SubPlan 1 (copy 2)
-> Broadcast Motion 3:3 (slice2; segments: 3)
-> Append
-> Seq Scan on bar_part_rls_1_prt_1 bar_part_rls_1
-> Seq Scan on bar_part_rls_1_prt_2 bar_part_rls_2
-> Seq Scan on bar_part_rls_1_prt_3 bar_part_rls_3
-> Seq Scan on bar_part_rls_1_prt_4 bar_part_rls_4
-> Seq Scan on bar_part_rls_1_prt_5 bar_part_rls_5
-> Seq Scan on foo_part_rls_1_prt_other_part foo_part_rls_2
Filter: ((hashed SubPlan 1 (copy 3)) AND (b < 15))
SubPlan 1 (copy 3)
-> Broadcast Motion 3:3 (slice3; segments: 3)
-> Append
-> Seq Scan on bar_part_rls_1_prt_1 bar_part_rls_7
-> Seq Scan on bar_part_rls_1_prt_2 bar_part_rls_8
-> Seq Scan on bar_part_rls_1_prt_3 bar_part_rls_9
-> Seq Scan on bar_part_rls_1_prt_4 bar_part_rls_10
-> Seq Scan on bar_part_rls_1_prt_5 bar_part_rls_11
Optimizer: Postgres query optimizer
(23 rows)
reset session authorization;
drop policy p1 on foo_part_rls;
-- ORCA will not generate any index plans when row level security is enabled
-- for a relation
create policy p1 on foo_part_rls as permissive for select using (b = 10);
create index btindex on foo_part_rls using btree(a);
-- Dynamic Index Scan is generated by ORCA when the owner of the table runs
-- the query
set enable_seqscan to off;
explain (costs off) select * from foo_part_rls where a is null;
QUERY PLAN
-----------------------------------------------------
Gather Motion 1:1 (slice1; segments: 1)
-> Dynamic Index Scan on btindex on foo_part_rls
Index Cond: (a IS NULL)
Number of partitions to scan: 6 (out of 6)
Optimizer: GPORCA
(5 rows)
-- Dynamic Index Scan is not generated by ORCA when RLS is enabled
set session authorization new_user;
explain (costs off) select * from foo_part_rls where a is null;
QUERY PLAN
----------------------------------------------------
Gather Motion 1:1 (slice1; segments: 1)
-> Dynamic Seq Scan on foo_part_rls
Number of partitions to scan: 6 (out of 6)
Filter: ((b = 10) AND (a IS NULL))
Optimizer: GPORCA
(5 rows)
reset session authorization;
reset enable_seqscan;
drop policy p1 on foo_part_rls;
drop table foo_part_rls cascade;
drop table bar_part_rls cascade;
drop function leak_data(int);
--------------------------------------------------------------------------------
----------------- Creating policies on leaf partitions -------------------------
--------------------------------------------------------------------------------
create table rootpart (a int, b int) distributed by (a) partition by range(a)
(start(0) end(10) every(5));
insert into rootpart select i,i from generate_series(0,9)i;
-- select permission is only for root partition and not for child partition
grant select on rootpart to new_user;
alter table rootpart_1_prt_2 enable row level security;
create policy p1 on rootpart_1_prt_2 using (a>=7);
set session authorization new_user;
explain (costs off) select * from rootpart;
QUERY PLAN
----------------------------------------------------
Gather Motion 3:1 (slice1; segments: 3)
-> Dynamic Seq Scan on rootpart
Number of partitions to scan: 2 (out of 2)
Optimizer: GPORCA
(4 rows)
-- Issue #16961
-- ORCA and planner should not generate a plan and show error permission denied
-- for leaf partition
explain (costs off) select * from rootpart_1_prt_2;
QUERY PLAN
------------------------------------------
Gather Motion 3:1 (slice1; segments: 3)
-> Seq Scan on rootpart_1_prt_2
Filter: (a >= 7)
Optimizer: GPORCA
(4 rows)
reset session authorization;
drop policy p1 on rootpart_1_prt_2;
drop table rootpart;
--------------------------------------------------------------------------------
----------------- Grant SELECT to only a leaf partition ------------------------
--------------------------------------------------------------------------------
create table rootpart (a int, b int) distributed by (a) partition by range(a)
(start(0) end(10) every(5));
insert into rootpart select i,i from generate_series(0,9)i;
grant select on rootpart_1_prt_2 to new_user;
alter table rootpart_1_prt_2 enable row level security;
create policy p1 on rootpart_1_prt_2 using (a>=7);
set session authorization new_user;
-- Since the SELECT permission is only for a leaf partition [rootpart_1_prt_2],
-- querying the root will error out
explain (costs off) select * from rootpart;
ERROR: permission denied for table rootpart
explain (costs off) select * from rootpart_1_prt_1;
ERROR: permission denied for table rootpart_1_prt_1
-- Below query will work fine as the user has SELECT permission on this root
-- partition
explain (costs off) select * from rootpart_1_prt_2;
QUERY PLAN
------------------------------------------
Gather Motion 3:1 (slice1; segments: 3)
-> Seq Scan on rootpart_1_prt_2
Filter: (a >= 7)
Optimizer: GPORCA
(4 rows)
reset session authorization;
drop policy p1 on rootpart_1_prt_2;
drop table rootpart;
--------------------------------------------------------------------------------
-------------------------- Multilevel Partitions -------------------------------
--------------------------------------------------------------------------------
create table rootTab(a int) distributed by (a) partition by range(a);
create table roottabmid1(a int) distributed by (a) partition by range(a);
create table roottableaf1(a int) distributed by (a);
create table roottableaf2(a int) distributed by (a);
alter table roottab attach partition roottabmid1 for values from (0) to (10);
alter table roottabmid1 attach partition roottableaf1 for values from (0) to (5);
alter table roottabmid1 attach partition roottableaf2 for values from (5) to (10);
insert into roottab select i from generate_series(0,9)i;
INFO: GPORCA failed to produce a plan, falling back to Postgres-based planner
DETAIL: Falling back to Postgres-based planner because GPORCA does not support the following feature: Multi-level partitioned tables
grant select on roottab to new_user;
alter table roottableaf2 enable row level security;
create policy p1 on roottableaf2 using (a>=7);
set session authorization new_user;
-- Querying the root will fallback to planner as ORCA doesn't supports
-- Multi-level partitioned tables
explain (costs off) select * from roottab;
INFO: GPORCA failed to produce a plan, falling back to Postgres-based planner
DETAIL: Falling back to Postgres-based planner because GPORCA does not support the following feature: Multi-level partitioned tables
QUERY PLAN
------------------------------------------------
Gather Motion 3:1 (slice1; segments: 3)
-> Append
-> Seq Scan on roottableaf1 roottab_1
-> Seq Scan on roottableaf2 roottab_2
Optimizer: Postgres query optimizer
(5 rows)
explain (costs off) select * from roottabmid1;
QUERY PLAN
----------------------------------------------------
Gather Motion 3:1 (slice1; segments: 3)
-> Dynamic Seq Scan on roottabmid1
Number of partitions to scan: 2 (out of 2)
Optimizer: GPORCA
(4 rows)
explain (costs off) select * from roottableaf1;
QUERY PLAN
------------------------------------------
Gather Motion 3:1 (slice1; segments: 3)
-> Seq Scan on roottableaf1
Optimizer: GPORCA
(3 rows)
explain (costs off) select * from roottableaf2;
QUERY PLAN
------------------------------------------
Gather Motion 3:1 (slice1; segments: 3)
-> Seq Scan on roottableaf2
Filter: (a >= 7)
Optimizer: GPORCA
(4 rows)
reset session authorization;
drop policy p1 on roottableaf2;
drop table roottab;
--------------------------------------------------------------------------------
------------------- Inherited Tables with RLS enabled --------------------------
--------------------------------------------------------------------------------
create table parent (a int, b int) distributed by (a);
create table child1 (c int) inherits (parent);
NOTICE: table has parent, setting distribution columns to match parent table
create table child2 (d int) inherits (parent);
NOTICE: table has parent, setting distribution columns to match parent table
-- SELECT permission on parent
grant select on parent to new_user;
alter table parent enable row level security;
create policy p1 on parent using (a>=7);
set session authorization new_user;
-- Querying the parent will fallback to planner as ORCA doesn't supports
-- Inherited tables
explain (costs off) select * from parent;
INFO: GPORCA failed to produce a plan, falling back to Postgres-based planner
DETAIL: Falling back to Postgres-based planner because GPORCA does not support the following feature: Inherited tables
QUERY PLAN
------------------------------------------
Gather Motion 3:1 (slice1; segments: 3)
-> Append
-> Seq Scan on parent parent_1
Filter: (a >= 7)
-> Seq Scan on child1 parent_2
Filter: (a >= 7)
-> Seq Scan on child2 parent_3
Filter: (a >= 7)
Optimizer: Postgres query optimizer
(9 rows)
-- Below queries will error out as no SELECT permission for user on child
-- relations
explain (costs off) select * from child1;
ERROR: permission denied for table child1
explain (costs off) select * from child2;
ERROR: permission denied for table child2
reset session authorization;
grant select on child1 to new_user;
set session authorization new_user;
-- After granting SELECT permission the below query will work
explain (costs off) select * from child1;
QUERY PLAN
------------------------------------------
Gather Motion 3:1 (slice1; segments: 3)
-> Seq Scan on child1
Optimizer: GPORCA
(3 rows)
reset session authorization;
drop policy p1 on parent;
drop table parent cascade;
NOTICE: drop cascades to 2 other objects
DETAIL: drop cascades to table child1
drop cascades to table child2
--------------------------------------------------------------------------------
----- INSERT queries on relation having row level security enabled -------------
------ ORCA will fallback to planner for INSERT with RLS anabled ---------------
--------------------------------------------------------------------------------
create table foo_rls ( a int, b int, c int) distributed by (a);
insert into foo_rls select i,i*10,i*100 from generate_series(1,5)i;
analyze foo_rls;
grant insert on foo_rls to new_user;
alter table foo_rls enable row level security;
create policy p1 on foo_rls using (c>=3);
set session authorization new_user;
explain (costs off) insert into foo_rls values (1,1,1);
INFO: GPORCA failed to produce a plan, falling back to Postgres-based planner
DETAIL: Falling back to Postgres-based planner because GPORCA does not support the following feature: View with WITH CHECK OPTION
QUERY PLAN
-----------------------------------
Insert on foo_rls
-> Result
Optimizer: Postgres-based planner
(3 rows)
reset session authorization;
drop policy p1 on foo_rls;
drop table foo_rls cascade;
--------------------------------------------------------------------------------
--------- INSERT queries on partitioned relation having RLS enabled ------------
--------- ORCA will fallback to planner for INSERT with RLS anabled ------------
--------------------------------------------------------------------------------
create table foo_part_rls ( a int, b int, c int) distributed by (a)
partition by range(b) (start(0) end(100) every(20));
insert into foo_part_rls select i,i*10,i*100 from generate_series(1,9)i;
analyze foo_part_rls;
grant insert on foo_part_rls to new_user;
alter table foo_part_rls enable row level security;
create policy p1 on foo_part_rls using (c>=500);
set session authorization new_user;
explain (costs off) insert into foo_part_rls values (-10,-10,-10);
INFO: GPORCA failed to produce a plan, falling back to Postgres-based planner
DETAIL: Falling back to Postgres-based planner because GPORCA does not support the following feature: View with WITH CHECK OPTION
QUERY PLAN
-----------------------------------
Insert on foo_part_rls
-> Result
Optimizer: Postgres-based planner
(3 rows)
reset session authorization;
drop policy p1 on foo_part_rls;
drop table foo_part_rls cascade;
--------------------------------------------------------------------------------
----- UPDATE queries on relation having row level security enabled -------------
------ ORCA will fallback to planner for UPDATE with RLS anabled ---------------
--------------------------------------------------------------------------------
create table foo_rls ( a int, b int, c int) distributed by (a);
insert into foo_rls select i,i*10,i*100 from generate_series(1,5)i;
analyze foo_rls;
grant select,update on foo_rls to new_user;
alter table foo_rls enable row level security;
create policy p1 on foo_rls using (c>=3);
set session authorization new_user;
explain (costs off) update foo_rls set a=-10 where c=4;
INFO: GPORCA failed to produce a plan, falling back to Postgres-based planner
DETAIL: Falling back to Postgres-based planner because GPORCA does not support the following feature: View with WITH CHECK OPTION
QUERY PLAN
---------------------------------------------------------------
Update on foo_rls
-> Explicit Redistribute Motion 3:3 (slice1; segments: 3)
-> Split
-> Seq Scan on foo_rls
Filter: ((c >= 3) AND (c = 4))
Optimizer: Postgres-based planner
(6 rows)
reset session authorization;
drop policy p1 on foo_rls;
drop table foo_rls cascade;
--------------------------------------------------------------------------------
--------- UPDATE queries on partitioned relation having RLS enabled ------------
--------- ORCA will fallback to planner for UPDATE with RLS anabled ------------
--------------------------------------------------------------------------------
create table foo_part_rls ( a int, b int, c int) distributed by (a)
partition by range(b) (start(0) end(100) every(20));
insert into foo_part_rls select i,i*10,i*100 from generate_series(1,9)i;
analyze foo_part_rls;
grant select,update on foo_part_rls to new_user;
alter table foo_part_rls enable row level security;
create policy p1 on foo_part_rls using (c>=500);
set session authorization new_user;
explain (costs off) update foo_part_rls set a=10 where foo_part_rls.c = 800;
INFO: GPORCA failed to produce a plan, falling back to Postgres-based planner
DETAIL: Falling back to Postgres-based planner because GPORCA does not support the following feature: View with WITH CHECK OPTION
QUERY PLAN
-------------------------------------------------------------------------
Update on foo_part_rls
Update on foo_part_rls_1_prt_1 foo_part_rls_1
Update on foo_part_rls_1_prt_2 foo_part_rls_2
Update on foo_part_rls_1_prt_3 foo_part_rls_3
Update on foo_part_rls_1_prt_4 foo_part_rls_4
Update on foo_part_rls_1_prt_5 foo_part_rls_5
-> Explicit Redistribute Motion 3:3 (slice1; segments: 3)
-> Split
-> Append
-> Seq Scan on foo_part_rls_1_prt_1 foo_part_rls_1
Filter: ((c >= 500) AND (c = 800))
-> Seq Scan on foo_part_rls_1_prt_2 foo_part_rls_2
Filter: ((c >= 500) AND (c = 800))
-> Seq Scan on foo_part_rls_1_prt_3 foo_part_rls_3
Filter: ((c >= 500) AND (c = 800))
-> Seq Scan on foo_part_rls_1_prt_4 foo_part_rls_4
Filter: ((c >= 500) AND (c = 800))
-> Seq Scan on foo_part_rls_1_prt_5 foo_part_rls_5
Filter: ((c >= 500) AND (c = 800))
Optimizer: Postgres query optimizer
(20 rows)
reset session authorization;
drop policy p1 on foo_part_rls;
drop table foo_part_rls cascade;
--------------------------------------------------------------------------------
----- DELETE queries on relation having row level security enabled -------------
--------------------------------------------------------------------------------
create table foo_rls ( a int, b int, c int) distributed by (a);
insert into foo_rls select i,i*10,i*100 from generate_series(1,5)i;
analyze foo_rls;
grant select,delete on foo_rls to new_user;
alter table foo_rls enable row level security;
create policy p1 on foo_rls using (c>=300);
set session authorization new_user;
explain (costs off) delete from foo_rls where foo_rls.b=30;
QUERY PLAN
-------------------------------------------
Delete on foo_rls
-> Seq Scan on foo_rls
Filter: ((c >= 300) AND (b = 30))
Optimizer: GPORCA
(4 rows)
reset session authorization;
drop policy p1 on foo_rls;
drop table foo_rls cascade;
--------------------------------------------------------------------------------
----- DELETE queries on on partitioned relation having RLS enabled -------------
--------------------------------------------------------------------------------
create table foo_part_rls ( a int, b int, c int) distributed by (a)
partition by range(b) (start(0) end(100) every(20));
insert into foo_part_rls select i,i*10,i*100 from generate_series(1,9)i;
analyze foo_part_rls;
grant select,delete on foo_part_rls to new_user;
alter table foo_part_rls enable row level security;
create policy p1 on foo_part_rls using (c>=300);
set session authorization new_user;
explain (costs off) delete from foo_part_rls where foo_part_rls.b=30;
INFO: GPORCA failed to produce a plan, falling back to Postgres-based planner
DETAIL: Falling back to Postgres-based planner because GPORCA does not support the following feature: DML(delete) on partitioned tables
QUERY PLAN
-------------------------------------------------------
Delete on foo_part_rls
Delete on foo_part_rls_1_prt_2 foo_part_rls_1
-> Seq Scan on foo_part_rls_1_prt_2 foo_part_rls_1
Filter: ((c >= 300) AND (b = 30))
Optimizer: Postgres query optimizer
(5 rows)
reset session authorization;
drop policy p1 on foo_part_rls;
drop table foo_part_rls cascade;
drop user new_user;
reset optimizer_trace_fallback;