-- Die Sichtbarkeit dreht sich um: **wer hinzugewählt ist**, nicht wer -- ausgeblendet ist. -- -- ═══ Was sich ändert ═══ -- -- Bisher (note_mutes): sichtbar war alles, eine Zeile blendete eine Person -- aus. Ab jetzt (note_subscriptions): sichtbar sind die eigenen Notizen, und -- eine Zeile holt eine Kollegin dazu. -- -- ═══ Warum die alten Zeilen nicht übernommen werden ═══ -- -- Die Bedeutung kehrt sich um, nicht bloss das Vorzeichen. Eine wörtliche -- Übernahme — „alle ausser den Abgewählten" — trüge jeden Bestand in die -- neue Tabelle und stellte damit genau den Zustand her, den die Umstellung -- abschaffen soll: alle sehen alle. Wer die Einstellung morgen aufschlägt, -- hielte die Änderung für wirkungslos. -- -- Also fängt jede Person bei sich selbst an und wählt hinzu, wen sie -- braucht. Die Tabelle ist einen Tag alt; was dabei verlorengeht, sind ein -- paar Haken aus dem Ausprobieren. -- -- ═══ Was das kostet ═══ -- -- Der stille Fall, der bei den Ausnahmen nicht auftreten konnte, tritt jetzt -- auf: wer eine Kollegin nicht hinzuwählt, sieht deren Wiedervorlagen nicht — -- auch nicht im Urlaubsfall. Das ist die Kehrseite der Entscheidung und -- gehört benannt, nicht versteckt. drop table if exists note_mutes; create table if not exists note_subscriptions ( -- Wessen Einstellung das ist. user_id uuid not null references app_users(id) on delete cascade, -- Wessen Notizen zusätzlich erscheinen sollen. author_user_id uuid not null references app_users(id) on delete cascade, created_at timestamptz not null default now(), primary key (user_id, author_user_id), -- Sich selbst hinzuzuwählen ergibt keinen Sinn: die eigenen Notizen sind -- ohnehin immer dabei. Eine Zeile dafür wäre ein Haken, der nichts tut, -- und ein Haken, der sich entfernen liesse, ohne dass etwas geschieht. constraint chk_note_abos_nicht_selbst check (user_id <> author_user_id) ); comment on table note_subscriptions is 'Hinzugewählte Kolleg:innen je Person. Eine Zeile holt die Notizen von author_user_id in die Glocke von user_id. Ohne Zeile sieht man nur die eigenen.'; -- Der Zugriffsweg fragt immer „meine Auswahl": ohne Index ein Tabellenscan -- je Seitenaufruf, mit ihm ein Indexzugriff. create index if not exists idx_note_subscriptions_user on note_subscriptions (user_id); alter table note_subscriptions enable row level security; -- Eigentümergebunden wie hire_drafts und saved_reports: man sieht und ändert -- ausschliesslich die eigenen Zeilen. drop policy if exists "note_subscriptions_owner" on note_subscriptions; create policy "note_subscriptions_owner" on note_subscriptions for all using (user_id = app_current_user_id() and is_hr_user()) with check (user_id = app_current_user_id() and is_hr_user()); -- ═══ Gegenprobe ═══════════════════════════════════════════════════ do $$ begin if exists (select 1 from pg_tables where tablename = 'note_mutes') then raise exception 'note_mutes steht noch — die alte Tabelle wurde nicht entfernt.'; end if; if not exists ( select 1 from pg_tables where tablename = 'note_subscriptions' and rowsecurity ) then raise exception 'note_subscriptions hat keinen Zeilenschutz.'; end if; if not exists ( select 1 from pg_policies where tablename = 'note_subscriptions' and policyname = 'note_subscriptions_owner' ) then raise exception 'Die Eigentuemerregel auf note_subscriptions fehlt.'; end if; begin insert into note_subscriptions (user_id, author_user_id) values ('00000000-0000-0000-0000-000000000001', '00000000-0000-0000-0000-000000000001'); raise exception 'Eine Person kann sich selbst hinzuwaehlen — die Pruefbedingung greift nicht.'; exception when check_violation then null; when foreign_key_violation then null; end; end $$;