started to work on duplicates support

This commit is contained in:
Blue 2023-08-15 15:48:19 -03:00
parent 7b26d57ab6
commit f0727aa73d
Signed by: blue
GPG Key ID: 9B203B252A63EE38
9 changed files with 132 additions and 108 deletions

View File

@ -1,14 +1,22 @@
# Changelog
## LMDBAL 0.5.0 (UNRELEASED, 2023)
### New Features
- duplicates support
## LMDBAL 0.4.0 (August 13, 2023)
### New Features
- read only cursors
### Bug fixes
- possible cache unsync
- doxygen-awesome build bix
### Improvements
- read only cursors
- some more documentation
- more tests
- doxygen-awesome build bix
## LMDBAL 0.3.1 (April 14, 2023)
### Bug fixes
@ -17,6 +25,7 @@
### Improvements
- exception documentation
## LMDBAL 0.3.0 (April 12, 2023)
### New features
- transaction functions

View File

@ -69,16 +69,16 @@ public:
void abortTransaction(TransactionID id) const;
template <class K, class V>
LMDBAL::Storage<K, V>* addStorage(const std::string& name);
LMDBAL::Storage<K, V>* addStorage(const std::string& storageName, bool duplicates = false);
template <class K, class V>
LMDBAL::Cache<K, V>* addCache(const std::string& name);
LMDBAL::Cache<K, V>* addCache(const std::string& storageName);
template <class K, class V>
LMDBAL::Storage<K, V>* getStorage(const std::string& name);
LMDBAL::Storage<K, V>* getStorage(const std::string& storageName);
template <class K, class V>
LMDBAL::Cache<K, V>* getCache(const std::string& name);
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*/
@ -114,7 +114,8 @@ private:
* Defines that the database is going to have the following storage.
* The LMDBAL::Base must be closed
*
* \param[in] _name - storage name
* \param[in] storageName - storage name
*
* \returns storage pointer. LMDBAL::Base keeps the ownership of the added storage, you don't need to destoroy it
*
* \tparam K - key type of the storage
@ -124,14 +125,14 @@ private:
* \exception LMDBAL::StorageDuplicate thrown if somebody tries to add storage with repeating name
*/
template <class K, class V>
LMDBAL::Storage<K, V>* LMDBAL::Base::addStorage(const std::string& _name) {
LMDBAL::Storage<K, V>* LMDBAL::Base::addStorage(const std::string& storageName, bool duplicates) {
if (opened) {
throw Opened(name, "add storage " + _name);
throw Opened(name, "add storage " + storageName);
}
Storage<K, V>* storage = new Storage<K, V>(_name, this);
std::pair<Storages::const_iterator, bool> pair = storages.insert(std::make_pair(_name, (iStorage*)storage));
Storage<K, V>* storage = new Storage<K, V>(this, storageName, duplicates);
std::pair<Storages::const_iterator, bool> pair = storages.insert(std::make_pair(storageName, (iStorage*)storage));
if (!pair.second)
throw StorageDuplicate(name, _name);
throw StorageDuplicate(name, storageName);
return storage;
}
@ -142,7 +143,7 @@ LMDBAL::Storage<K, V>* LMDBAL::Base::addStorage(const std::string& _name) {
* Defines that the database is going to have the following cache.
* The LMDBAL::Base must be closed
*
* \param[in] _name - cache name
* \param[in] storageName - cache name
* \returns cache pointer. LMDBAL::Base keeps the ownership of the added cache, you don't need to destoroy it
*
* \tparam K - key type of the cache
@ -152,14 +153,14 @@ LMDBAL::Storage<K, V>* LMDBAL::Base::addStorage(const std::string& _name) {
* \exception LMDBAL::StorageDuplicate thrown if somebody tries to add cache with repeating name
*/
template<class K, class V>
LMDBAL::Cache<K, V> * LMDBAL::Base::addCache(const std::string& _name) {
LMDBAL::Cache<K, V> * LMDBAL::Base::addCache(const std::string& storageName) {
if (opened) {
throw Opened(name, "add cache " + _name);
throw Opened(name, "add cache " + storageName);
}
Cache<K, V>* cache = new Cache<K, V>(_name, this);
std::pair<Storages::const_iterator, bool> pair = storages.insert(std::make_pair(_name, (iStorage*)cache));
Cache<K, V>* cache = new Cache<K, V>(this, storageName, false);
std::pair<Storages::const_iterator, bool> pair = storages.insert(std::make_pair(storageName, (iStorage*)cache));
if (!pair.second)
throw StorageDuplicate(name, _name);
throw StorageDuplicate(name, storageName);
return cache;
}
@ -173,7 +174,7 @@ LMDBAL::Cache<K, V> * LMDBAL::Base::addCache(const std::string& _name) {
* this method with template parameters <std::string, std::string>
* on the same name of the previously added storage, or calling it on cache - the behaviour is undefined
*
* \param[in] _name - storage name
* \param[in] storageName - storage name
* \returns storage pointer. LMDBAL::Base keeps the ownership of the added storage, you don't need to destoroy it
*
* \tparam K - key type of the storage
@ -182,8 +183,8 @@ LMDBAL::Cache<K, V> * LMDBAL::Base::addCache(const std::string& _name) {
* \exception std::out_of_range thrown if storage with the given name was not found
*/
template <class K, class V>
LMDBAL::Storage<K, V>* LMDBAL::Base::getStorage(const std::string& _name) {
return static_cast<Storage<K, V>*>(storages.at(_name));
LMDBAL::Storage<K, V>* LMDBAL::Base::getStorage(const std::string& storageName) {
return static_cast<Storage<K, V>*>(storages.at(storageName));
}
/**
@ -195,7 +196,7 @@ LMDBAL::Storage<K, V>* LMDBAL::Base::getStorage(const std::string& _name) {
* this method with template parameters <std::string, std::string>
* on the same name of the previously added cache, or calling it on storage - the behaviour is undefined
*
* \param[in] _name - cache name
* \param[in] storageName - cache name
* \returns cache pointer. LMDBAL::Base keeps the ownership of the added cache, you don't need to destoroy it
*
* \tparam K - key type of the cache
@ -204,8 +205,8 @@ LMDBAL::Storage<K, V>* LMDBAL::Base::getStorage(const std::string& _name) {
* \exception std::out_of_range thrown if cache with the given name was not found
*/
template <class K, class V>
LMDBAL::Cache<K, V>* LMDBAL::Base::getCache(const std::string& _name) {
return static_cast<Cache<K, V>*>(storages.at(_name));
LMDBAL::Cache<K, V>* LMDBAL::Base::getCache(const std::string& storageName) {
return static_cast<Cache<K, V>*>(storages.at(storageName));
}
#endif //LMDBAL_BASE_H

View File

@ -51,7 +51,7 @@ class Cache : public Storage<K, V> {
typedef std::map<TransactionID, Queue> TransactionCache;
protected:
Cache(const std::string& name, Base* parent);
Cache(Base* parent, const std::string& name, bool duplicates = false);
~Cache() override;
virtual void transactionStarted(TransactionID txn, bool readOnly) const override;

View File

@ -39,12 +39,13 @@
/**
* \brief Creates a cache
*
* \param[in] _name - name of the new cache
* \param[in] parent - parent database pointed (borrowed)
* \param[in] parent - LMDBAL::Base pointer for the owning database (borrowed)
* \param[in] name - the name of the storage
* \param[in] duplicates - true if storage supports duplicates, false otherwise (false by default)
*/
template<class K, class V>
LMDBAL::Cache<K, V>::Cache(const std::string& _name, Base* parent):
Storage<K, V>(_name, parent),
LMDBAL::Cache<K, V>::Cache(Base* parent, const std::string& name, bool duplicates):
Storage<K, V>(parent, name, duplicates),
mode(Mode::nothing),
cache(new std::map<K, V>()),
abscent(new std::set<K>()),

View File

@ -23,11 +23,12 @@
/**
* \class LMDBAL::Serializer
* \brief A class handling serialization/deserialization
*
* A class that is constructed in every LMDBAL::Storage
* to serialize or deserialize keys and values.
*
* It serializes to and deserializes from <a class="el" href="http://www.lmdb.tech/doc/group__mdb.html#structMDB__val">MDB_val</a>MDB_val
* It serializes to and deserializes from <a class="el" href="http://www.lmdb.tech/doc/group__mdb.html#structMDB__val">MDB_val</a>
*
* \tparam K type of the keys of the storage
* \tparam V type of the values of the storage

View File

@ -30,11 +30,16 @@
/**
* \brief Constructs a storage interface
*
* \param[in] parent - LMDBAL::Base pointer for the owning database (borrowed)
* \param[in] name - the name of the storage
* \param[in] duplicates - true if storage supports duplicates, false otherwise (false by default)
*/
LMDBAL::iStorage::iStorage(const std::string& p_name, Base* parent):
LMDBAL::iStorage::iStorage(Base* parent, const std::string& name, bool duplicates):
dbi(),
db(parent),
name(p_name)
name(name),
duplicates(duplicates)
{}
/**

View File

@ -19,10 +19,14 @@
#ifndef LMDBAL_STORAGE_H
#define LMDBAL_STORAGE_H
#include <type_traits>
#include "base.h"
#include "serializer.h"
#include "cursor.h"
class BaseTest;
namespace LMDBAL {
typedef uint32_t SizeType;
@ -32,7 +36,7 @@ class iStorage {
public:
protected:
iStorage(const std::string& name, Base* parent);
iStorage(Base* parent, const std::string& name, bool duplicates = false);
virtual ~iStorage();
/**
@ -77,9 +81,11 @@ protected:
MDB_dbi dbi; /**<\brief lmdb storage handle*/
Base* db; /**<\brief parent database pointer (borrowed)*/
const std::string name; /**<\brief this storage name*/
const bool duplicates; /**<\brief true if storage supports duplicates*/
inline static const std::string dropMethodName = "drop"; /**<\brief member function name, just for exceptions*/
inline static const std::string countMethodName = "count"; /**<\brief member function name, just for exceptions*/
inline static const std::string flagsMethodName = "flags"; /**<\brief member function name, just for exceptions*/
inline static const std::string addRecordMethodName = "addRecord"; /**<\brief member function name, just for exceptions*/
inline static const std::string forceRecordMethodName = "forceRecord"; /**<\brief member function name, just for exceptions*/
@ -92,8 +98,8 @@ protected:
inline static const std::string addRecordsMethodName = "addRecords"; /**<\brief member function name, just for exceptions*/
protected:
template <class T>
int makeStorage(MDB_txn* transaction);
template <class K, class V>
int makeStorage(MDB_txn* transaction, bool duplicates);
template <class T>
static std::string toString(const T& value);
@ -101,14 +107,16 @@ protected:
template <class K, class V>
class Storage : public iStorage {
friend class ::BaseTest;
friend class Base;
friend class Cursor<K, V>;
protected:
Storage(const std::string& name, Base* parent);
Storage(Base* parent, const std::string& name, bool duplicates = false);
~Storage() override;
virtual void discoveredRecord(const K& key, const V& value) const;
virtual void discoveredRecord(const K& key, const V& value, TransactionID txn) const;
uint32_t flags() const;
public:
using iStorage::drop;

View File

@ -41,12 +41,13 @@
/**
* \brief Creates a storage
*
* \param[in] _name - name of the new storage
* \param[in] parent - parent database pointed (borrowed)
* \param[in] parent - LMDBAL::Base pointer for the owning database (borrowed)
* \param[in] name - the name of the storage
* \param[in] duplicates - true if storage supports duplicates, false otherwise (false by default)
*/
template<class K, class V>
LMDBAL::Storage<K, V>::Storage(const std::string& _name, Base* parent):
iStorage(_name, parent),
LMDBAL::Storage<K, V>::Storage(Base* parent, const std::string& name, bool duplicates):
iStorage(parent, name, duplicates),
keySerializer(),
valueSerializer(),
cursors()
@ -692,7 +693,7 @@ void LMDBAL::Storage<K, V>::removeRecord(const K& key, TransactionID txn) {
*/
template<class K, class V>
int LMDBAL::Storage<K, V>::open(MDB_txn* transaction) {
return makeStorage<K>(transaction);
return makeStorage<K, V>(transaction, duplicates);
}
/**
@ -719,6 +720,30 @@ LMDBAL::Cursor<K, V>* LMDBAL::Storage<K, V>::createCursor() {
return cursor;
}
/**
* \brief Reads current storage flags it was opened with
*
* This function exists mostly for testing purposes
*
* \returns Third out parameter of <a href="http://www.lmdb.tech/doc/group__internal.html#ga95ba4cb721035478a8705e57b91ae4d4">mdb_dbi_flags</a> function
*
* \exception LMDBAL::Closed thrown if the database was not opened
* \exception LMDBAL::Unknown if the result of <a href="http://www.lmdb.tech/doc/group__internal.html#ga95ba4cb721035478a8705e57b91ae4d4">mdb_dbi_flags</a> was not successfull
*/
template<class K, class V>
uint32_t LMDBAL::Storage<K, V>::flags() const {
ensureOpened(flagsMethodName);
uint32_t result;
TransactionID txn = beginReadOnlyTransaction();
int res = mdb_dbi_flags(txn, dbi, &result);
abortTransaction(txn);
if (res != MDB_SUCCESS)
throwUnknown(res);
return result;
}
/**
* \brief Destroys cursor
*
@ -726,7 +751,7 @@ LMDBAL::Cursor<K, V>* LMDBAL::Storage<K, V>::createCursor() {
*
* \param[in] cursor a pointer to a cursor you want to destroy
*
* \throws LMDBAL::Unknown thrown if you try to destroy something that this storage didn't create
* \exception LMDBAL::Unknown thrown if you try to destroy something that this storage didn't create
*/
template<class K, class V>
void LMDBAL::Storage<K, V>::destroyCursor(Cursor<K, V>* cursor) {
@ -770,79 +795,28 @@ void LMDBAL::Storage<K, V>::discoveredRecord(const K& key, const V& value, Trans
* \tparam K type of keys in opening storage
*
* \param[in] transaction - lmdb transaction to call <a class="el" href="http://www.lmdb.tech/doc/group__mdb.html#gac08cad5b096925642ca359a6d6f0562a">mdb_dbi_open</a>, must be a writable transaction!
* \param[in] duplicates - true if you wish to enable duplicates support for the storage
*
* \returns MDB_SUCCESS if everything went smooth or MDB_<error> -like error code
*
* This and the following collection of specializations are a way to optimise database using
* MDB_INTEGERKEY flag, when the key is actually kind of an integer
* This is a way to optimise database using MDB_INTEGERKEY flag,
* when the key is actually kind of an integer
* This infrastructure also allowes us to customize mdb_dbi_open call in the future
*/
template<class K>
inline int LMDBAL::iStorage::makeStorage(MDB_txn* transaction) {
return mdb_dbi_open(transaction, name.c_str(), MDB_CREATE, &dbi);
}
template<class K, class V>
inline int LMDBAL::iStorage::makeStorage(MDB_txn* transaction, bool duplicates) {
unsigned int flags = MDB_CREATE;
if constexpr (std::is_integral<K>::value)
flags |= MDB_INTEGERKEY;
/**
* \brief Opening database function specialization for uint64_t
*/
template<>
inline int LMDBAL::iStorage::makeStorage<uint64_t>(MDB_txn* transaction) {
return mdb_dbi_open(transaction, name.c_str(), MDB_CREATE | MDB_INTEGERKEY, &dbi);
}
if (duplicates) {
flags |= MDB_DUPSORT;
/**
* \brief Opening database function specialization for uint32_t
*/
template<>
inline int LMDBAL::iStorage::makeStorage<uint32_t>(MDB_txn* transaction) {
return mdb_dbi_open(transaction, name.c_str(), MDB_CREATE | MDB_INTEGERKEY, &dbi);
}
if constexpr (std::is_integral<V>::value)
flags |= MDB_INTEGERDUP | MDB_DUPFIXED;
}
/**
* \brief Opening database function specialization for uint16_t
*/
template<>
inline int LMDBAL::iStorage::makeStorage<uint16_t>(MDB_txn* transaction) {
return mdb_dbi_open(transaction, name.c_str(), MDB_CREATE | MDB_INTEGERKEY, &dbi);
}
/**
* \brief Opening database function specialization for uint8_t
*/
template<>
inline int LMDBAL::iStorage::makeStorage<uint8_t>(MDB_txn* transaction) {
return mdb_dbi_open(transaction, name.c_str(), MDB_CREATE | MDB_INTEGERKEY, &dbi);
}
/**
* \brief Opening database function specialization for int64_t
*/
template<>
inline int LMDBAL::iStorage::makeStorage<int64_t>(MDB_txn* transaction) {
return mdb_dbi_open(transaction, name.c_str(), MDB_CREATE | MDB_INTEGERKEY, &dbi);
}
/**
* \brief Opening database function specialization for int32_t
*/
template<>
inline int LMDBAL::iStorage::makeStorage<int32_t>(MDB_txn* transaction) {
return mdb_dbi_open(transaction, name.c_str(), MDB_CREATE | MDB_INTEGERKEY, &dbi);
}
/**
* \brief Opening database function specialization for int16_t
*/
template<>
inline int LMDBAL::iStorage::makeStorage<int16_t>(MDB_txn* transaction) {
return mdb_dbi_open(transaction, name.c_str(), MDB_CREATE | MDB_INTEGERKEY, &dbi);
}
/**
* \brief Opening database function specialization for int8_t
*/
template<>
inline int LMDBAL::iStorage::makeStorage<int8_t>(MDB_txn* transaction) {
return mdb_dbi_open(transaction, name.c_str(), MDB_CREATE | MDB_INTEGERKEY, &dbi);
return mdb_dbi_open(transaction, name.c_str(), flags, &dbi);
}
/**

View File

@ -16,6 +16,10 @@ protected:
~BaseTest() {}
uint32_t getT1Flags() const {return t1->flags();}
uint32_t getT2Flags() const {return t2->flags();}
uint32_t getC1Flags() const {return c1->flags();}
static void SetUpTestSuite() {
if (db == nullptr) {
db = new LMDBAL::Base("testBase");
@ -56,6 +60,27 @@ TEST_F(BaseTest, OpeningClosingDatabase) {
EXPECT_EQ(db->ready(), true);
}
TEST_F(BaseTest, Flags) {
uint32_t t1Flags = getT1Flags();
uint32_t t2Flags = getT2Flags();
uint32_t c1Flags = getC1Flags();
EXPECT_TRUE(t1Flags & MDB_INTEGERKEY);
EXPECT_FALSE(t1Flags & MDB_DUPSORT);
EXPECT_FALSE(t1Flags & MDB_DUPFIXED);
EXPECT_FALSE(t1Flags & MDB_INTEGERDUP);
EXPECT_FALSE(t2Flags & MDB_INTEGERKEY);
EXPECT_FALSE(t2Flags & MDB_DUPSORT);
EXPECT_FALSE(t2Flags & MDB_DUPFIXED);
EXPECT_FALSE(t2Flags & MDB_INTEGERDUP);
EXPECT_TRUE(c1Flags & MDB_INTEGERKEY);
EXPECT_FALSE(c1Flags & MDB_DUPSORT);
EXPECT_FALSE(c1Flags & MDB_DUPFIXED);
EXPECT_FALSE(c1Flags & MDB_INTEGERDUP);
}
TEST_F(BaseTest, AddingIntegerKey) {
EXPECT_EQ(db->ready(), true);
t1->addRecord(1, 2);