Inside Bitcoin
Opening the selected file…
Opening the selected file…
The code behind the network, with room to understand it.
Release published 2026-10-28 · Snapshot fetched 2026-11-01. Latest stable release at fetch time; development branches and Git history are excluded. Only the selected code window and visible folders load here.
251throw std::runtime_error("SQLiteDatabase: Cannot reopen an in-memory database");252}253Open(m_additional_flags);254}256void SQLiteDatabase::Open(int additional_flags)257{258int flags = SQLITE_OPEN_FULLMUTEX | SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | additional_flags;260if (m_db == nullptr) {261if (!(flags & SQLITE_OPEN_MEMORY)) {262TryCreateDirectories(m_dir_path);263if (!IsDirWritable(m_dir_path)) {264throw std::runtime_error(strprintf("SQLiteDatabase: Failed to open database in directory '%s': directory is not writable", fs::PathToString(m_dir_path)));265}266}268int ret = sqlite3_open_v2(m_file_path.c_str(), &m_db, flags, nullptr);269if (ret != SQLITE_OK) {270throw std::runtime_error(strprintf("SQLiteDatabase: Failed to open database: %s\n", sqlite3_errstr(ret)));271}272ret = sqlite3_extended_result_codes(m_db, 1);273if (ret != SQLITE_OK) {274throw std::runtime_error(strprintf("SQLiteDatabase: Failed to enable extended result codes: %s\n", sqlite3_errstr(ret)));275}276// Trace SQL statements if tracing is enabled with -debug=walletdb -loglevel=walletdb:trace277if (util::log::ShouldTraceLog(BCLog::WALLETDB)) {278ret = sqlite3_trace_v2(m_db, SQLITE_TRACE_STMT, TraceSqlCallback, this);279if (ret != SQLITE_OK) {280LogWarning("Failed to enable SQL tracing for %s", Filename());281}282}283}285if (sqlite3_db_readonly(m_db, "main") != 0) {286throw std::runtime_error("SQLiteDatabase: Database opened in readonly mode but read-write permissions are needed");287}289// Acquire an exclusive lock on the database290// First change the locking mode to exclusive291SetPragma(m_db, "locking_mode", "exclusive", "Unable to change database locking mode to exclusive");292// Now begin a transaction to acquire the exclusive lock. This lock won't be released until we close because of the exclusive locking mode.293int ret = sqlite3_exec(m_db, "BEGIN EXCLUSIVE TRANSACTION", nullptr, nullptr, nullptr);294if (ret != SQLITE_OK) {295throw std::runtime_error("SQLiteDatabase: Unable to obtain an exclusive lock on the database, is it being used by another instance of " CLIENT_NAME "?\n");296}297ret = sqlite3_exec(m_db, "COMMIT", nullptr, nullptr, nullptr);298if (ret != SQLITE_OK) {299throw std::runtime_error(strprintf("SQLiteDatabase: Unable to end exclusive lock transaction: %s\n", sqlite3_errstr(ret)));300}302// Enable fullfsync for the platforms that use it303SetPragma(m_db, "fullfsync", "true", "Failed to enable fullfsync");305if (m_use_unsafe_sync) {306// Use normal synchronous mode for the journal307LogWarning("SQLite is configured to not wait for data to be flushed to disk. Data loss and corruption may occur.");308SetPragma(m_db, "synchronous", "OFF", "Failed to set synchronous mode to OFF");309}311// Make the table for our key-value pairs312// First check that the main table exists313sqlite3_stmt* check_main_stmt{nullptr};314ret = sqlite3_prepare_v2(m_db, "SELECT name FROM sqlite_master WHERE type='table' AND name='main'", -1, &check_main_stmt, nullptr);315if (ret != SQLITE_OK) {316throw std::runtime_error(strprintf("SQLiteDatabase: Failed to prepare statement to check table existence: %s\n", sqlite3_errstr(ret)));317}318ret = sqlite3_step(check_main_stmt);319if (sqlite3_finalize(check_main_stmt) != SQLITE_OK) {320throw std::runtime_error(strprintf("SQLiteDatabase: Failed to finalize statement checking table existence: %s\n", sqlite3_errstr(ret)));321}322bool table_exists;323if (ret == SQLITE_DONE) {324table_exists = false;325} else if (ret == SQLITE_ROW) {326table_exists = true;327} else {328throw std::runtime_error(strprintf("SQLiteDatabase: Failed to execute statement to check table existence: %s\n", sqlite3_errstr(ret)));329}331// Do the db setup things because the table doesn't exist only when we are creating a new wallet332if (!table_exists) {333ret = sqlite3_exec(m_db, "CREATE TABLE main(key BLOB PRIMARY KEY NOT NULL, value BLOB NOT NULL)", nullptr, nullptr, nullptr);334if (ret != SQLITE_OK) {335throw std::runtime_error(strprintf("SQLiteDatabase: Failed to create new database: %s\n", sqlite3_errstr(ret)));336}338// Set the application id339uint32_t app_id = ReadBE32(Params().MessageStart().data());340SetPragma(m_db, "application_id", strprintf("%d", static_cast<int32_t>(app_id)),341"Failed to set the application id");343// Set the user version344SetPragma(m_db, "user_version", strprintf("%d", WALLET_SCHEMA_VERSION),345"Failed to set the wallet schema version");346}347}349bool SQLiteDatabase::Rewrite()350{351// Rewrite the database using the VACUUM command: https://sqlite.org/lang_vacuum.html352int ret = sqlite3_exec(m_db, "VACUUM", nullptr, nullptr, nullptr);353return ret == SQLITE_OK;354}356bool SQLiteDatabase::Backup(const std::string& dest) const357{358sqlite3* db_copy;359int res = sqlite3_open(dest.c_str(), &db_copy);360if (res != SQLITE_OK) {361sqlite3_close(db_copy);362return false;363}364sqlite3_backup* backup = sqlite3_backup_init(db_copy, "main", m_db, "main");365if (!backup) {366LogWarning("Unable to begin sqlite backup: %s", sqlite3_errmsg(m_db));367sqlite3_close(db_copy);368return false;369}370// Specifying -1 will copy all of the pages371res = sqlite3_backup_step(backup, -1);372if (res != SQLITE_DONE) {373LogWarning("Unable to continue sqlite backup: %s", sqlite3_errstr(res));374sqlite3_backup_finish(backup);375sqlite3_close(db_copy);376return false;377}378res = sqlite3_backup_finish(backup);379sqlite3_close(db_copy);380return res == SQLITE_OK;381}383void SQLiteDatabase::Close()384{385int res = sqlite3_close(m_db);386if (res != SQLITE_OK) {387throw std::runtime_error(strprintf("SQLiteDatabase: Failed to close database: %s\n", sqlite3_errstr(res)));388}389m_db = nullptr;390}392bool SQLiteDatabase::HasActiveTxn()393{394// 'sqlite3_get_autocommit' returns true by default, and false if a transaction has begun and not been committed or rolled back.395return m_db && sqlite3_get_autocommit(m_db) == 0;396}398int SQliteExecHandler::Exec(SQLiteDatabase& database, const std::string& statement)399{400return sqlite3_exec(database.m_db, statement.data(), nullptr, nullptr, nullptr);401}403std::unique_ptr<DatabaseBatch> SQLiteDatabase::MakeBatch()404{405// We ignore flush_on_close because we don't do manual flushing for SQLite406return std::make_unique<SQLiteBatch>(*this);407}409SQLiteBatch::SQLiteBatch(SQLiteDatabase& database)410: m_database(database)411{412// Make sure we have a db handle413assert(m_database.m_db);415SetupSQLStatements();416}418void SQLiteBatch::Close()419{420bool force_conn_refresh = false;422// If we began a transaction, and it wasn't committed, abort the transaction in progress423if (m_txn) {424if (TxnAbort()) {425LogWarning("SQLiteBatch: Batch closed unexpectedly without the transaction being explicitly committed or aborted");426} else {427// If transaction cannot be aborted, it means there is a bug or there has been data corruption. Try to recover in this case428// by closing and reopening the database. Closing the database should also ensure that any changes made since the transaction429// was opened will be rolled back and future transactions can succeed without committing old data.430force_conn_refresh = true;431LogWarning("SQLiteBatch: Batch closed and failed to abort transaction, resetting db connection..");432}433}435// Free all of the prepared statements436const std::vector<std::pair<sqlite3_stmt**, const char*>> statements{437{&m_read_stmt, "read"},438{&m_insert_stmt, "insert"},439{&m_overwrite_stmt, "overwrite"},440{&m_delete_stmt, "delete"},441{&m_delete_prefix_stmt, "delete prefix"},442};444for (const auto& [stmt_prepared, stmt_description] : statements) {445int res = sqlite3_finalize(*stmt_prepared);446if (res != SQLITE_OK) {447LogWarning("SQLiteBatch: Batch closed but could not finalize %s statement: %s",448stmt_description, sqlite3_errstr(res));449}450*stmt_prepared = nullptr;451}453if (force_conn_refresh) {454if (m_database.m_additional_flags & SQLITE_OPEN_MEMORY) {455throw std::runtime_error("SQLiteDatabase: Cannot recover in-memory database connection");456}457m_database.Close();458try {459m_database.Open();460// If TxnAbort failed and we refreshed the connection, the semaphore was not released, so release it here to avoid deadlocks on future writes.461m_database.m_write_semaphore.release();462} catch (const std::runtime_error&) {463// If open fails, cleanup this object and rethrow the exception464m_database.Close();465throw;466}467}468}470bool SQLiteBatch::ReadKey(DataStream&& key, DataStream& value)471{472if (!m_database.m_db) return false;473assert(m_read_stmt);475// Bind: leftmost parameter in statement is index 1476if (!BindBlobToStatement(m_read_stmt, 1, key, "key")) return false;477int res = sqlite3_step(m_read_stmt);478if (res != SQLITE_ROW) {479if (res != SQLITE_DONE) {480// SQLITE_DONE means "not found", don't log an error in that case.481LogWarning("Unable to execute read statement: %s", sqlite3_errstr(res));482}483sqlite3_clear_bindings(m_read_stmt);484sqlite3_reset(m_read_stmt);485return false;486}487// Leftmost column in result is index 0488value.clear();489value.write(SpanFromBlob(m_read_stmt, 0));491sqlite3_clear_bindings(m_read_stmt);492sqlite3_reset(m_read_stmt);493return true;494}496bool SQLiteBatch::WriteKey(DataStream&& key, DataStream&& value, bool overwrite)497{498if (!m_database.m_db) return false;499assert(m_insert_stmt && m_overwrite_stmt);