회기 연속성과 멀티 케이스 계약을 영속화

This commit is contained in:
Yun Chan 2026-09-01 11:45:16 +09:00
parent be08c0b573
commit 72353ecd82
26 changed files with 2170 additions and 127 deletions

View file

@ -0,0 +1,129 @@
-- =============================================================================
-- Vignette · migration 22 — 동일 learner-persona의 새 사례 분리
-- =============================================================================
-- migration 21의 learner-persona 단일 활성 회기 제약은 그대로 유지한다.
-- 이 migration은 끝난 기존 사례를 보존한 채, 같은 내담자에 새 사례(case_profile)를
-- 만들 수 있도록 legacy pair unique만 제거한다. raw transcript·memory row는 수정하지 않는다.
--
-- CREATE INDEX CONCURRENTLY는 transaction block 안에서 실행할 수 없다.
-- release agent가 online migration으로 owner psql에 전달한다.
SET lock_timeout = '5s';
SET statement_timeout = '15min';
DO $$
DECLARE
legacy_constraints text[];
target_index oid := to_regclass('app.idx_case_profile_learner_persona_activity');
BEGIN
SELECT array_agg(candidate.conname ORDER BY candidate.conname)
INTO legacy_constraints
FROM (
SELECT constraint_meta.conname
FROM pg_constraint AS constraint_meta
WHERE constraint_meta.conrelid = 'app.case_profile'::regclass
AND constraint_meta.contype = 'u'
AND ARRAY(
SELECT attribute_meta.attname
FROM unnest(constraint_meta.conkey) WITH ORDINALITY AS key_column(attnum, ordinality)
JOIN pg_attribute AS attribute_meta
ON attribute_meta.attrelid = constraint_meta.conrelid
AND attribute_meta.attnum = key_column.attnum
ORDER BY key_column.ordinality
) = ARRAY['persona_id', 'learner_id']::text[]
) AS candidate;
IF COALESCE(array_length(legacy_constraints, 1), 0) > 1 THEN
RAISE EXCEPTION
'migration 22 blocked: multiple legacy case_profile persona-learner unique constraints exist';
END IF;
IF target_index IS NOT NULL AND NOT EXISTS (
SELECT 1
FROM pg_index AS index_meta
WHERE index_meta.indexrelid = target_index
AND index_meta.indrelid = 'app.case_profile'::regclass
AND index_meta.indisvalid
AND index_meta.indisready
AND NOT index_meta.indisunique
AND index_meta.indnkeyatts = 4
AND pg_get_indexdef(index_meta.indexrelid) LIKE
'%(learner_id, persona_id, updated_at DESC, case_id)%'
) THEN
RAISE EXCEPTION
'migration 22 blocked: target index exists but is invalid or has a different definition';
END IF;
END
$$;
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_case_profile_learner_persona_activity
ON app.case_profile (learner_id, persona_id, updated_at DESC, case_id);
DO $$
DECLARE
legacy_constraints text[];
BEGIN
IF NOT EXISTS (
SELECT 1
FROM pg_index AS index_meta
WHERE index_meta.indexrelid = 'app.idx_case_profile_learner_persona_activity'::regclass
AND index_meta.indrelid = 'app.case_profile'::regclass
AND index_meta.indisvalid
AND index_meta.indisready
AND NOT index_meta.indisunique
AND index_meta.indnkeyatts = 4
AND pg_get_indexdef(index_meta.indexrelid) LIKE
'%(learner_id, persona_id, updated_at DESC, case_id)%'
) THEN
RAISE EXCEPTION
'migration 22 failed: valid case activity index was not created';
END IF;
SELECT array_agg(candidate.conname ORDER BY candidate.conname)
INTO legacy_constraints
FROM (
SELECT constraint_meta.conname
FROM pg_constraint AS constraint_meta
WHERE constraint_meta.conrelid = 'app.case_profile'::regclass
AND constraint_meta.contype = 'u'
AND ARRAY(
SELECT attribute_meta.attname
FROM unnest(constraint_meta.conkey) WITH ORDINALITY AS key_column(attnum, ordinality)
JOIN pg_attribute AS attribute_meta
ON attribute_meta.attrelid = constraint_meta.conrelid
AND attribute_meta.attnum = key_column.attnum
ORDER BY key_column.ordinality
) = ARRAY['persona_id', 'learner_id']::text[]
) AS candidate;
IF COALESCE(array_length(legacy_constraints, 1), 0) > 1 THEN
RAISE EXCEPTION
'migration 22 blocked: multiple legacy case_profile persona-learner unique constraints exist';
END IF;
IF COALESCE(array_length(legacy_constraints, 1), 0) = 1 THEN
EXECUTE format(
'ALTER TABLE app.case_profile DROP CONSTRAINT %I',
legacy_constraints[1]
);
END IF;
IF EXISTS (
SELECT 1
FROM pg_constraint AS constraint_meta
WHERE constraint_meta.conrelid = 'app.case_profile'::regclass
AND constraint_meta.contype = 'u'
AND ARRAY(
SELECT attribute_meta.attname
FROM unnest(constraint_meta.conkey) WITH ORDINALITY AS key_column(attnum, ordinality)
JOIN pg_attribute AS attribute_meta
ON attribute_meta.attrelid = constraint_meta.conrelid
AND attribute_meta.attnum = key_column.attnum
ORDER BY key_column.ordinality
) = ARRAY['persona_id', 'learner_id']::text[]
) THEN
RAISE EXCEPTION
'migration 22 failed: legacy case_profile persona-learner unique constraint remains';
END IF;
END
$$;