Files
Oxicloud/migrations/20261022000000_derived_variant_encodes_format.sql
T

57 lines
2.9 KiB
SQL
Raw Normal View History

-- Put the output format inside `variant`, where the plan says new axes go.
--
-- `content_derived_blobs.variant` held the size alone (`icon` | `preview` |
-- `large`), so a size could hold exactly ONE stored artifact regardless of
-- codec. That surfaced when the read order flipped (step 10c): a JPEG request
-- matched the WebP row and would have been served the wrong codec, which the
-- old ordering hid because the `.jpg` sidecar won first. The flip had to be
-- gated to WebP, which in turn means JPEG clients can never leave the sidecar
-- — so the sidecar can never be deleted.
--
-- It blocks transcodes harder still: those are multi-format by nature, so
-- without a format term two output codecs of one source collide on the
-- primary key.
--
-- Per the column's own comment — "new axes go inside this string, never into
-- new columns" — the axis goes in the string rather than into a fourth PK
-- column. The PK stays `(source_hash, kind, variant)`.
--
-- Shape: `{size}.{ext}` — `preview.webp`, `icon.jpg`, and later `720p.webp`
-- for transcodes.
--
-- The backfill is deterministic rather than a guess: `store_derived_blob` has
-- only ever been called with `"image/webp"` for thumbnails, so every existing
-- thumbnail row is WebP. `content_type` is checked anyway rather than assumed
-- — if that assumption is ever wrong, the row is left alone for a human to
-- look at instead of being silently mislabelled.
UPDATE storage.content_derived_blobs
SET variant = variant || '.webp'
WHERE kind = 'thumbnail'
AND content_type = 'image/webp'
-- Idempotent: skip anything already carrying a format suffix, so a
-- re-applied migration cannot produce `preview.webp.webp`.
AND variant NOT LIKE '%.%';
-- Anything left without a format suffix did not match the WebP assumption.
-- Surfaced as a warning rather than coerced: the read path will simply miss
-- those rows and fall back to the sidecar, which is safe, whereas guessing a
-- codec would serve the wrong bytes.
DO $$
DECLARE
v_unsuffixed INT;
BEGIN
SELECT COUNT(*) INTO v_unsuffixed
FROM storage.content_derived_blobs
WHERE kind = 'thumbnail' AND variant NOT LIKE '%.%';
IF v_unsuffixed > 0 THEN
RAISE WARNING
'derived_variant_encodes_format: % thumbnail row(s) have no format suffix (content_type was not image/webp). They will be ignored by the read path and re-derived on demand; inspect before deleting the sidecars.',
v_unsuffixed;
END IF;
END $$;
COMMENT ON COLUMN storage.content_derived_blobs.variant IS
'Opaque discriminator carrying every axis but the source and the kind: size AND output format, as {size}.{ext} (preview.webp | icon.jpg | 720p.webp). New axes go inside this string, never into new columns. A format term is required — without one, two codecs of the same source collide on the primary key, and the read path cannot tell which codec a row holds.';