aboutsummaryrefslogtreecommitdiff
path: root/src/wallet/sqlite.h
blob: 0a3243fe19c5fd9518765b1346d7d70bbb092c15 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
// Copyright (c) 2020-2021 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#ifndef BITCOIN_WALLET_SQLITE_H
#define BITCOIN_WALLET_SQLITE_H

#include <sync.h>
#include <wallet/db.h>

struct bilingual_str;

struct sqlite3_stmt;
struct sqlite3;

namespace wallet {
class SQLiteDatabase;

/** RAII class that provides a database cursor */
class SQLiteCursor : public DatabaseCursor
{
public:
    sqlite3_stmt* m_cursor_stmt{nullptr};
    // Copies of the prefix things for the prefix cursor.
    // Prevents SQLite from accessing temp variables for the prefix things.
    std::vector<std::byte> m_prefix_range_start;
    std::vector<std::byte> m_prefix_range_end;

    explicit SQLiteCursor() {}
    explicit SQLiteCursor(std::vector<std::byte> start_range, std::vector<std::byte> end_range)
        : m_prefix_range_start(std::move(start_range)),
        m_prefix_range_end(std::move(end_range))
    {}
    ~SQLiteCursor() override;

    Status Next(DataStream& key, DataStream& value) override;
};

/** Class responsible for executing SQL statements in SQLite databases.
 *  Methods are virtual so they can be overridden by unit tests testing unusual database conditions. */
class SQliteExecHandler
{
public:
    virtual ~SQliteExecHandler() {}
    virtual int Exec(SQLiteDatabase& database, const std::string& statement);
};

/** RAII class that provides access to a WalletDatabase */
class SQLiteBatch : public DatabaseBatch
{
private:
    SQLiteDatabase& m_database;
    std::unique_ptr<SQliteExecHandler> m_exec_handler{std::make_unique<SQliteExecHandler>()};

    sqlite3_stmt* m_read_stmt{nullptr};
    sqlite3_stmt* m_insert_stmt{nullptr};
    sqlite3_stmt* m_overwrite_stmt{nullptr};
    sqlite3_stmt* m_delete_stmt{nullptr};
    sqlite3_stmt* m_delete_prefix_stmt{nullptr};

    /** Whether this batch has started a database transaction and whether it owns SQLiteDatabase::m_write_semaphore.
     * If the batch starts a db tx, it acquires the semaphore and sets this to true, keeping the semaphore
     * until the transaction ends to prevent other batch objects from writing to the database.
     *
     * If this batch did not start a transaction, the semaphore is acquired transiently when writing and m_txn
     * is not set.
     *
     * m_txn is different from HasActiveTxn() as it is only true when this batch has started the transaction,
     * not just when any batch has started a transaction.
     */
    bool m_txn{false};

    void SetupSQLStatements();
    bool ExecStatement(sqlite3_stmt* stmt, Span<const std::byte> blob);

    bool ReadKey(DataStream&& key, DataStream& value) override;
    bool WriteKey(DataStream&& key, DataStream&& value, bool overwrite = true) override;
    bool EraseKey(DataStream&& key) override;
    bool HasKey(DataStream&& key) override;
    bool ErasePrefix(Span<const std::byte> prefix) override;

public:
    explicit SQLiteBatch(SQLiteDatabase& database);
    ~SQLiteBatch() override { Close(); }

    void SetExecHandler(std::unique_ptr<SQliteExecHandler>&& handler) { m_exec_handler = std::move(handler); }

    /* No-op. See comment on SQLiteDatabase::Flush */
    void Flush() override {}

    void Close() override;

    std::unique_ptr<DatabaseCursor> GetNewCursor() override;
    std::unique_ptr<DatabaseCursor> GetNewPrefixCursor(Span<const std::byte> prefix) override;
    bool TxnBegin() override;
    bool TxnCommit() override;
    bool TxnAbort() override;
};

/** An instance of this class represents one SQLite3 database.
 **/
class SQLiteDatabase : public WalletDatabase
{
private:
    const bool m_mock{false};

    const std::string m_dir_path;

    const std::string m_file_path;

    /**
     * This mutex protects SQLite initialization and shutdown.
     * sqlite3_config() and sqlite3_shutdown() are not thread-safe (sqlite3_initialize() is).
     * Concurrent threads that execute SQLiteDatabase::SQLiteDatabase() should have just one
     * of them do the init and the rest wait for it to complete before all can proceed.
     */
    static Mutex g_sqlite_mutex;
    static int g_sqlite_count GUARDED_BY(g_sqlite_mutex);

    void Cleanup() noexcept EXCLUSIVE_LOCKS_REQUIRED(!g_sqlite_mutex);

public:
    SQLiteDatabase() = delete;

    /** Create DB handle to real database */
    SQLiteDatabase(const fs::path& dir_path, const fs::path& file_path, const DatabaseOptions& options, bool mock = false);

    ~SQLiteDatabase();

    // Batches must acquire this semaphore on writing, and release when done writing.
    // This ensures that only one batch is modifying the database at a time.
    CSemaphore m_write_semaphore;

    bool Verify(bilingual_str& error);

    /** Open the database if it is not already opened */
    void Open() override;

    /** Close the database */
    void Close() override;

    /* These functions are unused */
    void AddRef() override { assert(false); }
    void RemoveRef() override { assert(false); }

    /** Rewrite the entire database on disk */
    bool Rewrite(const char* skip = nullptr) override;

    /** Back up the entire database to a file.
     */
    bool Backup(const std::string& dest) const override;

    /** No-ops
     *
     * SQLite always flushes everything to the database file after each transaction
     * (each Read/Write/Erase that we do is its own transaction unless we called
     * TxnBegin) so there is no need to have Flush or Periodic Flush.
     *
     * There is no DB env to reload, so ReloadDbEnv has nothing to do
     */
    void Flush() override {}
    bool PeriodicFlush() override { return false; }
    void ReloadDbEnv() override {}

    void IncrementUpdateCounter() override { ++nUpdateCounter; }

    std::string Filename() override { return m_file_path; }
    std::string Format() override { return "sqlite"; }

    /** Make a SQLiteBatch connected to this database */
    std::unique_ptr<DatabaseBatch> MakeBatch(bool flush_on_close = true) override;

    /** Return true if there is an on-going txn in this connection */
    bool HasActiveTxn();

    sqlite3* m_db{nullptr};
    bool m_use_unsafe_sync;
};

std::unique_ptr<SQLiteDatabase> MakeSQLiteDatabase(const fs::path& path, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error);

std::string SQLiteDatabaseVersion();
} // namespace wallet

#endif // BITCOIN_WALLET_SQLITE_H