You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
synctv/migrations/20260425999_create_file_sto...

161 lines
6.2 KiB
MySQL

CREATE TABLE IF NOT EXISTS file_objects (
storage_backend TEXT NOT NULL,
object_key TEXT NOT NULL,
mime_type TEXT NOT NULL,
size_bytes BIGINT NOT NULL,
content_manifest_sha256 TEXT NOT NULL,
metadata JSONB NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
validated_at TIMESTAMPTZ,
deleting_at TIMESTAMPTZ,
PRIMARY KEY (storage_backend, object_key),
CHECK (size_bytes > 0),
CHECK (length(content_manifest_sha256) = 64),
CHECK (jsonb_typeof(metadata) = 'object')
);
CREATE INDEX IF NOT EXISTS idx_file_objects_manifest
ON file_objects(storage_backend, content_manifest_sha256, size_bytes);
CREATE INDEX IF NOT EXISTS idx_file_objects_validated
ON file_objects(validated_at DESC);
CREATE INDEX IF NOT EXISTS idx_file_objects_deleting
ON file_objects(deleting_at DESC)
WHERE deleting_at IS NOT NULL;
CREATE TABLE IF NOT EXISTS file_references (
id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
storage_backend TEXT NOT NULL,
object_key TEXT NOT NULL,
reference_kind TEXT NOT NULL,
reference_id TEXT NOT NULL,
expires_at TIMESTAMPTZ,
released_at TIMESTAMPTZ,
metadata JSONB NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
UNIQUE (reference_kind, reference_id, storage_backend, object_key),
CHECK (jsonb_typeof(metadata) = 'object'),
FOREIGN KEY (storage_backend, object_key)
REFERENCES file_objects(storage_backend, object_key) ON DELETE CASCADE
);
CREATE INDEX IF NOT EXISTS idx_file_references_object_active
ON file_references(storage_backend, object_key)
WHERE released_at IS NULL;
CREATE INDEX IF NOT EXISTS idx_file_references_expiry
ON file_references(expires_at)
WHERE released_at IS NULL AND expires_at IS NOT NULL;
CREATE TABLE IF NOT EXISTS file_blob_parts (
storage_backend TEXT NOT NULL,
object_key TEXT NOT NULL,
part_index INTEGER NOT NULL,
offset_bytes BIGINT NOT NULL,
size_bytes BIGINT NOT NULL,
checksum_sha256 TEXT NOT NULL,
compression SMALLINT NOT NULL DEFAULT 0,
data BYTEA NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (storage_backend, object_key, part_index),
CHECK (part_index >= 0),
CHECK (offset_bytes >= 0),
CHECK (size_bytes > 0),
CHECK (length(checksum_sha256) = 64),
FOREIGN KEY (storage_backend, object_key)
REFERENCES file_objects(storage_backend, object_key) ON DELETE CASCADE
);
CREATE INDEX IF NOT EXISTS idx_file_blob_parts_object_offset
ON file_blob_parts(storage_backend, object_key, offset_bytes);
CREATE INDEX IF NOT EXISTS idx_file_blob_parts_created_at
ON file_blob_parts(created_at DESC);
CREATE TABLE IF NOT EXISTS file_upload_sessions (
storage_backend TEXT NOT NULL,
upload_session_key TEXT NOT NULL,
object_key TEXT NOT NULL,
session_kind SMALLINT NOT NULL,
upload_id TEXT,
user_id BIGINT NOT NULL,
storage_scope TEXT NOT NULL,
mime_type TEXT NOT NULL,
size_bytes BIGINT NOT NULL,
content_manifest_sha256 TEXT NOT NULL,
part_size_bytes BIGINT NOT NULL,
metadata JSONB NOT NULL,
expires_at TIMESTAMPTZ NOT NULL,
completed_at TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (storage_backend, upload_session_key),
CHECK (size_bytes > 0),
CHECK (part_size_bytes > 0),
CHECK (length(content_manifest_sha256) = 64),
CHECK (jsonb_typeof(metadata) = 'object'),
FOREIGN KEY (storage_backend, object_key)
REFERENCES file_objects(storage_backend, object_key) ON DELETE CASCADE
);
CREATE INDEX IF NOT EXISTS idx_file_upload_sessions_upload_id
ON file_upload_sessions(storage_backend, upload_id)
WHERE upload_id IS NOT NULL;
CREATE INDEX IF NOT EXISTS idx_file_upload_sessions_manifest
ON file_upload_sessions(storage_backend, user_id, storage_scope, content_manifest_sha256, size_bytes)
WHERE completed_at IS NULL;
CREATE INDEX IF NOT EXISTS idx_file_upload_sessions_object
ON file_upload_sessions(storage_backend, object_key);
CREATE INDEX IF NOT EXISTS idx_file_upload_sessions_expiry
ON file_upload_sessions(expires_at)
WHERE completed_at IS NULL;
CREATE TABLE IF NOT EXISTS file_upload_session_parts (
storage_backend TEXT NOT NULL,
upload_session_key TEXT NOT NULL,
part_index INTEGER NOT NULL,
part_number INTEGER NOT NULL,
offset_bytes BIGINT NOT NULL,
size_bytes BIGINT NOT NULL,
checksum_sha256 TEXT,
etag TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (storage_backend, upload_session_key, part_index),
UNIQUE (storage_backend, upload_session_key, part_number),
CHECK (part_index >= 0),
CHECK (part_number > 0),
CHECK (offset_bytes >= 0),
CHECK (size_bytes > 0),
CHECK (checksum_sha256 IS NULL OR length(checksum_sha256) = 64)
);
CREATE INDEX IF NOT EXISTS idx_file_upload_session_parts_object_offset
ON file_upload_session_parts(storage_backend, upload_session_key, offset_bytes);
CREATE TABLE IF NOT EXISTS file_cleanup_jobs (
id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
origin TEXT NOT NULL,
storage_backend TEXT NOT NULL,
object_key TEXT NOT NULL,
reference_kind TEXT NOT NULL,
reference_id TEXT NOT NULL,
metadata JSONB NOT NULL,
attempt_count INTEGER NOT NULL DEFAULT 0,
last_error TEXT,
next_attempt_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
locked_at TIMESTAMPTZ,
locked_by TEXT,
completed_at TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
UNIQUE (reference_kind, reference_id, storage_backend, object_key),
CHECK (attempt_count >= 0),
CHECK (jsonb_typeof(metadata) = 'object')
);
CREATE INDEX IF NOT EXISTS idx_file_cleanup_jobs_due
ON file_cleanup_jobs(next_attempt_at, id)
WHERE completed_at IS NULL;
CREATE INDEX IF NOT EXISTS idx_file_cleanup_jobs_object
ON file_cleanup_jobs(storage_backend, object_key)
WHERE completed_at IS NULL;