From ee43743f126120b1dc2a601f149a88052f39152b Mon Sep 17 00:00:00 2001 From: Pablo Martin Date: Sat, 4 Jul 2026 15:30:13 -0300 Subject: [PATCH] wallet: store m_additional_flags in SQLiteDatabase to fix reopen path SQLiteDatabase::Open() (the public override) always reopens the database with no additional flags. If SQLiteBatch::Close() triggers the force_conn_refresh path (TxnAbort failed), it calls Open() which drops the original additional_flags, causing in-memory databases to be reopened as on-disk instead. Store additional_flags as a member and use it in Open() so the reconnect preserves the original flags. For in-memory databases, connection recovery makes no sense as all data would be lost; both the force_conn_refresh path and the public Open() now throw instead. Co-authored-by: Sjors Provoost --- src/wallet/sqlite.cpp | 12 +++++++++--- src/wallet/sqlite.h | 4 ++++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/wallet/sqlite.cpp b/src/wallet/sqlite.cpp index 8d39fbae2e1..28123ecf4f4 100644 --- a/src/wallet/sqlite.cpp +++ b/src/wallet/sqlite.cpp @@ -116,7 +116,7 @@ SQLiteDatabase::SQLiteDatabase(const fs::path& dir_path, const fs::path& file_pa {} SQLiteDatabase::SQLiteDatabase(const fs::path& dir_path, const fs::path& file_path, const DatabaseOptions& options, int additional_flags) - : WalletDatabase(), m_dir_path(dir_path), m_file_path(fs::PathToString(file_path)), m_write_semaphore(1), m_use_unsafe_sync(options.use_unsafe_sync) + : WalletDatabase(), m_dir_path(dir_path), m_file_path(fs::PathToString(file_path)), m_additional_flags(additional_flags), m_write_semaphore(1), m_use_unsafe_sync(options.use_unsafe_sync) { { LOCK(g_sqlite_mutex); @@ -139,7 +139,7 @@ SQLiteDatabase::SQLiteDatabase(const fs::path& dir_path, const fs::path& file_pa } try { - Open(additional_flags); + Open(m_additional_flags); } catch (const std::runtime_error&) { // If open fails, cleanup this object and rethrow the exception Cleanup(); @@ -247,7 +247,10 @@ bool SQLiteDatabase::Verify(bilingual_str& error) void SQLiteDatabase::Open() { - Open(/*additional_flags*/0); + if (m_additional_flags & SQLITE_OPEN_MEMORY) { + throw std::runtime_error("SQLiteDatabase: Cannot reopen an in-memory database"); + } + Open(m_additional_flags); } void SQLiteDatabase::Open(int additional_flags) @@ -448,6 +451,9 @@ void SQLiteBatch::Close() } if (force_conn_refresh) { + if (m_database.m_additional_flags & SQLITE_OPEN_MEMORY) { + throw std::runtime_error("SQLiteDatabase: Cannot recover in-memory database connection"); + } m_database.Close(); try { m_database.Open(); diff --git a/src/wallet/sqlite.h b/src/wallet/sqlite.h index fb0fa39c8bd..3a35bb24bba 100644 --- a/src/wallet/sqlite.h +++ b/src/wallet/sqlite.h @@ -103,10 +103,14 @@ public: class SQLiteDatabase : public WalletDatabase { private: + friend class SQLiteBatch; + const fs::path m_dir_path; const std::string m_file_path; + const int m_additional_flags; + /** * This mutex protects SQLite initialization and shutdown. * sqlite3_config() and sqlite3_shutdown() are not thread-safe (sqlite3_initialize() is).