Changeset View
Changeset View
Standalone View
Standalone View
src/wallet/bdb.cpp
Show First 20 Lines • Show All 335 Lines • ▼ Show 20 Lines | if (env) { | ||||
env->m_fileids.erase(strFile); | env->m_fileids.erase(strFile); | ||||
} | } | ||||
} | } | ||||
BerkeleyBatch::BerkeleyBatch(BerkeleyDatabase &database, const char *pszMode, | BerkeleyBatch::BerkeleyBatch(BerkeleyDatabase &database, const char *pszMode, | ||||
bool fFlushOnCloseIn) | bool fFlushOnCloseIn) | ||||
: pdb(nullptr), activeTxn(nullptr), m_cursor(nullptr), | : pdb(nullptr), activeTxn(nullptr), m_cursor(nullptr), | ||||
m_database(database) { | m_database(database) { | ||||
database.AddRef(); | |||||
database.Open(pszMode); | |||||
fReadOnly = (!strchr(pszMode, '+') && !strchr(pszMode, 'w')); | fReadOnly = (!strchr(pszMode, '+') && !strchr(pszMode, 'w')); | ||||
fFlushOnClose = fFlushOnCloseIn; | fFlushOnClose = fFlushOnCloseIn; | ||||
env = database.env.get(); | env = database.env.get(); | ||||
if (database.IsDummy()) { | pdb = database.m_db.get(); | ||||
return; | strFile = database.strFile; | ||||
bool fCreate = strchr(pszMode, 'c') != nullptr; | |||||
if (fCreate && !Exists(std::string("version"))) { | |||||
bool fTmp = fReadOnly; | |||||
fReadOnly = false; | |||||
Write(std::string("version"), CLIENT_VERSION); | |||||
fReadOnly = fTmp; | |||||
} | |||||
} | } | ||||
const std::string &strFilename = database.strFile; | void BerkeleyDatabase::Open(const char *pszMode) { | ||||
if (IsDummy()) { | |||||
return; | |||||
} | |||||
bool fCreate = strchr(pszMode, 'c') != nullptr; | bool fCreate = strchr(pszMode, 'c') != nullptr; | ||||
unsigned int nFlags = DB_THREAD; | unsigned int nFlags = DB_THREAD; | ||||
if (fCreate) { | if (fCreate) { | ||||
nFlags |= DB_CREATE; | nFlags |= DB_CREATE; | ||||
} | } | ||||
{ | { | ||||
LOCK(cs_db); | LOCK(cs_db); | ||||
bilingual_str open_err; | bilingual_str open_err; | ||||
if (!env->Open(open_err)) { | if (!env->Open(open_err)) { | ||||
throw std::runtime_error( | throw std::runtime_error( | ||||
"BerkeleyBatch: Failed to open database environment."); | "BerkeleyDatabase: Failed to open database environment."); | ||||
} | } | ||||
pdb = database.m_db.get(); | |||||
if (pdb == nullptr) { | if (m_db == nullptr) { | ||||
int ret; | int ret; | ||||
std::unique_ptr<Db> pdb_temp = | std::unique_ptr<Db> pdb_temp = | ||||
std::make_unique<Db>(env->dbenv.get(), 0); | std::make_unique<Db>(env->dbenv.get(), 0); | ||||
bool fMockDb = env->IsMock(); | bool fMockDb = env->IsMock(); | ||||
if (fMockDb) { | if (fMockDb) { | ||||
DbMpoolFile *mpf = pdb_temp->get_mpf(); | DbMpoolFile *mpf = pdb_temp->get_mpf(); | ||||
ret = mpf->set_flags(DB_MPOOL_NOFILE, 1); | ret = mpf->set_flags(DB_MPOOL_NOFILE, 1); | ||||
if (ret != 0) { | if (ret != 0) { | ||||
throw std::runtime_error( | throw std::runtime_error(strprintf( | ||||
strprintf("BerkeleyBatch: Failed to configure for no " | "BerkeleyDatabase: Failed to configure for no " | ||||
"temp file backing for database %s", | "temp file backing for database %s", | ||||
strFilename)); | strFile)); | ||||
} | } | ||||
} | } | ||||
ret = pdb_temp->open( | ret = pdb_temp->open( | ||||
nullptr, // Txn pointer | nullptr, // Txn pointer | ||||
fMockDb ? nullptr : strFilename.c_str(), // Filename | fMockDb ? nullptr : strFile.c_str(), // Filename | ||||
fMockDb ? strFilename.c_str() : "main", // Logical db name | fMockDb ? strFile.c_str() : "main", // Logical db name | ||||
DB_BTREE, // Database type | DB_BTREE, // Database type | ||||
nFlags, // Flags | nFlags, // Flags | ||||
0); | 0); | ||||
if (ret != 0) { | if (ret != 0) { | ||||
throw std::runtime_error( | throw std::runtime_error(strprintf( | ||||
strprintf("BerkeleyBatch: Error %d, can't open database %s", | "BerkeleyDatabase: Error %d, can't open database %s", ret, | ||||
ret, strFilename)); | strFile)); | ||||
} | } | ||||
// Call CheckUniqueFileid on the containing BDB environment to | // Call CheckUniqueFileid on the containing BDB environment to | ||||
// avoid BDB data consistency bugs that happen when different data | // avoid BDB data consistency bugs that happen when different data | ||||
// files in the same environment have the same fileid. | // files in the same environment have the same fileid. | ||||
// | // | ||||
// Also call CheckUniqueFileid on all the other g_dbenvs to prevent | // Also call CheckUniqueFileid on all the other g_dbenvs to prevent | ||||
// bitcoin from opening the same data file through another | // bitcoin from opening the same data file through another | ||||
// environment when the file is referenced through equivalent but | // environment when the file is referenced through equivalent but | ||||
// not obviously identical symlinked or hard linked or bind mounted | // not obviously identical symlinked or hard linked or bind mounted | ||||
// paths. In the future a more relaxed check for equal inode and | // paths. In the future a more relaxed check for equal inode and | ||||
// device ids could be done instead, which would allow opening | // device ids could be done instead, which would allow opening | ||||
// different backup copies of a wallet at the same time. Maybe even | // different backup copies of a wallet at the same time. Maybe even | ||||
// more ideally, an exclusive lock for accessing the database could | // more ideally, an exclusive lock for accessing the database could | ||||
// be implemented, so no equality checks are needed at all. (Newer | // be implemented, so no equality checks are needed at all. (Newer | ||||
// versions of BDB have an set_lk_exclusive method for this | // versions of BDB have an set_lk_exclusive method for this | ||||
// purpose, but the older version we use does not.) | // purpose, but the older version we use does not.) | ||||
for (const auto &dbenv : g_dbenvs) { | for (const auto &dbenv : g_dbenvs) { | ||||
CheckUniqueFileid(*dbenv.second.lock().get(), strFilename, | CheckUniqueFileid(*dbenv.second.lock().get(), strFile, | ||||
*pdb_temp, this->env->m_fileids[strFilename]); | *pdb_temp, this->env->m_fileids[strFile]); | ||||
} | } | ||||
pdb = pdb_temp.release(); | m_db.reset(pdb_temp.release()); | ||||
database.m_db.reset(pdb); | |||||
if (fCreate && !Exists(std::string("version"))) { | |||||
bool fTmp = fReadOnly; | |||||
fReadOnly = false; | |||||
Write(std::string("version"), CLIENT_VERSION); | |||||
fReadOnly = fTmp; | |||||
} | |||||
} | |||||
database.AddRef(); | |||||
strFile = strFilename; | |||||
} | } | ||||
} | } | ||||
void BerkeleyDatabase::Open(const char *mode) { | |||||
throw std::logic_error("BerkeleyDatabase does not implement Open. " | |||||
"This function should not be called."); | |||||
} | } | ||||
void BerkeleyBatch::Flush() { | void BerkeleyBatch::Flush() { | ||||
if (activeTxn) { | if (activeTxn) { | ||||
return; | return; | ||||
} | } | ||||
// Flush database activity from memory pool to disk log | // Flush database activity from memory pool to disk log | ||||
Show All 12 Lines | if (env) { | ||||
nMinutes, 0); | nMinutes, 0); | ||||
} | } | ||||
} | } | ||||
void BerkeleyDatabase::IncrementUpdateCounter() { | void BerkeleyDatabase::IncrementUpdateCounter() { | ||||
++nUpdateCounter; | ++nUpdateCounter; | ||||
} | } | ||||
BerkeleyBatch::~BerkeleyBatch() { | |||||
Close(); | |||||
m_database.RemoveRef(); | |||||
} | |||||
void BerkeleyBatch::Close() { | void BerkeleyBatch::Close() { | ||||
if (!pdb) { | if (!pdb) { | ||||
return; | return; | ||||
} | } | ||||
if (activeTxn) { | if (activeTxn) { | ||||
activeTxn->abort(); | activeTxn->abort(); | ||||
} | } | ||||
activeTxn = nullptr; | activeTxn = nullptr; | ||||
pdb = nullptr; | pdb = nullptr; | ||||
CloseCursor(); | CloseCursor(); | ||||
if (fFlushOnClose) { | if (fFlushOnClose) { | ||||
Flush(); | Flush(); | ||||
} | } | ||||
m_database.RemoveRef(); | |||||
} | } | ||||
void BerkeleyEnvironment::CloseDb(const std::string &strFile) { | void BerkeleyEnvironment::CloseDb(const std::string &strFile) { | ||||
LOCK(cs_db); | LOCK(cs_db); | ||||
auto it = m_databases.find(strFile); | auto it = m_databases.find(strFile); | ||||
assert(it != m_databases.end()); | assert(it != m_databases.end()); | ||||
BerkeleyDatabase &database = it->second.get(); | BerkeleyDatabase &database = it->second.get(); | ||||
if (database.m_db) { | if (database.m_db) { | ||||
▲ Show 20 Lines • Show All 434 Lines • ▼ Show 20 Lines | void BerkeleyDatabase::AddRef() { | ||||
} else { | } else { | ||||
m_refcount++; | m_refcount++; | ||||
} | } | ||||
} | } | ||||
void BerkeleyDatabase::RemoveRef() { | void BerkeleyDatabase::RemoveRef() { | ||||
LOCK(cs_db); | LOCK(cs_db); | ||||
m_refcount--; | m_refcount--; | ||||
if (env) { | |||||
env->m_db_in_use.notify_all(); | env->m_db_in_use.notify_all(); | ||||
} | } | ||||
} | |||||
std::unique_ptr<DatabaseBatch> | std::unique_ptr<DatabaseBatch> | ||||
BerkeleyDatabase::MakeBatch(const char *mode, bool flush_on_close) { | BerkeleyDatabase::MakeBatch(const char *mode, bool flush_on_close) { | ||||
return std::make_unique<BerkeleyBatch>(*this, mode, flush_on_close); | return std::make_unique<BerkeleyBatch>(*this, mode, flush_on_close); | ||||
} | } |