diff --git a/CHANGELOG.md b/CHANGELOG.md index 8329234..d596471 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,13 @@ # Changelog +## LMDBAL 0.5.2 (October 21, 2023) +### Improvements +- RAII cursors + ## LMDBAL 0.5.1 (October 21, 2023) ### Improvements - RAII transactions -- reduced overhead for private transaction finctions +- reduced overhead for private transaction functions ### Bug fixes - bug fix with cache fallthough diff --git a/CMakeLists.txt b/CMakeLists.txt index 6825b0e..3231cf3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.16) project(LMDBAL - VERSION 0.5.1 + VERSION 0.5.2 DESCRIPTION "LMDB (Lightning Memory-Mapped Database Manager) Abstraction Layer" LANGUAGES CXX ) diff --git a/packaging/Archlinux/PKGBUILD b/packaging/Archlinux/PKGBUILD index 4c0362b..0232f4e 100644 --- a/packaging/Archlinux/PKGBUILD +++ b/packaging/Archlinux/PKGBUILD @@ -1,6 +1,6 @@ # Maintainer: Yury Gubich pkgname=lmdbal -pkgver=0.5.1 +pkgver=0.5.2 pkgrel=1 pkgdesc="LMDB Abstraction Layer, qt5 version" arch=('i686' 'x86_64') diff --git a/src/cursor.h b/src/cursor.h index 4f0a355..2a5114b 100644 --- a/src/cursor.h +++ b/src/cursor.h @@ -38,37 +38,49 @@ private: openedPrivate /**< - opened with private transaction, only current storage will be notified when cursor is closed*/ }; +public: + Cursor(); Cursor(Storage* parent); + Cursor(const Cursor& other) = delete; + Cursor(Cursor&& other); ~Cursor(); -public: - void open() const; - void open(const Transaction& transaction) const; - void renew() const; - void renew(const Transaction& transaction) const; - void close() const; + Cursor& operator = (const Cursor& other) = delete; + Cursor& operator = (Cursor&& other); + + void open(); + void open(const Transaction& transaction); + void renew(); + void renew(const Transaction& transaction); + void close(); bool opened() const; + bool empty() const; - std::pair first() const; - std::pair last() const; - std::pair next() const; - std::pair prev() const; + void drop(); + + std::pair first(); + std::pair last(); + std::pair next(); + std::pair prev(); std::pair current() const; + bool set(const K& target); - void first(K& key, V& value) const; - void last(K& key, V& value) const; - void next(K& key, V& value) const; - void prev(K& key, V& value) const; + void first(K& key, V& value); + void last(K& key, V& value); + void next(K& key, V& value); + void prev(K& key, V& value); void current(K& key, V& value) const; private: - void terminated() const; + void dropped(); + void terminated(); void operateCursorRead(K& key, V& value, MDB_cursor_op operation, const std::string& methodName, const std::string& operationName) const; private: Storage* storage; - mutable MDB_cursor* cursor; - mutable State state; + MDB_cursor* cursor; + State state; + uint32_t id; inline static const std::string openCursorMethodName = "Cursor::open"; /**<\brief member function name, just for exceptions*/ inline static const std::string closeCursorMethodName = "Cursor::close"; /**<\brief member function name, just for exceptions*/ diff --git a/src/cursor.hpp b/src/cursor.hpp index cbc293f..027c6fd 100644 --- a/src/cursor.hpp +++ b/src/cursor.hpp @@ -41,6 +41,8 @@ * You are not supposed to instantiate or destory instances of this class yourself! */ +static uint32_t idCounter = 0; + /** * \brief Creates a cursor * @@ -50,9 +52,74 @@ template LMDBAL::Cursor::Cursor(Storage* parent): storage(parent), cursor(nullptr), - state(closed) + state(closed), + id(++idCounter) +{ + storage->cursors[id] = this; +} + +/** + * \brief Creates an empty cursor. + * + * It's not usable, but can exist just to be a target of moves + */ +template +LMDBAL::Cursor::Cursor(): + storage(nullptr), + cursor(nullptr), + state(closed), + id(0) {} +/** + * \brief Moves from another cursor + */ +template +LMDBAL::Cursor::Cursor(Cursor&& other): + storage(other.storage), + cursor(other.cursor), + state(other.state), + id(other.id) +{ + if (id != 0) + storage->cursors[id] = this; + + other.cursor = nullptr; + other.storage = nullptr; + other.id = 0; + other.state = closed; +} + +/** + * \brief A private function that turns cursor into an empty one + * + * This function is called from LMDBAL::Storage, when it gets destroyed, but still has some valid. + * Those cursors will become empty, and can't be used anymore + */ +template +LMDBAL::Cursor& LMDBAL::Cursor::operator = (Cursor&& other) { + terminated(); + + if (id != 0) + storage->cursors.erase(id); + + storage = other.storage; + cursor = other.cursor; + state = other.state; + id = other.id; + + if (id != 0) { + other.cursor = nullptr; + other.storage = nullptr; + other.id = 0; + other.state = closed; + + storage->cursors[id] = this; + } + + return *this; +} + /** * \brief Destroys a cursor * @@ -61,13 +128,58 @@ LMDBAL::Cursor::Cursor(Storage* parent): template LMDBAL::Cursor::~Cursor () { close(); + + if (id != 0) + storage->cursors.erase(id); +} + +/** + * \brief Turns cursor into an empty one, releasing resources + * + * This function is called from LMDBAL::Storage, when it gets destroyed, but still has some valid. + * Those cursors will become empty, and can't be used anymore + */ +template +void LMDBAL::Cursor::drop () { + close(); + + if (id != 0) + storage->cursors.erase(id); + + cursor = nullptr; + storage = nullptr; + id = 0; +} + +/** + * \brief A private method that turns cursor into an empty one + * + * This function is called from LMDBAL::Storage, when it gets destroyed, but still has some valid. + * Those cursors will become empty, and can't be used anymore + */ +template +void LMDBAL::Cursor::dropped () { + terminated(); + cursor = nullptr; + storage = nullptr; + id = 0; +} + +/** + * \brief Returns true if the cursor is empty + * + * Empty cursors can't be used, they can be only targets of move operations + */ +template +bool LMDBAL::Cursor::empty () const { + return id == 0; } /** * \brief A private function the storage owning this cursor will call to inform this cursor that the thansaction needs to be aborted */ template -void LMDBAL::Cursor::terminated () const { +void LMDBAL::Cursor::terminated () { close(); //for now it's the same, but if I ever going to make writable cursor - here is where it's gonna be different } @@ -80,11 +192,15 @@ void LMDBAL::Cursor::terminated () const { * This function should be called when the LMDBAL::Storage is already opened and before any query with this cursor! * It will do nothing to a cursor that was already opened (no matter what way). * - * \exception LMDBAL::Closed thrown if you try to open the cursor on a closed database - * \exception LMDBAL::Unknown thrown if there was a problem opening the cursor by the lmdb, or to begin a transaction + * \exception LMDBAL::Closed thrown if you try to open the cursor on a closed database + * \exception LMDBAL::Unknown thrown if there was a problem opening the cursor by the lmdb, or to begin a transaction + * \exception LMDBAL::CursorEmpty thrown if the cursor was empty */ template -void LMDBAL::Cursor::open () const { +void LMDBAL::Cursor::open () { + if (empty()) + throw CursorEmpty(openCursorMethodName); + storage->ensureOpened(openCursorMethodName); switch (state) { case closed: { @@ -115,9 +231,13 @@ void LMDBAL::Cursor::open () const { * \exception LMDBAL::Closed thrown if you try to open the cursor on a closed database * \exception LMDBAL::Unknown thrown if there was a problem opening the cursor by the lmdb * \exception LMDBAL::TransactionTerminated thrown if the passed transaction not active, any action with it's inner ID is an error + * \exception LMDBAL::CursorEmpty thrown if the cursor was empty */ template -void LMDBAL::Cursor::open (const Transaction& transaction) const { +void LMDBAL::Cursor::open (const Transaction& transaction) { + if (empty()) + throw CursorEmpty(openCursorMethodName); + storage->ensureOpened(openCursorMethodName); TransactionID txn = storage->extractTransactionId(transaction, openCursorMethodName); switch (state) { @@ -146,11 +266,15 @@ void LMDBAL::Cursor::open (const Transaction& transaction) const { * * This function does nothing if the cursor is closed * - * \exception LMDBAL::Closed thrown if you try to renew the cursor on a closed database - * \exception LMDBAL::Unknown thrown if there was a problem beginning new transaction or if there was a problem renewing the cursor by lmdb + * \exception LMDBAL::Closed thrown if you try to renew the cursor on a closed database + * \exception LMDBAL::Unknown thrown if there was a problem beginning new transaction or if there was a problem renewing the cursor by lmdb + * \exception LMDBAL::CursorEmpty thrown if the cursor was empty */ template -void LMDBAL::Cursor::renew () const { +void LMDBAL::Cursor::renew () { + if (empty()) + throw CursorEmpty(openCursorMethodName); + storage->ensureOpened(renewCursorMethodName); switch (state) { case openedPrivate: { @@ -191,9 +315,13 @@ void LMDBAL::Cursor::renew () const { * \exception LMDBAL::Closed thrown if you try to renew the cursor on a closed database * \exception LMDBAL::Unknown thrown if there was a problem renewing the cursor by lmdb * \exception LMDBAL::TransactionTerminated thrown if the passed transaction not active, any action with it's inner ID is an error + * \exception LMDBAL::CursorEmpty thrown if the cursor was empty */ template -void LMDBAL::Cursor::renew (const Transaction& transaction) const { +void LMDBAL::Cursor::renew (const Transaction& transaction) { + if (empty()) + throw CursorEmpty(openCursorMethodName); + storage->ensureOpened(renewCursorMethodName); TransactionID txn = storage->extractTransactionId(transaction, renewCursorMethodName); switch (state) { @@ -226,7 +354,7 @@ void LMDBAL::Cursor::renew (const Transaction& transaction) const { * This function does nothing on a closed cursor. */ template -void LMDBAL::Cursor::close () const { +void LMDBAL::Cursor::close () { switch (state) { case openedPublic: { mdb_cursor_close(cursor); @@ -267,7 +395,7 @@ bool LMDBAL::Cursor::opened () const { * \exception LMDBAL::Unknown thrown if there was some unexpected problem with lmdb */ template -void LMDBAL::Cursor::first (K& key, V& value) const { +void LMDBAL::Cursor::first (K& key, V& value) { operateCursorRead(key, value, MDB_FIRST, firstMethodName, firstOperationName); } @@ -284,7 +412,7 @@ void LMDBAL::Cursor::first (K& key, V& value) const { * \exception LMDBAL::Unknown thrown if there was some unexpected problem with lmdb */ template -void LMDBAL::Cursor::last (K& key, V& value) const { +void LMDBAL::Cursor::last (K& key, V& value) { operateCursorRead(key, value, MDB_LAST, lastMethodName, lastOperationName); } @@ -307,7 +435,7 @@ void LMDBAL::Cursor::last (K& key, V& value) const { * \exception LMDBAL::Unknown thrown if there was some unexpected problem with lmdb */ template -void LMDBAL::Cursor::next (K& key, V& value) const { +void LMDBAL::Cursor::next (K& key, V& value) { operateCursorRead(key, value, MDB_NEXT, nextMethodName, nextOperationName); } @@ -330,7 +458,7 @@ void LMDBAL::Cursor::next (K& key, V& value) const { * \exception LMDBAL::Unknown thrown if there was some unexpected problem with lmdb */ template -void LMDBAL::Cursor::prev (K& key, V& value) const { +void LMDBAL::Cursor::prev (K& key, V& value) { operateCursorRead(key, value, MDB_PREV, prevMethodName, prevOperationName); } @@ -349,7 +477,7 @@ void LMDBAL::Cursor::prev (K& key, V& value) const { * \exception LMDBAL::Unknown thrown if there was some unexpected problem with lmdb */ template -void LMDBAL::Cursor::current (K& key, V& value) const { +void LMDBAL::Cursor::current (K& key, V& value) const { operateCursorRead(key, value, MDB_GET_CURRENT, currentMethodName, currentOperationName); } @@ -365,7 +493,7 @@ void LMDBAL::Cursor::current (K& key, V& value) const { * \exception LMDBAL::Unknown thrown if there was some unexpected problem with lmdb */ template -std::pair LMDBAL::Cursor::first () const { +std::pair LMDBAL::Cursor::first () { std::pair result; operateCursorRead(result.first, result.second, MDB_FIRST, firstMethodName, firstOperationName); return result; @@ -383,7 +511,7 @@ std::pair LMDBAL::Cursor::first () const { * \exception LMDBAL::Unknown thrown if there was some unexpected problem with lmdb */ template -std::pair LMDBAL::Cursor::last () const { +std::pair LMDBAL::Cursor::last () { std::pair result; operateCursorRead(result.first, result.second, MDB_LAST, lastMethodName, lastOperationName); return result; @@ -407,7 +535,7 @@ std::pair LMDBAL::Cursor::last () const { * \exception LMDBAL::Unknown thrown if there was some unexpected problem with lmdb */ template -std::pair LMDBAL::Cursor::next () const { +std::pair LMDBAL::Cursor::next () { std::pair result; operateCursorRead(result.first, result.second, MDB_NEXT, nextMethodName, nextOperationName); return result; @@ -431,7 +559,7 @@ std::pair LMDBAL::Cursor::next () const { * \exception LMDBAL::Unknown thrown if there was some unexpected problem with lmdb */ template -std::pair LMDBAL::Cursor::prev () const { +std::pair LMDBAL::Cursor::prev () { std::pair result; operateCursorRead(result.first, result.second, MDB_PREV, prevMethodName, prevOperationName); return result; diff --git a/src/exceptions.cpp b/src/exceptions.cpp index 757e96e..c0d618a 100644 --- a/src/exceptions.cpp +++ b/src/exceptions.cpp @@ -72,6 +72,14 @@ std::string LMDBAL::CursorNotReady::getMessage() const { return msg; } +LMDBAL::CursorEmpty::CursorEmpty(const std::string & operation): + Exception(), + operation(operation) {} + +std::string LMDBAL::CursorEmpty::getMessage() const { + return "An attempt to perform an operation \"" + operation + "\" on an empty cursor"; +} + LMDBAL::Opened::Opened(const std::string& p_dbName, const std::string& p_action): Exception(), dbName(p_dbName), diff --git a/src/exceptions.h b/src/exceptions.h index 48642c1..58bbea4 100644 --- a/src/exceptions.h +++ b/src/exceptions.h @@ -97,6 +97,23 @@ private: std::string tableName; }; +/** + * \brief Thrown if an empty cursor was somehow operated + */ +class CursorEmpty : public Exception { +public: + /** + * \brief Creates exception + * + * \param operation - text name of the method that was called on an empty cursor + */ + CursorEmpty(const std::string& operation); + + std::string getMessage() const; +private: + std::string operation; +}; + /** * \brief Thrown if something in the database was called on opened state and it is not supported */ diff --git a/src/storage.h b/src/storage.h index 1f9db9e..de38568 100644 --- a/src/storage.h +++ b/src/storage.h @@ -29,6 +29,8 @@ class BaseTest; class DuplicatesTest; +class CacheCursorTest; +class StorageCursorTest; namespace LMDBAL { @@ -113,6 +115,8 @@ template class Storage : public iStorage { friend class ::BaseTest; friend class ::DuplicatesTest; + friend class ::CacheCursorTest; + friend class ::StorageCursorTest; friend class Base; friend class Cursor; protected: @@ -160,13 +164,13 @@ public: virtual uint32_t addRecords(const std::map& data, bool overwrite = false); virtual uint32_t addRecords(const std::map& data, const WriteTransaction& txn, bool overwrite = false); - Cursor* createCursor(); - void destroyCursor(Cursor* cursor); + Cursor createCursor(); + void destroyCursor(Cursor& cursor); protected: mutable Serializer keySerializer; /**<\brief internal object that would serialize and deserialize keys*/ mutable Serializer valueSerializer; /**<\brief internal object that would serialize and deserialize values*/ - std::set*> cursors; /**<\brief a set of cursors that has been created under this storage*/ + std::map*> cursors; /**<\brief a set of cursors that has been created under this storage*/ int open(MDB_txn* transaction) override; void close() override; diff --git a/src/storage.hpp b/src/storage.hpp index 4662fc3..f533217 100644 --- a/src/storage.hpp +++ b/src/storage.hpp @@ -58,8 +58,8 @@ LMDBAL::Storage::Storage(Base* parent, const std::string& name, bool dupli */ template LMDBAL::Storage::~Storage() { - for (Cursor* cursor : cursors) - delete cursor; + for (const std::pair*>& pair : cursors) + pair.second->dropped(); } /** @@ -1013,8 +1013,8 @@ int LMDBAL::Storage::open(MDB_txn* transaction) { */ template void LMDBAL::Storage::close() { - for (Cursor* cursor : cursors) - cursor->terminated(); + for (const std::pair*>& pair : cursors) + pair.second->terminated(); iStorage::close(); } @@ -1025,11 +1025,8 @@ void LMDBAL::Storage::close() { * \returns LMDBAL::Cursor for this storage and returs you a pointer to a created cursor */ template -LMDBAL::Cursor* LMDBAL::Storage::createCursor() { - Cursor* cursor = new Cursor(this); - cursors.insert(cursor); - - return cursor; +LMDBAL::Cursor LMDBAL::Storage::createCursor() { + return Cursor(this); } /** @@ -1056,25 +1053,6 @@ uint32_t LMDBAL::Storage::flags() const { return result; } -/** - * \brief Destroys cursor - * - * This a normal way to discard a cursor you don't need anymore - * - * \param[in] cursor a pointer to a cursor you want to destroy - * - * \exception LMDBAL::Unknown thrown if you try to destroy something that this storage didn't create - */ -template -void LMDBAL::Storage::destroyCursor(Cursor* cursor) { - typename std::set*>::const_iterator itr = cursors.find(cursor); - if (itr == cursors.end()) - throwUnknown("An attempt to destroy a cursor the storage doesn't own"); - - cursors.erase(itr); - delete cursor; -} - /** * \brief A private virtual method that cursor calls when he reads a record, does nothing here but populates the LMDBAL::Cache * diff --git a/test/cachecursor.cpp b/test/cachecursor.cpp index dc3f3e5..c988bc1 100644 --- a/test/cachecursor.cpp +++ b/test/cachecursor.cpp @@ -29,13 +29,10 @@ protected: db->removeDirectory(); delete db; db = nullptr; - cursor = nullptr; - emptyCursor = nullptr; } static LMDBAL::Base* db; - static LMDBAL::Cursor* cursor; - static LMDBAL::Cursor* emptyCursor; + static LMDBAL::Cursor cursor; static LMDBAL::Transaction transaction; LMDBAL::Cache* cache; @@ -43,8 +40,7 @@ protected: }; LMDBAL::Base* CacheCursorTest::db = nullptr; -LMDBAL::Cursor* CacheCursorTest::cursor = nullptr; -LMDBAL::Cursor* CacheCursorTest::emptyCursor = nullptr; +LMDBAL::Cursor CacheCursorTest::cursor; LMDBAL::Transaction CacheCursorTest::transaction; static const std::map data({ @@ -67,21 +63,20 @@ TEST_F(CacheCursorTest, PopulatingTheTable) { TEST_F(CacheCursorTest, Creation) { cursor = cache->createCursor(); - emptyCursor = emptyCache->createCursor(); - EXPECT_THROW(cursor->first(), LMDBAL::CursorNotReady); - EXPECT_THROW(cursor->last(), LMDBAL::CursorNotReady); - EXPECT_THROW(cursor->next(), LMDBAL::CursorNotReady); - EXPECT_THROW(cursor->prev(), LMDBAL::CursorNotReady); - EXPECT_THROW(cursor->current(), LMDBAL::CursorNotReady); + EXPECT_THROW(cursor.first(), LMDBAL::CursorNotReady); + EXPECT_THROW(cursor.last(), LMDBAL::CursorNotReady); + EXPECT_THROW(cursor.next(), LMDBAL::CursorNotReady); + EXPECT_THROW(cursor.prev(), LMDBAL::CursorNotReady); + EXPECT_THROW(cursor.current(), LMDBAL::CursorNotReady); - cursor->open(); + cursor.open(); } TEST_F(CacheCursorTest, FirstPrivate) { EXPECT_EQ(cache->count(), data.size()); - std::pair element = cursor->first(); + std::pair element = cursor.first(); std::map::const_iterator reference = data.begin(); EXPECT_EQ(element.first, reference->first); @@ -94,16 +89,16 @@ TEST_F(CacheCursorTest, NextPrivate) { reference++; for (; reference != data.end(); ++reference) { - std::pair element = cursor->next(); + std::pair element = cursor.next(); EXPECT_EQ(element.first, reference->first); EXPECT_EQ(element.second, reference->second); EXPECT_EQ(cache->count(), data.size()); } - EXPECT_THROW(cursor->next(), LMDBAL::NotFound); + EXPECT_THROW(cursor.next(), LMDBAL::NotFound); EXPECT_EQ(cache->count(), data.size()); - std::pair element = cursor->first(); + std::pair element = cursor.first(); reference = data.begin(); EXPECT_EQ(element.first, reference->first); @@ -114,7 +109,7 @@ TEST_F(CacheCursorTest, NextPrivate) { TEST_F(CacheCursorTest, LastPrivate) { EXPECT_EQ(cache->count(), data.size()); - std::pair element = cursor->last(); + std::pair element = cursor.last(); std::map::const_reverse_iterator reference = data.rbegin(); EXPECT_EQ(element.first, reference->first); @@ -127,16 +122,16 @@ TEST_F(CacheCursorTest, PrevPrivate) { reference++; for (; reference != data.rend(); ++reference) { - std::pair element = cursor->prev(); + std::pair element = cursor.prev(); EXPECT_EQ(element.first, reference->first); EXPECT_EQ(element.second, reference->second); EXPECT_EQ(cache->count(), data.size()); } - EXPECT_THROW(cursor->prev(), LMDBAL::NotFound); + EXPECT_THROW(cursor.prev(), LMDBAL::NotFound); EXPECT_EQ(cache->count(), data.size()); - std::pair element = cursor->last(); + std::pair element = cursor.last(); reference = data.rbegin(); EXPECT_EQ(element.first, reference->first); @@ -145,29 +140,29 @@ TEST_F(CacheCursorTest, PrevPrivate) { } TEST_F(CacheCursorTest, CurrentPrivate) { - std::pair element = cursor->first(); + std::pair element = cursor.first(); std::map::const_iterator reference = data.begin(); EXPECT_EQ(element.first, reference->first); EXPECT_EQ(element.second, reference->second); - element = cursor->current(); + element = cursor.current(); EXPECT_EQ(cache->count(), data.size()); EXPECT_EQ(element.first, reference->first); EXPECT_EQ(element.second, reference->second); - cursor->next(); - element = cursor->current(); + cursor.next(); + element = cursor.current(); ++reference; EXPECT_EQ(element.first, reference->first); EXPECT_EQ(element.second, reference->second); - cursor->next(); - cursor->next(); - cursor->prev(); - element = cursor->current(); + cursor.next(); + cursor.next(); + cursor.prev(); + element = cursor.current(); ++reference; ++reference; --reference; @@ -178,16 +173,15 @@ TEST_F(CacheCursorTest, CurrentPrivate) { } TEST_F(CacheCursorTest, Destruction) { - cursor->close(); + cursor.close(); - EXPECT_THROW(cursor->first(), LMDBAL::CursorNotReady); - EXPECT_THROW(cursor->last(), LMDBAL::CursorNotReady); - EXPECT_THROW(cursor->next(), LMDBAL::CursorNotReady); - EXPECT_THROW(cursor->prev(), LMDBAL::CursorNotReady); - EXPECT_THROW(cursor->current(), LMDBAL::CursorNotReady); + EXPECT_THROW(cursor.first(), LMDBAL::CursorNotReady); + EXPECT_THROW(cursor.last(), LMDBAL::CursorNotReady); + EXPECT_THROW(cursor.next(), LMDBAL::CursorNotReady); + EXPECT_THROW(cursor.prev(), LMDBAL::CursorNotReady); + EXPECT_THROW(cursor.current(), LMDBAL::CursorNotReady); - EXPECT_THROW(emptyCache->destroyCursor(cursor), LMDBAL::Unknown); - cache->destroyCursor(cursor); + cursor = LMDBAL::Cursor(); cursor = cache->createCursor(); } @@ -195,8 +189,8 @@ TEST_F(CacheCursorTest, Destruction) { TEST_F(CacheCursorTest, FirstPublic) { transaction = db->beginTransaction(); - cursor->open(transaction); - std::pair element = cursor->first(); + cursor.open(transaction); + std::pair element = cursor.first(); std::map::const_iterator reference = data.begin(); EXPECT_EQ(element.first, reference->first); @@ -209,16 +203,16 @@ TEST_F(CacheCursorTest, NextPublic) { reference++; for (; reference != data.end(); ++reference) { - std::pair element = cursor->next(); + std::pair element = cursor.next(); EXPECT_EQ(element.first, reference->first); EXPECT_EQ(element.second, reference->second); EXPECT_EQ(cache->count(), data.size()); } - EXPECT_THROW(cursor->next(), LMDBAL::NotFound); + EXPECT_THROW(cursor.next(), LMDBAL::NotFound); EXPECT_EQ(cache->count(), data.size()); - std::pair element = cursor->first(); + std::pair element = cursor.first(); reference = data.begin(); EXPECT_EQ(element.first, reference->first); @@ -226,7 +220,7 @@ TEST_F(CacheCursorTest, NextPublic) { } TEST_F(CacheCursorTest, LastPublic) { - std::pair element = cursor->last(); + std::pair element = cursor.last(); std::map::const_reverse_iterator reference = data.rbegin(); EXPECT_EQ(element.first, reference->first); @@ -239,15 +233,15 @@ TEST_F(CacheCursorTest, PrevPublic) { reference++; for (; reference != data.rend(); ++reference) { - std::pair element = cursor->prev(); + std::pair element = cursor.prev(); EXPECT_EQ(element.first, reference->first); EXPECT_EQ(element.second, reference->second); EXPECT_EQ(cache->count(), data.size()); } - EXPECT_THROW(cursor->prev(), LMDBAL::NotFound); + EXPECT_THROW(cursor.prev(), LMDBAL::NotFound); - std::pair element = cursor->last(); + std::pair element = cursor.last(); reference = data.rbegin(); EXPECT_EQ(element.first, reference->first); @@ -256,29 +250,29 @@ TEST_F(CacheCursorTest, PrevPublic) { } TEST_F(CacheCursorTest, CurrentPublic) { - std::pair element = cursor->first(); + std::pair element = cursor.first(); std::map::const_iterator reference = data.begin(); EXPECT_EQ(element.first, reference->first); EXPECT_EQ(element.second, reference->second); - element = cursor->current(); + element = cursor.current(); EXPECT_EQ(cache->count(), data.size()); EXPECT_EQ(element.first, reference->first); EXPECT_EQ(element.second, reference->second); - cursor->next(); - element = cursor->current(); + cursor.next(); + element = cursor.current(); ++reference; EXPECT_EQ(element.first, reference->first); EXPECT_EQ(element.second, reference->second); - cursor->next(); - cursor->next(); - cursor->prev(); - element = cursor->current(); + cursor.next(); + cursor.next(); + cursor.prev(); + element = cursor.current(); ++reference; ++reference; --reference; @@ -290,38 +284,39 @@ TEST_F(CacheCursorTest, CurrentPublic) { TEST_F(CacheCursorTest, CornerCases) { transaction.terminate(); - EXPECT_THROW(cursor->current(), LMDBAL::Unknown); - cursor->close(); + EXPECT_THROW(cursor.current(), LMDBAL::Unknown); + cursor.close(); - emptyCursor->open(); - EXPECT_THROW(emptyCursor->first(), LMDBAL::NotFound); - EXPECT_THROW(emptyCursor->last(), LMDBAL::NotFound); - EXPECT_THROW(emptyCursor->next(), LMDBAL::NotFound); - EXPECT_THROW(emptyCursor->prev(), LMDBAL::NotFound); - EXPECT_THROW(emptyCursor->current(), LMDBAL::Unknown); - emptyCursor->close(); + LMDBAL::Cursor emptyCursor = emptyCache->createCursor(); + emptyCursor.open(); + EXPECT_THROW(emptyCursor.first(), LMDBAL::NotFound); + EXPECT_THROW(emptyCursor.last(), LMDBAL::NotFound); + EXPECT_THROW(emptyCursor.next(), LMDBAL::NotFound); + EXPECT_THROW(emptyCursor.prev(), LMDBAL::NotFound); + EXPECT_THROW(emptyCursor.current(), LMDBAL::Unknown); + emptyCursor.close(); - cursor->open(); - EXPECT_THROW(cursor->current(), LMDBAL::Unknown); //yeah, nice thing to write in the doc + cursor.open(); + EXPECT_THROW(cursor.current(), LMDBAL::Unknown); //yeah, nice thing to write in the doc std::map::const_reverse_iterator breference = data.rbegin(); - std::pair element(cursor->prev()); + std::pair element(cursor.prev()); EXPECT_EQ(element.first, breference->first); //nice thing to write in the doc, again! EXPECT_EQ(element.second, breference->second); - element = cursor->current(); + element = cursor.current(); EXPECT_EQ(element.first, breference->first); EXPECT_EQ(element.second, breference->second); - EXPECT_THROW(cursor->next(), LMDBAL::NotFound); - cursor->close(); + EXPECT_THROW(cursor.next(), LMDBAL::NotFound); + cursor.close(); - cursor->open(); - element = cursor->next(); + cursor.open(); + element = cursor.next(); std::map::const_iterator reference = data.begin(); EXPECT_EQ(element.first, reference->first); EXPECT_EQ(element.second, reference->second); - element = cursor->current(); + element = cursor.current(); EXPECT_EQ(element.first, reference->first); EXPECT_EQ(element.second, reference->second); - EXPECT_THROW(cursor->prev(), LMDBAL::NotFound); + EXPECT_THROW(cursor.prev(), LMDBAL::NotFound); } diff --git a/test/duplicates.cpp b/test/duplicates.cpp index db16dce..4fbe12a 100644 --- a/test/duplicates.cpp +++ b/test/duplicates.cpp @@ -337,15 +337,15 @@ TEST_F(DuplicatesTest, GettingAllRecords) { std::map m1; std::set k1; - LMDBAL::Cursor* c1 = tu1->createCursor(); + LMDBAL::Cursor c1 = tu1->createCursor(); tu1->readAll(m1, txn); - c1->open(txn); + c1.open(txn); cycle = false; iterations = 0; do { try { - std::pair pair = c1->next(); + std::pair pair = c1.next(); cycle = true; std::pair::const_iterator, bool> probe = k1.insert(pair.first); if (probe.second) { @@ -359,25 +359,25 @@ TEST_F(DuplicatesTest, GettingAllRecords) { 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); + c1.drop(); std::map m2; std::set k2; - LMDBAL::Cursor* c2 = tu2->createCursor(); + LMDBAL::Cursor c2 = tu2->createCursor(); tu2->readAll(m2, txn); - c2->open(txn); + c2.open(txn); cycle = false; iterations = 0; do { try { - std::pair pair = c2->next(); + std::pair pair = c2.next(); cycle = true; std::pair::const_iterator, bool> probe = k2.insert(pair.first); if (probe.second) { @@ -391,25 +391,25 @@ TEST_F(DuplicatesTest, GettingAllRecords) { 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); + c2.drop(); std::map m3; std::set k3; - LMDBAL::Cursor* c3 = tu3->createCursor(); + LMDBAL::Cursor c3 = tu3->createCursor(); tu3->readAll(m3, txn); - c3->open(txn); + c3.open(txn); cycle = false; iterations = 0; do { try { - std::pair pair = c3->next(); + std::pair pair = c3.next(); cycle = true; std::pair::const_iterator, bool> probe = k3.insert(pair.first); if (probe.second) { @@ -423,25 +423,25 @@ TEST_F(DuplicatesTest, GettingAllRecords) { 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); + c3.drop(); std::map m4; std::set k4; - LMDBAL::Cursor* c4 = tu4->createCursor(); + LMDBAL::Cursor c4 = tu4->createCursor(); tu4->readAll(m4, txn); - c4->open(txn); + c4.open(txn); cycle = false; iterations = 0; do { try { - std::pair pair = c4->next(); + std::pair pair = c4.next(); cycle = true; std::pair::const_iterator, bool> probe = k4.insert(pair.first); if (probe.second) { @@ -455,7 +455,7 @@ TEST_F(DuplicatesTest, GettingAllRecords) { cycle = false; } } while (cycle); - tu4->destroyCursor(c4); + c4.drop(); EXPECT_EQ(iterations, tu4->count(txn)); EXPECT_EQ(k4.size(), m4.size()); diff --git a/test/storagecursor.cpp b/test/storagecursor.cpp index a75dba0..b8de67c 100644 --- a/test/storagecursor.cpp +++ b/test/storagecursor.cpp @@ -28,13 +28,10 @@ protected: db->removeDirectory(); delete db; db = nullptr; - cursor = nullptr; - emptyCursor = nullptr; } static LMDBAL::Base* db; - static LMDBAL::Cursor* cursor; - static LMDBAL::Cursor* emptyCursor; + static LMDBAL::Cursor cursor; static LMDBAL::Transaction transaction; LMDBAL::Storage* table; @@ -42,8 +39,7 @@ protected: }; LMDBAL::Base* StorageCursorTest::db = nullptr; -LMDBAL::Cursor* StorageCursorTest::cursor = nullptr; -LMDBAL::Cursor* StorageCursorTest::emptyCursor = nullptr; +LMDBAL::Cursor StorageCursorTest::cursor; LMDBAL::Transaction StorageCursorTest::transaction = LMDBAL::Transaction(); static const std::map data({ @@ -66,19 +62,18 @@ TEST_F(StorageCursorTest, PopulatingTheTable) { TEST_F(StorageCursorTest, Creation) { cursor = table->createCursor(); - emptyCursor = emptyTable->createCursor(); - EXPECT_THROW(cursor->first(), LMDBAL::CursorNotReady); - EXPECT_THROW(cursor->last(), LMDBAL::CursorNotReady); - EXPECT_THROW(cursor->next(), LMDBAL::CursorNotReady); - EXPECT_THROW(cursor->prev(), LMDBAL::CursorNotReady); - EXPECT_THROW(cursor->current(), LMDBAL::CursorNotReady); + EXPECT_THROW(cursor.first(), LMDBAL::CursorNotReady); + EXPECT_THROW(cursor.last(), LMDBAL::CursorNotReady); + EXPECT_THROW(cursor.next(), LMDBAL::CursorNotReady); + EXPECT_THROW(cursor.prev(), LMDBAL::CursorNotReady); + EXPECT_THROW(cursor.current(), LMDBAL::CursorNotReady); - cursor->open(); + cursor.open(); } TEST_F(StorageCursorTest, FirstPrivate) { - std::pair element = cursor->first(); + std::pair element = cursor.first(); std::map::const_iterator reference = data.begin(); EXPECT_EQ(element.first, reference->first); @@ -90,14 +85,14 @@ TEST_F(StorageCursorTest, NextPrivate) { reference++; for (; reference != data.end(); ++reference) { - std::pair element = cursor->next(); + std::pair element = cursor.next(); EXPECT_EQ(element.first, reference->first); EXPECT_EQ(element.second, reference->second); } - EXPECT_THROW(cursor->next(), LMDBAL::NotFound); + EXPECT_THROW(cursor.next(), LMDBAL::NotFound); - std::pair element = cursor->first(); + std::pair element = cursor.first(); reference = data.begin(); EXPECT_EQ(element.first, reference->first); @@ -105,7 +100,7 @@ TEST_F(StorageCursorTest, NextPrivate) { } TEST_F(StorageCursorTest, LastPrivate) { - std::pair element = cursor->last(); + std::pair element = cursor.last(); std::map::const_reverse_iterator reference = data.rbegin(); EXPECT_EQ(element.first, reference->first); @@ -117,14 +112,14 @@ TEST_F(StorageCursorTest, PrevPrivate) { reference++; for (; reference != data.rend(); ++reference) { - std::pair element = cursor->prev(); + std::pair element = cursor.prev(); EXPECT_EQ(element.first, reference->first); EXPECT_EQ(element.second, reference->second); } - EXPECT_THROW(cursor->prev(), LMDBAL::NotFound); + EXPECT_THROW(cursor.prev(), LMDBAL::NotFound); - std::pair element = cursor->last(); + std::pair element = cursor.last(); reference = data.rbegin(); EXPECT_EQ(element.first, reference->first); @@ -132,28 +127,28 @@ TEST_F(StorageCursorTest, PrevPrivate) { } TEST_F(StorageCursorTest, CurrentPrivate) { - std::pair element = cursor->first(); + std::pair element = cursor.first(); std::map::const_iterator reference = data.begin(); EXPECT_EQ(element.first, reference->first); EXPECT_EQ(element.second, reference->second); - element = cursor->current(); + element = cursor.current(); EXPECT_EQ(element.first, reference->first); EXPECT_EQ(element.second, reference->second); - cursor->next(); - element = cursor->current(); + cursor.next(); + element = cursor.current(); ++reference; EXPECT_EQ(element.first, reference->first); EXPECT_EQ(element.second, reference->second); - cursor->next(); - cursor->next(); - cursor->prev(); - element = cursor->current(); + cursor.next(); + cursor.next(); + cursor.prev(); + element = cursor.current(); ++reference; ++reference; --reference; @@ -163,16 +158,15 @@ TEST_F(StorageCursorTest, CurrentPrivate) { } TEST_F(StorageCursorTest, Destruction) { - cursor->close(); + cursor.close(); - EXPECT_THROW(cursor->first(), LMDBAL::CursorNotReady); - EXPECT_THROW(cursor->last(), LMDBAL::CursorNotReady); - EXPECT_THROW(cursor->next(), LMDBAL::CursorNotReady); - EXPECT_THROW(cursor->prev(), LMDBAL::CursorNotReady); - EXPECT_THROW(cursor->current(), LMDBAL::CursorNotReady); + EXPECT_THROW(cursor.first(), LMDBAL::CursorNotReady); + EXPECT_THROW(cursor.last(), LMDBAL::CursorNotReady); + EXPECT_THROW(cursor.next(), LMDBAL::CursorNotReady); + EXPECT_THROW(cursor.prev(), LMDBAL::CursorNotReady); + EXPECT_THROW(cursor.current(), LMDBAL::CursorNotReady); - EXPECT_THROW(emptyTable->destroyCursor(cursor), LMDBAL::Unknown); - table->destroyCursor(cursor); + cursor = LMDBAL::Cursor(); cursor = table->createCursor(); } @@ -180,8 +174,8 @@ TEST_F(StorageCursorTest, Destruction) { TEST_F(StorageCursorTest, FirstPublic) { transaction = db->beginReadOnlyTransaction(); - cursor->open(transaction); - std::pair element = cursor->first(); + cursor.open(transaction); + std::pair element = cursor.first(); std::map::const_iterator reference = data.begin(); EXPECT_EQ(element.first, reference->first); @@ -193,14 +187,14 @@ TEST_F(StorageCursorTest, NextPublic) { reference++; for (; reference != data.end(); ++reference) { - std::pair element = cursor->next(); + std::pair element = cursor.next(); EXPECT_EQ(element.first, reference->first); EXPECT_EQ(element.second, reference->second); } - EXPECT_THROW(cursor->next(), LMDBAL::NotFound); + EXPECT_THROW(cursor.next(), LMDBAL::NotFound); - std::pair element = cursor->first(); + std::pair element = cursor.first(); reference = data.begin(); EXPECT_EQ(element.first, reference->first); @@ -208,7 +202,7 @@ TEST_F(StorageCursorTest, NextPublic) { } TEST_F(StorageCursorTest, LastPublic) { - std::pair element = cursor->last(); + std::pair element = cursor.last(); std::map::const_reverse_iterator reference = data.rbegin(); EXPECT_EQ(element.first, reference->first); @@ -220,14 +214,14 @@ TEST_F(StorageCursorTest, PrevPublic) { reference++; for (; reference != data.rend(); ++reference) { - std::pair element = cursor->prev(); + std::pair element = cursor.prev(); EXPECT_EQ(element.first, reference->first); EXPECT_EQ(element.second, reference->second); } - EXPECT_THROW(cursor->prev(), LMDBAL::NotFound); + EXPECT_THROW(cursor.prev(), LMDBAL::NotFound); - std::pair element = cursor->last(); + std::pair element = cursor.last(); reference = data.rbegin(); EXPECT_EQ(element.first, reference->first); @@ -235,28 +229,28 @@ TEST_F(StorageCursorTest, PrevPublic) { } TEST_F(StorageCursorTest, CurrentPublic) { - std::pair element = cursor->first(); + std::pair element = cursor.first(); std::map::const_iterator reference = data.begin(); EXPECT_EQ(element.first, reference->first); EXPECT_EQ(element.second, reference->second); - element = cursor->current(); + element = cursor.current(); EXPECT_EQ(element.first, reference->first); EXPECT_EQ(element.second, reference->second); - cursor->next(); - element = cursor->current(); + cursor.next(); + element = cursor.current(); ++reference; EXPECT_EQ(element.first, reference->first); EXPECT_EQ(element.second, reference->second); - cursor->next(); - cursor->next(); - cursor->prev(); - element = cursor->current(); + cursor.next(); + cursor.next(); + cursor.prev(); + element = cursor.current(); ++reference; ++reference; --reference; @@ -267,37 +261,38 @@ TEST_F(StorageCursorTest, CurrentPublic) { TEST_F(StorageCursorTest, CornerCases) { transaction.terminate(); - EXPECT_THROW(cursor->current(), LMDBAL::Unknown); - cursor->close(); + EXPECT_THROW(cursor.current(), LMDBAL::Unknown); + cursor.close(); - emptyCursor->open(); - EXPECT_THROW(emptyCursor->first(), LMDBAL::NotFound); - EXPECT_THROW(emptyCursor->last(), LMDBAL::NotFound); - EXPECT_THROW(emptyCursor->next(), LMDBAL::NotFound); - EXPECT_THROW(emptyCursor->prev(), LMDBAL::NotFound); - EXPECT_THROW(emptyCursor->current(), LMDBAL::Unknown); - emptyCursor->close(); + LMDBAL::Cursor emptyCursor = emptyTable->createCursor(); + emptyCursor.open(); + EXPECT_THROW(emptyCursor.first(), LMDBAL::NotFound); + EXPECT_THROW(emptyCursor.last(), LMDBAL::NotFound); + EXPECT_THROW(emptyCursor.next(), LMDBAL::NotFound); + EXPECT_THROW(emptyCursor.prev(), LMDBAL::NotFound); + EXPECT_THROW(emptyCursor.current(), LMDBAL::Unknown); + emptyCursor.close(); - cursor->open(); - EXPECT_THROW(cursor->current(), LMDBAL::Unknown); //yeah, nice thing to write in the doc + cursor.open(); + EXPECT_THROW(cursor.current(), LMDBAL::Unknown); //yeah, nice thing to write in the doc std::map::const_reverse_iterator breference = data.rbegin(); - std::pair element(cursor->prev()); + std::pair element(cursor.prev()); EXPECT_EQ(element.first, breference->first); //nice thing to write in the doc, again! EXPECT_EQ(element.second, breference->second); - element = cursor->current(); + element = cursor.current(); EXPECT_EQ(element.first, breference->first); EXPECT_EQ(element.second, breference->second); - EXPECT_THROW(cursor->next(), LMDBAL::NotFound); - cursor->close(); + EXPECT_THROW(cursor.next(), LMDBAL::NotFound); + cursor.close(); - cursor->open(); - element = cursor->next(); + cursor.open(); + element = cursor.next(); std::map::const_iterator reference = data.begin(); EXPECT_EQ(element.first, reference->first); EXPECT_EQ(element.second, reference->second); - element = cursor->current(); + element = cursor.current(); EXPECT_EQ(element.first, reference->first); EXPECT_EQ(element.second, reference->second); - EXPECT_THROW(cursor->prev(), LMDBAL::NotFound); + EXPECT_THROW(cursor.prev(), LMDBAL::NotFound); }