2026-05-18 19:46:42 -04:00
|
|
|
|
-- Wild Dragon MAM – Editor sequences schema patch
|
|
|
|
|
|
-- Run with: psql $DATABASE_URL -f schema_patch_editor.sql
|
2026-05-18 19:41:21 -04:00
|
|
|
|
|
|
|
|
|
|
-- Named timelines within a project (multiple per project, like Premiere)
|
|
|
|
|
|
CREATE TABLE IF NOT EXISTS sequences (
|
|
|
|
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
|
|
|
|
project_id UUID NOT NULL REFERENCES projects ON DELETE CASCADE,
|
|
|
|
|
|
name TEXT NOT NULL DEFAULT 'Sequence 1',
|
|
|
|
|
|
frame_rate NUMERIC(6,3) NOT NULL DEFAULT 59.94,
|
|
|
|
|
|
width INTEGER NOT NULL DEFAULT 1920,
|
|
|
|
|
|
height INTEGER NOT NULL DEFAULT 1080,
|
|
|
|
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
2026-05-18 19:46:42 -04:00
|
|
|
|
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
|
|
|
|
|
CONSTRAINT uq_sequences_project_name UNIQUE (project_id, name)
|
2026-05-18 19:41:21 -04:00
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_sequences_project_id ON sequences(project_id);
|
|
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_sequences_updated_at ON sequences(updated_at DESC);
|
|
|
|
|
|
|
|
|
|
|
|
-- Clips placed on a sequence timeline
|
|
|
|
|
|
CREATE TABLE IF NOT EXISTS sequence_clips (
|
|
|
|
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
|
|
|
|
sequence_id UUID NOT NULL REFERENCES sequences ON DELETE CASCADE,
|
|
|
|
|
|
asset_id UUID NOT NULL REFERENCES assets ON DELETE CASCADE,
|
2026-05-18 19:46:42 -04:00
|
|
|
|
track INTEGER NOT NULL DEFAULT 0 CHECK (track >= 0),
|
2026-05-18 19:41:21 -04:00
|
|
|
|
-- track encoding: 0=V1, 1=V2, 100=A1, 101=A2
|
2026-05-18 19:46:42 -04:00
|
|
|
|
-- Open-ended CHECK (track >= 0) used instead of an enumerated list so that
|
|
|
|
|
|
-- additional tracks can be added in the future without a schema migration.
|
2026-05-18 19:41:21 -04:00
|
|
|
|
timeline_in_frames BIGINT NOT NULL,
|
|
|
|
|
|
timeline_out_frames BIGINT NOT NULL,
|
|
|
|
|
|
source_in_frames BIGINT NOT NULL DEFAULT 0,
|
|
|
|
|
|
source_out_frames BIGINT NOT NULL,
|
|
|
|
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
2026-05-18 19:46:42 -04:00
|
|
|
|
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
|
|
|
|
|
CONSTRAINT chk_timeline_range CHECK (timeline_out_frames > timeline_in_frames),
|
|
|
|
|
|
CONSTRAINT chk_source_range CHECK (source_out_frames > source_in_frames)
|
2026-05-18 19:41:21 -04:00
|
|
|
|
);
|
|
|
|
|
|
|
2026-05-18 19:46:42 -04:00
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_sequence_clips_sequence_id ON sequence_clips(sequence_id);
|
2026-05-18 19:41:21 -04:00
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_sequence_clips_track_position ON sequence_clips(sequence_id, track, timeline_in_frames);
|
2026-05-18 19:46:42 -04:00
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_sequence_clips_asset_id ON sequence_clips(asset_id);
|
|
|
|
|
|
|
|
|
|
|
|
-- ---------------------------------------------------------------------------
|
|
|
|
|
|
-- Idempotent ALTER TABLE block — applies the new constraints and index to
|
|
|
|
|
|
-- tables that were already created by an earlier run of this file.
|
2026-05-18 19:48:11 -04:00
|
|
|
|
-- Uses DO blocks because PostgreSQL does not support ADD CONSTRAINT IF NOT EXISTS.
|
|
|
|
|
|
-- Safe to re-run.
|
2026-05-18 19:46:42 -04:00
|
|
|
|
-- ---------------------------------------------------------------------------
|
|
|
|
|
|
|
2026-05-18 19:48:11 -04:00
|
|
|
|
DO $$ BEGIN
|
|
|
|
|
|
IF NOT EXISTS (
|
|
|
|
|
|
SELECT 1 FROM pg_constraint
|
|
|
|
|
|
WHERE conname = 'uq_sequences_project_name' AND conrelid = 'sequences'::regclass
|
|
|
|
|
|
) THEN
|
|
|
|
|
|
ALTER TABLE sequences ADD CONSTRAINT uq_sequences_project_name UNIQUE (project_id, name);
|
|
|
|
|
|
END IF;
|
|
|
|
|
|
END $$;
|
2026-05-18 19:46:42 -04:00
|
|
|
|
|
2026-05-18 19:48:11 -04:00
|
|
|
|
DO $$ BEGIN
|
|
|
|
|
|
IF NOT EXISTS (
|
|
|
|
|
|
SELECT 1 FROM pg_constraint
|
|
|
|
|
|
WHERE conname = 'chk_timeline_range' AND conrelid = 'sequence_clips'::regclass
|
|
|
|
|
|
) THEN
|
|
|
|
|
|
ALTER TABLE sequence_clips ADD CONSTRAINT chk_timeline_range CHECK (timeline_out_frames > timeline_in_frames);
|
|
|
|
|
|
END IF;
|
|
|
|
|
|
END $$;
|
2026-05-18 19:46:42 -04:00
|
|
|
|
|
2026-05-18 19:48:11 -04:00
|
|
|
|
DO $$ BEGIN
|
|
|
|
|
|
IF NOT EXISTS (
|
|
|
|
|
|
SELECT 1 FROM pg_constraint
|
|
|
|
|
|
WHERE conname = 'chk_source_range' AND conrelid = 'sequence_clips'::regclass
|
|
|
|
|
|
) THEN
|
|
|
|
|
|
ALTER TABLE sequence_clips ADD CONSTRAINT chk_source_range CHECK (source_out_frames > source_in_frames);
|
|
|
|
|
|
END IF;
|
|
|
|
|
|
END $$;
|
2026-05-18 19:46:42 -04:00
|
|
|
|
|
2026-05-18 19:48:11 -04:00
|
|
|
|
DO $$ BEGIN
|
|
|
|
|
|
IF NOT EXISTS (
|
|
|
|
|
|
SELECT 1 FROM pg_constraint
|
|
|
|
|
|
WHERE conname = 'chk_track_valid' AND conrelid = 'sequence_clips'::regclass
|
|
|
|
|
|
) THEN
|
|
|
|
|
|
ALTER TABLE sequence_clips ADD CONSTRAINT chk_track_valid CHECK (track >= 0);
|
|
|
|
|
|
END IF;
|
|
|
|
|
|
END $$;
|
2026-05-18 19:46:42 -04:00
|
|
|
|
|
|
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_sequence_clips_asset_id ON sequence_clips(asset_id);
|