-
Notifications
You must be signed in to change notification settings - Fork 212
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
feat(psl): add warning when SetDefault
is used on mysql
with relationMode = "foreignKeys"
#3435
Changes from all commits
ae425bb
12b1e6d
37b1a10
2909b2e
6c2ec0f
c00fd43
472ceb2
7b33f38
a028c2f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
mod cockroachdb; | ||
mod mysql; | ||
mod sqlite; | ||
|
||
use barrel::types; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
use introspection_engine_tests::test_api::*; | ||
|
||
// Older versions of MySQL (5.6+) raise a syntax error on `CREATE TABLE` declarations with `SET DEFAULT` referential actions, | ||
// so we can skip introspecting those. MariaDb 10.0 suffers from the same issue. | ||
// We should see validation warnings on MySQL 8+. | ||
#[test_connector(tags(Mysql8), exclude(Vitess))] | ||
async fn introspect_set_default_should_warn(api: &TestApi) -> TestResult { | ||
let setup = r#" | ||
CREATE TABLE `SomeUser` ( | ||
`id` INTEGER NOT NULL AUTO_INCREMENT, | ||
|
||
PRIMARY KEY (`id`) | ||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; | ||
|
||
CREATE TABLE `Post` ( | ||
`id` INTEGER NOT NULL AUTO_INCREMENT, | ||
`userId` INTEGER NULL DEFAULT 3, | ||
|
||
PRIMARY KEY (`id`) | ||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; | ||
|
||
ALTER TABLE `Post` ADD CONSTRAINT `Post_userId_fkey` | ||
FOREIGN KEY (`userId`) REFERENCES `SomeUser`(`id`) | ||
ON DELETE SET DEFAULT ON UPDATE SET DEFAULT; | ||
"#; | ||
|
||
api.raw_cmd(setup).await; | ||
|
||
let expected_schema = expect![[r#" | ||
generator client { | ||
provider = "prisma-client-js" | ||
} | ||
|
||
datasource db { | ||
provider = "mysql" | ||
url = "env(TEST_DATABASE_URL)" | ||
} | ||
|
||
model Post { | ||
id Int @id @default(autoincrement()) | ||
userId Int? @default(3) | ||
SomeUser SomeUser? @relation(fields: [userId], references: [id], onDelete: SetDefault, onUpdate: SetDefault) | ||
|
||
@@index([userId], map: "Post_userId_fkey") | ||
} | ||
|
||
model SomeUser { | ||
id Int @id @default(autoincrement()) | ||
Post Post[] | ||
} | ||
"#]]; | ||
|
||
expected_schema.assert_eq(&api.introspect().await?); | ||
let schema = psl::parse_schema(expected_schema.data())?; | ||
|
||
let warning_messages = schema | ||
.diagnostics | ||
.warnings_to_pretty_string("schema.prisma", &schema.db.source()); | ||
|
||
let expected_validation = expect![[r#" | ||
[1;93mwarning[0m: [1mUsing SetDefault on MySQL may yield to unexpected results, as the database will silently change the referential action to `NoAction`.[0m | ||
[1;94m-->[0m [4mschema.prisma:14[0m | ||
[1;94m | [0m | ||
[1;94m13 | [0m userId Int? @default(3) | ||
[1;94m14 | [0m SomeUser SomeUser? @relation(fields: [userId], references: [id], [1;93monDelete: SetDefault[0m, onUpdate: SetDefault) | ||
[1;94m | [0m | ||
[1;93mwarning[0m: [1mUsing SetDefault on MySQL may yield to unexpected results, as the database will silently change the referential action to `NoAction`.[0m | ||
[1;94m-->[0m [4mschema.prisma:14[0m | ||
[1;94m | [0m | ||
[1;94m13 | [0m userId Int? @default(3) | ||
[1;94m14 | [0m SomeUser SomeUser? @relation(fields: [userId], references: [id], onDelete: SetDefault, [1;93monUpdate: SetDefault[0m) | ||
[1;94m | [0m | ||
"#]]; | ||
expected_validation.assert_eq(&warning_messages); | ||
|
||
Ok(()) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -62,6 +62,17 @@ impl Diagnostics { | |
|
||
String::from_utf8_lossy(&message).into_owned() | ||
} | ||
|
||
pub fn warnings_to_pretty_string(&self, file_name: &str, datamodel_string: &str) -> String { | ||
let mut message: Vec<u8> = Vec::new(); | ||
|
||
for warn in self.warnings() { | ||
warn.pretty_print(&mut message, file_name, datamodel_string) | ||
.expect("printing datamodel warning"); | ||
} | ||
|
||
String::from_utf8_lossy(&message).into_owned() | ||
} | ||
Comment on lines
+66
to
+75
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is used to snapshot the warnings in the new There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It will also be useful for https://github.com/prisma/prisma-private/issues/198 |
||
} | ||
|
||
impl From<DatamodelError> for Diagnostics { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -240,7 +240,7 @@ pub(crate) fn primary_key_connector_specific(model: ModelWalker<'_>, ctx: &mut C | |
} | ||
|
||
pub(super) fn connector_specific(model: ModelWalker<'_>, ctx: &mut Context<'_>) { | ||
ctx.connector.validate_model(model, ctx.diagnostics) | ||
ctx.connector.validate_model(model, ctx.relation_mode, ctx.diagnostics) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Allowing access to |
||
} | ||
|
||
pub(super) fn id_has_fields(model: ModelWalker<'_>, ctx: &mut Context<'_>) { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
generator client { | ||
provider = "prisma-client-js" | ||
} | ||
|
||
datasource db { | ||
provider = "mysql" | ||
url = "mysql://" | ||
relationMode = "prisma" | ||
} | ||
|
||
model A { | ||
id Int @id | ||
bs B[] | ||
} | ||
|
||
model B { | ||
id Int @id | ||
aId Int? @default(3) | ||
a A? @relation(fields: [aId], references: [id], onUpdate: SetDefault, onDelete: SetDefault) | ||
} | ||
// [1;91merror[0m: [1mError validating: Invalid referential action: `SetDefault`. Allowed values: (`Cascade`, `Restrict`, `NoAction`, `SetNull`)[0m | ||
// [1;94m-->[0m [4mschema.prisma:19[0m | ||
// [1;94m | [0m | ||
// [1;94m18 | [0m aId Int? @default(3) | ||
// [1;94m19 | [0m a A? @relation(fields: [aId], references: [id], onUpdate: SetDefault, [1;91monDelete: SetDefault[0m) | ||
// [1;94m | [0m | ||
// [1;91merror[0m: [1mError validating: Invalid referential action: `SetDefault`. Allowed values: (`Cascade`, `Restrict`, `NoAction`, `SetNull`)[0m | ||
// [1;94m-->[0m [4mschema.prisma:19[0m | ||
// [1;94m | [0m | ||
// [1;94m18 | [0m aId Int? @default(3) | ||
// [1;94m19 | [0m a A? @relation(fields: [aId], references: [id], [1;91monUpdate: SetDefault[0m, onDelete: SetDefault) | ||
// [1;94m | [0m |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
datasource db { | ||
provider = "mysql" | ||
url = "mysql://" | ||
} | ||
|
||
model A { | ||
id Int @id | ||
bs B[] | ||
} | ||
|
||
model B { | ||
id Int @id | ||
aId Int? @default(3) | ||
a A? @relation(fields: [aId], references: [id], onUpdate: SetDefault, onDelete: SetDefault) | ||
} | ||
// [1;93mwarning[0m: [1mUsing SetDefault on MySQL may yield to unexpected results, as the database will silently change the referential action to `NoAction`.[0m | ||
// [1;94m-->[0m [4mschema.prisma:14[0m | ||
// [1;94m | [0m | ||
// [1;94m13 | [0m aId Int? @default(3) | ||
// [1;94m14 | [0m a A? @relation(fields: [aId], references: [id], onUpdate: SetDefault, [1;93monDelete: SetDefault[0m) | ||
// [1;94m | [0m | ||
// [1;93mwarning[0m: [1mUsing SetDefault on MySQL may yield to unexpected results, as the database will silently change the referential action to `NoAction`.[0m | ||
// [1;94m-->[0m [4mschema.prisma:14[0m | ||
// [1;94m | [0m | ||
// [1;94m13 | [0m aId Int? @default(3) | ||
// [1;94m14 | [0m a A? @relation(fields: [aId], references: [id], [1;93monUpdate: SetDefault[0m, onDelete: SetDefault) | ||
// [1;94m | [0m |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
generator client { | ||
provider = "prisma-client-js" | ||
} | ||
|
||
datasource db { | ||
provider = "mysql" | ||
url = "mysql://" | ||
relationMode = "foreignKeys" | ||
} | ||
|
||
model A { | ||
id Int @id | ||
bs B[] | ||
} | ||
|
||
model B { | ||
id Int @id | ||
aId Int? @default(3) | ||
a A? @relation(fields: [aId], references: [id], onUpdate: SetDefault, onDelete: SetDefault) | ||
} | ||
// [1;93mwarning[0m: [1mUsing SetDefault on MySQL may yield to unexpected results, as the database will silently change the referential action to `NoAction`.[0m | ||
// [1;94m-->[0m [4mschema.prisma:19[0m | ||
// [1;94m | [0m | ||
// [1;94m18 | [0m aId Int? @default(3) | ||
// [1;94m19 | [0m a A? @relation(fields: [aId], references: [id], onUpdate: SetDefault, [1;93monDelete: SetDefault[0m) | ||
// [1;94m | [0m | ||
// [1;93mwarning[0m: [1mUsing SetDefault on MySQL may yield to unexpected results, as the database will silently change the referential action to `NoAction`.[0m | ||
// [1;94m-->[0m [4mschema.prisma:19[0m | ||
// [1;94m | [0m | ||
// [1;94m18 | [0m aId Int? @default(3) | ||
// [1;94m19 | [0m a A? @relation(fields: [aId], references: [id], [1;93monUpdate: SetDefault[0m, onDelete: SetDefault) | ||
// [1;94m | [0m |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's agree on the actual message to show the users.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we could link to https://pris.ly/d/referential-actions?