letopis 0.20.3 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENT-CHEATSHEET.en.md +368 -0
- package/AGENT-CHEATSHEET.md +354 -0
- package/CHANGELOG.md +348 -0
- package/MIGRATION.md +190 -0
- package/README.en.md +1937 -0
- package/README.md +1493 -3466
- package/dist/acl.d.ts +26 -50
- package/dist/acl.js +22 -267
- package/dist/admin.d.ts +138 -0
- package/dist/admin.js +170 -0
- package/dist/auth.d.ts +120 -73
- package/dist/auth.js +121 -306
- package/dist/cache.d.ts +73 -0
- package/dist/cache.js +148 -0
- package/dist/chain.d.ts +124 -191
- package/dist/chain.js +369 -551
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +164 -0
- package/dist/demo/booking.d.ts +289 -0
- package/dist/demo/booking.js +159 -0
- package/dist/errors.d.ts +29 -0
- package/dist/errors.js +70 -0
- package/dist/import.d.ts +179 -0
- package/dist/import.js +792 -0
- package/dist/index.d.ts +172 -26
- package/dist/index.js +304 -178
- package/dist/jsonschema.d.ts +22 -0
- package/dist/jsonschema.js +167 -0
- package/dist/load.d.ts +76 -0
- package/dist/load.js +884 -0
- package/dist/model.d.ts +166 -0
- package/dist/model.js +224 -0
- package/dist/ops.d.ts +7 -6
- package/dist/ops.js +7 -51
- package/dist/pglite.d.ts +22 -0
- package/dist/pglite.js +45 -0
- package/dist/registry.d.ts +57 -0
- package/dist/registry.js +82 -0
- package/dist/sql.d.ts +59 -142
- package/dist/sql.js +568 -654
- package/dist/sync.d.ts +31 -0
- package/dist/sync.js +108 -0
- package/dist/tx.d.ts +129 -8
- package/dist/tx.js +300 -73
- package/dist/typed.d.ts +97 -0
- package/dist/typed.js +1 -0
- package/dist/types.d.ts +71 -250
- package/dist/types.js +27 -108
- package/dist/up.d.ts +140 -47
- package/dist/up.js +339 -267
- package/dist/uuid.d.ts +21 -6
- package/dist/uuid.js +48 -64
- package/dist/validate.d.ts +24 -0
- package/dist/validate.js +251 -0
- package/dist/watch.d.ts +62 -0
- package/dist/watch.js +168 -0
- package/dist/write.d.ts +117 -74
- package/dist/write.js +658 -720
- package/llms.txt +26 -0
- package/package.json +49 -19
- package/sql/10-core.sql +136 -0
- package/sql/15-errors.sql +60 -0
- package/sql/20-context.sql +153 -0
- package/sql/30-validate.sql +423 -0
- package/sql/40-class.sql +259 -0
- package/sql/50-acl.sql +539 -0
- package/sql/60-write.sql +1369 -0
- package/sql/70-read.sql +245 -0
- package/sql/80-auth.sql +827 -0
- package/sql/90-time.sql +957 -0
- package/sql/95-seed.system.sql +178 -0
- package/sql/99-revision.sql +3 -0
- package/sql/README.md +56 -0
- package/sql/seed.booking.sql +39 -112
- package/dist/schema.d.ts +0 -15
- package/dist/schema.js +0 -351
- package/dist/sessions.d.ts +0 -32
- package/dist/sessions.js +0 -114
- package/dist/tables.d.ts +0 -105
- package/dist/tables.js +0 -248
- package/docker/Dockerfile +0 -40
- package/docker/start.sh +0 -18
- package/scripts/check-docs.mjs +0 -375
- package/scripts/gen-api-contract.mjs +0 -226
- package/scripts/gen-types.mjs +0 -350
- package/scripts/release-notes.mjs +0 -76
- package/scripts/schema-sync.mjs +0 -185
- package/sql/ddl.sql +0 -600
- package/sql/seed.auth.sql +0 -73
package/sql/70-read.sql
ADDED
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
-- letopis 1.0 — поиск id для шагов цепочки (план, §2.7, §3 «Индексы под RLS», этап 3, п. 3).
|
|
2
|
+
--
|
|
3
|
+
-- Под RLS PostgreSQL пользуется индексом только для операторов, помеченных leakproof (равенство
|
|
4
|
+
-- uuid и текста); поиск внутри jsonb и массивов (@>) к ним не относится, и пометить его так может
|
|
5
|
+
-- только суперпользователь. Поэтому шаг цепочки делится на две части:
|
|
6
|
+
-- 1) функции владельца find_* выполняются без RLS и находят id по индексам GIN (entity_ends,
|
|
7
|
+
-- entity_data), сами применяя то же условие видимости, что и политики RLS (row_visible с планом
|
|
8
|
+
-- прав READ актора — acl_actor);
|
|
9
|
+
-- 2) строки читаются по этим id через первичный ключ под RLS, и там же применяются остальные
|
|
10
|
+
-- условия шага: операторы с приведением типов, диапазоны, like.
|
|
11
|
+
-- Прямой вызов find_* сырым SQL возвращает только id строк, которые пользователь и так видит.
|
|
12
|
+
-- Принимаются только условия вложения (@>), которые не падают на неожиданных данных.
|
|
13
|
+
-- Обратный переход и deep пробуют индекс entity_ends на каждый id отдельно (lateral … offset 0 —
|
|
14
|
+
-- подзапрос не поднимается в общий план, и планировщик не меняет пробы на перебор таблицы), а класс,
|
|
15
|
+
-- видимость и условия на data проверяют уже на найденных строках: пересечение каждой пробы с
|
|
16
|
+
-- индексами (class, tenant) или data перебирало бы весь класс.
|
|
17
|
+
--
|
|
18
|
+
-- Источник — текущее состояние (entity) или журнал: версии, действовавшие на момент p_at (срез
|
|
19
|
+
-- asOf), либо надгробия удалённых (p_dead, для withDeleted). Возвращается rev версии: строки
|
|
20
|
+
-- журнала читаются по паре (id, rev) через уникальный индекс log_id_rev. Записи purge, trim и reset
|
|
21
|
+
-- (этап 6) — не версии объекта: на момент после них объекта в срезе нет.
|
|
22
|
+
|
|
23
|
+
-- Цели концов строки: всех (p_roles is null) или только перечисленных ролей.
|
|
24
|
+
create or replace function @SCHEMA@.role_ids(p_links jsonb, p_roles text[]) returns uuid[]
|
|
25
|
+
language sql immutable parallel safe
|
|
26
|
+
return (
|
|
27
|
+
select coalesce(array_agg(distinct (v #>> '{}')::uuid), '{}'::uuid[])
|
|
28
|
+
from jsonb_each(coalesce(p_links, '{}'::jsonb)) k
|
|
29
|
+
cross join lateral jsonb_array_elements(case jsonb_typeof(k.value) when 'array' then k.value else jsonb_build_array(k.value) end) v
|
|
30
|
+
where (p_roles is null or k.key = any (p_roles)) and jsonb_typeof(v) = 'string'
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
-- Ссылается ли строка на p_id хотя бы одной из ролей (одиночный конец или элемент множественного:
|
|
34
|
+
-- массив jsonb содержит строку-примитив).
|
|
35
|
+
create or replace function @SCHEMA@.role_has(p_links jsonb, p_roles text[], p_id uuid) returns boolean
|
|
36
|
+
language sql immutable parallel safe
|
|
37
|
+
return exists (select 1 from unnest(p_roles) r(role) where (p_links -> r.role) @> to_jsonb(p_id::text));
|
|
38
|
+
|
|
39
|
+
-- Версии классов p_classes, действовавшие на момент p_at (последняя версия id среди версий этих классов
|
|
40
|
+
-- не позже p_at; p_at null — последняя вообще), которые ссылаются на p_src: кандидаты — версии,
|
|
41
|
+
-- ссылавшиеся на p_src (GIN log_ends), затем последняя версия каждого кандидата (log_id_rev); ссылку
|
|
42
|
+
-- проверяет она. Класс и время кандидатов — после пробы (offset 0): иначе планировщик объединяет пробу
|
|
43
|
+
-- с log_class_at и читает весь класс в индексе. Без прав: вызывают только функции владельца find_*,
|
|
44
|
+
-- видимость — на них.
|
|
45
|
+
create or replace function @SCHEMA@.log_refs_at(p_classes text[], p_src uuid, p_at timestamptz)
|
|
46
|
+
returns setof @SCHEMA@.log
|
|
47
|
+
language sql stable
|
|
48
|
+
set search_path = pg_catalog, @SCHEMA@, pg_temp
|
|
49
|
+
as $$
|
|
50
|
+
select w.* from (
|
|
51
|
+
select distinct r.id from (
|
|
52
|
+
select l.id, l.class, l.at from @SCHEMA@.log l where @SCHEMA@.link_ids(l.links) @> array[p_src] offset 0) r
|
|
53
|
+
where r.class = any (p_classes) and r.at <= coalesce(p_at, 'infinity'::timestamptz)) c
|
|
54
|
+
cross join lateral (
|
|
55
|
+
select y.* from (select x.* from @SCHEMA@.log x where x.id = c.id offset 0) y
|
|
56
|
+
where y.class = any (p_classes) and y.at <= coalesce(p_at, 'infinity'::timestamptz)
|
|
57
|
+
order by y.rev desc limit 1) w
|
|
58
|
+
where @SCHEMA@.link_ids(w.links) @> array[p_src]
|
|
59
|
+
$$;
|
|
60
|
+
|
|
61
|
+
-- id строк классов p_classes: по вложению в data (GIN entity_data), по целям концов (GIN entity_ends),
|
|
62
|
+
-- по списку id (первичный ключ) или по классу (entity_class_tenant).
|
|
63
|
+
create or replace function @SCHEMA@.find_ids(p_classes text[], p_data jsonb, p_ends uuid[], p_ids uuid[],
|
|
64
|
+
p_at timestamptz default null, p_dead boolean default false)
|
|
65
|
+
returns table (id uuid, rev int)
|
|
66
|
+
language plpgsql stable security definer
|
|
67
|
+
set search_path = pg_catalog, @SCHEMA@, pg_temp
|
|
68
|
+
as $$
|
|
69
|
+
#variable_conflict use_column
|
|
70
|
+
declare
|
|
71
|
+
a jsonb := @SCHEMA@.acl_actor('READ');
|
|
72
|
+
c_tenant uuid := (a->>'tenant')::uuid;
|
|
73
|
+
c_account uuid := (a->>'account')::uuid;
|
|
74
|
+
c_plan jsonb := a->'plan';
|
|
75
|
+
begin
|
|
76
|
+
if c_account is null then
|
|
77
|
+
return;
|
|
78
|
+
end if;
|
|
79
|
+
if p_at is null and not p_dead then
|
|
80
|
+
-- отдельные ветви вместо «p is null or …»: иначе общий план не выбрал бы нужный индекс
|
|
81
|
+
if p_ids is not null then
|
|
82
|
+
return query select e.id, e.rev from @SCHEMA@.entity e
|
|
83
|
+
where e.id = any (p_ids) and e.class = any (p_classes)
|
|
84
|
+
and (p_data is null or e.data @> p_data) and (p_ends is null or e.ends @> p_ends)
|
|
85
|
+
and @SCHEMA@.row_visible(e.tenant, e.class, e.id, e.owner, e.tags, e.data, e.links, c_tenant, c_account, c_plan);
|
|
86
|
+
elsif p_data is not null and p_ends is not null then
|
|
87
|
+
return query select e.id, e.rev from @SCHEMA@.entity e
|
|
88
|
+
where e.class = any (p_classes) and e.data @> p_data and e.ends @> p_ends
|
|
89
|
+
and @SCHEMA@.row_visible(e.tenant, e.class, e.id, e.owner, e.tags, e.data, e.links, c_tenant, c_account, c_plan);
|
|
90
|
+
elsif p_data is not null then
|
|
91
|
+
return query select e.id, e.rev from @SCHEMA@.entity e
|
|
92
|
+
where e.class = any (p_classes) and e.data @> p_data
|
|
93
|
+
and @SCHEMA@.row_visible(e.tenant, e.class, e.id, e.owner, e.tags, e.data, e.links, c_tenant, c_account, c_plan);
|
|
94
|
+
elsif p_ends is not null then
|
|
95
|
+
return query select e.id, e.rev from @SCHEMA@.entity e
|
|
96
|
+
where e.class = any (p_classes) and e.ends @> p_ends
|
|
97
|
+
and @SCHEMA@.row_visible(e.tenant, e.class, e.id, e.owner, e.tags, e.data, e.links, c_tenant, c_account, c_plan);
|
|
98
|
+
else
|
|
99
|
+
return query select e.id, e.rev from @SCHEMA@.entity e
|
|
100
|
+
where e.class = any (p_classes)
|
|
101
|
+
and @SCHEMA@.row_visible(e.tenant, e.class, e.id, e.owner, e.tags, e.data, e.links, c_tenant, c_account, c_plan);
|
|
102
|
+
end if;
|
|
103
|
+
elsif p_ids is not null then
|
|
104
|
+
-- журнал: последняя версия каждого id не позже p_at; удалён — если это надгробие
|
|
105
|
+
return query select v.id, v.rev from (
|
|
106
|
+
select distinct on (l.id) l.id, l.rev, l.op, l.class, l.tenant, l.owner, l.tags, l.data, l.links
|
|
107
|
+
from @SCHEMA@.log l
|
|
108
|
+
where l.id = any (p_ids) and l.at <= coalesce(p_at, 'infinity'::timestamptz)
|
|
109
|
+
order by l.id, l.rev desc) v
|
|
110
|
+
where v.class = any (p_classes) and (v.op = 'delete') = p_dead and v.op not in ('purge', 'trim', 'reset')
|
|
111
|
+
and (p_data is null or v.data @> p_data) and (p_ends is null or @SCHEMA@.link_ids(v.links) @> p_ends)
|
|
112
|
+
and @SCHEMA@.row_visible(v.tenant, v.class, v.id, v.owner, v.tags, v.data, v.links, c_tenant, c_account, c_plan);
|
|
113
|
+
elsif p_ends is not null then
|
|
114
|
+
-- по целям концов — от версий, ссылавшихся на первую цель (GIN log_ends)
|
|
115
|
+
return query select v.id, v.rev from @SCHEMA@.log_refs_at(p_classes, p_ends[1], p_at) v
|
|
116
|
+
where (v.op = 'delete') = p_dead and v.op not in ('purge', 'trim', 'reset')
|
|
117
|
+
and (p_data is null or v.data @> p_data) and @SCHEMA@.link_ids(v.links) @> p_ends
|
|
118
|
+
and @SCHEMA@.row_visible(v.tenant, v.class, v.id, v.owner, v.tags, v.data, v.links, c_tenant, c_account, c_plan);
|
|
119
|
+
else
|
|
120
|
+
return query select v.id, v.rev from (
|
|
121
|
+
select distinct on (l.id) l.id, l.rev, l.op, l.class, l.tenant, l.owner, l.tags, l.data, l.links
|
|
122
|
+
from @SCHEMA@.log l
|
|
123
|
+
where l.class = any (p_classes) and l.at <= coalesce(p_at, 'infinity'::timestamptz)
|
|
124
|
+
order by l.id, l.rev desc) v
|
|
125
|
+
where (v.op = 'delete') = p_dead and v.op not in ('purge', 'trim', 'reset')
|
|
126
|
+
and (p_data is null or v.data @> p_data) and (p_ends is null or @SCHEMA@.link_ids(v.links) @> p_ends)
|
|
127
|
+
and @SCHEMA@.row_visible(v.tenant, v.class, v.id, v.owner, v.tags, v.data, v.links, c_tenant, c_account, c_plan);
|
|
128
|
+
end if;
|
|
129
|
+
end $$;
|
|
130
|
+
|
|
131
|
+
-- Обратный переход: пары «откуда, куда» — строки классов p_classes, которые ссылаются на id из p_src
|
|
132
|
+
-- (любым концом или только ролями p_roles). Соединение с развёрнутым списком id: на каждый id —
|
|
133
|
+
-- одна проба индекса entity_ends (решение 5 точки А: «ends && <массив>» на широких списках медленно).
|
|
134
|
+
create or replace function @SCHEMA@.find_hop(p_classes text[], p_data jsonb, p_ends uuid[], p_roles text[], p_src uuid[],
|
|
135
|
+
p_at timestamptz default null, p_dead boolean default false)
|
|
136
|
+
returns table (src uuid, id uuid, rev int)
|
|
137
|
+
language plpgsql stable security definer
|
|
138
|
+
set search_path = pg_catalog, @SCHEMA@, pg_temp
|
|
139
|
+
as $$
|
|
140
|
+
#variable_conflict use_column
|
|
141
|
+
declare
|
|
142
|
+
a jsonb := @SCHEMA@.acl_actor('READ');
|
|
143
|
+
c_tenant uuid := (a->>'tenant')::uuid;
|
|
144
|
+
c_account uuid := (a->>'account')::uuid;
|
|
145
|
+
c_plan jsonb := a->'plan';
|
|
146
|
+
begin
|
|
147
|
+
if c_account is null or p_src is null then
|
|
148
|
+
return;
|
|
149
|
+
end if;
|
|
150
|
+
if p_at is null and not p_dead then
|
|
151
|
+
return query select s.src, e.id, e.rev
|
|
152
|
+
from (select distinct u.x as src from unnest(p_src) u(x)) s
|
|
153
|
+
cross join lateral (
|
|
154
|
+
select x.id, x.rev, x.class, x.tenant, x.owner, x.tags, x.data, x.ends, x.links from @SCHEMA@.entity x
|
|
155
|
+
where x.ends @> array[s.src] offset 0) e
|
|
156
|
+
where e.class = any (p_classes)
|
|
157
|
+
and @SCHEMA@.row_visible(e.tenant, e.class, e.id, e.owner, e.tags, e.data, e.links, c_tenant, c_account, c_plan)
|
|
158
|
+
and (p_data is null or e.data @> p_data) and (p_ends is null or e.ends @> p_ends)
|
|
159
|
+
and (p_roles is null or @SCHEMA@.role_has(e.links, p_roles, s.src));
|
|
160
|
+
else
|
|
161
|
+
-- на каждый id источника — версии, ссылавшиеся на него (log_refs_at), а не все версии класса
|
|
162
|
+
return query select s.src, v.id, v.rev
|
|
163
|
+
from (select distinct u.x as src from unnest(p_src) u(x)) s
|
|
164
|
+
cross join lateral @SCHEMA@.log_refs_at(p_classes, s.src, p_at) v
|
|
165
|
+
where (v.op = 'delete') = p_dead and v.op not in ('purge', 'trim', 'reset')
|
|
166
|
+
and (p_data is null or v.data @> p_data) and (p_ends is null or @SCHEMA@.link_ids(v.links) @> p_ends)
|
|
167
|
+
and (p_roles is null or @SCHEMA@.role_has(v.links, p_roles, s.src))
|
|
168
|
+
and @SCHEMA@.row_visible(v.tenant, v.class, v.id, v.owner, v.tags, v.data, v.links, c_tenant, c_account, c_plan);
|
|
169
|
+
end if;
|
|
170
|
+
end $$;
|
|
171
|
+
|
|
172
|
+
-- Рекурсивный обход (deep): потомки любой глубины до p_max по обратным ссылкам строк классов
|
|
173
|
+
-- p_classes. Видимость проверяется на каждом уровне: невидимый узел обрывает своё поддерево.
|
|
174
|
+
-- Циклы в данных обход не зацикливают. Для каждой пары (src, id) — наименьшая глубина.
|
|
175
|
+
create or replace function @SCHEMA@.find_deep(p_classes text[], p_roles text[], p_src uuid[], p_max int,
|
|
176
|
+
p_at timestamptz default null)
|
|
177
|
+
returns table (src uuid, id uuid, rev int, depth int)
|
|
178
|
+
language plpgsql stable security definer
|
|
179
|
+
set search_path = pg_catalog, @SCHEMA@, pg_temp
|
|
180
|
+
as $$
|
|
181
|
+
#variable_conflict use_column
|
|
182
|
+
declare
|
|
183
|
+
a jsonb := @SCHEMA@.acl_actor('READ');
|
|
184
|
+
c_tenant uuid := (a->>'tenant')::uuid;
|
|
185
|
+
c_account uuid := (a->>'account')::uuid;
|
|
186
|
+
c_plan jsonb := a->'plan';
|
|
187
|
+
begin
|
|
188
|
+
if c_account is null or p_src is null then
|
|
189
|
+
return;
|
|
190
|
+
end if;
|
|
191
|
+
if p_at is null then
|
|
192
|
+
return query with recursive d(src, id, rev, depth, seen) as (
|
|
193
|
+
select s.src, e.id, e.rev, 1, array[s.src, e.id]
|
|
194
|
+
from (select distinct u.x as src from unnest(p_src) u(x)) s
|
|
195
|
+
cross join lateral (
|
|
196
|
+
select x.id, x.rev, x.class, x.tenant, x.owner, x.tags, x.data, x.links from @SCHEMA@.entity x where x.ends @> array[s.src] offset 0) e
|
|
197
|
+
where e.class = any (p_classes)
|
|
198
|
+
and (p_roles is null or @SCHEMA@.role_has(e.links, p_roles, s.src))
|
|
199
|
+
and @SCHEMA@.row_visible(e.tenant, e.class, e.id, e.owner, e.tags, e.data, e.links, c_tenant, c_account, c_plan)
|
|
200
|
+
union all
|
|
201
|
+
select d.src, e.id, e.rev, d.depth + 1, d.seen || e.id
|
|
202
|
+
from d
|
|
203
|
+
cross join lateral (
|
|
204
|
+
select x.id, x.rev, x.class, x.tenant, x.owner, x.tags, x.data, x.links from @SCHEMA@.entity x where x.ends @> array[d.id] offset 0) e
|
|
205
|
+
where d.depth < p_max and not (e.id = any (d.seen)) and e.class = any (p_classes)
|
|
206
|
+
and (p_roles is null or @SCHEMA@.role_has(e.links, p_roles, d.id))
|
|
207
|
+
and @SCHEMA@.row_visible(e.tenant, e.class, e.id, e.owner, e.tags, e.data, e.links, c_tenant, c_account, c_plan))
|
|
208
|
+
select distinct on (d.src, d.id) d.src, d.id, d.rev, d.depth from d order by d.src, d.id, d.depth;
|
|
209
|
+
else
|
|
210
|
+
-- уровень за уровнем: версии на момент, ссылавшиеся на узел (log_refs_at), а не все версии класса
|
|
211
|
+
return query with recursive d(src, id, rev, depth, seen) as (
|
|
212
|
+
select s.src, v.id, v.rev, 1, array[s.src, v.id]
|
|
213
|
+
from (select distinct u.x as src from unnest(p_src) u(x)) s
|
|
214
|
+
cross join lateral @SCHEMA@.log_refs_at(p_classes, s.src, p_at) v
|
|
215
|
+
where v.op not in ('delete', 'purge', 'trim', 'reset')
|
|
216
|
+
and (p_roles is null or @SCHEMA@.role_has(v.links, p_roles, s.src))
|
|
217
|
+
and @SCHEMA@.row_visible(v.tenant, v.class, v.id, v.owner, v.tags, v.data, v.links, c_tenant, c_account, c_plan)
|
|
218
|
+
union all
|
|
219
|
+
select d.src, v.id, v.rev, d.depth + 1, d.seen || v.id
|
|
220
|
+
from d
|
|
221
|
+
cross join lateral @SCHEMA@.log_refs_at(p_classes, d.id, p_at) v
|
|
222
|
+
where d.depth < p_max and not (v.id = any (d.seen)) and v.op not in ('delete', 'purge', 'trim', 'reset')
|
|
223
|
+
and (p_roles is null or @SCHEMA@.role_has(v.links, p_roles, d.id))
|
|
224
|
+
and @SCHEMA@.row_visible(v.tenant, v.class, v.id, v.owner, v.tags, v.data, v.links, c_tenant, c_account, c_plan))
|
|
225
|
+
select distinct on (d.src, d.id) d.src, d.id, d.rev, d.depth from d order by d.src, d.id, d.depth;
|
|
226
|
+
end if;
|
|
227
|
+
end $$;
|
|
228
|
+
|
|
229
|
+
do $$
|
|
230
|
+
declare f text;
|
|
231
|
+
begin
|
|
232
|
+
foreach f in array array[
|
|
233
|
+
'role_ids(jsonb, text[])', 'role_has(jsonb, text[], uuid)', 'log_refs_at(text[], uuid, timestamptz)',
|
|
234
|
+
'find_ids(text[], jsonb, uuid[], uuid[], timestamptz, boolean)',
|
|
235
|
+
'find_hop(text[], jsonb, uuid[], text[], uuid[], timestamptz, boolean)',
|
|
236
|
+
'find_deep(text[], text[], uuid[], int, timestamptz)'] loop
|
|
237
|
+
execute format('alter function @SCHEMA@.%s owner to @OWNER@', f);
|
|
238
|
+
execute format('revoke all on function @SCHEMA@.%s from public', f);
|
|
239
|
+
end loop;
|
|
240
|
+
end $$;
|
|
241
|
+
-- построитель цепочек вызывает их от приложения; row_visible — внутри find_*
|
|
242
|
+
grant execute on function @SCHEMA@.role_ids(jsonb, text[]), @SCHEMA@.role_has(jsonb, text[], uuid),
|
|
243
|
+
@SCHEMA@.find_ids(text[], jsonb, uuid[], uuid[], timestamptz, boolean),
|
|
244
|
+
@SCHEMA@.find_hop(text[], jsonb, uuid[], text[], uuid[], timestamptz, boolean),
|
|
245
|
+
@SCHEMA@.find_deep(text[], text[], uuid[], int, timestamptz) to @APP@;
|