1
0
forked from blue/lmdbal

some thoughts about exceptions

This commit is contained in:
Blue 2022-09-09 20:15:40 +03:00
parent c3139974f1
commit c491793387
Signed by untrusted user: blue
GPG Key ID: 9B203B252A63EE38
9 changed files with 276 additions and 59 deletions

View File

@ -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
) )

View File

@ -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;
} }

View File

@ -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
View 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;
}

View File

@ -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;
}; };
} }

View File

@ -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;
} }

View File

@ -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
View File

@ -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
View File

@ -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