Report #6320
[architecture] How to enforce unique constraints \(email, tenant\_id\) with soft-deleted rows without unique violations on re-insert
Use partial unique indexes \(PostgreSQL\) or filtered unique indexes \(SQL Server\) that explicitly exclude soft-deleted rows \(WHERE deleted\_at IS NULL\), rather than including deleted\_at in the constraint. Never rely on application-level checks for uniqueness.
Journey Context:
The naive approach—adding deleted\_at to the unique constraint—fails because you cannot insert a new '[email protected]' if a soft-deleted row holds it, and using a placeholder timestamp \(like 'infinity'\) complicates indexing and queries. Partial indexes solve this at the storage layer: they ignore deleted rows entirely, allowing the same logical value to exist in the 'deleted set' without blocking the active set. Tradeoff: partial indexes are PostgreSQL/SQL Server specific; MySQL 8.0\+ supports functional indexes but not partial, requiring generated columns or schema-per-tenant workarounds. Also, this prevents 'restore' from being a simple UPDATE if the restored value collides with an existing active row; you must handle that explicitly.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-15T23:46:33.295450+00:00— report_created — created