From 0d3c8415778a6ff1b68a9e18c2526b0411a025e0 Mon Sep 17 00:00:00 2001 From: hyungi Date: Tue, 16 Jun 2026 14:16:21 +0900 Subject: [PATCH] =?UTF-8?q?feat(migrations):=20=EC=8A=A4=ED=82=A4=EB=A7=88?= =?UTF-8?q?=20baseline=20=EC=8A=A4=EB=83=85=EC=83=B7=20=E2=80=94=20fresh-D?= =?UTF-8?q?B/DR=20=EB=B6=80=ED=8C=85=20fix=20(R1)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit R0 가 입증했듯 migrations/ 전체 replay 는 011(view active_documents 가 documents.embedding 의존, DROP COLUMN CASCADE 부재)·326(enum-same-txn) 등 누적 비-replayable 로 깨져 신규/DR 환경 init_db 부팅이 불가능했다. 표준 squash baseline 로 해소: - migrations/_baseline/0358_schema_baseline.sql: prod 스키마 스냅샷(pg_dump --schema-only --no-owner --no-privileges, psql 메타·search_path='' 정리 = asyncpg exec_driver_sql 호환). - init_db._load_baseline_if_fresh: documents 테이블 부재(fresh) 시 baseline 적재 + schema_migrations 1..358 스탬프 → 이후 post-baseline(359/360)만 적용. ★기존 DB(documents 존재)는 skip = prod 무영향(additive). baseline 부재 시 기존 replay 경로(하위호환). - migration_smoke: baseline 경로 검증. ★실측 — 이전 FAIL(011 abort) → 이제 FRESH/INCREMENTAL 모두 PASS (pg16.14). cutoff(_BASELINE_CUTOFF=358) 갱신 시 baseline 재생성. 검증: py_compile + migration_smoke PASS. ★boot-path 변경이라 deploy 전 staging 부팅 검증 필수. Co-Authored-By: Claude Opus 4.8 (1M context) --- app/core/database.py | 57 +- migrations/_baseline/0358_schema_baseline.sql | 5231 +++++++++++++++++ scripts/ci/migration_smoke.sh | 55 +- 3 files changed, 5318 insertions(+), 25 deletions(-) create mode 100644 migrations/_baseline/0358_schema_baseline.sql diff --git a/app/core/database.py b/app/core/database.py index 9dca470..6d5ec1c 100644 --- a/app/core/database.py +++ b/app/core/database.py @@ -72,6 +72,50 @@ def _validate_sql_content(name: str, sql: str) -> None: ) +# R1: baseline 스냅샷이 대표하는 마지막 마이그레이션 버전 (이하 버전은 baseline 에 포함). +# 새 baseline 재생성 시 이 값을 갱신한다 (migrations/_baseline/_schema_baseline.sql). +_BASELINE_CUTOFF = 358 + + +async def _load_baseline_if_fresh(conn, migrations_dir: Path) -> None: + """fresh DB(documents 부재)면 baseline 스키마 스냅샷 적재 + schema_migrations 1..cutoff 스탬프. + + 기존 DB(documents 존재)는 즉시 반환 — baseline 미적재, 무영향. baseline 파일 부재 시도 + 기존 replay 경로 유지(하위호환). + """ + from sqlalchemy import text + + baseline_dir = migrations_dir / "_baseline" + baseline_files = ( + sorted(baseline_dir.glob("*_schema_baseline.sql")) if baseline_dir.is_dir() else [] + ) + if not baseline_files: + return + + docs_exists = ( + await conn.execute(text("SELECT to_regclass('public.documents') IS NOT NULL")) + ).scalar() + if docs_exists: + return # 기존 DB — baseline skip + + baseline_path = baseline_files[-1] + logger.info(f"[migration] fresh DB 감지 — baseline 적재: {baseline_path.name}") + await conn.exec_driver_sql(baseline_path.read_text(encoding="utf-8")) + # baseline = cutoff 까지의 스키마 → 실제 파일 버전 기준으로 schema_migrations 스탬프. + versions = [v for v, _, _ in _parse_migration_files(migrations_dir) if v <= _BASELINE_CUTOFF] + for v in versions: + await conn.execute( + text( + "INSERT INTO schema_migrations (version, name) " + "VALUES (:v, :n) ON CONFLICT DO NOTHING" + ), + {"v": v, "n": f"baseline:{v}"}, + ) + logger.info( + f"[migration] baseline 적재 + schema_migrations {len(versions)}건 스탬프 (cutoff {_BASELINE_CUTOFF})" + ) + + async def _run_migrations(conn) -> None: """미적용 migration 실행 (호출자가 트랜잭션 관리)""" from sqlalchemy import text @@ -90,10 +134,6 @@ async def _run_migrations(conn) -> None: f"SELECT pg_advisory_xact_lock({_MIGRATION_LOCK_KEY})" )) - # 적용 이력 조회 - result = await conn.execute(text("SELECT version FROM schema_migrations")) - applied = {row[0] for row in result} - # migration 파일 스캔 # /app/core/database.py → parent.parent = /app → /app/migrations (volume mount 위치) migrations_dir = Path(__file__).resolve().parent.parent / "migrations" @@ -101,6 +141,15 @@ async def _run_migrations(conn) -> None: logger.info("[migration] migrations/ 디렉토리 없음, 스킵") return + # R1: fresh DB(documents 부재)면 baseline 스냅샷 먼저 적재 + schema_migrations 스탬프. + # migrations/ 전체 replay 는 누적 비-replayable(011 view 의존·326 enum-same-txn 등)로 + # 깨지므로 신규/DR 환경은 prod 스키마 스냅샷에서 출발한다. 기존 DB 는 skip(무영향). + await _load_baseline_if_fresh(conn, migrations_dir) + + # 적용 이력 조회 (baseline 스탬프 반영 — fresh DB 는 1..cutoff 가 이미 applied) + result = await conn.execute(text("SELECT version FROM schema_migrations")) + applied = {row[0] for row in result} + files = _parse_migration_files(migrations_dir) pending = [(v, name, path) for v, name, path in files if v not in applied] diff --git a/migrations/_baseline/0358_schema_baseline.sql b/migrations/_baseline/0358_schema_baseline.sql new file mode 100644 index 0000000..51dd809 --- /dev/null +++ b/migrations/_baseline/0358_schema_baseline.sql @@ -0,0 +1,5231 @@ +-- +-- PostgreSQL database dump +-- + + +-- Dumped from database version 16.13 (Debian 16.13-1.pgdg12+1) +-- Dumped by pg_dump version 16.13 (Debian 16.13-1.pgdg12+1) + +SET statement_timeout = 0; +SET lock_timeout = 0; +SET idle_in_transaction_session_timeout = 0; +SET client_encoding = 'UTF8'; +SET standard_conforming_strings = on; +SET check_function_bodies = false; +SET xmloption = content; +SET client_min_messages = warning; +SET row_security = off; + +-- +-- Name: pg_trgm; Type: EXTENSION; Schema: -; Owner: - +-- + +CREATE EXTENSION IF NOT EXISTS pg_trgm WITH SCHEMA public; + + +-- +-- Name: EXTENSION pg_trgm; Type: COMMENT; Schema: -; Owner: - +-- + +COMMENT ON EXTENSION pg_trgm IS 'text similarity measurement and index searching based on trigrams'; + + +-- +-- Name: vector; Type: EXTENSION; Schema: -; Owner: - +-- + +CREATE EXTENSION IF NOT EXISTS vector WITH SCHEMA public; + + +-- +-- Name: EXTENSION vector; Type: COMMENT; Schema: -; Owner: - +-- + +COMMENT ON EXTENSION vector IS 'vector data type and ivfflat and hnsw access methods'; + + +-- +-- Name: data_origin; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.data_origin AS ENUM ( + 'work', + 'external' +); + + +-- +-- Name: doc_category; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.doc_category AS ENUM ( + 'document', + 'library', + 'news', + 'memo', + 'audio', + 'video', + 'mail', + 'calendar', + 'plex', + 'law' +); + + +-- +-- Name: doc_type; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.doc_type AS ENUM ( + 'immutable', + 'editable', + 'note' +); + + +-- +-- Name: document_purpose; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.document_purpose AS ENUM ( + 'business', + 'knowledge' +); + + +-- +-- Name: event_actor; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.event_actor AS ENUM ( + 'manual', + 'eid', + 'email_ingest', + 'system' +); + + +-- +-- Name: event_kind; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.event_kind AS ENUM ( + 'task', + 'calendar_event', + 'activity_log' +); + + +-- +-- Name: event_kind_hint; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.event_kind_hint AS ENUM ( + 'note', + 'task', + 'calendar_event', + 'activity_log', + 'reference' +); + + +-- +-- Name: event_source; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.event_source AS ENUM ( + 'manual', + 'memo', + 'email', + 'chat', + 'webhook', + 'git_commit', + 'claude_code' +); + + +-- +-- Name: event_status; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.event_status AS ENUM ( + 'inbox', + 'next', + 'scheduled', + 'in_progress', + 'done', + 'cancelled', + 'deferred' +); + + +-- +-- Name: history_change_kind; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.history_change_kind AS ENUM ( + 'create', + 'reschedule', + 'defer', + 'reactivate', + 'complete', + 'cancel' +); + + +-- +-- Name: process_stage; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.process_stage AS ENUM ( + 'extract', + 'classify', + 'embed', + 'preview', + 'summarize', + 'chunk', + 'stt', + 'thumbnail', + 'deep_summary', + 'markdown', + 'fulltext' +); + + +-- +-- Name: process_status; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.process_status AS ENUM ( + 'pending', + 'processing', + 'completed', + 'failed' +); + + +-- +-- Name: source_channel; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.source_channel AS ENUM ( + 'law_monitor', + 'devonagent', + 'email', + 'web_clip', + 'tksafety', + 'inbox_route', + 'manual', + 'drive_sync', + 'news', + 'memo', + 'voice', + 'hermes', + 'crawl' +); + + +SET default_tablespace = ''; + +SET default_table_access_method = heap; + +-- +-- Name: documents; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.documents ( + id bigint NOT NULL, + file_path text, + file_hash character(64) NOT NULL, + file_format character varying(20) NOT NULL, + file_size bigint, + file_type public.doc_type DEFAULT 'immutable'::public.doc_type NOT NULL, + import_source text, + extracted_text text, + extracted_at timestamp with time zone, + extractor_version character varying(50), + ai_summary text, + ai_tags jsonb DEFAULT '[]'::jsonb, + ai_domain character varying(100), + ai_sub_group character varying(100), + ai_model_version character varying(50), + ai_processed_at timestamp with time zone, + embed_model_version character varying(50), + embedded_at timestamp with time zone, + source_channel public.source_channel, + data_origin public.data_origin, + title text, + created_at timestamp with time zone DEFAULT now(), + updated_at timestamp with time zone DEFAULT now(), + user_note text, + preview_status character varying(20) DEFAULT 'none'::character varying, + preview_hash character varying(64), + preview_at timestamp with time zone, + edit_url text, + original_path text, + original_format character varying(20), + original_hash character varying(64), + conversion_status character varying(20) DEFAULT 'none'::character varying, + document_type character varying(50), + importance character varying(20) DEFAULT 'medium'::character varying, + ai_confidence double precision, + review_status character varying(20) DEFAULT 'pending'::character varying, + derived_path text, + deleted_at timestamp with time zone, + embedding public.vector(1024), + is_read boolean DEFAULT false, + user_tags jsonb DEFAULT '[]'::jsonb, + pinned boolean DEFAULT false, + ask_includable boolean DEFAULT true, + archived boolean DEFAULT false, + doc_purpose public.document_purpose, + facet_company text, + facet_topic text, + facet_year integer, + facet_doctype text, + extract_meta jsonb DEFAULT '{}'::jsonb, + category public.doc_category, + ai_suggestion jsonb, + thumbnail_path text, + needs_conversion boolean DEFAULT false NOT NULL, + ai_tldr text, + ai_bullets jsonb, + ai_detail_summary text, + ai_inconsistencies jsonb, + ai_analysis_tier text, + memo_task_state jsonb DEFAULT '{}'::jsonb NOT NULL, + md_content text, + md_frontmatter jsonb DEFAULT '{}'::jsonb NOT NULL, + md_format_version text DEFAULT '1.0'::text NOT NULL, + md_status text DEFAULT 'pending'::text NOT NULL, + md_extraction_engine text, + md_extraction_engine_version text, + md_extraction_quality jsonb, + md_extraction_error text, + md_content_hash text, + md_source_hash text, + md_generated_at timestamp with time zone, + content_origin text DEFAULT 'extracted'::text NOT NULL, + md_draft_status text, + ai_event_kind public.event_kind_hint, + ai_event_confidence numeric(3,2), + source_external_id text, + email_metadata jsonb, + source_metadata jsonb DEFAULT '{}'::jsonb NOT NULL, + ocr_derived boolean DEFAULT false NOT NULL, + original_filename text, + duplicate_of bigint, + duplicate_count integer DEFAULT 0 NOT NULL, + material_type text, + jurisdiction text, + published_date date, + CONSTRAINT chk_documents_jurisdiction CHECK ((jurisdiction = ANY (ARRAY['KR'::text, 'US'::text, 'EU'::text, 'JP'::text, 'GB'::text, 'INT'::text]))), + CONSTRAINT chk_documents_law_jurisdiction CHECK (((material_type <> 'law'::text) OR (jurisdiction IS NOT NULL))), + CONSTRAINT chk_documents_material_type CHECK ((material_type = ANY (ARRAY['law'::text, 'paper'::text, 'book'::text, 'incident'::text, 'manual'::text, 'standard'::text, 'guide'::text]))), + CONSTRAINT documents_ai_event_confidence_check CHECK (((ai_event_confidence IS NULL) OR ((ai_event_confidence >= (0)::numeric) AND (ai_event_confidence <= (1)::numeric)))), + CONSTRAINT documents_content_origin_check CHECK ((content_origin = ANY (ARRAY['extracted'::text, 'manual'::text, 'ai_drafted'::text, 'imported'::text]))), + CONSTRAINT documents_md_draft_status_check CHECK (((md_draft_status IS NULL) OR (md_draft_status = ANY (ARRAY['draft'::text, 'pending_review'::text, 'approved'::text, 'revised'::text, 'rejected'::text])))), + CONSTRAINT documents_md_draft_status_only_ai CHECK (((md_draft_status IS NULL) OR (content_origin = 'ai_drafted'::text))), + CONSTRAINT documents_md_status_check CHECK ((md_status = ANY (ARRAY['pending'::text, 'processing'::text, 'success'::text, 'partial'::text, 'failed'::text, 'skipped'::text]))) +); + + +-- +-- Name: active_documents; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.active_documents AS + SELECT id, + file_path, + file_hash, + file_format, + file_size, + file_type, + import_source, + extracted_text, + extracted_at, + extractor_version, + ai_summary, + ai_tags, + ai_domain, + ai_sub_group, + ai_model_version, + ai_processed_at, + embed_model_version, + embedded_at, + source_channel, + data_origin, + title, + created_at, + updated_at, + user_note, + preview_status, + preview_hash, + preview_at, + edit_url, + original_path, + original_format, + original_hash, + conversion_status, + document_type, + importance, + ai_confidence, + review_status, + derived_path, + deleted_at, + embedding + FROM public.documents + WHERE (deleted_at IS NULL); + + +-- +-- Name: analyze_events; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.analyze_events ( + id bigint NOT NULL, + doc_id bigint NOT NULL, + user_id bigint, + mode text DEFAULT 'quick'::text NOT NULL, + text_limit integer, + truncated boolean DEFAULT false, + layers_returned jsonb DEFAULT '[]'::jsonb, + cached boolean DEFAULT false, + latency_ms integer, + model_name text, + prompt_version text, + error_code text, + source text DEFAULT 'document_server'::text NOT NULL, + created_at timestamp with time zone DEFAULT now(), + subject_domain text, + risk_flags text[], + high_impact_task boolean, + escalated_to_26b boolean, + escalation_reasons text[], + confidence real, + policy_violation boolean, + policy_violation_ids text[], + shadow_would_route_to text, + policy_version text, + tier text, + suppressed_reason text, + answerability text, + partial_basis boolean, + suggested_query_count integer +); + + +-- +-- Name: analyze_events_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.analyze_events_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: analyze_events_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.analyze_events_id_seq OWNED BY public.analyze_events.id; + + +-- +-- Name: approval_requests; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.approval_requests ( + id bigint NOT NULL, + user_id bigint NOT NULL, + request_type character varying(40) NOT NULL, + payload jsonb NOT NULL, + status character varying(20) DEFAULT 'pending'::character varying NOT NULL, + requester character varying(20) NOT NULL, + decided_by character varying(40), + decided_at timestamp with time zone, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: approval_requests_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.approval_requests_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: approval_requests_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.approval_requests_id_seq OWNED BY public.approval_requests.id; + + +-- +-- Name: ask_events; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.ask_events ( + id bigint NOT NULL, + query text NOT NULL, + user_id bigint, + completeness text, + synthesis_status text, + confidence text, + refused boolean DEFAULT false, + classifier_verdict text, + max_rerank_score real, + aggregate_score real, + hallucination_flags jsonb DEFAULT '[]'::jsonb, + evidence_count integer, + citation_count integer, + defense_layers jsonb, + total_ms integer, + created_at timestamp with time zone DEFAULT now(), + answer_length integer, + covered_aspects jsonb, + missing_aspects jsonb, + model_name text, + prompt_version text, + source text DEFAULT 'document_server'::text NOT NULL, + eval_case_id text +); + + +-- +-- Name: ask_events_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.ask_events_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: ask_events_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.ask_events_id_seq OWNED BY public.ask_events.id; + + +-- +-- Name: audio_segments; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.audio_segments ( + id bigint NOT NULL, + document_id bigint NOT NULL, + start_s real NOT NULL, + end_s real NOT NULL, + text text NOT NULL +); + + +-- +-- Name: audio_segments_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.audio_segments_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: audio_segments_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.audio_segments_id_seq OWNED BY public.audio_segments.id; + + +-- +-- Name: automation_state; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.automation_state ( + id bigint NOT NULL, + job_name character varying(50) NOT NULL, + last_check_value text, + last_run_at timestamp with time zone, + updated_at timestamp with time zone DEFAULT now() +); + + +-- +-- Name: automation_state_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.automation_state_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: automation_state_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.automation_state_id_seq OWNED BY public.automation_state.id; + + +-- +-- Name: background_jobs; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.background_jobs ( + id bigint NOT NULL, + kind text NOT NULL, + label text, + state text DEFAULT 'running'::text NOT NULL, + processed integer DEFAULT 0 NOT NULL, + total integer, + detail jsonb DEFAULT '{}'::jsonb NOT NULL, + error text, + started_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + finished_at timestamp with time zone, + CONSTRAINT background_jobs_state_check CHECK ((state = ANY (ARRAY['running'::text, 'done'::text, 'failed'::text]))) +); + + +-- +-- Name: background_jobs_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.background_jobs_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: background_jobs_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.background_jobs_id_seq OWNED BY public.background_jobs.id; + + +-- +-- Name: briefing_topics; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.briefing_topics ( + id bigint NOT NULL, + briefing_id bigint NOT NULL, + topic_rank integer NOT NULL, + topic_label character varying(120) NOT NULL, + headline text NOT NULL, + country_perspectives jsonb DEFAULT '[]'::jsonb NOT NULL, + divergences jsonb DEFAULT '[]'::jsonb NOT NULL, + convergences jsonb DEFAULT '[]'::jsonb NOT NULL, + key_quotes jsonb DEFAULT '[]'::jsonb NOT NULL, + historical_article_ids jsonb, + historical_context text, + historical_window_days integer, + cluster_members jsonb DEFAULT '[]'::jsonb NOT NULL, + article_count integer NOT NULL, + country_count integer NOT NULL, + importance_score double precision NOT NULL, + raw_weight_sum double precision NOT NULL, + llm_model character varying(100), + llm_fallback_used boolean DEFAULT false NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + is_read boolean DEFAULT false NOT NULL, + read_at timestamp with time zone, + highlighted boolean DEFAULT false NOT NULL, + highlighted_at timestamp with time zone +); + + +-- +-- Name: briefing_topics_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.briefing_topics_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: briefing_topics_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.briefing_topics_id_seq OWNED BY public.briefing_topics.id; + + +-- +-- Name: chunk_section_analysis; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.chunk_section_analysis ( + id bigint NOT NULL, + chunk_id bigint NOT NULL, + status text NOT NULL, + summary text, + section_type text, + domain text, + confidence real, + model text, + prompt_version text NOT NULL, + source_content_hash text, + error text, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: chunk_section_analysis_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.chunk_section_analysis_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: chunk_section_analysis_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.chunk_section_analysis_id_seq OWNED BY public.chunk_section_analysis.id; + + +-- +-- Name: document_chunks; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.document_chunks ( + id bigint NOT NULL, + doc_id bigint NOT NULL, + chunk_index integer NOT NULL, + chunk_type character varying(30) NOT NULL, + section_title text, + heading_path text, + page integer, + language character varying(10), + country character varying(10), + source character varying(100), + domain_category character varying(20) NOT NULL, + text text NOT NULL, + embedding public.vector(1024), + created_at timestamp with time zone DEFAULT now(), + updated_at timestamp with time zone DEFAULT now(), + page_start integer, + page_end integer, + source_type text, + chunker_version text, + source_hash text, + chunk_content_hash text, + parent_id bigint, + level smallint, + node_type text, + is_leaf boolean DEFAULT false NOT NULL, + in_corpus boolean DEFAULT true NOT NULL, + char_start integer +); + + +-- +-- Name: corpus_chunks; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.corpus_chunks AS + SELECT id, + doc_id, + chunk_index, + chunk_type, + section_title, + heading_path, + page, + language, + country, + source, + domain_category, + text, + embedding, + created_at, + updated_at, + page_start, + page_end, + source_type, + chunker_version, + source_hash, + chunk_content_hash, + parent_id, + level, + node_type, + is_leaf, + in_corpus + FROM public.document_chunks + WHERE (in_corpus = true); + + +-- +-- Name: corpus_chunks_hier_sim_clean; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.corpus_chunks_hier_sim_clean AS + SELECT id, + doc_id, + chunk_index, + chunk_type, + section_title, + heading_path, + page, + language, + country, + source, + domain_category, + text, + embedding, + created_at, + updated_at, + page_start, + page_end, + source_type, + chunker_version, + source_hash, + chunk_content_hash, + parent_id, + level, + node_type, + is_leaf, + in_corpus + FROM public.document_chunks dc + WHERE ((embedding IS NOT NULL) AND (((source_type = 'hier_section'::text) AND (is_leaf = true) AND ((length(TRIM(BOTH FROM text)) >= 30) OR (EXISTS ( SELECT 1 + FROM public.document_chunks ch + WHERE (ch.parent_id = dc.id))))) OR ((source_type IS DISTINCT FROM 'hier_section'::text) AND (NOT (EXISTS ( SELECT 1 + FROM public.document_chunks h + WHERE ((h.doc_id = dc.doc_id) AND (h.source_type = 'hier_section'::text) AND (h.is_leaf = true) AND (h.embedding IS NOT NULL) AND ((length(TRIM(BOTH FROM h.text)) >= 30) OR (EXISTS ( SELECT 1 + FROM public.document_chunks ch2 + WHERE (ch2.parent_id = h.id))))))))))); + + +-- +-- Name: VIEW corpus_chunks_hier_sim_clean; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON VIEW public.corpus_chunks_hier_sim_clean IS 'EVAL-ONLY (Hier-Replace-Diagnose-1, NO-GO 2026-05-25). post-replace 시뮬(childless-tiny 제외). ?corpus_variant=hier_sim_clean 전용. production 검색 미사용.'; + + +-- +-- Name: corpus_chunks_hier_sim_raw; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.corpus_chunks_hier_sim_raw AS + SELECT id, + doc_id, + chunk_index, + chunk_type, + section_title, + heading_path, + page, + language, + country, + source, + domain_category, + text, + embedding, + created_at, + updated_at, + page_start, + page_end, + source_type, + chunker_version, + source_hash, + chunk_content_hash, + parent_id, + level, + node_type, + is_leaf, + in_corpus + FROM public.document_chunks dc + WHERE ((embedding IS NOT NULL) AND (((source_type = 'hier_section'::text) AND (is_leaf = true)) OR ((source_type IS DISTINCT FROM 'hier_section'::text) AND (NOT (EXISTS ( SELECT 1 + FROM public.document_chunks h + WHERE ((h.doc_id = dc.doc_id) AND (h.source_type = 'hier_section'::text) AND (h.is_leaf = true) AND (h.embedding IS NOT NULL)))))))); + + +-- +-- Name: VIEW corpus_chunks_hier_sim_raw; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON VIEW public.corpus_chunks_hier_sim_raw IS 'EVAL-ONLY (Hier-Replace-Diagnose-1, NO-GO 2026-05-25). post-replace 시뮬(raw). ?corpus_variant=hier_sim_raw 전용. production 검색 미사용.'; + + +-- +-- Name: corpus_chunks_prehier; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.corpus_chunks_prehier AS + SELECT id, + doc_id, + chunk_index, + chunk_type, + section_title, + heading_path, + page, + language, + country, + source, + domain_category, + text, + embedding, + created_at, + updated_at, + page_start, + page_end, + source_type, + chunker_version, + source_hash, + chunk_content_hash, + parent_id, + level, + node_type, + is_leaf, + in_corpus + FROM public.document_chunks + WHERE ((source_type IS DISTINCT FROM 'hier_section'::text) AND (embedding IS NOT NULL)); + + +-- +-- Name: VIEW corpus_chunks_prehier; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON VIEW public.corpus_chunks_prehier IS 'EVAL-ONLY (Hier-Replace-Diagnose-1, NO-GO 2026-05-25). pre-hier baseline. ?corpus_variant=prehier 전용. default retrieval 은 corpus_chunks 만.'; + + +-- +-- Name: csa_snapshot_20260609; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.csa_snapshot_20260609 ( + id bigint, + chunk_id bigint, + status text, + summary text, + section_type text, + domain text, + confidence real, + model text, + prompt_version text, + source_content_hash text, + error text, + created_at timestamp with time zone, + updated_at timestamp with time zone +); + + +-- +-- Name: digest_topics; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.digest_topics ( + id bigint NOT NULL, + digest_id bigint NOT NULL, + country character varying(10) NOT NULL, + topic_rank integer NOT NULL, + topic_label text NOT NULL, + summary text NOT NULL, + article_ids jsonb NOT NULL, + article_count integer NOT NULL, + importance_score double precision NOT NULL, + raw_weight_sum double precision NOT NULL, + centroid_sample jsonb, + llm_model character varying(100), + llm_fallback_used boolean DEFAULT false NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: digest_topics_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.digest_topics_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: digest_topics_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.digest_topics_id_seq OWNED BY public.digest_topics.id; + + +-- +-- Name: document_chunks_cand_qwen06; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.document_chunks_cand_qwen06 ( + id bigint NOT NULL, + doc_id bigint NOT NULL, + chunk_index integer, + section_title text, + text text, + embedding public.vector(1024) NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: document_chunks_cand_qwen4; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.document_chunks_cand_qwen4 ( + id bigint NOT NULL, + doc_id bigint NOT NULL, + chunk_index integer, + section_title text, + text text, + embedding public.vector(2560) NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: document_chunks_cand_qwen4m; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.document_chunks_cand_qwen4m ( + id bigint NOT NULL, + doc_id bigint NOT NULL, + chunk_index integer, + section_title text, + text text, + embedding public.vector(1024) NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: document_chunks_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.document_chunks_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: document_chunks_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.document_chunks_id_seq OWNED BY public.document_chunks.id; + + +-- +-- Name: document_images; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.document_images ( + id bigint NOT NULL, + document_id bigint NOT NULL, + image_key character varying(32) NOT NULL, + relative_path text NOT NULL, + file_path text NOT NULL, + mime_type text NOT NULL, + file_size bigint NOT NULL, + content_hash character varying(64) NOT NULL, + width integer, + height integer, + page_index integer, + alt_text text, + source_slug text, + extraction_engine character varying(32) DEFAULT 'marker'::character varying NOT NULL, + extraction_engine_version character varying(32), + created_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: document_images_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.document_images_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: document_images_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.document_images_id_seq OWNED BY public.document_images.id; + + +-- +-- Name: document_lineage; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.document_lineage ( + id bigint NOT NULL, + source_document_id bigint NOT NULL, + derived_document_id bigint NOT NULL, + relation_type text NOT NULL, + metadata jsonb DEFAULT '{}'::jsonb NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT document_lineage_no_self CHECK ((source_document_id <> derived_document_id)), + CONSTRAINT document_lineage_relation_type_check CHECK ((relation_type = ANY (ARRAY['cited'::text, 'summarized_from'::text, 'generated_from'::text, 'revised_from'::text]))) +); + + +-- +-- Name: document_lineage_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.document_lineage_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: document_lineage_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.document_lineage_id_seq OWNED BY public.document_lineage.id; + + +-- +-- Name: document_notes; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.document_notes ( + id bigint NOT NULL, + user_id bigint NOT NULL, + document_id bigint NOT NULL, + strokes_json jsonb, + canvas_width integer, + canvas_height integer, + schema_version integer DEFAULT 1 NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: document_notes_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.document_notes_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: document_notes_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.document_notes_id_seq OWNED BY public.document_notes.id; + + +-- +-- Name: document_reads; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.document_reads ( + id bigint NOT NULL, + user_id bigint NOT NULL, + document_id bigint NOT NULL, + read_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: document_reads_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.document_reads_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: document_reads_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.document_reads_id_seq OWNED BY public.document_reads.id; + + +-- +-- Name: documents_cand_qwen06; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.documents_cand_qwen06 ( + doc_id bigint NOT NULL, + embed_input_hash text, + embedding public.vector(1024) NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: documents_cand_qwen4; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.documents_cand_qwen4 ( + doc_id bigint NOT NULL, + embed_input_hash text, + embedding public.vector(2560) NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: documents_cand_qwen4m; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.documents_cand_qwen4m ( + doc_id bigint NOT NULL, + embed_input_hash text, + embedding public.vector(1024) NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: documents_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.documents_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: documents_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.documents_id_seq OWNED BY public.documents.id; + + +-- +-- Name: eid_review_set_draft; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.eid_review_set_draft ( + id bigint NOT NULL, + user_id bigint NOT NULL, + study_topic_id bigint, + question_ids jsonb NOT NULL, + reason character varying(40) NOT NULL, + actor character varying(20) NOT NULL, + source_weakness_id bigint, + source_generated_at timestamp with time zone NOT NULL, + supersedes_id bigint, + created_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: eid_review_set_draft_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.eid_review_set_draft_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: eid_review_set_draft_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.eid_review_set_draft_id_seq OWNED BY public.eid_review_set_draft.id; + + +-- +-- Name: eid_study_weakness; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.eid_study_weakness ( + id bigint NOT NULL, + user_id bigint NOT NULL, + weaknesses jsonb NOT NULL, + habit_signals jsonb NOT NULL, + trend_label character varying(20) NOT NULL, + sample_attempts integer DEFAULT 0 NOT NULL, + is_shallow_sample boolean DEFAULT false NOT NULL, + status character varying(20) DEFAULT 'active'::character varying NOT NULL, + supersedes_id bigint, + actor character varying(20) NOT NULL, + source_generated_at timestamp with time zone NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: eid_study_weakness_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.eid_study_weakness_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: eid_study_weakness_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.eid_study_weakness_id_seq OWNED BY public.eid_study_weakness.id; + + +-- +-- Name: eid_weekly_recap; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.eid_weekly_recap ( + id bigint NOT NULL, + user_id bigint NOT NULL, + period_start date NOT NULL, + period_end date NOT NULL, + recap jsonb NOT NULL, + trend_label character varying(20), + status character varying(20) DEFAULT 'active'::character varying NOT NULL, + supersedes_id bigint, + actor character varying(20) NOT NULL, + source_generated_at timestamp with time zone NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: eid_weekly_recap_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.eid_weekly_recap_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: eid_weekly_recap_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.eid_weekly_recap_id_seq OWNED BY public.eid_weekly_recap.id; + + +-- +-- Name: events; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.events ( + id bigint NOT NULL, + title text NOT NULL, + description text, + kind public.event_kind NOT NULL, + status public.event_status DEFAULT 'inbox'::public.event_status NOT NULL, + due_at timestamp with time zone, + start_at timestamp with time zone, + end_at timestamp with time zone, + started_at timestamp with time zone, + ended_at timestamp with time zone, + all_day boolean DEFAULT false NOT NULL, + timezone text, + defer_until timestamp with time zone, + completed_at timestamp with time zone, + cancelled_at timestamp with time zone, + priority smallint, + project_tag character varying(64), + tags jsonb DEFAULT '[]'::jsonb NOT NULL, + source public.event_source DEFAULT 'manual'::public.event_source NOT NULL, + source_ref text, + raw_metadata jsonb DEFAULT '{}'::jsonb NOT NULL, + memo_document_id bigint, + user_id bigint NOT NULL, + created_by public.event_actor DEFAULT 'manual'::public.event_actor NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT events_activity_log_requires_time CHECK (((kind <> 'activity_log'::public.event_kind) OR (started_at IS NOT NULL) OR (ended_at IS NOT NULL))), + CONSTRAINT events_calendar_event_requires_start CHECK (((kind <> 'calendar_event'::public.event_kind) OR (start_at IS NOT NULL))), + CONSTRAINT events_priority_check CHECK (((priority >= 1) AND (priority <= 4))) +); + + +-- +-- Name: events_history; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.events_history ( + id bigint NOT NULL, + event_id bigint NOT NULL, + changed_at timestamp with time zone DEFAULT now() NOT NULL, + changed_by public.event_actor NOT NULL, + change_kind public.history_change_kind NOT NULL, + before jsonb, + after jsonb NOT NULL +); + + +-- +-- Name: events_history_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.events_history_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: events_history_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.events_history_id_seq OWNED BY public.events_history.id; + + +-- +-- Name: events_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.events_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: events_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.events_id_seq OWNED BY public.events.id; + + +-- +-- Name: facet_values; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.facet_values ( + id bigint NOT NULL, + facet_type text NOT NULL, + value text NOT NULL, + is_system boolean DEFAULT false NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: facet_values_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.facet_values_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: facet_values_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.facet_values_id_seq OWNED BY public.facet_values.id; + + +-- +-- Name: global_digests; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.global_digests ( + id bigint NOT NULL, + digest_date date NOT NULL, + window_start timestamp with time zone NOT NULL, + window_end timestamp with time zone NOT NULL, + decay_lambda double precision NOT NULL, + total_articles integer DEFAULT 0 NOT NULL, + total_countries integer DEFAULT 0 NOT NULL, + total_topics integer DEFAULT 0 NOT NULL, + generation_ms integer, + llm_calls integer DEFAULT 0 NOT NULL, + llm_failures integer DEFAULT 0 NOT NULL, + status character varying(20) DEFAULT 'success'::character varying NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: global_digests_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.global_digests_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: global_digests_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.global_digests_id_seq OWNED BY public.global_digests.id; + + +-- +-- Name: hier_snapshot_20260609; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.hier_snapshot_20260609 ( + id bigint, + doc_id bigint, + chunk_index integer, + chunk_type character varying(30), + section_title text, + heading_path text, + page integer, + language character varying(10), + country character varying(10), + source character varying(100), + domain_category character varying(20), + text text, + embedding public.vector(1024), + created_at timestamp with time zone, + updated_at timestamp with time zone, + page_start integer, + page_end integer, + source_type text, + chunker_version text, + source_hash text, + chunk_content_hash text, + parent_id bigint, + level smallint, + node_type text, + is_leaf boolean, + in_corpus boolean, + char_start integer +); + + +-- +-- Name: legal_acts; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.legal_acts ( + family_id text NOT NULL, + jurisdiction text NOT NULL, + law_level text NOT NULL, + title text NOT NULL, + title_ko text, + parent_family_id text, + native_id text NOT NULL, + source_api text NOT NULL, + watch boolean DEFAULT true NOT NULL, + poll_cycle text DEFAULT 'daily'::text NOT NULL, + watermark text, + repeal_detected_at timestamp with time zone, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT legal_acts_jurisdiction_check CHECK ((jurisdiction = ANY (ARRAY['KR'::text, 'US'::text, 'EU'::text, 'JP'::text, 'GB'::text, 'INT'::text]))), + CONSTRAINT legal_acts_law_level_check CHECK ((law_level = ANY (ARRAY['statute'::text, 'decree'::text, 'rule'::text, 'admin_rule'::text, 'code'::text]))), + CONSTRAINT legal_acts_poll_cycle_check CHECK ((poll_cycle = ANY (ARRAY['daily'::text, 'weekly'::text, 'monthly'::text, 'quarterly'::text]))) +); + + +-- +-- Name: legal_meta; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.legal_meta ( + document_id bigint NOT NULL, + family_id text NOT NULL, + law_doc_kind text DEFAULT 'primary'::text NOT NULL, + version_key text NOT NULL, + promulgation_date date, + effective_date date, + version_status text DEFAULT 'pending'::text NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT legal_meta_law_doc_kind_check CHECK ((law_doc_kind = ANY (ARRAY['primary'::text, 'annex'::text, 'interpretation'::text]))), + CONSTRAINT legal_meta_version_status_check CHECK ((version_status = ANY (ARRAY['pending'::text, 'current'::text, 'superseded'::text, 'repealed'::text]))) +); + + +-- +-- Name: library_categories; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.library_categories ( + id bigint NOT NULL, + path text NOT NULL, + name text NOT NULL, + parent_path text, + depth integer DEFAULT 1 NOT NULL, + is_system boolean DEFAULT false NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: library_categories_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.library_categories_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: library_categories_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.library_categories_id_seq OWNED BY public.library_categories.id; + + +-- +-- Name: morning_briefings; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.morning_briefings ( + id bigint NOT NULL, + briefing_date date NOT NULL, + window_start timestamp with time zone NOT NULL, + window_end timestamp with time zone NOT NULL, + decay_lambda double precision NOT NULL, + total_articles integer DEFAULT 0 NOT NULL, + total_countries integer DEFAULT 0 NOT NULL, + total_topics integer DEFAULT 0 NOT NULL, + generation_ms integer, + llm_calls integer DEFAULT 0 NOT NULL, + llm_failures integer DEFAULT 0 NOT NULL, + status character varying(20) DEFAULT 'success'::character varying NOT NULL, + headline_oneliner text, + created_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: morning_briefings_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.morning_briefings_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: morning_briefings_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.morning_briefings_id_seq OWNED BY public.morning_briefings.id; + + +-- +-- Name: news_sources; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.news_sources ( + id integer NOT NULL, + name character varying(100) NOT NULL, + country character varying(10), + feed_url text NOT NULL, + feed_type character varying(20) DEFAULT 'rss'::character varying, + category character varying(50), + language character varying(10), + enabled boolean DEFAULT true, + last_fetched_at timestamp with time zone, + created_at timestamp with time zone DEFAULT now(), + fetch_method character varying(20) DEFAULT 'rss'::character varying NOT NULL, + fulltext_policy character varying(20) DEFAULT 'none'::character varying NOT NULL, + auth_profile character varying(50), + poll_interval_minutes integer, + etag text, + last_modified text, + feed_content_hash character varying(64), + selector_override jsonb, + parser_quirk character varying(30), + source_channel public.source_channel DEFAULT 'news'::public.source_channel NOT NULL, + material_type text, + license_scheme text, + license_redistribute boolean, + CONSTRAINT news_sources_material_type_check CHECK ((material_type = ANY (ARRAY['law'::text, 'paper'::text, 'book'::text, 'incident'::text, 'manual'::text, 'standard'::text, 'guide'::text]))) +); + + +-- +-- Name: news_sources_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.news_sources_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: news_sources_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.news_sources_id_seq OWNED BY public.news_sources.id; + + +-- +-- Name: processing_queue; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.processing_queue ( + id bigint NOT NULL, + document_id bigint NOT NULL, + stage public.process_stage NOT NULL, + status public.process_status DEFAULT 'pending'::public.process_status, + attempts smallint DEFAULT 0, + max_attempts smallint DEFAULT 3, + error_message text, + created_at timestamp with time zone DEFAULT now(), + started_at timestamp with time zone, + completed_at timestamp with time zone, + payload jsonb +); + + +-- +-- Name: processing_queue_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.processing_queue_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: processing_queue_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.processing_queue_id_seq OWNED BY public.processing_queue.id; + + +-- +-- Name: schema_migrations; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.schema_migrations ( + version integer NOT NULL, + name text NOT NULL, + applied_at timestamp with time zone DEFAULT now() +); + + +-- +-- Name: search_failure_logs; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.search_failure_logs ( + id bigint NOT NULL, + query text NOT NULL, + user_id bigint, + created_at timestamp with time zone DEFAULT now() NOT NULL, + result_count integer NOT NULL, + confidence double precision, + failure_reason character varying(30) NOT NULL, + context jsonb, + reviewed boolean DEFAULT false NOT NULL +); + + +-- +-- Name: search_failure_logs_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.search_failure_logs_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: search_failure_logs_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.search_failure_logs_id_seq OWNED BY public.search_failure_logs.id; + + +-- +-- Name: source_health; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.source_health ( + id integer NOT NULL, + source_id integer NOT NULL, + consecutive_failures integer DEFAULT 0 NOT NULL, + total_fetches bigint DEFAULT 0 NOT NULL, + total_failures bigint DEFAULT 0 NOT NULL, + last_success_at timestamp with time zone, + last_error text, + last_error_at timestamp with time zone, + last_fetch_items integer, + empty_streak integer DEFAULT 0 NOT NULL, + circuit_state character varying(10) DEFAULT 'closed'::character varying NOT NULL, + circuit_opened_at timestamp with time zone, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + relogin_requested boolean DEFAULT false NOT NULL, + last_probe_at timestamp with time zone, + last_probe_ok boolean +); + + +-- +-- Name: source_health_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.source_health_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: source_health_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.source_health_id_seq OWNED BY public.source_health.id; + + +-- +-- Name: study_memo_card_evidence; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.study_memo_card_evidence ( + id bigint NOT NULL, + card_id bigint NOT NULL, + source_type character varying(40) NOT NULL, + source_id bigint, + chunk_index integer, + snippet text, + created_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: study_memo_card_evidence_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.study_memo_card_evidence_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: study_memo_card_evidence_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.study_memo_card_evidence_id_seq OWNED BY public.study_memo_card_evidence.id; + + +-- +-- Name: study_memo_card_jobs; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.study_memo_card_jobs ( + id bigint NOT NULL, + user_id bigint NOT NULL, + source_kind character varying(40) NOT NULL, + source_id bigint NOT NULL, + source_version timestamp with time zone, + kind character varying(40) NOT NULL, + status character varying(20) DEFAULT 'pending'::character varying NOT NULL, + attempts smallint DEFAULT 0 NOT NULL, + max_attempts smallint DEFAULT 2 NOT NULL, + error_code character varying(40), + error_message text, + payload jsonb, + created_at timestamp with time zone DEFAULT now() NOT NULL, + started_at timestamp with time zone, + completed_at timestamp with time zone +); + + +-- +-- Name: study_memo_card_jobs_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.study_memo_card_jobs_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: study_memo_card_jobs_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.study_memo_card_jobs_id_seq OWNED BY public.study_memo_card_jobs.id; + + +-- +-- Name: study_memo_card_progress; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.study_memo_card_progress ( + id bigint NOT NULL, + user_id bigint NOT NULL, + study_topic_id bigint NOT NULL, + card_id bigint NOT NULL, + last_outcome character varying(20), + last_reviewed_at timestamp with time zone, + due_at timestamp with time zone, + review_stage smallint, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: study_memo_card_progress_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.study_memo_card_progress_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: study_memo_card_progress_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.study_memo_card_progress_id_seq OWNED BY public.study_memo_card_progress.id; + + +-- +-- Name: study_memo_cards; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.study_memo_cards ( + id bigint NOT NULL, + user_id bigint NOT NULL, + study_topic_id bigint NOT NULL, + source_kind character varying(40) NOT NULL, + source_question_id bigint, + source_subject_note_id bigint, + format character varying(20) NOT NULL, + cue text NOT NULL, + fact text NOT NULL, + cloze_text text, + extra jsonb, + source_generated_at timestamp with time zone, + dedup_hash character varying(64) NOT NULL, + needs_review boolean DEFAULT true NOT NULL, + flagged_at timestamp with time zone, + flagged_by character varying(40), + model character varying(120), + generated_at timestamp with time zone, + created_at timestamp with time zone DEFAULT now() NOT NULL, + deleted_at timestamp with time zone, + view_count integer DEFAULT 0 NOT NULL, + last_viewed_at timestamp with time zone +); + + +-- +-- Name: study_memo_cards_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.study_memo_cards_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: study_memo_cards_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.study_memo_cards_id_seq OWNED BY public.study_memo_cards.id; + + +-- +-- Name: study_question_attempts; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.study_question_attempts ( + id bigint NOT NULL, + user_id bigint NOT NULL, + study_question_id bigint NOT NULL, + study_topic_id bigint NOT NULL, + selected_choice smallint, + correct_choice smallint NOT NULL, + is_correct boolean NOT NULL, + answered_at timestamp with time zone DEFAULT now() NOT NULL, + outcome character varying(20) NOT NULL, + quiz_session_id bigint, + reviewed_at timestamp with time zone, + CONSTRAINT study_question_attempts_correct_choice_check CHECK (((correct_choice >= 1) AND (correct_choice <= 4))), + CONSTRAINT study_question_attempts_selected_choice_check CHECK (((selected_choice IS NULL) OR ((selected_choice >= 1) AND (selected_choice <= 4)))) +); + + +-- +-- Name: study_question_attempts_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.study_question_attempts_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: study_question_attempts_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.study_question_attempts_id_seq OWNED BY public.study_question_attempts.id; + + +-- +-- Name: study_question_images; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.study_question_images ( + id bigint NOT NULL, + user_id bigint NOT NULL, + study_question_id bigint NOT NULL, + file_path text NOT NULL, + file_size bigint NOT NULL, + mime_type character varying(80) NOT NULL, + sort_order integer DEFAULT 0 NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: study_question_images_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.study_question_images_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: study_question_images_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.study_question_images_id_seq OWNED BY public.study_question_images.id; + + +-- +-- Name: study_question_jobs; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.study_question_jobs ( + id bigint NOT NULL, + study_question_id bigint NOT NULL, + user_id bigint NOT NULL, + kind character varying(40) NOT NULL, + status character varying(20) DEFAULT 'pending'::character varying NOT NULL, + attempts smallint DEFAULT 0 NOT NULL, + max_attempts smallint DEFAULT 2 NOT NULL, + error_code character varying(40), + error_message text, + payload jsonb, + created_at timestamp with time zone DEFAULT now() NOT NULL, + started_at timestamp with time zone, + completed_at timestamp with time zone +); + + +-- +-- Name: study_question_jobs_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.study_question_jobs_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: study_question_jobs_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.study_question_jobs_id_seq OWNED BY public.study_question_jobs.id; + + +-- +-- Name: study_question_progress; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.study_question_progress ( + id bigint NOT NULL, + user_id bigint NOT NULL, + study_topic_id bigint NOT NULL, + study_question_id bigint NOT NULL, + last_outcome character varying(20), + last_attempted_at timestamp with time zone, + last_attempt_id bigint, + last_reviewed_at timestamp with time zone, + due_at timestamp with time zone, + review_stage smallint, + pattern_state character varying(30), + pattern_updated_at timestamp with time zone, + pattern_window_attempts smallint, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: study_question_progress_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.study_question_progress_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: study_question_progress_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.study_question_progress_id_seq OWNED BY public.study_question_progress.id; + + +-- +-- Name: study_questions; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.study_questions ( + id bigint NOT NULL, + user_id bigint NOT NULL, + study_topic_id bigint NOT NULL, + question_text text NOT NULL, + choice_1 text NOT NULL, + choice_2 text NOT NULL, + choice_3 text NOT NULL, + choice_4 text NOT NULL, + correct_choice smallint NOT NULL, + subject character varying(120), + scope character varying(200), + exam_name character varying(120), + exam_round character varying(120), + explanation text, + source_note text, + is_active boolean DEFAULT true NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + deleted_at timestamp with time zone, + ai_explanation text, + ai_explanation_status character varying(20) DEFAULT 'none'::character varying NOT NULL, + ai_explanation_generated_at timestamp with time zone, + ai_explanation_model character varying(120), + embedding public.vector(1024), + embedding_status character varying(20) DEFAULT 'none'::character varying NOT NULL, + embedding_updated_at timestamp with time zone, + embedding_model character varying(120), + exam_question_number smallint, + related_repeat jsonb, + related_similar jsonb, + related_repeat_round_count integer, + related_similar_round_count integer, + related_repeat_grade character varying(50), + related_computed_at timestamp with time zone, + related_threshold_version character varying(20), + needs_review boolean DEFAULT false NOT NULL, + flagged_at timestamp with time zone, + flagged_by character varying(40), + CONSTRAINT study_questions_correct_choice_check CHECK (((correct_choice >= 1) AND (correct_choice <= 4))), + CONSTRAINT study_questions_exam_question_number_check CHECK (((exam_question_number IS NULL) OR (exam_question_number > 0))) +); + + +-- +-- Name: study_questions_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.study_questions_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: study_questions_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.study_questions_id_seq OWNED BY public.study_questions.id; + + +-- +-- Name: study_quiz_session_analysis; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.study_quiz_session_analysis ( + study_quiz_session_id bigint NOT NULL, + user_id bigint NOT NULL, + summary_md text NOT NULL, + confidence character varying(10), + model_name character varying(120), + generated_at timestamp with time zone DEFAULT now() NOT NULL, + is_stale boolean DEFAULT false NOT NULL +); + + +-- +-- Name: study_quiz_session_jobs; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.study_quiz_session_jobs ( + id bigint NOT NULL, + study_quiz_session_id bigint NOT NULL, + user_id bigint NOT NULL, + status character varying(20) DEFAULT 'pending'::character varying NOT NULL, + attempts smallint DEFAULT 0 NOT NULL, + max_attempts smallint DEFAULT 2 NOT NULL, + error_code character varying(40), + error_message text, + payload jsonb, + created_at timestamp with time zone DEFAULT now() NOT NULL, + started_at timestamp with time zone, + completed_at timestamp with time zone +); + + +-- +-- Name: study_quiz_session_jobs_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.study_quiz_session_jobs_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: study_quiz_session_jobs_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.study_quiz_session_jobs_id_seq OWNED BY public.study_quiz_session_jobs.id; + + +-- +-- Name: study_quiz_sessions; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.study_quiz_sessions ( + id bigint NOT NULL, + user_id bigint NOT NULL, + study_topic_id bigint NOT NULL, + target_per_subject integer DEFAULT 20 NOT NULL, + subject_filter character varying(120), + wrong_only boolean DEFAULT false NOT NULL, + question_ids jsonb NOT NULL, + subject_distribution jsonb DEFAULT '{}'::jsonb NOT NULL, + status character varying(20) DEFAULT 'in_progress'::character varying NOT NULL, + cursor integer DEFAULT 0 NOT NULL, + correct_count integer DEFAULT 0 NOT NULL, + wrong_count integer DEFAULT 0 NOT NULL, + unsure_count integer DEFAULT 0 NOT NULL, + finished_at timestamp with time zone, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + quiz_mode character varying(30) DEFAULT 'random'::character varying NOT NULL, + newly_correct_count integer DEFAULT 0 NOT NULL, + relapsed_count integer DEFAULT 0 NOT NULL, + recovered_count integer DEFAULT 0 NOT NULL, + chronic_remaining_count integer DEFAULT 0 NOT NULL +); + + +-- +-- Name: study_quiz_sessions_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.study_quiz_sessions_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: study_quiz_sessions_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.study_quiz_sessions_id_seq OWNED BY public.study_quiz_sessions.id; + + +-- +-- Name: study_reminders; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.study_reminders ( + id bigint NOT NULL, + user_id bigint NOT NULL, + study_topic_id bigint, + due_count integer, + focus_topic_names jsonb, + fired_at timestamp with time zone NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: study_reminders_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.study_reminders_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: study_reminders_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.study_reminders_id_seq OWNED BY public.study_reminders.id; + + +-- +-- Name: study_session_assets; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.study_session_assets ( + id bigint NOT NULL, + study_session_id bigint NOT NULL, + document_id bigint NOT NULL, + asset_type character varying(30) NOT NULL, + role character varying(40), + sort_order integer DEFAULT 0 NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: study_session_assets_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.study_session_assets_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: study_session_assets_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.study_session_assets_id_seq OWNED BY public.study_session_assets.id; + + +-- +-- Name: study_sessions; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.study_sessions ( + id bigint NOT NULL, + user_id bigint NOT NULL, + study_type character varying(30) DEFAULT 'certification'::character varying NOT NULL, + certification character varying(120), + language_code character varying(20), + learning_level character varying(80), + subject character varying(120), + topic character varying(200), + source_text text, + source_page integer, + mode character varying(30) DEFAULT 'copy'::character varying NOT NULL, + prompt_question text, + expected_answer text, + metadata jsonb, + target_count integer, + repetition_count integer DEFAULT 0 NOT NULL, + strokes_json jsonb, + canvas_width integer, + canvas_height integer, + schema_version integer DEFAULT 1 NOT NULL, + ocr_text text, + user_corrected_text text, + ai_summary text, + review_state character varying(20), + next_review_at timestamp with time zone, + last_quiz_at timestamp with time zone, + correct_count integer DEFAULT 0 NOT NULL, + incorrect_count integer DEFAULT 0 NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + study_topic_id bigint +); + + +-- +-- Name: study_sessions_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.study_sessions_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: study_sessions_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.study_sessions_id_seq OWNED BY public.study_sessions.id; + + +-- +-- Name: study_topic_documents; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.study_topic_documents ( + study_topic_id bigint NOT NULL, + document_id bigint NOT NULL, + user_id bigint NOT NULL, + sort_order integer DEFAULT 0 NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: study_topic_subject_notes; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.study_topic_subject_notes ( + id bigint NOT NULL, + user_id bigint NOT NULL, + study_topic_id bigint NOT NULL, + subject character varying(120) NOT NULL, + scope character varying(200) DEFAULT ''::character varying NOT NULL, + content text, + status character varying(20) DEFAULT 'none'::character varying NOT NULL, + generated_at timestamp with time zone, + model character varying(120), + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: study_topic_subject_notes_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.study_topic_subject_notes_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: study_topic_subject_notes_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.study_topic_subject_notes_id_seq OWNED BY public.study_topic_subject_notes.id; + + +-- +-- Name: study_topics; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.study_topics ( + id bigint NOT NULL, + user_id bigint NOT NULL, + name character varying(120) NOT NULL, + description text, + color character varying(20), + study_type character varying(40), + sort_order integer DEFAULT 0 NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + deleted_at timestamp with time zone, + exam_round_size integer, + exam_subjects jsonb DEFAULT '[]'::jsonb NOT NULL, + focused_at timestamp with time zone, + CONSTRAINT study_topics_exam_round_size_check CHECK (((exam_round_size IS NULL) OR ((exam_round_size >= 1) AND (exam_round_size <= 300)))) +); + + +-- +-- Name: study_topics_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.study_topics_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: study_topics_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.study_topics_id_seq OWNED BY public.study_topics.id; + + +-- +-- Name: tasks; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.tasks ( + id bigint NOT NULL, + caldav_uid text, + title text NOT NULL, + description text, + due_date timestamp with time zone, + priority smallint DEFAULT 0, + completed boolean DEFAULT false, + completed_at timestamp with time zone, + document_id bigint, + source character varying(50), + created_at timestamp with time zone DEFAULT now(), + updated_at timestamp with time zone DEFAULT now() +); + + +-- +-- Name: tasks_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.tasks_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: tasks_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.tasks_id_seq OWNED BY public.tasks.id; + + +-- +-- Name: users; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.users ( + id bigint NOT NULL, + username character varying(50) NOT NULL, + password_hash text NOT NULL, + totp_secret character varying(64), + is_active boolean DEFAULT true, + created_at timestamp with time zone DEFAULT now(), + last_login_at timestamp with time zone, + is_admin boolean DEFAULT false NOT NULL, + password_changed_at timestamp with time zone +); + + +-- +-- Name: users_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.users_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: users_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.users_id_seq OWNED BY public.users.id; + + +-- +-- Name: v_schedule_defer_pattern; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_schedule_defer_pattern AS + SELECT event_id, + (count(*))::integer AS defer_reschedule_count, + max(changed_at) AS last_changed_at, + (count(*) >= 3) AS is_repeat_defer + FROM public.events_history eh + WHERE (change_kind = ANY (ARRAY['defer'::public.history_change_kind, 'reschedule'::public.history_change_kind])) + GROUP BY event_id; + + +-- +-- Name: v_schedule_today; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_schedule_today AS + SELECT e.id, + e.user_id, + e.title, + e.kind, + e.status, + e.priority, + e.due_at, + e.start_at, + e.end_at, + e.started_at, + e.defer_until, + e.project_tag + FROM (public.events e + CROSS JOIN LATERAL ( SELECT (date_trunc('day'::text, (now() AT TIME ZONE 'Asia/Seoul'::text)) AT TIME ZONE 'Asia/Seoul'::text) AS lo) b) + WHERE (((e.status = ANY (ARRAY['inbox'::public.event_status, 'next'::public.event_status, 'scheduled'::public.event_status, 'in_progress'::public.event_status])) OR ((e.status = 'deferred'::public.event_status) AND (e.defer_until IS NOT NULL) AND (e.defer_until <= now()))) AND (((e.kind = 'task'::public.event_kind) AND (e.due_at >= b.lo) AND (e.due_at < (b.lo + '1 day'::interval))) OR ((e.kind = 'calendar_event'::public.event_kind) AND (e.start_at >= b.lo) AND (e.start_at < (b.lo + '1 day'::interval))) OR ((e.kind = 'activity_log'::public.event_kind) AND (e.started_at >= b.lo) AND (e.started_at < (b.lo + '1 day'::interval))))); + + +-- +-- Name: worker_capabilities; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.worker_capabilities ( + worker_id text NOT NULL, + user_id bigint NOT NULL, + device_label text NOT NULL, + worker_class text NOT NULL, + tier text NOT NULL, + capabilities jsonb DEFAULT '[]'::jsonb NOT NULL, + models_loaded jsonb DEFAULT '[]'::jsonb NOT NULL, + endpoint text, + created_at timestamp with time zone DEFAULT now() NOT NULL, + last_registered_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: worker_heartbeats; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.worker_heartbeats ( + id bigint NOT NULL, + worker_id text NOT NULL, + heartbeat_at timestamp with time zone DEFAULT now() NOT NULL, + status text NOT NULL, + current_job_id bigint, + battery text, + thermal text, + raw_payload jsonb DEFAULT '{}'::jsonb NOT NULL +); + + +-- +-- Name: worker_heartbeats_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.worker_heartbeats_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: worker_heartbeats_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.worker_heartbeats_id_seq OWNED BY public.worker_heartbeats.id; + + +-- +-- Name: worker_jobs; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.worker_jobs ( + id bigint NOT NULL, + user_id bigint NOT NULL, + job_type text NOT NULL, + status text DEFAULT 'pending'::text NOT NULL, + worker_id text, + payload jsonb DEFAULT '{}'::jsonb NOT NULL, + result jsonb, + error_message text, + attempts smallint DEFAULT 0 NOT NULL, + max_attempts smallint DEFAULT 3 NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + claimed_at timestamp with time zone, + completed_at timestamp with time zone, + CONSTRAINT worker_jobs_status_check CHECK ((status = ANY (ARRAY['pending'::text, 'processing'::text, 'completed'::text, 'failed'::text]))) +); + + +-- +-- Name: worker_jobs_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.worker_jobs_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: worker_jobs_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.worker_jobs_id_seq OWNED BY public.worker_jobs.id; + + +-- +-- Name: analyze_events id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.analyze_events ALTER COLUMN id SET DEFAULT nextval('public.analyze_events_id_seq'::regclass); + + +-- +-- Name: approval_requests id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.approval_requests ALTER COLUMN id SET DEFAULT nextval('public.approval_requests_id_seq'::regclass); + + +-- +-- Name: ask_events id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.ask_events ALTER COLUMN id SET DEFAULT nextval('public.ask_events_id_seq'::regclass); + + +-- +-- Name: audio_segments id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.audio_segments ALTER COLUMN id SET DEFAULT nextval('public.audio_segments_id_seq'::regclass); + + +-- +-- Name: automation_state id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.automation_state ALTER COLUMN id SET DEFAULT nextval('public.automation_state_id_seq'::regclass); + + +-- +-- Name: background_jobs id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.background_jobs ALTER COLUMN id SET DEFAULT nextval('public.background_jobs_id_seq'::regclass); + + +-- +-- Name: briefing_topics id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.briefing_topics ALTER COLUMN id SET DEFAULT nextval('public.briefing_topics_id_seq'::regclass); + + +-- +-- Name: chunk_section_analysis id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.chunk_section_analysis ALTER COLUMN id SET DEFAULT nextval('public.chunk_section_analysis_id_seq'::regclass); + + +-- +-- Name: digest_topics id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.digest_topics ALTER COLUMN id SET DEFAULT nextval('public.digest_topics_id_seq'::regclass); + + +-- +-- Name: document_chunks id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.document_chunks ALTER COLUMN id SET DEFAULT nextval('public.document_chunks_id_seq'::regclass); + + +-- +-- Name: document_images id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.document_images ALTER COLUMN id SET DEFAULT nextval('public.document_images_id_seq'::regclass); + + +-- +-- Name: document_lineage id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.document_lineage ALTER COLUMN id SET DEFAULT nextval('public.document_lineage_id_seq'::regclass); + + +-- +-- Name: document_notes id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.document_notes ALTER COLUMN id SET DEFAULT nextval('public.document_notes_id_seq'::regclass); + + +-- +-- Name: document_reads id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.document_reads ALTER COLUMN id SET DEFAULT nextval('public.document_reads_id_seq'::regclass); + + +-- +-- Name: documents id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.documents ALTER COLUMN id SET DEFAULT nextval('public.documents_id_seq'::regclass); + + +-- +-- Name: eid_review_set_draft id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.eid_review_set_draft ALTER COLUMN id SET DEFAULT nextval('public.eid_review_set_draft_id_seq'::regclass); + + +-- +-- Name: eid_study_weakness id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.eid_study_weakness ALTER COLUMN id SET DEFAULT nextval('public.eid_study_weakness_id_seq'::regclass); + + +-- +-- Name: eid_weekly_recap id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.eid_weekly_recap ALTER COLUMN id SET DEFAULT nextval('public.eid_weekly_recap_id_seq'::regclass); + + +-- +-- Name: events id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.events ALTER COLUMN id SET DEFAULT nextval('public.events_id_seq'::regclass); + + +-- +-- Name: events_history id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.events_history ALTER COLUMN id SET DEFAULT nextval('public.events_history_id_seq'::regclass); + + +-- +-- Name: facet_values id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.facet_values ALTER COLUMN id SET DEFAULT nextval('public.facet_values_id_seq'::regclass); + + +-- +-- Name: global_digests id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.global_digests ALTER COLUMN id SET DEFAULT nextval('public.global_digests_id_seq'::regclass); + + +-- +-- Name: library_categories id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.library_categories ALTER COLUMN id SET DEFAULT nextval('public.library_categories_id_seq'::regclass); + + +-- +-- Name: morning_briefings id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.morning_briefings ALTER COLUMN id SET DEFAULT nextval('public.morning_briefings_id_seq'::regclass); + + +-- +-- Name: news_sources id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.news_sources ALTER COLUMN id SET DEFAULT nextval('public.news_sources_id_seq'::regclass); + + +-- +-- Name: processing_queue id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.processing_queue ALTER COLUMN id SET DEFAULT nextval('public.processing_queue_id_seq'::regclass); + + +-- +-- Name: search_failure_logs id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.search_failure_logs ALTER COLUMN id SET DEFAULT nextval('public.search_failure_logs_id_seq'::regclass); + + +-- +-- Name: source_health id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.source_health ALTER COLUMN id SET DEFAULT nextval('public.source_health_id_seq'::regclass); + + +-- +-- Name: study_memo_card_evidence id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.study_memo_card_evidence ALTER COLUMN id SET DEFAULT nextval('public.study_memo_card_evidence_id_seq'::regclass); + + +-- +-- Name: study_memo_card_jobs id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.study_memo_card_jobs ALTER COLUMN id SET DEFAULT nextval('public.study_memo_card_jobs_id_seq'::regclass); + + +-- +-- Name: study_memo_card_progress id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.study_memo_card_progress ALTER COLUMN id SET DEFAULT nextval('public.study_memo_card_progress_id_seq'::regclass); + + +-- +-- Name: study_memo_cards id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.study_memo_cards ALTER COLUMN id SET DEFAULT nextval('public.study_memo_cards_id_seq'::regclass); + + +-- +-- Name: study_question_attempts id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.study_question_attempts ALTER COLUMN id SET DEFAULT nextval('public.study_question_attempts_id_seq'::regclass); + + +-- +-- Name: study_question_images id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.study_question_images ALTER COLUMN id SET DEFAULT nextval('public.study_question_images_id_seq'::regclass); + + +-- +-- Name: study_question_jobs id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.study_question_jobs ALTER COLUMN id SET DEFAULT nextval('public.study_question_jobs_id_seq'::regclass); + + +-- +-- Name: study_question_progress id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.study_question_progress ALTER COLUMN id SET DEFAULT nextval('public.study_question_progress_id_seq'::regclass); + + +-- +-- Name: study_questions id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.study_questions ALTER COLUMN id SET DEFAULT nextval('public.study_questions_id_seq'::regclass); + + +-- +-- Name: study_quiz_session_jobs id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.study_quiz_session_jobs ALTER COLUMN id SET DEFAULT nextval('public.study_quiz_session_jobs_id_seq'::regclass); + + +-- +-- Name: study_quiz_sessions id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.study_quiz_sessions ALTER COLUMN id SET DEFAULT nextval('public.study_quiz_sessions_id_seq'::regclass); + + +-- +-- Name: study_reminders id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.study_reminders ALTER COLUMN id SET DEFAULT nextval('public.study_reminders_id_seq'::regclass); + + +-- +-- Name: study_session_assets id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.study_session_assets ALTER COLUMN id SET DEFAULT nextval('public.study_session_assets_id_seq'::regclass); + + +-- +-- Name: study_sessions id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.study_sessions ALTER COLUMN id SET DEFAULT nextval('public.study_sessions_id_seq'::regclass); + + +-- +-- Name: study_topic_subject_notes id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.study_topic_subject_notes ALTER COLUMN id SET DEFAULT nextval('public.study_topic_subject_notes_id_seq'::regclass); + + +-- +-- Name: study_topics id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.study_topics ALTER COLUMN id SET DEFAULT nextval('public.study_topics_id_seq'::regclass); + + +-- +-- Name: tasks id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.tasks ALTER COLUMN id SET DEFAULT nextval('public.tasks_id_seq'::regclass); + + +-- +-- Name: users id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.users ALTER COLUMN id SET DEFAULT nextval('public.users_id_seq'::regclass); + + +-- +-- Name: worker_heartbeats id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.worker_heartbeats ALTER COLUMN id SET DEFAULT nextval('public.worker_heartbeats_id_seq'::regclass); + + +-- +-- Name: worker_jobs id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.worker_jobs ALTER COLUMN id SET DEFAULT nextval('public.worker_jobs_id_seq'::regclass); + + +-- +-- Name: analyze_events analyze_events_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.analyze_events + ADD CONSTRAINT analyze_events_pkey PRIMARY KEY (id); + + +-- +-- Name: approval_requests approval_requests_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.approval_requests + ADD CONSTRAINT approval_requests_pkey PRIMARY KEY (id); + + +-- +-- Name: ask_events ask_events_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.ask_events + ADD CONSTRAINT ask_events_pkey PRIMARY KEY (id); + + +-- +-- Name: audio_segments audio_segments_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.audio_segments + ADD CONSTRAINT audio_segments_pkey PRIMARY KEY (id); + + +-- +-- Name: automation_state automation_state_job_name_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.automation_state + ADD CONSTRAINT automation_state_job_name_key UNIQUE (job_name); + + +-- +-- Name: automation_state automation_state_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.automation_state + ADD CONSTRAINT automation_state_pkey PRIMARY KEY (id); + + +-- +-- Name: background_jobs background_jobs_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.background_jobs + ADD CONSTRAINT background_jobs_pkey PRIMARY KEY (id); + + +-- +-- Name: briefing_topics briefing_topics_briefing_id_topic_rank_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.briefing_topics + ADD CONSTRAINT briefing_topics_briefing_id_topic_rank_key UNIQUE (briefing_id, topic_rank); + + +-- +-- Name: briefing_topics briefing_topics_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.briefing_topics + ADD CONSTRAINT briefing_topics_pkey PRIMARY KEY (id); + + +-- +-- Name: chunk_section_analysis chunk_section_analysis_chunk_id_prompt_version_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.chunk_section_analysis + ADD CONSTRAINT chunk_section_analysis_chunk_id_prompt_version_key UNIQUE (chunk_id, prompt_version); + + +-- +-- Name: chunk_section_analysis chunk_section_analysis_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.chunk_section_analysis + ADD CONSTRAINT chunk_section_analysis_pkey PRIMARY KEY (id); + + +-- +-- Name: digest_topics digest_topics_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.digest_topics + ADD CONSTRAINT digest_topics_pkey PRIMARY KEY (id); + + +-- +-- Name: document_chunks_cand_qwen06 document_chunks_cand_qwen06_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.document_chunks_cand_qwen06 + ADD CONSTRAINT document_chunks_cand_qwen06_pkey PRIMARY KEY (id); + + +-- +-- Name: document_chunks_cand_qwen4 document_chunks_cand_qwen4_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.document_chunks_cand_qwen4 + ADD CONSTRAINT document_chunks_cand_qwen4_pkey PRIMARY KEY (id); + + +-- +-- Name: document_chunks_cand_qwen4m document_chunks_cand_qwen4m_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.document_chunks_cand_qwen4m + ADD CONSTRAINT document_chunks_cand_qwen4m_pkey PRIMARY KEY (id); + + +-- +-- Name: document_chunks document_chunks_doc_id_chunk_index_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.document_chunks + ADD CONSTRAINT document_chunks_doc_id_chunk_index_key UNIQUE (doc_id, chunk_index); + + +-- +-- Name: document_chunks document_chunks_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.document_chunks + ADD CONSTRAINT document_chunks_pkey PRIMARY KEY (id); + + +-- +-- Name: document_images document_images_document_id_image_key_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.document_images + ADD CONSTRAINT document_images_document_id_image_key_key UNIQUE (document_id, image_key); + + +-- +-- Name: document_images document_images_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.document_images + ADD CONSTRAINT document_images_pkey PRIMARY KEY (id); + + +-- +-- Name: document_lineage document_lineage_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.document_lineage + ADD CONSTRAINT document_lineage_pkey PRIMARY KEY (id); + + +-- +-- Name: document_lineage document_lineage_uq; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.document_lineage + ADD CONSTRAINT document_lineage_uq UNIQUE (source_document_id, derived_document_id, relation_type); + + +-- +-- Name: document_notes document_notes_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.document_notes + ADD CONSTRAINT document_notes_pkey PRIMARY KEY (id); + + +-- +-- Name: document_notes document_notes_user_id_document_id_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.document_notes + ADD CONSTRAINT document_notes_user_id_document_id_key UNIQUE (user_id, document_id); + + +-- +-- Name: document_reads document_reads_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.document_reads + ADD CONSTRAINT document_reads_pkey PRIMARY KEY (id); + + +-- +-- Name: documents_cand_qwen06 documents_cand_qwen06_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.documents_cand_qwen06 + ADD CONSTRAINT documents_cand_qwen06_pkey PRIMARY KEY (doc_id); + + +-- +-- Name: documents_cand_qwen4 documents_cand_qwen4_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.documents_cand_qwen4 + ADD CONSTRAINT documents_cand_qwen4_pkey PRIMARY KEY (doc_id); + + +-- +-- Name: documents_cand_qwen4m documents_cand_qwen4m_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.documents_cand_qwen4m + ADD CONSTRAINT documents_cand_qwen4m_pkey PRIMARY KEY (doc_id); + + +-- +-- Name: documents documents_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.documents + ADD CONSTRAINT documents_pkey PRIMARY KEY (id); + + +-- +-- Name: eid_review_set_draft eid_review_set_draft_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.eid_review_set_draft + ADD CONSTRAINT eid_review_set_draft_pkey PRIMARY KEY (id); + + +-- +-- Name: eid_study_weakness eid_study_weakness_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.eid_study_weakness + ADD CONSTRAINT eid_study_weakness_pkey PRIMARY KEY (id); + + +-- +-- Name: eid_weekly_recap eid_weekly_recap_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.eid_weekly_recap + ADD CONSTRAINT eid_weekly_recap_pkey PRIMARY KEY (id); + + +-- +-- Name: events_history events_history_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.events_history + ADD CONSTRAINT events_history_pkey PRIMARY KEY (id); + + +-- +-- Name: events events_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.events + ADD CONSTRAINT events_pkey PRIMARY KEY (id); + + +-- +-- Name: facet_values facet_values_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.facet_values + ADD CONSTRAINT facet_values_pkey PRIMARY KEY (id); + + +-- +-- Name: global_digests global_digests_digest_date_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.global_digests + ADD CONSTRAINT global_digests_digest_date_key UNIQUE (digest_date); + + +-- +-- Name: global_digests global_digests_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.global_digests + ADD CONSTRAINT global_digests_pkey PRIMARY KEY (id); + + +-- +-- Name: legal_acts legal_acts_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.legal_acts + ADD CONSTRAINT legal_acts_pkey PRIMARY KEY (family_id); + + +-- +-- Name: legal_meta legal_meta_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.legal_meta + ADD CONSTRAINT legal_meta_pkey PRIMARY KEY (document_id); + + +-- +-- Name: library_categories library_categories_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.library_categories + ADD CONSTRAINT library_categories_pkey PRIMARY KEY (id); + + +-- +-- Name: morning_briefings morning_briefings_briefing_date_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.morning_briefings + ADD CONSTRAINT morning_briefings_briefing_date_key UNIQUE (briefing_date); + + +-- +-- Name: morning_briefings morning_briefings_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.morning_briefings + ADD CONSTRAINT morning_briefings_pkey PRIMARY KEY (id); + + +-- +-- Name: news_sources news_sources_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.news_sources + ADD CONSTRAINT news_sources_pkey PRIMARY KEY (id); + + +-- +-- Name: processing_queue processing_queue_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.processing_queue + ADD CONSTRAINT processing_queue_pkey PRIMARY KEY (id); + + +-- +-- Name: schema_migrations schema_migrations_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.schema_migrations + ADD CONSTRAINT schema_migrations_pkey PRIMARY KEY (version); + + +-- +-- Name: search_failure_logs search_failure_logs_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.search_failure_logs + ADD CONSTRAINT search_failure_logs_pkey PRIMARY KEY (id); + + +-- +-- Name: source_health source_health_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.source_health + ADD CONSTRAINT source_health_pkey PRIMARY KEY (id); + + +-- +-- Name: study_memo_card_evidence study_memo_card_evidence_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.study_memo_card_evidence + ADD CONSTRAINT study_memo_card_evidence_pkey PRIMARY KEY (id); + + +-- +-- Name: study_memo_card_jobs study_memo_card_jobs_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.study_memo_card_jobs + ADD CONSTRAINT study_memo_card_jobs_pkey PRIMARY KEY (id); + + +-- +-- Name: study_memo_card_progress study_memo_card_progress_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.study_memo_card_progress + ADD CONSTRAINT study_memo_card_progress_pkey PRIMARY KEY (id); + + +-- +-- Name: study_memo_cards study_memo_cards_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.study_memo_cards + ADD CONSTRAINT study_memo_cards_pkey PRIMARY KEY (id); + + +-- +-- Name: study_question_attempts study_question_attempts_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.study_question_attempts + ADD CONSTRAINT study_question_attempts_pkey PRIMARY KEY (id); + + +-- +-- Name: study_question_images study_question_images_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.study_question_images + ADD CONSTRAINT study_question_images_pkey PRIMARY KEY (id); + + +-- +-- Name: study_question_jobs study_question_jobs_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.study_question_jobs + ADD CONSTRAINT study_question_jobs_pkey PRIMARY KEY (id); + + +-- +-- Name: study_question_progress study_question_progress_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.study_question_progress + ADD CONSTRAINT study_question_progress_pkey PRIMARY KEY (id); + + +-- +-- Name: study_questions study_questions_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.study_questions + ADD CONSTRAINT study_questions_pkey PRIMARY KEY (id); + + +-- +-- Name: study_quiz_session_analysis study_quiz_session_analysis_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.study_quiz_session_analysis + ADD CONSTRAINT study_quiz_session_analysis_pkey PRIMARY KEY (study_quiz_session_id); + + +-- +-- Name: study_quiz_session_jobs study_quiz_session_jobs_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.study_quiz_session_jobs + ADD CONSTRAINT study_quiz_session_jobs_pkey PRIMARY KEY (id); + + +-- +-- Name: study_quiz_sessions study_quiz_sessions_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.study_quiz_sessions + ADD CONSTRAINT study_quiz_sessions_pkey PRIMARY KEY (id); + + +-- +-- Name: study_reminders study_reminders_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.study_reminders + ADD CONSTRAINT study_reminders_pkey PRIMARY KEY (id); + + +-- +-- Name: study_session_assets study_session_assets_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.study_session_assets + ADD CONSTRAINT study_session_assets_pkey PRIMARY KEY (id); + + +-- +-- Name: study_session_assets study_session_assets_study_session_id_document_id_asset_typ_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.study_session_assets + ADD CONSTRAINT study_session_assets_study_session_id_document_id_asset_typ_key UNIQUE (study_session_id, document_id, asset_type, role); + + +-- +-- Name: study_sessions study_sessions_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.study_sessions + ADD CONSTRAINT study_sessions_pkey PRIMARY KEY (id); + + +-- +-- Name: study_topic_documents study_topic_documents_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.study_topic_documents + ADD CONSTRAINT study_topic_documents_pkey PRIMARY KEY (study_topic_id, document_id); + + +-- +-- Name: study_topic_subject_notes study_topic_subject_notes_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.study_topic_subject_notes + ADD CONSTRAINT study_topic_subject_notes_pkey PRIMARY KEY (id); + + +-- +-- Name: study_topics study_topics_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.study_topics + ADD CONSTRAINT study_topics_pkey PRIMARY KEY (id); + + +-- +-- Name: tasks tasks_caldav_uid_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.tasks + ADD CONSTRAINT tasks_caldav_uid_key UNIQUE (caldav_uid); + + +-- +-- Name: tasks tasks_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.tasks + ADD CONSTRAINT tasks_pkey PRIMARY KEY (id); + + +-- +-- Name: study_memo_card_progress uq_card_progress_user_card; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.study_memo_card_progress + ADD CONSTRAINT uq_card_progress_user_card UNIQUE (user_id, card_id); + + +-- +-- Name: legal_meta uq_legal_meta_version; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.legal_meta + ADD CONSTRAINT uq_legal_meta_version UNIQUE (family_id, law_doc_kind, version_key); + + +-- +-- Name: study_question_progress uq_progress_user_topic_question; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.study_question_progress + ADD CONSTRAINT uq_progress_user_topic_question UNIQUE (user_id, study_topic_id, study_question_id); + + +-- +-- Name: study_reminders uq_study_reminders_user_fired; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.study_reminders + ADD CONSTRAINT uq_study_reminders_user_fired UNIQUE (user_id, fired_at); + + +-- +-- Name: users users_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.users + ADD CONSTRAINT users_pkey PRIMARY KEY (id); + + +-- +-- Name: users users_username_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.users + ADD CONSTRAINT users_username_key UNIQUE (username); + + +-- +-- Name: worker_capabilities worker_capabilities_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.worker_capabilities + ADD CONSTRAINT worker_capabilities_pkey PRIMARY KEY (worker_id); + + +-- +-- Name: worker_heartbeats worker_heartbeats_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.worker_heartbeats + ADD CONSTRAINT worker_heartbeats_pkey PRIMARY KEY (id); + + +-- +-- Name: worker_jobs worker_jobs_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.worker_jobs + ADD CONSTRAINT worker_jobs_pkey PRIMARY KEY (id); + + +-- +-- Name: events_source_ref_uq; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX events_source_ref_uq ON public.events USING btree (source, source_ref) WHERE (source_ref IS NOT NULL); + + +-- +-- Name: idx_analyze_events_answerability; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_analyze_events_answerability ON public.analyze_events USING btree (answerability, created_at DESC) WHERE (answerability IS NOT NULL); + + +-- +-- Name: idx_analyze_events_policy_violation; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_analyze_events_policy_violation ON public.analyze_events USING btree (created_at) WHERE (policy_violation = true); + + +-- +-- Name: idx_analyze_events_shadow_ts; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_analyze_events_shadow_ts ON public.analyze_events USING btree (created_at) WHERE (shadow_would_route_to IS NOT NULL); + + +-- +-- Name: idx_analyze_events_suppressed; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_analyze_events_suppressed ON public.analyze_events USING btree (created_at) WHERE (suppressed_reason IS NOT NULL); + + +-- +-- Name: idx_approval_requests_status; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_approval_requests_status ON public.approval_requests USING btree (status, created_at); + + +-- +-- Name: idx_ask_events_created; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_ask_events_created ON public.ask_events USING btree (created_at); + + +-- +-- Name: idx_ask_events_eval_case_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_ask_events_eval_case_id ON public.ask_events USING btree (eval_case_id) WHERE (eval_case_id IS NOT NULL); + + +-- +-- Name: idx_ask_events_prompt_version; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_ask_events_prompt_version ON public.ask_events USING btree (prompt_version); + + +-- +-- Name: idx_ask_events_source_created; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_ask_events_source_created ON public.ask_events USING btree (source, created_at DESC); + + +-- +-- Name: idx_audio_segments_doc_start; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_audio_segments_doc_start ON public.audio_segments USING btree (document_id, start_s); + + +-- +-- Name: idx_briefing_topics_briefing_rank; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_briefing_topics_briefing_rank ON public.briefing_topics USING btree (briefing_id, topic_rank); + + +-- +-- Name: idx_card_progress_due; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_card_progress_due ON public.study_memo_card_progress USING btree (user_id, due_at) WHERE (due_at IS NOT NULL); + + +-- +-- Name: idx_chunks_country; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_chunks_country ON public.document_chunks USING btree (country) WHERE (country IS NOT NULL); + + +-- +-- Name: idx_chunks_doc_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_chunks_doc_id ON public.document_chunks USING btree (doc_id); + + +-- +-- Name: idx_chunks_domain_category; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_chunks_domain_category ON public.document_chunks USING btree (domain_category); + + +-- +-- Name: idx_chunks_embedding; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_chunks_embedding ON public.document_chunks USING ivfflat (embedding public.vector_cosine_ops) WITH (lists='100') WHERE (in_corpus = true); + + +-- +-- Name: idx_chunks_fts; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_chunks_fts ON public.document_chunks USING gin (to_tsvector('simple'::regconfig, text)); + + +-- +-- Name: idx_chunks_language; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_chunks_language ON public.document_chunks USING btree (language) WHERE (language IS NOT NULL); + + +-- +-- Name: idx_chunks_source; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_chunks_source ON public.document_chunks USING btree (source) WHERE (source IS NOT NULL); + + +-- +-- Name: idx_chunks_trgm; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_chunks_trgm ON public.document_chunks USING gin (text public.gin_trgm_ops); + + +-- +-- Name: idx_digest_topics_country; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_digest_topics_country ON public.digest_topics USING btree (country); + + +-- +-- Name: idx_digest_topics_digest; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_digest_topics_digest ON public.digest_topics USING btree (digest_id); + + +-- +-- Name: idx_digest_topics_rank; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_digest_topics_rank ON public.digest_topics USING btree (digest_id, country, topic_rank); + + +-- +-- Name: idx_document_images_content_hash; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_document_images_content_hash ON public.document_images USING btree (content_hash); + + +-- +-- Name: idx_document_images_document_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_document_images_document_id ON public.document_images USING btree (document_id); + + +-- +-- Name: idx_document_lineage_derived; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_document_lineage_derived ON public.document_lineage USING btree (derived_document_id); + + +-- +-- Name: idx_document_lineage_source; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_document_lineage_source ON public.document_lineage USING btree (source_document_id); + + +-- +-- Name: idx_document_notes_user_doc; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_document_notes_user_doc ON public.document_notes USING btree (user_id, document_id); + + +-- +-- Name: idx_document_reads_doc_time; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_document_reads_doc_time ON public.document_reads USING btree (document_id, read_at DESC); + + +-- +-- Name: idx_document_reads_user_doc; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_document_reads_user_doc ON public.document_reads USING btree (user_id, document_id); + + +-- +-- Name: idx_documents_ai_event_kind; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_documents_ai_event_kind ON public.documents USING btree (ai_event_kind, created_at DESC) WHERE (ai_event_kind IS NOT NULL); + + +-- +-- Name: idx_documents_ai_summary_trgm; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_documents_ai_summary_trgm ON public.documents USING gin (ai_summary public.gin_trgm_ops) WHERE ((ai_summary IS NOT NULL) AND (length(ai_summary) > 0)); + + +-- +-- Name: idx_documents_ai_version; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_documents_ai_version ON public.documents USING btree (ai_model_version); + + +-- +-- Name: idx_documents_category; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_documents_category ON public.documents USING btree (category); + + +-- +-- Name: idx_documents_content_origin; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_documents_content_origin ON public.documents USING btree (content_origin); + + +-- +-- Name: idx_documents_embed_version; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_documents_embed_version ON public.documents USING btree (embed_model_version); + + +-- +-- Name: idx_documents_embedding_hnsw; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_documents_embedding_hnsw ON public.documents USING hnsw (embedding public.vector_cosine_ops) WHERE ((deleted_at IS NULL) AND (embedding IS NOT NULL)); + + +-- +-- Name: idx_documents_extracted_text_trgm; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_documents_extracted_text_trgm ON public.documents USING gin (extracted_text public.gin_trgm_ops) WHERE ((extracted_text IS NOT NULL) AND (length(extracted_text) > 0)); + + +-- +-- Name: idx_documents_extractor_version; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_documents_extractor_version ON public.documents USING btree (extractor_version); + + +-- +-- Name: idx_documents_facet_company; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_documents_facet_company ON public.documents USING btree (facet_company) WHERE (facet_company IS NOT NULL); + + +-- +-- Name: idx_documents_facet_doctype; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_documents_facet_doctype ON public.documents USING btree (facet_doctype) WHERE (facet_doctype IS NOT NULL); + + +-- +-- Name: idx_documents_facet_topic; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_documents_facet_topic ON public.documents USING btree (facet_topic) WHERE (facet_topic IS NOT NULL); + + +-- +-- Name: idx_documents_facet_year; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_documents_facet_year ON public.documents USING btree (facet_year) WHERE (facet_year IS NOT NULL); + + +-- +-- Name: idx_documents_fts; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_documents_fts ON public.documents USING gin (to_tsvector('simple'::regconfig, ((COALESCE(title, ''::text) || ' '::text) || COALESCE(extracted_text, ''::text)))); + + +-- +-- Name: idx_documents_fts_full; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_documents_fts_full ON public.documents USING gin (to_tsvector('simple'::regconfig, ((((((((COALESCE(title, ''::text) || ' '::text) || COALESCE((ai_tags)::text, ''::text)) || ' '::text) || COALESCE(ai_summary, ''::text)) || ' '::text) || COALESCE(user_note, ''::text)) || ' '::text) || COALESCE(extracted_text, ''::text)))); + + +-- +-- Name: idx_documents_has_suggestion; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_documents_has_suggestion ON public.documents USING btree (id) WHERE (ai_suggestion IS NOT NULL); + + +-- +-- Name: idx_documents_hash; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_documents_hash ON public.documents USING btree (file_hash); + + +-- +-- Name: idx_documents_is_read; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_documents_is_read ON public.documents USING btree (is_read) WHERE (source_channel = 'news'::public.source_channel); + + +-- +-- Name: idx_documents_jurisdiction; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_documents_jurisdiction ON public.documents USING btree (jurisdiction) WHERE (jurisdiction IS NOT NULL); + + +-- +-- Name: idx_documents_material_type; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_documents_material_type ON public.documents USING btree (material_type) WHERE (material_type IS NOT NULL); + + +-- +-- Name: idx_documents_md_draft_status; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_documents_md_draft_status ON public.documents USING btree (md_draft_status) WHERE (md_draft_status IS NOT NULL); + + +-- +-- Name: idx_documents_md_frontmatter_gin; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_documents_md_frontmatter_gin ON public.documents USING gin (md_frontmatter); + + +-- +-- Name: idx_documents_md_status_pending; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_documents_md_status_pending ON public.documents USING btree (md_status) WHERE (md_status = ANY (ARRAY['pending'::text, 'processing'::text])); + + +-- +-- Name: idx_documents_not_deleted; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_documents_not_deleted ON public.documents USING btree (deleted_at) WHERE (deleted_at IS NULL); + + +-- +-- Name: idx_documents_notes; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_documents_notes ON public.documents USING btree (pinned DESC, created_at DESC) WHERE ((file_type = 'note'::public.doc_type) AND (deleted_at IS NULL)); + + +-- +-- Name: idx_documents_review_status; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_documents_review_status ON public.documents USING btree (review_status); + + +-- +-- Name: idx_documents_title_trgm; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_documents_title_trgm ON public.documents USING gin (title public.gin_trgm_ops); + + +-- +-- Name: idx_documents_trgm; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_documents_trgm ON public.documents USING gin ((((COALESCE(title, ''::text) || ' '::text) || COALESCE(extracted_text, ''::text))) public.gin_trgm_ops); + + +-- +-- Name: idx_documents_user_tags_gin; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_documents_user_tags_gin ON public.documents USING gin (user_tags jsonb_path_ops) WHERE (user_tags IS NOT NULL); + + +-- +-- Name: idx_eid_recap_user_current; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_eid_recap_user_current ON public.eid_weekly_recap USING btree (user_id, created_at DESC) WHERE ((status)::text = 'active'::text); + + +-- +-- Name: idx_eid_review_draft_user; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_eid_review_draft_user ON public.eid_review_set_draft USING btree (user_id, created_at DESC); + + +-- +-- Name: idx_eid_weakness_user_current; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_eid_weakness_user_current ON public.eid_study_weakness USING btree (user_id, created_at DESC) WHERE ((status)::text = 'active'::text); + + +-- +-- Name: idx_events_active; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_events_active ON public.events USING btree (user_id, due_at, start_at) WHERE (status = ANY (ARRAY['inbox'::public.event_status, 'next'::public.event_status, 'scheduled'::public.event_status, 'deferred'::public.event_status])); + + +-- +-- Name: idx_events_activity_user_started; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_events_activity_user_started ON public.events USING btree (user_id, started_at DESC) WHERE (kind = 'activity_log'::public.event_kind); + + +-- +-- Name: idx_events_history_event; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_events_history_event ON public.events_history USING btree (event_id, changed_at); + + +-- +-- Name: idx_global_digests_date; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_global_digests_date ON public.global_digests USING btree (digest_date DESC); + + +-- +-- Name: idx_legal_meta_family; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_legal_meta_family ON public.legal_meta USING btree (family_id, effective_date DESC); + + +-- +-- Name: idx_library_categories_parent; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_library_categories_parent ON public.library_categories USING btree (parent_path); + + +-- +-- Name: idx_morning_briefings_date; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_morning_briefings_date ON public.morning_briefings USING btree (briefing_date DESC); + + +-- +-- Name: idx_news_feed; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_news_feed ON public.documents USING btree (source_channel, created_at DESC); + + +-- +-- Name: idx_progress_due; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_progress_due ON public.study_question_progress USING btree (user_id, due_at) WHERE (due_at IS NOT NULL); + + +-- +-- Name: idx_progress_pending_review; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_progress_pending_review ON public.study_question_progress USING btree (user_id, study_topic_id, last_attempted_at DESC) WHERE ((last_outcome)::text = ANY ((ARRAY['wrong'::character varying, 'unsure'::character varying])::text[])); + + +-- +-- Name: idx_progress_topic_pattern; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_progress_topic_pattern ON public.study_question_progress USING btree (user_id, study_topic_id, pattern_state); + + +-- +-- Name: idx_queue_pending; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_queue_pending ON public.processing_queue USING btree (stage, status) WHERE (status = 'pending'::public.process_status); + + +-- +-- Name: idx_search_failure_reason; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_search_failure_reason ON public.search_failure_logs USING btree (failure_reason, created_at DESC); + + +-- +-- Name: idx_search_failure_unreviewed; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_search_failure_unreviewed ON public.search_failure_logs USING btree (created_at DESC) WHERE (reviewed = false); + + +-- +-- Name: idx_search_failure_user_time; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_search_failure_user_time ON public.search_failure_logs USING btree (user_id, created_at DESC); + + +-- +-- Name: idx_session_assets_document; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_session_assets_document ON public.study_session_assets USING btree (document_id); + + +-- +-- Name: idx_session_assets_session; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_session_assets_session ON public.study_session_assets USING btree (study_session_id, sort_order); + + +-- +-- Name: idx_session_assets_type; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_session_assets_type ON public.study_session_assets USING btree (study_session_id, asset_type); + + +-- +-- Name: idx_study_memo_card_jobs_lookup; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_study_memo_card_jobs_lookup ON public.study_memo_card_jobs USING btree (source_kind, source_id, source_version); + + +-- +-- Name: idx_study_memo_cards_source_q; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_study_memo_cards_source_q ON public.study_memo_cards USING btree (source_question_id) WHERE (deleted_at IS NULL); + + +-- +-- Name: idx_study_q_related_stale; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_study_q_related_stale ON public.study_questions USING btree (study_topic_id, related_computed_at) WHERE ((deleted_at IS NULL) AND ((embedding_status)::text = 'ready'::text) AND (related_computed_at IS NULL)); + + +-- +-- Name: idx_study_question_attempts_question; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_study_question_attempts_question ON public.study_question_attempts USING btree (study_question_id, answered_at DESC); + + +-- +-- Name: idx_study_question_attempts_user_topic; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_study_question_attempts_user_topic ON public.study_question_attempts USING btree (user_id, study_topic_id, answered_at DESC); + + +-- +-- Name: idx_study_question_images_qid; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_study_question_images_qid ON public.study_question_images USING btree (study_question_id, sort_order, id); + + +-- +-- Name: idx_study_questions_ai_status; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_study_questions_ai_status ON public.study_questions USING btree (study_topic_id, ai_explanation_status) WHERE ((deleted_at IS NULL) AND ((ai_explanation_status)::text <> 'none'::text)); + + +-- +-- Name: idx_study_questions_embedding_hnsw; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_study_questions_embedding_hnsw ON public.study_questions USING hnsw (embedding public.vector_cosine_ops) WHERE ((deleted_at IS NULL) AND (embedding IS NOT NULL)); + + +-- +-- Name: idx_study_questions_needs_review; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_study_questions_needs_review ON public.study_questions USING btree (study_topic_id) WHERE ((deleted_at IS NULL) AND needs_review); + + +-- +-- Name: idx_study_questions_topic; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_study_questions_topic ON public.study_questions USING btree (study_topic_id, created_at DESC, id) WHERE (deleted_at IS NULL); + + +-- +-- Name: idx_study_questions_topic_round_qnum; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_study_questions_topic_round_qnum ON public.study_questions USING btree (study_topic_id, exam_round, exam_question_number) WHERE ((deleted_at IS NULL) AND (exam_round IS NOT NULL)); + + +-- +-- Name: idx_study_sessions_cert; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_study_sessions_cert ON public.study_sessions USING btree (user_id, certification, subject, topic) WHERE ((study_type)::text = 'certification'::text); + + +-- +-- Name: idx_study_sessions_lang; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_study_sessions_lang ON public.study_sessions USING btree (user_id, language_code, learning_level, subject, topic) WHERE ((study_type)::text = 'language'::text); + + +-- +-- Name: idx_study_sessions_quiz_stats; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_study_sessions_quiz_stats ON public.study_sessions USING btree (user_id, study_type, last_quiz_at) WHERE (last_quiz_at IS NOT NULL); + + +-- +-- Name: idx_study_sessions_review; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_study_sessions_review ON public.study_sessions USING btree (user_id, review_state, next_review_at) WHERE (review_state IS NOT NULL); + + +-- +-- Name: idx_study_sessions_topic; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_study_sessions_topic ON public.study_sessions USING btree (study_topic_id, created_at DESC) WHERE (study_topic_id IS NOT NULL); + + +-- +-- Name: idx_study_sessions_type_user_created; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_study_sessions_type_user_created ON public.study_sessions USING btree (user_id, study_type, created_at DESC); + + +-- +-- Name: idx_study_topic_documents_doc; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_study_topic_documents_doc ON public.study_topic_documents USING btree (document_id); + + +-- +-- Name: idx_study_topics_user; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_study_topics_user ON public.study_topics USING btree (user_id, sort_order, id) WHERE (deleted_at IS NULL); + + +-- +-- Name: idx_worker_capabilities_class; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_worker_capabilities_class ON public.worker_capabilities USING btree (worker_class); + + +-- +-- Name: idx_worker_capabilities_tier; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_worker_capabilities_tier ON public.worker_capabilities USING btree (tier); + + +-- +-- Name: idx_worker_heartbeats_worker_at; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_worker_heartbeats_worker_at ON public.worker_heartbeats USING btree (worker_id, heartbeat_at DESC); + + +-- +-- Name: idx_worker_jobs_pending_type; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_worker_jobs_pending_type ON public.worker_jobs USING btree (job_type, created_at) WHERE (status = 'pending'::text); + + +-- +-- Name: uq_document_chunks_source_version_index; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX uq_document_chunks_source_version_index ON public.document_chunks USING btree (doc_id, source_type, chunker_version, chunk_index); + + +-- +-- Name: uq_documents_email_source_external_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX uq_documents_email_source_external_id ON public.documents USING btree (source_external_id) WHERE ((source_channel = 'email'::public.source_channel) AND (source_external_id IS NOT NULL)); + + +-- +-- Name: uq_documents_file_path; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX uq_documents_file_path ON public.documents USING btree (file_path) WHERE (file_path IS NOT NULL); + + +-- +-- Name: uq_documents_paper_doi; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX uq_documents_paper_doi ON public.documents USING btree (lower((extract_meta #>> '{paper,doi}'::text[]))) WHERE ((material_type = 'paper'::text) AND ((extract_meta #>> '{paper,doi}'::text[]) IS NOT NULL)); + + +-- +-- Name: uq_facet_values_type_value; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX uq_facet_values_type_value ON public.facet_values USING btree (facet_type, value); + + +-- +-- Name: uq_library_categories_path; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX uq_library_categories_path ON public.library_categories USING btree (path); + + +-- +-- Name: uq_queue_active; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX uq_queue_active ON public.processing_queue USING btree (document_id, stage) WHERE (status = ANY (ARRAY['pending'::public.process_status, 'processing'::public.process_status])); + + +-- +-- Name: uq_source_health_source_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX uq_source_health_source_id ON public.source_health USING btree (source_id); + + +-- +-- Name: uq_study_memo_card_jobs_active; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX uq_study_memo_card_jobs_active ON public.study_memo_card_jobs USING btree (source_kind, source_id) WHERE ((status)::text = ANY ((ARRAY['pending'::character varying, 'processing'::character varying])::text[])); + + +-- +-- Name: uq_study_memo_cards_dedup; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX uq_study_memo_cards_dedup ON public.study_memo_cards USING btree (dedup_hash) WHERE (deleted_at IS NULL); + + +-- +-- Name: uq_study_q_jobs_active; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX uq_study_q_jobs_active ON public.study_question_jobs USING btree (study_question_id, kind) WHERE ((status)::text = ANY ((ARRAY['pending'::character varying, 'processing'::character varying])::text[])); + + +-- +-- Name: uq_study_quiz_sessions_active; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX uq_study_quiz_sessions_active ON public.study_quiz_sessions USING btree (user_id, study_topic_id) WHERE ((status)::text = 'in_progress'::text); + + +-- +-- Name: uq_study_session_jobs_active; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX uq_study_session_jobs_active ON public.study_quiz_session_jobs USING btree (study_quiz_session_id) WHERE ((status)::text = ANY ((ARRAY['pending'::character varying, 'processing'::character varying])::text[])); + + +-- +-- Name: uq_study_topic_subject_notes; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX uq_study_topic_subject_notes ON public.study_topic_subject_notes USING btree (user_id, study_topic_id, subject, scope); + + +-- +-- Name: uq_study_topics_user_name_active; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX uq_study_topics_user_name_active ON public.study_topics USING btree (user_id, name) WHERE (deleted_at IS NULL); + + +-- +-- Name: eid_review_set_draft eid_review_set_draft_no_delete; Type: RULE; Schema: public; Owner: - +-- + +CREATE RULE eid_review_set_draft_no_delete AS + ON DELETE TO public.eid_review_set_draft DO INSTEAD NOTHING; + + +-- +-- Name: eid_review_set_draft eid_review_set_draft_no_update; Type: RULE; Schema: public; Owner: - +-- + +CREATE RULE eid_review_set_draft_no_update AS + ON UPDATE TO public.eid_review_set_draft DO INSTEAD NOTHING; + + +-- +-- Name: eid_study_weakness eid_study_weakness_no_delete; Type: RULE; Schema: public; Owner: - +-- + +CREATE RULE eid_study_weakness_no_delete AS + ON DELETE TO public.eid_study_weakness DO INSTEAD NOTHING; + + +-- +-- Name: eid_study_weakness eid_study_weakness_no_update; Type: RULE; Schema: public; Owner: - +-- + +CREATE RULE eid_study_weakness_no_update AS + ON UPDATE TO public.eid_study_weakness DO INSTEAD NOTHING; + + +-- +-- Name: eid_weekly_recap eid_weekly_recap_no_delete; Type: RULE; Schema: public; Owner: - +-- + +CREATE RULE eid_weekly_recap_no_delete AS + ON DELETE TO public.eid_weekly_recap DO INSTEAD NOTHING; + + +-- +-- Name: eid_weekly_recap eid_weekly_recap_no_update; Type: RULE; Schema: public; Owner: - +-- + +CREATE RULE eid_weekly_recap_no_update AS + ON UPDATE TO public.eid_weekly_recap DO INSTEAD NOTHING; + + +-- +-- Name: analyze_events analyze_events_doc_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.analyze_events + ADD CONSTRAINT analyze_events_doc_id_fkey FOREIGN KEY (doc_id) REFERENCES public.documents(id) ON DELETE CASCADE; + + +-- +-- Name: analyze_events analyze_events_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.analyze_events + ADD CONSTRAINT analyze_events_user_id_fkey FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE SET NULL; + + +-- +-- Name: approval_requests approval_requests_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.approval_requests + ADD CONSTRAINT approval_requests_user_id_fkey FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE; + + +-- +-- Name: ask_events ask_events_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.ask_events + ADD CONSTRAINT ask_events_user_id_fkey FOREIGN KEY (user_id) REFERENCES public.users(id); + + +-- +-- Name: audio_segments audio_segments_document_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.audio_segments + ADD CONSTRAINT audio_segments_document_id_fkey FOREIGN KEY (document_id) REFERENCES public.documents(id) ON DELETE CASCADE; + + +-- +-- Name: briefing_topics briefing_topics_briefing_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.briefing_topics + ADD CONSTRAINT briefing_topics_briefing_id_fkey FOREIGN KEY (briefing_id) REFERENCES public.morning_briefings(id) ON DELETE CASCADE; + + +-- +-- Name: chunk_section_analysis chunk_section_analysis_chunk_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.chunk_section_analysis + ADD CONSTRAINT chunk_section_analysis_chunk_id_fkey FOREIGN KEY (chunk_id) REFERENCES public.document_chunks(id) ON DELETE CASCADE; + + +-- +-- Name: digest_topics digest_topics_digest_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.digest_topics + ADD CONSTRAINT digest_topics_digest_id_fkey FOREIGN KEY (digest_id) REFERENCES public.global_digests(id) ON DELETE CASCADE; + + +-- +-- Name: document_chunks document_chunks_doc_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.document_chunks + ADD CONSTRAINT document_chunks_doc_id_fkey FOREIGN KEY (doc_id) REFERENCES public.documents(id) ON DELETE CASCADE; + + +-- +-- Name: document_images document_images_document_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.document_images + ADD CONSTRAINT document_images_document_id_fkey FOREIGN KEY (document_id) REFERENCES public.documents(id) ON DELETE CASCADE; + + +-- +-- Name: document_lineage document_lineage_derived_document_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.document_lineage + ADD CONSTRAINT document_lineage_derived_document_id_fkey FOREIGN KEY (derived_document_id) REFERENCES public.documents(id) ON DELETE RESTRICT; + + +-- +-- Name: document_lineage document_lineage_source_document_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.document_lineage + ADD CONSTRAINT document_lineage_source_document_id_fkey FOREIGN KEY (source_document_id) REFERENCES public.documents(id) ON DELETE RESTRICT; + + +-- +-- Name: document_notes document_notes_document_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.document_notes + ADD CONSTRAINT document_notes_document_id_fkey FOREIGN KEY (document_id) REFERENCES public.documents(id) ON DELETE CASCADE; + + +-- +-- Name: document_notes document_notes_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.document_notes + ADD CONSTRAINT document_notes_user_id_fkey FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE; + + +-- +-- Name: document_reads document_reads_document_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.document_reads + ADD CONSTRAINT document_reads_document_id_fkey FOREIGN KEY (document_id) REFERENCES public.documents(id) ON DELETE CASCADE; + + +-- +-- Name: document_reads document_reads_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.document_reads + ADD CONSTRAINT document_reads_user_id_fkey FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE; + + +-- +-- Name: documents documents_duplicate_of_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.documents + ADD CONSTRAINT documents_duplicate_of_fkey FOREIGN KEY (duplicate_of) REFERENCES public.documents(id) ON DELETE SET NULL; + + +-- +-- Name: eid_review_set_draft eid_review_set_draft_source_weakness_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.eid_review_set_draft + ADD CONSTRAINT eid_review_set_draft_source_weakness_id_fkey FOREIGN KEY (source_weakness_id) REFERENCES public.eid_study_weakness(id) ON DELETE SET NULL; + + +-- +-- Name: eid_review_set_draft eid_review_set_draft_study_topic_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.eid_review_set_draft + ADD CONSTRAINT eid_review_set_draft_study_topic_id_fkey FOREIGN KEY (study_topic_id) REFERENCES public.study_topics(id) ON DELETE CASCADE; + + +-- +-- Name: eid_review_set_draft eid_review_set_draft_supersedes_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.eid_review_set_draft + ADD CONSTRAINT eid_review_set_draft_supersedes_id_fkey FOREIGN KEY (supersedes_id) REFERENCES public.eid_review_set_draft(id) ON DELETE SET NULL; + + +-- +-- Name: eid_review_set_draft eid_review_set_draft_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.eid_review_set_draft + ADD CONSTRAINT eid_review_set_draft_user_id_fkey FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE; + + +-- +-- Name: eid_study_weakness eid_study_weakness_supersedes_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.eid_study_weakness + ADD CONSTRAINT eid_study_weakness_supersedes_id_fkey FOREIGN KEY (supersedes_id) REFERENCES public.eid_study_weakness(id) ON DELETE SET NULL; + + +-- +-- Name: eid_study_weakness eid_study_weakness_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.eid_study_weakness + ADD CONSTRAINT eid_study_weakness_user_id_fkey FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE; + + +-- +-- Name: eid_weekly_recap eid_weekly_recap_supersedes_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.eid_weekly_recap + ADD CONSTRAINT eid_weekly_recap_supersedes_id_fkey FOREIGN KEY (supersedes_id) REFERENCES public.eid_weekly_recap(id) ON DELETE SET NULL; + + +-- +-- Name: eid_weekly_recap eid_weekly_recap_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.eid_weekly_recap + ADD CONSTRAINT eid_weekly_recap_user_id_fkey FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE; + + +-- +-- Name: events_history events_history_event_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.events_history + ADD CONSTRAINT events_history_event_id_fkey FOREIGN KEY (event_id) REFERENCES public.events(id) ON DELETE RESTRICT; + + +-- +-- Name: events events_memo_document_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.events + ADD CONSTRAINT events_memo_document_id_fkey FOREIGN KEY (memo_document_id) REFERENCES public.documents(id) ON DELETE SET NULL; + + +-- +-- Name: events events_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.events + ADD CONSTRAINT events_user_id_fkey FOREIGN KEY (user_id) REFERENCES public.users(id); + + +-- +-- Name: legal_acts legal_acts_parent_family_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.legal_acts + ADD CONSTRAINT legal_acts_parent_family_id_fkey FOREIGN KEY (parent_family_id) REFERENCES public.legal_acts(family_id); + + +-- +-- Name: legal_meta legal_meta_document_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.legal_meta + ADD CONSTRAINT legal_meta_document_id_fkey FOREIGN KEY (document_id) REFERENCES public.documents(id) ON DELETE CASCADE; + + +-- +-- Name: legal_meta legal_meta_family_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.legal_meta + ADD CONSTRAINT legal_meta_family_id_fkey FOREIGN KEY (family_id) REFERENCES public.legal_acts(family_id); + + +-- +-- Name: processing_queue processing_queue_document_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.processing_queue + ADD CONSTRAINT processing_queue_document_id_fkey FOREIGN KEY (document_id) REFERENCES public.documents(id); + + +-- +-- Name: search_failure_logs search_failure_logs_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.search_failure_logs + ADD CONSTRAINT search_failure_logs_user_id_fkey FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE SET NULL; + + +-- +-- Name: source_health source_health_source_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.source_health + ADD CONSTRAINT source_health_source_id_fkey FOREIGN KEY (source_id) REFERENCES public.news_sources(id) ON DELETE CASCADE; + + +-- +-- Name: study_memo_card_evidence study_memo_card_evidence_card_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.study_memo_card_evidence + ADD CONSTRAINT study_memo_card_evidence_card_id_fkey FOREIGN KEY (card_id) REFERENCES public.study_memo_cards(id) ON DELETE CASCADE; + + +-- +-- Name: study_memo_card_jobs study_memo_card_jobs_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.study_memo_card_jobs + ADD CONSTRAINT study_memo_card_jobs_user_id_fkey FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE; + + +-- +-- Name: study_memo_card_progress study_memo_card_progress_card_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.study_memo_card_progress + ADD CONSTRAINT study_memo_card_progress_card_id_fkey FOREIGN KEY (card_id) REFERENCES public.study_memo_cards(id) ON DELETE CASCADE; + + +-- +-- Name: study_memo_card_progress study_memo_card_progress_study_topic_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.study_memo_card_progress + ADD CONSTRAINT study_memo_card_progress_study_topic_id_fkey FOREIGN KEY (study_topic_id) REFERENCES public.study_topics(id) ON DELETE CASCADE; + + +-- +-- Name: study_memo_card_progress study_memo_card_progress_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.study_memo_card_progress + ADD CONSTRAINT study_memo_card_progress_user_id_fkey FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE; + + +-- +-- Name: study_memo_cards study_memo_cards_source_question_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.study_memo_cards + ADD CONSTRAINT study_memo_cards_source_question_id_fkey FOREIGN KEY (source_question_id) REFERENCES public.study_questions(id) ON DELETE CASCADE; + + +-- +-- Name: study_memo_cards study_memo_cards_study_topic_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.study_memo_cards + ADD CONSTRAINT study_memo_cards_study_topic_id_fkey FOREIGN KEY (study_topic_id) REFERENCES public.study_topics(id) ON DELETE CASCADE; + + +-- +-- Name: study_memo_cards study_memo_cards_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.study_memo_cards + ADD CONSTRAINT study_memo_cards_user_id_fkey FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE; + + +-- +-- Name: study_question_attempts study_question_attempts_quiz_session_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.study_question_attempts + ADD CONSTRAINT study_question_attempts_quiz_session_id_fkey FOREIGN KEY (quiz_session_id) REFERENCES public.study_quiz_sessions(id) ON DELETE SET NULL; + + +-- +-- Name: study_question_attempts study_question_attempts_study_question_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.study_question_attempts + ADD CONSTRAINT study_question_attempts_study_question_id_fkey FOREIGN KEY (study_question_id) REFERENCES public.study_questions(id) ON DELETE RESTRICT; + + +-- +-- Name: study_question_attempts study_question_attempts_study_topic_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.study_question_attempts + ADD CONSTRAINT study_question_attempts_study_topic_id_fkey FOREIGN KEY (study_topic_id) REFERENCES public.study_topics(id) ON DELETE CASCADE; + + +-- +-- Name: study_question_attempts study_question_attempts_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.study_question_attempts + ADD CONSTRAINT study_question_attempts_user_id_fkey FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE; + + +-- +-- Name: study_question_images study_question_images_study_question_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.study_question_images + ADD CONSTRAINT study_question_images_study_question_id_fkey FOREIGN KEY (study_question_id) REFERENCES public.study_questions(id) ON DELETE CASCADE; + + +-- +-- Name: study_question_images study_question_images_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.study_question_images + ADD CONSTRAINT study_question_images_user_id_fkey FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE; + + +-- +-- Name: study_question_jobs study_question_jobs_study_question_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.study_question_jobs + ADD CONSTRAINT study_question_jobs_study_question_id_fkey FOREIGN KEY (study_question_id) REFERENCES public.study_questions(id) ON DELETE CASCADE; + + +-- +-- Name: study_question_jobs study_question_jobs_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.study_question_jobs + ADD CONSTRAINT study_question_jobs_user_id_fkey FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE; + + +-- +-- Name: study_question_progress study_question_progress_last_attempt_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.study_question_progress + ADD CONSTRAINT study_question_progress_last_attempt_id_fkey FOREIGN KEY (last_attempt_id) REFERENCES public.study_question_attempts(id) ON DELETE SET NULL; + + +-- +-- Name: study_question_progress study_question_progress_study_question_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.study_question_progress + ADD CONSTRAINT study_question_progress_study_question_id_fkey FOREIGN KEY (study_question_id) REFERENCES public.study_questions(id) ON DELETE RESTRICT; + + +-- +-- Name: study_question_progress study_question_progress_study_topic_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.study_question_progress + ADD CONSTRAINT study_question_progress_study_topic_id_fkey FOREIGN KEY (study_topic_id) REFERENCES public.study_topics(id) ON DELETE CASCADE; + + +-- +-- Name: study_question_progress study_question_progress_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.study_question_progress + ADD CONSTRAINT study_question_progress_user_id_fkey FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE; + + +-- +-- Name: study_questions study_questions_study_topic_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.study_questions + ADD CONSTRAINT study_questions_study_topic_id_fkey FOREIGN KEY (study_topic_id) REFERENCES public.study_topics(id) ON DELETE CASCADE; + + +-- +-- Name: study_questions study_questions_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.study_questions + ADD CONSTRAINT study_questions_user_id_fkey FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE; + + +-- +-- Name: study_quiz_session_analysis study_quiz_session_analysis_study_quiz_session_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.study_quiz_session_analysis + ADD CONSTRAINT study_quiz_session_analysis_study_quiz_session_id_fkey FOREIGN KEY (study_quiz_session_id) REFERENCES public.study_quiz_sessions(id) ON DELETE CASCADE; + + +-- +-- Name: study_quiz_session_analysis study_quiz_session_analysis_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.study_quiz_session_analysis + ADD CONSTRAINT study_quiz_session_analysis_user_id_fkey FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE; + + +-- +-- Name: study_quiz_session_jobs study_quiz_session_jobs_study_quiz_session_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.study_quiz_session_jobs + ADD CONSTRAINT study_quiz_session_jobs_study_quiz_session_id_fkey FOREIGN KEY (study_quiz_session_id) REFERENCES public.study_quiz_sessions(id) ON DELETE CASCADE; + + +-- +-- Name: study_quiz_session_jobs study_quiz_session_jobs_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.study_quiz_session_jobs + ADD CONSTRAINT study_quiz_session_jobs_user_id_fkey FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE; + + +-- +-- Name: study_quiz_sessions study_quiz_sessions_study_topic_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.study_quiz_sessions + ADD CONSTRAINT study_quiz_sessions_study_topic_id_fkey FOREIGN KEY (study_topic_id) REFERENCES public.study_topics(id) ON DELETE CASCADE; + + +-- +-- Name: study_quiz_sessions study_quiz_sessions_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.study_quiz_sessions + ADD CONSTRAINT study_quiz_sessions_user_id_fkey FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE; + + +-- +-- Name: study_reminders study_reminders_study_topic_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.study_reminders + ADD CONSTRAINT study_reminders_study_topic_id_fkey FOREIGN KEY (study_topic_id) REFERENCES public.study_topics(id) ON DELETE SET NULL; + + +-- +-- Name: study_reminders study_reminders_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.study_reminders + ADD CONSTRAINT study_reminders_user_id_fkey FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE; + + +-- +-- Name: study_session_assets study_session_assets_document_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.study_session_assets + ADD CONSTRAINT study_session_assets_document_id_fkey FOREIGN KEY (document_id) REFERENCES public.documents(id) ON DELETE CASCADE; + + +-- +-- Name: study_session_assets study_session_assets_study_session_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.study_session_assets + ADD CONSTRAINT study_session_assets_study_session_id_fkey FOREIGN KEY (study_session_id) REFERENCES public.study_sessions(id) ON DELETE CASCADE; + + +-- +-- Name: study_sessions study_sessions_study_topic_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.study_sessions + ADD CONSTRAINT study_sessions_study_topic_id_fkey FOREIGN KEY (study_topic_id) REFERENCES public.study_topics(id) ON DELETE SET NULL; + + +-- +-- Name: study_sessions study_sessions_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.study_sessions + ADD CONSTRAINT study_sessions_user_id_fkey FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE; + + +-- +-- Name: study_topic_documents study_topic_documents_document_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.study_topic_documents + ADD CONSTRAINT study_topic_documents_document_id_fkey FOREIGN KEY (document_id) REFERENCES public.documents(id) ON DELETE CASCADE; + + +-- +-- Name: study_topic_documents study_topic_documents_study_topic_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.study_topic_documents + ADD CONSTRAINT study_topic_documents_study_topic_id_fkey FOREIGN KEY (study_topic_id) REFERENCES public.study_topics(id) ON DELETE CASCADE; + + +-- +-- Name: study_topic_documents study_topic_documents_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.study_topic_documents + ADD CONSTRAINT study_topic_documents_user_id_fkey FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE; + + +-- +-- Name: study_topic_subject_notes study_topic_subject_notes_study_topic_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.study_topic_subject_notes + ADD CONSTRAINT study_topic_subject_notes_study_topic_id_fkey FOREIGN KEY (study_topic_id) REFERENCES public.study_topics(id) ON DELETE CASCADE; + + +-- +-- Name: study_topic_subject_notes study_topic_subject_notes_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.study_topic_subject_notes + ADD CONSTRAINT study_topic_subject_notes_user_id_fkey FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE; + + +-- +-- Name: study_topics study_topics_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.study_topics + ADD CONSTRAINT study_topics_user_id_fkey FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE; + + +-- +-- Name: tasks tasks_document_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.tasks + ADD CONSTRAINT tasks_document_id_fkey FOREIGN KEY (document_id) REFERENCES public.documents(id); + + +-- +-- Name: worker_capabilities worker_capabilities_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.worker_capabilities + ADD CONSTRAINT worker_capabilities_user_id_fkey FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE RESTRICT; + + +-- +-- Name: worker_heartbeats worker_heartbeats_worker_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.worker_heartbeats + ADD CONSTRAINT worker_heartbeats_worker_id_fkey FOREIGN KEY (worker_id) REFERENCES public.worker_capabilities(worker_id) ON DELETE CASCADE; + + +-- +-- Name: worker_jobs worker_jobs_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.worker_jobs + ADD CONSTRAINT worker_jobs_user_id_fkey FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE RESTRICT; + + +-- +-- Name: worker_jobs worker_jobs_worker_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.worker_jobs + ADD CONSTRAINT worker_jobs_worker_id_fkey FOREIGN KEY (worker_id) REFERENCES public.worker_capabilities(worker_id) ON DELETE SET NULL; + + +-- +-- PostgreSQL database dump complete +-- + + diff --git a/scripts/ci/migration_smoke.sh b/scripts/ci/migration_smoke.sh index 244c9a8..1266988 100755 --- a/scripts/ci/migration_smoke.sh +++ b/scripts/ci/migration_smoke.sh @@ -77,28 +77,41 @@ run_scenario() { fi } -scenario_fresh() { - reset_db - emit_single_txn "${MIGS[@]}" | psql_exec -d "$DB" -} +BASELINE_CUTOFF=358 +BASELINE_FILE="$MIG_DIR/_baseline/0358_schema_baseline.sql" -scenario_dr() { - reset_db - local phase1=() phase2=() f base ver p1out p1rc +# post-baseline(버전 > cutoff) 마이그 파일만 출력 +_post_baseline() { + local f base ver for f in "${MIGS[@]}"; do base="$(basename "$f")"; ver="${base%%_*}"; ver="$((10#$ver))" - if [ "$ver" -le 319 ]; then phase1+=("$f"); else phase2+=("$f"); fi + [ "$ver" -gt "$BASELINE_CUTOFF" ] && printf '%s\n' "$f" done - # phase1: 001~319 자동커밋 (과거 운영 DB = 타입/값 모두 커밋된 상태) - p1out="$( emit_autocommit "${phase1[@]}" 2>/dev/null | psql_exec -d "$DB" 2>&1 )"; p1rc=$? - if [ "$p1rc" -ne 0 ]; then - local p1last; p1last="$(printf '%s\n' "$p1out" | grep '>>>APPLY' | tail -1 | sed 's/>>>APPLY //')" - printf '%s\n' ">>>APPLY ${p1last}" # run_scenario 가 마지막 마커를 읽도록 전달 - printf '%s\n' "$p1out" | grep -iE 'ERROR|unsafe|DETAIL' | head -2 - return 1 +} + +# FRESH — init_db fresh 경로 미러: baseline 적재 + post-baseline 을 단일 트랜잭션 +scenario_fresh() { + reset_db + local post=(); while IFS= read -r f; do post+=("$f"); done < <(_post_baseline) + { + echo '\set ON_ERROR_STOP on'; echo 'BEGIN;' + echo "\\echo >>>APPLY _baseline" + cat "$BASELINE_FILE"; echo + for f in "${post[@]}"; do + echo "\\echo >>>APPLY $(basename "$f")"; cat "$f"; echo + done + echo 'COMMIT;' + } | psql_exec -d "$DB" +} + +# INCREMENTAL — 기존 운영 DB(at cutoff) 모사: baseline 커밋 후 post-baseline 을 별 트랜잭션 +scenario_dr() { + reset_db + if ! { echo '\set ON_ERROR_STOP on'; cat "$BASELINE_FILE"; } | psql_exec -d "$DB" >/dev/null 2>&1; then + printf '%s\n' ">>>APPLY _baseline"; echo "baseline 적재 실패"; return 1 fi - # phase2: 320~end 단일 트랜잭션 (catch-up 업그레이드) - emit_single_txn "${phase2[@]}" 2>/dev/null | psql_exec -d "$DB" + local post=(); while IFS= read -r f; do post+=("$f"); done < <(_post_baseline) + emit_single_txn "${post[@]}" 2>/dev/null | psql_exec -d "$DB" } # ── 컨테이너 기동 ── @@ -109,17 +122,17 @@ echo "pg: $(docker exec "$CNAME" psql -U postgres -tAc 'show server_version' 2>/ echo fail=0 -echo "── FRESH (빈 DB 단일 트랜잭션) ──" +echo "── FRESH (baseline 적재 + post-baseline 단일 트랜잭션 = init_db fresh 경로) ──" run_scenario FRESH scenario_fresh || fail=1 echo -echo "── DR (001~319 커밋 후 320~end 단일 트랜잭션) ──" +echo "── INCREMENTAL (baseline 커밋 후 post-baseline 별 트랜잭션 = 기존 DB 증분) ──" run_scenario DR scenario_dr || fail=1 echo if [ "$fail" -eq 0 ]; then - echo "RESULT: PASS — 빈 DB/DR 모두 단일 트랜잭션 적용 가능 (enum-barrier 적용됨)" + echo "RESULT: PASS — fresh/incremental 모두 baseline+post-baseline 적용 가능" exit 0 else - echo "RESULT: FAIL — 위 지점에서 단일 트랜잭션 적용 불가 (enum-same-txn 등 미수정)" + echo "RESULT: FAIL — baseline/post-baseline 적용 불가 (위 지점)" exit 1 fi