-- ============================================================
-- 日志表按月分区统一管理（幂等迁移脚本）
-- 创建时间：2026-05-06
-- 说明：确保 log_collect_raw、log_collect_analysis、log_collect_cycle
--       三张日志表均按月分区，并统一存储过程管理
-- 执行前提：USE cnc_log; 已执行 01-init-schema.sql 和 03-collect-analysis-tables.sql
-- ============================================================

USE cnc_log;

-- ============================================================
-- 1. log_collect_raw 按月分区
--    该表在 01-init-schema.sql 中已定义分区，此处确认分区存在
--    分区键：request_time
-- ============================================================
-- 检查是否已有分区，若无则重建（幂等）
SET @has_partition := (SELECT COUNT(*) FROM information_schema.PARTITIONS
    WHERE TABLE_SCHEMA = 'cnc_log' AND TABLE_NAME = 'log_collect_raw' AND PARTITION_NAME IS NOT NULL);

-- 如果表没有分区（旧表），则需要重建
-- 注意：如果表已有分区（从DDL创建），此步骤会跳过
SET @sql_rebuild := IF(@has_partition = 0,
    'ALTER TABLE cnc_log.log_collect_raw PARTITION BY RANGE (TO_DAYS(request_time)) (
        PARTITION p202604 VALUES LESS THAN (TO_DAYS(''2026-05-01'')),
        PARTITION p202605 VALUES LESS THAN (TO_DAYS(''2026-06-01'')),
        PARTITION p202606 VALUES LESS THAN (TO_DAYS(''2026-07-01'')),
        PARTITION p202607 VALUES LESS THAN (TO_DAYS(''2026-08-01'')),
        PARTITION p_future VALUES LESS THAN MAXVALUE
    )',
    'SELECT ''log_collect_raw 已有分区，跳过''');
PREPARE stmt FROM @sql_rebuild;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

-- ============================================================
-- 2. log_collect_analysis 按月分区
--    该表在 03-collect-analysis-tables.sql 中已定义分区
--    分区键：analysis_time
-- ============================================================
SET @has_partition_a := (SELECT COUNT(*) FROM information_schema.PARTITIONS
    WHERE TABLE_SCHEMA = 'cnc_log' AND TABLE_NAME = 'log_collect_analysis' AND PARTITION_NAME IS NOT NULL);

SET @sql_rebuild_a := IF(@has_partition_a = 0,
    'ALTER TABLE cnc_log.log_collect_analysis PARTITION BY RANGE (TO_DAYS(analysis_time)) (
        PARTITION p202605 VALUES LESS THAN (TO_DAYS(''2026-06-01'')),
        PARTITION p202606 VALUES LESS THAN (TO_DAYS(''2026-07-01'')),
        PARTITION p202607 VALUES LESS THAN (TO_DAYS(''2026-08-01'')),
        PARTITION p_future VALUES LESS THAN MAXVALUE
    )',
    'SELECT ''log_collect_analysis 已有分区，跳过''');
PREPARE stmt FROM @sql_rebuild_a;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

-- ============================================================
-- 3. log_collect_cycle 按月分区
--    该表在 03-collect-analysis-tables.sql 中已定义分区
--    分区键：cycle_time
-- ============================================================
SET @has_partition_c := (SELECT COUNT(*) FROM information_schema.PARTITIONS
    WHERE TABLE_SCHEMA = 'cnc_log' AND TABLE_NAME = 'log_collect_cycle' AND PARTITION_NAME IS NOT NULL);

SET @sql_rebuild_c := IF(@has_partition_c = 0,
    'ALTER TABLE cnc_log.log_collect_cycle PARTITION BY RANGE (TO_DAYS(cycle_time)) (
        PARTITION p202605 VALUES LESS THAN (TO_DAYS(''2026-06-01'')),
        PARTITION p202606 VALUES LESS THAN (TO_DAYS(''2026-07-01'')),
        PARTITION p202607 VALUES LESS THAN (TO_DAYS(''2026-08-01'')),
        PARTITION p_future VALUES LESS THAN MAXVALUE
    )',
    'SELECT ''log_collect_cycle 已有分区，跳过''');
PREPARE stmt FROM @sql_rebuild_c;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

-- ============================================================
-- 4. 更新存储过程 sp_ensure_partitions：覆盖全部3张分区表
-- ============================================================
DROP PROCEDURE IF EXISTS sp_ensure_partitions;
DELIMITER $$
CREATE PROCEDURE sp_ensure_partitions()
BEGIN
    -- 当前月的第一天
    SET @base := DATE_FORMAT(CURDATE(), '%Y-%m-01');
    SET @d1 := DATE_ADD(@base, INTERVAL 1 MONTH);
    SET @d2 := DATE_ADD(@base, INTERVAL 2 MONTH);
    SET @p1 := CONCAT('p', DATE_FORMAT(@d1, '%Y%m'));
    SET @p2 := CONCAT('p', DATE_FORMAT(@d2, '%Y%m'));

    -- ============================
    -- log_collect_raw（分区键：request_time）
    -- ============================
    IF NOT EXISTS (SELECT 1 FROM information_schema.PARTITIONS
                   WHERE TABLE_SCHEMA = 'cnc_log' AND TABLE_NAME = 'log_collect_raw' AND PARTITION_NAME = @p1) THEN
        SET @v1 := DATE_FORMAT(@d1, '%Y-%m-01');
        SET @sql := CONCAT('ALTER TABLE cnc_log.log_collect_raw ADD PARTITION (PARTITION ', @p1,
                           ' VALUES LESS THAN (TO_DAYS(', '''', @v1, '''', '))');
        PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
        INSERT IGNORE INTO log_partition_tracker(table_name, partition_name, partition_value) VALUES ('log_collect_raw', @p1, @v1);
    END IF;

    IF NOT EXISTS (SELECT 1 FROM information_schema.PARTITIONS
                   WHERE TABLE_SCHEMA = 'cnc_log' AND TABLE_NAME = 'log_collect_raw' AND PARTITION_NAME = @p2) THEN
        SET @v2 := DATE_FORMAT(@d2, '%Y-%m-01');
        SET @sql := CONCAT('ALTER TABLE cnc_log.log_collect_raw ADD PARTITION (PARTITION ', @p2,
                           ' VALUES LESS THAN (TO_DAYS(', '''', @v2, '''', '))');
        PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
        INSERT IGNORE INTO log_partition_tracker(table_name, partition_name, partition_value) VALUES ('log_collect_raw', @p2, @v2);
    END IF;

    -- ============================
    -- log_collect_analysis（分区键：analysis_time）
    -- ============================
    IF NOT EXISTS (SELECT 1 FROM information_schema.PARTITIONS
                   WHERE TABLE_SCHEMA = 'cnc_log' AND TABLE_NAME = 'log_collect_analysis' AND PARTITION_NAME = @p1) THEN
        SET @v1 := DATE_FORMAT(@d1, '%Y-%m-01');
        SET @sql := CONCAT('ALTER TABLE cnc_log.log_collect_analysis ADD PARTITION (PARTITION ', @p1,
                           ' VALUES LESS THAN (TO_DAYS(', '''', @v1, '''', '))');
        PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
        INSERT IGNORE INTO log_partition_tracker(table_name, partition_name, partition_value) VALUES ('log_collect_analysis', @p1, @v1);
    END IF;

    IF NOT EXISTS (SELECT 1 FROM information_schema.PARTITIONS
                   WHERE TABLE_SCHEMA = 'cnc_log' AND TABLE_NAME = 'log_collect_analysis' AND PARTITION_NAME = @p2) THEN
        SET @v2 := DATE_FORMAT(@d2, '%Y-%m-01');
        SET @sql := CONCAT('ALTER TABLE cnc_log.log_collect_analysis ADD PARTITION (PARTITION ', @p2,
                           ' VALUES LESS THAN (TO_DAYS(', '''', @v2, '''', '))');
        PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
        INSERT IGNORE INTO log_partition_tracker(table_name, partition_name, partition_value) VALUES ('log_collect_analysis', @p2, @v2);
    END IF;

    -- ============================
    -- log_collect_cycle（分区键：cycle_time）
    -- ============================
    IF NOT EXISTS (SELECT 1 FROM information_schema.PARTITIONS
                   WHERE TABLE_SCHEMA = 'cnc_log' AND TABLE_NAME = 'log_collect_cycle' AND PARTITION_NAME = @p1) THEN
        SET @v1 := DATE_FORMAT(@d1, '%Y-%m-01');
        SET @sql := CONCAT('ALTER TABLE cnc_log.log_collect_cycle ADD PARTITION (PARTITION ', @p1,
                           ' VALUES LESS THAN (TO_DAYS(', '''', @v1, '''', '))');
        PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
        INSERT IGNORE INTO log_partition_tracker(table_name, partition_name, partition_value) VALUES ('log_collect_cycle', @p1, @v1);
    END IF;

    IF NOT EXISTS (SELECT 1 FROM information_schema.PARTITIONS
                   WHERE TABLE_SCHEMA = 'cnc_log' AND TABLE_NAME = 'log_collect_cycle' AND PARTITION_NAME = @p2) THEN
        SET @v2 := DATE_FORMAT(@d2, '%Y-%m-01');
        SET @sql := CONCAT('ALTER TABLE cnc_log.log_collect_cycle ADD PARTITION (PARTITION ', @p2,
                           ' VALUES LESS THAN (TO_DAYS(', '''', @v2, '''', '))');
        PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
        INSERT IGNORE INTO log_partition_tracker(table_name, partition_name, partition_value) VALUES ('log_collect_cycle', @p2, @v2);
    END IF;
END$$
DELIMITER ;

-- ============================================================
-- 5. 更新 sp_check_partitions：覆盖全部3张分区表
-- ============================================================
DROP PROCEDURE IF EXISTS sp_check_partitions;
DELIMITER $$
CREATE PROCEDURE sp_check_partitions()
BEGIN
    SET @base := DATE_FORMAT(CURDATE(), '%Y-%m-01');
    SET @d1 := DATE_ADD(@base, INTERVAL 1 MONTH);
    SET @d2 := DATE_ADD(@base, INTERVAL 2 MONTH);
    SET @p1 := CONCAT('p', DATE_FORMAT(@d1, '%Y%m'));
    SET @p2 := CONCAT('p', DATE_FORMAT(@d2, '%Y%m'));

    SET @need := 0;

    -- log_collect_raw
    IF (SELECT COUNT(*) FROM information_schema.PARTITIONS WHERE TABLE_SCHEMA = 'cnc_log' AND TABLE_NAME = 'log_collect_raw' AND PARTITION_NAME = @p1) = 0 THEN SET @need = 1; END IF;
    IF (SELECT COUNT(*) FROM information_schema.PARTITIONS WHERE TABLE_SCHEMA = 'cnc_log' AND TABLE_NAME = 'log_collect_raw' AND PARTITION_NAME = @p2) = 0 THEN SET @need = 1; END IF;

    -- log_collect_analysis
    IF (SELECT COUNT(*) FROM information_schema.PARTITIONS WHERE TABLE_SCHEMA = 'cnc_log' AND TABLE_NAME = 'log_collect_analysis' AND PARTITION_NAME = @p1) = 0 THEN SET @need = 1; END IF;
    IF (SELECT COUNT(*) FROM information_schema.PARTITIONS WHERE TABLE_SCHEMA = 'cnc_log' AND TABLE_NAME = 'log_collect_analysis' AND PARTITION_NAME = @p2) = 0 THEN SET @need = 1; END IF;

    -- log_collect_cycle
    IF (SELECT COUNT(*) FROM information_schema.PARTITIONS WHERE TABLE_SCHEMA = 'cnc_log' AND TABLE_NAME = 'log_collect_cycle' AND PARTITION_NAME = @p1) = 0 THEN SET @need = 1; END IF;
    IF (SELECT COUNT(*) FROM information_schema.PARTITIONS WHERE TABLE_SCHEMA = 'cnc_log' AND TABLE_NAME = 'log_collect_cycle' AND PARTITION_NAME = @p2) = 0 THEN SET @need = 1; END IF;

    IF @need = 1 THEN
        CALL sp_ensure_partitions();
    END IF;

    SELECT @need AS need_partition_creation;
END$$
DELIMITER ;

-- ============================================================
-- 6. 确保分区追踪表存在
-- ============================================================
CREATE TABLE IF NOT EXISTS log_partition_tracker (
    table_name VARCHAR(100) NOT NULL,
    partition_name VARCHAR(50) NOT NULL,
    partition_value VARCHAR(30) NOT NULL,
    created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (table_name, partition_name)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
  COMMENT='分区管理追踪表';

-- ============================================================
-- 7. 立即执行一次分区确保
-- ============================================================
CALL sp_ensure_partitions();

-- ============================================================
-- 8. 更新 MariaDB 事件：每月1日凌晨2:00执行
-- ============================================================
SET GLOBAL event_scheduler = ON;
DROP EVENT IF EXISTS ev_ensure_partitions;
CREATE EVENT IF NOT EXISTS ev_ensure_partitions
ON SCHEDULE
    EVERY 1 MONTH
    STARTS TIMESTAMP(DATE_FORMAT(DATE_ADD(CURDATE(), INTERVAL 1 MONTH), '%Y-%m-01 02:00:00'))
DO
    CALL sp_check_partitions();
