fix(canonical): split Phase 1A migrations into single-statement files (211-219)
asyncpg exec_driver_sql 의 prepared statement 제약상 multi-statement 파일은
"cannot insert multiple commands into a prepared statement" 에러로 적용 실패.
규칙: 한 migration = 한 statement (다중 ADD COLUMN 절은 단일 statement 라 허용,
인덱스/CHECK/CREATE TABLE 은 별도 파일).
이전 cee01af 의 211_md_canonical_layer.sql (6 statements) + 212_document_lineage.sql
(3 statements) 을 9 파일로 분할:
211 ALTER TABLE documents ADD COLUMN x13
212 ADD CONSTRAINT documents_md_draft_status_only_ai
213 idx_documents_md_status_pending
214 idx_documents_content_origin
215 idx_documents_md_frontmatter_gin (선제 인덱스)
216 idx_documents_md_draft_status
217 CREATE TABLE document_lineage
218 idx_document_lineage_source
219 idx_document_lineage_derived
dry-run 재검증: 13 cols / 28 doc idx / 4 lineage idx PASS.
계획 변경 없음 — schema 결과 동일, 적용 단위만 분할.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,58 +0,0 @@
|
||||
-- 211_md_canonical_layer.sql
|
||||
-- Phase 1A: Markdown canonical layer (schema only).
|
||||
-- Plan: ~/.claude/plans/plan-idempotent-sundae.md (round 3 approved)
|
||||
--
|
||||
-- 어떤 워커도 이 컬럼을 채우지 않는다 (Phase 1A scope).
|
||||
-- 신규 컬럼은 모두 nullable 또는 default 보장 → 기존 코드 경로 영향 0.
|
||||
-- 상태값은 TEXT+CHECK (확장 시 enum drop/rebuild 비용 회피).
|
||||
-- Postgres 16.13: ADD COLUMN ... DEFAULT <constant> 는 fast path (table rewrite 없음).
|
||||
|
||||
ALTER TABLE documents
|
||||
ADD COLUMN md_content TEXT,
|
||||
ADD COLUMN md_frontmatter JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||
ADD COLUMN md_format_version TEXT NOT NULL DEFAULT '1.0',
|
||||
ADD COLUMN md_status TEXT NOT NULL DEFAULT 'pending'
|
||||
CHECK (md_status IN ('pending','processing','success','partial','failed','skipped')),
|
||||
ADD COLUMN md_extraction_engine TEXT,
|
||||
ADD COLUMN md_extraction_engine_version TEXT,
|
||||
ADD COLUMN md_extraction_quality JSONB,
|
||||
ADD COLUMN md_extraction_error TEXT,
|
||||
ADD COLUMN md_content_hash TEXT,
|
||||
ADD COLUMN md_source_hash TEXT,
|
||||
ADD COLUMN md_generated_at TIMESTAMPTZ,
|
||||
-- content_origin: canonical markdown 생성 경로 분류.
|
||||
-- 'extracted' = 업로드 원본에서 추출되어야 하는 계열 (완료 여부는 md_status가 결정).
|
||||
-- 'manual' = 사람이 UI에서 직접 작성한 markdown.
|
||||
-- 'ai_drafted'= LLM이 검색 evidence 기반으로 작성 (Phase 4).
|
||||
-- 'imported' = 외부 시스템/기존 markdown에서 가져온 문서 (DEVONthink 등).
|
||||
ADD COLUMN content_origin TEXT NOT NULL DEFAULT 'extracted'
|
||||
CHECK (content_origin IN ('extracted','manual','ai_drafted','imported')),
|
||||
-- md_draft_status: AI-generated Markdown review lifecycle only.
|
||||
-- 사용 조건은 별도 CHECK (documents_md_draft_status_only_ai)에서 강제.
|
||||
ADD COLUMN md_draft_status TEXT
|
||||
CHECK (md_draft_status IS NULL OR md_draft_status IN ('draft','pending_review','approved','revised','rejected'));
|
||||
|
||||
ALTER TABLE documents
|
||||
ADD CONSTRAINT documents_md_draft_status_only_ai
|
||||
CHECK (md_draft_status IS NULL OR content_origin = 'ai_drafted');
|
||||
|
||||
-- 큐 폴링용 (pending/processing만)
|
||||
CREATE INDEX idx_documents_md_status_pending
|
||||
ON documents (md_status)
|
||||
WHERE md_status IN ('pending', 'processing');
|
||||
|
||||
-- content_origin 기반 필터 (AI 생성문서 분리 등)
|
||||
CREATE INDEX idx_documents_content_origin
|
||||
ON documents (content_origin);
|
||||
|
||||
-- frontmatter 기반 검색 (project_code, doc_type 등 GIN).
|
||||
-- Phase 1A 시점엔 모든 행이 '{}' 이므로 즉시 활용 X.
|
||||
-- Phase 1B 이후 md_frontmatter 필터링/조회를 위한 선제 인덱스.
|
||||
-- INSERT latency 문제 발생 시 1순위 rollback 대상.
|
||||
CREATE INDEX idx_documents_md_frontmatter_gin
|
||||
ON documents USING GIN (md_frontmatter);
|
||||
|
||||
-- 검토 큐 (Phase 4 이후)
|
||||
CREATE INDEX idx_documents_md_draft_status
|
||||
ON documents (md_draft_status)
|
||||
WHERE md_draft_status IS NOT NULL;
|
||||
@@ -0,0 +1,25 @@
|
||||
-- 211_md_columns.sql
|
||||
-- Phase 1A: documents 테이블에 markdown canonical layer 컬럼 13개 추가.
|
||||
-- Plan: ~/.claude/plans/plan-idempotent-sundae.md (round 3)
|
||||
--
|
||||
-- asyncpg exec_driver_sql 단일 prepared statement 제약 — ALTER TABLE 다중 ADD COLUMN
|
||||
-- 절은 단일 statement 라 허용. 인덱스/CHECK/CREATE TABLE 은 별도 파일 (212~219).
|
||||
-- PG 16.13: ADD COLUMN ... DEFAULT <constant> 는 fast path (table rewrite 없음).
|
||||
|
||||
ALTER TABLE documents
|
||||
ADD COLUMN IF NOT EXISTS md_content TEXT,
|
||||
ADD COLUMN IF NOT EXISTS md_frontmatter JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||
ADD COLUMN IF NOT EXISTS md_format_version TEXT NOT NULL DEFAULT '1.0',
|
||||
ADD COLUMN IF NOT EXISTS md_status TEXT NOT NULL DEFAULT 'pending'
|
||||
CHECK (md_status IN ('pending','processing','success','partial','failed','skipped')),
|
||||
ADD COLUMN IF NOT EXISTS md_extraction_engine TEXT,
|
||||
ADD COLUMN IF NOT EXISTS md_extraction_engine_version TEXT,
|
||||
ADD COLUMN IF NOT EXISTS md_extraction_quality JSONB,
|
||||
ADD COLUMN IF NOT EXISTS md_extraction_error TEXT,
|
||||
ADD COLUMN IF NOT EXISTS md_content_hash TEXT,
|
||||
ADD COLUMN IF NOT EXISTS md_source_hash TEXT,
|
||||
ADD COLUMN IF NOT EXISTS md_generated_at TIMESTAMPTZ,
|
||||
ADD COLUMN IF NOT EXISTS content_origin TEXT NOT NULL DEFAULT 'extracted'
|
||||
CHECK (content_origin IN ('extracted','manual','ai_drafted','imported')),
|
||||
ADD COLUMN IF NOT EXISTS md_draft_status TEXT
|
||||
CHECK (md_draft_status IS NULL OR md_draft_status IN ('draft','pending_review','approved','revised','rejected'));
|
||||
@@ -0,0 +1,6 @@
|
||||
-- 212_md_draft_status_check.sql
|
||||
-- Phase 1A: md_draft_status 사용 조건 — content_origin='ai_drafted' 일 때만 NOT NULL 허용.
|
||||
|
||||
ALTER TABLE documents
|
||||
ADD CONSTRAINT documents_md_draft_status_only_ai
|
||||
CHECK (md_draft_status IS NULL OR content_origin = 'ai_drafted');
|
||||
@@ -0,0 +1,6 @@
|
||||
-- 213_md_status_pending_idx.sql
|
||||
-- Phase 1A: 큐 폴링용 partial index (pending/processing 만).
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_documents_md_status_pending
|
||||
ON documents (md_status)
|
||||
WHERE md_status IN ('pending', 'processing');
|
||||
@@ -0,0 +1,5 @@
|
||||
-- 214_content_origin_idx.sql
|
||||
-- Phase 1A: content_origin 기반 필터 (AI 생성문서 분리 등).
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_documents_content_origin
|
||||
ON documents (content_origin);
|
||||
@@ -0,0 +1,7 @@
|
||||
-- 215_md_frontmatter_gin_idx.sql
|
||||
-- Phase 1A: frontmatter JSONB 기반 검색 (project_code, doc_type 등) GIN.
|
||||
-- Phase 1A 시점엔 모든 행 '{}' 이므로 즉시 활용 X. Phase 1B 이후 frontmatter 필터링용.
|
||||
-- INSERT latency 문제 발생 시 1순위 rollback 대상.
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_documents_md_frontmatter_gin
|
||||
ON documents USING GIN (md_frontmatter);
|
||||
@@ -0,0 +1,6 @@
|
||||
-- 216_md_draft_status_idx.sql
|
||||
-- Phase 1A: AI 생성문서 검토 큐 partial index (Phase 4 이후 활용).
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_documents_md_draft_status
|
||||
ON documents (md_draft_status)
|
||||
WHERE md_draft_status IS NOT NULL;
|
||||
@@ -1,12 +1,10 @@
|
||||
-- 212_document_lineage.sql
|
||||
-- 217_document_lineage.sql
|
||||
-- Phase 1A: AI 생성 문서가 어느 원본에서 파생됐는지 추적.
|
||||
-- Plan: ~/.claude/plans/plan-idempotent-sundae.md (round 3 approved)
|
||||
--
|
||||
-- 이력 테이블 FK = ON DELETE RESTRICT (feedback_history_table_fk_restrict).
|
||||
-- 부모 문서 hard delete 차단, soft delete (documents.deleted_at) 만 허용.
|
||||
-- relation_type 도 TEXT+CHECK (확장 시 enum drop/rebuild 비용 회피).
|
||||
|
||||
CREATE TABLE document_lineage (
|
||||
CREATE TABLE IF NOT EXISTS document_lineage (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
source_document_id BIGINT NOT NULL REFERENCES documents(id) ON DELETE RESTRICT,
|
||||
derived_document_id BIGINT NOT NULL REFERENCES documents(id) ON DELETE RESTRICT,
|
||||
@@ -19,11 +17,3 @@ CREATE TABLE document_lineage (
|
||||
CONSTRAINT document_lineage_uq
|
||||
UNIQUE (source_document_id, derived_document_id, relation_type)
|
||||
);
|
||||
|
||||
-- reverse-lookup: '이 문서가 어느 생성문서의 source 였나'
|
||||
CREATE INDEX idx_document_lineage_source
|
||||
ON document_lineage (source_document_id);
|
||||
|
||||
-- forward-lookup: '이 생성문서의 source 는 무엇이었나'
|
||||
CREATE INDEX idx_document_lineage_derived
|
||||
ON document_lineage (derived_document_id);
|
||||
@@ -0,0 +1,5 @@
|
||||
-- 218_lineage_source_idx.sql
|
||||
-- Phase 1A: lineage reverse-lookup ("이 문서가 어느 생성문서의 source 였나").
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_document_lineage_source
|
||||
ON document_lineage (source_document_id);
|
||||
@@ -0,0 +1,5 @@
|
||||
-- 219_lineage_derived_idx.sql
|
||||
-- Phase 1A: lineage forward-lookup ("이 생성문서의 source 는 무엇이었나").
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_document_lineage_derived
|
||||
ON document_lineage (derived_document_id);
|
||||
Reference in New Issue
Block a user