fix(db): enable PRAGMA foreign_keys = ON on every connection

SQLite defaults foreign_keys to OFF, which meant every ondelete=CASCADE
and ondelete=SET NULL declared on an FK was inert. Bulk Query.delete()
calls — which bypass ORM cascade — then silently orphaned child rows,
and Paper.journal_id would not NULL out when a Journal was deleted.

Wiring the pragma into apply_performance_pragmas (which is already
registered via event.listen(engine, "connect")) makes every pooled
connection honor DDL-level cascade.
This commit is contained in:
LearningCircuit
2026-04-15 01:18:05 +02:00
parent c1047177b2
commit 5078c867e6
2 changed files with 8 additions and 1 deletions

View File

@@ -438,6 +438,11 @@ def apply_performance_pragmas(cursor_or_conn: Any) -> None:
cursor_or_conn.execute("PRAGMA temp_store = MEMORY")
cursor_or_conn.execute("PRAGMA busy_timeout = 10000") # 10 second timeout
# SQLite defaults foreign_keys to OFF — without this every
# ondelete="CASCADE"/"SET NULL" declared on an FK is inert,
# including for raw-SQL DELETEs issued by Query.delete().
cursor_or_conn.execute("PRAGMA foreign_keys = ON")
# Cache size - registry validates min/max range
cache_mb = get_env_setting("db_config.cache_size_mb", 64)
cache_pages = -(cache_mb * 1024) # Negative for KB cache size

View File

@@ -925,7 +925,9 @@ def clear_history():
except Exception:
logger.exception("Error removing report file")
# Delete records from the database, except active research
# Query.delete() bypasses ORM cascade; child rows clean up
# via DDL-level ondelete="CASCADE" only because PRAGMA
# foreign_keys = ON is set on every connection.
if active_ids:
db_session.query(ResearchHistory).filter(
~ResearchHistory.id.in_(active_ids)