1414
1515from .app_paths import get_output_databases_dir
1616from .logging_config import get_logger
17+ from .sqlite_diagnostics import collect_sqlite_diagnostics , format_sqlite_diagnostics
1718
1819try :
1920 import zstandard as zstd # type: ignore
@@ -1755,9 +1756,10 @@ def _load_latest_message_previews(account_dir: Path, usernames: list[str]) -> di
17551756
17561757 session_db_path = Path (account_dir ) / "session.db"
17571758 if session_db_path .exists () and remaining :
1758- sconn = sqlite3 .connect (str (session_db_path ))
1759- sconn .row_factory = sqlite3 .Row
1759+ sconn : Optional [sqlite3 .Connection ] = None
17601760 try :
1761+ sconn = sqlite3 .connect (str (session_db_path ))
1762+ sconn .row_factory = sqlite3 .Row
17611763 uniq = list (dict .fromkeys ([u for u in remaining if u ]))
17621764 chunk_size = 900
17631765 for i in range (0 , len (uniq ), chunk_size ):
@@ -1786,10 +1788,24 @@ def _load_latest_message_previews(account_dir: Path, usernames: list[str]) -> di
17861788 if not u :
17871789 continue
17881790 expected_ts_by_user [u ] = int (r ["last_timestamp" ] or 0 )
1791+ except sqlite3 .DatabaseError as e :
1792+ expected_ts_by_user = {}
1793+ logger .warning (
1794+ "[sessions.preview] session timestamp lookup failed account=%s db=%s usernames=%s sample_usernames=%s error=%s diag=%s" ,
1795+ account_dir .name ,
1796+ str (session_db_path ),
1797+ len (remaining ),
1798+ sorted ([u for u in remaining if u ])[:5 ],
1799+ str (e ),
1800+ format_sqlite_diagnostics (
1801+ collect_sqlite_diagnostics (session_db_path , quick_check = True , table_name = "SessionTable" )
1802+ ),
1803+ )
17891804 except Exception :
17901805 expected_ts_by_user = {}
17911806 finally :
1792- sconn .close ()
1807+ if sconn is not None :
1808+ sconn .close ()
17931809
17941810 if _DEBUG_SESSIONS :
17951811 logger .info (
@@ -1800,9 +1816,16 @@ def _load_latest_message_previews(account_dir: Path, usernames: list[str]) -> di
18001816 )
18011817
18021818 for db_path in db_paths :
1803- conn = sqlite3 .connect (str (db_path ))
1804- conn .row_factory = sqlite3 .Row
1819+ conn : Optional [sqlite3 .Connection ] = None
1820+ stage = "connect"
1821+ stage_username = ""
1822+ stage_table = ""
18051823 try :
1824+ stage = "connect"
1825+ conn = sqlite3 .connect (str (db_path ))
1826+ conn .row_factory = sqlite3 .Row
1827+
1828+ stage = "sqlite_master"
18061829 rows = conn .execute ("SELECT name FROM sqlite_master WHERE type='table'" ).fetchall ()
18071830 names = [str (r [0 ]) for r in rows if r and r [0 ]]
18081831 lower_to_actual = {n .lower (): n for n in names }
@@ -1818,9 +1841,12 @@ def _load_latest_message_previews(account_dir: Path, usernames: list[str]) -> di
18181841
18191842 conn .text_factory = bytes
18201843 for u , tn in found .items ():
1844+ stage_username = str (u )
1845+ stage_table = str (tn )
18211846 quoted = _quote_ident (tn )
18221847 try :
18231848 try :
1849+ stage = "latest_row_with_name2id"
18241850 r = conn .execute (
18251851 "SELECT "
18261852 "m.local_type, m.message_content, m.compress_content, m.create_time, m.sort_seq, m.local_id, "
@@ -1831,13 +1857,28 @@ def _load_latest_message_previews(account_dir: Path, usernames: list[str]) -> di
18311857 "LIMIT 1"
18321858 ).fetchone ()
18331859 except Exception :
1860+ stage = "latest_row_without_name2id"
18341861 r = conn .execute (
18351862 "SELECT "
18361863 "local_type, message_content, compress_content, create_time, sort_seq, local_id, '' AS sender_username "
18371864 f"FROM { quoted } "
18381865 "ORDER BY sort_seq DESC, local_id DESC "
18391866 "LIMIT 1"
18401867 ).fetchone ()
1868+ except sqlite3 .DatabaseError as e :
1869+ logger .warning (
1870+ "[sessions.preview] latest row query failed account=%s db=%s username=%s table=%s stage=%s error=%s diag=%s" ,
1871+ account_dir .name ,
1872+ str (db_path ),
1873+ str (u ),
1874+ str (tn ),
1875+ stage ,
1876+ str (e ),
1877+ format_sqlite_diagnostics (
1878+ collect_sqlite_diagnostics (db_path , quick_check = True , table_name = tn )
1879+ ),
1880+ )
1881+ continue
18411882 except Exception as e :
18421883 if _DEBUG_SESSIONS :
18431884 logger .info (
@@ -1855,6 +1896,7 @@ def _load_latest_message_previews(account_dir: Path, usernames: list[str]) -> di
18551896 expected_ts = int (expected_ts_by_user .get (u ) or 0 )
18561897 if expected_ts > 0 and create_time > 0 and create_time < expected_ts :
18571898 try :
1899+ stage = "latest_row_by_create_time_with_name2id"
18581900 r2 = conn .execute (
18591901 "SELECT "
18601902 "m.local_type, m.message_content, m.compress_content, m.create_time, m.sort_seq, m.local_id, "
@@ -1866,13 +1908,28 @@ def _load_latest_message_previews(account_dir: Path, usernames: list[str]) -> di
18661908 ).fetchone ()
18671909 except Exception :
18681910 try :
1911+ stage = "latest_row_by_create_time_without_name2id"
18691912 r2 = conn .execute (
18701913 "SELECT "
18711914 "local_type, message_content, compress_content, create_time, sort_seq, local_id, '' AS sender_username "
18721915 f"FROM { quoted } "
18731916 "ORDER BY COALESCE(create_time, 0) DESC, COALESCE(sort_seq, 0) DESC, local_id DESC "
18741917 "LIMIT 1"
18751918 ).fetchone ()
1919+ except sqlite3 .DatabaseError as e :
1920+ logger .warning (
1921+ "[sessions.preview] latest row requery failed account=%s db=%s username=%s table=%s stage=%s error=%s diag=%s" ,
1922+ account_dir .name ,
1923+ str (db_path ),
1924+ str (u ),
1925+ str (tn ),
1926+ stage ,
1927+ str (e ),
1928+ format_sqlite_diagnostics (
1929+ collect_sqlite_diagnostics (db_path , quick_check = True , table_name = tn )
1930+ ),
1931+ )
1932+ r2 = None
18761933 except Exception :
18771934 r2 = None
18781935
@@ -1900,8 +1957,28 @@ def _load_latest_message_previews(account_dir: Path, usernames: list[str]) -> di
19001957 prev = best .get (u )
19011958 if prev is None or sort_key > prev [0 ]:
19021959 best [u ] = (sort_key , preview )
1960+ except sqlite3 .DatabaseError as e :
1961+ logger .warning (
1962+ "[sessions.preview] malformed message db account=%s db=%s stage=%s username=%s table=%s remaining=%s sample_usernames=%s error=%s diag=%s" ,
1963+ account_dir .name ,
1964+ str (db_path ),
1965+ stage ,
1966+ stage_username ,
1967+ stage_table ,
1968+ len (remaining ),
1969+ sorted ([u for u in remaining if u ])[:5 ],
1970+ str (e ),
1971+ format_sqlite_diagnostics (
1972+ collect_sqlite_diagnostics (db_path , quick_check = True , table_name = (stage_table or None ))
1973+ ),
1974+ )
1975+ continue
19031976 finally :
1904- conn .close ()
1977+ if conn is not None :
1978+ try :
1979+ conn .close ()
1980+ except Exception :
1981+ pass
19051982
19061983 previews = {u : v [1 ] for u , v in best .items () if v and v [1 ]}
19071984 if _DEBUG_SESSIONS :
0 commit comments