Coverage Report

Created: 2026-07-23 20:35

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/tmp/bitcoin/src/wallet/sqlite.cpp
Line
Count
Source
1
// Copyright (c) 2020-present 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 <bitcoin-build-config.h> // IWYU pragma: keep
6
7
#include <wallet/sqlite.h>
8
9
#include <chainparams.h>
10
#include <crypto/common.h>
11
#include <sync.h>
12
#include <util/check.h>
13
#include <util/fs_helpers.h>
14
#include <util/log.h>
15
#include <util/strencodings.h>
16
#include <util/translation.h>
17
#include <wallet/db.h>
18
19
#include <sqlite3.h>
20
21
#include <cstdint>
22
#include <optional>
23
#include <utility>
24
#include <vector>
25
26
namespace wallet {
27
static constexpr int32_t WALLET_SCHEMA_VERSION = 0;
28
29
static std::span<const std::byte> SpanFromBlob(sqlite3_stmt* stmt, int col)
30
56.1k
{
31
56.1k
    return {reinterpret_cast<const std::byte*>(sqlite3_column_blob(stmt, col)),
32
56.1k
            static_cast<size_t>(sqlite3_column_bytes(stmt, col))};
33
56.1k
}
34
35
static void ErrorLogCallback(void* arg, int code, const char* msg)
36
6
{
37
    // From sqlite3_config() documentation for the SQLITE_CONFIG_LOG option:
38
    // "The void pointer that is the second argument to SQLITE_CONFIG_LOG is passed through as
39
    // the first parameter to the application-defined logger function whenever that function is
40
    // invoked."
41
    // Assert that this is the case:
42
6
    assert(arg == nullptr);
43
6
    LogWarning("SQLite Error. Code: %d. Message: %s", code, msg);
44
6
}
45
46
static int TraceSqlCallback(unsigned code, void* context, void* param1, void* param2)
47
452k
{
48
452k
    auto* db = static_cast<SQLiteDatabase*>(context);
49
452k
    if (code == SQLITE_TRACE_STMT) {
50
452k
        auto* stmt = static_cast<sqlite3_stmt*>(param1);
51
        // To be conservative and avoid leaking potentially secret information
52
        // in the log file, only expand statements that query the database, not
53
        // statements that update the database.
54
452k
        char* expanded{sqlite3_stmt_readonly(stmt) ? sqlite3_expanded_sql(stmt) : nullptr};
55
452k
        LogTrace(BCLog::WALLETDB, "[%s] SQLite Statement: %s\n", db->Filename(), expanded ? expanded : sqlite3_sql(stmt));
56
452k
        if (expanded) sqlite3_free(expanded);
57
452k
    }
58
452k
    return SQLITE_OK;
59
452k
}
60
61
static bool BindBlobToStatement(sqlite3_stmt* stmt,
62
                                int index,
63
                                std::span<const std::byte> blob,
64
                                const std::string& description)
65
584k
{
66
    // Pass a pointer to the empty string "" below instead of passing the
67
    // blob.data() pointer if the blob.data() pointer is null. Passing a null
68
    // data pointer to bind_blob would cause sqlite to bind the SQL NULL value
69
    // instead of the empty blob value X'', which would mess up SQL comparisons.
70
584k
    int res = sqlite3_bind_blob(stmt, index, blob.data() ? static_cast<const void*>(blob.data()) : "", blob.size(), SQLITE_STATIC);
71
584k
    if (res != SQLITE_OK) {
72
0
        LogWarning("Unable to bind %s to statement: %s", description, sqlite3_errstr(res));
73
0
        sqlite3_clear_bindings(stmt);
74
0
        sqlite3_reset(stmt);
75
0
        return false;
76
0
    }
77
78
584k
    return true;
79
584k
}
80
81
static std::optional<int> ReadPragmaInteger(sqlite3* db, const std::string& key, const std::string& description, bilingual_str& error)
82
2.03k
{
83
2.03k
    std::string stmt_text = strprintf("PRAGMA %s", key);
84
2.03k
    sqlite3_stmt* pragma_read_stmt{nullptr};
85
2.03k
    int ret = sqlite3_prepare_v2(db, stmt_text.c_str(), -1, &pragma_read_stmt, nullptr);
86
2.03k
    if (ret != SQLITE_OK) {
87
0
        sqlite3_finalize(pragma_read_stmt);
88
0
        error = Untranslated(strprintf("SQLiteDatabase: Failed to prepare the statement to fetch %s: %s", description, sqlite3_errstr(ret)));
89
0
        return std::nullopt;
90
0
    }
91
2.03k
    ret = sqlite3_step(pragma_read_stmt);
92
2.03k
    if (ret != SQLITE_ROW) {
93
0
        sqlite3_finalize(pragma_read_stmt);
94
0
        error = Untranslated(strprintf("SQLiteDatabase: Failed to fetch %s: %s", description, sqlite3_errstr(ret)));
95
0
        return std::nullopt;
96
0
    }
97
2.03k
    int result = sqlite3_column_int(pragma_read_stmt, 0);
98
2.03k
    sqlite3_finalize(pragma_read_stmt);
99
2.03k
    return result;
100
2.03k
}
101
102
static void SetPragma(sqlite3* db, const std::string& key, const std::string& value, const std::string& err_msg)
103
4.90k
{
104
4.90k
    std::string stmt_text = strprintf("PRAGMA %s = %s", key, value);
105
4.90k
    int ret = sqlite3_exec(db, stmt_text.c_str(), nullptr, nullptr, nullptr);
106
4.90k
    if (ret != SQLITE_OK) {
107
0
        throw std::runtime_error(strprintf("SQLiteDatabase: %s: %s\n", err_msg, sqlite3_errstr(ret)));
108
0
    }
109
4.90k
}
110
111
Mutex SQLiteDatabase::g_sqlite_mutex;
112
int SQLiteDatabase::g_sqlite_count = 0;
113
114
SQLiteDatabase::SQLiteDatabase(const fs::path& dir_path, const fs::path& file_path, const DatabaseOptions& options)
115
1.11k
    : SQLiteDatabase(dir_path, file_path, options, /*additional_flags=*/0)
116
1.11k
{}
117
118
SQLiteDatabase::SQLiteDatabase(const fs::path& dir_path, const fs::path& file_path, const DatabaseOptions& options, int additional_flags)
119
1.20k
    : 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)
120
1.20k
{
121
1.20k
    {
122
1.20k
        LOCK(g_sqlite_mutex);
123
1.20k
        if (++g_sqlite_count == 1) {
124
            // Setup logging
125
485
            int ret = sqlite3_config(SQLITE_CONFIG_LOG, ErrorLogCallback, nullptr);
126
485
            if (ret != SQLITE_OK) {
127
0
                throw std::runtime_error(strprintf("SQLiteDatabase: Failed to setup error log: %s\n", sqlite3_errstr(ret)));
128
0
            }
129
            // Force serialized threading mode
130
485
            ret = sqlite3_config(SQLITE_CONFIG_SERIALIZED);
131
485
            if (ret != SQLITE_OK) {
132
0
                throw std::runtime_error(strprintf("SQLiteDatabase: Failed to configure serialized threading mode: %s\n", sqlite3_errstr(ret)));
133
0
            }
134
485
        }
135
1.20k
        int ret = sqlite3_initialize(); // This is a no-op if sqlite3 is already initialized
136
1.20k
        if (ret != SQLITE_OK) {
137
0
            throw std::runtime_error(strprintf("SQLiteDatabase: Failed to initialize SQLite: %s\n", sqlite3_errstr(ret)));
138
0
        }
139
1.20k
    }
140
141
1.20k
    try {
142
1.20k
        Open(m_additional_flags);
143
1.20k
    } catch (const std::runtime_error&) {
144
        // If open fails, cleanup this object and rethrow the exception
145
8
        Cleanup();
146
8
        throw;
147
8
    }
148
1.20k
}
149
150
void SQLiteBatch::SetupSQLStatements()
151
166k
{
152
166k
    const std::vector<std::pair<sqlite3_stmt**, const char*>> statements{
153
166k
        {&m_read_stmt, "SELECT value FROM main WHERE key = ?"},
154
166k
        {&m_insert_stmt, "INSERT INTO main VALUES(?, ?)"},
155
166k
        {&m_overwrite_stmt, "INSERT or REPLACE into main values(?, ?)"},
156
166k
        {&m_delete_stmt, "DELETE FROM main WHERE key = ?"},
157
166k
        {&m_delete_prefix_stmt, "DELETE FROM main WHERE instr(key, ?) = 1"},
158
166k
    };
159
160
832k
    for (const auto& [stmt_prepared, stmt_text] : statements) {
161
832k
        if (*stmt_prepared == nullptr) {
162
832k
            int res = sqlite3_prepare_v2(m_database.m_db, stmt_text, -1, stmt_prepared, nullptr);
163
832k
            if (res != SQLITE_OK) {
164
0
                throw std::runtime_error(strprintf(
165
0
                    "SQLiteDatabase: Failed to setup SQL statements: %s\n", sqlite3_errstr(res)));
166
0
            }
167
832k
        }
168
832k
    }
169
166k
}
170
171
SQLiteDatabase::~SQLiteDatabase()
172
1.19k
{
173
1.19k
    Cleanup();
174
1.19k
}
175
176
void SQLiteDatabase::Cleanup() noexcept
177
1.20k
{
178
1.20k
    AssertLockNotHeld(g_sqlite_mutex);
179
180
1.20k
    Close();
181
182
1.20k
    LOCK(g_sqlite_mutex);
183
1.20k
    if (--g_sqlite_count == 0) {
184
485
        int ret = sqlite3_shutdown();
185
485
        if (ret != SQLITE_OK) {
186
0
            LogWarning("SQLiteDatabase: Failed to shutdown SQLite: %s", sqlite3_errstr(ret));
187
0
        }
188
485
    }
189
1.20k
}
190
191
bool SQLiteDatabase::Verify(bilingual_str& error)
192
1.01k
{
193
1.01k
    assert(m_db);
194
195
    // Check the application ID matches our network magic
196
1.01k
    auto read_result = ReadPragmaInteger(m_db, "application_id", "the application id", error);
197
1.01k
    if (!read_result.has_value()) return false;
198
1.01k
    uint32_t app_id = static_cast<uint32_t>(read_result.value());
199
1.01k
    uint32_t net_magic = ReadBE32(Params().MessageStart().data());
200
1.01k
    if (app_id != net_magic) {
201
0
        error = strprintf(_("SQLiteDatabase: Unexpected application id. Expected %u, got %u"), net_magic, app_id);
202
0
        return false;
203
0
    }
204
205
    // Check our schema version
206
1.01k
    read_result = ReadPragmaInteger(m_db, "user_version", "sqlite wallet schema version", error);
207
1.01k
    if (!read_result.has_value()) return false;
208
1.01k
    int32_t user_ver = read_result.value();
209
1.01k
    if (user_ver != WALLET_SCHEMA_VERSION) {
210
0
        error = strprintf(_("SQLiteDatabase: Unknown sqlite wallet schema version %d. Only version %d is supported"), user_ver, WALLET_SCHEMA_VERSION);
211
0
        return false;
212
0
    }
213
214
1.01k
    sqlite3_stmt* stmt{nullptr};
215
1.01k
    int ret = sqlite3_prepare_v2(m_db, "PRAGMA integrity_check", -1, &stmt, nullptr);
216
1.01k
    if (ret != SQLITE_OK) {
217
0
        sqlite3_finalize(stmt);
218
0
        error = strprintf(_("SQLiteDatabase: Failed to prepare statement to verify database: %s"), sqlite3_errstr(ret));
219
0
        return false;
220
0
    }
221
2.03k
    while (true) {
222
2.03k
        ret = sqlite3_step(stmt);
223
2.03k
        if (ret == SQLITE_DONE) {
224
1.01k
            break;
225
1.01k
        }
226
1.01k
        if (ret != SQLITE_ROW) {
227
0
            error = strprintf(_("SQLiteDatabase: Failed to execute statement to verify database: %s"), sqlite3_errstr(ret));
228
0
            break;
229
0
        }
230
1.01k
        const char* msg = (const char*)sqlite3_column_text(stmt, 0);
231
1.01k
        if (!msg) {
232
0
            error = strprintf(_("SQLiteDatabase: Failed to read database verification error: %s"), sqlite3_errstr(ret));
233
0
            break;
234
0
        }
235
1.01k
        std::string str_msg(msg);
236
1.01k
        if (str_msg == "ok") {
237
1.01k
            continue;
238
1.01k
        }
239
0
        if (error.empty()) {
240
0
            error = _("Failed to verify database") + Untranslated("\n");
241
0
        }
242
0
        error += Untranslated(strprintf("%s\n", str_msg));
243
0
    }
244
1.01k
    sqlite3_finalize(stmt);
245
1.01k
    return error.empty();
246
1.01k
}
247
248
void SQLiteDatabase::Open()
249
2
{
250
2
    if (m_additional_flags & SQLITE_OPEN_MEMORY) {
251
1
        throw std::runtime_error("SQLiteDatabase: Cannot reopen an in-memory database");
252
1
    }
253
1
    Open(m_additional_flags);
254
1
}
255
256
void SQLiteDatabase::Open(int additional_flags)
257
1.20k
{
258
1.20k
    int flags = SQLITE_OPEN_FULLMUTEX | SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | additional_flags;
259
260
1.20k
    if (m_db == nullptr) {
261
1.20k
        if (!(flags & SQLITE_OPEN_MEMORY)) {
262
1.11k
            TryCreateDirectories(m_dir_path);
263
1.11k
            if (!IsDirWritable(m_dir_path)) {
264
0
                throw std::runtime_error(strprintf("SQLiteDatabase: Failed to open database in directory '%s': directory is not writable", fs::PathToString(m_dir_path)));
265
0
            }
266
1.11k
        }
267
268
1.20k
        int ret = sqlite3_open_v2(m_file_path.c_str(), &m_db, flags, nullptr);
269
1.20k
        if (ret != SQLITE_OK) {
270
0
            throw std::runtime_error(strprintf("SQLiteDatabase: Failed to open database: %s\n", sqlite3_errstr(ret)));
271
0
        }
272
1.20k
        ret = sqlite3_extended_result_codes(m_db, 1);
273
1.20k
        if (ret != SQLITE_OK) {
274
0
            throw std::runtime_error(strprintf("SQLiteDatabase: Failed to enable extended result codes: %s\n", sqlite3_errstr(ret)));
275
0
        }
276
        // Trace SQL statements if tracing is enabled with -debug=walletdb -loglevel=walletdb:trace
277
1.20k
        if (util::log::ShouldTraceLog(BCLog::WALLETDB)) {
278
1.18k
           ret = sqlite3_trace_v2(m_db, SQLITE_TRACE_STMT, TraceSqlCallback, this);
279
1.18k
           if (ret != SQLITE_OK) {
280
0
               LogWarning("Failed to enable SQL tracing for %s", Filename());
281
0
           }
282
1.18k
        }
283
1.20k
    }
284
285
1.20k
    if (sqlite3_db_readonly(m_db, "main") != 0) {
286
0
        throw std::runtime_error("SQLiteDatabase: Database opened in readonly mode but read-write permissions are needed");
287
0
    }
288
289
    // Acquire an exclusive lock on the database
290
    // First change the locking mode to exclusive
291
1.20k
    SetPragma(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.
293
1.20k
    int ret = sqlite3_exec(m_db, "BEGIN EXCLUSIVE TRANSACTION", nullptr, nullptr, nullptr);
294
1.20k
    if (ret != SQLITE_OK) {
295
6
        throw 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
6
    }
297
1.19k
    ret = sqlite3_exec(m_db, "COMMIT", nullptr, nullptr, nullptr);
298
1.19k
    if (ret != SQLITE_OK) {
299
0
        throw std::runtime_error(strprintf("SQLiteDatabase: Unable to end exclusive lock transaction: %s\n", sqlite3_errstr(ret)));
300
0
    }
301
302
    // Enable fullfsync for the platforms that use it
303
1.19k
    SetPragma(m_db, "fullfsync", "true", "Failed to enable fullfsync");
304
305
1.19k
    if (m_use_unsafe_sync) {
306
        // Use normal synchronous mode for the journal
307
963
        LogWarning("SQLite is configured to not wait for data to be flushed to disk. Data loss and corruption may occur.");
308
963
        SetPragma(m_db, "synchronous", "OFF", "Failed to set synchronous mode to OFF");
309
963
    }
310
311
    // Make the table for our key-value pairs
312
    // First check that the main table exists
313
1.19k
    sqlite3_stmt* check_main_stmt{nullptr};
314
1.19k
    ret = sqlite3_prepare_v2(m_db, "SELECT name FROM sqlite_master WHERE type='table' AND name='main'", -1, &check_main_stmt, nullptr);
315
1.19k
    if (ret != SQLITE_OK) {
316
0
        throw std::runtime_error(strprintf("SQLiteDatabase: Failed to prepare statement to check table existence: %s\n", sqlite3_errstr(ret)));
317
0
    }
318
1.19k
    ret = sqlite3_step(check_main_stmt);
319
1.19k
    if (sqlite3_finalize(check_main_stmt) != SQLITE_OK) {
320
0
        throw std::runtime_error(strprintf("SQLiteDatabase: Failed to finalize statement checking table existence: %s\n", sqlite3_errstr(ret)));
321
0
    }
322
1.19k
    bool table_exists;
323
1.19k
    if (ret == SQLITE_DONE) {
324
771
        table_exists = false;
325
771
    } else if (ret == SQLITE_ROW) {
326
424
        table_exists = true;
327
424
    } else {
328
2
        throw std::runtime_error(strprintf("SQLiteDatabase: Failed to execute statement to check table existence: %s\n", sqlite3_errstr(ret)));
329
2
    }
330
331
    // Do the db setup things because the table doesn't exist only when we are creating a new wallet
332
1.19k
    if (!table_exists) {
333
771
        ret = sqlite3_exec(m_db, "CREATE TABLE main(key BLOB PRIMARY KEY NOT NULL, value BLOB NOT NULL)", nullptr, nullptr, nullptr);
334
771
        if (ret != SQLITE_OK) {
335
0
            throw std::runtime_error(strprintf("SQLiteDatabase: Failed to create new database: %s\n", sqlite3_errstr(ret)));
336
0
        }
337
338
        // Set the application id
339
771
        uint32_t app_id = ReadBE32(Params().MessageStart().data());
340
771
        SetPragma(m_db, "application_id", strprintf("%d", static_cast<int32_t>(app_id)),
341
771
                  "Failed to set the application id");
342
343
        // Set the user version
344
771
        SetPragma(m_db, "user_version", strprintf("%d", WALLET_SCHEMA_VERSION),
345
771
                  "Failed to set the wallet schema version");
346
771
    }
347
1.19k
}
348
349
bool SQLiteDatabase::Rewrite()
350
25
{
351
    // Rewrite the database using the VACUUM command: https://sqlite.org/lang_vacuum.html
352
25
    int ret = sqlite3_exec(m_db, "VACUUM", nullptr, nullptr, nullptr);
353
25
    return ret == SQLITE_OK;
354
25
}
355
356
bool SQLiteDatabase::Backup(const std::string& dest) const
357
73
{
358
73
    sqlite3* db_copy;
359
73
    int res = sqlite3_open(dest.c_str(), &db_copy);
360
73
    if (res != SQLITE_OK) {
361
2
        sqlite3_close(db_copy);
362
2
        return false;
363
2
    }
364
71
    sqlite3_backup* backup = sqlite3_backup_init(db_copy, "main", m_db, "main");
365
71
    if (!backup) {
366
0
        LogWarning("Unable to begin sqlite backup: %s", sqlite3_errmsg(m_db));
367
0
        sqlite3_close(db_copy);
368
0
        return false;
369
0
    }
370
    // Specifying -1 will copy all of the pages
371
71
    res = sqlite3_backup_step(backup, -1);
372
71
    if (res != SQLITE_DONE) {
373
2
        LogWarning("Unable to continue sqlite backup: %s", sqlite3_errstr(res));
374
2
        sqlite3_backup_finish(backup);
375
2
        sqlite3_close(db_copy);
376
2
        return false;
377
2
    }
378
69
    res = sqlite3_backup_finish(backup);
379
69
    sqlite3_close(db_copy);
380
69
    return res == SQLITE_OK;
381
71
}
382
383
void SQLiteDatabase::Close()
384
1.22k
{
385
1.22k
    int res = sqlite3_close(m_db);
386
1.22k
    if (res != SQLITE_OK) {
387
0
        throw std::runtime_error(strprintf("SQLiteDatabase: Failed to close database: %s\n", sqlite3_errstr(res)));
388
0
    }
389
1.22k
    m_db = nullptr;
390
1.22k
}
391
392
bool SQLiteDatabase::HasActiveTxn()
393
145k
{
394
    // 'sqlite3_get_autocommit' returns true by default, and false if a transaction has begun and not been committed or rolled back.
395
145k
    return m_db && sqlite3_get_autocommit(m_db) == 0;
396
145k
}
397
398
int SQliteExecHandler::Exec(SQLiteDatabase& database, const std::string& statement)
399
145k
{
400
145k
    return sqlite3_exec(database.m_db, statement.data(), nullptr, nullptr, nullptr);
401
145k
}
402
403
std::unique_ptr<DatabaseBatch> SQLiteDatabase::MakeBatch()
404
147k
{
405
    // We ignore flush_on_close because we don't do manual flushing for SQLite
406
147k
    return std::make_unique<SQLiteBatch>(*this);
407
147k
}
408
409
SQLiteBatch::SQLiteBatch(SQLiteDatabase& database)
410
166k
    : m_database(database)
411
166k
{
412
    // Make sure we have a db handle
413
166k
    assert(m_database.m_db);
414
415
166k
    SetupSQLStatements();
416
166k
}
417
418
void SQLiteBatch::Close()
419
166k
{
420
166k
    bool force_conn_refresh = false;
421
422
    // If we began a transaction, and it wasn't committed, abort the transaction in progress
423
166k
    if (m_txn) {
424
2
        if (TxnAbort()) {
425
1
            LogWarning("SQLiteBatch: Batch closed unexpectedly without the transaction being explicitly committed or aborted");
426
1
        } else {
427
            // If transaction cannot be aborted, it means there is a bug or there has been data corruption. Try to recover in this case
428
            // by closing and reopening the database. Closing the database should also ensure that any changes made since the transaction
429
            // was opened will be rolled back and future transactions can succeed without committing old data.
430
1
            force_conn_refresh = true;
431
1
            LogWarning("SQLiteBatch: Batch closed and failed to abort transaction, resetting db connection..");
432
1
        }
433
2
    }
434
435
    // Free all of the prepared statements
436
166k
    const std::vector<std::pair<sqlite3_stmt**, const char*>> statements{
437
166k
        {&m_read_stmt, "read"},
438
166k
        {&m_insert_stmt, "insert"},
439
166k
        {&m_overwrite_stmt, "overwrite"},
440
166k
        {&m_delete_stmt, "delete"},
441
166k
        {&m_delete_prefix_stmt, "delete prefix"},
442
166k
    };
443
444
832k
    for (const auto& [stmt_prepared, stmt_description] : statements) {
445
832k
        int res = sqlite3_finalize(*stmt_prepared);
446
832k
        if (res != SQLITE_OK) {
447
0
            LogWarning("SQLiteBatch: Batch closed but could not finalize %s statement: %s",
448
0
                      stmt_description, sqlite3_errstr(res));
449
0
        }
450
832k
        *stmt_prepared = nullptr;
451
832k
    }
452
453
166k
    if (force_conn_refresh) {
454
1
        if (m_database.m_additional_flags & SQLITE_OPEN_MEMORY) {
455
0
            throw std::runtime_error("SQLiteDatabase: Cannot recover in-memory database connection");
456
0
        }
457
1
        m_database.Close();
458
1
        try {
459
1
            m_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.
461
1
            m_database.m_write_semaphore.release();
462
1
        } catch (const std::runtime_error&) {
463
            // If open fails, cleanup this object and rethrow the exception
464
0
            m_database.Close();
465
0
            throw;
466
0
        }
467
1
    }
468
166k
}
469
470
bool SQLiteBatch::ReadKey(DataStream&& key, DataStream& value)
471
4.42k
{
472
4.42k
    if (!m_database.m_db) return false;
473
4.42k
    assert(m_read_stmt);
474
475
    // Bind: leftmost parameter in statement is index 1
476
4.42k
    if (!BindBlobToStatement(m_read_stmt, 1, key, "key")) return false;
477
4.42k
    int res = sqlite3_step(m_read_stmt);
478
4.42k
    if (res != SQLITE_ROW) {
479
23
        if (res != SQLITE_DONE) {
480
            // SQLITE_DONE means "not found", don't log an error in that case.
481
0
            LogWarning("Unable to execute read statement: %s", sqlite3_errstr(res));
482
0
        }
483
23
        sqlite3_clear_bindings(m_read_stmt);
484
23
        sqlite3_reset(m_read_stmt);
485
23
        return false;
486
23
    }
487
    // Leftmost column in result is index 0
488
4.40k
    value.clear();
489
4.40k
    value.write(SpanFromBlob(m_read_stmt, 0));
490
491
4.40k
    sqlite3_clear_bindings(m_read_stmt);
492
4.40k
    sqlite3_reset(m_read_stmt);
493
4.40k
    return true;
494
4.42k
}
495
496
bool SQLiteBatch::WriteKey(DataStream&& key, DataStream&& value, bool overwrite)
497
272k
{
498
272k
    if (!m_database.m_db) return false;
499
272k
    assert(m_insert_stmt && m_overwrite_stmt);
500
501
272k
    sqlite3_stmt* stmt;
502
272k
    if (overwrite) {
503
267k
        stmt = m_overwrite_stmt;
504
267k
    } else {
505
4.68k
        stmt = m_insert_stmt;
506
4.68k
    }
507
508
    // Bind: leftmost parameter in statement is index 1
509
    // Insert index 1 is key, 2 is value
510
272k
    if (!BindBlobToStatement(stmt, 1, key, "key")) return false;
511
272k
    if (!BindBlobToStatement(stmt, 2, value, "value")) return false;
512
513
    // Acquire semaphore if not previously acquired when creating a transaction.
514
272k
    if (!m_txn) m_database.m_write_semaphore.acquire();
515
516
    // Execute
517
272k
    int res = sqlite3_step(stmt);
518
272k
    sqlite3_clear_bindings(stmt);
519
272k
    sqlite3_reset(stmt);
520
272k
    if (res != SQLITE_DONE) {
521
2
        LogWarning("Unable to execute write statement: %s", sqlite3_errstr(res));
522
2
    }
523
524
272k
    if (!m_txn) m_database.m_write_semaphore.release();
525
526
272k
    return res == SQLITE_DONE;
527
272k
}
528
529
bool SQLiteBatch::ExecStatement(sqlite3_stmt* stmt, std::span<const std::byte> blob)
530
760
{
531
760
    if (!m_database.m_db) return false;
532
760
    assert(stmt);
533
534
    // Bind: leftmost parameter in statement is index 1
535
760
    if (!BindBlobToStatement(stmt, 1, blob, "key")) return false;
536
537
    // Acquire semaphore if not previously acquired when creating a transaction.
538
760
    if (!m_txn) m_database.m_write_semaphore.acquire();
539
540
    // Execute
541
760
    int res = sqlite3_step(stmt);
542
760
    sqlite3_clear_bindings(stmt);
543
760
    sqlite3_reset(stmt);
544
760
    if (res != SQLITE_DONE) {
545
0
        LogWarning("Unable to execute exec statement: %s", sqlite3_errstr(res));
546
0
    }
547
548
760
    if (!m_txn) m_database.m_write_semaphore.release();
549
550
760
    return res == SQLITE_DONE;
551
760
}
552
553
bool SQLiteBatch::EraseKey(DataStream&& key)
554
349
{
555
349
    return ExecStatement(m_delete_stmt, key);
556
349
}
557
558
bool SQLiteBatch::ErasePrefix(std::span<const std::byte> prefix)
559
411
{
560
411
    return ExecStatement(m_delete_prefix_stmt, prefix);
561
411
}
562
563
bool SQLiteBatch::HasKey(DataStream&& key)
564
11
{
565
11
    if (!m_database.m_db) return false;
566
11
    assert(m_read_stmt);
567
568
    // Bind: leftmost parameter in statement is index 1
569
11
    if (!BindBlobToStatement(m_read_stmt, 1, key, "key")) return false;
570
11
    int res = sqlite3_step(m_read_stmt);
571
11
    sqlite3_clear_bindings(m_read_stmt);
572
11
    sqlite3_reset(m_read_stmt);
573
11
    return res == SQLITE_ROW;
574
11
}
575
576
DatabaseCursor::Status SQLiteCursor::Next(DataStream& key, DataStream& value)
577
43.0k
{
578
43.0k
    int res = sqlite3_step(m_cursor_stmt);
579
43.0k
    if (res == SQLITE_DONE) {
580
17.1k
        return Status::DONE;
581
17.1k
    }
582
25.8k
    if (res != SQLITE_ROW) {
583
0
        LogWarning("Unable to execute cursor step: %s", sqlite3_errstr(res));
584
0
        return Status::FAIL;
585
0
    }
586
587
25.8k
    key.clear();
588
25.8k
    value.clear();
589
590
    // Leftmost column in result is index 0
591
25.8k
    key.write(SpanFromBlob(m_cursor_stmt, 0));
592
25.8k
    value.write(SpanFromBlob(m_cursor_stmt, 1));
593
25.8k
    return Status::MORE;
594
25.8k
}
595
596
SQLiteCursor::~SQLiteCursor()
597
17.2k
{
598
17.2k
    sqlite3_clear_bindings(m_cursor_stmt);
599
17.2k
    sqlite3_reset(m_cursor_stmt);
600
17.2k
    int res = sqlite3_finalize(m_cursor_stmt);
601
17.2k
    if (res != SQLITE_OK) {
602
0
        LogWarning("Cursor closed but could not finalize cursor statement: %s",
603
0
                   sqlite3_errstr(res));
604
0
    }
605
17.2k
}
606
607
std::unique_ptr<DatabaseCursor> SQLiteBatch::GetNewCursor()
608
5
{
609
5
    if (!m_database.m_db) return nullptr;
610
5
    auto cursor = std::make_unique<SQLiteCursor>();
611
612
5
    const char* stmt_text = "SELECT key, value FROM main";
613
5
    int res = sqlite3_prepare_v2(m_database.m_db, stmt_text, -1, &cursor->m_cursor_stmt, nullptr);
614
5
    if (res != SQLITE_OK) {
615
0
        throw std::runtime_error(strprintf(
616
0
            "%s: Failed to setup cursor SQL statement: %s\n", __func__, sqlite3_errstr(res)));
617
0
    }
618
619
5
    return cursor;
620
5
}
621
622
std::unique_ptr<DatabaseCursor> SQLiteBatch::GetNewPrefixCursor(std::span<const std::byte> prefix)
623
17.1k
{
624
17.1k
    if (!m_database.m_db) return nullptr;
625
626
    // To get just the records we want, the SQL statement does a comparison of the binary data
627
    // where the data must be greater than or equal to the prefix, and less than
628
    // the prefix incremented by one (when interpreted as an integer)
629
17.1k
    std::vector<std::byte> start_range(prefix.begin(), prefix.end());
630
17.1k
    std::vector<std::byte> end_range(prefix.begin(), prefix.end());
631
17.1k
    auto it = end_range.rbegin();
632
17.2k
    for (; it != end_range.rend(); ++it) {
633
17.2k
        if (*it == std::byte(std::numeric_limits<unsigned char>::max())) {
634
96
            *it = std::byte(0);
635
96
            continue;
636
96
        }
637
17.1k
        *it = std::byte(std::to_integer<unsigned char>(*it) + 1);
638
17.1k
        break;
639
17.2k
    }
640
17.1k
    if (it == end_range.rend()) {
641
        // If the prefix is all 0xff bytes, clear end_range as we won't need it
642
6
        end_range.clear();
643
6
    }
644
645
17.1k
    auto cursor = std::make_unique<SQLiteCursor>(start_range, end_range);
646
17.1k
    if (!cursor) return nullptr;
647
648
17.1k
    const char* stmt_text = end_range.empty() ? "SELECT key, value FROM main WHERE key >= ?" :
649
17.1k
                            "SELECT key, value FROM main WHERE key >= ? AND key < ?";
650
17.1k
    int res = sqlite3_prepare_v2(m_database.m_db, stmt_text, -1, &cursor->m_cursor_stmt, nullptr);
651
17.1k
    if (res != SQLITE_OK) {
652
0
        throw std::runtime_error(strprintf(
653
0
            "SQLiteDatabase: Failed to setup cursor SQL statement: %s\n", sqlite3_errstr(res)));
654
0
    }
655
656
17.1k
    if (!BindBlobToStatement(cursor->m_cursor_stmt, 1, cursor->m_prefix_range_start, "prefix_start")) return nullptr;
657
17.1k
    if (!end_range.empty()) {
658
17.1k
        if (!BindBlobToStatement(cursor->m_cursor_stmt, 2, cursor->m_prefix_range_end, "prefix_end")) return nullptr;
659
17.1k
    }
660
661
17.1k
    return cursor;
662
17.1k
}
663
664
bool SQLiteBatch::TxnBegin()
665
72.9k
{
666
72.9k
    if (!m_database.m_db || m_txn) return false;
667
72.9k
    m_database.m_write_semaphore.acquire();
668
72.9k
    Assert(!m_database.HasActiveTxn());
669
72.9k
    int res = Assert(m_exec_handler)->Exec(m_database, "BEGIN TRANSACTION");
670
72.9k
    if (res != SQLITE_OK) {
671
0
        LogWarning("SQLiteBatch: Failed to begin the transaction");
672
0
        m_database.m_write_semaphore.release();
673
72.9k
    } else {
674
72.9k
        m_txn = true;
675
72.9k
    }
676
72.9k
    return res == SQLITE_OK;
677
72.9k
}
678
679
bool SQLiteBatch::TxnCommit()
680
72.9k
{
681
72.9k
    if (!m_database.m_db || !m_txn) return false;
682
72.9k
    Assert(m_database.HasActiveTxn());
683
72.9k
    int res = Assert(m_exec_handler)->Exec(m_database, "COMMIT TRANSACTION");
684
72.9k
    if (res != SQLITE_OK) {
685
0
        LogWarning("SQLiteBatch: Failed to commit the transaction");
686
72.9k
    } else {
687
72.9k
        m_txn = false;
688
72.9k
        m_database.m_write_semaphore.release();
689
72.9k
    }
690
72.9k
    return res == SQLITE_OK;
691
72.9k
}
692
693
bool SQLiteBatch::TxnAbort()
694
8
{
695
8
    if (!m_database.m_db || !m_txn) return false;
696
7
    Assert(m_database.HasActiveTxn());
697
7
    int res = Assert(m_exec_handler)->Exec(m_database, "ROLLBACK TRANSACTION");
698
7
    if (res != SQLITE_OK) {
699
1
        LogWarning("SQLiteBatch: Failed to abort the transaction");
700
6
    } else {
701
6
        m_txn = false;
702
6
        m_database.m_write_semaphore.release();
703
6
    }
704
7
    return res == SQLITE_OK;
705
8
}
706
707
std::unique_ptr<SQLiteDatabase> MakeSQLiteDatabase(const fs::path& path, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error)
708
1.11k
{
709
1.11k
    try {
710
1.11k
        fs::path data_file = SQLiteDataFile(path);
711
1.11k
        auto db = std::make_unique<SQLiteDatabase>(data_file.parent_path(), data_file, options);
712
1.11k
        if (options.verify && !db->Verify(error)) {
713
0
            status = DatabaseStatus::FAILED_VERIFY;
714
0
            return nullptr;
715
0
        }
716
1.11k
        status = DatabaseStatus::SUCCESS;
717
1.11k
        return db;
718
1.11k
    } catch (const std::runtime_error& e) {
719
8
        status = DatabaseStatus::FAILED_LOAD;
720
8
        error = Untranslated(e.what());
721
8
        return nullptr;
722
8
    }
723
1.11k
}
724
725
InMemoryWalletDatabase::InMemoryWalletDatabase()
726
86
    : SQLiteDatabase(fs::path{}, fs::path{":memory:"}, DatabaseOptions(), SQLITE_OPEN_MEMORY)
727
86
{}
728
729
std::unique_ptr<WalletDatabase> MakeInMemoryWalletDatabase()
730
7
{
731
7
    return std::make_unique<InMemoryWalletDatabase>();
732
7
}
733
734
std::string SQLiteDatabaseVersion()
735
396
{
736
396
    return std::string(sqlite3_libversion());
737
396
}
738
} // namespace wallet