Files
arr/crates/arr-db/migrations/0014_parked_state.sql
T
Miguel PalhasandClaude Fable 5 ef4722e1dd fix(db): bump sqlx to 0.9, honour no-transaction
sqlx-sqlite 0.8 ignored Migration.no_tx and wrapped every
migration in a transaction, where PRAGMA foreign_keys = OFF
is a no-op — so any table rebuild cascade-deleted children.
0.9 honours `-- no-transaction`, so 0014 drops its
movie_releases_backup / episode_releases_backup workaround
and runs the plain rebuild recipe with foreign keys off.
A migration test rebuilds both parents from a pre-0014
database and asserts the child link rows survive.

Closes #155

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-24 18:10:53 +01:00

105 lines
4.0 KiB
SQL

-- no-transaction
-- #108: a vanished torrent must not re-grab. Overrides #86, which reopened
-- the gap (state = 'missing', wanted untouched) so the next tick re-grabbed
-- the same release. Adds 'parked' so the daemon can clear `wanted` and mark
-- the title honestly instead — distinct from 'missing' (an open gap) and
-- 'available' (satisfied). SQLite cannot alter a CHECK, so both tables are
-- rebuilt (see 0007).
--
-- #155: runs outside a transaction so `PRAGMA foreign_keys = OFF` takes
-- effect and dropping the old tables does not cascade into their children.
PRAGMA foreign_keys = OFF;
CREATE TABLE movies_new (
id INTEGER PRIMARY KEY,
tmdb_id INTEGER NOT NULL UNIQUE,
title TEXT NOT NULL,
year INTEGER,
original_language TEXT,
root_id INTEGER NOT NULL REFERENCES roots (id),
wanted INTEGER NOT NULL DEFAULT 1 CHECK (wanted IN (0, 1)),
overrides TEXT NOT NULL DEFAULT '{}' CHECK (json_valid(overrides)),
state TEXT NOT NULL DEFAULT 'missing'
CHECK (state IN ('missing', 'downloading', 'available', 'parked')),
blocked INTEGER NOT NULL DEFAULT 0 CHECK (blocked IN (0, 1)),
search_attempts INTEGER NOT NULL DEFAULT 0,
last_searched_at TEXT,
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
digital_release TEXT,
metadata_refreshed_at TEXT,
imdb_id TEXT
) STRICT;
INSERT INTO movies_new (
id, tmdb_id, title, year, original_language, root_id, wanted, overrides,
state, blocked, search_attempts, last_searched_at, created_at, updated_at,
digital_release, metadata_refreshed_at, imdb_id
)
SELECT
id, tmdb_id, title, year, original_language, root_id, wanted, overrides,
state, blocked, search_attempts, last_searched_at, created_at, updated_at,
digital_release, metadata_refreshed_at, imdb_id
FROM movies;
DROP TABLE movies;
ALTER TABLE movies_new RENAME TO movies;
CREATE INDEX movies_pending_search
ON movies (last_searched_at)
WHERE wanted = 1 AND blocked = 0;
CREATE INDEX movies_state ON movies (state);
CREATE INDEX movies_root ON movies (root_id);
CREATE TRIGGER movies_require_movie_root
BEFORE INSERT ON movies
WHEN EXISTS (SELECT 1 FROM roots WHERE id = NEW.root_id AND kind != 'movie')
BEGIN
SELECT RAISE(ABORT, 'movies require a movie root');
END;
CREATE TRIGGER movies_require_movie_root_on_update
BEFORE UPDATE OF root_id ON movies
WHEN EXISTS (SELECT 1 FROM roots WHERE id = NEW.root_id AND kind != 'movie')
BEGIN
SELECT RAISE(ABORT, 'movies require a movie root');
END;
CREATE TABLE episodes_new (
id INTEGER PRIMARY KEY,
season_id INTEGER NOT NULL REFERENCES seasons (id) ON DELETE CASCADE,
number INTEGER NOT NULL CHECK (number >= 0),
title TEXT NOT NULL,
air_date TEXT,
wanted INTEGER NOT NULL DEFAULT 0 CHECK (wanted IN (0, 1)),
state TEXT NOT NULL DEFAULT 'missing'
CHECK (state IN ('missing', 'downloading', 'available', 'parked')),
search_attempts INTEGER NOT NULL DEFAULT 0,
last_searched_at TEXT,
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
UNIQUE (season_id, number)
) STRICT;
INSERT INTO episodes_new (
id, season_id, number, title, air_date, wanted, state, search_attempts,
last_searched_at, created_at, updated_at
)
SELECT
id, season_id, number, title, air_date, wanted, state, search_attempts,
last_searched_at, created_at, updated_at
FROM episodes;
DROP TABLE episodes;
ALTER TABLE episodes_new RENAME TO episodes;
CREATE INDEX episodes_pending_search
ON episodes (last_searched_at)
WHERE wanted = 1 AND state = 'missing';
CREATE INDEX episodes_state ON episodes (state);
PRAGMA foreign_keys = ON;