2018-12-10 22:08:49 +01:00
|
|
|
#pragma once
|
2022-01-18 21:58:46 +01:00
|
|
|
|
2018-12-07 13:52:17 +01:00
|
|
|
#include <lmdb.h>
|
2022-01-18 21:58:46 +01:00
|
|
|
|
2018-12-07 13:52:17 +01:00
|
|
|
#include <iostream>
|
|
|
|
#include <fstream>
|
|
|
|
#include <set>
|
2018-12-07 18:17:03 +01:00
|
|
|
#include <map>
|
|
|
|
#include <thread>
|
|
|
|
#include <memory>
|
2018-12-10 14:51:02 +01:00
|
|
|
#include <string>
|
2022-01-18 21:58:46 +01:00
|
|
|
#include <cstring>
|
2018-12-08 14:08:26 +01:00
|
|
|
#include <mutex>
|
2019-10-24 17:56:45 +02:00
|
|
|
#include <vector>
|
|
|
|
#include <algorithm>
|
2021-12-05 19:28:08 +01:00
|
|
|
#include <limits>
|
2022-01-22 18:59:54 +01:00
|
|
|
#include <stdexcept>
|
2018-12-07 13:52:17 +01:00
|
|
|
|
2022-01-22 18:18:15 +01:00
|
|
|
/*!
|
|
|
|
* \brief The LMDBSafe namespace contains all classes/types contained by the lmdb-safe and
|
|
|
|
* lmdb-typed libraries.
|
|
|
|
* \remarks
|
|
|
|
* - Error strategy: Anything that "should never happen" turns into an exception. But things
|
|
|
|
* like "duplicate entry" or "no such key" are for you to deal with.
|
|
|
|
* - Thread safety: We are as safe as LMDB. You can talk to MDBEnv from as many threads as you
|
|
|
|
* want.
|
|
|
|
*/
|
2022-01-18 22:08:36 +01:00
|
|
|
namespace LMDBSafe {
|
|
|
|
|
2019-01-05 14:38:52 +01:00
|
|
|
// apple compiler somehow has string_view even in c++11!
|
2020-06-06 19:56:46 +02:00
|
|
|
#ifdef __cpp_lib_string_view
|
|
|
|
using std::string_view;
|
|
|
|
#else
|
2018-12-28 22:02:29 +01:00
|
|
|
#include <boost/version.hpp>
|
2018-12-28 19:14:00 +01:00
|
|
|
#if BOOST_VERSION > 105400
|
2018-12-10 22:08:49 +01:00
|
|
|
#include <boost/utility/string_view.hpp>
|
|
|
|
using boost::string_view;
|
|
|
|
#else
|
2018-12-28 19:14:00 +01:00
|
|
|
#include <boost/utility/string_ref.hpp>
|
|
|
|
using string_view = boost::string_ref;
|
|
|
|
#endif
|
2018-12-10 22:08:49 +01:00
|
|
|
#endif
|
|
|
|
|
2022-01-22 18:59:54 +01:00
|
|
|
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))
|
|
|
|
{
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2022-01-22 18:18:15 +01:00
|
|
|
/*!
|
|
|
|
* \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.
|
2018-12-07 13:52:17 +01:00
|
|
|
*/
|
|
|
|
class MDBDbi
|
|
|
|
{
|
|
|
|
public:
|
2018-12-13 23:16:46 +01:00
|
|
|
MDBDbi()
|
|
|
|
{
|
2021-12-05 19:28:08 +01:00
|
|
|
d_dbi = std::numeric_limits<decltype (d_dbi)>::max();
|
2018-12-13 23:16:46 +01:00
|
|
|
}
|
2021-12-05 19:28:08 +01:00
|
|
|
explicit MDBDbi(MDB_env* env, MDB_txn* txn, string_view dbname, unsigned int flags);
|
2018-12-07 13:52:17 +01:00
|
|
|
|
|
|
|
operator const MDB_dbi&() const
|
|
|
|
{
|
|
|
|
return d_dbi;
|
|
|
|
}
|
|
|
|
|
|
|
|
MDB_dbi d_dbi;
|
|
|
|
};
|
|
|
|
|
2019-10-26 11:42:38 +02:00
|
|
|
class MDBRWTransactionImpl;
|
|
|
|
class MDBROTransactionImpl;
|
|
|
|
|
|
|
|
using MDBROTransaction = std::unique_ptr<MDBROTransactionImpl>;
|
|
|
|
using MDBRWTransaction = std::unique_ptr<MDBRWTransactionImpl>;
|
2018-12-07 13:52:17 +01:00
|
|
|
|
|
|
|
class MDBEnv
|
|
|
|
{
|
|
|
|
public:
|
2022-01-16 20:05:34 +01:00
|
|
|
MDBEnv(const char* fname, unsigned int flags, mdb_mode_t mode, MDB_dbi maxDBs);
|
2018-12-07 13:52:17 +01:00
|
|
|
|
|
|
|
~MDBEnv()
|
|
|
|
{
|
|
|
|
// Only a single thread may call this function. All transactions, databases, and cursors must already be closed before calling this function
|
|
|
|
mdb_env_close(d_env);
|
|
|
|
// but, elsewhere, docs say database handles do not need to be closed?
|
|
|
|
}
|
|
|
|
|
2021-12-05 19:28:08 +01:00
|
|
|
MDBDbi openDB(const string_view dbname, unsigned int flags);
|
2018-12-07 13:52:17 +01:00
|
|
|
|
|
|
|
MDBRWTransaction getRWTransaction();
|
|
|
|
MDBROTransaction getROTransaction();
|
|
|
|
|
|
|
|
operator MDB_env*& ()
|
|
|
|
{
|
|
|
|
return d_env;
|
|
|
|
}
|
|
|
|
MDB_env* d_env;
|
2018-12-08 14:08:26 +01:00
|
|
|
|
|
|
|
int getRWTX();
|
|
|
|
void incRWTX();
|
|
|
|
void decRWTX();
|
|
|
|
int getROTX();
|
|
|
|
void incROTX();
|
|
|
|
void decROTX();
|
|
|
|
private:
|
2018-12-08 20:58:19 +01:00
|
|
|
std::mutex d_openmut;
|
|
|
|
std::mutex d_countmutex;
|
2018-12-07 18:17:03 +01:00
|
|
|
std::map<std::thread::id, int> d_RWtransactionsOut;
|
|
|
|
std::map<std::thread::id, int> d_ROtransactionsOut;
|
2018-12-07 13:52:17 +01:00
|
|
|
};
|
|
|
|
|
2022-01-16 20:05:34 +01:00
|
|
|
std::shared_ptr<MDBEnv> getMDBEnv(const char* fname, unsigned int flags, mdb_mode_t mode, MDB_dbi maxDBs = 128);
|
2018-12-07 18:17:03 +01:00
|
|
|
|
2018-12-10 14:51:02 +01:00
|
|
|
|
|
|
|
|
|
|
|
struct MDBOutVal
|
|
|
|
{
|
|
|
|
operator MDB_val&()
|
|
|
|
{
|
|
|
|
return d_mdbval;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class T,
|
|
|
|
typename std::enable_if<std::is_arithmetic<T>::value,
|
2019-01-04 20:51:04 +01:00
|
|
|
T>::type* = nullptr> const
|
2018-12-10 14:51:02 +01:00
|
|
|
T get()
|
|
|
|
{
|
|
|
|
T ret;
|
|
|
|
if(d_mdbval.mv_size != sizeof(T))
|
2022-01-22 18:59:54 +01:00
|
|
|
throw LMDBError("MDB data has wrong length for type");
|
2018-12-10 14:51:02 +01:00
|
|
|
|
|
|
|
memcpy(&ret, d_mdbval.mv_data, sizeof(T));
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class T,
|
|
|
|
typename std::enable_if<std::is_class<T>::value,T>::type* = nullptr>
|
2019-01-04 20:51:04 +01:00
|
|
|
T get() const;
|
2018-12-10 14:51:02 +01:00
|
|
|
|
|
|
|
template<class T>
|
2019-01-04 20:51:04 +01:00
|
|
|
T get_struct() const
|
2018-12-10 14:51:02 +01:00
|
|
|
{
|
|
|
|
T ret;
|
|
|
|
if(d_mdbval.mv_size != sizeof(T))
|
2022-01-22 18:59:54 +01:00
|
|
|
throw LMDBError("MDB data has wrong length for type");
|
2018-12-10 14:51:02 +01:00
|
|
|
|
|
|
|
memcpy(&ret, d_mdbval.mv_data, sizeof(T));
|
|
|
|
return ret;
|
|
|
|
}
|
2019-06-24 11:16:03 +02:00
|
|
|
|
|
|
|
template<class T>
|
|
|
|
const T* get_struct_ptr() const
|
|
|
|
{
|
|
|
|
if(d_mdbval.mv_size != sizeof(T))
|
2022-01-22 18:59:54 +01:00
|
|
|
throw LMDBError("MDB data has wrong length for type");
|
2019-06-24 11:16:03 +02:00
|
|
|
|
|
|
|
return reinterpret_cast<const T*>(d_mdbval.mv_data);
|
|
|
|
}
|
|
|
|
|
2018-12-10 14:51:02 +01:00
|
|
|
|
|
|
|
MDB_val d_mdbval;
|
|
|
|
};
|
|
|
|
|
2019-01-04 20:51:04 +01:00
|
|
|
template<> inline std::string MDBOutVal::get<std::string>() const
|
2018-12-10 14:51:02 +01:00
|
|
|
{
|
2021-12-05 19:28:08 +01:00
|
|
|
return std::string(static_cast<char*>(d_mdbval.mv_data), d_mdbval.mv_size);
|
2018-12-10 14:51:02 +01:00
|
|
|
}
|
|
|
|
|
2019-01-04 20:51:04 +01:00
|
|
|
template<> inline string_view MDBOutVal::get<string_view>() const
|
2018-12-10 14:51:02 +01:00
|
|
|
{
|
2021-12-05 19:28:08 +01:00
|
|
|
return string_view(static_cast<char*>(d_mdbval.mv_data), d_mdbval.mv_size);
|
2018-12-10 14:51:02 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
class MDBInVal
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
MDBInVal(const MDBOutVal& rhs)
|
|
|
|
{
|
|
|
|
d_mdbval = rhs.d_mdbval;
|
|
|
|
}
|
2018-12-15 21:06:47 +01:00
|
|
|
|
2018-12-10 14:51:02 +01:00
|
|
|
template <class T,
|
|
|
|
typename std::enable_if<std::is_arithmetic<T>::value,
|
|
|
|
T>::type* = nullptr>
|
|
|
|
MDBInVal(T i)
|
|
|
|
{
|
|
|
|
memcpy(&d_memory[0], &i, sizeof(i));
|
|
|
|
d_mdbval.mv_size = sizeof(T);
|
|
|
|
d_mdbval.mv_data = d_memory;;
|
|
|
|
}
|
|
|
|
|
|
|
|
MDBInVal(const char* s)
|
|
|
|
{
|
|
|
|
d_mdbval.mv_size = strlen(s);
|
2021-12-05 19:28:08 +01:00
|
|
|
d_mdbval.mv_data = static_cast<void*>(const_cast<char*>(s));
|
2018-12-10 14:51:02 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
MDBInVal(const string_view& v)
|
|
|
|
{
|
|
|
|
d_mdbval.mv_size = v.size();
|
2021-12-05 19:28:08 +01:00
|
|
|
d_mdbval.mv_data = static_cast<void*>(const_cast<char*>(v.data()));
|
2018-12-10 14:51:02 +01:00
|
|
|
}
|
|
|
|
|
2018-12-10 22:08:49 +01:00
|
|
|
MDBInVal(const std::string& v)
|
2018-12-10 15:02:36 +01:00
|
|
|
{
|
|
|
|
d_mdbval.mv_size = v.size();
|
2021-12-05 19:28:08 +01:00
|
|
|
d_mdbval.mv_data = static_cast<void*>(const_cast<char*>(v.data()));
|
2018-12-10 15:02:36 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-12-10 14:51:02 +01:00
|
|
|
template<typename T>
|
|
|
|
static MDBInVal fromStruct(const T& t)
|
|
|
|
{
|
|
|
|
MDBInVal ret;
|
|
|
|
ret.d_mdbval.mv_size = sizeof(T);
|
2021-12-05 19:28:08 +01:00
|
|
|
ret.d_mdbval.mv_data = static_cast<void*>(const_cast<char*>(&t[0]));
|
2018-12-10 14:51:02 +01:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
operator MDB_val&()
|
|
|
|
{
|
|
|
|
return d_mdbval;
|
|
|
|
}
|
|
|
|
MDB_val d_mdbval;
|
|
|
|
private:
|
|
|
|
MDBInVal(){}
|
|
|
|
char d_memory[sizeof(double)];
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2018-12-07 13:52:17 +01:00
|
|
|
class MDBROCursor;
|
|
|
|
|
2019-10-26 11:42:38 +02:00
|
|
|
class MDBROTransactionImpl
|
2018-12-07 13:52:17 +01:00
|
|
|
{
|
2019-10-24 17:56:45 +02:00
|
|
|
protected:
|
2019-10-26 11:42:38 +02:00
|
|
|
MDBROTransactionImpl(MDBEnv *parent, MDB_txn *txn);
|
2019-10-24 17:56:45 +02:00
|
|
|
|
|
|
|
private:
|
2021-12-05 19:28:08 +01:00
|
|
|
static MDB_txn *openROTransaction(MDBEnv *env, MDB_txn *parent, unsigned int flags=0);
|
2019-10-24 17:56:45 +02:00
|
|
|
|
|
|
|
MDBEnv* d_parent;
|
2019-10-26 11:42:38 +02:00
|
|
|
std::vector<MDBROCursor*> d_cursors;
|
2019-10-24 17:56:45 +02:00
|
|
|
|
|
|
|
protected:
|
|
|
|
MDB_txn* d_txn;
|
|
|
|
|
|
|
|
void closeROCursors();
|
|
|
|
|
2018-12-07 13:52:17 +01:00
|
|
|
public:
|
2021-12-05 19:28:08 +01:00
|
|
|
explicit MDBROTransactionImpl(MDBEnv* parent, unsigned int flags=0);
|
2018-12-07 13:52:17 +01:00
|
|
|
|
2019-10-26 11:42:38 +02:00
|
|
|
MDBROTransactionImpl(const MDBROTransactionImpl& src) = delete;
|
|
|
|
MDBROTransactionImpl &operator=(const MDBROTransactionImpl& src) = delete;
|
2018-12-07 13:52:17 +01:00
|
|
|
|
2019-10-26 11:42:38 +02:00
|
|
|
// The move constructor/operator cannot be made safe due to Object Slicing with MDBRWTransaction.
|
|
|
|
MDBROTransactionImpl(MDBROTransactionImpl&& rhs) = delete;
|
|
|
|
MDBROTransactionImpl &operator=(MDBROTransactionImpl &&rhs) = delete;
|
2018-12-07 13:52:17 +01:00
|
|
|
|
2019-10-26 11:42:38 +02:00
|
|
|
virtual ~MDBROTransactionImpl();
|
2019-10-24 17:56:45 +02:00
|
|
|
|
|
|
|
virtual void abort();
|
|
|
|
virtual void commit();
|
2018-12-07 13:52:17 +01:00
|
|
|
|
2018-12-10 14:51:02 +01:00
|
|
|
int get(MDB_dbi dbi, const MDBInVal& key, MDBOutVal& val)
|
2018-12-07 13:52:17 +01:00
|
|
|
{
|
2018-12-08 14:08:26 +01:00
|
|
|
if(!d_txn)
|
2022-01-22 18:59:54 +01:00
|
|
|
throw LMDBError("Attempt to use a closed RO transaction for get");
|
2018-12-08 14:08:26 +01:00
|
|
|
|
2022-01-22 18:59:54 +01:00
|
|
|
const auto rc = mdb_get(d_txn, dbi, const_cast<MDB_val*>(&key.d_mdbval),
|
2022-01-22 19:06:59 +01:00
|
|
|
&val.d_mdbval);
|
2018-12-08 20:58:19 +01:00
|
|
|
if(rc && rc != MDB_NOTFOUND)
|
2022-01-22 18:59:54 +01:00
|
|
|
throw LMDBError("Getting data: ", rc);
|
2018-12-08 20:58:19 +01:00
|
|
|
|
|
|
|
return rc;
|
2018-12-07 13:52:17 +01:00
|
|
|
}
|
|
|
|
|
2018-12-10 14:51:02 +01:00
|
|
|
int get(MDB_dbi dbi, const MDBInVal& key, string_view& val)
|
|
|
|
{
|
|
|
|
MDBOutVal out;
|
|
|
|
int rc = get(dbi, key, out);
|
|
|
|
if(!rc)
|
2018-12-10 22:08:49 +01:00
|
|
|
val = out.get<string_view>();
|
2018-12-10 14:51:02 +01:00
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-12-07 13:52:17 +01:00
|
|
|
// this is something you can do, readonly
|
2021-12-05 19:28:08 +01:00
|
|
|
MDBDbi openDB(string_view dbname, unsigned int flags)
|
2018-12-07 13:52:17 +01:00
|
|
|
{
|
2018-12-27 17:49:41 +01:00
|
|
|
return MDBDbi( d_parent->d_env, d_txn, dbname, flags);
|
2018-12-07 13:52:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
MDBROCursor getCursor(const MDBDbi&);
|
2019-10-24 17:56:45 +02:00
|
|
|
MDBROCursor getROCursor(const MDBDbi&);
|
2018-12-07 14:16:21 +01:00
|
|
|
|
2019-10-24 17:56:45 +02:00
|
|
|
operator MDB_txn*()
|
2018-12-07 13:52:17 +01:00
|
|
|
{
|
2019-10-24 17:56:45 +02:00
|
|
|
return d_txn;
|
2018-12-07 13:52:17 +01:00
|
|
|
}
|
|
|
|
|
2019-10-24 17:56:45 +02:00
|
|
|
inline operator bool() const {
|
2018-12-07 13:52:17 +01:00
|
|
|
return d_txn;
|
|
|
|
}
|
2019-10-24 17:56:45 +02:00
|
|
|
|
|
|
|
inline MDBEnv &environment()
|
|
|
|
{
|
|
|
|
return *d_parent;
|
|
|
|
}
|
2018-12-07 13:52:17 +01:00
|
|
|
};
|
|
|
|
|
2022-01-22 18:18:15 +01:00
|
|
|
/*!
|
|
|
|
* \brief The MDBGenCursor class represents a MDB_cursor handle.
|
|
|
|
* \remarks
|
|
|
|
* - A cursor in a read-only transaction must be closed explicitly, before or after its transaction ends.
|
|
|
|
* It can be reused with mdb_cursor_renew() before finally closing it.
|
|
|
|
* - "If the parent transaction commits, the cursor must not be used again."
|
|
|
|
*/
|
2019-10-24 17:56:45 +02:00
|
|
|
template<class Transaction, class T>
|
2019-01-09 09:58:04 +01:00
|
|
|
class MDBGenCursor
|
2018-12-07 13:52:17 +01:00
|
|
|
{
|
2019-10-24 17:56:45 +02:00
|
|
|
private:
|
|
|
|
std::vector<T*> *d_registry;
|
|
|
|
MDB_cursor* d_cursor;
|
|
|
|
|
|
|
|
public:
|
2019-10-26 11:42:38 +02:00
|
|
|
MDBGenCursor():
|
|
|
|
d_registry(nullptr),
|
|
|
|
d_cursor(nullptr)
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2019-10-24 17:56:45 +02:00
|
|
|
MDBGenCursor(std::vector<T*> ®istry, MDB_cursor *cursor):
|
|
|
|
d_registry(®istry),
|
|
|
|
d_cursor(cursor)
|
|
|
|
{
|
|
|
|
registry.emplace_back(static_cast<T*>(this));
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
void move_from(MDBGenCursor *src)
|
|
|
|
{
|
2019-10-26 11:42:38 +02:00
|
|
|
if (!d_registry) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-10-24 17:56:45 +02:00
|
|
|
auto iter = std::find(d_registry->begin(),
|
|
|
|
d_registry->end(),
|
|
|
|
src);
|
|
|
|
if (iter != d_registry->end()) {
|
|
|
|
*iter = static_cast<T*>(this);
|
|
|
|
} else {
|
|
|
|
d_registry->emplace_back(static_cast<T*>(this));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public:
|
|
|
|
MDBGenCursor(const MDBGenCursor &src) = delete;
|
|
|
|
|
|
|
|
MDBGenCursor(MDBGenCursor &&src) noexcept:
|
|
|
|
d_registry(src.d_registry),
|
|
|
|
d_cursor(src.d_cursor)
|
|
|
|
{
|
|
|
|
move_from(&src);
|
|
|
|
src.d_registry = nullptr;
|
|
|
|
src.d_cursor = nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
MDBGenCursor &operator=(const MDBGenCursor &src) = delete;
|
|
|
|
|
|
|
|
MDBGenCursor &operator=(MDBGenCursor &&src) noexcept
|
|
|
|
{
|
|
|
|
d_registry = src.d_registry;
|
|
|
|
d_cursor = src.d_cursor;
|
|
|
|
move_from(&src);
|
|
|
|
src.d_registry = nullptr;
|
|
|
|
src.d_cursor = nullptr;
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
~MDBGenCursor()
|
|
|
|
{
|
|
|
|
close();
|
|
|
|
}
|
|
|
|
|
2018-12-07 13:52:17 +01:00
|
|
|
public:
|
2018-12-10 14:51:02 +01:00
|
|
|
int get(MDBOutVal& key, MDBOutVal& data, MDB_cursor_op op)
|
2018-12-07 13:52:17 +01:00
|
|
|
{
|
2022-01-22 18:59:54 +01:00
|
|
|
const auto rc = mdb_cursor_get(d_cursor, &key.d_mdbval, &data.d_mdbval, op);
|
2019-01-05 14:38:52 +01:00
|
|
|
if(rc && rc != MDB_NOTFOUND)
|
2022-01-22 18:59:54 +01:00
|
|
|
throw LMDBError("Unable to get from cursor: ", rc);
|
2019-01-05 14:38:52 +01:00
|
|
|
return rc;
|
2018-12-07 13:52:17 +01:00
|
|
|
}
|
2018-12-08 14:08:26 +01:00
|
|
|
|
2018-12-13 23:16:46 +01:00
|
|
|
int find(const MDBInVal& in, MDBOutVal& key, MDBOutVal& data)
|
|
|
|
{
|
|
|
|
key.d_mdbval = in.d_mdbval;
|
2022-01-22 18:59:54 +01:00
|
|
|
const auto rc = mdb_cursor_get(d_cursor, const_cast<MDB_val*>(&key.d_mdbval), &data.d_mdbval, MDB_SET);
|
2019-01-05 14:38:52 +01:00
|
|
|
if(rc && rc != MDB_NOTFOUND)
|
2022-01-22 18:59:54 +01:00
|
|
|
throw LMDBError("Unable to find from cursor: ", rc);
|
2019-01-05 14:38:52 +01:00
|
|
|
return rc;
|
|
|
|
}
|
2019-01-09 09:58:04 +01:00
|
|
|
|
2019-01-05 14:38:52 +01:00
|
|
|
int lower_bound(const MDBInVal& in, MDBOutVal& key, MDBOutVal& data)
|
|
|
|
{
|
|
|
|
key.d_mdbval = in.d_mdbval;
|
|
|
|
|
2022-01-22 18:59:54 +01:00
|
|
|
const auto rc = mdb_cursor_get(d_cursor, const_cast<MDB_val*>(&key.d_mdbval), &data.d_mdbval, MDB_SET_RANGE);
|
2019-01-05 14:38:52 +01:00
|
|
|
if(rc && rc != MDB_NOTFOUND)
|
2022-01-22 18:59:54 +01:00
|
|
|
throw LMDBError("Unable to lower_bound from cursor: ", rc);
|
2019-01-05 14:38:52 +01:00
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2019-01-09 09:58:04 +01:00
|
|
|
|
|
|
|
int nextprev(MDBOutVal& key, MDBOutVal& data, MDB_cursor_op op)
|
|
|
|
{
|
2022-01-22 19:06:59 +01:00
|
|
|
const auto rc = mdb_cursor_get(d_cursor, &key.d_mdbval, &data.d_mdbval, op);
|
2019-01-09 09:58:04 +01:00
|
|
|
if(rc && rc != MDB_NOTFOUND)
|
2022-01-22 18:59:54 +01:00
|
|
|
throw LMDBError("Unable to prevnext from cursor: ", rc);
|
2019-01-09 09:58:04 +01:00
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2019-01-05 14:38:52 +01:00
|
|
|
int next(MDBOutVal& key, MDBOutVal& data)
|
|
|
|
{
|
2019-01-09 09:58:04 +01:00
|
|
|
return nextprev(key, data, MDB_NEXT);
|
|
|
|
}
|
|
|
|
|
|
|
|
int prev(MDBOutVal& key, MDBOutVal& data)
|
|
|
|
{
|
|
|
|
return nextprev(key, data, MDB_PREV);
|
|
|
|
}
|
|
|
|
|
|
|
|
int currentlast(MDBOutVal& key, MDBOutVal& data, MDB_cursor_op op)
|
|
|
|
{
|
2022-01-22 19:06:59 +01:00
|
|
|
const auto rc = mdb_cursor_get(d_cursor, &key.d_mdbval, &data.d_mdbval, op);
|
2019-01-05 14:38:52 +01:00
|
|
|
if(rc && rc != MDB_NOTFOUND)
|
2022-01-22 18:59:54 +01:00
|
|
|
throw LMDBError("Unable to next from cursor: ", rc);
|
2019-01-05 14:38:52 +01:00
|
|
|
return rc;
|
2018-12-13 23:16:46 +01:00
|
|
|
}
|
2019-01-09 09:58:04 +01:00
|
|
|
|
|
|
|
int current(MDBOutVal& key, MDBOutVal& data)
|
|
|
|
{
|
|
|
|
return currentlast(key, data, MDB_GET_CURRENT);
|
|
|
|
}
|
|
|
|
int last(MDBOutVal& key, MDBOutVal& data)
|
|
|
|
{
|
|
|
|
return currentlast(key, data, MDB_LAST);
|
|
|
|
}
|
2019-01-09 10:30:59 +01:00
|
|
|
int first(MDBOutVal& key, MDBOutVal& data)
|
|
|
|
{
|
|
|
|
return currentlast(key, data, MDB_FIRST);
|
|
|
|
}
|
2019-01-09 09:58:04 +01:00
|
|
|
|
2019-10-24 17:56:45 +02:00
|
|
|
operator MDB_cursor*()
|
2019-01-09 09:58:04 +01:00
|
|
|
{
|
|
|
|
return d_cursor;
|
|
|
|
}
|
|
|
|
|
2019-10-24 17:56:45 +02:00
|
|
|
operator bool() const
|
2019-01-09 09:58:04 +01:00
|
|
|
{
|
2019-10-24 17:56:45 +02:00
|
|
|
return d_cursor;
|
2019-01-09 09:58:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void close()
|
|
|
|
{
|
2019-10-24 17:56:45 +02:00
|
|
|
if (d_registry) {
|
|
|
|
auto iter = std::find(d_registry->begin(),
|
|
|
|
d_registry->end(),
|
|
|
|
static_cast<T*>(this));
|
|
|
|
if (iter != d_registry->end()) {
|
|
|
|
d_registry->erase(iter);
|
|
|
|
}
|
|
|
|
d_registry = nullptr;
|
|
|
|
}
|
|
|
|
if (d_cursor) {
|
2019-01-09 09:58:04 +01:00
|
|
|
mdb_cursor_close(d_cursor);
|
2019-10-24 17:56:45 +02:00
|
|
|
d_cursor = nullptr;
|
|
|
|
}
|
2019-01-09 09:58:04 +01:00
|
|
|
}
|
2019-10-24 17:56:45 +02:00
|
|
|
};
|
|
|
|
|
2019-10-26 11:42:38 +02:00
|
|
|
class MDBROCursor : public MDBGenCursor<MDBROTransactionImpl, MDBROCursor>
|
2019-10-24 17:56:45 +02:00
|
|
|
{
|
|
|
|
public:
|
2019-10-26 11:42:38 +02:00
|
|
|
MDBROCursor() = default;
|
|
|
|
using MDBGenCursor<MDBROTransactionImpl, MDBROCursor>::MDBGenCursor;
|
2019-10-24 17:56:45 +02:00
|
|
|
MDBROCursor(const MDBROCursor &src) = delete;
|
|
|
|
MDBROCursor(MDBROCursor &&src) = default;
|
|
|
|
MDBROCursor &operator=(const MDBROCursor &src) = delete;
|
|
|
|
MDBROCursor &operator=(MDBROCursor &&src) = default;
|
|
|
|
~MDBROCursor() = default;
|
2019-01-09 09:58:04 +01:00
|
|
|
|
2018-12-07 13:52:17 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
class MDBRWCursor;
|
|
|
|
|
2019-10-26 11:42:38 +02:00
|
|
|
class MDBRWTransactionImpl: public MDBROTransactionImpl
|
2018-12-07 13:52:17 +01:00
|
|
|
{
|
2019-10-26 12:07:03 +02:00
|
|
|
protected:
|
|
|
|
MDBRWTransactionImpl(MDBEnv* parent, MDB_txn* txn);
|
|
|
|
|
2019-10-24 17:56:45 +02:00
|
|
|
private:
|
2021-12-05 19:28:08 +01:00
|
|
|
static MDB_txn *openRWTransaction(MDBEnv* env, MDB_txn *parent, unsigned int flags);
|
2019-10-24 17:56:45 +02:00
|
|
|
|
|
|
|
private:
|
2019-10-26 11:42:38 +02:00
|
|
|
std::vector<MDBRWCursor*> d_rw_cursors;
|
2019-10-24 17:56:45 +02:00
|
|
|
|
|
|
|
void closeRWCursors();
|
|
|
|
inline void closeRORWCursors() {
|
|
|
|
closeROCursors();
|
|
|
|
closeRWCursors();
|
|
|
|
}
|
|
|
|
|
2018-12-07 13:52:17 +01:00
|
|
|
public:
|
2021-12-05 19:28:08 +01:00
|
|
|
explicit MDBRWTransactionImpl(MDBEnv* parent, unsigned int flags=0);
|
2019-10-24 17:56:45 +02:00
|
|
|
|
2019-10-26 11:42:38 +02:00
|
|
|
MDBRWTransactionImpl(const MDBRWTransactionImpl& rhs) = delete;
|
|
|
|
MDBRWTransactionImpl(MDBRWTransactionImpl&& rhs) = delete;
|
|
|
|
MDBRWTransactionImpl &operator=(const MDBRWTransactionImpl& rhs) = delete;
|
|
|
|
MDBRWTransactionImpl &operator=(MDBRWTransactionImpl&& rhs) = delete;
|
2018-12-07 13:52:17 +01:00
|
|
|
|
2019-10-26 11:42:38 +02:00
|
|
|
~MDBRWTransactionImpl() override;
|
2018-12-07 13:52:17 +01:00
|
|
|
|
2019-10-24 17:56:45 +02:00
|
|
|
void commit() override;
|
|
|
|
void abort() override;
|
2018-12-07 13:52:17 +01:00
|
|
|
|
2018-12-09 14:37:08 +01:00
|
|
|
void clear(MDB_dbi dbi);
|
|
|
|
|
2021-12-05 19:28:08 +01:00
|
|
|
void put(MDB_dbi dbi, const MDBInVal& key, const MDBInVal& val, unsigned int flags=0)
|
2018-12-07 13:52:17 +01:00
|
|
|
{
|
2018-12-08 14:08:26 +01:00
|
|
|
if(!d_txn)
|
2022-01-22 18:59:54 +01:00
|
|
|
throw LMDBError("Attempt to use a closed RW transaction for put");
|
|
|
|
if(const auto rc = mdb_put(d_txn, dbi,
|
2018-12-10 15:02:36 +01:00
|
|
|
const_cast<MDB_val*>(&key.d_mdbval),
|
2022-01-22 18:59:54 +01:00
|
|
|
const_cast<MDB_val*>(&val.d_mdbval), flags))
|
|
|
|
throw LMDBError("Putting data: ", rc);
|
2018-12-07 13:52:17 +01:00
|
|
|
}
|
|
|
|
|
2018-12-13 23:16:46 +01:00
|
|
|
|
2018-12-14 23:00:44 +01:00
|
|
|
int del(MDBDbi& dbi, const MDBInVal& key, const MDBInVal& val)
|
2018-12-10 15:02:36 +01:00
|
|
|
{
|
2022-01-22 18:59:54 +01:00
|
|
|
const auto rc = mdb_del(d_txn, dbi, const_cast<MDB_val*>(&key.d_mdbval), const_cast<MDB_val*>(&val.d_mdbval));
|
2018-12-13 23:16:46 +01:00
|
|
|
if(rc && rc != MDB_NOTFOUND)
|
2022-01-22 18:59:54 +01:00
|
|
|
throw LMDBError("Deleting data: ", rc);
|
2018-12-13 23:16:46 +01:00
|
|
|
return rc;
|
2018-12-10 15:02:36 +01:00
|
|
|
}
|
2018-12-07 13:52:17 +01:00
|
|
|
|
2018-12-14 23:00:44 +01:00
|
|
|
int del(MDBDbi& dbi, const MDBInVal& key)
|
2018-12-07 13:52:17 +01:00
|
|
|
{
|
2022-01-22 18:59:54 +01:00
|
|
|
const auto rc = mdb_del(d_txn, dbi, const_cast<MDB_val*>(&key.d_mdbval), 0);
|
2018-12-07 13:52:17 +01:00
|
|
|
if(rc && rc != MDB_NOTFOUND)
|
2022-01-22 18:59:54 +01:00
|
|
|
throw LMDBError("Deleting data: ", rc);
|
2018-12-07 13:52:17 +01:00
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2018-12-10 14:51:02 +01:00
|
|
|
|
2018-12-14 23:00:44 +01:00
|
|
|
int get(MDBDbi& dbi, const MDBInVal& key, MDBOutVal& val)
|
2018-12-07 13:52:17 +01:00
|
|
|
{
|
2018-12-08 14:08:26 +01:00
|
|
|
if(!d_txn)
|
2022-01-22 18:59:54 +01:00
|
|
|
throw LMDBError("Attempt to use a closed RW transaction for get");
|
2018-12-08 14:08:26 +01:00
|
|
|
|
2022-01-22 18:59:54 +01:00
|
|
|
const auto rc = mdb_get(d_txn, dbi, const_cast<MDB_val*>(&key.d_mdbval),
|
2022-01-22 19:06:59 +01:00
|
|
|
&val.d_mdbval);
|
2018-12-08 20:58:19 +01:00
|
|
|
if(rc && rc != MDB_NOTFOUND)
|
2022-01-22 18:59:54 +01:00
|
|
|
throw LMDBError("Getting data: ", rc);
|
2018-12-08 20:58:19 +01:00
|
|
|
return rc;
|
2018-12-07 13:52:17 +01:00
|
|
|
}
|
|
|
|
|
2018-12-14 23:00:44 +01:00
|
|
|
int get(MDBDbi& dbi, const MDBInVal& key, string_view& val)
|
2018-12-10 14:51:02 +01:00
|
|
|
{
|
|
|
|
MDBOutVal out;
|
2022-01-22 18:59:54 +01:00
|
|
|
const auto rc = get(dbi, key, out);
|
2018-12-10 14:51:02 +01:00
|
|
|
if(!rc)
|
2018-12-10 22:08:49 +01:00
|
|
|
val = out.get<string_view>();
|
2018-12-10 14:51:02 +01:00
|
|
|
return rc;
|
|
|
|
}
|
2018-12-07 13:52:17 +01:00
|
|
|
|
2021-12-05 19:28:08 +01:00
|
|
|
MDBDbi openDB(string_view dbname, unsigned int flags)
|
2018-12-07 13:52:17 +01:00
|
|
|
{
|
2019-10-24 17:56:45 +02:00
|
|
|
return MDBDbi(environment().d_env, d_txn, dbname, flags);
|
2018-12-07 13:52:17 +01:00
|
|
|
}
|
|
|
|
|
2019-10-24 17:56:45 +02:00
|
|
|
MDBRWCursor getRWCursor(const MDBDbi&);
|
2018-12-07 13:52:17 +01:00
|
|
|
MDBRWCursor getCursor(const MDBDbi&);
|
2019-10-26 12:07:03 +02:00
|
|
|
|
|
|
|
MDBRWTransaction getRWTransaction();
|
|
|
|
MDBROTransaction getROTransaction();
|
2018-12-07 13:52:17 +01:00
|
|
|
};
|
|
|
|
|
2022-01-22 18:18:15 +01:00
|
|
|
/*!
|
|
|
|
* \brief The MDBRWCursor class implements RW operations based on MDBGenCursor.
|
|
|
|
* \remarks
|
|
|
|
* - "A cursor in a write-transaction can be closed before its transaction ends, and will otherwise
|
|
|
|
* be closed when its transaction ends." This is a problem for us since it may means we are closing
|
|
|
|
* the cursor twice, which is bad.
|
|
|
|
*/
|
2019-10-26 11:42:38 +02:00
|
|
|
class MDBRWCursor : public MDBGenCursor<MDBRWTransactionImpl, MDBRWCursor>
|
2018-12-07 13:52:17 +01:00
|
|
|
{
|
|
|
|
public:
|
2019-10-26 11:42:38 +02:00
|
|
|
MDBRWCursor() = default;
|
|
|
|
using MDBGenCursor<MDBRWTransactionImpl, MDBRWCursor>::MDBGenCursor;
|
|
|
|
MDBRWCursor(const MDBRWCursor &src) = delete;
|
2019-10-24 17:56:45 +02:00
|
|
|
MDBRWCursor(MDBRWCursor &&src) = default;
|
2019-10-26 11:42:38 +02:00
|
|
|
MDBRWCursor &operator=(const MDBRWCursor &src) = delete;
|
2019-10-24 17:56:45 +02:00
|
|
|
MDBRWCursor &operator=(MDBRWCursor &&src) = default;
|
|
|
|
~MDBRWCursor() = default;
|
2019-01-09 09:58:04 +01:00
|
|
|
|
|
|
|
void put(const MDBOutVal& key, const MDBInVal& data)
|
2018-12-07 13:52:17 +01:00
|
|
|
{
|
2022-01-22 18:59:54 +01:00
|
|
|
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);
|
2018-12-07 13:52:17 +01:00
|
|
|
}
|
|
|
|
|
2018-12-10 17:42:25 +01:00
|
|
|
|
2021-12-05 19:28:08 +01:00
|
|
|
int put(const MDBOutVal& key, const MDBOutVal& data, unsigned int flags=0)
|
2018-12-07 13:52:17 +01:00
|
|
|
{
|
2018-12-13 23:16:46 +01:00
|
|
|
// XXX check errors
|
2019-10-24 17:56:45 +02:00
|
|
|
return mdb_cursor_put(*this,
|
2018-12-10 15:02:36 +01:00
|
|
|
const_cast<MDB_val*>(&key.d_mdbval),
|
|
|
|
const_cast<MDB_val*>(&data.d_mdbval), flags);
|
2018-12-07 13:52:17 +01:00
|
|
|
}
|
|
|
|
|
2021-12-05 19:28:08 +01:00
|
|
|
int del(unsigned int flags=0)
|
2018-12-07 13:52:17 +01:00
|
|
|
{
|
2019-10-24 17:56:45 +02:00
|
|
|
return mdb_cursor_del(*this, flags);
|
2018-12-07 13:52:17 +01:00
|
|
|
}
|
2019-10-24 17:56:45 +02:00
|
|
|
|
2018-12-07 13:52:17 +01:00
|
|
|
};
|
|
|
|
|
2022-01-18 22:08:36 +01:00
|
|
|
}
|