From 4f7b8277163787c4f50d39ceb9da3dd4a2dd6a16 Mon Sep 17 00:00:00 2001 From: Vivian Balakrishnan Date: Mon, 16 Mar 2026 09:45:57 +0800 Subject: [PATCH] fix: skip writes in IncrementAccessCount and LogOp when read-only DB opened with OpenReadOnly now sets readOnly=true on the struct. IncrementAccessCount and LogOp both return early when readOnly, preventing spurious stderr warnings when mnemon is used against a read-only mount. Co-Authored-By: Claude Sonnet 4.6 --- internal/store/db.go | 12 ++++++++---- internal/store/node.go | 4 ++++ internal/store/oplog.go | 4 ++++ 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/internal/store/db.go b/internal/store/db.go index 33bba10..cf55c7f 100644 --- a/internal/store/db.go +++ b/internal/store/db.go @@ -26,11 +26,15 @@ type dbExecer interface { // DB wraps the SQLite database connection. type DB struct { - conn *sql.DB - tx *sql.Tx // current active transaction (nil = no transaction) - path string + conn *sql.DB + tx *sql.Tx // current active transaction (nil = no transaction) + path string + readOnly bool } +// IsReadOnly returns true if the database was opened in read-only mode. +func (db *DB) IsReadOnly() bool { return db.readOnly } + // execer returns the active transaction if set, otherwise the raw connection. func (db *DB) execer() dbExecer { if db.tx != nil { @@ -186,7 +190,7 @@ func OpenReadOnly(dataDir string) (*DB, error) { return nil, fmt.Errorf("open readonly database: %w", err) } conn.SetMaxOpenConns(1) - return &DB{conn: conn, path: dbPath}, nil + return &DB{conn: conn, path: dbPath, readOnly: true}, nil } // Open opens (or creates) the SQLite database at the given directory. diff --git a/internal/store/node.go b/internal/store/node.go index 593afaf..5447430 100644 --- a/internal/store/node.go +++ b/internal/store/node.go @@ -135,7 +135,11 @@ func (db *DB) UpdateEntities(id string, entities []string) error { } // IncrementAccessCount bumps the access count and refreshes last_accessed_at. +// No-op when the database is read-only. func (db *DB) IncrementAccessCount(id string) error { + if db.readOnly { + return nil + } _, err := db.execer().Exec( `UPDATE insights SET access_count = access_count + 1, last_accessed_at = ? WHERE id = ?`, time.Now().UTC().Format(time.RFC3339), id) diff --git a/internal/store/oplog.go b/internal/store/oplog.go index cecbaea..afc07ac 100644 --- a/internal/store/oplog.go +++ b/internal/store/oplog.go @@ -11,7 +11,11 @@ const MaxOplogEntries = 5000 // LogOp records an operation to the oplog and trims old entries beyond MaxOplogEntries. // Best-effort: failures are logged to stderr but do not propagate. +// No-op when the database is read-only. func (db *DB) LogOp(operation, insightID, detail string) { + if db.readOnly { + return + } if _, err := db.execer().Exec( `INSERT INTO oplog (operation, insight_id, detail, created_at) VALUES (?, ?, ?, ?)`, operation, insightID, detail, time.Now().UTC().Format(time.RFC3339)); err != nil {