Skip to content

Commit

Permalink
fix(mongodb): warn when relation fields have diff native type (#3455)
Browse files Browse the repository at this point in the history
  • Loading branch information
Weakky committed Nov 30, 2022
1 parent 31ead21 commit 9527a31
Show file tree
Hide file tree
Showing 10 changed files with 156 additions and 2 deletions.
8 changes: 8 additions & 0 deletions psl/builtin-connectors/src/mongodb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,14 @@ impl Connector for MongoDbDatamodelConnector {
}
}

fn validate_relation_field(
&self,
field: psl_core::parser_database::walkers::RelationFieldWalker<'_>,
errors: &mut Diagnostics,
) {
validations::relation_same_native_type(field, errors);
}

fn available_native_type_constructors(&self) -> &'static [NativeTypeConstructor] {
mongodb_types::CONSTRUCTORS
}
Expand Down
59 changes: 58 additions & 1 deletion psl/builtin-connectors/src/mongodb/validations.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use psl_core::{
diagnostics::{DatamodelError, Diagnostics},
diagnostics::{DatamodelError, DatamodelWarning, Diagnostics},
parser_database::{
ast::{WithName, WithSpan},
walkers::{IndexWalker, ModelWalker, PrimaryKeyWalker, ScalarFieldWalker},
Expand Down Expand Up @@ -182,3 +182,60 @@ pub(crate) fn field_name_uses_valid_characters(field: ScalarFieldWalker<'_>, err
));
}
}

/// Makes sure underlying fields of a relation have the same native types.
pub(crate) fn relation_same_native_type(
field: psl_core::parser_database::walkers::RelationFieldWalker<'_>,
errors: &mut Diagnostics,
) {
let references = field.referenced_fields();
let fields = field.referencing_fields();

if let (Some(fields), Some(references)) = (fields, references) {
for (a_field, b_ref) in fields.into_iter().zip(references) {
let field_nt = a_field.raw_native_type().map(|nt| (nt.0, nt.1));
let ref_nt = b_ref.raw_native_type().map(|nt| (nt.0, nt.1));
let span = a_field.ast_field().span();
let a_model_name = a_field.model().name();
let a_field_name = a_field.name();
let b_model_name = b_ref.model().name();
let b_field_name = b_ref.name();

let msg = match (field_nt, ref_nt) {
(Some(a), Some(b)) if a != b => {
format!(
"Field {a_model_name}.{a_field_name} and {b_model_name}.{b_field_name} must have the same native type for MongoDB to join those collections correctly. Consider updating those fields to either use '@{}.{}' or '@{}.{}'.",
a.0,
a.1,
b.0,
b.1
)
}
(None, Some(b)) => {
format!(
"Field {a_model_name}.{a_field_name} and {b_model_name}.{b_field_name} must have the same native type for MongoDB to join those collections correctly. Consider either removing {b_model_name}.{b_field_name}'s native type attribute or adding '@{}.{}' to {a_model_name}.{a_field_name}.",
b.0,
b.1
)
}
(Some(a), None) => {
format!(
"Field {a_model_name}.{a_field_name} and {b_model_name}.{b_field_name} must have the same native type for MongoDB to join those collections correctly. Consider either removing {a_model_name}.{a_field_name}'s native type attribute or adding '@{}.{}' to {b_model_name}.{b_field_name}.",
a.0,
a.1
)
}
_ => continue,
};

let msg = format!("{msg} Beware that this will become an error in the future.");

errors.push_warning(DatamodelWarning::new_field_validation(
&msg,
field.model().name(),
field.name(),
span,
));
}
};
}
9 changes: 9 additions & 0 deletions psl/diagnostics/src/warning.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,15 @@ impl DatamodelWarning {
Self::new(message, span)
}

pub fn new_field_validation(message: &str, model: &str, field: &str, span: Span) -> DatamodelWarning {
let msg = format!(
"Warning validating field `{}` in {} `{}`: {}",
field, "model", model, message
);

Self::new(msg, span)
}

/// The user-facing warning message.
pub fn message(&self) -> &str {
&self.message
Expand Down
1 change: 1 addition & 0 deletions psl/psl-core/src/datamodel_connector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ pub trait Connector: Send + Sync {

fn validate_enum(&self, _enum: walkers::EnumWalker<'_>, _: &mut Diagnostics) {}
fn validate_model(&self, _model: walkers::ModelWalker<'_>, _: RelationMode, _: &mut Diagnostics) {}
fn validate_relation_field(&self, _field: walkers::RelationFieldWalker<'_>, _: &mut Diagnostics) {}
fn validate_datasource(&self, _: BitFlags<PreviewFeature>, _: &Datasource, _: &mut Diagnostics) {}

fn validate_scalar_field_unknown_default_functions(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ pub(super) fn validate(ctx: &mut Context<'_>) {
relation_fields::referential_actions(field, ctx);
relation_fields::map(field, ctx);
relation_fields::validate_missing_relation_indexes(field, ctx);
relation_fields::connector_specific(field, ctx);
}

for index in model.indexes() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -314,3 +314,7 @@ mod tests {
assert_eq!(is_leftwise_included_it(item.iter(), group.iter()), false);
}
}

pub(super) fn connector_specific(field: RelationFieldWalker<'_>, ctx: &mut Context<'_>) {
ctx.connector.validate_relation_field(field, ctx.diagnostics)
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,6 @@ model SomeUser {

model Post {
id String @id @map("_id") @default(auto()) @db.ObjectId
userId String
userId String @db.ObjectId
user SomeUser @relation(fields: [userId], references: [id])
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
datasource db {
provider = "mongodb"
url = "mongodb://"
}

model Parent {
id String @id @default(auto()) @map("_id") @db.ObjectId
childId String @unique
child Child? @relation(fields: [childId], references: [parentId])
}

model Child {
id String @id @default(auto()) @map("_id") @db.ObjectId
parentId String @unique @db.ObjectId
parent Parent?
}
// warning: Warning validating field `child` in model `Parent`: Field Parent.childId and Child.parentId must have the same native type for MongoDB to join those collections correctly. Consider either removing Child.parentId's native type attribute or adding '@db.ObjectId' to Parent.childId. Beware that this will become an error in the future.
// --> schema.prisma:9
//  | 
//  8 | 
//  9 |  childId String @unique
// 10 |  child Child? @relation(fields: [childId], references: [parentId])
//  | 
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
datasource db {
provider = "mongodb"
url = "mongodb://"
}

model Parent {
id String @id @default(auto()) @map("_id") @db.ObjectId
childId String @unique @db.ObjectId
child Child? @relation(fields: [childId], references: [parentId])
}

model Child {
id String @id @default(auto()) @map("_id") @db.ObjectId
parentId String @unique
parent Parent?
}

// warning: Warning validating field `child` in model `Parent`: Field Parent.childId and Child.parentId must have the same native type for MongoDB to join those collections correctly. Consider either removing Parent.childId's native type attribute or adding '@db.ObjectId' to Child.parentId. Beware that this will become an error in the future.
// --> schema.prisma:9
//  | 
//  8 | 
//  9 |  childId String @unique @db.ObjectId
// 10 |  child Child? @relation(fields: [childId], references: [parentId])
//  | 
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
datasource db {
provider = "mongodb"
url = "mongodb://"
}

model Parent {
id String @id @default(auto()) @map("_id") @db.ObjectId
childId String @unique @db.ObjectId
child Child? @relation(fields: [childId], references: [parentId])
}

model Child {
id String @id @default(auto()) @map("_id") @db.ObjectId
parentId String @unique @db.String
parent Parent?
}

// warning: Warning validating field `child` in model `Parent`: Field Parent.childId and Child.parentId must have the same native type for MongoDB to join those collections correctly. Consider updating those fields to either use '@db.ObjectId' or '@db.String'. Beware that this will become an error in the future.
// --> schema.prisma:9
//  | 
//  8 | 
//  9 |  childId String @unique @db.ObjectId
// 10 |  child Child? @relation(fields: [childId], references: [parentId])
//  | 

0 comments on commit 9527a31

Please sign in to comment.