forked from blue/lmdbal
some thoughts about exceptions
This commit is contained in:
parent
c3139974f1
commit
c491793387
@ -25,6 +25,7 @@ endif ()
|
||||
add_executable(storage
|
||||
main.cpp
|
||||
exception.cpp
|
||||
exceptions.cpp
|
||||
table.cpp
|
||||
database.cpp
|
||||
)
|
||||
|
@ -89,10 +89,11 @@ QString Core::DataBase::getName() const
|
||||
|
||||
template <class K, class V>
|
||||
Core::DataBase::Table<K, V>* Core::DataBase::addTable(const QString& p_name) {
|
||||
std::string nm = p_name.toStdString();
|
||||
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>();
|
||||
tables.insert(std::make_pair(p_name.toStdString(), table));
|
||||
Core::DataBase::Table<K, V>* table = new Core::DataBase::Table<K, V>(nm, this);
|
||||
tables.insert(std::make_pair(nm, table));
|
||||
return table;
|
||||
}
|
||||
|
@ -50,9 +50,7 @@ public:
|
||||
class Closed;
|
||||
class Opened;
|
||||
class NotFound;
|
||||
class Empty;
|
||||
class Exist;
|
||||
class NoAvatar;
|
||||
class Unknown;
|
||||
|
||||
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 {
|
||||
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:
|
||||
std::string path;
|
||||
};
|
||||
|
||||
class DataBase::Closed : public Utils::Exception {
|
||||
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:
|
||||
std::string operation;
|
||||
std::string account;
|
||||
std::string dbName;
|
||||
std::string tableName;
|
||||
};
|
||||
|
||||
class DataBase::Opened : public Utils::Exception {
|
||||
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:
|
||||
std::string dbName;
|
||||
std::string tableName;
|
||||
};
|
||||
|
||||
class DataBase::NotFound : public Utils::Exception {
|
||||
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:
|
||||
std::string key;
|
||||
std::string account;
|
||||
};
|
||||
|
||||
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;
|
||||
std::string dbName;
|
||||
std::string tableName;
|
||||
};
|
||||
|
||||
class DataBase::Exist : public Utils::Exception {
|
||||
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:
|
||||
std::string account;
|
||||
std::string key;
|
||||
};
|
||||
|
||||
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;
|
||||
std::string dbName;
|
||||
std::string tableName;
|
||||
};
|
||||
|
||||
class DataBase::Unknown : public Utils::Exception {
|
||||
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:
|
||||
std::string account;
|
||||
std::string dbName;
|
||||
std::optional<std::string> tableName;
|
||||
std::string msg;
|
||||
};
|
||||
}
|
||||
|
13
main.cpp
13
main.cpp
@ -1,8 +1,17 @@
|
||||
#include <iostream>
|
||||
|
||||
#include <storage.h>
|
||||
#include "database.h"
|
||||
#include "table.h"
|
||||
|
||||
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;
|
||||
}
|
||||
|
@ -16,9 +16,10 @@
|
||||
|
||||
#include "table.h"
|
||||
|
||||
Core::DataBase::_Table::_Table(DataBase* parent):
|
||||
Core::DataBase::_Table::_Table(const std::string& p_name, DataBase* parent):
|
||||
dbi(),
|
||||
db(parent)
|
||||
db(parent),
|
||||
name(p_name)
|
||||
{
|
||||
}
|
||||
|
||||
|
15
table.h
15
table.h
@ -23,19 +23,20 @@ namespace Core {
|
||||
|
||||
class DataBase::_Table {
|
||||
public:
|
||||
_Table(DataBase* parent);
|
||||
_Table(const std::string& name, DataBase* parent);
|
||||
virtual ~_Table();
|
||||
|
||||
public:
|
||||
MDB_dbi dbi;
|
||||
DataBase* db;
|
||||
const std::string name;
|
||||
};
|
||||
|
||||
template <class K, class V>
|
||||
class DataBase::Table : private DataBase::_Table {
|
||||
friend class DataBase;
|
||||
private:
|
||||
Table(DataBase* parent);
|
||||
Table(const std::string& name, DataBase* parent);
|
||||
~Table() override;
|
||||
|
||||
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"
|
||||
|
||||
#endif // CORE_TABLE_H
|
||||
|
127
table.hpp
127
table.hpp
@ -18,22 +18,27 @@
|
||||
#define CORE_TABLE_HPP
|
||||
|
||||
#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>
|
||||
void Core::DataBase::Table<K, V>::addRecord(const K& key, const V& value) {
|
||||
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;
|
||||
lmdbKey << key;
|
||||
|
||||
lmdbData.mv_size = ba.size();
|
||||
lmdbData.mv_data = (uint8_t*)ba.data();
|
||||
lmdbData << value;
|
||||
MDB_txn *txn;
|
||||
mdb_txn_begin(db->environment, NULL, 0, &txn);
|
||||
int rc;
|
||||
@ -41,13 +46,117 @@ void Core::DataBase::Table<K, V>::addRecord(const K& key, const V& value) {
|
||||
if (rc != 0) {
|
||||
mdb_txn_abort(txn);
|
||||
if (rc == MDB_KEYEXIST) {
|
||||
throw Exist(db->name, std::to_string(key));
|
||||
throw Exist(std::to_string(key), db->name, name);
|
||||
} else {
|
||||
throw Unknown(db->name, mdb_strerror(rc));
|
||||
throw Unknown(db->name, mdb_strerror(rc), name);
|
||||
}
|
||||
} else {
|
||||
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
|
||||
|
Loading…
Reference in New Issue
Block a user