Skip to main content

sea_orm_migration/migrator/
with_self.rs

1use super::{Migration, MigrationStatus, exec::*};
2use crate::{IntoSchemaManagerConnection, MigrationTrait, SchemaManager, seaql_migrations};
3use sea_orm::sea_query::IntoIden;
4use sea_orm::{ConnectionTrait, DbErr, DynIden};
5
6use tracing::info;
7
8/// Performing migrations on a database
9#[async_trait::async_trait]
10pub trait MigratorTraitSelf: Sized + Send + Sync {
11    /// Vector of migrations in time sequence
12    fn migrations(&self) -> Vec<Box<dyn MigrationTrait>>;
13
14    /// Name of the migration table, it is `seaql_migrations` by default
15    fn migration_table_name(&self) -> DynIden {
16        seaql_migrations::Entity.into_iden()
17    }
18
19    /// Get list of migrations wrapped in `Migration` struct
20    fn get_migration_files(&self) -> Vec<Migration> {
21        self.migrations()
22            .into_iter()
23            .map(|migration| Migration {
24                migration,
25                status: MigrationStatus::Pending,
26            })
27            .collect()
28    }
29
30    /// Get list of applied migrations from database
31    async fn get_migration_models<C>(&self, db: &C) -> Result<Vec<seaql_migrations::Model>, DbErr>
32    where
33        C: ConnectionTrait,
34    {
35        self.install(db).await?;
36        get_migration_models(db, self.migration_table_name()).await
37    }
38
39    /// Get list of migrations with status
40    async fn get_migration_with_status<C>(&self, db: &C) -> Result<Vec<Migration>, DbErr>
41    where
42        C: ConnectionTrait,
43    {
44        self.install(db).await?;
45        get_migration_with_status(
46            self.get_migration_files(),
47            self.get_migration_models(db).await?,
48        )
49    }
50
51    /// Get list of pending migrations
52    async fn get_pending_migrations<C>(&self, db: &C) -> Result<Vec<Migration>, DbErr>
53    where
54        C: ConnectionTrait,
55    {
56        self.install(db).await?;
57        Ok(self
58            .get_migration_with_status(db)
59            .await?
60            .into_iter()
61            .filter(|file| file.status == MigrationStatus::Pending)
62            .collect())
63    }
64
65    /// Get list of applied migrations
66    async fn get_applied_migrations<C>(&self, db: &C) -> Result<Vec<Migration>, DbErr>
67    where
68        C: ConnectionTrait,
69    {
70        self.install(db).await?;
71        Ok(self
72            .get_migration_with_status(db)
73            .await?
74            .into_iter()
75            .filter(|file| file.status == MigrationStatus::Applied)
76            .collect())
77    }
78
79    /// Get list of migrations with status, without creating the migration table.
80    ///
81    /// Unlike [`get_migration_with_status`](Self::get_migration_with_status), this never runs
82    /// `CREATE TABLE`, so it can be called by a database user without DDL privileges (for
83    /// example, checking pending migrations from a read-only connection). If the migration
84    /// table does not exist, every migration is reported as [`MigrationStatus::Pending`].
85    async fn get_migration_with_status_read_only<C>(&self, db: &C) -> Result<Vec<Migration>, DbErr>
86    where
87        C: ConnectionTrait,
88    {
89        get_migration_with_status(
90            self.get_migration_files(),
91            get_migration_models_read_only(db, self.migration_table_name()).await?,
92        )
93    }
94
95    /// Get list of pending migrations without creating the migration table.
96    ///
97    /// The read-only counterpart of [`get_pending_migrations`](Self::get_pending_migrations);
98    /// see [`get_migration_with_status_read_only`](Self::get_migration_with_status_read_only).
99    async fn get_pending_migrations_read_only<C>(&self, db: &C) -> Result<Vec<Migration>, DbErr>
100    where
101        C: ConnectionTrait,
102    {
103        Ok(self
104            .get_migration_with_status_read_only(db)
105            .await?
106            .into_iter()
107            .filter(|file| file.status == MigrationStatus::Pending)
108            .collect())
109    }
110
111    /// Get list of applied migrations without creating the migration table.
112    ///
113    /// The read-only counterpart of [`get_applied_migrations`](Self::get_applied_migrations);
114    /// see [`get_migration_with_status_read_only`](Self::get_migration_with_status_read_only).
115    async fn get_applied_migrations_read_only<C>(&self, db: &C) -> Result<Vec<Migration>, DbErr>
116    where
117        C: ConnectionTrait,
118    {
119        Ok(self
120            .get_migration_with_status_read_only(db)
121            .await?
122            .into_iter()
123            .filter(|file| file.status == MigrationStatus::Applied)
124            .collect())
125    }
126
127    /// Create migration table `seaql_migrations` in the database
128    async fn install<C>(&self, db: &C) -> Result<(), DbErr>
129    where
130        C: ConnectionTrait,
131    {
132        install(db, self.migration_table_name()).await
133    }
134
135    /// Check the status of all migrations
136    async fn status<C>(&self, db: &C) -> Result<(), DbErr>
137    where
138        C: ConnectionTrait,
139    {
140        self.install(db).await?;
141
142        info!("Checking migration status");
143
144        for Migration { migration, status } in self.get_migration_with_status(db).await? {
145            info!("Migration '{}'... {}", migration.name(), status);
146        }
147
148        Ok(())
149    }
150
151    /// Drop all tables from the database, then reapply all migrations
152    async fn fresh<'c, C>(&self, db: C) -> Result<(), DbErr>
153    where
154        C: IntoSchemaManagerConnection<'c>,
155    {
156        let db = db.into_database_executor();
157        let manager = SchemaManager::new(db);
158        exec_fresh(self, &manager).await
159    }
160
161    /// Rollback all applied migrations, then reapply all migrations
162    async fn refresh<'c, C>(&self, db: C) -> Result<(), DbErr>
163    where
164        C: IntoSchemaManagerConnection<'c>,
165    {
166        let db = db.into_database_executor();
167        let manager = SchemaManager::new(db);
168        exec_down(self, &manager, None).await?;
169        exec_up(self, &manager, None).await
170    }
171
172    /// Rollback all applied migrations
173    async fn reset<'c, C>(&self, db: C) -> Result<(), DbErr>
174    where
175        C: IntoSchemaManagerConnection<'c>,
176    {
177        let db = db.into_database_executor();
178        let manager = SchemaManager::new(db);
179        exec_down(self, &manager, None).await?;
180        uninstall(&manager, self.migration_table_name()).await
181    }
182
183    /// Uninstall migration tracking table only (non-destructive)
184    /// This will drop the `seaql_migrations` table but won't rollback other schema changes.
185    async fn uninstall<'c, C>(&self, db: C) -> Result<(), DbErr>
186    where
187        C: IntoSchemaManagerConnection<'c>,
188    {
189        let db = db.into_database_executor();
190        let manager = SchemaManager::new(db);
191        uninstall(&manager, self.migration_table_name()).await
192    }
193
194    /// Apply pending migrations
195    async fn up<'c, C>(&self, db: C, steps: Option<u32>) -> Result<(), DbErr>
196    where
197        C: IntoSchemaManagerConnection<'c>,
198    {
199        let db = db.into_database_executor();
200        let manager = SchemaManager::new(db);
201        exec_up(self, &manager, steps).await
202    }
203
204    /// Rollback applied migrations
205    async fn down<'c, C>(&self, db: C, steps: Option<u32>) -> Result<(), DbErr>
206    where
207        C: IntoSchemaManagerConnection<'c>,
208    {
209        let db = db.into_database_executor();
210        let manager = SchemaManager::new(db);
211        exec_down(self, &manager, steps).await
212    }
213}
214
215#[async_trait::async_trait]
216impl<M> MigratorTraitSelf for M
217where
218    M: super::MigratorTrait + Sized + Send + Sync,
219{
220    fn migrations(&self) -> Vec<Box<dyn MigrationTrait>> {
221        M::migrations()
222    }
223
224    fn migration_table_name(&self) -> DynIden {
225        M::migration_table_name()
226    }
227
228    fn get_migration_files(&self) -> Vec<Migration> {
229        M::get_migration_files()
230    }
231
232    async fn get_migration_models<C>(&self, db: &C) -> Result<Vec<seaql_migrations::Model>, DbErr>
233    where
234        C: ConnectionTrait,
235    {
236        M::get_migration_models(db).await
237    }
238
239    async fn get_migration_with_status<C>(&self, db: &C) -> Result<Vec<Migration>, DbErr>
240    where
241        C: ConnectionTrait,
242    {
243        M::get_migration_with_status(db).await
244    }
245
246    async fn get_pending_migrations<C>(&self, db: &C) -> Result<Vec<Migration>, DbErr>
247    where
248        C: ConnectionTrait,
249    {
250        M::get_pending_migrations(db).await
251    }
252
253    async fn get_migration_with_status_read_only<C>(&self, db: &C) -> Result<Vec<Migration>, DbErr>
254    where
255        C: ConnectionTrait,
256    {
257        M::get_migration_with_status_read_only(db).await
258    }
259
260    async fn get_pending_migrations_read_only<C>(&self, db: &C) -> Result<Vec<Migration>, DbErr>
261    where
262        C: ConnectionTrait,
263    {
264        M::get_pending_migrations_read_only(db).await
265    }
266
267    async fn get_applied_migrations_read_only<C>(&self, db: &C) -> Result<Vec<Migration>, DbErr>
268    where
269        C: ConnectionTrait,
270    {
271        M::get_applied_migrations_read_only(db).await
272    }
273
274    async fn get_applied_migrations<C>(&self, db: &C) -> Result<Vec<Migration>, DbErr>
275    where
276        C: ConnectionTrait,
277    {
278        M::get_applied_migrations(db).await
279    }
280
281    async fn install<C>(&self, db: &C) -> Result<(), DbErr>
282    where
283        C: ConnectionTrait,
284    {
285        M::install(db).await
286    }
287
288    /// Check the status of all migrations
289    async fn status<C>(&self, db: &C) -> Result<(), DbErr>
290    where
291        C: ConnectionTrait,
292    {
293        M::status(db).await
294    }
295
296    async fn fresh<'c, C>(&self, db: C) -> Result<(), DbErr>
297    where
298        C: IntoSchemaManagerConnection<'c>,
299    {
300        M::fresh(db).await
301    }
302
303    async fn refresh<'c, C>(&self, db: C) -> Result<(), DbErr>
304    where
305        C: IntoSchemaManagerConnection<'c>,
306    {
307        M::refresh(db).await
308    }
309
310    async fn reset<'c, C>(&self, db: C) -> Result<(), DbErr>
311    where
312        C: IntoSchemaManagerConnection<'c>,
313    {
314        M::reset(db).await
315    }
316
317    async fn uninstall<'c, C>(&self, db: C) -> Result<(), DbErr>
318    where
319        C: IntoSchemaManagerConnection<'c>,
320    {
321        M::uninstall(db).await
322    }
323
324    async fn up<'c, C>(&self, db: C, steps: Option<u32>) -> Result<(), DbErr>
325    where
326        C: IntoSchemaManagerConnection<'c>,
327    {
328        M::up(db, steps).await
329    }
330
331    async fn down<'c, C>(&self, db: C, steps: Option<u32>) -> Result<(), DbErr>
332    where
333        C: IntoSchemaManagerConnection<'c>,
334    {
335        M::down(db, steps).await
336    }
337}
338
339async fn exec_fresh<M>(migrator: &M, manager: &SchemaManager<'_>) -> Result<(), DbErr>
340where
341    M: MigratorTraitSelf,
342{
343    let db = manager.get_connection();
344
345    migrator.install(db).await?;
346
347    drop_everything(db).await?;
348
349    exec_up(migrator, manager, None).await
350}
351
352async fn exec_up<M>(
353    migrator: &M,
354    manager: &SchemaManager<'_>,
355    steps: Option<u32>,
356) -> Result<(), DbErr>
357where
358    M: MigratorTraitSelf,
359{
360    let db = manager.get_connection();
361
362    migrator.install(db).await?;
363
364    exec_up_with(
365        manager,
366        steps,
367        migrator.get_pending_migrations(db).await?,
368        migrator.migration_table_name(),
369    )
370    .await
371}
372
373async fn exec_down<M>(
374    migrator: &M,
375    manager: &SchemaManager<'_>,
376    steps: Option<u32>,
377) -> Result<(), DbErr>
378where
379    M: MigratorTraitSelf,
380{
381    let db = manager.get_connection();
382
383    migrator.install(db).await?;
384
385    exec_down_with(
386        manager,
387        steps,
388        migrator.get_applied_migrations(db).await?,
389        migrator.migration_table_name(),
390    )
391    .await
392}