add tests, prefix_range
This commit is contained in:
parent
59b3b602fa
commit
b3eb2a03cb
4
Makefile
4
Makefile
|
@ -8,7 +8,7 @@ CXXFLAGS:= $(CXXVERSIONFLAG) -Wall -O2 -MMD -MP -ggdb -pthread $(INCLUDES) -Iext
|
||||||
CFLAGS:= -Wall -O2 -MMD -MP -ggdb
|
CFLAGS:= -Wall -O2 -MMD -MP -ggdb
|
||||||
|
|
||||||
PROGRAMS = lmdb-various basic-example scale-example multi-example rel-example \
|
PROGRAMS = lmdb-various basic-example scale-example multi-example rel-example \
|
||||||
resize-example typed-example test-basic lmdb-view
|
resize-example typed-example testrunner lmdb-view
|
||||||
|
|
||||||
all: $(PROGRAMS)
|
all: $(PROGRAMS)
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ clean:
|
||||||
|
|
||||||
-include *.d
|
-include *.d
|
||||||
|
|
||||||
test-basic: test-basic.o lmdb-safe.o
|
testrunner: test-basic.o typed-test.o lmdb-safe.o lmdb-typed.o -lboost_serialization
|
||||||
g++ $(CXXVERSIONFLAG) $^ -o $@ -pthread $(LIBS)
|
g++ $(CXXVERSIONFLAG) $^ -o $@ -pthread $(LIBS)
|
||||||
|
|
||||||
lmdb-various: lmdb-various.o lmdb-safe.o
|
lmdb-various: lmdb-various.o lmdb-safe.o
|
||||||
|
|
|
@ -20,11 +20,12 @@ using std::endl;
|
||||||
Everything should go into a namespace
|
Everything should go into a namespace
|
||||||
What is an error? What is an exception?
|
What is an error? What is an exception?
|
||||||
could id=0 be magic? ('no such id')
|
could id=0 be magic? ('no such id')
|
||||||
|
yes
|
||||||
Is boost the best serializer?
|
Is boost the best serializer?
|
||||||
|
good default
|
||||||
Perhaps use the separate index concept from multi_index
|
Perhaps use the separate index concept from multi_index
|
||||||
A dump function would be nice (typed)
|
|
||||||
including indexes
|
|
||||||
perhaps get eiter to be of same type so for(auto& a : x) works
|
perhaps get eiter to be of same type so for(auto& a : x) works
|
||||||
|
make it more value "like" with unique_ptr
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
@ -35,7 +36,7 @@ using std::endl;
|
||||||
unsigned int MDBGetMaxID(MDBRWTransaction& txn, MDBDbi& dbi);
|
unsigned int MDBGetMaxID(MDBRWTransaction& txn, MDBDbi& dbi);
|
||||||
|
|
||||||
/** This is our serialization interface.
|
/** This is our serialization interface.
|
||||||
We could/should templatize this so you could pick something else
|
You can define your own serToString for your type if you know better
|
||||||
*/
|
*/
|
||||||
template<typename T>
|
template<typename T>
|
||||||
std::string serToString(const T& t)
|
std::string serToString(const T& t)
|
||||||
|
@ -66,10 +67,10 @@ void serFromString(const std::string& str, T& ret)
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// mini-serializer for keys. Again, you can override
|
||||||
template<typename KeyType>
|
template<typename KeyType>
|
||||||
std::string keyConv(const KeyType& t);
|
std::string keyConv(const KeyType& t);
|
||||||
|
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
inline std::string keyConv(const std::string& t)
|
inline std::string keyConv(const std::string& t)
|
||||||
{
|
{
|
||||||
|
@ -276,6 +277,32 @@ public:
|
||||||
d_on_index(on_index), // is this an iterator on main database or on index?
|
d_on_index(on_index), // is this an iterator on main database or on index?
|
||||||
d_one_key(one_key), // should we stop at end of key? (equal range)
|
d_one_key(one_key), // should we stop at end of key? (equal range)
|
||||||
d_end(end)
|
d_end(end)
|
||||||
|
{
|
||||||
|
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) {
|
||||||
|
if(d_parent->d_txn->get(d_parent->d_parent->d_main, d_id, d_data))
|
||||||
|
throw std::runtime_error("Missing id in constructor");
|
||||||
|
serFromString(d_data.get<std::string>(), d_t);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
serFromString(d_id.get<std::string>(), d_t);
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
{
|
{
|
||||||
if(d_end)
|
if(d_end)
|
||||||
return;
|
return;
|
||||||
|
@ -293,6 +320,7 @@ public:
|
||||||
else
|
else
|
||||||
serFromString(d_id.get<std::string>(), d_t);
|
serFromString(d_id.get<std::string>(), d_t);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::function<bool(const MDBOutVal&)> filter;
|
std::function<bool(const MDBOutVal&)> filter;
|
||||||
void del()
|
void del()
|
||||||
|
@ -300,12 +328,12 @@ public:
|
||||||
d_cursor.del();
|
d_cursor.del();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool operator!=(const eiter_t& rhs)
|
bool operator!=(const eiter_t& rhs) const
|
||||||
{
|
{
|
||||||
return !d_end;
|
return !d_end;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool operator==(const eiter_t& rhs)
|
bool operator==(const eiter_t& rhs) const
|
||||||
{
|
{
|
||||||
return d_end;
|
return d_end;
|
||||||
}
|
}
|
||||||
|
@ -333,6 +361,9 @@ public:
|
||||||
else if(rc) {
|
else if(rc) {
|
||||||
throw std::runtime_error("in genoperator, " + std::string(mdb_strerror(rc)));
|
throw std::runtime_error("in genoperator, " + std::string(mdb_strerror(rc)));
|
||||||
}
|
}
|
||||||
|
else if(!d_prefix.empty() && d_key.get<std::string>().rfind(d_prefix, 0)!=0) {
|
||||||
|
d_end = true;
|
||||||
|
}
|
||||||
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))
|
||||||
|
@ -378,6 +409,7 @@ public:
|
||||||
MDBOutVal d_key{0,0}, d_data{0,0}, d_id{0,0};
|
MDBOutVal d_key{0,0}, d_data{0,0}, d_id{0,0};
|
||||||
bool d_on_index;
|
bool d_on_index;
|
||||||
bool d_one_key;
|
bool d_one_key;
|
||||||
|
std::string d_prefix;
|
||||||
bool d_end{false};
|
bool d_end{false};
|
||||||
T d_t;
|
T d_t;
|
||||||
};
|
};
|
||||||
|
@ -479,6 +511,26 @@ public:
|
||||||
return {iter_t{&d_parent, std::move(cursor), true, true}, eiter_t()};
|
return {iter_t{&d_parent, std::move(cursor), true, true}, eiter_t()};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//! equal range - could possibly be expressed through genfind
|
||||||
|
template<int N>
|
||||||
|
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);
|
||||||
|
|
||||||
|
std::string keyString=keyConv(key);
|
||||||
|
MDBInVal in(keyString);
|
||||||
|
MDBOutVal out, id;
|
||||||
|
out.d_mdbval = in.d_mdbval;
|
||||||
|
|
||||||
|
if(cursor.get(out, id, MDB_SET_RANGE)) {
|
||||||
|
// on_index, one_key, end
|
||||||
|
return {iter_t{&d_parent, std::move(cursor), true, true, true}, eiter_t()};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {iter_t(&d_parent, std::move(cursor), keyString), eiter_t()};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
Parent& d_parent;
|
Parent& d_parent;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
#include "lmdb-safe.hh"
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
void countDB(MDBEnv& env, MDBROTransaction& txn, const std::string& dbname)
|
||||||
|
{
|
||||||
|
auto db = txn.openDB(dbname, 0);
|
||||||
|
auto cursor = txn.getCursor(db);
|
||||||
|
uint32_t count = 0;
|
||||||
|
MDBOutVal key, val;
|
||||||
|
while(!cursor.get(key, val, count ? MDB_NEXT : MDB_FIRST)) {
|
||||||
|
cout << key.get<string>();
|
||||||
|
if(key.d_mdbval.mv_size == 4)
|
||||||
|
cout << " " << key.get<uint32_t>();
|
||||||
|
cout<<": " << val.get<std::string>();
|
||||||
|
cout << "\n";
|
||||||
|
++count;
|
||||||
|
|
||||||
|
}
|
||||||
|
cout <<count<<endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char** argv)
|
||||||
|
{
|
||||||
|
MDBEnv env(argv[1], MDB_RDONLY | MDB_NOSUBDIR, 0600);
|
||||||
|
auto main = env.openDB("", 0);
|
||||||
|
auto txn = env.getROTransaction();
|
||||||
|
|
||||||
|
auto cursor = txn.getCursor(main);
|
||||||
|
|
||||||
|
MDBOutVal key, val;
|
||||||
|
if(cursor.get(key, val, MDB_FIRST)) {
|
||||||
|
cout << "Database is empty" <<endl;
|
||||||
|
}
|
||||||
|
do {
|
||||||
|
cout << key.get<string>() << endl;
|
||||||
|
countDB(env, txn, key.get<string>());
|
||||||
|
} while(!cursor.get(key, val, MDB_NEXT));
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
#define CATCH_CONFIG_MAIN
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include "catch2/catch.hpp"
|
||||||
|
#include "lmdb-safe.hh"
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
TEST_CASE("Most basic tests", "[mostbasic]") {
|
||||||
|
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, "lmdb", "hot");
|
||||||
|
|
||||||
|
REQUIRE(txn.get(main, "lmdb", out) == 0);
|
||||||
|
REQUIRE(out.get<std::string>() == "hot");
|
||||||
|
txn.abort();
|
||||||
|
|
||||||
|
auto rotxn = env.getROTransaction();
|
||||||
|
REQUIRE(rotxn.get(main, "lmdb", out) == MDB_NOTFOUND);
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#include "catch2/catch.hpp"
|
||||||
|
#include "lmdb-typed.hh"
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
struct Member
|
||||||
|
{
|
||||||
|
std::string firstName;
|
||||||
|
std::string lastName;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class Archive>
|
||||||
|
void serialize(Archive & ar, Member& g, const unsigned int version)
|
||||||
|
{
|
||||||
|
ar & g.firstName & g.lastName;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
TEST_CASE("Basic typed tests", "[basictyped]") {
|
||||||
|
unlink("./tests-typed");
|
||||||
|
typedef TypedDBI<Member,
|
||||||
|
index_on<Member, string, &Member::firstName>,
|
||||||
|
index_on<Member, string, &Member::lastName> > tmembers_t;
|
||||||
|
|
||||||
|
auto tmembers = tmembers_t(getMDBEnv("./tests-typed", MDB_CREATE | MDB_NOSUBDIR, 0600), "members");
|
||||||
|
|
||||||
|
REQUIRE(1);
|
||||||
|
|
||||||
|
auto txn = tmembers.getRWTransaction();
|
||||||
|
Member m{"bert", "hubert"};
|
||||||
|
txn.put(m);
|
||||||
|
m.firstName="bertus";
|
||||||
|
m.lastName = "testperson";
|
||||||
|
txn.put(m);
|
||||||
|
|
||||||
|
m.firstName = "other";
|
||||||
|
txn.put(m);
|
||||||
|
|
||||||
|
Member out;
|
||||||
|
REQUIRE(txn.get(1,out));
|
||||||
|
REQUIRE(out.firstName == "bert");
|
||||||
|
REQUIRE(txn.get(2,out));
|
||||||
|
REQUIRE(out.lastName == "testperson");
|
||||||
|
|
||||||
|
REQUIRE(!txn.get(4,out));
|
||||||
|
|
||||||
|
auto range = txn.prefix_range<0>("bert");
|
||||||
|
vector<std::string> names;
|
||||||
|
for(auto& iter = range.first; iter != range.second; ++iter) {
|
||||||
|
names.push_back(iter->firstName);
|
||||||
|
}
|
||||||
|
REQUIRE(names == vector<std::string>{"bert", "bertus"});
|
||||||
|
|
||||||
|
auto range2 = txn.prefix_range<0>("nosuchperson");
|
||||||
|
REQUIRE(!(range2.first == range2.second));
|
||||||
|
|
||||||
|
txn.abort();
|
||||||
|
}
|
Loading…
Reference in New Issue