From 06e1aca45a35c425c0b4bdf44dff73e3906b3824 Mon Sep 17 00:00:00 2001 From: blue Date: Thu, 17 Aug 2023 11:45:11 -0300 Subject: [PATCH] some more ideas about duplicates --- src/base.h | 21 +++++++---- src/cache.h | 2 +- src/cache.hpp | 4 +- src/storage.cpp | 4 +- src/storage.h | 11 +++--- src/storage.hpp | 30 +++++++++++---- test/CMakeLists.txt | 1 + test/duplicates.cpp | 90 +++++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 139 insertions(+), 24 deletions(-) create mode 100644 test/duplicates.cpp diff --git a/src/base.h b/src/base.h index 85f75c4..f09330e 100644 --- a/src/base.h +++ b/src/base.h @@ -48,6 +48,12 @@ class Cache; typedef MDB_txn* TransactionID; /**AND value pairs allowed*/ + full /** - any configuration of duplicates goes*/ +}; + class Base { friend class iStorage; public: @@ -69,7 +75,7 @@ public: void abortTransaction(TransactionID id) const; template - LMDBAL::Storage* addStorage(const std::string& storageName, bool duplicates = false); + LMDBAL::Storage* addStorage(const std::string& storageName, Duplicates duplicates = uniqueKey); template LMDBAL::Cache* addCache(const std::string& storageName); @@ -115,6 +121,7 @@ private: * The LMDBAL::Base must be closed * * \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 * @@ -125,10 +132,10 @@ private: * \exception LMDBAL::StorageDuplicate thrown if somebody tries to add storage with repeating name */ template -LMDBAL::Storage* LMDBAL::Base::addStorage(const std::string& storageName, bool duplicates) { - if (opened) { +LMDBAL::Storage* LMDBAL::Base::addStorage(const std::string& storageName, Duplicates duplicates) { + if (opened) throw Opened(name, "add storage " + storageName); - } + Storage* storage = new Storage(this, storageName, duplicates); std::pair pair = storages.insert(std::make_pair(storageName, (iStorage*)storage)); if (!pair.second) @@ -154,10 +161,10 @@ LMDBAL::Storage* LMDBAL::Base::addStorage(const std::string& storageName, */ template LMDBAL::Cache * LMDBAL::Base::addCache(const std::string& storageName) { - if (opened) { + if (opened) throw Opened(name, "add cache " + storageName); - } - Cache* cache = new Cache(this, storageName, false); + + Cache* cache = new Cache(this, storageName, uniqueKey); std::pair pair = storages.insert(std::make_pair(storageName, (iStorage*)cache)); if (!pair.second) throw StorageDuplicate(name, storageName); diff --git a/src/cache.h b/src/cache.h index ff0d996..7a6bef2 100644 --- a/src/cache.h +++ b/src/cache.h @@ -51,7 +51,7 @@ class Cache : public Storage { typedef std::map TransactionCache; protected: - Cache(Base* parent, const std::string& name, bool duplicates = false); + Cache(Base* parent, const std::string& name, Duplicates duplicates = uniqueKey); ~Cache() override; virtual void transactionStarted(TransactionID txn, bool readOnly) const override; diff --git a/src/cache.hpp b/src/cache.hpp index 063b1bd..5f5edb9 100644 --- a/src/cache.hpp +++ b/src/cache.hpp @@ -41,10 +41,10 @@ * * \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) + * \param[in] duplicates - LMDBAL::Duplicates duplicates mode (uniqueKey by default) */ template -LMDBAL::Cache::Cache(Base* parent, const std::string& name, bool duplicates): +LMDBAL::Cache::Cache(Base* parent, const std::string& name, Duplicates duplicates): Storage(parent, name, duplicates), mode(Mode::nothing), cache(new std::map()), diff --git a/src/storage.cpp b/src/storage.cpp index 0f09886..28cd319 100644 --- a/src/storage.cpp +++ b/src/storage.cpp @@ -33,9 +33,9 @@ * * \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) + * \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(), db(parent), name(name), diff --git a/src/storage.h b/src/storage.h index bd1229a..adcc0f0 100644 --- a/src/storage.h +++ b/src/storage.h @@ -26,6 +26,7 @@ #include "cursor.h" class BaseTest; +class DuplicatesTest; namespace LMDBAL { @@ -34,9 +35,8 @@ typedef uint32_t SizeType; class iStorage { friend class Base; public: - protected: - iStorage(Base* parent, const std::string& name, bool duplicates = false); + iStorage(Base* parent, const std::string& name, Duplicates duplicates = uniqueKey); virtual ~iStorage(); /** @@ -81,7 +81,7 @@ 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*/ + 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 countMethodName = "count"; /**<\brief member function name, just for exceptions*/ @@ -99,7 +99,7 @@ protected: protected: template - int makeStorage(MDB_txn* transaction, bool duplicates); + int makeStorage(MDB_txn* transaction, Duplicates duplicates = uniqueKey); template static std::string toString(const T& value); @@ -108,10 +108,11 @@ protected: template class Storage : public iStorage { friend class ::BaseTest; + friend class ::DuplicatesTest; friend class Base; friend class Cursor; protected: - Storage(Base* parent, const std::string& name, bool duplicates = false); + Storage(Base* parent, const std::string& name, Duplicates duplicates = uniqueKey); ~Storage() override; virtual void discoveredRecord(const K& key, const V& value) const; diff --git a/src/storage.hpp b/src/storage.hpp index d1dcbae..3f5296f 100644 --- a/src/storage.hpp +++ b/src/storage.hpp @@ -43,10 +43,10 @@ * * \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) + * \param[in] duplicates - LMDBAL::Duplicates duplicates mode (uniqueKey by default) */ template -LMDBAL::Storage::Storage(Base* parent, const std::string& name, bool duplicates): +LMDBAL::Storage::Storage(Base* parent, const std::string& name, Duplicates duplicates): iStorage(parent, name, duplicates), keySerializer(), valueSerializer(), @@ -111,7 +111,20 @@ void LMDBAL::Storage::addRecord(const K& key, const V& value, TransactionI MDB_val lmdbKey = keySerializer.setData(key); 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) throwDuplicateOrUnknown(rc, toString(key)); } @@ -804,16 +817,19 @@ void LMDBAL::Storage::discoveredRecord(const K& key, const V& value, Trans * This infrastructure also allowes us to customize mdb_dbi_open call in the future */ template -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; if constexpr (std::is_integral::value) flags |= MDB_INTEGERKEY; - if (duplicates) { + if (duplicates != uniqueKey) { flags |= MDB_DUPSORT; - if constexpr (std::is_integral::value) - flags |= MDB_INTEGERDUP | MDB_DUPFIXED; + if constexpr (std::is_scalar::value && std::is_scalar::value) + flags |= MDB_DUPFIXED; + else if constexpr (std::is_integral::value) + flags |= MDB_INTEGERDUP; + } return mdb_dbi_open(transaction, name.c_str(), flags, &dbi); diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index b2b1eba..ab59a88 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -9,6 +9,7 @@ add_executable(runUnitTests cachetransaction.cpp storagecursor.cpp cachecursor.cpp + duplicates.cpp ) target_compile_options(runUnitTests PRIVATE -fPIC) diff --git a/test/duplicates.cpp b/test/duplicates.cpp new file mode 100644 index 0000000..f25feba --- /dev/null +++ b/test/duplicates.cpp @@ -0,0 +1,90 @@ +#include + +#include "base.h" +#include "storage.h" + +class DuplicatesTest : public ::testing::Test { +protected: + DuplicatesTest(): + ::testing::Test(), + t1(db->getStorage("sameSizeInts")), + t2(db->getStorage("stringInt")), + t3(db->getStorage("differentSizeInts")), + t4(db->getStorage("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("sameSizeInts", LMDBAL::uniquePair); + db->addStorage("stringInt", LMDBAL::uniquePair); + db->addStorage("differentSizeInts", LMDBAL::uniquePair); + db->addStorage("intDouble", LMDBAL::uniquePair); + + db->open(); + } + } + + static void TearDownTestSuite() { + db->close(); + db->removeDirectory(); + delete db; + db = nullptr; + } + + static LMDBAL::Base* db; + + LMDBAL::Storage* t1; + LMDBAL::Storage* t2; + LMDBAL::Storage* t3; + LMDBAL::Storage* 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); +}