/* * LMDB Abstraction Layer. * Copyright (C) 2023 Yury Gubich * * 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 . */ #ifndef LMDBAL_STORAGE_HPP #define LMDBAL_STORAGE_HPP #include "storage.h" #include "exceptions.h" template LMDBAL::Storage::Storage(const std::string& p_name, Base* parent): iStorage(p_name, parent), keySerializer(new Serializer()), valueSerializer(new Serializer()) {} template LMDBAL::Storage::~Storage() { delete valueSerializer; delete keySerializer; } template void LMDBAL::Storage::addRecord(const K& key, const V& value) { ensureOpened(addRecordMethodName); TransactionID txn = beginTransaction(); try { Storage::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, toString(key)); } template bool LMDBAL::Storage::forceRecord(const K& key, const V& value) { ensureOpened(forceRecordMethodName); TransactionID txn = beginTransaction(); bool added; try { added = Storage::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; int rc = mdb_get(txn, dbi, &lmdbKey, &lmdbData); switch (rc) { case MDB_SUCCESS: added = false; break; case MDB_NOTFOUND: added = true; break; default: added = false; throwUnknown(rc); } lmdbData = valueSerializer->setData(value); rc = mdb_put(txn, dbi, &lmdbKey, &lmdbData, 0); if (rc != MDB_SUCCESS) throwUnknown(rc); return added; } template void LMDBAL::Storage::changeRecord(const K& key, const V& value) { ensureOpened(changeRecordMethodName); TransactionID txn = beginTransaction(); try { Storage::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); } template V LMDBAL::Storage::getRecord(const K& key) const { ensureOpened(getRecordMethodName); V value; Storage::getRecord(key, value); return value; } template void LMDBAL::Storage::getRecord(const K& key, V& value) const { ensureOpened(getRecordMethodName); TransactionID txn = beginReadOnlyTransaction(); try { Storage::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; Storage::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, toString(key)); valueSerializer->deserialize(lmdbData, value); } template bool LMDBAL::Storage::checkRecord(const K& key) const { ensureOpened(checkRecordMethodName); TransactionID txn = beginReadOnlyTransaction(); bool result; try { result = Storage::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); if (rc == MDB_SUCCESS) return true; if (rc != MDB_NOTFOUND) throwUnknown(rc); return false; } template std::map LMDBAL::Storage::readAll() const { ensureOpened(readAllMethodName); std::map result; Storage::readAll(result); return result; } template void LMDBAL::Storage::readAll(std::map& result) const { ensureOpened(readAllMethodName); TransactionID txn = beginReadOnlyTransaction(); try { Storage::readAll(result, txn); } catch (...) { abortTransaction(txn); throw; } abortTransaction(txn); } template std::map LMDBAL::Storage::readAll(TransactionID txn) const { ensureOpened(readAllMethodName); std::map result; Storage::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); rc = mdb_cursor_get(cursor, &lmdbKey, &lmdbData, MDB_FIRST); while (rc == MDB_SUCCESS) { K key; keySerializer->deserialize(lmdbKey, key); V& value = result[key]; valueSerializer->deserialize(lmdbData, value); rc = mdb_cursor_get(cursor, &lmdbKey, &lmdbData, MDB_NEXT); } if (rc != MDB_NOTFOUND) throwUnknown(rc); } template void LMDBAL::Storage::replaceAll(const std::map& data) { ensureOpened(replaceAllMethodName); TransactionID txn = beginTransaction(); try { Storage::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); MDB_val lmdbKey, lmdbData; for (const std::pair& pair : data) { lmdbKey = keySerializer->setData(pair.first); lmdbData = valueSerializer->setData(pair.second); rc = mdb_put(txn, dbi, &lmdbKey, &lmdbData, MDB_NOOVERWRITE); if (rc != MDB_SUCCESS) throwUnknown(rc); } } template uint32_t LMDBAL::Storage::addRecords(const std::map& data, bool overwrite) { ensureOpened(addRecordsMethodName); TransactionID txn = beginTransaction(); uint32_t amount; try { amount = Storage::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_KEYEXIST) throwDuplicate(toString(pair.first)); if (rc != MDB_SUCCESS) throwUnknown(rc); } MDB_stat stat; rc = mdb_stat(txn, dbi, &stat); if (rc != MDB_SUCCESS) throwUnknown(rc); return stat.ms_entries; } template void LMDBAL::Storage::removeRecord(const K& key) { ensureOpened(removeRecordMethodName); TransactionID txn = beginTransaction(); try { Storage::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, toString(key)); } template int LMDBAL::Storage::createStorage(MDB_txn* transaction) { return makeTable(transaction); } template inline int LMDBAL::iStorage::makeTable(MDB_txn* transaction) { return mdb_dbi_open(transaction, name.c_str(), MDB_CREATE, &dbi); } template<> inline int LMDBAL::iStorage::makeTable(MDB_txn* transaction) { return mdb_dbi_open(transaction, name.c_str(), MDB_CREATE | MDB_INTEGERKEY, &dbi); } template<> inline int LMDBAL::iStorage::makeTable(MDB_txn* transaction) { return mdb_dbi_open(transaction, name.c_str(), MDB_CREATE | MDB_INTEGERKEY, &dbi); } template<> inline int LMDBAL::iStorage::makeTable(MDB_txn* transaction) { return mdb_dbi_open(transaction, name.c_str(), MDB_CREATE | MDB_INTEGERKEY, &dbi); } template<> inline int LMDBAL::iStorage::makeTable(MDB_txn* transaction) { return mdb_dbi_open(transaction, name.c_str(), MDB_CREATE | MDB_INTEGERKEY, &dbi); } template<> inline int LMDBAL::iStorage::makeTable(MDB_txn* transaction) { return mdb_dbi_open(transaction, name.c_str(), MDB_CREATE | MDB_INTEGERKEY, &dbi); } template<> inline int LMDBAL::iStorage::makeTable(MDB_txn* transaction) { return mdb_dbi_open(transaction, name.c_str(), MDB_CREATE | MDB_INTEGERKEY, &dbi); } template<> inline int LMDBAL::iStorage::makeTable(MDB_txn* transaction) { return mdb_dbi_open(transaction, name.c_str(), MDB_CREATE | MDB_INTEGERKEY, &dbi); } template<> inline int LMDBAL::iStorage::makeTable(MDB_txn* transaction) { return mdb_dbi_open(transaction, name.c_str(), MDB_CREATE | MDB_INTEGERKEY, &dbi); } template inline std::string LMDBAL::iStorage::toString(const T& value) { return std::to_string(value); } template<> inline std::string LMDBAL::iStorage::toString(const QString& value) { return value.toStdString(); } template<> inline std::string LMDBAL::iStorage::toString(const std::string& value) { return value; } #endif //LMDBAL_STORAGE_HPP