Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Recognize Foreign Key Shorthand on Sqlite #697

Merged
merged 2 commits into from
Apr 21, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,40 @@ async fn introspecting_a_one_to_one_req_relation_should_work(api: &TestApi) {
custom_assert(&result, dm);
}

#[test_each_connector(tags("sqlite"))]
async fn introspecting_a_one_to_one_req_relation_using_shortened_syntax_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 NOT NULL UNIQUE,
FOREIGN KEY(user_id) REFERENCES User",
)
});
})
.await;

let dm = r#"
model User {
id Int @id @default(autoincrement())
Post Post?
}

model Post {
id Int @id @default(autoincrement())
user_id Int @unique
User User @relation(fields: [user_id], references: [id])
}
"#;
let result = dbg!(api.introspect().await);
custom_assert(&result, dm);
}

#[test_each_connector(tags("sqlite"))]
async fn introspecting_two_one_to_one_relations_between_the_same_models_should_work(api: &TestApi) {
let barrel = api.barrel();
Expand Down
28 changes: 25 additions & 3 deletions libs/sql-schema-describer/src/sqlite.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,22 @@ impl super::SqlSchemaDescriberBackend for SqlSchemaDescriber {
tables.push(self.get_table(schema, table_name).await)
}

//sqlite allows foreign key definitions without specifying the referenced columns, it then assumes the pk is used
let mut foreign_keys_without_referenced_columns = vec![];
for (table_index, table) in tables.iter().enumerate() {
for (fk_index, foreign_key) in table.foreign_keys.iter().enumerate() {
if foreign_key.referenced_columns.is_empty() {
let referenced_table = tables.iter().find(|t| t.name == foreign_key.referenced_table).unwrap();
let referenced_pk = referenced_table.primary_key.as_ref().unwrap();
foreign_keys_without_referenced_columns.push((table_index, fk_index, referenced_pk.columns.clone()))
}
}
}

for (table_index, fk_index, columns) in foreign_keys_without_referenced_columns {
tables[table_index].foreign_keys[fk_index].referenced_columns = columns
}

Ok(SqlSchema {
// There's no enum type in SQLite.
enums: vec![],
Expand Down Expand Up @@ -262,18 +278,24 @@ impl SqlSchemaDescriber {
let id = row.get("id").and_then(|x| x.as_i64()).expect("id");
let seq = row.get("seq").and_then(|x| x.as_i64()).expect("seq");
let column = row.get("from").and_then(|x| x.to_string()).expect("from");
let referenced_column = row.get("to").and_then(|x| x.to_string()).expect("to");
// this can be null if the primary key and shortened fk syntax was used
let referenced_column = row.get("to").and_then(|x| x.to_string());
let referenced_table = row.get("table").and_then(|x| x.to_string()).expect("table");
match intermediate_fks.get_mut(&id) {
Some(fk) => {
fk.columns.insert(seq, column);
fk.referenced_columns.insert(seq, referenced_column);
if let Some(column) = referenced_column {
fk.referenced_columns.insert(seq, column);
};
}
None => {
let mut columns: HashMap<i64, String> = HashMap::new();
columns.insert(seq, column);
let mut referenced_columns: HashMap<i64, String> = HashMap::new();
referenced_columns.insert(seq, referenced_column);

if let Some(column) = referenced_column {
referenced_columns.insert(seq, column);
};
let on_delete_action = match row
.get("on_delete")
.and_then(|x| x.to_string())
Expand Down