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
|
||||
|
||||
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)
|
||||
|
||||
|
@ -17,7 +17,7 @@ clean:
|
|||
|
||||
-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)
|
||||
|
||||
lmdb-various: lmdb-various.o lmdb-safe.o
|
||||
|
|
|
@ -20,11 +20,12 @@ using std::endl;
|
|||
Everything should go into a namespace
|
||||
What is an error? What is an exception?
|
||||
could id=0 be magic? ('no such id')
|
||||
yes
|
||||
Is boost the best serializer?
|
||||
good default
|
||||
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
|
||||
make it more value "like" with unique_ptr
|
||||
*/
|
||||
|
||||
|
||||
|
@ -35,7 +36,7 @@ using std::endl;
|
|||
unsigned int MDBGetMaxID(MDBRWTransaction& txn, MDBDbi& dbi);
|
||||
|
||||
/** 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>
|
||||
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>
|
||||
std::string keyConv(const KeyType& t);
|
||||
|
||||
|
||||
template<>
|
||||
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_one_key(one_key), // should we stop at end of key? (equal range)
|
||||
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)
|
||||
return;
|
||||
|
@ -293,6 +320,7 @@ public:
|
|||
else
|
||||
serFromString(d_id.get<std::string>(), d_t);
|
||||
}
|
||||
|
||||
|
||||
std::function<bool(const MDBOutVal&)> filter;
|
||||
void del()
|
||||
|
@ -300,12 +328,12 @@ public:
|
|||
d_cursor.del();
|
||||
}
|
||||
|
||||
bool operator!=(const eiter_t& rhs)
|
||||
bool operator!=(const eiter_t& rhs) const
|
||||
{
|
||||
return !d_end;
|
||||
}
|
||||
|
||||
bool operator==(const eiter_t& rhs)
|
||||
bool operator==(const eiter_t& rhs) const
|
||||
{
|
||||
return d_end;
|
||||
}
|
||||
|
@ -333,6 +361,9 @@ public:
|
|||
else if(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 {
|
||||
if(d_on_index) {
|
||||
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};
|
||||
bool d_on_index;
|
||||
bool d_one_key;
|
||||
std::string d_prefix;
|
||||
bool d_end{false};
|
||||
T d_t;
|
||||
};
|
||||
|
@ -479,6 +511,26 @@ public:
|
|||
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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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