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.
This commit is contained in:
@@ -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
|
||||
))
|
||||
)
|
||||
);
|
||||
Reference in New Issue
Block a user