forked from blue/lmdbal
some more ideas about duplicates
This commit is contained in:
parent
f0727aa73d
commit
06e1aca45a
21
src/base.h
21
src/base.h
@ -48,6 +48,12 @@ class Cache;
|
|||||||
|
|
||||||
typedef MDB_txn* TransactionID; /**<I'm going to use transaction pointers as transaction IDs*/
|
typedef MDB_txn* TransactionID; /**<I'm going to use transaction pointers as transaction IDs*/
|
||||||
|
|
||||||
|
enum Duplicates { /**<Duplicates mode:*/
|
||||||
|
uniqueKey, /** - no duplicate keys allowed*/
|
||||||
|
uniquePair, /** - no duplicate key <b>AND</b> value pairs allowed*/
|
||||||
|
full /** - any configuration of duplicates goes*/
|
||||||
|
};
|
||||||
|
|
||||||
class Base {
|
class Base {
|
||||||
friend class iStorage;
|
friend class iStorage;
|
||||||
public:
|
public:
|
||||||
@ -69,7 +75,7 @@ public:
|
|||||||
void abortTransaction(TransactionID id) const;
|
void abortTransaction(TransactionID id) const;
|
||||||
|
|
||||||
template <class K, class V>
|
template <class K, class V>
|
||||||
LMDBAL::Storage<K, V>* addStorage(const std::string& storageName, bool duplicates = false);
|
LMDBAL::Storage<K, V>* addStorage(const std::string& storageName, Duplicates duplicates = uniqueKey);
|
||||||
|
|
||||||
template <class K, class V>
|
template <class K, class V>
|
||||||
LMDBAL::Cache<K, V>* addCache(const std::string& storageName);
|
LMDBAL::Cache<K, V>* addCache(const std::string& storageName);
|
||||||
@ -115,6 +121,7 @@ private:
|
|||||||
* The LMDBAL::Base must be closed
|
* The LMDBAL::Base must be closed
|
||||||
*
|
*
|
||||||
* \param[in] storageName - storage name
|
* \param[in] storageName - storage name
|
||||||
|
* \param[in] duplicates - LMDBAL::Duplicates duplicates mode (uniqueKey by default)
|
||||||
*
|
*
|
||||||
* \returns storage pointer. LMDBAL::Base keeps the ownership of the added storage, you don't need to destoroy it
|
* \returns storage pointer. LMDBAL::Base keeps the ownership of the added storage, you don't need to destoroy it
|
||||||
*
|
*
|
||||||
@ -125,10 +132,10 @@ private:
|
|||||||
* \exception LMDBAL::StorageDuplicate thrown if somebody tries to add storage with repeating name
|
* \exception LMDBAL::StorageDuplicate thrown if somebody tries to add storage with repeating name
|
||||||
*/
|
*/
|
||||||
template <class K, class V>
|
template <class K, class V>
|
||||||
LMDBAL::Storage<K, V>* LMDBAL::Base::addStorage(const std::string& storageName, bool duplicates) {
|
LMDBAL::Storage<K, V>* LMDBAL::Base::addStorage(const std::string& storageName, Duplicates duplicates) {
|
||||||
if (opened) {
|
if (opened)
|
||||||
throw Opened(name, "add storage " + storageName);
|
throw Opened(name, "add storage " + storageName);
|
||||||
}
|
|
||||||
Storage<K, V>* storage = new Storage<K, V>(this, storageName, duplicates);
|
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));
|
std::pair<Storages::const_iterator, bool> pair = storages.insert(std::make_pair(storageName, (iStorage*)storage));
|
||||||
if (!pair.second)
|
if (!pair.second)
|
||||||
@ -154,10 +161,10 @@ LMDBAL::Storage<K, V>* LMDBAL::Base::addStorage(const std::string& storageName,
|
|||||||
*/
|
*/
|
||||||
template<class K, class V>
|
template<class K, class V>
|
||||||
LMDBAL::Cache<K, V> * LMDBAL::Base::addCache(const std::string& storageName) {
|
LMDBAL::Cache<K, V> * LMDBAL::Base::addCache(const std::string& storageName) {
|
||||||
if (opened) {
|
if (opened)
|
||||||
throw Opened(name, "add cache " + storageName);
|
throw Opened(name, "add cache " + storageName);
|
||||||
}
|
|
||||||
Cache<K, V>* cache = new Cache<K, V>(this, storageName, false);
|
Cache<K, V>* cache = new Cache<K, V>(this, storageName, uniqueKey);
|
||||||
std::pair<Storages::const_iterator, bool> pair = storages.insert(std::make_pair(storageName, (iStorage*)cache));
|
std::pair<Storages::const_iterator, bool> pair = storages.insert(std::make_pair(storageName, (iStorage*)cache));
|
||||||
if (!pair.second)
|
if (!pair.second)
|
||||||
throw StorageDuplicate(name, storageName);
|
throw StorageDuplicate(name, storageName);
|
||||||
|
@ -51,7 +51,7 @@ class Cache : public Storage<K, V> {
|
|||||||
typedef std::map<TransactionID, Queue> TransactionCache;
|
typedef std::map<TransactionID, Queue> TransactionCache;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Cache(Base* parent, const std::string& name, bool duplicates = false);
|
Cache(Base* parent, const std::string& name, Duplicates duplicates = uniqueKey);
|
||||||
~Cache() override;
|
~Cache() override;
|
||||||
|
|
||||||
virtual void transactionStarted(TransactionID txn, bool readOnly) const override;
|
virtual void transactionStarted(TransactionID txn, bool readOnly) const override;
|
||||||
|
@ -41,10 +41,10 @@
|
|||||||
*
|
*
|
||||||
* \param[in] parent - LMDBAL::Base pointer for the owning database (borrowed)
|
* \param[in] parent - LMDBAL::Base pointer for the owning database (borrowed)
|
||||||
* \param[in] name - the name of the storage
|
* \param[in] name - the name of the storage
|
||||||
* \param[in] duplicates - true if storage supports duplicates, false otherwise (false by default)
|
* \param[in] duplicates - LMDBAL::Duplicates duplicates mode (uniqueKey by default)
|
||||||
*/
|
*/
|
||||||
template<class K, class V>
|
template<class K, class V>
|
||||||
LMDBAL::Cache<K, V>::Cache(Base* parent, const std::string& name, bool duplicates):
|
LMDBAL::Cache<K, V>::Cache(Base* parent, const std::string& name, Duplicates duplicates):
|
||||||
Storage<K, V>(parent, name, duplicates),
|
Storage<K, V>(parent, name, duplicates),
|
||||||
mode(Mode::nothing),
|
mode(Mode::nothing),
|
||||||
cache(new std::map<K, V>()),
|
cache(new std::map<K, V>()),
|
||||||
|
@ -33,9 +33,9 @@
|
|||||||
*
|
*
|
||||||
* \param[in] parent - LMDBAL::Base pointer for the owning database (borrowed)
|
* \param[in] parent - LMDBAL::Base pointer for the owning database (borrowed)
|
||||||
* \param[in] name - the name of the storage
|
* \param[in] name - the name of the storage
|
||||||
* \param[in] duplicates - true if storage supports duplicates, false otherwise (false by default)
|
* \param[in] duplicates - LMDBAL::Duplicates duplicates mode (uniqueKey by default)
|
||||||
*/
|
*/
|
||||||
LMDBAL::iStorage::iStorage(Base* parent, const std::string& name, bool duplicates):
|
LMDBAL::iStorage::iStorage(Base* parent, const std::string& name, Duplicates duplicates):
|
||||||
dbi(),
|
dbi(),
|
||||||
db(parent),
|
db(parent),
|
||||||
name(name),
|
name(name),
|
||||||
|
@ -26,6 +26,7 @@
|
|||||||
#include "cursor.h"
|
#include "cursor.h"
|
||||||
|
|
||||||
class BaseTest;
|
class BaseTest;
|
||||||
|
class DuplicatesTest;
|
||||||
|
|
||||||
namespace LMDBAL {
|
namespace LMDBAL {
|
||||||
|
|
||||||
@ -34,9 +35,8 @@ typedef uint32_t SizeType;
|
|||||||
class iStorage {
|
class iStorage {
|
||||||
friend class Base;
|
friend class Base;
|
||||||
public:
|
public:
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
iStorage(Base* parent, const std::string& name, bool duplicates = false);
|
iStorage(Base* parent, const std::string& name, Duplicates duplicates = uniqueKey);
|
||||||
virtual ~iStorage();
|
virtual ~iStorage();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -81,7 +81,7 @@ protected:
|
|||||||
MDB_dbi dbi; /**<\brief lmdb storage handle*/
|
MDB_dbi dbi; /**<\brief lmdb storage handle*/
|
||||||
Base* db; /**<\brief parent database pointer (borrowed)*/
|
Base* db; /**<\brief parent database pointer (borrowed)*/
|
||||||
const std::string name; /**<\brief this storage name*/
|
const std::string name; /**<\brief this storage name*/
|
||||||
const bool duplicates; /**<\brief true if storage supports duplicates*/
|
const Duplicates 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 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 countMethodName = "count"; /**<\brief member function name, just for exceptions*/
|
||||||
@ -99,7 +99,7 @@ protected:
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
template <class K, class V>
|
template <class K, class V>
|
||||||
int makeStorage(MDB_txn* transaction, bool duplicates);
|
int makeStorage(MDB_txn* transaction, Duplicates duplicates = uniqueKey);
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
static std::string toString(const T& value);
|
static std::string toString(const T& value);
|
||||||
@ -108,10 +108,11 @@ protected:
|
|||||||
template <class K, class V>
|
template <class K, class V>
|
||||||
class Storage : public iStorage {
|
class Storage : public iStorage {
|
||||||
friend class ::BaseTest;
|
friend class ::BaseTest;
|
||||||
|
friend class ::DuplicatesTest;
|
||||||
friend class Base;
|
friend class Base;
|
||||||
friend class Cursor<K, V>;
|
friend class Cursor<K, V>;
|
||||||
protected:
|
protected:
|
||||||
Storage(Base* parent, const std::string& name, bool duplicates = false);
|
Storage(Base* parent, const std::string& name, Duplicates duplicates = uniqueKey);
|
||||||
~Storage() override;
|
~Storage() override;
|
||||||
|
|
||||||
virtual void discoveredRecord(const K& key, const V& value) const;
|
virtual void discoveredRecord(const K& key, const V& value) const;
|
||||||
|
@ -43,10 +43,10 @@
|
|||||||
*
|
*
|
||||||
* \param[in] parent - LMDBAL::Base pointer for the owning database (borrowed)
|
* \param[in] parent - LMDBAL::Base pointer for the owning database (borrowed)
|
||||||
* \param[in] name - the name of the storage
|
* \param[in] name - the name of the storage
|
||||||
* \param[in] duplicates - true if storage supports duplicates, false otherwise (false by default)
|
* \param[in] duplicates - LMDBAL::Duplicates duplicates mode (uniqueKey by default)
|
||||||
*/
|
*/
|
||||||
template<class K, class V>
|
template<class K, class V>
|
||||||
LMDBAL::Storage<K, V>::Storage(Base* parent, const std::string& name, bool duplicates):
|
LMDBAL::Storage<K, V>::Storage(Base* parent, const std::string& name, Duplicates duplicates):
|
||||||
iStorage(parent, name, duplicates),
|
iStorage(parent, name, duplicates),
|
||||||
keySerializer(),
|
keySerializer(),
|
||||||
valueSerializer(),
|
valueSerializer(),
|
||||||
@ -111,7 +111,20 @@ void LMDBAL::Storage<K, V>::addRecord(const K& key, const V& value, TransactionI
|
|||||||
MDB_val lmdbKey = keySerializer.setData(key);
|
MDB_val lmdbKey = keySerializer.setData(key);
|
||||||
MDB_val lmdbData = valueSerializer.setData(value);
|
MDB_val lmdbData = valueSerializer.setData(value);
|
||||||
|
|
||||||
int rc = mdb_put(txn, dbi, &lmdbKey, &lmdbData, MDB_NOOVERWRITE);
|
unsigned int flags;
|
||||||
|
switch (duplicates) {
|
||||||
|
case uniqueKey:
|
||||||
|
flags = MDB_NOOVERWRITE;
|
||||||
|
break;
|
||||||
|
case uniquePair:
|
||||||
|
flags = MDB_NODUPDATA;
|
||||||
|
break;
|
||||||
|
case full:
|
||||||
|
flags = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
int rc = mdb_put(txn, dbi, &lmdbKey, &lmdbData, flags);
|
||||||
if (rc != MDB_SUCCESS)
|
if (rc != MDB_SUCCESS)
|
||||||
throwDuplicateOrUnknown(rc, toString(key));
|
throwDuplicateOrUnknown(rc, toString(key));
|
||||||
}
|
}
|
||||||
@ -804,16 +817,19 @@ void LMDBAL::Storage<K, V>::discoveredRecord(const K& key, const V& value, Trans
|
|||||||
* This infrastructure also allowes us to customize mdb_dbi_open call in the future
|
* This infrastructure also allowes us to customize mdb_dbi_open call in the future
|
||||||
*/
|
*/
|
||||||
template<class K, class V>
|
template<class K, class V>
|
||||||
inline int LMDBAL::iStorage::makeStorage(MDB_txn* transaction, bool duplicates) {
|
inline int LMDBAL::iStorage::makeStorage(MDB_txn* transaction, Duplicates duplicates) {
|
||||||
unsigned int flags = MDB_CREATE;
|
unsigned int flags = MDB_CREATE;
|
||||||
if constexpr (std::is_integral<K>::value)
|
if constexpr (std::is_integral<K>::value)
|
||||||
flags |= MDB_INTEGERKEY;
|
flags |= MDB_INTEGERKEY;
|
||||||
|
|
||||||
if (duplicates) {
|
if (duplicates != uniqueKey) {
|
||||||
flags |= MDB_DUPSORT;
|
flags |= MDB_DUPSORT;
|
||||||
|
|
||||||
if constexpr (std::is_integral<V>::value)
|
if constexpr (std::is_scalar<K>::value && std::is_scalar<V>::value)
|
||||||
flags |= MDB_INTEGERDUP | MDB_DUPFIXED;
|
flags |= MDB_DUPFIXED;
|
||||||
|
else if constexpr (std::is_integral<V>::value)
|
||||||
|
flags |= MDB_INTEGERDUP;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return mdb_dbi_open(transaction, name.c_str(), flags, &dbi);
|
return mdb_dbi_open(transaction, name.c_str(), flags, &dbi);
|
||||||
|
@ -9,6 +9,7 @@ add_executable(runUnitTests
|
|||||||
cachetransaction.cpp
|
cachetransaction.cpp
|
||||||
storagecursor.cpp
|
storagecursor.cpp
|
||||||
cachecursor.cpp
|
cachecursor.cpp
|
||||||
|
duplicates.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
target_compile_options(runUnitTests PRIVATE -fPIC)
|
target_compile_options(runUnitTests PRIVATE -fPIC)
|
||||||
|
90
test/duplicates.cpp
Normal file
90
test/duplicates.cpp
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include "base.h"
|
||||||
|
#include "storage.h"
|
||||||
|
|
||||||
|
class DuplicatesTest : public ::testing::Test {
|
||||||
|
protected:
|
||||||
|
DuplicatesTest():
|
||||||
|
::testing::Test(),
|
||||||
|
t1(db->getStorage<int16_t, uint16_t>("sameSizeInts")),
|
||||||
|
t2(db->getStorage<std::string, int8_t>("stringInt")),
|
||||||
|
t3(db->getStorage<int64_t, int8_t>("differentSizeInts")),
|
||||||
|
t4(db->getStorage<uint16_t, double>("intDouble")) {}
|
||||||
|
|
||||||
|
~DuplicatesTest() {}
|
||||||
|
|
||||||
|
uint32_t getT1Flags() const {return t1->flags();}
|
||||||
|
uint32_t getT2Flags() const {return t2->flags();}
|
||||||
|
uint32_t getT3Flags() const {return t3->flags();}
|
||||||
|
uint32_t getT4Flags() const {return t4->flags();}
|
||||||
|
|
||||||
|
static void SetUpTestSuite() {
|
||||||
|
if (db == nullptr) {
|
||||||
|
db = new LMDBAL::Base("testBase");
|
||||||
|
db->addStorage<int16_t, uint16_t>("sameSizeInts", LMDBAL::uniquePair);
|
||||||
|
db->addStorage<std::string, int8_t>("stringInt", LMDBAL::uniquePair);
|
||||||
|
db->addStorage<int64_t, int8_t>("differentSizeInts", LMDBAL::uniquePair);
|
||||||
|
db->addStorage<uint16_t, double>("intDouble", LMDBAL::uniquePair);
|
||||||
|
|
||||||
|
db->open();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void TearDownTestSuite() {
|
||||||
|
db->close();
|
||||||
|
db->removeDirectory();
|
||||||
|
delete db;
|
||||||
|
db = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static LMDBAL::Base* db;
|
||||||
|
|
||||||
|
LMDBAL::Storage<int16_t, uint16_t>* t1;
|
||||||
|
LMDBAL::Storage<std::string, int8_t>* t2;
|
||||||
|
LMDBAL::Storage<int64_t, int8_t>* t3;
|
||||||
|
LMDBAL::Storage<uint16_t, double>* t4;
|
||||||
|
};
|
||||||
|
|
||||||
|
LMDBAL::Base* DuplicatesTest::db = nullptr;
|
||||||
|
|
||||||
|
TEST_F(DuplicatesTest, Flags) {
|
||||||
|
uint32_t t1Flags = getT1Flags();
|
||||||
|
uint32_t t2Flags = getT2Flags();
|
||||||
|
uint32_t t3Flags = getT3Flags();
|
||||||
|
uint32_t t4Flags = getT4Flags();
|
||||||
|
|
||||||
|
EXPECT_TRUE(t1Flags & MDB_INTEGERKEY);
|
||||||
|
EXPECT_TRUE(t1Flags & MDB_DUPSORT);
|
||||||
|
EXPECT_TRUE(t1Flags & MDB_DUPFIXED);
|
||||||
|
EXPECT_FALSE(t1Flags & MDB_INTEGERDUP);
|
||||||
|
|
||||||
|
EXPECT_FALSE(t2Flags & MDB_INTEGERKEY);
|
||||||
|
EXPECT_TRUE(t2Flags & MDB_DUPSORT);
|
||||||
|
EXPECT_FALSE(t2Flags & MDB_DUPFIXED);
|
||||||
|
EXPECT_TRUE(t2Flags & MDB_INTEGERDUP);
|
||||||
|
|
||||||
|
EXPECT_TRUE(t3Flags & MDB_INTEGERKEY);
|
||||||
|
EXPECT_TRUE(t3Flags & MDB_DUPSORT);
|
||||||
|
EXPECT_TRUE(t3Flags & MDB_DUPFIXED);
|
||||||
|
EXPECT_FALSE(t3Flags & MDB_INTEGERDUP);
|
||||||
|
|
||||||
|
EXPECT_TRUE(t4Flags & MDB_INTEGERKEY);
|
||||||
|
EXPECT_TRUE(t4Flags & MDB_DUPSORT);
|
||||||
|
EXPECT_TRUE(t4Flags & MDB_DUPFIXED);
|
||||||
|
EXPECT_FALSE(t4Flags & MDB_INTEGERDUP);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(DuplicatesTest, Adding) {
|
||||||
|
t1->addRecord(1, 1);
|
||||||
|
t1->addRecord(2, 2);
|
||||||
|
t1->addRecord(2, 1);
|
||||||
|
t1->addRecord(1, 2);
|
||||||
|
EXPECT_THROW(t1->addRecord(1, 1), LMDBAL::Exist);
|
||||||
|
EXPECT_THROW(t1->addRecord(1, 2), LMDBAL::Exist);
|
||||||
|
EXPECT_THROW(t1->addRecord(2, 2), LMDBAL::Exist);
|
||||||
|
|
||||||
|
EXPECT_EQ(t1->count(), 4);
|
||||||
|
EXPECT_EQ(t1->getRecord(1), 1);
|
||||||
|
EXPECT_EQ(t1->getRecord(2), 1);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user