1
0
forked from blue/lmdbal

readAll method now works correctly for duplicates mode, testing, some doc fixes

This commit is contained in:
Blue 2023-08-20 13:38:29 -03:00
parent dbbc46e7c9
commit de210b44f5
Signed by untrusted user: blue
GPG Key ID: 9B203B252A63EE38
5 changed files with 163 additions and 8 deletions

View File

@ -15,7 +15,7 @@
* <a class="el" href="https://en.cppreference.com/w/cpp/container/map">std::map</a>
* 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.
* 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&)

View File

@ -46,8 +46,8 @@ class Storage;
template <class K, class V>
class Cache;
typedef MDB_txn* TransactionID; /**<I'm going to use transaction pointers as transaction IDs*/
typedef uint32_t SizeType; /**<All LMDBAL sizes are uint32_t*/
typedef MDB_txn* TransactionID; /**<\brief I'm going to use transaction pointers as transaction IDs*/
typedef uint32_t SizeType; /**<\brief All LMDBAL sizes are uint32_t*/
class Base {
friend class iStorage;
@ -82,8 +82,8 @@ public:
LMDBAL::Cache<K, V>* getCache(const std::string& storageName);
private:
typedef std::map<std::string, LMDBAL::iStorage*> Storages; /**<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::map<std::string, LMDBAL::iStorage*> Storages; /**<\brief Storage and Cache pointers are saved in the std::map*/
typedef std::set<TransactionID> Transactions; /**<\brief Piblic transaction IDs are saved in the std::set*/
TransactionID beginReadOnlyTransaction(const std::string& storageName) const;
TransactionID beginTransaction(const std::string& storageName) const;

View File

@ -23,6 +23,8 @@
/**
* \class LMDBAL::iStorage
*
* \brief Storage interface
*
* 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
* from the heirs code

View File

@ -31,7 +31,7 @@
* \tparam K type of the keys 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.
* 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
*
* 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::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
*
* 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
*
* \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().
* 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
*
* \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().
* 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[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) {
K key;
keySerializer.deserialize(lmdbKey, key);
V& value = result[key];
valueSerializer.deserialize(lmdbData, value);
std::pair<typename std::map<K, V>::iterator, bool> probe = result.emplace(key, V{});
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);
}
mdb_cursor_close(cursor);

View File

@ -1,9 +1,12 @@
#include <gtest/gtest.h>
#include <limits>
#include <map>
#include <set>
#include "base.h"
#include "storage.h"
#include "cursor.h"
class DuplicatesTest : public ::testing::Test {
protected:
@ -326,3 +329,139 @@ TEST_F(DuplicatesTest, Changing) {
EXPECT_EQ(tu4->getRecord(852), 213.85);
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);
}