Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion sqlx-postgres/src/migrate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ CREATE TABLE IF NOT EXISTS {table_name} (
if migration.no_tx {
revert_migration(self, table_name, migration).await?;
} else {
// Use a single transaction for the actual migration script and the essential bookeeping so we never
// Use a single transaction for the actual migration script and the essential bookkeeping so we never
// execute migrations twice. See https://github.com/launchbadge/sqlx/issues/1966.
let mut tx = self.begin().await?;
revert_migration(&mut tx, table_name, migration).await?;
Expand Down
113 changes: 72 additions & 41 deletions sqlx-sqlite/src/migrate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,41 +160,27 @@ CREATE TABLE IF NOT EXISTS {table_name} (
migration: &'e Migration,
) -> BoxFuture<'e, Result<Duration, MigrateError>> {
Box::pin(async move {
let mut tx = self.begin().await?;
let start = Instant::now();

// Use a single transaction for the actual migration script and the essential bookeeping so we never
// execute migrations twice. See https://github.com/launchbadge/sqlx/issues/1966.
// The `execution_time` however can only be measured for the whole transaction. This value _only_ exists for
// data lineage and debugging reasons, so it is not super important if it is lost. So we initialize it to -1
// and update it once the actual transaction completed.
let _ = tx
.execute(migration.sql.clone())
.await
.map_err(|e| MigrateError::ExecuteMigration(e, migration.version))?;

// language=SQL
let _ = query(AssertSqlSafe(format!(
r#"
INSERT INTO {table_name} ( version, description, success, checksum, execution_time )
VALUES ( ?1, ?2, TRUE, ?3, -1 )
"#
)))
.bind(migration.version)
.bind(&*migration.description)
.bind(&*migration.checksum)
.execute(&mut *tx)
.await?;

tx.commit().await?;
if migration.no_tx {
execute_migration(self, table_name, migration).await?;
} else {
// Use a single transaction for the actual migration script and the essential bookkeeping so we never
// execute migrations twice. See https://github.com/launchbadge/sqlx/issues/1966.
// The `execution_time` however can only be measured for the whole transaction. This value _only_ exists for
// data lineage and debugging reasons, so it is not super important if it is lost. So we initialize it to -1
// and update it once the actual transaction completed.
let mut tx = self.begin().await?;
execute_migration(&mut tx, table_name, migration).await?;
tx.commit().await?;
}

// Update `elapsed_time`.
// NOTE: The process may disconnect/die at this point, so the elapsed time value might be lost. We accept
// this small risk since this value is not super important.

let elapsed = start.elapsed();

// language=SQL
// language=SQLite
#[allow(clippy::cast_possible_truncation)]
let _ = query(AssertSqlSafe(format!(
r#"
Expand All @@ -218,26 +204,71 @@ CREATE TABLE IF NOT EXISTS {table_name} (
migration: &'e Migration,
) -> BoxFuture<'e, Result<Duration, MigrateError>> {
Box::pin(async move {
// Use a single transaction for the actual migration script and the essential bookeeping so we never
// execute migrations twice. See https://github.com/launchbadge/sqlx/issues/1966.
let mut tx = self.begin().await?;
let start = Instant::now();

let _ = tx.execute(migration.sql.clone()).await?;

// language=SQLite
let _ = query(AssertSqlSafe(format!(
r#"DELETE FROM {table_name} WHERE version = ?1"#
)))
.bind(migration.version)
.execute(&mut *tx)
.await?;

tx.commit().await?;
if migration.no_tx {
execute_migration(self, table_name, migration).await?;
} else {
// Use a single transaction for the actual migration script and the essential bookkeeping so we never
// execute migrations twice. See https://github.com/launchbadge/sqlx/issues/1966.
let mut tx = self.begin().await?;
revert_migration(&mut tx, table_name, migration).await?;
tx.commit().await?;
}

let elapsed = start.elapsed();

Ok(elapsed)
})
}
}

async fn execute_migration(
conn: &mut SqliteConnection,
table_name: &str,
migration: &Migration,
) -> Result<(), MigrateError> {
let _ = conn
.execute(migration.sql.clone())
.await
.map_err(|e| MigrateError::ExecuteMigration(e, migration.version))?;

// language=SQLite
let _ = query(AssertSqlSafe(format!(
r#"
INSERT INTO {table_name} ( version, description, success, checksum, execution_time )
VALUES ( ?1, ?2, TRUE, ?3, -1 )
"#
)))
.bind(migration.version)
.bind(&*migration.description)
.bind(&*migration.checksum)
.execute(conn)
.await?;

Ok(())
}

async fn revert_migration(
conn: &mut SqliteConnection,
table_name: &str,
migration: &Migration,
) -> Result<(), MigrateError> {
let _ = conn
.execute(migration.sql.clone())
.await
.map_err(|e| MigrateError::ExecuteMigration(e, migration.version))?;

// language=SQLite
let _ = query(AssertSqlSafe(format!(
r#"
DELETE FROM {table_name}
WHERE version = ?1
"#
)))
.bind(migration.version)
.execute(conn)
.await?;

Ok(())
}
11 changes: 11 additions & 0 deletions tests/sqlite/migrate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,17 @@ async fn reversible(mut conn: PoolConnection<Sqlite>) -> anyhow::Result<()> {
Ok(())
}

#[sqlx::test(migrations = false)]
async fn no_tx(mut conn: PoolConnection<Sqlite>) -> anyhow::Result<()> {
clean_up(&mut conn).await?;
let migrator = Migrator::new(Path::new("tests/sqlite/migrations_no_tx")).await?;

// run migration
migrator.run(&mut conn).await?;

Ok(())
}

/// Ensure that we have a clean initial state.
async fn clean_up(conn: &mut SqliteConnection) -> anyhow::Result<()> {
conn.execute("DROP TABLE migrations_simple_test").await.ok();
Expand Down
3 changes: 3 additions & 0 deletions tests/sqlite/migrations_no_tx/0_vacuum.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
-- no-transaction

VACUUM;
Loading