forked from blue/lmdbal
readAll method now works correctly for duplicates mode, testing, some doc fixes
This commit is contained in:
parent
dbbc46e7c9
commit
de210b44f5
@ -15,7 +15,7 @@
|
|||||||
* <a class="el" href="https://en.cppreference.com/w/cpp/container/map">std::map</a>
|
* <a class="el" href="https://en.cppreference.com/w/cpp/container/map">std::map</a>
|
||||||
* to speed up the access.
|
* to speed up the access.
|
||||||
*
|
*
|
||||||
* You can obtain handlers by calling LMDBAL::Base::addStorage(const std::string&) or LMDBAL::Base::addCache(const std::string& name).
|
* You can obtain handlers by calling LMDBAL::Base::addStorage(const std::string&, bool) or LMDBAL::Base::addCache(const std::string& name).
|
||||||
* Note that the handlers still belong to the LMDBAL::Base and it's his responsibility to destroy them.
|
* Note that the handlers still belong to the LMDBAL::Base and it's his responsibility to destroy them.
|
||||||
* You are not obliged to save those handlers,
|
* You are not obliged to save those handlers,
|
||||||
* you can obtain them at any time later using methods LMDBAL::Base::getStorage(const std::string&) or LMDBAL::Base::getCache(const std::string&)
|
* you can obtain them at any time later using methods LMDBAL::Base::getStorage(const std::string&) or LMDBAL::Base::getCache(const std::string&)
|
||||||
|
@ -46,8 +46,8 @@ class Storage;
|
|||||||
template <class K, class V>
|
template <class K, class V>
|
||||||
class Cache;
|
class Cache;
|
||||||
|
|
||||||
typedef MDB_txn* TransactionID; /**<I'm going to use transaction pointers as transaction IDs*/
|
typedef MDB_txn* TransactionID; /**<\brief I'm going to use transaction pointers as transaction IDs*/
|
||||||
typedef uint32_t SizeType; /**<All LMDBAL sizes are uint32_t*/
|
typedef uint32_t SizeType; /**<\brief All LMDBAL sizes are uint32_t*/
|
||||||
|
|
||||||
class Base {
|
class Base {
|
||||||
friend class iStorage;
|
friend class iStorage;
|
||||||
@ -82,8 +82,8 @@ public:
|
|||||||
LMDBAL::Cache<K, V>* getCache(const std::string& storageName);
|
LMDBAL::Cache<K, V>* getCache(const std::string& storageName);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
typedef std::map<std::string, LMDBAL::iStorage*> Storages; /**<Storage and Cache pointers are saved in the std::map*/
|
typedef std::map<std::string, LMDBAL::iStorage*> Storages; /**<\brief Storage and Cache pointers are saved in the std::map*/
|
||||||
typedef std::set<TransactionID> Transactions; /**<Piblic transaction IDs are saved in the std::set*/
|
typedef std::set<TransactionID> Transactions; /**<\brief Piblic transaction IDs are saved in the std::set*/
|
||||||
|
|
||||||
TransactionID beginReadOnlyTransaction(const std::string& storageName) const;
|
TransactionID beginReadOnlyTransaction(const std::string& storageName) const;
|
||||||
TransactionID beginTransaction(const std::string& storageName) const;
|
TransactionID beginTransaction(const std::string& storageName) const;
|
||||||
|
@ -23,6 +23,8 @@
|
|||||||
/**
|
/**
|
||||||
* \class LMDBAL::iStorage
|
* \class LMDBAL::iStorage
|
||||||
*
|
*
|
||||||
|
* \brief Storage interface
|
||||||
|
*
|
||||||
* This is a interface-like class, it's designed to be an inner database interface to
|
* This is a interface-like class, it's designed to be an inner database interface to
|
||||||
* be used as a polymorphic entity, and provide protected interaction with the database
|
* be used as a polymorphic entity, and provide protected interaction with the database
|
||||||
* from the heirs code
|
* from the heirs code
|
||||||
|
@ -31,7 +31,7 @@
|
|||||||
* \tparam K type of the keys of the storage
|
* \tparam K type of the keys of the storage
|
||||||
* \tparam V type of the values of the storage
|
* \tparam V type of the values of the storage
|
||||||
*
|
*
|
||||||
* You can receive an instance of this class calling LMDBAL::Base::addStorage(const std::string&)
|
* You can receive an instance of this class calling LMDBAL::Base::addStorage(const std::string&, bool)
|
||||||
* if the database is yet closed and you're defining the storages you're going to need.
|
* if the database is yet closed and you're defining the storages you're going to need.
|
||||||
* Or you can call LMDBAL::Base::getStorage(const std::string&) if you didn't save a pointer to the storage at first
|
* Or you can call LMDBAL::Base::getStorage(const std::string&) if you didn't save a pointer to the storage at first
|
||||||
*
|
*
|
||||||
@ -501,6 +501,9 @@ bool LMDBAL::Storage<K, V>::checkRecord(const K& key, TransactionID txn) const {
|
|||||||
*
|
*
|
||||||
* Basically just reads all database in an std::map, usefull when you store small storages
|
* Basically just reads all database in an std::map, usefull when you store small storages
|
||||||
*
|
*
|
||||||
|
* In case storage supports duplicates <b>only what lmdb considered to be lowest value</b>
|
||||||
|
* (see LMDBAL::Storage::getRecord() description) is returned in the resulting map
|
||||||
|
*
|
||||||
* \exception LMDBAL::Closed thrown if the database was not opened
|
* \exception LMDBAL::Closed thrown if the database was not opened
|
||||||
* \exception LMDBAL::Unknown thrown if something unexpected happend within lmdb
|
* \exception LMDBAL::Unknown thrown if something unexpected happend within lmdb
|
||||||
*/
|
*/
|
||||||
@ -518,6 +521,9 @@ std::map<K, V> LMDBAL::Storage<K, V>::readAll() const {
|
|||||||
*
|
*
|
||||||
* Basically just reads all database in an std::map, usefull when you store small storages
|
* Basically just reads all database in an std::map, usefull when you store small storages
|
||||||
*
|
*
|
||||||
|
* In case storage supports duplicates <b>only what lmdb considered to be lowest value</b>
|
||||||
|
* (see LMDBAL::Storage::getRecord() description) is returned in the resulting map
|
||||||
|
*
|
||||||
* \param[out] result a map that is going to contain all data
|
* \param[out] result a map that is going to contain all data
|
||||||
*
|
*
|
||||||
* \exception LMDBAL::Closed thrown if the database was not opened
|
* \exception LMDBAL::Closed thrown if the database was not opened
|
||||||
@ -545,6 +551,9 @@ void LMDBAL::Storage<K, V>::readAll(std::map<K, V>& result) const {
|
|||||||
* You can obtain LMDBAL::TransactionID calling LMDBAL::Base::beginReadOnlyTransaction() or LMDBAL::Base::beginTransaction().
|
* You can obtain LMDBAL::TransactionID calling LMDBAL::Base::beginReadOnlyTransaction() or LMDBAL::Base::beginTransaction().
|
||||||
* If you just want to read data you should prefer LMDBAL::Base::beginReadOnlyTransaction().
|
* If you just want to read data you should prefer LMDBAL::Base::beginReadOnlyTransaction().
|
||||||
*
|
*
|
||||||
|
* In case storage supports duplicates <b>only what lmdb considered to be lowest value</b>
|
||||||
|
* (see LMDBAL::Storage::getRecord() description) is returned in the resulting map
|
||||||
|
*
|
||||||
* \param[in] txn transaction ID, can be read only transaction
|
* \param[in] txn transaction ID, can be read only transaction
|
||||||
*
|
*
|
||||||
* \exception LMDBAL::Closed thrown if the database was not opened
|
* \exception LMDBAL::Closed thrown if the database was not opened
|
||||||
@ -566,6 +575,9 @@ std::map<K, V> LMDBAL::Storage<K, V>::readAll(TransactionID txn) const {
|
|||||||
* You can obtain LMDBAL::TransactionID calling LMDBAL::Base::beginReadOnlyTransaction() or LMDBAL::Base::beginTransaction().
|
* You can obtain LMDBAL::TransactionID calling LMDBAL::Base::beginReadOnlyTransaction() or LMDBAL::Base::beginTransaction().
|
||||||
* If you just want to read data you should prefer LMDBAL::Base::beginReadOnlyTransaction().
|
* If you just want to read data you should prefer LMDBAL::Base::beginReadOnlyTransaction().
|
||||||
*
|
*
|
||||||
|
* In case storage supports duplicates <b>only what lmdb considered to be lowest value</b>
|
||||||
|
* (see LMDBAL::Storage::getRecord() description) is returned in the resulting map
|
||||||
|
*
|
||||||
* \param[out] result a map that is going to contain all data
|
* \param[out] result a map that is going to contain all data
|
||||||
* \param[in] txn transaction ID, can be read only transaction
|
* \param[in] txn transaction ID, can be read only transaction
|
||||||
*
|
*
|
||||||
@ -587,8 +599,10 @@ void LMDBAL::Storage<K, V>::readAll(std::map<K, V>& result, TransactionID txn) c
|
|||||||
while (rc == MDB_SUCCESS) {
|
while (rc == MDB_SUCCESS) {
|
||||||
K key;
|
K key;
|
||||||
keySerializer.deserialize(lmdbKey, key);
|
keySerializer.deserialize(lmdbKey, key);
|
||||||
V& value = result[key];
|
std::pair<typename std::map<K, V>::iterator, bool> probe = result.emplace(key, V{});
|
||||||
valueSerializer.deserialize(lmdbData, value);
|
if (probe.second) //I do this to avoid overwrites in case duplicates are enabled
|
||||||
|
valueSerializer.deserialize(lmdbData, probe.first->second);
|
||||||
|
|
||||||
rc = mdb_cursor_get(cursor, &lmdbKey, &lmdbData, MDB_NEXT);
|
rc = mdb_cursor_get(cursor, &lmdbKey, &lmdbData, MDB_NEXT);
|
||||||
}
|
}
|
||||||
mdb_cursor_close(cursor);
|
mdb_cursor_close(cursor);
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
#include <limits>
|
#include <limits>
|
||||||
|
#include <map>
|
||||||
|
#include <set>
|
||||||
|
|
||||||
#include "base.h"
|
#include "base.h"
|
||||||
#include "storage.h"
|
#include "storage.h"
|
||||||
|
#include "cursor.h"
|
||||||
|
|
||||||
class DuplicatesTest : public ::testing::Test {
|
class DuplicatesTest : public ::testing::Test {
|
||||||
protected:
|
protected:
|
||||||
@ -326,3 +329,139 @@ TEST_F(DuplicatesTest, Changing) {
|
|||||||
EXPECT_EQ(tu4->getRecord(852), 213.85);
|
EXPECT_EQ(tu4->getRecord(852), 213.85);
|
||||||
EXPECT_THROW(tu4->changeRecord(852, 46324.1135), LMDBAL::Exist);
|
EXPECT_THROW(tu4->changeRecord(852, 46324.1135), LMDBAL::Exist);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(DuplicatesTest, GettingAllRecords) {
|
||||||
|
LMDBAL::TransactionID txn = db->beginReadOnlyTransaction();
|
||||||
|
bool cycle;
|
||||||
|
LMDBAL::SizeType iterations;
|
||||||
|
|
||||||
|
std::map<int16_t, uint16_t> m1;
|
||||||
|
std::set<int16_t> k1;
|
||||||
|
LMDBAL::Cursor<int16_t, uint16_t>* c1 = tu1->createCursor();
|
||||||
|
tu1->readAll(m1, txn);
|
||||||
|
c1->open(txn);
|
||||||
|
|
||||||
|
cycle = false;
|
||||||
|
iterations = 0;
|
||||||
|
do {
|
||||||
|
try {
|
||||||
|
std::pair<int16_t, uint16_t> pair = c1->next();
|
||||||
|
cycle = true;
|
||||||
|
std::pair<std::set<int16_t>::const_iterator, bool> probe = k1.insert(pair.first);
|
||||||
|
if (probe.second) {
|
||||||
|
uint16_t valueAll = m1.at(pair.first);
|
||||||
|
uint16_t valueGet;
|
||||||
|
EXPECT_NO_THROW(tu1->getRecord(pair.first, valueGet, txn));
|
||||||
|
EXPECT_EQ(valueAll, valueGet);
|
||||||
|
}
|
||||||
|
++iterations;
|
||||||
|
} catch (const LMDBAL::NotFound& e) {
|
||||||
|
cycle = false;
|
||||||
|
}
|
||||||
|
} while (cycle);
|
||||||
|
tu1->destroyCursor(c1);
|
||||||
|
|
||||||
|
EXPECT_EQ(iterations, tu1->count(txn));
|
||||||
|
EXPECT_EQ(k1.size(), m1.size());
|
||||||
|
EXPECT_NE(iterations, 0);
|
||||||
|
EXPECT_NE(k1.size(), 0);
|
||||||
|
|
||||||
|
|
||||||
|
std::map<std::string, int8_t> m2;
|
||||||
|
std::set<std::string> k2;
|
||||||
|
LMDBAL::Cursor<std::string, int8_t>* c2 = tu2->createCursor();
|
||||||
|
tu2->readAll(m2, txn);
|
||||||
|
c2->open(txn);
|
||||||
|
|
||||||
|
cycle = false;
|
||||||
|
iterations = 0;
|
||||||
|
do {
|
||||||
|
try {
|
||||||
|
std::pair<std::string, int8_t> pair = c2->next();
|
||||||
|
cycle = true;
|
||||||
|
std::pair<std::set<std::string>::const_iterator, bool> probe = k2.insert(pair.first);
|
||||||
|
if (probe.second) {
|
||||||
|
int8_t valueAll = m2.at(pair.first);
|
||||||
|
int8_t valueGet;
|
||||||
|
EXPECT_NO_THROW(tu2->getRecord(pair.first, valueGet, txn));
|
||||||
|
EXPECT_EQ(valueAll, valueGet);
|
||||||
|
}
|
||||||
|
++iterations;
|
||||||
|
} catch (const LMDBAL::NotFound& e) {
|
||||||
|
cycle = false;
|
||||||
|
}
|
||||||
|
} while (cycle);
|
||||||
|
tu2->destroyCursor(c2);
|
||||||
|
|
||||||
|
EXPECT_EQ(iterations, tu2->count(txn));
|
||||||
|
EXPECT_EQ(k2.size(), m2.size());
|
||||||
|
EXPECT_NE(iterations, 0);
|
||||||
|
EXPECT_NE(k2.size(), 0);
|
||||||
|
|
||||||
|
|
||||||
|
std::map<float, float> m3;
|
||||||
|
std::set<float> k3;
|
||||||
|
LMDBAL::Cursor<float, float>* c3 = tu3->createCursor();
|
||||||
|
tu3->readAll(m3, txn);
|
||||||
|
c3->open(txn);
|
||||||
|
|
||||||
|
cycle = false;
|
||||||
|
iterations = 0;
|
||||||
|
do {
|
||||||
|
try {
|
||||||
|
std::pair<float, float> pair = c3->next();
|
||||||
|
cycle = true;
|
||||||
|
std::pair<std::set<float>::const_iterator, bool> probe = k3.insert(pair.first);
|
||||||
|
if (probe.second) {
|
||||||
|
float valueAll = m3.at(pair.first);
|
||||||
|
float valueGet;
|
||||||
|
EXPECT_NO_THROW(tu3->getRecord(pair.first, valueGet, txn));
|
||||||
|
EXPECT_EQ(valueAll, valueGet);
|
||||||
|
}
|
||||||
|
++iterations;
|
||||||
|
} catch (const LMDBAL::NotFound& e) {
|
||||||
|
cycle = false;
|
||||||
|
}
|
||||||
|
} while (cycle);
|
||||||
|
tu3->destroyCursor(c3);
|
||||||
|
|
||||||
|
EXPECT_EQ(iterations, tu3->count(txn));
|
||||||
|
EXPECT_EQ(k3.size(), m3.size());
|
||||||
|
EXPECT_NE(iterations, 0);
|
||||||
|
EXPECT_NE(k3.size(), 0);
|
||||||
|
|
||||||
|
|
||||||
|
std::map<uint16_t, double> m4;
|
||||||
|
std::set<uint16_t> k4;
|
||||||
|
LMDBAL::Cursor<uint16_t, double>* c4 = tu4->createCursor();
|
||||||
|
tu4->readAll(m4, txn);
|
||||||
|
c4->open(txn);
|
||||||
|
|
||||||
|
cycle = false;
|
||||||
|
iterations = 0;
|
||||||
|
do {
|
||||||
|
try {
|
||||||
|
std::pair<uint16_t, double> pair = c4->next();
|
||||||
|
cycle = true;
|
||||||
|
std::pair<std::set<uint16_t>::const_iterator, bool> probe = k4.insert(pair.first);
|
||||||
|
if (probe.second) {
|
||||||
|
double valueAll = m4.at(pair.first);
|
||||||
|
double valueGet;
|
||||||
|
EXPECT_NO_THROW(tu4->getRecord(pair.first, valueGet, txn));
|
||||||
|
EXPECT_EQ(valueAll, valueGet);
|
||||||
|
}
|
||||||
|
++iterations;
|
||||||
|
} catch (const LMDBAL::NotFound& e) {
|
||||||
|
cycle = false;
|
||||||
|
}
|
||||||
|
} while (cycle);
|
||||||
|
tu4->destroyCursor(c4);
|
||||||
|
|
||||||
|
EXPECT_EQ(iterations, tu4->count(txn));
|
||||||
|
EXPECT_EQ(k4.size(), m4.size());
|
||||||
|
EXPECT_NE(iterations, 0);
|
||||||
|
EXPECT_NE(k4.size(), 0);
|
||||||
|
|
||||||
|
|
||||||
|
db->abortTransaction(txn);
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user