Source code for imednet.utils.db
"""Centralized database connection utilities."""
import sqlite3
import threading
from collections.abc import Iterator
from contextlib import contextmanager
from pathlib import Path
_db_init_locks: dict[str, threading.RLock] = {}
_db_init_locks_guard = threading.RLock()
def _get_db_init_lock(resolved_path: Path) -> threading.RLock:
"""Return a thread-safe lock for initializing a specific database file."""
key = str(resolved_path)
with _db_init_locks_guard:
return _db_init_locks.setdefault(key, threading.RLock())
[docs]def get_sqlite_connection(
db_path: str | Path,
timeout: float = 30.0,
busy_timeout_ms: int = 30000,
foreign_keys: bool = False,
) -> sqlite3.Connection:
"""Return a SQLite connection configured for concurrent access."""
resolved_path = Path(db_path).expanduser()
resolved_path.parent.mkdir(parents=True, exist_ok=True)
lock = _get_db_init_lock(resolved_path)
with lock:
try:
conn = sqlite3.connect(resolved_path, timeout=timeout)
except sqlite3.Error as exc:
raise sqlite3.OperationalError(f"Unable to open sqlite database: {exc}") from exc
conn.row_factory = sqlite3.Row
if busy_timeout_ms:
conn.execute(f"PRAGMA busy_timeout={busy_timeout_ms};")
conn.execute("PRAGMA journal_mode=WAL;")
conn.execute("PRAGMA synchronous=NORMAL;")
if foreign_keys:
conn.execute("PRAGMA foreign_keys=ON;")
return conn
[docs]@contextmanager
def sqlite_connection(
db_path: str | Path,
timeout: float = 30.0,
busy_timeout_ms: int = 30000,
foreign_keys: bool = False,
) -> Iterator[sqlite3.Connection]:
"""Context manager yielding a safely configured SQLite connection."""
conn = get_sqlite_connection(
db_path,
timeout=timeout,
busy_timeout_ms=busy_timeout_ms,
foreign_keys=foreign_keys,
)
try:
yield conn
finally:
conn.close()