-- Destructive cleanup for the SOP single-version migration. -- Run only after the backfill script completed and all sop_id values are set. -- The statements discover constraint/index names because deployments may use -- different GORM naming conventions. -- The application does not use database foreign keys. Remove every existing -- foreign key in the database first so no historical constraint remains and -- no constraint blocks the old SOP column cleanup. DROP PROCEDURE IF EXISTS drop_all_iqudo_foreign_keys; DELIMITER // CREATE PROCEDURE drop_all_iqudo_foreign_keys() BEGIN DECLARE finished INT DEFAULT 0; DECLARE table_name_value VARCHAR(64); DECLARE constraint_name_value VARCHAR(64); DECLARE foreign_keys CURSOR FOR SELECT TABLE_NAME, CONSTRAINT_NAME FROM information_schema.TABLE_CONSTRAINTS WHERE CONSTRAINT_SCHEMA = DATABASE() AND CONSTRAINT_TYPE = 'FOREIGN KEY'; DECLARE CONTINUE HANDLER FOR NOT FOUND SET finished = 1; OPEN foreign_keys; drop_loop: LOOP FETCH foreign_keys INTO table_name_value, constraint_name_value; IF finished = 1 THEN LEAVE drop_loop; END IF; SET @sql = CONCAT('ALTER TABLE `', table_name_value, '` DROP FOREIGN KEY `', constraint_name_value, '`'); PREPARE stmt_all_fk FROM @sql; EXECUTE stmt_all_fk; DEALLOCATE PREPARE stmt_all_fk; END LOOP; CLOSE foreign_keys; END// DELIMITER ; CALL drop_all_iqudo_foreign_keys(); DROP PROCEDURE drop_all_iqudo_foreign_keys; -- The following blocks are retained as idempotent guards for databases where -- a concurrent/manual migration changed the schema between the procedure and -- the SOP cleanup statements. -- Remove foreign keys on the old sop_version_id columns, if present. SET @fk = ( SELECT kcu.CONSTRAINT_NAME FROM information_schema.KEY_COLUMN_USAGE kcu WHERE kcu.CONSTRAINT_SCHEMA = DATABASE() AND kcu.TABLE_NAME = 'sop_nodes' AND kcu.COLUMN_NAME = 'sop_version_id' AND kcu.REFERENCED_TABLE_NAME = 'sop_versions' LIMIT 1 ); SET @sql = IF(@fk IS NULL, 'SELECT 1', CONCAT('ALTER TABLE sop_nodes DROP FOREIGN KEY `', @fk, '`')); PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; SET @fk = ( SELECT kcu.CONSTRAINT_NAME FROM information_schema.KEY_COLUMN_USAGE kcu WHERE kcu.CONSTRAINT_SCHEMA = DATABASE() AND kcu.TABLE_NAME = 'sop_edges' AND kcu.COLUMN_NAME = 'sop_version_id' AND kcu.REFERENCED_TABLE_NAME = 'sop_versions' LIMIT 1 ); SET @sql = IF(@fk IS NULL, 'SELECT 1', CONCAT('ALTER TABLE sop_edges DROP FOREIGN KEY `', @fk, '`')); PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; SET @fk = ( SELECT kcu.CONSTRAINT_NAME FROM information_schema.KEY_COLUMN_USAGE kcu WHERE kcu.CONSTRAINT_SCHEMA = DATABASE() AND kcu.TABLE_NAME = 'sop_runs' AND kcu.COLUMN_NAME = 'sop_version_id' AND kcu.REFERENCED_TABLE_NAME = 'sop_versions' LIMIT 1 ); SET @sql = IF(@fk IS NULL, 'SELECT 1', CONCAT('ALTER TABLE sop_runs DROP FOREIGN KEY `', @fk, '`')); PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; -- Drop indexes containing the old column, regardless of their names. SET @indexes = ( SELECT GROUP_CONCAT(DISTINCT CONCAT('DROP INDEX `', INDEX_NAME, '`') SEPARATOR ', ') FROM information_schema.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sop_nodes' AND COLUMN_NAME = 'sop_version_id' AND INDEX_NAME <> 'PRIMARY' ); SET @sql = IF(@indexes IS NULL, 'SELECT 1', CONCAT('ALTER TABLE sop_nodes ', @indexes)); PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; SET @indexes = ( SELECT GROUP_CONCAT(DISTINCT CONCAT('DROP INDEX `', INDEX_NAME, '`') SEPARATOR ', ') FROM information_schema.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sop_edges' AND COLUMN_NAME = 'sop_version_id' AND INDEX_NAME <> 'PRIMARY' ); SET @sql = IF(@indexes IS NULL, 'SELECT 1', CONCAT('ALTER TABLE sop_edges ', @indexes)); PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; SET @indexes = ( SELECT GROUP_CONCAT(DISTINCT CONCAT('DROP INDEX `', INDEX_NAME, '`') SEPARATOR ', ') FROM information_schema.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sop_runs' AND COLUMN_NAME = 'sop_version_id' AND INDEX_NAME <> 'PRIMARY' ); SET @sql = IF(@indexes IS NULL, 'SELECT 1', CONCAT('ALTER TABLE sop_runs ', @indexes)); PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; -- Add replacement indexes once. SET @sql = IF( EXISTS (SELECT 1 FROM information_schema.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sop_nodes' AND INDEX_NAME = 'uk_sop_nodes_key'), 'SELECT 1', 'ALTER TABLE sop_nodes ADD UNIQUE KEY uk_sop_nodes_key (sop_id, node_key)' ); PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; SET @sql = IF( EXISTS (SELECT 1 FROM information_schema.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sop_edges' AND INDEX_NAME = 'idx_sop_edges_source'), 'SELECT 1', 'ALTER TABLE sop_edges ADD INDEX idx_sop_edges_source (sop_id, source_node_key, priority)' ); PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; -- Drop old columns only when they still exist. SET @sql = IF( EXISTS (SELECT 1 FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sop_nodes' AND COLUMN_NAME = 'sop_version_id'), 'ALTER TABLE sop_nodes DROP COLUMN sop_version_id', 'SELECT 1' ); PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; SET @sql = IF( EXISTS (SELECT 1 FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sop_edges' AND COLUMN_NAME = 'sop_version_id'), 'ALTER TABLE sop_edges DROP COLUMN sop_version_id', 'SELECT 1' ); PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; SET @sql = IF( EXISTS (SELECT 1 FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sop_runs' AND COLUMN_NAME = 'sop_version_id'), 'ALTER TABLE sop_runs DROP COLUMN sop_version_id', 'SELECT 1' ); PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; DROP TABLE IF EXISTS sop_versions;