diff --git a/sqlx-core/src/migrate/migration_type.rs b/sqlx-core/src/migrate/migration_type.rs index 350ddb3f27..401e6c3555 100644 --- a/sqlx-core/src/migrate/migration_type.rs +++ b/sqlx-core/src/migrate/migration_type.rs @@ -74,6 +74,14 @@ impl MigrationType { } } + /// Ordering helper to sort ups before downs when versions tie. + pub fn direction_order(&self) -> u8 { + match self { + MigrationType::ReversibleDown => 1, + MigrationType::Simple | MigrationType::ReversibleUp => 0, + } + } + #[deprecated = "unused"] pub fn infer(migrator: &Migrator, reversible: bool) -> MigrationType { match migrator.iter().last() { diff --git a/sqlx-core/src/migrate/migrator.rs b/sqlx-core/src/migrate/migrator.rs index 53295c92d0..1c09ca880b 100644 --- a/sqlx-core/src/migrate/migrator.rs +++ b/sqlx-core/src/migrate/migrator.rs @@ -87,8 +87,14 @@ impl Migrator { /// let m = Migrator::with_migrations(migrations); /// ``` pub fn with_migrations(mut migrations: Vec) -> Self { - // Ensure that we are sorted by version in ascending order. - migrations.sort_by_key(|m| m.version); + // Ensure deterministic order: version ascending, then up before down when versions match. + migrations.sort_by(|a, b| { + a.version.cmp(&b.version).then_with(|| { + a.migration_type + .direction_order() + .cmp(&b.migration_type.direction_order()) + }) + }); Self { migrations: Cow::Owned(migrations), ..Self::DEFAULT diff --git a/sqlx-core/src/migrate/source.rs b/sqlx-core/src/migrate/source.rs index 4648e53f1e..9eb99e18fd 100644 --- a/sqlx-core/src/migrate/source.rs +++ b/sqlx-core/src/migrate/source.rs @@ -248,8 +248,14 @@ pub fn resolve_blocking_with_config( )); } - // Ensure that we are sorted by version in ascending order. - migrations.sort_by_key(|(m, _)| m.version); + // Ensure deterministic order: version ascending, then up before down when versions match. + migrations.sort_by(|(a, _), (b, _)| { + a.version.cmp(&b.version).then_with(|| { + a.migration_type + .direction_order() + .cmp(&b.migration_type.direction_order()) + }) + }); Ok(migrations) }