-- Narrow the client-upload idempotency index so it stops covering soft-deleted rows.
--
-- The bug (H9). Migration 022 created the index partial on `client_upload_id IS NOT NULL`
-- only, so a soft-deleted row kept occupying its key. But `find_by_client_upload_id` filters
-- `deleted_at IS NULL` — deliberately, and its doc comment says so: "if the guest deleted the
-- photo and their queue later retries, they should get a fresh upload rather than a
-- resurrection of a deleted one." The index and the lookup therefore disagreed, and the
-- disagreement is reachable by an ordinary guest:
--
--   1. guest uploads a photo, then deletes it (soft delete — the row stays, `deleted_at` set)
--   2. their queue retries the same item (reconnect requeue, or they tap "Erneut")
--   3. the whole body is re-streamed and re-validated, then `ON CONFLICT DO NOTHING` matches
--      the DEAD row and inserts nothing
--   4. the replay lookup filters that row out and finds nothing, so the handler returns 409
--   5. the client classifies 409 as terminal and DELETES the blob from IndexedDB
--
-- The photo is now gone from the device with no row in the gallery, and there is no path back.
-- Re-selecting the same file from the camera roll mints a new `client_upload_id`, so that does
-- work — but the guest has no way to know that is what is required.
--
-- Adding `deleted_at IS NULL` makes the index agree with the lookup: a key is claimed only
-- while a LIVE row holds it, so step 3 inserts a fresh row and the retry succeeds.
--
-- Uniqueness among live rows is what the feature actually needs. The property migration 022
-- was protecting — "the same photo must not land in the gallery twice" — is about rows the
-- guest can see, and a soft-deleted row is not one of those.

DROP INDEX IF EXISTS upload_client_upload_id_key;

-- CONCURRENTLY is deliberately NOT used: sqlx runs each migration inside a transaction, and
-- CREATE INDEX CONCURRENTLY cannot run in one. The table is small (one event's uploads) and
-- this runs at boot before the server accepts requests, so the brief lock costs nothing.
CREATE UNIQUE INDEX upload_client_upload_id_key
    ON upload (client_upload_id)
    WHERE client_upload_id IS NOT NULL AND deleted_at IS NULL;
