From e1ed87557b0fa6ce629af011920c6bb353816c7b Mon Sep 17 00:00:00 2001 From: Markus Gasser Date: Sun, 14 Sep 2025 19:41:15 +0200 Subject: [PATCH 1/2] Fix migration reverts for no-TX SQLite --- sqlx-sqlite/src/migrate.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sqlx-sqlite/src/migrate.rs b/sqlx-sqlite/src/migrate.rs index 7eb8cc1ca9..dd9611b873 100644 --- a/sqlx-sqlite/src/migrate.rs +++ b/sqlx-sqlite/src/migrate.rs @@ -207,7 +207,7 @@ CREATE TABLE IF NOT EXISTS {table_name} ( let start = Instant::now(); if migration.no_tx { - execute_migration(self, table_name, migration).await?; + revert_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. From 1ee9ca691a72aec9b9661f08493facd0554dabea Mon Sep 17 00:00:00 2001 From: Markus Gasser Date: Sun, 14 Sep 2025 19:42:50 +0200 Subject: [PATCH 2/2] Add regression test --- tests/sqlite/migrate.rs | 23 +++++++++++++++++++ .../0_vacuum.down.sql | 3 +++ .../0_vacuum.up.sql | 3 +++ 3 files changed, 29 insertions(+) create mode 100644 tests/sqlite/migrations_no_tx_reversible/0_vacuum.down.sql create mode 100644 tests/sqlite/migrations_no_tx_reversible/0_vacuum.up.sql diff --git a/tests/sqlite/migrate.rs b/tests/sqlite/migrate.rs index a2315af284..ed5835225c 100644 --- a/tests/sqlite/migrate.rs +++ b/tests/sqlite/migrate.rs @@ -77,6 +77,29 @@ async fn no_tx(mut conn: PoolConnection) -> anyhow::Result<()> { Ok(()) } +#[sqlx::test(migrations = false)] +async fn no_tx_reversible(mut conn: PoolConnection) -> anyhow::Result<()> { + clean_up(&mut conn).await?; + + let migrator = Migrator::new(Path::new("tests/sqlite/migrations_no_tx_reversible")).await?; + + // run migration + migrator.run(&mut conn).await?; + + // check outcome + let res: String = conn.fetch_one("PRAGMA JOURNAL_MODE").await?.get(0); + assert_eq!(res, "wal".to_string()); + + // roll back + migrator.undo(&mut conn, -1).await?; + + // check outcome + let res: String = conn.fetch_one("PRAGMA JOURNAL_MODE").await?.get(0); + assert_eq!(res, "delete".to_string()); + + 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(); diff --git a/tests/sqlite/migrations_no_tx_reversible/0_vacuum.down.sql b/tests/sqlite/migrations_no_tx_reversible/0_vacuum.down.sql new file mode 100644 index 0000000000..46a2dfbcc9 --- /dev/null +++ b/tests/sqlite/migrations_no_tx_reversible/0_vacuum.down.sql @@ -0,0 +1,3 @@ +-- no-transaction + +PRAGMA JOURNAL_MODE = DELETE; diff --git a/tests/sqlite/migrations_no_tx_reversible/0_vacuum.up.sql b/tests/sqlite/migrations_no_tx_reversible/0_vacuum.up.sql new file mode 100644 index 0000000000..4014bc92a6 --- /dev/null +++ b/tests/sqlite/migrations_no_tx_reversible/0_vacuum.up.sql @@ -0,0 +1,3 @@ +-- no-transaction + +PRAGMA JOURNAL_MODE = WAL;