From ea38528ecf58273e8764afd5c7f7a0d976ea94db Mon Sep 17 00:00:00 2001 From: Lars Date: Mon, 7 Sep 2026 12:50:53 +0200 Subject: [PATCH] Fix Postgres 500s on days and entries by dropping SQLite rowid. Co-authored-by: Cursor --- backend/dialogue_store.py | 2 +- backend/journal_store.py | 4 ++-- backend/retrieval.py | 4 ++-- backend/sql_compat.py | 2 ++ backend/tests/test_sql_compat.py | 4 ++++ 5 files changed, 11 insertions(+), 5 deletions(-) diff --git a/backend/dialogue_store.py b/backend/dialogue_store.py index b5721ec..b9d99b5 100644 --- a/backend/dialogue_store.py +++ b/backend/dialogue_store.py @@ -339,7 +339,7 @@ def list_conversations_for_day(profile_id: str, journal_day_id: str) -> list[dic (SELECT COUNT(*) FROM messages m WHERE m.conversation_id = c.id) AS message_count FROM conversations c WHERE c.profile_id = ? AND c.journal_day_id = ? - ORDER BY c.created, c.rowid + ORDER BY c.created, c.id """, (profile_id, journal_day_id), ).fetchall() diff --git a/backend/journal_store.py b/backend/journal_store.py index cdf3676..f8a44a1 100644 --- a/backend/journal_store.py +++ b/backend/journal_store.py @@ -285,7 +285,7 @@ def current_entries(profile_id: str, journal_day_id: str) -> list[dict]: FROM journal_entries e LEFT JOIN journal_entry_versions v ON v.id = e.current_version_id WHERE e.profile_id = ? AND e.journal_day_id = ? AND e.deleted_at IS NULL - ORDER BY e.created, e.rowid + ORDER BY e.created, e.id """, (profile_id, journal_day_id), ).fetchall() @@ -514,7 +514,7 @@ def list_space_entries(profile_id: str, space_id: str) -> list[dict]: JOIN journal_days d ON d.id = e.journal_day_id LEFT JOIN journal_entry_versions v ON v.id = e.current_version_id WHERE e.profile_id = ? AND e.space_id = ? AND e.deleted_at IS NULL - ORDER BY d.calendar_date DESC, e.created ASC, e.rowid ASC + ORDER BY d.calendar_date DESC, e.created ASC, e.id ASC """, (profile_id, space_id), ).fetchall() diff --git a/backend/retrieval.py b/backend/retrieval.py index 4b01d8c..6c4134e 100644 --- a/backend/retrieval.py +++ b/backend/retrieval.py @@ -95,7 +95,7 @@ def _day_messages(profile_id: str, spec: dict[str, Any]) -> list[dict]: FROM messages m JOIN conversations c ON c.id = m.conversation_id WHERE m.profile_id = ? AND c.journal_day_id = ? AND m.conversation_id IN ({placeholders}) - ORDER BY c.created, c.rowid, m.seq, m.id + ORDER BY c.created, c.id, m.seq, m.id LIMIT ? """, (profile_id, journal_day_id, *conversation_ids, fetch_limit), @@ -107,7 +107,7 @@ def _day_messages(profile_id: str, spec: dict[str, Any]) -> list[dict]: FROM messages m JOIN conversations c ON c.id = m.conversation_id WHERE m.profile_id = ? AND c.journal_day_id = ? - ORDER BY c.created, c.rowid, m.seq, m.id + ORDER BY c.created, c.id, m.seq, m.id LIMIT ? """, (profile_id, journal_day_id, fetch_limit), diff --git a/backend/sql_compat.py b/backend/sql_compat.py index 84ac0c3..bad48a1 100644 --- a/backend/sql_compat.py +++ b/backend/sql_compat.py @@ -9,6 +9,7 @@ import os import re _INSERT_OR_IGNORE = re.compile(r"INSERT\s+OR\s+IGNORE\s+INTO", re.IGNORECASE) +_ROWID_COL = re.compile(r"\.rowid\b", re.IGNORECASE) _ON_CONFLICT_PAREN = re.compile(r"ON\s+CONFLICT\s*\(", re.IGNORECASE) _PRAGMA_TABLE = re.compile( r"^\s*PRAGMA\s+table_info\(\s*['\"]?(\w+)['\"]?\s*\)\s*;?\s*$", @@ -101,6 +102,7 @@ def replace_placeholders(sql: str) -> str: def adapt_sql(sql: str) -> str: """Make a SQLite-shaped statement runnable on PostgreSQL.""" sql = sqlite_schema_to_postgres(sql) + sql = _ROWID_COL.sub(".id", sql) sql = _ON_CONFLICT_PAREN.sub("ON CONFLICT (", sql) used_ignore = bool(_INSERT_OR_IGNORE.search(sql)) sql = _INSERT_OR_IGNORE.sub("INSERT INTO", sql) diff --git a/backend/tests/test_sql_compat.py b/backend/tests/test_sql_compat.py index 037c67f..348a204 100644 --- a/backend/tests/test_sql_compat.py +++ b/backend/tests/test_sql_compat.py @@ -60,6 +60,10 @@ def main() -> None: case_sql = adapt_sql("UPDATE t SET x = CASE WHEN ? = 1 THEN ? ELSE x END") expect("CASE WHEN %s = 1 THEN %s ELSE x END" in case_sql, "boolean-safe case placeholder") + rowid_sql = adapt_sql("SELECT e.* FROM journal_entries e ORDER BY e.created, e.rowid") + expect("e.rowid" not in rowid_sql, "sqlite rowid removed for postgres") + expect("ORDER BY e.created, e.id" in rowid_sql, "rowid becomes primary id") + parts = split_sql("CREATE TABLE a (id TEXT); CREATE TABLE b (id TEXT);") expect(parts == ["CREATE TABLE a (id TEXT)", "CREATE TABLE b (id TEXT)"], "split two statements") schema = (ROOT / "schema.sql").read_text(encoding="utf-8")