deepshield / db /database.py
ar07xd's picture
Sync from GitHub via hub-sync
26f3f24 verified
from sqlalchemy import create_engine, event
from sqlalchemy.orm import DeclarativeBase, sessionmaker
from config import settings
engine = create_engine(
settings.DATABASE_URL,
connect_args={"check_same_thread": False} if settings.DATABASE_URL.startswith("sqlite") else {},
pool_pre_ping=True,
pool_recycle=300,
)
if settings.DATABASE_URL.startswith("sqlite"):
@event.listens_for(engine, "connect")
def _sqlite_on_connect(dbapi_conn, _):
# Enforce FK constraints (needed for ON DELETE SET NULL) + WAL for better
# concurrent reads while a writer is active.
cur = dbapi_conn.cursor()
cur.execute("PRAGMA foreign_keys=ON")
cur.execute("PRAGMA journal_mode=WAL")
cur.close()
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
class Base(DeclarativeBase):
pass
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
def init_db():
from db import models # noqa: F401
from sqlalchemy import inspect, text
Base.metadata.create_all(bind=engine)
# Phase 19.4 — lightweight in-place migration for new columns.
# Alembic is overkill here; just ALTER TABLE when a new column is missing.
insp = inspect(engine)
if "analyses" in insp.get_table_names():
existing = {c["name"] for c in insp.get_columns("analyses")}
with engine.begin() as conn:
if "media_hash" not in existing:
conn.execute(text("ALTER TABLE analyses ADD COLUMN media_hash VARCHAR(64)"))
if "media_path" not in existing:
conn.execute(text("ALTER TABLE analyses ADD COLUMN media_path VARCHAR(512)"))
if "thumbnail_url" not in existing:
conn.execute(text("ALTER TABLE analyses ADD COLUMN thumbnail_url VARCHAR(512)"))
# Indices (CREATE INDEX IF NOT EXISTS is SQLite+Postgres safe)
for ddl in (
"CREATE INDEX IF NOT EXISTS ix_analyses_media_hash ON analyses (media_hash)",
"CREATE INDEX IF NOT EXISTS ix_record_user_created ON analyses (user_id, created_at)",
"CREATE INDEX IF NOT EXISTS ix_report_analysis ON reports (analysis_id)",
):
try:
conn.execute(text(ddl))
except Exception: # noqa: BLE001
pass