From e2dbea21d10e7c4ed38406b2b3b0acc9f0bf8f39 Mon Sep 17 00:00:00 2001 From: blue Date: Tue, 28 Mar 2023 23:45:35 +0300 Subject: [PATCH] external transaction methods for storage --- packaging/Archlinux/PKGBUILD | 2 +- src/serializer.h | 1 + src/serializer.hpp | 11 +- src/serializer_double.hpp | 5 +- src/serializer_float.hpp | 5 +- src/serializer_int16.hpp | 5 +- src/serializer_int32.hpp | 5 +- src/serializer_int64.hpp | 5 +- src/serializer_int8.hpp | 5 +- src/serializer_qbytearray.hpp | 5 +- src/serializer_qstring.hpp | 4 + src/serializer_stdstring.hpp | 5 +- src/serializer_uint16.hpp | 5 +- src/serializer_uint32.hpp | 5 +- src/serializer_uint64.hpp | 5 +- src/serializer_uint8.hpp | 5 +- src/storage.cpp | 12 +- src/storage.h | 15 +++ src/storage.hpp | 226 ++++++++++++++++++++++++++++------ 19 files changed, 273 insertions(+), 58 deletions(-) diff --git a/packaging/Archlinux/PKGBUILD b/packaging/Archlinux/PKGBUILD index 7dc7ed7..c8451c9 100644 --- a/packaging/Archlinux/PKGBUILD +++ b/packaging/Archlinux/PKGBUILD @@ -10,7 +10,7 @@ depends=( 'lmdb' 'qt5-base') makedepends=('cmake>=3.16') optdepends=() -source=("$pkgname-$pkgver.tar.gz") +source=("$pkgname-$pkgver.tar.gz::https://git.macaw.me/blue/lmdbal/archive/$pkgver.tar.gz") sha256sums=('SKIP') build() { cd "$srcdir/$pkgname" diff --git a/src/serializer.h b/src/serializer.h index 84a368b..734ac51 100644 --- a/src/serializer.h +++ b/src/serializer.h @@ -37,6 +37,7 @@ public: ~Serializer(); T deserialize(const MDB_val& value); + void deserialize(const MDB_val& value, T& result); MDB_val setData(const T& value); MDB_val getData(); void clear(); diff --git a/src/serializer.hpp b/src/serializer.hpp index c0c5172..36bb7d5 100644 --- a/src/serializer.hpp +++ b/src/serializer.hpp @@ -54,14 +54,19 @@ MDB_val LMDBAL::Serializer::setData(const T& value) { template T LMDBAL::Serializer::deserialize(const MDB_val& value) { - clear(); - bytes.setRawData((char*)value.mv_data, value.mv_size); T result; - stream >> result; + deserialize(value, result); return result; } +template +void LMDBAL::Serializer::deserialize(const MDB_val& value, T& result) { + clear(); + bytes.setRawData((char*)value.mv_data, value.mv_size); + stream >> result; +} + template void LMDBAL::Serializer::_setData(const T& value) { stream << value; diff --git a/src/serializer_double.hpp b/src/serializer_double.hpp index 8e9dc5d..4582459 100644 --- a/src/serializer_double.hpp +++ b/src/serializer_double.hpp @@ -29,9 +29,12 @@ public: ~Serializer() {}; double deserialize(const MDB_val& data) { - std::memcpy(&value, data.mv_data, 8); + deserialize(data, value); return value; }; + void deserialize(const MDB_val& data, double& result) { + std::memcpy(&result, data.mv_data, 8); + } MDB_val setData(const double& data) { value = data; return getData(); diff --git a/src/serializer_float.hpp b/src/serializer_float.hpp index d385150..be2c72d 100644 --- a/src/serializer_float.hpp +++ b/src/serializer_float.hpp @@ -29,9 +29,12 @@ public: ~Serializer() {}; float deserialize(const MDB_val& data) { - std::memcpy(&value, data.mv_data, 4); + deserialize(data, value); return value; }; + void deserialize(const MDB_val& data, float& result) { + std::memcpy(&result, data.mv_data, 4); + } MDB_val setData(const float& data) { value = data; return getData(); diff --git a/src/serializer_int16.hpp b/src/serializer_int16.hpp index 05d0091..17e5ea2 100644 --- a/src/serializer_int16.hpp +++ b/src/serializer_int16.hpp @@ -31,9 +31,12 @@ public: ~Serializer() {}; int16_t deserialize(const MDB_val& data) { - std::memcpy(&value, data.mv_data, 2); + deserialize(data, value); return value; }; + void deserialize(const MDB_val& data, int16_t& result) { + std::memcpy(&result, data.mv_data, 2); + } MDB_val setData(const int16_t& data) { value = data; return getData(); diff --git a/src/serializer_int32.hpp b/src/serializer_int32.hpp index be6c6a9..78405c5 100644 --- a/src/serializer_int32.hpp +++ b/src/serializer_int32.hpp @@ -31,9 +31,12 @@ public: ~Serializer() {}; int32_t deserialize(const MDB_val& data) { - std::memcpy(&value, data.mv_data, 4); + deserialize(data, value); return value; }; + void deserialize(const MDB_val& data, int32_t& result) { + std::memcpy(&result, data.mv_data, 4); + } MDB_val setData(const int32_t& data) { value = data; return getData(); diff --git a/src/serializer_int64.hpp b/src/serializer_int64.hpp index 1729557..4992ae1 100644 --- a/src/serializer_int64.hpp +++ b/src/serializer_int64.hpp @@ -31,9 +31,12 @@ public: ~Serializer() {}; int64_t deserialize(const MDB_val& data) { - std::memcpy(&value, data.mv_data, 8); + deserialize(data, value); return value; }; + void deserialize(const MDB_val& data, int64_t& result) { + std::memcpy(&result, data.mv_data, 8); + } MDB_val setData(const int64_t& data) { value = data; return getData(); diff --git a/src/serializer_int8.hpp b/src/serializer_int8.hpp index 37c1588..eafd5c6 100644 --- a/src/serializer_int8.hpp +++ b/src/serializer_int8.hpp @@ -31,9 +31,12 @@ public: ~Serializer() {}; int8_t deserialize(const MDB_val& data) { - std::memcpy(&value, data.mv_data, 1); + deserialize(data, value); return value; }; + void deserialize(const MDB_val& data, int8_t& result) { + std::memcpy(&result, data.mv_data, 1); + } MDB_val setData(const int8_t& data) { value = data; return getData(); diff --git a/src/serializer_qbytearray.hpp b/src/serializer_qbytearray.hpp index 43713ec..6f1c5d1 100644 --- a/src/serializer_qbytearray.hpp +++ b/src/serializer_qbytearray.hpp @@ -31,9 +31,12 @@ public: ~Serializer() {}; QByteArray deserialize(const MDB_val& data) { - value.setRawData((char*)data.mv_data, data.mv_size); + deserialize(data, value); return value; }; + void deserialize(const MDB_val& data, QByteArray& result) { + result.setRawData((char*)data.mv_data, data.mv_size); + } MDB_val setData(const QByteArray& data) { value = data; return getData(); diff --git a/src/serializer_qstring.hpp b/src/serializer_qstring.hpp index af716a7..1a36fcb 100644 --- a/src/serializer_qstring.hpp +++ b/src/serializer_qstring.hpp @@ -35,6 +35,10 @@ public: value = QByteArray((char*)data.mv_data, data.mv_size); return QString::fromUtf8(value); }; + void deserialize(const MDB_val& data, QString& result) { + value = QByteArray((char*)data.mv_data, data.mv_size); + result = QString::fromUtf8(value); + } MDB_val setData(const QString& data) { value = data.toUtf8(); return getData(); diff --git a/src/serializer_stdstring.hpp b/src/serializer_stdstring.hpp index 64a8970..416cfd6 100644 --- a/src/serializer_stdstring.hpp +++ b/src/serializer_stdstring.hpp @@ -31,9 +31,12 @@ public: ~Serializer() {}; std::string deserialize(const MDB_val& data) { - value = std::string((char*)data.mv_data, data.mv_size); + deserialize(data, value); return value; }; + void deserialize(const MDB_val& data, std::string& result) { + result.assign((char*)data.mv_data, data.mv_size); + } MDB_val setData(const std::string& data) { value = data; return getData(); diff --git a/src/serializer_uint16.hpp b/src/serializer_uint16.hpp index b78d948..d648695 100644 --- a/src/serializer_uint16.hpp +++ b/src/serializer_uint16.hpp @@ -31,9 +31,12 @@ public: ~Serializer() {}; uint16_t deserialize(const MDB_val& data) { - std::memcpy(&value, data.mv_data, 2); + deserialize(data, value); return value; }; + void deserialize(const MDB_val& data, uint16_t& result) { + std::memcpy(&result, data.mv_data, 2); + } MDB_val setData(const uint16_t& data) { value = data; return getData(); diff --git a/src/serializer_uint32.hpp b/src/serializer_uint32.hpp index f41a845..211f151 100644 --- a/src/serializer_uint32.hpp +++ b/src/serializer_uint32.hpp @@ -31,9 +31,12 @@ public: ~Serializer() {}; uint32_t deserialize(const MDB_val& data) { - std::memcpy(&value, data.mv_data, 4); + deserialize(data, value); return value; }; + void deserialize(const MDB_val& data, uint32_t& result) { + std::memcpy(&result, data.mv_data, 4); + } MDB_val setData(const uint32_t& data) { value = data; return getData(); diff --git a/src/serializer_uint64.hpp b/src/serializer_uint64.hpp index f59b91e..250ec03 100644 --- a/src/serializer_uint64.hpp +++ b/src/serializer_uint64.hpp @@ -31,9 +31,12 @@ public: ~Serializer() {}; uint64_t deserialize(const MDB_val& data) { - std::memcpy(&value, data.mv_data, 8); + deserialize(data, value); return value; }; + void deserialize(const MDB_val& data, uint64_t& result) { + std::memcpy(&result, data.mv_data, 8); + } MDB_val setData(const uint64_t& data) { value = data; return getData(); diff --git a/src/serializer_uint8.hpp b/src/serializer_uint8.hpp index ba19ee3..8f4cd04 100644 --- a/src/serializer_uint8.hpp +++ b/src/serializer_uint8.hpp @@ -31,9 +31,12 @@ public: ~Serializer() {}; uint8_t deserialize(const MDB_val& data) { - std::memcpy(&value, data.mv_data, 1); + deserialzie(data, value); return value; }; + void deserialzie(const MDB_val& data, uint8_t& result) { + std::memcpy(&result, data.mv_data, 1); + } MDB_val setData(const uint8_t& data) { value = data; return getData(); diff --git a/src/storage.cpp b/src/storage.cpp index fd4ee4d..1823f44 100644 --- a/src/storage.cpp +++ b/src/storage.cpp @@ -82,14 +82,22 @@ uint32_t LMDBAL::iStorage::count() const { void LMDBAL::iStorage::throwDuplicateOrUnknown(int rc, TransactionID txn, const std::string& key) const { abortTransaction(txn); + throwDuplicateOrUnknown(rc, key); +} + +void LMDBAL::iStorage::throwNotFoundOrUnknown(int rc, LMDBAL::TransactionID txn, const std::string& key) const { + abortTransaction(txn); + throwNotFoundOrUnknown(rc, key); +} + +void LMDBAL::iStorage::throwDuplicateOrUnknown(int rc, const std::string& key) const { if (rc == MDB_KEYEXIST) throwDuplicate(key); else throwUnknown(rc); } -void LMDBAL::iStorage::throwNotFoundOrUnknown(int rc, LMDBAL::TransactionID txn, const std::string& key) const { - abortTransaction(txn); +void LMDBAL::iStorage::throwNotFoundOrUnknown(int rc, const std::string& key) const { if (rc == MDB_NOTFOUND) throwNotFound(key); else diff --git a/src/storage.h b/src/storage.h index 0d53fc7..ba7d022 100644 --- a/src/storage.h +++ b/src/storage.h @@ -37,7 +37,9 @@ protected: 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; @@ -89,14 +91,27 @@ protected: public: using iStorage::drop; virtual void addRecord(const K& key, const V& value); + virtual void addRecord(const K& key, const V& value, TransactionID txn); virtual bool forceRecord(const K& key, const V& value); //returns true if there was addition, false if change + virtual bool forceRecord(const K& key, const V& value, TransactionID txn); virtual void changeRecord(const K& key, const V& value); + virtual void changeRecord(const K& key, const V& value, TransactionID txn); virtual void removeRecord(const K& key); + virtual void removeRecord(const K& key, TransactionID txn); virtual bool checkRecord(const K& key) const; //checks if there is a record with given key + virtual bool checkRecord(const K& key, TransactionID txn) const; + virtual void getRecord(const K& key, V& value) const; + virtual void getRecord(const K& key, V& value, TransactionID txn) const; virtual V getRecord(const K& key) const; + virtual V getRecord(const K& key, TransactionID txn) const; virtual std::map readAll() const; + virtual std::map readAll(TransactionID txn) const; + virtual void readAll(std::map& result) const; + virtual void readAll(std::map& result, TransactionID txn) const; virtual void replaceAll(const std::map& data); + virtual void replaceAll(const std::map& data, TransactionID txn); virtual uint32_t addRecords(const std::map& data, bool overwrite = false); + virtual uint32_t addRecords(const std::map& data, TransactionID txn, bool overwrite = false); protected: Serializer* keySerializer; diff --git a/src/storage.hpp b/src/storage.hpp index 816dd2f..8e7da60 100644 --- a/src/storage.hpp +++ b/src/storage.hpp @@ -38,24 +38,51 @@ LMDBAL::Storage::~Storage() { template void LMDBAL::Storage::addRecord(const K& key, const V& value) { ensureOpened(addRecordMethodName); - TransactionID txn = beginTransaction(); + try { + addRecord(key, value, txn); + } catch (...) { + abortTransaction(txn); + throw; + } + + commitTransaction(txn); +} + +template +void LMDBAL::Storage::addRecord(const K& key, const V& value, TransactionID txn) { + ensureOpened(addRecordMethodName); + MDB_val lmdbKey = keySerializer->setData(key); MDB_val lmdbData = valueSerializer->setData(value); int rc = mdb_put(txn, dbi, &lmdbKey, &lmdbData, MDB_NOOVERWRITE); if (rc != MDB_SUCCESS) - throwDuplicateOrUnknown(rc, txn, toString(key)); - - commitTransaction(txn); + throwDuplicateOrUnknown(rc, toString(key)); } template bool LMDBAL::Storage::forceRecord(const K& key, const V& value) { ensureOpened(forceRecordMethodName); - bool added; TransactionID txn = beginTransaction(); + bool added; + try { + added = forceRecord(key, value, txn); + } catch (...) { + abortTransaction(txn); + throw; + } + + commitTransaction(txn); + return added; +} + +template +bool LMDBAL::Storage::forceRecord(const K& key, const V& value, TransactionID txn) { + ensureOpened(forceRecordMethodName); + + bool added; MDB_val lmdbKey = keySerializer->setData(key); MDB_val lmdbData; @@ -69,15 +96,13 @@ bool LMDBAL::Storage::forceRecord(const K& key, const V& value) { break; default: added = false; - throwUnknown(rc, txn); + throwUnknown(rc); } lmdbData = valueSerializer->setData(value); rc = mdb_put(txn, dbi, &lmdbKey, &lmdbData, 0); if (rc != MDB_SUCCESS) - throwUnknown(rc, txn); - - commitTransaction(txn); + throwUnknown(rc); return added; } @@ -87,32 +112,73 @@ void LMDBAL::Storage::changeRecord(const K& key, const V& value) { ensureOpened(changeRecordMethodName); TransactionID txn = beginTransaction(); + try { + changeRecord(key, value, txn); + } catch (...) { + abortTransaction(txn); + throw; + } + + commitTransaction(txn); +} + +template +void LMDBAL::Storage::changeRecord(const K& key, const V& value, TransactionID txn) { + ensureOpened(changeRecordMethodName); + MDB_val lmdbKey = keySerializer->setData(key); MDB_val lmdbData = valueSerializer->setData(value); int rc = mdb_put(txn, dbi, &lmdbKey, &lmdbData, 0); if (rc != MDB_SUCCESS) - throwUnknown(rc, txn); - - commitTransaction(txn); + throwUnknown(rc); } template V LMDBAL::Storage::getRecord(const K& key) const { ensureOpened(getRecordMethodName); + V value; + getRecord(key, value); + return value; +} + +template +void LMDBAL::Storage::getRecord(const K& key, V& value) const { + ensureOpened(getRecordMethodName); + TransactionID txn = beginReadOnlyTransaction(); + try { + getRecord(key, value, txn); + } catch (...) { + abortTransaction(txn); + throw; + } + + abortTransaction(txn); +} + +template +V LMDBAL::Storage::getRecord(const K& key, TransactionID txn) const { + ensureOpened(getRecordMethodName); + + V value; + getRecord(key, value, txn); + return value; +} + +template +void LMDBAL::Storage::getRecord(const K& key, V& value, TransactionID txn) const { + ensureOpened(getRecordMethodName); + MDB_val lmdbKey = keySerializer->setData(key); MDB_val lmdbData; int rc = mdb_get(txn, dbi, &lmdbKey, &lmdbData); if (rc != MDB_SUCCESS) - throwNotFoundOrUnknown(rc, txn, toString(key)); + throwNotFoundOrUnknown(rc, toString(key)); - V value = valueSerializer->deserialize(lmdbData); - abortTransaction(txn); - - return value; + valueSerializer->deserialize(lmdbData, value); } template @@ -120,12 +186,26 @@ bool LMDBAL::Storage::checkRecord(const K& key) const { ensureOpened(checkRecordMethodName); TransactionID txn = beginReadOnlyTransaction(); + bool result; + try { + result = checkRecord(key, txn); + } catch (...) { + abortTransaction(txn); + throw; + } + + abortTransaction(txn); + return result; +} + +template +bool LMDBAL::Storage::checkRecord(const K& key, TransactionID txn) const { + ensureOpened(checkRecordMethodName); + MDB_val lmdbKey = keySerializer->setData(key); MDB_val lmdbData; int rc = mdb_get(txn, dbi, &lmdbKey, &lmdbData); - abortTransaction(txn); - if (rc == MDB_SUCCESS) return true; @@ -139,28 +219,57 @@ template std::map LMDBAL::Storage::readAll() const { ensureOpened(readAllMethodName); - TransactionID txn = beginReadOnlyTransaction(); std::map result; + readAll(result); + return result; +} + +template +void LMDBAL::Storage::readAll(std::map& result) const { + ensureOpened(readAllMethodName); + + TransactionID txn = beginReadOnlyTransaction(); + try { + readAll(result, txn); + } catch (...) { + abortTransaction(txn); + throw; + } + + abortTransaction(txn); +} + +template +std::map LMDBAL::Storage::readAll(TransactionID txn) const { + ensureOpened(readAllMethodName); + + std::map result; + readAll(result, txn); + return result; +} + +template +void LMDBAL::Storage::readAll(std::map& result, TransactionID txn) const { + ensureOpened(readAllMethodName); + MDB_cursor* cursor; MDB_val lmdbKey, lmdbData; int rc = mdb_cursor_open(txn, dbi, &cursor); if (rc != MDB_SUCCESS) - throwUnknown(rc, txn); + throwUnknown(rc); rc = mdb_cursor_get(cursor, &lmdbKey, &lmdbData, MDB_FIRST); while (rc == MDB_SUCCESS) { - K key = keySerializer->deserialize(lmdbKey); - V value = valueSerializer->deserialize(lmdbData); - result.insert(std::make_pair(key, value)); + K key; + keySerializer->deserialize(lmdbKey, key); + V& value = result[key]; + valueSerializer->deserialize(lmdbData, value); rc = mdb_cursor_get(cursor, &lmdbKey, &lmdbData, MDB_NEXT); } - abortTransaction(txn); if (rc != MDB_NOTFOUND) throwUnknown(rc); - - return result; } template @@ -168,9 +277,23 @@ void LMDBAL::Storage::replaceAll(const std::map& data) { ensureOpened(replaceAllMethodName); TransactionID txn = beginTransaction(); + try { + replaceAll(data, txn); + } catch (...) { + abortTransaction(txn); + throw; + } + + commitTransaction(txn); +} + +template +void LMDBAL::Storage::replaceAll(const std::map& data, TransactionID txn) { + ensureOpened(replaceAllMethodName); + int rc = drop(txn); if (rc != MDB_SUCCESS) - throwUnknown(rc, txn); + throwUnknown(rc); MDB_val lmdbKey, lmdbData; for (const std::pair& pair : data) { @@ -179,9 +302,8 @@ void LMDBAL::Storage::replaceAll(const std::map& data) { rc = mdb_put(txn, dbi, &lmdbKey, &lmdbData, MDB_NOOVERWRITE); if (rc != MDB_SUCCESS) - throwUnknown(rc, txn); + throwUnknown(rc); } - commitTransaction(txn); } template @@ -189,27 +311,39 @@ uint32_t LMDBAL::Storage::addRecords(const std::map& data, bool over ensureOpened(addRecordsMethodName); TransactionID txn = beginTransaction(); + uint32_t amount; + try { + amount = addRecords(data, txn, overwrite); + } catch (...) { + abortTransaction(txn); + throw; + } + + commitTransaction(txn); + return amount; +} + +template +uint32_t LMDBAL::Storage::addRecords(const std::map& data, TransactionID txn, bool overwrite) { + ensureOpened(addRecordsMethodName); + MDB_val lmdbKey, lmdbData; int rc; - for (const std::pair& pair : data) { lmdbKey = keySerializer->setData(pair.first); lmdbData = valueSerializer->setData(pair.second); rc = mdb_put(txn, dbi, &lmdbKey, &lmdbData, overwrite ? 0 : MDB_NOOVERWRITE); if (rc != MDB_SUCCESS) - throwUnknown(rc, txn); + throwUnknown(rc); } MDB_stat stat; rc = mdb_stat(txn, dbi, &stat); if (rc != MDB_SUCCESS) - throwUnknown(rc, txn); + throwUnknown(rc); - uint32_t amount = stat.ms_entries; - commitTransaction(txn); - - return amount; + return stat.ms_entries; } template @@ -217,12 +351,24 @@ void LMDBAL::Storage::removeRecord(const K& key) { ensureOpened(removeRecordMethodName); TransactionID txn = beginTransaction(); + try { + removeRecord(key, txn); + } catch (...) { + abortTransaction(txn); + throw; + } + + commitTransaction(txn); +} + +template +void LMDBAL::Storage::removeRecord(const K& key, TransactionID txn) { + ensureOpened(removeRecordMethodName); + MDB_val lmdbKey = keySerializer->setData(key); int rc = mdb_del(txn, dbi, &lmdbKey, NULL); if (rc != MDB_SUCCESS) - throwNotFoundOrUnknown(rc, txn, toString(key)); - - commitTransaction(txn); + throwNotFoundOrUnknown(rc, toString(key)); } template