CREATE TABLE shows_v4 ( id TEXT PRIMARY KEY, title TEXT NOT NULL, starts_at_utc TEXT, twitch_title TEXT NOT NULL DEFAULT '', announcement_text TEXT NOT NULL DEFAULT '', status TEXT NOT NULL CHECK (status IN ('draft', 'ready', 'live', 'completed')), revision INTEGER NOT NULL DEFAULT 1 CHECK (revision >= 1), created_at_utc TEXT NOT NULL, updated_at_utc TEXT NOT NULL ); CREATE TABLE show_sections_v4 ( id TEXT PRIMARY KEY, show_id TEXT NOT NULL REFERENCES shows_v4(id) ON DELETE CASCADE, name TEXT NOT NULL, category_id TEXT REFERENCES section_categories(id) ON DELETE SET NULL, position INTEGER NOT NULL CHECK (position >= 0), explicit_duration_seconds INTEGER CHECK ( explicit_duration_seconds IS NULL OR (explicit_duration_seconds >= 0 AND explicit_duration_seconds <= 86400) ), is_reserve_bank INTEGER NOT NULL DEFAULT 0 CHECK (is_reserve_bank IN (0, 1)), UNIQUE (show_id, position) ); CREATE TABLE piece_usages_v4 ( id TEXT PRIMARY KEY, show_section_id TEXT NOT NULL REFERENCES show_sections_v4(id) ON DELETE CASCADE, original_piece_id TEXT REFERENCES pieces(id) ON DELETE SET NULL, position INTEGER NOT NULL CHECK (position >= 0), status TEXT NOT NULL CHECK (status IN ('open', 'current', 'done', 'skipped')), duration_seconds INTEGER NOT NULL CHECK (duration_seconds >= 0 AND duration_seconds <= 86400), title_snapshot TEXT NOT NULL, intro_snapshot TEXT NOT NULL DEFAULT '', research_snapshot TEXT NOT NULL DEFAULT '', moderation_snapshot TEXT NOT NULL DEFAULT '', transition_snapshot TEXT NOT NULL DEFAULT '', presentation_snapshot TEXT NOT NULL DEFAULT '', own_notes TEXT NOT NULL DEFAULT '', is_reserve INTEGER NOT NULL DEFAULT 0 CHECK (is_reserve IN (0, 1)), UNIQUE (show_section_id, position) ); INSERT INTO shows_v4 ( id, title, starts_at_utc, twitch_title, announcement_text, status, revision, created_at_utc, updated_at_utc ) SELECT id, title, starts_at_utc, twitch_title, announcement_text, status, 1, created_at_utc, updated_at_utc FROM shows; INSERT INTO show_sections_v4 ( id, show_id, name, category_id, position, explicit_duration_seconds, is_reserve_bank ) SELECT id, show_id, name, category_id, position, explicit_duration_seconds, 0 FROM show_sections; INSERT INTO show_sections_v4 ( id, show_id, name, category_id, position, explicit_duration_seconds, is_reserve_bank ) SELECT 'reserve-bank-' || id, id, 'Reservebank', NULL, COALESCE((SELECT max(position) + 1 FROM show_sections WHERE show_id = shows.id), 0), NULL, 1 FROM shows; INSERT INTO piece_usages_v4 ( id, show_section_id, original_piece_id, position, status, duration_seconds, title_snapshot, intro_snapshot, research_snapshot, moderation_snapshot, transition_snapshot, presentation_snapshot, own_notes, is_reserve ) SELECT u.id, CASE WHEN u.is_reserve = 1 THEN 'reserve-bank-' || s.show_id ELSE u.show_section_id END, u.original_piece_id, CASE WHEN u.is_reserve = 1 THEN ( SELECT count(*) - 1 FROM piece_usages earlier JOIN show_sections earlier_section ON earlier_section.id = earlier.show_section_id WHERE earlier.is_reserve = 1 AND earlier_section.show_id = s.show_id AND (earlier_section.position < s.position OR (earlier_section.position = s.position AND earlier.position < u.position) OR (earlier_section.position = s.position AND earlier.position = u.position AND earlier.id <= u.id)) ) ELSE u.position END, u.status, u.duration_seconds, u.title_snapshot, u.intro_snapshot, COALESCE(p.research_text, ''), u.moderation_snapshot, u.transition_snapshot, u.presentation_snapshot, '', u.is_reserve FROM piece_usages u JOIN show_sections s ON s.id = u.show_section_id LEFT JOIN pieces p ON p.id = u.original_piece_id; DROP TABLE piece_usages; DROP TABLE show_sections; DROP TABLE shows; ALTER TABLE shows_v4 RENAME TO shows; ALTER TABLE show_sections_v4 RENAME TO show_sections; ALTER TABLE piece_usages_v4 RENAME TO piece_usages; CREATE INDEX idx_shows_status_start ON shows(status, starts_at_utc); CREATE INDEX idx_sections_show ON show_sections(show_id, position); CREATE INDEX idx_usages_section ON piece_usages(show_section_id, position); CREATE UNIQUE INDEX idx_one_reserve_bank_per_show ON show_sections(show_id) WHERE is_reserve_bank = 1; CREATE TABLE show_templates ( id TEXT PRIMARY KEY, name TEXT NOT NULL UNIQUE, description TEXT NOT NULL DEFAULT '' ); CREATE TABLE show_template_sections ( id TEXT PRIMARY KEY, template_id TEXT NOT NULL REFERENCES show_templates(id) ON DELETE CASCADE, name TEXT NOT NULL, category_id TEXT REFERENCES section_categories(id) ON DELETE SET NULL, position INTEGER NOT NULL CHECK (position >= 0), explicit_duration_seconds INTEGER CHECK ( explicit_duration_seconds IS NULL OR (explicit_duration_seconds >= 0 AND explicit_duration_seconds <= 86400) ), UNIQUE (template_id, position) ); CREATE INDEX idx_show_template_sections_template ON show_template_sections(template_id, position); INSERT INTO show_templates (id, name, description) VALUES ('template-default', 'Standardablauf', 'Start, zwei JC-Blöcke und zwei Spiele.'), ('template-games', 'Spieleabend', 'Ein ruhiger Einstieg und drei flexible Spieleblöcke.'); INSERT INTO show_template_sections ( id, template_id, name, category_id, position, explicit_duration_seconds ) VALUES ('template-default-start', 'template-default', 'Start', NULL, 0, 300), ('template-default-jc-reaction', 'template-default', 'JC / Reaction', NULL, 1, NULL), ('template-default-game-1', 'template-default', 'Spiel 1', NULL, 2, 3600), ('template-default-jc', 'template-default', 'JC', NULL, 3, NULL), ('template-default-game-2', 'template-default', 'Spiel 2', NULL, 4, 3600), ('template-games-start', 'template-games', 'Start', NULL, 0, 300), ('template-games-game-1', 'template-games', 'Spiel 1', NULL, 1, 2700), ('template-games-break', 'template-games', 'Pause & Chat', NULL, 2, 600), ('template-games-game-2', 'template-games', 'Spiel 2', NULL, 3, 2700), ('template-games-game-3', 'template-games', 'Spiel 3', NULL, 4, 2700);