-- 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 );