New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Proposal: onDelete and onUpdate relation properties #6996
Comments
For required relations, my preference would be to align the default Migrate behaviour with the default at the database level (RESTRICT). One big downside is that it would be a breaking change. |
Note: Amended the defaults section to now use agreed upon schema defaults. |
A couple of points:
|
We agree, but can't give anything more concrete at this point.
Prisma will stop emulating any referential actions and rely on the database for all SQL connectors. Connectors that do not support referential actions at all (but really need them to be ergonomic) will behave differently, but we'll get there in time. Additionally, those will be clearly marked with behavior such as
It will be treated as a breaking change. To be clear, we can't circumvent a breaking change without being inconsistent in the schema <> engines behavior. We'll be careful how to communicate this.
This has technically nothing to do with this topic. You will get API errors on violation that have error codes and messages you can match against, not a generic error blob. Much like unique violations are exposed right now, for example. While I agree that this ticket improves ergonomics, it is not required to make it work on the application side. |
Note: Amended error in defaults where |
Note: Added |
Looks good👍. Can't really think of anything that should be done differently. Besides maybe more examples in docs with more complicated associations and what exactly would happen when a model gets deleted(how it is going to cascade) |
Just giving feedback: this is exactly how I feel things should be handled.
It's easy (and logical) to understand onDelete.
Yes. |
As someone used to SQL syntax, I found it to be the perfect proposal. But I imagine that for newcomers, perhaps the direction of the relationships can be confusing. For example, I expect a lot of questions like that to rise: "If I'm using cascade delete and I delete a User, will the Post records be deleted together?" or "How to delete a Post cascade deleting the JoinTable records but keeping User records?". I think the documentation needs to be very explicit about what's going to be happening in all cases of actions between the related tables. |
@ErickPetru I agree on that with you, I think the vscode plugin could help a lot on that front by providing interactive documentation based on what the user is looking at. |
Proposal implemented in prisma/prisma-engines#1947 Instructions on how to use this, and a place to give feedback: #7816 |
Am I reading this right as saying that, in the future, Prisma will default to |
Exactly. If the relation has at least one scalar field that is |
Thanks @pimeys that's wonderful! Really appreciate the amount of thought that's gone into Prisma and the quality of the communication and documentation |
This has now been released as a preview feature flag. You can read about it in the release notes for 2.26.0: https://github.com/prisma/prisma/releases/tag/2.26.0 If you have any feedback, please use this issue: #7816 |
Prisma on delete and on update behavior
A fundamental feature of Prisma is expressing relations between models. A relation can express many different concepts and can have many different meanings depending on the application domain: inclusion, association, dependence, state, etc. They are an essential tool of relational data model design.
As such, there is no single right answer to the question of what should happen when one side of a relation is changed or deleted.
This issue is a concrete proposal to enable that functionality and the result of multiple internal design discussions. Prisma Schema Language (PSL) syntax and client/migration/introspection behavior to consider will be outlined.
Please note that this proposal is only valid for SQL connectors for now. The behavior for other connectors will be considered separately in the future.
We are looking forward to any feedback on this proposal.
PSL Syntax Proposal
Without further ado, the following is an example of the syntax the internal working group settled on:
A note on terminology:
fields
of@relation
. For 1-m and 1-1 relations, this is the fields in thefields
property of@relation
. In the example above, this would beauthor_id
. M-n relations do not have relation scalar fields in the same sense as other relations, they only have their IDs (on both sides) as "referenced scalar fields" due to the underlying join table (see below).references
of@relation
. For m-n relations, it's currently always the IDs of both models (which are stored as tuples in an implicit join table).The semantics of
onDelete
andonUpdate
are almost exactly how SQL expresses on update and on delete. For the example above: If the related author (User
) of aPost
is deleted (onDelete
), delete allPost
rows that are related to the deletedUser
(Cascade
). If theid
field of the relatedUser
is updated, also updateauthor_id
of allPost
s that references thatUser
.Possible keywords for
onDelete
andonUpdate
are:Cascade
: Deletes record if dependent record is deleted. Updates relation scalar fields if referenced scalar fields of the dependent record are updated.Restrict
: Prevents operation (both updates and deletes) from succeeding if any records are connected. This behavior will always result in a runtime error for required relations.NoAction
: Behavior is database specific. Either defers throwing an integrity check error until the end of the transaction or errors immediately. If deferred, this makes it possible to temporarily violate integrity in a transaction while making sure that subsequent operations in the transaction restore integrity.SetNull
: Sets relation scalar fields tonull
if the relation is deleted or updated. This will always result in a runtime error if one or more of the relation scalar fields are required.SetDefault
: Sets relation scalar fields to their default values on update or delete of relation. Will always result in a runtime error if no defaults are provided for any relation scalar fields.Note that the availability of these keywords and the exact behavior depends on the provider. For example, SQL Server does not support
Restrict
, but the semantics ofNoAction
are identical toRestrict
.Limitation: Implicit Many-to-many
Prisma offers an implicit many-to-many relation syntax, for example:
Internally, this maps to a join table with two foreign keys. Providing
onDelete
/onUpdate
behavior for m-n relations would be inconsistent with 1-1 or 1-m relations:onDelete
/onUpdate
on m-n misleads people into thinking that related records are deleted, when in fact they are only disconnected.Elaborating on the first point, this is an example of an m-n relation written out with an explicit join table and
onDelete
:To be consistent with other relations, the definition of delete or update behavior must happen on the join table as this is the place where the relations are actually defined and follow the logic of "if x happens with the related record, then something happens with this record". This leads into the second point, which is that having the syntax on implicit m-n would suggest different behavior to what it does. Let's write out a hypothetical
onDelete
example for implicit m-n:To have consistent semantics with the other relations types, it would be expected that if a query deletes either a User or a Post record, dependent other records would be deleted. However, under the hood, because of the hidden join table, they would be disconnected, as the foreign keys are actually defined on the join table and only the join table records would be deleted. Even more, the construct has unclear implications, even if it would behave as expected - a Post may have many authors, deleting a post that triggers a user delete would in turn trigger deletes of more users, triggering more deletes of posts, ...
In conclusion, we feel that consistency of the schema is more important than expanding the Prisma-specific construct of implicit m-n relations. With this proposal, we will require users that want to customize their join table behavior to use explicit join tables. We understand that this will cause inconveniences, but we have m-n relation changes planned in the future that will greatly improve the query ergonomics of explicit join tables.
Emulation
For connectors without referential action support (e.g. MongoDB), we want to provide an emulated subset of actions for
onDelete
andonUpdate
. The emulated versions try to be as close as possible to the semantics of those provided by the other connectors:Cascade
: UnchangedRestrict
: UnchangedSetNull
: Unchanged.SetDefault
: Unchanged.NoAction
: Signals that nothing will be done by Prisma. Will result in inconsistent data if not taken care of by the application.We will gradually introduce these, the initial versions for MongoDB and co. may only support a few.
Defaults
This section discusses the current and desired default for
onDelete
/onUpdate
for Prisma tools if the behavior is not specified explicitly in the Prisma schema. Note that the Prisma client emulates database behavior on the application layer, due to the fact that it can't know what the database behavior is without an annotation in the schema, so the current implementation chose to emulate sensible defaults.Required relations:
onUpdate
Cascade
Cascade
(assumed)onDelete
Cascade
Restrict
(emulated)Optional relations:
onUpdate
Cascade
Cascade
(assumed)onDelete
SetNull
SetNull
(emulated)Note for Migrate and many-to-many relations: M-n is set to
Cascade
for both deletes and updates on the join tables, except for many-to-many self-relations on SQL Server, where we will useNoAction
to avoid cycles.Database Management Systems (DBMS):
All SQL DBMS Prisma supports have different defaults (if one creates schemas manually in SQL) than the ones Prisma currently uses:
onUpdate
:NoAction
(Restrict
for MySQL)onDelete
:NoAction
(Restrict
for MySQL)The Prisma schema follows the principle of optional complexity, which means that we strive to provide sensible defaults if a property is not defined in the schema. The above raises the question which defaults we want to follow when introducing
onDelete
andonUpdate
.We settled on the current Prisma defaults for referential actions (connectors without direct support emulate these):
onUpdate
Cascade
Cascade
onDelete
SetNull
Restrict
We believe this is the best compromise to offer a smooth path forward for the following reasons:
Concrete Feedback Questions
@relation(onDelete: ..., onUpdate: ...)
? If not, would you prefer a naming scheme like@relation(onDeleteOther: ...)
or@relation(onDeleteUser: ...)
?The text was updated successfully, but these errors were encountered: