// 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 . #include "database.h" #include "exceptions.h" #include "storage.h" LMDBDataBase::DataBase::DataBase(const QString& p_name, uint16_t mapSize): name(p_name.toStdString()), opened(false), size(mapSize), environment(), tables(), transactions(new Transactions()) {} LMDBDataBase::DataBase::~DataBase() { close(); delete transactions; for (const std::pair& pair : tables) delete pair.second; } void LMDBDataBase::DataBase::close() { if (opened) { for (const std::pair& pair : tables) { StorageBase* table = pair.second; mdb_dbi_close(environment, table->dbi); } mdb_env_close(environment); opened = false; } } void LMDBDataBase::DataBase::open() { if (!opened) { mdb_env_create(&environment); QString path = createDirectory(); mdb_env_set_maxdbs(environment, tables.size()); mdb_env_set_mapsize(environment, size * 1024UL * 1024UL); mdb_env_open(environment, path.toStdString().c_str(), 0, 0664); MDB_txn *txn; mdb_txn_begin(environment, NULL, 0, &txn); for (const std::pair& pair : tables) { StorageBase* table = pair.second; int rc = table->createTable(txn); if (rc) throw Unknown(name, mdb_strerror(rc)); } mdb_txn_commit(txn); opened = true; } } bool LMDBDataBase::DataBase::removeDirectory() { if (opened) throw Opened(name, "remove database directory"); QString path(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)); path += "/" + getName(); QDir cache(path); if (cache.exists()) return cache.removeRecursively(); else return true; } QString LMDBDataBase::DataBase::createDirectory() { if (opened) throw Opened(name, "create database directory"); QString path(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)); path += "/" + getName(); QDir cache(path); if (!cache.exists()) { bool res = cache.mkpath(path); if (!res) throw Directory(path.toStdString()); } return path; } QString LMDBDataBase::DataBase::getName() const { return QString::fromStdString(name);} bool LMDBDataBase::DataBase::ready() const { return opened;} void LMDBDataBase::DataBase::drop() { if (!opened) throw Closed("drop", name); MDB_txn *txn; int rc = mdb_txn_begin(environment, NULL, 0, &txn); if (rc) throw Unknown(name, mdb_strerror(rc)); for (const std::pair& pair : tables) { rc = pair.second->drop(txn); if (rc) throw Unknown(name, mdb_strerror(rc), pair.first); } mdb_txn_commit(txn); } LMDBDataBase::TransactionID LMDBDataBase::DataBase::beginReadOnlyTransaction() const { return beginReadOnlyTransaction(emptyName);} LMDBDataBase::TransactionID LMDBDataBase::DataBase::beginTransaction() const { return beginTransaction(emptyName);} void LMDBDataBase::DataBase::abortTransaction(LMDBDataBase::TransactionID id) const { return abortTransaction(id, emptyName);} void LMDBDataBase::DataBase::commitTransaction(LMDBDataBase::TransactionID id) const { return commitTransaction(id, emptyName);} LMDBDataBase::TransactionID LMDBDataBase::DataBase::beginReadOnlyTransaction(const std::string& storageName) const { if (!opened) throw Closed("beginReadOnlyTransaction", name, storageName); MDB_txn* txn; int rc = mdb_txn_begin(environment, NULL, MDB_RDONLY, &txn); if (rc) { mdb_txn_abort(txn); throw Unknown(name, mdb_strerror(rc), storageName); } transactions->emplace(txn); return txn; } LMDBDataBase::TransactionID LMDBDataBase::DataBase::beginTransaction(const std::string& storageName) const { if (!opened) throw Closed("beginTransaction", name, storageName); MDB_txn* txn; int rc = mdb_txn_begin(environment, NULL, 0, &txn); if (rc) { mdb_txn_abort(txn); throw Unknown(name, mdb_strerror(rc), storageName); } transactions->emplace(txn); return txn; } void LMDBDataBase::DataBase::abortTransaction(LMDBDataBase::TransactionID id, const std::string& storageName) const { if (!opened) throw Closed("abortTransaction", name, storageName); Transactions::iterator itr = transactions->find(id); if (itr == transactions->end()) //TODO may be it's a good idea to make an exception class for this throw Unknown(name, "unable to abort transaction: transaction was not found", storageName); mdb_txn_abort(id); transactions->erase(itr); } void LMDBDataBase::DataBase::commitTransaction(LMDBDataBase::TransactionID id, const std::string& storageName) const { if (!opened) throw Closed("abortTransaction", name, storageName); Transactions::iterator itr = transactions->find(id); if (itr == transactions->end()) //TODO may be it's a good idea to make an exception class for this throw Unknown(name, "unable to commit transaction: transaction was not found", storageName); int rc = mdb_txn_commit(id); transactions->erase(itr); if (rc != MDB_SUCCESS) throw Unknown(name, mdb_strerror(rc), storageName); }