Use distinct exception class and unify format of error messages

* Avoid manual code for making error message
* Use `const` for the return code variable where possible
* Unify formatting of error messages
This commit is contained in:
Martchus 2022-01-22 18:59:54 +01:00
parent a002cdecdf
commit 3bafada5cc
3 changed files with 82 additions and 81 deletions

View File

@ -12,19 +12,13 @@ using namespace std;
namespace LMDBSafe {
static string MDBError(int rc)
{
return mdb_strerror(rc);
}
MDBDbi::MDBDbi(MDB_env* env, MDB_txn* txn, const string_view dbname, unsigned int flags)
{
(void)env;
// A transaction that uses this function must finish (either commit or abort) before any other transaction in the process may use this function.
int rc = mdb_dbi_open(txn, dbname.empty() ? 0 : &dbname[0], flags, &d_dbi);
if(rc)
throw std::runtime_error("Unable to open named database: " + MDBError(rc));
if(const auto rc = mdb_dbi_open(txn, dbname.empty() ? 0 : &dbname[0], flags, &d_dbi))
throw LMDBError("Unable to open named database: ", rc);
// Database names are keys in the unnamed database, and may be read but not written.
}
@ -33,18 +27,18 @@ MDBEnv::MDBEnv(const char* fname, unsigned int flags, mdb_mode_t mode, MDB_dbi m
{
mdb_env_create(&d_env);
if(const auto rc = mdb_env_set_mapsize(d_env, 16ULL * 4096 * 244140ULL)) { // 4GB
throw std::runtime_error("setting map size: " + MDBError(rc));
throw LMDBError("Setting map size: ", rc);
}
// Various other options may also need to be set before opening the handle, e.g. mdb_env_set_mapsize(), mdb_env_set_maxreaders(), mdb_env_set_maxdbs(),
if (const auto rc = mdb_env_set_maxdbs(d_env, maxDBs)) {
throw std::runtime_error("setting maxdbs: " + MDBError(rc));
throw LMDBError("Setting maxdbs: ", rc);
}
// we need MDB_NOTLS since we rely on its semantics
if(int rc=mdb_env_open(d_env, fname, flags | MDB_NOTLS, mode)) {
if(const auto rc = mdb_env_open(d_env, fname, flags | MDB_NOTLS, mode)) {
// If this function fails, mdb_env_close() must be called to discard the MDB_env handle.
mdb_env_close(d_env);
throw std::runtime_error("Unable to open database file "+std::string(fname)+": " + MDBError(rc));
throw LMDBError("Unable to open database file " + std::string(fname) + ": ", rc);
}
}
@ -98,12 +92,12 @@ std::shared_ptr<MDBEnv> getMDBEnv(const char* fname, unsigned int flags, mdb_mod
struct stat statbuf;
if(stat(fname, &statbuf)) {
if(errno != ENOENT)
throw std::runtime_error("Unable to stat prospective mdb database: "+string(strerror(errno)));
throw LMDBError("Unable to stat prospective mdb database: " + string(strerror(errno)));
else {
std::lock_guard<std::mutex> l(mut);
auto fresh = std::make_shared<MDBEnv>(fname, flags, mode, maxDBs);
if(stat(fname, &statbuf))
throw std::runtime_error("Unable to stat prospective mdb database: "+string(strerror(errno)));
throw LMDBError("Unable to stat prospective mdb database: " + string(strerror(errno)));
auto key = std::tie(statbuf.st_dev, statbuf.st_ino);
s_envs[key] = {fresh, flags};
return fresh;
@ -117,7 +111,7 @@ std::shared_ptr<MDBEnv> getMDBEnv(const char* fname, unsigned int flags, mdb_mod
auto sp = iter->second.wp.lock();
if(sp) {
if(iter->second.flags != flags)
throw std::runtime_error("Can't open mdb with differing flags");
throw LMDBError("Can't open mdb with differing flags");
return sp;
}
@ -168,7 +162,7 @@ MDB_txn *MDBRWTransactionImpl::openRWTransaction(MDBEnv *env, MDB_txn *parent, u
{
MDB_txn *result;
if(env->getRWTX())
throw std::runtime_error("Duplicate RW transaction");
throw LMDBError("Duplicate RW transaction");
for(int tries =0 ; tries < 3; ++tries) { // it might happen twice, who knows
if(int rc=mdb_txn_begin(env->d_env, parent, flags, &result)) {
@ -178,7 +172,7 @@ MDB_txn *MDBRWTransactionImpl::openRWTransaction(MDBEnv *env, MDB_txn *parent, u
mdb_env_set_mapsize(env->d_env, 0);
continue;
}
throw std::runtime_error("Unable to start RW transaction: "+std::string(mdb_strerror(rc)));
throw LMDBError("Unable to start RW transaction: ", rc);
}
break;
}
@ -203,8 +197,8 @@ void MDBRWTransactionImpl::commit()
return;
}
if(int rc = mdb_txn_commit(d_txn)) {
throw std::runtime_error("committing: " + std::string(mdb_strerror(rc)));
if(const auto rc = mdb_txn_commit(d_txn)) {
throw LMDBError("Committing transaction: ", rc);
}
environment().decRWTX();
d_txn = nullptr;
@ -234,21 +228,20 @@ MDBROTransactionImpl::MDBROTransactionImpl(MDBEnv *parent, MDB_txn *txn):
MDB_txn *MDBROTransactionImpl::openROTransaction(MDBEnv *env, MDB_txn *parent, unsigned int flags)
{
if(env->getRWTX())
throw std::runtime_error("Duplicate RO transaction");
throw LMDBError("Duplicate RO transaction");
/*
A transaction and its cursors must only be used by a single thread, and a thread may only have a single transaction at a time. If MDB_NOTLS is in use, this does not apply to read-only transactions. */
MDB_txn *result = nullptr;
for(int tries =0 ; tries < 3; ++tries) { // it might happen twice, who knows
if(int rc=mdb_txn_begin(env->d_env, parent, MDB_RDONLY | flags, &result)) {
for(int tries = 0; tries < 3; ++tries) { // it might happen twice, who knows
if(const auto rc = mdb_txn_begin(env->d_env, parent, MDB_RDONLY | flags, &result)) {
if(rc == MDB_MAP_RESIZED && tries < 2) {
// "If the mapsize is increased by another process (..) mdb_txn_begin() will return MDB_MAP_RESIZED.
// call mdb_env_set_mapsize with a size of zero to adopt the new size."
mdb_env_set_mapsize(env->d_env, 0);
continue;
}
throw std::runtime_error("Unable to start RO transaction: "+string(mdb_strerror(rc)));
throw LMDBError("Unable to start RO transaction: ", rc);
}
break;
}
@ -297,7 +290,7 @@ void MDBROTransactionImpl::commit()
if (d_txn) {
d_parent->decROTX();
if (const auto rc = mdb_txn_commit(d_txn)) { // this appears to work better than abort for r/o database opening
throw std::runtime_error("Error comitting transaction: " + MDBError(rc));
throw LMDBError("Error comitting transaction: ", rc);
}
d_txn = nullptr;
}
@ -307,17 +300,16 @@ void MDBROTransactionImpl::commit()
void MDBRWTransactionImpl::clear(MDB_dbi dbi)
{
if(int rc = mdb_drop(d_txn, dbi, 0)) {
throw runtime_error("Error clearing database: " + MDBError(rc));
if(const auto rc = mdb_drop(d_txn, dbi, 0)) {
throw LMDBError("Error clearing database: ", rc);
}
}
MDBRWCursor MDBRWTransactionImpl::getRWCursor(const MDBDbi& dbi)
{
MDB_cursor *cursor;
int rc= mdb_cursor_open(d_txn, dbi, &cursor);
if(rc) {
throw std::runtime_error("Error creating RO cursor: "+std::string(mdb_strerror(rc)));
MDB_cursor *cursor;;
if(const auto rc = mdb_cursor_open(d_txn, dbi, &cursor)) {
throw LMDBError("Error creating RO cursor: ", rc);
}
return MDBRWCursor(d_rw_cursors, cursor);
}
@ -330,8 +322,8 @@ MDBRWCursor MDBRWTransactionImpl::getCursor(const MDBDbi &dbi)
MDBRWTransaction MDBRWTransactionImpl::getRWTransaction()
{
MDB_txn *txn;
if (int rc = mdb_txn_begin(environment(), *this, 0, &txn)) {
throw std::runtime_error(std::string("failed to start child transaction: ")+mdb_strerror(rc));
if (const auto rc = mdb_txn_begin(environment(), *this, 0, &txn)) {
throw LMDBError("Failed to start child transaction: ", rc);
}
// we need to increase the counter here because commit/abort on the child transaction will decrease it
environment().incRWTX();
@ -370,9 +362,8 @@ MDBROCursor MDBROTransactionImpl::getCursor(const MDBDbi& dbi)
MDBROCursor MDBROTransactionImpl::getROCursor(const MDBDbi &dbi)
{
MDB_cursor *cursor;
int rc= mdb_cursor_open(d_txn, dbi, &cursor);
if(rc) {
throw std::runtime_error("Error creating RO cursor: "+std::string(mdb_strerror(rc)));
if(const auto rc = mdb_cursor_open(d_txn, dbi, &cursor)) {
throw LMDBError("Error creating RO cursor: ", rc);
}
return MDBROCursor(d_cursors, cursor);
}

View File

@ -14,6 +14,7 @@
#include <vector>
#include <algorithm>
#include <limits>
#include <stdexcept>
/*!
* \brief The LMDBSafe namespace contains all classes/types contained by the lmdb-safe and
@ -40,6 +41,20 @@ using string_view = boost::string_ref;
#endif
#endif
class LMDBError : public std::runtime_error
{
public:
explicit LMDBError(const std::string &error) noexcept
: std::runtime_error(error)
{
}
explicit LMDBError(const std::string &context, int error) noexcept
: std::runtime_error(context + mdb_strerror(error))
{
}
};
/*!
* \brief The MDBDbi class is our only 'value type' object as 1) a dbi is actually an integer
* and 2) per LMDB documentation, we never close it.
@ -121,7 +136,7 @@ struct MDBOutVal
{
T ret;
if(d_mdbval.mv_size != sizeof(T))
throw std::runtime_error("MDB data has wrong length for type");
throw LMDBError("MDB data has wrong length for type");
memcpy(&ret, d_mdbval.mv_data, sizeof(T));
return ret;
@ -136,7 +151,7 @@ struct MDBOutVal
{
T ret;
if(d_mdbval.mv_size != sizeof(T))
throw std::runtime_error("MDB data has wrong length for type");
throw LMDBError("MDB data has wrong length for type");
memcpy(&ret, d_mdbval.mv_data, sizeof(T));
return ret;
@ -146,7 +161,7 @@ struct MDBOutVal
const T* get_struct_ptr() const
{
if(d_mdbval.mv_size != sizeof(T))
throw std::runtime_error("MDB data has wrong length for type");
throw LMDBError("MDB data has wrong length for type");
return reinterpret_cast<const T*>(d_mdbval.mv_data);
}
@ -261,12 +276,12 @@ public:
int get(MDB_dbi dbi, const MDBInVal& key, MDBOutVal& val)
{
if(!d_txn)
throw std::runtime_error("Attempt to use a closed RO transaction for get");
throw LMDBError("Attempt to use a closed RO transaction for get");
int rc = mdb_get(d_txn, dbi, const_cast<MDB_val*>(&key.d_mdbval),
const auto rc = mdb_get(d_txn, dbi, const_cast<MDB_val*>(&key.d_mdbval),
const_cast<MDB_val*>(&val.d_mdbval));
if(rc && rc != MDB_NOTFOUND)
throw std::runtime_error("getting data: " + std::string(mdb_strerror(rc)));
throw LMDBError("Getting data: ", rc);
return rc;
}
@ -383,18 +398,18 @@ public:
public:
int get(MDBOutVal& key, MDBOutVal& data, MDB_cursor_op op)
{
int rc = mdb_cursor_get(d_cursor, &key.d_mdbval, &data.d_mdbval, op);
const auto rc = mdb_cursor_get(d_cursor, &key.d_mdbval, &data.d_mdbval, op);
if(rc && rc != MDB_NOTFOUND)
throw std::runtime_error("Unable to get from cursor: " + std::string(mdb_strerror(rc)));
throw LMDBError("Unable to get from cursor: ", rc);
return rc;
}
int find(const MDBInVal& in, MDBOutVal& key, MDBOutVal& data)
{
key.d_mdbval = in.d_mdbval;
int rc=mdb_cursor_get(d_cursor, const_cast<MDB_val*>(&key.d_mdbval), &data.d_mdbval, MDB_SET);
const auto rc = mdb_cursor_get(d_cursor, const_cast<MDB_val*>(&key.d_mdbval), &data.d_mdbval, MDB_SET);
if(rc && rc != MDB_NOTFOUND)
throw std::runtime_error("Unable to find from cursor: " + std::string(mdb_strerror(rc)));
throw LMDBError("Unable to find from cursor: ", rc);
return rc;
}
@ -402,18 +417,18 @@ public:
{
key.d_mdbval = in.d_mdbval;
int rc = mdb_cursor_get(d_cursor, const_cast<MDB_val*>(&key.d_mdbval), &data.d_mdbval, MDB_SET_RANGE);
const auto rc = mdb_cursor_get(d_cursor, const_cast<MDB_val*>(&key.d_mdbval), &data.d_mdbval, MDB_SET_RANGE);
if(rc && rc != MDB_NOTFOUND)
throw std::runtime_error("Unable to lower_bound from cursor: " + std::string(mdb_strerror(rc)));
throw LMDBError("Unable to lower_bound from cursor: ", rc);
return rc;
}
int nextprev(MDBOutVal& key, MDBOutVal& data, MDB_cursor_op op)
{
int rc = mdb_cursor_get(d_cursor, const_cast<MDB_val*>(&key.d_mdbval), &data.d_mdbval, op);
const auto rc = mdb_cursor_get(d_cursor, const_cast<MDB_val*>(&key.d_mdbval), &data.d_mdbval, op);
if(rc && rc != MDB_NOTFOUND)
throw std::runtime_error("Unable to prevnext from cursor: " + std::string(mdb_strerror(rc)));
throw LMDBError("Unable to prevnext from cursor: ", rc);
return rc;
}
@ -429,9 +444,9 @@ public:
int currentlast(MDBOutVal& key, MDBOutVal& data, MDB_cursor_op op)
{
int rc = mdb_cursor_get(d_cursor, const_cast<MDB_val*>(&key.d_mdbval), &data.d_mdbval, op);
const auto rc = mdb_cursor_get(d_cursor, const_cast<MDB_val*>(&key.d_mdbval), &data.d_mdbval, op);
if(rc && rc != MDB_NOTFOUND)
throw std::runtime_error("Unable to next from cursor: " + std::string(mdb_strerror(rc)));
throw LMDBError("Unable to next from cursor: ", rc);
return rc;
}
@ -526,30 +541,27 @@ public:
void put(MDB_dbi dbi, const MDBInVal& key, const MDBInVal& val, unsigned int flags=0)
{
if(!d_txn)
throw std::runtime_error("Attempt to use a closed RW transaction for put");
int rc;
if((rc=mdb_put(d_txn, dbi,
throw LMDBError("Attempt to use a closed RW transaction for put");
if(const auto rc = mdb_put(d_txn, dbi,
const_cast<MDB_val*>(&key.d_mdbval),
const_cast<MDB_val*>(&val.d_mdbval), flags)))
throw std::runtime_error("putting data: " + std::string(mdb_strerror(rc)));
const_cast<MDB_val*>(&val.d_mdbval), flags))
throw LMDBError("Putting data: ", rc);
}
int del(MDBDbi& dbi, const MDBInVal& key, const MDBInVal& val)
{
int rc;
rc=mdb_del(d_txn, dbi, const_cast<MDB_val*>(&key.d_mdbval), const_cast<MDB_val*>(&val.d_mdbval));
const auto rc = mdb_del(d_txn, dbi, const_cast<MDB_val*>(&key.d_mdbval), const_cast<MDB_val*>(&val.d_mdbval));
if(rc && rc != MDB_NOTFOUND)
throw std::runtime_error("deleting data: " + std::string(mdb_strerror(rc)));
throw LMDBError("Deleting data: ", rc);
return rc;
}
int del(MDBDbi& dbi, const MDBInVal& key)
{
int rc;
rc=mdb_del(d_txn, dbi, const_cast<MDB_val*>(&key.d_mdbval), 0);
const auto rc = mdb_del(d_txn, dbi, const_cast<MDB_val*>(&key.d_mdbval), 0);
if(rc && rc != MDB_NOTFOUND)
throw std::runtime_error("deleting data: " + std::string(mdb_strerror(rc)));
throw LMDBError("Deleting data: ", rc);
return rc;
}
@ -557,19 +569,19 @@ public:
int get(MDBDbi& dbi, const MDBInVal& key, MDBOutVal& val)
{
if(!d_txn)
throw std::runtime_error("Attempt to use a closed RW transaction for get");
throw LMDBError("Attempt to use a closed RW transaction for get");
int rc = mdb_get(d_txn, dbi, const_cast<MDB_val*>(&key.d_mdbval),
const auto rc = mdb_get(d_txn, dbi, const_cast<MDB_val*>(&key.d_mdbval),
const_cast<MDB_val*>(&val.d_mdbval));
if(rc && rc != MDB_NOTFOUND)
throw std::runtime_error("getting data: " + std::string(mdb_strerror(rc)));
throw LMDBError("Getting data: ", rc);
return rc;
}
int get(MDBDbi& dbi, const MDBInVal& key, string_view& val)
{
MDBOutVal out;
int rc = get(dbi, key, out);
const auto rc = get(dbi, key, out);
if(!rc)
val = out.get<string_view>();
return rc;
@ -607,11 +619,10 @@ public:
void put(const MDBOutVal& key, const MDBInVal& data)
{
int rc = mdb_cursor_put(*this,
const_cast<MDB_val*>(&key.d_mdbval),
const_cast<MDB_val*>(&data.d_mdbval), MDB_CURRENT);
if(rc)
throw std::runtime_error("mdb_cursor_put: " + std::string(mdb_strerror(rc)));
if(const auto rc = mdb_cursor_put(*this,
const_cast<MDB_val*>(&key.d_mdbval),
const_cast<MDB_val*>(&data.d_mdbval), MDB_CURRENT))
throw LMDBError("Putting data via mdb_cursor_put: ", rc);
}

View File

@ -69,8 +69,8 @@ struct LMDBIndexOps
void del(MDBRWTransaction& txn, const Class& t, uint32_t id)
{
if(int rc = txn->del(d_idx, keyConv(d_parent->getMember(t)), id)) {
throw std::runtime_error("Error deleting from index: " + std::string(mdb_strerror(rc)));
if(const auto rc = txn->del(d_idx, keyConv(d_parent->getMember(t)), id)) {
throw LMDBError("Error deleting from index: ", rc);
}
}
@ -259,7 +259,7 @@ public:
if(d_on_index) {
if((*d_parent->d_txn)->get(d_parent->d_parent->d_main, d_id, d_data))
throw std::runtime_error("Missing id in constructor");
throw LMDBError("Missing id in constructor");
serFromString(d_data.get<std::string>(), d_t);
}
else
@ -284,7 +284,7 @@ public:
if(d_on_index) {
if((*d_parent->d_txn)->get(d_parent->d_parent->d_main, d_id, d_data))
throw std::runtime_error("Missing id in constructor");
throw LMDBError("Missing id in constructor");
serFromString(d_data.get<std::string>(), d_t);
}
else
@ -327,14 +327,13 @@ public:
iter_t& genoperator(MDB_cursor_op dupop, MDB_cursor_op op)
{
MDBOutVal data;
int rc;
next:;
rc = d_cursor.get(d_key, d_id, d_one_key ? dupop : op);
const auto rc = d_cursor.get(d_key, d_id, d_one_key ? dupop : op);
if(rc == MDB_NOTFOUND) {
d_end = true;
}
else if(rc) {
throw std::runtime_error("in genoperator, " + std::string(mdb_strerror(rc)));
throw LMDBError("Unable to get in genoperator: ", rc);
}
else if(!d_prefix.empty() && d_key.get<std::string>().rfind(d_prefix, 0)!=0) {
d_end = true;
@ -342,7 +341,7 @@ public:
else {
if(d_on_index) {
if((*d_parent->d_txn)->get(d_parent->d_parent->d_main, d_id, data))
throw std::runtime_error("Missing id field");
throw LMDBError("Missing id field in genoperator");
if(filter && !filter(data))
goto next;
@ -591,7 +590,7 @@ public:
{
T t;
if(!this->get(id, t))
throw std::runtime_error("Could not modify id "+std::to_string(id));
throw LMDBError("Could not modify id " + std::to_string(id));
func(t);
del(id); // this is the lazy way. We could test for changed index fields