259 lines
11 KiB
SQL
259 lines
11 KiB
SQL
-- R1 Katalog: games, game_variants, Begriffe (Genre, Mechanik, Kategorie) und Zuordnungen.
|
|
-- Lesen ist auch ohne Anmeldung möglich (Entscheidung O6), Schreiben nie.
|
|
|
|
create table public.games (
|
|
id uuid primary key default gen_random_uuid(),
|
|
owner_id uuid references public.profiles (id) on delete cascade,
|
|
title text not null check (char_length(title) between 1 and 200),
|
|
description text check (char_length(description) <= 5000),
|
|
year smallint check (year between 1800 and 2100),
|
|
min_players smallint check (min_players >= 1),
|
|
max_players smallint check (max_players >= 1),
|
|
min_minutes smallint check (min_minutes >= 0),
|
|
max_minutes smallint check (max_minutes >= 0),
|
|
min_age smallint check (min_age >= 0),
|
|
image_path text,
|
|
bgg_id integer,
|
|
visibility text not null default 'private' check (visibility in ('catalog', 'private')),
|
|
created_at timestamptz not null default now(),
|
|
updated_at timestamptz not null default now(),
|
|
-- Katalogspiele gehören niemandem, private Spiele immer einem Nutzer (O5).
|
|
constraint games_owner_matches_visibility check (
|
|
(visibility = 'catalog' and owner_id is null)
|
|
or (visibility = 'private' and owner_id is not null)
|
|
),
|
|
constraint games_players_range check (max_players is null or min_players is null or max_players >= min_players),
|
|
constraint games_minutes_range check (max_minutes is null or min_minutes is null or max_minutes >= min_minutes)
|
|
);
|
|
|
|
-- Die BGG-ID ist optional. Im Katalog einmalig; private Einträge mehrerer Nutzer dürfen sie teilen.
|
|
create unique index games_bgg_id_catalog_key on public.games (bgg_id)
|
|
where visibility = 'catalog' and bgg_id is not null;
|
|
create unique index games_bgg_id_private_key on public.games (owner_id, bgg_id)
|
|
where visibility = 'private' and bgg_id is not null;
|
|
create index games_owner_id_idx on public.games (owner_id);
|
|
create index games_title_idx on public.games (lower(title));
|
|
|
|
create table public.game_variants (
|
|
id uuid primary key default gen_random_uuid(),
|
|
game_id uuid not null references public.games (id) on delete cascade,
|
|
name text not null check (char_length(name) between 1 and 200),
|
|
year smallint check (year between 1800 and 2100),
|
|
language text check (char_length(language) <= 50),
|
|
bgg_id integer,
|
|
created_at timestamptz not null default now(),
|
|
updated_at timestamptz not null default now(),
|
|
-- Ermöglicht den zusammengesetzten Verweis (variant_id, game_id) in Sammlung und Partien:
|
|
-- so kann eine Variante nie zu einem anderen Spiel gehören.
|
|
constraint game_variants_id_game_key unique (id, game_id)
|
|
);
|
|
create index game_variants_game_id_idx on public.game_variants (game_id);
|
|
|
|
create table public.genres (
|
|
id uuid primary key default gen_random_uuid(),
|
|
name_de text not null unique check (char_length(name_de) between 1 and 100),
|
|
name_en text not null unique check (char_length(name_en) between 1 and 100),
|
|
created_at timestamptz not null default now(),
|
|
updated_at timestamptz not null default now()
|
|
);
|
|
|
|
create table public.mechanics (
|
|
id uuid primary key default gen_random_uuid(),
|
|
name_de text not null unique check (char_length(name_de) between 1 and 100),
|
|
name_en text not null unique check (char_length(name_en) between 1 and 100),
|
|
created_at timestamptz not null default now(),
|
|
updated_at timestamptz not null default now()
|
|
);
|
|
|
|
create table public.categories (
|
|
id uuid primary key default gen_random_uuid(),
|
|
name_de text not null unique check (char_length(name_de) between 1 and 100),
|
|
name_en text not null unique check (char_length(name_en) between 1 and 100),
|
|
created_at timestamptz not null default now(),
|
|
updated_at timestamptz not null default now()
|
|
);
|
|
|
|
create table public.game_genres (
|
|
id uuid primary key default gen_random_uuid(),
|
|
game_id uuid not null references public.games (id) on delete cascade,
|
|
genre_id uuid not null references public.genres (id) on delete cascade,
|
|
created_at timestamptz not null default now(),
|
|
updated_at timestamptz not null default now(),
|
|
unique (game_id, genre_id)
|
|
);
|
|
create index game_genres_genre_id_idx on public.game_genres (genre_id);
|
|
|
|
create table public.game_mechanics (
|
|
id uuid primary key default gen_random_uuid(),
|
|
game_id uuid not null references public.games (id) on delete cascade,
|
|
mechanic_id uuid not null references public.mechanics (id) on delete cascade,
|
|
created_at timestamptz not null default now(),
|
|
updated_at timestamptz not null default now(),
|
|
unique (game_id, mechanic_id)
|
|
);
|
|
create index game_mechanics_mechanic_id_idx on public.game_mechanics (mechanic_id);
|
|
|
|
create table public.game_categories (
|
|
id uuid primary key default gen_random_uuid(),
|
|
game_id uuid not null references public.games (id) on delete cascade,
|
|
category_id uuid not null references public.categories (id) on delete cascade,
|
|
created_at timestamptz not null default now(),
|
|
updated_at timestamptz not null default now(),
|
|
unique (game_id, category_id)
|
|
);
|
|
create index game_categories_category_id_idx on public.game_categories (category_id);
|
|
|
|
-- updated_at-Trigger für alle Tabellen dieser Migration
|
|
create trigger set_updated_at before update on public.games
|
|
for each row execute function public.set_updated_at();
|
|
create trigger set_updated_at before update on public.game_variants
|
|
for each row execute function public.set_updated_at();
|
|
create trigger set_updated_at before update on public.genres
|
|
for each row execute function public.set_updated_at();
|
|
create trigger set_updated_at before update on public.mechanics
|
|
for each row execute function public.set_updated_at();
|
|
create trigger set_updated_at before update on public.categories
|
|
for each row execute function public.set_updated_at();
|
|
create trigger set_updated_at before update on public.game_genres
|
|
for each row execute function public.set_updated_at();
|
|
create trigger set_updated_at before update on public.game_mechanics
|
|
for each row execute function public.set_updated_at();
|
|
create trigger set_updated_at before update on public.game_categories
|
|
for each row execute function public.set_updated_at();
|
|
|
|
-- Rechte und RLS
|
|
alter table public.games enable row level security;
|
|
alter table public.game_variants enable row level security;
|
|
alter table public.genres enable row level security;
|
|
alter table public.mechanics enable row level security;
|
|
alter table public.categories enable row level security;
|
|
alter table public.game_genres enable row level security;
|
|
alter table public.game_mechanics enable row level security;
|
|
alter table public.game_categories enable row level security;
|
|
|
|
revoke all on public.games, public.game_variants, public.genres, public.mechanics,
|
|
public.categories, public.game_genres, public.game_mechanics, public.game_categories
|
|
from anon, authenticated;
|
|
|
|
grant select on public.games, public.game_variants, public.genres, public.mechanics,
|
|
public.categories, public.game_genres, public.game_mechanics, public.game_categories
|
|
to anon, authenticated;
|
|
grant insert, update, delete on public.games, public.game_variants, public.genres,
|
|
public.mechanics, public.categories, public.game_genres, public.game_mechanics,
|
|
public.game_categories to authenticated;
|
|
|
|
-- games: Katalog für alle sichtbar, private Spiele nur für den Ersteller.
|
|
create policy games_select on public.games
|
|
for select to anon, authenticated
|
|
using (visibility = 'catalog' or owner_id = (select auth.uid()));
|
|
|
|
create policy games_insert_private on public.games
|
|
for insert to authenticated
|
|
with check (visibility = 'private' and owner_id = (select auth.uid()));
|
|
|
|
create policy games_insert_admin on public.games
|
|
for insert to authenticated
|
|
with check (public.is_admin());
|
|
|
|
create policy games_update_private on public.games
|
|
for update to authenticated
|
|
using (visibility = 'private' and owner_id = (select auth.uid()))
|
|
with check (visibility = 'private' and owner_id = (select auth.uid()));
|
|
|
|
create policy games_update_admin on public.games
|
|
for update to authenticated
|
|
using (public.is_admin())
|
|
with check (public.is_admin());
|
|
|
|
create policy games_delete_private on public.games
|
|
for delete to authenticated
|
|
using (visibility = 'private' and owner_id = (select auth.uid()));
|
|
|
|
create policy games_delete_admin on public.games
|
|
for delete to authenticated
|
|
using (public.is_admin());
|
|
|
|
-- Begriffe: lesbar für alle, pflegbar nur durch Admin.
|
|
create policy genres_select on public.genres
|
|
for select to anon, authenticated using (true);
|
|
create policy genres_write_admin on public.genres
|
|
for all to authenticated
|
|
using (public.is_admin()) with check (public.is_admin());
|
|
|
|
create policy mechanics_select on public.mechanics
|
|
for select to anon, authenticated using (true);
|
|
create policy mechanics_write_admin on public.mechanics
|
|
for all to authenticated
|
|
using (public.is_admin()) with check (public.is_admin());
|
|
|
|
create policy categories_select on public.categories
|
|
for select to anon, authenticated using (true);
|
|
create policy categories_write_admin on public.categories
|
|
for all to authenticated
|
|
using (public.is_admin()) with check (public.is_admin());
|
|
|
|
-- Varianten und Zuordnungen folgen dem zugehörigen Spiel: Wer das Spiel sehen darf, sieht sie.
|
|
-- Ändern darf Admin oder der Ersteller eines privaten Spiels.
|
|
create policy game_variants_select on public.game_variants
|
|
for select to anon, authenticated
|
|
using (exists (select 1 from public.games g where g.id = game_id));
|
|
create policy game_variants_write on public.game_variants
|
|
for all to authenticated
|
|
using (
|
|
public.is_admin()
|
|
or exists (select 1 from public.games g
|
|
where g.id = game_id and g.owner_id = (select auth.uid()))
|
|
)
|
|
with check (
|
|
public.is_admin()
|
|
or exists (select 1 from public.games g
|
|
where g.id = game_id and g.owner_id = (select auth.uid()))
|
|
);
|
|
|
|
create policy game_genres_select on public.game_genres
|
|
for select to anon, authenticated
|
|
using (exists (select 1 from public.games g where g.id = game_id));
|
|
create policy game_genres_write on public.game_genres
|
|
for all to authenticated
|
|
using (
|
|
public.is_admin()
|
|
or exists (select 1 from public.games g
|
|
where g.id = game_id and g.owner_id = (select auth.uid()))
|
|
)
|
|
with check (
|
|
public.is_admin()
|
|
or exists (select 1 from public.games g
|
|
where g.id = game_id and g.owner_id = (select auth.uid()))
|
|
);
|
|
|
|
create policy game_mechanics_select on public.game_mechanics
|
|
for select to anon, authenticated
|
|
using (exists (select 1 from public.games g where g.id = game_id));
|
|
create policy game_mechanics_write on public.game_mechanics
|
|
for all to authenticated
|
|
using (
|
|
public.is_admin()
|
|
or exists (select 1 from public.games g
|
|
where g.id = game_id and g.owner_id = (select auth.uid()))
|
|
)
|
|
with check (
|
|
public.is_admin()
|
|
or exists (select 1 from public.games g
|
|
where g.id = game_id and g.owner_id = (select auth.uid()))
|
|
);
|
|
|
|
create policy game_categories_select on public.game_categories
|
|
for select to anon, authenticated
|
|
using (exists (select 1 from public.games g where g.id = game_id));
|
|
create policy game_categories_write on public.game_categories
|
|
for all to authenticated
|
|
using (
|
|
public.is_admin()
|
|
or exists (select 1 from public.games g
|
|
where g.id = game_id and g.owner_id = (select auth.uid()))
|
|
)
|
|
with check (
|
|
public.is_admin()
|
|
or exists (select 1 from public.games g
|
|
where g.id = game_id and g.owner_id = (select auth.uid()))
|
|
);
|