Skip to main content

doido_model/migration/
foreign_key.rs

1//! Foreign-key operations: `add_foreign_key`, `remove_foreign_key`.
2
3use sea_orm::sea_query::{
4    Alias, ForeignKey as SqForeignKey, ForeignKeyCreateStatement, ForeignKeyDropStatement,
5};
6use sea_orm::{ConnectionTrait, DbErr};
7use sea_orm_migration::SchemaManager;
8
9/// Default foreign-key constraint name: `fk_<table>_<column>`.
10fn foreign_key_name(table: &str, column: &str) -> String {
11    format!("fk_{table}_{column}")
12}
13
14fn add_foreign_key_statement(
15    from_table: &str,
16    from_column: &str,
17    to_table: &str,
18    to_column: &str,
19) -> ForeignKeyCreateStatement {
20    let mut stmt = SqForeignKey::create();
21    stmt.name(foreign_key_name(from_table, from_column))
22        .from(Alias::new(from_table), Alias::new(from_column))
23        .to(Alias::new(to_table), Alias::new(to_column));
24    stmt
25}
26
27fn remove_foreign_key_statement(from_table: &str, from_column: &str) -> ForeignKeyDropStatement {
28    let mut stmt = SqForeignKey::drop();
29    stmt.name(foreign_key_name(from_table, from_column))
30        .table(Alias::new(from_table));
31    stmt
32}
33
34/// `add_foreign_key :from_table, :to_table` — constraint
35/// `fk_<from_table>_<from_column>` linking `from_table.from_column` to
36/// `to_table.to_column`.
37///
38/// Note: SQLite cannot add foreign keys via `ALTER TABLE`; define them inside
39/// [`super::create_table`] there. This works on PostgreSQL and MySQL.
40pub async fn add_foreign_key(
41    manager: &SchemaManager<'_>,
42    from_table: &str,
43    from_column: &str,
44    to_table: &str,
45    to_column: &str,
46) -> Result<(), DbErr> {
47    manager
48        .get_connection()
49        .execute(&add_foreign_key_statement(
50            from_table,
51            from_column,
52            to_table,
53            to_column,
54        ))
55        .await
56        .map(|_| ())
57}
58
59/// `remove_foreign_key :from_table, column: :from_column`.
60pub async fn remove_foreign_key(
61    manager: &SchemaManager<'_>,
62    from_table: &str,
63    from_column: &str,
64) -> Result<(), DbErr> {
65    manager
66        .get_connection()
67        .execute(&remove_foreign_key_statement(from_table, from_column))
68        .await
69        .map(|_| ())
70}
71
72#[cfg(test)]
73mod tests {
74    use super::*;
75    use sea_orm::sea_query::PostgresQueryBuilder;
76
77    #[test]
78    fn foreign_key_name_is_derived() {
79        assert_eq!(foreign_key_name("posts", "user_id"), "fk_posts_user_id");
80    }
81
82    #[test]
83    fn add_foreign_key_renders_constraint() {
84        let sql = add_foreign_key_statement("posts", "user_id", "users", "id")
85            .to_string(PostgresQueryBuilder);
86        assert!(sql.contains("\"fk_posts_user_id\""));
87        assert!(sql.contains("\"posts\""));
88        assert!(sql.contains("\"users\""));
89    }
90}