refactor(hermes_cli): unify session_recovery copy_table/copy_state_meta(_salvage) into salvage-flagged helpers

This commit is contained in:
Teknium
2026-09-02 20:51:04 -07:00
parent 113f04616b
commit eef274150f
+37 -47
View File
@@ -511,26 +511,31 @@ def _copy_table(
destination: sqlite3.Connection,
table: str,
*,
salvage: bool,
chunk_size: int,
progress_cb: Optional[ProgressCallback],
source_rows: Optional[int],
) -> dict[str, Any]:
result: dict[str, Any] = {
"source_rows": source_rows,
"copied_rows": 0,
}
"""Copy one canonical table: straight chunked copy, or rowid-range salvage when ``salvage``."""
if table == "state_meta":
return _copy_state_meta(
source, destination, salvage=salvage, chunk_size=chunk_size, progress_cb=progress_cb, source_rows=source_rows
)
if salvage:
return _copy_table_salvage(
source, destination, table, chunk_size=chunk_size, progress_cb=progress_cb, source_rows=source_rows
)
result: dict[str, Any] = {"source_rows": source_rows, "copied_rows": 0}
columns = _compatible_columns(source, destination, table, result)
if columns is None:
return result
quoted, placeholders = _quoted_columns(columns)
insert_prefix = "INSERT OR REPLACE" if table == "state_meta" else "INSERT"
return _copy_rows(
source,
destination,
f'SELECT {quoted} FROM "{table}"',
(),
f'{insert_prefix} INTO "{table}" ({quoted}) VALUES ({placeholders})',
f'INSERT INTO "{table}" ({quoted}) VALUES ({placeholders})',
table=table,
chunk_size=chunk_size,
progress_cb=progress_cb,
@@ -863,14 +868,34 @@ def _copy_state_meta(
source: sqlite3.Connection,
destination: sqlite3.Connection,
*,
salvage: bool,
chunk_size: int,
progress_cb: Optional[ProgressCallback],
source_rows: Optional[int],
) -> dict[str, Any]:
problem = _state_meta_precheck(source, destination, source_rows, salvage=False)
"""Copy user metadata rows; derived FTS/topic keys (``_GENERATED_META_KEYS``) are regenerated, not copied."""
problem = _state_meta_precheck(source, destination, source_rows, salvage=salvage)
if problem is not None:
return problem
if salvage:
def keep_user_meta(row: tuple[Any, ...], columns: tuple[str, ...]) -> bool:
return str(row[columns.index("key")]) not in _GENERATED_META_KEYS
result = _copy_table_salvage(
source,
destination,
"state_meta",
chunk_size=chunk_size,
progress_cb=progress_cb,
source_rows=source_rows,
insert_prefix="INSERT OR REPLACE",
row_filter=keep_user_meta,
)
result["source_meta_rows"] = result.pop("source_rows")
result["excluded_keys"] = sorted(_GENERATED_META_KEYS)
return result
placeholders = ", ".join("?" for _ in _GENERATED_META_KEYS)
params = tuple(_GENERATED_META_KEYS)
filtered_source_rows: Optional[int] = None
@@ -882,8 +907,7 @@ def _copy_state_meta(
).fetchone()[0]
)
except sqlite3.DatabaseError:
# The copy loop below will return the concrete read error.
pass
pass # the copy loop below will return the concrete read error
return _copy_rows(
source,
@@ -899,37 +923,6 @@ def _copy_state_meta(
)
def _copy_state_meta_salvage(
source: sqlite3.Connection,
destination: sqlite3.Connection,
*,
chunk_size: int,
progress_cb: Optional[ProgressCallback],
source_rows: Optional[int],
) -> dict[str, Any]:
"""Salvage readable user metadata while regenerating derived FTS state."""
problem = _state_meta_precheck(source, destination, source_rows, salvage=True)
if problem is not None:
return problem
def keep_user_meta(row: tuple[Any, ...], columns: tuple[str, ...]) -> bool:
return str(row[columns.index("key")]) not in _GENERATED_META_KEYS
result = _copy_table_salvage(
source,
destination,
"state_meta",
chunk_size=chunk_size,
progress_cb=progress_cb,
source_rows=source_rows,
insert_prefix="INSERT OR REPLACE",
row_filter=keep_user_meta,
)
result["source_meta_rows"] = result.pop("source_rows")
result["excluded_keys"] = sorted(_GENERATED_META_KEYS)
return result
def _reconstruct_missing_sessions(
destination: sqlite3.Connection,
) -> dict[str, Any]:
@@ -1453,9 +1446,6 @@ def recover_session_database(
topic_tables=any(inspection["tables"][table].get("available") for table in _TOPIC_TABLES),
)
copy_meta, copy_rows = (
(_copy_state_meta_salvage, _copy_table_salvage) if allow_partial else (_copy_state_meta, _copy_table)
)
copy_report: dict[str, dict[str, Any]] = {}
for table in (*_CANONICAL_TABLES, "state_meta", *_TOPIC_TABLES, *_AUXILIARY_TABLES):
if table not in _CANONICAL_TABLES and not inspection["tables"][table].get("available"):
@@ -1463,11 +1453,11 @@ def recover_session_database(
continue
if table in _AUXILIARY_TABLES:
_ensure_auxiliary_destination_schema(destination_conn, table)
copy_function, args = (copy_meta, ()) if table == "state_meta" else (copy_rows, (table,))
copy_report[table] = copy_function(
copy_report[table] = _copy_table(
source_conn,
destination_conn,
*args,
table,
salvage=allow_partial,
chunk_size=chunk_size,
progress_cb=progress_cb,
source_rows=inspection["tables"][table].get("rows"),