-- Drop the unique index on (user_id, shift_date) for work_plan_overrides. -- MySQL unique indexes don't ignore soft-deleted rows, which would prevent -- createOverride from soft-deleting an existing override and creating a new -- one for the same (user_id, shift_date). Application code enforces the -- "at most one active override per (user_id, shift_date)" invariant. -- The application-level check is wrapped in a transaction; see -- src/services/plan.service.ts:createOverride. -- -- The original index was created in -- 20260605120000_add_work_plan/migration.sql as `uniq_wpo_user_date`. -- IMPORTANT ordering: `uniq_wpo_user_date` is the only index covering the -- `user_id` foreign key, and MySQL refuses to drop an index still needed by a -- FK. So we CREATE the replacement non-unique index FIRST (giving the FK -- another covering index), THEN drop the unique one. The reverse order fails -- with "Cannot drop index ... needed in a foreign key constraint" on a fresh -- apply. -- Add a non-unique index for the (user_id, shift_date) lookup used by -- createOverride's "is there an active override for this user-day?" query -- and by resolveCell / resolveGrid. This is a plain index, not a unique -- one — multiple rows (active and soft-deleted) can share the same -- (user_id, shift_date) pair. CREATE INDEX `idx_wpo_user_date` ON `work_plan_overrides`(`user_id`, `shift_date`); DROP INDEX `uniq_wpo_user_date` ON `work_plan_overrides`;