forked from blue/lmdbal
Cursors refactoring part one
This commit is contained in:
parent
ef86d0adf9
commit
bfb1d007ad
19 changed files with 824 additions and 677 deletions
342
src/cursorcommon.cpp
Normal file
342
src/cursorcommon.cpp
Normal file
|
@ -0,0 +1,342 @@
|
|||
/*
|
||||
* LMDB Abstraction Layer.
|
||||
* Copyright (C) 2023 Yury Gubich <blue@macaw.me>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "cursorcommon.h"
|
||||
|
||||
/**
|
||||
* \class LMDBAL::CursorCommon
|
||||
* \brief An object to manage cursor internals and state.
|
||||
*
|
||||
* Cursors are owned by the storage, they die with the storage.
|
||||
* They also get closed if the storage is closed (if you close by the database for example)
|
||||
*
|
||||
* You can obtain an instance of this class calling LMDBAL::Storage::createCursor()
|
||||
* and destory it calling LMDBAL::Storage::destoryCursor() at any time, LMDBAL::Base doesn't necessarily need to be opened.
|
||||
*
|
||||
* You are not supposed to instantiate or destory instances of this class yourself!
|
||||
*/
|
||||
|
||||
#include "storagecommon.h"
|
||||
|
||||
static uint32_t idCounter = 0;
|
||||
|
||||
/**
|
||||
* \brief Creates a empty class
|
||||
*/
|
||||
LMDBAL::CursorCommon::CursorCommon ():
|
||||
id(0),
|
||||
state(closed),
|
||||
handle(nullptr),
|
||||
storage(nullptr)
|
||||
{}
|
||||
|
||||
/**
|
||||
* \brief Creates a cursor
|
||||
*
|
||||
* \param[in] _storage a storage that created this cursor
|
||||
*/
|
||||
LMDBAL::CursorCommon::CursorCommon (StorageCommon* _storage):
|
||||
id(++idCounter),
|
||||
state(closed),
|
||||
handle(nullptr),
|
||||
storage(_storage)
|
||||
{}
|
||||
|
||||
/**
|
||||
* \brief Moves other cursor into this class
|
||||
*
|
||||
* \param[in] other other instance that is being moved
|
||||
*/
|
||||
LMDBAL::CursorCommon::CursorCommon (CursorCommon&& other):
|
||||
id(other.id),
|
||||
state(other.state),
|
||||
handle(other.handle),
|
||||
storage(other.storage)
|
||||
{
|
||||
other.dropped();
|
||||
|
||||
if (state == openedPublic)
|
||||
storage->attachCursorToTransaction(id, handle, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Destroys this cursor
|
||||
*
|
||||
* If the cursor wasn't properly closed - it's going to be upon destruction
|
||||
*/
|
||||
LMDBAL::CursorCommon::~CursorCommon () noexcept {
|
||||
close();
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Move assignment operator
|
||||
*
|
||||
* Transfers other cursor into this one
|
||||
*/
|
||||
LMDBAL::CursorCommon& LMDBAL::CursorCommon::operator = (CursorCommon&& other) {
|
||||
terminated();
|
||||
|
||||
id = other.id;
|
||||
state = other.state;
|
||||
handle = other.handle;
|
||||
storage = other.storage;
|
||||
|
||||
other.reset();
|
||||
|
||||
if (state == openedPublic)
|
||||
storage->attachCursorToTransaction(id, handle, this);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief A private method that turns cursor into an empty one
|
||||
*
|
||||
* This method is called from LMDBAL::Storage, when the cursor is getting destoryed.
|
||||
* After this method cursors will become empty, and can't be used anymore
|
||||
*/
|
||||
void LMDBAL::CursorCommon::reset () {
|
||||
id = 0;
|
||||
state = closed;
|
||||
handle = nullptr;
|
||||
storage = nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* \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 cursors.
|
||||
* Those cursors will become empty, and can't be used anymore
|
||||
*/
|
||||
void LMDBAL::CursorCommon::dropped () {
|
||||
terminated();
|
||||
reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief A private function called to inform the cursor he has been terminated
|
||||
*
|
||||
* Is expected to be called from transaction, database, storage or move constructor
|
||||
*/
|
||||
void LMDBAL::CursorCommon::terminated () {
|
||||
switch (state) {
|
||||
case openedPublic:
|
||||
storage->_mdbCursorClose(handle);
|
||||
state = closed;
|
||||
break;
|
||||
case openedPrivate:
|
||||
storage->closeCursorTransaction(handle, true);
|
||||
state = closed;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Termiates a sequence of operations with the cursor
|
||||
*
|
||||
* This is a normal way to tell that you're done with the cursor and don't want to continue the sequence of queries.
|
||||
* The state of the cursor is lost after calling this method, some inner resorce is freed.
|
||||
*
|
||||
* If the cursor was opened with the private transaction - the owner storage will be notified of the aborted transaction.
|
||||
*
|
||||
* This function does nothing on a closed cursor.
|
||||
*/
|
||||
void LMDBAL::CursorCommon::close () {
|
||||
switch (state) {
|
||||
case openedPublic:
|
||||
storage->disconnectCursorFromTransaction(id, handle);
|
||||
storage->_mdbCursorClose(handle);
|
||||
|
||||
state = closed;
|
||||
break;
|
||||
case openedPrivate:
|
||||
storage->closeCursorTransaction(handle, true);
|
||||
|
||||
state = closed;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Opens the cursor for operations.
|
||||
*
|
||||
* This is a normal way to start the sequence of operations with the cursor.
|
||||
* This variant of the function creates a read only transaction just for this cursor
|
||||
*
|
||||
* 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::CursorEmpty thrown if the cursor was empty
|
||||
*/
|
||||
void LMDBAL::CursorCommon::open () {
|
||||
if (empty())
|
||||
throw CursorEmpty(openCursorMethodName);
|
||||
|
||||
switch (state) {
|
||||
case closed:
|
||||
storage->openCursorTransaction(&handle, false);
|
||||
state = openedPrivate;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Opens the cursor for operations.
|
||||
*
|
||||
* This is a normal way to start the sequence of operations with the cursor.
|
||||
* This variant of the function uses for queries a transaction you have obtained somewhere else.
|
||||
*
|
||||
* 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).
|
||||
*
|
||||
* \param[in] transaction - a transaction, can be read only
|
||||
*
|
||||
* \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
|
||||
*/
|
||||
void LMDBAL::CursorCommon::open (const Transaction& transaction) {
|
||||
if (empty())
|
||||
throw CursorEmpty(openCursorMethodName);
|
||||
|
||||
storage->ensureOpened(openCursorMethodName);
|
||||
TransactionID txn = storage->extractTransactionId(transaction, openCursorMethodName);
|
||||
switch (state) {
|
||||
case closed: {
|
||||
int result = storage->_mdbCursorOpen(txn, &handle);
|
||||
if (result != MDB_SUCCESS)
|
||||
storage->throwUnknown(result);
|
||||
|
||||
storage->attachCursorToTransaction(id, handle, this);
|
||||
state = openedPublic;
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Renews a cursor
|
||||
*
|
||||
* This function aborts current transaction if the cursor was opened with it's own transaction
|
||||
* (does not mess up if the transaction was public),
|
||||
* creates new private transaction and rebinds this cursor to it.
|
||||
*
|
||||
* Theoretically you could call this method if your public transaction was aborted (or commited)
|
||||
* but you wish to continue to keep working with your cursor.
|
||||
* Or if you just want to rebind your cursor to a new private transaction.
|
||||
*
|
||||
* 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::CursorEmpty thrown if the cursor was empty
|
||||
*/
|
||||
void LMDBAL::CursorCommon::renew () {
|
||||
if (empty())
|
||||
throw CursorEmpty(openCursorMethodName);
|
||||
|
||||
storage->ensureOpened(renewCursorMethodName);
|
||||
switch (state) {
|
||||
case openedPrivate:
|
||||
storage->closeCursorTransaction(handle, false);
|
||||
storage->openCursorTransaction(&handle, true);
|
||||
break;
|
||||
case openedPublic:
|
||||
storage->disconnectCursorFromTransaction(id, handle);
|
||||
storage->openCursorTransaction(&handle, true);
|
||||
state = openedPrivate;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Renews a cursor
|
||||
*
|
||||
* This function aborts current transaction if the cursor was opened with it's own transaction
|
||||
* (does not mess up if the transaction was public),
|
||||
* and rebinds this cursor to a passed new transaction.
|
||||
*
|
||||
* Theoretically you could call this method if your previous public transaction was aborted (or commited)
|
||||
* but you wish to continue to keep working with your cursor.
|
||||
* Or if you just want to rebind your cursor to another public transaction.
|
||||
*
|
||||
* This function does nothing if the cursor is closed
|
||||
*
|
||||
* \param[in] transaction - a transaction you wish this cursor to be bound to
|
||||
*
|
||||
* \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
|
||||
*/
|
||||
void LMDBAL::CursorCommon::renew (const Transaction& transaction) {
|
||||
if (empty())
|
||||
throw CursorEmpty(openCursorMethodName);
|
||||
|
||||
storage->ensureOpened(renewCursorMethodName);
|
||||
TransactionID txn = storage->extractTransactionId(transaction, renewCursorMethodName);
|
||||
switch (state) {
|
||||
case openedPrivate: {
|
||||
storage->closeCursorTransaction(handle, false);
|
||||
int result = storage->_mdbCursorRenew(txn, handle);
|
||||
if (result != MDB_SUCCESS)
|
||||
storage->throwUnknown(result);
|
||||
|
||||
storage->attachCursorToTransaction(id, handle, this);
|
||||
state = openedPublic;
|
||||
} break;
|
||||
case openedPublic: {
|
||||
storage->disconnectCursorFromTransaction(id, handle);
|
||||
int result = storage->_mdbCursorRenew(txn, handle);
|
||||
if (result != MDB_SUCCESS)
|
||||
storage->throwUnknown(result);
|
||||
|
||||
storage->attachCursorToTransaction(id, handle, this);
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Returns true if the cursor is empty
|
||||
*
|
||||
* Empty cursors can't be used, they can be only targets of move operations
|
||||
*/
|
||||
bool LMDBAL::CursorCommon::empty () const {
|
||||
return id == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Tells if the cursor is open
|
||||
*/
|
||||
bool LMDBAL::CursorCommon::opened () const {
|
||||
return state != closed;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue