/* * 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(); 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); } template bool LMDBAL::Storage::forceRecord(const K& key, const V& value) { ensureOpened(forceRecordMethodName); bool added; TransactionID txn = beginTransaction(); 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, txn); } lmdbData = valueSerializer->setData(value); rc = mdb_put(txn, dbi, &lmdbKey, &lmdbData, 0); if (rc != MDB_SUCCESS) throwUnknown(rc, txn); commitTransaction(txn); return added; } template void LMDBAL::Storage::changeRecord(const K& key, const V& value) { ensureOpened(changeRecordMethodName); TransactionID txn = beginTransaction(); 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); } template V LMDBAL::Storage::getRecord(const K& key) const { ensureOpened(getRecordMethodName); TransactionID txn = beginReadOnlyTransaction(); 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)); V value = valueSerializer->deserialize(lmdbData); abortTransaction(txn); return value; } template bool LMDBAL::Storage::checkRecord(const K& key) const { ensureOpened(checkRecordMethodName); TransactionID txn = beginReadOnlyTransaction(); 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; if (rc != MDB_NOTFOUND) throwUnknown(rc); return false; } template std::map LMDBAL::Storage::readAll() const { ensureOpened(readAllMethodName); TransactionID txn = beginReadOnlyTransaction(); std::map result; MDB_cursor* cursor; MDB_val lmdbKey, lmdbData; int rc = mdb_cursor_open(txn, dbi, &cursor); if (rc != MDB_SUCCESS) throwUnknown(rc, txn); 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)); rc = mdb_cursor_get(cursor, &lmdbKey, &lmdbData, MDB_NEXT); } abortTransaction(txn); if (rc != MDB_NOTFOUND) throwUnknown(rc); return result; } template void LMDBAL::Storage::replaceAll(const std::map& data) { ensureOpened(replaceAllMethodName); TransactionID txn = beginTransaction(); int rc = drop(txn); if (rc != MDB_SUCCESS) throwUnknown(rc, txn); 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, txn); } commitTransaction(txn); } template uint32_t LMDBAL::Storage::addRecords(const std::map& data, bool overwrite) { ensureOpened(addRecordsMethodName); TransactionID txn = beginTransaction(); 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); } MDB_stat stat; rc = mdb_stat(txn, dbi, &stat); if (rc != MDB_SUCCESS) throwUnknown(rc, txn); uint32_t amount = stat.ms_entries; commitTransaction(txn); return amount; } template void LMDBAL::Storage::removeRecord(const K& key) { ensureOpened(removeRecordMethodName); TransactionID txn = beginTransaction(); 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); } template int LMDBAL::Storage::createTable(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