risingwave_meta_model_migration/
m20260705_000000_subscription_dependent_object_fk.rs1use sea_orm_migration::prelude::*;
2
3use crate::m20230908_072257_init::Object;
4use crate::sea_orm::{ConnectionTrait, DatabaseBackend, Statement, TransactionTrait};
5use crate::utils::ColumnDefExt;
6
7#[derive(DeriveMigrationName)]
8pub struct Migration;
9
10const FK_NAME: &str = "FK_subscription_dependent_table_id";
11const NEW_TABLE: &str = "subscription_new";
12
13#[async_trait::async_trait]
14impl MigrationTrait for Migration {
15 async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
16 let backend = manager.get_database_backend();
19 let cleanup_sql = match backend {
20 DatabaseBackend::MySql => {
21 "DELETE o FROM object AS o \
22 JOIN subscription AS s ON s.subscription_id = o.oid \
23 LEFT JOIN object AS dependent ON dependent.oid = s.dependent_table_id \
24 WHERE dependent.oid IS NULL"
25 }
26 DatabaseBackend::Postgres => {
27 "DELETE FROM object WHERE oid IN (\
28 SELECT subscription_id FROM subscription \
29 WHERE dependent_table_id NOT IN (SELECT oid FROM object))"
30 }
31 DatabaseBackend::Sqlite => return recreate_table(manager, true).await,
32 };
33 manager
34 .get_connection()
35 .execute(Statement::from_string(backend, cleanup_sql))
36 .await?;
37 manager
38 .alter_table(
39 Table::alter()
40 .table(Subscription::Table)
41 .add_foreign_key(&dependent_object_foreign_key())
42 .to_owned(),
43 )
44 .await?;
45 Ok(())
46 }
47
48 async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
49 match manager.get_database_backend() {
50 DatabaseBackend::MySql | DatabaseBackend::Postgres => {
51 manager
52 .alter_table(
53 Table::alter()
54 .table(Subscription::Table)
55 .drop_foreign_key(Alias::new(FK_NAME))
56 .to_owned(),
57 )
58 .await?;
59 }
60 DatabaseBackend::Sqlite => {
61 recreate_table(manager, false).await?;
62 }
63 }
64 Ok(())
65 }
66}
67
68fn dependent_object_foreign_key() -> TableForeignKey {
69 TableForeignKey::new()
70 .name(FK_NAME)
71 .from_tbl(Subscription::Table)
72 .from_col(Subscription::DependentTableId)
73 .to_tbl(Object::Table)
74 .to_col(Object::Oid)
75 .on_delete(ForeignKeyAction::Cascade)
76 .to_owned()
77}
78
79async fn recreate_table(manager: &SchemaManager<'_>, with_dependent_fk: bool) -> Result<(), DbErr> {
80 let backend = manager.get_database_backend();
81 let txn = manager.get_connection().begin().await?;
82 {
83 let txn_manager = SchemaManager::new(&txn);
84
85 if with_dependent_fk {
86 txn.execute(Statement::from_string(
87 backend,
88 "DELETE FROM object WHERE oid IN (\
89 SELECT subscription_id FROM subscription \
90 WHERE dependent_table_id NOT IN (SELECT oid FROM object))",
91 ))
92 .await?;
93 }
94
95 let mut create = Table::create();
96 create
97 .table(Alias::new(NEW_TABLE))
98 .col(
99 ColumnDef::new(Subscription::SubscriptionId)
100 .integer()
101 .primary_key(),
102 )
103 .col(ColumnDef::new(Subscription::Name).string().not_null())
104 .col(
105 ColumnDef::new(Subscription::Definition)
106 .rw_long_text(&txn_manager)
107 .not_null(),
108 )
109 .col(ColumnDef::new(Subscription::RetentionSeconds).big_integer())
110 .col(ColumnDef::new(Subscription::SubscriptionState).integer())
111 .col(
112 ColumnDef::new(Subscription::DependentTableId)
113 .integer()
114 .not_null(),
115 )
116 .foreign_key(
117 &mut ForeignKey::create()
118 .name("FK_subscription_object_id")
119 .from(Alias::new(NEW_TABLE), Subscription::SubscriptionId)
120 .to(Object::Table, Object::Oid)
121 .on_delete(ForeignKeyAction::Cascade)
122 .to_owned(),
123 );
124 if with_dependent_fk {
125 create.foreign_key(
126 &mut ForeignKey::create()
127 .name(FK_NAME)
128 .from(Alias::new(NEW_TABLE), Subscription::DependentTableId)
129 .to(Object::Table, Object::Oid)
130 .on_delete(ForeignKeyAction::Cascade)
131 .to_owned(),
132 );
133 }
134 txn_manager.create_table(create).await?;
135
136 txn.execute(Statement::from_string(
137 backend,
138 "INSERT INTO subscription_new \
139 (subscription_id, name, retention_seconds, definition, subscription_state, dependent_table_id) \
140 SELECT subscription_id, name, retention_seconds, definition, subscription_state, dependent_table_id \
141 FROM subscription WHERE dependent_table_id IN (SELECT oid FROM object)",
142 ))
143 .await?;
144
145 txn_manager
146 .drop_table(Table::drop().table(Subscription::Table).to_owned())
147 .await?;
148 txn_manager
149 .rename_table(
150 Table::rename()
151 .table(Alias::new(NEW_TABLE), Subscription::Table)
152 .to_owned(),
153 )
154 .await?;
155 }
156 txn.commit().await?;
157 Ok(())
158}
159
160#[derive(DeriveIden)]
161enum Subscription {
162 Table,
163 SubscriptionId,
164 Name,
165 Definition,
166 RetentionSeconds,
167 SubscriptionState,
168 DependentTableId,
169}