From e38dc62f4ae1555c2b7ef302f7428885c7b02af2 Mon Sep 17 00:00:00 2001 From: ItzSiL3Nce Date: Mon, 7 Aug 2023 20:20:34 +0200 Subject: [PATCH 1/7] psl: add the relationMode 'prismaSkipIntegrity' for the MongoDB connector --- psl/builtin-connectors/src/mongodb.rs | 2 +- psl/psl-core/src/datamodel_connector.rs | 2 ++ .../src/datamodel_connector/relation_mode.rs | 12 +++++++++++- psl/psl-core/src/validate/datasource_loader.rs | 1 + .../validations/relation_fields.rs | 7 +++++++ 5 files changed, 22 insertions(+), 2 deletions(-) diff --git a/psl/builtin-connectors/src/mongodb.rs b/psl/builtin-connectors/src/mongodb.rs index e2e820f6b16..cd788402ae1 100644 --- a/psl/builtin-connectors/src/mongodb.rs +++ b/psl/builtin-connectors/src/mongodb.rs @@ -139,7 +139,7 @@ impl Connector for MongoDbDatamodelConnector { } fn allowed_relation_mode_settings(&self) -> enumflags2::BitFlags { - RelationMode::Prisma.into() + RelationMode::Prisma | RelationMode::PrismaSkipIntegrity } /// Avoid checking whether the fields appearing in a `@relation` attribute are included in an index. diff --git a/psl/psl-core/src/datamodel_connector.rs b/psl/psl-core/src/datamodel_connector.rs index b94fc45aaa6..fa42aea59f0 100644 --- a/psl/psl-core/src/datamodel_connector.rs +++ b/psl/psl-core/src/datamodel_connector.rs @@ -135,6 +135,8 @@ pub trait Connector: Send + Sync { match relation_mode { RelationMode::ForeignKeys => self.referential_actions().contains(action), RelationMode::Prisma => self.emulated_referential_actions().contains(action), + // PrismaSkipIntegrity does not support any referential action, cause it is used to skip any integrity check. + RelationMode::PrismaSkipIntegrity => false, } } diff --git a/psl/psl-core/src/datamodel_connector/relation_mode.rs b/psl/psl-core/src/datamodel_connector/relation_mode.rs index 632824ada68..492bf21422a 100644 --- a/psl/psl-core/src/datamodel_connector/relation_mode.rs +++ b/psl/psl-core/src/datamodel_connector/relation_mode.rs @@ -13,6 +13,11 @@ pub enum RelationMode { /// Enforced in Prisma. Slower, but for databases that do not support /// foreign keys. Prisma, + /// Enforced in Prisma. Currently allowed only for MongoDB. + /// Skips any referential integrity checks (emulated relations) after an update or delete operation. + /// Using this mode, Prisma will STOP ensuring that every relation-field is valid (left intact), thus stricter checks + /// must be done at the PrismaClient side before executing a query or with a try-catch pattern. + PrismaSkipIntegrity, } impl RelationMode { @@ -23,7 +28,11 @@ impl RelationMode { } pub fn is_prisma(&self) -> bool { - matches!(self, Self::Prisma) + matches!(self, Self::Prisma) || matches!(self, Self::PrismaSkipIntegrity) + } + + pub fn should_skip_emulated_referential_integrity(&self) -> bool { + matches!(self, Self::ForeignKeys) || matches!(self, Self::PrismaSkipIntegrity) } /// True, if integrity is in database foreign keys @@ -43,6 +52,7 @@ impl fmt::Display for RelationMode { match self { RelationMode::ForeignKeys => write!(f, "foreignKeys"), RelationMode::Prisma => write!(f, "prisma"), + RelationMode::PrismaSkipIntegrity => write!(f, "prismaSkipIntegrity"), } } } diff --git a/psl/psl-core/src/validate/datasource_loader.rs b/psl/psl-core/src/validate/datasource_loader.rs index 5b053edfe3c..e7e5b78f52e 100644 --- a/psl/psl-core/src/validate/datasource_loader.rs +++ b/psl/psl-core/src/validate/datasource_loader.rs @@ -245,6 +245,7 @@ fn get_relation_mode( let relation_mode = match coerce::string(rm, diagnostics)? { "prisma" => RelationMode::Prisma, "foreignKeys" => RelationMode::ForeignKeys, + "prismaSkipIntegrity" => RelationMode::PrismaSkipIntegrity, other => { let message = format!( "Invalid relation mode setting: \"{other}\". Supported values: \"prisma\", \"foreignKeys\"", diff --git a/psl/psl-core/src/validate/validation_pipeline/validations/relation_fields.rs b/psl/psl-core/src/validate/validation_pipeline/validations/relation_fields.rs index 671d1bda43d..ffc5a1d7e58 100644 --- a/psl/psl-core/src/validate/validation_pipeline/validations/relation_fields.rs +++ b/psl/psl-core/src/validate/validation_pipeline/validations/relation_fields.rs @@ -195,10 +195,17 @@ pub(super) fn referential_actions(field: RelationFieldWalker<'_>, ctx: &mut Cont ) }; + // validation template for relationMode = "prismaSkipIntegrity" + let msg_prisma_skip_integrity = |action: ReferentialAction| { + let additional_info = "When using `relationMode = \"prismaSkipIntegrity\"`, you cannot specify any referential action. Learn more at https://pris.ly/d/relation-mode"; + format!("Invalid referential action: `{}`. {additional_info}", action.as_str()) + }; + let msg_template = |action: ReferentialAction| -> String { match relation_mode { RelationMode::ForeignKeys => msg_foreign_keys(action), RelationMode::Prisma => msg_prisma(action), + RelationMode::PrismaSkipIntegrity => msg_prisma_skip_integrity(action), } }; From 7e17ad20b9820c4221e1e87603283c6fbb308b05 Mon Sep 17 00:00:00 2001 From: ItzSiL3Nce Date: Mon, 7 Aug 2023 20:20:53 +0200 Subject: [PATCH 2/7] qe: skip emulated referential integrity when using the new relationMode 'prismaSkipIntegrity' --- .../src/query_graph_builder/write/utils.rs | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/query-engine/core/src/query_graph_builder/write/utils.rs b/query-engine/core/src/query_graph_builder/write/utils.rs index 113e09e3923..5d334bf468b 100644 --- a/query-engine/core/src/query_graph_builder/write/utils.rs +++ b/query-engine/core/src/query_graph_builder/write/utils.rs @@ -388,8 +388,11 @@ pub(crate) fn insert_emulated_on_delete( parent_node: &NodeRef, child_node: &NodeRef, ) -> QueryGraphBuilderResult<()> { - // If the connector uses the `RelationMode::ForeignKeys` mode, we do not do any checks / emulation. - if query_schema.relation_mode().uses_foreign_keys() { + // If the connector uses the `RelationMode::ForeignKeys` or `RelationMode::PrismaSkipIntegrity` mode, we do not do any checks / emulation. + if query_schema + .relation_mode() + .should_skip_emulated_referential_integrity() + { return Ok(()); } @@ -934,8 +937,11 @@ pub fn insert_emulated_on_update_with_intermediary_node( parent_node: &NodeRef, child_node: &NodeRef, ) -> QueryGraphBuilderResult> { - // If the connector uses the `RelationMode::ForeignKeys` mode, we do not do any checks / emulation. - if query_schema.relation_mode().uses_foreign_keys() { + // If the connector uses the `RelationMode::ForeignKeys` mode or the `RelationMode::PrismaSkipIntegrity`, we do not do any checks / emulation. + if query_schema + .relation_mode() + .should_skip_emulated_referential_integrity() + { return Ok(None); } @@ -981,8 +987,11 @@ pub fn insert_emulated_on_update( parent_node: &NodeRef, child_node: &NodeRef, ) -> QueryGraphBuilderResult<()> { - // If the connector uses the `RelationMode::ForeignKeys` mode, we do not do any checks / emulation. - if query_schema.relation_mode().uses_foreign_keys() { + // If the connector uses the `RelationMode::ForeignKeys` mode or the `RelationMode::PrismaSkipIntegrity` mode, we do not do any checks / emulation. + if query_schema + .relation_mode() + .should_skip_emulated_referential_integrity() + { return Ok(()); } From 9c373b793ac8be8cb72561de6058c9520eebe6dd Mon Sep 17 00:00:00 2001 From: ItzSiL3Nce Date: Mon, 7 Aug 2023 20:21:14 +0200 Subject: [PATCH 3/7] psl: add tests for the new relationMode 'prismaSkipIntegrity' --- .../relations/referential_actions.rs | 33 +++++++++++++++++++ .../relations/relations_negative.rs | 28 ++++++++++++++++ 2 files changed, 61 insertions(+) diff --git a/psl/psl/tests/attributes/relations/referential_actions.rs b/psl/psl/tests/attributes/relations/referential_actions.rs index d1c234ec83d..339315ee386 100644 --- a/psl/psl/tests/attributes/relations/referential_actions.rs +++ b/psl/psl/tests/attributes/relations/referential_actions.rs @@ -912,3 +912,36 @@ fn on_update_cannot_be_defined_on_the_wrong_side_1_1() { expect.assert_eq(&parse_unwrap_err(dml)); } + +#[test] +fn mongo_prisma_skip_integrity_relation_mode_parses_correctly() { + let dml = indoc! {r#" + datasource db { + provider = "mongodb" + relationMode = "prismaSkipIntegrity" + url = "mongodb://" + } + + generator client { + provider = "prisma-client-js" + } + + model A { + id String @id @map("_id") @db.ObjectId + bs B[] + } + + model B { + id String @id @map("_id") @db.ObjectId + aId String @db.ObjectId + a A @relation(fields: [aId], references: [id]) + } + "#}; + + let schema = parse_schema(&dml); + + schema.assert_has_model("A"); + schema.assert_has_model("B").assert_has_relation_field("a"); + + assert_eq!(schema.relation_mode(), RelationMode::PrismaSkipIntegrity) +} diff --git a/psl/psl/tests/attributes/relations/relations_negative.rs b/psl/psl/tests/attributes/relations/relations_negative.rs index 6ce8192f1a9..8e3b45ecca9 100644 --- a/psl/psl/tests/attributes/relations/relations_negative.rs +++ b/psl/psl/tests/attributes/relations/relations_negative.rs @@ -1105,3 +1105,31 @@ fn multiple_relation_validation_errors_do_not_prevent_each_other_across_models() expect_error(schema, &expected_error) } + +#[test] +fn mongo_prisma_skip_integrity_relation_mode_shouldnt_allow_any_referential_action() { + let dml = indoc! {r#" + datasource db { + provider = "mongodb" + relationMode = "prismaSkipIntegrity" + url = "mongodb://" + } + + generator client { + provider = "prisma-client-js" + } + + model A { + id String @id @map("_id") @db.ObjectId + bs B[] + } + + model B { + id String @id @map("_id") @db.ObjectId + aId String @db.ObjectId + a A @relation(fields: [aId], references: [id], onUpdate: Restrict, onDelete: Restrict) + } + "#}; + + assert!(parse_unwrap_err(dml).contains("Invalid referential action: `Restrict`. When using `relationMode = \"prismaSkipIntegrity\"`, you cannot specify any referential action.")); +} From 71be3ed6af18ba9c6604f3c844a6f2727e27d55f Mon Sep 17 00:00:00 2001 From: ItzSiL3Nce Date: Mon, 7 Aug 2023 20:21:29 +0200 Subject: [PATCH 4/7] psl: do not check for cyclic relations when using the new relation mode 'prismaSkipIntegrity' --- .../src/validate/validation_pipeline/validations/relations.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/psl/psl-core/src/validate/validation_pipeline/validations/relations.rs b/psl/psl-core/src/validate/validation_pipeline/validations/relations.rs index a960e7d59b5..a2746a30feb 100644 --- a/psl/psl-core/src/validate/validation_pipeline/validations/relations.rs +++ b/psl/psl-core/src/validate/validation_pipeline/validations/relations.rs @@ -227,7 +227,7 @@ pub(super) fn cycles(relation: CompleteInlineRelationWalker<'_>, ctx: &mut Conte .has_capability(ConnectorCapability::ReferenceCycleDetection) && ctx .datasource - .map(|ds| ds.relation_mode().uses_foreign_keys()) + .map(|ds| ds.relation_mode().should_skip_emulated_referential_integrity()) .unwrap_or(true) { return; From 2fa2a53015c044a71faa251455207c5e0ac8a310 Mon Sep 17 00:00:00 2001 From: ItzSiL3Nce Date: Mon, 7 Aug 2023 20:21:48 +0200 Subject: [PATCH 5/7] psl: add negative test when using the new relation mode 'prismaSkipIntegrity' with non-MongoDB connectors. --- .../relations/relations_negative.rs | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/psl/psl/tests/attributes/relations/relations_negative.rs b/psl/psl/tests/attributes/relations/relations_negative.rs index 8e3b45ecca9..c0346738400 100644 --- a/psl/psl/tests/attributes/relations/relations_negative.rs +++ b/psl/psl/tests/attributes/relations/relations_negative.rs @@ -1133,3 +1133,33 @@ fn mongo_prisma_skip_integrity_relation_mode_shouldnt_allow_any_referential_acti assert!(parse_unwrap_err(dml).contains("Invalid referential action: `Restrict`. When using `relationMode = \"prismaSkipIntegrity\"`, you cannot specify any referential action.")); } + +#[test] +fn sql_should_fail_with_prisma_skip_integrity_relation_mode() { + let dml = indoc! {r#" + datasource db { + provider = "mysql" + relationMode = "prismaSkipIntegrity" + url = env("PLACEHOLDER") + } + + generator client { + provider = "prisma-client-js" + } + + model A { + id Int @id + bs B[] + } + + model B { + id Int @id + aId Int + a A @relation(fields: [aId], references: [id], onUpdate: Restrict, onDelete: Restrict) + } + "#}; + + assert!(parse_unwrap_err(dml).contains( + "Error validating datasource `relationMode`: Invalid relation mode setting: \"prismaSkipIntegrity\"." + )); +} From 3c9438c1e93f0aebe6c3fe182573b7f97659bb4d Mon Sep 17 00:00:00 2001 From: ItzSiL3Nce Date: Tue, 8 Aug 2023 10:26:50 +0200 Subject: [PATCH 6/7] psl: extend the error diagnostic message when choosing a bad relation mode with the new 'prismaSkipIntegrity' mode --- psl/psl-core/src/validate/datasource_loader.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/psl/psl-core/src/validate/datasource_loader.rs b/psl/psl-core/src/validate/datasource_loader.rs index e7e5b78f52e..20fa9f4eb3c 100644 --- a/psl/psl-core/src/validate/datasource_loader.rs +++ b/psl/psl-core/src/validate/datasource_loader.rs @@ -248,7 +248,7 @@ fn get_relation_mode( "prismaSkipIntegrity" => RelationMode::PrismaSkipIntegrity, other => { let message = format!( - "Invalid relation mode setting: \"{other}\". Supported values: \"prisma\", \"foreignKeys\"", + "Invalid relation mode setting: \"{other}\". Supported values: \"prisma\", \"foreignKeys\", \"prismaSkipIntegrity\"", ); let error = DatamodelError::new_source_validation_error(&message, "relationMode", source.span); diagnostics.push_error(error); From ed91a7b2497b937965f670a81b14ad3b5b2318cc Mon Sep 17 00:00:00 2001 From: ItzSiL3Nce Date: Tue, 8 Aug 2023 10:28:17 +0200 Subject: [PATCH 7/7] psl: fix an old unit test that now fails cause it doesn't expect the existence of the new 'prismaSkipIntegrity' relation mode --- psl/psl/tests/attributes/relations/referential_actions.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/psl/psl/tests/attributes/relations/referential_actions.rs b/psl/psl/tests/attributes/relations/referential_actions.rs index 339315ee386..dbc23408cb6 100644 --- a/psl/psl/tests/attributes/relations/referential_actions.rs +++ b/psl/psl/tests/attributes/relations/referential_actions.rs @@ -380,7 +380,7 @@ fn foreign_keys_not_allowed_on_mongo() { "#}; let expected = expect![[r#" - error: Error validating datasource `relationMode`: Invalid relation mode setting: "foreignKeys". Supported values: "prisma" + error: Error validating datasource `relationMode`: Invalid relation mode setting: "foreignKeys". Supported values: "prisma", "prismaSkipIntegrity" --> schema.prisma:3  |   2 |  provider = "mongodb"