Branch data Line data Source code
# 1 : : // Copyright (c) 2020 The Bitcoin Core developers
# 2 : : // Distributed under the MIT software license, see the accompanying
# 3 : : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
# 4 : :
# 5 : : #include <wallet/sqlite.h>
# 6 : :
# 7 : : #include <chainparams.h>
# 8 : : #include <crypto/common.h>
# 9 : : #include <logging.h>
# 10 : : #include <sync.h>
# 11 : : #include <util/strencodings.h>
# 12 : : #include <util/system.h>
# 13 : : #include <util/translation.h>
# 14 : : #include <wallet/db.h>
# 15 : :
# 16 : : #include <sqlite3.h>
# 17 : : #include <stdint.h>
# 18 : :
# 19 : : #include <optional>
# 20 : : #include <utility>
# 21 : : #include <vector>
# 22 : :
# 23 : : static constexpr int32_t WALLET_SCHEMA_VERSION = 0;
# 24 : :
# 25 : : static Mutex g_sqlite_mutex;
# 26 : : static int g_sqlite_count GUARDED_BY(g_sqlite_mutex) = 0;
# 27 : :
# 28 : : static void ErrorLogCallback(void* arg, int code, const char* msg)
# 29 : 4 : {
# 30 : : // From sqlite3_config() documentation for the SQLITE_CONFIG_LOG option:
# 31 : : // "The void pointer that is the second argument to SQLITE_CONFIG_LOG is passed through as
# 32 : : // the first parameter to the application-defined logger function whenever that function is
# 33 : : // invoked."
# 34 : : // Assert that this is the case:
# 35 : 4 : assert(arg == nullptr);
# 36 : 4 : LogPrintf("SQLite Error. Code: %d. Message: %s\n", code, msg);
# 37 : 4 : }
# 38 : :
# 39 : : static std::optional<int> ReadPragmaInteger(sqlite3* db, const std::string& key, const std::string& description, bilingual_str& error)
# 40 : 544 : {
# 41 : 544 : std::string stmt_text = strprintf("PRAGMA %s", key);
# 42 : 544 : sqlite3_stmt* pragma_read_stmt{nullptr};
# 43 : 544 : int ret = sqlite3_prepare_v2(db, stmt_text.c_str(), -1, &pragma_read_stmt, nullptr);
# 44 [ - + ]: 544 : if (ret != SQLITE_OK) {
# 45 : 0 : sqlite3_finalize(pragma_read_stmt);
# 46 : 0 : error = Untranslated(strprintf("SQLiteDatabase: Failed to prepare the statement to fetch %s: %s", description, sqlite3_errstr(ret)));
# 47 : 0 : return std::nullopt;
# 48 : 0 : }
# 49 : 544 : ret = sqlite3_step(pragma_read_stmt);
# 50 [ - + ]: 544 : if (ret != SQLITE_ROW) {
# 51 : 0 : sqlite3_finalize(pragma_read_stmt);
# 52 : 0 : error = Untranslated(strprintf("SQLiteDatabase: Failed to fetch %s: %s", description, sqlite3_errstr(ret)));
# 53 : 0 : return std::nullopt;
# 54 : 0 : }
# 55 : 544 : int result = sqlite3_column_int(pragma_read_stmt, 0);
# 56 : 544 : sqlite3_finalize(pragma_read_stmt);
# 57 : 544 : return result;
# 58 : 544 : }
# 59 : :
# 60 : : static void SetPragma(sqlite3* db, const std::string& key, const std::string& value, const std::string& err_msg)
# 61 : 1252 : {
# 62 : 1252 : std::string stmt_text = strprintf("PRAGMA %s = %s", key, value);
# 63 : 1252 : int ret = sqlite3_exec(db, stmt_text.c_str(), nullptr, nullptr, nullptr);
# 64 [ - + ]: 1252 : if (ret != SQLITE_OK) {
# 65 : 0 : throw std::runtime_error(strprintf("SQLiteDatabase: %s: %s\n", err_msg, sqlite3_errstr(ret)));
# 66 : 0 : }
# 67 : 1252 : }
# 68 : :
# 69 : : SQLiteDatabase::SQLiteDatabase(const fs::path& dir_path, const fs::path& file_path, bool mock)
# 70 : : : WalletDatabase(), m_mock(mock), m_dir_path(dir_path.string()), m_file_path(file_path.string())
# 71 : 322 : {
# 72 : 322 : {
# 73 : 322 : LOCK(g_sqlite_mutex);
# 74 : 322 : LogPrintf("Using SQLite Version %s\n", SQLiteDatabaseVersion());
# 75 : 322 : LogPrintf("Using wallet %s\n", m_dir_path);
# 76 : :
# 77 [ + + ]: 322 : if (++g_sqlite_count == 1) {
# 78 : : // Setup logging
# 79 : 196 : int ret = sqlite3_config(SQLITE_CONFIG_LOG, ErrorLogCallback, nullptr);
# 80 [ - + ]: 196 : if (ret != SQLITE_OK) {
# 81 : 0 : throw std::runtime_error(strprintf("SQLiteDatabase: Failed to setup error log: %s\n", sqlite3_errstr(ret)));
# 82 : 0 : }
# 83 : : // Force serialized threading mode
# 84 : 196 : ret = sqlite3_config(SQLITE_CONFIG_SERIALIZED);
# 85 [ - + ]: 196 : if (ret != SQLITE_OK) {
# 86 : 0 : throw std::runtime_error(strprintf("SQLiteDatabase: Failed to configure serialized threading mode: %s\n", sqlite3_errstr(ret)));
# 87 : 0 : }
# 88 : 322 : }
# 89 : 322 : int ret = sqlite3_initialize(); // This is a no-op if sqlite3 is already initialized
# 90 [ - + ]: 322 : if (ret != SQLITE_OK) {
# 91 : 0 : throw std::runtime_error(strprintf("SQLiteDatabase: Failed to initialize SQLite: %s\n", sqlite3_errstr(ret)));
# 92 : 0 : }
# 93 : 322 : }
# 94 : :
# 95 : 322 : try {
# 96 : 322 : Open();
# 97 : 322 : } catch (const std::runtime_error&) {
# 98 : : // If open fails, cleanup this object and rethrow the exception
# 99 : 6 : Cleanup();
# 100 : 6 : throw;
# 101 : 6 : }
# 102 : 322 : }
# 103 : :
# 104 : : void SQLiteBatch::SetupSQLStatements()
# 105 : 61390 : {
# 106 : 61390 : const std::vector<std::pair<sqlite3_stmt**, const char*>> statements{
# 107 : 61390 : {&m_read_stmt, "SELECT value FROM main WHERE key = ?"},
# 108 : 61390 : {&m_insert_stmt, "INSERT INTO main VALUES(?, ?)"},
# 109 : 61390 : {&m_overwrite_stmt, "INSERT or REPLACE into main values(?, ?)"},
# 110 : 61390 : {&m_delete_stmt, "DELETE FROM main WHERE key = ?"},
# 111 : 61390 : {&m_cursor_stmt, "SELECT key, value FROM main"},
# 112 : 61390 : };
# 113 : :
# 114 [ + + ]: 306950 : for (const auto& [stmt_prepared, stmt_text] : statements) {
# 115 [ + - ]: 306950 : if (*stmt_prepared == nullptr) {
# 116 : 306950 : int res = sqlite3_prepare_v2(m_database.m_db, stmt_text, -1, stmt_prepared, nullptr);
# 117 [ - + ]: 306950 : if (res != SQLITE_OK) {
# 118 : 0 : throw std::runtime_error(strprintf(
# 119 : 0 : "SQLiteDatabase: Failed to setup SQL statements: %s\n", sqlite3_errstr(res)));
# 120 : 0 : }
# 121 : 306950 : }
# 122 : 306950 : }
# 123 : 61390 : }
# 124 : :
# 125 : : SQLiteDatabase::~SQLiteDatabase()
# 126 : 316 : {
# 127 : 316 : Cleanup();
# 128 : 316 : }
# 129 : :
# 130 : : void SQLiteDatabase::Cleanup() noexcept
# 131 : 322 : {
# 132 : 322 : Close();
# 133 : :
# 134 : 322 : LOCK(g_sqlite_mutex);
# 135 [ + + ]: 322 : if (--g_sqlite_count == 0) {
# 136 : 196 : int ret = sqlite3_shutdown();
# 137 [ - + ]: 196 : if (ret != SQLITE_OK) {
# 138 : 0 : LogPrintf("SQLiteDatabase: Failed to shutdown SQLite: %s\n", sqlite3_errstr(ret));
# 139 : 0 : }
# 140 : 196 : }
# 141 : 322 : }
# 142 : :
# 143 : : bool SQLiteDatabase::Verify(bilingual_str& error)
# 144 : 272 : {
# 145 : 272 : assert(m_db);
# 146 : :
# 147 : : // Check the application ID matches our network magic
# 148 : 272 : auto read_result = ReadPragmaInteger(m_db, "application_id", "the application id", error);
# 149 [ - + ]: 272 : if (!read_result.has_value()) return false;
# 150 : 272 : uint32_t app_id = static_cast<uint32_t>(read_result.value());
# 151 : 272 : uint32_t net_magic = ReadBE32(Params().MessageStart());
# 152 [ - + ]: 272 : if (app_id != net_magic) {
# 153 : 0 : error = strprintf(_("SQLiteDatabase: Unexpected application id. Expected %u, got %u"), net_magic, app_id);
# 154 : 0 : return false;
# 155 : 0 : }
# 156 : :
# 157 : : // Check our schema version
# 158 : 272 : read_result = ReadPragmaInteger(m_db, "user_version", "sqlite wallet schema version", error);
# 159 [ - + ]: 272 : if (!read_result.has_value()) return false;
# 160 : 272 : int32_t user_ver = read_result.value();
# 161 [ - + ]: 272 : if (user_ver != WALLET_SCHEMA_VERSION) {
# 162 : 0 : error = strprintf(_("SQLiteDatabase: Unknown sqlite wallet schema version %d. Only version %d is supported"), user_ver, WALLET_SCHEMA_VERSION);
# 163 : 0 : return false;
# 164 : 0 : }
# 165 : :
# 166 : 272 : sqlite3_stmt* stmt{nullptr};
# 167 : 272 : int ret = sqlite3_prepare_v2(m_db, "PRAGMA integrity_check", -1, &stmt, nullptr);
# 168 [ - + ]: 272 : if (ret != SQLITE_OK) {
# 169 : 0 : sqlite3_finalize(stmt);
# 170 : 0 : error = strprintf(_("SQLiteDatabase: Failed to prepare statement to verify database: %s"), sqlite3_errstr(ret));
# 171 : 0 : return false;
# 172 : 0 : }
# 173 : 544 : while (true) {
# 174 : 544 : ret = sqlite3_step(stmt);
# 175 [ + + ]: 544 : if (ret == SQLITE_DONE) {
# 176 : 272 : break;
# 177 : 272 : }
# 178 [ - + ]: 272 : if (ret != SQLITE_ROW) {
# 179 : 0 : error = strprintf(_("SQLiteDatabase: Failed to execute statement to verify database: %s"), sqlite3_errstr(ret));
# 180 : 0 : break;
# 181 : 0 : }
# 182 : 272 : const char* msg = (const char*)sqlite3_column_text(stmt, 0);
# 183 [ - + ]: 272 : if (!msg) {
# 184 : 0 : error = strprintf(_("SQLiteDatabase: Failed to read database verification error: %s"), sqlite3_errstr(ret));
# 185 : 0 : break;
# 186 : 0 : }
# 187 : 272 : std::string str_msg(msg);
# 188 [ + - ]: 272 : if (str_msg == "ok") {
# 189 : 272 : continue;
# 190 : 272 : }
# 191 [ # # ]: 0 : if (error.empty()) {
# 192 : 0 : error = _("Failed to verify database") + Untranslated("\n");
# 193 : 0 : }
# 194 : 0 : error += Untranslated(strprintf("%s\n", str_msg));
# 195 : 0 : }
# 196 : 272 : sqlite3_finalize(stmt);
# 197 : 272 : return error.empty();
# 198 : 272 : }
# 199 : :
# 200 : : void SQLiteDatabase::Open()
# 201 : 322 : {
# 202 : 322 : int flags = SQLITE_OPEN_FULLMUTEX | SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE;
# 203 [ - + ]: 322 : if (m_mock) {
# 204 : 0 : flags |= SQLITE_OPEN_MEMORY; // In memory database for mock db
# 205 : 0 : }
# 206 : :
# 207 [ + - ]: 322 : if (m_db == nullptr) {
# 208 [ + - ]: 322 : if (!m_mock) {
# 209 : 322 : TryCreateDirectories(m_dir_path);
# 210 : 322 : }
# 211 : 322 : int ret = sqlite3_open_v2(m_file_path.c_str(), &m_db, flags, nullptr);
# 212 [ - + ]: 322 : if (ret != SQLITE_OK) {
# 213 : 0 : throw std::runtime_error(strprintf("SQLiteDatabase: Failed to open database: %s\n", sqlite3_errstr(ret)));
# 214 : 0 : }
# 215 : 322 : }
# 216 : :
# 217 [ - + ]: 322 : if (sqlite3_db_readonly(m_db, "main") != 0) {
# 218 : 0 : throw std::runtime_error("SQLiteDatabase: Database opened in readonly mode but read-write permissions are needed");
# 219 : 0 : }
# 220 : :
# 221 : : // Acquire an exclusive lock on the database
# 222 : : // First change the locking mode to exclusive
# 223 : 322 : SetPragma(m_db, "locking_mode", "exclusive", "Unable to change database locking mode to exclusive");
# 224 : : // Now begin a transaction to acquire the exclusive lock. This lock won't be released until we close because of the exclusive locking mode.
# 225 : 322 : int ret = sqlite3_exec(m_db, "BEGIN EXCLUSIVE TRANSACTION", nullptr, nullptr, nullptr);
# 226 [ + + ]: 322 : if (ret != SQLITE_OK) {
# 227 : 5 : throw std::runtime_error("SQLiteDatabase: Unable to obtain an exclusive lock on the database, is it being used by another bitcoind?\n");
# 228 : 5 : }
# 229 : 317 : ret = sqlite3_exec(m_db, "COMMIT", nullptr, nullptr, nullptr);
# 230 [ - + ]: 317 : if (ret != SQLITE_OK) {
# 231 : 0 : throw std::runtime_error(strprintf("SQLiteDatabase: Unable to end exclusive lock transaction: %s\n", sqlite3_errstr(ret)));
# 232 : 0 : }
# 233 : :
# 234 : : // Enable fullfsync for the platforms that use it
# 235 : 317 : SetPragma(m_db, "fullfsync", "true", "Failed to enable fullfsync");
# 236 : :
# 237 [ + + ]: 317 : if (gArgs.GetBoolArg("-unsafesqlitesync", false)) {
# 238 : : // Use normal synchronous mode for the journal
# 239 : 301 : LogPrintf("WARNING SQLite is configured to not wait for data to be flushed to disk. Data loss and corruption may occur.\n");
# 240 : 301 : SetPragma(m_db, "synchronous", "OFF", "Failed to set synchronous mode to OFF");
# 241 : 301 : }
# 242 : :
# 243 : : // Make the table for our key-value pairs
# 244 : : // First check that the main table exists
# 245 : 317 : sqlite3_stmt* check_main_stmt{nullptr};
# 246 : 317 : ret = sqlite3_prepare_v2(m_db, "SELECT name FROM sqlite_master WHERE type='table' AND name='main'", -1, &check_main_stmt, nullptr);
# 247 [ - + ]: 317 : if (ret != SQLITE_OK) {
# 248 : 0 : throw std::runtime_error(strprintf("SQLiteDatabase: Failed to prepare statement to check table existence: %s\n", sqlite3_errstr(ret)));
# 249 : 0 : }
# 250 : 317 : ret = sqlite3_step(check_main_stmt);
# 251 [ - + ]: 317 : if (sqlite3_finalize(check_main_stmt) != SQLITE_OK) {
# 252 : 0 : throw std::runtime_error(strprintf("SQLiteDatabase: Failed to finalize statement checking table existence: %s\n", sqlite3_errstr(ret)));
# 253 : 0 : }
# 254 : 317 : bool table_exists;
# 255 [ + + ]: 317 : if (ret == SQLITE_DONE) {
# 256 : 157 : table_exists = false;
# 257 [ + + ]: 160 : } else if (ret == SQLITE_ROW) {
# 258 : 159 : table_exists = true;
# 259 : 159 : } else {
# 260 : 1 : throw std::runtime_error(strprintf("SQLiteDatabase: Failed to execute statement to check table existence: %s\n", sqlite3_errstr(ret)));
# 261 : 1 : }
# 262 : :
# 263 : : // Do the db setup things because the table doesn't exist only when we are creating a new wallet
# 264 [ + + ]: 316 : if (!table_exists) {
# 265 : 157 : ret = sqlite3_exec(m_db, "CREATE TABLE main(key BLOB PRIMARY KEY NOT NULL, value BLOB NOT NULL)", nullptr, nullptr, nullptr);
# 266 [ - + ]: 157 : if (ret != SQLITE_OK) {
# 267 : 0 : throw std::runtime_error(strprintf("SQLiteDatabase: Failed to create new database: %s\n", sqlite3_errstr(ret)));
# 268 : 0 : }
# 269 : :
# 270 : : // Set the application id
# 271 : 157 : uint32_t app_id = ReadBE32(Params().MessageStart());
# 272 : 157 : SetPragma(m_db, "application_id", strprintf("%d", static_cast<int32_t>(app_id)),
# 273 : 157 : "Failed to set the application id");
# 274 : :
# 275 : : // Set the user version
# 276 : 157 : SetPragma(m_db, "user_version", strprintf("%d", WALLET_SCHEMA_VERSION),
# 277 : 157 : "Failed to set the wallet schema version");
# 278 : 157 : }
# 279 : 316 : }
# 280 : :
# 281 : : bool SQLiteDatabase::Rewrite(const char* skip)
# 282 : 11 : {
# 283 : : // Rewrite the database using the VACUUM command: https://sqlite.org/lang_vacuum.html
# 284 : 11 : int ret = sqlite3_exec(m_db, "VACUUM", nullptr, nullptr, nullptr);
# 285 : 11 : return ret == SQLITE_OK;
# 286 : 11 : }
# 287 : :
# 288 : : bool SQLiteDatabase::Backup(const std::string& dest) const
# 289 : 16 : {
# 290 : 16 : sqlite3* db_copy;
# 291 : 16 : int res = sqlite3_open(dest.c_str(), &db_copy);
# 292 [ + + ]: 16 : if (res != SQLITE_OK) {
# 293 : 2 : sqlite3_close(db_copy);
# 294 : 2 : return false;
# 295 : 2 : }
# 296 : 14 : sqlite3_backup* backup = sqlite3_backup_init(db_copy, "main", m_db, "main");
# 297 [ - + ]: 14 : if (!backup) {
# 298 : 0 : LogPrintf("%s: Unable to begin backup: %s\n", __func__, sqlite3_errmsg(m_db));
# 299 : 0 : sqlite3_close(db_copy);
# 300 : 0 : return false;
# 301 : 0 : }
# 302 : : // Specifying -1 will copy all of the pages
# 303 : 14 : res = sqlite3_backup_step(backup, -1);
# 304 [ + + ]: 14 : if (res != SQLITE_DONE) {
# 305 : 2 : LogPrintf("%s: Unable to backup: %s\n", __func__, sqlite3_errstr(res));
# 306 : 2 : sqlite3_backup_finish(backup);
# 307 : 2 : sqlite3_close(db_copy);
# 308 : 2 : return false;
# 309 : 2 : }
# 310 : 12 : res = sqlite3_backup_finish(backup);
# 311 : 12 : sqlite3_close(db_copy);
# 312 : 12 : return res == SQLITE_OK;
# 313 : 12 : }
# 314 : :
# 315 : : void SQLiteDatabase::Close()
# 316 : 542 : {
# 317 : 542 : int res = sqlite3_close(m_db);
# 318 [ - + ]: 542 : if (res != SQLITE_OK) {
# 319 : 0 : throw std::runtime_error(strprintf("SQLiteDatabase: Failed to close database: %s\n", sqlite3_errstr(res)));
# 320 : 0 : }
# 321 : 542 : m_db = nullptr;
# 322 : 542 : }
# 323 : :
# 324 : : std::unique_ptr<DatabaseBatch> SQLiteDatabase::MakeBatch(bool flush_on_close)
# 325 : 61390 : {
# 326 : : // We ignore flush_on_close because we don't do manual flushing for SQLite
# 327 : 61390 : return std::make_unique<SQLiteBatch>(*this);
# 328 : 61390 : }
# 329 : :
# 330 : : SQLiteBatch::SQLiteBatch(SQLiteDatabase& database)
# 331 : : : m_database(database)
# 332 : 61390 : {
# 333 : : // Make sure we have a db handle
# 334 : 61390 : assert(m_database.m_db);
# 335 : :
# 336 : 61390 : SetupSQLStatements();
# 337 : 61390 : }
# 338 : :
# 339 : : void SQLiteBatch::Close()
# 340 : 61390 : {
# 341 : : // If m_db is in a transaction (i.e. not in autocommit mode), then abort the transaction in progress
# 342 [ + - ][ - + ]: 61390 : if (m_database.m_db && sqlite3_get_autocommit(m_database.m_db) == 0) {
# 343 [ # # ]: 0 : if (TxnAbort()) {
# 344 : 0 : LogPrintf("SQLiteBatch: Batch closed unexpectedly without the transaction being explicitly committed or aborted\n");
# 345 : 0 : } else {
# 346 : 0 : LogPrintf("SQLiteBatch: Batch closed and failed to abort transaction\n");
# 347 : 0 : }
# 348 : 0 : }
# 349 : :
# 350 : : // Free all of the prepared statements
# 351 : 61390 : const std::vector<std::pair<sqlite3_stmt**, const char*>> statements{
# 352 : 61390 : {&m_read_stmt, "read"},
# 353 : 61390 : {&m_insert_stmt, "insert"},
# 354 : 61390 : {&m_overwrite_stmt, "overwrite"},
# 355 : 61390 : {&m_delete_stmt, "delete"},
# 356 : 61390 : {&m_cursor_stmt, "cursor"},
# 357 : 61390 : };
# 358 : :
# 359 [ + + ]: 306950 : for (const auto& [stmt_prepared, stmt_description] : statements) {
# 360 : 306950 : int res = sqlite3_finalize(*stmt_prepared);
# 361 [ - + ]: 306950 : if (res != SQLITE_OK) {
# 362 : 0 : LogPrintf("SQLiteBatch: Batch closed but could not finalize %s statement: %s\n",
# 363 : 0 : stmt_description, sqlite3_errstr(res));
# 364 : 0 : }
# 365 : 306950 : *stmt_prepared = nullptr;
# 366 : 306950 : }
# 367 : 61390 : }
# 368 : :
# 369 : : bool SQLiteBatch::ReadKey(CDataStream&& key, CDataStream& value)
# 370 : 1320 : {
# 371 [ - + ]: 1320 : if (!m_database.m_db) return false;
# 372 : 1320 : assert(m_read_stmt);
# 373 : :
# 374 : : // Bind: leftmost parameter in statement is index 1
# 375 : 1320 : int res = sqlite3_bind_blob(m_read_stmt, 1, key.data(), key.size(), SQLITE_STATIC);
# 376 [ - + ]: 1320 : if (res != SQLITE_OK) {
# 377 : 0 : LogPrintf("%s: Unable to bind statement: %s\n", __func__, sqlite3_errstr(res));
# 378 : 0 : sqlite3_clear_bindings(m_read_stmt);
# 379 : 0 : sqlite3_reset(m_read_stmt);
# 380 : 0 : return false;
# 381 : 0 : }
# 382 : 1320 : res = sqlite3_step(m_read_stmt);
# 383 [ + + ]: 1320 : if (res != SQLITE_ROW) {
# 384 [ - + ]: 584 : if (res != SQLITE_DONE) {
# 385 : : // SQLITE_DONE means "not found", don't log an error in that case.
# 386 : 0 : LogPrintf("%s: Unable to execute statement: %s\n", __func__, sqlite3_errstr(res));
# 387 : 0 : }
# 388 : 584 : sqlite3_clear_bindings(m_read_stmt);
# 389 : 584 : sqlite3_reset(m_read_stmt);
# 390 : 584 : return false;
# 391 : 584 : }
# 392 : : // Leftmost column in result is index 0
# 393 : 736 : const char* data = reinterpret_cast<const char*>(sqlite3_column_blob(m_read_stmt, 0));
# 394 : 736 : int data_size = sqlite3_column_bytes(m_read_stmt, 0);
# 395 : 736 : value.write(data, data_size);
# 396 : :
# 397 : 736 : sqlite3_clear_bindings(m_read_stmt);
# 398 : 736 : sqlite3_reset(m_read_stmt);
# 399 : 736 : return true;
# 400 : 736 : }
# 401 : :
# 402 : : bool SQLiteBatch::WriteKey(CDataStream&& key, CDataStream&& value, bool overwrite)
# 403 : 74835 : {
# 404 [ - + ]: 74835 : if (!m_database.m_db) return false;
# 405 : 74835 : assert(m_insert_stmt && m_overwrite_stmt);
# 406 : :
# 407 : 74835 : sqlite3_stmt* stmt;
# 408 [ + + ]: 74835 : if (overwrite) {
# 409 : 73911 : stmt = m_overwrite_stmt;
# 410 : 73911 : } else {
# 411 : 924 : stmt = m_insert_stmt;
# 412 : 924 : }
# 413 : :
# 414 : : // Bind: leftmost parameter in statement is index 1
# 415 : : // Insert index 1 is key, 2 is value
# 416 : 74835 : int res = sqlite3_bind_blob(stmt, 1, key.data(), key.size(), SQLITE_STATIC);
# 417 [ - + ]: 74835 : if (res != SQLITE_OK) {
# 418 : 0 : LogPrintf("%s: Unable to bind key to statement: %s\n", __func__, sqlite3_errstr(res));
# 419 : 0 : sqlite3_clear_bindings(stmt);
# 420 : 0 : sqlite3_reset(stmt);
# 421 : 0 : return false;
# 422 : 0 : }
# 423 : 74835 : res = sqlite3_bind_blob(stmt, 2, value.data(), value.size(), SQLITE_STATIC);
# 424 [ - + ]: 74835 : if (res != SQLITE_OK) {
# 425 : 0 : LogPrintf("%s: Unable to bind value to statement: %s\n", __func__, sqlite3_errstr(res));
# 426 : 0 : sqlite3_clear_bindings(stmt);
# 427 : 0 : sqlite3_reset(stmt);
# 428 : 0 : return false;
# 429 : 0 : }
# 430 : :
# 431 : : // Execute
# 432 : 74835 : res = sqlite3_step(stmt);
# 433 : 74835 : sqlite3_clear_bindings(stmt);
# 434 : 74835 : sqlite3_reset(stmt);
# 435 [ - + ]: 74835 : if (res != SQLITE_DONE) {
# 436 : 0 : LogPrintf("%s: Unable to execute statement: %s\n", __func__, sqlite3_errstr(res));
# 437 : 0 : }
# 438 : 74835 : return res == SQLITE_DONE;
# 439 : 74835 : }
# 440 : :
# 441 : : bool SQLiteBatch::EraseKey(CDataStream&& key)
# 442 : 114 : {
# 443 [ - + ]: 114 : if (!m_database.m_db) return false;
# 444 : 114 : assert(m_delete_stmt);
# 445 : :
# 446 : : // Bind: leftmost parameter in statement is index 1
# 447 : 114 : int res = sqlite3_bind_blob(m_delete_stmt, 1, key.data(), key.size(), SQLITE_STATIC);
# 448 [ - + ]: 114 : if (res != SQLITE_OK) {
# 449 : 0 : LogPrintf("%s: Unable to bind statement: %s\n", __func__, sqlite3_errstr(res));
# 450 : 0 : sqlite3_clear_bindings(m_delete_stmt);
# 451 : 0 : sqlite3_reset(m_delete_stmt);
# 452 : 0 : return false;
# 453 : 0 : }
# 454 : :
# 455 : : // Execute
# 456 : 114 : res = sqlite3_step(m_delete_stmt);
# 457 : 114 : sqlite3_clear_bindings(m_delete_stmt);
# 458 : 114 : sqlite3_reset(m_delete_stmt);
# 459 [ - + ]: 114 : if (res != SQLITE_DONE) {
# 460 : 0 : LogPrintf("%s: Unable to execute statement: %s\n", __func__, sqlite3_errstr(res));
# 461 : 0 : }
# 462 : 114 : return res == SQLITE_DONE;
# 463 : 114 : }
# 464 : :
# 465 : : bool SQLiteBatch::HasKey(CDataStream&& key)
# 466 : 0 : {
# 467 [ # # ]: 0 : if (!m_database.m_db) return false;
# 468 : 0 : assert(m_read_stmt);
# 469 : :
# 470 : : // Bind: leftmost parameter in statement is index 1
# 471 : 0 : bool ret = false;
# 472 : 0 : int res = sqlite3_bind_blob(m_read_stmt, 1, key.data(), key.size(), SQLITE_STATIC);
# 473 [ # # ]: 0 : if (res == SQLITE_OK) {
# 474 : 0 : res = sqlite3_step(m_read_stmt);
# 475 [ # # ]: 0 : if (res == SQLITE_ROW) {
# 476 : 0 : ret = true;
# 477 : 0 : }
# 478 : 0 : }
# 479 : :
# 480 : 0 : sqlite3_clear_bindings(m_read_stmt);
# 481 : 0 : sqlite3_reset(m_read_stmt);
# 482 : 0 : return ret;
# 483 : 0 : }
# 484 : :
# 485 : : bool SQLiteBatch::StartCursor()
# 486 : 278 : {
# 487 : 278 : assert(!m_cursor_init);
# 488 [ - + ]: 278 : if (!m_database.m_db) return false;
# 489 : 278 : m_cursor_init = true;
# 490 : 278 : return true;
# 491 : 278 : }
# 492 : :
# 493 : : bool SQLiteBatch::ReadAtCursor(CDataStream& key, CDataStream& value, bool& complete)
# 494 : 11472 : {
# 495 : 11472 : complete = false;
# 496 : :
# 497 [ - + ]: 11472 : if (!m_cursor_init) return false;
# 498 : :
# 499 : 11472 : int res = sqlite3_step(m_cursor_stmt);
# 500 [ + + ]: 11472 : if (res == SQLITE_DONE) {
# 501 : 278 : complete = true;
# 502 : 278 : return true;
# 503 : 278 : }
# 504 [ - + ]: 11194 : if (res != SQLITE_ROW) {
# 505 : 0 : LogPrintf("SQLiteBatch::ReadAtCursor: Unable to execute cursor step: %s\n", sqlite3_errstr(res));
# 506 : 0 : return false;
# 507 : 0 : }
# 508 : :
# 509 : : // Leftmost column in result is index 0
# 510 : 11194 : const char* key_data = reinterpret_cast<const char*>(sqlite3_column_blob(m_cursor_stmt, 0));
# 511 : 11194 : int key_data_size = sqlite3_column_bytes(m_cursor_stmt, 0);
# 512 : 11194 : key.write(key_data, key_data_size);
# 513 : 11194 : const char* value_data = reinterpret_cast<const char*>(sqlite3_column_blob(m_cursor_stmt, 1));
# 514 : 11194 : int value_data_size = sqlite3_column_bytes(m_cursor_stmt, 1);
# 515 : 11194 : value.write(value_data, value_data_size);
# 516 : 11194 : return true;
# 517 : 11194 : }
# 518 : :
# 519 : : void SQLiteBatch::CloseCursor()
# 520 : 278 : {
# 521 : 278 : sqlite3_reset(m_cursor_stmt);
# 522 : 278 : m_cursor_init = false;
# 523 : 278 : }
# 524 : :
# 525 : : bool SQLiteBatch::TxnBegin()
# 526 : 17 : {
# 527 [ - + ][ - + ]: 17 : if (!m_database.m_db || sqlite3_get_autocommit(m_database.m_db) == 0) return false;
# 528 : 17 : int res = sqlite3_exec(m_database.m_db, "BEGIN TRANSACTION", nullptr, nullptr, nullptr);
# 529 [ - + ]: 17 : if (res != SQLITE_OK) {
# 530 : 0 : LogPrintf("SQLiteBatch: Failed to begin the transaction\n");
# 531 : 0 : }
# 532 : 17 : return res == SQLITE_OK;
# 533 : 17 : }
# 534 : :
# 535 : : bool SQLiteBatch::TxnCommit()
# 536 : 14 : {
# 537 [ - + ][ - + ]: 14 : if (!m_database.m_db || sqlite3_get_autocommit(m_database.m_db) != 0) return false;
# 538 : 14 : int res = sqlite3_exec(m_database.m_db, "COMMIT TRANSACTION", nullptr, nullptr, nullptr);
# 539 [ - + ]: 14 : if (res != SQLITE_OK) {
# 540 : 0 : LogPrintf("SQLiteBatch: Failed to commit the transaction\n");
# 541 : 0 : }
# 542 : 14 : return res == SQLITE_OK;
# 543 : 14 : }
# 544 : :
# 545 : : bool SQLiteBatch::TxnAbort()
# 546 : 3 : {
# 547 [ - + ][ - + ]: 3 : if (!m_database.m_db || sqlite3_get_autocommit(m_database.m_db) != 0) return false;
# 548 : 3 : int res = sqlite3_exec(m_database.m_db, "ROLLBACK TRANSACTION", nullptr, nullptr, nullptr);
# 549 [ - + ]: 3 : if (res != SQLITE_OK) {
# 550 : 0 : LogPrintf("SQLiteBatch: Failed to abort the transaction\n");
# 551 : 0 : }
# 552 : 3 : return res == SQLITE_OK;
# 553 : 3 : }
# 554 : :
# 555 : : std::unique_ptr<SQLiteDatabase> MakeSQLiteDatabase(const fs::path& path, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error)
# 556 : 322 : {
# 557 : 322 : try {
# 558 : 322 : fs::path data_file = SQLiteDataFile(path);
# 559 : 322 : auto db = std::make_unique<SQLiteDatabase>(data_file.parent_path(), data_file);
# 560 [ + + ][ - + ]: 322 : if (options.verify && !db->Verify(error)) {
# 561 : 0 : status = DatabaseStatus::FAILED_VERIFY;
# 562 : 0 : return nullptr;
# 563 : 0 : }
# 564 : 322 : status = DatabaseStatus::SUCCESS;
# 565 : 322 : return db;
# 566 : 322 : } catch (const std::runtime_error& e) {
# 567 : 6 : status = DatabaseStatus::FAILED_LOAD;
# 568 : 6 : error = Untranslated(e.what());
# 569 : 6 : return nullptr;
# 570 : 6 : }
# 571 : 322 : }
# 572 : :
# 573 : : std::string SQLiteDatabaseVersion()
# 574 : 322 : {
# 575 : 322 : return std::string(sqlite3_libversion());
# 576 : 322 : }
|