diff --git a/database.cpp b/database.cpp index 0b10e11..2c2791a 100644 --- a/database.cpp +++ b/database.cpp @@ -18,7 +18,7 @@ #include "exceptions.h" #include "table.h" -Core::DataBase::DataBase(const QString& p_name, uint16_t mapSize): +DataBase::DataBase(const QString& p_name, uint16_t mapSize): name(p_name.toStdString()), opened(false), size(mapSize), @@ -27,7 +27,7 @@ Core::DataBase::DataBase(const QString& p_name, uint16_t mapSize): { } -Core::DataBase::~DataBase() +DataBase::~DataBase() { close(); for (const std::pair& pair : tables) { @@ -35,7 +35,7 @@ Core::DataBase::~DataBase() } } -void Core::DataBase::close() +void DataBase::close() { if (opened) { for (const std::pair& pair : tables) { @@ -47,7 +47,7 @@ void Core::DataBase::close() } } -void Core::DataBase::open() +void DataBase::open() { if (!opened) { mdb_env_create(&environment); @@ -71,7 +71,7 @@ void Core::DataBase::open() for (const std::pair& pair : tables) { _Table* table = pair.second; - int rc = mdb_dbi_open(txn, pair.first.c_str(), MDB_CREATE, &table->dbi); + int rc = table->createTable(txn); if (rc) { throw Unknown(name, mdb_strerror(rc)); } @@ -82,18 +82,9 @@ void Core::DataBase::open() } } -QString Core::DataBase::getName() const +QString DataBase::getName() const { return QString::fromStdString(name); } -template -Core::DataBase::Table* Core::DataBase::addTable(const QString& p_name) { - std::string nm = p_name.toStdString(); - if (opened) { - throw Core::DataBase::Opened(name, nm); - } - Core::DataBase::Table* table = new Core::DataBase::Table(nm, this); - tables.insert(std::make_pair(nm, table)); - return table; -} + diff --git a/database.h b/database.h index 685e3fe..815ae61 100644 --- a/database.h +++ b/database.h @@ -25,8 +25,6 @@ #include #include -namespace Core { - class DataBase { class _Table; @@ -63,6 +61,17 @@ private: std::map tables; }; +#include "exceptions.h" + +template +DataBase::Table* DataBase::addTable(const QString& p_name) { + std::string nm = p_name.toStdString(); + if (opened) { + throw Opened(name, nm); + } + DataBase::Table* table = new DataBase::Table(nm, this); + tables.insert(std::make_pair(nm, (_Table*)table)); + return table; } #endif // CORE_DATABASE_H diff --git a/exceptions.cpp b/exceptions.cpp index 0c0c7d5..ac607aa 100644 --- a/exceptions.cpp +++ b/exceptions.cpp @@ -16,14 +16,14 @@ #include "exceptions.h" -Core::DataBase::Directory::Directory(const std::string& p_path): +DataBase::Directory::Directory(const std::string& p_path): Exception(), path(p_path) {} -std::string Core::DataBase::Directory::getMessage() const { +std::string DataBase::Directory::getMessage() const { return "Can't create directory for database at " + path;} -Core::DataBase::Closed::Closed( +DataBase::Closed::Closed( const std::string& p_operation, const std::string& p_dbName, const std::string& p_tableName @@ -33,26 +33,26 @@ Core::DataBase::Closed::Closed( dbName(p_dbName), tableName(p_tableName) {} -std::string Core::DataBase::Closed::getMessage() const { +std::string DataBase::Closed::getMessage() const { return "An attempt to perform operation " + operation + " on closed database " + dbName + " on table " + tableName; } -Core::DataBase::Opened::Opened(const std::string& p_dbName, const std::string& p_tableName): +DataBase::Opened::Opened(const std::string& p_dbName, const std::string& p_tableName): Exception(), dbName(p_dbName), tableName(p_tableName) {} -std::string Core::DataBase::Opened::getMessage() const { +std::string DataBase::Opened::getMessage() const { return "An attempt to add table " + tableName + " to the database " + dbName + " but it's can't be done because the DataBase is already opened." + " Please add all tables before opening DataBase."; } -Core::DataBase::NotFound::NotFound( +DataBase::NotFound::NotFound( const std::string& p_key, const std::string& p_dbName, const std::string& p_tableName @@ -62,12 +62,12 @@ Core::DataBase::NotFound::NotFound( dbName(p_dbName), tableName(p_tableName) {} -std::string Core::DataBase::NotFound::getMessage() const { +std::string DataBase::NotFound::getMessage() const { return "Element for id " + key + " wasn't found " + " in database " + dbName + " in table " + tableName;} -Core::DataBase::Exist::Exist( +DataBase::Exist::Exist( const std::string& p_key, const std::string& p_dbName, const std::string& p_tableName @@ -77,14 +77,14 @@ Core::DataBase::Exist::Exist( dbName(p_dbName), tableName(p_tableName) {} -std::string Core::DataBase::Exist::getMessage() const { +std::string DataBase::Exist::getMessage() const { return "An attempt to insert element with key " + key + " to database " + dbName + " to table " + tableName + " but it already has an element with given id"; } -Core::DataBase::Unknown::Unknown( +DataBase::Unknown::Unknown( const std::string& p_dbName, const std::string& message, const std::optional& p_tableName @@ -94,7 +94,7 @@ Core::DataBase::Unknown::Unknown( tableName(p_tableName), msg(message) {} -std::string Core::DataBase::Unknown::getMessage() const +std::string DataBase::Unknown::getMessage() const { std::string result = "Unknown error in database " + dbName; if (tableName.has_value()) { diff --git a/exceptions.h b/exceptions.h index 7e2bd34..e77f681 100644 --- a/exceptions.h +++ b/exceptions.h @@ -20,8 +20,6 @@ #include "exception.h" #include "database.h" -namespace Core { - class DataBase::Directory: public Utils::Exception { public: Directory(const std::string& path); @@ -84,5 +82,5 @@ private: std::optional tableName; std::string msg; }; -} + #endif //CORE_DATABASE_EXCEPTIONS_H diff --git a/main.cpp b/main.cpp index 505c051..3b2104a 100644 --- a/main.cpp +++ b/main.cpp @@ -4,38 +4,31 @@ #include "table.h" -template -class Serializer { -public: - Serializer(const T& value):val(value) {} - - void print() { - std::cout << val << std::endl; - } - - T val; -}; - - -template<> -class Serializer { -public: - Serializer(int v): value(v) {} - - int value; -}; - int main(int argc, char **argv) { - Core::DataBase base("test1"); - Core::DataBase::Table* table1 = base.addTable("table1"); - Core::DataBase::Table* table2 = base.addTable("table2"); + DataBase base("test1"); + DataBase::Table* table1 = base.addTable("table1"); + DataBase::Table* table2 = base.addTable("table2"); base.open(); - table1->addRecord(1, 2); + try { + table1->addRecord(1, 2); + } catch (const DataBase::Exist& error) { + std::cout << error.getMessage() << std::endl; + } + uint32_t rec1 = table1->getRecord(1); + std::cout << "table1 record under 1 is " << rec1 << std::endl; + try { + table2->addRecord("hello", "world"); + } catch (const DataBase::Exist& error) { + std::cout << error.getMessage() << std::endl; + } + + QString rec2 = table2->getRecord("hello"); + std::cout << "table2 record under hello is " << rec2.toStdString() << std::endl; return 0; } diff --git a/serializer.h b/serializer.h index db29197..992f3f4 100644 --- a/serializer.h +++ b/serializer.h @@ -23,8 +23,6 @@ #include "database.h" -namespace Core { - template class DataBase::Serializer { @@ -33,6 +31,7 @@ public: Serializer(const T& value); ~Serializer(); + T deserialize(const MDB_val& value); MDB_val setData(const T& value); MDB_val getData(); void clear(); @@ -47,6 +46,7 @@ private: QDataStream stream; }; -} +#include "serializer.hpp" +#include "serializer_uint32.hpp" #endif // CORE_DATABASE_SERIALIZER_H diff --git a/serializer.hpp b/serializer.hpp index 4a43c66..59305c0 100644 --- a/serializer.hpp +++ b/serializer.hpp @@ -14,57 +14,73 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . + +#ifndef CORE_DATABASE_SERIALIZER_HPP +#define CORE_DATABASE_SERIALIZER_HPP + #include "serializer.h" template -Core::DataBase::Serializer::Serializer() : +DataBase::Serializer::Serializer() : bytes(), buffer(&bytes), stream(&buffer) { - buffer.open(QIODevice::WriteOnly); + buffer.open(QIODevice::ReadWrite); } template -Core::DataBase::Serializer::Serializer(const T& value) : +DataBase::Serializer::Serializer(const T& value) : bytes(), buffer(&bytes), stream(&buffer) { - buffer.open(QIODevice::WriteOnly); + buffer.open(QIODevice::ReadWrite); _setValue(value); } template -Core::DataBase::Serializer::~Serializer() { +DataBase::Serializer::~Serializer() { buffer.close(); } template -MDB_val Core::DataBase::Serializer::setData(const T& value) { +MDB_val DataBase::Serializer::setData(const T& value) { clear(); - _setValue(value); - return getValue(); + _setData(value); + return getData(); } template -void Core::DataBase::Serializer::_setData(const T& value) { +T DataBase::Serializer::deserialize(const MDB_val& value) { + clear(); + bytes.setRawData((char*)value.mv_data, value.mv_size); + T result; + stream >> result; + + return result; +} + +template +void DataBase::Serializer::_setData(const T& value) { stream << value; } template -void Core::DataBase::Serializer::clear() { +void DataBase::Serializer::clear() { if (buffer.pos() > 0) { buffer.seek(0); } } template -MDB_val Core::DataBase::Serializer::getData() { +MDB_val DataBase::Serializer::getData() { MDB_val val; val.mv_size = buffer.pos(); - val.mv_data = (uint8_t*)bytes.data(); + val.mv_data = (char*)bytes.data(); return val; } + +#endif //CORE_DATABASE_SERIALIZER_HPP diff --git a/serializer_uint32.hpp b/serializer_uint32.hpp new file mode 100644 index 0000000..7c1832e --- /dev/null +++ b/serializer_uint32.hpp @@ -0,0 +1,50 @@ +// 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_DATABASE_SERIALIZER_UINT32_HPP +#define CORE_DATABASE_SERIALIZER_UINT32_HPP + +template<> +class DataBase::Serializer +{ +public: + Serializer():value(0) {}; + Serializer(const uint32_t& p_value):value(p_value) {}; + ~Serializer() {}; + + uint32_t deserialize(const MDB_val& data) { + std::memcpy(&value, data.mv_data, 4); + return value; + }; + MDB_val setData(const uint32_t& data) { + value = data; + return getData(); + }; + MDB_val getData() { + MDB_val result; + result.mv_data = &value, + result.mv_size = 4; + return result; + }; + void clear() {}; //not possible; + +private: + uint32_t value; +}; + + +#endif //CORE_DATABASE_SERIALIZER_UINT32_HPP diff --git a/table.cpp b/table.cpp index 17b6e03..ea26110 100644 --- a/table.cpp +++ b/table.cpp @@ -16,11 +16,11 @@ #include "table.h" -Core::DataBase::_Table::_Table(const std::string& p_name, DataBase* parent): +DataBase::_Table::_Table(const std::string& p_name, DataBase* parent): dbi(), db(parent), name(p_name) { } -Core::DataBase::_Table::~_Table() {} +DataBase::_Table::~_Table() {} diff --git a/table.h b/table.h index 1fadee5..0c25231 100644 --- a/table.h +++ b/table.h @@ -20,17 +20,23 @@ #include "database.h" #include "serializer.h" -namespace Core { - class DataBase::_Table { public: _Table(const std::string& name, DataBase* parent); virtual ~_Table(); + virtual int createTable(MDB_txn * transaction) = 0; public: MDB_dbi dbi; DataBase* db; const std::string name; + +protected: + template + int makeTable(MDB_txn* transaction); + + template + static std::string toString(const T& value); }; template @@ -47,16 +53,12 @@ public: V getRecord(const K& key) const; private: - Serializer keySerializer; - Serializer valueSerializer; + Serializer* keySerializer; + Serializer* valueSerializer; + + int createTable(MDB_txn* transaction) override; }; -} - -namespace std { - std::string to_string(const QString& str); -} - #include "table.hpp" #endif // CORE_TABLE_H diff --git a/table.hpp b/table.hpp index ee6766c..9365ec5 100644 --- a/table.hpp +++ b/table.hpp @@ -20,26 +20,28 @@ #include "table.h" #include "exceptions.h" -template -Core::DataBase::Table::Table(const std::string& p_name, Core::DataBase* parent): +template +DataBase::Table::Table(const std::string& p_name, DataBase* parent): _Table(p_name, parent), - keySerializer(), - valueSerializer() + keySerializer(new Serializer()), + valueSerializer(new Serializer()) { } -template -Core::DataBase::Table::~Table() { +template +DataBase::Table::~Table() { + delete valueSerializer; + delete keySerializer; } -template -void Core::DataBase::Table::addRecord(const K& key, const V& value) { +template +void DataBase::Table::addRecord(const K& key, const V& value) { if (!db->opened) { throw Closed("addRecord", db->name, name); } - MDB_val lmdbKey = keySerializer.setData(key); - MDB_val lmdbData = valueSerializer.setData(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; @@ -47,7 +49,7 @@ void Core::DataBase::Table::addRecord(const K& key, const V& value) { if (rc != 0) { mdb_txn_abort(txn); if (rc == MDB_KEYEXIST) { - throw Exist(std::to_string(key), db->name, name); + throw Exist(toString(key), db->name, name); } else { throw Unknown(db->name, mdb_strerror(rc), name); } @@ -58,14 +60,14 @@ void Core::DataBase::Table::addRecord(const K& key, const V& value) { -template -void Core::DataBase::Table::changeRecord(const K& key, const V& value) { +template +void DataBase::Table::changeRecord(const K& key, const V& value) { if (!db->opened) { throw Closed("changeRecord", db->name, name); } - MDB_val lmdbKey = keySerializer.setData(key); - MDB_val lmdbData = valueSerializer.setData(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; @@ -80,13 +82,13 @@ void Core::DataBase::Table::changeRecord(const K& key, const V& value) { } } -template -V Core::DataBase::Table::getRecord(const K& key) const { +template +V DataBase::Table::getRecord(const K& key) const { if (!db->opened) { throw Closed("getRecord", db->name, name); } - MDB_val lmdbKey = keySerializer.setData(key); + MDB_val lmdbKey = keySerializer->setData(key); MDB_val lmdbData; MDB_txn *txn; @@ -96,26 +98,25 @@ V Core::DataBase::Table::getRecord(const K& key) const { if (rc) { mdb_txn_abort(txn); if (rc == MDB_NOTFOUND) { - throw NotFound(std::to_string(key), db->name, name); + throw NotFound(toString(key), db->name, name); } else { throw Unknown(db->name, mdb_strerror(rc), name); } } else { - V value; - lmdbData >> value; + V value = valueSerializer->deserialize(lmdbData); mdb_txn_abort(txn); return value; } } -template -void Core::DataBase::Table::removeRecord(const K& key) { +template +void DataBase::Table::removeRecord(const K& key) { if (!db->opened) { throw Closed("removeRecord", db->name, name); } - MDB_val lmdbKey = keySerializer.setData(key); + MDB_val lmdbKey = keySerializer->setData(key); MDB_txn *txn; int rc; @@ -124,7 +125,7 @@ void Core::DataBase::Table::removeRecord(const K& key) { if (rc) { mdb_txn_abort(txn); if (rc == MDB_NOTFOUND) { - throw NotFound(std::to_string(key), db->name, name); + throw NotFound(toString(key), db->name, name); } else { throw Unknown(db->name, mdb_strerror(rc), name); } @@ -133,8 +134,29 @@ void Core::DataBase::Table::removeRecord(const K& key) { } } -std::string std::to_string(const QString& str) { - return str.toStdString(); +template +int DataBase::Table::createTable(MDB_txn* transaction) { + return makeTable(transaction); +} + +template +inline int DataBase::_Table::makeTable(MDB_txn* transaction) { + return mdb_dbi_open(transaction, name.c_str(), MDB_CREATE, &dbi); +} + +template<> +inline int DataBase::_Table::makeTable(MDB_txn* transaction) { + return mdb_dbi_open(transaction, name.c_str(), MDB_CREATE | MDB_INTEGERKEY, &dbi); +} + +template +inline std::string DataBase::_Table::toString(const T& value) { + return std::to_string(value); +} + +template<> +inline std::string DataBase::_Table::toString(const QString& value) { + return value.toStdString(); } #endif //CORE_TABLE_HPP