Fix Postgres 500s on days and entries by dropping SQLite rowid.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Lars 2026-09-07 12:50:53 +02:00
parent 9d5befdde9
commit ea38528ecf
5 changed files with 11 additions and 5 deletions

View File

@ -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 (SELECT COUNT(*) FROM messages m WHERE m.conversation_id = c.id) AS message_count
FROM conversations c FROM conversations c
WHERE c.profile_id = ? AND c.journal_day_id = ? 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), (profile_id, journal_day_id),
).fetchall() ).fetchall()

View File

@ -285,7 +285,7 @@ def current_entries(profile_id: str, journal_day_id: str) -> list[dict]:
FROM journal_entries e FROM journal_entries e
LEFT JOIN journal_entry_versions v ON v.id = e.current_version_id 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 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), (profile_id, journal_day_id),
).fetchall() ).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 JOIN journal_days d ON d.id = e.journal_day_id
LEFT JOIN journal_entry_versions v ON v.id = e.current_version_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 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), (profile_id, space_id),
).fetchall() ).fetchall()

View File

@ -95,7 +95,7 @@ def _day_messages(profile_id: str, spec: dict[str, Any]) -> list[dict]:
FROM messages m FROM messages m
JOIN conversations c ON c.id = m.conversation_id JOIN conversations c ON c.id = m.conversation_id
WHERE m.profile_id = ? AND c.journal_day_id = ? AND m.conversation_id IN ({placeholders}) 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 ? LIMIT ?
""", """,
(profile_id, journal_day_id, *conversation_ids, fetch_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 FROM messages m
JOIN conversations c ON c.id = m.conversation_id JOIN conversations c ON c.id = m.conversation_id
WHERE m.profile_id = ? AND c.journal_day_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 ? LIMIT ?
""", """,
(profile_id, journal_day_id, fetch_limit), (profile_id, journal_day_id, fetch_limit),

View File

@ -9,6 +9,7 @@ import os
import re import re
_INSERT_OR_IGNORE = re.compile(r"INSERT\s+OR\s+IGNORE\s+INTO", re.IGNORECASE) _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) _ON_CONFLICT_PAREN = re.compile(r"ON\s+CONFLICT\s*\(", re.IGNORECASE)
_PRAGMA_TABLE = re.compile( _PRAGMA_TABLE = re.compile(
r"^\s*PRAGMA\s+table_info\(\s*['\"]?(\w+)['\"]?\s*\)\s*;?\s*$", 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: def adapt_sql(sql: str) -> str:
"""Make a SQLite-shaped statement runnable on PostgreSQL.""" """Make a SQLite-shaped statement runnable on PostgreSQL."""
sql = sqlite_schema_to_postgres(sql) sql = sqlite_schema_to_postgres(sql)
sql = _ROWID_COL.sub(".id", sql)
sql = _ON_CONFLICT_PAREN.sub("ON CONFLICT (", sql) sql = _ON_CONFLICT_PAREN.sub("ON CONFLICT (", sql)
used_ignore = bool(_INSERT_OR_IGNORE.search(sql)) used_ignore = bool(_INSERT_OR_IGNORE.search(sql))
sql = _INSERT_OR_IGNORE.sub("INSERT INTO", sql) sql = _INSERT_OR_IGNORE.sub("INSERT INTO", sql)

View File

@ -60,6 +60,10 @@ def main() -> None:
case_sql = adapt_sql("UPDATE t SET x = CASE WHEN ? = 1 THEN ? ELSE x END") 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") 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);") 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") expect(parts == ["CREATE TABLE a (id TEXT)", "CREATE TABLE b (id TEXT)"], "split two statements")
schema = (ROOT / "schema.sql").read_text(encoding="utf-8") schema = (ROOT / "schema.sql").read_text(encoding="utf-8")