--------------------------------------------------------------------------------
-- 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);
select * from foo_rls where leak_data(a);
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);
select * from foo_rls where leak_data(c);
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);
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);
select * from foo_rls where leak_data(a);
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);

-- 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;

-- 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);

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;
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;
-- 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;
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;
-- 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;
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);
select * from foo_part_rls where leak_data(a);
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);
select * from foo_part_rls where leak_data(c);
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);
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);
select * from foo_part_rls where leak_data(a);
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);

-- 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;

-- 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);

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;
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;
-- 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;
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;
-- 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;
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;
explain (costs off) select * from 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;
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;
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;
explain (costs off) select * from roottabmid1;
explain (costs off) select * from roottableaf1;
explain (costs off) select * from roottableaf2;
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);
create table child2 (d int) inherits (parent);
-- 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;
-- Below queries will error out as no SELECT permission for user on child
-- relations
explain (costs off) select * from child1;
explain (costs off) select * from 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;
reset session authorization;
drop policy p1 on parent;
drop table parent cascade;


--------------------------------------------------------------------------------
----- 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);
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);
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;
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;
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;
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;
reset session authorization;
drop policy p1 on foo_part_rls;
drop table foo_part_rls cascade;
drop user new_user;
reset optimizer_trace_fallback;
