some thoughts about exceptions
This commit is contained in:
parent
c3139974f1
commit
c491793387
@ -25,6 +25,7 @@ endif ()
|
|||||||
add_executable(storage
|
add_executable(storage
|
||||||
main.cpp
|
main.cpp
|
||||||
exception.cpp
|
exception.cpp
|
||||||
|
exceptions.cpp
|
||||||
table.cpp
|
table.cpp
|
||||||
database.cpp
|
database.cpp
|
||||||
)
|
)
|
||||||
|
@ -89,10 +89,11 @@ QString Core::DataBase::getName() const
|
|||||||
|
|
||||||
template <class K, class V>
|
template <class K, class V>
|
||||||
Core::DataBase::Table<K, V>* Core::DataBase::addTable(const QString& p_name) {
|
Core::DataBase::Table<K, V>* Core::DataBase::addTable(const QString& p_name) {
|
||||||
|
std::string nm = p_name.toStdString();
|
||||||
if (opened) {
|
if (opened) {
|
||||||
throw Core::DataBase::Opened(name);
|
throw Core::DataBase::Opened(name, nm);
|
||||||
}
|
}
|
||||||
Core::DataBase::Table<K, V>* table = new Core::DataBase::Table<K, V>();
|
Core::DataBase::Table<K, V>* table = new Core::DataBase::Table<K, V>(nm, this);
|
||||||
tables.insert(std::make_pair(p_name.toStdString(), table));
|
tables.insert(std::make_pair(nm, table));
|
||||||
return table;
|
return table;
|
||||||
}
|
}
|
||||||
|
@ -50,9 +50,7 @@ public:
|
|||||||
class Closed;
|
class Closed;
|
||||||
class Opened;
|
class Opened;
|
||||||
class NotFound;
|
class NotFound;
|
||||||
class Empty;
|
|
||||||
class Exist;
|
class Exist;
|
||||||
class NoAvatar;
|
|
||||||
class Unknown;
|
class Unknown;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
105
exceptions.cpp
Normal file
105
exceptions.cpp
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
// Squawk messenger.
|
||||||
|
// Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
||||||
|
//
|
||||||
|
// 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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
#include "exceptions.h"
|
||||||
|
|
||||||
|
Core::DataBase::Directory::Directory(const std::string& p_path):
|
||||||
|
Exception(),
|
||||||
|
path(p_path) {}
|
||||||
|
|
||||||
|
std::string Core::DataBase::Directory::getMessage() const {
|
||||||
|
return "Can't create directory for database at " + path;}
|
||||||
|
|
||||||
|
Core::DataBase::Closed::Closed(
|
||||||
|
const std::string& p_operation,
|
||||||
|
const std::string& p_dbName,
|
||||||
|
const std::string& p_tableName
|
||||||
|
):
|
||||||
|
Exception(),
|
||||||
|
operation(p_operation),
|
||||||
|
dbName(p_dbName),
|
||||||
|
tableName(p_tableName) {}
|
||||||
|
|
||||||
|
std::string Core::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):
|
||||||
|
Exception(),
|
||||||
|
dbName(p_dbName),
|
||||||
|
tableName(p_tableName) {}
|
||||||
|
|
||||||
|
|
||||||
|
std::string Core::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(
|
||||||
|
const std::string& p_key,
|
||||||
|
const std::string& p_dbName,
|
||||||
|
const std::string& p_tableName
|
||||||
|
):
|
||||||
|
Exception(),
|
||||||
|
key(p_key),
|
||||||
|
dbName(p_dbName),
|
||||||
|
tableName(p_tableName) {}
|
||||||
|
|
||||||
|
std::string Core::DataBase::NotFound::getMessage() const {
|
||||||
|
return "Element for id " + key + " wasn't found "
|
||||||
|
+ " in database " + dbName
|
||||||
|
+ " in table " + tableName;}
|
||||||
|
|
||||||
|
Core::DataBase::Exist::Exist(
|
||||||
|
const std::string& p_key,
|
||||||
|
const std::string& p_dbName,
|
||||||
|
const std::string& p_tableName
|
||||||
|
):
|
||||||
|
Exception(),
|
||||||
|
key(p_key),
|
||||||
|
dbName(p_dbName),
|
||||||
|
tableName(p_tableName) {}
|
||||||
|
|
||||||
|
std::string Core::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(
|
||||||
|
const std::string& p_dbName,
|
||||||
|
const std::string& message,
|
||||||
|
const std::optional<std::string>& p_tableName
|
||||||
|
):
|
||||||
|
Exception(),
|
||||||
|
dbName(p_dbName),
|
||||||
|
tableName(p_tableName),
|
||||||
|
msg(message) {}
|
||||||
|
|
||||||
|
std::string Core::DataBase::Unknown::getMessage() const
|
||||||
|
{
|
||||||
|
std::string result = "Unknown error in database " + dbName;
|
||||||
|
if (tableName.has_value()) {
|
||||||
|
result += " in table " + tableName.value();
|
||||||
|
}
|
||||||
|
result += ": " + msg;
|
||||||
|
return result;
|
||||||
|
}
|
60
exceptions.h
60
exceptions.h
@ -24,82 +24,64 @@ namespace Core {
|
|||||||
|
|
||||||
class DataBase::Directory: public Utils::Exception {
|
class DataBase::Directory: public Utils::Exception {
|
||||||
public:
|
public:
|
||||||
Directory(const std::string& p_path):Exception(), path(p_path){}
|
Directory(const std::string& path);
|
||||||
|
|
||||||
std::string getMessage() const{return "Can't create directory for database at " + path;}
|
std::string getMessage() const;
|
||||||
private:
|
private:
|
||||||
std::string path;
|
std::string path;
|
||||||
};
|
};
|
||||||
|
|
||||||
class DataBase::Closed : public Utils::Exception {
|
class DataBase::Closed : public Utils::Exception {
|
||||||
public:
|
public:
|
||||||
Closed(const std::string& op, const std::string& acc):Exception(), operation(op), account(acc){}
|
Closed(const std::string& p_operation, const std::string& dbName, const std::string& tableName);
|
||||||
|
|
||||||
std::string getMessage() const{return "An attempt to perform operation " + operation + " on closed archive for " + account;}
|
std::string getMessage() const;
|
||||||
private:
|
private:
|
||||||
std::string operation;
|
std::string operation;
|
||||||
std::string account;
|
std::string dbName;
|
||||||
|
std::string tableName;
|
||||||
};
|
};
|
||||||
|
|
||||||
class DataBase::Opened : public Utils::Exception {
|
class DataBase::Opened : public Utils::Exception {
|
||||||
public:
|
public:
|
||||||
Opened(const std::string& tn):Exception(), tableName(tn){}
|
Opened(const std::string& dbName, const std::string& tableName);
|
||||||
|
|
||||||
std::string getMessage() const{return "An attempt to add table " + tableName + " but it's impossible to do if the DataBase is already opened";}
|
std::string getMessage() const;
|
||||||
private:
|
private:
|
||||||
|
std::string dbName;
|
||||||
std::string tableName;
|
std::string tableName;
|
||||||
};
|
};
|
||||||
|
|
||||||
class DataBase::NotFound : public Utils::Exception {
|
class DataBase::NotFound : public Utils::Exception {
|
||||||
public:
|
public:
|
||||||
NotFound(const std::string& k, const std::string& acc):Exception(), key(k), account(acc){}
|
NotFound(const std::string& key, const std::string& dbName, const std::string& tableName);
|
||||||
|
|
||||||
std::string getMessage() const{return "Element for id " + key + " wasn't found in database " + account;}
|
std::string getMessage() const;
|
||||||
private:
|
private:
|
||||||
std::string key;
|
std::string key;
|
||||||
std::string account;
|
std::string dbName;
|
||||||
};
|
std::string tableName;
|
||||||
|
|
||||||
class DataBase::Empty : public Utils::Exception {
|
|
||||||
public:
|
|
||||||
Empty(const std::string& acc):Exception(), account(acc){}
|
|
||||||
|
|
||||||
std::string getMessage() const{return "An attempt to read ordered elements from database " + account + " but it's empty";}
|
|
||||||
private:
|
|
||||||
std::string account;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class DataBase::Exist : public Utils::Exception {
|
class DataBase::Exist : public Utils::Exception {
|
||||||
public:
|
public:
|
||||||
Exist(const std::string& acc, const std::string& p_key):Exception(), account(acc), key(p_key){}
|
Exist(const std::string& key, const std::string& dbName, const std::string& tableName);
|
||||||
|
|
||||||
std::string getMessage() const{return "An attempt to insert element " + key + " to database " + account + " but it already has an element with given id";}
|
std::string getMessage() const;
|
||||||
private:
|
private:
|
||||||
std::string account;
|
|
||||||
std::string key;
|
std::string key;
|
||||||
};
|
std::string dbName;
|
||||||
|
std::string tableName;
|
||||||
class DataBase::NoAvatar : public Utils::Exception {
|
|
||||||
public:
|
|
||||||
NoAvatar(const std::string& el, const std::string& res):Exception(), element(el), resource(res){
|
|
||||||
if (resource.size() == 0) {
|
|
||||||
resource = "for himself";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string getMessage() const{return "Element " + element + " has no avatar for " + resource ;}
|
|
||||||
private:
|
|
||||||
std::string element;
|
|
||||||
std::string resource;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class DataBase::Unknown : public Utils::Exception {
|
class DataBase::Unknown : public Utils::Exception {
|
||||||
public:
|
public:
|
||||||
Unknown(const std::string& acc, const std::string& message):Exception(), account(acc), msg(message){}
|
Unknown(const std::string& dbName, const std::string& message, const std::optional<std::string>& tableName = std::nullopt);
|
||||||
|
|
||||||
std::string getMessage() const{return "Unknown error on database " + account + ": " + msg;}
|
std::string getMessage() const;
|
||||||
private:
|
private:
|
||||||
std::string account;
|
std::string dbName;
|
||||||
|
std::optional<std::string> tableName;
|
||||||
std::string msg;
|
std::string msg;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
13
main.cpp
13
main.cpp
@ -1,8 +1,17 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
#include <storage.h>
|
#include "database.h"
|
||||||
|
#include "table.h"
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
int main(int argc, char **argv) {
|
||||||
std::cout << "Hello, world!" << std::endl;
|
|
||||||
|
Core::DataBase base("test1");
|
||||||
|
Core::DataBase::Table<uint32_t, uint32_t>* table1 = base.addTable<uint32_t, uint32_t>("table1");
|
||||||
|
Core::DataBase::Table<QString, QString>* table2 = base.addTable<QString, QString>("table2");
|
||||||
|
|
||||||
|
base.open();
|
||||||
|
|
||||||
|
table1->addRecord(1, 2);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -16,9 +16,10 @@
|
|||||||
|
|
||||||
#include "table.h"
|
#include "table.h"
|
||||||
|
|
||||||
Core::DataBase::_Table::_Table(DataBase* parent):
|
Core::DataBase::_Table::_Table(const std::string& p_name, DataBase* parent):
|
||||||
dbi(),
|
dbi(),
|
||||||
db(parent)
|
db(parent),
|
||||||
|
name(p_name)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
15
table.h
15
table.h
@ -23,19 +23,20 @@ namespace Core {
|
|||||||
|
|
||||||
class DataBase::_Table {
|
class DataBase::_Table {
|
||||||
public:
|
public:
|
||||||
_Table(DataBase* parent);
|
_Table(const std::string& name, DataBase* parent);
|
||||||
virtual ~_Table();
|
virtual ~_Table();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
MDB_dbi dbi;
|
MDB_dbi dbi;
|
||||||
DataBase* db;
|
DataBase* db;
|
||||||
|
const std::string name;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <class K, class V>
|
template <class K, class V>
|
||||||
class DataBase::Table : private DataBase::_Table {
|
class DataBase::Table : private DataBase::_Table {
|
||||||
friend class DataBase;
|
friend class DataBase;
|
||||||
private:
|
private:
|
||||||
Table(DataBase* parent);
|
Table(const std::string& name, DataBase* parent);
|
||||||
~Table() override;
|
~Table() override;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@ -47,6 +48,16 @@ public:
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MDB_val& operator << (MDB_val& data, const QString& value);
|
||||||
|
MDB_val& operator >> (MDB_val& data, QString& value);
|
||||||
|
|
||||||
|
MDB_val& operator << (MDB_val& data, const uint32_t& value);
|
||||||
|
MDB_val& operator >> (MDB_val& data, uint32_t& value);
|
||||||
|
|
||||||
|
namespace std {
|
||||||
|
std::string to_string(const QString& str);
|
||||||
|
}
|
||||||
|
|
||||||
#include "table.hpp"
|
#include "table.hpp"
|
||||||
|
|
||||||
#endif // CORE_TABLE_H
|
#endif // CORE_TABLE_H
|
||||||
|
127
table.hpp
127
table.hpp
@ -18,22 +18,27 @@
|
|||||||
#define CORE_TABLE_HPP
|
#define CORE_TABLE_HPP
|
||||||
|
|
||||||
#include "table.h"
|
#include "table.h"
|
||||||
|
#include "exceptions.h"
|
||||||
|
|
||||||
|
template<typename K, typename V>
|
||||||
|
Core::DataBase::Table<K, V>::Table(const std::string& p_name, Core::DataBase* parent):
|
||||||
|
_Table(p_name, parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename K, typename V>
|
||||||
|
Core::DataBase::Table<K, V>::~Table() {
|
||||||
|
}
|
||||||
|
|
||||||
template<typename K, typename V>
|
template<typename K, typename V>
|
||||||
void Core::DataBase::Table<K, V>::addRecord(const K& key, const V& value) {
|
void Core::DataBase::Table<K, V>::addRecord(const K& key, const V& value) {
|
||||||
if (!db->opened) {
|
if (!db->opened) {
|
||||||
throw Closed("addRecord", db->name);
|
throw Closed("addRecord", db->name, name);
|
||||||
}
|
}
|
||||||
QByteArray ba;
|
|
||||||
QDataStream ds(&ba, QIODevice::WriteOnly);
|
|
||||||
ds << value;
|
|
||||||
|
|
||||||
MDB_val lmdbKey, lmdbData;
|
MDB_val lmdbKey, lmdbData;
|
||||||
lmdbKey << key;
|
lmdbKey << key;
|
||||||
|
lmdbData << value;
|
||||||
lmdbData.mv_size = ba.size();
|
|
||||||
lmdbData.mv_data = (uint8_t*)ba.data();
|
|
||||||
MDB_txn *txn;
|
MDB_txn *txn;
|
||||||
mdb_txn_begin(db->environment, NULL, 0, &txn);
|
mdb_txn_begin(db->environment, NULL, 0, &txn);
|
||||||
int rc;
|
int rc;
|
||||||
@ -41,13 +46,117 @@ void Core::DataBase::Table<K, V>::addRecord(const K& key, const V& value) {
|
|||||||
if (rc != 0) {
|
if (rc != 0) {
|
||||||
mdb_txn_abort(txn);
|
mdb_txn_abort(txn);
|
||||||
if (rc == MDB_KEYEXIST) {
|
if (rc == MDB_KEYEXIST) {
|
||||||
throw Exist(db->name, std::to_string(key));
|
throw Exist(std::to_string(key), db->name, name);
|
||||||
} else {
|
} else {
|
||||||
throw Unknown(db->name, mdb_strerror(rc));
|
throw Unknown(db->name, mdb_strerror(rc), name);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
mdb_txn_commit(txn);
|
mdb_txn_commit(txn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
template<typename K, typename V>
|
||||||
|
void Core::DataBase::Table<K, V>::changeRecord(const K& key, const V& value) {
|
||||||
|
if (!db->opened) {
|
||||||
|
throw Closed("changeRecord", db->name, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
MDB_val lmdbKey, lmdbData;
|
||||||
|
lmdbKey << key;
|
||||||
|
lmdbData << value;
|
||||||
|
MDB_txn *txn;
|
||||||
|
mdb_txn_begin(db->environment, NULL, 0, &txn);
|
||||||
|
int rc;
|
||||||
|
rc = mdb_put(txn, dbi, &lmdbKey, &lmdbData, 0);
|
||||||
|
if (rc != 0) {
|
||||||
|
mdb_txn_abort(txn);
|
||||||
|
if (rc) {
|
||||||
|
throw Unknown(db->name, mdb_strerror(rc), name);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
mdb_txn_commit(txn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename K, typename V>
|
||||||
|
V Core::DataBase::Table<K, V>::getRecord(const K& key) const {
|
||||||
|
if (!db->opened) {
|
||||||
|
throw Closed("getRecord", db->name, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
MDB_val lmdbKey, lmdbData;
|
||||||
|
lmdbKey << key;
|
||||||
|
|
||||||
|
MDB_txn *txn;
|
||||||
|
int rc;
|
||||||
|
mdb_txn_begin(db->environment, NULL, MDB_RDONLY, &txn);
|
||||||
|
rc = mdb_get(txn, dbi, &lmdbKey, &lmdbData);
|
||||||
|
if (rc) {
|
||||||
|
mdb_txn_abort(txn);
|
||||||
|
if (rc == MDB_NOTFOUND) {
|
||||||
|
throw NotFound(std::to_string(key), db->name, name);
|
||||||
|
} else {
|
||||||
|
throw Unknown(db->name, mdb_strerror(rc), name);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
V value;
|
||||||
|
lmdbData >> value;
|
||||||
|
mdb_txn_abort(txn);
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename K, typename V>
|
||||||
|
void Core::DataBase::Table<K, V>::removeRecord(const K& key) {
|
||||||
|
if (!db->opened) {
|
||||||
|
throw Closed("removeRecord", db->name, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
MDB_val lmdbKey;
|
||||||
|
lmdbKey << key;
|
||||||
|
|
||||||
|
MDB_txn *txn;
|
||||||
|
int rc;
|
||||||
|
mdb_txn_begin(db->environment, NULL, 0, &txn);
|
||||||
|
rc = mdb_del(txn, dbi, &lmdbKey, NULL);
|
||||||
|
if (rc) {
|
||||||
|
mdb_txn_abort(txn);
|
||||||
|
if (rc == MDB_NOTFOUND) {
|
||||||
|
throw NotFound(std::to_string(key), db->name, name);
|
||||||
|
} else {
|
||||||
|
throw Unknown(db->name, mdb_strerror(rc), name);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
mdb_txn_commit(txn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MDB_val& operator << (MDB_val& data, const QString& value) {
|
||||||
|
QByteArray ba = value.toUtf8();
|
||||||
|
data.mv_size = ba.size();
|
||||||
|
data.mv_data = ba.data();
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
MDB_val& operator >> (MDB_val& data, QString& value) {
|
||||||
|
value = QString::fromUtf8((const char*)data.mv_data, data.mv_size);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
MDB_val& operator << (MDB_val& data, const uint32_t& value) {
|
||||||
|
data.mv_size = 4;
|
||||||
|
data.mv_data = &value;
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
MDB_val& operator >> (MDB_val& data, uint32_t& value) {
|
||||||
|
std::memcpy(&value, data.mv_data, data.mv_size);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string std::to_string(const QString& str) {
|
||||||
|
return str.toStdString();
|
||||||
|
}
|
||||||
|
|
||||||
#endif //CORE_TABLE_HPP
|
#endif //CORE_TABLE_HPP
|
||||||
|
Loading…
Reference in New Issue
Block a user