Skip to content

Commit 5938ad0

Browse files
committed
wallet: Add DatabaseBatch::ErasePrefix method
This new function is not used yet this commit, but next commit adds usages and test coverage for both BDB and sqlite.
1 parent cae0608 commit 5938ad0

File tree

6 files changed

+51
-9
lines changed

6 files changed

+51
-9
lines changed

src/wallet/bdb.cpp

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -665,12 +665,14 @@ void BerkeleyDatabase::ReloadDbEnv()
665665
env->ReloadDbEnv();
666666
}
667667

668-
BerkeleyCursor::BerkeleyCursor(BerkeleyDatabase& database)
668+
BerkeleyCursor::BerkeleyCursor(BerkeleyDatabase& database, BerkeleyBatch* batch)
669669
{
670670
if (!database.m_db.get()) {
671671
throw std::runtime_error(STR_INTERNAL_BUG("BerkeleyDatabase does not exist"));
672672
}
673-
int ret = database.m_db->cursor(nullptr, &m_cursor, 0);
673+
// Transaction argument to cursor is only needed when using the cursor to
674+
// write to the database. Read-only cursors do not need a txn pointer.
675+
int ret = database.m_db->cursor(batch ? batch->txn() : nullptr, &m_cursor, 0);
674676
if (ret != 0) {
675677
throw std::runtime_error(STR_INTERNAL_BUG(strprintf("BDB Cursor could not be created. Returned %d", ret)));
676678
}
@@ -817,6 +819,25 @@ bool BerkeleyBatch::HasKey(DataStream&& key)
817819
return ret == 0;
818820
}
819821

822+
bool BerkeleyBatch::ErasePrefix(Span<const std::byte> prefix)
823+
{
824+
if (!TxnBegin()) return false;
825+
auto cursor{std::make_unique<BerkeleyCursor>(m_database, this)};
826+
// const_cast is safe below even though prefix_key is an in/out parameter,
827+
// because we are not using the DB_DBT_USERMEM flag, so BDB will allocate
828+
// and return a different output data pointer
829+
Dbt prefix_key{const_cast<std::byte*>(prefix.data()), static_cast<uint32_t>(prefix.size())}, prefix_value{};
830+
int ret{cursor->dbc()->get(&prefix_key, &prefix_value, DB_SET_RANGE)};
831+
for (int flag{DB_CURRENT}; ret == 0; flag = DB_NEXT) {
832+
SafeDbt key, value;
833+
ret = cursor->dbc()->get(key, value, flag);
834+
if (ret != 0 || key.get_size() < prefix.size() || memcmp(key.get_data(), prefix.data(), prefix.size()) != 0) break;
835+
ret = cursor->dbc()->del(0);
836+
}
837+
cursor.reset();
838+
return TxnCommit() && (ret == 0 || ret == DB_NOTFOUND);
839+
}
840+
820841
void BerkeleyDatabase::AddRef()
821842
{
822843
LOCK(cs_db);

src/wallet/bdb.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,10 +191,11 @@ class BerkeleyCursor : public DatabaseCursor
191191
Dbc* m_cursor;
192192

193193
public:
194-
explicit BerkeleyCursor(BerkeleyDatabase& database);
194+
explicit BerkeleyCursor(BerkeleyDatabase& database, BerkeleyBatch* batch=nullptr);
195195
~BerkeleyCursor() override;
196196

197197
Status Next(DataStream& key, DataStream& value) override;
198+
Dbc* dbc() const { return m_cursor; }
198199
};
199200

200201
/** RAII class that provides access to a Berkeley database */
@@ -205,6 +206,7 @@ class BerkeleyBatch : public DatabaseBatch
205206
bool WriteKey(DataStream&& key, DataStream&& value, bool overwrite = true) override;
206207
bool EraseKey(DataStream&& key) override;
207208
bool HasKey(DataStream&& key) override;
209+
bool ErasePrefix(Span<const std::byte> prefix) override;
208210

209211
protected:
210212
Db* pdb{nullptr};
@@ -229,6 +231,7 @@ class BerkeleyBatch : public DatabaseBatch
229231
bool TxnBegin() override;
230232
bool TxnCommit() override;
231233
bool TxnAbort() override;
234+
DbTxn* txn() const { return activeTxn; }
232235
};
233236

234237
std::string BerkeleyDatabaseVersion();

src/wallet/db.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ class DatabaseBatch
110110

111111
return HasKey(std::move(ssKey));
112112
}
113+
virtual bool ErasePrefix(Span<const std::byte> prefix) = 0;
113114

114115
virtual std::unique_ptr<DatabaseCursor> GetNewCursor() = 0;
115116
virtual bool TxnBegin() = 0;
@@ -186,6 +187,7 @@ class DummyBatch : public DatabaseBatch
186187
bool WriteKey(DataStream&& key, DataStream&& value, bool overwrite = true) override { return true; }
187188
bool EraseKey(DataStream&& key) override { return true; }
188189
bool HasKey(DataStream&& key) override { return true; }
190+
bool ErasePrefix(Span<const std::byte> prefix) override { return true; }
189191

190192
public:
191193
void Flush() override {}

src/wallet/sqlite.cpp

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ void SQLiteBatch::SetupSQLStatements()
125125
{&m_insert_stmt, "INSERT INTO main VALUES(?, ?)"},
126126
{&m_overwrite_stmt, "INSERT or REPLACE into main values(?, ?)"},
127127
{&m_delete_stmt, "DELETE FROM main WHERE key = ?"},
128+
{&m_delete_prefix_stmt, "DELETE FROM main WHERE instr(key, ?) = 1"},
128129
};
129130

130131
for (const auto& [stmt_prepared, stmt_text] : statements) {
@@ -375,6 +376,7 @@ void SQLiteBatch::Close()
375376
{&m_insert_stmt, "insert"},
376377
{&m_overwrite_stmt, "overwrite"},
377378
{&m_delete_stmt, "delete"},
379+
{&m_delete_prefix_stmt, "delete prefix"},
378380
};
379381

380382
for (const auto& [stmt_prepared, stmt_description] : statements) {
@@ -441,24 +443,34 @@ bool SQLiteBatch::WriteKey(DataStream&& key, DataStream&& value, bool overwrite)
441443
return res == SQLITE_DONE;
442444
}
443445

444-
bool SQLiteBatch::EraseKey(DataStream&& key)
446+
bool SQLiteBatch::ExecStatement(sqlite3_stmt* stmt, Span<const std::byte> blob)
445447
{
446448
if (!m_database.m_db) return false;
447-
assert(m_delete_stmt);
449+
assert(stmt);
448450

449451
// Bind: leftmost parameter in statement is index 1
450-
if (!BindBlobToStatement(m_delete_stmt, 1, key, "key")) return false;
452+
if (!BindBlobToStatement(stmt, 1, blob, "key")) return false;
451453

452454
// Execute
453-
int res = sqlite3_step(m_delete_stmt);
454-
sqlite3_clear_bindings(m_delete_stmt);
455-
sqlite3_reset(m_delete_stmt);
455+
int res = sqlite3_step(stmt);
456+
sqlite3_clear_bindings(stmt);
457+
sqlite3_reset(stmt);
456458
if (res != SQLITE_DONE) {
457459
LogPrintf("%s: Unable to execute statement: %s\n", __func__, sqlite3_errstr(res));
458460
}
459461
return res == SQLITE_DONE;
460462
}
461463

464+
bool SQLiteBatch::EraseKey(DataStream&& key)
465+
{
466+
return ExecStatement(m_delete_stmt, key);
467+
}
468+
469+
bool SQLiteBatch::ErasePrefix(Span<const std::byte> prefix)
470+
{
471+
return ExecStatement(m_delete_prefix_stmt, prefix);
472+
}
473+
462474
bool SQLiteBatch::HasKey(DataStream&& key)
463475
{
464476
if (!m_database.m_db) return false;

src/wallet/sqlite.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,16 @@ class SQLiteBatch : public DatabaseBatch
3636
sqlite3_stmt* m_insert_stmt{nullptr};
3737
sqlite3_stmt* m_overwrite_stmt{nullptr};
3838
sqlite3_stmt* m_delete_stmt{nullptr};
39+
sqlite3_stmt* m_delete_prefix_stmt{nullptr};
3940

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

4244
bool ReadKey(DataStream&& key, DataStream& value) override;
4345
bool WriteKey(DataStream&& key, DataStream&& value, bool overwrite = true) override;
4446
bool EraseKey(DataStream&& key) override;
4547
bool HasKey(DataStream&& key) override;
48+
bool ErasePrefix(Span<const std::byte> prefix) override;
4649

4750
public:
4851
explicit SQLiteBatch(SQLiteDatabase& database);

src/wallet/test/wallet_tests.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -922,6 +922,7 @@ class FailBatch : public DatabaseBatch
922922
bool WriteKey(DataStream&& key, DataStream&& value, bool overwrite = true) override { return m_pass; }
923923
bool EraseKey(DataStream&& key) override { return m_pass; }
924924
bool HasKey(DataStream&& key) override { return m_pass; }
925+
bool ErasePrefix(Span<const std::byte> prefix) override { return m_pass; }
925926

926927
public:
927928
explicit FailBatch(bool pass) : m_pass(pass) {}

0 commit comments

Comments
 (0)