fix(opencode): queries open the database read-only without touching its sidecars (Issue #60, Linux item 5)
A read-write open checkpointed the WAL and deleted -wal/-shm on close; a plain mode=ro open created missing ones and left them. _ro_connect uses the sidecars only when both exist (a live writer keeps them) and reads the file as immutable otherwise. verify_artifact, _has_session, discover, _descendants and residual_artifacts (db_rows_naming takes a connect) use it; the purge's own writes are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
14d7a46181
commit
ac5f1154c7
3 files changed
+103
-8
No files matched your search
@@ -10,6 +10,15 @@ _LEGACY_TABLES = (("message", "session_id"), ("messages", "session_id"), ("part"
|
||||
("parts", "session_id"), ("todo", "session_id"), ("session_share", "session_id"),
|
||||
("session", "id"), ("sessions", "id"))
|
||||
|
||||
def _ro_connect(db: str):
|
||||
"""A read that neither creates nor removes anything beside db (Issue #60):
|
||||
the WAL sidecars are used only when both already exist (a live writer
|
||||
keeps them); otherwise the file is read as immutable."""
|
||||
path = os.path.abspath(db)
|
||||
both = os.path.exists(path + "-wal") and os.path.exists(path + "-shm")
|
||||
return sqlite3.connect(f"file:{quote(path)}?mode=ro" + ("" if both else "&immutable=1"), uri=True)
|
||||
|
||||
|
||||
_PROVIDER_ENV_VARS = (
|
||||
'ANTHROPIC_API_KEY',
|
||||
'OPENAI_API_KEY',
|
||||
@@ -139,7 +148,7 @@ class OpenCodeAgentAdapter(BaseAgentAdapter):
|
||||
try:
|
||||
# opencode 2.x keeps sessions in session_v2 (a 1.x `session` table
|
||||
# survives a migration but is no longer written).
|
||||
conn = sqlite3.connect(db)
|
||||
conn = _ro_connect(db)
|
||||
r = conn.execute("SELECT directory, time_created FROM session_v2 WHERE id=?", (uuid,)).fetchone()
|
||||
conn.close()
|
||||
if not r:
|
||||
@@ -202,7 +211,7 @@ class OpenCodeAgentAdapter(BaseAgentAdapter):
|
||||
"""Child sessions (any depth) of uuid in session_v2 and a 1.x session table."""
|
||||
found, todo = [], [uuid]
|
||||
try:
|
||||
conn = sqlite3.connect(f"file:{quote(os.path.abspath(db))}?mode=ro", uri=True)
|
||||
conn = _ro_connect(db)
|
||||
except Exception:
|
||||
return found
|
||||
try:
|
||||
@@ -224,7 +233,7 @@ class OpenCodeAgentAdapter(BaseAgentAdapter):
|
||||
|
||||
def _has_session(self, db: str, uuid: str) -> bool:
|
||||
try:
|
||||
conn = sqlite3.connect(db)
|
||||
conn = _ro_connect(db)
|
||||
try:
|
||||
return conn.execute("SELECT 1 FROM session_v2 WHERE id=?", (uuid,)).fetchone() is not None
|
||||
finally:
|
||||
@@ -239,7 +248,7 @@ class OpenCodeAgentAdapter(BaseAgentAdapter):
|
||||
found = scan_names(os.path.join(self._data_home(ctx), "opencode"), uuid)
|
||||
for i in [uuid] + (self._descendants(db, uuid) if db and os.path.isfile(db) else []):
|
||||
rows = db_rows_naming(db, i, ("session_id", "aggregate_id"),
|
||||
id_tables=(("session", "id"), ("session_v2", "id")))
|
||||
id_tables=(("session", "id"), ("session_v2", "id")), connect=_ro_connect)
|
||||
found += rows if i == uuid else [f"{r} [child session {i}]" for r in rows]
|
||||
return found
|
||||
|
||||
@@ -306,7 +315,7 @@ class OpenCodeAgentAdapter(BaseAgentAdapter):
|
||||
params.append(int(float(ctx.epoch) * 1000))
|
||||
query += " ORDER BY time_created DESC LIMIT 20"
|
||||
|
||||
conn = sqlite3.connect(db)
|
||||
conn = _ro_connect(db)
|
||||
rows = conn.execute(query, params).fetchall()
|
||||
conn.close()
|
||||
candidates = []
|
||||
|
||||
Reference in new issue
Block a user