// Squawk messenger. // Copyright (C) 2019 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 LMDBDATABASE_STORAGE_HPP #define LMDBDATABASE_STORAGE_HPP #include "storage.h" #include "exceptions.h" template LMDBDataBase::Storage::Storage(const std::string& p_name, DataBase* parent): StorageBase(p_name, parent), keySerializer(new Serializer()), valueSerializer(new Serializer()) {} template LMDBDataBase::Storage::~Storage() { delete valueSerializer; delete keySerializer; } template void LMDBDataBase::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 LMDBDataBase::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 LMDBDataBase::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 LMDBDataBase::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 LMDBDataBase::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 LMDBDataBase::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 LMDBDataBase::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 LMDBDataBase::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 LMDBDataBase::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 LMDBDataBase::Storage::createTable(MDB_txn* transaction) { return makeTable(transaction); } template inline int LMDBDataBase::StorageBase::makeTable(MDB_txn* transaction) { return mdb_dbi_open(transaction, name.c_str(), MDB_CREATE, &dbi); } template<> inline int LMDBDataBase::StorageBase::makeTable(MDB_txn* transaction) { return mdb_dbi_open(transaction, name.c_str(), MDB_CREATE | MDB_INTEGERKEY, &dbi); } template<> inline int LMDBDataBase::StorageBase::makeTable(MDB_txn* transaction) { return mdb_dbi_open(transaction, name.c_str(), MDB_CREATE | MDB_INTEGERKEY, &dbi); } template<> inline int LMDBDataBase::StorageBase::makeTable(MDB_txn* transaction) { return mdb_dbi_open(transaction, name.c_str(), MDB_CREATE | MDB_INTEGERKEY, &dbi); } template<> inline int LMDBDataBase::StorageBase::makeTable(MDB_txn* transaction) { return mdb_dbi_open(transaction, name.c_str(), MDB_CREATE | MDB_INTEGERKEY, &dbi); } template<> inline int LMDBDataBase::StorageBase::makeTable(MDB_txn* transaction) { return mdb_dbi_open(transaction, name.c_str(), MDB_CREATE | MDB_INTEGERKEY, &dbi); } template<> inline int LMDBDataBase::StorageBase::makeTable(MDB_txn* transaction) { return mdb_dbi_open(transaction, name.c_str(), MDB_CREATE | MDB_INTEGERKEY, &dbi); } template<> inline int LMDBDataBase::StorageBase::makeTable(MDB_txn* transaction) { return mdb_dbi_open(transaction, name.c_str(), MDB_CREATE | MDB_INTEGERKEY, &dbi); } template<> inline int LMDBDataBase::StorageBase::makeTable(MDB_txn* transaction) { return mdb_dbi_open(transaction, name.c_str(), MDB_CREATE | MDB_INTEGERKEY, &dbi); } template inline std::string LMDBDataBase::StorageBase::toString(const T& value) { return std::to_string(value); } template<> inline std::string LMDBDataBase::StorageBase::toString(const QString& value) { return value.toStdString(); } template<> inline std::string LMDBDataBase::StorageBase::toString(const std::string& value) { return value; } #endif //LMDBDATABASE_STORAGE_HPP