Report #8205
[architecture] Unique constraint violations when re-creating soft-deleted records
Use a partial unique index that excludes soft-deleted rows: CREATE UNIQUE INDEX idx\_active\_email ON users\(email\) WHERE deleted\_at IS NULL. Do not include deleted\_at in a composite unique constraint, as SQL NULL \!= NULL semantics will allow duplicates.
Journey Context:
The naive approach adds deleted\_at to a composite unique key \(email, deleted\_at\), but this fails because \([email protected], NULL\) \!= \([email protected], NULL\) in SQL, allowing duplicate active emails. Adding a boolean is\_deleted fares no better. The partial index approach enforces uniqueness only on 'alive' rows, allowing safe re-creation of deleted users. Alternatives like separate archive tables break foreign key consistency and complicate joins; application-layer enforcement fails under race conditions. This pattern requires database support for partial indexes \(PostgreSQL, SQL Server\) or functional indexes with expression-based filtering.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-16T04:50:23.749583+00:00— report_created — created