Skip to content

Commit

Permalink
Merge bitcoin/bitcoin#26690: wallet: Refactor database cursor into it…
Browse files Browse the repository at this point in the history
…s own object with proper return codes

4aebd83 db: Change DatabaseCursor::Next to return status enum (Andrew Chow)
d79e8dc wallet: Have cursor users use DatabaseCursor directly (Andrew Chow)
7a198bb wallet: Introduce DatabaseCursor RAII class for managing cursor (Andrew Chow)
69efbc0 Move SafeDbt out of BerkeleyBatch (Andrew Chow)

Pull request description:

  Instead of having database cursors be tied to a particular `DatabaseBatch` object and requiring its setup and teardown be separate functions in that batch, we can have cursors be separate RAII classes. This makes it easier to create and destroy cursors as well as having cursors that have slightly different behaviors.

  Additionally, since reading data from a cursor is a tri-state, this PR changes the return value of the `Next` function (formerly `ReadAtCursor`) to return an Enum rather than the current system of 2 booleans. This greatly simplifies and unifies the code that deals with cursors as now there is no confusion as to what the function returns when there are no records left to be read.

  Extracted from #24914

ACKs for top commit:
  furszy:
    diff ACK 4aebd83
  theStack:
    Code-review ACK 4aebd83

Tree-SHA512: 5d0be56a18de5b08c777dd5a73ba5a6ef1e696fdb07d1dca952a88ded07887b7c5c04342f9a76feb2f6fe24a45dc31f094f1f5d9500e6bdf4a44f4edb66dcaa1
  • Loading branch information
fanquake committed Jan 23, 2023
2 parents 5271c77 + 4aebd83 commit a62231b
Show file tree
Hide file tree
Showing 11 changed files with 178 additions and 134 deletions.
66 changes: 36 additions & 30 deletions src/wallet/bdb.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <wallet/bdb.h>
#include <wallet/db.h>

#include <util/check.h>
#include <util/strencodings.h>
#include <util/translation.h>

Expand Down Expand Up @@ -220,17 +221,17 @@ BerkeleyEnvironment::BerkeleyEnvironment() : m_use_shared_memory(false)
fMockDb = true;
}

BerkeleyBatch::SafeDbt::SafeDbt()
SafeDbt::SafeDbt()
{
m_dbt.set_flags(DB_DBT_MALLOC);
}

BerkeleyBatch::SafeDbt::SafeDbt(void* data, size_t size)
SafeDbt::SafeDbt(void* data, size_t size)
: m_dbt(data, size)
{
}

BerkeleyBatch::SafeDbt::~SafeDbt()
SafeDbt::~SafeDbt()
{
if (m_dbt.get_data() != nullptr) {
// Clear memory, e.g. in case it was a private key
Expand All @@ -244,17 +245,17 @@ BerkeleyBatch::SafeDbt::~SafeDbt()
}
}

const void* BerkeleyBatch::SafeDbt::get_data() const
const void* SafeDbt::get_data() const
{
return m_dbt.get_data();
}

uint32_t BerkeleyBatch::SafeDbt::get_size() const
uint32_t SafeDbt::get_size() const
{
return m_dbt.get_size();
}

BerkeleyBatch::SafeDbt::operator Dbt*()
SafeDbt::operator Dbt*()
{
return &m_dbt;
}
Expand Down Expand Up @@ -307,7 +308,7 @@ BerkeleyDatabase::~BerkeleyDatabase()
}
}

BerkeleyBatch::BerkeleyBatch(BerkeleyDatabase& database, const bool read_only, bool fFlushOnCloseIn) : pdb(nullptr), activeTxn(nullptr), m_cursor(nullptr), m_database(database)
BerkeleyBatch::BerkeleyBatch(BerkeleyDatabase& database, const bool read_only, bool fFlushOnCloseIn) : pdb(nullptr), activeTxn(nullptr), m_database(database)
{
database.AddRef();
database.Open();
Expand Down Expand Up @@ -398,7 +399,6 @@ void BerkeleyBatch::Close()
activeTxn->abort();
activeTxn = nullptr;
pdb = nullptr;
CloseCursor();

if (fFlushOnClose)
Flush();
Expand Down Expand Up @@ -476,15 +476,15 @@ bool BerkeleyDatabase::Rewrite(const char* pszSkip)
fSuccess = false;
}

if (db.StartCursor()) {
std::unique_ptr<DatabaseCursor> cursor = db.GetNewCursor();
if (cursor) {
while (fSuccess) {
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
CDataStream ssValue(SER_DISK, CLIENT_VERSION);
bool complete;
bool ret1 = db.ReadAtCursor(ssKey, ssValue, complete);
if (complete) {
DatabaseCursor::Status ret1 = cursor->Next(ssKey, ssValue);
if (ret1 == DatabaseCursor::Status::DONE) {
break;
} else if (!ret1) {
} else if (ret1 == DatabaseCursor::Status::FAIL) {
fSuccess = false;
break;
}
Expand All @@ -502,7 +502,7 @@ bool BerkeleyDatabase::Rewrite(const char* pszSkip)
if (ret2 > 0)
fSuccess = false;
}
db.CloseCursor();
cursor.reset();
}
if (fSuccess) {
db.Close();
Expand Down Expand Up @@ -656,30 +656,30 @@ void BerkeleyDatabase::ReloadDbEnv()
env->ReloadDbEnv();
}

bool BerkeleyBatch::StartCursor()
BerkeleyCursor::BerkeleyCursor(BerkeleyDatabase& database)
{
assert(!m_cursor);
if (!pdb)
return false;
int ret = pdb->cursor(nullptr, &m_cursor, 0);
return ret == 0;
if (!database.m_db.get()) {
throw std::runtime_error(STR_INTERNAL_BUG("BerkeleyDatabase does not exist"));
}
int ret = database.m_db->cursor(nullptr, &m_cursor, 0);
if (ret != 0) {
throw std::runtime_error(STR_INTERNAL_BUG(strprintf("BDB Cursor could not be created. Returned %d", ret).c_str()));
}
}

bool BerkeleyBatch::ReadAtCursor(CDataStream& ssKey, CDataStream& ssValue, bool& complete)
DatabaseCursor::Status BerkeleyCursor::Next(CDataStream& ssKey, CDataStream& ssValue)
{
complete = false;
if (m_cursor == nullptr) return false;
if (m_cursor == nullptr) return Status::FAIL;
// Read at cursor
SafeDbt datKey;
SafeDbt datValue;
int ret = m_cursor->get(datKey, datValue, DB_NEXT);
if (ret == DB_NOTFOUND) {
complete = true;
return Status::DONE;
}
if (ret != 0 || datKey.get_data() == nullptr || datValue.get_data() == nullptr) {
return Status::FAIL;
}
if (ret != 0)
return false;
else if (datKey.get_data() == nullptr || datValue.get_data() == nullptr)
return false;

// Convert to streams
ssKey.SetType(SER_DISK);
Expand All @@ -688,16 +688,22 @@ bool BerkeleyBatch::ReadAtCursor(CDataStream& ssKey, CDataStream& ssValue, bool&
ssValue.SetType(SER_DISK);
ssValue.clear();
ssValue.write({AsBytePtr(datValue.get_data()), datValue.get_size()});
return true;
return Status::MORE;
}

void BerkeleyBatch::CloseCursor()
BerkeleyCursor::~BerkeleyCursor()
{
if (!m_cursor) return;
m_cursor->close();
m_cursor = nullptr;
}

std::unique_ptr<DatabaseCursor> BerkeleyBatch::GetNewCursor()
{
if (!pdb) return nullptr;
return std::make_unique<BerkeleyCursor>(m_database);
}

bool BerkeleyBatch::TxnBegin()
{
if (!pdb || activeTxn)
Expand Down
53 changes: 31 additions & 22 deletions src/wallet/bdb.h
Original file line number Diff line number Diff line change
Expand Up @@ -165,29 +165,41 @@ class BerkeleyDatabase : public WalletDatabase
std::unique_ptr<DatabaseBatch> MakeBatch(bool flush_on_close = true) override;
};

/** RAII class that provides access to a Berkeley database */
class BerkeleyBatch : public DatabaseBatch
/** RAII class that automatically cleanses its data on destruction */
class SafeDbt final
{
/** RAII class that automatically cleanses its data on destruction */
class SafeDbt final
{
Dbt m_dbt;
Dbt m_dbt;

public:
// construct Dbt with internally-managed data
SafeDbt();
// construct Dbt with provided data
SafeDbt(void* data, size_t size);
~SafeDbt();
public:
// construct Dbt with internally-managed data
SafeDbt();
// construct Dbt with provided data
SafeDbt(void* data, size_t size);
~SafeDbt();

// delegate to Dbt
const void* get_data() const;
uint32_t get_size() const;

// conversion operator to access the underlying Dbt
operator Dbt*();
};

// delegate to Dbt
const void* get_data() const;
uint32_t get_size() const;
class BerkeleyCursor : public DatabaseCursor
{
private:
Dbc* m_cursor;

// conversion operator to access the underlying Dbt
operator Dbt*();
};
public:
explicit BerkeleyCursor(BerkeleyDatabase& database);
~BerkeleyCursor() override;

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

/** RAII class that provides access to a Berkeley database */
class BerkeleyBatch : public DatabaseBatch
{
private:
bool ReadKey(CDataStream&& key, CDataStream& value) override;
bool WriteKey(CDataStream&& key, CDataStream&& value, bool overwrite = true) override;
Expand All @@ -198,7 +210,6 @@ class BerkeleyBatch : public DatabaseBatch
Db* pdb;
std::string strFile;
DbTxn* activeTxn;
Dbc* m_cursor;
bool fReadOnly;
bool fFlushOnClose;
BerkeleyEnvironment *env;
Expand All @@ -214,9 +225,7 @@ class BerkeleyBatch : public DatabaseBatch
void Flush() override;
void Close() override;

bool StartCursor() override;
bool ReadAtCursor(CDataStream& ssKey, CDataStream& ssValue, bool& complete) override;
void CloseCursor() override;
std::unique_ptr<DatabaseCursor> GetNewCursor() override;
bool TxnBegin() override;
bool TxnCommit() override;
bool TxnAbort() override;
Expand Down
32 changes: 26 additions & 6 deletions src/wallet/db.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,25 @@ struct bilingual_str;
namespace wallet {
void SplitWalletPath(const fs::path& wallet_path, fs::path& env_directory, std::string& database_filename);

class DatabaseCursor
{
public:
explicit DatabaseCursor() {}
virtual ~DatabaseCursor() {}

DatabaseCursor(const DatabaseCursor&) = delete;
DatabaseCursor& operator=(const DatabaseCursor&) = delete;

enum class Status
{
FAIL,
MORE,
DONE,
};

virtual Status Next(CDataStream& key, CDataStream& value) { return Status::FAIL; }
};

/** RAII class that provides access to a WalletDatabase */
class DatabaseBatch
{
Expand Down Expand Up @@ -92,9 +111,7 @@ class DatabaseBatch
return HasKey(std::move(ssKey));
}

virtual bool StartCursor() = 0;
virtual bool ReadAtCursor(CDataStream& ssKey, CDataStream& ssValue, bool& complete) = 0;
virtual void CloseCursor() = 0;
virtual std::unique_ptr<DatabaseCursor> GetNewCursor() = 0;
virtual bool TxnBegin() = 0;
virtual bool TxnCommit() = 0;
virtual bool TxnAbort() = 0;
Expand Down Expand Up @@ -156,6 +173,11 @@ class WalletDatabase
virtual std::unique_ptr<DatabaseBatch> MakeBatch(bool flush_on_close = true) = 0;
};

class DummyCursor : public DatabaseCursor
{
Status Next(CDataStream& key, CDataStream& value) override { return Status::FAIL; }
};

/** RAII class that provides access to a DummyDatabase. Never fails. */
class DummyBatch : public DatabaseBatch
{
Expand All @@ -169,9 +191,7 @@ class DummyBatch : public DatabaseBatch
void Flush() override {}
void Close() override {}

bool StartCursor() override { return true; }
bool ReadAtCursor(CDataStream& ssKey, CDataStream& ssValue, bool& complete) override { return true; }
void CloseCursor() override {}
std::unique_ptr<DatabaseCursor> GetNewCursor() override { return std::make_unique<DummyCursor>(); }
bool TxnBegin() override { return true; }
bool TxnCommit() override { return true; }
bool TxnAbort() override { return true; }
Expand Down
13 changes: 7 additions & 6 deletions src/wallet/dump.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ bool DumpWallet(const ArgsManager& args, CWallet& wallet, bilingual_str& error)
std::unique_ptr<DatabaseBatch> batch = db.MakeBatch();

bool ret = true;
if (!batch->StartCursor()) {
std::unique_ptr<DatabaseCursor> cursor = batch->GetNewCursor();
if (!cursor) {
error = _("Error: Couldn't create cursor into database");
ret = false;
}
Expand All @@ -68,13 +69,13 @@ bool DumpWallet(const ArgsManager& args, CWallet& wallet, bilingual_str& error)
while (true) {
CDataStream ss_key(SER_DISK, CLIENT_VERSION);
CDataStream ss_value(SER_DISK, CLIENT_VERSION);
bool complete;
ret = batch->ReadAtCursor(ss_key, ss_value, complete);
if (complete) {
DatabaseCursor::Status status = cursor->Next(ss_key, ss_value);
if (status == DatabaseCursor::Status::DONE) {
ret = true;
break;
} else if (!ret) {
} else if (status == DatabaseCursor::Status::FAIL) {
error = _("Error reading next record from wallet database");
ret = false;
break;
}
std::string key_str = HexStr(ss_key);
Expand All @@ -85,7 +86,7 @@ bool DumpWallet(const ArgsManager& args, CWallet& wallet, bilingual_str& error)
}
}

batch->CloseCursor();
cursor.reset();
batch.reset();

// Close the wallet after we're done with it. The caller won't be doing this
Expand Down
Loading

0 comments on commit a62231b

Please sign in to comment.