2018-12-14 23:00:44 +01:00
|
|
|
#pragma once
|
2022-01-30 00:05:02 +01:00
|
|
|
|
|
|
|
#include "./lmdb-safe.hh"
|
2021-12-22 20:48:53 +01:00
|
|
|
|
2022-02-19 00:06:13 +01:00
|
|
|
#include <c++utilities/conversion/binaryconversion.h>
|
|
|
|
#include <c++utilities/conversion/conversionexception.h>
|
2022-01-31 22:23:58 +01:00
|
|
|
#include <c++utilities/conversion/stringbuilder.h>
|
|
|
|
|
2022-01-18 22:08:36 +01:00
|
|
|
namespace LMDBSafe {
|
|
|
|
|
2022-02-21 22:57:15 +01:00
|
|
|
/*!
|
|
|
|
* \brief The type used to store IDs. "0" indicates "no such ID".
|
|
|
|
*/
|
|
|
|
using IDType = std::uint32_t;
|
|
|
|
|
2022-02-21 23:46:32 +01:00
|
|
|
/*!
|
|
|
|
* \brief Converts \a t to an std::string.
|
|
|
|
*
|
|
|
|
* This is the serialization interface. You need to define this function for the
|
|
|
|
* types you'd like to store.
|
|
|
|
*/
|
2022-01-30 21:14:43 +01:00
|
|
|
template <typename T> std::string serToString(const T &t);
|
2018-12-14 23:00:44 +01:00
|
|
|
|
2022-02-21 23:46:32 +01:00
|
|
|
/*!
|
|
|
|
* \brief Initializes \a ret from \a str.
|
|
|
|
*
|
|
|
|
* This is the deserialization interface. You need to define this function for the
|
|
|
|
* types you'd like to store.
|
|
|
|
*/
|
2022-01-30 21:14:43 +01:00
|
|
|
template <typename T> void serFromString(string_view str, T &ret);
|
2018-12-27 17:49:41 +01:00
|
|
|
|
2022-02-21 23:46:32 +01:00
|
|
|
/// \cond
|
|
|
|
/// Define some "shortcuts" (to avoid full-blown serialization stuff for trivial cases):
|
2022-02-19 00:06:13 +01:00
|
|
|
template <> inline std::string serToString(const std::string_view &t)
|
|
|
|
{
|
|
|
|
return std::string(t);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <> inline std::string serToString(const std::string &t)
|
|
|
|
{
|
|
|
|
return t;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <> inline std::string serToString(const std::uint8_t &t)
|
|
|
|
{
|
|
|
|
return std::string(reinterpret_cast<const std::string::value_type *>(&t), sizeof(t));
|
|
|
|
}
|
|
|
|
|
|
|
|
template <> inline std::string serToString(const std::uint16_t &t)
|
|
|
|
{
|
|
|
|
auto str = std::string(sizeof(t), '\0');
|
|
|
|
CppUtilities::LE::getBytes(t, str.data());
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <> inline std::string serToString(const std::uint32_t &t)
|
|
|
|
{
|
|
|
|
auto str = std::string(sizeof(t), '\0');
|
|
|
|
CppUtilities::LE::getBytes(t, str.data());
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <> inline std::string serToString(const std::uint64_t &t)
|
|
|
|
{
|
|
|
|
auto str = std::string(sizeof(t), '\0');
|
|
|
|
CppUtilities::LE::getBytes(t, str.data());
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <> inline void serFromString<std::string>(string_view str, std::string &ret)
|
|
|
|
{
|
|
|
|
ret = std::string(str);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <> inline void serFromString<>(string_view str, std::uint8_t &ret)
|
|
|
|
{
|
|
|
|
if (str.size() != sizeof(ret)) {
|
|
|
|
throw CppUtilities::ConversionException(CppUtilities::argsToString("value not 8-bit, got ", str.size(), " bytes instead"));
|
|
|
|
}
|
|
|
|
ret = static_cast<std::uint8_t>(*str.data());
|
|
|
|
}
|
|
|
|
|
|
|
|
template <> inline void serFromString<>(string_view str, std::uint16_t &ret)
|
|
|
|
{
|
|
|
|
if (str.size() != sizeof(ret)) {
|
|
|
|
throw CppUtilities::ConversionException(CppUtilities::argsToString("value not 16-bit, got ", str.size(), " bytes instead"));
|
|
|
|
}
|
|
|
|
ret = CppUtilities::LE::toUInt16(str.data());
|
|
|
|
}
|
|
|
|
|
|
|
|
template <> inline void serFromString<>(string_view str, std::uint32_t &ret)
|
|
|
|
{
|
|
|
|
if (str.size() != sizeof(ret)) {
|
|
|
|
throw CppUtilities::ConversionException(CppUtilities::argsToString("value not 32-bit, got ", str.size(), " bytes instead"));
|
|
|
|
}
|
|
|
|
ret = CppUtilities::LE::toUInt32(str.data());
|
|
|
|
}
|
|
|
|
|
|
|
|
template <> inline void serFromString<>(string_view str, std::uint64_t &ret)
|
|
|
|
{
|
|
|
|
if (str.size() != sizeof(ret)) {
|
|
|
|
throw CppUtilities::ConversionException(CppUtilities::argsToString("value not 64-bit, got ", str.size(), " bytes instead"));
|
|
|
|
}
|
|
|
|
ret = CppUtilities::LE::toUInt64(str.data());
|
|
|
|
}
|
|
|
|
|
2022-02-21 23:46:32 +01:00
|
|
|
/// \endcond
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* \brief Converts \a t to an std::string.
|
|
|
|
*
|
|
|
|
* This is the serialization interface for keys. You need to define your this function
|
|
|
|
* for the types you'd like to use as keys.
|
|
|
|
*/
|
2022-01-30 21:14:43 +01:00
|
|
|
template <class T, class Enable> inline std::string keyConv(const T &t);
|
2018-12-27 17:49:41 +01:00
|
|
|
|
2022-01-30 21:14:43 +01:00
|
|
|
template <class T, typename std::enable_if<std::is_arithmetic<T>::value, T>::type * = nullptr> inline string_view keyConv(const T &t)
|
2018-12-27 17:49:41 +01:00
|
|
|
{
|
2022-01-30 21:14:43 +01:00
|
|
|
return string_view(reinterpret_cast<const char *>(&t), sizeof(t));
|
2018-12-27 17:49:41 +01:00
|
|
|
}
|
2019-01-05 14:40:28 +01:00
|
|
|
|
2022-02-21 23:46:32 +01:00
|
|
|
/// \cond
|
|
|
|
/// Define keyConv for trivial cases:
|
2022-01-30 21:14:43 +01:00
|
|
|
template <class T, typename std::enable_if<std::is_same<T, std::string>::value, T>::type * = nullptr> inline string_view keyConv(const T &t)
|
2018-12-28 16:45:44 +01:00
|
|
|
{
|
2022-01-30 21:14:43 +01:00
|
|
|
return t;
|
2018-12-28 16:45:44 +01:00
|
|
|
}
|
|
|
|
|
2022-01-30 21:14:43 +01:00
|
|
|
template <class T, typename std::enable_if<std::is_same<T, string_view>::value, T>::type * = nullptr> inline string_view keyConv(string_view t)
|
2022-01-25 23:25:27 +01:00
|
|
|
{
|
2022-01-30 21:14:43 +01:00
|
|
|
return t;
|
2022-01-25 23:25:27 +01:00
|
|
|
}
|
2022-02-21 23:46:32 +01:00
|
|
|
/// \endcond
|
2022-01-25 23:25:27 +01:00
|
|
|
|
2022-01-22 18:18:15 +01:00
|
|
|
/*!
|
|
|
|
* \brief The LMDBIndexOps struct implements index operations, but only the operations that
|
|
|
|
* are broadcast to all indexes.
|
|
|
|
*
|
|
|
|
* Specifically, to deal with databases with less than the maximum number of interfaces, this
|
|
|
|
* only includes calls that should be ignored for empty indexes.
|
|
|
|
*
|
|
|
|
* This class only needs methods that must happen for all indexes at once. So specifically, *not*
|
2022-02-21 23:46:32 +01:00
|
|
|
* size<t> or get<t>. People ask for those themselves, and should not do that on indexes that
|
2022-01-22 18:18:15 +01:00
|
|
|
* don't exist.
|
|
|
|
*/
|
2022-01-30 21:14:43 +01:00
|
|
|
template <class Class, typename Type, typename Parent> struct LMDB_SAFE_EXPORT LMDBIndexOps {
|
|
|
|
explicit LMDBIndexOps(Parent *parent)
|
|
|
|
: d_parent(parent)
|
|
|
|
{
|
|
|
|
}
|
2022-02-21 22:57:15 +01:00
|
|
|
void put(MDBRWTransaction &txn, const Class &t, IDType id, unsigned int flags = 0)
|
2022-01-30 21:14:43 +01:00
|
|
|
{
|
|
|
|
txn->put(d_idx, keyConv(d_parent->getMember(t)), id, flags);
|
2018-12-27 17:49:41 +01:00
|
|
|
}
|
2018-12-14 23:00:44 +01:00
|
|
|
|
2022-02-21 22:57:15 +01:00
|
|
|
void del(MDBRWTransaction &txn, const Class &t, IDType id)
|
2022-01-30 21:14:43 +01:00
|
|
|
{
|
|
|
|
if (const auto rc = txn->del(d_idx, keyConv(d_parent->getMember(t)), id)) {
|
|
|
|
throw LMDBError("Error deleting from index: ", rc);
|
|
|
|
}
|
2022-01-26 00:43:21 +01:00
|
|
|
}
|
2022-01-30 21:14:43 +01:00
|
|
|
|
|
|
|
void clear(MDBRWTransaction &txn)
|
|
|
|
{
|
|
|
|
if (const auto rc = mdb_drop(*txn, d_idx, 0)) {
|
|
|
|
throw LMDBError("Error clearing index: ", rc);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void openDB(std::shared_ptr<MDBEnv> &env, string_view str, unsigned int flags)
|
|
|
|
{
|
|
|
|
d_idx = env->openDB(str, flags);
|
|
|
|
}
|
|
|
|
MDBDbi d_idx;
|
|
|
|
Parent *d_parent;
|
2018-12-16 15:58:38 +01:00
|
|
|
};
|
2018-12-14 23:00:44 +01:00
|
|
|
|
2022-02-21 23:46:32 +01:00
|
|
|
/*!
|
|
|
|
* \brief The index_on struct is used to declare an index on a member variable of a particular type.
|
|
|
|
*/
|
2022-01-30 21:14:43 +01:00
|
|
|
template <class Class, typename Type, Type Class::*PtrToMember> struct index_on : LMDBIndexOps<Class, Type, index_on<Class, Type, PtrToMember>> {
|
|
|
|
index_on()
|
|
|
|
: LMDBIndexOps<Class, Type, index_on<Class, Type, PtrToMember>>(this)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
static Type getMember(const Class &c)
|
|
|
|
{
|
|
|
|
return c.*PtrToMember;
|
|
|
|
}
|
|
|
|
|
|
|
|
typedef Type type;
|
2018-12-16 15:58:38 +01:00
|
|
|
};
|
|
|
|
|
2022-02-21 23:46:32 +01:00
|
|
|
/*!
|
|
|
|
* \brief The index_on_function struct is used to declare an index which is dynamically computed via
|
|
|
|
* a function.
|
|
|
|
*/
|
2022-01-30 21:14:43 +01:00
|
|
|
template <class Class, typename Type, class Func> struct index_on_function : LMDBIndexOps<Class, Type, index_on_function<Class, Type, Func>> {
|
|
|
|
index_on_function()
|
|
|
|
: LMDBIndexOps<Class, Type, index_on_function<Class, Type, Func>>(this)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
static Type getMember(const Class &c)
|
|
|
|
{
|
|
|
|
Func f;
|
|
|
|
return f(c);
|
|
|
|
}
|
2018-12-14 23:00:44 +01:00
|
|
|
|
2022-01-30 21:14:43 +01:00
|
|
|
typedef Type type;
|
2018-12-14 23:00:44 +01:00
|
|
|
};
|
|
|
|
|
2022-01-31 22:23:58 +01:00
|
|
|
/*!
|
|
|
|
* \brief The TypedDBI class is the main class.
|
|
|
|
* \tparam T Specifies the type to store within the database.
|
|
|
|
* \tparam I Specifies an index, should be an instantiation of index_on.
|
2022-02-21 23:46:32 +01:00
|
|
|
*
|
|
|
|
* Open issues:
|
|
|
|
* - Perhaps use the separate index concept from multi_index.
|
2022-03-01 00:42:50 +01:00
|
|
|
* - Perhaps get either to be of same type so for(auto& a : x) works
|
2022-02-21 23:46:32 +01:00
|
|
|
* make it more value "like" with unique_ptr.
|
2022-01-31 22:23:58 +01:00
|
|
|
*/
|
|
|
|
template <typename T, typename... I> class LMDB_SAFE_EXPORT TypedDBI {
|
|
|
|
public:
|
|
|
|
// declare tuple for indexes
|
|
|
|
using tuple_t = std::tuple<I...>;
|
|
|
|
template <std::size_t N> using index_t = typename std::tuple_element_t<N, tuple_t>::type;
|
2018-12-14 23:46:46 +01:00
|
|
|
|
2022-01-31 22:23:58 +01:00
|
|
|
private:
|
|
|
|
tuple_t d_tuple;
|
|
|
|
|
2022-02-21 23:46:32 +01:00
|
|
|
/// \cond
|
2022-01-31 22:23:58 +01:00
|
|
|
template <class Tuple, std::size_t N> struct IndexIterator {
|
|
|
|
static inline void apply(Tuple &tuple, auto &&func)
|
|
|
|
{
|
|
|
|
IndexIterator<Tuple, N - 1>::apply(tuple, std::forward<decltype(func)>(func));
|
|
|
|
func(std::get<N - 1>(tuple));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
template <class Tuple> struct IndexIterator<Tuple, 1> {
|
|
|
|
static inline void apply(Tuple &tuple, auto &&func)
|
|
|
|
{
|
|
|
|
func(std::get<0>(tuple));
|
|
|
|
}
|
|
|
|
};
|
2022-02-10 22:37:32 +01:00
|
|
|
template <class Tuple> struct IndexIterator<Tuple, 0> {
|
|
|
|
static inline void apply(Tuple &tuple, auto &&func)
|
|
|
|
{
|
|
|
|
CPP_UTILITIES_UNUSED(tuple)
|
|
|
|
CPP_UTILITIES_UNUSED(func)
|
|
|
|
}
|
|
|
|
};
|
2022-01-31 22:23:58 +01:00
|
|
|
void forEachIndex(auto &&func)
|
2018-12-14 23:00:44 +01:00
|
|
|
{
|
2022-01-31 22:23:58 +01:00
|
|
|
IndexIterator<tuple_t, std::tuple_size_v<tuple_t>>::apply(d_tuple, std::forward<decltype(func)>(func));
|
2018-12-14 23:00:44 +01:00
|
|
|
}
|
2022-02-21 23:46:32 +01:00
|
|
|
/// \endcond
|
2018-12-14 23:00:44 +01:00
|
|
|
|
2022-01-30 21:14:43 +01:00
|
|
|
public:
|
|
|
|
TypedDBI(std::shared_ptr<MDBEnv> env, string_view name)
|
|
|
|
: d_env(env)
|
|
|
|
, d_name(name)
|
2018-12-14 23:00:44 +01:00
|
|
|
{
|
2022-01-30 21:14:43 +01:00
|
|
|
d_main = d_env->openDB(name, MDB_CREATE | MDB_INTEGERKEY);
|
2022-01-31 22:23:58 +01:00
|
|
|
std::size_t index = 0;
|
|
|
|
forEachIndex([&](auto &&i) { i.openDB(d_env, CppUtilities::argsToString(name, '_', index++), MDB_CREATE | MDB_DUPFIXED | MDB_DUPSORT); });
|
2018-12-14 23:00:44 +01:00
|
|
|
}
|
2018-12-15 17:53:29 +01:00
|
|
|
|
2022-02-21 23:46:32 +01:00
|
|
|
/*!
|
|
|
|
* \brief The ReadonlyOperations struct defines read-only operations.
|
|
|
|
*/
|
2022-01-30 21:14:43 +01:00
|
|
|
template <class Parent> struct ReadonlyOperations {
|
|
|
|
ReadonlyOperations(Parent &parent)
|
|
|
|
: d_parent(parent)
|
|
|
|
{
|
2018-12-28 16:33:20 +01:00
|
|
|
}
|
|
|
|
|
2022-01-30 21:14:43 +01:00
|
|
|
//! Number of entries in main database
|
2022-02-01 19:47:30 +01:00
|
|
|
std::size_t size()
|
2022-01-30 21:14:43 +01:00
|
|
|
{
|
|
|
|
MDB_stat stat;
|
|
|
|
mdb_stat(**d_parent.d_txn, d_parent.d_parent->d_main, &stat);
|
|
|
|
return stat.ms_entries;
|
2018-12-28 16:33:20 +01:00
|
|
|
}
|
2022-01-30 21:14:43 +01:00
|
|
|
|
|
|
|
//! Number of entries in the various indexes - should be the same
|
2022-02-01 19:47:30 +01:00
|
|
|
template <std::size_t N> std::size_t size()
|
2022-01-30 21:14:43 +01:00
|
|
|
{
|
|
|
|
MDB_stat stat;
|
|
|
|
mdb_stat(**d_parent.d_txn, std::get<N>(d_parent.d_parent->d_tuple).d_idx, &stat);
|
|
|
|
return stat.ms_entries;
|
2018-12-15 17:53:29 +01:00
|
|
|
}
|
2018-12-15 21:06:47 +01:00
|
|
|
|
2022-04-15 19:18:35 +02:00
|
|
|
/*!
|
|
|
|
* \brief Returns the highest ID or 0 if the database is empty.
|
|
|
|
*/
|
|
|
|
IDType maxID()
|
|
|
|
{
|
|
|
|
auto cursor = (*d_parent.d_txn)->getCursor(d_parent.d_parent->d_main);
|
|
|
|
MDBOutVal idval, maxcontent;
|
|
|
|
auto id = IDType(0);
|
|
|
|
if (!cursor.get(idval, maxcontent, MDB_LAST)) {
|
|
|
|
id = idval.get<IDType>();
|
|
|
|
}
|
|
|
|
return id;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* \brief Returns the next highest ID in the database.
|
|
|
|
* \remarks Never returns 0 so it can be used as special "no such ID" value.
|
|
|
|
* \throws Throws LMDBError when running out of IDs.
|
|
|
|
*/
|
|
|
|
IDType nextID()
|
|
|
|
{
|
|
|
|
const auto id = maxID();
|
|
|
|
if (id < std::numeric_limits<IDType>::max()) {
|
|
|
|
return id + 1;
|
|
|
|
}
|
|
|
|
throw LMDBError("Running out of IDs");
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* \brief Returns an ID not used in the database so far.
|
|
|
|
* \remarks
|
|
|
|
* - Lower IDs are reused but an extensive search for "gabs" is avoided.
|
|
|
|
* - Never returns 0 so it can be used as special "no such ID" value.
|
|
|
|
* \throws Throws LMDBError when running out of IDs.
|
|
|
|
*/
|
|
|
|
IDType newID()
|
|
|
|
{
|
|
|
|
auto cursor = (*d_parent.d_txn)->getCursor(d_parent.d_parent->d_main);
|
|
|
|
MDBOutVal idval, maxcontent;
|
|
|
|
auto id = IDType(1);
|
|
|
|
if (!cursor.get(idval, maxcontent, MDB_FIRST)) {
|
|
|
|
id = idval.get<IDType>();
|
|
|
|
}
|
|
|
|
if (id > 1) {
|
|
|
|
return id - 1;
|
|
|
|
}
|
|
|
|
if (!cursor.get(idval, maxcontent, MDB_LAST)) {
|
|
|
|
id = idval.get<IDType>();
|
|
|
|
} else {
|
|
|
|
id = 0;
|
|
|
|
}
|
|
|
|
if (id < std::numeric_limits<IDType>::max()) {
|
|
|
|
return id + 1;
|
|
|
|
}
|
|
|
|
throw LMDBError("Running out of IDs");
|
|
|
|
}
|
|
|
|
|
2022-01-30 21:14:43 +01:00
|
|
|
//! Get item with id, from main table directly
|
2022-02-21 22:57:15 +01:00
|
|
|
bool get(IDType id, T &t)
|
2022-01-30 21:14:43 +01:00
|
|
|
{
|
|
|
|
MDBOutVal data;
|
|
|
|
if ((*d_parent.d_txn)->get(d_parent.d_parent->d_main, id, data))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
serFromString(data.get<string_view>(), t);
|
|
|
|
return true;
|
2018-12-15 21:06:47 +01:00
|
|
|
}
|
2022-01-30 21:14:43 +01:00
|
|
|
|
|
|
|
//! Get item through index N, then via the main database
|
2022-02-21 22:57:15 +01:00
|
|
|
template <std::size_t N> IDType get(const index_t<N> &key, T &out)
|
2022-01-30 21:14:43 +01:00
|
|
|
{
|
|
|
|
MDBOutVal id;
|
|
|
|
if (!(*d_parent.d_txn)->get(std::get<N>(d_parent.d_parent->d_tuple).d_idx, keyConv(key), id)) {
|
2022-02-21 22:57:15 +01:00
|
|
|
if (get(id.get<IDType>(), out))
|
|
|
|
return id.get<IDType>();
|
2022-01-30 21:14:43 +01:00
|
|
|
}
|
|
|
|
return 0;
|
2018-12-15 17:53:29 +01:00
|
|
|
}
|
2022-01-30 21:14:43 +01:00
|
|
|
|
|
|
|
//! Cardinality of index N
|
2022-02-21 22:57:15 +01:00
|
|
|
template <std::size_t N> IDType cardinality()
|
2022-01-30 21:14:43 +01:00
|
|
|
{
|
|
|
|
auto cursor = (*d_parent.d_txn)->getCursor(std::get<N>(d_parent.d_parent->d_tuple).d_idx);
|
|
|
|
bool first = true;
|
|
|
|
MDBOutVal key, data;
|
2022-02-21 22:57:15 +01:00
|
|
|
IDType count = 0;
|
2022-01-30 21:14:43 +01:00
|
|
|
while (!cursor.get(key, data, first ? MDB_FIRST : MDB_NEXT_NODUP)) {
|
|
|
|
++count;
|
|
|
|
first = false;
|
|
|
|
}
|
|
|
|
return count;
|
2018-12-27 17:49:41 +01:00
|
|
|
}
|
2022-01-30 21:14:43 +01:00
|
|
|
|
2022-02-21 23:46:32 +01:00
|
|
|
/*!
|
|
|
|
* \brief The eiter_t struct is the end iterator.
|
|
|
|
*/
|
2022-01-30 21:14:43 +01:00
|
|
|
struct eiter_t {
|
|
|
|
};
|
|
|
|
|
2022-02-18 17:17:47 +01:00
|
|
|
//! Store the object as immediate member of iter_t (as opposed to using an std::unique_ptr or std::shared_ptr)
|
|
|
|
template <typename> struct DirectStorage {
|
|
|
|
};
|
|
|
|
|
2022-02-21 23:46:32 +01:00
|
|
|
/*!
|
|
|
|
* \brief The iter_t struct is the iterator type for walking through the database rows.
|
|
|
|
* \remarks
|
|
|
|
* - The iterator can be on the main database or on an index. It returns the data directly on
|
|
|
|
* the main database and indirectly when on an index.
|
|
|
|
* - An iterator can be limited to one key or iterate over the entire database.
|
|
|
|
* - The iter_t struct requires you to put the cursor in the right place first.
|
|
|
|
* - The object can be stored as direct member of iter_t or as std::unique_ptr or std::shared_ptr
|
|
|
|
* by specifying the corresponding template as \tp ElementType. The pointer can then be accessed
|
|
|
|
* via getPointer(). Note that the returned pointer object is re-used when the iterator is incremented
|
|
|
|
* or decremented unless the owned object is moved into another pointer object.
|
|
|
|
*/
|
2022-04-18 23:10:21 +02:00
|
|
|
template <template <typename...> class StorageType, typename ElementType = T> struct iter_t {
|
2022-02-19 00:10:17 +01:00
|
|
|
using UsingDirectStorage = CppUtilities::Traits::IsSpecializationOf<StorageType<ElementType>, DirectStorage>;
|
2022-02-18 17:17:47 +01:00
|
|
|
|
2022-01-30 21:14:43 +01:00
|
|
|
explicit iter_t(Parent *parent, typename Parent::cursor_t &&cursor, bool on_index, bool one_key, bool end = false)
|
|
|
|
: d_parent(parent)
|
|
|
|
, d_cursor(std::move(cursor))
|
2022-02-18 17:17:47 +01:00
|
|
|
, d_on_index(on_index) // is this an iterator on main database or on index?
|
|
|
|
, d_one_key(one_key) // should we stop at end of key? (equal range)
|
|
|
|
, d_end(end)
|
|
|
|
, d_deserialized(false)
|
2022-01-30 21:14:43 +01:00
|
|
|
{
|
|
|
|
if (d_end)
|
|
|
|
return;
|
2022-02-18 17:17:47 +01:00
|
|
|
if (d_cursor.get(d_key, d_id, MDB_GET_CURRENT))
|
2022-01-30 21:14:43 +01:00
|
|
|
d_end = true;
|
2022-02-18 17:17:47 +01:00
|
|
|
else if (d_on_index && (*d_parent->d_txn)->get(d_parent->d_parent->d_main, d_id, d_data))
|
|
|
|
throw LMDBError("Missing id in constructor");
|
2022-01-30 21:14:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
explicit iter_t(Parent *parent, typename Parent::cursor_t &&cursor, string_view prefix)
|
|
|
|
: d_parent(parent)
|
|
|
|
, d_cursor(std::move(cursor))
|
|
|
|
, d_prefix(prefix)
|
2022-02-21 23:46:32 +01:00
|
|
|
, d_on_index(true)
|
2022-02-18 17:17:47 +01:00
|
|
|
, d_one_key(false)
|
2022-01-30 21:14:43 +01:00
|
|
|
, d_end(false)
|
2022-02-18 17:17:47 +01:00
|
|
|
, d_deserialized(false)
|
2022-01-30 21:14:43 +01:00
|
|
|
{
|
2022-02-18 17:17:47 +01:00
|
|
|
if (d_cursor.get(d_key, d_id, MDB_GET_CURRENT))
|
2022-01-30 21:14:43 +01:00
|
|
|
d_end = true;
|
2022-02-18 17:17:47 +01:00
|
|
|
else if (d_on_index && (*d_parent->d_txn)->get(d_parent->d_parent->d_main, d_id, d_data))
|
|
|
|
throw LMDBError("Missing id in constructor");
|
2022-01-30 21:14:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void del()
|
|
|
|
{
|
|
|
|
d_cursor.del();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool operator!=(const eiter_t &) const
|
|
|
|
{
|
|
|
|
return !d_end;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool operator==(const eiter_t &) const
|
|
|
|
{
|
|
|
|
return d_end;
|
|
|
|
}
|
|
|
|
|
2022-02-18 17:17:47 +01:00
|
|
|
string_view getRawData()
|
|
|
|
{
|
|
|
|
return d_on_index ? d_data.get<string_view>() : d_id.get<string_view>();
|
|
|
|
}
|
|
|
|
|
2022-02-19 00:10:17 +01:00
|
|
|
StorageType<ElementType> &allocatePointer()
|
2022-02-18 17:17:47 +01:00
|
|
|
{
|
|
|
|
static_assert(!UsingDirectStorage::value, "Cannot call getPointer() when using direct storage.");
|
2022-02-19 00:10:17 +01:00
|
|
|
static_assert(CppUtilities::Traits::IsSpecializingAnyOf<StorageType<ElementType>, std::unique_ptr, std::shared_ptr>(),
|
|
|
|
"Pointer type not supported.");
|
2022-02-18 17:17:47 +01:00
|
|
|
if (d_t != nullptr) {
|
|
|
|
return d_t;
|
|
|
|
}
|
2022-02-19 00:10:17 +01:00
|
|
|
if constexpr (CppUtilities::Traits::IsSpecializationOf<StorageType<ElementType>, std::unique_ptr>()) {
|
2022-02-18 17:17:47 +01:00
|
|
|
return d_t = std::make_unique<T>();
|
2022-02-19 00:10:17 +01:00
|
|
|
} else if constexpr (CppUtilities::Traits::IsSpecializationOf<StorageType<ElementType>, std::shared_ptr>()) {
|
2022-02-18 17:17:47 +01:00
|
|
|
return d_t = std::make_shared<T>();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-19 00:10:17 +01:00
|
|
|
StorageType<ElementType> &getPointer()
|
2022-01-30 21:14:43 +01:00
|
|
|
{
|
2022-02-18 17:17:47 +01:00
|
|
|
static_assert(!UsingDirectStorage::value, "Cannot call getPointer() when using direct storage.");
|
|
|
|
if (!d_deserialized) {
|
|
|
|
allocatePointer();
|
|
|
|
serFromString(getRawData(), *d_t);
|
|
|
|
d_deserialized = true;
|
|
|
|
}
|
2022-01-30 21:14:43 +01:00
|
|
|
return d_t;
|
|
|
|
}
|
|
|
|
|
2022-02-19 00:10:17 +01:00
|
|
|
ElementType &derefValue()
|
2022-01-30 21:14:43 +01:00
|
|
|
{
|
2022-02-18 17:17:47 +01:00
|
|
|
if constexpr (UsingDirectStorage::value) {
|
|
|
|
return d_t;
|
|
|
|
} else {
|
|
|
|
allocatePointer();
|
|
|
|
return *d_t;
|
|
|
|
}
|
2022-01-30 21:14:43 +01:00
|
|
|
}
|
|
|
|
|
2022-02-19 00:10:17 +01:00
|
|
|
ElementType &value()
|
2022-01-30 21:14:43 +01:00
|
|
|
{
|
2022-02-18 17:17:47 +01:00
|
|
|
auto &res = derefValue();
|
|
|
|
if (!d_deserialized) {
|
|
|
|
serFromString(getRawData(), res);
|
|
|
|
d_deserialized = true;
|
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2022-02-19 00:10:17 +01:00
|
|
|
const ElementType &operator*()
|
2022-02-18 17:17:47 +01:00
|
|
|
{
|
|
|
|
return value();
|
|
|
|
}
|
|
|
|
|
2022-02-19 00:10:17 +01:00
|
|
|
const ElementType *operator->()
|
2022-02-18 17:17:47 +01:00
|
|
|
{
|
|
|
|
return &value();
|
2022-01-30 21:14:43 +01:00
|
|
|
}
|
|
|
|
|
2022-02-21 23:46:32 +01:00
|
|
|
//! Implements generic ++ or --.
|
2022-01-30 21:14:43 +01:00
|
|
|
iter_t &genoperator(MDB_cursor_op dupop, MDB_cursor_op op)
|
|
|
|
{
|
2022-02-18 17:17:47 +01:00
|
|
|
d_deserialized = false;
|
2022-01-30 21:14:43 +01:00
|
|
|
next:;
|
|
|
|
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 LMDBError("Unable to get in genoperator: ", rc);
|
2022-02-18 17:17:47 +01:00
|
|
|
} else if (!d_prefix.empty() && d_key.get<std::string_view>().rfind(d_prefix, 0) != 0) {
|
2022-01-30 21:14:43 +01:00
|
|
|
d_end = true;
|
|
|
|
} else {
|
2022-02-18 17:17:47 +01:00
|
|
|
if (d_on_index && (*d_parent->d_txn)->get(d_parent->d_parent->d_main, d_id, d_data))
|
|
|
|
throw LMDBError("Missing id field in genoperator");
|
|
|
|
if (filter && !filter(d_data))
|
|
|
|
goto next;
|
2022-01-30 21:14:43 +01:00
|
|
|
}
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
iter_t &operator++()
|
|
|
|
{
|
|
|
|
return genoperator(MDB_NEXT_DUP, MDB_NEXT);
|
|
|
|
}
|
|
|
|
iter_t &operator--()
|
|
|
|
{
|
|
|
|
return genoperator(MDB_PREV_DUP, MDB_PREV);
|
|
|
|
}
|
|
|
|
|
2022-02-21 23:46:32 +01:00
|
|
|
//! Returns the ID this iterator points to.
|
2022-02-21 22:57:15 +01:00
|
|
|
IDType getID()
|
2022-01-30 21:14:43 +01:00
|
|
|
{
|
2022-02-21 22:57:15 +01:00
|
|
|
return d_on_index ? d_id.get<IDType>() : d_key.get<IDType>();
|
2022-01-30 21:14:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
const MDBOutVal &getKey()
|
|
|
|
{
|
|
|
|
return d_key;
|
|
|
|
}
|
|
|
|
|
2022-02-21 23:46:32 +01:00
|
|
|
//! A filter to allow skipping rows by their raw value.
|
|
|
|
std::function<bool(const MDBOutVal &)> filter;
|
|
|
|
|
2022-02-18 17:17:47 +01:00
|
|
|
private:
|
2022-02-21 23:46:32 +01:00
|
|
|
//! The transaction the iterator is part of.
|
2022-01-30 21:14:43 +01:00
|
|
|
Parent *d_parent;
|
|
|
|
typename Parent::cursor_t d_cursor;
|
|
|
|
|
2022-02-18 17:17:47 +01:00
|
|
|
std::string d_prefix;
|
2022-01-30 21:14:43 +01:00
|
|
|
MDBOutVal d_key{ { 0, 0 } }, d_data{ { 0, 0 } }, d_id{ { 0, 0 } };
|
2022-02-21 23:46:32 +01:00
|
|
|
//! Whether it is an iterator on the main database or an index.
|
2022-01-30 21:14:43 +01:00
|
|
|
bool d_on_index;
|
|
|
|
bool d_one_key;
|
2022-02-18 17:17:47 +01:00
|
|
|
bool d_end;
|
|
|
|
bool d_deserialized;
|
2022-02-19 00:10:17 +01:00
|
|
|
CppUtilities::Traits::Conditional<UsingDirectStorage, ElementType, StorageType<ElementType>> d_t;
|
2022-01-30 21:14:43 +01:00
|
|
|
};
|
|
|
|
|
2022-02-18 17:17:47 +01:00
|
|
|
template <std::size_t N, template <typename> class StorageType = DirectStorage> iter_t<StorageType> genbegin(MDB_cursor_op op)
|
2022-01-30 21:14:43 +01:00
|
|
|
{
|
|
|
|
typename Parent::cursor_t cursor = (*d_parent.d_txn)->getCursor(std::get<N>(d_parent.d_parent->d_tuple).d_idx);
|
|
|
|
|
|
|
|
MDBOutVal out, id;
|
|
|
|
|
|
|
|
if (cursor.get(out, id, op)) {
|
|
|
|
// on_index, one_key, end
|
2022-02-18 17:17:47 +01:00
|
|
|
return iter_t<StorageType>{ &d_parent, std::move(cursor), true, false, true };
|
2022-01-30 21:14:43 +01:00
|
|
|
}
|
|
|
|
|
2022-02-18 17:17:47 +01:00
|
|
|
return iter_t<StorageType>{ &d_parent, std::move(cursor), true, false };
|
2022-01-30 21:14:43 +01:00
|
|
|
};
|
|
|
|
|
2022-02-18 17:17:47 +01:00
|
|
|
template <std::size_t N, template <typename> class StorageType = DirectStorage> iter_t<StorageType> begin()
|
2022-01-30 21:14:43 +01:00
|
|
|
{
|
2022-02-18 17:17:47 +01:00
|
|
|
return genbegin<N, StorageType>(MDB_FIRST);
|
2018-12-28 16:33:20 +01:00
|
|
|
}
|
2022-01-30 21:14:43 +01:00
|
|
|
|
2022-02-18 17:17:47 +01:00
|
|
|
template <std::size_t N, template <typename> class StorageType = DirectStorage> iter_t<StorageType> rbegin()
|
2022-01-30 21:14:43 +01:00
|
|
|
{
|
2022-02-18 17:17:47 +01:00
|
|
|
return genbegin<N, StorageType>(MDB_LAST);
|
2018-12-15 17:53:29 +01:00
|
|
|
}
|
2018-12-14 23:00:44 +01:00
|
|
|
|
2022-02-18 17:17:47 +01:00
|
|
|
template <template <typename> class StorageType = DirectStorage> iter_t<StorageType> begin()
|
2022-01-30 21:14:43 +01:00
|
|
|
{
|
|
|
|
typename Parent::cursor_t cursor = (*d_parent.d_txn)->getCursor(d_parent.d_parent->d_main);
|
2018-12-15 21:06:47 +01:00
|
|
|
|
2022-01-30 21:14:43 +01:00
|
|
|
MDBOutVal out, id;
|
2018-12-27 17:49:41 +01:00
|
|
|
|
2022-01-30 21:14:43 +01:00
|
|
|
if (cursor.get(out, id, MDB_FIRST)) {
|
|
|
|
// on_index, one_key, end
|
2022-02-18 17:17:47 +01:00
|
|
|
return iter_t<StorageType>{ &d_parent, std::move(cursor), false, false, true };
|
2022-01-30 21:14:43 +01:00
|
|
|
}
|
2018-12-27 17:49:41 +01:00
|
|
|
|
2022-02-18 17:17:47 +01:00
|
|
|
return iter_t<StorageType>{ &d_parent, std::move(cursor), false, false };
|
2022-02-25 00:11:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
template <template <typename> class StorageType = DirectStorage> iter_t<StorageType> rbegin()
|
|
|
|
{
|
|
|
|
typename Parent::cursor_t cursor = (*d_parent.d_txn)->getCursor(d_parent.d_parent->d_main);
|
|
|
|
|
|
|
|
MDBOutVal out, id;
|
|
|
|
|
|
|
|
if (cursor.get(out, id, MDB_LAST)) {
|
|
|
|
// on_index, one_key, end
|
|
|
|
return iter_t<StorageType>{ &d_parent, std::move(cursor), false, false, true };
|
|
|
|
}
|
|
|
|
|
|
|
|
return iter_t<StorageType>{ &d_parent, std::move(cursor), false, false };
|
|
|
|
}
|
|
|
|
|
|
|
|
template <template <typename> class StorageType = DirectStorage> iter_t<StorageType> lower_bound(IDType id)
|
|
|
|
{
|
|
|
|
typename Parent::cursor_t cursor = (*d_parent.d_txn)->getCursor(d_parent.d_parent->d_main);
|
|
|
|
|
|
|
|
MDBInVal in(id);
|
|
|
|
MDBOutVal out, id2;
|
|
|
|
out.d_mdbval = in.d_mdbval;
|
|
|
|
|
|
|
|
if (cursor.get(out, id2, MDB_SET_RANGE)) {
|
|
|
|
// on_index, one_key, end
|
|
|
|
return iter_t<StorageType>{ &d_parent, std::move(cursor), false, false, true };
|
|
|
|
}
|
|
|
|
|
|
|
|
return iter_t<StorageType>{ &d_parent, std::move(cursor), false, false };
|
|
|
|
}
|
2018-12-15 21:06:47 +01:00
|
|
|
|
2022-02-21 22:57:15 +01:00
|
|
|
template <std::size_t N, template <typename> class StorageType = DirectStorage> iter_t<DirectStorage, IDType> begin_idx()
|
2022-02-19 00:10:17 +01:00
|
|
|
{
|
|
|
|
typename Parent::cursor_t cursor = (*d_parent.d_txn)->getCursor(std::get<N>(d_parent.d_parent->d_tuple).d_idx);
|
|
|
|
|
|
|
|
MDBOutVal out, id;
|
|
|
|
|
|
|
|
if (cursor.get(out, id, MDB_FIRST)) {
|
|
|
|
// on_index, one_key, end
|
2022-02-21 22:57:15 +01:00
|
|
|
return iter_t<DirectStorage, IDType>{ &d_parent, std::move(cursor), false, false, true };
|
2022-02-19 00:10:17 +01:00
|
|
|
}
|
|
|
|
|
2022-02-21 22:57:15 +01:00
|
|
|
return iter_t<DirectStorage, IDType>{ &d_parent, std::move(cursor), false, false };
|
2022-02-19 00:10:17 +01:00
|
|
|
}
|
|
|
|
|
2022-01-30 21:14:43 +01:00
|
|
|
eiter_t end()
|
|
|
|
{
|
|
|
|
return eiter_t();
|
|
|
|
}
|
2018-12-16 20:28:49 +01:00
|
|
|
|
2022-01-30 21:14:43 +01:00
|
|
|
// basis for find, lower_bound
|
2022-02-18 17:17:47 +01:00
|
|
|
template <std::size_t N, template <typename> class StorageType>
|
|
|
|
iter_t<StorageType> genfind(const typename std::tuple_element<N, tuple_t>::type::type &key, MDB_cursor_op op)
|
2022-01-30 21:14:43 +01:00
|
|
|
{
|
|
|
|
typename Parent::cursor_t cursor = (*d_parent.d_txn)->getCursor(std::get<N>(d_parent.d_parent->d_tuple).d_idx);
|
2018-12-16 15:58:38 +01:00
|
|
|
|
2022-01-30 21:14:43 +01:00
|
|
|
const auto keystr = keyConv(key);
|
|
|
|
MDBInVal in(keystr);
|
|
|
|
MDBOutVal out, id;
|
|
|
|
out.d_mdbval = in.d_mdbval;
|
2018-12-16 20:28:49 +01:00
|
|
|
|
2022-01-30 21:14:43 +01:00
|
|
|
if (cursor.get(out, id, op)) {
|
|
|
|
// on_index, one_key, end
|
2022-02-18 17:17:47 +01:00
|
|
|
return iter_t<StorageType>{ &d_parent, std::move(cursor), true, false, true };
|
2022-01-30 21:14:43 +01:00
|
|
|
}
|
2018-12-16 20:28:49 +01:00
|
|
|
|
2022-02-18 17:17:47 +01:00
|
|
|
return iter_t<StorageType>{ &d_parent, std::move(cursor), true, false };
|
2022-01-30 21:14:43 +01:00
|
|
|
};
|
2018-12-16 20:28:49 +01:00
|
|
|
|
2022-02-18 17:17:47 +01:00
|
|
|
template <std::size_t N, template <typename> class StorageType = DirectStorage> iter_t<StorageType> find(const index_t<N> &key)
|
2022-01-30 21:14:43 +01:00
|
|
|
{
|
2022-02-18 17:17:47 +01:00
|
|
|
return genfind<N, StorageType>(key, MDB_SET);
|
2022-01-30 21:14:43 +01:00
|
|
|
}
|
2018-12-16 15:58:38 +01:00
|
|
|
|
2022-02-18 17:17:47 +01:00
|
|
|
template <std::size_t N, template <typename> class StorageType = DirectStorage> iter_t<StorageType> lower_bound(const index_t<N> &key)
|
2022-01-30 21:14:43 +01:00
|
|
|
{
|
2022-02-18 17:17:47 +01:00
|
|
|
return genfind<N, StorageType>(key, MDB_SET_RANGE);
|
2022-01-30 21:14:43 +01:00
|
|
|
}
|
|
|
|
|
2022-02-21 23:46:32 +01:00
|
|
|
//! Returns the range matching the specified \a key.
|
2022-02-18 17:17:47 +01:00
|
|
|
template <std::size_t N, template <typename> class StorageType = DirectStorage>
|
|
|
|
std::pair<iter_t<StorageType>, eiter_t> equal_range(const index_t<N> &key)
|
2022-01-30 21:14:43 +01:00
|
|
|
{
|
|
|
|
typename Parent::cursor_t cursor = (*d_parent.d_txn)->getCursor(std::get<N>(d_parent.d_parent->d_tuple).d_idx);
|
|
|
|
|
|
|
|
const auto keyString = keyConv(key);
|
|
|
|
MDBInVal in(keyString);
|
|
|
|
MDBOutVal out, id;
|
|
|
|
out.d_mdbval = in.d_mdbval;
|
|
|
|
|
|
|
|
if (cursor.get(out, id, MDB_SET)) {
|
|
|
|
// on_index, one_key, end
|
2022-02-18 17:17:47 +01:00
|
|
|
return { iter_t<StorageType>{ &d_parent, std::move(cursor), true, true, true }, eiter_t() };
|
2022-01-30 21:14:43 +01:00
|
|
|
}
|
|
|
|
|
2022-02-18 17:17:47 +01:00
|
|
|
return { iter_t<StorageType>{ &d_parent, std::move(cursor), true, true }, eiter_t() };
|
2022-01-30 21:14:43 +01:00
|
|
|
};
|
|
|
|
|
2022-02-21 23:46:32 +01:00
|
|
|
//! Returns the range where the key starts with the specified \a key.
|
2022-02-18 17:17:47 +01:00
|
|
|
template <std::size_t N, template <typename> class StorageType = DirectStorage>
|
|
|
|
std::pair<iter_t<StorageType>, eiter_t> prefix_range(const index_t<N> &key)
|
2022-01-30 21:14:43 +01:00
|
|
|
{
|
|
|
|
typename Parent::cursor_t cursor = (*d_parent.d_txn)->getCursor(std::get<N>(d_parent.d_parent->d_tuple).d_idx);
|
|
|
|
|
|
|
|
const auto keyString = keyConv(key);
|
|
|
|
MDBInVal in(keyString);
|
|
|
|
MDBOutVal out, id;
|
|
|
|
out.d_mdbval = in.d_mdbval;
|
|
|
|
|
|
|
|
if (cursor.get(out, id, MDB_SET_RANGE)) {
|
|
|
|
// on_index, one_key, end
|
2022-02-18 17:17:47 +01:00
|
|
|
return { iter_t<StorageType>{ &d_parent, std::move(cursor), true, true, true }, eiter_t() };
|
2022-01-30 21:14:43 +01:00
|
|
|
}
|
|
|
|
|
2022-02-18 17:17:47 +01:00
|
|
|
return { iter_t<StorageType>(&d_parent, std::move(cursor), keyString), eiter_t() };
|
2022-01-30 21:14:43 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
Parent &d_parent;
|
2018-12-28 16:33:20 +01:00
|
|
|
};
|
|
|
|
|
2022-02-21 23:46:32 +01:00
|
|
|
/*!
|
|
|
|
* \brief The ROTransaction class represents a read-only transaction.
|
|
|
|
*/
|
2022-01-30 21:14:43 +01:00
|
|
|
class LMDB_SAFE_EXPORT ROTransaction : public ReadonlyOperations<ROTransaction> {
|
|
|
|
public:
|
2022-02-01 22:02:25 +01:00
|
|
|
explicit ROTransaction()
|
|
|
|
: ReadonlyOperations<ROTransaction>(*this)
|
|
|
|
, d_parent(nullptr)
|
|
|
|
, d_txn(nullptr)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2022-01-30 21:14:43 +01:00
|
|
|
explicit ROTransaction(TypedDBI *parent)
|
|
|
|
: ReadonlyOperations<ROTransaction>(*this)
|
|
|
|
, d_parent(parent)
|
|
|
|
, d_txn(std::make_shared<MDBROTransaction>(d_parent->d_env->getROTransaction()))
|
|
|
|
{
|
|
|
|
}
|
2018-12-14 23:00:44 +01:00
|
|
|
|
2022-02-01 22:02:25 +01:00
|
|
|
explicit ROTransaction(TypedDBI *parent, const std::shared_ptr<MDBROTransaction> &txn)
|
2022-01-30 21:14:43 +01:00
|
|
|
: ReadonlyOperations<ROTransaction>(*this)
|
|
|
|
, d_parent(parent)
|
|
|
|
, d_txn(txn)
|
|
|
|
{
|
|
|
|
}
|
2018-12-17 14:56:01 +01:00
|
|
|
|
2022-01-30 21:14:43 +01:00
|
|
|
ROTransaction(ROTransaction &&rhs)
|
|
|
|
: ReadonlyOperations<ROTransaction>(*this)
|
|
|
|
, d_parent(rhs.d_parent)
|
|
|
|
, d_txn(std::move(rhs.d_txn))
|
2018-12-14 23:00:44 +01:00
|
|
|
|
2022-01-30 21:14:43 +01:00
|
|
|
{
|
2022-02-01 22:02:25 +01:00
|
|
|
rhs.d_parent = nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
ROTransaction &operator=(ROTransaction &&rhs)
|
|
|
|
{
|
|
|
|
d_parent = rhs.d_parent;
|
|
|
|
d_txn = std::move(rhs.d_txn);
|
|
|
|
rhs.d_parent = nullptr;
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
operator bool() const
|
|
|
|
{
|
|
|
|
return d_txn != nullptr;
|
2022-01-30 21:14:43 +01:00
|
|
|
}
|
2018-12-14 23:00:44 +01:00
|
|
|
|
2022-01-30 21:14:43 +01:00
|
|
|
std::shared_ptr<MDBROTransaction> getTransactionHandle()
|
|
|
|
{
|
|
|
|
return d_txn;
|
|
|
|
}
|
2018-12-17 14:56:01 +01:00
|
|
|
|
2022-02-01 22:02:25 +01:00
|
|
|
void close()
|
|
|
|
{
|
|
|
|
d_txn.reset();
|
|
|
|
}
|
|
|
|
|
2022-01-30 21:14:43 +01:00
|
|
|
typedef MDBROCursor cursor_t;
|
2018-12-14 23:00:44 +01:00
|
|
|
|
2022-01-30 21:14:43 +01:00
|
|
|
TypedDBI *d_parent;
|
|
|
|
std::shared_ptr<MDBROTransaction> d_txn;
|
|
|
|
};
|
|
|
|
|
2022-02-21 23:46:32 +01:00
|
|
|
/*!
|
|
|
|
* \brief The RWTransaction class represents a read-write transaction.
|
|
|
|
*/
|
2022-01-30 21:14:43 +01:00
|
|
|
class LMDB_SAFE_EXPORT RWTransaction : public ReadonlyOperations<RWTransaction> {
|
|
|
|
public:
|
2022-02-01 22:02:25 +01:00
|
|
|
explicit RWTransaction()
|
|
|
|
: ReadonlyOperations<RWTransaction>(*this)
|
|
|
|
, d_parent(nullptr)
|
|
|
|
, d_txn(nullptr)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2022-01-30 21:14:43 +01:00
|
|
|
explicit RWTransaction(TypedDBI *parent)
|
|
|
|
: ReadonlyOperations<RWTransaction>(*this)
|
|
|
|
, d_parent(parent)
|
|
|
|
{
|
|
|
|
d_txn = std::make_shared<MDBRWTransaction>(d_parent->d_env->getRWTransaction());
|
|
|
|
}
|
|
|
|
|
|
|
|
explicit RWTransaction(TypedDBI *parent, std::shared_ptr<MDBRWTransaction> txn)
|
|
|
|
: ReadonlyOperations<RWTransaction>(*this)
|
|
|
|
, d_parent(parent)
|
|
|
|
, d_txn(txn)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
RWTransaction(RWTransaction &&rhs)
|
|
|
|
: ReadonlyOperations<RWTransaction>(*this)
|
|
|
|
, d_parent(rhs.d_parent)
|
|
|
|
, d_txn(std::move(rhs.d_txn))
|
|
|
|
{
|
|
|
|
rhs.d_parent = 0;
|
|
|
|
}
|
|
|
|
|
2022-02-01 22:02:25 +01:00
|
|
|
RWTransaction &operator=(RWTransaction &&rhs)
|
|
|
|
{
|
|
|
|
d_parent = rhs.d_parent;
|
|
|
|
d_txn = std::move(rhs.d_txn);
|
|
|
|
rhs.d_parent = nullptr;
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
operator bool() const
|
|
|
|
{
|
|
|
|
return d_txn != nullptr;
|
|
|
|
}
|
|
|
|
|
2022-02-21 23:46:32 +01:00
|
|
|
//! Inserts something, with possibly a specific id.
|
2022-02-21 22:57:15 +01:00
|
|
|
IDType put(const T &t, IDType id = 0)
|
2022-01-30 21:14:43 +01:00
|
|
|
{
|
|
|
|
unsigned int flags = 0;
|
|
|
|
if (!id) {
|
2022-04-15 19:18:35 +02:00
|
|
|
id = this->nextID();
|
2022-01-30 21:14:43 +01:00
|
|
|
flags = MDB_APPEND;
|
|
|
|
}
|
|
|
|
(*d_txn)->put(d_parent->d_main, id, serToString(t), flags);
|
2022-01-31 22:23:58 +01:00
|
|
|
d_parent->forEachIndex([&](auto &&i) { i.put(*d_txn, t, id); });
|
2022-01-30 21:14:43 +01:00
|
|
|
return id;
|
|
|
|
}
|
2018-12-14 23:00:44 +01:00
|
|
|
|
2022-02-21 23:46:32 +01:00
|
|
|
//! Modifies an item "in place" updating indexes.
|
2022-03-07 23:30:41 +01:00
|
|
|
void modify(IDType id, const std::function<void(T &)> &func)
|
2022-01-30 21:14:43 +01:00
|
|
|
{
|
|
|
|
T t;
|
|
|
|
if (!this->get(id, t))
|
|
|
|
throw LMDBError("Could not modify id " + std::to_string(id));
|
|
|
|
func(t);
|
2018-12-14 23:00:44 +01:00
|
|
|
|
2022-01-30 21:14:43 +01:00
|
|
|
del(id); // this is the lazy way. We could test for changed index fields
|
|
|
|
put(t, id);
|
|
|
|
}
|
2018-12-15 17:53:29 +01:00
|
|
|
|
2022-02-21 23:46:32 +01:00
|
|
|
//! Deletes an item from the main database and from indexes.
|
2022-02-21 22:57:15 +01:00
|
|
|
void del(IDType id)
|
2022-01-30 21:14:43 +01:00
|
|
|
{
|
|
|
|
T t;
|
|
|
|
if (!this->get(id, t))
|
|
|
|
return;
|
|
|
|
|
|
|
|
(*d_txn)->del(d_parent->d_main, id);
|
|
|
|
clearIndex(id, t);
|
|
|
|
}
|
|
|
|
|
2022-02-21 23:46:32 +01:00
|
|
|
//! Clears the database and indexes.
|
2022-01-30 21:14:43 +01:00
|
|
|
void clear()
|
|
|
|
{
|
|
|
|
if (const auto rc = mdb_drop(**d_txn, d_parent->d_main, 0)) {
|
|
|
|
throw LMDBError("Error database: ", rc);
|
|
|
|
}
|
2022-01-31 22:23:58 +01:00
|
|
|
d_parent->forEachIndex([&](auto &&i) { i.clear(*d_txn); });
|
2022-01-30 21:14:43 +01:00
|
|
|
}
|
2018-12-16 20:28:49 +01:00
|
|
|
|
2022-03-07 23:31:47 +01:00
|
|
|
//! \brief Rebuilds the database, possibly throwing out invalid objects.
|
|
|
|
//! \param func Specifies a function which is supposed to return whether an object is still valid.
|
|
|
|
//! It might modify the passed object in order to "fix" it.
|
|
|
|
void rebuild(const std::function<bool(IDType id, T *obj)> &func)
|
|
|
|
{
|
|
|
|
// clear all indexes to get rid of invalid entries
|
|
|
|
d_parent->forEachIndex([&](auto &&i) { i.clear(*d_txn); });
|
|
|
|
// check all objects via func
|
|
|
|
for (auto i = this->begin(), end = this->end(); i != end; ++i) {
|
|
|
|
T *val = nullptr;
|
|
|
|
try {
|
|
|
|
val = &i.value();
|
|
|
|
} catch (...) { // catch possible deserialization errors
|
|
|
|
}
|
|
|
|
if (func(i.getID(), val)) {
|
|
|
|
if (val) {
|
|
|
|
put(*val, i.getID());
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
i.del();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-21 23:46:32 +01:00
|
|
|
//! Commits this transaction.
|
2022-01-30 21:14:43 +01:00
|
|
|
void commit()
|
|
|
|
{
|
|
|
|
(*d_txn)->commit();
|
|
|
|
}
|
|
|
|
|
2022-02-21 23:46:32 +01:00
|
|
|
//! Aborts this transaction.
|
2022-01-30 21:14:43 +01:00
|
|
|
void abort()
|
|
|
|
{
|
|
|
|
(*d_txn)->abort();
|
|
|
|
}
|
|
|
|
|
|
|
|
typedef MDBRWCursor cursor_t;
|
|
|
|
|
|
|
|
std::shared_ptr<MDBRWTransaction> getTransactionHandle()
|
|
|
|
{
|
|
|
|
return d_txn;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
// clear this ID from all indexes
|
2022-02-21 22:57:15 +01:00
|
|
|
void clearIndex(IDType id, const T &t)
|
2022-01-30 21:14:43 +01:00
|
|
|
{
|
2022-01-31 22:23:58 +01:00
|
|
|
d_parent->forEachIndex([&](auto &&i) { i.del(*d_txn, t, id); });
|
2022-01-30 21:14:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
public:
|
|
|
|
TypedDBI *d_parent;
|
|
|
|
std::shared_ptr<MDBRWTransaction> d_txn;
|
|
|
|
};
|
|
|
|
|
|
|
|
//! Get an RW transaction
|
|
|
|
RWTransaction getRWTransaction()
|
2018-12-15 17:53:29 +01:00
|
|
|
{
|
2022-01-30 21:14:43 +01:00
|
|
|
return RWTransaction(this);
|
2018-12-15 17:53:29 +01:00
|
|
|
}
|
|
|
|
|
2022-01-30 21:14:43 +01:00
|
|
|
//! Get an RO transaction
|
|
|
|
ROTransaction getROTransaction()
|
2018-12-15 17:53:29 +01:00
|
|
|
{
|
2022-01-30 21:14:43 +01:00
|
|
|
return ROTransaction(this);
|
2018-12-15 17:53:29 +01:00
|
|
|
}
|
2018-12-15 00:46:15 +01:00
|
|
|
|
2022-01-30 21:14:43 +01:00
|
|
|
//! Get an RW transaction
|
|
|
|
RWTransaction getRWTransaction(std::shared_ptr<MDBRWTransaction> txn)
|
|
|
|
{
|
|
|
|
return RWTransaction(this, txn);
|
|
|
|
}
|
2018-12-17 14:56:01 +01:00
|
|
|
|
2022-01-30 21:14:43 +01:00
|
|
|
//! Get an RO transaction
|
|
|
|
ROTransaction getROTransaction(std::shared_ptr<MDBROTransaction> txn)
|
2018-12-17 14:56:01 +01:00
|
|
|
{
|
2022-01-30 21:14:43 +01:00
|
|
|
return ROTransaction(this, txn);
|
2018-12-17 14:56:01 +01:00
|
|
|
}
|
|
|
|
|
2022-01-30 21:14:43 +01:00
|
|
|
std::shared_ptr<MDBEnv> getEnv()
|
2018-12-14 23:46:46 +01:00
|
|
|
{
|
2022-01-30 21:14:43 +01:00
|
|
|
return d_env;
|
2018-12-14 23:46:46 +01:00
|
|
|
}
|
2018-12-15 17:53:29 +01:00
|
|
|
|
2018-12-14 23:46:46 +01:00
|
|
|
private:
|
2022-01-30 21:14:43 +01:00
|
|
|
std::shared_ptr<MDBEnv> d_env;
|
|
|
|
MDBDbi d_main;
|
|
|
|
std::string d_name;
|
2018-12-14 23:46:46 +01:00
|
|
|
};
|
|
|
|
|
2022-01-30 21:14:43 +01:00
|
|
|
} // namespace LMDBSafe
|