Files

84 lines
4.7 KiB
SQL
Raw Permalink Normal View History

-- Reshape the downloads table for downloads v2 (offline sync for mobile).
--
-- One device-aware, format-aware downloads table carries both lifecycles:
-- * device_id IS NULL -> ephemeral / account-level web download (today's behavior)
-- * device_id set -> managed device-library entry (Phase 1+)
-- Downloads ship default-off with little/no production data, so widening the
-- table in place is safe. See docs/superpowers/specs/2026-06-18-offline-sync-mobile-design.md.
-- +goose Up
-- +goose StatementBegin
ALTER TABLE public.downloads
ADD COLUMN profile_id text, -- NULL for ephemeral/web rows
ADD COLUMN device_id text, -- NULL = ephemeral; set = managed device entry
ADD COLUMN format text NOT NULL DEFAULT 'original', -- internal delivery format
ADD COLUMN quality text NOT NULL DEFAULT 'original', -- client-requested quality preset
ADD COLUMN effective_quality text NOT NULL DEFAULT 'original',
ADD COLUMN target_bitrate_kbps integer NOT NULL DEFAULT 0,
ADD COLUMN revision integer NOT NULL DEFAULT 1,
ADD COLUMN artifact_id text; -- set for remux/transcode once prepared
ALTER TABLE public.downloads
ADD CONSTRAINT downloads_format_check CHECK (format IN ('original','remux','transcode'));
ALTER TABLE public.downloads
ADD CONSTRAINT downloads_quality_check CHECK (quality IN ('original','20mbps','10mbps','5mbps','2mbps','1mbps'));
ALTER TABLE public.downloads
ADD CONSTRAINT downloads_effective_quality_check CHECK (effective_quality IN ('original','20mbps','10mbps','5mbps','2mbps','1mbps'));
ALTER TABLE public.downloads
ADD CONSTRAINT downloads_target_bitrate_check CHECK (target_bitrate_kbps >= 0);
ALTER TABLE public.downloads
ADD CONSTRAINT downloads_revision_check CHECK (revision >= 1);
-- Widen the status enum to cover the managed-entry lifecycle alongside the existing serve states.
ALTER TABLE public.downloads DROP CONSTRAINT downloads_status_check;
ALTER TABLE public.downloads ADD CONSTRAINT downloads_status_check
CHECK (status IN ('queued','downloading','completed','failed','cancelled', -- existing
'preparing','ready','revoked')); -- managed entries
-- One managed entry per (user, profile, device, content, episode). Ephemeral rows (NULL device_id)
-- are exempt via the partial index. COALESCE(episode_id,'') collapses movies (NULL episode_id) to
-- one managed entry per (device, content) without NULL-comparison surprises.
CREATE UNIQUE INDEX downloads_device_entry_uidx
ON public.downloads (user_id, profile_id, device_id, content_id, COALESCE(episode_id, ''))
WHERE device_id IS NOT NULL;
-- No separate (user_id, profile_id, device_id) index: downloads_device_entry_uidx
-- already serves that prefix under the same partial predicate.
CREATE INDEX downloads_artifact_idx ON public.downloads (artifact_id) WHERE artifact_id IS NOT NULL;
-- Composite FK to user_devices. MATCH SIMPLE (the default) skips the check whenever any column is NULL,
-- so ephemeral rows (NULL device_id) are not constrained while managed entries are.
ALTER TABLE public.downloads
ADD CONSTRAINT downloads_device_fkey
FOREIGN KEY (user_id, profile_id, device_id)
REFERENCES public.user_devices(user_id, profile_id, device_id) ON DELETE CASCADE;
-- +goose StatementEnd
-- +goose Down
-- +goose StatementBegin
ALTER TABLE public.downloads DROP CONSTRAINT IF EXISTS downloads_device_fkey;
DROP INDEX IF EXISTS downloads_artifact_idx;
DROP INDEX IF EXISTS downloads_device_entry_uidx;
ALTER TABLE public.downloads DROP CONSTRAINT IF EXISTS downloads_revision_check;
ALTER TABLE public.downloads DROP CONSTRAINT IF EXISTS downloads_target_bitrate_check;
ALTER TABLE public.downloads DROP CONSTRAINT IF EXISTS downloads_effective_quality_check;
ALTER TABLE public.downloads DROP CONSTRAINT IF EXISTS downloads_quality_check;
ALTER TABLE public.downloads DROP CONSTRAINT IF EXISTS downloads_format_check;
ALTER TABLE public.downloads DROP CONSTRAINT IF EXISTS downloads_status_check;
-- Rollback must not fail validation: collapse managed-lifecycle rows to the
-- legacy enum before re-narrowing the CHECK (data loss is expected on a
-- reshape Down; a failing Down is not).
UPDATE public.downloads SET status = 'cancelled' WHERE status IN ('preparing','ready','revoked');
ALTER TABLE public.downloads ADD CONSTRAINT downloads_status_check
CHECK (status IN ('queued','downloading','completed','failed','cancelled'));
ALTER TABLE public.downloads
DROP COLUMN artifact_id,
DROP COLUMN revision,
DROP COLUMN target_bitrate_kbps,
DROP COLUMN effective_quality,
DROP COLUMN quality,
DROP COLUMN format,
DROP COLUMN device_id,
DROP COLUMN profile_id;
-- +goose StatementEnd