From 2d4069256067541dfad13e0b991b7ee8d14b3780 Mon Sep 17 00:00:00 2001 From: blue Date: Sat, 19 Aug 2023 16:25:52 -0300 Subject: [PATCH] duplicates handling for forceRecord and changeRecord methods, tests and docs --- src/base.h | 1 + src/storage.h | 3 +- src/storage.hpp | 137 +++++++++++++++++++++++++----------- test/duplicates.cpp | 165 +++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 263 insertions(+), 43 deletions(-) diff --git a/src/base.h b/src/base.h index 2b7353e..fd5ba18 100644 --- a/src/base.h +++ b/src/base.h @@ -47,6 +47,7 @@ template class Cache; typedef MDB_txn* TransactionID; /** +#include #include "base.h" #include "serializer.h" @@ -30,8 +31,6 @@ class DuplicatesTest; namespace LMDBAL { -typedef uint32_t SizeType; - class iStorage { friend class Base; public: diff --git a/src/storage.hpp b/src/storage.hpp index 601449c..8a8ef31 100644 --- a/src/storage.hpp +++ b/src/storage.hpp @@ -93,7 +93,7 @@ void LMDBAL::Storage::addRecord(const K& key, const V& value) { /** * \brief Adds a key-value record to the storage (transaction variant) * - * This function schedules an addition of a key-value record, but doesn't immidiately adds it. + * This method schedules an addition of a key-value record, but doesn't immidiately adds it. * You can obtain LMDBAL::TransactionID calling LMDBAL::Base::beginTransaction(). * * Take a note that if the storage already had a record you want to add LMDBAL::Exist is thrown. @@ -128,8 +128,15 @@ void LMDBAL::Storage::addRecord(const K& key, const V& value, TransactionI /** * \brief Adds a key-value record to the storage, overwrites if it already exists + * + * This method is mostly useless in duplicates mode. + * In this mode it basically does the same thing LMDBAL::Storage::addRecord() does, + * but suppresses LMDBAL::Exist exception if the record with the same key-value pair existed in the storage. + * In this case just false is returned from the method. + * * \param[in] key key of the record * \param[in] value value of the record + * * \returns true if the record was added, false otherwise * * \exception LMDBAL::Closed thrown if the database was not opened @@ -155,10 +162,15 @@ bool LMDBAL::Storage::forceRecord(const K& key, const V& value) { /** * \brief Adds a key-value record to the storage, overwrites if it already exists (transaction variant) * - * This function schedules an addition of a key-value record, but doesn't immidiately adds it. + * This method schedules an addition of a key-value record, but doesn't immidiately add it. * You can obtain LMDBAL::TransactionID calling LMDBAL::Base::beginTransaction(). * If the record did already exist in the database the actual overwrite will be done only after calling LMDBAL::Base::commitTransaction(). * + * This method is mostly useless in duplicates mode. + * In this mode it basically does the same thing LMDBAL::Storage::addRecord() does, + * but suppresses LMDBAL::Exist exception if the record with the same key-value pair existed in the storage. + * In this case just false is returned from the method. + * * \param[in] key key of the record * \param[in] value value of the record * \param[in] txn transaction ID, needs to be a writable transaction! @@ -172,27 +184,35 @@ bool LMDBAL::Storage::forceRecord(const K& key, const V& value, Transactio ensureOpened(forceRecordMethodName); bool added; - MDB_val lmdbKey = keySerializer.setData(key); - MDB_val lmdbData; - - int rc = mdb_get(txn, dbi, &lmdbKey, &lmdbData); - switch (rc) { - case MDB_SUCCESS: - added = false; - break; - case MDB_NOTFOUND: + if (duplicates) { + try { + addRecord(key, value, txn); added = true; - break; - default: + } catch (const LMDBAL::Exist& e) { added = false; + } + } else { + MDB_val lmdbKey = keySerializer.setData(key); + MDB_val lmdbData; + + int rc = mdb_get(txn, dbi, &lmdbKey, &lmdbData); + switch (rc) { + case MDB_SUCCESS: + added = false; + break; + case MDB_NOTFOUND: + added = true; + break; + default: + added = false; + throwUnknown(rc); + } + + lmdbData = valueSerializer.setData(value); + rc = mdb_put(txn, dbi, &lmdbKey, &lmdbData, 0); + if (rc != MDB_SUCCESS) throwUnknown(rc); } - - lmdbData = valueSerializer.setData(value); - rc = mdb_put(txn, dbi, &lmdbKey, &lmdbData, 0); - if (rc != MDB_SUCCESS) - throwUnknown(rc); - return added; } @@ -201,10 +221,17 @@ bool LMDBAL::Storage::forceRecord(const K& key, const V& value, Transactio * * Take a note that if the storage didn't have a record you want to change LMDBAL::NotFound is thrown * + * If duplicates mode is enabled this function will find the first entry of the key + * (which is pretty couterintuitive, see LMDBAL::Storage::getRecord() description) + * and change it's value to the given one. + * If the given value matches some of the other values for given key the method will throw LMDBAL::Exist, + * if no key was found it will still throw LMDBAL::NotFound. + * * \param[in] key key of the record * \param[in] value new value of the record * * \exception LMDBAL::NotFound thrown if the storage doesn't have a record with the given key + * \exception LMDBAL::Exist thrown in duplicates mode when the given value matches some of existing values for the given key * \exception LMDBAL::Closed thrown if the database was not opened * \exception LMDBAL::Unknown thrown if something unexpected happend within lmdb */ @@ -226,16 +253,23 @@ void LMDBAL::Storage::changeRecord(const K& key, const V& value) { /** * \brief Changes key-value record to the storage (transaction variant) * - * This function schedules a modification of a key-value record, but doesn't immidiately changes it. + * This method schedules a modification of a key-value record, but doesn't immidiately changes it. * You can obtain LMDBAL::TransactionID calling LMDBAL::Base::beginTransaction(). * * Take a note that if the storage didn't have a record you want to change LMDBAL::NotFound is thrown * + * If duplicates mode is enabled this function will find the first entry of the key + * (which is pretty couterintuitive, see LMDBAL::Storage::getRecord() description) + * and change it's value to the given one. + * If the given value matches some of the other values for given key the method will throw LMDBAL::Exist, + * if no key was found it will still throw LMDBAL::NotFound. + * * \param[in] key key of the record * \param[in] value new value of the record * \param[in] txn transaction ID, needs to be a writable transaction! * * \exception LMDBAL::NotFound thrown if the storage doesn't have a record with the given key + * \exception LMDBAL::Exist thrown in duplicates mode when the given value matches some of existing values for the given key * \exception LMDBAL::Closed thrown if the database was not opened * \exception LMDBAL::Unknown thrown if something unexpected happend within lmdb */ @@ -249,15 +283,34 @@ void LMDBAL::Storage::changeRecord(const K& key, const V& value, Transacti throwUnknown(rc); MDB_val lmdbKey = keySerializer.setData(key); - rc = mdb_cursor_get(cursor, &lmdbKey, nullptr, MDB_SET); + MDB_val lmdbData; + rc = mdb_cursor_get(cursor, &lmdbKey, &lmdbData, MDB_SET); if (rc != MDB_SUCCESS) throwNotFoundOrUnknown(rc, toString(key)); - MDB_val lmdbData = valueSerializer.setData(value); - rc = mdb_cursor_put(cursor, &lmdbKey, &lmdbData, MDB_CURRENT); + MDB_val lmdbNewData = valueSerializer.setData(value); + bool sameSize = lmdbData.mv_size == lmdbNewData.mv_size; + int firstDifferentByte = 0; + if (sameSize) { //can compare only if they are the same size + firstDifferentByte = memcmp(lmdbData.mv_data, lmdbNewData.mv_data, lmdbData.mv_size); + if (firstDifferentByte == 0) { //old and new is the same, nothing to do + mdb_cursor_close(cursor); + return; + } + } + + unsigned int flags = MDB_CURRENT; + if (duplicates && (!sameSize || firstDifferentByte < 0)) { //if new value is greater than the old one + rc = mdb_cursor_del(cursor, 0); //we need to initiate duplicates sort, for it to be in the correct place + flags = MDB_NODUPDATA; + } + + if (rc == MDB_SUCCESS) + rc = mdb_cursor_put(cursor, &lmdbKey, &lmdbNewData, flags); + mdb_cursor_close(cursor); if (rc != MDB_SUCCESS) - throwUnknown(rc); + throwDuplicateOrUnknown(rc, toString(key)); } /** @@ -265,6 +318,12 @@ void LMDBAL::Storage::changeRecord(const K& key, const V& value, Transacti * Take a note that if the storage didn't have a record you want to get LMDBAL::NotFound is thrown * + * If the storage supports duplicates the exact value returned from it depends on comparison function of lmdb. + * It's not very straight forward, so, you shouldn't really use this method if you use duplicates and you rely on exact result. + * Anyway: + * - if your values are signed or unsigned integer of any size the LOWEST value is returned compared as UNSIGNED. For example for storage with int32_t as value, from the same key, from the set of values {-33, -1, 5573, 77753} 5573 is returned as it is the lowest by UNSIGNED comparison. + * - if your values are anything else - they are compared byte by byte as if they are strings, it makes it especially complicated to predict the exact value for float or double templated storages. For strings if makes a bit more sence: if the choise is from "50" and "100" - "100" is returned, because the first byte of the "100" is lower than the first byte of the "50" + * * \param[in] key key of the record you look for * \returns the value from the storage * @@ -287,7 +346,7 @@ V LMDBAL::Storage::getRecord(const K& key) const { * Take a note that if the storage didn't have a record you want to get LMDBAL::NotFound is thrown * * If the storage supports duplicates the exact value returned from it depends on comparison function of lmdb. - * It's not very straight forward, so, you shouldn't really use this function if you use duplicates and you rely on exact result. + * It's not very straight forward, so, you shouldn't really use this method if you use duplicates and you rely on exact result. * Anyway: * - if your values are signed or unsigned integer of any size the LOWEST value is returned compared as UNSIGNED. For example for storage with int32_t as value, from the same key, from the set of values {-33, -1, 5573, 77753} 5573 is returned as it is the lowest by UNSIGNED comparison. * - if your values are anything else - they are compared byte by byte as if they are strings, it makes it especially complicated to predict the exact value for float or double templated storages. For strings if makes a bit more sence: if the choise is from "50" and "100" - "100" is returned, because the first byte of the "100" is lower than the first byte of the "50" @@ -323,7 +382,7 @@ void LMDBAL::Storage::getRecord(const K& key, V& value) const { * Take a note that if the storage didn't have a record you want to get LMDBAL::NotFound is thrown * * If the storage supports duplicates the exact value returned from it depends on comparison function of lmdb. - * It's not very straight forward, so, you shouldn't really use this function if you use duplicates and you rely on exact result. + * It's not very straight forward, so, you shouldn't really use this method if you use duplicates and you rely on exact result. * Anyway: * - if your values are signed or unsigned integer of any size the LOWEST value is returned compared as UNSIGNED. For example for storage with int32_t as value, from the same key, from the set of values {-33, -1, 5573, 77753} 5573 is returned as it is the lowest by UNSIGNED comparison. * - if your values are anything else - they are compared byte by byte as if they are strings, it makes it especially complicated to predict the exact value for float or double templated storages. For strings if makes a bit more sence: if the choise is from "50" and "100" - "100" is returned, because the first byte of the "100" is lower than the first byte of the "50" @@ -354,7 +413,7 @@ V LMDBAL::Storage::getRecord(const K& key, TransactionID txn) const { * Take a note that if the storage didn't have a record you want to get LMDBAL::NotFound is thrown * * If the storage supports duplicates the exact value returned from it depends on comparison function of lmdb. - * It's not very straight forward, so, you shouldn't really use this function if you use duplicates and you rely on exact result. + * It's not very straight forward, so, you shouldn't really use this method if you use duplicates and you rely on exact result. * Anyway: * - if your values are signed or unsigned integer of any size the LOWEST value is returned compared as UNSIGNED. For example for storage with int32_t as value, from the same key, from the set of values {-33, -1, 5573, 77753} 5573 is returned as it is the lowest by UNSIGNED comparison. * - if your values are anything else - they are compared byte by byte as if they are strings, it makes it especially complicated to predict the exact value for float or double templated storages. For strings if makes a bit more sence: if the choise is from "50" and "100" - "100" is returned, because the first byte of the "100" is lower than the first byte of the "50" @@ -540,7 +599,7 @@ void LMDBAL::Storage::readAll(std::map& result, TransactionID txn) c /** * \brief Replaces the content of the whole storage with the given * - * Basically this function drops the database and adds all the records from the given map + * Basically this method drops the database and adds all the records from the given map * * \param[in] data new data of the storage * @@ -565,8 +624,8 @@ void LMDBAL::Storage::replaceAll(const std::map& data) { /** * \brief Replaces the content of the whole storage with the given (transaction variant) * - * Basically this function drops the database and adds all the records from the given map - * This function schedules a data replacement, but doesn't immidiately execute it. + * Basically this method drops the database and adds all the records from the given map + * This method schedules a data replacement, but doesn't immidiately execute it. * You can obtain LMDBAL::TransactionID calling LMDBAL::Base::beginTransaction(). * * \param[in] data new data of the storage @@ -598,7 +657,7 @@ void LMDBAL::Storage::replaceAll(const std::map& data, TransactionID * \brief Adds records in bulk * * \param[in] data the data to be added - * \param[in] overwrite if false function throws LMDBAL::Exist on repeated key, if true - overwrites it + * \param[in] overwrite if false method throws LMDBAL::Exist on repeated key, if true - overwrites it * \returns new actual amount of records in the storage * * \exception LMDBAL::Closed thrown if the database was not opened @@ -625,12 +684,12 @@ uint32_t LMDBAL::Storage::addRecords(const std::map& data, bool over /** * \brief Adds records in bulk (transaction variant) * - * This function schedules a data addition, but doesn't immidiately execute it. + * This method schedules a data addition, but doesn't immidiately execute it. * You can obtain LMDBAL::TransactionID calling LMDBAL::Base::beginTransaction(). * * \param[in] data the data to be added * \param[in] txn transaction ID, needs to be a writable transaction! - * \param[in] overwrite if false function throws LMDBAL::Exist on repeated key, if true - overwrites it + * \param[in] overwrite if false method throws LMDBAL::Exist on repeated key, if true - overwrites it * \returns new actual amount of records in the storage * * \exception LMDBAL::Closed thrown if the database was not opened @@ -693,7 +752,7 @@ void LMDBAL::Storage::removeRecord(const K& key) { * \brief Removes one of the records (transaction variant) * * Take a note that if the storage didn't have a record you want to remove LMDBAL::NotFound is thrown - * This function schedules a record removal, but doesn't immidiately execute it. + * This method schedules a record removal, but doesn't immidiately execute it. * You can obtain LMDBAL::TransactionID calling LMDBAL::Base::beginTransaction(). * * \param[in] key key of the record you wish to be removed @@ -714,7 +773,7 @@ void LMDBAL::Storage::removeRecord(const K& key, TransactionID txn) { } /** - * \brief A private virtual function I need to open each storage in the database + * \brief A private virtual method I need to open each storage in the database * * \param[in] transaction - lmdb transaction to call mdb_dbi_open * \returns MDB_SUCCESS if everything went smooth or MDB_ -like error code @@ -725,7 +784,7 @@ int LMDBAL::Storage::open(MDB_txn* transaction) { } /** - * \brief A private virtual function I need to close each storage in the database + * \brief A private virtual method I need to close each storage in the database */ template void LMDBAL::Storage::close() { @@ -751,7 +810,7 @@ LMDBAL::Cursor* LMDBAL::Storage::createCursor() { /** * \brief Reads current storage flags it was opened with * - * This function exists mostly for testing purposes + * This method exists mostly for testing purposes * * \returns Third out parameter of mdb_dbi_flags function * @@ -792,7 +851,7 @@ void LMDBAL::Storage::destroyCursor(Cursor* cursor) { } /** - * \brief A private virtual function that cursor calls when he reads a record, does nothing here but populates the LMDBAL::Cache + * \brief A private virtual method that cursor calls when he reads a record, does nothing here but populates the LMDBAL::Cache * * \param[in] key a key of discovered record * \param[in] value a value of discovered record @@ -804,7 +863,7 @@ void LMDBAL::Storage::discoveredRecord(const K& key, const V& value) const } /** - * \brief A private virtual function that cursor calls when he reads a record, does nothing here but populates the LMDBAL::Cache + * \brief A private virtual method that cursor calls when he reads a record, does nothing here but populates the LMDBAL::Cache * * \param[in] key a key of discovered record * \param[in] value a value of discovered record diff --git a/test/duplicates.cpp b/test/duplicates.cpp index 7ffa2fa..d84ba2b 100644 --- a/test/duplicates.cpp +++ b/test/duplicates.cpp @@ -54,7 +54,7 @@ protected: LMDBAL::Base* DuplicatesTest::db = nullptr; -TEST_F(DuplicatesTest, FlagsUnique) { +TEST_F(DuplicatesTest, Flags) { uint32_t tu1Flags = getTU1Flags(); uint32_t tu2Flags = getTU2Flags(); uint32_t tu3Flags = getTU3Flags(); @@ -87,7 +87,7 @@ TEST_F(DuplicatesTest, FlagsUnique) { EXPECT_TRUE(tu5Flags & MDB_INTEGERDUP); } -TEST_F(DuplicatesTest, AddingPairUnique) { +TEST_F(DuplicatesTest, Adding) { tu1->addRecord(1, 1); tu1->addRecord(2, 2); tu1->addRecord(2, 1); @@ -165,3 +165,164 @@ TEST_F(DuplicatesTest, AddingPairUnique) { EXPECT_EQ(tu5->getRecord(-84.7), 45656753); EXPECT_EQ(tu5->getRecord(52.87), intMax); } + +TEST_F(DuplicatesTest, Forcing) { + LMDBAL::SizeType tu1Size = tu1->count(); + tu1->addRecord(-56, 71); + tu1->addRecord(-56, 274); + tu1->addRecord(-56, 732); + EXPECT_EQ(tu1->count(), tu1Size += 3); + EXPECT_TRUE(tu1->forceRecord(-56, 322)); + EXPECT_EQ(tu1->count(), tu1Size += 1); + EXPECT_EQ(tu1->getRecord(-56), 274); //like yeah, it's really counterintuitive, since it's compared byte by byte + EXPECT_TRUE(tu1->forceRecord(-56, 14)); + EXPECT_EQ(tu1->count(), tu1Size += 1); + EXPECT_EQ(tu1->getRecord(-56), 14); + EXPECT_FALSE(tu1->forceRecord(-56, 274)); + EXPECT_EQ(tu1->count(), tu1Size); + + LMDBAL::SizeType tu2Size = tu2->count(); + tu2->addRecord("printable", -2); + tu2->addRecord("printable", 4); + EXPECT_EQ(tu2->count(), tu2Size += 2); + EXPECT_TRUE(tu2->forceRecord("printable", 18)); + EXPECT_EQ(tu2->count(), tu2Size += 1); + EXPECT_EQ(tu2->getRecord("printable"), 4); + EXPECT_TRUE(tu2->forceRecord("printable", 3)); + EXPECT_EQ(tu2->count(), tu2Size += 1); + EXPECT_EQ(tu2->getRecord("printable"), 3); + EXPECT_FALSE(tu2->forceRecord("printable", 4)); + EXPECT_EQ(tu2->count(), tu2Size); + + LMDBAL::SizeType tu3Size = tu3->count(); + tu3->addRecord(17.3, 93.21); + tu3->addRecord(17.3, 6.6); + tu3->addRecord(17.3, 105.1); + EXPECT_EQ(tu3->count(), tu3Size += 3); + EXPECT_TRUE(tu3->forceRecord(17.3, 74.9)); + EXPECT_EQ(tu3->count(), tu3Size += 1); + EXPECT_EQ(tu3->getRecord(17.3), 105.1f); //here too, really one should not use this function with duplicates, + EXPECT_TRUE(tu3->forceRecord(17.3, 5.1)); //unless he wishes for kinda randomish result + EXPECT_EQ(tu3->count(), tu3Size += 1); + EXPECT_EQ(tu3->getRecord(17.3), 5.1f); + EXPECT_FALSE(tu3->forceRecord(17.3, 93.21)); + EXPECT_EQ(tu3->count(), tu3Size); + + LMDBAL::SizeType tu4Size = tu4->count(); + tu4->addRecord(84, -359.109); + tu4->addRecord(84, 2879.654); + EXPECT_EQ(tu4->count(), tu4Size += 2); + EXPECT_TRUE(tu4->forceRecord(84, 72.9)); + EXPECT_EQ(tu4->count(), tu4Size += 1); + EXPECT_EQ(tu4->getRecord(84), 2879.654); + EXPECT_TRUE(tu4->forceRecord(84, 2679.5)); + EXPECT_EQ(tu4->count(), tu4Size += 1); + EXPECT_EQ(tu4->getRecord(84), 2679.5); + EXPECT_FALSE(tu4->forceRecord(84, -359.109)); + EXPECT_EQ(tu4->count(), tu4Size); + + LMDBAL::SizeType tu5Size = tu5->count(); + tu5->addRecord(0.45, -85645); + tu5->addRecord(0.45, 10573); + tu5->addRecord(0.45, 573); + tu5->addRecord(0.45, 73285); + EXPECT_EQ(tu5->count(), tu5Size += 4); + EXPECT_TRUE(tu5->forceRecord(0.45, -473)); + EXPECT_EQ(tu5->count(), tu5Size += 1); + EXPECT_EQ(tu5->getRecord(0.45), 573); + EXPECT_TRUE(tu5->forceRecord(0.45, 394)); + EXPECT_EQ(tu5->count(), tu5Size += 1); + EXPECT_EQ(tu5->getRecord(0.45), 394); + EXPECT_FALSE(tu5->forceRecord(0.45, 10573)); + EXPECT_EQ(tu5->count(), tu5Size); +} + +TEST_F(DuplicatesTest, Changing) { + LMDBAL::SizeType tu1Size = tu1->count(); + EXPECT_THROW(tu1->changeRecord(-31, 53), LMDBAL::NotFound); + EXPECT_EQ(tu1->count(), tu1Size); + tu1->addRecord(-31, 53); + EXPECT_EQ(tu1->count(), tu1Size += 1); + EXPECT_EQ(tu1->getRecord(-31), 53); + tu1->changeRecord(-31, 53); //should just do nothing usefull, but work normally + EXPECT_EQ(tu1->getRecord(-31), 53); + tu1->changeRecord(-31, 19); + EXPECT_EQ(tu1->count(), tu1Size); + EXPECT_EQ(tu1->getRecord(-31), 19); + tu1->addRecord(-31, 60); + EXPECT_EQ(tu1->count(), tu1Size += 1); + EXPECT_EQ(tu1->getRecord(-31), 19); + tu1->changeRecord(-31, 16); + EXPECT_EQ(tu1->count(), tu1Size); + EXPECT_EQ(tu1->getRecord(-31), 16); + tu1->changeRecord(-31, 203); + EXPECT_EQ(tu1->count(), tu1Size); + EXPECT_EQ(tu1->getRecord(-31), 60); + EXPECT_THROW(tu1->changeRecord(-31, 203), LMDBAL::Exist); + + LMDBAL::SizeType tu2Size = tu2->count(); + EXPECT_THROW(tu2->changeRecord("jeremy spins", -5), LMDBAL::NotFound); + EXPECT_EQ(tu2->count(), tu2Size); + tu2->addRecord("jeremy spins", -5); + EXPECT_EQ(tu2->count(), tu2Size += 1); + EXPECT_EQ(tu2->getRecord("jeremy spins"), -5); + tu2->changeRecord("jeremy spins", -5); //should just do nothing usefull, but work normally + EXPECT_EQ(tu2->getRecord("jeremy spins"), -5); + tu2->changeRecord("jeremy spins", 11); + EXPECT_EQ(tu2->count(), tu2Size); + EXPECT_EQ(tu2->getRecord("jeremy spins"), 11); + tu2->addRecord("jeremy spins", 24); + EXPECT_EQ(tu2->count(), tu2Size += 1); + EXPECT_EQ(tu2->getRecord("jeremy spins"), 11); + tu2->changeRecord("jeremy spins", 4); + EXPECT_EQ(tu2->count(), tu2Size); + EXPECT_EQ(tu2->getRecord("jeremy spins"), 4); + tu2->changeRecord("jeremy spins", -7); + EXPECT_EQ(tu2->count(), tu2Size); + EXPECT_EQ(tu2->getRecord("jeremy spins"), 24); //cuz it's compared as usigned down there + EXPECT_THROW(tu2->changeRecord("jeremy spins", -7), LMDBAL::Exist); + + LMDBAL::SizeType tu3Size = tu3->count(); + EXPECT_THROW(tu3->changeRecord(26.7, 68.22), LMDBAL::NotFound); + EXPECT_EQ(tu3->count(), tu3Size); + tu3->addRecord(26.7, 68.22); + EXPECT_EQ(tu3->count(), tu3Size += 1); + EXPECT_EQ(tu3->getRecord(26.7), 68.22f); + tu3->changeRecord(26.7, 68.22); //should just do nothing usefull, but work normally + EXPECT_EQ(tu3->getRecord(26.7), 68.22f); + tu3->changeRecord(26.7, 23.18); + EXPECT_EQ(tu3->count(), tu3Size); + EXPECT_EQ(tu3->getRecord(26.7), 23.18f); + tu3->addRecord(26.7, 22.16); + EXPECT_EQ(tu3->count(), tu3Size += 1); + EXPECT_EQ(tu3->getRecord(26.7), 23.18f); + tu3->changeRecord(26.7, 21.7); + EXPECT_EQ(tu3->count(), tu3Size); + EXPECT_EQ(tu3->getRecord(26.7), 21.7f); + tu3->changeRecord(26.7, 54.33); + EXPECT_EQ(tu3->count(), tu3Size); + EXPECT_EQ(tu3->getRecord(26.7), 22.16f); + EXPECT_THROW(tu3->changeRecord(26.7, 54.33), LMDBAL::Exist); + + LMDBAL::SizeType tu4Size = tu4->count(); + EXPECT_THROW(tu4->changeRecord(852, 6795.349), LMDBAL::NotFound); + EXPECT_EQ(tu4->count(), tu4Size); + tu4->addRecord(852, 6795.349); + EXPECT_EQ(tu4->count(), tu4Size += 1); + EXPECT_EQ(tu4->getRecord(852), 6795.349); + tu4->changeRecord(852, 6795.349); //should just do nothing usefull, but work normally + EXPECT_EQ(tu4->getRecord(852), 6795.349); + tu4->changeRecord(852, 13.54); + EXPECT_EQ(tu4->count(), tu4Size); + EXPECT_EQ(tu4->getRecord(852), 13.54); + tu4->addRecord(852, 213.85); + EXPECT_EQ(tu4->count(), tu4Size += 1); + EXPECT_EQ(tu4->getRecord(852), 13.54); + tu4->changeRecord(852, 236.21); + EXPECT_EQ(tu4->count(), tu4Size); + EXPECT_EQ(tu4->getRecord(852), 236.21); + tu4->changeRecord(852, 46324.1135); + EXPECT_EQ(tu4->count(), tu4Size); + EXPECT_EQ(tu4->getRecord(852), 213.85); + EXPECT_THROW(tu4->changeRecord(852, 46324.1135), LMDBAL::Exist); +}