Compare commits
No commits in common. "main" and "migrations" have entirely different histories.
main
...
migrations
@ -1,3 +0,0 @@
|
||||
POSTGRES_USER=postgres
|
||||
POSTGRES_PASSWORD=password
|
||||
POSTGRES_DB=dnim
|
@ -1,2 +0,0 @@
|
||||
POSTGRES_USER=postgres
|
||||
POSTGRES_PASSWORD=password
|
@ -1,23 +0,0 @@
|
||||
FROM node:20-bookworm
|
||||
|
||||
ENV PIP_BREAK_SYSTEM_PACKAGES 1
|
||||
|
||||
RUN apt-get update && apt-get upgrade -y
|
||||
RUN DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends tzdata \
|
||||
&& apt-get install -y curl ca-certificates gnupg postgresql-common \
|
||||
&& YES=y sh /usr/share/postgresql-common/pgdg/apt.postgresql.org.sh \
|
||||
&& apt-get install -y postgresql-15
|
||||
RUN apt-get update \
|
||||
&& install -m 0755 -d /etc/apt/keyrings \
|
||||
&& curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg \
|
||||
&& chmod a+r /etc/apt/keyrings/docker.gpg \
|
||||
&& echo \
|
||||
"deb [arch="$(dpkg --print-architecture)" signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/debian \
|
||||
"$(. /etc/os-release && echo "$VERSION_CODENAME")" stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null \
|
||||
&& apt-get update \
|
||||
&& apt-get install -y docker-ce-cli docker-compose-plugin
|
||||
RUN set -x \
|
||||
&& apt-get install -y python3 python3-pip \
|
||||
&& pip install psycopg2-binary \
|
||||
&& pip install pg8000 \
|
||||
&& pip install migra
|
@ -1,44 +0,0 @@
|
||||
name: 'gen-migrations'
|
||||
on: { push: { branches: ['main'] } }
|
||||
|
||||
jobs:
|
||||
gen-migrations:
|
||||
if: |
|
||||
!startsWith(gitea.event.head_commit.message, 'chore')
|
||||
container:
|
||||
image: 'git.orionkindel.com/dnim/db-ci-runner:specified-policeman-saw-kindly'
|
||||
volumes:
|
||||
- '/run/user/1001/docker.sock:/run/user/1001/docker.sock'
|
||||
- '/var/run/postgresql:/var/run/postgresql'
|
||||
steps:
|
||||
- uses: 'actions/checkout@v3'
|
||||
with: { fetch-depth: 0, submodules: 'recursive', token: '${{ secrets._GITEA_TOKEN }}' }
|
||||
- name: 'git config'
|
||||
run: |
|
||||
git config --global user.email 'noreply@dnim.org'
|
||||
git config --global user.name '🤖'
|
||||
- name: 'update migrations submodule (and push if needed)'
|
||||
run: |
|
||||
set -ex
|
||||
git fetch --all --recurse-submodules
|
||||
git submodule update --init --remote migrations;
|
||||
git add -A
|
||||
git commit -m 'chore: update migrations' || true
|
||||
git push || true
|
||||
- name: 'gen migrations (and push if needed)'
|
||||
run: |
|
||||
set -ex
|
||||
./scripts/gen_migrations.sh
|
||||
if ! (git diff-index --quiet HEAD --ignore-submodules=none); then
|
||||
cd migrations
|
||||
git switch migrations
|
||||
git add -A
|
||||
git commit -m 'chore: babe wake up new migrations just dropped'
|
||||
git push -u origin migrations
|
||||
cd ../
|
||||
git add -A
|
||||
git commit -m 'chore: update migrations'
|
||||
git push
|
||||
fi
|
||||
env:
|
||||
DOCKER_HOST: 'unix:///run/user/1001/docker.sock' # HACK: rootless docker on gitea action runner
|
@ -1,15 +0,0 @@
|
||||
name: 'migrate-devel'
|
||||
on: { push: { branches: ['main'], paths: ['migrations'] } }
|
||||
|
||||
jobs:
|
||||
migrate-devel:
|
||||
container:
|
||||
image: 'git.orionkindel.com/dnim/db-ci-runner:specified-policeman-saw-kindly'
|
||||
volumes: ['/run/user/1001/docker.sock:/run/user/1001/docker.sock']
|
||||
steps:
|
||||
- uses: 'actions/checkout@v3'
|
||||
with: { fetch-depth: 0, ref: 'main', submodules: 'recursive' }
|
||||
- run: './scripts/migrate.sh'
|
||||
env:
|
||||
DOCKER_HOST: 'unix:///run/user/1001/docker.sock' # HACK: rootless docker on gitea action runner
|
||||
POSTGRES_URI: '${{ secrets.POSTGRES_DEVEL_URI }}'
|
@ -1,15 +0,0 @@
|
||||
name: 'migrate-stage'
|
||||
on: { push: { branches: ['main'], paths: ['migrations'] } }
|
||||
|
||||
jobs:
|
||||
migrate-stage:
|
||||
container:
|
||||
image: 'git.orionkindel.com/dnim/db-ci-runner:specified-policeman-saw-kindly'
|
||||
volumes: ['/run/user/1001/docker.sock:/run/user/1001/docker.sock']
|
||||
steps:
|
||||
- uses: 'actions/checkout@v3'
|
||||
with: { fetch-depth: 0, ref: 'main', submodules: 'recursive' }
|
||||
- run: './scripts/migrate.sh'
|
||||
env:
|
||||
DOCKER_HOST: 'unix:///run/user/1001/docker.sock' # HACK: rootless docker on gitea action runner
|
||||
POSTGRES_URI: '${{ secrets.POSTGRES_STAGE_URI }}'
|
5
.gitignore
vendored
5
.gitignore
vendored
@ -1,5 +0,0 @@
|
||||
.env
|
||||
data
|
||||
tmp
|
||||
head
|
||||
base
|
4
.gitmodules
vendored
4
.gitmodules
vendored
@ -1,4 +0,0 @@
|
||||
[submodule "migrations"]
|
||||
branch = migrations
|
||||
path = migrations
|
||||
url = git@git.orionkindel.com:dnim/db
|
@ -1 +0,0 @@
|
||||
postgres 15.3
|
1
01ee432_to_658ad01.sql
Normal file
1
01ee432_to_658ad01.sql
Normal file
@ -0,0 +1 @@
|
||||
|
1
032c1f7_to_3c7189c.sql
Normal file
1
032c1f7_to_3c7189c.sql
Normal file
@ -0,0 +1 @@
|
||||
|
33
047a51b_to_49bed63.sql
Normal file
33
047a51b_to_49bed63.sql
Normal file
@ -0,0 +1,33 @@
|
||||
alter type "public"."usr_username" rename attribute "username" to "str" cascade;
|
||||
|
||||
set check_function_bodies = off;
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.create_newtype_text(qualified_name text)
|
||||
RETURNS void
|
||||
LANGUAGE plpgsql
|
||||
AS $function$
|
||||
begin
|
||||
execute concat('create type ', qualified_name, ' as (str text);');
|
||||
execute concat( 'create function '
|
||||
, qualified_name || '_to_string(val ' || qualified_name || ')'
|
||||
, E' returns text language sql as \'select (val.str);\';'
|
||||
);
|
||||
execute concat( 'create function '
|
||||
, qualified_name || '_of_string(val text)'
|
||||
, ' returns ' || qualified_name || E' language sql as \'select row(val);\';'
|
||||
);
|
||||
end;
|
||||
$function$
|
||||
;
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.usr_username_of_string(val text)
|
||||
RETURNS usr_username
|
||||
LANGUAGE sql
|
||||
AS $function$select row(val);$function$
|
||||
;
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.usr_username_to_string(val usr_username)
|
||||
RETURNS text
|
||||
LANGUAGE sql
|
||||
AS $function$select (val.str);$function$
|
||||
;
|
1
04ffd5d_to_d89a4fa.sql
Normal file
1
04ffd5d_to_d89a4fa.sql
Normal file
@ -0,0 +1 @@
|
||||
|
1
0502154_to_87f7a4d.sql
Normal file
1
0502154_to_87f7a4d.sql
Normal file
@ -0,0 +1 @@
|
||||
|
1
053511d_to_70a8bca.sql
Normal file
1
053511d_to_70a8bca.sql
Normal file
@ -0,0 +1 @@
|
||||
|
1
0728ba9_to_442d4e8.sql
Normal file
1
0728ba9_to_442d4e8.sql
Normal file
@ -0,0 +1 @@
|
||||
|
192
0b9c597_to_0502154.sql
Normal file
192
0b9c597_to_0502154.sql
Normal file
@ -0,0 +1,192 @@
|
||||
create type "public"."usr_session_device" as enum ('linux', 'macos', 'win', 'android', 'ios', 'other');
|
||||
|
||||
create type "public"."usr_tag_or_email" as ("str" text);
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.usr_tag_or_email_of_string(val text)
|
||||
RETURNS usr_tag_or_email
|
||||
LANGUAGE sql
|
||||
AS $function$select row(val);$function$
|
||||
;
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.usr_tag_or_email_to_email(toe usr_tag_or_email)
|
||||
RETURNS email
|
||||
LANGUAGE plpgsql
|
||||
IMMUTABLE
|
||||
AS $function$
|
||||
begin
|
||||
if position('@' in usr_tag_or_email_to_string(toe)) > 0 then
|
||||
return email_of_string(usr_tag_or_email_to_string(toe));
|
||||
else
|
||||
return null;
|
||||
end if;
|
||||
end;
|
||||
$function$
|
||||
;
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.usr_tag_or_email_to_string(val usr_tag_or_email)
|
||||
RETURNS text
|
||||
LANGUAGE sql
|
||||
AS $function$select (val.str);$function$
|
||||
;
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.usr_tag_or_email_to_tag(toe usr_tag_or_email)
|
||||
RETURNS usr_tag
|
||||
LANGUAGE plpgsql
|
||||
IMMUTABLE
|
||||
AS $function$
|
||||
begin
|
||||
if usr_tag_or_email_to_email(toe) is null then
|
||||
return usr_tag_of_string(usr_tag_or_email_to_string(toe));
|
||||
else
|
||||
return null;
|
||||
end if;
|
||||
end;
|
||||
$function$
|
||||
;
|
||||
|
||||
|
||||
create type "public"."usr_session_key" as ("str" text);
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.usr_session_key_of_string(val text)
|
||||
RETURNS usr_session_key
|
||||
LANGUAGE sql
|
||||
AS $function$select row(val);$function$
|
||||
;
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.usr_session_key_to_string(val usr_session_key)
|
||||
RETURNS text
|
||||
LANGUAGE sql
|
||||
AS $function$select (val.str);$function$
|
||||
;
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.usr_session_key_gen()
|
||||
RETURNS usr_session_key
|
||||
LANGUAGE sql
|
||||
AS $function$
|
||||
select usr_session_key_of_string(
|
||||
md5(extract(epoch from now()) || gen_random_bytes(32) :: text)
|
||||
);
|
||||
$function$
|
||||
;
|
||||
|
||||
|
||||
create table "public"."usr_session" (
|
||||
"id" integer generated always as identity not null,
|
||||
"key" usr_session_key not null default usr_session_key_gen(),
|
||||
"expired" boolean not null default false,
|
||||
"expires_at" timestamp without time zone not null,
|
||||
"usr" integer not null,
|
||||
"location" text,
|
||||
"device" usr_session_device,
|
||||
"ip" inet
|
||||
);
|
||||
|
||||
|
||||
CREATE UNIQUE INDEX usr_session_key_key ON public.usr_session USING btree (key);
|
||||
|
||||
CREATE UNIQUE INDEX usr_session_pkey ON public.usr_session USING btree (id);
|
||||
|
||||
alter table "public"."usr_session" add constraint "usr_session_pkey" PRIMARY KEY using index "usr_session_pkey";
|
||||
|
||||
alter table "public"."usr_session" add constraint "usr_session_key_key" UNIQUE using index "usr_session_key_key";
|
||||
|
||||
alter table "public"."usr_session" add constraint "usr_session_usr_fkey" FOREIGN KEY (usr) REFERENCES usr(id) not valid;
|
||||
|
||||
alter table "public"."usr_session" validate constraint "usr_session_usr_fkey";
|
||||
|
||||
set check_function_bodies = off;
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.do_usr_session_immutable_columns()
|
||||
RETURNS trigger
|
||||
LANGUAGE plpgsql
|
||||
AS $function$
|
||||
begin
|
||||
if OLD.id <> NEW.id then
|
||||
raise exception 'public.usr_session.id is immutable' using errcode = 'restrict_violation';
|
||||
elsif OLD.key <> NEW.key then
|
||||
raise exception 'public.usr_session.key is immutable' using errcode = 'restrict_violation';
|
||||
elsif OLD.expires_at <> NEW.expires_at then
|
||||
raise exception 'public.usr_session.expires_at is immutable' using errcode = 'restrict_violation';
|
||||
elsif OLD.usr <> NEW.usr then
|
||||
raise exception 'public.usr_session.usr is immutable' using errcode = 'restrict_violation';
|
||||
elsif OLD.location <> NEW.location then
|
||||
raise exception 'public.usr_session.location is immutable' using errcode = 'restrict_violation';
|
||||
elsif OLD.device <> NEW.device then
|
||||
raise exception 'public.usr_session.device is immutable' using errcode = 'restrict_violation';
|
||||
elsif OLD.ip <> NEW.ip then
|
||||
raise exception 'public.usr_session.ip is immutable' using errcode = 'restrict_violation';
|
||||
end if;
|
||||
|
||||
return NEW;
|
||||
end;
|
||||
$function$
|
||||
;
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.usr_session_login(tag_or_email usr_tag_or_email, password text, remember boolean DEFAULT false, location text DEFAULT NULL::text, device usr_session_device DEFAULT NULL::usr_session_device, ip inet DEFAULT NULL::inet)
|
||||
RETURNS usr_session_key
|
||||
LANGUAGE plpgsql
|
||||
AS $function$
|
||||
declare
|
||||
usr public.usr;
|
||||
key public.usr_session_key;
|
||||
expires_at timestamp;
|
||||
begin
|
||||
usr := public.usr_session_login_validate(tag_or_email, password);
|
||||
|
||||
if remember then
|
||||
expires_at := now() + interval '1 week';
|
||||
else
|
||||
expires_at := now() + interval '1 hour';
|
||||
end if;
|
||||
|
||||
insert into public.usr_session
|
||||
(expires_at, usr, location, device, ip)
|
||||
values
|
||||
(expires_at, usr.id, location, device, ip)
|
||||
returning usr_session.key
|
||||
into key;
|
||||
|
||||
return key;
|
||||
end;
|
||||
$function$
|
||||
;
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.usr_session_login_validate(tag_or_email usr_tag_or_email, password text)
|
||||
RETURNS usr
|
||||
LANGUAGE plpgsql
|
||||
STABLE
|
||||
AS $function$
|
||||
declare
|
||||
usr_email public.email := public.usr_tag_or_email_to_email(tag_or_email);
|
||||
usr_tag public.usr_tag := public.usr_tag_or_email_to_tag(tag_or_email);
|
||||
usr public.usr;
|
||||
begin
|
||||
select *
|
||||
from public.usr as u
|
||||
where u.email = usr_email
|
||||
or u.tag = usr_tag
|
||||
into usr;
|
||||
|
||||
if usr.id = 1 or usr.tag = usr_tag_of_string('system') then
|
||||
raise notice 'system user may not be logged into';
|
||||
raise exception 'incorrect password for user %', usr_tag_or_email_to_string(tag_or_email);
|
||||
end if;
|
||||
|
||||
if usr is null then
|
||||
-- prevent email guess bruteforces by raising the same exception
|
||||
-- for invalid password and user not found
|
||||
raise notice 'user % not found', usr_tag_or_email_to_string(tag_or_email);
|
||||
raise exception 'incorrect password for user %', usr_tag_or_email_to_string(tag_or_email);
|
||||
end if;
|
||||
|
||||
if not hashed_text_matches(password, usr.password) then
|
||||
raise notice 'password does not match for user %', usr_tag_or_email_to_string(tag_or_email);
|
||||
raise exception 'incorrect password for user %', usr_tag_or_email_to_string(tag_or_email);
|
||||
end if;
|
||||
|
||||
return usr;
|
||||
end;
|
||||
$function$
|
||||
;
|
||||
|
||||
CREATE TRIGGER trigger_usr_session_immutable_columns BEFORE UPDATE ON public.usr_session FOR EACH ROW EXECUTE FUNCTION do_usr_session_immutable_columns();
|
1
0d639e8_to_bfbd8ff.sql
Normal file
1
0d639e8_to_bfbd8ff.sql
Normal file
@ -0,0 +1 @@
|
||||
|
1
0db5ca4_to_0b9c597.sql
Normal file
1
0db5ca4_to_0b9c597.sql
Normal file
@ -0,0 +1 @@
|
||||
|
1
0e93478_to_1590c59.sql
Normal file
1
0e93478_to_1590c59.sql
Normal file
@ -0,0 +1 @@
|
||||
|
1
0eb9b42_to_2c288ef.sql
Normal file
1
0eb9b42_to_2c288ef.sql
Normal file
@ -0,0 +1 @@
|
||||
|
1
111338a_skipped.sql
Normal file
1
111338a_skipped.sql
Normal file
@ -0,0 +1 @@
|
||||
|
1
1590c59_to_01ee432.sql
Normal file
1
1590c59_to_01ee432.sql
Normal file
@ -0,0 +1 @@
|
||||
|
1
1652073_skipped.sql
Normal file
1
1652073_skipped.sql
Normal file
@ -0,0 +1 @@
|
||||
|
1
16a20c2_to_dcd1193.sql
Normal file
1
16a20c2_to_dcd1193.sql
Normal file
@ -0,0 +1 @@
|
||||
|
1
19fc181_to_d94188f.sql
Normal file
1
19fc181_to_d94188f.sql
Normal file
@ -0,0 +1 @@
|
||||
|
91
1bf3d29_to_c83a4ce.sql
Normal file
91
1bf3d29_to_c83a4ce.sql
Normal file
@ -0,0 +1,91 @@
|
||||
set check_function_bodies = off;
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.grp_admins()
|
||||
RETURNS grp
|
||||
LANGUAGE sql
|
||||
STABLE
|
||||
AS $function$select * from public.grp where tag = public.grp_tag_of_string('admins')$function$
|
||||
;
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.usr_root()
|
||||
RETURNS usr
|
||||
LANGUAGE sql
|
||||
STABLE
|
||||
AS $function$select * from public.usr where tag = public.usr_tag_of_string('root')$function$
|
||||
;
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.do_insert_usr_perm()
|
||||
RETURNS trigger
|
||||
LANGUAGE plpgsql
|
||||
AS $function$
|
||||
declare
|
||||
admins int;
|
||||
begin
|
||||
admins := public.grp_admins();
|
||||
|
||||
insert into public.perm
|
||||
(path, owner_user, owner_group, owner_user_mode, owner_group_mode, everyone_mode)
|
||||
values
|
||||
('/users/' || NEW.id || '/tag', NEW.id, admins, 'w', 'w', 'r')
|
||||
, ('/users/' || NEW.id || '/email', NEW.id, admins, 'w', 'w', '-')
|
||||
, ('/users/' || NEW.id || '/deleted', NEW.id, admins, 'w', 'w', '-')
|
||||
, ('/users/' || NEW.id || '/password', NEW.id, admins, 'w', 'w', '-')
|
||||
;
|
||||
|
||||
return new;
|
||||
end;
|
||||
$function$
|
||||
;
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.do_usr_create_default_grp()
|
||||
RETURNS trigger
|
||||
LANGUAGE plpgsql
|
||||
AS $function$
|
||||
declare
|
||||
new_grp int;
|
||||
begin
|
||||
insert into public.grp (tag)
|
||||
values (grp_tag_of_string('usr_' || new.uid))
|
||||
returning id into new_grp;
|
||||
|
||||
perform public.grp_add_member(to_grp => new_grp, add_usr => new.id);
|
||||
|
||||
update public.perm
|
||||
set owner_user = public.usr_root()
|
||||
, owner_group = public.grp_admins()
|
||||
where path = '/groups/' || new_grp || '/members'
|
||||
or path = '/groups/' || new_grp || '/tag';
|
||||
|
||||
return null;
|
||||
end;
|
||||
$function$
|
||||
;
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.get_acting_usr()
|
||||
RETURNS usr
|
||||
LANGUAGE plpgsql
|
||||
AS $function$
|
||||
declare
|
||||
uid text;
|
||||
acting_usr public.usr;
|
||||
begin
|
||||
if nullif(current_setting('dnim.usr_uid', true), '') is null then
|
||||
acting_usr := public.usr_root();
|
||||
else
|
||||
select u.*
|
||||
from public.usr u
|
||||
where u.uid = current_setting('dnim.usr_uid', true) :: uuid
|
||||
into acting_usr;
|
||||
end if;
|
||||
|
||||
return acting_usr;
|
||||
end;
|
||||
$function$
|
||||
;
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.grp_members_admins()
|
||||
RETURNS SETOF usr
|
||||
LANGUAGE sql
|
||||
STABLE
|
||||
AS $function$select * from public.grp_members((public.grp_admins()).id)$function$
|
||||
;
|
1
1c76bcb_skipped.sql
Normal file
1
1c76bcb_skipped.sql
Normal file
@ -0,0 +1 @@
|
||||
|
1
1dae8d2_to_e758bc6.sql
Normal file
1
1dae8d2_to_e758bc6.sql
Normal file
@ -0,0 +1 @@
|
||||
|
440
202de79_to_5f9c3c7.sql
Normal file
440
202de79_to_5f9c3c7.sql
Normal file
@ -0,0 +1,440 @@
|
||||
alter table "public"."usr_session" drop column "expired";
|
||||
|
||||
alter table "public"."usr_session" add column "remembered" boolean not null default false;
|
||||
|
||||
set check_function_bodies = off;
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.usr_session_touch(session usr_session_key)
|
||||
RETURNS usr
|
||||
LANGUAGE plpgsql
|
||||
AS $function$
|
||||
declare
|
||||
session public.usr_session;
|
||||
session_usr public.usr;
|
||||
new_exp timestamp;
|
||||
begin
|
||||
select s
|
||||
from public.usr_session s
|
||||
where s.key = session
|
||||
into session;
|
||||
|
||||
if session is null then
|
||||
raise exception 'usr_session_invalid';
|
||||
end if;
|
||||
|
||||
if session.expires_at <= now() then
|
||||
raise exception 'usr_session_expired';
|
||||
end if;
|
||||
|
||||
if session.remembered then
|
||||
new_exp := now() + interval '1 week';
|
||||
else
|
||||
new_exp := now() + interval '1 hour';
|
||||
end if;
|
||||
|
||||
update public.usr_session s
|
||||
set s.expires_at = new_exp
|
||||
where s.id = session.id;
|
||||
|
||||
select u.*
|
||||
from public.usr u
|
||||
where u.id = session.usr
|
||||
into session_usr;
|
||||
|
||||
return session_usr;
|
||||
end;
|
||||
$function$
|
||||
;
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.do_check_usr_tag()
|
||||
RETURNS trigger
|
||||
LANGUAGE plpgsql
|
||||
STABLE
|
||||
AS $function$
|
||||
begin
|
||||
if new.tag = usr_tag_of_string('root') then
|
||||
raise exception 'tag_invalid';
|
||||
else
|
||||
return new;
|
||||
end if;
|
||||
end;
|
||||
$function$
|
||||
;
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.do_community_immutable_columns()
|
||||
RETURNS trigger
|
||||
LANGUAGE plpgsql
|
||||
AS $function$
|
||||
begin
|
||||
if OLD.id <> NEW.id then
|
||||
raise exception 'immutable_field'
|
||||
using detail = 'public.community.id is immutable',
|
||||
errcode = 'restrict_violation';
|
||||
elsif OLD.uid <> NEW.uid then
|
||||
raise exception 'immutable_field'
|
||||
using detail = 'public.community.uid is immutable',
|
||||
errcode = 'restrict_violation';
|
||||
end if;
|
||||
|
||||
return NEW;
|
||||
end;
|
||||
$function$
|
||||
;
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.do_grp_immutable_columns()
|
||||
RETURNS trigger
|
||||
LANGUAGE plpgsql
|
||||
AS $function$
|
||||
begin
|
||||
if OLD.id <> NEW.id then
|
||||
raise exception 'immutable_field'
|
||||
using detail = 'public.grp.id is immutable',
|
||||
errcode = 'restrict_violation';
|
||||
elsif OLD.uid <> NEW.uid then
|
||||
raise exception 'immutable_field'
|
||||
using detail = 'public.grp.uid is immutable',
|
||||
errcode = 'restrict_violation';
|
||||
elsif OLD.tag <> NEW.tag then
|
||||
raise exception 'immutable_field'
|
||||
using detail = 'public.grp.tag is immutable',
|
||||
errcode = 'restrict_violation';
|
||||
end if;
|
||||
|
||||
return NEW;
|
||||
end;
|
||||
$function$
|
||||
;
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.do_perm_immutable_columns()
|
||||
RETURNS trigger
|
||||
LANGUAGE plpgsql
|
||||
AS $function$
|
||||
begin
|
||||
if OLD.id <> NEW.id then
|
||||
raise exception 'immutable_field'
|
||||
using detail = 'public.perm.id is immutable',
|
||||
errcode = 'restrict_violation';
|
||||
elsif OLD.uid <> NEW.uid then
|
||||
raise exception 'immutable_field'
|
||||
using detail = 'public.perm.uid is immutable',
|
||||
errcode = 'restrict_violation';
|
||||
elsif OLD.path <> NEW.path then
|
||||
raise exception 'immutable_field'
|
||||
using detail = 'public.perm.path is immutable',
|
||||
errcode = 'restrict_violation';
|
||||
end if;
|
||||
|
||||
return NEW;
|
||||
end;
|
||||
$function$
|
||||
;
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.do_thread_attachment_emoji_immutable_columns()
|
||||
RETURNS trigger
|
||||
LANGUAGE plpgsql
|
||||
AS $function$
|
||||
begin
|
||||
if OLD.id <> NEW.id then
|
||||
raise exception 'immutable_field'
|
||||
using detail = 'public.thread_attachment_emoji.id is immutable',
|
||||
errcode = 'restrict_violation';
|
||||
elsif OLD.uid <> NEW.uid then
|
||||
raise exception 'immutable_field'
|
||||
using detail = 'public.thread_attachment_emoji.uid is immutable',
|
||||
errcode = 'restrict_violation';
|
||||
elsif OLD.thread_attachment <> NEW.thread_attachment then
|
||||
raise exception 'immutable_field'
|
||||
using detail = 'public.thread_attachment_emoji.thread_attachment is immutable',
|
||||
errcode = 'restrict_violation';
|
||||
elsif OLD.emoji <> NEW.emoji then
|
||||
raise exception 'immutable_field'
|
||||
using detail = 'public.thread_attachment_emoji.emoji is immutable',
|
||||
errcode = 'restrict_violation';
|
||||
end if;
|
||||
|
||||
return NEW;
|
||||
end;
|
||||
$function$
|
||||
;
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.do_thread_attachment_immutable_columns()
|
||||
RETURNS trigger
|
||||
LANGUAGE plpgsql
|
||||
AS $function$
|
||||
begin
|
||||
if OLD.id <> NEW.id then
|
||||
raise exception 'immutable_field'
|
||||
using detail = 'public.thread_attachment.id is immutable',
|
||||
errcode = 'restrict_violation';
|
||||
elsif OLD.uid <> NEW.uid then
|
||||
raise exception 'immutable_field'
|
||||
using detail = 'public.thread_attachment.uid is immutable',
|
||||
errcode = 'restrict_violation';
|
||||
elsif OLD.thread <> NEW.thread then
|
||||
raise exception 'immutable_field'
|
||||
using detail = 'public.thread_attachment.thread is immutable',
|
||||
errcode = 'restrict_violation';
|
||||
elsif OLD.kind <> NEW.kind then
|
||||
raise exception 'immutable_field'
|
||||
using detail = 'public.thread_attachment.kind is immutable',
|
||||
errcode = 'restrict_violation';
|
||||
end if;
|
||||
|
||||
return NEW;
|
||||
end;
|
||||
$function$
|
||||
;
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.do_thread_attachment_vote_immutable_columns()
|
||||
RETURNS trigger
|
||||
LANGUAGE plpgsql
|
||||
AS $function$
|
||||
begin
|
||||
if OLD.id <> NEW.id then
|
||||
raise exception 'immutable_field'
|
||||
using detail = 'public.thread_attachment_vote.id is immutable',
|
||||
errcode = 'restrict_violation';
|
||||
elsif OLD.uid <> NEW.uid then
|
||||
raise exception 'immutable_field'
|
||||
using detail = 'public.thread_attachment_vote.uid is immutable',
|
||||
errcode = 'restrict_violation';
|
||||
elsif OLD.thread_attachment <> NEW.thread_attachment then
|
||||
raise exception 'immutable_field'
|
||||
using detail = 'public.thread_attachment_vote.thread_attachment is immutable',
|
||||
errcode = 'restrict_violation';
|
||||
elsif OLD.direction <> NEW.direction then
|
||||
raise exception 'immutable_field'
|
||||
using detail = 'public.thread_attachment_vote.direction is immutable',
|
||||
errcode = 'restrict_violation';
|
||||
end if;
|
||||
|
||||
return NEW;
|
||||
end;
|
||||
$function$
|
||||
;
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.do_thread_feed_immutable_columns()
|
||||
RETURNS trigger
|
||||
LANGUAGE plpgsql
|
||||
AS $function$
|
||||
begin
|
||||
if OLD.id <> NEW.id then
|
||||
raise exception 'immutable_field'
|
||||
using detail = 'public.thread_feed.id is immutable',
|
||||
errcode = 'restrict_violation';
|
||||
elsif OLD.uid <> NEW.uid then
|
||||
raise exception 'immutable_field'
|
||||
using detail = 'public.thread_feed.uid is immutable',
|
||||
errcode = 'restrict_violation';
|
||||
elsif OLD.thread <> NEW.thread then
|
||||
raise exception 'immutable_field'
|
||||
using detail = 'public.thread_feed.thread is immutable',
|
||||
errcode = 'restrict_violation';
|
||||
elsif OLD.community <> NEW.community then
|
||||
raise exception 'immutable_field'
|
||||
using detail = 'public.thread_feed.community is immutable',
|
||||
errcode = 'restrict_violation';
|
||||
end if;
|
||||
|
||||
return NEW;
|
||||
end;
|
||||
$function$
|
||||
;
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.do_thread_immutable_columns()
|
||||
RETURNS trigger
|
||||
LANGUAGE plpgsql
|
||||
AS $function$
|
||||
begin
|
||||
if OLD.id <> NEW.id then
|
||||
raise exception 'immutable_field'
|
||||
using detail = 'public.thread.id is immutable',
|
||||
errcode = 'restrict_violation';
|
||||
elsif OLD.uid <> NEW.uid then
|
||||
raise exception 'immutable_field'
|
||||
using detail = 'public.thread.uid is immutable',
|
||||
errcode = 'restrict_violation';
|
||||
elsif OLD.kind <> NEW.kind then
|
||||
raise exception 'immutable_field'
|
||||
using detail = 'public.thread.kind is immutable',
|
||||
errcode = 'restrict_violation';
|
||||
end if;
|
||||
|
||||
return NEW;
|
||||
end;
|
||||
$function$
|
||||
;
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.do_usr_immutable_columns()
|
||||
RETURNS trigger
|
||||
LANGUAGE plpgsql
|
||||
AS $function$
|
||||
begin
|
||||
if OLD.id <> NEW.id then
|
||||
raise exception 'immutable_field'
|
||||
using detail = 'public.usr.id is immutable',
|
||||
errcode = 'restrict_violation';
|
||||
elsif OLD.uid <> NEW.uid then
|
||||
raise exception 'immutable_field'
|
||||
using detail = 'public.usr.uid is immutable',
|
||||
errcode = 'restrict_violation';
|
||||
end if;
|
||||
|
||||
return NEW;
|
||||
end;
|
||||
$function$
|
||||
;
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.do_usr_session_immutable_columns()
|
||||
RETURNS trigger
|
||||
LANGUAGE plpgsql
|
||||
AS $function$
|
||||
begin
|
||||
if OLD.id <> NEW.id then
|
||||
raise exception 'immutable_field'
|
||||
using detail = 'public.usr_session.id is immutable',
|
||||
errcode = 'restrict_violation';
|
||||
elsif OLD.key <> NEW.key then
|
||||
raise exception 'immutable_field'
|
||||
using detail = 'public.usr_session.key is immutable',
|
||||
errcode = 'restrict_violation';
|
||||
elsif OLD.expires_at <> NEW.expires_at then
|
||||
raise exception 'immutable_field'
|
||||
using detail = 'public.usr_session.expires_at is immutable',
|
||||
errcode = 'restrict_violation';
|
||||
elsif OLD.usr <> NEW.usr then
|
||||
raise exception 'immutable_field'
|
||||
using detail = 'public.usr_session.usr is immutable',
|
||||
errcode = 'restrict_violation';
|
||||
elsif OLD.location <> NEW.location then
|
||||
raise exception 'immutable_field'
|
||||
using detail = 'public.usr_session.location is immutable',
|
||||
errcode = 'restrict_violation';
|
||||
elsif OLD.device <> NEW.device then
|
||||
raise exception 'immutable_field'
|
||||
using detail = 'public.usr_session.device is immutable',
|
||||
errcode = 'restrict_violation';
|
||||
elsif OLD.ip <> NEW.ip then
|
||||
raise exception 'immutable_field'
|
||||
using detail = 'public.usr_session.ip is immutable',
|
||||
errcode = 'restrict_violation';
|
||||
end if;
|
||||
|
||||
return NEW;
|
||||
end;
|
||||
$function$
|
||||
;
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.immutable(schema text, table_ text, columns text[])
|
||||
RETURNS void
|
||||
LANGUAGE plpgsql
|
||||
AS $function$
|
||||
declare
|
||||
col text;
|
||||
qualified_name text := concat(schema, '.', table_);
|
||||
do_table_immutable_columns text := concat('do_', table_, '_immutable_columns');
|
||||
trigger_table_immutable_columns text := concat('trigger_', table_, '_immutable_columns');
|
||||
create_do_table_immutable_columns text;
|
||||
create_trigger_table_immutable_columns text;
|
||||
begin
|
||||
create_do_table_immutable_columns := concat( create_do_table_immutable_columns
|
||||
, 'create function ', schema, '.', do_table_immutable_columns, E'() returns trigger language plpgsql as \$\$\n'
|
||||
, E'begin\n'
|
||||
, ' if OLD.', columns[1], ' <> NEW.', columns[1], E' then\n'
|
||||
, E' raise exception \'immutable_field\'\n'
|
||||
, E' using detail = \'', qualified_name, '.', columns[1], E' is immutable\',\n'
|
||||
, E' errcode = \'restrict_violation\';\n'
|
||||
);
|
||||
foreach col in array columns[2:] loop
|
||||
create_do_table_immutable_columns := concat( create_do_table_immutable_columns
|
||||
, E' elsif OLD.', col, ' <> NEW.', col, E' then\n'
|
||||
, E' raise exception \'immutable_field\'\n'
|
||||
, E' using detail = \'', qualified_name, '.', col, E' is immutable\',\n'
|
||||
, E' errcode = \'restrict_violation\';\n'
|
||||
);
|
||||
end loop;
|
||||
|
||||
create_do_table_immutable_columns := concat( create_do_table_immutable_columns
|
||||
, E' end if;\n'
|
||||
, E'\n'
|
||||
, E' return NEW;\n'
|
||||
, E'end;\n'
|
||||
, E'\$\$;\n'
|
||||
);
|
||||
|
||||
create_trigger_table_immutable_columns := concat( create_trigger_table_immutable_columns
|
||||
, 'create trigger ', trigger_table_immutable_columns, E'\n'
|
||||
, 'before update on ', qualified_name, E'\n'
|
||||
, 'for each row execute function ', do_table_immutable_columns, '();'
|
||||
);
|
||||
|
||||
execute create_do_table_immutable_columns;
|
||||
execute create_trigger_table_immutable_columns;
|
||||
end;
|
||||
$function$
|
||||
;
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.usr_session_login(tag_or_email usr_tag_or_email, password text, remember boolean DEFAULT false, location text DEFAULT NULL::text, device usr_session_device DEFAULT NULL::usr_session_device, ip inet DEFAULT NULL::inet)
|
||||
RETURNS usr_session_key
|
||||
LANGUAGE plpgsql
|
||||
AS $function$
|
||||
declare
|
||||
usr public.usr;
|
||||
key public.usr_session_key := usr_session_key_gen();
|
||||
expires_at timestamp;
|
||||
begin
|
||||
usr := public.usr_session_login_validate(tag_or_email, password);
|
||||
|
||||
if remember then
|
||||
expires_at := now() + interval '1 week';
|
||||
else
|
||||
expires_at := now() + interval '1 hour';
|
||||
end if;
|
||||
|
||||
insert into public.usr_session
|
||||
(key, expires_at, usr, remembered, location, device, ip)
|
||||
values
|
||||
(key, expires_at, usr.id, remember, location, device, ip);
|
||||
|
||||
return key;
|
||||
end;
|
||||
$function$
|
||||
;
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.usr_session_login_validate(tag_or_email usr_tag_or_email, password text)
|
||||
RETURNS usr
|
||||
LANGUAGE plpgsql
|
||||
STABLE
|
||||
AS $function$
|
||||
declare
|
||||
usr_email public.email := public.usr_tag_or_email_to_email(tag_or_email);
|
||||
usr_tag public.usr_tag := public.usr_tag_or_email_to_tag(tag_or_email);
|
||||
usr public.usr;
|
||||
begin
|
||||
select *
|
||||
from public.usr as u
|
||||
where u.email = usr_email
|
||||
or u.tag = usr_tag
|
||||
into usr;
|
||||
|
||||
if usr.id = 1 or usr.tag = usr_tag_of_string('root') then
|
||||
raise notice 'root user may not be logged into';
|
||||
raise exception 'incorrect_password';
|
||||
end if;
|
||||
|
||||
if usr is null then
|
||||
-- prevent email guess bruteforces by raising the same exception
|
||||
-- for invalid password and user not found
|
||||
raise notice 'user % not found', usr_tag_or_email_to_string(tag_or_email);
|
||||
raise exception 'incorrect_password';
|
||||
end if;
|
||||
|
||||
if not hashed_text_matches(password, usr.password) then
|
||||
raise notice 'password does not match for user %', usr_tag_or_email_to_string(tag_or_email);
|
||||
raise exception 'incorrect_password';
|
||||
end if;
|
||||
|
||||
return usr;
|
||||
end;
|
||||
$function$
|
||||
;
|
1
2080a44_to_0e93478.sql
Normal file
1
2080a44_to_0e93478.sql
Normal file
@ -0,0 +1 @@
|
||||
|
1
21957cf_skipped.sql
Normal file
1
21957cf_skipped.sql
Normal file
@ -0,0 +1 @@
|
||||
|
1
2345c61_to_8b08d9f.sql
Normal file
1
2345c61_to_8b08d9f.sql
Normal file
@ -0,0 +1 @@
|
||||
|
1
26c2c9c_to_b9d80dd.sql
Normal file
1
26c2c9c_to_b9d80dd.sql
Normal file
@ -0,0 +1 @@
|
||||
|
1
2704c5b_skipped.sql
Normal file
1
2704c5b_skipped.sql
Normal file
@ -0,0 +1 @@
|
||||
|
@ -1,30 +1,19 @@
|
||||
create function public.set_acting_usr(uid text)
|
||||
returns void
|
||||
language plpgsql
|
||||
volatile
|
||||
as $$
|
||||
begin
|
||||
if uid is not null and uid != '' then
|
||||
perform set_config('dnim.usr_uid', uid, false);
|
||||
end if;
|
||||
end;
|
||||
$$;
|
||||
set check_function_bodies = off;
|
||||
|
||||
create function public.unset_acting_usr()
|
||||
returns void
|
||||
language plpgsql
|
||||
volatile
|
||||
as $$
|
||||
CREATE OR REPLACE FUNCTION public.unset_acting_usr()
|
||||
RETURNS void
|
||||
LANGUAGE plpgsql
|
||||
AS $function$
|
||||
begin
|
||||
perform set_config('dnim.usr_uid', '', false);
|
||||
end;
|
||||
$$;
|
||||
$function$
|
||||
;
|
||||
|
||||
create function public.get_acting_usr()
|
||||
returns public.usr
|
||||
language plpgsql
|
||||
volatile
|
||||
as $$
|
||||
CREATE OR REPLACE FUNCTION public.get_acting_usr()
|
||||
RETURNS usr
|
||||
LANGUAGE plpgsql
|
||||
AS $function$
|
||||
declare
|
||||
acting_usr public.usr;
|
||||
begin
|
||||
@ -39,4 +28,17 @@ begin
|
||||
|
||||
return coalesce(acting_usr, public.usr_root());
|
||||
end;
|
||||
$$;
|
||||
$function$
|
||||
;
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.set_acting_usr(uid text)
|
||||
RETURNS void
|
||||
LANGUAGE plpgsql
|
||||
AS $function$
|
||||
begin
|
||||
if uid is not null and uid != '' then
|
||||
perform set_config('dnim.usr_uid', uid, false);
|
||||
end if;
|
||||
end;
|
||||
$function$
|
||||
;
|
1
28b6544_skipped.sql
Normal file
1
28b6544_skipped.sql
Normal file
@ -0,0 +1 @@
|
||||
|
1
2c288ef_to_5169bcb.sql
Normal file
1
2c288ef_to_5169bcb.sql
Normal file
@ -0,0 +1 @@
|
||||
|
@ -1,162 +1,311 @@
|
||||
create type public.audit_kind as enum
|
||||
( 'modify'
|
||||
, 'delete'
|
||||
, 'create'
|
||||
);
|
||||
alter table "public"."community_audit" drop column "prev_role_nonmember";
|
||||
|
||||
create type audited_column as (column_ text, type_ text);
|
||||
alter table "public"."community_member" add column "community" integer not null;
|
||||
|
||||
create function audit(schema text, table_ text, tracked_columns audited_column[], soft_delete boolean)
|
||||
returns void
|
||||
language plpgsql as $$
|
||||
begin
|
||||
if soft_delete then
|
||||
perform setup_audit_and_soft_delete(schema, table_, tracked_columns);
|
||||
else
|
||||
perform setup_audit_and_hard_delete(schema, table_, tracked_columns);
|
||||
end if;
|
||||
end;
|
||||
$$;
|
||||
alter table "public"."community_member_role" add column "community" integer not null;
|
||||
|
||||
create function setup_audit_and_soft_delete( schema text
|
||||
, table_ text
|
||||
, tracked_columns audited_column[]
|
||||
)
|
||||
returns void
|
||||
language plpgsql as $$
|
||||
declare
|
||||
col audited_column;
|
||||
audit_table text := concat(schema, '.', table_, '_audit');
|
||||
do_table_audit text := concat(schema, '.do_', table_, '_audit');
|
||||
do_table_soft_delete text := concat(schema, '.do_', table_, '_soft_delete');
|
||||
create_audit_table text := '';
|
||||
create_do_table_audit text := '';
|
||||
create_do_table_soft_delete text := '';
|
||||
create_trigger_table_audit text := '';
|
||||
create_trigger_table_soft_delete text := '';
|
||||
begin
|
||||
-- create table X_audit() >>>
|
||||
create_audit_table := concat( create_audit_table
|
||||
, 'create table ', audit_table, E'\n'
|
||||
, ' ( ', table_, ' int not null references ', table_, '(id)', E'\n'
|
||||
, ' , kind public.audit_kind not null', E'\n'
|
||||
, ' , actor int not null references public.usr (id)', E'\n'
|
||||
);
|
||||
alter table "public"."community_member" add constraint "community_member_community_fkey" FOREIGN KEY (community) REFERENCES community(id) not valid;
|
||||
|
||||
foreach col in array tracked_columns loop
|
||||
create_audit_table := concat( create_audit_table
|
||||
, E' , prev_', (col.column_), ' ', (col.type_), E' null\n'
|
||||
);
|
||||
end loop;
|
||||
alter table "public"."community_member" validate constraint "community_member_community_fkey";
|
||||
|
||||
create_audit_table := concat( create_audit_table
|
||||
, ' );'
|
||||
);
|
||||
-- <<< create table X_audit()
|
||||
alter table "public"."community_member_role" add constraint "community_member_role_community_fkey" FOREIGN KEY (community) REFERENCES community(id) not valid;
|
||||
|
||||
-- create function do_X_soft_delete() >>>
|
||||
create_do_table_soft_delete := concat( create_do_table_soft_delete
|
||||
, 'create function ', do_table_soft_delete, E'() returns trigger language plpgsql as \$\$\n'
|
||||
, E'begin\n'
|
||||
, E' insert into ', audit_table, E'\n'
|
||||
, E' (', table_, E', kind, actor)\n'
|
||||
, E' values\n'
|
||||
, E' (OLD.id, \'delete\', (select (public.get_acting_usr()).id));\n'
|
||||
, E'\n'
|
||||
, E' update ', schema, '.', table_, E'\n'
|
||||
, E' set deleted = true\n'
|
||||
, E' where id = OLD.id;\n'
|
||||
, E'\n'
|
||||
, E' return null;\n'
|
||||
, E'end;\n'
|
||||
, E'\$\$;'
|
||||
);
|
||||
-- <<< create function do_X_soft_delete()
|
||||
alter table "public"."community_member_role" validate constraint "community_member_role_community_fkey";
|
||||
|
||||
-- create function do_X_audit() >>>
|
||||
create_do_table_audit := concat( create_do_table_audit
|
||||
, 'create function ', do_table_audit, E'() returns trigger language plpgsql as \$\$\n'
|
||||
, E'declare\n'
|
||||
, E' audit_kind public.audit_kind;\n'
|
||||
, E' id int;\n'
|
||||
);
|
||||
set check_function_bodies = off;
|
||||
|
||||
foreach col in array tracked_columns loop
|
||||
create_do_table_audit := concat( create_do_table_audit
|
||||
, 'prev_', (col.column_), ' ', (col.type_), E';\n'
|
||||
);
|
||||
end loop;
|
||||
CREATE OR REPLACE FUNCTION public.do_community_audit()
|
||||
RETURNS trigger
|
||||
LANGUAGE plpgsql
|
||||
AS $function$
|
||||
declare
|
||||
audit_kind public.audit_kind;
|
||||
id int;
|
||||
prev_tag public.community_tag;
|
||||
begin
|
||||
if (TG_OP = 'INSERT') then
|
||||
id := NEW.id;
|
||||
audit_kind := 'create';
|
||||
elsif (TG_OP = 'UPDATE') then
|
||||
id := OLD.id;
|
||||
audit_kind := 'modify';
|
||||
prev_tag := OLD.tag;
|
||||
end if;
|
||||
insert into public.community_audit
|
||||
(kind, community, actor, prev_tag )
|
||||
values
|
||||
(audit_kind, id, 1, prev_tag);
|
||||
return NEW;
|
||||
end;
|
||||
$function$
|
||||
;
|
||||
|
||||
create_do_table_audit := concat( create_do_table_audit
|
||||
, E'begin\n'
|
||||
, E' if (TG_OP = \'INSERT\') then\n'
|
||||
, E' id := NEW.id;\n'
|
||||
, E' audit_kind := \'create\';\n'
|
||||
, E' elsif (TG_OP = \'UPDATE\') then\n'
|
||||
, E' id := OLD.id;\n'
|
||||
, E' audit_kind := \'modify\';\n'
|
||||
);
|
||||
CREATE OR REPLACE FUNCTION public.do_community_member_audit()
|
||||
RETURNS trigger
|
||||
LANGUAGE plpgsql
|
||||
AS $function$
|
||||
declare
|
||||
audit_kind public.audit_kind;
|
||||
id int;
|
||||
prev_usr int;
|
||||
prev_role_ int;
|
||||
begin
|
||||
if (TG_OP = 'INSERT') then
|
||||
id := NEW.id;
|
||||
audit_kind := 'create';
|
||||
elsif (TG_OP = 'DELETE') then
|
||||
id := OLD.id;
|
||||
audit_kind := 'delete';
|
||||
elsif (TG_OP = 'UPDATE') then
|
||||
id := OLD.id;
|
||||
audit_kind := 'modify';
|
||||
prev_usr := OLD.usr;
|
||||
prev_role_ := OLD.role_;
|
||||
end if;
|
||||
insert into public.community_member_audit
|
||||
(kind, community_member, actor, prev_usr, prev_role_ )
|
||||
values
|
||||
(audit_kind, id, 1, prev_usr, prev_role_);
|
||||
return NEW;
|
||||
end;
|
||||
$function$
|
||||
;
|
||||
|
||||
foreach col in array tracked_columns loop
|
||||
create_do_table_audit := concat( create_do_table_audit
|
||||
, ' prev_', (col.column_), ' := OLD.', (col.column_), E';\n'
|
||||
);
|
||||
end loop;
|
||||
CREATE OR REPLACE FUNCTION public.do_community_member_role_audit()
|
||||
RETURNS trigger
|
||||
LANGUAGE plpgsql
|
||||
AS $function$
|
||||
declare
|
||||
audit_kind public.audit_kind;
|
||||
id int;
|
||||
prev_title text;
|
||||
prev_description text;
|
||||
begin
|
||||
if (TG_OP = 'INSERT') then
|
||||
id := NEW.id;
|
||||
audit_kind := 'create';
|
||||
elsif (TG_OP = 'DELETE') then
|
||||
id := OLD.id;
|
||||
audit_kind := 'delete';
|
||||
elsif (TG_OP = 'UPDATE') then
|
||||
id := OLD.id;
|
||||
audit_kind := 'modify';
|
||||
prev_title := OLD.title;
|
||||
prev_description := OLD.description;
|
||||
end if;
|
||||
insert into public.community_member_role_audit
|
||||
(kind, community_member_role, actor, prev_title, prev_description )
|
||||
values
|
||||
(audit_kind, id, 1, prev_title, prev_description);
|
||||
return NEW;
|
||||
end;
|
||||
$function$
|
||||
;
|
||||
|
||||
create_do_table_audit := concat( create_do_table_audit
|
||||
, E' end if;\n'
|
||||
, E' insert into ', audit_table, E'\n'
|
||||
, ' (kind, ', table_, ', actor'
|
||||
);
|
||||
CREATE OR REPLACE FUNCTION public.do_community_member_role_immutable_columns()
|
||||
RETURNS trigger
|
||||
LANGUAGE plpgsql
|
||||
AS $function$
|
||||
begin
|
||||
if OLD.id <> NEW.id then
|
||||
raise exception 'public.community_member_role.id is immutable' using errcode = 'restrict_violation';
|
||||
elsif OLD.uid <> NEW.uid then
|
||||
raise exception 'public.community_member_role.uid is immutable' using errcode = 'restrict_violation';
|
||||
elsif OLD.community <> NEW.community then
|
||||
raise exception 'public.community_member_role.community is immutable' using errcode = 'restrict_violation';
|
||||
end if;
|
||||
|
||||
foreach col in array tracked_columns loop
|
||||
create_do_table_audit := concat( create_do_table_audit
|
||||
, ', prev_', (col.column_)
|
||||
);
|
||||
end loop;
|
||||
return NEW;
|
||||
end;
|
||||
$function$
|
||||
;
|
||||
|
||||
create_do_table_audit := concat( create_do_table_audit
|
||||
, E' )\n'
|
||||
, E' values\n'
|
||||
, E' (audit_kind, id, (public.get_acting_usr()).id'
|
||||
);
|
||||
CREATE OR REPLACE FUNCTION public.do_community_member_role_scope_audit()
|
||||
RETURNS trigger
|
||||
LANGUAGE plpgsql
|
||||
AS $function$
|
||||
declare
|
||||
audit_kind public.audit_kind;
|
||||
id int;
|
||||
prev_role_ int;
|
||||
prev_scope public.authz_scope;
|
||||
begin
|
||||
if (TG_OP = 'INSERT') then
|
||||
id := NEW.id;
|
||||
audit_kind := 'create';
|
||||
elsif (TG_OP = 'DELETE') then
|
||||
id := OLD.id;
|
||||
audit_kind := 'delete';
|
||||
elsif (TG_OP = 'UPDATE') then
|
||||
id := OLD.id;
|
||||
audit_kind := 'modify';
|
||||
prev_role_ := OLD.role_;
|
||||
prev_scope := OLD.scope;
|
||||
end if;
|
||||
insert into public.community_member_role_scope_audit
|
||||
(kind, community_member_role_scope, actor, prev_role_, prev_scope )
|
||||
values
|
||||
(audit_kind, id, 1, prev_role_, prev_scope);
|
||||
return NEW;
|
||||
end;
|
||||
$function$
|
||||
;
|
||||
|
||||
foreach col in array tracked_columns loop
|
||||
create_do_table_audit := concat( create_do_table_audit
|
||||
, ', prev_', (col.column_)
|
||||
);
|
||||
end loop;
|
||||
CREATE OR REPLACE FUNCTION public.do_community_nonmember_scope_audit()
|
||||
RETURNS trigger
|
||||
LANGUAGE plpgsql
|
||||
AS $function$
|
||||
declare
|
||||
audit_kind public.audit_kind;
|
||||
id int;
|
||||
prev_community int;
|
||||
prev_scope public.authz_scope;
|
||||
begin
|
||||
if (TG_OP = 'INSERT') then
|
||||
id := NEW.id;
|
||||
audit_kind := 'create';
|
||||
elsif (TG_OP = 'DELETE') then
|
||||
id := OLD.id;
|
||||
audit_kind := 'delete';
|
||||
elsif (TG_OP = 'UPDATE') then
|
||||
id := OLD.id;
|
||||
audit_kind := 'modify';
|
||||
prev_community := OLD.community;
|
||||
prev_scope := OLD.scope;
|
||||
end if;
|
||||
insert into public.community_nonmember_scope_audit
|
||||
(kind, community_nonmember_scope, actor, prev_community, prev_scope )
|
||||
values
|
||||
(audit_kind, id, 1, prev_community, prev_scope);
|
||||
return NEW;
|
||||
end;
|
||||
$function$
|
||||
;
|
||||
|
||||
create_do_table_audit := concat( create_do_table_audit
|
||||
, E');\n'
|
||||
, E' return NEW;\n'
|
||||
, E'end;\n'
|
||||
, E'\$\$;'
|
||||
);
|
||||
-- <<< create function do_X_audit()
|
||||
CREATE OR REPLACE FUNCTION public.do_thread_attachment_audit()
|
||||
RETURNS trigger
|
||||
LANGUAGE plpgsql
|
||||
AS $function$
|
||||
declare
|
||||
audit_kind public.audit_kind;
|
||||
id int;
|
||||
begin
|
||||
if (TG_OP = 'INSERT') then
|
||||
id := NEW.id;
|
||||
audit_kind := 'create';
|
||||
elsif (TG_OP = 'UPDATE') then
|
||||
id := OLD.id;
|
||||
audit_kind := 'modify';
|
||||
end if;
|
||||
insert into public.thread_attachment_audit
|
||||
(kind, thread_attachment, actor )
|
||||
values
|
||||
(audit_kind, id, 1);
|
||||
return NEW;
|
||||
end;
|
||||
$function$
|
||||
;
|
||||
|
||||
create_trigger_table_audit := concat( 'create trigger trigger_', table_, E'_audit\n'
|
||||
, 'after insert or update on ', schema, '.', table_, E'\n'
|
||||
, 'for each row execute function ', do_table_audit, '();'
|
||||
);
|
||||
create_trigger_table_soft_delete := concat( 'create trigger trigger_', table_, E'_soft_delete\n'
|
||||
, 'before delete on ', schema, '.', table_, E'\n'
|
||||
, 'for each row execute function ', do_table_soft_delete, '();'
|
||||
);
|
||||
execute create_audit_table;
|
||||
execute create_do_table_audit;
|
||||
execute create_do_table_soft_delete;
|
||||
execute create_trigger_table_audit;
|
||||
execute create_trigger_table_soft_delete;
|
||||
end;
|
||||
$$;
|
||||
CREATE OR REPLACE FUNCTION public.do_thread_attachment_emoji_audit()
|
||||
RETURNS trigger
|
||||
LANGUAGE plpgsql
|
||||
AS $function$
|
||||
declare
|
||||
audit_kind public.audit_kind;
|
||||
id int;
|
||||
begin
|
||||
if (TG_OP = 'INSERT') then
|
||||
id := NEW.id;
|
||||
audit_kind := 'create';
|
||||
elsif (TG_OP = 'UPDATE') then
|
||||
id := OLD.id;
|
||||
audit_kind := 'modify';
|
||||
end if;
|
||||
insert into public.thread_attachment_emoji_audit
|
||||
(kind, thread_attachment_emoji, actor )
|
||||
values
|
||||
(audit_kind, id, 1);
|
||||
return NEW;
|
||||
end;
|
||||
$function$
|
||||
;
|
||||
|
||||
create function setup_audit_and_hard_delete( schema text
|
||||
, table_ text
|
||||
, tracked_columns audited_column[]
|
||||
)
|
||||
returns void
|
||||
language plpgsql as $$
|
||||
CREATE OR REPLACE FUNCTION public.do_thread_attachment_vote_audit()
|
||||
RETURNS trigger
|
||||
LANGUAGE plpgsql
|
||||
AS $function$
|
||||
declare
|
||||
audit_kind public.audit_kind;
|
||||
id int;
|
||||
begin
|
||||
if (TG_OP = 'INSERT') then
|
||||
id := NEW.id;
|
||||
audit_kind := 'create';
|
||||
elsif (TG_OP = 'UPDATE') then
|
||||
id := OLD.id;
|
||||
audit_kind := 'modify';
|
||||
end if;
|
||||
insert into public.thread_attachment_vote_audit
|
||||
(kind, thread_attachment_vote, actor )
|
||||
values
|
||||
(audit_kind, id, 1);
|
||||
return NEW;
|
||||
end;
|
||||
$function$
|
||||
;
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.do_thread_audit()
|
||||
RETURNS trigger
|
||||
LANGUAGE plpgsql
|
||||
AS $function$
|
||||
declare
|
||||
audit_kind public.audit_kind;
|
||||
id int;
|
||||
begin
|
||||
if (TG_OP = 'INSERT') then
|
||||
id := NEW.id;
|
||||
audit_kind := 'create';
|
||||
elsif (TG_OP = 'UPDATE') then
|
||||
id := OLD.id;
|
||||
audit_kind := 'modify';
|
||||
end if;
|
||||
insert into public.thread_audit
|
||||
(kind, thread, actor )
|
||||
values
|
||||
(audit_kind, id, 1);
|
||||
return NEW;
|
||||
end;
|
||||
$function$
|
||||
;
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.do_thread_feed_audit()
|
||||
RETURNS trigger
|
||||
LANGUAGE plpgsql
|
||||
AS $function$
|
||||
declare
|
||||
audit_kind public.audit_kind;
|
||||
id int;
|
||||
begin
|
||||
if (TG_OP = 'INSERT') then
|
||||
id := NEW.id;
|
||||
audit_kind := 'create';
|
||||
elsif (TG_OP = 'UPDATE') then
|
||||
id := OLD.id;
|
||||
audit_kind := 'modify';
|
||||
end if;
|
||||
insert into public.thread_feed_audit
|
||||
(kind, thread_feed, actor )
|
||||
values
|
||||
(audit_kind, id, 1);
|
||||
return NEW;
|
||||
end;
|
||||
$function$
|
||||
;
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.setup_audit_and_hard_delete(schema text, table_ text, tracked_columns audited_column[])
|
||||
RETURNS void
|
||||
LANGUAGE plpgsql
|
||||
AS $function$
|
||||
declare
|
||||
col audited_column;
|
||||
audit_table text := concat(schema, '.', table_, '_audit');
|
||||
@ -235,7 +384,7 @@ create function setup_audit_and_hard_delete( schema text
|
||||
create_do_table_audit := concat( create_do_table_audit
|
||||
, E' )\n'
|
||||
, E' values\n'
|
||||
, E' (audit_kind, id, (public.get_acting_usr()).id'
|
||||
, E' (audit_kind, id, 1'
|
||||
);
|
||||
|
||||
foreach col in array tracked_columns loop
|
||||
@ -262,4 +411,137 @@ create function setup_audit_and_hard_delete( schema text
|
||||
execute create_trigger_table_audit;
|
||||
execute create_trigger_table_soft_delete;
|
||||
end;
|
||||
$$;
|
||||
$function$
|
||||
;
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.setup_audit_and_soft_delete(schema text, table_ text, tracked_columns audited_column[])
|
||||
RETURNS void
|
||||
LANGUAGE plpgsql
|
||||
AS $function$
|
||||
declare
|
||||
col audited_column;
|
||||
audit_table text := concat(schema, '.', table_, '_audit');
|
||||
do_table_audit text := concat(schema, '.do_', table_, '_audit');
|
||||
do_table_soft_delete text := concat(schema, '.do_', table_, '_soft_delete');
|
||||
create_audit_table text := '';
|
||||
create_do_table_audit text := '';
|
||||
create_do_table_soft_delete text := '';
|
||||
create_trigger_table_audit text := '';
|
||||
create_trigger_table_soft_delete text := '';
|
||||
begin
|
||||
-- create table X_audit() >>>
|
||||
create_audit_table := concat( create_audit_table
|
||||
, 'create table ', audit_table, E'\n'
|
||||
, ' ( ', table_, ' int not null references ', table_, '(id)', E'\n'
|
||||
, ' , kind public.audit_kind not null', E'\n'
|
||||
, ' , actor int not null references public.usr (id)', E'\n'
|
||||
);
|
||||
|
||||
foreach col in array tracked_columns loop
|
||||
create_audit_table := concat( create_audit_table
|
||||
, E' , prev_', (col.column_), ' ', (col.type_), E' null\n'
|
||||
);
|
||||
end loop;
|
||||
|
||||
create_audit_table := concat( create_audit_table
|
||||
, ' );'
|
||||
);
|
||||
-- <<< create table X_audit()
|
||||
|
||||
-- create function do_X_soft_delete() >>>
|
||||
create_do_table_soft_delete := concat( create_do_table_soft_delete
|
||||
, 'create function ', do_table_soft_delete, E'() returns trigger language plpgsql as \$\$\n'
|
||||
, E'begin\n'
|
||||
, E' insert into ', audit_table, E'\n'
|
||||
, E' (', table_, E', kind, actor)\n'
|
||||
, E' values\n'
|
||||
, E' (OLD.id, \'delete\', 1);\n'
|
||||
, E'\n'
|
||||
, E' update ', schema, '.', table_, E'\n'
|
||||
, E' set deleted = true\n'
|
||||
, E' where id = OLD.id;\n'
|
||||
, E'\n'
|
||||
, E' return null;\n'
|
||||
, E'end;\n'
|
||||
, E'\$\$;'
|
||||
);
|
||||
-- <<< create function do_X_soft_delete()
|
||||
|
||||
-- create function do_X_audit() >>>
|
||||
create_do_table_audit := concat( create_do_table_audit
|
||||
, 'create function ', do_table_audit, E'() returns trigger language plpgsql as \$\$\n'
|
||||
, E'declare\n'
|
||||
, E' audit_kind public.audit_kind;\n'
|
||||
, E' id int;\n'
|
||||
);
|
||||
|
||||
foreach col in array tracked_columns loop
|
||||
create_do_table_audit := concat( create_do_table_audit
|
||||
, 'prev_', (col.column_), ' ', (col.type_), E';\n'
|
||||
);
|
||||
end loop;
|
||||
|
||||
create_do_table_audit := concat( create_do_table_audit
|
||||
, E'begin\n'
|
||||
, E' if (TG_OP = \'INSERT\') then\n'
|
||||
, E' id := NEW.id;\n'
|
||||
, E' audit_kind := \'create\';\n'
|
||||
, E' elsif (TG_OP = \'UPDATE\') then\n'
|
||||
, E' id := OLD.id;\n'
|
||||
, E' audit_kind := \'modify\';\n'
|
||||
);
|
||||
|
||||
foreach col in array tracked_columns loop
|
||||
create_do_table_audit := concat( create_do_table_audit
|
||||
, ' prev_', (col.column_), ' := OLD.', (col.column_), E';\n'
|
||||
);
|
||||
end loop;
|
||||
|
||||
create_do_table_audit := concat( create_do_table_audit
|
||||
, E' end if;\n'
|
||||
, E' insert into ', audit_table, E'\n'
|
||||
, ' (kind, ', table_, ', actor'
|
||||
);
|
||||
|
||||
foreach col in array tracked_columns loop
|
||||
create_do_table_audit := concat( create_do_table_audit
|
||||
, ', prev_', (col.column_)
|
||||
);
|
||||
end loop;
|
||||
|
||||
create_do_table_audit := concat( create_do_table_audit
|
||||
, E' )\n'
|
||||
, E' values\n'
|
||||
, E' (audit_kind, id, 1'
|
||||
);
|
||||
|
||||
foreach col in array tracked_columns loop
|
||||
create_do_table_audit := concat( create_do_table_audit
|
||||
, ', prev_', (col.column_)
|
||||
);
|
||||
end loop;
|
||||
|
||||
create_do_table_audit := concat( create_do_table_audit
|
||||
, E');\n'
|
||||
, E' return NEW;\n'
|
||||
, E'end;\n'
|
||||
, E'\$\$;'
|
||||
);
|
||||
-- <<< create function do_X_audit()
|
||||
|
||||
create_trigger_table_audit := concat( 'create trigger trigger_', table_, E'_audit\n'
|
||||
, 'after insert or update on ', schema, '.', table_, E'\n'
|
||||
, 'for each row execute function ', do_table_audit, '();'
|
||||
);
|
||||
create_trigger_table_soft_delete := concat( 'create trigger trigger_', table_, E'_soft_delete\n'
|
||||
, 'before delete on ', schema, '.', table_, E'\n'
|
||||
, 'for each row execute function ', do_table_soft_delete, '();'
|
||||
);
|
||||
execute create_audit_table;
|
||||
execute create_do_table_audit;
|
||||
execute create_do_table_soft_delete;
|
||||
execute create_trigger_table_audit;
|
||||
execute create_trigger_table_soft_delete;
|
||||
end;
|
||||
$function$
|
||||
;
|
1
309bbd5_to_0eb9b42.sql
Normal file
1
309bbd5_to_0eb9b42.sql
Normal file
@ -0,0 +1 @@
|
||||
|
1
3234a35_to_835faa5.sql
Normal file
1
3234a35_to_835faa5.sql
Normal file
@ -0,0 +1 @@
|
||||
|
35
3712154_to_38893b5.sql
Normal file
35
3712154_to_38893b5.sql
Normal file
@ -0,0 +1,35 @@
|
||||
set check_function_bodies = off;
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.do_grp_add_admins()
|
||||
RETURNS trigger
|
||||
LANGUAGE plpgsql
|
||||
AS $function$
|
||||
declare
|
||||
admins int[];
|
||||
begin
|
||||
admins := ( select array_agg(usr.id)
|
||||
from public.grp_members_admins() as usr
|
||||
);
|
||||
|
||||
perform public.grp_add_members( to_grp => NEW.id
|
||||
, add_usrs => admins
|
||||
);
|
||||
|
||||
return null;
|
||||
end;
|
||||
$function$
|
||||
;
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.grp_members(of_grp integer)
|
||||
RETURNS SETOF usr
|
||||
LANGUAGE plpgsql
|
||||
STABLE
|
||||
AS $function$
|
||||
begin
|
||||
return query select u.*
|
||||
from public.grp_usr gu
|
||||
inner join public.usr u on gu.usr = u.id
|
||||
where gu.grp = of_grp;
|
||||
end;
|
||||
$function$
|
||||
;
|
1
38893b5_to_fe4288d.sql
Normal file
1
38893b5_to_fe4288d.sql
Normal file
@ -0,0 +1 @@
|
||||
|
1
3a14f00_to_d0526f0.sql
Normal file
1
3a14f00_to_d0526f0.sql
Normal file
@ -0,0 +1 @@
|
||||
|
1
3c7189c_to_d5749b0.sql
Normal file
1
3c7189c_to_d5749b0.sql
Normal file
@ -0,0 +1 @@
|
||||
|
1
3e57563_to_e92e56d.sql
Normal file
1
3e57563_to_e92e56d.sql
Normal file
@ -0,0 +1 @@
|
||||
|
1
43d2f59_to_c462e62.sql
Normal file
1
43d2f59_to_c462e62.sql
Normal file
@ -0,0 +1 @@
|
||||
|
1
442d4e8_to_5da43dd.sql
Normal file
1
442d4e8_to_5da43dd.sql
Normal file
@ -0,0 +1 @@
|
||||
|
1
46b4bc6_to_309bbd5.sql
Normal file
1
46b4bc6_to_309bbd5.sql
Normal file
@ -0,0 +1 @@
|
||||
|
200
49bed63_to_7a4d728.sql
Normal file
200
49bed63_to_7a4d728.sql
Normal file
@ -0,0 +1,200 @@
|
||||
alter table "public"."usr" drop constraint "usr_username_key";
|
||||
|
||||
alter table "public"."usr_audit" drop constraint "fk_usr_audit_actor";
|
||||
|
||||
alter table "public"."usr_audit" drop constraint "fk_usr_audit_usr";
|
||||
|
||||
drop index if exists "public"."usr_username_key";
|
||||
|
||||
alter table "public"."usr_audit" add constraint "usr_audit_actor_fkey" FOREIGN KEY (actor) REFERENCES usr(id) not valid;
|
||||
|
||||
alter table "public"."usr_audit" validate constraint "usr_audit_actor_fkey";
|
||||
|
||||
alter table "public"."usr_audit" add constraint "usr_audit_usr_fkey" FOREIGN KEY (usr) REFERENCES usr(id) not valid;
|
||||
|
||||
alter table "public"."usr_audit" validate constraint "usr_audit_usr_fkey";
|
||||
|
||||
set check_function_bodies = off;
|
||||
|
||||
create type "public"."audited_column" as ("column_" text, "type_" text);
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.setup_audit_and_soft_delete(schema text, table_ text, tracked_columns audited_column[])
|
||||
RETURNS void
|
||||
LANGUAGE plpgsql
|
||||
AS $function$
|
||||
declare
|
||||
col audited_column;
|
||||
audit_table text := concat(schema, '.', table_, '_audit');
|
||||
do_table_audit text := concat(schema, '.do_', table_, '_audit');
|
||||
do_table_soft_delete text := concat(schema, '.do_', table_, '_soft_delete');
|
||||
create_audit_table text := '';
|
||||
create_do_table_audit text := '';
|
||||
create_do_table_soft_delete text := '';
|
||||
create_trigger_table_audit text := '';
|
||||
create_trigger_table_soft_delete text := '';
|
||||
begin
|
||||
-- create table X_audit() >>>
|
||||
create_audit_table := concat( create_audit_table
|
||||
, 'create table ', audit_table, E'\n'
|
||||
, ' ( ', table_, ' int not null references ', table_, '(id)', E'\n'
|
||||
, ' , kind public.audit_kind not null', E'\n'
|
||||
, ' , actor int not null references public.usr (id)', E'\n'
|
||||
);
|
||||
|
||||
foreach col in array tracked_columns loop
|
||||
create_audit_table := concat( create_audit_table
|
||||
, E' , prev_', (col.column_), ' ', (col.type_), E' null\n'
|
||||
);
|
||||
end loop;
|
||||
|
||||
create_audit_table := concat( create_audit_table
|
||||
, ' );'
|
||||
);
|
||||
-- <<< create table X_audit()
|
||||
|
||||
-- create function do_X_soft_delete() >>>
|
||||
create_do_table_soft_delete := concat( create_do_table_soft_delete
|
||||
, 'create function ', do_table_soft_delete, E'() returns trigger language plpgsql as \$\$\n'
|
||||
, E'begin\n'
|
||||
, E' insert into ', audit_table, E'\n'
|
||||
, E' (', table_, E', kind, actor)\n'
|
||||
, E' values\n'
|
||||
, E' (OLD.id, \'delete\', 1);\n'
|
||||
, E'\n'
|
||||
, E' update ', schema, '.', table_, E'\n'
|
||||
, E' set deleted = true\n'
|
||||
, E' where id = OLD.id;\n'
|
||||
, E'\n'
|
||||
, E' return null;\n'
|
||||
, E'end;\n'
|
||||
, E'\$\$;'
|
||||
);
|
||||
-- <<< create function do_X_soft_delete()
|
||||
|
||||
-- create function do_X_audit() >>>
|
||||
create_do_table_audit := concat( create_do_table_audit
|
||||
, 'create function ', do_table_audit, E'() returns trigger language plpgsql as \$\$\n'
|
||||
, E'declare\n'
|
||||
, E' audit_kind public.audit_kind;\n'
|
||||
, E' id int;\n'
|
||||
);
|
||||
|
||||
foreach col in array tracked_columns loop
|
||||
create_do_table_audit := concat( create_do_table_audit
|
||||
, 'prev_', (col.column_), ' ', (col.type_), E';\n'
|
||||
);
|
||||
end loop;
|
||||
|
||||
create_do_table_audit := concat( create_do_table_audit
|
||||
, E'begin\n'
|
||||
, E' if (TG_OP = \'INSERT\') then\n'
|
||||
, E' id := NEW.id;\n'
|
||||
, E' audit_kind := \'create\';\n'
|
||||
, E' elsif (TG_OP = \'UPDATE\') then\n'
|
||||
, E' id := OLD.id;\n'
|
||||
, E' audit_kind := \'modify\';\n'
|
||||
);
|
||||
|
||||
foreach col in array tracked_columns loop
|
||||
create_do_table_audit := concat( create_do_table_audit
|
||||
, ' prev_', (col.column_), ' := OLD.', (col.column_), E';\n'
|
||||
);
|
||||
end loop;
|
||||
|
||||
create_do_table_audit := concat( create_do_table_audit
|
||||
, E' end if;\n'
|
||||
, E' insert into public.usr_audit\n'
|
||||
, E' (kind, usr, actor'
|
||||
);
|
||||
|
||||
foreach col in array tracked_columns loop
|
||||
create_do_table_audit := concat( create_do_table_audit
|
||||
, ', prev_', (col.column_)
|
||||
);
|
||||
end loop;
|
||||
|
||||
create_do_table_audit := concat( create_do_table_audit
|
||||
, E' )\n'
|
||||
, E' values\n'
|
||||
, E' (audit_kind, id, 1'
|
||||
);
|
||||
|
||||
foreach col in array tracked_columns loop
|
||||
create_do_table_audit := concat( create_do_table_audit
|
||||
, ', prev_', (col.column_)
|
||||
);
|
||||
end loop;
|
||||
|
||||
create_do_table_audit := concat( create_do_table_audit
|
||||
, E');\n'
|
||||
, E' return NEW;\n'
|
||||
, E'end;\n'
|
||||
, E'\$\$;'
|
||||
);
|
||||
-- <<< create function do_X_audit()
|
||||
|
||||
create_trigger_table_audit := concat( 'create trigger trigger_', table_, E'_audit\n'
|
||||
, 'after insert or update on ', schema, '.', table_, E'\n'
|
||||
, 'for each row execute function ', do_table_audit, '();'
|
||||
);
|
||||
create_trigger_table_soft_delete := concat( 'create trigger trigger_', table_, E'_soft_delete\n'
|
||||
, 'before delete on ', schema, '.', table_, E'\n'
|
||||
, 'for each row execute function ', do_table_soft_delete, '();'
|
||||
);
|
||||
execute create_audit_table;
|
||||
execute create_do_table_audit;
|
||||
execute create_do_table_soft_delete;
|
||||
execute create_trigger_table_audit;
|
||||
execute create_trigger_table_soft_delete;
|
||||
end;
|
||||
$function$
|
||||
;
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.do_usr_audit()
|
||||
RETURNS trigger
|
||||
LANGUAGE plpgsql
|
||||
AS $function$
|
||||
declare
|
||||
audit_kind public.audit_kind;
|
||||
id int;
|
||||
prev_username public.usr_username;
|
||||
prev_password public.hashed_text;
|
||||
prev_email public.email;
|
||||
begin
|
||||
if (TG_OP = 'INSERT') then
|
||||
id := NEW.id;
|
||||
audit_kind := 'create';
|
||||
elsif (TG_OP = 'UPDATE') then
|
||||
id := OLD.id;
|
||||
audit_kind := 'modify';
|
||||
prev_username := OLD.username;
|
||||
prev_password := OLD.password;
|
||||
prev_email := OLD.email;
|
||||
end if;
|
||||
insert into public.usr_audit
|
||||
(kind, usr, actor, prev_username, prev_password, prev_email )
|
||||
values
|
||||
(audit_kind, id, 1, prev_username, prev_password, prev_email);
|
||||
return NEW;
|
||||
end;
|
||||
$function$
|
||||
;
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.do_usr_soft_delete()
|
||||
RETURNS trigger
|
||||
LANGUAGE plpgsql
|
||||
AS $function$
|
||||
begin
|
||||
insert into public.usr_audit
|
||||
(usr, kind, actor)
|
||||
values
|
||||
(OLD.id, 'delete', 1);
|
||||
|
||||
update public.usr
|
||||
set deleted = true
|
||||
where id = OLD.id;
|
||||
|
||||
return null;
|
||||
end;
|
||||
$function$
|
||||
;
|
1
4a81f5f_to_eba0043.sql
Normal file
1
4a81f5f_to_eba0043.sql
Normal file
@ -0,0 +1 @@
|
||||
|
1
4b9a59a_skipped.sql
Normal file
1
4b9a59a_skipped.sql
Normal file
@ -0,0 +1 @@
|
||||
|
1
508c1c2_skipped.sql
Normal file
1
508c1c2_skipped.sql
Normal file
@ -0,0 +1 @@
|
||||
|
1
5105b7f_to_c0d37ca.sql
Normal file
1
5105b7f_to_c0d37ca.sql
Normal file
@ -0,0 +1 @@
|
||||
|
1
5169bcb_to_6a5d100.sql
Normal file
1
5169bcb_to_6a5d100.sql
Normal file
@ -0,0 +1 @@
|
||||
|
1
533493e_to_6941558.sql
Normal file
1
533493e_to_6941558.sql
Normal file
@ -0,0 +1 @@
|
||||
|
1
548f60b_skipped.sql
Normal file
1
548f60b_skipped.sql
Normal file
@ -0,0 +1 @@
|
||||
|
1
5521049_to_7825f89.sql
Normal file
1
5521049_to_7825f89.sql
Normal file
@ -0,0 +1 @@
|
||||
|
1
55fca68_skipped.sql
Normal file
1
55fca68_skipped.sql
Normal file
@ -0,0 +1 @@
|
||||
|
1
56957d3_to_e6a9378.sql
Normal file
1
56957d3_to_e6a9378.sql
Normal file
@ -0,0 +1 @@
|
||||
|
1
56980bf_to_f84b8ed.sql
Normal file
1
56980bf_to_f84b8ed.sql
Normal file
@ -0,0 +1 @@
|
||||
|
1
5da43dd_to_0d639e8.sql
Normal file
1
5da43dd_to_0d639e8.sql
Normal file
@ -0,0 +1 @@
|
||||
|
1
5e39afb_skipped.sql
Normal file
1
5e39afb_skipped.sql
Normal file
@ -0,0 +1 @@
|
||||
|
1
5f31077_to_be07aca.sql
Normal file
1
5f31077_to_be07aca.sql
Normal file
@ -0,0 +1 @@
|
||||
|
1
5f9c3c7_to_e4984dd.sql
Normal file
1
5f9c3c7_to_e4984dd.sql
Normal file
@ -0,0 +1 @@
|
||||
|
1
60a672d_to_f9fa376.sql
Normal file
1
60a672d_to_f9fa376.sql
Normal file
@ -0,0 +1 @@
|
||||
|
1
629bc32_to_533493e.sql
Normal file
1
629bc32_to_533493e.sql
Normal file
@ -0,0 +1 @@
|
||||
|
1
658ad01_to_3712154.sql
Normal file
1
658ad01_to_3712154.sql
Normal file
@ -0,0 +1 @@
|
||||
|
1
65b6f0f_to_0728ba9.sql
Normal file
1
65b6f0f_to_0728ba9.sql
Normal file
@ -0,0 +1 @@
|
||||
|
1
65f7926_to_885e22c.sql
Normal file
1
65f7926_to_885e22c.sql
Normal file
@ -0,0 +1 @@
|
||||
|
50
6700413_to_68cd9c5.sql
Normal file
50
6700413_to_68cd9c5.sql
Normal file
@ -0,0 +1,50 @@
|
||||
set check_function_bodies = off;
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.create_newtype_text(qualified_name text)
|
||||
RETURNS void
|
||||
LANGUAGE plpgsql
|
||||
AS $function$
|
||||
begin
|
||||
execute concat('create type ', qualified_name, ' as (str text);');
|
||||
execute concat( 'create function '
|
||||
, qualified_name || '_to_string(val ' || qualified_name || ')'
|
||||
, E' returns text language sql as \'select (val.str);\';'
|
||||
);
|
||||
execute concat( 'create function '
|
||||
, qualified_name || '_of_string(val text)'
|
||||
, ' returns ' || qualified_name || E' language sql as \'select row(val);\';'
|
||||
);
|
||||
execute concat( 'create cast '
|
||||
, ' (' || qualified_name || ' as text)'
|
||||
, ' with function ' || qualified_name || E'_to_string(' || qualified_name || ')'
|
||||
, ' as assignment;'
|
||||
);
|
||||
execute concat( 'create cast '
|
||||
, ' (text as ' || qualified_name || ')'
|
||||
, ' with function ' || qualified_name || E'_of_string(text)'
|
||||
, ' as assignment;'
|
||||
);
|
||||
end;
|
||||
$function$
|
||||
;
|
||||
|
||||
create cast (public.usr_tag as text) with function public.usr_tag_to_string(public.usr_tag) as assignment;
|
||||
create cast (text as public.usr_tag) with function public.usr_tag_of_string(text) as assignment;
|
||||
|
||||
create cast (public.usr_session_key as text) with function public.usr_session_key_to_string(public.usr_session_key) as assignment;
|
||||
create cast (text as public.usr_session_key) with function public.usr_session_key_of_string(text) as assignment;
|
||||
|
||||
create cast (public.grp_tag as text) with function public.grp_tag_to_string(public.grp_tag) as assignment;
|
||||
create cast (text as public.grp_tag) with function public.grp_tag_of_string(text) as assignment;
|
||||
|
||||
create cast (public.community_tag as text) with function public.community_tag_to_string(public.community_tag) as assignment;
|
||||
create cast (text as public.community_tag) with function public.community_tag_of_string(text) as assignment;
|
||||
|
||||
create cast (public.email as text) with function public.email_to_string(public.email) as assignment;
|
||||
create cast (text as public.email) with function public.email_of_string(text) as assignment;
|
||||
|
||||
create cast (public.hashed_text as text) with function public.hashed_text_to_string(public.hashed_text) as assignment;
|
||||
create cast (text as public.hashed_text) with function public.hashed_text_of_string(text) as assignment;
|
||||
|
||||
create cast (human_uuid.huid as text) with function human_uuid.huid_to_string(human_uuid.huid) as assignment;
|
||||
create cast (text as human_uuid.huid) with function human_uuid.huid_of_string(text) as assignment;
|
1
68cd9c5_to_ee6390d.sql
Normal file
1
68cd9c5_to_ee6390d.sql
Normal file
@ -0,0 +1 @@
|
||||
|
1
6941558_to_cda6320.sql
Normal file
1
6941558_to_cda6320.sql
Normal file
@ -0,0 +1 @@
|
||||
|
1
6a5d100_to_b402f08.sql
Normal file
1
6a5d100_to_b402f08.sql
Normal file
@ -0,0 +1 @@
|
||||
|
1
6b1557a_to_c310921.sql
Normal file
1
6b1557a_to_c310921.sql
Normal file
@ -0,0 +1 @@
|
||||
|
1
6d10cb3_skipped.sql
Normal file
1
6d10cb3_skipped.sql
Normal file
@ -0,0 +1 @@
|
||||
|
1
6f08ab4_skipped.sql
Normal file
1
6f08ab4_skipped.sql
Normal file
@ -0,0 +1 @@
|
||||
|
1
6f52cad_skipped.sql
Normal file
1
6f52cad_skipped.sql
Normal file
@ -0,0 +1 @@
|
||||
|
1
70a8bca_to_fd395c2.sql
Normal file
1
70a8bca_to_fd395c2.sql
Normal file
@ -0,0 +1 @@
|
||||
|
1
72e1e0e_to_5521049.sql
Normal file
1
72e1e0e_to_5521049.sql
Normal file
@ -0,0 +1 @@
|
||||
|
62
7371374_to_94501b7.sql
Normal file
62
7371374_to_94501b7.sql
Normal file
@ -0,0 +1,62 @@
|
||||
alter table "public"."audit" drop constraint "audit_pkey";
|
||||
|
||||
drop index if exists "public"."audit_pkey";
|
||||
|
||||
drop table "public"."audit";
|
||||
|
||||
set check_function_bodies = off;
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.do_usr_audit()
|
||||
RETURNS trigger
|
||||
LANGUAGE plpgsql
|
||||
AS $function$
|
||||
declare
|
||||
audit_kind public.audit_kind;
|
||||
usr_id int;
|
||||
prev_username public.usr_username := null;
|
||||
prev_password public.hashed_text := null;
|
||||
prev_email public.email := null;
|
||||
begin
|
||||
if (TG_OP = 'UPDATE') then
|
||||
usr_id := OLD.id;
|
||||
audit_kind := 'modify';
|
||||
prev_username := OLD.username;
|
||||
prev_password := OLD.password;
|
||||
prev_email := OLD.email;
|
||||
elsif (TG_OP = 'INSERT') then
|
||||
usr_id := NEW.id;
|
||||
audit_kind := 'create';
|
||||
end if;
|
||||
|
||||
insert into public.usr_audit
|
||||
(kind, usr, actor, prev_username, prev_email, prev_password )
|
||||
values
|
||||
-- TODO actor
|
||||
(audit_kind, usr_id, usr_id, prev_username, prev_email, prev_password );
|
||||
|
||||
return NEW;
|
||||
end;
|
||||
$function$
|
||||
;
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.do_usr_soft_delete()
|
||||
RETURNS trigger
|
||||
LANGUAGE plpgsql
|
||||
AS $function$
|
||||
declare
|
||||
audit_id int;
|
||||
begin
|
||||
insert into public.usr_audit
|
||||
(kind, usr, actor)
|
||||
values
|
||||
-- TODO actor
|
||||
('delete' :: public.audit_kind, OLD.id, OLD.id);
|
||||
|
||||
update public.usr
|
||||
set deleted = true
|
||||
where id = OLD.id;
|
||||
|
||||
return null;
|
||||
end;
|
||||
$function$
|
||||
;
|
1
7613dc8_to_3a14f00.sql
Normal file
1
7613dc8_to_3a14f00.sql
Normal file
@ -0,0 +1 @@
|
||||
|
1
7825f89_to_994fa9d.sql
Normal file
1
7825f89_to_994fa9d.sql
Normal file
@ -0,0 +1 @@
|
||||
|
53
7a4d728_to_c19a46b.sql
Normal file
53
7a4d728_to_c19a46b.sql
Normal file
@ -0,0 +1,53 @@
|
||||
drop function if exists "public"."hashed_text_string"(hashed hashed_text);
|
||||
|
||||
alter type "public"."email" rename attribute "email" to "str";
|
||||
|
||||
alter type "public"."hashed_text" rename attribute "hashed" to "str";
|
||||
|
||||
set check_function_bodies = off;
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.email_of_string(val text)
|
||||
RETURNS email
|
||||
LANGUAGE sql
|
||||
AS $function$select row(val);$function$
|
||||
;
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.email_to_string(val email)
|
||||
RETURNS text
|
||||
LANGUAGE sql
|
||||
AS $function$select (val.str);$function$
|
||||
;
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.hashed_text_of_string(val text)
|
||||
RETURNS hashed_text
|
||||
LANGUAGE sql
|
||||
AS $function$select row(val);$function$
|
||||
;
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.hashed_text_to_string(val hashed_text)
|
||||
RETURNS text
|
||||
LANGUAGE sql
|
||||
AS $function$select (val.str);$function$
|
||||
;
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.hash_text(plain text)
|
||||
RETURNS hashed_text
|
||||
LANGUAGE plpgsql
|
||||
IMMUTABLE
|
||||
AS $function$
|
||||
begin
|
||||
return hashed_text_of_string(crypt(plain, gen_salt('bf')));
|
||||
end;
|
||||
$function$
|
||||
;
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.hashed_text_matches(plain text, hashed hashed_text)
|
||||
RETURNS boolean
|
||||
LANGUAGE plpgsql
|
||||
IMMUTABLE
|
||||
AS $function$
|
||||
begin
|
||||
return hashed_text_to_string(hashed) = crypt(plain, hashed_text_to_string(hashed));
|
||||
end;
|
||||
$function$
|
||||
;
|
1
7cda196_to_1bf3d29.sql
Normal file
1
7cda196_to_1bf3d29.sql
Normal file
@ -0,0 +1 @@
|
||||
|
1
80d94f9_skipped.sql
Normal file
1
80d94f9_skipped.sql
Normal file
@ -0,0 +1 @@
|
||||
|
1
835faa5_to_3e57563.sql
Normal file
1
835faa5_to_3e57563.sql
Normal file
@ -0,0 +1 @@
|
||||
|
1
86bb834_to_1dae8d2.sql
Normal file
1
86bb834_to_1dae8d2.sql
Normal file
@ -0,0 +1 @@
|
||||
|
30
87f7a4d_to_b3de72d.sql
Normal file
30
87f7a4d_to_b3de72d.sql
Normal file
@ -0,0 +1,30 @@
|
||||
alter table "public"."usr_session" alter column "key" drop default;
|
||||
|
||||
set check_function_bodies = off;
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.usr_session_login(tag_or_email usr_tag_or_email, password text, remember boolean DEFAULT false, location text DEFAULT NULL::text, device usr_session_device DEFAULT NULL::usr_session_device, ip inet DEFAULT NULL::inet)
|
||||
RETURNS usr_session_key
|
||||
LANGUAGE plpgsql
|
||||
AS $function$
|
||||
declare
|
||||
usr public.usr;
|
||||
key public.usr_session_key := usr_session_key_gen();
|
||||
expires_at timestamp;
|
||||
begin
|
||||
usr := public.usr_session_login_validate(tag_or_email, password);
|
||||
|
||||
if remember then
|
||||
expires_at := now() + interval '1 week';
|
||||
else
|
||||
expires_at := now() + interval '1 hour';
|
||||
end if;
|
||||
|
||||
insert into public.usr_session
|
||||
(key, expires_at, usr, location, device, ip)
|
||||
values
|
||||
(key, expires_at, usr.id, location, device, ip);
|
||||
|
||||
return key;
|
||||
end;
|
||||
$function$
|
||||
;
|
1
885e22c_to_27a69a5.sql
Normal file
1
885e22c_to_27a69a5.sql
Normal file
@ -0,0 +1 @@
|
||||
|
1
89df399_to_bba7d98.sql
Normal file
1
89df399_to_bba7d98.sql
Normal file
@ -0,0 +1 @@
|
||||
|
4812
8b08d9f_to_4a81f5f.sql
Normal file
4812
8b08d9f_to_4a81f5f.sql
Normal file
File diff suppressed because it is too large
Load Diff
1
8b648a8_to_89df399.sql
Normal file
1
8b648a8_to_89df399.sql
Normal file
@ -0,0 +1 @@
|
||||
|
1
9226df1_skipped.sql
Normal file
1
9226df1_skipped.sql
Normal file
@ -0,0 +1 @@
|
||||
|
1
93e81ad_to_16a20c2.sql
Normal file
1
93e81ad_to_16a20c2.sql
Normal file
@ -0,0 +1 @@
|
||||
|
32
94501b7_to_047a51b.sql
Normal file
32
94501b7_to_047a51b.sql
Normal file
@ -0,0 +1,32 @@
|
||||
create extension if not exists "pgcrypto" with schema "public" version '1.3';
|
||||
|
||||
set check_function_bodies = off;
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.hash_text(plain text)
|
||||
RETURNS hashed_text
|
||||
LANGUAGE plpgsql
|
||||
IMMUTABLE
|
||||
AS $function$
|
||||
begin
|
||||
return row(crypt(plain, gen_salt('bf')));
|
||||
end;
|
||||
$function$
|
||||
;
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.hashed_text_matches(plain text, hashed hashed_text)
|
||||
RETURNS boolean
|
||||
LANGUAGE plpgsql
|
||||
IMMUTABLE
|
||||
AS $function$
|
||||
begin
|
||||
return hashed_text_string(hashed) = crypt(plain, hashed_text_string(hashed));
|
||||
end;
|
||||
$function$
|
||||
;
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.hashed_text_string(hashed hashed_text)
|
||||
RETURNS text
|
||||
LANGUAGE sql
|
||||
IMMUTABLE
|
||||
AS $function$select (hashed.hashed);$function$
|
||||
;
|
1
94ab122_to_9abd45a.sql
Normal file
1
94ab122_to_9abd45a.sql
Normal file
@ -0,0 +1 @@
|
||||
|
1
9569e36_to_da31970.sql
Normal file
1
9569e36_to_da31970.sql
Normal file
@ -0,0 +1 @@
|
||||
|
1
96937df_skipped.sql
Normal file
1
96937df_skipped.sql
Normal file
@ -0,0 +1 @@
|
||||
|
1
9929ee9_skipped.sql
Normal file
1
9929ee9_skipped.sql
Normal file
@ -0,0 +1 @@
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user