-- Normalize `rel_path` columns to forward slashes. Windows ingest -- historically produced a mix of `\` and `/`, which broke lookups and -- caused spurious UNIQUE-constraint violations on re-registration. -- -- SQLite enforces UNIQUE per-row during UPDATE, so we have to drop -- losing duplicates BEFORE normalizing. For each table that has a -- UNIQUE on rel_path, we delete rows whose normalized form already -- exists in canonical (forward-slash) form — keeping the existing -- forward-slash row as the survivor. Then a flat UPDATE finishes the -- job for remaining backslash rows. -- image_exif: UNIQUE(library_id, rel_path) DELETE FROM image_exif WHERE rel_path LIKE '%\%' AND EXISTS ( SELECT 1 FROM image_exif AS other WHERE other.library_id = image_exif.library_id AND other.rel_path = REPLACE(image_exif.rel_path, '\', '/') AND other.id != image_exif.id ); UPDATE image_exif SET rel_path = REPLACE(rel_path, '\', '/') WHERE rel_path LIKE '%\%'; -- favorites: UNIQUE(userid, rel_path) DELETE FROM favorites WHERE rel_path LIKE '%\%' AND EXISTS ( SELECT 1 FROM favorites AS other WHERE other.userid = favorites.userid AND other.rel_path = REPLACE(favorites.rel_path, '\', '/') AND other.id != favorites.id ); UPDATE favorites SET rel_path = REPLACE(rel_path, '\', '/') WHERE rel_path LIKE '%\%'; -- tagged_photo: UNIQUE(rel_path, tag_id) DELETE FROM tagged_photo WHERE rel_path LIKE '%\%' AND EXISTS ( SELECT 1 FROM tagged_photo AS other WHERE other.tag_id = tagged_photo.tag_id AND other.rel_path = REPLACE(tagged_photo.rel_path, '\', '/') AND other.id != tagged_photo.id ); UPDATE tagged_photo SET rel_path = REPLACE(rel_path, '\', '/') WHERE rel_path LIKE '%\%'; -- entity_photo_links: UNIQUE(entity_id, library_id, rel_path, role) DELETE FROM entity_photo_links WHERE rel_path LIKE '%\%' AND EXISTS ( SELECT 1 FROM entity_photo_links AS other WHERE other.entity_id = entity_photo_links.entity_id AND other.library_id = entity_photo_links.library_id AND other.role = entity_photo_links.role AND other.rel_path = REPLACE(entity_photo_links.rel_path, '\', '/') AND other.id != entity_photo_links.id ); UPDATE entity_photo_links SET rel_path = REPLACE(rel_path, '\', '/') WHERE rel_path LIKE '%\%'; -- video_preview_clips: UNIQUE(library_id, rel_path) DELETE FROM video_preview_clips WHERE rel_path LIKE '%\%' AND EXISTS ( SELECT 1 FROM video_preview_clips AS other WHERE other.library_id = video_preview_clips.library_id AND other.rel_path = REPLACE(video_preview_clips.rel_path, '\', '/') AND other.id != video_preview_clips.id ); UPDATE video_preview_clips SET rel_path = REPLACE(rel_path, '\', '/') WHERE rel_path LIKE '%\%'; -- photo_insights has no UNIQUE on rel_path (history table), so a plain -- normalize is safe. UPDATE photo_insights SET rel_path = REPLACE(rel_path, '\', '/') WHERE rel_path LIKE '%\%'; ANALYZE;