-- Follow-up to the final whole-branch review before merge (deferred, fast -- follow-up task): public.bookings had no uniqueness constraint on -- (date, time), so two concurrent bookings for the same slot could both -- insert successfully -- the second one would silently no-op on the -- booked_slots mirror (sync_booked_slot()'s `on conflict do nothing`) -- instead of failing, leaving the app's slot-availability check unreliable -- under concurrency. -- -- A partial unique index on confirmed bookings only (not a plain unique -- constraint on the whole table) is required because status can be -- 'cancelled': a cancelled booking must not permanently block the slot it -- once held, and multiple cancelled bookings may legitimately share a -- (date, time) (e.g. several people asked for the same slot, all declined). -- -- SELF-HEALING: on `supabase db push` against the live project, this -- `create unique index` could fail outright if any existing confirmed -- booking already shares a (date, time) with another -- exactly the bug -- being fixed here, so entirely plausible in already-live data. Rather than -- have the migration fail and block deploy, dedupe_confirmed_booking_slots() -- below collapses each duplicate group down to the earliest-created row -- (keeping it 'confirmed') and cancels the rest before the index is -- created, so index creation is guaranteed to succeed either way. It's -- defined as a standalone function (called once below, but also directly -- callable) so the pgTAP test can exercise the exact same logic the -- migration runs, rather than a copy that could drift from it. create or replace function public.dedupe_confirmed_booking_slots() returns void language plpgsql security definer set search_path = '' as $$ declare dup record; begin for dup in select date, time, count(*) as cnt from public.bookings where status = 'confirmed' group by date, time having count(*) > 1 loop update public.bookings set status = 'cancelled' where date = dup.date and time = dup.time and status = 'confirmed' and id <> ( select id from public.bookings where date = dup.date and time = dup.time and status = 'confirmed' order by created_at asc, id asc limit 1 ); raise notice 'dedupe_confirmed_booking_slots: cancelled % duplicate confirmed booking(s) for slot % %, keeping the earliest-created one', dup.cnt - 1, dup.date, dup.time; end loop; end; $$; revoke execute on function public.dedupe_confirmed_booking_slots() from public, anon, authenticated; grant execute on function public.dedupe_confirmed_booking_slots() to service_role; select public.dedupe_confirmed_booking_slots(); create unique index bookings_active_slot_idx on public.bookings (date, time) where status = 'confirmed';