-- Wild Dragon MAM – Editor sequences schema patch -- Run with: psql $DATABASE_URL -f schema_patch_editor.sql -- 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(), updated_at TIMESTAMPTZ DEFAULT NOW(), CONSTRAINT uq_sequences_project_name UNIQUE (project_id, name) ); 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, track INTEGER NOT NULL DEFAULT 0 CHECK (track >= 0), -- track encoding: 0=V1, 1=V2, 100=A1, 101=A2 -- 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. 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(), 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) ); CREATE INDEX IF NOT EXISTS idx_sequence_clips_sequence_id ON sequence_clips(sequence_id); CREATE INDEX IF NOT EXISTS idx_sequence_clips_track_position ON sequence_clips(sequence_id, track, timeline_in_frames); 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. -- Uses DO blocks because PostgreSQL does not support ADD CONSTRAINT IF NOT EXISTS. -- Safe to re-run. -- --------------------------------------------------------------------------- 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 $$; 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 $$; 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 $$; 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 $$; CREATE INDEX IF NOT EXISTS idx_sequence_clips_asset_id ON sequence_clips(asset_id);