diff --git a/introspection-engine/connectors/sql-introspection-connector/src/calculate_datamodel.rs b/introspection-engine/connectors/sql-introspection-connector/src/calculate_datamodel.rs index 49891e484b29..9d755db0f05f 100644 --- a/introspection-engine/connectors/sql-introspection-connector/src/calculate_datamodel.rs +++ b/introspection-engine/connectors/sql-introspection-connector/src/calculate_datamodel.rs @@ -1,5 +1,4 @@ use crate::commenting_out_guardrails::commenting_out_guardrails; -use crate::misc_helpers::columns_match; use crate::misc_helpers::*; use crate::sanitize_datamodel_names::sanitize_datamodel_names; use crate::SqlIntrospectionResult; @@ -23,53 +22,21 @@ pub fn calculate_model(schema: &SqlSchema) -> SqlIntrospectionResult debug!("Calculating model: {}", table.name); let mut model = Model::new(table.name.clone(), None); - for column in table - .columns - .iter() - .filter(|column| !is_foreign_key_column(&table, &column)) - { - let field = calculate_scalar_field(&schema, &table, &column); + for column in &table.columns { + let field = calculate_scalar_field(&table, &column); model.add_field(field); } for foreign_key in &table.foreign_keys { - model.add_field(calculate_relation_field( - schema, - table, - foreign_key, - &table.foreign_keys, - )); + model.add_field(calculate_relation_field(schema, table, foreign_key)); } - for index in &table.indices { - let fk_on_index = table - .foreign_keys - .iter() - .find(|fk| columns_match(&fk.columns, &index.columns)); - - let compound_name = || { - model - .fields - .iter() - .find(|f| { - !f.database_names.is_empty() - && columns_match(&f.database_names, &index.columns) - }) - .expect("Error finding field matching a compound index.") - .name - .clone() - }; - - let index_to_add = match (fk_on_index, index.columns.len(), index.is_unique()) { - (Some(_), _, true) => None, // just make the relation 1:1 and dont print the unique index - (Some(_), 1, false) => Some(calculate_index(index)), - (Some(_), _, false) => Some(calculate_compound_index(index, compound_name())), - (None, 1, true) => None, // this is expressed by the @unique already - (None, _, true) => Some(calculate_index(index)), - (None, _, false) => Some(calculate_index(index)), - }; - - index_to_add.map(|i| model.add_index(i)); + for index in table + .indices + .iter() + .filter(|i| !(i.columns.len() == 1 && i.is_unique())) + { + model.add_index(calculate_index(index)); } if table.primary_key_columns().len() > 1 { @@ -140,15 +107,17 @@ pub fn calculate_model(schema: &SqlSchema) -> SqlIntrospectionResult } } - deduplicate_names_of_fields_to_be_added(&mut fields_to_be_added); - for (model, field) in fields_to_be_added { let model = data_model.find_model_mut(&model).unwrap(); model.add_field(field); } + //todo sanitizing might need to be adjusted to also change the fields in the RelationInfo sanitize_datamodel_names(&mut data_model); //todo warnings commenting_out_guardrails(&mut data_model); //todo warnings + + deduplicate_field_names(&mut data_model); + debug!("Done calculating data model {:?}", data_model); Ok(data_model) diff --git a/introspection-engine/connectors/sql-introspection-connector/src/misc_helpers.rs b/introspection-engine/connectors/sql-introspection-connector/src/misc_helpers.rs index 76140cfa58f3..43e5348e8861 100644 --- a/introspection-engine/connectors/sql-introspection-connector/src/misc_helpers.rs +++ b/introspection-engine/connectors/sql-introspection-connector/src/misc_helpers.rs @@ -1,7 +1,6 @@ -use datamodel::common::names::NameNormalizer; use datamodel::{ - DefaultValue as DMLDef, Field, FieldArity, FieldType, IndexDefinition, Model, OnDeleteStrategy, - RelationInfo, ScalarType, ScalarValue as SV, ValueGenerator as VG, + Datamodel, DefaultValue as DMLDef, Field, FieldArity, FieldType, IndexDefinition, Model, + OnDeleteStrategy, RelationInfo, ScalarType, ScalarValue as SV, ValueGenerator as VG, }; use log::debug; use once_cell::sync::Lazy; @@ -62,14 +61,6 @@ fn common_prisma_m_to_n_relation_conditions(table: &Table) -> bool { } } -pub(crate) fn is_foreign_key_column(table: &Table, column: &Column) -> bool { - table - .foreign_keys - .iter() - .find(|fk| fk.columns.contains(&column.name)) - .is_some() -} - //calculators pub fn calculate_many_to_many_field( @@ -79,7 +70,7 @@ pub fn calculate_many_to_many_field( ) -> Field { let field_type = FieldType::Relation(RelationInfo { name: relation_name, - fields: todo!("fix virtual relation fields"), + fields: vec![], to: foreign_key.referenced_table.clone(), to_fields: foreign_key.referenced_columns.clone(), on_delete: OnDeleteStrategy::None, @@ -122,18 +113,9 @@ pub(crate) fn calculate_index(index: &Index) -> IndexDefinition { index_definition } -pub(crate) fn calculate_compound_index(index: &Index, name: String) -> IndexDefinition { - debug!("Handling compound index {:?}", name); - IndexDefinition { - name: Some(index.name.clone()), - fields: vec![name], - tpe: datamodel::dml::IndexType::Normal, - } -} - -pub(crate) fn calculate_scalar_field(schema: &SqlSchema, table: &Table, column: &Column) -> Field { +pub(crate) fn calculate_scalar_field(table: &Table, column: &Column) -> Field { debug!("Handling column {:?}", column); - let field_type = calculate_field_type(&schema, &column, &table); + let field_type = calculate_scalar_field_type(&column); let (is_commented_out, documentation) = match field_type { FieldType::Unsupported(_) => ( true, @@ -175,13 +157,12 @@ pub(crate) fn calculate_relation_field( schema: &SqlSchema, table: &Table, foreign_key: &ForeignKey, - foreign_keys: &Vec, ) -> Field { - debug!("Handling compound foreign key {:?}", foreign_key); + debug!("Handling foreign key {:?}", foreign_key); let field_type = FieldType::Relation(RelationInfo { name: calculate_relation_name(schema, foreign_key, table), - fields: todo!("fix virtual relation fields"), + fields: foreign_key.columns.clone(), to: foreign_key.referenced_table.clone(), to_fields: foreign_key.referenced_columns.clone(), on_delete: OnDeleteStrategy::None, @@ -198,42 +179,15 @@ pub(crate) fn calculate_relation_field( false => FieldArity::Required, }; - let more_then_one_compound_to_same_table = || { - foreign_keys - .iter() - .filter(|fk| { - fk.referenced_table == foreign_key.referenced_table && fk.columns.len() > 1 - }) - .count() - > 1 - }; - - let (name, database_name) = match columns.len() { - 1 => (columns[0].name.clone(), vec![]), - _ if more_then_one_compound_to_same_table() => ( - format!( - "{}_{}", - foreign_key.referenced_table.clone(), - columns[0].name.clone() - ), - columns.iter().map(|c| c.name.clone()).collect(), - ), - _ => ( - foreign_key.referenced_table.clone(), - columns.iter().map(|c| c.name.clone()).collect(), - ), - }; - - let is_id = is_relation_and_id(columns, &table); - + // todo Should this be an extra type? It uses just a small subset of the features of a scalar field Field { - name, + name: foreign_key.referenced_table.clone(), arity, - field_type, - database_names: database_name, + field_type, // todo we could remove relation out of the type and make relationinfo part of RelationField + database_names: vec![], default_value: None, is_unique: false, - is_id, + is_id: false, documentation: None, is_generated: false, is_updated_at: false, @@ -250,30 +204,23 @@ pub(crate) fn calculate_backrelation_field( relation_info: &RelationInfo, ) -> Field { let table = schema.table_bang(&model.name); - let fk = table.foreign_key_for_column(&relation_field.name); - let on_delete = match fk { - // TODO: bring `onDelete` back once `prisma migrate` is a thing - // Some(fk) if fk.on_delete_action == ForeignKeyAction::Cascade => OnDeleteStrategy::Cascade, - _ => OnDeleteStrategy::None, - }; + let field_type = FieldType::Relation(RelationInfo { name: relation_info.name.clone(), to: model.name.clone(), - fields: todo!("fix virtual relation fields"), + fields: vec![], to_fields: vec![], - on_delete, + on_delete: OnDeleteStrategy::None, }); - let other_is_unique = || match &relation_field.database_names.len() { - 0 => table.is_column_unique(&relation_field.name), + let other_is_unique = || match &relation_info.fields.len() { 1 => { - let column_name = relation_field.database_names.first().unwrap(); + let column_name = &relation_info.fields.first().unwrap(); table.is_column_unique(column_name) } - _ => table - .indices - .iter() - .any(|i| i.columns == relation_field.database_names && i.tpe == IndexType::Unique), + _ => table.indices.iter().any(|i| { + columns_match(&i.columns, &relation_info.fields) && i.tpe == IndexType::Unique + }), }; let arity = match relation_field.arity { @@ -349,30 +296,6 @@ pub(crate) fn is_id(column: &Column, table: &Table) -> bool { .unwrap_or(false) } -pub(crate) fn is_relation_and_id(columns: Vec<&Column>, table: &Table) -> bool { - table - .primary_key - .as_ref() - .map(|pk| { - columns_match( - &pk.columns, - &columns - .iter() - .map(|c| c.name.clone()) - .collect::>(), - ) - }) - .unwrap_or(false) -} - -pub(crate) fn is_part_of_id(column: &Column, table: &Table) -> bool { - table - .primary_key - .as_ref() - .map(|pk| pk.columns.contains(&column.name)) - .unwrap_or(false) -} - pub(crate) fn is_sequence(column: &Column, table: &Table) -> bool { table .primary_key @@ -421,80 +344,59 @@ pub(crate) fn calculate_relation_name( } } -pub(crate) fn calculate_field_type( - schema: &SqlSchema, - column: &Column, - table: &Table, -) -> FieldType { +pub(crate) fn calculate_scalar_field_type(column: &Column) -> FieldType { debug!("Calculating field type for '{}'", column.name); - // Look for a foreign key referencing this column - match table - .foreign_keys - .iter() - .find(|fk| fk.columns.contains(&column.name)) - { - Some(fk) if !is_id(column, table) && !is_part_of_id(column, table) => { - debug!("Found corresponding foreign key"); - let idx = fk - .columns - .iter() - .position(|n| n == &column.name) - .expect("get column FK position"); - let referenced_col = &fk.referenced_columns[idx]; - - FieldType::Relation(RelationInfo { - name: calculate_relation_name(schema, fk, table), - fields: todo!("fix virtual relation fields"), - to: fk.referenced_table.clone(), - to_fields: vec![referenced_col.clone()], - on_delete: OnDeleteStrategy::None, - }) - } - _ => { - debug!("Found no corresponding foreign key"); - match &column.tpe.family { - ColumnTypeFamily::Boolean => FieldType::Base(ScalarType::Boolean, None), - ColumnTypeFamily::DateTime => FieldType::Base(ScalarType::DateTime, None), - ColumnTypeFamily::Float => FieldType::Base(ScalarType::Float, None), - ColumnTypeFamily::Int => FieldType::Base(ScalarType::Int, None), - ColumnTypeFamily::String => FieldType::Base(ScalarType::String, None), - ColumnTypeFamily::Enum(name) => FieldType::Enum(name.clone()), - ColumnTypeFamily::Uuid => FieldType::Base(ScalarType::String, None), - ColumnTypeFamily::Json => FieldType::Base(ScalarType::String, None), - x => FieldType::Unsupported(x.to_string()), - } - } + + match &column.tpe.family { + ColumnTypeFamily::Boolean => FieldType::Base(ScalarType::Boolean, None), + ColumnTypeFamily::DateTime => FieldType::Base(ScalarType::DateTime, None), + ColumnTypeFamily::Float => FieldType::Base(ScalarType::Float, None), + ColumnTypeFamily::Int => FieldType::Base(ScalarType::Int, None), + ColumnTypeFamily::String => FieldType::Base(ScalarType::String, None), + ColumnTypeFamily::Enum(name) => FieldType::Enum(name.clone()), + ColumnTypeFamily::Uuid => FieldType::Base(ScalarType::String, None), + ColumnTypeFamily::Json => FieldType::Base(ScalarType::String, None), + x => FieldType::Unsupported(x.to_string()), } } // misc -pub fn deduplicate_names_of_fields_to_be_added(fields_to_be_added: &mut Vec<(String, Field)>) { +pub fn deduplicate_field_names(datamodel: &mut Datamodel) { let mut duplicated_relation_fields = Vec::new(); - fields_to_be_added + + for model in &datamodel.models { + for field in &model.fields { + let is_duplicated = model.fields.iter().filter(|f| field.name == f.name).count() > 1; + + if let FieldType::Relation(RelationInfo { + name: relation_name, + .. + }) = &field.field_type + { + if is_duplicated { + duplicated_relation_fields.push(( + model.name.clone(), + field.name.clone(), + relation_name.clone(), + )); + } + }; + } + } + + duplicated_relation_fields .iter() - .enumerate() - .for_each(|(index, (model, field))| { - let is_duplicated = fields_to_be_added - .iter() - .filter(|(other_model, other_field)| { - model == other_model && field.name == other_field.name - }) - .count() - > 1; - - if is_duplicated { - duplicated_relation_fields.push(index); - } + .for_each(|(model, field, relation_name)| { + let mut field = datamodel + .find_model_mut(model) + .unwrap() + .find_relation_field_mut(field) + .unwrap(); + + //todo self vs normal relation? + field.name = format!("{}_{}", field.name, &relation_name); }); - - duplicated_relation_fields.iter().for_each(|index| { - let (_, ref mut field) = fields_to_be_added.get_mut(*index).unwrap(); - field.name = match &field.field_type { - FieldType::Relation(RelationInfo { name, .. }) => format!("{}_{}", field.name, &name), - _ => field.name.clone(), - }; - }); } static RE_NUM: Lazy = Lazy::new(|| Regex::new(r"^'?(\d+)'?$").expect("compile regex")); diff --git a/introspection-engine/connectors/sql-introspection-connector/src/sanitize_datamodel_names.rs b/introspection-engine/connectors/sql-introspection-connector/src/sanitize_datamodel_names.rs index 87b45d7c01ec..8d40cc4220e9 100644 --- a/introspection-engine/connectors/sql-introspection-connector/src/sanitize_datamodel_names.rs +++ b/introspection-engine/connectors/sql-introspection-connector/src/sanitize_datamodel_names.rs @@ -12,7 +12,6 @@ pub fn sanitize_datamodel_names(datamodel: &mut Datamodel) { for field in &mut model.fields { let (sanitized_field_name, field_db_name) = sanitize_name(field.name.clone()); let id_field_option = model.id_fields.iter_mut().find(|name| **name == field.name); - let mut no_db_name_because_field_is_virtual = false; match &mut field.field_type { FieldType::Relation(info) => { @@ -23,49 +22,60 @@ pub fn sanitize_datamodel_names(datamodel: &mut Datamodel) { .iter() .map(|f: &std::string::String| sanitize_name(f.clone()).0) .collect(); - - no_db_name_because_field_is_virtual = info.to_fields.is_empty(); } FieldType::Enum(enum_name) => { - let (sanitized_enum_name, enum_db_name) = if *enum_name == format!("{}_{}", model.name, field.name) - { - //MySql - if model_db_name.is_none() && field_db_name.is_none() { - (enum_name.clone(), None) + let (sanitized_enum_name, enum_db_name) = + if *enum_name == format!("{}_{}", model.name, field.name) { + //MySql + if model_db_name.is_none() && field_db_name.is_none() { + (enum_name.clone(), None) + } else { + ( + format!("{}_{}", sanitized_model_name, sanitized_field_name), + Some(enum_name.clone()), + ) + } } else { - ( - format!("{}_{}", sanitized_model_name, sanitized_field_name), - Some(enum_name.clone()), - ) - } - } else { - sanitize_name(enum_name.clone()) - }; + sanitize_name(enum_name.clone()) + }; if let Some(old_name) = enum_db_name { - enum_renames.insert(old_name.clone(), (sanitized_enum_name.clone(), Some(old_name.clone()))); + enum_renames.insert( + old_name.clone(), + (sanitized_enum_name.clone(), Some(old_name.clone())), + ); }; *enum_name = sanitized_enum_name; - if let Some(DefaultValue::Single(ScalarValue::ConstantLiteral(name))) = &mut field.default_value { + if let Some(DefaultValue::Single(ScalarValue::ConstantLiteral(name))) = + &mut field.default_value + { let (sanitized_value, _) = sanitize_name(name.to_string()); *name = sanitized_value; }; + + if field.database_names.is_empty() { + field.database_names = field_db_name.map(|db| vec![db]).unwrap_or(vec![]); + } + } + _ => { + if field.database_names.is_empty() { + field.database_names = field_db_name.map(|db| vec![db]).unwrap_or(vec![]); + } } - _ => (), } field.name = sanitized_field_name.clone(); id_field_option.map(|id_field| *id_field = sanitized_field_name.clone()); - - if field.database_names.is_empty() && !no_db_name_because_field_is_virtual { - field.database_names = field_db_name.map(|db| vec![db]).unwrap_or(vec![]); - } } for index in &mut model.indices { - index.fields = index.fields.iter().map(|f| sanitize_name(f.clone()).0).collect(); + index.fields = index + .fields + .iter() + .map(|f| sanitize_name(f.clone()).0) + .collect(); } model.name = sanitized_model_name; @@ -104,7 +114,10 @@ fn sanitize_name(name: String) -> (String, Option) { if needs_sanitation { let start_cleaned: String = RE_START.replace_all(name.as_str(), "").parse().unwrap(); - (RE.replace_all(start_cleaned.as_str(), "_").parse().unwrap(), Some(name)) + ( + RE.replace_all(start_cleaned.as_str(), "_").parse().unwrap(), + Some(name), + ) } else { (name, None) } diff --git a/introspection-engine/connectors/sql-introspection-connector/tests/common_unit_tests/mod.rs b/introspection-engine/connectors/sql-introspection-connector/tests/common_unit_tests/mod.rs index 73485e5f6e2c..a734060b1a60 100644 --- a/introspection-engine/connectors/sql-introspection-connector/tests/common_unit_tests/mod.rs +++ b/introspection-engine/connectors/sql-introspection-connector/tests/common_unit_tests/mod.rs @@ -627,7 +627,6 @@ fn uniqueness_is_preserved_when_generating_data_model_from_a_schema() { } #[test] -#[ignore] fn compound_foreign_keys_are_preserved_when_generating_data_model_from_a_schema() { let ref_data_model = Datamodel { models: vec![ @@ -668,6 +667,26 @@ fn compound_foreign_keys_are_preserved_when_generating_data_model_from_a_schema( data_source_fields: vec![], is_commented_out: false, }, + Field { + name: "User".to_string(), + field_type: FieldType::Relation(RelationInfo { + to: "User".to_string(), + fields: vec![], + to_fields: vec![], + name: "CityToUser".to_string(), + on_delete: OnDeleteStrategy::None, + }), + arity: FieldArity::List, + database_names: vec![], + default_value: None, + is_unique: false, + is_id: false, + documentation: None, + is_generated: false, + is_updated_at: false, + data_source_fields: vec![], + is_commented_out: false, + }, ], is_generated: false, indices: vec![], @@ -685,6 +704,22 @@ fn compound_foreign_keys_are_preserved_when_generating_data_model_from_a_schema( arity: FieldArity::Required, field_type: FieldType::Base(ScalarType::Int, None), database_names: Vec::new(), + default_value: Some(DMLDefault::Expression( + ValueGenerator::new_autoincrement(), + )), + is_unique: false, + is_id: true, + documentation: None, + is_generated: false, + is_updated_at: false, + data_source_fields: vec![], + is_commented_out: false, + }, + Field { + name: "city_id".to_string(), + arity: FieldArity::Required, + field_type: FieldType::Base(ScalarType::Int, None), + database_names: vec!["city-id".to_string()], default_value: None, is_unique: false, is_id: false, @@ -695,16 +730,10 @@ fn compound_foreign_keys_are_preserved_when_generating_data_model_from_a_schema( is_commented_out: false, }, Field { - name: "city-id".to_string(), + name: "city_name".to_string(), + field_type: FieldType::Base(ScalarType::String, None), arity: FieldArity::Required, - field_type: FieldType::Relation(RelationInfo { - name: "".to_string(), - to: "City".to_string(), - fields: todo!("fix virtual relation fields"), - to_fields: vec!["id".to_string()], - on_delete: OnDeleteStrategy::None, - }), - database_names: Vec::new(), + database_names: vec!["city-name".to_string()], default_value: None, is_unique: false, is_id: false, @@ -715,13 +744,13 @@ fn compound_foreign_keys_are_preserved_when_generating_data_model_from_a_schema( is_commented_out: false, }, Field { - name: "city-name".to_string(), + name: "City".to_string(), arity: FieldArity::Required, field_type: FieldType::Relation(RelationInfo { - name: "".to_string(), + name: "CityToUser".to_string(), to: "City".to_string(), - fields: todo!("fix virtual relation fields"), - to_fields: vec!["name".to_string()], + fields: vec!["city-id".to_string(), "city-name".to_string()], + to_fields: vec!["id".to_string(), "name".to_string()], on_delete: OnDeleteStrategy::None, }), database_names: Vec::new(), @@ -811,7 +840,10 @@ fn compound_foreign_keys_are_preserved_when_generating_data_model_from_a_schema( }, ], indices: vec![], - primary_key: None, + primary_key: Some(PrimaryKey { + columns: vec!["id".to_string()], + sequence: None, + }), foreign_keys: vec![ForeignKey { // what does this mean? the from columns are not targeting a specific to column? constraint_name: None, @@ -996,7 +1028,7 @@ fn foreign_keys_are_preserved_when_generating_data_model_from_a_schema() { arity: FieldArity::List, field_type: FieldType::Relation(RelationInfo { to: "User".to_string(), - fields: todo!("fix virtual relation fields"), + fields: vec![], to_fields: vec![], name: "CityToUser".to_string(), on_delete: OnDeleteStrategy::None, @@ -1045,7 +1077,7 @@ fn foreign_keys_are_preserved_when_generating_data_model_from_a_schema() { field_type: FieldType::Relation(RelationInfo { name: "CityToUser".to_string(), to: "City".to_string(), - fields: todo!("fix virtual relation fields"), + fields: vec![], to_fields: vec!["id".to_string()], on_delete: OnDeleteStrategy::None, }), diff --git a/introspection-engine/connectors/sql-introspection-connector/tests/db_specific_introspection/mysql/relations_mysql.rs b/introspection-engine/connectors/sql-introspection-connector/tests/db_specific_introspection/mysql/relations_mysql.rs index d70c99377628..517f0c4edcdb 100644 --- a/introspection-engine/connectors/sql-introspection-connector/tests/db_specific_introspection/mysql/relations_mysql.rs +++ b/introspection-engine/connectors/sql-introspection-connector/tests/db_specific_introspection/mysql/relations_mysql.rs @@ -24,9 +24,10 @@ async fn introspecting_a_one_to_one_req_relation_should_work(api: &TestApi) { .await; let dm = r#" - model Post { - id Int @id @default(autoincrement()) - user_id User + model Post { + id Int @id @default(autoincrement()) + user_id Int @unique + User User @relation(fields: [user_id], references: [id]) } model User { @@ -75,17 +76,19 @@ async fn introspecting_two_one_to_one_relations_between_the_same_models_should_w let dm = r#" model Post { - id Int @id @default(autoincrement()) - user_id User @relation("Post_user_idToUser", references: [id]) - User User? @relation("PostToUser_post_id") + id Int @default(autoincrement()) @id + user_id Int @unique + User_Post_user_idToUser User @relation("Post_user_idToUser", fields: [user_id], references: [id]) + User_PostToUser_post_id User? @relation("PostToUser_post_id") } - + model User { - id Int @id @default(autoincrement()) - post_id Post @relation("PostToUser_post_id", references: [id]) - Post Post? @relation("Post_user_idToUser") + id Int @default(autoincrement()) @id + post_id Int @unique + Post_PostToUser_post_id Post @relation("PostToUser_post_id", fields: [post_id], references: [id]) + Post_Post_user_idToUser Post? @relation("Post_user_idToUser") } - "#; + "#; let result = dbg!(api.introspect().await); custom_assert(&result, dm); } @@ -113,15 +116,15 @@ async fn introspecting_a_one_to_one_relation_should_work(api: &TestApi) { let dm = r#" model Post { - id Int @id @default(autoincrement()) - user_id User? + id Int @default(autoincrement()) @id + user_id Int? @unique + User User? @relation(fields: [user_id], references: [id]) } - + model User { - id Int @id @default(autoincrement()) - Post Post? - - } + id Int @default(autoincrement()) @id + Post Post? + } "#; let result = dbg!(api.introspect().await); custom_assert(&result, dm); @@ -146,15 +149,16 @@ async fn introspecting_a_one_to_one_relation_referencing_non_id_should_work(api: }) .await; let dm = r#" - model Post { - id Int @id @default(autoincrement()) - user_email User? @relation(references: [email]) + model Post { + id Int @default(autoincrement()) @id + user_email String? @unique + User User? @relation(fields: [user_email], references: [email]) } - + model User { - email String? @unique - id Int @id @default(autoincrement()) - Post Post? + email String? @unique + id Int @default(autoincrement()) @id + Post Post? } "#; let result = dbg!(api.introspect().await); @@ -184,15 +188,16 @@ async fn introspecting_a_one_to_many_relation_should_work(api: &TestApi) { let dm = r#" model Post { - id Int @id @default(autoincrement()) - user_id User? - - @@index([user_id], name: "user_id") + id Int @default(autoincrement()) @id + user_id Int? + User User? @relation(fields: [user_id], references: [id]) + + @@index([user_id], name: "user_id") } - + model User { - id Int @id @default(autoincrement()) - Post Post[] + id Int @default(autoincrement()) @id + Post Post[] } "#; let result = dbg!(api.introspect().await); @@ -222,15 +227,16 @@ async fn introspecting_a_one_req_to_many_relation_should_work(api: &TestApi) { let dm = r#" model Post { - id Int @id @default(autoincrement()) - user_id User - - @@index([user_id], name: "user_id") + id Int @default(autoincrement()) @id + user_id Int + User User @relation(fields: [user_id], references: [id]) + + @@index([user_id], name: "user_id") } - + model User { - id Int @id @default(autoincrement()) - Post Post[] + id Int @default(autoincrement()) @id + Post Post[] } "#; let result = dbg!(api.introspect().await); @@ -406,23 +412,25 @@ async fn introspecting_a_many_to_many_relation_with_an_id_should_work(api: &Test let dm = r#" model Post { - id Int @id @default(autoincrement()) - PostsToUsers PostsToUsers[] + id Int @default(autoincrement()) @id + PostsToUsers PostsToUsers[] } - + model PostsToUsers { - id Int @id - post_id Post - user_id User - - @@index([post_id], name: "post_id") - @@index([user_id], name: "user_id") + id Int @id + post_id Int + user_id Int + Post Post @relation(fields: [post_id], references: [id]) + User User @relation(fields: [user_id], references: [id]) + + @@index([post_id], name: "post_id") + @@index([user_id], name: "user_id") } - + model User { - id Int @id @default(autoincrement()) - PostsToUsers PostsToUsers[] - } + id Int @default(autoincrement()) @id + PostsToUsers PostsToUsers[] + } "#; let result = dbg!(api.introspect().await); custom_assert(&result, dm); @@ -449,13 +457,15 @@ async fn introspecting_a_self_relation_should_work(api: &TestApi) { .await; let dm = r#" - model User { - id Int @id @default(autoincrement()) - direct_report User? @relation("UserToUser_direct_report") - recruited_by User? @relation("UserToUser_recruited_by") - User_UserToUser_direct_report User[] @relation("UserToUser_direct_report") - User_UserToUser_recruited_by User[] @relation("UserToUser_recruited_by") - + model User { + direct_report Int? + id Int @default(autoincrement()) @id + recruited_by Int? + User_UserToUser_direct_report User? @relation("UserToUser_direct_report", fields: [direct_report], references: [id]) + User_UserToUser_recruited_by User? @relation("UserToUser_recruited_by", fields: [recruited_by], references: [id]) + other_User_UserToUser_direct_report User[] @relation("UserToUser_direct_report") + other_User_UserToUser_recruited_by User[] @relation("UserToUser_recruited_by") + @@index([direct_report], name: "direct_report") @@index([recruited_by], name: "recruited_by") } @@ -468,41 +478,41 @@ async fn introspecting_a_self_relation_should_work(api: &TestApi) { // TODO: bring `onDelete` back once `prisma migrate` is a thing //#[test_each_connector(tags("mysql"))] -async fn introspecting_cascading_delete_behaviour_should_work(api: &TestApi) { - let barrel = api.barrel(); - let _setup_schema = barrel - .execute_with_schema( - |migration| { - migration.create_table("User", |t| { - t.add_column("id", types::primary()); - }); - migration.create_table("Post", |t| { - t.add_column("id", types::primary()); - t.inject_custom( - "user_id INTEGER, FOREIGN KEY (`user_id`) REFERENCES `User`(`id`) ON DELETE CASCADE", - ); - }); - }, - api.db_name(), - ) - .await; - - let dm = r#" - model Post { - id Int @id @default(autoincrement()) - user_id User? - - @@index([user_id], name: "user_id") - } - - model User { - id Int @id @default(autoincrement()) - Post Post[] @relation(onDelete: CASCADE) - } - "#; - let result = api.introspect().await; - custom_assert(&result, dm); -} +// async fn introspecting_cascading_delete_behaviour_should_work(api: &TestApi) { +// let barrel = api.barrel(); +// let _setup_schema = barrel +// .execute_with_schema( +// |migration| { +// migration.create_table("User", |t| { +// t.add_column("id", types::primary()); +// }); +// migration.create_table("Post", |t| { +// t.add_column("id", types::primary()); +// t.inject_custom( +// "user_id INTEGER, FOREIGN KEY (`user_id`) REFERENCES `User`(`id`) ON DELETE CASCADE", +// ); +// }); +// }, +// api.db_name(), +// ) +// .await; +// +// let dm = r#" +// model Post { +// id Int @id @default(autoincrement()) +// user_id User? +// +// @@index([user_id], name: "user_id") +// } +// +// model User { +// id Int @id @default(autoincrement()) +// Post Post[] @relation(onDelete: CASCADE) +// } +// "#; +// let result = api.introspect().await; +// custom_assert(&result, dm); +// } #[test_each_connector(tags("mysql"))] async fn introspecting_id_fields_with_foreign_key_should_work(api: &TestApi) { @@ -521,16 +531,17 @@ async fn introspecting_id_fields_with_foreign_key_should_work(api: &TestApi) { .await; let dm = r#" - model Post { - test String - user_id User @id @relation(references: [id]) - } - - model User { - id Int @id @default(autoincrement()) - Post Post[] - } - "#; + model Post { + test String + user_id Int @id + User User @relation(fields: [user_id], references: [id]) + } + + model User { + id Int @default(autoincrement()) @id + Post Post[] + } +"#; let result = dbg!(api.introspect().await); custom_assert(&result, dm); } diff --git a/introspection-engine/connectors/sql-introspection-connector/tests/db_specific_introspection/mysql/relations_with_compound_fk_mysql.rs b/introspection-engine/connectors/sql-introspection-connector/tests/db_specific_introspection/mysql/relations_with_compound_fk_mysql.rs index 36b86261d756..165e1f5c0f3f 100644 --- a/introspection-engine/connectors/sql-introspection-connector/tests/db_specific_introspection/mysql/relations_with_compound_fk_mysql.rs +++ b/introspection-engine/connectors/sql-introspection-connector/tests/db_specific_introspection/mysql/relations_with_compound_fk_mysql.rs @@ -25,18 +25,22 @@ async fn compound_foreign_keys_should_work_for_one_to_one_relations(api: &TestAp }) .await; - let dm = r#" + let dm = r#" model Post { - id Int @id @default(autoincrement()) - User User? @map(["user_id", "user_age"]) @relation(references:[id, age]) + id Int @default(autoincrement()) @id + user_age Int? + user_id Int? + User User? @relation(fields: [user_id, user_age], references: [id, age]) + + @@unique([user_id, user_age], name: "post_user_unique") } model User { - age Int - id Int @id @default(autoincrement()) - Post Post? - - @@unique([id, age], name: "user_unique") + age Int + id Int @default(autoincrement()) @id + Post Post? + + @@unique([id, age], name: "user_unique") } "#; let result = dbg!(api.introspect().await); @@ -67,16 +71,21 @@ async fn compound_foreign_keys_should_work_for_required_one_to_one_relations(api .await; let dm = r#" - model Post { - id Int @id @default(autoincrement()) - User User @map(["user_id", "user_age"]) @relation(references:[id, age]) + model Post { + id Int @default(autoincrement()) @id + user_age Int + user_id Int + User User @relation(fields: [user_id, user_age], references: [id, age]) + + @@unique([user_id, user_age], name: "post_user_unique") } - + + model User { - age Int - id Int @id @default(autoincrement()) - Post Post? - + age Int + id Int @default(autoincrement()) @id + Post Post? + @@unique([id, age], name: "user_unique") } "#; @@ -106,21 +115,24 @@ async fn compound_foreign_keys_should_work_for_one_to_many_relations(api: &TestA }) .await; - let dm = r#" + let dm = r#" model Post { - id Int @id @default(autoincrement()) - User User? @map(["user_id", "user_age"]) @relation(references:[id, age]) - - @@index([User], name: "user_id") + id Int @default(autoincrement()) @id + user_age Int? + user_id Int? + User User? @relation(fields: [user_id, user_age], references: [id, age]) + + @@index([user_id, user_age], name: "user_id") } - + model User { - age Int - id Int @id @default(autoincrement()) - Post Post[] - - @@unique([id, age], name: "user_unique") + age Int + id Int @default(autoincrement()) @id + Post Post[] + + @@unique([id, age], name: "user_unique") } + "#; let result = dbg!(api.introspect().await); custom_assert(&result, dm); @@ -150,18 +162,20 @@ async fn compound_foreign_keys_should_work_for_required_one_to_many_relations(ap let dm = r#" model Post { - id Int @id @default(autoincrement()) - User User @map(["user_id", "user_age"]) @relation(references:[id, age]) - - @@index([User], name: "user_id") + id Int @default(autoincrement()) @id + user_age Int + user_id Int + User User @relation(fields: [user_id, user_age], references: [id, age]) + + @@index([user_id, user_age], name: "user_id") } - + model User { - age Int - id Int @id @default(autoincrement()) - Post Post[] - - @@unique([id, age], name: "user_unique") + age Int + id Int @default(autoincrement()) @id + Post Post[] + + @@unique([id, age], name: "user_unique") } "#; let result = dbg!(api.introspect().await); @@ -188,14 +202,16 @@ async fn compound_foreign_keys_should_work_for_required_self_relations(api: &Tes .await; let dm = r#" - model Person { - age Int - id Int @id @default(autoincrement()) - Person Person @map(["partner_id", "partner_age"]) @relation("PersonToPerson_partner_id_partner_age", references: [id, age]) - other_Person Person[] @relation("PersonToPerson_partner_id_partner_age") - - @@unique([id, age], name: "person_unique") - @@index([Person], name: "partner_id") + model Person { + age Int + id Int @default(autoincrement()) @id + partner_age Int + partner_id Int + Person Person @relation("PersonToPerson_partner_id_partner_age", fields: [partner_id, partner_age], references: [id, age]) + other_Person Person[] @relation("PersonToPerson_partner_id_partner_age") + + @@index([partner_id, partner_age], name: "partner_id") + @@unique([id, age], name: "person_unique") } "#; let result = dbg!(api.introspect().await); @@ -222,15 +238,17 @@ async fn compound_foreign_keys_should_work_for_self_relations(api: &TestApi) { .await; let dm = r#" - model Person { - age Int - id Int @id @default(autoincrement()) - Person Person? @map(["partner_id", "partner_age"]) @relation("PersonToPerson_partner_id_partner_age", references: [id, age]) - other_Person Person[] @relation("PersonToPerson_partner_id_partner_age") - - @@unique([id, age], name: "person_unique") - @@index([Person], name: "partner_id") - } + model Person { + age Int + id Int @default(autoincrement()) @id + partner_age Int? + partner_id Int? + Person Person? @relation("PersonToPerson_partner_id_partner_age", fields: [partner_id, partner_age], references: [id, age]) + other_Person Person[] @relation("PersonToPerson_partner_id_partner_age") + + @@index([partner_id, partner_age], name: "partner_id") + @@unique([id, age], name: "person_unique") + } "#; let result = dbg!(api.introspect().await); custom_assert(&result, dm); @@ -256,15 +274,17 @@ async fn compound_foreign_keys_should_work_with_defaults(api: &TestApi) { .await; let dm = r#" - model Person { - age Int - id Int @id @default(autoincrement()) - Person Person @map(["partner_id", "partner_age"]) @relation("PersonToPerson_partner_id_partner_age", references: [id, age]) - other_Person Person[] @relation("PersonToPerson_partner_id_partner_age") - - @@unique([id, age], name: "person_unique") - @@index([Person], name: "partner_id") - } + model Person { + age Int + id Int @default(autoincrement()) @id + partner_age Int @default(0) + partner_id Int @default(0) + Person Person @relation("PersonToPerson_partner_id_partner_age", fields: [partner_id, partner_age], references: [id, age]) + other_Person Person[] @relation("PersonToPerson_partner_id_partner_age") + + @@index([partner_id, partner_age], name: "partner_id") + @@unique([id, age], name: "person_unique") + } "#; let result = dbg!(api.introspect().await); custom_assert(&result, dm); @@ -341,18 +361,20 @@ async fn compound_foreign_keys_should_work_for_one_to_many_relations_with_non_un let dm = r#" model Post { - id Int @id @default(autoincrement()) - User User @map(["user_id", "user_age"]) @relation(references:[id, age]) - - @@index([User], name: "user_id") + id Int @default(autoincrement()) @id + user_age Int + user_id Int + User User @relation(fields: [user_id, user_age], references: [id, age]) + + @@index([user_id, user_age], name: "user_id") } - + model User { - age Int - id Int @id @default(autoincrement()) - Post Post[] - - @@unique([id, age], name: "user_unique") + age Int + id Int @default(autoincrement()) @id + Post Post[] + + @@unique([id, age], name: "user_unique") } "#; let result = dbg!(api.introspect().await); diff --git a/introspection-engine/connectors/sql-introspection-connector/tests/db_specific_introspection/mysql/remapping_database_names_mysql.rs b/introspection-engine/connectors/sql-introspection-connector/tests/db_specific_introspection/mysql/remapping_database_names_mysql.rs index d30652e00c96..063e04fc05ac 100644 --- a/introspection-engine/connectors/sql-introspection-connector/tests/db_specific_introspection/mysql/remapping_database_names_mysql.rs +++ b/introspection-engine/connectors/sql-introspection-connector/tests/db_specific_introspection/mysql/remapping_database_names_mysql.rs @@ -90,16 +90,17 @@ async fn remapping_models_in_relations_should_work(api: &TestApi) { let dm = r#" model Post { - id Int @id @default(autoincrement()) - user_id User_with_Space + id Int @default(autoincrement()) @id + user_id Int @unique + User_with_Space User_with_Space @relation(fields: [user_id], references: [id]) } - + model User_with_Space { - id Int @id @default(autoincrement()) - name String - Post Post? - - @@map("User with Space") + id Int @default(autoincrement()) @id + name String + Post Post? + + @@map("User with Space") } "#; let result = dbg!(api.introspect().await); @@ -126,17 +127,18 @@ async fn remapping_models_in_relations_should_not_map_virtual_fields(api: &TestA let dm = r#" model Post_With_Space { - id Int @id @default(autoincrement()) - user_id User + id Int @default(autoincrement()) @id + user_id Int @unique + User User @relation(fields: [user_id], references: [id]) @@map("Post With Space") } - + model User { - id Int @id @default(autoincrement()) - name String - Post_With_Space Post_With_Space? - } + id Int @default(autoincrement()) @id + name String + Post_With_Space Post_With_Space? + } "#; let result = dbg!(api.introspect().await); custom_assert(&result, dm); @@ -167,17 +169,21 @@ async fn remapping_models_in_compound_relations_should_work(api: &TestApi) { let dm = r#" model Post { - id Int @id @default(autoincrement()) - User_with_Space User_with_Space @map(["user_id", "user_age"]) @relation(references:[id, age]) + id Int @default(autoincrement()) @id + user_age Int + user_id Int + User_with_Space User_with_Space @relation(fields: [user_id, user_age], references: [id, age]) + + @@unique([user_id, user_age], name: "post_user_unique") } - + model User_with_Space { - age Int - id Int @id @default(autoincrement()) - Post Post? - - @@map("User with Space") - @@unique([id, age], name: "user_unique") + age Int + id Int @default(autoincrement()) @id + Post Post? + + @@map("User with Space") + @@unique([id, age], name: "user_unique") } "#; let result = dbg!(api.introspect().await); @@ -205,18 +211,22 @@ async fn remapping_fields_in_compound_relations_should_work(api: &TestApi) { }) .await; - let dm = r#" + let dm = r#" model Post { - id Int @id @default(autoincrement()) - User User @map(["user_id", "user_age"]) @relation(references:[id, age_that_is_invalid]) + id Int @default(autoincrement()) @id + user_age Int + user_id Int + User User @relation(fields: [user_id, user_age], references: [id, age_that_is_invalid]) + + @@unique([user_id, user_age], name: "post_user_unique") } - + model User { - age_that_is_invalid Int @map("age-that-is-invalid") - id Int @id @default(autoincrement()) - Post Post? - - @@unique([id, age_that_is_invalid], name: "user_unique") + age_that_is_invalid Int @map("age-that-is-invalid") + id Int @default(autoincrement()) @id + Post Post? + + @@unique([id, age_that_is_invalid], name: "user_unique") } "#; let result = dbg!(api.introspect().await); diff --git a/introspection-engine/connectors/sql-introspection-connector/tests/db_specific_introspection/mysql/tables_mysql.rs b/introspection-engine/connectors/sql-introspection-connector/tests/db_specific_introspection/mysql/tables_mysql.rs index 320362eadbef..1a42481b28bd 100644 --- a/introspection-engine/connectors/sql-introspection-connector/tests/db_specific_introspection/mysql/tables_mysql.rs +++ b/introspection-engine/connectors/sql-introspection-connector/tests/db_specific_introspection/mysql/tables_mysql.rs @@ -282,7 +282,7 @@ async fn introspecting_a_table_without_uniques_should_comment_it_out(api: &TestA }) .await; - let dm = "// The underlying table does not contain a unique identifier and can therefore currently not be handled.\n// model Post {\n // id Int\n // user_id User @relation(references: [id])\n\n // @@index([user_id], name: \"user_id\")\n// }\n\nmodel User {\n id Int @default(autoincrement()) @id\n}"; + let dm = "// The underlying table does not contain a unique identifier and can therefore currently not be handled.\n// model Post {\n // id Int\n // user_id Int\n // User User @relation(fields: [user_id], references: [id])\n\n // @@index([user_id], name: \"user_id\")\n// }\n\nmodel User {\n id Int @default(autoincrement()) @id\n}"; let result = dbg!(api.introspect().await); assert_eq!(&result, dm); diff --git a/introspection-engine/connectors/sql-introspection-connector/tests/db_specific_introspection/postgres/relations_postgres.rs b/introspection-engine/connectors/sql-introspection-connector/tests/db_specific_introspection/postgres/relations_postgres.rs index 64bfe9fb81ae..070bcb85966b 100644 --- a/introspection-engine/connectors/sql-introspection-connector/tests/db_specific_introspection/postgres/relations_postgres.rs +++ b/introspection-engine/connectors/sql-introspection-connector/tests/db_specific_introspection/postgres/relations_postgres.rs @@ -21,9 +21,10 @@ async fn introspecting_a_one_to_one_req_relation_should_work(api: &TestApi) { .await; let dm = r#" - model Post { - id Int @id @default(autoincrement()) - user_id User + model Post { + id Int @id @default(autoincrement()) + user_id Int @unique + User User @relation(fields: [user_id], references: [id]) } model User { @@ -61,16 +62,18 @@ async fn introspecting_two_one_to_one_relations_between_the_same_models_should_w let dm = r#" model Post { - id Int @id @default(autoincrement()) - user_id User @relation("Post_user_idToUser", references: [id]) - User User? @relation("PostToUser_post_id") - } - - model User { - id Int @id @default(autoincrement()) - post_id Post @relation("PostToUser_post_id", references: [id]) - Post Post? @relation("Post_user_idToUser") - } + id Int @default(autoincrement()) @id + user_id Int @unique + User_Post_user_idToUser User @relation("Post_user_idToUser", fields: [user_id], references: [id]) + User_PostToUser_post_id User? @relation("PostToUser_post_id") + } + + model User { + id Int @default(autoincrement()) @id + post_id Int @unique + Post_PostToUser_post_id Post @relation("PostToUser_post_id", fields: [post_id], references: [id]) + Post_Post_user_idToUser Post? @relation("Post_user_idToUser") + } "#; let result = dbg!(api.introspect().await); custom_assert(&result, dm); @@ -94,15 +97,16 @@ async fn introspecting_a_one_to_one_relation_should_work(api: &TestApi) { }) .await; let dm = r#" - model Post { - id Int @id @default(autoincrement()) - user_id User? + model Post { + id Int @default(autoincrement()) @id + user_id Int? @unique + User User? @relation(fields: [user_id], references: [id]) } - + model User { - id Int @id @default(autoincrement()) - Post Post? - } + id Int @default(autoincrement()) @id + Post Post? + } "#; let result = dbg!(api.introspect().await); custom_assert(&result, dm); @@ -125,14 +129,15 @@ async fn introspecting_a_one_to_one_relation_referencing_non_id_should_work(api: .await; let dm = r#" model Post { - id Int @id @default(autoincrement()) - user_email User? @relation(references: [email]) + id Int @default(autoincrement()) @id + user_email String? @unique + User User? @relation(fields: [user_email], references: [email]) } - + model User { - email String? @unique - id Int @id @default(autoincrement()) - Post Post? + email String? @unique + id Int @default(autoincrement()) @id + Post Post? } "#; let result = dbg!(api.introspect().await); @@ -154,14 +159,15 @@ async fn introspecting_a_one_to_many_relation_should_work(api: &TestApi) { }) .await; let dm = r#" - model Post { - id Int @id @default(autoincrement()) - user_id User? + model Post { + id Int @default(autoincrement()) @id + user_id Int? + User User? @relation(fields: [user_id], references: [id]) } - + model User { - id Int @id @default(autoincrement()) - Post Post[] + id Int @default(autoincrement()) @id + Post Post[] } "#; let result = dbg!(api.introspect().await); @@ -184,13 +190,14 @@ async fn introspecting_a_one_req_to_many_relation_should_work(api: &TestApi) { .await; let dm = r#" model Post { - id Int @id @default(autoincrement()) - user_id User + id Int @default(autoincrement()) @id + user_id Int + User User @relation(fields: [user_id], references: [id]) } - + model User { - id Int @id @default(autoincrement()) - Post Post[] + id Int @default(autoincrement()) @id + Post Post[] } "#; let result = dbg!(api.introspect().await); @@ -360,20 +367,22 @@ async fn introspecting_a_many_to_many_relation_with_an_id_should_work(api: &Test let dm = r#" model Post { - id Int @id @default(autoincrement()) - PostsToUsers PostsToUsers[] + id Int @default(autoincrement()) @id + PostsToUsers PostsToUsers[] } - + model PostsToUsers { - id Int @id - post_id Post - user_id User + id Int @id + post_id Int + user_id Int + Post Post @relation(fields: [post_id], references: [id]) + User User @relation(fields: [user_id], references: [id]) } - + model User { - id Int @id @default(autoincrement()) - PostsToUsers PostsToUsers[] - } + id Int @default(autoincrement()) @id + PostsToUsers PostsToUsers[] + } "#; let result = dbg!(api.introspect().await); custom_assert(&result, dm); @@ -394,11 +403,13 @@ async fn introspecting_a_self_relation_should_work(api: &TestApi) { .await; let dm = r#" model User { - id Int @id @default(autoincrement()) - direct_report User? @relation("UserToUser_direct_report") - recruited_by User? @relation("UserToUser_recruited_by") - User_UserToUser_direct_report User[] @relation("UserToUser_direct_report") - User_UserToUser_recruited_by User[] @relation("UserToUser_recruited_by") + direct_report Int? + id Int @default(autoincrement()) @id + recruited_by Int? + User_UserToUser_direct_report User? @relation("UserToUser_direct_report", fields: [direct_report], references: [id]) + User_UserToUser_recruited_by User? @relation("UserToUser_recruited_by", fields: [recruited_by], references: [id]) + other_User_UserToUser_direct_report User[] @relation("UserToUser_direct_report") + other_User_UserToUser_recruited_by User[] @relation("UserToUser_recruited_by") } "#; let result = dbg!(api.introspect().await); @@ -409,29 +420,60 @@ async fn introspecting_a_self_relation_should_work(api: &TestApi) { // TODO: bring `onDelete` back once `prisma migrate` is a thing //#[test_each_connector(tags("postgres"))] -async fn introspecting_cascading_delete_behaviour_should_work(api: &TestApi) { +// async fn introspecting_cascading_delete_behaviour_should_work(api: &TestApi) { +// let barrel = api.barrel(); +// barrel +// .execute(|migration| { +// migration.create_table("User", |t| { +// t.add_column("id", types::primary()); +// }); +// migration.create_table("Post", |t| { +// t.add_column("id", types::primary()); +// t.inject_custom("user_id INTEGER REFERENCES \"User\"(\"id\") ON DELETE CASCADE"); +// }); +// }) +// .await; +// +// let dm = r#" +// model Post { +// id Int @id @default(autoincrement()) +// user_id User? +// } +// +// model User { +// id Int @id @default(autoincrement()) +// Post Post[] @relation(onDelete: CASCADE) +// } +// "#; +// let result = dbg!(api.introspect().await); +// custom_assert(&result, dm); +// } + +#[test_each_connector(tags("postgres"))] +async fn introspecting_default_values_on_relations_should_work(api: &TestApi) { let barrel = api.barrel(); - barrel + let _setup_schema = barrel .execute(|migration| { migration.create_table("User", |t| { t.add_column("id", types::primary()); }); migration.create_table("Post", |t| { t.add_column("id", types::primary()); - t.inject_custom("user_id INTEGER REFERENCES \"User\"(\"id\") ON DELETE CASCADE"); + t.inject_custom("user_id INTEGER REFERENCES \"User\"(\"id\") Default 0"); }); }) .await; let dm = r#" model Post { - id Int @id @default(autoincrement()) - user_id User? + id Int @default(autoincrement()) @id + user_id Int? @default(0) + User User? @relation(fields: [user_id], references: [id]) } - + model User { - id Int @id @default(autoincrement()) - Post Post[] @relation(onDelete: CASCADE) + id Int @default(autoincrement()) @id + Post Post[] } "#; let result = dbg!(api.introspect().await); @@ -439,7 +481,7 @@ async fn introspecting_cascading_delete_behaviour_should_work(api: &TestApi) { } #[test_each_connector(tags("postgres"))] -async fn introspecting_default_values_on_relations_should_be_ignored(api: &TestApi) { +async fn introspecting_id_fields_with_foreign_key_should_work(api: &TestApi) { let barrel = api.barrel(); let _setup_schema = barrel .execute(|migration| { @@ -447,25 +489,22 @@ async fn introspecting_default_values_on_relations_should_be_ignored(api: &TestA t.add_column("id", types::primary()); }); migration.create_table("Post", |t| { - t.add_column("id", types::primary()); - t.inject_custom("user_id INTEGER REFERENCES \"User\"(\"id\") Default 0"); + t.add_column("test", types::text()); + t.inject_custom("user_id INTEGER REFERENCES \"User\"(\"id\") Primary Key"); }); }) .await; let dm = r#" - datasource pg { - provider = "postgres" - url = "postgresql://localhost:5432" - } model Post { - id Int @id @default(autoincrement()) - user_id User? + test String + user_id Int @id + User User @relation(fields: [user_id], references: [id]) } - + model User { - id Int @id @default(autoincrement()) - Post Post[] + id Int @default(autoincrement()) @id + Post Post[] } "#; let result = dbg!(api.introspect().await); @@ -473,28 +512,55 @@ async fn introspecting_default_values_on_relations_should_be_ignored(api: &TestA } #[test_each_connector(tags("postgres"))] -async fn introspecting_default_values_on_lists_should_be_ignored(api: &TestApi) { +async fn introspecting_prisma_10_relations_should_work(api: &TestApi) { let barrel = api.barrel(); let _setup_schema = barrel .execute(|migration| { - migration.create_table("User", |t| { - t.add_column("id", types::primary()); - t.inject_custom("ints Integer[] DEFAULT array[]::Integer[]"); - t.inject_custom("ints2 Integer[] DEFAULT '{}'"); + migration.create_table("Book", |t| { + t.inject_custom("id CHAR(25) NOT NULL PRIMARY KEY"); + }); + migration.create_table("Royalty", |t| { + t.inject_custom("id CHAR(25) NOT NULL PRIMARY KEY"); + }); + migration.create_table("_BookRoyalty", |t| { + t.inject_custom("id CHAR(25) NOT NULL PRIMARY KEY"); + t.inject_custom("A CHAR(25) NOT NULL REFERENCES \"Book\"(\"id\")"); + t.inject_custom("B CHAR(25) NOT NULL REFERENCES \"Royalty\"(\"id\")"); }); }) .await; + api.database() + .execute_raw( + &format!( + "CREATE UNIQUE INDEX double on \"{}\".\"_BookRoyalty\" (\"a\", \"b\");", + api.schema_name() + ), + &[], + ) + .await + .unwrap(); + + api.database() + .execute_raw( + &format!( + "CREATE INDEX single on \"{}\".\"_BookRoyalty\" (\"b\");", + api.schema_name() + ), + &[], + ) + .await + .unwrap(); + let dm = r#" - datasource pg { - provider = "postgres" - url = "postgresql://localhost:5432" + model Book { + id String @id + Royalty Royalty[] @relation("BookRoyalty") } - - model User { - id Int @id @default(autoincrement()) - ints Int [] - ints2 Int [] + + model Royalty { + id String @id + Book Book[] @relation("BookRoyalty") } "#; let result = dbg!(api.introspect().await); @@ -502,50 +568,73 @@ async fn introspecting_default_values_on_lists_should_be_ignored(api: &TestApi) } #[test_each_connector(tags("postgres"))] -async fn introspecting_id_fields_with_foreign_key_should_work(api: &TestApi) { +async fn introspecting_relations_should_avoid_name_clashes(api: &TestApi) { let barrel = api.barrel(); let _setup_schema = barrel .execute(|migration| { - migration.create_table("User", |t| { - t.add_column("id", types::primary()); + migration.create_table("y", |t| { + t.inject_custom("id CHAR(25) NOT NULL PRIMARY KEY"); + t.inject_custom("x CHAR(25) NOT NULL"); }); - migration.create_table("Post", |t| { - t.add_column("test", types::text()); - t.inject_custom("user_id INTEGER REFERENCES \"User\"(\"id\") Primary Key"); + migration.create_table("x", |t| { + t.inject_custom("id CHAR(25) NOT NULL PRIMARY KEY"); + t.inject_custom("y CHAR(25) NOT NULL REFERENCES \"y\"(\"id\")"); }); }) .await; let dm = r#" - model Post { - test String - user_id User @id @relation(references: [id]) + model x { + id String @id + y String + y_xToy y @relation(fields: [y], references: [id]) } - - model User { - id Int @id @default(autoincrement()) - Post Post[] + + model y { + id String @id + x String + x_xToy x[] } "#; let result = dbg!(api.introspect().await); custom_assert(&result, dm); } +// +// CREATE TABLE IF NOT EXISTS `x` ( +// `y` int(11) DEFAULT NULL, +// `id` int(11) DEFAULT NULL, +// UNIQUE KEY `unique_id` (`id`) USING BTREE, +// UNIQUE KEY `unique_y_id` (`y`,`id`) USING BTREE, +// KEY `FK__y` (`y`), +// CONSTRAINT `FK__y` FOREIGN KEY (`y`) REFERENCES `y` (`id`) +// ) ENGINE=InnoDB DEFAULT; +// +// CREATE TABLE IF NOT EXISTS `y` ( +// `id` int(11) DEFAULT NULL, +// `x` int(11) DEFAULT NULL, +// `fk_x_1` int(11) DEFAULT NULL, +// `fk_x_2` int(11) DEFAULT NULL, +// UNIQUE KEY `unique_id` (`id`) USING BTREE, +// KEY `FK_y_x` (`fk_x_1`,`fk_x_2`) USING BTREE, +// CONSTRAINT `FK_y_x` FOREIGN KEY (`fk_x_1`, `fk_x_2`) REFERENCES `x` (`y`, `id`) +// ) ENGINE=InnoDB DEFAULT; + #[test_each_connector(tags("postgres"))] -async fn introspecting_prisma_10_relations_should_work(api: &TestApi) { +async fn introspecting_relations_should_avoid_name_clashes_2(api: &TestApi) { let barrel = api.barrel(); let _setup_schema = barrel .execute(|migration| { - migration.create_table("Book", |t| { + migration.create_table("x", |t| { t.inject_custom("id CHAR(25) NOT NULL PRIMARY KEY"); + t.inject_custom("y CHAR(25) NOT NULL"); }); - migration.create_table("Royalty", |t| { - t.inject_custom("id CHAR(25) NOT NULL PRIMARY KEY"); - }); - migration.create_table("_BookRoyalty", |t| { + + migration.create_table("y", |t| { t.inject_custom("id CHAR(25) NOT NULL PRIMARY KEY"); - t.inject_custom("A CHAR(25) NOT NULL REFERENCES \"Book\"(\"id\")"); - t.inject_custom("B CHAR(25) NOT NULL REFERENCES \"Royalty\"(\"id\")"); + t.inject_custom("x CHAR(25) NOT NULL"); + t.inject_custom("fk_x_1 CHAR(25) NOT NULL"); + t.inject_custom("fk_x_2 CHAR(25) NOT NULL"); }); }) .await; @@ -553,7 +642,7 @@ async fn introspecting_prisma_10_relations_should_work(api: &TestApi) { api.database() .execute_raw( &format!( - "CREATE UNIQUE INDEX double on \"{}\".\"_BookRoyalty\" (\"a\", \"b\");", + "CREATE UNIQUE INDEX unique_y_id on \"{}\".\"x\" (\"id\", \"y\");", api.schema_name() ), &[], @@ -564,7 +653,18 @@ async fn introspecting_prisma_10_relations_should_work(api: &TestApi) { api.database() .execute_raw( &format!( - "CREATE INDEX single on \"{}\".\"_BookRoyalty\" (\"b\");", + "Alter table \"{}\".\"x\" ADD CONSTRAINT fk_y FOREIGN KEY (\"y\") REFERENCES \"y\" (\"id\");", + api.schema_name() + ), + &[], + ) + .await + .unwrap(); + + api.database() + .execute_raw( + &format!( + "Alter table \"{}\".\"y\" ADD CONSTRAINT fk_y_x FOREIGN KEY (\"fk_x_1\", \"fk_x_2\") REFERENCES \"x\" (\"y\",\"id\");", api.schema_name() ), &[], @@ -573,14 +673,22 @@ async fn introspecting_prisma_10_relations_should_work(api: &TestApi) { .unwrap(); let dm = r#" - model Book { - id String @id - Royalty Royalty[] @relation("BookRoyalty") + model x { + id String @id + y String + y_x_yToy y @relation("x_yToy", fields: [y], references: [id]) + y_xToy_fk_x_1_fk_x_2 y[] @relation("xToy_fk_x_1_fk_x_2") + + @@unique([id, y], name: "unique_y_id") } - - model Royalty { - id String @id - Book Book[] @relation("BookRoyalty") + + model y { + fk_x_1 String + fk_x_2 String + id String @id + x String + x_xToy_fk_x_1_fk_x_2 x @relation("xToy_fk_x_1_fk_x_2", fields: [fk_x_1, fk_x_2], references: [y, id]) + x_x_yToy x[] @relation("x_yToy") } "#; let result = dbg!(api.introspect().await); diff --git a/introspection-engine/connectors/sql-introspection-connector/tests/db_specific_introspection/postgres/relations_with_compound_fk_postgres.rs b/introspection-engine/connectors/sql-introspection-connector/tests/db_specific_introspection/postgres/relations_with_compound_fk_postgres.rs index 8475d16e249d..b9c775bb116b 100644 --- a/introspection-engine/connectors/sql-introspection-connector/tests/db_specific_introspection/postgres/relations_with_compound_fk_postgres.rs +++ b/introspection-engine/connectors/sql-introspection-connector/tests/db_specific_introspection/postgres/relations_with_compound_fk_postgres.rs @@ -27,16 +27,20 @@ async fn compound_foreign_keys_should_work_for_required_one_to_one_relations(api let dm = r#" model Post { - id Int @id @default(autoincrement()) - User User @map(["user_id", "user_name"]) @relation(references:[id, name]) + id Int @default(autoincrement()) @id + user_id Int + user_name String + User User @relation(fields: [user_id, user_name], references: [id, name]) + + @@unique([user_id, user_name], name: "post_user_unique") } - + model User { - id Int @id @default(autoincrement()) - name String - Post Post? - - @@unique([id, name], name: "user_unique") + id Int @default(autoincrement()) @id + name String + Post Post? + + @@unique([id, name], name: "user_unique") } "#; let result = dbg!(api.introspect().await); @@ -68,16 +72,20 @@ async fn compound_foreign_keys_should_work_for_one_to_one_relations(api: &TestAp let dm = r#" model Post { - id Int @id @default(autoincrement()) - User User? @map(["user_id", "user_name"]) @relation(references:[id, name]) + id Int @default(autoincrement()) @id + user_id Int? + user_name String? + User User? @relation(fields: [user_id, user_name], references: [id, name]) + + @@unique([user_id, user_name], name: "post_user_unique") } - + model User { - id Int @id @default(autoincrement()) - name String - Post Post? - - @@unique([id, name], name: "user_unique") + id Int @default(autoincrement()) @id + name String + Post Post? + + @@unique([id, name], name: "user_unique") } "#; let result = dbg!(api.introspect().await); @@ -107,18 +115,20 @@ async fn compound_foreign_keys_should_work_for_one_to_many_relations(api: &TestA .await; let dm = r#" - model Post { - id Int @id @default(autoincrement()) - User User? @map(["user_id", "user_name"]) @relation(references:[id, name]) - } - - model User { - id Int @id @default(autoincrement()) - name String - Post Post[] - - @@unique([id, name], name: "user_unique") - } + model Post { + id Int @default(autoincrement()) @id + user_id Int? + user_name String? + User User? @relation(fields: [user_id, user_name], references: [id, name]) + } + + model User { + id Int @default(autoincrement()) @id + name String + Post Post[] + + @@unique([id, name], name: "user_unique") + } "#; let result = dbg!(api.introspect().await); custom_assert(&result, dm); @@ -148,16 +158,18 @@ async fn compound_foreign_keys_should_work_for_required_one_to_many_relations(ap let dm = r#" model Post { - id Int @id @default(autoincrement()) - User User @map(["user_id", "user_name"]) @relation(references:[id, name]) + id Int @default(autoincrement()) @id + user_id Int + user_name String + User User @relation(fields: [user_id, user_name], references: [id, name]) } - + model User { - id Int @id @default(autoincrement()) - name String - Post Post[] - - @@unique([id, name], name: "user_unique") + id Int @default(autoincrement()) @id + name String + Post Post[] + + @@unique([id, name], name: "user_unique") } "#; let result = dbg!(api.introspect().await); @@ -184,14 +196,17 @@ async fn compound_foreign_keys_should_work_for_self_relations(api: &TestApi) { .await; let dm = r#" - model Person { - id Int @id @default(autoincrement()) - name String - Person Person @map(["partner_id", "partner_name"]) @relation("PersonToPerson_partner_id_partner_name", references: [id,name]) - other_Person Person[] @relation("PersonToPerson_partner_id_partner_name") - - @@unique([id, name], name: "person_unique") + model Person { + id Int @default(autoincrement()) @id + name String + partner_id Int + partner_name String + Person Person @relation("PersonToPerson_partner_id_partner_name", fields: [partner_id, partner_name], references: [id, name]) + other_Person Person[] @relation("PersonToPerson_partner_id_partner_name") + + @@unique([id, name], name: "person_unique") } + "#; let result = dbg!(api.introspect().await); custom_assert(&result, dm); @@ -217,14 +232,16 @@ async fn compound_foreign_keys_should_work_with_defaults(api: &TestApi) { .await; let dm = r#" - model Person { - id Int @id @default(autoincrement()) - name String - Person Person @map(["partner_id", "partner_name"]) @relation("PersonToPerson_partner_id_partner_name", references: [id, name]) - other_Person Person[] @relation("PersonToPerson_partner_id_partner_name") - - @@unique([id, name], name: "person_unique") - } + model Person { + id Int @default(autoincrement()) @id + name String + partner_id Int @default(0) + partner_name String @default("") + Person Person @relation("PersonToPerson_partner_id_partner_name", fields: [partner_id, partner_name], references: [id, name]) + other_Person Person[] @relation("PersonToPerson_partner_id_partner_name") + + @@unique([id, name], name: "person_unique") + } "#; let result = dbg!(api.introspect().await); custom_assert(&result, dm); @@ -302,18 +319,20 @@ async fn compound_foreign_keys_should_work_for_one_to_many_relations_with_non_un let dm = r#" model Post { - id Int @id @default(autoincrement()) - User User @map(["user_id", "user_age"]) @relation(references:[id, age]) - - @@index(User, name: "test") + id Int @default(autoincrement()) @id + user_age Int + user_id Int + User User @relation(fields: [user_id, user_age], references: [id, age]) + + @@index([user_id, user_age], name: "test") } - + model User { - age Int - id Int @id @default(autoincrement()) - Post Post[] - - @@unique([id, age], name: "user_unique") + age Int + id Int @default(autoincrement()) @id + Post Post[] + + @@unique([id, age], name: "user_unique") } "#; let result = dbg!(api.introspect().await); @@ -342,61 +361,65 @@ async fn repro_matt_references_on_wrong_side(api: &TestApi) { let dm = r#" model a { - one Int - two Int - b b[] - - @@id([one, two]) + one Int + two Int + b b[] + + @@id([one, two]) + } + + model b { + id Int @default(autoincrement()) @id + one Int + two Int + + a a @relation(fields: [one, two], references: [one, two]) + } + "#; + let result = dbg!(api.introspect().await); + custom_assert(&result, dm); +} + +#[test_each_connector(tags("postgres"))] +#[test] +async fn introspecting_a_compound_fk_pk_with_overlapping_primary_key_should_work(api: &TestApi) { + let barrel = api.barrel(); + let _setup_schema = barrel + .execute(|migration| { + migration.create_table("a", |t| { + t.add_column("one", types::integer().nullable(false)); + t.add_column("two", types::integer().nullable(false)); + t.inject_custom("Primary Key (\"one\", \"two\")"); + }); + migration.create_table("b", |t| { + t.add_column("dummy", types::integer().nullable(false)); + t.add_column("one", types::integer().nullable(false)); + t.add_column("two", types::integer().nullable(false)); + t.inject_custom("Foreign Key (\"one\", \"two\") references a(\"one\", \"two\")"); + t.inject_custom("Primary Key (\"dummy\",\"one\", \"two\")"); + }); + }) + .await; + + let dm = r#" + model a { + one Int + two Int + b b[] + + @@id([one, two]) } model b { - id Int @id @default(autoincrement()) - a a @map(["one", "two"]) + dummy Int + one Int + two Int + a a @relation(fields: [one, two], references: [one, two]) + + @@id([dummy, one, two]) } - + "#; let result = dbg!(api.introspect().await); custom_assert(&result, dm); } - -//todo referencing unknown field in index due to compound, will be fixed in follow up PR -// #[test_each_connector(tags("postgres"))] -// #[test] -// async fn compound_fk_pk(api: &TestApi) { -// let barrel = api.barrel(); -// let _setup_schema = barrel -// .execute(|migration| { -// migration.create_table("a", |t| { -// t.add_column("one", types::integer().nullable(false)); -// t.add_column("two", types::integer().nullable(false)); -// t.inject_custom("Primary Key (\"one\", \"two\")"); -// }); -// migration.create_table("b", |t| { -// t.add_column("dummy", types::integer().nullable(false)); -// t.add_column("one", types::integer().nullable(false)); -// t.add_column("two", types::integer().nullable(false)); -// t.inject_custom("Foreign Key (\"one\", \"two\") references a(\"one\", \"two\")"); -// t.inject_custom("Primary Key (\"dummy\",\"one\", \"two\")"); -// }); -// }) -// .await; -// -// let dm = r#" -// model a { -// one Int -// two Int -// b b[] -// -// @@id([one, two]) -// } -// -// model b { -// dummy Int -// a a @map(["one", "two"]) -// -// @@id([dummy, one, two]) -// } -// "#; -// let result = dbg!(api.introspect().await); -// custom_assert(&result, dm); -// } diff --git a/introspection-engine/connectors/sql-introspection-connector/tests/db_specific_introspection/postgres/remapping_database_names_postgres.rs b/introspection-engine/connectors/sql-introspection-connector/tests/db_specific_introspection/postgres/remapping_database_names_postgres.rs index aed1918a875c..d84706fba1ce 100644 --- a/introspection-engine/connectors/sql-introspection-connector/tests/db_specific_introspection/postgres/remapping_database_names_postgres.rs +++ b/introspection-engine/connectors/sql-introspection-connector/tests/db_specific_introspection/postgres/remapping_database_names_postgres.rs @@ -91,17 +91,19 @@ async fn remapping_models_in_relations_should_work(api: &TestApi) { let dm = r#" model Post { - id Int @id @default(autoincrement()) - user_id User_with_Space + id Int @default(autoincrement()) @id + user_id Int @unique + User_with_Space User_with_Space @relation(fields: [user_id], references: [id]) } - + model User_with_Space { - id Int @id @default(autoincrement()) - name String - Post Post? - - @@map("User with Space") + id Int @default(autoincrement()) @id + name String + Post Post? + + @@map("User with Space") } + "#; let result = dbg!(api.introspect().await); custom_assert(&result, dm); @@ -132,18 +134,23 @@ async fn remapping_models_in_compound_relations_should_work(api: &TestApi) { let dm = r#" model Post { - id Int @id @default(autoincrement()) - User_with_Space User_with_Space @map(["user_id", "user_name"]) @relation(references:[id, name]) + id Int @default(autoincrement()) @id + user_id Int + user_name String + User_with_Space User_with_Space @relation(fields: [user_id, user_name], references: [id, name]) + + @@unique([user_id, user_name], name: "post_user_unique") } - + model User_with_Space { - id Int @id @default(autoincrement()) - name String - Post Post? - - @@map("User with Space") - @@unique([id, name], name: "user_unique") + id Int @default(autoincrement()) @id + name String + Post Post? + + @@map("User with Space") + @@unique([id, name], name: "user_unique") } + "#; let result = dbg!(api.introspect().await); custom_assert(&result, dm); @@ -174,17 +181,22 @@ async fn remapping_fields_in_compound_relations_should_work(api: &TestApi) { let dm = r#" model Post { - id Int @id @default(autoincrement()) - User User @map(["user_id", "user_name"]) @relation(references:[id, name_that_is_invalid]) + id Int @default(autoincrement()) @id + user_id Int + user_name String + User User @relation(fields: [user_id, user_name], references: [id, name_that_is_invalid]) + + @@unique([user_id, user_name], name: "post_user_unique") } - + model User { - id Int @id @default(autoincrement()) - name_that_is_invalid String @map("name-that-is-invalid") - Post Post? - - @@unique([id, name_that_is_invalid], name: "user_unique") + id Int @default(autoincrement()) @id + name_that_is_invalid String @map("name-that-is-invalid") + Post Post? + + @@unique([id, name_that_is_invalid], name: "user_unique") } + "#; let result = dbg!(api.introspect().await); custom_assert(&result, dm); diff --git a/introspection-engine/connectors/sql-introspection-connector/tests/db_specific_introspection/postgres/tables_postgres.rs b/introspection-engine/connectors/sql-introspection-connector/tests/db_specific_introspection/postgres/tables_postgres.rs index 37df0f2bbf9d..37f95ffab0d9 100644 --- a/introspection-engine/connectors/sql-introspection-connector/tests/db_specific_introspection/postgres/tables_postgres.rs +++ b/introspection-engine/connectors/sql-introspection-connector/tests/db_specific_introspection/postgres/tables_postgres.rs @@ -274,7 +274,7 @@ async fn introspecting_a_table_without_uniques_should_comment_it_out(api: &TestA }) .await; - let dm = "// The underlying table does not contain a unique identifier and can therefore currently not be handled.\n// model Post {\n // id Int\n // user_id User @relation(references: [id])\n// }\n\nmodel User {\n id Int @default(autoincrement()) @id\n}"; + let dm = "// The underlying table does not contain a unique identifier and can therefore currently not be handled.\n// model Post {\n // id Int\n // user_id Int\n // User User @relation(fields: [user_id], references: [id])\n// }\n\nmodel User {\n id Int @default(autoincrement()) @id\n}"; let result = dbg!(api.introspect().await); assert_eq!(&result, dm); @@ -480,3 +480,32 @@ async fn introspecting_a_legacy_m_to_n_relation_should_work(api: &TestApi) { let result = dbg!(api.introspect().await); custom_assert(&result, dm); } + +#[test_each_connector(tags("postgres"))] +async fn introspecting_default_values_on_lists_should_be_ignored(api: &TestApi) { + let barrel = api.barrel(); + let _setup_schema = barrel + .execute(|migration| { + migration.create_table("User", |t| { + t.add_column("id", types::primary()); + t.inject_custom("ints Integer[] DEFAULT array[]::Integer[]"); + t.inject_custom("ints2 Integer[] DEFAULT '{}'"); + }); + }) + .await; + + let dm = r#" + datasource pg { + provider = "postgres" + url = "postgresql://localhost:5432" + } + + model User { + id Int @id @default(autoincrement()) + ints Int [] + ints2 Int [] + } + "#; + let result = dbg!(api.introspect().await); + custom_assert(&result, dm); +} diff --git a/introspection-engine/connectors/sql-introspection-connector/tests/db_specific_introspection/sqlite/relations_sqlite.rs b/introspection-engine/connectors/sql-introspection-connector/tests/db_specific_introspection/sqlite/relations_sqlite.rs index 0343b51f89d5..60bd9605cb16 100644 --- a/introspection-engine/connectors/sql-introspection-connector/tests/db_specific_introspection/sqlite/relations_sqlite.rs +++ b/introspection-engine/connectors/sql-introspection-connector/tests/db_specific_introspection/sqlite/relations_sqlite.rs @@ -22,13 +22,14 @@ async fn introspecting_a_one_to_one_req_relation_should_work(api: &TestApi) { let dm = r#" model User { - id Int @id @default(autoincrement()) - Post Post? + id Int @id @default(autoincrement()) + Post Post? } model Post { - id Int @id @default(autoincrement()) - user_id User + id Int @id @default(autoincrement()) + user_id Int @unique + User User @relation(fields: [user_id], references: [id]) } "#; let result = dbg!(api.introspect().await); @@ -59,15 +60,17 @@ async fn introspecting_two_one_to_one_relations_between_the_same_models_should_w let dm = r#" model User { - id Int @id @default(autoincrement()) - post_id Post @relation("PostToUser_post_id", references: [id]) - Post Post? @relation("Post_user_idToUser") + id Int @default(autoincrement()) @id + post_id Int @unique + Post_PostToUser_post_id Post @relation("PostToUser_post_id", fields: [post_id], references: [id]) + Post_Post_user_idToUser Post? @relation("Post_user_idToUser") } - + model Post { - id Int @id @default(autoincrement()) - user_id User @relation("Post_user_idToUser", references: [id]) - User User? @relation("PostToUser_post_id") + id Int @default(autoincrement()) @id + user_id Int @unique + User_Post_user_idToUser User @relation("Post_user_idToUser", fields: [user_id], references: [id]) + User_PostToUser_post_id User? @relation("PostToUser_post_id") } "#; let result = dbg!(api.introspect().await); @@ -94,13 +97,14 @@ async fn introspecting_a_one_to_one_relation_should_work(api: &TestApi) { let dm = r#" model User { - id Int @id @default(autoincrement()) - Post Post? + id Int @default(autoincrement()) @id + Post Post? } - + model Post { - id Int @id @default(autoincrement()) - user_id User? + id Int @default(autoincrement()) @id + user_id Int? @unique + User User? @relation(fields: [user_id], references: [id]) } "#; let result = dbg!(api.introspect().await); @@ -127,14 +131,15 @@ async fn introspecting_a_one_to_one_relation_referencing_non_id_should_work(api: .await; let dm = r#" model User { - email String? @unique - id Int @id @default(autoincrement()) - Post Post? + email String? @unique + id Int @default(autoincrement()) @id + Post Post? } - + model Post { - id Int @id @default(autoincrement()) - user_email User? @relation(references: [email]) + id Int @default(autoincrement()) @id + user_email String? @unique + User User? @relation(fields: [user_email], references: [email]) } "#; let result = dbg!(api.introspect().await); @@ -161,13 +166,14 @@ async fn introspecting_a_one_to_many_relation_should_work(api: &TestApi) { let dm = r#" model User { - id Int @id @default(autoincrement()) - Post Post[] + id Int @default(autoincrement()) @id + Post Post[] } - + model Post { - id Int @id @default(autoincrement()) - user_id User? + id Int @default(autoincrement()) @id + user_id Int? + User User? @relation(fields: [user_id], references: [id]) } "#; let result = dbg!(api.introspect().await); @@ -194,13 +200,14 @@ async fn introspecting_a_one_req_to_many_relation_should_work(api: &TestApi) { let dm = r#" model User { - id Int @id @default(autoincrement()) - Post Post[] + id Int @default(autoincrement()) @id + Post Post[] } - + model Post { - id Int @id @default(autoincrement()) - user_id User + id Int @default(autoincrement()) @id + user_id Int + User User @relation(fields: [user_id], references: [id]) } "#; let result = dbg!(api.introspect().await); @@ -380,20 +387,23 @@ async fn introspecting_a_many_to_many_relation_with_an_id_should_work(api: &Test let dm = r#" model User { - id Int @id @default(autoincrement()) - PostsToUsers PostsToUsers[] + id Int @default(autoincrement()) @id + PostsToUsers PostsToUsers[] } - + model Post { - id Int @id @default(autoincrement()) - PostsToUsers PostsToUsers[] + id Int @default(autoincrement()) @id + PostsToUsers PostsToUsers[] } - + model PostsToUsers { - id Int @id @default(autoincrement()) - post_id Post - user_id User + id Int @default(autoincrement()) @id + post_id String + user_id String + Post Post @relation(fields: [post_id], references: [id]) + User User @relation(fields: [user_id], references: [id]) } + "#; let result = dbg!(api.introspect().await); custom_assert(&result, dm); @@ -418,11 +428,13 @@ async fn introspecting_a_self_relation_should_work(api: &TestApi) { let dm = r#" model User { - id Int @id @default(autoincrement()) - direct_report User? @relation("UserToUser_direct_report") - recruited_by User? @relation("UserToUser_recruited_by") - User_UserToUser_direct_report User[] @relation("UserToUser_direct_report") - User_UserToUser_recruited_by User[] @relation("UserToUser_recruited_by") + direct_report Int? + id Int @default(autoincrement()) @id + recruited_by Int? + User_UserToUser_direct_report User? @relation("UserToUser_direct_report", fields: [direct_report], references: [id]) + User_UserToUser_recruited_by User? @relation("UserToUser_recruited_by", fields: [recruited_by], references: [id]) + other_User_UserToUser_direct_report User[] @relation("UserToUser_direct_report") + other_User_UserToUser_recruited_by User[] @relation("UserToUser_recruited_by") } "#; let result = dbg!(api.introspect().await); @@ -433,37 +445,37 @@ async fn introspecting_a_self_relation_should_work(api: &TestApi) { // TODO: bring `onDelete` back once `prisma migrate` is a thing //#[test_each_connector(tags("sqlite"))] -async fn introspecting_cascading_delete_behaviour_should_work(api: &TestApi) { - let barrel = api.barrel(); - let _setup_schema = barrel - .execute(|migration| { - migration.create_table("User", |t| { - t.add_column("id", types::primary()); - }); - migration.create_table("Post", |t| { - t.add_column("id", types::primary()); - t.inject_custom( - "user_id INTEGER,\ - FOREIGN KEY (user_id) REFERENCES User(id) ON DELETE CASCADE", - ); - }); - }) - .await; - - let dm = r#" - model User { - id Int @id @default(autoincrement()) - Post Post[] @relation(onDelete: CASCADE) - } - - model Post { - id Int @id @default(autoincrement()) - user_id User? - } - "#; - let result = dbg!(api.introspect().await); - custom_assert(&result, dm); -} +// async fn introspecting_cascading_delete_behaviour_should_work(api: &TestApi) { +// let barrel = api.barrel(); +// let _setup_schema = barrel +// .execute(|migration| { +// migration.create_table("User", |t| { +// t.add_column("id", types::primary()); +// }); +// migration.create_table("Post", |t| { +// t.add_column("id", types::primary()); +// t.inject_custom( +// "user_id INTEGER,\ +// FOREIGN KEY (user_id) REFERENCES User(id) ON DELETE CASCADE", +// ); +// }); +// }) +// .await; +// +// let dm = r#" +// model User { +// id Int @id @default(autoincrement()) +// Post Post[] @relation(onDelete: CASCADE) +// } +// +// model Post { +// id Int @id @default(autoincrement()) +// user_id User? +// } +// "#; +// let result = dbg!(api.introspect().await); +// custom_assert(&result, dm); +// } #[test_each_connector(tags("sqlite"))] async fn introspecting_id_fields_with_foreign_key_should_work(api: &TestApi) { @@ -481,15 +493,16 @@ async fn introspecting_id_fields_with_foreign_key_should_work(api: &TestApi) { }) .await; - let dm = r#" + let dm = r#" model User { - id Int @id @default(autoincrement()) - Post Post[] + id Int @default(autoincrement()) @id + Post Post[] } - + model Post { - test String - user_id User @id @relation(references: [id]) + test String + user_id Int @default(autoincrement()) @id + User User @relation(fields: [user_id], references: [id]) } "#; let result = dbg!(api.introspect().await); diff --git a/introspection-engine/connectors/sql-introspection-connector/tests/db_specific_introspection/sqlite/relations_with_compound_fk_sqlite.rs b/introspection-engine/connectors/sql-introspection-connector/tests/db_specific_introspection/sqlite/relations_with_compound_fk_sqlite.rs index 769bcaf6f957..f05aeca765d2 100644 --- a/introspection-engine/connectors/sql-introspection-connector/tests/db_specific_introspection/sqlite/relations_with_compound_fk_sqlite.rs +++ b/introspection-engine/connectors/sql-introspection-connector/tests/db_specific_introspection/sqlite/relations_with_compound_fk_sqlite.rs @@ -27,16 +27,20 @@ async fn compound_foreign_keys_should_work_for_one_to_one_relations(api: &TestAp let dm = r#" model User { - age Int - id Int @id @default(autoincrement()) - Post Post? - - @@unique([id, age], name: "sqlite_autoindex_User_1") + age Int + id Int @default(autoincrement()) @id + Post Post? + + @@unique([id, age], name: "sqlite_autoindex_User_1") } - + model Post { - id Int @id @default(autoincrement()) - User User? @map(["user_id", "user_age"]) @relation(references:[id, age]) + id Int @default(autoincrement()) @id + user_age Int? + user_id Int? + User User? @relation(fields: [user_id, user_age], references: [id, age]) + + @@unique([user_id, user_age], name: "sqlite_autoindex_Post_1") } "#; @@ -69,16 +73,20 @@ async fn compound_foreign_keys_should_work_for_required_one_to_one_relations(api let dm = r#" model User { - age Int - id Int @id @default(autoincrement()) - Post Post? - - @@unique([id, age], name: "sqlite_autoindex_User_1") + age Int + id Int @default(autoincrement()) @id + Post Post? + + @@unique([id, age], name: "sqlite_autoindex_User_1") } - + model Post { - id Int @id @default(autoincrement()) - User User @map(["user_id", "user_age"]) @relation(references:[id, age]) + id Int @default(autoincrement()) @id + user_age Int + user_id Int + User User @relation(fields: [user_id, user_age], references: [id, age]) + + @@unique([user_id, user_age], name: "sqlite_autoindex_Post_1") } "#; @@ -109,17 +117,19 @@ async fn compound_foreign_keys_should_work_for_one_to_many_relations(api: &TestA .await; let dm = r#" - model User { - age Int - id Int @id @default(autoincrement()) - Post Post[] - - @@unique([id, age], name: "sqlite_autoindex_User_1") + model User { + age Int + id Int @default(autoincrement()) @id + Post Post[] + + @@unique([id, age], name: "sqlite_autoindex_User_1") } - + model Post { - id Int @id @default(autoincrement()) - User User? @map(["user_id", "user_age"]) @relation(references:[id, age]) + id Int @default(autoincrement()) @id + user_age Int? + user_id Int? + User User? @relation(fields: [user_id, user_age], references: [id, age]) } "#; @@ -156,18 +166,22 @@ async fn compound_foreign_keys_should_work_for_duplicate_one_to_many_relations(a let dm = r#" model User { - age Int - id Int @id @default(autoincrement()) - Post_Post_other_user_id_other_user_ageToUser Post[] @relation("Post_other_user_id_other_user_ageToUser") - Post_Post_user_id_user_ageToUser Post[] @relation("Post_user_id_user_ageToUser") - - @@unique([id, age], name: "sqlite_autoindex_User_1") + age Int + id Int @default(autoincrement()) @id + Post_Post_other_user_id_other_user_ageToUser Post[] @relation("Post_other_user_id_other_user_ageToUser") + Post_Post_user_id_user_ageToUser Post[] @relation("Post_user_id_user_ageToUser") + + @@unique([id, age], name: "sqlite_autoindex_User_1") } - + model Post { - id Int @id @default(autoincrement()) - User_other_user_id User? @map(["other_user_id", "other_user_age"]) @relation(name: "Post_other_user_id_other_user_ageToUser", references:[id, age]) - User_user_id User? @map(["user_id", "user_age"]) @relation(name: "Post_user_id_user_ageToUser", references:[id, age]) + id Int @default(autoincrement()) @id + other_user_age Int? + other_user_id Int? + user_age Int? + user_id Int? + User_Post_other_user_id_other_user_ageToUser User? @relation("Post_other_user_id_other_user_ageToUser", fields: [other_user_id, other_user_age], references: [id, age]) + User_Post_user_id_user_ageToUser User? @relation("Post_user_id_user_ageToUser", fields: [user_id, user_age], references: [id, age]) } "#; @@ -199,16 +213,18 @@ async fn compound_foreign_keys_should_work_for_required_one_to_many_relations(ap let dm = r#" model User { - age Int - id Int @id @default(autoincrement()) - Post Post[] - - @@unique([id, age], name: "sqlite_autoindex_User_1") + age Int + id Int @default(autoincrement()) @id + Post Post[] + + @@unique([id, age], name: "sqlite_autoindex_User_1") } - + model Post { - id Int @id @default(autoincrement()) - User User @map(["user_id", "user_age"]) @relation(references:[id, age]) + id Int @default(autoincrement()) @id + user_age Int + user_id Int + User User @relation(fields: [user_id, user_age], references: [id, age]) } "#; let result = dbg!(api.introspect().await); @@ -236,13 +252,16 @@ async fn compound_foreign_keys_should_work_for_required_self_relations(api: &Tes let dm = r#" model Person { - age Int - id Int @id @default(autoincrement()) - Person Person @map(["partner_id", "partner_age"]) @relation("PersonToPerson_partner_id_partner_age", references: [id,age]) - other_Person Person[] @relation("PersonToPerson_partner_id_partner_age") - - @@unique([id, age], name: "sqlite_autoindex_Person_1") + age Int + id Int @default(autoincrement()) @id + partner_age Int + partner_id Int + Person Person @relation("PersonToPerson_partner_id_partner_age", fields: [partner_id, partner_age], references: [id, age]) + other_Person Person[] @relation("PersonToPerson_partner_id_partner_age") + + @@unique([id, age], name: "sqlite_autoindex_Person_1") } + "#; let result = dbg!(api.introspect().await); custom_assert(&result, dm); @@ -269,13 +288,16 @@ async fn compound_foreign_keys_should_work_for_self_relations(api: &TestApi) { let dm = r#" model Person { - age Int - id Int @id @default(autoincrement()) - Person Person? @map(["partner_id", "partner_age"]) @relation("PersonToPerson_partner_id_partner_age", references: [id, age]) - other_Person Person[] @relation("PersonToPerson_partner_id_partner_age") - - @@unique([id, age], name: "sqlite_autoindex_Person_1") + age Int + id Int @default(autoincrement()) @id + partner_age Int? + partner_id Int? + Person Person? @relation("PersonToPerson_partner_id_partner_age", fields: [partner_id, partner_age], references: [id, age]) + other_Person Person[] @relation("PersonToPerson_partner_id_partner_age") + + @@unique([id, age], name: "sqlite_autoindex_Person_1") } + "#; let result = dbg!(api.introspect().await); custom_assert(&result, dm); @@ -302,13 +324,16 @@ async fn compound_foreign_keys_should_work_with_defaults(api: &TestApi) { let dm = r#" model Person { - age Int - id Int @id @default(autoincrement()) - Person Person @map(["partner_id", "partner_age"]) @relation("PersonToPerson_partner_id_partner_age", references: [id, age]) - other_Person Person[] @relation("PersonToPerson_partner_id_partner_age") - - @@unique([id, age], name: "sqlite_autoindex_Person_1") + age Int + id Int @default(autoincrement()) @id + partner_age Int @default(0) + partner_id Int @default(0) + Person Person @relation("PersonToPerson_partner_id_partner_age", fields: [partner_id, partner_age], references: [id, age]) + other_Person Person[] @relation("PersonToPerson_partner_id_partner_age") + + @@unique([id, age], name: "sqlite_autoindex_Person_1") } + "#; let result = dbg!(api.introspect().await); custom_assert(&result, dm); @@ -387,19 +412,22 @@ async fn compound_foreign_keys_should_work_for_one_to_many_relations_with_non_un let dm = r#" model User { - age Int - id Int @id @default(autoincrement()) - Post Post[] - - @@unique([id, age], name: "sqlite_autoindex_User_1") + age Int + id Int @default(autoincrement()) @id + Post Post[] + + @@unique([id, age], name: "sqlite_autoindex_User_1") } - + model Post { - id Int @id @default(autoincrement()) - User User @map(["user_id", "user_age"]) @relation(references:[id, age]) - - @@index([User], name: "test") + id Int @default(autoincrement()) @id + user_age Int + user_id Int + User User @relation(fields: [user_id, user_age], references: [id, age]) + + @@index([user_id, user_age], name: "test") } + "#; let result = dbg!(api.introspect().await); custom_assert(&result, dm); diff --git a/introspection-engine/connectors/sql-introspection-connector/tests/db_specific_introspection/sqlite/remapping_database_names_sqlite.rs b/introspection-engine/connectors/sql-introspection-connector/tests/db_specific_introspection/sqlite/remapping_database_names_sqlite.rs index 974ded365fb4..16778029b868 100644 --- a/introspection-engine/connectors/sql-introspection-connector/tests/db_specific_introspection/sqlite/remapping_database_names_sqlite.rs +++ b/introspection-engine/connectors/sql-introspection-connector/tests/db_specific_introspection/sqlite/remapping_database_names_sqlite.rs @@ -90,17 +90,19 @@ async fn remapping_models_in_relations_should_work(api: &TestApi) { let dm = r#" model User_with_Space { - id Int @id @default(autoincrement()) - name String - Post Post? - - @@map("User with Space") + id Int @default(autoincrement()) @id + name String + Post Post? + + @@map("User with Space") } - + model Post { - id Int @id @default(autoincrement()) - user_id User_with_Space + id Int @default(autoincrement()) @id + user_id Int @unique + User_with_Space User_with_Space @relation(fields: [user_id], references: [id]) } + "#; let result = dbg!(api.introspect().await); custom_assert(&result, dm); @@ -131,17 +133,21 @@ async fn remapping_models_in_compound_relations_should_work(api: &TestApi) { let dm = r#" model User_with_Space { - age Int - id Int @id @default(autoincrement()) - Post Post? - - @@map("User with Space") - @@unique([id, age], name: "sqlite_autoindex_User with Space_1") + age Int + id Int @default(autoincrement()) @id + Post Post? + + @@map("User with Space") + @@unique([id, age], name: "sqlite_autoindex_User with Space_1") } - + model Post { - id Int @id @default(autoincrement()) - User_with_Space User_with_Space @map(["user_id", "user_age"]) @relation(references:[id, age]) + id Int @default(autoincrement()) @id + user_age Int + user_id Int + User_with_Space User_with_Space @relation(fields: [user_id, user_age], references: [id, age]) + + @@unique([user_id, user_age], name: "sqlite_autoindex_Post_1") } "#; let result = dbg!(api.introspect().await); @@ -171,16 +177,20 @@ async fn remapping_fields_in_compound_relations_should_work(api: &TestApi) { let dm = r#" model User { - age_that_is_invalid Int @map("age-that-is-invalid") - id Int @id @default(autoincrement()) - Post Post? - - @@unique([id, age_that_is_invalid], name: "sqlite_autoindex_User_1") + age_that_is_invalid Int @map("age-that-is-invalid") + id Int @default(autoincrement()) @id + Post Post? + + @@unique([id, age_that_is_invalid], name: "sqlite_autoindex_User_1") } - + model Post { - id Int @id @default(autoincrement()) - User User @map(["user_id", "user_age"]) @relation(references:[id, age_that_is_invalid]) + id Int @default(autoincrement()) @id + user_age Int + user_id Int + User User @relation(fields: [user_id, user_age], references: [id, age_that_is_invalid]) + + @@unique([user_id, user_age], name: "sqlite_autoindex_Post_1") } "#; let result = dbg!(api.introspect().await); diff --git a/introspection-engine/connectors/sql-introspection-connector/tests/db_specific_introspection/sqlite/tables_sqlite.rs b/introspection-engine/connectors/sql-introspection-connector/tests/db_specific_introspection/sqlite/tables_sqlite.rs index 9d591d5346ec..f38d1f5237a0 100644 --- a/introspection-engine/connectors/sql-introspection-connector/tests/db_specific_introspection/sqlite/tables_sqlite.rs +++ b/introspection-engine/connectors/sql-introspection-connector/tests/db_specific_introspection/sqlite/tables_sqlite.rs @@ -277,7 +277,7 @@ async fn introspecting_a_table_without_uniques_should_comment_it_out(api: &TestA }) .await; - let dm = "model User {\n id Int @default(autoincrement()) @id\n}\n\n// The underlying table does not contain a unique identifier and can therefore currently not be handled.\n// model Post {\n // id Int\n // user_id User @relation(references: [id])\n// }"; + let dm = "model User {\n id Int @default(autoincrement()) @id\n}\n\n// The underlying table does not contain a unique identifier and can therefore currently not be handled.\n// model Post {\n // id Int\n // user_id Int\n // User User @relation(fields: [user_id], references: [id])\n// }"; let result = dbg!(api.introspect().await); assert_eq!(&result, dm); diff --git a/libs/datamodel/core/src/dml/model.rs b/libs/datamodel/core/src/dml/model.rs index 7f05ab89b863..20697d74f057 100644 --- a/libs/datamodel/core/src/dml/model.rs +++ b/libs/datamodel/core/src/dml/model.rs @@ -91,6 +91,14 @@ impl Model { self.fields_mut().find(|f| f.name == *name) } + /// Finds a relation field by name and returns a mutable reference. + pub fn find_relation_field_mut(&mut self, name: &str) -> Option<&mut Field> { + self.fields_mut().find(|f| match f.field_type { + FieldType::Relation(_) => f.name == *name, + _ => false, + }) + } + /// Finds the name of all id fields pub fn id_field_names(&self) -> Vec { let singular_id_field = self.singular_id_fields().next(); diff --git a/libs/datamodel/core/src/validator/standardise.rs b/libs/datamodel/core/src/validator/standardise.rs index 243029b14a00..56aaf303b13d 100644 --- a/libs/datamodel/core/src/validator/standardise.rs +++ b/libs/datamodel/core/src/validator/standardise.rs @@ -81,8 +81,7 @@ impl Standardiser { (x, y) if x < y => true, (x, y) if x > y => false, // SELF RELATIONS - (x, y) if x == y => field.name < related_field.name, - _ => unreachable!(), // no clue why the compiler does not understand it is exhaustive + _ => field.name < related_field.name, }, };