From 6a9a0bb804af8038512632a162b6cb842aad11ab Mon Sep 17 00:00:00 2001 From: byGalax Date: Fri, 15 May 2026 23:25:49 +0200 Subject: [PATCH] fix(db): swap conversation_keys natural PK for synthetic row_id Migration 20260515000002 failed on prod because dropping NOT NULL on recipient_device_id was rejected (column is part of the natural primary key). This fix-up drops the PK, adds a synthetic row_id BIGSERIAL PK, re-applies the NOT NULL drop, and re-runs the indexes/policies that were skipped after the failure. --- ...0260515000005_conversation_keys_pk_fix.sql | 89 +++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 supabase/migrations/20260515000005_conversation_keys_pk_fix.sql diff --git a/supabase/migrations/20260515000005_conversation_keys_pk_fix.sql b/supabase/migrations/20260515000005_conversation_keys_pk_fix.sql new file mode 100644 index 0000000..de3cb5e --- /dev/null +++ b/supabase/migrations/20260515000005_conversation_keys_pk_fix.sql @@ -0,0 +1,89 @@ +-- 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 + )) + ) + );