diff --git a/CMakeLists.txt b/CMakeLists.txt index 9eb708d..9a400b8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -56,6 +56,7 @@ set(HEADERS serializer_stdstring.hpp serializer_qstring.hpp serializer_qbytearray.hpp + operators.hpp ) if (BUILD_STATIC) diff --git a/cache.h b/cache.h index 00e3042..6f7042c 100644 --- a/cache.h +++ b/cache.h @@ -45,8 +45,12 @@ public: virtual bool checkRecord(const K& key) const override; virtual V getRecord(const K& key) const override; virtual uint32_t count() const override; + + using DataBase::Table::drop; virtual int drop(MDB_txn * transaction) override; virtual std::map readAll() const override; + virtual void replaceAll(const std::map& data) override; + virtual uint32_t addRecords(const std::map& data, bool overwrite = false) override; protected: Mode* mode; diff --git a/cache.hpp b/cache.hpp index 89011ae..503b2f4 100644 --- a/cache.hpp +++ b/cache.hpp @@ -102,11 +102,10 @@ void DataBase::Cache::changeRecord(const K& key, const V& value) { try { Table::changeRecord(key, value); - typename std::map::iterator itr = cache->find(key); - if (itr != cache->end()) { - itr->second = value; + typename std::pair::iterator, bool> res = cache->insert(std::make_pair(key, value)); + if (!res.second) { + res.first->second = value; } else { - cache->insert(std::make_pair(key, value)); handleMode(); } } catch (const NotFound& error) { @@ -188,6 +187,44 @@ std::map DataBase::Cache::readAll() const { return *cache; } +template +void DataBase::Cache::replaceAll(const std::map& data) { + DataBase::Table::replaceAll(data); + *cache = data; +} + +template +uint32_t DataBase::Cache::addRecords(const std::map& data, bool overwrite) { + uint32_t newSize = DataBase::Table::addRecords(data, overwrite); + + Mode& m = *mode; + if (m == Mode::nothing) { + m = Mode::size; + } + std::map& c = *cache; + std::set& a = *abscent; + for (const std::pair& pair : data) { + std::pair::iterator, bool> res = c.insert(pair); + if (!res.second) { + if (overwrite) { + res.first->second = pair.second; + } + } else if (m != Mode::full) { + a.erase(pair.first); + } + } + + if (m != Mode::full) { + *sizeDifference = newSize - c.size(); + if (*sizeDifference == 0) { + *mode = Mode::full; + abscent->clear(); + } + } + + return newSize; +} + template void DataBase::Cache::removeRecord(const K& key) { if (!DataBase::Table::db->opened) { diff --git a/database.h b/database.h index b1903b5..005c1d9 100644 --- a/database.h +++ b/database.h @@ -80,6 +80,7 @@ private: }; #include "exceptions.h" +#include "operators.hpp" template DataBase::Table* DataBase::addTable(const std::string& p_name) { diff --git a/operators.hpp b/operators.hpp new file mode 100644 index 0000000..c4010ff --- /dev/null +++ b/operators.hpp @@ -0,0 +1,209 @@ +// 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 CORE_OPERATORS_HPP +#define CORE_OPERATORS_HPP + +#include +#include +#include +#include +#include + +#include + +template +QDataStream& operator << (QDataStream &out, const std::map& container) { + uint32_t size = container.size(); + out << size; + for (const std::pair& pair : container) { + out << pair; + } + + return out; +} + +template +QDataStream& operator >> (QDataStream &in, std::map& container) { + uint32_t size; + in >> size; + for (uint32_t i = 0; i < size; ++i) { + std::pair pair; + in >> pair; + container.insert(pair); + } + + return in; +} + +template +QDataStream& operator << (QDataStream &out, const std::multimap& container) { + uint32_t size = container.size(); + out << size; + for (const std::pair& pair : container) { + out << pair; + } + + return out; +} + +template +QDataStream& operator >> (QDataStream &in, std::multimap& container) { + uint32_t size; + in >> size; + for (uint32_t i = 0; i < size; ++i) { + std::pair pair; + in >> pair; + container.insert(pair); + } + + return in; +} + +template +QDataStream& operator << (QDataStream &out, const std::pair& pair) { + out << pair.first; + out << pair.second; + + return out; +} + +template +QDataStream& operator >> (QDataStream &in, std::pair& container) { + in >> container.first; + in >> container.second; + + return in; +} + +template +QDataStream& operator << (QDataStream &out, const std::set& container) { + uint32_t size = container.size(); + out << size; + for (const K& value : container) { + out << value; + } + + return out; +} + +template +QDataStream& operator >> (QDataStream &in, std::set& container) { + uint32_t size; + in >> size; + for (uint32_t i = 0; i < size; ++i) { + K value; + in >> value; + container.insert(value); + } + + return in; +} + +template +QDataStream& operator << (QDataStream &out, const std::multiset& container) { + uint32_t size = container.size(); + out << size; + for (const K& value : container) { + out << value; + } + + return out; +} + +template +QDataStream& operator >> (QDataStream &in, std::multiset& container) { + uint32_t size; + in >> size; + for (uint32_t i = 0; i < size; ++i) { + K value; + in >> value; + container.insert(value); + } + + return in; +} + +template +QDataStream& operator << (QDataStream &out, const std::vector& container) { + uint32_t size = container.size(); + out << size; + for (const K& value : container) { + out << value; + } + + return out; +} + +template +QDataStream& operator >> (QDataStream &in, std::vector& container) { + uint32_t size; + in >> size; + container.resize(size); + for (uint32_t i = 0; i < size; ++i) { + in >> container[i]; + } + + return in; +} + +template +QDataStream& operator << (QDataStream &out, const std::deque& container) { + uint32_t size = container.size(); + out << size; + for (const K& value : container) { + out << value; + } + + return out; +} + +template +QDataStream& operator >> (QDataStream &in, std::deque& container) { + uint32_t size; + in >> size; + container.resize(size); + for (uint32_t i = 0; i < size; ++i) { + in >> container[i]; + } + + return in; +} + +template +QDataStream& operator << (QDataStream &out, const std::list& container) { + uint32_t size = container.size(); + out << size; + for (const K& value : container) { + out << value; + } + + return out; +} + +template +QDataStream& operator >> (QDataStream &in, std::list& container) { + uint32_t size; + in >> size; + for (uint32_t i = 0; i < size; ++i) { + typename std::list::iterator itr = container.emplace(container.end()); + in >> *itr; + } + + return in; +} + +#endif //CORE_OPERATORS_HPP diff --git a/table.cpp b/table.cpp index 141df13..80ac7bc 100644 --- a/table.cpp +++ b/table.cpp @@ -35,10 +35,12 @@ void DataBase::_Table::drop() MDB_txn *txn; int rc = mdb_txn_begin(db->environment, NULL, 0, &txn); if (rc) { + mdb_txn_abort(txn); throw Unknown(db->name, mdb_strerror(rc), name); } rc = drop(txn); if (rc) { + mdb_txn_abort(txn); throw Unknown(db->name, mdb_strerror(rc), name); } @@ -61,6 +63,7 @@ uint32_t DataBase::_Table::count() const int rc = mdb_txn_begin(db->environment, NULL, 0, &txn); rc = mdb_stat(txn, dbi, &stat); if (rc) { + mdb_txn_abort(txn); throw Unknown(db->name, mdb_strerror(rc), name); } uint32_t amount = stat.ms_entries; diff --git a/table.h b/table.h index 8803e2e..8279237 100644 --- a/table.h +++ b/table.h @@ -54,6 +54,7 @@ protected: ~Table() override; public: + using DataBase::_Table::drop; virtual void addRecord(const K& key, const V& value); virtual bool forceRecord(const K& key, const V& value); //returns true if there was addition, false if change virtual void changeRecord(const K& key, const V& value); @@ -61,6 +62,8 @@ public: virtual bool checkRecord(const K& key) const; //checks if there is a record with given key virtual V getRecord(const K& key) const; virtual std::map readAll() const; + virtual void replaceAll(const std::map& data); + virtual uint32_t addRecords(const std::map& data, bool overwrite = false); protected: Serializer* keySerializer; diff --git a/table.hpp b/table.hpp index 573c828..49cb38e 100644 --- a/table.hpp +++ b/table.hpp @@ -43,8 +43,12 @@ void DataBase::Table::addRecord(const K& key, const V& value) { MDB_val lmdbKey = keySerializer->setData(key); MDB_val lmdbData = valueSerializer->setData(value); MDB_txn *txn; - mdb_txn_begin(db->environment, NULL, 0, &txn); - int rc; + int rc = mdb_txn_begin(db->environment, NULL, 0, &txn); + if (rc != MDB_SUCCESS) { + mdb_txn_abort(txn); + throw Unknown(db->name, mdb_strerror(rc), name); + } + rc = mdb_put(txn, dbi, &lmdbKey, &lmdbData, MDB_NOOVERWRITE); if (rc != 0) { mdb_txn_abort(txn); @@ -68,8 +72,12 @@ bool DataBase::Table::forceRecord(const K& key, const V& value) { MDB_val lmdbKey = keySerializer->setData(key); MDB_val lmdbData; MDB_txn *txn; - mdb_txn_begin(db->environment, NULL, 0, &txn); - int rc; + int rc = mdb_txn_begin(db->environment, NULL, 0, &txn); + if (rc != MDB_SUCCESS) { + mdb_txn_abort(txn); + throw Unknown(db->name, mdb_strerror(rc), name); + } + rc = mdb_get(txn, dbi, &lmdbKey, &lmdbData); if (rc == 0) { added = false; @@ -100,8 +108,13 @@ void DataBase::Table::changeRecord(const K& key, const V& value) { MDB_val lmdbKey = keySerializer->setData(key); MDB_val lmdbData = valueSerializer->setData(value); MDB_txn *txn; - mdb_txn_begin(db->environment, NULL, 0, &txn); - int rc; + + int rc = mdb_txn_begin(db->environment, NULL, 0, &txn); + if (rc != MDB_SUCCESS) { + mdb_txn_abort(txn); + throw Unknown(db->name, mdb_strerror(rc), name); + } + rc = mdb_put(txn, dbi, &lmdbKey, &lmdbData, 0); if (rc != 0) { mdb_txn_abort(txn); @@ -123,8 +136,12 @@ V DataBase::Table::getRecord(const K& key) const { MDB_val lmdbData; MDB_txn *txn; - int rc; - mdb_txn_begin(db->environment, NULL, MDB_RDONLY, &txn); + int rc = mdb_txn_begin(db->environment, NULL, MDB_RDONLY, &txn); + if (rc != MDB_SUCCESS) { + mdb_txn_abort(txn); + throw Unknown(db->name, mdb_strerror(rc), name); + } + rc = mdb_get(txn, dbi, &lmdbKey, &lmdbData); if (rc) { mdb_txn_abort(txn); @@ -151,8 +168,12 @@ bool DataBase::Table::checkRecord(const K& key) const { MDB_val lmdbData; MDB_txn *txn; - int rc; - mdb_txn_begin(db->environment, NULL, MDB_RDONLY, &txn); + int rc = mdb_txn_begin(db->environment, NULL, MDB_RDONLY, &txn); + if (rc != MDB_SUCCESS) { + mdb_txn_abort(txn); + throw Unknown(db->name, mdb_strerror(rc), name); + } + rc = mdb_get(txn, dbi, &lmdbKey, &lmdbData); mdb_txn_abort(txn); @@ -179,10 +200,12 @@ std::map DataBase::Table::readAll() const { MDB_val lmdbKey, lmdbData; int rc = mdb_txn_begin(db->environment, NULL, MDB_RDONLY, &txn); if (rc != MDB_SUCCESS) { + mdb_txn_abort(txn); throw Unknown(db->name, mdb_strerror(rc), name); } rc = mdb_cursor_open(txn, dbi, &cursor); if (rc != MDB_SUCCESS) { + mdb_txn_abort(txn); throw Unknown(db->name, mdb_strerror(rc), name); } rc = mdb_cursor_get(cursor, &lmdbKey, &lmdbData, MDB_FIRST); @@ -193,6 +216,7 @@ std::map DataBase::Table::readAll() const { rc = mdb_cursor_get(cursor, &lmdbKey, &lmdbData, MDB_NEXT); } + mdb_txn_abort(txn); if (rc != MDB_NOTFOUND) { throw Unknown(db->name, mdb_strerror(rc), name); } @@ -200,6 +224,75 @@ std::map DataBase::Table::readAll() const { return result; } +template +void DataBase::Table::replaceAll(const std::map& data) { + if (!db->opened) { + throw Closed("replaceAll", db->name, name); + } + + MDB_txn *txn; + int rc = mdb_txn_begin(db->environment, NULL, 0, &txn); + if (rc) { + mdb_txn_abort(txn); + throw Unknown(db->name, mdb_strerror(rc), name); + } + rc = drop(txn); + if (rc) { + mdb_txn_abort(txn); + throw Unknown(db->name, mdb_strerror(rc), name); + } + + 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 != 0) { + mdb_txn_abort(txn); + throw Unknown(db->name, mdb_strerror(rc), name); + } + } + mdb_txn_commit(txn); +} + +template +uint32_t DataBase::Table::addRecords(const std::map& data, bool overwrite) { + if (!db->opened) { + throw Closed("addRecords", db->name, name); + } + + MDB_txn *txn; + int rc = mdb_txn_begin(db->environment, NULL, 0, &txn); + if (rc) { + mdb_txn_abort(txn); + throw Unknown(db->name, mdb_strerror(rc), name); + } + + 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, overwrite ? 0 : MDB_NOOVERWRITE); + if (rc != 0) { + mdb_txn_abort(txn); + throw Unknown(db->name, mdb_strerror(rc), name); + } + } + + MDB_stat stat; + rc = mdb_stat(txn, dbi, &stat); + if (rc) { + mdb_txn_abort(txn); + throw Unknown(db->name, mdb_strerror(rc), name); + } + uint32_t amount = stat.ms_entries; + + mdb_txn_commit(txn); + return amount; +} + template void DataBase::Table::removeRecord(const K& key) { if (!db->opened) {