diff --git a/schema/000_immutable.sql b/schema/000_immutable.sql new file mode 100644 index 0000000..7238e8f --- /dev/null +++ b/schema/000_immutable.sql @@ -0,0 +1,43 @@ +create function mark_columns_immutable(schema text, table_ text, columns text[]) + returns void + language plpgsql + as $$ +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 \'', qualified_name, '.', columns[1], E' is immutable\' using 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 \'', qualified_name, '.', col, E' is immutable\' using 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; +$$; diff --git a/schema/020_thread.sql b/schema/020_thread.sql deleted file mode 100644 index 49f0a94..0000000 --- a/schema/020_thread.sql +++ /dev/null @@ -1,11 +0,0 @@ -create type public.thread_kind as enum - ( 'post' - , 'short' - , 'message' - ); - -create table public.thread - ( id int not null primary key generated always as identity - , uid uuid not null default gen_random_uuid() - , kind public.thread_kind not null - ); diff --git a/schema/020_user.sql b/schema/020_user.sql index fd1b8df..1fe71fa 100644 --- a/schema/020_user.sql +++ b/schema/020_user.sql @@ -16,3 +16,10 @@ select setup_audit_and_soft_delete( 'public' , row('email', 'public.email') ] :: audited_column[] ); + +select mark_columns_immutable( 'public' + , 'usr' + , array[ 'id' + , 'uid' + ] + ); diff --git a/schema/021_thread.sql b/schema/021_thread.sql new file mode 100644 index 0000000..26788c2 --- /dev/null +++ b/schema/021_thread.sql @@ -0,0 +1,70 @@ +create type public.thread_kind as enum + ( 'feed' + , 'message_feed' + , 'message' + , 'post_feed' + , 'post' + , 'comment' + , 'symbolic' + ); + +create table public.thread + ( id int not null primary key generated always as identity + , uid uuid not null default gen_random_uuid() + , deleted boolean not null default false + , kind public.thread_kind not null + ); + +select setup_audit_and_soft_delete('public', 'thread', array[] :: audited_column[]); +select mark_columns_immutable('public', 'thread', array['id', 'uid', 'kind']); + +create table public.thread_feed + ( id int not null primary key generated always as identity + , uid uuid not null default gen_random_uuid() + , deleted boolean not null default false + , thread int not null references public.thread(id) + -- , community int not null references public.community(id) + ); + +select setup_audit_and_soft_delete('public', 'thread_feed', array[] :: audited_column[]); +select mark_columns_immutable('public', 'thread_feed', array['id', 'uid', 'thread', 'community']); + +create type public.thread_attachment_kind as enum + ( 'vote' + , 'emoji' + ); + +create type public.thread_attachment_vote_direction as enum + ( 'up' + , 'down' + ); + +create table public.thread_attachment + ( id int not null primary key generated always as identity + , uid uuid not null default gen_random_uuid() + , thread int not null references public.thread(id) + , kind public.thread_attachment_kind not null + ); + +select setup_audit_and_soft_delete('public', 'thread_attachment', array[] :: audited_column[]); +select mark_columns_immutable('public', 'thread_attachment', array['id', 'uid', 'thread', 'kind']); + +create table public.thread_attachment_emoji + ( id int not null primary key generated always as identity + , uid uuid not null default gen_random_uuid() + , thread_attachment int not null references public.thread_attachment(id) + , emoji text not null + ); + +select setup_audit_and_soft_delete('public', 'thread_attachment_emoji', array[] :: audited_column[]); +select mark_columns_immutable('public', 'thread_attachment_emoji', array['id', 'uid', 'thread_attachment', 'emoji']); + +create table public.thread_attachment_vote + ( id int not null primary key generated always as identity + , uid uuid not null default gen_random_uuid() + , thread_attachment int not null references public.thread_attachment(id) + , direction public.thread_attachment_vote_direction not null + ); + +select setup_audit_and_soft_delete('public', 'thread_attachment_vote', array[] :: audited_column[]); +select mark_columns_immutable('public', 'thread_attachment_vote', array['id', 'uid', 'thread_attachment', 'direction']);