CREATE TABLE IF NOT EXISTS release_executions ( id VARCHAR(36) PRIMARY KEY, release_batch_id VARCHAR(36) NOT NULL REFERENCES release_batches(id) ON DELETE RESTRICT, operation VARCHAR(16) NOT NULL CHECK (operation IN ('publish', 'rollback')), prior_execution_id VARCHAR(36) REFERENCES release_executions(id) ON DELETE RESTRICT, state VARCHAR(16) NOT NULL DEFAULT 'pending' CHECK (state IN ('pending', 'running', 'completed', 'failed')), target_environment VARCHAR(16) NOT NULL DEFAULT 'production' CHECK (target_environment = 'production'), authorized_by VARCHAR(128) NOT NULL, authorization_code VARCHAR(64) NOT NULL, job_id VARCHAR(36) NOT NULL UNIQUE REFERENCES jobs(id) ON DELETE RESTRICT, manifest_sha256 CHAR(64) NOT NULL DEFAULT '' CHECK (manifest_sha256 = '' OR manifest_sha256 ~ '^[0-9a-f]{64}$'), item_count INTEGER NOT NULL DEFAULT 0 CHECK (item_count >= 0), completed_item_count INTEGER NOT NULL DEFAULT 0 CHECK (completed_item_count >= 0), failed_item_count INTEGER NOT NULL DEFAULT 0 CHECK (failed_item_count >= 0), error_code VARCHAR(64) NOT NULL DEFAULT '', created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), started_at TIMESTAMPTZ, completed_at TIMESTAMPTZ, CHECK (operation = 'publish' OR prior_execution_id IS NOT NULL), CHECK (completed_item_count + failed_item_count <= item_count) ); CREATE UNIQUE INDEX IF NOT EXISTS uq_release_publish_batch ON release_executions (release_batch_id) WHERE operation = 'publish'; CREATE UNIQUE INDEX IF NOT EXISTS uq_release_rollback_prior ON release_executions (prior_execution_id) WHERE operation = 'rollback'; CREATE INDEX IF NOT EXISTS idx_release_executions_batch_created ON release_executions (release_batch_id, created_at DESC); CREATE INDEX IF NOT EXISTS idx_release_executions_state_created ON release_executions (state, created_at); CREATE TABLE IF NOT EXISTS release_item_executions ( id VARCHAR(36) PRIMARY KEY, release_execution_id VARCHAR(36) NOT NULL REFERENCES release_executions(id) ON DELETE RESTRICT, publication_id VARCHAR(36) NOT NULL REFERENCES test_publications(id) ON DELETE RESTRICT, state VARCHAR(24) NOT NULL DEFAULT 'pending' CHECK (state IN ('pending', 'staged', 'activated', 'verified', 'failed', 'rolled_back')), bundle_sha256 CHAR(64) NOT NULL DEFAULT '' CHECK (bundle_sha256 = '' OR bundle_sha256 ~ '^[0-9a-f]{64}$'), previous_production_revision VARCHAR(128) NOT NULL DEFAULT '', production_revision VARCHAR(128) NOT NULL DEFAULT '', error_code VARCHAR(64) NOT NULL DEFAULT '', created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), completed_at TIMESTAMPTZ, UNIQUE (release_execution_id, publication_id) ); CREATE INDEX IF NOT EXISTS idx_release_item_executions_execution ON release_item_executions (release_execution_id, created_at); CREATE INDEX IF NOT EXISTS idx_release_item_executions_publication ON release_item_executions (publication_id, created_at DESC);