-- Fix-up for failed 20260515000002: dropping NOT NULL on a PK column requires -- dropping the PK first. Replace the natural PK (conversation_id, -- recipient_device_id, key_version) with a synthetic row_id and re-establish -- uniqueness via two partial unique indexes — one per recipient mode. -- -- Also re-runs the policies + indexes from 20260515000002 that were skipped -- when migration 2 errored out at the ALTER COLUMN step. alter table public.conversation_keys drop constraint if exists conversation_keys_pkey; alter table public.conversation_keys add column if not exists row_id bigserial primary key; alter table public.conversation_keys alter column recipient_device_id drop not null, alter column sender_device_id drop not null; -- Partial unique constraints: device-mode and user-mode rows can coexist for -- the same (conv, version) but each mode is unique on its own. create unique index if not exists conversation_keys_device_recipient_uniq on public.conversation_keys(conversation_id, recipient_device_id, key_version) where recipient_device_id is not null; create unique index if not exists conversation_keys_user_recipient_uniq on public.conversation_keys(conversation_id, recipient_user_id, key_version) where recipient_user_id is not null; create index if not exists conversation_keys_recipient_user_idx on public.conversation_keys(recipient_user_id); -- Re-apply the policies from migration 20260515000002 (those didn't run -- because the failing ALTER stopped the file). drop policy if exists conversation_keys_select_owner on public.conversation_keys; create policy conversation_keys_select_owner on public.conversation_keys for select to authenticated using ( recipient_user_id = auth.uid() or exists ( select 1 from public.devices d where d.id = recipient_device_id and d.user_id = auth.uid() ) ); drop policy if exists conversation_keys_insert_member on public.conversation_keys; create policy conversation_keys_insert_member on public.conversation_keys for insert to authenticated with check ( exists ( select 1 from public.conversation_members m where m.conversation_id = conversation_keys.conversation_id and m.user_id = auth.uid() and m.accepted = true ) and ( sender_user_id = auth.uid() or exists ( select 1 from public.devices d where d.id = sender_device_id and d.user_id = auth.uid() ) ) and ( (recipient_user_id is not null and exists ( select 1 from public.conversation_members m where m.conversation_id = conversation_keys.conversation_id and m.user_id = recipient_user_id and m.accepted = true )) or (recipient_device_id is not null and exists ( select 1 from public.devices d join public.conversation_members m on m.user_id = d.user_id and m.conversation_id = conversation_keys.conversation_id where d.id = recipient_device_id and m.accepted = true )) ) );