mikras_utils 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.rspec +3 -0
- data/.ruby-version +1 -0
- data/Gemfile +10 -0
- data/README.md +31 -0
- data/Rakefile +8 -0
- data/acl-build +5 -0
- data/build +11 -0
- data/exe/mikras_utils +5 -0
- data/exe/mkacl +59 -0
- data/lib/mikras_utils/mkacl/analyzer.rb +46 -0
- data/lib/mikras_utils/mkacl/generator.rb +55 -0
- data/lib/mikras_utils/mkacl/generators/acl_functions.rb +208 -0
- data/lib/mikras_utils/mkacl/generators/id_functions.rb +262 -0
- data/lib/mikras_utils/mkacl/generators/insert_triggers.rb +267 -0
- data/lib/mikras_utils/mkacl/generators/role_functions.rb +72 -0
- data/lib/mikras_utils/mkacl/generators/rules.rb +80 -0
- data/lib/mikras_utils/mkacl/parser.rb +73 -0
- data/lib/mikras_utils/mkacl/spec.rb +154 -0
- data/lib/mikras_utils/mkacl.rb +18 -0
- data/lib/mikras_utils/version.rb +5 -0
- data/lib/mikras_utils.rb +6 -0
- data/sig/mikras_utils.rbs +4 -0
- data/tests/acl.fox +312 -0
- data/tests/acl.spec +135 -0
- data/tests/acl.sql +132 -0
- data/tests/acl_portal-functions.sql +94 -0
- data/tests/acl_portal-tables.sql +23 -0
- data/tests/acl_portal-views.sql +73 -0
- data/tests/agg.sql +25 -0
- data/tests/app.sql +48 -0
- data/tests/app_portal-tables.sql +138 -0
- data/tests/app_portal-triggers.sql +23 -0
- data/tests/app_portal-views.sql +203 -0
- data/tests/auth.sql +12 -0
- data/tests/auth.users.sql +5 -0
- data/tests/build +7 -0
- data/tests/final-functions.sql +28 -0
- data/tests/fox.sql +158 -0
- data/tests/initial-functions.sql +6 -0
- data/tests/meta.sql +197 -0
- data/tests/reflections.yml +5 -0
- data/tests/schemas.sql +25 -0
- data/tests/setup.sql +8 -0
- data/tests/sys_portal.sql +172 -0
- metadata +145 -0
@@ -0,0 +1,203 @@
|
|
1
|
+
\set ON_ERROR_STOP on
|
2
|
+
|
3
|
+
set search_path to app_portal;
|
4
|
+
|
5
|
+
-- Union of case_roles and event_roles
|
6
|
+
drop view if exists acl_portal.domain_roles cascade;
|
7
|
+
create view acl_portal.domain_roles as
|
8
|
+
select
|
9
|
+
id,
|
10
|
+
case_id as "domain_id",
|
11
|
+
role
|
12
|
+
from
|
13
|
+
app_portal.case_roles
|
14
|
+
|
15
|
+
union all
|
16
|
+
|
17
|
+
select
|
18
|
+
id,
|
19
|
+
event_id as "domain_id",
|
20
|
+
role
|
21
|
+
from
|
22
|
+
app_portal.event_roles
|
23
|
+
;
|
24
|
+
|
25
|
+
-- Union of case_users and event_users
|
26
|
+
drop view if exists acl_portal.domain_users cascade;
|
27
|
+
create view acl_portal.domain_users as
|
28
|
+
select
|
29
|
+
case_role_id as "domain_role_id",
|
30
|
+
user_id
|
31
|
+
from
|
32
|
+
app_portal.case_users
|
33
|
+
|
34
|
+
union all
|
35
|
+
|
36
|
+
select
|
37
|
+
event_role_id as "domain_role_id",
|
38
|
+
user_id
|
39
|
+
from
|
40
|
+
app_portal.event_users
|
41
|
+
;
|
42
|
+
|
43
|
+
-- Combines cases, case_roles, and case_users and also computes the virtual CLA
|
44
|
+
-- and CTA roles from the event ELA and ETA roles and assigns the CLA role to
|
45
|
+
-- RES users. Only ACL roles are included
|
46
|
+
drop view if exists case_role_users cascade;
|
47
|
+
create view case_role_users as
|
48
|
+
with
|
49
|
+
-- Base roles
|
50
|
+
roles as (
|
51
|
+
select
|
52
|
+
cr.case_id,
|
53
|
+
cu.user_id,
|
54
|
+
cr.id as "role_id",
|
55
|
+
cr.role
|
56
|
+
from app_portal.role_kinds rk
|
57
|
+
join app_portal.case_roles cr on cr.role = rk.kind
|
58
|
+
join app_portal.case_users cu on cu.case_role_id = cr.id
|
59
|
+
where rk.acl
|
60
|
+
),
|
61
|
+
-- RES user is also CLA
|
62
|
+
res_roles as (
|
63
|
+
select
|
64
|
+
r.case_id,
|
65
|
+
r.user_id,
|
66
|
+
cr.id as "role_id",
|
67
|
+
cr.role
|
68
|
+
from roles r
|
69
|
+
join case_roles cr on cr.case_id = r.case_id and cr.role = 'CLA'
|
70
|
+
where r.role = 'RES'
|
71
|
+
),
|
72
|
+
-- Event roles ELA and ETA are translated to CLA and CTA case roles
|
73
|
+
eta_ela_roles as (
|
74
|
+
select
|
75
|
+
e.case_id,
|
76
|
+
eu.user_id,
|
77
|
+
cr.id,
|
78
|
+
cr.role
|
79
|
+
from
|
80
|
+
events e
|
81
|
+
join event_roles er on er.event_id = e.id
|
82
|
+
join event_users eu on eu.event_role_id = er.id
|
83
|
+
join case_roles cr on
|
84
|
+
cr.case_id = e.case_id
|
85
|
+
and (
|
86
|
+
(cr.role = 'CTA' and er.role = 'ETA')
|
87
|
+
or (cr.role = 'CLA' and er.role = 'ELA')
|
88
|
+
)
|
89
|
+
where not e.closed
|
90
|
+
)
|
91
|
+
select * from roles
|
92
|
+
union
|
93
|
+
select * from res_roles
|
94
|
+
union
|
95
|
+
select * from eta_ela_roles
|
96
|
+
order by case_id, user_id -- FIXME: Yt
|
97
|
+
;
|
98
|
+
|
99
|
+
-- Combines case_role_users with event_roles and event_users. It is the set of
|
100
|
+
-- roles (case or event) that is associated with an event. Users that are no
|
101
|
+
-- longer associated with the case are excluded
|
102
|
+
drop view if exists event_role_users cascade;
|
103
|
+
create view event_role_users as
|
104
|
+
-- Roles inherited from case
|
105
|
+
select
|
106
|
+
e.id as "event_id",
|
107
|
+
cru.user_id,
|
108
|
+
cru.role_id,
|
109
|
+
cru.role
|
110
|
+
from
|
111
|
+
events e
|
112
|
+
join case_role_users cru on cru.case_id = e.case_id
|
113
|
+
where
|
114
|
+
not e.closed
|
115
|
+
|
116
|
+
union all
|
117
|
+
|
118
|
+
-- Roles from event users. KON and AKK are duplicates
|
119
|
+
select
|
120
|
+
e.id as "event_id",
|
121
|
+
eu.user_id,
|
122
|
+
er.id as "role_id",
|
123
|
+
er.role
|
124
|
+
from
|
125
|
+
events e
|
126
|
+
join event_roles er on er.event_id = e.id
|
127
|
+
join event_users eu on eu.event_role_id = er.id
|
128
|
+
where
|
129
|
+
not e.closed
|
130
|
+
|
131
|
+
order by event_id -- FIXME Yt
|
132
|
+
;
|
133
|
+
|
134
|
+
drop view if exists domain_role_users cascade;
|
135
|
+
create view domain_role_users as
|
136
|
+
select
|
137
|
+
case_id as "domain_id",
|
138
|
+
user_id,
|
139
|
+
role_id,
|
140
|
+
role
|
141
|
+
from
|
142
|
+
case_role_users
|
143
|
+
|
144
|
+
union all
|
145
|
+
|
146
|
+
select
|
147
|
+
event_id as "domain_id",
|
148
|
+
user_id,
|
149
|
+
role_id,
|
150
|
+
role
|
151
|
+
from
|
152
|
+
event_role_users
|
153
|
+
;
|
154
|
+
|
155
|
+
drop view if exists agg.domain_role_users cascade;
|
156
|
+
create view agg.domain_role_users as
|
157
|
+
select
|
158
|
+
domain_id,
|
159
|
+
user_id,
|
160
|
+
array_agg(role_id) as "role_ids",
|
161
|
+
array_agg(role) as "roles"
|
162
|
+
from
|
163
|
+
app_portal.domain_role_users
|
164
|
+
group by
|
165
|
+
domain_id,
|
166
|
+
user_id
|
167
|
+
;
|
168
|
+
|
169
|
+
drop view if exists name.domain_role_users cascade;
|
170
|
+
create view name.domain_role_users as
|
171
|
+
select
|
172
|
+
dru.domain_id,
|
173
|
+
dru.user_id,
|
174
|
+
dru.role_id,
|
175
|
+
coalesce(c.ident, e.label) as "domain",
|
176
|
+
u.rolename as "username",
|
177
|
+
dru.role
|
178
|
+
from
|
179
|
+
domain_role_users dru
|
180
|
+
join auth.roles u on u.id = dru.user_id
|
181
|
+
left join cases c on c.id = dru.domain_id
|
182
|
+
left join events e on e.id = dru.domain_id
|
183
|
+
;
|
184
|
+
|
185
|
+
drop view if exists agg_name.domain_role_users cascade;
|
186
|
+
create view agg_name.domain_role_users as
|
187
|
+
select
|
188
|
+
domain_id,
|
189
|
+
user_id,
|
190
|
+
domain,
|
191
|
+
username,
|
192
|
+
array_agg(role_id) as "role_ids",
|
193
|
+
array_agg(role) as "roles"
|
194
|
+
from
|
195
|
+
name.domain_role_users
|
196
|
+
group by
|
197
|
+
domain_id,
|
198
|
+
user_id,
|
199
|
+
domain,
|
200
|
+
username
|
201
|
+
order by domain_id, username
|
202
|
+
;
|
203
|
+
|
data/tests/auth.sql
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
\set ON_ERROR_STOP on
|
2
|
+
|
3
|
+
create table auth.roles (
|
4
|
+
id integer generated by default as identity primary key,
|
5
|
+
rolename varchar not null
|
6
|
+
);
|
7
|
+
|
8
|
+
create table auth.users (
|
9
|
+
id integer generated by default as identity primary key,
|
10
|
+
rolename varchar not null
|
11
|
+
);
|
12
|
+
|
data/tests/build
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
|
2
|
+
-- Returns an array of role IDs for the current user (from both auth and
|
3
|
+
-- app_portal). This array is matched against the relevant ACL field in the RLS
|
4
|
+
-- policies so performance is important (TODO)
|
5
|
+
drop function if exists public.current_role_ids() cascade;
|
6
|
+
create function public.current_role_ids() returns integer[] as $$
|
7
|
+
select acl_portal.select_user_acl(public.current_user_id());
|
8
|
+
$$ language sql
|
9
|
+
stable
|
10
|
+
leakproof
|
11
|
+
security definer
|
12
|
+
;
|
13
|
+
|
14
|
+
-- Returns an array of app_portal role names (kind) for the current user in the
|
15
|
+
-- given domain
|
16
|
+
drop function if exists public.current_domain_roles(integer) cascade;
|
17
|
+
create function public.current_domain_roles(_domain_id integer) returns text[] as $$
|
18
|
+
select coalesce(array_agg(dr.role), array[]::text[])
|
19
|
+
from acl_portal.domain_users du
|
20
|
+
join acl_portal.domain_roles dr on dr.id = du.domain_role_id
|
21
|
+
where du.user_id = public.current_user_id()
|
22
|
+
and dr.domain_id = _domain_id;
|
23
|
+
$$ language sql
|
24
|
+
stable
|
25
|
+
leakproof
|
26
|
+
security definer
|
27
|
+
;
|
28
|
+
|
data/tests/fox.sql
ADDED
@@ -0,0 +1,158 @@
|
|
1
|
+
\set ON_ERROR_STOP on
|
2
|
+
|
3
|
+
/*
|
4
|
+
delete from acl_portal.attach_acls;
|
5
|
+
|
6
|
+
insert into acl_portal.attach_acls (parent_table, parent_id, child_table, child_field, acls) values
|
7
|
+
('cases', 1, 'events', 'case_id', array[1]), -- case-hdj can create events on case 1
|
8
|
+
('events', 3, 'visits', 'event_id', array[16]), -- event-hdj can create visits on event 3 of case 1
|
9
|
+
('visits', 1, 'noncompliances', 'visit_id', array[16, 17]),
|
10
|
+
('visits', 2, 'noncompliances', 'visit_id', array[16, 2]),
|
11
|
+
('events', 3, 'bookings', 'event_id', array[16])
|
12
|
+
;
|
13
|
+
|
14
|
+
insert into acl_portal.attach_acls (parent_table, parent_id, child_table, child_field, acls)
|
15
|
+
select
|
16
|
+
'case_roles' as "parent_table",
|
17
|
+
id as "parent_id",
|
18
|
+
'case_users' as "child_table",
|
19
|
+
'case_role_id' as "child_id",
|
20
|
+
array[2] -- hdj
|
21
|
+
from
|
22
|
+
app_portal.case_roles
|
23
|
+
where case_id = 1
|
24
|
+
and role = 'CLA'
|
25
|
+
;
|
26
|
+
*/
|
27
|
+
|
28
|
+
delete from auth.users;
|
29
|
+
insert into auth.users select * from auth.roles;
|
30
|
+
|
31
|
+
select acl_portal.update_acls();
|
32
|
+
select acl_portal.update_user_acls();
|
33
|
+
|
34
|
+
/*
|
35
|
+
drop schema if exists acl cascade;
|
36
|
+
create schema acl;
|
37
|
+
set search_path to acl;
|
38
|
+
|
39
|
+
create view tables as
|
40
|
+
select distinct
|
41
|
+
schema_name,
|
42
|
+
table_name
|
43
|
+
from
|
44
|
+
meta.columns
|
45
|
+
where
|
46
|
+
column_name like 'acl\_%'
|
47
|
+
;
|
48
|
+
|
49
|
+
\echo ACL_TABLES
|
50
|
+
select * from tables where schema_name = 'app_portal';
|
51
|
+
|
52
|
+
-- Subset of meta.columns where both referencing and referenced tables are ACL tables
|
53
|
+
drop view if exists links cascade;
|
54
|
+
create view links as
|
55
|
+
select c.*
|
56
|
+
from tables tf
|
57
|
+
join meta.links c
|
58
|
+
on c.schema_name = tf.schema_name
|
59
|
+
and c.table_name = tf.table_name
|
60
|
+
join tables tt
|
61
|
+
on tt.schema_name = c.ref_schema_name
|
62
|
+
and tt.table_name = c.ref_table_name
|
63
|
+
;
|
64
|
+
|
65
|
+
\echo ACL_LINKS
|
66
|
+
select * from links where schema_name = 'app_portal';
|
67
|
+
|
68
|
+
-- Subset of meta.chains where first and last table are ACL tables
|
69
|
+
drop view if exists chains cascade;
|
70
|
+
create view chains as
|
71
|
+
select mc.*
|
72
|
+
from meta.chains mc
|
73
|
+
join tables src on
|
74
|
+
mc.src_schema_name = src.schema_name
|
75
|
+
and mc.src_table_name = src.table_name
|
76
|
+
join tables dst on
|
77
|
+
mc.dst_schema_name = dst.schema_name
|
78
|
+
and mc.dst_table_name = dst.table_name
|
79
|
+
;
|
80
|
+
|
81
|
+
\echo ACL_CHAINS
|
82
|
+
--select * from chains where src_schema_name = 'app_portal';
|
83
|
+
|
84
|
+
drop view if exists closures cascade;
|
85
|
+
create view closures as
|
86
|
+
select
|
87
|
+
at.schema_name,
|
88
|
+
at.table_name,
|
89
|
+
coalesce(
|
90
|
+
array_agg(ac.src_schema_name || '.' || ac.src_table_name) filter (where ac.src_schema_name is not null),
|
91
|
+
array[]::varchar[]
|
92
|
+
) as closure_uids
|
93
|
+
from
|
94
|
+
tables at
|
95
|
+
left join chains ac
|
96
|
+
on ac.dst_schema_name = at.schema_name
|
97
|
+
and ac.dst_table_name = at.table_name
|
98
|
+
group by
|
99
|
+
at.schema_name,
|
100
|
+
at.table_name
|
101
|
+
;
|
102
|
+
|
103
|
+
\echo ACL_CLOSURES
|
104
|
+
--select * from closures;
|
105
|
+
|
106
|
+
drop view if exists paths cascade;
|
107
|
+
create view paths as
|
108
|
+
with
|
109
|
+
recursive search_path as (
|
110
|
+
with edges as (
|
111
|
+
select distinct
|
112
|
+
schema_name || '.' || table_name as "from_table",
|
113
|
+
ref_schema_name || '.' || ref_table_name as "to_table",
|
114
|
+
column_name as "from_column",
|
115
|
+
ref_column_name as "to_column"
|
116
|
+
from
|
117
|
+
acl.links
|
118
|
+
)
|
119
|
+
|
120
|
+
-- Anchor member: start from the initial node
|
121
|
+
select
|
122
|
+
from_table as "start_table",
|
123
|
+
from_table,
|
124
|
+
to_table,
|
125
|
+
array[from_table] as path,
|
126
|
+
array[array[from_table || '.' || from_column, to_table || '.' || to_column]] as link,
|
127
|
+
false as cycle
|
128
|
+
from edges
|
129
|
+
|
130
|
+
union all
|
131
|
+
|
132
|
+
-- Recursive member: find the next links
|
133
|
+
select
|
134
|
+
sp.start_table,
|
135
|
+
e.from_table,
|
136
|
+
e.to_table,
|
137
|
+
sp.path || e.from_table as "path",
|
138
|
+
sp.link || array[array[e.from_table || '.' || e.from_column, e.to_table || '.' || e.to_column]] as "link",
|
139
|
+
e.from_table = any(sp.path) as "cycle"
|
140
|
+
from search_path sp
|
141
|
+
join edges e on e.from_table = sp.to_table
|
142
|
+
where not sp.cycle
|
143
|
+
)
|
144
|
+
select
|
145
|
+
start_table,
|
146
|
+
to_table as "stop_table",
|
147
|
+
path || to_table as "path",
|
148
|
+
link as "links"
|
149
|
+
from search_path
|
150
|
+
order by
|
151
|
+
start_table,
|
152
|
+
"stop_table"
|
153
|
+
;
|
154
|
+
|
155
|
+
\echo ACL_PATHS
|
156
|
+
--select * from paths;
|
157
|
+
|
158
|
+
*/
|
data/tests/meta.sql
ADDED
@@ -0,0 +1,197 @@
|
|
1
|
+
|
2
|
+
\set ON_ERROR_STOP on
|
3
|
+
|
4
|
+
drop schema if exists meta cascade;
|
5
|
+
create schema meta;
|
6
|
+
set search_path to meta;
|
7
|
+
|
8
|
+
create view schemas as
|
9
|
+
select
|
10
|
+
nspname as "schema_name"
|
11
|
+
from
|
12
|
+
pg_namespace
|
13
|
+
where nspname <> 'information_schema'
|
14
|
+
and nspname not like 'pg\_%'
|
15
|
+
and nspname not like 'fdw\_%'
|
16
|
+
and nspname not in ('pgcrypto') -- array because more libraries may come
|
17
|
+
;
|
18
|
+
|
19
|
+
create view tables as
|
20
|
+
select
|
21
|
+
relnamespace::regnamespace::varchar as "schema_name",
|
22
|
+
relname as "table_name"
|
23
|
+
from
|
24
|
+
pg_class
|
25
|
+
where
|
26
|
+
relkind = 'r'
|
27
|
+
and relnamespace::regnamespace::varchar <> 'information_schema'
|
28
|
+
and relnamespace::regnamespace::varchar not like 'pg\_%'
|
29
|
+
-- FIXME relation "fdw_webtoolpublished.publisheddata" does not exist
|
30
|
+
and relnamespace::regnamespace::varchar not like 'fdw\_%'
|
31
|
+
;
|
32
|
+
|
33
|
+
\echo META_TABLES
|
34
|
+
select *
|
35
|
+
from tables
|
36
|
+
where
|
37
|
+
schema_name = 'app_portal'
|
38
|
+
;
|
39
|
+
|
40
|
+
create view columns as
|
41
|
+
with
|
42
|
+
referencing_fields as (
|
43
|
+
select
|
44
|
+
tf.relnamespace::regnamespace::varchar as "schema_name",
|
45
|
+
tf.relname as "table_name", -- avoids qualified table name
|
46
|
+
af.attname as "column_name",
|
47
|
+
tt.relnamespace::regnamespace::varchar as "ref_schema_name",
|
48
|
+
tt.relname as "ref_table_name", -- avoids qualified table name
|
49
|
+
at.attname as "ref_column_name"
|
50
|
+
from
|
51
|
+
pg_constraint as c
|
52
|
+
join pg_attribute as af on af.attnum = any(c.conkey) and af.attrelid = c.conrelid
|
53
|
+
join pg_attribute as at on at.attnum = any(c.confkey) and at.attrelid = c.confrelid
|
54
|
+
join pg_class tf on tf.oid = c.conrelid
|
55
|
+
join pg_class tt on tt.oid = c.confrelid
|
56
|
+
where
|
57
|
+
c.contype = 'f'
|
58
|
+
order by
|
59
|
+
"schema_name",
|
60
|
+
"table_name",
|
61
|
+
"column_name"
|
62
|
+
)
|
63
|
+
select
|
64
|
+
t.schema_name,
|
65
|
+
t.table_name,
|
66
|
+
af.attname as "column_name",
|
67
|
+
at.ref_schema_name,
|
68
|
+
at.ref_table_name,
|
69
|
+
at.ref_column_name,
|
70
|
+
af.attnum as "ordinal"
|
71
|
+
from
|
72
|
+
tables t
|
73
|
+
join pg_attribute af
|
74
|
+
on af.attrelid = (schema_name || '.' || table_name)::regclass
|
75
|
+
and af.attnum > 0
|
76
|
+
left join referencing_fields at
|
77
|
+
on at.schema_name = t.schema_name
|
78
|
+
and at.table_name = t.table_name
|
79
|
+
and at.column_name = af.attname
|
80
|
+
order by
|
81
|
+
"schema_name",
|
82
|
+
"table_name",
|
83
|
+
"ordinal"
|
84
|
+
;
|
85
|
+
|
86
|
+
\echo META_COLUMNS
|
87
|
+
/*
|
88
|
+
select
|
89
|
+
schema_name, table_name, column_name,
|
90
|
+
ref_schema_name, ref_table_name, ref_column_name
|
91
|
+
from columns
|
92
|
+
where
|
93
|
+
schema_name = 'app_portal'
|
94
|
+
;
|
95
|
+
*/
|
96
|
+
|
97
|
+
create view links as
|
98
|
+
select *
|
99
|
+
from columns
|
100
|
+
where ref_table_name is not null
|
101
|
+
;
|
102
|
+
|
103
|
+
\echo META_LINKS
|
104
|
+
--select * from links;
|
105
|
+
|
106
|
+
create view chains as
|
107
|
+
with
|
108
|
+
recursive search_path as (
|
109
|
+
with
|
110
|
+
edges as (
|
111
|
+
select distinct
|
112
|
+
schema_name || '.' || table_name as "from_table",
|
113
|
+
ref_schema_name || '.' || ref_table_name as "to_table",
|
114
|
+
column_name as "from_column",
|
115
|
+
ref_column_name as "to_column"
|
116
|
+
from
|
117
|
+
links
|
118
|
+
)
|
119
|
+
|
120
|
+
-- Anchor member: src from the initial node
|
121
|
+
select
|
122
|
+
from_table as "src_table",
|
123
|
+
from_table,
|
124
|
+
to_table,
|
125
|
+
array[from_table] as path,
|
126
|
+
array[array[from_table || '.' || from_column, to_table || '.' || to_column]] as link,
|
127
|
+
false as cycle
|
128
|
+
from edges
|
129
|
+
|
130
|
+
union all
|
131
|
+
|
132
|
+
-- Recursive member: find the next links
|
133
|
+
select
|
134
|
+
sp.src_table,
|
135
|
+
e.from_table,
|
136
|
+
e.to_table,
|
137
|
+
sp.path || e.from_table as "path",
|
138
|
+
sp.link || array[array[e.from_table || '.' || e.from_column, e.to_table || '.' || e.to_column]] as "link",
|
139
|
+
e.from_table = any(sp.path) as "cycle"
|
140
|
+
from search_path sp
|
141
|
+
join edges e on e.from_table = sp.to_table
|
142
|
+
where not sp.cycle
|
143
|
+
)
|
144
|
+
select
|
145
|
+
regexp_replace(src_table, '\..*', '') as src_schema_name,
|
146
|
+
regexp_replace(src_table, '^.*\.', '') as src_table_name,
|
147
|
+
regexp_replace(to_table, '\..*', '') as dst_schema_name,
|
148
|
+
regexp_replace(to_table, '^.*\.', '') as dst_table_name,
|
149
|
+
path || to_table as "path",
|
150
|
+
link as "links"
|
151
|
+
from search_path
|
152
|
+
order by
|
153
|
+
"src_schema_name",
|
154
|
+
"src_table_name",
|
155
|
+
"dst_schema_name",
|
156
|
+
"dst_table_name"
|
157
|
+
;
|
158
|
+
|
159
|
+
\echo META_CHAINS
|
160
|
+
--select * from chains;
|
161
|
+
|
162
|
+
create view closures as
|
163
|
+
select
|
164
|
+
mt.schema_name,
|
165
|
+
mt.table_name,
|
166
|
+
coalesce(
|
167
|
+
array_agg(mc.src_schema_name || '.' || mc.src_table_name) filter (where mc.src_schema_name is not null),
|
168
|
+
array[]::varchar[]
|
169
|
+
) as closure_uids
|
170
|
+
from
|
171
|
+
tables mt
|
172
|
+
left join chains mc
|
173
|
+
on mc.dst_schema_name = mt.schema_name
|
174
|
+
and mc.dst_table_name = mt.table_name
|
175
|
+
group by
|
176
|
+
mt.schema_name,
|
177
|
+
mt.table_name
|
178
|
+
;
|
179
|
+
|
180
|
+
\echo META_CLOSURES
|
181
|
+
--select * from closures;
|
182
|
+
|
183
|
+
create view functions as
|
184
|
+
select
|
185
|
+
s.schema_name,
|
186
|
+
proname as "function_name",
|
187
|
+
string_to_array(pg_catalog.pg_get_function_identity_arguments(oid), ',') as arguments,
|
188
|
+
pg_catalog.format_type(prorettype, null) as return_type,
|
189
|
+
proconfig as "config"
|
190
|
+
from
|
191
|
+
meta.schemas s
|
192
|
+
join pg_proc p on p.pronamespace::regnamespace::varchar = s.schema_name
|
193
|
+
order by
|
194
|
+
s.schema_name,
|
195
|
+
"function_name"
|
196
|
+
;
|
197
|
+
|
data/tests/schemas.sql
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
\set ON_ERROR_STOP on
|
2
|
+
|
3
|
+
drop schema if exists app_portal cascade;
|
4
|
+
drop schema if exists acl_portal cascade;
|
5
|
+
drop schema if exists sys_portal cascade;
|
6
|
+
|
7
|
+
drop schema if exists meta cascade;
|
8
|
+
drop schema if exists acl cascade;
|
9
|
+
|
10
|
+
drop schema if exists agg cascade;
|
11
|
+
drop schema if exists name cascade;
|
12
|
+
drop schema if exists agg_name cascade;
|
13
|
+
|
14
|
+
drop schema if exists auth cascade;
|
15
|
+
|
16
|
+
create schema auth;
|
17
|
+
create schema app_portal;
|
18
|
+
create schema acl_portal;
|
19
|
+
create schema sys_portal;
|
20
|
+
create schema meta;
|
21
|
+
create schema acl;
|
22
|
+
create schema agg;
|
23
|
+
create schema name;
|
24
|
+
create schema agg_name;
|
25
|
+
|
data/tests/setup.sql
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
|
2
|
+
grant all on schema app_portal to alt;
|
3
|
+
grant all on all tables in schema app_portal to alt;
|
4
|
+
alter table cases enable row level security;
|
5
|
+
alter table events enable row level security;
|
6
|
+
alter table visits enable row level security;
|
7
|
+
alter table noncompliances enable row level security;
|
8
|
+
|