Merge pull request #6 from horazont/feature/nested-transactions
Implement support for nesting transactions
This commit is contained in:
commit
469de3c0e2
24
README.md
24
README.md
|
@ -87,16 +87,16 @@ transaction is aborted automatically. To commit or abort, use `commit()` or
|
||||||
`abort()`, after which going out of scope has no further effect.
|
`abort()`, after which going out of scope has no further effect.
|
||||||
|
|
||||||
```
|
```
|
||||||
txn.put(dbi, "lmdb", "great");
|
txn->put(dbi, "lmdb", "great");
|
||||||
|
|
||||||
string_view data;
|
string_view data;
|
||||||
if(!txn.get(dbi, "lmdb", data)) {
|
if(!txn->get(dbi, "lmdb", data)) {
|
||||||
cout<< "Within RW transaction, found that lmdb = " << data <<endl;
|
cout<< "Within RW transaction, found that lmdb = " << data <<endl;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
cout<<"Found nothing" << endl;
|
cout<<"Found nothing" << endl;
|
||||||
|
|
||||||
txn.commit();
|
txn->commit();
|
||||||
```
|
```
|
||||||
|
|
||||||
LMDB is so fast because it does not copy data unless it really needs to.
|
LMDB is so fast because it does not copy data unless it really needs to.
|
||||||
|
@ -129,8 +129,8 @@ For example, to store `double` values for 64 bit IDs:
|
||||||
auto txn = env->getRWTransaction();
|
auto txn = env->getRWTransaction();
|
||||||
uint64_t id=12345678901;
|
uint64_t id=12345678901;
|
||||||
double score=3.14159;
|
double score=3.14159;
|
||||||
txn.put(dbi, id, score);
|
txn->put(dbi, id, score);
|
||||||
txn.commit();
|
txn->commit();
|
||||||
```
|
```
|
||||||
|
|
||||||
Behind the scenes, the `id` and `score` values are wrapped by `MDBInVal`
|
Behind the scenes, the `id` and `score` values are wrapped by `MDBInVal`
|
||||||
|
@ -142,7 +142,7 @@ works similary:
|
||||||
uint64_t id=12345678901;
|
uint64_t id=12345678901;
|
||||||
MDBOutValue val;
|
MDBOutValue val;
|
||||||
|
|
||||||
txn.get(dbi, id, val);
|
txn->get(dbi, id, val);
|
||||||
|
|
||||||
cout << "Score: " << val.get<double>() << "\n";
|
cout << "Score: " << val.get<double>() << "\n";
|
||||||
```
|
```
|
||||||
|
@ -170,10 +170,10 @@ struct Coordinate
|
||||||
|
|
||||||
C c{12.0, 13.0};
|
C c{12.0, 13.0};
|
||||||
|
|
||||||
txn.put(dbi, MDBInVal::fromStruct(c), 12.0);
|
txn->put(dbi, MDBInVal::fromStruct(c), 12.0);
|
||||||
|
|
||||||
MDBOutVal res;
|
MDBOutVal res;
|
||||||
txn.get(dbi, MDBInVal::fromStruct(c), res);
|
txn->get(dbi, MDBInVal::fromStruct(c), res);
|
||||||
|
|
||||||
auto c1 = res.get_struct<Coordinate>();
|
auto c1 = res.get_struct<Coordinate>();
|
||||||
```
|
```
|
||||||
|
@ -193,7 +193,7 @@ calls to mdb.
|
||||||
This is the usual opening sequence.
|
This is the usual opening sequence.
|
||||||
|
|
||||||
```
|
```
|
||||||
auto cursor=txn.getCursor(dbi);
|
auto cursor=txn->getCursor(dbi);
|
||||||
MDBOutVal key, data;
|
MDBOutVal key, data;
|
||||||
int count=0;
|
int count=0;
|
||||||
cout<<"Counting records.. "; cout.flush();
|
cout<<"Counting records.. "; cout.flush();
|
||||||
|
@ -212,7 +212,7 @@ records in under a second (!).
|
||||||
|
|
||||||
```
|
```
|
||||||
cout<<"Clearing records.. "; cout.flush();
|
cout<<"Clearing records.. "; cout.flush();
|
||||||
mdb_drop(txn, dbi, 0); // clear records
|
mdb_drop(*txn, dbi, 0); // clear records
|
||||||
cout<<"Done!"<<endl;
|
cout<<"Done!"<<endl;
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -224,11 +224,11 @@ native `mdb_drop` function which we did not wrap. This is possible because
|
||||||
```
|
```
|
||||||
cout << "Adding "<<limit<<" values .. "; cout.flush();
|
cout << "Adding "<<limit<<" values .. "; cout.flush();
|
||||||
for(unsigned int n = 0 ; n < limit; ++n) {
|
for(unsigned int n = 0 ; n < limit; ++n) {
|
||||||
txn.put(dbi, n, n);
|
txn->put(dbi, n, n);
|
||||||
}
|
}
|
||||||
cout <<"Done!"<<endl;
|
cout <<"Done!"<<endl;
|
||||||
cout <<"Calling commit.. "; cout.flush();
|
cout <<"Calling commit.. "; cout.flush();
|
||||||
txn.commit();
|
txn->commit();
|
||||||
cout<<"Done!"<<endl;
|
cout<<"Done!"<<endl;
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ void checkLMDB(MDBEnv* env, MDBDbi dbi)
|
||||||
{
|
{
|
||||||
auto rotxn = env->getROTransaction();
|
auto rotxn = env->getROTransaction();
|
||||||
MDBOutVal data;
|
MDBOutVal data;
|
||||||
if(!rotxn.get(dbi, "lmdb", data)) {
|
if(!rotxn->get(dbi, "lmdb", data)) {
|
||||||
cout<< "Outside RW transaction, found that lmdb = " << data.get<string_view>() <<endl;
|
cout<< "Outside RW transaction, found that lmdb = " << data.get<string_view>() <<endl;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -18,11 +18,11 @@ int main()
|
||||||
auto dbi = env->openDB("example", MDB_CREATE);
|
auto dbi = env->openDB("example", MDB_CREATE);
|
||||||
|
|
||||||
auto txn = env->getRWTransaction();
|
auto txn = env->getRWTransaction();
|
||||||
mdb_drop(txn, dbi, 0);
|
mdb_drop(*txn, dbi, 0);
|
||||||
txn.put(dbi, "lmdb", "great");
|
txn->put(dbi, "lmdb", "great");
|
||||||
|
|
||||||
MDBOutVal data;
|
MDBOutVal data;
|
||||||
if(!txn.get(dbi, "lmdb", data)) {
|
if(!txn->get(dbi, "lmdb", data)) {
|
||||||
cout<< "Within RW transaction, found that lmdb = " << data.get<string_view>() <<endl;
|
cout<< "Within RW transaction, found that lmdb = " << data.get<string_view>() <<endl;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -31,12 +31,12 @@ int main()
|
||||||
std::thread elsewhere(checkLMDB, env.get(), dbi);
|
std::thread elsewhere(checkLMDB, env.get(), dbi);
|
||||||
elsewhere.join();
|
elsewhere.join();
|
||||||
|
|
||||||
txn.commit();
|
txn->commit();
|
||||||
|
|
||||||
cout<<"Committed data"<<endl;
|
cout<<"Committed data"<<endl;
|
||||||
|
|
||||||
checkLMDB(env.get(), dbi);
|
checkLMDB(env.get(), dbi);
|
||||||
txn = env->getRWTransaction();
|
txn = env->getRWTransaction();
|
||||||
mdb_drop(txn, dbi, 0);
|
mdb_drop(*txn, dbi, 0);
|
||||||
txn.commit();
|
txn->commit();
|
||||||
}
|
}
|
||||||
|
|
188
lmdb-safe.cc
188
lmdb-safe.cc
|
@ -139,53 +139,107 @@ MDBDbi MDBEnv::openDB(const string_view dbname, int flags)
|
||||||
|
|
||||||
if(!(envflags & MDB_RDONLY)) {
|
if(!(envflags & MDB_RDONLY)) {
|
||||||
auto rwt = getRWTransaction();
|
auto rwt = getRWTransaction();
|
||||||
MDBDbi ret = rwt.openDB(dbname, flags);
|
MDBDbi ret = rwt->openDB(dbname, flags);
|
||||||
rwt.commit();
|
rwt->commit();
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
MDBDbi ret;
|
MDBDbi ret;
|
||||||
{
|
{
|
||||||
auto rwt = getROTransaction();
|
auto rwt = getROTransaction();
|
||||||
ret = rwt.openDB(dbname, flags);
|
ret = rwt->openDB(dbname, flags);
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
MDBRWTransaction::MDBRWTransaction(MDBEnv* parent, int flags) : d_parent(parent)
|
MDBRWTransactionImpl::MDBRWTransactionImpl(MDBEnv *parent, MDB_txn *txn):
|
||||||
|
MDBROTransactionImpl(parent, txn)
|
||||||
|
|
||||||
{
|
{
|
||||||
if(d_parent->getROTX() || d_parent->getRWTX())
|
|
||||||
|
}
|
||||||
|
|
||||||
|
MDB_txn *MDBRWTransactionImpl::openRWTransaction(MDBEnv *env, MDB_txn *parent, int flags)
|
||||||
|
{
|
||||||
|
MDB_txn *result;
|
||||||
|
if(env->getROTX() || env->getRWTX())
|
||||||
throw std::runtime_error("Duplicate RW transaction");
|
throw std::runtime_error("Duplicate RW transaction");
|
||||||
|
|
||||||
for(int tries =0 ; tries < 3; ++tries) { // it might happen twice, who knows
|
for(int tries =0 ; tries < 3; ++tries) { // it might happen twice, who knows
|
||||||
if(int rc=mdb_txn_begin(d_parent->d_env, 0, flags, &d_txn)) {
|
if(int rc=mdb_txn_begin(env->d_env, parent, flags, &result)) {
|
||||||
if(rc == MDB_MAP_RESIZED && tries < 2) {
|
if(rc == MDB_MAP_RESIZED && tries < 2) {
|
||||||
// "If the mapsize is increased by another process (..) mdb_txn_begin() will return MDB_MAP_RESIZED.
|
// "If the mapsize is increased by another process (..) mdb_txn_begin() will return MDB_MAP_RESIZED.
|
||||||
// call mdb_env_set_mapsize with a size of zero to adopt the new size."
|
// call mdb_env_set_mapsize with a size of zero to adopt the new size."
|
||||||
mdb_env_set_mapsize(d_parent->d_env, 0);
|
mdb_env_set_mapsize(env->d_env, 0);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
throw std::runtime_error("Unable to start RW transaction: "+std::string(mdb_strerror(rc)));
|
throw std::runtime_error("Unable to start RW transaction: "+std::string(mdb_strerror(rc)));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
d_parent->incRWTX();
|
env->incRWTX();
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
MDBROTransaction::MDBROTransaction(MDBEnv* parent, int flags) : d_parent(parent)
|
MDBRWTransactionImpl::MDBRWTransactionImpl(MDBEnv* parent, int flags):
|
||||||
|
MDBRWTransactionImpl(parent, openRWTransaction(parent, nullptr, flags))
|
||||||
{
|
{
|
||||||
if(d_parent->getRWTX())
|
}
|
||||||
|
|
||||||
|
MDBRWTransactionImpl::~MDBRWTransactionImpl()
|
||||||
|
{
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MDBRWTransactionImpl::commit()
|
||||||
|
{
|
||||||
|
closeRORWCursors();
|
||||||
|
if (!d_txn) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(int rc = mdb_txn_commit(d_txn)) {
|
||||||
|
throw std::runtime_error("committing: " + std::string(mdb_strerror(rc)));
|
||||||
|
}
|
||||||
|
environment().decRWTX();
|
||||||
|
d_txn = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MDBRWTransactionImpl::abort()
|
||||||
|
{
|
||||||
|
closeRORWCursors();
|
||||||
|
if (!d_txn) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mdb_txn_abort(d_txn);
|
||||||
|
// prevent the RO destructor from cleaning up the transaction itself
|
||||||
|
environment().decRWTX();
|
||||||
|
d_txn = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
MDBROTransactionImpl::MDBROTransactionImpl(MDBEnv *parent, MDB_txn *txn):
|
||||||
|
d_parent(parent),
|
||||||
|
d_cursors(),
|
||||||
|
d_txn(txn)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
MDB_txn *MDBROTransactionImpl::openROTransaction(MDBEnv *env, MDB_txn *parent, int flags)
|
||||||
|
{
|
||||||
|
if(env->getRWTX())
|
||||||
throw std::runtime_error("Duplicate RO transaction");
|
throw std::runtime_error("Duplicate RO transaction");
|
||||||
|
|
||||||
/*
|
/*
|
||||||
A transaction and its cursors must only be used by a single thread, and a thread may only have a single transaction at a time. If MDB_NOTLS is in use, this does not apply to read-only transactions. */
|
A transaction and its cursors must only be used by a single thread, and a thread may only have a single transaction at a time. If MDB_NOTLS is in use, this does not apply to read-only transactions. */
|
||||||
|
MDB_txn *result = nullptr;
|
||||||
for(int tries =0 ; tries < 3; ++tries) { // it might happen twice, who knows
|
for(int tries =0 ; tries < 3; ++tries) { // it might happen twice, who knows
|
||||||
if(int rc=mdb_txn_begin(d_parent->d_env, 0, MDB_RDONLY | flags, &d_txn)) {
|
if(int rc=mdb_txn_begin(env->d_env, parent, MDB_RDONLY | flags, &result)) {
|
||||||
if(rc == MDB_MAP_RESIZED && tries < 2) {
|
if(rc == MDB_MAP_RESIZED && tries < 2) {
|
||||||
// "If the mapsize is increased by another process (..) mdb_txn_begin() will return MDB_MAP_RESIZED.
|
// "If the mapsize is increased by another process (..) mdb_txn_begin() will return MDB_MAP_RESIZED.
|
||||||
// call mdb_env_set_mapsize with a size of zero to adopt the new size."
|
// call mdb_env_set_mapsize with a size of zero to adopt the new size."
|
||||||
mdb_env_set_mapsize(d_parent->d_env, 0);
|
mdb_env_set_mapsize(env->d_env, 0);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -193,43 +247,125 @@ MDBROTransaction::MDBROTransaction(MDBEnv* parent, int flags) : d_parent(parent)
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
d_parent->incROTX();
|
env->incROTX();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MDBROTransactionImpl::closeROCursors()
|
||||||
|
{
|
||||||
|
// we need to move the vector away to ensure that the cursors don’t mess with our iteration.
|
||||||
|
std::vector<MDBROCursor*> buf;
|
||||||
|
std::swap(d_cursors, buf);
|
||||||
|
for (auto &cursor: buf) {
|
||||||
|
cursor->close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MDBROTransactionImpl::MDBROTransactionImpl(MDBEnv *parent, int flags):
|
||||||
|
MDBROTransactionImpl(parent, openROTransaction(parent, nullptr, flags))
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
MDBROTransactionImpl::~MDBROTransactionImpl()
|
||||||
|
{
|
||||||
|
// this is safe because C++ will not call overrides of virtual methods in destructors.
|
||||||
|
commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MDBROTransactionImpl::abort()
|
||||||
|
{
|
||||||
|
closeROCursors();
|
||||||
|
// if d_txn is non-nullptr here, either the transaction object was invalidated earlier (e.g. by moving from it), or it is an RW transaction which has already cleaned up the d_txn pointer (with an abort).
|
||||||
|
if (d_txn) {
|
||||||
|
d_parent->decROTX();
|
||||||
|
mdb_txn_abort(d_txn); // this appears to work better than abort for r/o database opening
|
||||||
|
d_txn = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MDBROTransactionImpl::commit()
|
||||||
|
{
|
||||||
|
closeROCursors();
|
||||||
|
// if d_txn is non-nullptr here, either the transaction object was invalidated earlier (e.g. by moving from it), or it is an RW transaction which has already cleaned up the d_txn pointer (with an abort).
|
||||||
|
if (d_txn) {
|
||||||
|
d_parent->decROTX();
|
||||||
|
mdb_txn_commit(d_txn); // this appears to work better than abort for r/o database opening
|
||||||
|
d_txn = nullptr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void MDBRWTransaction::clear(MDB_dbi dbi)
|
void MDBRWTransactionImpl::clear(MDB_dbi dbi)
|
||||||
{
|
{
|
||||||
if(int rc = mdb_drop(d_txn, dbi, 0)) {
|
if(int rc = mdb_drop(d_txn, dbi, 0)) {
|
||||||
throw runtime_error("Error clearing database: " + MDBError(rc));
|
throw runtime_error("Error clearing database: " + MDBError(rc));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MDBRWCursor MDBRWTransaction::getCursor(const MDBDbi& dbi)
|
MDBRWCursor MDBRWTransactionImpl::getRWCursor(const MDBDbi& dbi)
|
||||||
{
|
{
|
||||||
return MDBRWCursor(this, dbi);
|
MDB_cursor *cursor;
|
||||||
|
int rc= mdb_cursor_open(d_txn, dbi, &cursor);
|
||||||
|
if(rc) {
|
||||||
|
throw std::runtime_error("Error creating RO cursor: "+std::string(mdb_strerror(rc)));
|
||||||
|
}
|
||||||
|
return MDBRWCursor(d_rw_cursors, cursor);
|
||||||
|
}
|
||||||
|
|
||||||
|
MDBRWCursor MDBRWTransactionImpl::getCursor(const MDBDbi &dbi)
|
||||||
|
{
|
||||||
|
return getRWCursor(dbi);
|
||||||
|
}
|
||||||
|
|
||||||
|
MDBRWTransaction MDBRWTransactionImpl::getRWTransaction()
|
||||||
|
{
|
||||||
|
MDB_txn *txn;
|
||||||
|
if (int rc = mdb_txn_begin(environment(), *this, 0, &txn)) {
|
||||||
|
throw std::runtime_error(std::string("failed to start child transaction: ")+mdb_strerror(rc));
|
||||||
|
}
|
||||||
|
// we need to increase the counter here because commit/abort on the child transaction will decrease it
|
||||||
|
environment().incRWTX();
|
||||||
|
return MDBRWTransaction(new MDBRWTransactionImpl(&environment(), txn));
|
||||||
|
}
|
||||||
|
|
||||||
|
MDBROTransaction MDBRWTransactionImpl::getROTransaction()
|
||||||
|
{
|
||||||
|
return std::move(getRWTransaction());
|
||||||
}
|
}
|
||||||
|
|
||||||
MDBROTransaction MDBEnv::getROTransaction()
|
MDBROTransaction MDBEnv::getROTransaction()
|
||||||
{
|
{
|
||||||
return MDBROTransaction(this);
|
return MDBROTransaction(new MDBROTransactionImpl(this));
|
||||||
}
|
}
|
||||||
MDBRWTransaction MDBEnv::getRWTransaction()
|
MDBRWTransaction MDBEnv::getRWTransaction()
|
||||||
{
|
{
|
||||||
return MDBRWTransaction(this);
|
return MDBRWTransaction(new MDBRWTransactionImpl(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void MDBRWTransaction::closeCursors()
|
void MDBRWTransactionImpl::closeRWCursors()
|
||||||
{
|
{
|
||||||
for(auto& c : d_cursors)
|
decltype(d_rw_cursors) buf;
|
||||||
c->close();
|
std::swap(d_rw_cursors, buf);
|
||||||
d_cursors.clear();
|
for (auto &cursor: buf) {
|
||||||
|
cursor->close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MDBROCursor MDBROTransaction::getCursor(const MDBDbi& dbi)
|
MDBROCursor MDBROTransactionImpl::getCursor(const MDBDbi& dbi)
|
||||||
{
|
{
|
||||||
return MDBROCursor(this, dbi);
|
return getROCursor(dbi);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MDBROCursor MDBROTransactionImpl::getROCursor(const MDBDbi &dbi)
|
||||||
|
{
|
||||||
|
MDB_cursor *cursor;
|
||||||
|
int rc= mdb_cursor_open(d_txn, dbi, &cursor);
|
||||||
|
if(rc) {
|
||||||
|
throw std::runtime_error("Error creating RO cursor: "+std::string(mdb_strerror(rc)));
|
||||||
|
}
|
||||||
|
return MDBROCursor(d_cursors, cursor);
|
||||||
|
}
|
||||||
|
|
354
lmdb-safe.hh
354
lmdb-safe.hh
|
@ -9,6 +9,8 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
#include <vector>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
// apple compiler somehow has string_view even in c++11!
|
// apple compiler somehow has string_view even in c++11!
|
||||||
#if __cplusplus < 201703L && !defined(__APPLE__)
|
#if __cplusplus < 201703L && !defined(__APPLE__)
|
||||||
|
@ -57,8 +59,11 @@ public:
|
||||||
MDB_dbi d_dbi;
|
MDB_dbi d_dbi;
|
||||||
};
|
};
|
||||||
|
|
||||||
class MDBRWTransaction;
|
class MDBRWTransactionImpl;
|
||||||
class MDBROTransaction;
|
class MDBROTransactionImpl;
|
||||||
|
|
||||||
|
using MDBROTransaction = std::unique_ptr<MDBROTransactionImpl>;
|
||||||
|
using MDBRWTransaction = std::unique_ptr<MDBRWTransactionImpl>;
|
||||||
|
|
||||||
class MDBEnv
|
class MDBEnv
|
||||||
{
|
{
|
||||||
|
@ -220,35 +225,36 @@ private:
|
||||||
|
|
||||||
class MDBROCursor;
|
class MDBROCursor;
|
||||||
|
|
||||||
class MDBROTransaction
|
class MDBROTransactionImpl
|
||||||
{
|
{
|
||||||
|
protected:
|
||||||
|
MDBROTransactionImpl(MDBEnv *parent, MDB_txn *txn);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static MDB_txn *openROTransaction(MDBEnv *env, MDB_txn *parent, int flags=0);
|
||||||
|
|
||||||
|
MDBEnv* d_parent;
|
||||||
|
std::vector<MDBROCursor*> d_cursors;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
MDB_txn* d_txn;
|
||||||
|
|
||||||
|
void closeROCursors();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit MDBROTransaction(MDBEnv* parent, int flags=0);
|
explicit MDBROTransactionImpl(MDBEnv* parent, int flags=0);
|
||||||
|
|
||||||
MDBROTransaction(MDBROTransaction&& rhs)
|
MDBROTransactionImpl(const MDBROTransactionImpl& src) = delete;
|
||||||
{
|
MDBROTransactionImpl &operator=(const MDBROTransactionImpl& src) = delete;
|
||||||
d_parent = rhs.d_parent;
|
|
||||||
d_txn = rhs.d_txn;
|
|
||||||
rhs.d_parent = 0;
|
|
||||||
rhs.d_txn = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void reset()
|
// The move constructor/operator cannot be made safe due to Object Slicing with MDBRWTransaction.
|
||||||
{
|
MDBROTransactionImpl(MDBROTransactionImpl&& rhs) = delete;
|
||||||
// this does not free cursors
|
MDBROTransactionImpl &operator=(MDBROTransactionImpl &&rhs) = delete;
|
||||||
mdb_txn_reset(d_txn);
|
|
||||||
d_parent->decROTX();
|
|
||||||
}
|
|
||||||
|
|
||||||
void renew()
|
virtual ~MDBROTransactionImpl();
|
||||||
{
|
|
||||||
if(d_parent->getROTX())
|
virtual void abort();
|
||||||
throw std::runtime_error("Duplicate RO transaction");
|
virtual void commit();
|
||||||
if(int rc = mdb_txn_renew(d_txn))
|
|
||||||
throw std::runtime_error("Renewing RO transaction: "+std::string(mdb_strerror(rc)));
|
|
||||||
d_parent->incROTX();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int get(MDB_dbi dbi, const MDBInVal& key, MDBOutVal& val)
|
int get(MDB_dbi dbi, const MDBInVal& key, MDBOutVal& val)
|
||||||
{
|
{
|
||||||
|
@ -280,22 +286,21 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
MDBROCursor getCursor(const MDBDbi&);
|
MDBROCursor getCursor(const MDBDbi&);
|
||||||
|
MDBROCursor getROCursor(const MDBDbi&);
|
||||||
|
|
||||||
~MDBROTransaction()
|
operator MDB_txn*()
|
||||||
{
|
|
||||||
if(d_txn) {
|
|
||||||
d_parent->decROTX();
|
|
||||||
mdb_txn_commit(d_txn); // this appears to work better than abort for r/o database opening
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
operator MDB_txn*&()
|
|
||||||
{
|
{
|
||||||
return d_txn;
|
return d_txn;
|
||||||
}
|
}
|
||||||
|
|
||||||
MDBEnv* d_parent;
|
inline operator bool() const {
|
||||||
MDB_txn* d_txn;
|
return d_txn;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline MDBEnv &environment()
|
||||||
|
{
|
||||||
|
return *d_parent;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -304,12 +309,75 @@ public:
|
||||||
"If the parent transaction commits, the cursor must not be used again."
|
"If the parent transaction commits, the cursor must not be used again."
|
||||||
*/
|
*/
|
||||||
|
|
||||||
template<class Transaction>
|
template<class Transaction, class T>
|
||||||
class MDBGenCursor
|
class MDBGenCursor
|
||||||
{
|
{
|
||||||
|
private:
|
||||||
|
std::vector<T*> *d_registry;
|
||||||
|
MDB_cursor* d_cursor;
|
||||||
|
|
||||||
|
public:
|
||||||
|
MDBGenCursor():
|
||||||
|
d_registry(nullptr),
|
||||||
|
d_cursor(nullptr)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
if (!d_registry) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
MDBGenCursor(Transaction *t) : d_parent(t)
|
|
||||||
{}
|
|
||||||
int get(MDBOutVal& key, MDBOutVal& data, MDB_cursor_op op)
|
int get(MDBOutVal& key, MDBOutVal& data, MDB_cursor_op op)
|
||||||
{
|
{
|
||||||
int rc = mdb_cursor_get(d_cursor, &key.d_mdbval, &data.d_mdbval, op);
|
int rc = mdb_cursor_get(d_cursor, &key.d_mdbval, &data.d_mdbval, op);
|
||||||
|
@ -377,101 +445,78 @@ public:
|
||||||
return currentlast(key, data, MDB_FIRST);
|
return currentlast(key, data, MDB_FIRST);
|
||||||
}
|
}
|
||||||
|
|
||||||
operator MDB_cursor*&()
|
operator MDB_cursor*()
|
||||||
{
|
{
|
||||||
return d_cursor;
|
return d_cursor;
|
||||||
}
|
}
|
||||||
|
|
||||||
MDB_cursor* d_cursor;
|
operator bool() const
|
||||||
Transaction* d_parent;
|
|
||||||
};
|
|
||||||
|
|
||||||
class MDBROCursor : public MDBGenCursor<MDBROTransaction>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
MDBROCursor(MDBROTransaction* parent, const MDB_dbi& dbi) : MDBGenCursor<MDBROTransaction>(parent)
|
|
||||||
{
|
{
|
||||||
int rc= mdb_cursor_open(d_parent->d_txn, dbi, &d_cursor);
|
return d_cursor;
|
||||||
if(rc) {
|
|
||||||
throw std::runtime_error("Error creating RO cursor: "+std::string(mdb_strerror(rc)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
MDBROCursor(MDBROCursor&& rhs) : MDBGenCursor<MDBROTransaction>(rhs.d_parent)
|
|
||||||
{
|
|
||||||
d_cursor = rhs.d_cursor;
|
|
||||||
rhs.d_cursor=0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void close()
|
void close()
|
||||||
{
|
{
|
||||||
mdb_cursor_close(d_cursor);
|
if (d_registry) {
|
||||||
d_cursor=0;
|
auto iter = std::find(d_registry->begin(),
|
||||||
}
|
d_registry->end(),
|
||||||
|
static_cast<T*>(this));
|
||||||
~MDBROCursor()
|
if (iter != d_registry->end()) {
|
||||||
{
|
d_registry->erase(iter);
|
||||||
if(d_cursor)
|
}
|
||||||
|
d_registry = nullptr;
|
||||||
|
}
|
||||||
|
if (d_cursor) {
|
||||||
mdb_cursor_close(d_cursor);
|
mdb_cursor_close(d_cursor);
|
||||||
|
d_cursor = nullptr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class MDBROCursor : public MDBGenCursor<MDBROTransactionImpl, MDBROCursor>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
MDBROCursor() = default;
|
||||||
|
using MDBGenCursor<MDBROTransactionImpl, MDBROCursor>::MDBGenCursor;
|
||||||
|
MDBROCursor(const MDBROCursor &src) = delete;
|
||||||
|
MDBROCursor(MDBROCursor &&src) = default;
|
||||||
|
MDBROCursor &operator=(const MDBROCursor &src) = delete;
|
||||||
|
MDBROCursor &operator=(MDBROCursor &&src) = default;
|
||||||
|
~MDBROCursor() = default;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class MDBRWCursor;
|
class MDBRWCursor;
|
||||||
|
|
||||||
class MDBRWTransaction
|
class MDBRWTransactionImpl: public MDBROTransactionImpl
|
||||||
{
|
{
|
||||||
|
protected:
|
||||||
|
MDBRWTransactionImpl(MDBEnv* parent, MDB_txn* txn);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static MDB_txn *openRWTransaction(MDBEnv* env, MDB_txn *parent, int flags);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<MDBRWCursor*> d_rw_cursors;
|
||||||
|
|
||||||
|
void closeRWCursors();
|
||||||
|
inline void closeRORWCursors() {
|
||||||
|
closeROCursors();
|
||||||
|
closeRWCursors();
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit MDBRWTransaction(MDBEnv* parent, int flags=0);
|
explicit MDBRWTransactionImpl(MDBEnv* parent, int flags=0);
|
||||||
|
|
||||||
MDBRWTransaction(MDBRWTransaction&& rhs)
|
MDBRWTransactionImpl(const MDBRWTransactionImpl& rhs) = delete;
|
||||||
{
|
MDBRWTransactionImpl(MDBRWTransactionImpl&& rhs) = delete;
|
||||||
d_parent = rhs.d_parent;
|
MDBRWTransactionImpl &operator=(const MDBRWTransactionImpl& rhs) = delete;
|
||||||
d_txn = rhs.d_txn;
|
MDBRWTransactionImpl &operator=(MDBRWTransactionImpl&& rhs) = delete;
|
||||||
rhs.d_parent = 0;
|
|
||||||
rhs.d_txn = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
MDBRWTransaction& operator=(MDBRWTransaction&& rhs)
|
~MDBRWTransactionImpl() override;
|
||||||
{
|
|
||||||
if(d_txn)
|
|
||||||
abort();
|
|
||||||
|
|
||||||
d_parent = rhs.d_parent;
|
|
||||||
d_txn = rhs.d_txn;
|
|
||||||
rhs.d_parent = 0;
|
|
||||||
rhs.d_txn = 0;
|
|
||||||
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
~MDBRWTransaction()
|
|
||||||
{
|
|
||||||
if(d_txn) {
|
|
||||||
d_parent->decRWTX();
|
|
||||||
closeCursors();
|
|
||||||
mdb_txn_abort(d_txn); // XXX check response?
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void closeCursors();
|
|
||||||
|
|
||||||
void commit()
|
void commit() override;
|
||||||
{
|
void abort() override;
|
||||||
closeCursors();
|
|
||||||
if(int rc = mdb_txn_commit(d_txn)) {
|
|
||||||
throw std::runtime_error("committing: " + std::string(mdb_strerror(rc)));
|
|
||||||
}
|
|
||||||
d_parent->decRWTX();
|
|
||||||
|
|
||||||
d_txn=0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void abort()
|
|
||||||
{
|
|
||||||
closeCursors();
|
|
||||||
mdb_txn_abort(d_txn); // XXX check error?
|
|
||||||
d_txn = 0;
|
|
||||||
d_parent->decRWTX();
|
|
||||||
}
|
|
||||||
|
|
||||||
void clear(MDB_dbi dbi);
|
void clear(MDB_dbi dbi);
|
||||||
|
|
||||||
|
@ -529,79 +574,35 @@ public:
|
||||||
|
|
||||||
MDBDbi openDB(string_view dbname, int flags)
|
MDBDbi openDB(string_view dbname, int flags)
|
||||||
{
|
{
|
||||||
return MDBDbi(d_parent->d_env, d_txn, dbname, flags);
|
return MDBDbi(environment().d_env, d_txn, dbname, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MDBRWCursor getRWCursor(const MDBDbi&);
|
||||||
MDBRWCursor getCursor(const MDBDbi&);
|
MDBRWCursor getCursor(const MDBDbi&);
|
||||||
|
|
||||||
void reportCursor(MDBRWCursor* child)
|
MDBRWTransaction getRWTransaction();
|
||||||
{
|
MDBROTransaction getROTransaction();
|
||||||
d_cursors.insert(child);
|
|
||||||
}
|
|
||||||
void unreportCursor(MDBRWCursor* child)
|
|
||||||
{
|
|
||||||
d_cursors.erase(child);
|
|
||||||
}
|
|
||||||
|
|
||||||
void reportCursorMove(MDBRWCursor* from, MDBRWCursor* to)
|
|
||||||
{
|
|
||||||
d_cursors.erase(from);
|
|
||||||
d_cursors.insert(to);
|
|
||||||
}
|
|
||||||
|
|
||||||
operator MDB_txn*&()
|
|
||||||
{
|
|
||||||
return d_txn;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
std::set<MDBRWCursor*> d_cursors;
|
|
||||||
MDBEnv* d_parent;
|
|
||||||
MDB_txn* d_txn;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/* "A cursor in a write-transaction can be closed before its transaction ends, and will otherwise be closed when its transaction ends"
|
/* "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
|
This is a problem for us since it may means we are closing the cursor twice, which is bad
|
||||||
*/
|
*/
|
||||||
class MDBRWCursor : public MDBGenCursor<MDBRWTransaction>
|
class MDBRWCursor : public MDBGenCursor<MDBRWTransactionImpl, MDBRWCursor>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
MDBRWCursor(MDBRWTransaction* parent, const MDB_dbi& dbi) : MDBGenCursor<MDBRWTransaction>(parent)
|
MDBRWCursor() = default;
|
||||||
{
|
using MDBGenCursor<MDBRWTransactionImpl, MDBRWCursor>::MDBGenCursor;
|
||||||
int rc= mdb_cursor_open(d_parent->d_txn, dbi, &d_cursor);
|
MDBRWCursor(const MDBRWCursor &src) = delete;
|
||||||
if(rc) {
|
MDBRWCursor(MDBRWCursor &&src) = default;
|
||||||
throw std::runtime_error("Error creating RW cursor: "+std::string(mdb_strerror(rc)));
|
MDBRWCursor &operator=(const MDBRWCursor &src) = delete;
|
||||||
}
|
MDBRWCursor &operator=(MDBRWCursor &&src) = default;
|
||||||
d_parent->reportCursor(this);
|
~MDBRWCursor() = default;
|
||||||
}
|
|
||||||
MDBRWCursor(MDBRWCursor&& rhs) : MDBGenCursor<MDBRWTransaction>(rhs.d_parent)
|
|
||||||
{
|
|
||||||
d_cursor = rhs.d_cursor;
|
|
||||||
rhs.d_cursor=0;
|
|
||||||
d_parent->reportCursorMove(&rhs, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void close()
|
|
||||||
{
|
|
||||||
if(d_cursor)
|
|
||||||
mdb_cursor_close(d_cursor);
|
|
||||||
d_cursor=0;
|
|
||||||
}
|
|
||||||
|
|
||||||
~MDBRWCursor()
|
|
||||||
{
|
|
||||||
if(d_cursor)
|
|
||||||
mdb_cursor_close(d_cursor);
|
|
||||||
d_parent->unreportCursor(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void put(const MDBOutVal& key, const MDBInVal& data)
|
void put(const MDBOutVal& key, const MDBInVal& data)
|
||||||
{
|
{
|
||||||
int rc = mdb_cursor_put(d_cursor,
|
int rc = mdb_cursor_put(*this,
|
||||||
const_cast<MDB_val*>(&key.d_mdbval),
|
const_cast<MDB_val*>(&key.d_mdbval),
|
||||||
const_cast<MDB_val*>(&data.d_mdbval), MDB_CURRENT);
|
const_cast<MDB_val*>(&data.d_mdbval), MDB_CURRENT);
|
||||||
if(rc)
|
if(rc)
|
||||||
throw std::runtime_error("mdb_cursor_put: " + std::string(mdb_strerror(rc)));
|
throw std::runtime_error("mdb_cursor_put: " + std::string(mdb_strerror(rc)));
|
||||||
}
|
}
|
||||||
|
@ -610,14 +611,15 @@ public:
|
||||||
int put(const MDBOutVal& key, const MDBOutVal& data, int flags=0)
|
int put(const MDBOutVal& key, const MDBOutVal& data, int flags=0)
|
||||||
{
|
{
|
||||||
// XXX check errors
|
// XXX check errors
|
||||||
return mdb_cursor_put(d_cursor,
|
return mdb_cursor_put(*this,
|
||||||
const_cast<MDB_val*>(&key.d_mdbval),
|
const_cast<MDB_val*>(&key.d_mdbval),
|
||||||
const_cast<MDB_val*>(&data.d_mdbval), flags);
|
const_cast<MDB_val*>(&data.d_mdbval), flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
int del(int flags=0)
|
int del(int flags=0)
|
||||||
{
|
{
|
||||||
return mdb_cursor_del(d_cursor, flags);
|
return mdb_cursor_del(*this, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
unsigned int MDBGetMaxID(MDBRWTransaction& txn, MDBDbi& dbi)
|
unsigned int MDBGetMaxID(MDBRWTransaction& txn, MDBDbi& dbi)
|
||||||
{
|
{
|
||||||
auto cursor = txn.getCursor(dbi);
|
auto cursor = txn->getRWCursor(dbi);
|
||||||
MDBOutVal maxidval, maxcontent;
|
MDBOutVal maxidval, maxcontent;
|
||||||
unsigned int maxid{0};
|
unsigned int maxid{0};
|
||||||
if(!cursor.get(maxidval, maxcontent, MDB_LAST)) {
|
if(!cursor.get(maxidval, maxcontent, MDB_LAST)) {
|
||||||
|
|
|
@ -101,12 +101,12 @@ struct LMDBIndexOps
|
||||||
explicit LMDBIndexOps(Parent* parent) : d_parent(parent){}
|
explicit LMDBIndexOps(Parent* parent) : d_parent(parent){}
|
||||||
void put(MDBRWTransaction& txn, const Class& t, uint32_t id, int flags=0)
|
void put(MDBRWTransaction& txn, const Class& t, uint32_t id, int flags=0)
|
||||||
{
|
{
|
||||||
txn.put(d_idx, keyConv(d_parent->getMember(t)), id, flags);
|
txn->put(d_idx, keyConv(d_parent->getMember(t)), id, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
void del(MDBRWTransaction& txn, const Class& t, uint32_t id)
|
void del(MDBRWTransaction& txn, const Class& t, uint32_t id)
|
||||||
{
|
{
|
||||||
if(int rc = txn.del(d_idx, keyConv(d_parent->getMember(t)), id)) {
|
if(int rc = txn->del(d_idx, keyConv(d_parent->getMember(t)), id)) {
|
||||||
throw std::runtime_error("Error deleting from index: " + std::string(mdb_strerror(rc)));
|
throw std::runtime_error("Error deleting from index: " + std::string(mdb_strerror(rc)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -205,7 +205,7 @@ public:
|
||||||
uint32_t size()
|
uint32_t size()
|
||||||
{
|
{
|
||||||
MDB_stat stat;
|
MDB_stat stat;
|
||||||
mdb_stat(*d_parent.d_txn, d_parent.d_parent->d_main, &stat);
|
mdb_stat(**d_parent.d_txn, d_parent.d_parent->d_main, &stat);
|
||||||
return stat.ms_entries;
|
return stat.ms_entries;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -214,7 +214,7 @@ public:
|
||||||
uint32_t size()
|
uint32_t size()
|
||||||
{
|
{
|
||||||
MDB_stat stat;
|
MDB_stat stat;
|
||||||
mdb_stat(*d_parent.d_txn, std::get<N>(d_parent.d_parent->d_tuple).d_idx, &stat);
|
mdb_stat(**d_parent.d_txn, std::get<N>(d_parent.d_parent->d_tuple).d_idx, &stat);
|
||||||
return stat.ms_entries;
|
return stat.ms_entries;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -222,7 +222,7 @@ public:
|
||||||
bool get(uint32_t id, T& t)
|
bool get(uint32_t id, T& t)
|
||||||
{
|
{
|
||||||
MDBOutVal data;
|
MDBOutVal data;
|
||||||
if(d_parent.d_txn->get(d_parent.d_parent->d_main, id, data))
|
if((*d_parent.d_txn)->get(d_parent.d_parent->d_main, id, data))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
serFromString(data.get<std::string>(), t);
|
serFromString(data.get<std::string>(), t);
|
||||||
|
@ -234,7 +234,7 @@ public:
|
||||||
uint32_t get(const typename std::tuple_element<N, tuple_t>::type::type& key, T& out)
|
uint32_t get(const typename std::tuple_element<N, tuple_t>::type::type& key, T& out)
|
||||||
{
|
{
|
||||||
MDBOutVal id;
|
MDBOutVal id;
|
||||||
if(!d_parent.d_txn->get(std::get<N>(d_parent.d_parent->d_tuple).d_idx, keyConv(key), id)) {
|
if(!(*d_parent.d_txn)->get(std::get<N>(d_parent.d_parent->d_tuple).d_idx, keyConv(key), id)) {
|
||||||
if(get(id.get<uint32_t>(), out))
|
if(get(id.get<uint32_t>(), out))
|
||||||
return id.get<uint32_t>();
|
return id.get<uint32_t>();
|
||||||
}
|
}
|
||||||
|
@ -245,7 +245,7 @@ public:
|
||||||
template<int N>
|
template<int N>
|
||||||
uint32_t cardinality()
|
uint32_t cardinality()
|
||||||
{
|
{
|
||||||
auto cursor = d_parent.d_txn->getCursor(std::get<N>(d_parent.d_parent->d_tuple).d_idx);
|
auto cursor = (*d_parent.d_txn)->getCursor(std::get<N>(d_parent.d_parent->d_tuple).d_idx);
|
||||||
bool first = true;
|
bool first = true;
|
||||||
MDBOutVal key, data;
|
MDBOutVal key, data;
|
||||||
uint32_t count = 0;
|
uint32_t count = 0;
|
||||||
|
@ -284,7 +284,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
if(d_on_index) {
|
if(d_on_index) {
|
||||||
if(d_parent->d_txn->get(d_parent->d_parent->d_main, d_id, d_data))
|
if((*d_parent->d_txn)->get(d_parent->d_parent->d_main, d_id, d_data))
|
||||||
throw std::runtime_error("Missing id in constructor");
|
throw std::runtime_error("Missing id in constructor");
|
||||||
serFromString(d_data.get<std::string>(), d_t);
|
serFromString(d_data.get<std::string>(), d_t);
|
||||||
}
|
}
|
||||||
|
@ -309,7 +309,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
if(d_on_index) {
|
if(d_on_index) {
|
||||||
if(d_parent->d_txn->get(d_parent->d_parent->d_main, d_id, d_data))
|
if((*d_parent->d_txn)->get(d_parent->d_parent->d_main, d_id, d_data))
|
||||||
throw std::runtime_error("Missing id in constructor");
|
throw std::runtime_error("Missing id in constructor");
|
||||||
serFromString(d_data.get<std::string>(), d_t);
|
serFromString(d_data.get<std::string>(), d_t);
|
||||||
}
|
}
|
||||||
|
@ -362,7 +362,7 @@ public:
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if(d_on_index) {
|
if(d_on_index) {
|
||||||
if(d_parent->d_txn->get(d_parent->d_parent->d_main, d_id, data))
|
if((*d_parent->d_txn)->get(d_parent->d_parent->d_main, d_id, data))
|
||||||
throw std::runtime_error("Missing id field");
|
throw std::runtime_error("Missing id field");
|
||||||
if(filter && !filter(data))
|
if(filter && !filter(data))
|
||||||
goto next;
|
goto next;
|
||||||
|
@ -419,7 +419,7 @@ public:
|
||||||
template<int N>
|
template<int N>
|
||||||
iter_t genbegin(MDB_cursor_op op)
|
iter_t genbegin(MDB_cursor_op op)
|
||||||
{
|
{
|
||||||
typename Parent::cursor_t cursor = d_parent.d_txn->getCursor(std::get<N>(d_parent.d_parent->d_tuple).d_idx);
|
typename Parent::cursor_t cursor = (*d_parent.d_txn)->getCursor(std::get<N>(d_parent.d_parent->d_tuple).d_idx);
|
||||||
|
|
||||||
MDBOutVal out, id;
|
MDBOutVal out, id;
|
||||||
|
|
||||||
|
@ -445,7 +445,7 @@ public:
|
||||||
|
|
||||||
iter_t begin()
|
iter_t begin()
|
||||||
{
|
{
|
||||||
typename Parent::cursor_t cursor = d_parent.d_txn->getCursor(d_parent.d_parent->d_main);
|
typename Parent::cursor_t cursor = (*d_parent.d_txn)->getCursor(d_parent.d_parent->d_main);
|
||||||
|
|
||||||
MDBOutVal out, id;
|
MDBOutVal out, id;
|
||||||
|
|
||||||
|
@ -466,7 +466,7 @@ public:
|
||||||
template<int N>
|
template<int N>
|
||||||
iter_t genfind(const typename std::tuple_element<N, tuple_t>::type::type& key, MDB_cursor_op op)
|
iter_t genfind(const typename std::tuple_element<N, tuple_t>::type::type& key, MDB_cursor_op op)
|
||||||
{
|
{
|
||||||
typename Parent::cursor_t cursor = d_parent.d_txn->getCursor(std::get<N>(d_parent.d_parent->d_tuple).d_idx);
|
typename Parent::cursor_t cursor = (*d_parent.d_txn)->getCursor(std::get<N>(d_parent.d_parent->d_tuple).d_idx);
|
||||||
|
|
||||||
std::string keystr = keyConv(key);
|
std::string keystr = keyConv(key);
|
||||||
MDBInVal in(keystr);
|
MDBInVal in(keystr);
|
||||||
|
@ -498,7 +498,7 @@ public:
|
||||||
template<int N>
|
template<int N>
|
||||||
std::pair<iter_t,eiter_t> equal_range(const typename std::tuple_element<N, tuple_t>::type::type& key)
|
std::pair<iter_t,eiter_t> equal_range(const typename std::tuple_element<N, tuple_t>::type::type& key)
|
||||||
{
|
{
|
||||||
typename Parent::cursor_t cursor = d_parent.d_txn->getCursor(std::get<N>(d_parent.d_parent->d_tuple).d_idx);
|
typename Parent::cursor_t cursor = (*d_parent.d_txn)->getCursor(std::get<N>(d_parent.d_parent->d_tuple).d_idx);
|
||||||
|
|
||||||
std::string keyString=keyConv(key);
|
std::string keyString=keyConv(key);
|
||||||
MDBInVal in(keyString);
|
MDBInVal in(keyString);
|
||||||
|
@ -517,7 +517,7 @@ public:
|
||||||
template<int N>
|
template<int N>
|
||||||
std::pair<iter_t,eiter_t> prefix_range(const typename std::tuple_element<N, tuple_t>::type::type& key)
|
std::pair<iter_t,eiter_t> prefix_range(const typename std::tuple_element<N, tuple_t>::type::type& key)
|
||||||
{
|
{
|
||||||
typename Parent::cursor_t cursor = d_parent.d_txn->getCursor(std::get<N>(d_parent.d_parent->d_tuple).d_idx);
|
typename Parent::cursor_t cursor = (*d_parent.d_txn)->getCursor(std::get<N>(d_parent.d_parent->d_tuple).d_idx);
|
||||||
|
|
||||||
std::string keyString=keyConv(key);
|
std::string keyString=keyConv(key);
|
||||||
MDBInVal in(keyString);
|
MDBInVal in(keyString);
|
||||||
|
@ -595,7 +595,7 @@ public:
|
||||||
id = MDBGetMaxID(*d_txn, d_parent->d_main) + 1;
|
id = MDBGetMaxID(*d_txn, d_parent->d_main) + 1;
|
||||||
flags = MDB_APPEND;
|
flags = MDB_APPEND;
|
||||||
}
|
}
|
||||||
d_txn->put(d_parent->d_main, id, serToString(t), flags);
|
(*d_txn)->put(d_parent->d_main, id, serToString(t), flags);
|
||||||
|
|
||||||
#define insertMacro(N) std::get<N>(d_parent->d_tuple).put(*d_txn, t, id);
|
#define insertMacro(N) std::get<N>(d_parent->d_tuple).put(*d_txn, t, id);
|
||||||
insertMacro(0);
|
insertMacro(0);
|
||||||
|
@ -626,14 +626,14 @@ public:
|
||||||
if(!this->get(id, t))
|
if(!this->get(id, t))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
d_txn->del(d_parent->d_main, id);
|
(*d_txn)->del(d_parent->d_main, id);
|
||||||
clearIndex(id, t);
|
clearIndex(id, t);
|
||||||
}
|
}
|
||||||
|
|
||||||
//! clear database & indexes (by hand!)
|
//! clear database & indexes (by hand!)
|
||||||
void clear()
|
void clear()
|
||||||
{
|
{
|
||||||
auto cursor = d_txn->getCursor(d_parent->d_main);
|
auto cursor = (*d_txn)->getRWCursor(d_parent->d_main);
|
||||||
bool first = true;
|
bool first = true;
|
||||||
MDBOutVal key, data;
|
MDBOutVal key, data;
|
||||||
while(!cursor.get(key, data, first ? MDB_FIRST : MDB_NEXT)) {
|
while(!cursor.get(key, data, first ? MDB_FIRST : MDB_NEXT)) {
|
||||||
|
@ -648,13 +648,13 @@ public:
|
||||||
//! commit this transaction
|
//! commit this transaction
|
||||||
void commit()
|
void commit()
|
||||||
{
|
{
|
||||||
d_txn->commit();
|
(*d_txn)->commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
//! abort this transaction
|
//! abort this transaction
|
||||||
void abort()
|
void abort()
|
||||||
{
|
{
|
||||||
d_txn->abort();
|
(*d_txn)->abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef MDBRWCursor cursor_t;
|
typedef MDBRWCursor cursor_t;
|
||||||
|
|
|
@ -18,7 +18,7 @@ static void closeTest()
|
||||||
|
|
||||||
auto txn = env->getROTransaction();
|
auto txn = env->getROTransaction();
|
||||||
for(auto& d : {&main, &dbi, &hyc}) {
|
for(auto& d : {&main, &dbi, &hyc}) {
|
||||||
auto rocursor = txn.getCursor(*d);
|
auto rocursor = txn->getCursor(*d);
|
||||||
MDBOutVal key, data;
|
MDBOutVal key, data;
|
||||||
if(rocursor.get(key, data, MDB_FIRST))
|
if(rocursor.get(key, data, MDB_FIRST))
|
||||||
continue;
|
continue;
|
||||||
|
@ -41,8 +41,8 @@ try
|
||||||
for(int n=0; n < 15; ++n) {
|
for(int n=0; n < 15; ++n) {
|
||||||
auto txn = env->getRWTransaction();
|
auto txn = env->getRWTransaction();
|
||||||
int val = n + 1000*tid;
|
int val = n + 1000*tid;
|
||||||
txn.put(dbi, val, val);
|
txn->put(dbi, val, val);
|
||||||
txn.commit();
|
txn->commit();
|
||||||
cout << "Done with transaction "<<n<<" in thread " << tid<<endl;
|
cout << "Done with transaction "<<n<<" in thread " << tid<<endl;
|
||||||
}
|
}
|
||||||
cout<<"Done with thread "<<tid<<endl;
|
cout<<"Done with thread "<<tid<<endl;
|
||||||
|
@ -62,7 +62,7 @@ try
|
||||||
auto txn = env->getROTransaction();
|
auto txn = env->getROTransaction();
|
||||||
int val = n + 1000*tid;
|
int val = n + 1000*tid;
|
||||||
MDBOutVal res;
|
MDBOutVal res;
|
||||||
if(txn.get(dbi, val, res)) {
|
if(txn->get(dbi, val, res)) {
|
||||||
throw std::runtime_error("no record");
|
throw std::runtime_error("no record");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,9 +85,9 @@ void doFill()
|
||||||
auto txn = env->getRWTransaction();
|
auto txn = env->getRWTransaction();
|
||||||
for(int j=0; j < 1000000; ++j) {
|
for(int j=0; j < 1000000; ++j) {
|
||||||
MDBInVal mv(n*1000000+j);
|
MDBInVal mv(n*1000000+j);
|
||||||
txn.put(dbi, mv, mv, 0);
|
txn->put(dbi, mv, mv, 0);
|
||||||
}
|
}
|
||||||
txn.commit();
|
txn->commit();
|
||||||
}
|
}
|
||||||
cout<<"Done filling"<<endl;
|
cout<<"Done filling"<<endl;
|
||||||
}
|
}
|
||||||
|
@ -104,7 +104,7 @@ void doMeasure()
|
||||||
for(int j=0; j < 1000000; ++j) {
|
for(int j=0; j < 1000000; ++j) {
|
||||||
MDBInVal mv(n*1000000+j);
|
MDBInVal mv(n*1000000+j);
|
||||||
MDBOutVal res;
|
MDBOutVal res;
|
||||||
if(!txn.get(dbi, mv, res))
|
if(!txn->get(dbi, mv, res))
|
||||||
++count;
|
++count;
|
||||||
}
|
}
|
||||||
cout<<count<<" ";
|
cout<<count<<" ";
|
||||||
|
|
|
@ -5,8 +5,8 @@ using namespace std;
|
||||||
|
|
||||||
void countDB(MDBEnv& env, MDBROTransaction& txn, const std::string& dbname)
|
void countDB(MDBEnv& env, MDBROTransaction& txn, const std::string& dbname)
|
||||||
{
|
{
|
||||||
auto db = txn.openDB(dbname, 0);
|
auto db = txn->openDB(dbname, 0);
|
||||||
auto cursor = txn.getCursor(db);
|
auto cursor = txn->getCursor(db);
|
||||||
uint32_t count = 0;
|
uint32_t count = 0;
|
||||||
MDBOutVal key, val;
|
MDBOutVal key, val;
|
||||||
while(!cursor.get(key, val, count ? MDB_NEXT : MDB_FIRST)) {
|
while(!cursor.get(key, val, count ? MDB_NEXT : MDB_FIRST)) {
|
||||||
|
@ -27,7 +27,7 @@ int main(int argc, char** argv)
|
||||||
auto main = env.openDB("", 0);
|
auto main = env.openDB("", 0);
|
||||||
auto txn = env.getROTransaction();
|
auto txn = env.getROTransaction();
|
||||||
|
|
||||||
auto cursor = txn.getCursor(main);
|
auto cursor = txn->getCursor(main);
|
||||||
|
|
||||||
MDBOutVal key, val;
|
MDBOutVal key, val;
|
||||||
if(cursor.get(key, val, MDB_FIRST)) {
|
if(cursor.get(key, val, MDB_FIRST)) {
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
#include "lmdb-safe.hh"
|
#include "lmdb-safe.hh"
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
unlink("./multi");
|
unlink("./multi");
|
||||||
|
@ -8,26 +10,26 @@ int main()
|
||||||
auto dbi = env->openDB("qnames", MDB_DUPSORT | MDB_CREATE);
|
auto dbi = env->openDB("qnames", MDB_DUPSORT | MDB_CREATE);
|
||||||
|
|
||||||
auto txn = env->getRWTransaction();
|
auto txn = env->getRWTransaction();
|
||||||
txn.clear(dbi);
|
txn->clear(dbi);
|
||||||
|
|
||||||
txn.put(dbi, "bdb", "old");
|
txn->put(dbi, "bdb", "old");
|
||||||
txn.put(dbi, "lmdb", "hot");
|
txn->put(dbi, "lmdb", "hot");
|
||||||
txn.put(dbi, "lmdb", "fast");
|
txn->put(dbi, "lmdb", "fast");
|
||||||
txn.put(dbi, "lmdb", "zooms");
|
txn->put(dbi, "lmdb", "zooms");
|
||||||
txn.put(dbi, "lmdb", "c");
|
txn->put(dbi, "lmdb", "c");
|
||||||
txn.put(dbi, "mdb", "old name");
|
txn->put(dbi, "mdb", "old name");
|
||||||
|
|
||||||
string_view v1;
|
string_view v1;
|
||||||
if(!txn.get(dbi, "mdb", v1)) {
|
if(!txn->get(dbi, "mdb", v1)) {
|
||||||
cout<<v1<<endl;
|
cout<<v1<<endl;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
cout << "found nothing" << endl;
|
cout << "found nothing" << endl;
|
||||||
}
|
}
|
||||||
txn.commit();
|
txn->commit();
|
||||||
|
|
||||||
txn = env->getRWTransaction();
|
txn = env->getRWTransaction();
|
||||||
auto cursor = txn.getCursor(dbi);
|
auto cursor = txn->getRWCursor(dbi);
|
||||||
|
|
||||||
MDBOutVal key, data;
|
MDBOutVal key, data;
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,7 @@ struct Record
|
||||||
|
|
||||||
static unsigned int getMaxID(MDBRWTransaction& txn, MDBDbi& dbi)
|
static unsigned int getMaxID(MDBRWTransaction& txn, MDBDbi& dbi)
|
||||||
{
|
{
|
||||||
auto cursor = txn.getCursor(dbi);
|
auto cursor = txn->getRWCursor(dbi);
|
||||||
MDBOutVal maxidval, maxcontent;
|
MDBOutVal maxidval, maxcontent;
|
||||||
unsigned int maxid{0};
|
unsigned int maxid{0};
|
||||||
if(!cursor.get(maxidval, maxcontent, MDB_LAST)) {
|
if(!cursor.get(maxidval, maxcontent, MDB_LAST)) {
|
||||||
|
@ -42,9 +42,9 @@ static void store(MDBRWTransaction& txn, MDBDbi& records, MDBDbi& domainidx, MDB
|
||||||
boost::archive::binary_oarchive oa(oss,boost::archive::no_header );
|
boost::archive::binary_oarchive oa(oss,boost::archive::no_header );
|
||||||
oa << r;
|
oa << r;
|
||||||
|
|
||||||
txn.put(records, r.id, oss.str(), MDB_APPEND);
|
txn->put(records, r.id, oss.str(), MDB_APPEND);
|
||||||
txn.put(domainidx, r.domain_id, r.id);
|
txn->put(domainidx, r.domain_id, r.id);
|
||||||
txn.put(nameidx, r.name, r.id);
|
txn->put(nameidx, r.name, r.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -122,12 +122,12 @@ int main(int argc, char** argv)
|
||||||
store(txn, records, domainidx, nameidx, r);
|
store(txn, records, domainidx, nameidx, r);
|
||||||
}
|
}
|
||||||
|
|
||||||
txn.commit();
|
txn->commit();
|
||||||
|
|
||||||
auto rotxn = env->getROTransaction();
|
auto rotxn = env->getROTransaction();
|
||||||
auto rotxn2 = env->getROTransaction();
|
auto rotxn2 = env->getROTransaction();
|
||||||
|
|
||||||
auto rocursor = rotxn.getCursor(nameidx);
|
auto rocursor = rotxn->getCursor(nameidx);
|
||||||
|
|
||||||
MDBOutVal data;
|
MDBOutVal data;
|
||||||
int count = 0;
|
int count = 0;
|
||||||
|
@ -143,7 +143,7 @@ int main(int argc, char** argv)
|
||||||
cout<<"Got something: id="<<id<<endl;
|
cout<<"Got something: id="<<id<<endl;
|
||||||
MDBOutVal record;
|
MDBOutVal record;
|
||||||
|
|
||||||
if(!rotxn.get(records, data, record)) {
|
if(!rotxn->get(records, data, record)) {
|
||||||
Record test;
|
Record test;
|
||||||
stringstream istr{record.get<string>()};
|
stringstream istr{record.get<string>()};
|
||||||
boost::archive::binary_iarchive oi(istr,boost::archive::no_header );
|
boost::archive::binary_iarchive oi(istr,boost::archive::no_header );
|
||||||
|
|
|
@ -10,42 +10,42 @@ int main(int argc, char** argv)
|
||||||
MDBInVal key("counter");
|
MDBInVal key("counter");
|
||||||
|
|
||||||
auto rwtxn = env->getRWTransaction();
|
auto rwtxn = env->getRWTransaction();
|
||||||
rwtxn.put(main, "counter", "1234");
|
rwtxn->put(main, "counter", "1234");
|
||||||
rwtxn.put(main, MDBInVal::fromStruct(std::make_pair(12,13)), "hoi dan 12,13");
|
rwtxn->put(main, MDBInVal::fromStruct(std::make_pair(12,13)), "hoi dan 12,13");
|
||||||
|
|
||||||
rwtxn.put(main, MDBInVal::fromStruct(std::make_pair(14,15)),
|
rwtxn->put(main, MDBInVal::fromStruct(std::make_pair(14,15)),
|
||||||
MDBInVal::fromStruct(std::make_pair(20,23)));
|
MDBInVal::fromStruct(std::make_pair(20,23)));
|
||||||
|
|
||||||
|
|
||||||
MDBOutVal out;
|
MDBOutVal out;
|
||||||
if(!rwtxn.get(main, MDBInVal::fromStruct(std::make_pair(12,13)), out))
|
if(!rwtxn->get(main, MDBInVal::fromStruct(std::make_pair(12,13)), out))
|
||||||
cout << "Got: " << out.get<string_view>() << endl;
|
cout << "Got: " << out.get<string_view>() << endl;
|
||||||
else
|
else
|
||||||
cout << "Got nothing!1"<<endl;
|
cout << "Got nothing!1"<<endl;
|
||||||
|
|
||||||
if(!rwtxn.get(main, MDBInVal::fromStruct(std::make_pair(14,15)), out)) {
|
if(!rwtxn->get(main, MDBInVal::fromStruct(std::make_pair(14,15)), out)) {
|
||||||
auto res = out.get_struct<pair<int,int>>();
|
auto res = out.get_struct<pair<int,int>>();
|
||||||
cout << "Got: " << res.first<<", "<<res.second << endl;
|
cout << "Got: " << res.first<<", "<<res.second << endl;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
cout << "Got nothing!1"<<endl;
|
cout << "Got nothing!1"<<endl;
|
||||||
|
|
||||||
rwtxn.put(main, 12.12, 7.3);
|
rwtxn->put(main, 12.12, 7.3);
|
||||||
if(!rwtxn.get(main, 12.12, out)) {
|
if(!rwtxn->get(main, 12.12, out)) {
|
||||||
cout<<"Got: "<< out.get<double>() <<endl;
|
cout<<"Got: "<< out.get<double>() <<endl;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
cout << "Got nothing!1"<<endl;
|
cout << "Got nothing!1"<<endl;
|
||||||
|
|
||||||
|
|
||||||
rwtxn.commit();
|
rwtxn->commit();
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if(argc==1) {
|
if(argc==1) {
|
||||||
for(;;) {
|
for(;;) {
|
||||||
auto rotxn = env->getROTransaction();
|
auto rotxn = env->getROTransaction();
|
||||||
MDBOutVal data;
|
MDBOutVal data;
|
||||||
if(!rotxn.get(main, key, data)) {
|
if(!rotxn->get(main, key, data)) {
|
||||||
cout<<"Counter is "<<data.get<unsigned int>() << endl;
|
cout<<"Counter is "<<data.get<unsigned int>() << endl;
|
||||||
cout <<data.get<string>() << endl;
|
cout <<data.get<string>() << endl;
|
||||||
cout<<data.get<string_view>() << endl;
|
cout<<data.get<string_view>() << endl;
|
||||||
|
@ -73,10 +73,10 @@ int main(int argc, char** argv)
|
||||||
cout<<"Did resize"<<endl;
|
cout<<"Did resize"<<endl;
|
||||||
}
|
}
|
||||||
auto txn = env->getRWTransaction();
|
auto txn = env->getRWTransaction();
|
||||||
txn.put(main, key, MDBInVal(n));
|
txn->put(main, key, MDBInVal(n));
|
||||||
for(int k=0; k < 100; ++k)
|
for(int k=0; k < 100; ++k)
|
||||||
txn.put(main, MDBInVal(n+1000*k), MDBInVal(n+1000*k));
|
txn->put(main, MDBInVal(n+1000*k), MDBInVal(n+1000*k));
|
||||||
txn.commit();
|
txn->commit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,7 @@ int main(int argc, char** argv)
|
||||||
limit = atoi(argv[1]);
|
limit = atoi(argv[1]);
|
||||||
|
|
||||||
cout<<"Counting records.. "; cout.flush();
|
cout<<"Counting records.. "; cout.flush();
|
||||||
auto cursor=txn.getCursor(dbi);
|
auto cursor = txn->getCursor(dbi);
|
||||||
MDBOutVal key, data;
|
MDBOutVal key, data;
|
||||||
int count=0;
|
int count=0;
|
||||||
while(!cursor.get(key, data, count ? MDB_NEXT : MDB_FIRST)) {
|
while(!cursor.get(key, data, count ? MDB_NEXT : MDB_FIRST)) {
|
||||||
|
@ -40,15 +40,15 @@ int main(int argc, char** argv)
|
||||||
cout<<"Have "<<count<<"!"<<endl;
|
cout<<"Have "<<count<<"!"<<endl;
|
||||||
|
|
||||||
cout<<"Clearing records.. "; cout.flush();
|
cout<<"Clearing records.. "; cout.flush();
|
||||||
mdb_drop(txn, dbi, 0); // clear records
|
mdb_drop(*txn, dbi, 0); // clear records
|
||||||
cout<<"Done!"<<endl;
|
cout<<"Done!"<<endl;
|
||||||
|
|
||||||
cout << "Adding "<<limit<<" values .. "; cout.flush();
|
cout << "Adding "<<limit<<" values .. "; cout.flush();
|
||||||
for(unsigned long n = 0 ; n < limit; ++n) {
|
for(unsigned long n = 0 ; n < limit; ++n) {
|
||||||
txn.put(dbi, n, n, MDB_APPEND);
|
txn->put(dbi, n, n, MDB_APPEND);
|
||||||
}
|
}
|
||||||
cout <<"Done!"<<endl;
|
cout <<"Done!"<<endl;
|
||||||
cout <<"Calling commit.. "; cout.flush();
|
cout <<"Calling commit.. "; cout.flush();
|
||||||
txn.commit();
|
txn->commit();
|
||||||
cout<<"Done!"<<endl;
|
cout<<"Done!"<<endl;
|
||||||
}
|
}
|
||||||
|
|
283
test-basic.cc
283
test-basic.cc
|
@ -13,20 +13,20 @@ TEST_CASE("Most basic tests", "[mostbasic]") {
|
||||||
REQUIRE(1);
|
REQUIRE(1);
|
||||||
|
|
||||||
MDBDbi main = env.openDB("", MDB_CREATE);
|
MDBDbi main = env.openDB("", MDB_CREATE);
|
||||||
|
|
||||||
auto txn = env.getRWTransaction();
|
auto txn = env.getRWTransaction();
|
||||||
MDBOutVal out;
|
MDBOutVal out;
|
||||||
|
|
||||||
REQUIRE(txn.get(main, "lmdb", out) == MDB_NOTFOUND);
|
REQUIRE(txn->get(main, "lmdb", out) == MDB_NOTFOUND);
|
||||||
|
|
||||||
txn.put(main, "lmdb", "hot");
|
|
||||||
|
|
||||||
REQUIRE(txn.get(main, "lmdb", out) == 0);
|
txn->put(main, "lmdb", "hot");
|
||||||
|
|
||||||
|
REQUIRE(txn->get(main, "lmdb", out) == 0);
|
||||||
REQUIRE(out.get<std::string>() == "hot");
|
REQUIRE(out.get<std::string>() == "hot");
|
||||||
txn.abort();
|
txn->abort();
|
||||||
|
|
||||||
auto rotxn = env.getROTransaction();
|
auto rotxn = env.getROTransaction();
|
||||||
REQUIRE(rotxn.get(main, "lmdb", out) == MDB_NOTFOUND);
|
REQUIRE(rotxn->get(main, "lmdb", out) == MDB_NOTFOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Range tests", "[range]") {
|
TEST_CASE("Range tests", "[range]") {
|
||||||
|
@ -36,20 +36,20 @@ TEST_CASE("Range tests", "[range]") {
|
||||||
REQUIRE(1);
|
REQUIRE(1);
|
||||||
|
|
||||||
MDBDbi main = env.openDB("", MDB_CREATE);
|
MDBDbi main = env.openDB("", MDB_CREATE);
|
||||||
|
|
||||||
auto txn = env.getRWTransaction();
|
auto txn = env.getRWTransaction();
|
||||||
MDBOutVal out;
|
MDBOutVal out;
|
||||||
|
|
||||||
REQUIRE(txn.get(main, "lmdb", out) == MDB_NOTFOUND);
|
REQUIRE(txn->get(main, "lmdb", out) == MDB_NOTFOUND);
|
||||||
|
|
||||||
txn.put(main, "bert", "hubert");
|
txn->put(main, "bert", "hubert");
|
||||||
txn.put(main, "bertt", "1975");
|
txn->put(main, "bertt", "1975");
|
||||||
txn.put(main, "berthubert", "lmdb");
|
txn->put(main, "berthubert", "lmdb");
|
||||||
txn.put(main, "bert1", "one");
|
txn->put(main, "bert1", "one");
|
||||||
txn.put(main, "beru", "not");
|
txn->put(main, "beru", "not");
|
||||||
|
|
||||||
{
|
{
|
||||||
auto cursor = txn.getCursor(main);
|
auto cursor = txn->getCursor(main);
|
||||||
MDBInVal bert("bert");
|
MDBInVal bert("bert");
|
||||||
MDBOutVal key, val;
|
MDBOutVal key, val;
|
||||||
REQUIRE(cursor.lower_bound(bert, key, val) == 0);
|
REQUIRE(cursor.lower_bound(bert, key, val) == 0);
|
||||||
|
@ -64,14 +64,14 @@ TEST_CASE("Range tests", "[range]") {
|
||||||
REQUIRE(key.get<string>() == "berthubert");
|
REQUIRE(key.get<string>() == "berthubert");
|
||||||
REQUIRE(val.get<string>() == "lmdb");
|
REQUIRE(val.get<string>() == "lmdb");
|
||||||
|
|
||||||
REQUIRE(cursor.lower_bound("kees", key, val) == MDB_NOTFOUND);
|
REQUIRE(cursor.lower_bound("kees", key, val) == MDB_NOTFOUND);
|
||||||
|
|
||||||
txn.commit();
|
txn->commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto rotxn = env.getROTransaction();
|
auto rotxn = env.getROTransaction();
|
||||||
{
|
{
|
||||||
auto cursor = rotxn.getCursor(main);
|
auto cursor = rotxn->getCursor(main);
|
||||||
MDBInVal bert("bert");
|
MDBInVal bert("bert");
|
||||||
MDBOutVal key, val;
|
MDBOutVal key, val;
|
||||||
REQUIRE(cursor.lower_bound(bert, key, val) == 0);
|
REQUIRE(cursor.lower_bound(bert, key, val) == 0);
|
||||||
|
@ -86,7 +86,244 @@ TEST_CASE("Range tests", "[range]") {
|
||||||
REQUIRE(key.get<string>() == "berthubert");
|
REQUIRE(key.get<string>() == "berthubert");
|
||||||
REQUIRE(val.get<string>() == "lmdb");
|
REQUIRE(val.get<string>() == "lmdb");
|
||||||
|
|
||||||
REQUIRE(cursor.lower_bound("kees", key, val) == MDB_NOTFOUND);
|
REQUIRE(cursor.lower_bound("kees", key, val) == MDB_NOTFOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("moving transactions")
|
||||||
|
{
|
||||||
|
unlink("./tests");
|
||||||
|
|
||||||
|
MDBEnv env("./tests", MDB_NOSUBDIR, 0600);
|
||||||
|
REQUIRE(1);
|
||||||
|
|
||||||
|
MDBDbi main = env.openDB("", MDB_CREATE);
|
||||||
|
|
||||||
|
auto txn = env.getRWTransaction();
|
||||||
|
MDBOutVal out;
|
||||||
|
|
||||||
|
REQUIRE(txn->get(main, "lmdb", out) == MDB_NOTFOUND);
|
||||||
|
|
||||||
|
txn->put(main, "bert", "hubert");
|
||||||
|
txn->put(main, "bertt", "1975");
|
||||||
|
txn->put(main, "berthubert", "lmdb");
|
||||||
|
txn->put(main, "bert1", "one");
|
||||||
|
txn->put(main, "beru", "not");
|
||||||
|
|
||||||
|
auto cursor = txn->getCursor(main);
|
||||||
|
auto txn2 = std::move(txn);
|
||||||
|
{
|
||||||
|
auto cursor2 = std::move(cursor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("transaction inheritance and moving")
|
||||||
|
{
|
||||||
|
unlink("./tests");
|
||||||
|
|
||||||
|
MDBEnv env("./tests", MDB_NOSUBDIR, 0600);
|
||||||
|
MDBDbi main = env.openDB("", MDB_CREATE);
|
||||||
|
|
||||||
|
MDBRWCursor cursor;
|
||||||
|
{
|
||||||
|
MDBRWTransaction txn = env.getRWTransaction();
|
||||||
|
MDBOutVal out;
|
||||||
|
|
||||||
|
REQUIRE(txn->get(main, "lmdb", out) == MDB_NOTFOUND);
|
||||||
|
|
||||||
|
// lets just keep this cursor to ensure that it invalidates
|
||||||
|
cursor = txn->getRWCursor(main);
|
||||||
|
txn->put(main, "bert", "hubert");
|
||||||
|
txn->put(main, "bertt", "1975");
|
||||||
|
txn->put(main, "berthubert", "lmdb");
|
||||||
|
txn->put(main, "bert1", "one");
|
||||||
|
txn->put(main, "beru", "not");
|
||||||
|
|
||||||
|
MDBROTransaction ro_txn(std::move(txn));
|
||||||
|
// despite being moved to an ro_txn (which normally commits instead of
|
||||||
|
// aborting by default)
|
||||||
|
}
|
||||||
|
|
||||||
|
CHECK(!cursor);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("nested RW transactions", "[transactions]")
|
||||||
|
{
|
||||||
|
unlink("./tests");
|
||||||
|
|
||||||
|
MDBEnv env("./tests", MDB_NOSUBDIR, 0600);
|
||||||
|
REQUIRE(1);
|
||||||
|
|
||||||
|
MDBDbi main = env.openDB("", MDB_CREATE);
|
||||||
|
|
||||||
|
/* bootstrap some data */
|
||||||
|
{
|
||||||
|
auto txn = env.getRWTransaction();
|
||||||
|
txn->put(main, "bert", "hubert");
|
||||||
|
txn->put(main, "bertt", "1975");
|
||||||
|
txn->put(main, "berthubert", "lmdb");
|
||||||
|
txn->put(main, "bert1", "one");
|
||||||
|
txn->put(main, "beru", "not");
|
||||||
|
txn->commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto main_txn = env.getRWTransaction();
|
||||||
|
main_txn->del(main, "bertt", "1975");
|
||||||
|
|
||||||
|
MDBOutVal dummy{};
|
||||||
|
CHECK(main_txn->get(main, "bertt", dummy) == MDB_NOTFOUND);
|
||||||
|
|
||||||
|
{
|
||||||
|
auto sub_txn = main_txn->getRWTransaction();
|
||||||
|
CHECK(sub_txn->get(main, "berthubert", dummy) == 0);
|
||||||
|
sub_txn->del(main, "berthubert", "lmdb");
|
||||||
|
CHECK(sub_txn->get(main, "berthubert", dummy) == MDB_NOTFOUND);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check that subtransaction got rolled back */
|
||||||
|
CHECK(main_txn->get(main, "berthubert", dummy) == 0);
|
||||||
|
/* and that the main changes are still there */
|
||||||
|
CHECK(main_txn->get(main, "bertt", dummy) == MDB_NOTFOUND);
|
||||||
|
|
||||||
|
{
|
||||||
|
auto sub_txn = main_txn->getRWTransaction();
|
||||||
|
CHECK(sub_txn->get(main, "berthubert", dummy) == 0);
|
||||||
|
sub_txn->del(main, "berthubert", "lmdb");
|
||||||
|
CHECK(sub_txn->get(main, "berthubert", dummy) == MDB_NOTFOUND);
|
||||||
|
/* this time for real! */
|
||||||
|
sub_txn->commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
CHECK(main_txn->get(main, "berthubert", dummy) == MDB_NOTFOUND);
|
||||||
|
CHECK(main_txn->get(main, "bertt", dummy) == MDB_NOTFOUND);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TEST_CASE("nesting RW -> RO", "[transactions]")
|
||||||
|
{
|
||||||
|
unlink("./tests");
|
||||||
|
|
||||||
|
MDBEnv env("./tests", MDB_NOSUBDIR, 0600);
|
||||||
|
REQUIRE(1);
|
||||||
|
|
||||||
|
MDBDbi main = env.openDB("", MDB_CREATE);
|
||||||
|
|
||||||
|
/* bootstrap some data */
|
||||||
|
{
|
||||||
|
auto txn = env.getRWTransaction();
|
||||||
|
txn->put(main, "bert", "hubert");
|
||||||
|
txn->put(main, "bertt", "1975");
|
||||||
|
txn->put(main, "berthubert", "lmdb");
|
||||||
|
txn->put(main, "bert1", "one");
|
||||||
|
txn->put(main, "beru", "not");
|
||||||
|
txn->commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto main_txn = env.getRWTransaction();
|
||||||
|
main_txn->del(main, "bertt", "1975");
|
||||||
|
|
||||||
|
MDBOutVal dummy{};
|
||||||
|
CHECK(main_txn->get(main, "bertt", dummy) == MDB_NOTFOUND);
|
||||||
|
|
||||||
|
{
|
||||||
|
MDBROTransaction sub_txn = main_txn->getROTransaction();
|
||||||
|
CHECK(sub_txn->get(main, "berthubert", dummy) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check that subtransaction got rolled back */
|
||||||
|
CHECK(main_txn->get(main, "berthubert", dummy) == 0);
|
||||||
|
/* and that the main changes are still there */
|
||||||
|
CHECK(main_txn->get(main, "bertt", dummy) == MDB_NOTFOUND);
|
||||||
|
|
||||||
|
{
|
||||||
|
auto sub_txn = main_txn->getRWTransaction();
|
||||||
|
CHECK(sub_txn->get(main, "berthubert", dummy) == 0);
|
||||||
|
sub_txn->del(main, "berthubert", "lmdb");
|
||||||
|
CHECK(sub_txn->get(main, "berthubert", dummy) == MDB_NOTFOUND);
|
||||||
|
{
|
||||||
|
MDBROTransaction sub_sub_txn = sub_txn->getROTransaction();
|
||||||
|
CHECK(sub_sub_txn->get(main, "berthubert", dummy) == MDB_NOTFOUND);
|
||||||
|
}
|
||||||
|
/* this time for real! */
|
||||||
|
sub_txn->commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
CHECK(main_txn->get(main, "berthubert", dummy) == MDB_NOTFOUND);
|
||||||
|
CHECK(main_txn->get(main, "bertt", dummy) == MDB_NOTFOUND);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("try to nest twice", "[transactions]")
|
||||||
|
{
|
||||||
|
unlink("./tests");
|
||||||
|
|
||||||
|
MDBEnv env("./tests", MDB_NOSUBDIR, 0600);
|
||||||
|
REQUIRE(1);
|
||||||
|
|
||||||
|
MDBDbi main = env.openDB("", MDB_CREATE);
|
||||||
|
|
||||||
|
/* bootstrap some data */
|
||||||
|
{
|
||||||
|
auto txn = env.getRWTransaction();
|
||||||
|
txn->put(main, "bert", "hubert");
|
||||||
|
txn->put(main, "bertt", "1975");
|
||||||
|
txn->put(main, "berthubert", "lmdb");
|
||||||
|
txn->put(main, "bert1", "one");
|
||||||
|
txn->put(main, "beru", "not");
|
||||||
|
txn->commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto main_txn = env.getRWTransaction();
|
||||||
|
main_txn->del(main, "bertt", "1975");
|
||||||
|
|
||||||
|
MDBOutVal dummy{};
|
||||||
|
CHECK(main_txn->get(main, "bertt", dummy) == MDB_NOTFOUND);
|
||||||
|
|
||||||
|
{
|
||||||
|
auto sub_txn = main_txn->getRWTransaction();
|
||||||
|
CHECK(sub_txn->get(main, "berthubert", dummy) == 0);
|
||||||
|
sub_txn->del(main, "berthubert", "lmdb");
|
||||||
|
CHECK(sub_txn->get(main, "berthubert", dummy) == MDB_NOTFOUND);
|
||||||
|
|
||||||
|
CHECK_THROWS_AS(
|
||||||
|
main_txn->getRWTransaction(),
|
||||||
|
std::runtime_error
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("transaction counter correctness for RW->RW nesting")
|
||||||
|
{
|
||||||
|
unlink("./tests");
|
||||||
|
|
||||||
|
MDBEnv env("./tests", MDB_NOSUBDIR, 0600);
|
||||||
|
REQUIRE(1);
|
||||||
|
|
||||||
|
MDBDbi main = env.openDB("", MDB_CREATE);
|
||||||
|
|
||||||
|
{
|
||||||
|
auto txn = env.getRWTransaction();
|
||||||
|
auto sub_txn = txn->getRWTransaction();
|
||||||
|
}
|
||||||
|
|
||||||
|
CHECK_NOTHROW(env.getRWTransaction());
|
||||||
|
CHECK_NOTHROW(env.getROTransaction());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("transaction counter correctness for RW->RO nesting")
|
||||||
|
{
|
||||||
|
unlink("./tests");
|
||||||
|
|
||||||
|
MDBEnv env("./tests", MDB_NOSUBDIR, 0600);
|
||||||
|
REQUIRE(1);
|
||||||
|
|
||||||
|
MDBDbi main = env.openDB("", MDB_CREATE);
|
||||||
|
|
||||||
|
{
|
||||||
|
auto txn = env.getRWTransaction();
|
||||||
|
auto sub_txn = txn->getROTransaction();
|
||||||
|
}
|
||||||
|
|
||||||
|
CHECK_NOTHROW(env.getRWTransaction());
|
||||||
|
CHECK_NOTHROW(env.getROTransaction());
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue