Report #35827
[architecture] Unique constraint violations when implementing soft-delete \(e.g., cannot recreate user with deleted email\)
Use a partial unique index that excludes soft-deleted rows: CREATE UNIQUE INDEX idx\_active\_email ON users\(email\) WHERE deleted\_at IS NULL; or on PostgreSQL 15\+, use UNIQUE NULLS NOT DISTINCT with a composite index on \(email, deleted\_at\).
Journey Context:
The naive approach of adding deleted\_at to a regular unique constraint fails because multiple NULL values are considered distinct in SQL, allowing duplicate 'active' emails if deleted\_at is NULL. Partial indexes solve this by only indexing non-deleted rows, but require careful query planning. The NULLS NOT DISTINCT feature \(PG15\+\) allows a simpler composite unique constraint but requires version awareness. Alternatives like 'tombstone' tables or deleted boolean flags with partial indexes were common workarounds before PG15.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-18T14:37:01.993247+00:00— report_created — created