Allow deferral of unique constraints using definitions in Prisma schema
See original GitHub issueProblem
Unique constraints are enforced during each statement in a database operation by default. However, it would be useful to defer this enforcement to the end of the full transaction in some cases where values are getting “shifted” and may be temporarily duplicated.
For example, take a table that represents an ordered list organized with an index
column which is a unique identifier (only one row can occupy the index in the list.) If an item is to be “inserted” into the list, all the index
es need to adjust to accommodate this. However, if an updateMany
method is used to increment the index
, the default order of the update operation will cause a failure on the unique constraint because the index
is already in use by another row. If the unique enforcement waited till the full transaction completed, this would not cause an issue.
A more specific example and use case is explained in my Q&A thread here: https://github.com/prisma/prisma/discussions/8789
This is possible in PostgreSQL and possibly other databases: https://www.postgresql.org/docs/9.1/sql-set-constraints.html
Suggested solution
Allow DEFERRED
options to be defined in the Prisma schema, alongside the @unique
declarations. This will allow prisma to keep the DEFERRED options in sync with the schema AND the database. It can currently be done directly in the database manually but will be overwritten when the schema is deployed with Migrate.
Possible example to match SQL syntax, probably a way that fits better with the prisma syntax:
@@unique([guildId, queueIndex], name: "queuePosition") DEFERRABLE INITIALLY DEFERRED
Alternatives
One early alternative I used was first querying all the rows to be affected, sorted in a way that would allow the unique constraint to be maintained (start at the highest index if they are to be incremented by 1.) Then, using a for loop, do a single update for each, maintaining the operations in the order that the query was sorted by. This works, but is not ideal for performance.
The next alternative I found was to manually add the DEFERRED options to the database. In order to get this to work, I had to:
- Drop the @unique constraint from the Prisma schema and migrate it to the database.
- Manually alter the constraint in the database directly with custom SQL to add the deferrals.
- Use
prisma db pull
to “rediscover” the unique constraint so it would be enforced in the code/typings. However, the database kept the DEFERRED options intact.
This will however be overwritten if prisma migrate dev
for example is run in the future, so it’s not a sustainable solution.
Additional context
Please see the discussion thread I opened for a more specific example. As this is a general feature request, here I attempted to be more general so it can apply to all projects. https://github.com/prisma/prisma/discussions/8789
Other similar suggestions in the past
I found a few other mentions of this so I figured I’d link them: https://github.com/prisma/prisma/issues/3502 https://github.com/prisma/prisma/discussions/3495
Issue Analytics
- State:
- Created 2 years ago
- Reactions:13
- Comments:9 (4 by maintainers)
Top GitHub Comments
Allowing the part that you were missing (https://github.com/prisma/prisma/issues/13115#issuecomment-1115077872) is indeed content of this feature request here.
Looks like it’s unsupported in MySQL and SQLite, unfortunately. Still trying to track down an answer for MongoDB. It doesn’t appear to have explicitly deferrable constraints, but I also don’t know how uniqueness is enforced by default (for example, if it’s already post-transaction this wouldn’t matter anyway.)