2026-09-15 17:27:38 +08:00
-- 006_accommodation_history_constraint.sql
-- 目标:移除 train_accommodation 全局唯一约束 uq_accommodation_enroll,
-- 创建部分唯一索引 uq_accommodation_enroll_active (每个 enrollment 最多 1 条有效住宿),
-- 允许学员拥有多条退宿历史记录 (status=2),彻底支持调房与历史留痕。
DO $$
DECLARE
v_dup_count INTEGER ;
v_con_def TEXT ;
v_idx_def TEXT ;
BEGIN
-- 1. Pre-Migration Data Safety Guard
-- 检查是否已存在同一个 enrollment_id 拥有 2 条或以上处于有效状态 (0=预分配, 1=已入住) 的住宿记录
SELECT COUNT ( * ) INTO v_dup_count
FROM (
SELECT enrollment_id
FROM train_accommodation
WHERE accommodation_status IN ( 0 , 1 )
GROUP BY enrollment_id
HAVING COUNT ( * ) > 1
) t ;
IF v_dup_count > 0 THEN
RAISE EXCEPTION 'Pre-migration check failed: found % enrollment(s) with multiple active accommodations (status IN (0,1)). Manual resolution required before applying migration 006.' , v_dup_count ;
END IF ;
-- 2. Existing Constraint Guard (Idempotent Drop)
-- 检查约束 uq_accommodation_enroll 是否存在且符合预期
SELECT pg_get_constraintdef ( c . oid ) INTO v_con_def
FROM pg_constraint c
JOIN pg_class t ON c . conrelid = t . oid
WHERE t . relname = 'train_accommodation'
AND c . conname = 'uq_accommodation_enroll' ;
IF v_con_def IS NOT NULL THEN
-- 验证旧约束是否为 UNIQUE (enrollment_id)
IF v_con_def ILIKE 'UNIQUE (enrollment_id)%' THEN
RAISE NOTICE 'Dropping legacy global unique constraint: uq_accommodation_enroll' ;
ALTER TABLE train_accommodation DROP CONSTRAINT uq_accommodation_enroll ;
ELSE
RAISE EXCEPTION 'Constraint collision: constraint uq_accommodation_enroll exists but definition is unexpected: %' , v_con_def ;
END IF ;
ELSE
RAISE NOTICE 'Constraint uq_accommodation_enroll does not exist, skipping drop.' ;
END IF ;
-- 3. New Index Collision Guard & Creation
-- 检查部分唯一索引 uq_accommodation_enroll_active
SELECT pg_get_indexdef ( i . indexrelid ) INTO v_idx_def
FROM pg_index i
JOIN pg_class c ON i . indexrelid = c . oid
JOIN pg_class t ON i . indrelid = t . oid
WHERE t . relname = 'train_accommodation'
AND c . relname = 'uq_accommodation_enroll_active' ;
IF v_idx_def IS NOT NULL THEN
-- 验证已有索引是否为预期的部分唯一索引
2026-09-16 12:01:30 +08:00
IF v_idx_def ILIKE '%UNIQUE INDEX uq_accommodation_enroll_active ON %train_accommodation%enrollment_id%WHERE%accommodation_status%' THEN
2026-09-15 17:27:38 +08:00
RAISE NOTICE 'Partial unique index uq_accommodation_enroll_active already exists with expected definition, skipping creation.' ;
ELSE
RAISE EXCEPTION 'Index collision: index uq_accommodation_enroll_active exists but definition differs: %' , v_idx_def ;
END IF ;
ELSE
RAISE NOTICE 'Creating partial unique index: uq_accommodation_enroll_active' ;
CREATE UNIQUE INDEX uq_accommodation_enroll_active
ON train_accommodation ( enrollment_id )
WHERE accommodation_status IN ( 0 , 1 );
END IF ;
END $$ ;