Files
silo-server/migrations/sql/20260728132326_jellycompat_displayprefs.sql
T
QuickandClaude Fable 5 440a75b189 refactor(jellycompat): give DisplayPreferences its own table
The Jellyfin DisplayPreferences blobs rode the legacy user_settings
key/value table under synthetic jellycompat:* keys, which forced the
legacy settings API to carry a prefix carve-out in its otherwise-closed
unknown-key gate. They are the compat subsystem's storage, not user
settings: the contract neither validates nor resolves them.

Move them to a dedicated jellycompat_displayprefs table in both
backends, keyed by (prefs id, client) per user, with the blob stored as
opaque text served back byte-for-byte (deliberately not jsonb, which
would re-serialize it). The data-copy migrations — per-user SQLite V16
and a paired SQL + Go goose migration for Postgres — are transactional
and harmless to re-run, and both drive their key parsing and row
classification from the new internal/jellycompat/displayprefs package
so the backends cannot diverge, following the internal/settingsmigrate
precedent. A jellycompat:* row that does not parse as a DisplayPrefs
key (only ever writable through the removed carve-out) is recorded in
user_setting_migration_rejects rather than silently deleted.

With the last non-settings tenant gone, the jellycompatSettingPrefix
carve-out is deleted: the legacy settings endpoints now refuse
jellycompat:* keys like any other unknown key and never surface them.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-28 13:32:41 +00:00

38 lines
1.6 KiB
SQL

-- Dedicated home for Jellyfin DisplayPreferences blobs, which used to ride
-- user_settings under synthetic jellycompat:* keys. They are the jellycompat
-- subsystem's storage rather than user settings — the settings contract
-- neither validates nor resolves them — so rehoming them removes the last
-- non-settings tenant of the legacy key/value table and lets the legacy
-- settings API drop its jellycompat carve-out.
--
-- value is text, not jsonb, deliberately: the blob is opaque Jellyfin client
-- JSON served back byte-for-byte, and jsonb would re-serialize it (reordering
-- keys, normalizing numbers). This table stores what the client sent; it does
-- not interpret it. The per-user SQLite store carries the same table with
-- user_id omitted.
--
-- The data copy out of user_settings is the companion Go migration
-- (jellycompat_displayprefs_move in internal/database): the key parsing is
-- shared with the SQLite backend through internal/jellycompat/displayprefs,
-- which SQL cannot express without duplicating those rules.
-- +goose Up
-- +goose StatementBegin
CREATE TABLE public.jellycompat_displayprefs (
user_id integer NOT NULL,
prefs_id text NOT NULL,
client text NOT NULL,
value text NOT NULL,
updated_at timestamptz NOT NULL DEFAULT now(),
CONSTRAINT jellycompat_displayprefs_pkey
PRIMARY KEY (user_id, prefs_id, client),
CONSTRAINT jellycompat_displayprefs_user_id_fkey
FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE
);
-- +goose StatementEnd
-- +goose Down
-- +goose StatementBegin
DROP TABLE IF EXISTS public.jellycompat_displayprefs;
-- +goose StatementEnd