1
0
forked from blue/lmdbal

finally, first working prototype

This commit is contained in:
Blue 2022-09-15 01:18:31 +03:00
parent d67a3c32eb
commit 5f90a21fe6
Signed by untrusted user: blue
GPG Key ID: 9B203B252A63EE38
11 changed files with 193 additions and 112 deletions

View File

@ -18,7 +18,7 @@
#include "exceptions.h" #include "exceptions.h"
#include "table.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()), name(p_name.toStdString()),
opened(false), opened(false),
size(mapSize), size(mapSize),
@ -27,7 +27,7 @@ Core::DataBase::DataBase(const QString& p_name, uint16_t mapSize):
{ {
} }
Core::DataBase::~DataBase() DataBase::~DataBase()
{ {
close(); close();
for (const std::pair<const std::string, _Table*>& pair : tables) { for (const std::pair<const std::string, _Table*>& pair : tables) {
@ -35,7 +35,7 @@ Core::DataBase::~DataBase()
} }
} }
void Core::DataBase::close() void DataBase::close()
{ {
if (opened) { if (opened) {
for (const std::pair<const std::string, _Table*>& pair : tables) { for (const std::pair<const std::string, _Table*>& pair : tables) {
@ -47,7 +47,7 @@ void Core::DataBase::close()
} }
} }
void Core::DataBase::open() void DataBase::open()
{ {
if (!opened) { if (!opened) {
mdb_env_create(&environment); mdb_env_create(&environment);
@ -71,7 +71,7 @@ void Core::DataBase::open()
for (const std::pair<const std::string, _Table*>& pair : tables) { for (const std::pair<const std::string, _Table*>& pair : tables) {
_Table* table = pair.second; _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) { if (rc) {
throw Unknown(name, mdb_strerror(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); return QString::fromStdString(name);
} }
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, nm);
}
Core::DataBase::Table<K, V>* table = new Core::DataBase::Table<K, V>(nm, this);
tables.insert(std::make_pair(nm, table));
return table;
}

View File

@ -25,8 +25,6 @@
#include <QDir> #include <QDir>
#include <lmdb.h> #include <lmdb.h>
namespace Core {
class DataBase class DataBase
{ {
class _Table; class _Table;
@ -63,6 +61,17 @@ private:
std::map<std::string, _Table*> tables; std::map<std::string, _Table*> tables;
}; };
#include "exceptions.h"
template <class K, class V>
DataBase::Table<K, V>* DataBase::addTable(const QString& p_name) {
std::string nm = p_name.toStdString();
if (opened) {
throw Opened(name, nm);
}
DataBase::Table<K, V>* table = new DataBase::Table<K, V>(nm, this);
tables.insert(std::make_pair(nm, (_Table*)table));
return table;
} }
#endif // CORE_DATABASE_H #endif // CORE_DATABASE_H

View File

@ -16,14 +16,14 @@
#include "exceptions.h" #include "exceptions.h"
Core::DataBase::Directory::Directory(const std::string& p_path): DataBase::Directory::Directory(const std::string& p_path):
Exception(), Exception(),
path(p_path) {} 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;} 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_operation,
const std::string& p_dbName, const std::string& p_dbName,
const std::string& p_tableName const std::string& p_tableName
@ -33,26 +33,26 @@ Core::DataBase::Closed::Closed(
dbName(p_dbName), dbName(p_dbName),
tableName(p_tableName) {} tableName(p_tableName) {}
std::string Core::DataBase::Closed::getMessage() const { std::string DataBase::Closed::getMessage() const {
return "An attempt to perform operation " + operation return "An attempt to perform operation " + operation
+ " on closed database " + dbName + " on closed database " + dbName
+ " on table " + tableName; + " 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(), Exception(),
dbName(p_dbName), dbName(p_dbName),
tableName(p_tableName) {} tableName(p_tableName) {}
std::string Core::DataBase::Opened::getMessage() const { std::string DataBase::Opened::getMessage() const {
return "An attempt to add table " + tableName return "An attempt to add table " + tableName
+ " to the database " + dbName + " to the database " + dbName
+ " but it's can't be done because the DataBase is already opened." + " but it's can't be done because the DataBase is already opened."
+ " Please add all tables before opening DataBase."; + " Please add all tables before opening DataBase.";
} }
Core::DataBase::NotFound::NotFound( DataBase::NotFound::NotFound(
const std::string& p_key, const std::string& p_key,
const std::string& p_dbName, const std::string& p_dbName,
const std::string& p_tableName const std::string& p_tableName
@ -62,12 +62,12 @@ Core::DataBase::NotFound::NotFound(
dbName(p_dbName), dbName(p_dbName),
tableName(p_tableName) {} tableName(p_tableName) {}
std::string Core::DataBase::NotFound::getMessage() const { std::string DataBase::NotFound::getMessage() const {
return "Element for id " + key + " wasn't found " return "Element for id " + key + " wasn't found "
+ " in database " + dbName + " in database " + dbName
+ " in table " + tableName;} + " in table " + tableName;}
Core::DataBase::Exist::Exist( DataBase::Exist::Exist(
const std::string& p_key, const std::string& p_key,
const std::string& p_dbName, const std::string& p_dbName,
const std::string& p_tableName const std::string& p_tableName
@ -77,14 +77,14 @@ Core::DataBase::Exist::Exist(
dbName(p_dbName), dbName(p_dbName),
tableName(p_tableName) {} 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 return "An attempt to insert element with key " + key
+ " to database " + dbName + " to database " + dbName
+ " to table " + tableName + " to table " + tableName
+ " but it already has an element with given id"; + " but it already has an element with given id";
} }
Core::DataBase::Unknown::Unknown( DataBase::Unknown::Unknown(
const std::string& p_dbName, const std::string& p_dbName,
const std::string& message, const std::string& message,
const std::optional<std::string>& p_tableName const std::optional<std::string>& p_tableName
@ -94,7 +94,7 @@ Core::DataBase::Unknown::Unknown(
tableName(p_tableName), tableName(p_tableName),
msg(message) {} msg(message) {}
std::string Core::DataBase::Unknown::getMessage() const std::string DataBase::Unknown::getMessage() const
{ {
std::string result = "Unknown error in database " + dbName; std::string result = "Unknown error in database " + dbName;
if (tableName.has_value()) { if (tableName.has_value()) {

View File

@ -20,8 +20,6 @@
#include "exception.h" #include "exception.h"
#include "database.h" #include "database.h"
namespace Core {
class DataBase::Directory: public Utils::Exception { class DataBase::Directory: public Utils::Exception {
public: public:
Directory(const std::string& path); Directory(const std::string& path);
@ -84,5 +82,5 @@ private:
std::optional<std::string> tableName; std::optional<std::string> tableName;
std::string msg; std::string msg;
}; };
}
#endif //CORE_DATABASE_EXCEPTIONS_H #endif //CORE_DATABASE_EXCEPTIONS_H

View File

@ -4,38 +4,31 @@
#include "table.h" #include "table.h"
template<class T>
class Serializer {
public:
Serializer(const T& value):val(value) {}
void print() {
std::cout << val << std::endl;
}
T val;
};
template<>
class Serializer<int> {
public:
Serializer(int v): value(v) {}
int value;
};
int main(int argc, char **argv) { int main(int argc, char **argv) {
Core::DataBase base("test1"); DataBase base("test1");
Core::DataBase::Table<uint32_t, uint32_t>* table1 = base.addTable<uint32_t, uint32_t>("table1"); 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"); DataBase::Table<QString, QString>* table2 = base.addTable<QString, QString>("table2");
base.open(); base.open();
try {
table1->addRecord(1, 2); 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; return 0;
} }

View File

@ -23,8 +23,6 @@
#include "database.h" #include "database.h"
namespace Core {
template<class T> template<class T>
class DataBase::Serializer class DataBase::Serializer
{ {
@ -33,6 +31,7 @@ public:
Serializer(const T& value); Serializer(const T& value);
~Serializer(); ~Serializer();
T deserialize(const MDB_val& value);
MDB_val setData(const T& value); MDB_val setData(const T& value);
MDB_val getData(); MDB_val getData();
void clear(); void clear();
@ -47,6 +46,7 @@ private:
QDataStream stream; QDataStream stream;
}; };
} #include "serializer.hpp"
#include "serializer_uint32.hpp"
#endif // CORE_DATABASE_SERIALIZER_H #endif // CORE_DATABASE_SERIALIZER_H

View File

@ -14,57 +14,73 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef CORE_DATABASE_SERIALIZER_HPP
#define CORE_DATABASE_SERIALIZER_HPP
#include "serializer.h" #include "serializer.h"
template<class T> template<class T>
Core::DataBase::Serializer<T>::Serializer() : DataBase::Serializer<T>::Serializer() :
bytes(), bytes(),
buffer(&bytes), buffer(&bytes),
stream(&buffer) stream(&buffer)
{ {
buffer.open(QIODevice::WriteOnly); buffer.open(QIODevice::ReadWrite);
} }
template<class T> template<class T>
Core::DataBase::Serializer<T>::Serializer(const T& value) : DataBase::Serializer<T>::Serializer(const T& value) :
bytes(), bytes(),
buffer(&bytes), buffer(&bytes),
stream(&buffer) stream(&buffer)
{ {
buffer.open(QIODevice::WriteOnly); buffer.open(QIODevice::ReadWrite);
_setValue(value); _setValue(value);
} }
template<class T> template<class T>
Core::DataBase::Serializer<T>::~Serializer() { DataBase::Serializer<T>::~Serializer() {
buffer.close(); buffer.close();
} }
template<class T> template<class T>
MDB_val Core::DataBase::Serializer<T>::setData(const T& value) { MDB_val DataBase::Serializer<T>::setData(const T& value) {
clear(); clear();
_setValue(value); _setData(value);
return getValue(); return getData();
} }
template<class T> template<class T>
void Core::DataBase::Serializer<T>::_setData(const T& value) { T DataBase::Serializer<T>::deserialize(const MDB_val& value) {
clear();
bytes.setRawData((char*)value.mv_data, value.mv_size);
T result;
stream >> result;
return result;
}
template<class T>
void DataBase::Serializer<T>::_setData(const T& value) {
stream << value; stream << value;
} }
template<class T> template<class T>
void Core::DataBase::Serializer<T>::clear() { void DataBase::Serializer<T>::clear() {
if (buffer.pos() > 0) { if (buffer.pos() > 0) {
buffer.seek(0); buffer.seek(0);
} }
} }
template<class T> template<class T>
MDB_val Core::DataBase::Serializer<T>::getData() { MDB_val DataBase::Serializer<T>::getData() {
MDB_val val; MDB_val val;
val.mv_size = buffer.pos(); val.mv_size = buffer.pos();
val.mv_data = (uint8_t*)bytes.data(); val.mv_data = (char*)bytes.data();
return val; return val;
} }
#endif //CORE_DATABASE_SERIALIZER_HPP

50
serializer_uint32.hpp Normal file
View File

@ -0,0 +1,50 @@
// 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/>.
#ifndef CORE_DATABASE_SERIALIZER_UINT32_HPP
#define CORE_DATABASE_SERIALIZER_UINT32_HPP
template<>
class DataBase::Serializer<uint32_t>
{
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

View File

@ -16,11 +16,11 @@
#include "table.h" #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(), dbi(),
db(parent), db(parent),
name(p_name) name(p_name)
{ {
} }
Core::DataBase::_Table::~_Table() {} DataBase::_Table::~_Table() {}

22
table.h
View File

@ -20,17 +20,23 @@
#include "database.h" #include "database.h"
#include "serializer.h" #include "serializer.h"
namespace Core {
class DataBase::_Table { class DataBase::_Table {
public: public:
_Table(const std::string& name, DataBase* parent); _Table(const std::string& name, DataBase* parent);
virtual ~_Table(); virtual ~_Table();
virtual int createTable(MDB_txn * transaction) = 0;
public: public:
MDB_dbi dbi; MDB_dbi dbi;
DataBase* db; DataBase* db;
const std::string name; const std::string name;
protected:
template <class T>
int makeTable(MDB_txn* transaction);
template <class T>
static std::string toString(const T& value);
}; };
template <class K, class V> template <class K, class V>
@ -47,16 +53,12 @@ public:
V getRecord(const K& key) const; V getRecord(const K& key) const;
private: private:
Serializer<K> keySerializer; Serializer<K>* keySerializer;
Serializer<V> valueSerializer; Serializer<V>* valueSerializer;
int createTable(MDB_txn* transaction) override;
}; };
}
namespace std {
std::string to_string(const QString& str);
}
#include "table.hpp" #include "table.hpp"
#endif // CORE_TABLE_H #endif // CORE_TABLE_H

View File

@ -20,26 +20,28 @@
#include "table.h" #include "table.h"
#include "exceptions.h" #include "exceptions.h"
template<typename K, typename V> template<class K, class V>
Core::DataBase::Table<K, V>::Table(const std::string& p_name, Core::DataBase* parent): DataBase::Table<K, V>::Table(const std::string& p_name, DataBase* parent):
_Table(p_name, parent), _Table(p_name, parent),
keySerializer(), keySerializer(new Serializer<K>()),
valueSerializer() valueSerializer(new Serializer<V>())
{ {
} }
template<typename K, typename V> template<class K, class V>
Core::DataBase::Table<K, V>::~Table() { DataBase::Table<K, V>::~Table() {
delete valueSerializer;
delete keySerializer;
} }
template<typename K, typename V> template<class K, class V>
void Core::DataBase::Table<K, V>::addRecord(const K& key, const V& value) { void DataBase::Table<K, V>::addRecord(const K& key, const V& value) {
if (!db->opened) { if (!db->opened) {
throw Closed("addRecord", db->name, name); throw Closed("addRecord", db->name, name);
} }
MDB_val lmdbKey = keySerializer.setData(key); MDB_val lmdbKey = keySerializer->setData(key);
MDB_val lmdbData = valueSerializer.setData(value); MDB_val lmdbData = valueSerializer->setData(value);
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;
@ -47,7 +49,7 @@ 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(std::to_string(key), db->name, name); throw Exist(toString(key), db->name, name);
} else { } else {
throw Unknown(db->name, mdb_strerror(rc), name); throw Unknown(db->name, mdb_strerror(rc), name);
} }
@ -58,14 +60,14 @@ void Core::DataBase::Table<K, V>::addRecord(const K& key, const V& value) {
template<typename K, typename V> template<class K, class V>
void Core::DataBase::Table<K, V>::changeRecord(const K& key, const V& value) { void DataBase::Table<K, V>::changeRecord(const K& key, const V& value) {
if (!db->opened) { if (!db->opened) {
throw Closed("changeRecord", db->name, name); throw Closed("changeRecord", db->name, name);
} }
MDB_val lmdbKey = keySerializer.setData(key); MDB_val lmdbKey = keySerializer->setData(key);
MDB_val lmdbData = valueSerializer.setData(value); MDB_val lmdbData = valueSerializer->setData(value);
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;
@ -80,13 +82,13 @@ void Core::DataBase::Table<K, V>::changeRecord(const K& key, const V& value) {
} }
} }
template<typename K, typename V> template<class K, class V>
V Core::DataBase::Table<K, V>::getRecord(const K& key) const { V DataBase::Table<K, V>::getRecord(const K& key) const {
if (!db->opened) { if (!db->opened) {
throw Closed("getRecord", db->name, name); throw Closed("getRecord", db->name, name);
} }
MDB_val lmdbKey = keySerializer.setData(key); MDB_val lmdbKey = keySerializer->setData(key);
MDB_val lmdbData; MDB_val lmdbData;
MDB_txn *txn; MDB_txn *txn;
@ -96,26 +98,25 @@ V Core::DataBase::Table<K, V>::getRecord(const K& key) const {
if (rc) { if (rc) {
mdb_txn_abort(txn); mdb_txn_abort(txn);
if (rc == MDB_NOTFOUND) { if (rc == MDB_NOTFOUND) {
throw NotFound(std::to_string(key), db->name, name); throw NotFound(toString(key), db->name, name);
} else { } else {
throw Unknown(db->name, mdb_strerror(rc), name); throw Unknown(db->name, mdb_strerror(rc), name);
} }
} else { } else {
V value; V value = valueSerializer->deserialize(lmdbData);
lmdbData >> value;
mdb_txn_abort(txn); mdb_txn_abort(txn);
return value; return value;
} }
} }
template<typename K, typename V> template<class K, class V>
void Core::DataBase::Table<K, V>::removeRecord(const K& key) { void DataBase::Table<K, V>::removeRecord(const K& key) {
if (!db->opened) { if (!db->opened) {
throw Closed("removeRecord", db->name, name); throw Closed("removeRecord", db->name, name);
} }
MDB_val lmdbKey = keySerializer.setData(key); MDB_val lmdbKey = keySerializer->setData(key);
MDB_txn *txn; MDB_txn *txn;
int rc; int rc;
@ -124,7 +125,7 @@ void Core::DataBase::Table<K, V>::removeRecord(const K& key) {
if (rc) { if (rc) {
mdb_txn_abort(txn); mdb_txn_abort(txn);
if (rc == MDB_NOTFOUND) { if (rc == MDB_NOTFOUND) {
throw NotFound(std::to_string(key), db->name, name); throw NotFound(toString(key), db->name, name);
} else { } else {
throw Unknown(db->name, mdb_strerror(rc), name); throw Unknown(db->name, mdb_strerror(rc), name);
} }
@ -133,8 +134,29 @@ void Core::DataBase::Table<K, V>::removeRecord(const K& key) {
} }
} }
std::string std::to_string(const QString& str) { template<class K, class V>
return str.toStdString(); int DataBase::Table<K, V>::createTable(MDB_txn* transaction) {
return makeTable<K>(transaction);
}
template<class K>
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<uint32_t>(MDB_txn* transaction) {
return mdb_dbi_open(transaction, name.c_str(), MDB_CREATE | MDB_INTEGERKEY, &dbi);
}
template<class T>
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 #endif //CORE_TABLE_HPP