diff --git a/sqlx-core/src/migrate/migrator.rs b/sqlx-core/src/migrate/migrator.rs index 3209ba6e45..cf7b9886c3 100644 --- a/sqlx-core/src/migrate/migrator.rs +++ b/sqlx-core/src/migrate/migrator.rs @@ -110,6 +110,24 @@ impl Migrator { self.iter().any(|m| m.version == version) } + /// Get the latest version of the applied migrations. + pub async fn latest_version<'a, A>(&self, migrator: A) -> Result, MigrateError> + where + A: Acquire<'a>, + ::Target: Migrate, + { + let mut conn = migrator.acquire().await?; + + conn.ensure_migrations_table().await?; + let applied_migrations = conn.list_applied_migrations().await?; + let latest_version = applied_migrations + .iter() + .max_by(|x, y| x.version.cmp(&y.version)) + .map(|migration| migration.version); + + Ok(latest_version) + } + /// Run any pending migrations against the database; and, validate previously applied migrations /// against the current migration source to detect accidental changes in previously-applied migrations. /// @@ -255,4 +273,73 @@ impl Migrator { Ok(()) } + + /// Run up migrations against the database until a specific version. + /// + /// # Examples + /// + /// ```rust,no_run + /// # use sqlx::migrate::MigrateError; + /// # fn main() -> Result<(), MigrateError> { + /// # sqlx::__rt::test_block_on(async move { + /// use sqlx::migrate::Migrator; + /// use sqlx::sqlite::SqlitePoolOptions; + /// + /// let m = Migrator::new(std::path::Path::new("./migrations")).await?; + /// let pool = SqlitePoolOptions::new().connect("sqlite::memory:").await?; + /// m.run_through_version(&pool, 4).await + /// # }) + /// # } + /// ``` + pub async fn run_through_version<'a, A>( + &self, + migrator: A, + target: i64, + ) -> Result<(), MigrateError> + where + A: Acquire<'a>, + ::Target: Migrate, + { + let mut conn = migrator.acquire().await?; + + // lock the database for exclusive access by the migrator + if self.locking { + conn.lock().await?; + } + + // creates [_migrations] table only if needed + // eventually this will likely migrate previous versions of the table + conn.ensure_migrations_table().await?; + + let version = conn.dirty_version().await?; + if let Some(version) = version { + return Err(MigrateError::Dirty(version)); + } + + let applied_migrations = conn.list_applied_migrations().await?; + validate_applied_migrations(&applied_migrations, self)?; + + let applied_migrations: HashMap<_, _> = applied_migrations + .into_iter() + .map(|m| (m.version, m)) + .collect(); + + for migration in self + .iter() + .rev() + .filter(|m| m.migration_type.is_up_migration()) + .filter(|m| applied_migrations.contains_key(&m.version)) + .filter(|m| m.version <= target) + { + conn.apply(migration).await?; + } + + // unlock the migrator to allow other migrators to run + // but do nothing as we already migrated + if self.locking { + conn.unlock().await?; + } + + Ok(()) + } } diff --git a/tests/mysql/migrate.rs b/tests/mysql/migrate.rs index 97caa38005..3ebdbc2afc 100644 --- a/tests/mysql/migrate.rs +++ b/tests/mysql/migrate.rs @@ -33,6 +33,11 @@ async fn reversible(mut conn: PoolConnection) -> anyhow::Result<()> { let migrator = Migrator::new(Path::new("tests/mysql/migrations_reversible")).await?; + // run only until first reversible migration + migrator + .run_through_version(&mut conn, 20220721124650) + .await?; + // run migration migrator.run(&mut conn).await?; @@ -43,6 +48,9 @@ async fn reversible(mut conn: PoolConnection) -> anyhow::Result<()> { .get(0); assert_eq!(res, 101); + let latest_version = migrator.latest_version(&mut conn).await?.unwrap(); + assert_eq!(latest_version, 20220721125033); + // roll back nothing (last version) migrator.undo(&mut conn, 20220721125033).await?; diff --git a/tests/postgres/migrate.rs b/tests/postgres/migrate.rs index 636dffe860..c5f1837964 100644 --- a/tests/postgres/migrate.rs +++ b/tests/postgres/migrate.rs @@ -33,8 +33,15 @@ async fn reversible(mut conn: PoolConnection) -> anyhow::Result<()> { let migrator = Migrator::new(Path::new("tests/postgres/migrations_reversible")).await?; + // run only until first reversible migration + migrator + .run_through_version(&mut conn, 20220721124650) + .await?; + // run migration - migrator.run(&mut conn).await?; + migrator + .run_through_version(&mut conn, 20220721125033) + .await?; // check outcome let res: i64 = conn @@ -43,6 +50,9 @@ async fn reversible(mut conn: PoolConnection) -> anyhow::Result<()> { .get(0); assert_eq!(res, 101); + let latest_version = migrator.latest_version(&mut conn).await?.unwrap(); + assert_eq!(latest_version, 20220721125033); + // roll back nothing (last version) migrator.undo(&mut conn, 20220721125033).await?; diff --git a/tests/sqlite/migrate.rs b/tests/sqlite/migrate.rs index 19e8690f9a..02ea95c3bd 100644 --- a/tests/sqlite/migrate.rs +++ b/tests/sqlite/migrate.rs @@ -33,6 +33,11 @@ async fn reversible(mut conn: PoolConnection) -> anyhow::Result<()> { let migrator = Migrator::new(Path::new("tests/sqlite/migrations_reversible")).await?; + // run only until first reversible migration + migrator + .run_through_version(&mut conn, 20220721124650) + .await?; + // run migration migrator.run(&mut conn).await?; @@ -43,6 +48,9 @@ async fn reversible(mut conn: PoolConnection) -> anyhow::Result<()> { .get(0); assert_eq!(res, 101); + let latest_version = migrator.latest_version(&mut conn).await?.unwrap(); + assert_eq!(latest_version, 20220721125033); + // roll back nothing (last version) migrator.undo(&mut conn, 20220721125033).await?;

AltStyle によって変換されたページ (->オリジナル) /