c4d4ade4da
#243 normalised the incoming path but compared it against the value read raw from the database, so a root stored with a trailing separator never compared equal. Every edit of it -- a policy change included -- took the relocation branch, where each planned destination is its own source and the pre-check refuses. That root could not be edited at all. `update` now normalises both sides, and hands `relocate_root` the normalised stored value. `path_is_free` normalises the stored side in SQL and `create` goes through it too, so `/mnt/x` and `/mnt/x/` cannot be two roots for one directory -- the unique index compares raw strings and cannot see that. Migration 0031 strips the separator from rows already written. It skips any row whose stripped form another row would also hold, rather than tripping the unique index: a migration that cannot apply stops the daemon booting, which is worse than two roots naming one directory. Also from the same review: `undo` recorded only the leaf directory, so a failed move into `/mnt/media-v2/tv/kids` left `tv` behind. It now records every level `create_dir_all` materialised, deepest first, and still never touches one that was already on disk. Refs #244. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
35 lines
1.9 KiB
SQL
35 lines
1.9 KiB
SQL
-- Issue #244. A root's path could be stored with a trailing separator until
|
|
-- #243 normalised the incoming value. `roots::update` normalises the payload
|
|
-- and compares it against the stored value, so such a row never compares
|
|
-- equal: every edit, a policy change included, takes the relocation branch,
|
|
-- and there every planned destination is its own source. Normalising the
|
|
-- payload alone fixed the half that cannot bite; this is the other half.
|
|
--
|
|
-- `rtrim` strips every trailing separator at once, so '/mnt/x//' normalises
|
|
-- in one pass. A bare '/' rtrims to the empty string and is put back, which
|
|
-- is what `normalize_path` in arr-api does.
|
|
--
|
|
-- Guarded, because `roots.path` is UNIQUE and a migration that cannot apply
|
|
-- stops the daemon booting -- worse than the bug it fixes. A row is
|
|
-- normalised only when no other row shares its normalised path: neither a
|
|
-- row already holding the stripped value, nor another trailing-separator row
|
|
-- that would strip to the same thing. Every row in such a group is left
|
|
-- exactly as it is. That leaves two roots naming one directory, which is a
|
|
-- settings mistake for the operator to resolve by hand, not a reason to
|
|
-- refuse to boot.
|
|
--
|
|
-- This cannot introduce a collision either. An updated row's new value is a
|
|
-- normalised path no other row normalises to, and a row left alone whose raw
|
|
-- path equalled that value would have had the same normalised path, which is
|
|
-- the case the guard excludes.
|
|
UPDATE roots
|
|
SET path = CASE WHEN rtrim(path, '/') = '' THEN '/' ELSE rtrim(path, '/') END
|
|
WHERE path <> CASE WHEN rtrim(path, '/') = '' THEN '/' ELSE rtrim(path, '/') END
|
|
AND NOT EXISTS (
|
|
SELECT 1
|
|
FROM roots AS other
|
|
WHERE other.id <> roots.id
|
|
AND CASE WHEN rtrim(other.path, '/') = '' THEN '/' ELSE rtrim(other.path, '/') END
|
|
= CASE WHEN rtrim(roots.path, '/') = '' THEN '/' ELSE rtrim(roots.path, '/') END
|
|
);
|