fix(db): keep migration checksums stable
ci / web (push) Successful in 38s
ci / rust (push) Successful in 1m4s
e2e / e2e (push) Successful in 1m14s

Issue #155 removed the sqlx 0.8 rebuild workarounds by editing migrations
0007, 0014 and 0021 in place. Editing an applied migration changes its
checksum, and `migrate()` refuses to run when one no longer matches what
`_sqlx_migrations` recorded, so the daemon exited on startup against any
database that had already applied them — production included.

The sqlx 0.9 bump is the fix and survives: a new migration can carry
`-- no-transaction` so `PRAGMA foreign_keys = OFF` holds and a table
rebuild stops cascade-deleting its children. Only the retroactive cleanup
of migrations that already ran is reverted, along with #153's
`CHECK (title <> '')`, which rode on the 0021 edit and needs a migration
of its own rather than a rewrite of history.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Miguel Palhas
2026-08-24 19:14:42 +01:00
parent 83a0b247c9
commit a3897df0ab
4 changed files with 21 additions and 106 deletions
-49
View File
@@ -419,55 +419,6 @@ mod tests {
);
}
/// #153: an empty episode title takes the TBA placeholder during 0021's
/// backfill, and the rebuilt table's CHECK rejects new empty titles.
#[tokio::test]
async fn empty_episode_titles_are_backfilled_and_then_rejected() {
let dir = tempfile::tempdir().expect("tempdir");
let db = Db::connect(dir.path().join("arr.db"))
.await
.expect("connect");
MIGRATOR
.run_to(20, db.pool())
.await
.expect("migrations before the #153 backfill");
let series_id = sqlx::query(
"INSERT INTO series (tmdb_id, title, root_id)
SELECT 82728, 'Bluey', id FROM roots WHERE kind = 'tv' LIMIT 1",
)
.execute(db.pool())
.await
.expect("series")
.last_insert_rowid();
let season_id = sqlx::query("INSERT INTO seasons (series_id, number) VALUES (?, 1)")
.bind(series_id)
.execute(db.pool())
.await
.expect("season")
.last_insert_rowid();
sqlx::query("INSERT INTO episodes (season_id, number, title) VALUES (?, 1, '')")
.bind(season_id)
.execute(db.pool())
.await
.expect("empty title, valid before 0021");
db.migrate().await.expect("remaining migrations");
let title: String = sqlx::query_scalar("SELECT title FROM episodes")
.fetch_one(db.pool())
.await
.expect("episode row survives the rebuild");
assert_eq!(title, "TBA");
sqlx::query("INSERT INTO episodes (season_id, number, title) VALUES (?, 2, '')")
.bind(season_id)
.execute(db.pool())
.await
.expect_err("the rebuilt column rejects the empty string");
}
#[tokio::test]
async fn seeds_two_tv_roots_with_distinct_policies() {
let (_dir, db) = fresh().await;