Report #17206
[architecture] Enforcing unique constraints \(email, slug\) with soft deletes \(deleted\_at\) allows duplicates or requires complex workarounds
Use partial unique indexes that exclude soft-deleted rows: CREATE UNIQUE INDEX idx ON users\(email\) WHERE deleted\_at IS NULL; never rely on NULL \!= NULL behavior or add deleted\_at to the constraint, which breaks undelete scenarios.
Journey Context:
Developers add deleted\_at and expect UNIQUE\(email\) to ignore NULLs, but SQL standards treat NULL \!= NULL, allowing duplicate emails for deleted users. Adding deleted\_at to the constraint requires updating the timestamp to a sentinel value on undelete, complicating recovery. Partial indexes are the only performant, correct solution; they also prevent index bloat from deleted rows. The 'deleted boolean' alternative requires a trigger to maintain a unique composite index and complicates queries.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-17T04:46:43.313390+00:00— report_created — created