"""SQLite connection helper — stdlib sqlite3, parameterized queries only. PRAGMA foreign_keys=ON, journal_mode=WAL, busy_timeout=5000 on every connection. No ORM. Initializes schema on first boot and seeds number_sequences for current year. """ from __future__ import annotations import os import sqlite3 import uuid from datetime import datetime, timezone from pathlib import Path from typing import Any _SCHEMA_FILE = Path(__file__).resolve().parent / "schema.sql" def _utcnow_iso() -> str: return datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ") def get_db_path() -> str: return os.environ.get("DRE_DB_PATH", "/opt/dre-portal/data/dre.db") def get_upload_dir() -> str: return os.environ.get("DRE_UPLOAD_DIR", "/opt/dre-portal/data/uploads") def _apply_pragmas(conn: sqlite3.Connection) -> None: conn.execute("PRAGMA foreign_keys = ON;") conn.execute("PRAGMA journal_mode = WAL;") conn.execute("PRAGMA busy_timeout = 5000;") def get_conn() -> sqlite3.Connection: """Return a connection with pragmas applied and row factory.""" db_path = get_db_path() Path(db_path).parent.mkdir(parents=True, exist_ok=True) conn = sqlite3.connect(db_path, timeout=5.0) conn.row_factory = sqlite3.Row _apply_pragmas(conn) return conn def init_db() -> None: """Create schema if absent and seed number_sequences for the current year.""" with get_conn() as conn: schema_sql = _SCHEMA_FILE.read_text() conn.executescript(schema_sql) # Seed sequences for current year (idempotent) year = datetime.now(timezone.utc).year for prefix in ("DRE", "CLT"): conn.execute( "INSERT OR IGNORE INTO number_sequences (prefix, year, last_value) VALUES (?, ?, 0)", (prefix, year), ) conn.commit() def new_uuid() -> str: return str(uuid.uuid4()) def utcnow_iso() -> str: return _utcnow_iso() def next_sequence_number(conn: sqlite3.Connection, prefix: str) -> str: """Atomically allocate the next DRE-YYYY-NNNN / CLT-YYYY-NNNN in a single transaction. Uses UPDATE ... RETURNING (SQLite 3.35+) — no races.""" year = datetime.now(timezone.utc).year # Ensure row exists conn.execute( "INSERT OR IGNORE INTO number_sequences (prefix, year, last_value) VALUES (?, ?, 0)", (prefix, year), ) cur = conn.execute( "UPDATE number_sequences SET last_value = last_value + 1 WHERE prefix = ? AND year = ? " "RETURNING last_value", (prefix, year), ) row = cur.fetchone() if row is None: # Should not happen, but handle defensively raise RuntimeError(f"failed to allocate sequence for {prefix}/{year}") n = row[0] if isinstance(row, tuple) else row["last_value"] val = n if isinstance(n, int) else int(n) return f"{prefix}-{year}-{val:04d}" def query_one(conn: sqlite3.Connection, sql: str, params: tuple[Any, ...] = ()) -> sqlite3.Row | None: cur = conn.execute(sql, params) return cur.fetchone() def query_all(conn: sqlite3.Connection, sql: str, params: tuple[Any, ...] = ()) -> list[sqlite3.Row]: cur = conn.execute(sql, params) return cur.fetchall()