2018-12-14 23:00:44 +01:00
|
|
|
#pragma once
|
|
|
|
#include "lmdb-safe.hh"
|
2021-12-22 20:48:53 +01:00
|
|
|
|
2022-01-18 22:08:36 +01:00
|
|
|
namespace LMDBSafe {
|
|
|
|
|
2018-12-15 17:53:29 +01:00
|
|
|
/*
|
|
|
|
Open issues:
|
|
|
|
|
|
|
|
What is an error? What is an exception?
|
2018-12-16 15:58:38 +01:00
|
|
|
could id=0 be magic? ('no such id')
|
2018-12-28 16:33:20 +01:00
|
|
|
yes
|
2018-12-15 17:53:29 +01:00
|
|
|
Perhaps use the separate index concept from multi_index
|
2018-12-16 21:46:53 +01:00
|
|
|
perhaps get eiter to be of same type so for(auto& a : x) works
|
2018-12-28 16:33:20 +01:00
|
|
|
make it more value "like" with unique_ptr
|
2018-12-15 17:53:29 +01:00
|
|
|
*/
|
|
|
|
|
2018-12-16 20:28:49 +01:00
|
|
|
/** Return the highest ID used in a database. Returns 0 for an empty DB.
|
|
|
|
This makes us start everything at ID=1, which might make it possible to
|
|
|
|
treat id 0 as special
|
|
|
|
*/
|
2018-12-17 14:56:01 +01:00
|
|
|
unsigned int MDBGetMaxID(MDBRWTransaction& txn, MDBDbi& dbi);
|
2018-12-14 23:00:44 +01:00
|
|
|
|
2022-01-18 22:24:31 +01:00
|
|
|
/** This is the serialization interface.
|
|
|
|
You need to define your these functions for the types you'd like to store.
|
2018-12-16 20:28:49 +01:00
|
|
|
*/
|
2018-12-14 23:00:44 +01:00
|
|
|
template<typename T>
|
2022-01-18 22:24:31 +01:00
|
|
|
std::string serToString(const T& t);
|
2018-12-14 23:00:44 +01:00
|
|
|
|
|
|
|
template<typename T>
|
2022-01-25 23:15:05 +01:00
|
|
|
void serFromString(string_view str, T& ret);
|
2018-12-27 17:49:41 +01:00
|
|
|
|
2022-01-18 22:24:31 +01:00
|
|
|
/** This is the serialization interface for keys.
|
|
|
|
You need to define your these functions for the types you'd like to use as keys.
|
|
|
|
*/
|
2019-01-05 14:40:28 +01:00
|
|
|
template <class T, class Enable>
|
|
|
|
inline std::string keyConv(const T& t);
|
2018-12-27 17:49:41 +01:00
|
|
|
|
2019-01-05 14:40:28 +01:00
|
|
|
template <class T, typename std::enable_if<std::is_arithmetic<T>::value,T>::type* = nullptr>
|
2022-01-16 20:37:48 +01:00
|
|
|
inline string_view keyConv(const T& t)
|
2018-12-27 17:49:41 +01:00
|
|
|
{
|
2022-01-16 20:37:48 +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
|
|
|
|
|
|
|
template<class T, typename std::enable_if<std::is_same<T, std::string>::value,T>::type* = nullptr>
|
2022-01-16 20:37:48 +01:00
|
|
|
inline string_view keyConv(const T& t)
|
2018-12-28 16:45:44 +01:00
|
|
|
{
|
2019-01-05 14:40:28 +01:00
|
|
|
return t;
|
2018-12-28 16:45:44 +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*
|
|
|
|
* size<t> or get<t>. People ask for those themselves, and should no do that on indexes that
|
|
|
|
* don't exist.
|
|
|
|
*/
|
2018-12-16 15:58:38 +01:00
|
|
|
template<class Class,typename Type, typename Parent>
|
|
|
|
struct LMDBIndexOps
|
|
|
|
{
|
|
|
|
explicit LMDBIndexOps(Parent* parent) : d_parent(parent){}
|
2021-12-05 19:28:08 +01:00
|
|
|
void put(MDBRWTransaction& txn, const Class& t, uint32_t id, unsigned int flags=0)
|
2018-12-14 23:00:44 +01:00
|
|
|
{
|
2019-10-26 11:42:38 +02:00
|
|
|
txn->put(d_idx, keyConv(d_parent->getMember(t)), id, flags);
|
2018-12-14 23:00:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void del(MDBRWTransaction& txn, const Class& t, uint32_t id)
|
|
|
|
{
|
2022-01-22 18:59:54 +01:00
|
|
|
if(const auto rc = txn->del(d_idx, keyConv(d_parent->getMember(t)), id)) {
|
|
|
|
throw LMDBError("Error deleting from index: ", rc);
|
2018-12-27 17:49:41 +01:00
|
|
|
}
|
2018-12-14 23:00:44 +01:00
|
|
|
}
|
|
|
|
|
2021-12-05 19:28:08 +01:00
|
|
|
void openDB(std::shared_ptr<MDBEnv>& env, string_view str, unsigned int flags)
|
2018-12-14 23:00:44 +01:00
|
|
|
{
|
|
|
|
d_idx = env->openDB(str, flags);
|
|
|
|
}
|
2018-12-16 15:58:38 +01:00
|
|
|
MDBDbi d_idx;
|
|
|
|
Parent* d_parent;
|
|
|
|
};
|
2018-12-14 23:00:44 +01:00
|
|
|
|
2018-12-16 20:28:49 +01:00
|
|
|
/** This is an index on a field in a struct, it derives from the LMDBIndexOps */
|
|
|
|
|
2018-12-16 15:58:38 +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;
|
|
|
|
}
|
2018-12-14 23:00:44 +01:00
|
|
|
|
|
|
|
typedef Type type;
|
2018-12-16 15:58:38 +01:00
|
|
|
};
|
|
|
|
|
2018-12-16 20:28:49 +01:00
|
|
|
/** This is a calculated index */
|
2018-12-16 15:58:38 +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);
|
|
|
|
}
|
|
|
|
|
|
|
|
typedef Type type;
|
2018-12-14 23:00:44 +01:00
|
|
|
};
|
|
|
|
|
2018-12-16 20:28:49 +01:00
|
|
|
/** nop index, so we can fill our N indexes, even if you don't use them all */
|
2018-12-14 23:00:44 +01:00
|
|
|
struct nullindex_t
|
|
|
|
{
|
|
|
|
template<typename Class>
|
2021-12-05 19:28:08 +01:00
|
|
|
void put(MDBRWTransaction& txn, const Class& t, uint32_t id, unsigned int flags=0)
|
|
|
|
{
|
|
|
|
(void)txn;
|
|
|
|
(void)t;
|
|
|
|
(void)id;
|
|
|
|
(void)flags;
|
|
|
|
}
|
2018-12-14 23:00:44 +01:00
|
|
|
template<typename Class>
|
|
|
|
void del(MDBRWTransaction& txn, const Class& t, uint32_t id)
|
2021-12-05 19:28:08 +01:00
|
|
|
{
|
|
|
|
(void)txn;
|
|
|
|
(void)t;
|
|
|
|
(void)id;
|
|
|
|
}
|
2018-12-14 23:46:46 +01:00
|
|
|
|
2021-12-05 19:28:08 +01:00
|
|
|
void openDB(std::shared_ptr<MDBEnv>& env, string_view str, unsigned int flags)
|
2018-12-14 23:00:44 +01:00
|
|
|
{
|
2021-12-05 19:28:08 +01:00
|
|
|
(void)env;
|
|
|
|
(void)str;
|
|
|
|
(void)flags;
|
2018-12-14 23:00:44 +01:00
|
|
|
}
|
|
|
|
typedef uint32_t type; // dummy
|
|
|
|
};
|
|
|
|
|
2018-12-27 17:49:41 +01:00
|
|
|
|
2018-12-16 20:28:49 +01:00
|
|
|
/** The main class. Templatized only on the indexes and typename right now */
|
2018-12-14 23:00:44 +01:00
|
|
|
template<typename T, class I1=nullindex_t, class I2=nullindex_t, class I3 = nullindex_t, class I4 = nullindex_t>
|
|
|
|
class TypedDBI
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
TypedDBI(std::shared_ptr<MDBEnv> env, string_view name)
|
|
|
|
: d_env(env), d_name(name)
|
|
|
|
{
|
|
|
|
d_main = d_env->openDB(name, MDB_CREATE | MDB_INTEGERKEY);
|
2018-12-16 20:28:49 +01:00
|
|
|
|
|
|
|
// now you might be tempted to go all MPL on this so we can get rid of the
|
|
|
|
// ugly macro. I'm not very receptive to that idea since it will make things
|
|
|
|
// EVEN uglier.
|
2018-12-15 00:46:15 +01:00
|
|
|
#define openMacro(N) std::get<N>(d_tuple).openDB(d_env, std::string(name)+"_"#N, MDB_CREATE | MDB_DUPFIXED | MDB_DUPSORT);
|
|
|
|
openMacro(0);
|
|
|
|
openMacro(1);
|
|
|
|
openMacro(2);
|
|
|
|
openMacro(3);
|
|
|
|
#undef openMacro
|
2018-12-14 23:00:44 +01:00
|
|
|
}
|
|
|
|
|
2018-12-27 17:49:41 +01:00
|
|
|
|
2018-12-16 20:28:49 +01:00
|
|
|
// we get a lot of our smarts from this tuple, it enables get<0> etc
|
2018-12-15 00:46:15 +01:00
|
|
|
typedef std::tuple<I1, I2, I3, I4> tuple_t;
|
|
|
|
tuple_t d_tuple;
|
2018-12-14 23:00:44 +01:00
|
|
|
|
2018-12-16 20:28:49 +01:00
|
|
|
// We support readonly and rw transactions. Here we put the Readonly operations
|
|
|
|
// which get sourced by both kinds of transactions
|
2018-12-15 17:53:29 +01:00
|
|
|
template<class Parent>
|
|
|
|
struct ReadonlyOperations
|
|
|
|
{
|
|
|
|
ReadonlyOperations(Parent& parent) : d_parent(parent)
|
|
|
|
{}
|
2018-12-14 23:00:44 +01:00
|
|
|
|
2018-12-16 20:28:49 +01:00
|
|
|
//! Number of entries in main database
|
2021-12-05 19:28:08 +01:00
|
|
|
size_t size()
|
2018-12-14 23:46:46 +01:00
|
|
|
{
|
|
|
|
MDB_stat stat;
|
2019-10-26 11:42:38 +02:00
|
|
|
mdb_stat(**d_parent.d_txn, d_parent.d_parent->d_main, &stat);
|
2018-12-14 23:46:46 +01:00
|
|
|
return stat.ms_entries;
|
|
|
|
}
|
2018-12-15 00:46:15 +01:00
|
|
|
|
2018-12-16 20:28:49 +01:00
|
|
|
//! Number of entries in the various indexes - should be the same
|
2018-12-15 17:53:29 +01:00
|
|
|
template<int N>
|
2021-12-05 19:28:08 +01:00
|
|
|
size_t size()
|
2018-12-15 17:53:29 +01:00
|
|
|
{
|
2018-12-15 21:06:47 +01:00
|
|
|
MDB_stat stat;
|
2019-10-26 11:42:38 +02:00
|
|
|
mdb_stat(**d_parent.d_txn, std::get<N>(d_parent.d_parent->d_tuple).d_idx, &stat);
|
2018-12-15 21:06:47 +01:00
|
|
|
return stat.ms_entries;
|
2018-12-14 23:00:44 +01:00
|
|
|
}
|
2018-12-16 20:28:49 +01:00
|
|
|
|
|
|
|
//! Get item with id, from main table directly
|
2018-12-15 17:53:29 +01:00
|
|
|
bool get(uint32_t id, T& t)
|
2018-12-14 23:00:44 +01:00
|
|
|
{
|
|
|
|
MDBOutVal data;
|
2019-10-26 11:42:38 +02:00
|
|
|
if((*d_parent.d_txn)->get(d_parent.d_parent->d_main, id, data))
|
2018-12-14 23:00:44 +01:00
|
|
|
return false;
|
|
|
|
|
2022-01-25 23:16:09 +01:00
|
|
|
serFromString(data.get<string_view>(), t);
|
2018-12-14 23:00:44 +01:00
|
|
|
return true;
|
|
|
|
}
|
2018-12-14 23:46:46 +01:00
|
|
|
|
2018-12-16 20:28:49 +01:00
|
|
|
//! Get item through index N, then via the main database
|
2021-12-05 19:28:08 +01:00
|
|
|
template<std::size_t N>
|
2018-12-15 00:46:15 +01:00
|
|
|
uint32_t get(const typename std::tuple_element<N, tuple_t>::type::type& key, T& out)
|
2018-12-14 23:00:44 +01:00
|
|
|
{
|
|
|
|
MDBOutVal id;
|
2019-10-26 11:42:38 +02:00
|
|
|
if(!(*d_parent.d_txn)->get(std::get<N>(d_parent.d_parent->d_tuple).d_idx, keyConv(key), id)) {
|
2018-12-17 14:56:01 +01:00
|
|
|
if(get(id.get<uint32_t>(), out))
|
|
|
|
return id.get<uint32_t>();
|
|
|
|
}
|
2018-12-14 23:00:44 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-12-16 20:28:49 +01:00
|
|
|
//! Cardinality of index N
|
2021-12-05 19:28:08 +01:00
|
|
|
template<std::size_t N>
|
2018-12-15 00:46:15 +01:00
|
|
|
uint32_t cardinality()
|
2018-12-14 23:00:44 +01:00
|
|
|
{
|
2019-10-26 11:42:38 +02:00
|
|
|
auto cursor = (*d_parent.d_txn)->getCursor(std::get<N>(d_parent.d_parent->d_tuple).d_idx);
|
2018-12-15 00:46:15 +01:00
|
|
|
bool first = true;
|
|
|
|
MDBOutVal key, data;
|
|
|
|
uint32_t count = 0;
|
|
|
|
while(!cursor.get(key, data, first ? MDB_FIRST : MDB_NEXT_NODUP)) {
|
|
|
|
++count;
|
|
|
|
first=false;
|
|
|
|
}
|
|
|
|
return count;
|
2018-12-14 23:00:44 +01:00
|
|
|
}
|
2018-12-15 17:53:29 +01:00
|
|
|
|
2018-12-16 20:28:49 +01:00
|
|
|
//! End iderator type
|
2018-12-15 17:53:29 +01:00
|
|
|
struct eiter_t
|
|
|
|
{};
|
|
|
|
|
2018-12-15 21:06:47 +01:00
|
|
|
// can be on main, or on an index
|
|
|
|
// when on main, return data directly
|
|
|
|
// when on index, indirect
|
2018-12-16 20:28:49 +01:00
|
|
|
// we can be limited to one key, or iterate over entire database
|
|
|
|
// iter requires you to put the cursor in the right place first!
|
2018-12-15 17:53:29 +01:00
|
|
|
struct iter_t
|
2018-12-14 23:00:44 +01:00
|
|
|
{
|
2018-12-15 21:06:47 +01:00
|
|
|
explicit iter_t(Parent* parent, typename Parent::cursor_t&& cursor, bool on_index, bool one_key, bool end=false) :
|
2018-12-15 17:53:29 +01:00
|
|
|
d_parent(parent),
|
2018-12-15 21:06:47 +01:00
|
|
|
d_cursor(std::move(cursor)),
|
2018-12-16 20:28:49 +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)
|
2018-12-15 21:06:47 +01:00
|
|
|
d_end(end)
|
2018-12-28 16:33:20 +01:00
|
|
|
{
|
|
|
|
if(d_end)
|
|
|
|
return;
|
|
|
|
d_prefix.clear();
|
|
|
|
|
|
|
|
if(d_cursor.get(d_key, d_id, MDB_GET_CURRENT)) {
|
|
|
|
d_end = true;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(d_on_index) {
|
2019-10-26 11:42:38 +02:00
|
|
|
if((*d_parent->d_txn)->get(d_parent->d_parent->d_main, d_id, d_data))
|
2022-01-22 18:59:54 +01:00
|
|
|
throw LMDBError("Missing id in constructor");
|
2022-01-25 23:16:09 +01:00
|
|
|
serFromString(d_data.get<string_view>(), d_t);
|
2018-12-28 16:33:20 +01:00
|
|
|
}
|
|
|
|
else
|
2022-01-25 23:16:09 +01:00
|
|
|
serFromString(d_id.get<string_view>(), d_t);
|
2018-12-28 16:33:20 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
explicit iter_t(Parent* parent, typename Parent::cursor_t&& cursor, const std::string& prefix) :
|
|
|
|
d_parent(parent),
|
|
|
|
d_cursor(std::move(cursor)),
|
|
|
|
d_on_index(true), // is this an iterator on main database or on index?
|
|
|
|
d_one_key(false),
|
|
|
|
d_prefix(prefix),
|
|
|
|
d_end(false)
|
2018-12-15 17:53:29 +01:00
|
|
|
{
|
2018-12-15 21:06:47 +01:00
|
|
|
if(d_end)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if(d_cursor.get(d_key, d_id, MDB_GET_CURRENT)) {
|
2018-12-15 17:53:29 +01:00
|
|
|
d_end = true;
|
|
|
|
return;
|
|
|
|
}
|
2018-12-15 21:06:47 +01:00
|
|
|
|
|
|
|
if(d_on_index) {
|
2019-10-26 11:42:38 +02:00
|
|
|
if((*d_parent->d_txn)->get(d_parent->d_parent->d_main, d_id, d_data))
|
2022-01-22 18:59:54 +01:00
|
|
|
throw LMDBError("Missing id in constructor");
|
2022-01-25 23:16:09 +01:00
|
|
|
serFromString(d_data.get<string_view>(), d_t);
|
2018-12-15 21:06:47 +01:00
|
|
|
}
|
|
|
|
else
|
2022-01-25 23:16:09 +01:00
|
|
|
serFromString(d_id.get<string_view>(), d_t);
|
2018-12-15 17:53:29 +01:00
|
|
|
}
|
2018-12-28 16:33:20 +01:00
|
|
|
|
2018-12-15 17:53:29 +01:00
|
|
|
|
2018-12-27 17:49:41 +01:00
|
|
|
std::function<bool(const MDBOutVal&)> filter;
|
2018-12-17 14:56:01 +01:00
|
|
|
void del()
|
|
|
|
{
|
|
|
|
d_cursor.del();
|
|
|
|
}
|
2018-12-15 17:53:29 +01:00
|
|
|
|
2021-12-05 19:28:08 +01:00
|
|
|
bool operator!=(const eiter_t&) const
|
2018-12-15 17:53:29 +01:00
|
|
|
{
|
|
|
|
return !d_end;
|
|
|
|
}
|
|
|
|
|
2021-12-05 19:28:08 +01:00
|
|
|
bool operator==(const eiter_t&) const
|
2018-12-15 17:53:29 +01:00
|
|
|
{
|
|
|
|
return d_end;
|
|
|
|
}
|
|
|
|
|
|
|
|
const T& operator*()
|
|
|
|
{
|
|
|
|
return d_t;
|
|
|
|
}
|
|
|
|
|
|
|
|
const T* operator->()
|
|
|
|
{
|
|
|
|
return &d_t;
|
|
|
|
}
|
2018-12-16 20:28:49 +01:00
|
|
|
|
2021-12-23 23:31:19 +01:00
|
|
|
T &value()
|
|
|
|
{
|
|
|
|
return d_t;
|
|
|
|
}
|
|
|
|
|
2018-12-16 20:28:49 +01:00
|
|
|
// implements generic ++ or --
|
2018-12-16 15:58:38 +01:00
|
|
|
iter_t& genoperator(MDB_cursor_op dupop, MDB_cursor_op op)
|
2018-12-15 17:53:29 +01:00
|
|
|
{
|
2018-12-15 21:06:47 +01:00
|
|
|
MDBOutVal data;
|
2018-12-27 17:49:41 +01:00
|
|
|
next:;
|
2022-01-22 18:59:54 +01:00
|
|
|
const auto rc = d_cursor.get(d_key, d_id, d_one_key ? dupop : op);
|
2018-12-15 17:53:29 +01:00
|
|
|
if(rc == MDB_NOTFOUND) {
|
|
|
|
d_end = true;
|
|
|
|
}
|
2018-12-27 17:49:41 +01:00
|
|
|
else if(rc) {
|
2022-01-22 18:59:54 +01:00
|
|
|
throw LMDBError("Unable to get in genoperator: ", rc);
|
2018-12-27 17:49:41 +01:00
|
|
|
}
|
2018-12-28 16:33:20 +01:00
|
|
|
else if(!d_prefix.empty() && d_key.get<std::string>().rfind(d_prefix, 0)!=0) {
|
|
|
|
d_end = true;
|
|
|
|
}
|
2018-12-15 17:53:29 +01:00
|
|
|
else {
|
2018-12-15 21:06:47 +01:00
|
|
|
if(d_on_index) {
|
2019-10-26 11:42:38 +02:00
|
|
|
if((*d_parent->d_txn)->get(d_parent->d_parent->d_main, d_id, data))
|
2022-01-22 18:59:54 +01:00
|
|
|
throw LMDBError("Missing id field in genoperator");
|
2018-12-27 17:49:41 +01:00
|
|
|
if(filter && !filter(data))
|
|
|
|
goto next;
|
2018-12-15 21:06:47 +01:00
|
|
|
|
2022-01-25 23:16:09 +01:00
|
|
|
serFromString(data.get<string_view>(), d_t);
|
2018-12-15 21:06:47 +01:00
|
|
|
}
|
2018-12-27 17:49:41 +01:00
|
|
|
else {
|
|
|
|
if(filter && !filter(data))
|
|
|
|
goto next;
|
|
|
|
|
2022-01-25 23:16:09 +01:00
|
|
|
serFromString(d_id.get<string_view>(), d_t);
|
2018-12-27 17:49:41 +01:00
|
|
|
}
|
2018-12-15 17:53:29 +01:00
|
|
|
}
|
|
|
|
return *this;
|
|
|
|
}
|
2018-12-15 21:06:47 +01:00
|
|
|
|
2018-12-16 15:58:38 +01:00
|
|
|
iter_t& operator++()
|
|
|
|
{
|
|
|
|
return genoperator(MDB_NEXT_DUP, MDB_NEXT);
|
|
|
|
}
|
|
|
|
iter_t& operator--()
|
|
|
|
{
|
|
|
|
return genoperator(MDB_PREV_DUP, MDB_PREV);
|
|
|
|
}
|
2018-12-16 20:28:49 +01:00
|
|
|
|
|
|
|
// get ID this iterator points to
|
2018-12-15 21:06:47 +01:00
|
|
|
uint32_t getID()
|
|
|
|
{
|
|
|
|
if(d_on_index)
|
|
|
|
return d_id.get<uint32_t>();
|
|
|
|
else
|
|
|
|
return d_key.get<uint32_t>();
|
|
|
|
}
|
2018-12-16 20:28:49 +01:00
|
|
|
|
2019-01-04 20:51:04 +01:00
|
|
|
const MDBOutVal& getKey()
|
|
|
|
{
|
|
|
|
return d_key;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-12-16 20:28:49 +01:00
|
|
|
// transaction we are part of
|
2018-12-15 17:53:29 +01:00
|
|
|
Parent* d_parent;
|
|
|
|
typename Parent::cursor_t d_cursor;
|
2018-12-16 20:28:49 +01:00
|
|
|
|
|
|
|
// gcc complains if I don't zero-init these, which is worrying XXX
|
2018-12-28 19:21:32 +01:00
|
|
|
MDBOutVal d_key{{0,0}}, d_data{{0,0}}, d_id{{0,0}};
|
2018-12-15 21:06:47 +01:00
|
|
|
bool d_on_index;
|
|
|
|
bool d_one_key;
|
2018-12-28 16:33:20 +01:00
|
|
|
std::string d_prefix;
|
2018-12-15 17:53:29 +01:00
|
|
|
bool d_end{false};
|
|
|
|
T d_t;
|
|
|
|
};
|
2018-12-14 23:00:44 +01:00
|
|
|
|
2018-12-15 17:53:29 +01:00
|
|
|
template<int N>
|
2018-12-27 17:49:41 +01:00
|
|
|
iter_t genbegin(MDB_cursor_op op)
|
2018-12-14 23:00:44 +01:00
|
|
|
{
|
2019-10-26 11:42:38 +02:00
|
|
|
typename Parent::cursor_t cursor = (*d_parent.d_txn)->getCursor(std::get<N>(d_parent.d_parent->d_tuple).d_idx);
|
2018-12-15 21:06:47 +01:00
|
|
|
|
|
|
|
MDBOutVal out, id;
|
|
|
|
|
2018-12-27 17:49:41 +01:00
|
|
|
if(cursor.get(out, id, op)) {
|
|
|
|
// on_index, one_key, end
|
2018-12-16 15:58:38 +01:00
|
|
|
return iter_t{&d_parent, std::move(cursor), true, false, true};
|
2018-12-15 21:06:47 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return iter_t{&d_parent, std::move(cursor), true, false};
|
|
|
|
};
|
|
|
|
|
2018-12-27 17:49:41 +01:00
|
|
|
template<int N>
|
|
|
|
iter_t begin()
|
|
|
|
{
|
|
|
|
return genbegin<N>(MDB_FIRST);
|
|
|
|
}
|
|
|
|
|
|
|
|
template<int N>
|
|
|
|
iter_t rbegin()
|
|
|
|
{
|
|
|
|
return genbegin<N>(MDB_LAST);
|
|
|
|
}
|
|
|
|
|
2018-12-15 21:06:47 +01:00
|
|
|
iter_t begin()
|
|
|
|
{
|
2019-10-26 11:42:38 +02: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
|
|
|
|
|
|
|
MDBOutVal out, id;
|
|
|
|
|
|
|
|
if(cursor.get(out, id, MDB_FIRST)) {
|
2018-12-16 15:58:38 +01:00
|
|
|
// on_index, one_key, end
|
|
|
|
return iter_t{&d_parent, std::move(cursor), false, false, true};
|
2018-12-15 21:06:47 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return iter_t{&d_parent, std::move(cursor), false, false};
|
|
|
|
};
|
|
|
|
|
2018-12-16 20:28:49 +01:00
|
|
|
eiter_t end()
|
|
|
|
{
|
|
|
|
return eiter_t();
|
|
|
|
}
|
|
|
|
|
|
|
|
// basis for find, lower_bound
|
2021-12-05 19:28:08 +01:00
|
|
|
template<std::size_t N>
|
2018-12-16 20:28:49 +01:00
|
|
|
iter_t genfind(const typename std::tuple_element<N, tuple_t>::type::type& key, MDB_cursor_op op)
|
2018-12-15 21:06:47 +01:00
|
|
|
{
|
2019-10-26 11:42:38 +02:00
|
|
|
typename Parent::cursor_t cursor = (*d_parent.d_txn)->getCursor(std::get<N>(d_parent.d_parent->d_tuple).d_idx);
|
2018-12-27 17:49:41 +01:00
|
|
|
|
2022-01-16 20:37:48 +01:00
|
|
|
const auto keystr = keyConv(key);
|
2018-12-27 17:49:41 +01:00
|
|
|
MDBInVal in(keystr);
|
2018-12-15 21:06:47 +01:00
|
|
|
MDBOutVal out, id;
|
|
|
|
out.d_mdbval = in.d_mdbval;
|
|
|
|
|
2018-12-16 20:28:49 +01:00
|
|
|
if(cursor.get(out, id, op)) {
|
2018-12-16 15:58:38 +01:00
|
|
|
// on_index, one_key, end
|
|
|
|
return iter_t{&d_parent, std::move(cursor), true, false, true};
|
|
|
|
}
|
|
|
|
|
|
|
|
return iter_t{&d_parent, std::move(cursor), true, false};
|
|
|
|
};
|
|
|
|
|
2021-12-05 19:28:08 +01:00
|
|
|
template<std::size_t N>
|
2018-12-16 20:28:49 +01:00
|
|
|
iter_t find(const typename std::tuple_element<N, tuple_t>::type::type& key)
|
|
|
|
{
|
|
|
|
return genfind<N>(key, MDB_SET);
|
|
|
|
}
|
|
|
|
|
2021-12-05 19:28:08 +01:00
|
|
|
template<std::size_t N>
|
2018-12-16 20:28:49 +01:00
|
|
|
iter_t lower_bound(const typename std::tuple_element<N, tuple_t>::type::type& key)
|
|
|
|
{
|
|
|
|
return genfind<N>(key, MDB_SET_RANGE);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//! equal range - could possibly be expressed through genfind
|
2021-12-05 19:28:08 +01:00
|
|
|
template<std::size_t N>
|
2018-12-16 15:58:38 +01:00
|
|
|
std::pair<iter_t,eiter_t> equal_range(const typename std::tuple_element<N, tuple_t>::type::type& key)
|
|
|
|
{
|
2019-10-26 11:42:38 +02:00
|
|
|
typename Parent::cursor_t cursor = (*d_parent.d_txn)->getCursor(std::get<N>(d_parent.d_parent->d_tuple).d_idx);
|
2018-12-27 17:49:41 +01:00
|
|
|
|
2022-01-16 20:37:48 +01:00
|
|
|
const auto keyString=keyConv(key);
|
2018-12-27 17:49:41 +01:00
|
|
|
MDBInVal in(keyString);
|
2018-12-16 15:58:38 +01:00
|
|
|
MDBOutVal out, id;
|
|
|
|
out.d_mdbval = in.d_mdbval;
|
|
|
|
|
|
|
|
if(cursor.get(out, id, MDB_SET)) {
|
|
|
|
// on_index, one_key, end
|
|
|
|
return {iter_t{&d_parent, std::move(cursor), true, true, true}, eiter_t()};
|
2018-12-15 21:06:47 +01:00
|
|
|
}
|
|
|
|
|
2018-12-16 15:58:38 +01:00
|
|
|
return {iter_t{&d_parent, std::move(cursor), true, true}, eiter_t()};
|
2018-12-15 17:53:29 +01:00
|
|
|
};
|
2018-12-16 15:58:38 +01:00
|
|
|
|
2018-12-28 16:33:20 +01:00
|
|
|
//! equal range - could possibly be expressed through genfind
|
2021-12-05 19:28:08 +01:00
|
|
|
template<std::size_t N>
|
2018-12-28 16:33:20 +01:00
|
|
|
std::pair<iter_t,eiter_t> prefix_range(const typename std::tuple_element<N, tuple_t>::type::type& key)
|
|
|
|
{
|
2019-10-26 11:42:38 +02:00
|
|
|
typename Parent::cursor_t cursor = (*d_parent.d_txn)->getCursor(std::get<N>(d_parent.d_parent->d_tuple).d_idx);
|
2018-12-28 16:33:20 +01:00
|
|
|
|
2022-01-16 20:37:48 +01:00
|
|
|
const auto keyString=keyConv(key);
|
2018-12-28 16:33:20 +01:00
|
|
|
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
|
|
|
|
return {iter_t{&d_parent, std::move(cursor), true, true, true}, eiter_t()};
|
|
|
|
}
|
|
|
|
|
|
|
|
return {iter_t(&d_parent, std::move(cursor), keyString), eiter_t()};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2018-12-15 17:53:29 +01:00
|
|
|
Parent& d_parent;
|
|
|
|
};
|
|
|
|
|
|
|
|
class ROTransaction : public ReadonlyOperations<ROTransaction>
|
2018-12-14 23:00:44 +01:00
|
|
|
{
|
2018-12-15 17:53:29 +01:00
|
|
|
public:
|
2018-12-17 14:56:01 +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
|
|
|
{
|
2018-12-15 17:53:29 +01:00
|
|
|
}
|
2018-12-14 23:00:44 +01:00
|
|
|
|
2018-12-17 14:56:01 +01:00
|
|
|
explicit ROTransaction(TypedDBI* parent, std::shared_ptr<MDBROTransaction> txn) : ReadonlyOperations<ROTransaction>(*this), d_parent(parent), d_txn(txn)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-12-15 17:53:29 +01:00
|
|
|
ROTransaction(ROTransaction&& rhs) :
|
|
|
|
ReadonlyOperations<ROTransaction>(*this), d_parent(rhs.d_parent),d_txn(std::move(rhs.d_txn))
|
|
|
|
|
|
|
|
{
|
|
|
|
rhs.d_parent = 0;
|
2018-12-14 23:00:44 +01:00
|
|
|
}
|
|
|
|
|
2018-12-17 14:56:01 +01:00
|
|
|
std::shared_ptr<MDBROTransaction> getTransactionHandle()
|
|
|
|
{
|
|
|
|
return d_txn;
|
|
|
|
}
|
|
|
|
|
2018-12-15 17:53:29 +01:00
|
|
|
typedef MDBROCursor cursor_t;
|
|
|
|
|
|
|
|
TypedDBI* d_parent;
|
2018-12-17 14:56:01 +01:00
|
|
|
std::shared_ptr<MDBROTransaction> d_txn;
|
2018-12-15 17:53:29 +01:00
|
|
|
};
|
2018-12-14 23:00:44 +01:00
|
|
|
|
2018-12-15 17:53:29 +01:00
|
|
|
|
|
|
|
class RWTransaction : public ReadonlyOperations<RWTransaction>
|
|
|
|
{
|
|
|
|
public:
|
2018-12-17 14:56:01 +01:00
|
|
|
explicit RWTransaction(TypedDBI* parent) : ReadonlyOperations<RWTransaction>(*this), d_parent(parent)
|
2018-12-14 23:00:44 +01:00
|
|
|
{
|
2018-12-17 14:56:01 +01:00
|
|
|
d_txn = std::make_shared<MDBRWTransaction>(d_parent->d_env->getRWTransaction());
|
2018-12-14 23:00:44 +01:00
|
|
|
}
|
|
|
|
|
2018-12-17 14:56:01 +01:00
|
|
|
explicit RWTransaction(TypedDBI* parent, std::shared_ptr<MDBRWTransaction> txn) : ReadonlyOperations<RWTransaction>(*this), d_parent(parent), d_txn(txn)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-12-15 17:53:29 +01:00
|
|
|
RWTransaction(RWTransaction&& rhs) :
|
|
|
|
ReadonlyOperations<RWTransaction>(*this),
|
|
|
|
d_parent(rhs.d_parent), d_txn(std::move(rhs.d_txn))
|
2018-12-14 23:00:44 +01:00
|
|
|
{
|
2018-12-15 17:53:29 +01:00
|
|
|
rhs.d_parent = 0;
|
2018-12-14 23:00:44 +01:00
|
|
|
}
|
|
|
|
|
2018-12-16 20:28:49 +01:00
|
|
|
// insert something, with possibly a specific id
|
2018-12-16 21:46:53 +01:00
|
|
|
uint32_t put(const T& t, uint32_t id=0)
|
2018-12-14 23:00:44 +01:00
|
|
|
{
|
2021-12-05 19:28:08 +01:00
|
|
|
unsigned int flags = 0;
|
2018-12-27 17:49:41 +01:00
|
|
|
if(!id) {
|
2018-12-17 14:56:01 +01:00
|
|
|
id = MDBGetMaxID(*d_txn, d_parent->d_main) + 1;
|
2018-12-27 17:49:41 +01:00
|
|
|
flags = MDB_APPEND;
|
|
|
|
}
|
2019-10-26 11:42:38 +02:00
|
|
|
(*d_txn)->put(d_parent->d_main, id, serToString(t), flags);
|
2018-12-15 17:53:29 +01:00
|
|
|
|
2018-12-17 14:56:01 +01:00
|
|
|
#define insertMacro(N) std::get<N>(d_parent->d_tuple).put(*d_txn, t, id);
|
2018-12-15 17:53:29 +01:00
|
|
|
insertMacro(0);
|
|
|
|
insertMacro(1);
|
|
|
|
insertMacro(2);
|
|
|
|
insertMacro(3);
|
|
|
|
#undef insertMacro
|
|
|
|
|
|
|
|
return id;
|
2018-12-14 23:00:44 +01:00
|
|
|
}
|
|
|
|
|
2018-12-16 20:28:49 +01:00
|
|
|
// modify an item 'in place', plus update indexes
|
2018-12-15 17:53:29 +01:00
|
|
|
void modify(uint32_t id, std::function<void(T&)> func)
|
2018-12-14 23:00:44 +01:00
|
|
|
{
|
2018-12-15 17:53:29 +01:00
|
|
|
T t;
|
2018-12-27 17:49:41 +01:00
|
|
|
if(!this->get(id, t))
|
2022-01-22 18:59:54 +01:00
|
|
|
throw LMDBError("Could not modify id " + std::to_string(id));
|
2018-12-15 17:53:29 +01:00
|
|
|
func(t);
|
|
|
|
|
|
|
|
del(id); // this is the lazy way. We could test for changed index fields
|
2018-12-16 21:46:53 +01:00
|
|
|
put(t, id);
|
2018-12-14 23:00:44 +01:00
|
|
|
}
|
|
|
|
|
2018-12-16 20:28:49 +01:00
|
|
|
//! delete an item, and from indexes
|
2018-12-15 17:53:29 +01:00
|
|
|
void del(uint32_t id)
|
2018-12-14 23:00:44 +01:00
|
|
|
{
|
2018-12-15 17:53:29 +01:00
|
|
|
T t;
|
|
|
|
if(!this->get(id, t))
|
|
|
|
return;
|
|
|
|
|
2019-10-26 11:42:38 +02:00
|
|
|
(*d_txn)->del(d_parent->d_main, id);
|
2018-12-15 17:53:29 +01:00
|
|
|
clearIndex(id, t);
|
|
|
|
}
|
|
|
|
|
2018-12-16 20:28:49 +01:00
|
|
|
//! clear database & indexes (by hand!)
|
2018-12-15 17:53:29 +01:00
|
|
|
void clear()
|
|
|
|
{
|
2019-10-26 11:42:38 +02:00
|
|
|
auto cursor = (*d_txn)->getRWCursor(d_parent->d_main);
|
2018-12-15 17:53:29 +01:00
|
|
|
bool first = true;
|
|
|
|
MDBOutVal key, data;
|
|
|
|
while(!cursor.get(key, data, first ? MDB_FIRST : MDB_NEXT)) {
|
|
|
|
first = false;
|
|
|
|
T t;
|
2022-01-25 23:16:09 +01:00
|
|
|
serFromString(data.get<string_view>(), t);
|
2018-12-15 17:53:29 +01:00
|
|
|
clearIndex(key.get<uint32_t>(), t);
|
|
|
|
cursor.del();
|
2018-12-14 23:00:44 +01:00
|
|
|
}
|
|
|
|
}
|
2018-12-16 20:28:49 +01:00
|
|
|
|
|
|
|
//! commit this transaction
|
2018-12-15 17:53:29 +01:00
|
|
|
void commit()
|
|
|
|
{
|
2019-10-26 11:42:38 +02:00
|
|
|
(*d_txn)->commit();
|
2018-12-15 17:53:29 +01:00
|
|
|
}
|
|
|
|
|
2018-12-16 20:28:49 +01:00
|
|
|
//! abort this transaction
|
2018-12-15 17:53:29 +01:00
|
|
|
void abort()
|
|
|
|
{
|
2019-10-26 11:42:38 +02:00
|
|
|
(*d_txn)->abort();
|
2018-12-15 17:53:29 +01:00
|
|
|
}
|
2018-12-15 00:46:15 +01:00
|
|
|
|
2018-12-15 17:53:29 +01:00
|
|
|
typedef MDBRWCursor cursor_t;
|
2018-12-17 14:56:01 +01:00
|
|
|
|
|
|
|
std::shared_ptr<MDBRWTransaction> getTransactionHandle()
|
|
|
|
{
|
|
|
|
return d_txn;
|
|
|
|
}
|
|
|
|
|
2018-12-14 23:46:46 +01:00
|
|
|
|
|
|
|
private:
|
2018-12-16 20:28:49 +01:00
|
|
|
// clear this ID from all indexes
|
2018-12-14 23:46:46 +01:00
|
|
|
void clearIndex(uint32_t id, const T& t)
|
|
|
|
{
|
2018-12-17 14:56:01 +01:00
|
|
|
#define clearMacro(N) std::get<N>(d_parent->d_tuple).del(*d_txn, t, id);
|
2018-12-15 00:46:15 +01:00
|
|
|
clearMacro(0);
|
|
|
|
clearMacro(1);
|
|
|
|
clearMacro(2);
|
|
|
|
clearMacro(3);
|
|
|
|
#undef clearMacro
|
2018-12-14 23:46:46 +01:00
|
|
|
}
|
2018-12-15 17:53:29 +01:00
|
|
|
|
|
|
|
public:
|
2018-12-14 23:46:46 +01:00
|
|
|
TypedDBI* d_parent;
|
2018-12-17 14:56:01 +01:00
|
|
|
std::shared_ptr<MDBRWTransaction> d_txn;
|
2018-12-14 23:46:46 +01:00
|
|
|
};
|
2018-12-16 20:28:49 +01:00
|
|
|
|
|
|
|
//! Get an RW transaction
|
2018-12-14 23:46:46 +01:00
|
|
|
RWTransaction getRWTransaction()
|
|
|
|
{
|
|
|
|
return RWTransaction(this);
|
|
|
|
}
|
|
|
|
|
2018-12-16 20:28:49 +01:00
|
|
|
//! Get an RO transaction
|
2018-12-15 17:53:29 +01:00
|
|
|
ROTransaction getROTransaction()
|
|
|
|
{
|
|
|
|
return ROTransaction(this);
|
|
|
|
}
|
2018-12-17 14:56:01 +01:00
|
|
|
|
|
|
|
//! Get an RW transaction
|
|
|
|
RWTransaction getRWTransaction(std::shared_ptr<MDBRWTransaction> txn)
|
|
|
|
{
|
|
|
|
return RWTransaction(this, txn);
|
|
|
|
}
|
|
|
|
|
|
|
|
//! Get an RO transaction
|
|
|
|
ROTransaction getROTransaction(std::shared_ptr<MDBROTransaction> txn)
|
|
|
|
{
|
|
|
|
return ROTransaction(this, txn);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::shared_ptr<MDBEnv> getEnv()
|
|
|
|
{
|
|
|
|
return d_env;
|
|
|
|
}
|
2018-12-15 17:53:29 +01:00
|
|
|
|
2018-12-14 23:46:46 +01:00
|
|
|
private:
|
|
|
|
std::shared_ptr<MDBEnv> d_env;
|
|
|
|
MDBDbi d_main;
|
|
|
|
std::string d_name;
|
|
|
|
};
|
|
|
|
|
2022-01-18 22:08:36 +01:00
|
|
|
}
|