"""Database connection helpers. Provides: - :func:`get_conn` — a context manager that yields a psycopg connection. - :func:`apply_schema` — idempotently applies ``infra/schema.sql``. The connection pool (psycopg_pool) is intentionally deferred to F-05 (ingest); here we expose a simple per-call ``psycopg.connect`` wrapper that is sufficient for schema migration and unit-testing without requiring an additional ``psycopg[pool]`` dependency at scaffold stage. """ from __future__ import annotations from collections.abc import Generator from contextlib import contextmanager from pathlib import Path import psycopg import psycopg.rows from codex.config import get_settings @contextmanager def get_conn() -> Generator[psycopg.Connection[psycopg.rows.DictRow], None, None]: """Yield a psycopg connection with dict-row factory. The connection is opened from :func:`codex.config.get_settings` ``database_url`` and closed automatically when the context exits. Usage:: with get_conn() as conn: conn.execute("SELECT 1") """ settings = get_settings() with psycopg.connect( settings.database_url, row_factory=psycopg.rows.dict_row, ) as conn: yield conn def apply_schema(conn: psycopg.Connection[psycopg.rows.DictRow]) -> None: """Idempotently apply ``infra/schema.sql`` to the connected database. Safe to call on every startup — all DDL statements use ``CREATE … IF NOT EXISTS``. Args: conn: An open psycopg connection (obtained via :func:`get_conn`). """ schema_path = Path(__file__).parent.parent / "infra" / "schema.sql" sql = schema_path.read_text(encoding="utf-8") conn.execute(sql) conn.commit()