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

feat(psl, qe): Add relation mode prismaSkipIntegrity for MongoDB #4116

Open
wants to merge 21 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
e38dc62
psl: add the relationMode 'prismaSkipIntegrity' for the MongoDB conne…
ItzSiL3Nce Aug 7, 2023
7e17ad2
qe: skip emulated referential integrity when using the new relationMo…
ItzSiL3Nce Aug 7, 2023
9c373b7
psl: add tests for the new relationMode 'prismaSkipIntegrity'
ItzSiL3Nce Aug 7, 2023
71be3ed
psl: do not check for cyclic relations when using the new relation mo…
ItzSiL3Nce Aug 7, 2023
2fa2a53
psl: add negative test when using the new relation mode 'prismaSkipIn…
ItzSiL3Nce Aug 7, 2023
3c9438c
psl: extend the error diagnostic message when choosing a bad relation…
ItzSiL3Nce Aug 8, 2023
ed91a7b
psl: fix an old unit test that now fails cause it doesn't expect the …
ItzSiL3Nce Aug 8, 2023
92a3e16
Dev: sync the fork with upstream
ItzSiL3Nce Aug 13, 2024
61c4b79
Merge branch 'main' into feat/mongo-relationMode-prismaSkipIntegrity
ItzSiL3Nce Aug 26, 2024
d28c4f6
Merge branch 'main' into feat/mongo-relationMode-prismaSkipIntegrity
ItzSiL3Nce Aug 28, 2024
2b891ee
Merge branch 'main' into feat/mongo-relationMode-prismaSkipIntegrity
ItzSiL3Nce Aug 29, 2024
1eae767
Merge branch 'main' into feat/mongo-relationMode-prismaSkipIntegrity
ItzSiL3Nce Sep 2, 2024
a45459c
Merge branch 'main' into feat/mongo-relationMode-prismaSkipIntegrity
ItzSiL3Nce Sep 6, 2024
47289e3
Merge branch 'main' into feat/mongo-relationMode-prismaSkipIntegrity
ItzSiL3Nce Sep 12, 2024
dd7ec12
Merge branch 'main' into feat/mongo-relationMode-prismaSkipIntegrity
ItzSiL3Nce Sep 24, 2024
99dac5c
Merge branch 'main' into feat/mongo-relationMode-prismaSkipIntegrity
ItzSiL3Nce Sep 25, 2024
2cbb534
Merge branch 'main' into feat/mongo-relationMode-prismaSkipIntegrity
ItzSiL3Nce Sep 28, 2024
2dc61d8
Merge branch 'main' into feat/mongo-relationMode-prismaSkipIntegrity
ItzSiL3Nce Oct 8, 2024
273a071
Merge branch 'main' into feat/mongo-relationMode-prismaSkipIntegrity
ItzSiL3Nce Oct 13, 2024
fce1206
Merge branch 'main' into feat/mongo-relationMode-prismaSkipIntegrity
ItzSiL3Nce Oct 15, 2024
cdb2615
Merge branch 'main' into feat/mongo-relationMode-prismaSkipIntegrity
ItzSiL3Nce Oct 21, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion psl/psl-core/src/builtin_connectors/mongodb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ impl Connector for MongoDbDatamodelConnector {
}

fn allowed_relation_mode_settings(&self) -> enumflags2::BitFlags<RelationMode> {
RelationMode::Prisma.into()
RelationMode::Prisma | RelationMode::PrismaSkipIntegrity
}

/// Avoid checking whether the fields appearing in a `@relation` attribute are included in an index.
Expand Down
2 changes: 2 additions & 0 deletions psl/psl-core/src/datamodel_connector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ pub trait Connector: Send + Sync {
match relation_mode {
RelationMode::ForeignKeys => self.foreign_key_referential_actions(),
RelationMode::Prisma => self.emulated_referential_actions(),
// PrismaSkipIntegrity does not support any referential action, cause it is used to skip any integrity check.
RelationMode::PrismaSkipIntegrity => BitFlags::empty(),
}
}

Expand Down
12 changes: 11 additions & 1 deletion psl/psl-core/src/datamodel_connector/relation_mode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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 {
self.uses_foreign_keys() || matches!(self, Self::PrismaSkipIntegrity)
}

/// True, if integrity is in database foreign keys
Expand All @@ -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"),
}
}
}
3 changes: 2 additions & 1 deletion psl/psl-core/src/validate/datasource_loader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -265,9 +265,10 @@ 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\"",
"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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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),
}
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ pub(super) fn cycles(relation: CompleteInlineRelationWalker<'_>, ctx: &mut Conte
if !ctx.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;
Expand Down
35 changes: 34 additions & 1 deletion psl/psl/tests/attributes/relations/referential_actions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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)
}
58 changes: 58 additions & 0 deletions psl/psl/tests/attributes/relations/relations_negative.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1105,3 +1105,61 @@ 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."));
}

#[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\"."
));
}
21 changes: 15 additions & 6 deletions query-engine/core/src/query_graph_builder/write/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -385,8 +385,11 @@ pub(crate) fn insert_emulated_on_delete(
model_to_delete: &Model,
node_providing_ids: &NodeRef,
) -> QueryGraphBuilderResult<Vec<NodeRef>> {
// 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(vec![]);
}

Expand Down Expand Up @@ -915,8 +918,11 @@ pub fn insert_emulated_on_update_with_intermediary_node(
parent_node: &NodeRef,
child_node: &NodeRef,
) -> QueryGraphBuilderResult<Option<NodeRef>> {
// 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);
}

Expand Down Expand Up @@ -962,8 +968,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(());
}

Expand Down