Skip to content
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

SQL Server: Detect referential action cycles #2113

Merged
merged 4 commits into from Aug 2, 2021

Conversation

pimeys
Copy link
Contributor

@pimeys pimeys commented Jul 30, 2021

Implementing an early detection of cascading cycles, allowing us to highlight the relations and to give a better error than what SQL Server is giving us.

Examples what we get here.

Self-relation

A self-relation is a cycle, and a special case of the feature. We give a different message in these cases, where the user can solve the problem by adding onDelete: NoAction, onUpdate: NoAction to one of the sides.

model A {
  id     Int  @id @default(autoincrement())
  child  A?   @relation(name: "a_self_relation")
  parent A?   @relation(name: "a_self_relation", fields: [aId], references: [id], onUpdate: SetDefault)
  aId    Int?
}

2021-08-02_13-08-1627905355

Cycle between two tables

If we refer a table that has a relation back to the original table, one of the relations needs to have onDelete: NoAction, onUpdate: NoAction set to solve the issue.

model A {
  id     Int  @id @default(autoincrement())
  b      B    @relation(name: "foo", fields: [bId], references: [id], onDelete: Cascade)
  bId    Int
  bs     B[]  @relation(name: "bar")
}

model B {
  id     Int @id @default(autoincrement())
  a      A   @relation(name: "bar", fields: [aId], references: [id], onUpdate: Cascade)
  as     A[] @relation(name: "foo")
  aId    Int
}

2021-08-02_13-08-1627905449

Hop over one table

A cycle where we go over one table, and then back to table A.

model A {
  id     Int  @id @default(autoincrement())
  bId    Int
  b      B    @relation(fields: [bId], references: [id])
  cs     C[]
}

model B {
  id     Int  @id @default(autoincrement())
  as     A[]
  cId    Int
  c      C    @relation(fields: [cId], references: [id])
}

model C {
  id     Int @id @default(autoincrement())
  bs     B[]
  aId    Int
  a      A   @relation(fields: [aId], references: [id])
}

2021-08-02_14-08-1627907565

Multiple separate paths between two tables

The referential actions can be in any side of the relation to matter. Here we have a link between A and B, C and B, C and A.

model A {
  id     Int  @id @default(autoincrement())
  bId    Int
  b      B    @relation(fields: [bId], references: [id])
  cs     C[]
}

model B {
  id     Int  @id @default(autoincrement())
  as     A[]
  cs     C[]
}

model C {
  id     Int  @id @default(autoincrement())
  aId    Int
  bId    Int
  a      A    @relation(fields: [aId], references: [id])
  b      B    @relation(fields: [bId], references: [id])
}

2021-08-02_14-08-1627907670

Implicit values in errors

If referential actions are not written to the relations, but they have an effect to the cycle errors, we write them to the error message.

Closes: prisma/prisma#4580
Closes: prisma/prisma#5782

@pimeys pimeys added this to the 2.29.0 milestone Jul 30, 2021
@pimeys pimeys force-pushed the data-model-validator/cascade-cycle-detection branch from 036a376 to ba4f48d Compare July 30, 2021 14:55
Copy link
Member

@do4gr do4gr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM.
Only thing I am wondering is if we should mention the implicit default action when no explicit action is set in the generated warnings. Otherwise users might be irritated that they are getting cycles but are not seeing the concrete action that's causing that. Could just be a static comment: If actions are not spelled out this is what we assume...

@pimeys
Copy link
Contributor Author

pimeys commented Jul 30, 2021

LGTM.
Only thing I am wondering is if we should mention the implicit default action when no explicit action is set in the generated warnings. Otherwise users might be irritated that they are getting cycles but are not seeing the concrete action that's causing that. Could just be a static comment: If actions are not spelled out this is what we assume...

I'll see what I can do...

@pimeys
Copy link
Contributor Author

pimeys commented Aug 1, 2021

LGTM.
Only thing I am wondering is if we should mention the implicit default action when no explicit action is set in the generated warnings. Otherwise users might be irritated that they are getting cycles but are not seeing the concrete action that's causing that. Could just be a static comment: If actions are not spelled out this is what we assume...

Made it a bit nicer here: 3e94407

So, if we have an implicit actions in the data model, and either one of them causes a cycle error, the error tells which implicit actions caused it.

@pimeys pimeys merged commit 8062e30 into master Aug 2, 2021
@pimeys pimeys deleted the data-model-validator/cascade-cycle-detection branch August 2, 2021 12:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment