forked from blue/lmdbal
Cursors refactoring part one
This commit is contained in:
parent
ef86d0adf9
commit
bfb1d007ad
@ -1,8 +1,8 @@
|
||||
# LMDBAL - Lightning Memory Data Base Abstraction Level
|
||||
|
||||
[![AUR license](https://img.shields.io/aur/license/lmdbal?style=flat-square)](https://git.macaw.me/blue/lmdbal/raw/branch/master/LICENSE.md)
|
||||
[![AUR qt5 version](https://img.shields.io/aur/version/lmdbal-qt5?style=flat-square)](https://aur.archlinux.org/packages/lmdbal-qt5/)
|
||||
[![AUR qt6 version](https://img.shields.io/aur/version/lmdbal-qt6?style=flat-square)](https://aur.archlinux.org/packages/lmdbal-qt6/)
|
||||
[![AUR qt5 version](https://img.shields.io/aur/version/lmdbal-qt5?style=flat-square&label=lmdbal-qt5)](https://aur.archlinux.org/packages/lmdbal-qt5/)
|
||||
[![AUR qt6 version](https://img.shields.io/aur/version/lmdbal-qt6?style=flat-square&label=lmdbal-qt6)](https://aur.archlinux.org/packages/lmdbal-qt6/)
|
||||
[![Liberapay patrons](https://img.shields.io/liberapay/patrons/macaw.me?logo=liberapay&style=flat-square)](https://liberapay.com/macaw.me)
|
||||
[![Documentation](https://img.shields.io/badge/Documentation-HTML-green)](https://macaw.me/lmdbal/doc/html)
|
||||
|
||||
|
@ -1,8 +1,9 @@
|
||||
set(SOURCES
|
||||
exceptions.cpp
|
||||
storage.cpp
|
||||
storagecommon.cpp
|
||||
base.cpp
|
||||
transaction.cpp
|
||||
cursorcommon.cpp
|
||||
)
|
||||
|
||||
set(HEADERS
|
||||
@ -10,13 +11,15 @@ set(HEADERS
|
||||
exceptions.h
|
||||
storage.h
|
||||
storage.hpp
|
||||
storagecommon.h
|
||||
storagecommon.hpp
|
||||
cursor.h
|
||||
cursor.hpp
|
||||
cursorcommon.h
|
||||
cache.h
|
||||
cache.hpp
|
||||
operators.hpp
|
||||
transaction.h
|
||||
icursor.h
|
||||
)
|
||||
|
||||
target_sources(${LMDBAL_NAME} PRIVATE ${SOURCES})
|
||||
|
20
src/base.cpp
20
src/base.cpp
@ -52,7 +52,7 @@ LMDBAL::Base::Base(const QString& _name, uint16_t _mapSize):
|
||||
LMDBAL::Base::~Base() {
|
||||
close();
|
||||
|
||||
for (const std::pair<const std::string, iStorage*>& pair : storages)
|
||||
for (const std::pair<const std::string, StorageCommon*>& pair : storages)
|
||||
delete pair.second;
|
||||
}
|
||||
|
||||
@ -71,7 +71,7 @@ void LMDBAL::Base::close() {
|
||||
pair.second->reset();
|
||||
}
|
||||
|
||||
for (const std::pair<const std::string, iStorage*>& pair : storages)
|
||||
for (const std::pair<const std::string, StorageCommon*>& pair : storages)
|
||||
pair.second->close();
|
||||
|
||||
mdb_env_close(environment);
|
||||
@ -99,8 +99,8 @@ void LMDBAL::Base::open() {
|
||||
mdb_env_open(environment, path.toStdString().c_str(), 0, 0664);
|
||||
|
||||
TransactionID txn = beginPrivateTransaction(emptyName);
|
||||
for (const std::pair<const std::string, iStorage*>& pair : storages) {
|
||||
iStorage* storage = pair.second;
|
||||
for (const std::pair<const std::string, StorageCommon*>& pair : storages) {
|
||||
StorageCommon* storage = pair.second;
|
||||
int rc = storage->open(txn);
|
||||
if (rc)
|
||||
throw Unknown(name, mdb_strerror(rc));
|
||||
@ -200,7 +200,7 @@ void LMDBAL::Base::drop() {
|
||||
throw Closed("drop", name);
|
||||
|
||||
TransactionID txn = beginPrivateTransaction(emptyName);
|
||||
for (const std::pair<const std::string, iStorage*>& pair : storages) {
|
||||
for (const std::pair<const std::string, StorageCommon*>& pair : storages) {
|
||||
int rc = pair.second->drop(txn);
|
||||
if (rc != MDB_SUCCESS) {
|
||||
abortPrivateTransaction(txn, emptyName);
|
||||
@ -209,7 +209,7 @@ void LMDBAL::Base::drop() {
|
||||
}
|
||||
|
||||
commitPrivateTransaction(txn, emptyName);
|
||||
for (const std::pair<const std::string, iStorage*>& pair : storages)
|
||||
for (const std::pair<const std::string, StorageCommon*>& pair : storages)
|
||||
pair.second->handleDrop();
|
||||
}
|
||||
|
||||
@ -306,7 +306,7 @@ LMDBAL::TransactionID LMDBAL::Base::beginReadOnlyTransaction(const std::string&
|
||||
throw Closed("beginReadOnlyTransaction", name, storageName);
|
||||
|
||||
TransactionID txn = beginPrivateReadOnlyTransaction(storageName);
|
||||
for (const std::pair<const std::string, LMDBAL::iStorage*>& pair : storages)
|
||||
for (const std::pair<const std::string, LMDBAL::StorageCommon*>& pair : storages)
|
||||
pair.second->transactionStarted(txn, true);
|
||||
|
||||
return txn;
|
||||
@ -331,7 +331,7 @@ LMDBAL::TransactionID LMDBAL::Base::beginTransaction(const std::string& storageN
|
||||
throw Closed("beginTransaction", name, storageName);
|
||||
|
||||
TransactionID txn = beginPrivateTransaction(storageName);
|
||||
for (const std::pair<const std::string, LMDBAL::iStorage*>& pair : storages)
|
||||
for (const std::pair<const std::string, LMDBAL::StorageCommon*>& pair : storages)
|
||||
pair.second->transactionStarted(txn, false);
|
||||
|
||||
return txn;
|
||||
@ -356,7 +356,7 @@ void LMDBAL::Base::abortTransaction(LMDBAL::TransactionID id, const std::string&
|
||||
throw Closed("abortTransaction", name, storageName);
|
||||
|
||||
abortPrivateTransaction(id, storageName);
|
||||
for (const std::pair<const std::string, LMDBAL::iStorage*>& pair : storages)
|
||||
for (const std::pair<const std::string, LMDBAL::StorageCommon*>& pair : storages)
|
||||
pair.second->transactionAborted(id);
|
||||
}
|
||||
|
||||
@ -379,7 +379,7 @@ void LMDBAL::Base::commitTransaction(LMDBAL::TransactionID id, const std::string
|
||||
throw Closed("abortTransaction", name, storageName);
|
||||
|
||||
commitPrivateTransaction(id, storageName);
|
||||
for (const std::pair<const std::string, LMDBAL::iStorage*>& pair : storages)
|
||||
for (const std::pair<const std::string, LMDBAL::StorageCommon*>& pair : storages)
|
||||
pair.second->transactionCommited(id);
|
||||
}
|
||||
|
||||
|
10
src/base.h
10
src/base.h
@ -34,7 +34,7 @@
|
||||
|
||||
namespace LMDBAL {
|
||||
|
||||
class iStorage;
|
||||
class StorageCommon;
|
||||
class Transaction;
|
||||
class WriteTransaction;
|
||||
|
||||
@ -51,7 +51,7 @@ typedef MDB_txn* TransactionID; /**<\brief I'm going to
|
||||
typedef uint32_t SizeType; /**<\brief All LMDBAL sizes are uint32_t*/
|
||||
|
||||
class Base {
|
||||
friend class iStorage;
|
||||
friend class StorageCommon;
|
||||
friend class Transaction;
|
||||
friend class WriteTransaction;
|
||||
public:
|
||||
@ -84,7 +84,7 @@ public:
|
||||
LMDBAL::Cache<K, V>* getCache(const std::string& storageName);
|
||||
|
||||
private:
|
||||
typedef std::map<std::string, LMDBAL::iStorage*> Storages; /**<\brief Storage and Cache pointers are saved in the std::map*/
|
||||
typedef std::map<std::string, LMDBAL::StorageCommon *> Storages; /**<\brief Storage and Cache pointers are saved in the std::map*/
|
||||
typedef std::map<TransactionID, Transaction*> Transactions; /**<\brief Piblic transaction IDs are saved in the std::set*/
|
||||
|
||||
void commitTransaction(TransactionID id);
|
||||
@ -136,7 +136,7 @@ LMDBAL::Storage<K, V>* LMDBAL::Base::addStorage(const std::string& storageName,
|
||||
throw Opened(name, "add storage " + storageName);
|
||||
|
||||
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, (StorageCommon *)storage));
|
||||
if (!pair.second)
|
||||
throw StorageDuplicate(name, storageName);
|
||||
|
||||
@ -164,7 +164,7 @@ LMDBAL::Cache<K, V> * LMDBAL::Base::addCache(const std::string& storageName) {
|
||||
throw Opened(name, "add cache " + storageName);
|
||||
|
||||
Cache<K, V>* cache = new Cache<K, V>(this, storageName, false);
|
||||
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, (StorageCommon *)cache));
|
||||
if (!pair.second)
|
||||
throw StorageDuplicate(name, storageName);
|
||||
|
||||
|
@ -63,10 +63,10 @@ LMDBAL::Cache<K, V>::~Cache() {
|
||||
|
||||
template<class K, class V>
|
||||
void LMDBAL::Cache<K, V>::addRecord(const K& key, const V& value) {
|
||||
iStorage::ensureOpened(iStorage::addRecordMethodName);
|
||||
StorageCommon::ensureOpened(StorageCommon::addRecordMethodName);
|
||||
|
||||
if (cache->count(key) > 0)
|
||||
iStorage::throwDuplicate(iStorage::toString(key));
|
||||
StorageCommon::throwDuplicate(StorageCommon::toString(key));
|
||||
|
||||
Storage<K, V>::addRecord(key, value);
|
||||
handleAddRecord(key, value);
|
||||
@ -75,7 +75,7 @@ void LMDBAL::Cache<K, V>::addRecord(const K& key, const V& value) {
|
||||
template<class K, class V>
|
||||
void LMDBAL::Cache<K, V>::addRecord(const K& key, const V& value, TransactionID txn) {
|
||||
if (cache->count(key) > 0)
|
||||
iStorage::throwDuplicate(iStorage::toString(key));
|
||||
StorageCommon::throwDuplicate(StorageCommon::toString(key));
|
||||
|
||||
Storage<K, V>::addRecord(key, value, txn);
|
||||
|
||||
@ -96,7 +96,7 @@ void LMDBAL::Cache<K, V>::handleAddRecord(const K& key, const V& value) {
|
||||
|
||||
template<class K, class V>
|
||||
bool LMDBAL::Cache<K, V>::forceRecord(const K& key, const V& value) {
|
||||
iStorage::ensureOpened(iStorage::forceRecordMethodName);
|
||||
StorageCommon::ensureOpened(StorageCommon::forceRecordMethodName);
|
||||
|
||||
bool added = Storage<K, V>::forceRecord(key, value);
|
||||
handleForceRecord(key, value, added);
|
||||
@ -136,18 +136,18 @@ void LMDBAL::Cache<K, V>::handleForceRecord(const K& key, const V& value, bool a
|
||||
|
||||
template<class K, class V>
|
||||
void LMDBAL::Cache<K, V>::changeRecord(const K& key, const V& value) {
|
||||
iStorage::ensureOpened(iStorage::changeRecordMethodName);
|
||||
StorageCommon::ensureOpened(StorageCommon::changeRecordMethodName);
|
||||
|
||||
if (mode == Mode::full) {
|
||||
typename std::map<K, V>::iterator itr = cache->find(key);
|
||||
if (itr == cache->end())
|
||||
iStorage::throwNotFound(iStorage::toString(key));
|
||||
StorageCommon::throwNotFound(StorageCommon::toString(key));
|
||||
|
||||
Storage<K, V>::changeRecord(key, value);
|
||||
itr->second = value;
|
||||
} else {
|
||||
if (abscent->count(key) > 0)
|
||||
iStorage::throwNotFound(iStorage::toString(key));
|
||||
StorageCommon::throwNotFound(StorageCommon::toString(key));
|
||||
|
||||
try {
|
||||
Storage<K, V>::changeRecord(key, value);
|
||||
@ -169,12 +169,12 @@ void LMDBAL::Cache<K, V>::changeRecord(const K& key, const V& value, Transaction
|
||||
if (mode == Mode::full) {
|
||||
typename std::map<K, V>::iterator itr = cache->find(key);
|
||||
if (itr == cache->end())
|
||||
iStorage::throwNotFound(iStorage::toString(key));
|
||||
StorageCommon::throwNotFound(StorageCommon::toString(key));
|
||||
|
||||
Storage<K, V>::changeRecord(key, value, txn);
|
||||
} else {
|
||||
if (abscent->count(key) > 0)
|
||||
iStorage::throwNotFound(iStorage::toString(key));
|
||||
StorageCommon::throwNotFound(StorageCommon::toString(key));
|
||||
|
||||
try {
|
||||
Storage<K, V>::changeRecord(key, value, txn);
|
||||
@ -207,7 +207,7 @@ void LMDBAL::Cache<K, V>::handleChangeRecord(const K& key, const V& value) {
|
||||
|
||||
template<class K, class V>
|
||||
V LMDBAL::Cache<K, V>::getRecord(const K& key) const {
|
||||
iStorage::ensureOpened(iStorage::getRecordMethodName);
|
||||
StorageCommon::ensureOpened(StorageCommon::getRecordMethodName);
|
||||
|
||||
V value;
|
||||
Cache<K, V>::getRecord(key, value);
|
||||
@ -216,7 +216,7 @@ V LMDBAL::Cache<K, V>::getRecord(const K& key) const {
|
||||
|
||||
template<class K, class V>
|
||||
void LMDBAL::Cache<K, V>::getRecord(const K& key, V& out) const {
|
||||
iStorage::ensureOpened(iStorage::getRecordMethodName);
|
||||
StorageCommon::ensureOpened(StorageCommon::getRecordMethodName);
|
||||
|
||||
typename std::map<K, V>::const_iterator itr = cache->find(key);
|
||||
if (itr != cache->end()) {
|
||||
@ -225,7 +225,7 @@ void LMDBAL::Cache<K, V>::getRecord(const K& key, V& out) const {
|
||||
}
|
||||
|
||||
if (mode == Mode::full || abscent->count(key) != 0)
|
||||
iStorage::throwNotFound(iStorage::toString(key));
|
||||
StorageCommon::throwNotFound(StorageCommon::toString(key));
|
||||
|
||||
try {
|
||||
Storage<K, V>::getRecord(key, out);
|
||||
@ -269,7 +269,7 @@ void LMDBAL::Cache<K, V>::getRecord(const K& key, V& out, TransactionID txn) con
|
||||
}
|
||||
break;
|
||||
case Operation::remove:
|
||||
iStorage::throwNotFound(iStorage::toString(key));
|
||||
StorageCommon::throwNotFound(StorageCommon::toString(key));
|
||||
break;
|
||||
case Operation::change:
|
||||
if (static_cast<std::pair<K, V>*>(entry.second)->first == key) {
|
||||
@ -285,7 +285,7 @@ void LMDBAL::Cache<K, V>::getRecord(const K& key, V& out, TransactionID txn) con
|
||||
}
|
||||
break;
|
||||
case Operation::drop:
|
||||
iStorage::throwNotFound(iStorage::toString(key));
|
||||
StorageCommon::throwNotFound(StorageCommon::toString(key));
|
||||
break;
|
||||
case Operation::replace: {
|
||||
std::map<K, V>* newMap = static_cast<std::map<K, V>*>(entry.second);
|
||||
@ -294,7 +294,7 @@ void LMDBAL::Cache<K, V>::getRecord(const K& key, V& out, TransactionID txn) con
|
||||
out = vitr->second;
|
||||
return;
|
||||
} else {
|
||||
iStorage::throwNotFound(iStorage::toString(key));
|
||||
StorageCommon::throwNotFound(StorageCommon::toString(key));
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -322,7 +322,7 @@ void LMDBAL::Cache<K, V>::getRecord(const K& key, V& out, TransactionID txn) con
|
||||
}
|
||||
|
||||
if (mode == Mode::full || abscent->count(key) != 0)
|
||||
iStorage::throwNotFound(iStorage::toString(key));
|
||||
StorageCommon::throwNotFound(StorageCommon::toString(key));
|
||||
|
||||
try {
|
||||
Storage<K, V>::getRecord(key, out, txn);
|
||||
@ -352,7 +352,7 @@ void LMDBAL::Cache<K, V>::discoveredRecord(const K& key, const V& value, Transac
|
||||
|
||||
template<class K, class V>
|
||||
bool LMDBAL::Cache<K, V>::checkRecord(const K& key) const {
|
||||
iStorage::ensureOpened(iStorage::checkRecordMethodName);
|
||||
StorageCommon::ensureOpened(StorageCommon::checkRecordMethodName);
|
||||
|
||||
typename std::map<K, V>::const_iterator itr = cache->find(key);
|
||||
if (itr != cache->end())
|
||||
@ -457,7 +457,7 @@ void LMDBAL::Cache<K, V>::appendToCache(const K& key, const V& value) const {
|
||||
|
||||
template<class K, class V>
|
||||
std::map<K, V> LMDBAL::Cache<K, V>::readAll() const {
|
||||
iStorage::ensureOpened(iStorage::readAllMethodName);
|
||||
StorageCommon::ensureOpened(StorageCommon::readAllMethodName);
|
||||
|
||||
if (mode != Mode::full) { //there is a room for optimization
|
||||
mode = Mode::full; //I can read and deserialize only those values
|
||||
@ -471,7 +471,7 @@ std::map<K, V> LMDBAL::Cache<K, V>::readAll() const {
|
||||
|
||||
template<class K, class V>
|
||||
void LMDBAL::Cache<K, V>::readAll(std::map<K, V>& out) const {
|
||||
iStorage::ensureOpened(iStorage::readAllMethodName);
|
||||
StorageCommon::ensureOpened(StorageCommon::readAllMethodName);
|
||||
|
||||
if (mode != Mode::full) { //there is a room for optimization
|
||||
mode = Mode::full; //I can read and deserialize only those values
|
||||
@ -642,7 +642,7 @@ void LMDBAL::Cache<K, V>::handleAddRecords(const std::map<K, V>& data, bool over
|
||||
|
||||
template<class K, class V>
|
||||
void LMDBAL::Cache<K, V>::removeRecord(const K& key) {
|
||||
iStorage::ensureOpened(iStorage::removeRecordMethodName);
|
||||
StorageCommon::ensureOpened(StorageCommon::removeRecordMethodName);
|
||||
|
||||
bool noKey = false;
|
||||
if (mode != Mode::full)
|
||||
@ -651,7 +651,7 @@ void LMDBAL::Cache<K, V>::removeRecord(const K& key) {
|
||||
noKey = abscent->count(key) > 0;
|
||||
|
||||
if (noKey)
|
||||
iStorage::throwNotFound(iStorage::toString(key));
|
||||
StorageCommon::throwNotFound(StorageCommon::toString(key));
|
||||
|
||||
Storage<K, V>::removeRecord(key);
|
||||
handleRemoveRecord(key);
|
||||
@ -666,7 +666,7 @@ void LMDBAL::Cache<K, V>::removeRecord(const K& key, TransactionID txn) {
|
||||
noKey = abscent->count(key) > 0;
|
||||
|
||||
if (noKey)
|
||||
iStorage::throwNotFound(iStorage::toString(key));
|
||||
StorageCommon::throwNotFound(StorageCommon::toString(key));
|
||||
|
||||
Storage<K, V>::removeRecord(key, txn);
|
||||
|
||||
@ -781,8 +781,8 @@ void LMDBAL::Cache<K, V>::handleMode() const {
|
||||
|
||||
template<class K, class V>
|
||||
int LMDBAL::Cache<K, V>::drop(const WriteTransaction& transaction) {
|
||||
iStorage::ensureOpened(iStorage::dropMethodName);
|
||||
TransactionID txn = iStorage::extractTransactionId(transaction, iStorage::dropMethodName);
|
||||
StorageCommon::ensureOpened(StorageCommon::dropMethodName);
|
||||
TransactionID txn = StorageCommon::extractTransactionId(transaction, StorageCommon::dropMethodName);
|
||||
int res = Storage<K, V>::drop(txn);
|
||||
|
||||
if (res != MDB_SUCCESS)
|
||||
|
45
src/cursor.h
45
src/cursor.h
@ -24,20 +24,13 @@
|
||||
#include "base.h"
|
||||
#include "storage.h"
|
||||
#include "transaction.h"
|
||||
#include "icursor.h"
|
||||
#include "cursorcommon.h"
|
||||
|
||||
namespace LMDBAL {
|
||||
|
||||
template <class K, class V>
|
||||
class Cursor : public iCursor {
|
||||
class Cursor : public CursorCommon {
|
||||
friend class Storage<K, V>;
|
||||
private:
|
||||
enum State { /**<Cursor state:*/
|
||||
closed, /**< - closed*/
|
||||
openedPublic, /**< - opened with public transaction, all storages will be notified about it after it's done*/
|
||||
openedPrivate /**< - opened with private transaction, only current storage will be notified when cursor is closed*/
|
||||
};
|
||||
|
||||
public:
|
||||
Cursor();
|
||||
Cursor(Storage<K, V>* parent);
|
||||
@ -48,14 +41,6 @@ public:
|
||||
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;
|
||||
|
||||
void drop();
|
||||
|
||||
std::pair<K, V> first();
|
||||
@ -72,33 +57,7 @@ public:
|
||||
void current(K& key, V& value) const;
|
||||
|
||||
private:
|
||||
virtual void terminated() override;
|
||||
void dropped();
|
||||
void freed();
|
||||
void operateCursorRead(K& key, V& value, MDB_cursor_op operation, const std::string& methodName, const std::string& operationName) const;
|
||||
|
||||
private:
|
||||
Storage<K, V>* storage;
|
||||
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*/
|
||||
inline static const std::string renewCursorMethodName = "Cursor::renew"; /**<\brief member function name, just for exceptions*/
|
||||
|
||||
inline static const std::string firstMethodName = "first"; /**<\brief member function name, just for exceptions*/
|
||||
inline static const std::string lastMethodName = "last"; /**<\brief member function name, just for exceptions*/
|
||||
inline static const std::string nextMethodName = "next"; /**<\brief member function name, just for exceptions*/
|
||||
inline static const std::string prevMethodName = "prev"; /**<\brief member function name, just for exceptions*/
|
||||
inline static const std::string currentMethodName = "current"; /**<\brief member function name, just for exceptions*/
|
||||
inline static const std::string setMethodName = "set"; /**<\brief member function name, just for exceptions*/
|
||||
|
||||
inline static const std::string firstOperationName = "Cursor::first"; /**<\brief member function name, just for exceptions*/
|
||||
inline static const std::string lastOperationName = "Cursor::last"; /**<\brief member function name, just for exceptions*/
|
||||
inline static const std::string nextOperationName = "Cursor::next"; /**<\brief member function name, just for exceptions*/
|
||||
inline static const std::string prevOperationName = "Cursor::prev"; /**<\brief member function name, just for exceptions*/
|
||||
inline static const std::string currentOperationName = "Cursor::current"; /**<\brief member function name, just for exceptions*/
|
||||
};
|
||||
|
||||
};
|
||||
|
333
src/cursor.hpp
333
src/cursor.hpp
@ -41,8 +41,6 @@
|
||||
* You are not supposed to instantiate or destory instances of this class yourself!
|
||||
*/
|
||||
|
||||
static uint32_t idCounter = 0;
|
||||
|
||||
/**
|
||||
* \brief Creates a cursor
|
||||
*
|
||||
@ -50,12 +48,9 @@ static uint32_t idCounter = 0;
|
||||
*/
|
||||
template<class K, class V>
|
||||
LMDBAL::Cursor<K, V>::Cursor(Storage<K, V>* parent):
|
||||
storage(parent),
|
||||
cursor(nullptr),
|
||||
state(closed),
|
||||
id(++idCounter)
|
||||
CursorCommon(parent)
|
||||
{
|
||||
storage->cursors[id] = this;
|
||||
parent->cursors[id] = this;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -65,10 +60,7 @@ LMDBAL::Cursor<K, V>::Cursor(Storage<K, V>* parent):
|
||||
*/
|
||||
template<class K, class V>
|
||||
LMDBAL::Cursor<K, V>::Cursor():
|
||||
storage(nullptr),
|
||||
cursor(nullptr),
|
||||
state(closed),
|
||||
id(0)
|
||||
CursorCommon()
|
||||
{}
|
||||
|
||||
/**
|
||||
@ -76,64 +68,39 @@ LMDBAL::Cursor<K, V>::Cursor():
|
||||
*/
|
||||
template<class K, class V>
|
||||
LMDBAL::Cursor<K, V>::Cursor(Cursor&& other):
|
||||
storage(other.storage),
|
||||
cursor(other.cursor),
|
||||
state(other.state),
|
||||
id(other.id)
|
||||
CursorCommon(std::move(other))
|
||||
{
|
||||
other.terminated();
|
||||
if (id != 0)
|
||||
storage->cursors[id] = this;
|
||||
|
||||
if (state == openedPublic)
|
||||
storage->attachCursorToTransaction(id, cursor, this);
|
||||
|
||||
|
||||
other.freed();
|
||||
if (!empty())
|
||||
static_cast<Storage<K, V>*>(storage)->cursors[id] = this;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief A private function that turns cursor into an empty one
|
||||
* \brief Move assignment operator
|
||||
*
|
||||
* 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
|
||||
* Transfers other cursor into this one
|
||||
*/
|
||||
template<class K, class V>
|
||||
LMDBAL::Cursor<K, V>& LMDBAL::Cursor<K, V>::operator = (Cursor&& other) {
|
||||
terminated();
|
||||
if (!empty() && other.empty())
|
||||
static_cast<Storage<K, V>*>(storage)->cursors.erase(id);
|
||||
|
||||
if (id != 0)
|
||||
storage->cursors.erase(id);
|
||||
CursorCommon::operator=(std::move(other));
|
||||
|
||||
storage = other.storage;
|
||||
cursor = other.cursor;
|
||||
state = other.state;
|
||||
id = other.id;
|
||||
|
||||
if (id != 0) {
|
||||
other.freed();
|
||||
other.state = closed;
|
||||
|
||||
storage->cursors[id] = this;
|
||||
|
||||
if (state == openedPublic)
|
||||
storage->attachCursorToTransaction(id, cursor, this);
|
||||
}
|
||||
if (!empty())
|
||||
static_cast<Storage<K, V>*>(storage)->cursors[id] = this;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Destroys a cursor
|
||||
* \brief Destroys this cursor
|
||||
*
|
||||
* If the cursor wasn't properly closed - it's going to be upon destruction
|
||||
*/
|
||||
template<class K, class V>
|
||||
LMDBAL::Cursor<K, V>::~Cursor () {
|
||||
close();
|
||||
|
||||
if (id != 0)
|
||||
storage->cursors.erase(id);
|
||||
static_cast<Storage<K, V>*>(storage)->cursors.erase(id);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -147,253 +114,9 @@ void LMDBAL::Cursor<K, V>::drop () {
|
||||
close();
|
||||
|
||||
if (id != 0)
|
||||
storage->cursors.erase(id);
|
||||
static_cast<Storage<K, V>*>(storage)->cursors.erase(id);
|
||||
|
||||
freed();
|
||||
}
|
||||
|
||||
/**
|
||||
* \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
|
||||
*/
|
||||
template<class K, class V>
|
||||
void LMDBAL::Cursor<K, V>::dropped () {
|
||||
terminated();
|
||||
freed();
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief A private method that turns cursor into an empty one (submethod)
|
||||
*
|
||||
* This function is called from LMDBAL::Storage, when the cursor is getting destoryed.
|
||||
* Those cursors will become empty, and can't be used anymore
|
||||
*/
|
||||
template<class K, class V>
|
||||
void LMDBAL::Cursor<K, V>::freed () {
|
||||
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<class K, class V>
|
||||
bool LMDBAL::Cursor<K, V>::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<class K, class V>
|
||||
void LMDBAL::Cursor<K, V>::terminated () {
|
||||
switch (state) {
|
||||
case openedPublic:
|
||||
storage->_mdbCursorClose(cursor);
|
||||
state = closed;
|
||||
break;
|
||||
case openedPrivate:
|
||||
storage->closeCursorTransaction(cursor, 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
|
||||
*/
|
||||
template<class K, class V>
|
||||
void LMDBAL::Cursor<K, V>::open () {
|
||||
if (empty())
|
||||
throw CursorEmpty(openCursorMethodName);
|
||||
|
||||
switch (state) {
|
||||
case closed:
|
||||
storage->openCursorTransaction(&cursor, 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
|
||||
*/
|
||||
template<class K, class V>
|
||||
void LMDBAL::Cursor<K, V>::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, &cursor);
|
||||
if (result != MDB_SUCCESS)
|
||||
storage->throwUnknown(result);
|
||||
|
||||
storage->attachCursorToTransaction(id, cursor, 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
|
||||
*/
|
||||
template<class K, class V>
|
||||
void LMDBAL::Cursor<K, V>::renew () {
|
||||
if (empty())
|
||||
throw CursorEmpty(openCursorMethodName);
|
||||
|
||||
storage->ensureOpened(renewCursorMethodName);
|
||||
switch (state) {
|
||||
case openedPrivate:
|
||||
storage->closeCursorTransaction(cursor, false);
|
||||
storage->openCursorTransaction(&cursor, true);
|
||||
break;
|
||||
case openedPublic:
|
||||
storage->disconnectCursorFromTransaction(id, cursor);
|
||||
storage->openCursorTransaction(&cursor, 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
|
||||
*/
|
||||
template<class K, class V>
|
||||
void LMDBAL::Cursor<K, V>::renew (const Transaction& transaction) {
|
||||
if (empty())
|
||||
throw CursorEmpty(openCursorMethodName);
|
||||
|
||||
storage->ensureOpened(renewCursorMethodName);
|
||||
TransactionID txn = storage->extractTransactionId(transaction, renewCursorMethodName);
|
||||
switch (state) {
|
||||
case openedPrivate: {
|
||||
storage->closeCursorTransaction(cursor, false);
|
||||
int result = storage->_mdbCursorRenew(txn, cursor);
|
||||
if (result != MDB_SUCCESS)
|
||||
storage->throwUnknown(result);
|
||||
|
||||
storage->attachCursorToTransaction(id, cursor, this);
|
||||
state = openedPublic;
|
||||
} break;
|
||||
case openedPublic: {
|
||||
storage->disconnectCursorFromTransaction(id, cursor);
|
||||
int result = storage->_mdbCursorRenew(txn, cursor);
|
||||
if (result != MDB_SUCCESS)
|
||||
storage->throwUnknown(result);
|
||||
|
||||
storage->attachCursorToTransaction(id, cursor, this);
|
||||
} 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.
|
||||
*/
|
||||
template<class K, class V>
|
||||
void LMDBAL::Cursor<K, V>::close () {
|
||||
switch (state) {
|
||||
case openedPublic:
|
||||
storage->disconnectCursorFromTransaction(id, cursor);
|
||||
storage->_mdbCursorClose(cursor);
|
||||
|
||||
state = closed;
|
||||
break;
|
||||
case openedPrivate:
|
||||
storage->closeCursorTransaction(cursor, true);
|
||||
|
||||
state = closed;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Tells if the cursor is open
|
||||
*/
|
||||
template<class K, class V>
|
||||
bool LMDBAL::Cursor<K, V>::opened () const {
|
||||
return state != closed;
|
||||
reset();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -614,16 +337,16 @@ std::pair<K, V> LMDBAL::Cursor<K, V>::current () const {
|
||||
template<class K, class V>
|
||||
bool LMDBAL::Cursor<K, V>::set (const K& key) {
|
||||
if (state == closed)
|
||||
storage->throwCursorNotReady(setMethodName);
|
||||
static_cast<Storage<K, V>*>(storage)->throwCursorNotReady(setMethodName);
|
||||
|
||||
MDB_val mdbKey = storage->keySerializer.setData(key);
|
||||
int result = storage->_mdbCursorSet(cursor, mdbKey);
|
||||
MDB_val mdbKey = static_cast<Storage<K, V>*>(storage)->keySerializer.setData(key);
|
||||
int result = static_cast<Storage<K, V>*>(storage)->_mdbCursorSet(handle, mdbKey);
|
||||
if (result == MDB_SUCCESS)
|
||||
return true;
|
||||
else if (result == MDB_NOTFOUND)
|
||||
return false;
|
||||
|
||||
storage->throwUnknown(result);
|
||||
static_cast<Storage<K, V>*>(storage)->throwUnknown(result);
|
||||
return false; //unreachable, just to suppress the warning
|
||||
}
|
||||
|
||||
@ -651,18 +374,18 @@ void LMDBAL::Cursor<K, V>::operateCursorRead(
|
||||
const std::string& operationName
|
||||
) const {
|
||||
if (state == closed)
|
||||
storage->throwCursorNotReady(methodName);
|
||||
static_cast<Storage<K, V>*>(storage)->throwCursorNotReady(methodName);
|
||||
|
||||
MDB_val mdbKey, mdbValue;
|
||||
int result = storage->_mdbCursorGet(cursor, mdbKey, mdbValue, operation);
|
||||
int result = static_cast<Storage<K, V>*>(storage)->_mdbCursorGet(handle, mdbKey, mdbValue, operation);
|
||||
if (result != MDB_SUCCESS)
|
||||
storage->throwNotFoundOrUnknown(result, operationName);
|
||||
static_cast<Storage<K, V>*>(storage)->throwNotFoundOrUnknown(result, operationName);
|
||||
|
||||
storage->keySerializer.deserialize(mdbKey, key);
|
||||
storage->valueSerializer.deserialize(mdbValue, value);
|
||||
static_cast<Storage<K, V>*>(storage)->keySerializer.deserialize(mdbKey, key);
|
||||
static_cast<Storage<K, V>*>(storage)->valueSerializer.deserialize(mdbValue, value);
|
||||
|
||||
if (state == openedPrivate)
|
||||
storage->discoveredRecord(key, value);
|
||||
static_cast<Storage<K, V>*>(storage)->discoveredRecord(key, value);
|
||||
else
|
||||
storage->discoveredRecord(key, value, storage->_mdbCursorTxn(cursor));
|
||||
static_cast<Storage<K, V>*>(storage)->discoveredRecord(key, value, static_cast<Storage<K, V>*>(storage)->_mdbCursorTxn(handle));
|
||||
}
|
||||
|
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;
|
||||
}
|
90
src/cursorcommon.h
Normal file
90
src/cursorcommon.h
Normal file
@ -0,0 +1,90 @@
|
||||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
|
||||
#include <lmdb.h>
|
||||
|
||||
namespace LMDBAL {
|
||||
|
||||
class Transaction;
|
||||
class StorageCommon;
|
||||
|
||||
class CursorCommon {
|
||||
friend class Transaction;
|
||||
protected:
|
||||
enum State { /**<Cursor state:*/
|
||||
closed, /**< - closed*/
|
||||
openedPublic, /**< - opened with public transaction, all storages will be notified about it after it's done*/
|
||||
openedPrivate /**< - opened with private transaction, only current storage will be notified when cursor is closed*/
|
||||
};
|
||||
|
||||
protected:
|
||||
CursorCommon();
|
||||
CursorCommon(StorageCommon* storage);
|
||||
CursorCommon(const CursorCommon& other) = delete;
|
||||
CursorCommon(CursorCommon&& other);
|
||||
virtual ~CursorCommon() noexcept;
|
||||
|
||||
CursorCommon& operator = (const CursorCommon& other) = delete;
|
||||
CursorCommon& operator = (CursorCommon&& other);
|
||||
|
||||
public:
|
||||
void open();
|
||||
void open(const Transaction& transaction);
|
||||
void renew();
|
||||
void renew(const Transaction& transaction);
|
||||
bool opened() const;
|
||||
bool empty() const;
|
||||
void close();
|
||||
|
||||
protected:
|
||||
void terminated();
|
||||
void dropped();
|
||||
void reset();
|
||||
|
||||
protected:
|
||||
uint32_t id;
|
||||
State state;
|
||||
MDB_cursor* handle;
|
||||
StorageCommon* storage;
|
||||
|
||||
private:
|
||||
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*/
|
||||
inline static const std::string renewCursorMethodName = "Cursor::renew"; /**<\brief member function name, just for exceptions*/
|
||||
|
||||
protected:
|
||||
inline static const std::string firstMethodName = "first"; /**<\brief member function name, just for exceptions in heir*/
|
||||
inline static const std::string lastMethodName = "last"; /**<\brief member function name, just for exceptions in heir*/
|
||||
inline static const std::string nextMethodName = "next"; /**<\brief member function name, just for exceptions in heir*/
|
||||
inline static const std::string prevMethodName = "prev"; /**<\brief member function name, just for exceptions in heir*/
|
||||
inline static const std::string currentMethodName = "current"; /**<\brief member function name, just for exceptions in heir*/
|
||||
inline static const std::string setMethodName = "set"; /**<\brief member function name, just for exceptions in heir*/
|
||||
|
||||
inline static const std::string firstOperationName = "Cursor::first"; /**<\brief member function name, just for exceptions in heir*/
|
||||
inline static const std::string lastOperationName = "Cursor::last"; /**<\brief member function name, just for exceptions in heir*/
|
||||
inline static const std::string nextOperationName = "Cursor::next"; /**<\brief member function name, just for exceptions in heir*/
|
||||
inline static const std::string prevOperationName = "Cursor::prev"; /**<\brief member function name, just for exceptions in heir*/
|
||||
inline static const std::string currentOperationName = "Cursor::current"; /**<\brief member function name, just for exceptions in heir*/
|
||||
};
|
||||
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace LMDBAL {
|
||||
|
||||
class Transaction;
|
||||
|
||||
class iCursor {
|
||||
friend class Transaction;
|
||||
protected:
|
||||
virtual ~iCursor() {}
|
||||
virtual void terminated() = 0;
|
||||
};
|
||||
|
||||
}
|
108
src/storage.h
108
src/storage.h
@ -25,6 +25,7 @@
|
||||
#include "serializer.h"
|
||||
#include "cursor.h"
|
||||
#include "transaction.h"
|
||||
#include "storagecommon.h"
|
||||
|
||||
class BaseTest;
|
||||
class DuplicatesTest;
|
||||
@ -33,111 +34,8 @@ class StorageCursorTest;
|
||||
|
||||
namespace LMDBAL {
|
||||
|
||||
class iStorage {
|
||||
friend class Base;
|
||||
public:
|
||||
protected:
|
||||
iStorage(Base* parent, const std::string& name, bool duplicates = false);
|
||||
virtual ~iStorage();
|
||||
|
||||
/**
|
||||
* \brief A private virtual function I need to open each storage in the database
|
||||
*
|
||||
* \param[in] transaction - lmdb transaction to call <a class="el" href="http://www.lmdb.tech/doc/group__mdb.html#gac08cad5b096925642ca359a6d6f0562a">mdb_dbi_open</a>
|
||||
* \returns MDB_SUCCESS if everything went smooth or MDB_<error> -like error code
|
||||
*/
|
||||
virtual int open(MDB_txn * transaction) = 0;
|
||||
virtual void close();
|
||||
virtual void handleDrop();
|
||||
|
||||
bool isDBOpened() const;
|
||||
const std::string& dbName() const;
|
||||
|
||||
void ensureOpened(const std::string& methodName) const;
|
||||
void throwDuplicateOrUnknown(int rc, const std::string& key) const;
|
||||
void throwDuplicateOrUnknown(int rc, TransactionID txn, const std::string& key) const;
|
||||
void throwNotFoundOrUnknown(int rc, const std::string& key) const;
|
||||
void throwNotFoundOrUnknown(int rc, TransactionID txn, const std::string& key) const;
|
||||
void throwUnknown(int rc, TransactionID txn) const;
|
||||
void throwUnknown(int rc) const;
|
||||
void throwUnknown(const std::string& message) const;
|
||||
void throwDuplicate(const std::string& key) const;
|
||||
void throwNotFound(const std::string& key) const;
|
||||
void throwCursorNotReady(const std::string& method) const;
|
||||
|
||||
TransactionID extractTransactionId(const Transaction& txn, const std::string& action = "") const;
|
||||
TransactionID beginReadOnlyTransaction() const;
|
||||
TransactionID beginTransaction() const;
|
||||
void commitTransaction(TransactionID id);
|
||||
void abortTransaction(TransactionID id) const;
|
||||
virtual void transactionStarted(TransactionID txn, bool readOnly) const;
|
||||
virtual void transactionCommited(TransactionID txn);
|
||||
virtual void transactionAborted(TransactionID txn) const;
|
||||
virtual int drop(TransactionID transaction);
|
||||
virtual SizeType count(TransactionID txn) const;
|
||||
|
||||
void openCursorTransaction(MDB_cursor** cursor, bool renew = false) const;
|
||||
void closeCursorTransaction(MDB_cursor* cursor, bool closeCursor = false) const;
|
||||
void attachCursorToTransaction(uint32_t cursorId, MDB_cursor* cursorHandle, iCursor* pointer) const;
|
||||
void disconnectCursorFromTransaction(uint32_t cursorId, MDB_cursor* cursorHandle) const;
|
||||
|
||||
int _mdbOpen(MDB_txn* txn, unsigned int flags = 0);
|
||||
|
||||
int _mdbPut(MDB_txn* txn, MDB_val& key, MDB_val& data, unsigned int flags = 0);
|
||||
int _mdbGet(MDB_txn* txn, MDB_val& key, MDB_val& data) const;
|
||||
int _mdbDel(MDB_txn* txn, MDB_val& key);
|
||||
int _mdbDel(MDB_txn* txn, MDB_val& key, MDB_val& data);
|
||||
|
||||
int _mdbStat(MDB_txn* txn, MDB_stat& stat) const;
|
||||
int _mdbFlags(MDB_txn* txn, uint32_t& flags) const;
|
||||
|
||||
int _mdbCursorOpen(MDB_txn* txn, MDB_cursor** cursor) const;
|
||||
void _mdbCursorClose(MDB_cursor* cursor) const;
|
||||
int _mdbCursorGet(MDB_cursor* cursor, MDB_val& key, MDB_val& data, MDB_cursor_op operation) const;
|
||||
int _mdbCursorSet(MDB_cursor* cursor, MDB_val& key) const;
|
||||
int _mdbCursorDel(MDB_cursor* cursor, unsigned int flags = 0);
|
||||
int _mdbCursorPut(MDB_cursor* cursor, MDB_val& key, MDB_val& data, unsigned int flags = 0);
|
||||
|
||||
int _mdbCursorRenew(MDB_txn* txn, MDB_cursor* cursor) const;
|
||||
MDB_txn* _mdbCursorTxn(MDB_cursor* cursor) const;
|
||||
|
||||
public:
|
||||
virtual void drop();
|
||||
virtual int drop(const WriteTransaction& txn);
|
||||
virtual SizeType count() const;
|
||||
virtual SizeType count(const Transaction& txn) const;
|
||||
|
||||
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*/
|
||||
|
||||
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 flagsMethodName = "flags"; /**<\brief member function name, just for exceptions*/
|
||||
|
||||
inline static const std::string addRecordMethodName = "addRecord"; /**<\brief member function name, just for exceptions*/
|
||||
inline static const std::string forceRecordMethodName = "forceRecord"; /**<\brief member function name, just for exceptions*/
|
||||
inline static const std::string changeRecordMethodName = "changeRecord"; /**<\brief member function name, just for exceptions*/
|
||||
inline static const std::string removeRecordMethodName = "removeRecord"; /**<\brief member function name, just for exceptions*/
|
||||
inline static const std::string checkRecordMethodName = "checkRecord"; /**<\brief member function name, just for exceptions*/
|
||||
inline static const std::string getRecordMethodName = "getRecord"; /**<\brief member function name, just for exceptions*/
|
||||
inline static const std::string readAllMethodName = "readAllRecord"; /**<\brief member function name, just for exceptions*/
|
||||
inline static const std::string replaceAllMethodName = "replaceAll"; /**<\brief member function name, just for exceptions*/
|
||||
inline static const std::string addRecordsMethodName = "addRecords"; /**<\brief member function name, just for exceptions*/
|
||||
inline static const std::string openCursorTransactionMethodName = "openCursorTransaction"; /**<\brief member function name, just for exceptions*/
|
||||
|
||||
protected:
|
||||
template <class K, class V>
|
||||
int makeStorage(MDB_txn* transaction, bool duplicates = false);
|
||||
|
||||
template <class T>
|
||||
static std::string toString(const T& value);
|
||||
};
|
||||
|
||||
template <class K, class V>
|
||||
class Storage : public iStorage {
|
||||
class Storage : public StorageCommon {
|
||||
friend class ::BaseTest;
|
||||
friend class ::DuplicatesTest;
|
||||
friend class ::CacheCursorTest;
|
||||
@ -165,7 +63,7 @@ protected:
|
||||
virtual uint32_t addRecords(const std::map<K, V>& data, TransactionID txn, bool overwrite = false);
|
||||
|
||||
public:
|
||||
using iStorage::drop;
|
||||
using StorageCommon::drop;
|
||||
virtual void addRecord(const K& key, const V& value);
|
||||
virtual void addRecord(const K& key, const V& value, const WriteTransaction& txn);
|
||||
virtual bool forceRecord(const K& key, const V& value); //returns true if there was addition, false if change
|
||||
|
@ -46,7 +46,7 @@
|
||||
*/
|
||||
template<class K, class V>
|
||||
LMDBAL::Storage<K, V>::Storage(Base* parent, const std::string& name, bool duplicates):
|
||||
iStorage(parent, name, duplicates),
|
||||
StorageCommon(parent, name, duplicates),
|
||||
keySerializer(),
|
||||
valueSerializer(),
|
||||
cursors()
|
||||
@ -1015,7 +1015,7 @@ void LMDBAL::Storage<K, V>::close() {
|
||||
for (const std::pair<const uint32_t, Cursor<K, V>*>& pair : cursors)
|
||||
pair.second->terminated();
|
||||
|
||||
iStorage::close();
|
||||
StorageCommon::close();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1051,7 +1051,7 @@ void LMDBAL::Storage<K, V>::destroyCursor(LMDBAL::Cursor<K, V>& cursor) {
|
||||
|
||||
cursor.close();
|
||||
cursors.erase(itr);
|
||||
cursor.freed();
|
||||
cursor.reset();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1103,81 +1103,3 @@ void LMDBAL::Storage<K, V>::discoveredRecord(const K& key, const V& value, Trans
|
||||
UNUSED(value);
|
||||
UNUSED(txn);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief A functiion to actually open <a class="el" href="http://www.lmdb.tech/doc/group__mdb.html#gadbe68a06c448dfb62da16443d251a78b">MDB_dbi</a> storage
|
||||
*
|
||||
* \tparam K type of keys in opening storage
|
||||
*
|
||||
* \param[in] transaction - lmdb transaction to call <a class="el" href="http://www.lmdb.tech/doc/group__mdb.html#gac08cad5b096925642ca359a6d6f0562a">mdb_dbi_open</a>, must be a writable transaction!
|
||||
* \param[in] duplicates - true if key duplicates are allowed (false by default)
|
||||
*
|
||||
* \returns MDB_SUCCESS if everything went smooth or MDB_<error> -like error code
|
||||
*
|
||||
* This is a way to optimise database using MDB_INTEGERKEY flag,
|
||||
* when the key is actually kind of an integer
|
||||
* This infrastructure also allowes us to customize mdb_dbi_open call in the future
|
||||
*/
|
||||
template<class K, class V>
|
||||
inline int LMDBAL::iStorage::makeStorage(MDB_txn* transaction, bool duplicates) {
|
||||
unsigned int flags = MDB_CREATE;
|
||||
if constexpr (std::is_integral<K>::value)
|
||||
flags |= MDB_INTEGERKEY;
|
||||
|
||||
if (duplicates) {
|
||||
flags |= MDB_DUPSORT;
|
||||
|
||||
if constexpr (std::is_scalar<V>::value)
|
||||
flags |= MDB_DUPFIXED;
|
||||
|
||||
if constexpr (
|
||||
std::is_same<V, uint32_t>::value ||
|
||||
std::is_same<V, int32_t>::value ||
|
||||
std::is_same<V, uint64_t>::value ||
|
||||
std::is_same<V, int64_t>::value
|
||||
) //for some reason lmdb breaks if it's not one of these types in MDB_DUPFIXED mode
|
||||
flags |= MDB_INTEGERDUP;
|
||||
}
|
||||
|
||||
return _mdbOpen(transaction, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief A method to cast a value (which can be a value or a key) to string.
|
||||
*
|
||||
* This function is mainly used in exceptions, to report which key was duplicated or not found.
|
||||
* You can define your own specializations to this function in case std::to_string doesn't cover your case
|
||||
*
|
||||
* \param[in] value a value that should be converted to string
|
||||
* \returns a string presentation of value
|
||||
*/
|
||||
template<class T>
|
||||
inline std::string LMDBAL::iStorage::toString(const T& value) {
|
||||
return std::to_string(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief A method to cast a value (which can be a value or a key) to string.
|
||||
*
|
||||
* QString spectialization
|
||||
*
|
||||
* \param[in] value a value that should be converted to string
|
||||
* \returns a string presentation of value
|
||||
*/
|
||||
template<>
|
||||
inline std::string LMDBAL::iStorage::toString(const QString& value) {
|
||||
return value.toStdString();
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief A method to cast a value (which can be a value or a key) to string.
|
||||
*
|
||||
* std::string spectialization
|
||||
*
|
||||
* \param[in] value a value that should be converted to string
|
||||
* \returns a string presentation of value
|
||||
*/
|
||||
template<>
|
||||
inline std::string LMDBAL::iStorage::toString(const std::string& value) {
|
||||
return value;
|
||||
}
|
||||
|
@ -16,7 +16,9 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "storage.h"
|
||||
#include "storagecommon.h"
|
||||
|
||||
#include "cursorcommon.h"
|
||||
|
||||
#define UNUSED(x) (void)(x)
|
||||
|
||||
@ -37,7 +39,7 @@
|
||||
* \param[in] name - the name of the storage
|
||||
* \param[in] duplicates - true if key duplicates are allowed (false by default)
|
||||
*/
|
||||
LMDBAL::iStorage::iStorage(Base* parent, const std::string& name, bool duplicates):
|
||||
LMDBAL::StorageCommon::StorageCommon(Base* parent, const std::string& name, bool duplicates):
|
||||
dbi(),
|
||||
db(parent),
|
||||
name(name),
|
||||
@ -47,12 +49,12 @@ LMDBAL::iStorage::iStorage(Base* parent, const std::string& name, bool duplicate
|
||||
/**
|
||||
* \brief Destroys a storage interface
|
||||
*/
|
||||
LMDBAL::iStorage::~iStorage() {}
|
||||
LMDBAL::StorageCommon::~StorageCommon () {}
|
||||
|
||||
/**
|
||||
* \brief A private virtual function to close each storage in the database
|
||||
*/
|
||||
void LMDBAL::iStorage::close() {
|
||||
void LMDBAL::StorageCommon::close() {
|
||||
mdb_dbi_close(db->environment, dbi);
|
||||
}
|
||||
|
||||
@ -67,7 +69,7 @@ void LMDBAL::iStorage::close() {
|
||||
*
|
||||
* \exception LMDBAL::TransactionTerminated thrown if the passed transaction not active, any action with it's inner ID is an error
|
||||
*/
|
||||
LMDBAL::TransactionID LMDBAL::iStorage::extractTransactionId(const Transaction& txn, const std::string& action) const {
|
||||
LMDBAL::TransactionID LMDBAL::StorageCommon::extractTransactionId(const Transaction& txn, const std::string& action) const {
|
||||
if (!txn.isActive())
|
||||
throw TransactionTerminated(db->name, name, action);
|
||||
|
||||
@ -82,11 +84,11 @@ LMDBAL::TransactionID LMDBAL::iStorage::extractTransactionId(const Transaction&
|
||||
* \exception LMDBAL::Closed thrown if the database was closed
|
||||
* \exception LMDBAL::Unknown thrown if something unexpected happened
|
||||
*/
|
||||
void LMDBAL::iStorage::drop() {
|
||||
void LMDBAL::StorageCommon::drop() {
|
||||
ensureOpened(dropMethodName);
|
||||
|
||||
TransactionID txn = beginTransaction();
|
||||
int rc = iStorage::drop(txn);
|
||||
int rc = StorageCommon::drop(txn);
|
||||
if (rc != MDB_SUCCESS) {
|
||||
abortTransaction(txn);
|
||||
throw Unknown(db->name, mdb_strerror(rc), name);
|
||||
@ -104,7 +106,7 @@ void LMDBAL::iStorage::drop() {
|
||||
* \param[in] transaction - transaction ID, must be writable transaction!
|
||||
* \returns MDB_SUCCESS if everything went fine, MDB_<error> code otherwise
|
||||
*/
|
||||
int LMDBAL::iStorage::drop(TransactionID transaction) {
|
||||
int LMDBAL::StorageCommon::drop(TransactionID transaction) {
|
||||
return mdb_drop(transaction, dbi, 0);
|
||||
}
|
||||
|
||||
@ -118,7 +120,7 @@ int LMDBAL::iStorage::drop(TransactionID transaction) {
|
||||
*
|
||||
* \exception LMDBAL::TransactionTerminated thrown if the transaction was not active
|
||||
*/
|
||||
int LMDBAL::iStorage::drop(const WriteTransaction& txn) {
|
||||
int LMDBAL::StorageCommon::drop(const WriteTransaction& txn) {
|
||||
ensureOpened(dropMethodName);
|
||||
return drop(extractTransactionId(txn, dropMethodName));
|
||||
}
|
||||
@ -130,7 +132,7 @@ int LMDBAL::iStorage::drop(const WriteTransaction& txn) {
|
||||
*
|
||||
* \exception LMDBAL::Closed thrown if the database was closed
|
||||
*/
|
||||
void LMDBAL::iStorage::ensureOpened(const std::string& methodName) const {
|
||||
void LMDBAL::StorageCommon::ensureOpened(const std::string& methodName) const {
|
||||
if (!isDBOpened())
|
||||
throw Closed(methodName, db->name, name);
|
||||
}
|
||||
@ -143,7 +145,7 @@ void LMDBAL::iStorage::ensureOpened(const std::string& methodName) const {
|
||||
* \exception LMDBAL::Closed thrown if the database was closed
|
||||
* \exception LMDBAL::Unknown thrown if something unexpected happened
|
||||
*/
|
||||
LMDBAL::SizeType LMDBAL::iStorage::count() const {
|
||||
LMDBAL::SizeType LMDBAL::StorageCommon::count() const {
|
||||
ensureOpened(countMethodName);
|
||||
|
||||
TransactionID txn = beginReadOnlyTransaction();
|
||||
@ -168,7 +170,7 @@ LMDBAL::SizeType LMDBAL::iStorage::count() const {
|
||||
* \exception std::out_of_range thrown if LMDBAL::Transaction pointer wasn't found in the LMDBAL::Base
|
||||
*/
|
||||
|
||||
void LMDBAL::iStorage::attachCursorToTransaction (uint32_t cursorId, MDB_cursor* cursorHandle, iCursor* pointer) const {
|
||||
void LMDBAL::StorageCommon::attachCursorToTransaction (uint32_t cursorId, MDB_cursor* cursorHandle, CursorCommon* pointer) const {
|
||||
TransactionID txnID = _mdbCursorTxn(cursorHandle);
|
||||
Transaction* txn = db->transactions.at(txnID);
|
||||
txn->cursors[cursorId] = pointer;
|
||||
@ -186,7 +188,7 @@ void LMDBAL::iStorage::attachCursorToTransaction (uint32_t cursorId, MDB_cursor*
|
||||
* \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
|
||||
*/
|
||||
void LMDBAL::iStorage::openCursorTransaction (MDB_cursor** cursor, bool renew) const {
|
||||
void LMDBAL::StorageCommon::openCursorTransaction (MDB_cursor** cursor, bool renew) const {
|
||||
ensureOpened(openCursorTransactionMethodName);
|
||||
|
||||
TransactionID txn = beginReadOnlyTransaction();
|
||||
@ -210,7 +212,7 @@ void LMDBAL::iStorage::openCursorTransaction (MDB_cursor** cursor, bool renew) c
|
||||
* \param[in] cursor - cursor handle
|
||||
* \param[in] closeCursor - true if the cursor should also get closed, false if you wish to leave it open
|
||||
*/
|
||||
void LMDBAL::iStorage::closeCursorTransaction (MDB_cursor* cursor, bool closeCursor) const {
|
||||
void LMDBAL::StorageCommon::closeCursorTransaction (MDB_cursor* cursor, bool closeCursor) const {
|
||||
TransactionID txn = _mdbCursorTxn(cursor);
|
||||
if (closeCursor)
|
||||
_mdbCursorClose(cursor);
|
||||
@ -228,7 +230,7 @@ void LMDBAL::iStorage::closeCursorTransaction (MDB_cursor* cursor, bool closeCur
|
||||
* \exception std::out_of_range thrown if LMDBAL::Transaction pointer wasn't found in the LMDBAL::Base
|
||||
*/
|
||||
|
||||
void LMDBAL::iStorage::disconnectCursorFromTransaction (uint32_t cursorId, MDB_cursor* cursorHandle) const {
|
||||
void LMDBAL::StorageCommon::disconnectCursorFromTransaction (uint32_t cursorId, MDB_cursor* cursorHandle) const {
|
||||
TransactionID txnID = _mdbCursorTxn(cursorHandle);
|
||||
Transaction* txn = db->transactions.at(txnID);
|
||||
txn->cursors.erase(cursorId);
|
||||
@ -243,7 +245,7 @@ void LMDBAL::iStorage::disconnectCursorFromTransaction (uint32_t cursorId, MDB_c
|
||||
*
|
||||
* \exception LMDBAL::Unknown thrown if something unexpected happened
|
||||
*/
|
||||
LMDBAL::SizeType LMDBAL::iStorage::count(TransactionID txn) const {
|
||||
LMDBAL::SizeType LMDBAL::StorageCommon::count(TransactionID txn) const {
|
||||
MDB_stat stat;
|
||||
int rc = mdb_stat(txn, dbi, &stat);
|
||||
if (rc != MDB_SUCCESS)
|
||||
@ -262,7 +264,7 @@ LMDBAL::SizeType LMDBAL::iStorage::count(TransactionID txn) const {
|
||||
* \exception LMDBAL::Unknown thrown if something unexpected happened
|
||||
* \exception LMDBAL::TransactionTerminated thrown if the passed transaction not active, any action with it's inner ID is an error
|
||||
*/
|
||||
LMDBAL::SizeType LMDBAL::iStorage::count(const Transaction& txn) const {
|
||||
LMDBAL::SizeType LMDBAL::StorageCommon::count(const Transaction& txn) const {
|
||||
ensureOpened(countMethodName);
|
||||
return count(extractTransactionId(txn, countMethodName));
|
||||
}
|
||||
@ -279,7 +281,7 @@ LMDBAL::SizeType LMDBAL::iStorage::count(const Transaction& txn) const {
|
||||
* \exception LMDBAL::Exist thrown if rc == MDB_KEYEXIST
|
||||
* \exception LMDBAL::Unknown thrown if rc != MDB_KEYEXIST
|
||||
*/
|
||||
void LMDBAL::iStorage::throwDuplicateOrUnknown(int rc, TransactionID txn, const std::string& key) const {
|
||||
void LMDBAL::StorageCommon::throwDuplicateOrUnknown(int rc, TransactionID txn, const std::string& key) const {
|
||||
abortTransaction(txn);
|
||||
throwDuplicateOrUnknown(rc, key);
|
||||
}
|
||||
@ -296,7 +298,7 @@ void LMDBAL::iStorage::throwDuplicateOrUnknown(int rc, TransactionID txn, const
|
||||
* \exception LMDBAL::NotFound thrown if rc == MDB_NOTFOUND
|
||||
* \exception LMDBAL::Unknown thrown if rc != MDB_NOTFOUND
|
||||
*/
|
||||
void LMDBAL::iStorage::throwNotFoundOrUnknown(int rc, LMDBAL::TransactionID txn, const std::string& key) const {
|
||||
void LMDBAL::StorageCommon::throwNotFoundOrUnknown(int rc, LMDBAL::TransactionID txn, const std::string& key) const {
|
||||
abortTransaction(txn);
|
||||
throwNotFoundOrUnknown(rc, key);
|
||||
}
|
||||
@ -312,7 +314,7 @@ void LMDBAL::iStorage::throwNotFoundOrUnknown(int rc, LMDBAL::TransactionID txn,
|
||||
* \exception LMDBAL::Exist thrown if rc == MDB_KEYEXIST
|
||||
* \exception LMDBAL::Unknown thrown if rc != MDB_KEYEXIST
|
||||
*/
|
||||
void LMDBAL::iStorage::throwDuplicateOrUnknown(int rc, const std::string& key) const {
|
||||
void LMDBAL::StorageCommon::throwDuplicateOrUnknown(int rc, const std::string& key) const {
|
||||
if (rc == MDB_KEYEXIST)
|
||||
throwDuplicate(key);
|
||||
else
|
||||
@ -330,7 +332,7 @@ void LMDBAL::iStorage::throwDuplicateOrUnknown(int rc, const std::string& key) c
|
||||
* \exception LMDBAL::NotFound thrown if rc == MDB_NOTFOUND
|
||||
* \exception LMDBAL::Unknown thrown if rc != MDB_NOTFOUND
|
||||
*/
|
||||
void LMDBAL::iStorage::throwNotFoundOrUnknown(int rc, const std::string& key) const {
|
||||
void LMDBAL::StorageCommon::throwNotFoundOrUnknown(int rc, const std::string& key) const {
|
||||
if (rc == MDB_NOTFOUND)
|
||||
throwNotFound(key);
|
||||
else
|
||||
@ -347,7 +349,7 @@ void LMDBAL::iStorage::throwNotFoundOrUnknown(int rc, const std::string& key) co
|
||||
*
|
||||
* \exception LMDBAL::Unknown thrown everytime
|
||||
*/
|
||||
void LMDBAL::iStorage::throwUnknown(int rc, LMDBAL::TransactionID txn) const {
|
||||
void LMDBAL::StorageCommon::throwUnknown(int rc, LMDBAL::TransactionID txn) const {
|
||||
abortTransaction(txn);
|
||||
throwUnknown(rc);
|
||||
}
|
||||
@ -359,7 +361,7 @@ void LMDBAL::iStorage::throwUnknown(int rc, LMDBAL::TransactionID txn) const {
|
||||
*
|
||||
* \returns database name
|
||||
*/
|
||||
const std::string & LMDBAL::iStorage::dbName() const {
|
||||
const std::string & LMDBAL::StorageCommon::dbName() const {
|
||||
return db->name;}
|
||||
|
||||
/**
|
||||
@ -369,7 +371,7 @@ const std::string & LMDBAL::iStorage::dbName() const {
|
||||
*
|
||||
* \returns true if database is ipened, false otherwise
|
||||
*/
|
||||
bool LMDBAL::iStorage::isDBOpened() const {
|
||||
bool LMDBAL::StorageCommon::isDBOpened() const {
|
||||
return db->opened;}
|
||||
|
||||
/**
|
||||
@ -381,7 +383,7 @@ bool LMDBAL::iStorage::isDBOpened() const {
|
||||
*
|
||||
* \exception LMDBAL::Unknown thrown everytime
|
||||
*/
|
||||
void LMDBAL::iStorage::throwUnknown(int rc) const {
|
||||
void LMDBAL::StorageCommon::throwUnknown(int rc) const {
|
||||
throw Unknown(db->name, mdb_strerror(rc), name);}
|
||||
|
||||
/**
|
||||
@ -393,7 +395,7 @@ void LMDBAL::iStorage::throwUnknown(int rc) const {
|
||||
*
|
||||
* \exception LMDBAL::Unknown thrown everytime
|
||||
*/
|
||||
void LMDBAL::iStorage::throwUnknown(const std::string& message) const {
|
||||
void LMDBAL::StorageCommon::throwUnknown(const std::string& message) const {
|
||||
throw Unknown(db->name, message, name);}
|
||||
|
||||
/**
|
||||
@ -405,7 +407,7 @@ void LMDBAL::iStorage::throwUnknown(const std::string& message) const {
|
||||
*
|
||||
* \exception LMDBAL::Exist thrown everytime
|
||||
*/
|
||||
void LMDBAL::iStorage::throwDuplicate(const std::string& key) const {
|
||||
void LMDBAL::StorageCommon::throwDuplicate(const std::string& key) const {
|
||||
throw Exist(key, db->name, name);}
|
||||
|
||||
/**
|
||||
@ -417,7 +419,7 @@ void LMDBAL::iStorage::throwDuplicate(const std::string& key) const {
|
||||
*
|
||||
* \exception LMDBAL::NotFound thrown everytime
|
||||
*/
|
||||
void LMDBAL::iStorage::throwNotFound(const std::string& key) const {
|
||||
void LMDBAL::StorageCommon::throwNotFound(const std::string& key) const {
|
||||
throw NotFound(key, db->name, name);}
|
||||
|
||||
/**
|
||||
@ -429,7 +431,7 @@ void LMDBAL::iStorage::throwNotFound(const std::string& key) const {
|
||||
*
|
||||
* \exception LMDBAL::CursorNotReady thrown everytime
|
||||
*/
|
||||
void LMDBAL::iStorage::throwCursorNotReady(const std::string& method) const {
|
||||
void LMDBAL::StorageCommon::throwCursorNotReady(const std::string& method) const {
|
||||
throw CursorNotReady(method, db->name, name);}
|
||||
|
||||
/**
|
||||
@ -439,7 +441,7 @@ void LMDBAL::iStorage::throwCursorNotReady(const std::string& method) const {
|
||||
*
|
||||
* \returns read only transaction
|
||||
*/
|
||||
LMDBAL::TransactionID LMDBAL::iStorage::beginReadOnlyTransaction() const {
|
||||
LMDBAL::TransactionID LMDBAL::StorageCommon::beginReadOnlyTransaction() const {
|
||||
return db->beginPrivateReadOnlyTransaction(name);}
|
||||
|
||||
/**
|
||||
@ -449,7 +451,7 @@ LMDBAL::TransactionID LMDBAL::iStorage::beginReadOnlyTransaction() const {
|
||||
*
|
||||
* \returns read only transaction
|
||||
*/
|
||||
LMDBAL::TransactionID LMDBAL::iStorage::beginTransaction() const {
|
||||
LMDBAL::TransactionID LMDBAL::StorageCommon::beginTransaction() const {
|
||||
return db->beginPrivateTransaction(name);}
|
||||
|
||||
/**
|
||||
@ -457,7 +459,7 @@ LMDBAL::TransactionID LMDBAL::iStorage::beginTransaction() const {
|
||||
*
|
||||
* Ment to be called from heirs, name is reported to the database just to be displayed in std::exception::what() message
|
||||
*/
|
||||
void LMDBAL::iStorage::abortTransaction(LMDBAL::TransactionID id) const {
|
||||
void LMDBAL::StorageCommon::abortTransaction(LMDBAL::TransactionID id) const {
|
||||
db->abortPrivateTransaction(id, name);}
|
||||
|
||||
/**
|
||||
@ -467,7 +469,7 @@ void LMDBAL::iStorage::abortTransaction(LMDBAL::TransactionID id) const {
|
||||
*
|
||||
* \exception LMDBAL::Unknown thrown if something unexpected happened
|
||||
*/
|
||||
void LMDBAL::iStorage::commitTransaction(LMDBAL::TransactionID id) {
|
||||
void LMDBAL::StorageCommon::commitTransaction(LMDBAL::TransactionID id) {
|
||||
db->commitPrivateTransaction(id, name);}
|
||||
|
||||
/**
|
||||
@ -482,7 +484,7 @@ void LMDBAL::iStorage::commitTransaction(LMDBAL::TransactionID id) {
|
||||
* \param[in] txn - ID of started transaction
|
||||
* \param[in] readOnly - true if transaction is read-only, false otherwise
|
||||
*/
|
||||
void LMDBAL::iStorage::transactionStarted(LMDBAL::TransactionID txn, bool readOnly) const {
|
||||
void LMDBAL::StorageCommon::transactionStarted(LMDBAL::TransactionID txn, bool readOnly) const {
|
||||
UNUSED(txn);
|
||||
UNUSED(readOnly);
|
||||
}
|
||||
@ -498,7 +500,7 @@ void LMDBAL::iStorage::transactionStarted(LMDBAL::TransactionID txn, bool readOn
|
||||
*
|
||||
* \param[in] txn - ID of started transaction
|
||||
*/
|
||||
void LMDBAL::iStorage::transactionCommited(LMDBAL::TransactionID txn) {
|
||||
void LMDBAL::StorageCommon::transactionCommited(LMDBAL::TransactionID txn) {
|
||||
UNUSED(txn);}
|
||||
|
||||
/**
|
||||
@ -512,7 +514,7 @@ void LMDBAL::iStorage::transactionCommited(LMDBAL::TransactionID txn) {
|
||||
*
|
||||
* \param[in] txn - ID of started transaction
|
||||
*/
|
||||
void LMDBAL::iStorage::transactionAborted(LMDBAL::TransactionID txn) const {
|
||||
void LMDBAL::StorageCommon::transactionAborted(LMDBAL::TransactionID txn) const {
|
||||
UNUSED(txn);}
|
||||
|
||||
/**
|
||||
@ -521,65 +523,65 @@ void LMDBAL::iStorage::transactionAborted(LMDBAL::TransactionID txn) const {
|
||||
* It's a protected method that is called to optimise drop process
|
||||
* after the transaction is commited. Used just for optimisations.
|
||||
*/
|
||||
void LMDBAL::iStorage::handleDrop() {}
|
||||
void LMDBAL::StorageCommon::handleDrop() {}
|
||||
|
||||
int LMDBAL::iStorage::_mdbOpen(MDB_txn *txn, unsigned int flags) {
|
||||
int LMDBAL::StorageCommon::_mdbOpen(MDB_txn *txn, unsigned int flags) {
|
||||
return mdb_dbi_open(txn, name.c_str(), flags, &dbi);
|
||||
}
|
||||
|
||||
int LMDBAL::iStorage::_mdbPut(MDB_txn* txn, MDB_val& key, MDB_val& data, unsigned int flags) {
|
||||
int LMDBAL::StorageCommon::_mdbPut(MDB_txn* txn, MDB_val& key, MDB_val& data, unsigned int flags) {
|
||||
return mdb_put(txn, dbi, &key, &data, flags);
|
||||
}
|
||||
|
||||
int LMDBAL::iStorage::_mdbGet(MDB_txn* txn, MDB_val& key, MDB_val& data) const {
|
||||
int LMDBAL::StorageCommon::_mdbGet(MDB_txn* txn, MDB_val& key, MDB_val& data) const {
|
||||
return mdb_get(txn, dbi, &key, &data);
|
||||
}
|
||||
|
||||
int LMDBAL::iStorage::_mdbDel(MDB_txn* txn, MDB_val& key) {
|
||||
int LMDBAL::StorageCommon::_mdbDel(MDB_txn* txn, MDB_val& key) {
|
||||
return mdb_del(txn, dbi, &key, NULL);
|
||||
}
|
||||
|
||||
int LMDBAL::iStorage::_mdbDel(MDB_txn* txn, MDB_val& key, MDB_val& data) {
|
||||
int LMDBAL::StorageCommon::_mdbDel(MDB_txn* txn, MDB_val& key, MDB_val& data) {
|
||||
return mdb_del(txn, dbi, &key, &data);
|
||||
}
|
||||
|
||||
int LMDBAL::iStorage::_mdbStat(MDB_txn* txn, MDB_stat& stat) const {
|
||||
int LMDBAL::StorageCommon::_mdbStat(MDB_txn* txn, MDB_stat& stat) const {
|
||||
return mdb_stat(txn, dbi, &stat);
|
||||
}
|
||||
|
||||
int LMDBAL::iStorage::_mdbFlags(MDB_txn* txn, uint32_t& flags) const {
|
||||
int LMDBAL::StorageCommon::_mdbFlags(MDB_txn* txn, uint32_t& flags) const {
|
||||
return mdb_dbi_flags(txn, dbi, &flags);
|
||||
}
|
||||
|
||||
|
||||
int LMDBAL::iStorage::_mdbCursorOpen(MDB_txn* txn, MDB_cursor** cursor) const {
|
||||
int LMDBAL::StorageCommon::_mdbCursorOpen(MDB_txn* txn, MDB_cursor** cursor) const {
|
||||
return mdb_cursor_open(txn, dbi, cursor);
|
||||
}
|
||||
|
||||
void LMDBAL::iStorage::_mdbCursorClose(MDB_cursor *cursor) const {
|
||||
void LMDBAL::StorageCommon::_mdbCursorClose(MDB_cursor *cursor) const {
|
||||
mdb_cursor_close(cursor);
|
||||
}
|
||||
|
||||
int LMDBAL::iStorage::_mdbCursorGet(MDB_cursor* cursor, MDB_val& key, MDB_val& data, MDB_cursor_op operation) const {
|
||||
int LMDBAL::StorageCommon::_mdbCursorGet(MDB_cursor* cursor, MDB_val& key, MDB_val& data, MDB_cursor_op operation) const {
|
||||
return mdb_cursor_get(cursor, &key, &data, operation);
|
||||
}
|
||||
|
||||
int LMDBAL::iStorage::_mdbCursorSet(MDB_cursor* cursor, MDB_val& key) const {
|
||||
int LMDBAL::StorageCommon::_mdbCursorSet(MDB_cursor* cursor, MDB_val& key) const {
|
||||
return mdb_cursor_get(cursor, &key, NULL, MDB_SET);
|
||||
}
|
||||
|
||||
int LMDBAL::iStorage::_mdbCursorDel(MDB_cursor* cursor, unsigned int flags) {
|
||||
int LMDBAL::StorageCommon::_mdbCursorDel(MDB_cursor* cursor, unsigned int flags) {
|
||||
return mdb_cursor_del(cursor, flags);
|
||||
}
|
||||
|
||||
int LMDBAL::iStorage::_mdbCursorPut(MDB_cursor* cursor, MDB_val& key, MDB_val& data, unsigned int flags) {
|
||||
int LMDBAL::StorageCommon::_mdbCursorPut(MDB_cursor* cursor, MDB_val& key, MDB_val& data, unsigned int flags) {
|
||||
return mdb_cursor_put(cursor, &key, &data, flags);
|
||||
}
|
||||
|
||||
int LMDBAL::iStorage::_mdbCursorRenew(MDB_txn* txn, MDB_cursor* cursor) const {
|
||||
int LMDBAL::StorageCommon::_mdbCursorRenew(MDB_txn* txn, MDB_cursor* cursor) const {
|
||||
return mdb_cursor_renew(txn, cursor);
|
||||
}
|
||||
|
||||
MDB_txn* LMDBAL::iStorage::_mdbCursorTxn(MDB_cursor* cursor) const {
|
||||
MDB_txn* LMDBAL::StorageCommon::_mdbCursorTxn(MDB_cursor* cursor) const {
|
||||
return mdb_cursor_txn(cursor);
|
||||
}
|
139
src/storagecommon.h
Normal file
139
src/storagecommon.h
Normal file
@ -0,0 +1,139 @@
|
||||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <lmdb.h>
|
||||
|
||||
#include "base.h"
|
||||
#include "transaction.h"
|
||||
|
||||
namespace LMDBAL {
|
||||
|
||||
class CursorCommon;
|
||||
|
||||
class StorageCommon {
|
||||
friend class Base;
|
||||
friend class CursorCommon;
|
||||
public:
|
||||
protected:
|
||||
StorageCommon(Base* parent, const std::string& name, bool duplicates = false);
|
||||
virtual ~ StorageCommon();
|
||||
|
||||
/**
|
||||
* \brief A private virtual function I need to open each storage in the database
|
||||
*
|
||||
* \param[in] transaction - lmdb transaction to call <a class="el" href="http://www.lmdb.tech/doc/group__mdb.html#gac08cad5b096925642ca359a6d6f0562a">mdb_dbi_open</a>
|
||||
* \returns MDB_SUCCESS if everything went smooth or MDB_<error> -like error code
|
||||
*/
|
||||
virtual int open(MDB_txn * transaction) = 0;
|
||||
virtual void close();
|
||||
virtual void handleDrop();
|
||||
|
||||
bool isDBOpened() const;
|
||||
const std::string& dbName() const;
|
||||
|
||||
void ensureOpened(const std::string& methodName) const;
|
||||
void throwDuplicateOrUnknown(int rc, const std::string& key) const;
|
||||
void throwDuplicateOrUnknown(int rc, TransactionID txn, const std::string& key) const;
|
||||
void throwNotFoundOrUnknown(int rc, const std::string& key) const;
|
||||
void throwNotFoundOrUnknown(int rc, TransactionID txn, const std::string& key) const;
|
||||
void throwUnknown(int rc, TransactionID txn) const;
|
||||
void throwUnknown(int rc) const;
|
||||
void throwUnknown(const std::string& message) const;
|
||||
void throwDuplicate(const std::string& key) const;
|
||||
void throwNotFound(const std::string& key) const;
|
||||
void throwCursorNotReady(const std::string& method) const;
|
||||
|
||||
TransactionID extractTransactionId(const Transaction& txn, const std::string& action = "") const;
|
||||
TransactionID beginReadOnlyTransaction() const;
|
||||
TransactionID beginTransaction() const;
|
||||
void commitTransaction(TransactionID id);
|
||||
void abortTransaction(TransactionID id) const;
|
||||
virtual void transactionStarted(TransactionID txn, bool readOnly) const;
|
||||
virtual void transactionCommited(TransactionID txn);
|
||||
virtual void transactionAborted(TransactionID txn) const;
|
||||
virtual int drop(TransactionID transaction);
|
||||
virtual SizeType count(TransactionID txn) const;
|
||||
|
||||
void openCursorTransaction(MDB_cursor** cursor, bool renew = false) const;
|
||||
void closeCursorTransaction(MDB_cursor* cursor, bool closeCursor = false) const;
|
||||
void attachCursorToTransaction(uint32_t cursorId, MDB_cursor* cursorHandle, CursorCommon* pointer) const;
|
||||
void disconnectCursorFromTransaction(uint32_t cursorId, MDB_cursor* cursorHandle) const;
|
||||
|
||||
int _mdbOpen(MDB_txn* txn, unsigned int flags = 0);
|
||||
|
||||
int _mdbPut(MDB_txn* txn, MDB_val& key, MDB_val& data, unsigned int flags = 0);
|
||||
int _mdbGet(MDB_txn* txn, MDB_val& key, MDB_val& data) const;
|
||||
int _mdbDel(MDB_txn* txn, MDB_val& key);
|
||||
int _mdbDel(MDB_txn* txn, MDB_val& key, MDB_val& data);
|
||||
|
||||
int _mdbStat(MDB_txn* txn, MDB_stat& stat) const;
|
||||
int _mdbFlags(MDB_txn* txn, uint32_t& flags) const;
|
||||
|
||||
int _mdbCursorOpen(MDB_txn* txn, MDB_cursor** cursor) const;
|
||||
void _mdbCursorClose(MDB_cursor* cursor) const;
|
||||
int _mdbCursorGet(MDB_cursor* cursor, MDB_val& key, MDB_val& data, MDB_cursor_op operation) const;
|
||||
int _mdbCursorSet(MDB_cursor* cursor, MDB_val& key) const;
|
||||
int _mdbCursorDel(MDB_cursor* cursor, unsigned int flags = 0);
|
||||
int _mdbCursorPut(MDB_cursor* cursor, MDB_val& key, MDB_val& data, unsigned int flags = 0);
|
||||
|
||||
int _mdbCursorRenew(MDB_txn* txn, MDB_cursor* cursor) const;
|
||||
MDB_txn* _mdbCursorTxn(MDB_cursor* cursor) const;
|
||||
|
||||
public:
|
||||
virtual void drop();
|
||||
virtual int drop(const WriteTransaction& txn);
|
||||
virtual SizeType count() const;
|
||||
virtual SizeType count(const Transaction& txn) const;
|
||||
|
||||
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*/
|
||||
|
||||
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 flagsMethodName = "flags"; /**<\brief member function name, just for exceptions*/
|
||||
|
||||
inline static const std::string addRecordMethodName = "addRecord"; /**<\brief member function name, just for exceptions*/
|
||||
inline static const std::string forceRecordMethodName = "forceRecord"; /**<\brief member function name, just for exceptions*/
|
||||
inline static const std::string changeRecordMethodName = "changeRecord"; /**<\brief member function name, just for exceptions*/
|
||||
inline static const std::string removeRecordMethodName = "removeRecord"; /**<\brief member function name, just for exceptions*/
|
||||
inline static const std::string checkRecordMethodName = "checkRecord"; /**<\brief member function name, just for exceptions*/
|
||||
inline static const std::string getRecordMethodName = "getRecord"; /**<\brief member function name, just for exceptions*/
|
||||
inline static const std::string readAllMethodName = "readAllRecord"; /**<\brief member function name, just for exceptions*/
|
||||
inline static const std::string replaceAllMethodName = "replaceAll"; /**<\brief member function name, just for exceptions*/
|
||||
inline static const std::string addRecordsMethodName = "addRecords"; /**<\brief member function name, just for exceptions*/
|
||||
inline static const std::string openCursorTransactionMethodName = "openCursorTransaction"; /**<\brief member function name, just for exceptions*/
|
||||
|
||||
protected:
|
||||
template <class K, class V>
|
||||
int makeStorage(MDB_txn* transaction, bool duplicates = false);
|
||||
|
||||
template <class T>
|
||||
static std::string toString(const T& value);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#include "storagecommon.hpp"
|
99
src/storagecommon.hpp
Normal file
99
src/storagecommon.hpp
Normal file
@ -0,0 +1,99 @@
|
||||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "storagecommon.h"
|
||||
|
||||
/**
|
||||
* \brief A functiion to actually open <a class="el" href="http://www.lmdb.tech/doc/group__mdb.html#gadbe68a06c448dfb62da16443d251a78b">MDB_dbi</a> storage
|
||||
*
|
||||
* \tparam K type of keys in opening storage
|
||||
*
|
||||
* \param[in] transaction - lmdb transaction to call <a class="el" href="http://www.lmdb.tech/doc/group__mdb.html#gac08cad5b096925642ca359a6d6f0562a">mdb_dbi_open</a>, must be a writable transaction!
|
||||
* \param[in] duplicates - true if key duplicates are allowed (false by default)
|
||||
*
|
||||
* \returns MDB_SUCCESS if everything went smooth or MDB_<error> -like error code
|
||||
*
|
||||
* This is a way to optimise database using MDB_INTEGERKEY flag,
|
||||
* when the key is actually kind of an integer
|
||||
* This infrastructure also allowes us to customize mdb_dbi_open call in the future
|
||||
*/
|
||||
template<class K, class V>
|
||||
inline int LMDBAL::StorageCommon::makeStorage(MDB_txn* transaction, bool duplicates) {
|
||||
unsigned int flags = MDB_CREATE;
|
||||
if constexpr (std::is_integral<K>::value)
|
||||
flags |= MDB_INTEGERKEY;
|
||||
|
||||
if (duplicates) {
|
||||
flags |= MDB_DUPSORT;
|
||||
|
||||
if constexpr (std::is_scalar<V>::value)
|
||||
flags |= MDB_DUPFIXED;
|
||||
|
||||
if constexpr (
|
||||
std::is_same<V, uint32_t>::value ||
|
||||
std::is_same<V, int32_t>::value ||
|
||||
std::is_same<V, uint64_t>::value ||
|
||||
std::is_same<V, int64_t>::value
|
||||
) //for some reason lmdb breaks if it's not one of these types in MDB_DUPFIXED mode
|
||||
flags |= MDB_INTEGERDUP;
|
||||
}
|
||||
|
||||
return _mdbOpen(transaction, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief A method to cast a value (which can be a value or a key) to string.
|
||||
*
|
||||
* This function is mainly used in exceptions, to report which key was duplicated or not found.
|
||||
* You can define your own specializations to this function in case std::to_string doesn't cover your case
|
||||
*
|
||||
* \param[in] value a value that should be converted to string
|
||||
* \returns a string presentation of value
|
||||
*/
|
||||
template<class T>
|
||||
inline std::string LMDBAL::StorageCommon::toString(const T& value) {
|
||||
return std::to_string(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief A method to cast a value (which can be a value or a key) to string.
|
||||
*
|
||||
* QString spectialization
|
||||
*
|
||||
* \param[in] value a value that should be converted to string
|
||||
* \returns a string presentation of value
|
||||
*/
|
||||
template<>
|
||||
inline std::string LMDBAL::StorageCommon::toString(const QString& value) {
|
||||
return value.toStdString();
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief A method to cast a value (which can be a value or a key) to string.
|
||||
*
|
||||
* std::string spectialization
|
||||
*
|
||||
* \param[in] value a value that should be converted to string
|
||||
* \returns a string presentation of value
|
||||
*/
|
||||
template<>
|
||||
inline std::string LMDBAL::StorageCommon::toString(const std::string& value) {
|
||||
return value;
|
||||
}
|
@ -18,6 +18,8 @@
|
||||
|
||||
#include "transaction.h"
|
||||
|
||||
#include "cursorcommon.h"
|
||||
|
||||
/**
|
||||
* \class LMDBAL::Transaction
|
||||
* \brief Public read only transaction
|
||||
@ -131,7 +133,7 @@ void LMDBAL::Transaction::reset() {
|
||||
* \brief Closes attached curors;
|
||||
*/
|
||||
void LMDBAL::Transaction::closeCursors () {
|
||||
for (const std::pair<const uint32_t, iCursor*>& pair : cursors)
|
||||
for (const std::pair<const uint32_t, CursorCommon*>& pair : cursors)
|
||||
pair.second->terminated();
|
||||
}
|
||||
|
||||
|
@ -19,14 +19,14 @@
|
||||
#pragma once
|
||||
|
||||
#include "base.h"
|
||||
#include "icursor.h"
|
||||
|
||||
namespace LMDBAL {
|
||||
class iStorage;
|
||||
class StorageCommon;
|
||||
class CursorCommon;
|
||||
|
||||
class Transaction {
|
||||
friend class Base;
|
||||
friend class iStorage;
|
||||
friend class StorageCommon;
|
||||
public:
|
||||
explicit Transaction();
|
||||
explicit Transaction(Transaction&& other);
|
||||
@ -47,7 +47,7 @@ protected:
|
||||
TransactionID txn; /**<\brief Transaction inner handler*/
|
||||
bool active; /**<\brief Transaction state*/
|
||||
const Base* parent; /**<\brief Pointer to the database this transaction belongs to*/
|
||||
std::map<uint32_t, iCursor*> cursors; /**<\brief a collection of cursors curently opened under this transaction*/
|
||||
std::map<uint32_t, CursorCommon*> cursors; /**<\brief a collection of cursors curently opened under this transaction*/
|
||||
};
|
||||
|
||||
class WriteTransaction : public Transaction {
|
||||
|
@ -182,7 +182,7 @@ TEST_F(CacheTransactionsTest, ConcurentModification) {
|
||||
|
||||
int pid = fork();
|
||||
if (pid == 0) { // I am the child
|
||||
usleep(1);
|
||||
usleep(5);
|
||||
std::cout << "beggining second transaction" << std::endl;
|
||||
LMDBAL::WriteTransaction txn2 = db->beginTransaction(); //<--- this is where the execution should pause
|
||||
//and wait for the first transaction to get finished
|
||||
@ -208,7 +208,7 @@ TEST_F(CacheTransactionsTest, ConcurentModification) {
|
||||
LMDBAL::WriteTransaction txn1 = db->beginTransaction();
|
||||
|
||||
std::cout << "putting parent thread to sleep for 5 ms" << std::endl;
|
||||
usleep(5);
|
||||
usleep(10);
|
||||
|
||||
std::cout << "adding first transaction value" << std::endl;
|
||||
c1->addRecord(5, 812, txn1);
|
||||
@ -235,7 +235,7 @@ TEST_F(CacheTransactionsTest, RAIIResourceFree) {
|
||||
|
||||
int pid = fork();
|
||||
if (pid == 0) { // I am the child
|
||||
usleep(1);
|
||||
usleep(5);
|
||||
std::cout << "beggining child transaction" << std::endl;
|
||||
LMDBAL::WriteTransaction txn2 = db->beginTransaction(); //<--- this is where the execution should pause
|
||||
//and wait for the first transaction to get finished
|
||||
@ -256,7 +256,7 @@ TEST_F(CacheTransactionsTest, RAIIResourceFree) {
|
||||
LMDBAL::WriteTransaction txn1 = db->beginTransaction();
|
||||
|
||||
std::cout << "putting parent thread to sleep for 5 ms" << std::endl;
|
||||
usleep(5);
|
||||
usleep(10);
|
||||
std::cout << "parent thread woke up" << std::endl;
|
||||
|
||||
std::cout << "adding value from parent thread" << std::endl;
|
||||
|
@ -181,7 +181,7 @@ TEST_F(StorageTransactionsTest, ConcurentModification) {
|
||||
|
||||
int pid = fork();
|
||||
if (pid == 0) { // I am the child
|
||||
usleep(1);
|
||||
usleep(5);
|
||||
std::cout << "beggining second transaction" << std::endl;
|
||||
LMDBAL::WriteTransaction txn2 = db->beginTransaction(); //<--- this is where the execution should pause
|
||||
//and wait for the first transaction to get finished
|
||||
@ -207,7 +207,7 @@ TEST_F(StorageTransactionsTest, ConcurentModification) {
|
||||
LMDBAL::WriteTransaction txn1 = db->beginTransaction();
|
||||
|
||||
std::cout << "putting parent thread to sleep for 5 ms" << std::endl;
|
||||
usleep(5);
|
||||
usleep(10);
|
||||
|
||||
std::cout << "adding first transaction value" << std::endl;
|
||||
t1->addRecord(5, 812, txn1);
|
||||
@ -234,7 +234,7 @@ TEST_F(StorageTransactionsTest, RAIIResourceFree) {
|
||||
|
||||
int pid = fork();
|
||||
if (pid == 0) { // I am the child
|
||||
usleep(1);
|
||||
usleep(5);
|
||||
std::cout << "beggining child transaction" << std::endl;
|
||||
LMDBAL::WriteTransaction txn2 = db->beginTransaction(); //<--- this is where the execution should pause
|
||||
//and wait for the first transaction to get finished
|
||||
@ -255,7 +255,7 @@ TEST_F(StorageTransactionsTest, RAIIResourceFree) {
|
||||
LMDBAL::WriteTransaction txn1 = db->beginTransaction();
|
||||
|
||||
std::cout << "putting parent thread to sleep for 5 ms" << std::endl;
|
||||
usleep(5);
|
||||
usleep(10);
|
||||
std::cout << "parent thread woke up" << std::endl;
|
||||
|
||||
std::cout << "adding value from parent thread" << std::endl;
|
||||
|
Loading…
Reference in New Issue
Block a user