diff --git a/sqlx-core/src/any/migrate.rs b/sqlx-core/src/any/migrate.rs index 764991b5..7a91c50c 100644 --- a/sqlx-core/src/any/migrate.rs +++ b/sqlx-core/src/any/migrate.rs @@ -80,6 +80,22 @@ impl Migrate for AnyConnection { } } + fn version(&mut self) -> BoxFuture<'_, Result, MigrateError>> { + match &mut self.0 { + #[cfg(feature = "postgres")] + AnyConnectionKind::Postgres(conn) => conn.version(), + + #[cfg(feature = "sqlite")] + AnyConnectionKind::Sqlite(conn) => conn.version(), + + #[cfg(feature = "mysql")] + AnyConnectionKind::MySql(conn) => conn.version(), + + #[cfg(feature = "mssql")] + AnyConnectionKind::Mssql(_conn) => unimplemented!(), + } + } + fn dirty_version(&mut self) -> BoxFuture<'_, Result, MigrateError>> { match &mut self.0 { #[cfg(feature = "postgres")] @@ -96,6 +112,28 @@ impl Migrate for AnyConnection { } } + fn validate<'e: 'm, 'm>( + &'e mut self, + migration: &'m Migration, + ) -> BoxFuture<'m, Result<(), MigrateError>> { + match &mut self.0 { + #[cfg(feature = "postgres")] + AnyConnectionKind::Postgres(conn) => conn.validate(migration), + + #[cfg(feature = "sqlite")] + AnyConnectionKind::Sqlite(conn) => conn.validate(migration), + + #[cfg(feature = "mysql")] + AnyConnectionKind::MySql(conn) => conn.validate(migration), + + #[cfg(feature = "mssql")] + AnyConnectionKind::Mssql(_conn) => { + let _ = migration; + unimplemented!() + } + } + } + fn list_applied_migrations( &mut self, ) -> BoxFuture<'_, Result, MigrateError>> { diff --git a/sqlx-core/src/migrate/migrate.rs b/sqlx-core/src/migrate/migrate.rs index a3958e15..3c3f4c36 100644 --- a/sqlx-core/src/migrate/migrate.rs +++ b/sqlx-core/src/migrate/migrate.rs @@ -27,6 +27,19 @@ pub trait Migrate { // "dirty" means there is a partially applied migration that failed. fn dirty_version(&mut self) -> BoxFuture<'_, Result, MigrateError>>; + // Return the current version and if the database is "dirty". + // "dirty" means there is a partially applied migration that failed. + #[deprecated] + fn version(&mut self) -> BoxFuture<'_, Result, MigrateError>>; + + // validate the migration + // checks that it does exist on the database and that the checksum matches + #[deprecated] + fn validate<'e: 'm, 'm>( + &'e mut self, + migration: &'m Migration, + ) -> BoxFuture<'m, Result<(), MigrateError>>; + // Return the ordered list of applied migrations fn list_applied_migrations( &mut self, diff --git a/sqlx-core/src/mysql/migrate.rs b/sqlx-core/src/mysql/migrate.rs index 66894b9c..248fd629 100644 --- a/sqlx-core/src/mysql/migrate.rs +++ b/sqlx-core/src/mysql/migrate.rs @@ -97,6 +97,19 @@ CREATE TABLE IF NOT EXISTS _sqlx_migrations ( }) } + fn version(&mut self) -> BoxFuture<'_, Result, MigrateError>> { + Box::pin(async move { + // language=SQL + let row = query_as( + "SELECT version, NOT success FROM _sqlx_migrations ORDER BY version DESC LIMIT 1", + ) + .fetch_optional(self) + .await?; + + Ok(row) + }) + } + fn dirty_version(&mut self) -> BoxFuture<'_, Result, MigrateError>> { Box::pin(async move { // language=SQL @@ -168,6 +181,30 @@ CREATE TABLE IF NOT EXISTS _sqlx_migrations ( }) } + fn validate<'e: 'm, 'm>( + &'e mut self, + migration: &'m Migration, + ) -> BoxFuture<'m, Result<(), MigrateError>> { + Box::pin(async move { + // language=SQL + let checksum: Option> = + query_scalar("SELECT checksum FROM _sqlx_migrations WHERE version = ?") + .bind(migration.version) + .fetch_optional(self) + .await?; + + if let Some(checksum) = checksum { + return if checksum == &*migration.checksum { + Ok(()) + } else { + Err(MigrateError::VersionMismatch(migration.version)) + }; + } else { + Err(MigrateError::VersionMissing(migration.version)) + } + }) + } + fn apply<'e: 'm, 'm>( &'e mut self, migration: &'m Migration, diff --git a/sqlx-core/src/postgres/migrate.rs b/sqlx-core/src/postgres/migrate.rs index b24966d2..142918a6 100644 --- a/sqlx-core/src/postgres/migrate.rs +++ b/sqlx-core/src/postgres/migrate.rs @@ -107,6 +107,19 @@ CREATE TABLE IF NOT EXISTS _sqlx_migrations ( }) } + fn version(&mut self) -> BoxFuture<'_, Result, MigrateError>> { + Box::pin(async move { + // language=SQL + let row = query_as( + "SELECT version, NOT success FROM _sqlx_migrations ORDER BY version DESC LIMIT 1", + ) + .fetch_optional(self) + .await?; + + Ok(row) + }) + } + fn dirty_version(&mut self) -> BoxFuture<'_, Result, MigrateError>> { Box::pin(async move { // language=SQL @@ -178,6 +191,30 @@ CREATE TABLE IF NOT EXISTS _sqlx_migrations ( }) } + fn validate<'e: 'm, 'm>( + &'e mut self, + migration: &'m Migration, + ) -> BoxFuture<'m, Result<(), MigrateError>> { + Box::pin(async move { + // language=SQL + let checksum: Option> = + query_scalar("SELECT checksum FROM _sqlx_migrations WHERE version = $1") + .bind(migration.version) + .fetch_optional(self) + .await?; + + if let Some(checksum) = checksum { + return if checksum == &*migration.checksum { + Ok(()) + } else { + Err(MigrateError::VersionMismatch(migration.version)) + }; + } else { + Err(MigrateError::VersionMissing(migration.version)) + } + }) + } + fn apply<'e: 'm, 'm>( &'e mut self, migration: &'m Migration, diff --git a/sqlx-core/src/sqlite/migrate.rs b/sqlx-core/src/sqlite/migrate.rs index 315c9345..6f9320d3 100644 --- a/sqlx-core/src/sqlite/migrate.rs +++ b/sqlx-core/src/sqlite/migrate.rs @@ -6,6 +6,7 @@ use crate::migrate::{AppliedMigration, Migration}; use crate::migrate::{Migrate, MigrateDatabase}; use crate::query::query; use crate::query_as::query_as; +use crate::query_scalar::query_scalar; use crate::sqlite::{Sqlite, SqliteConnectOptions, SqliteConnection}; use futures_core::future::BoxFuture; use sqlx_rt::fs; @@ -73,6 +74,19 @@ CREATE TABLE IF NOT EXISTS _sqlx_migrations ( }) } + fn version(&mut self) -> BoxFuture<'_, Result, MigrateError>> { + Box::pin(async move { + // language=SQLite + let row = query_as( + "SELECT version, NOT success FROM _sqlx_migrations ORDER BY version DESC LIMIT 1", + ) + .fetch_optional(self) + .await?; + + Ok(row) + }) + } + fn dirty_version(&mut self) -> BoxFuture<'_, Result, MigrateError>> { Box::pin(async move { // language=SQLite @@ -116,6 +130,30 @@ CREATE TABLE IF NOT EXISTS _sqlx_migrations ( Box::pin(async move { Ok(()) }) } + fn validate<'e: 'm, 'm>( + &'e mut self, + migration: &'m Migration, + ) -> BoxFuture<'m, Result<(), MigrateError>> { + Box::pin(async move { + // language=SQL + let checksum: Option> = + query_scalar("SELECT checksum FROM _sqlx_migrations WHERE version = ?1") + .bind(migration.version) + .fetch_optional(self) + .await?; + + if let Some(checksum) = checksum { + if checksum == &*migration.checksum { + Ok(()) + } else { + Err(MigrateError::VersionMismatch(migration.version)) + } + } else { + Err(MigrateError::VersionMissing(migration.version)) + } + }) + } + fn apply<'e: 'm, 'm>( &'e mut self, migration: &'m Migration,