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 64e637d01988..09fc02d4c57b 100644 --- a/introspection-engine/connectors/sql-introspection-connector/src/misc_helpers.rs +++ b/introspection-engine/connectors/sql-introspection-connector/src/misc_helpers.rs @@ -19,45 +19,47 @@ pub fn is_migration_table(table: &Table) -> bool { pub(crate) fn is_prisma_1_point_1_join_table(table: &Table) -> bool { table.columns.len() == 2 - && table.foreign_keys.len() == 2 - && table.foreign_keys[0].referenced_table < table.foreign_keys[1].referenced_table - && table.name.starts_with("_") - && table - .columns - .iter() - .find(|column| column.name.to_lowercase() == "a") - .is_some() - && table - .columns - .iter() - .find(|column| column.name.to_lowercase() == "b") - .is_some() - && table.indices.len() >= 1 - && table.indices.last().unwrap().columns.len() == 2 - && table.indices.last().unwrap().tpe == IndexType::Unique + && table.indices.len() >= 2 + && common_prisma_m_to_n_relation_conditions(table) } pub(crate) fn is_prisma_1_point_0_join_table(table: &Table) -> bool { table.columns.len() == 3 - && table.foreign_keys.len() == 2 - && table.foreign_keys[0].referenced_table < table.foreign_keys[1].referenced_table - && table.name.starts_with("_") - && table - .columns - .iter() - .find(|column| column.name.to_lowercase() == "a") - .is_some() - && table - .columns - .iter() - .find(|column| column.name.to_lowercase() == "b") - .is_some() + && table.indices.len() >= 2 + && table.columns.iter().any(|c| c.name.as_str() == "id") + && common_prisma_m_to_n_relation_conditions(table) +} + +fn common_prisma_m_to_n_relation_conditions(table: &Table) -> bool { + fn is_a(column: &String) -> bool { + column.to_lowercase() == "a" + } + + fn is_b(column: &String) -> bool { + column.to_lowercase() == "b" + } + + table.name.starts_with("_") + //UNIQUE INDEX [A,B] + && table.indices.iter().any(|i| { + i.columns.len() == 2 + && is_a(&i.columns[0]) + && is_b(&i.columns[1]) + && i.tpe == IndexType::Unique + }) + //INDEX [B] && table - .columns + .indices .iter() - .find(|column| column.name.to_lowercase() == "id") - .is_some() - && table.indices.len() >= 1 + .any(|i| i.columns.len() == 1 && is_b(&i.columns[0]) && i.tpe == IndexType::Normal) + // 2 FKs + && table.foreign_keys.len() == 2 + // Lexicographically lower model referenced by A + && if table.foreign_keys[0].referenced_table <= table.foreign_keys[1].referenced_table { + is_a(&table.foreign_keys[0].columns[0]) && is_b(&table.foreign_keys[1].columns[0]) + } else { + is_b(&table.foreign_keys[0].columns[0]) && is_a(&table.foreign_keys[1].columns[0]) + } } pub(crate) fn is_foreign_key_column(table: &Table, column: &Column) -> bool { 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 13fbd825d012..ea473066b69f 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 @@ -257,6 +257,7 @@ async fn introspecting_a_prisma_many_to_many_relation_should_work(api: &TestApi) FOREIGN KEY (`B`) REFERENCES `User`(`id`) ON DELETE CASCADE", ); t.add_index("test", types::index(vec!["A", "B"]).unique(true)); + t.add_index("test2", types::index(vec!["B"]).unique(false)); }); }, api.db_name(), 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 f2170653b355..9ae1ace34365 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 @@ -228,6 +228,17 @@ async fn introspecting_a_prisma_many_to_many_relation_should_work(api: &TestApi) .await .unwrap(); + api.database() + .execute_raw( + &format!( + "CREATE INDEX test2 ON \"{}\".\"_PostToUser\" (\"b\");", + api.schema_name() + ), + &[], + ) + .await + .unwrap(); + let dm = r#" model Post { id Int @id @default(autoincrement()) 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 d19d60e05a2a..95fe96f504c2 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 @@ -439,3 +439,44 @@ async fn introspecting_an_unsupported_type_should_comment_it_out(api: &TestApi) let result = dbg!(api.introspect().await); assert_eq!(&result, "model Test {\n id Int @default(autoincrement()) @id\n network_inet String?\n // This type is currently not supported.\n // network_mac macaddr?\n}"); } + +#[test_each_connector(tags("postgres"))] +async fn introspecting_a_legacy_m_to_n_relation_should_work(api: &TestApi) { + let barrel = api.barrel(); + let _setup_schema = barrel + .execute(|migration| { + migration.create_table("Post", |t| { + t.inject_custom("id integer PRIMARY KEY"); + }); + migration.create_table("Category", |t| { + t.inject_custom("id integer PRIMARY KEY"); + }); + + migration.create_table("_CategoryToPost", |t| { + t.inject_custom("A integer NOT NULL REFERENCES \"Category\"(id) ON DELETE CASCADE ON UPDATE CASCADE"); + t.inject_custom("B integer NOT NULL REFERENCES \"Post\"(id) ON DELETE CASCADE ON UPDATE CASCADE"); + }); + }) + .await; + let unique = + "CREATE UNIQUE INDEX _CategoryToPost_AB_unique ON \"_CategoryToPost\"(\"a\",\"b\" )"; + let index = "CREATE INDEX _CategoryToPost_AB_index ON \"_CategoryToPost\"(\"b\" )"; + + api.database().execute_raw(unique, &[]).await.unwrap(); + api.database().execute_raw(index, &[]).await.unwrap(); + + let dm = r#" + model Category { + id Int @id + post Post[] + } + + model Post { + id Int @id + category Category[] + } + "#; + + 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 292d92bca34c..2c06314a436e 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 @@ -242,6 +242,17 @@ async fn introspecting_a_prisma_many_to_many_relation_should_work(api: &TestApi) .await .unwrap(); + api.database() + .execute_raw( + &format!( + "CREATE INDEX \"{}\".test2 ON \"_PostToUser\" (\"B\");", + api.schema_name(), + ), + &[], + ) + .await + .unwrap(); + let dm = r#" model User { id Int @id @default(autoincrement())