forked from blue/lmdbal
some tuning, specializations, basic testing
This commit is contained in:
parent
5f90a21fe6
commit
047f96b54a
@ -4,6 +4,11 @@ project(storage VERSION 0.0.1 LANGUAGES CXX)
|
||||
|
||||
cmake_policy(SET CMP0076 NEW)
|
||||
cmake_policy(SET CMP0079 NEW)
|
||||
|
||||
option(BUILD_STATIC "Builds library as static library" ON)
|
||||
option(BUILD_TESTS "Builds tests" ON)
|
||||
include(GNUInstallDirs)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake")
|
||||
@ -22,14 +27,36 @@ if (NOT CMAKE_BUILD_TYPE)
|
||||
set(CMAKE_BUILD_TYPE Debug)
|
||||
endif ()
|
||||
|
||||
add_executable(storage
|
||||
main.cpp
|
||||
exception.cpp
|
||||
set(SOURCES
|
||||
exceptions.cpp
|
||||
table.cpp
|
||||
database.cpp
|
||||
)
|
||||
|
||||
set(HEADERS
|
||||
database.h
|
||||
exceptions.h
|
||||
table.h
|
||||
table.hpp
|
||||
serializer.h
|
||||
serializer.hpp
|
||||
serializer_uint8.hpp
|
||||
serializer_uint16.hpp
|
||||
serializer_uint32.hpp
|
||||
serializer_uint64.hpp
|
||||
)
|
||||
|
||||
if (BUILD_STATIC)
|
||||
add_library(storage STATIC ${SOURCES})
|
||||
else ()
|
||||
add_library(storage SHARED ${SOURCES})
|
||||
endif()
|
||||
|
||||
if (BUILD_TESTS)
|
||||
add_subdirectory(test)
|
||||
endif ()
|
||||
|
||||
set_target_properties(storage PROPERTIES PUBLIC_HEADER "${HEADERS}")
|
||||
|
||||
target_include_directories(storage PRIVATE ${CMAKE_SOURCE_DIR})
|
||||
target_include_directories(storage PRIVATE ${Qt${QT_VERSION_MAJOR}_INCLUDE_DIRS})
|
||||
@ -41,4 +68,9 @@ target_link_libraries(storage
|
||||
)
|
||||
target_link_libraries(storage PRIVATE lmdb)
|
||||
|
||||
install(TARGETS storage RUNTIME DESTINATION bin)
|
||||
install(TARGETS storage
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/storage
|
||||
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/storage
|
||||
)
|
||||
|
||||
|
20
database.cpp
20
database.cpp
@ -82,9 +82,29 @@ void DataBase::open()
|
||||
}
|
||||
}
|
||||
|
||||
bool DataBase::removeDirectory()
|
||||
{
|
||||
if (opened) {
|
||||
throw Opened(name, "");
|
||||
}
|
||||
QString path(QStandardPaths::writableLocation(QStandardPaths::CacheLocation));
|
||||
path += "/" + getName();
|
||||
QDir cache(path);
|
||||
|
||||
if (cache.exists()) {
|
||||
return cache.removeRecursively();
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
QString DataBase::getName() const
|
||||
{
|
||||
return QString::fromStdString(name);
|
||||
}
|
||||
|
||||
bool DataBase::ready() const
|
||||
{
|
||||
return opened;
|
||||
}
|
||||
|
||||
|
22
database.h
22
database.h
@ -39,13 +39,19 @@ public:
|
||||
|
||||
void open();
|
||||
void close();
|
||||
bool ready() const;
|
||||
bool removeDirectory();
|
||||
QString getName() const;
|
||||
|
||||
template <class K, class V>
|
||||
Table<K, V>* addTable(const QString& name);
|
||||
Table<K, V>* addTable(const std::string& name);
|
||||
|
||||
template <class K, class V>
|
||||
Table<K, V>* getTable(const std::string& name);
|
||||
|
||||
public:
|
||||
//exceptions
|
||||
class Exception;
|
||||
class Directory;
|
||||
class Closed;
|
||||
class Opened;
|
||||
@ -64,14 +70,18 @@ private:
|
||||
#include "exceptions.h"
|
||||
|
||||
template <class K, class V>
|
||||
DataBase::Table<K, V>* DataBase::addTable(const QString& p_name) {
|
||||
std::string nm = p_name.toStdString();
|
||||
DataBase::Table<K, V>* DataBase::addTable(const std::string& p_name) {
|
||||
if (opened) {
|
||||
throw Opened(name, nm);
|
||||
throw Opened(name, p_name);
|
||||
}
|
||||
DataBase::Table<K, V>* table = new DataBase::Table<K, V>(nm, this);
|
||||
tables.insert(std::make_pair(nm, (_Table*)table));
|
||||
DataBase::Table<K, V>* table = new DataBase::Table<K, V>(p_name, this);
|
||||
tables.insert(std::make_pair(p_name, (_Table*)table));
|
||||
return table;
|
||||
}
|
||||
|
||||
template <class K, class V>
|
||||
DataBase::Table<K, V>* DataBase::getTable(const std::string& p_name) {
|
||||
return static_cast<DataBase::Table<K, V>*>(tables.at(p_name));
|
||||
}
|
||||
|
||||
#endif // CORE_DATABASE_H
|
||||
|
@ -1,33 +0,0 @@
|
||||
/*
|
||||
* 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 "exception.h"
|
||||
|
||||
Utils::Exception::Exception()
|
||||
{
|
||||
}
|
||||
|
||||
Utils::Exception::~Exception()
|
||||
{
|
||||
}
|
||||
|
||||
const char* Utils::Exception::what() const noexcept( true )
|
||||
{
|
||||
std::string* msg = new std::string(getMessage());
|
||||
return msg->c_str();
|
||||
}
|
40
exception.h
40
exception.h
@ -1,40 +0,0 @@
|
||||
/*
|
||||
* 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 EXCEPTION_H
|
||||
#define EXCEPTION_H
|
||||
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
namespace Utils
|
||||
{
|
||||
class Exception:
|
||||
public std::exception
|
||||
{
|
||||
public:
|
||||
Exception();
|
||||
virtual ~Exception();
|
||||
|
||||
virtual std::string getMessage() const = 0;
|
||||
|
||||
const char* what() const noexcept( true );
|
||||
};
|
||||
}
|
||||
|
||||
#endif // EXCEPTION_H
|
@ -16,6 +16,18 @@
|
||||
|
||||
#include "exceptions.h"
|
||||
|
||||
DataBase::Exception::Exception():
|
||||
std::exception()
|
||||
{}
|
||||
|
||||
DataBase::Exception::~Exception() {}
|
||||
|
||||
const char* DataBase::Exception::what() const noexcept( true )
|
||||
{
|
||||
std::string* msg = new std::string(getMessage());
|
||||
return msg->c_str();
|
||||
}
|
||||
|
||||
DataBase::Directory::Directory(const std::string& p_path):
|
||||
Exception(),
|
||||
path(p_path) {}
|
||||
|
26
exceptions.h
26
exceptions.h
@ -17,10 +17,22 @@
|
||||
#ifndef CORE_DATABASE_EXCEPTIONS_H
|
||||
#define CORE_DATABASE_EXCEPTIONS_H
|
||||
|
||||
#include "exception.h"
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include "database.h"
|
||||
|
||||
class DataBase::Directory: public Utils::Exception {
|
||||
class DataBase::Exception : public std::exception
|
||||
{
|
||||
public:
|
||||
Exception();
|
||||
virtual ~Exception();
|
||||
|
||||
virtual std::string getMessage() const = 0;
|
||||
|
||||
const char* what() const noexcept( true );
|
||||
};
|
||||
|
||||
class DataBase::Directory: public DataBase::Exception {
|
||||
public:
|
||||
Directory(const std::string& path);
|
||||
|
||||
@ -29,7 +41,7 @@ private:
|
||||
std::string path;
|
||||
};
|
||||
|
||||
class DataBase::Closed : public Utils::Exception {
|
||||
class DataBase::Closed : public DataBase::Exception {
|
||||
public:
|
||||
Closed(const std::string& p_operation, const std::string& dbName, const std::string& tableName);
|
||||
|
||||
@ -40,7 +52,7 @@ private:
|
||||
std::string tableName;
|
||||
};
|
||||
|
||||
class DataBase::Opened : public Utils::Exception {
|
||||
class DataBase::Opened : public DataBase::Exception {
|
||||
public:
|
||||
Opened(const std::string& dbName, const std::string& tableName);
|
||||
|
||||
@ -50,7 +62,7 @@ private:
|
||||
std::string tableName;
|
||||
};
|
||||
|
||||
class DataBase::NotFound : public Utils::Exception {
|
||||
class DataBase::NotFound : public DataBase::Exception {
|
||||
public:
|
||||
NotFound(const std::string& key, const std::string& dbName, const std::string& tableName);
|
||||
|
||||
@ -61,7 +73,7 @@ private:
|
||||
std::string tableName;
|
||||
};
|
||||
|
||||
class DataBase::Exist : public Utils::Exception {
|
||||
class DataBase::Exist : public DataBase::Exception {
|
||||
public:
|
||||
Exist(const std::string& key, const std::string& dbName, const std::string& tableName);
|
||||
|
||||
@ -72,7 +84,7 @@ private:
|
||||
std::string tableName;
|
||||
};
|
||||
|
||||
class DataBase::Unknown : public Utils::Exception {
|
||||
class DataBase::Unknown : public DataBase::Exception {
|
||||
public:
|
||||
Unknown(const std::string& dbName, const std::string& message, const std::optional<std::string>& tableName = std::nullopt);
|
||||
|
||||
|
34
main.cpp
34
main.cpp
@ -1,34 +0,0 @@
|
||||
#include <iostream>
|
||||
|
||||
#include "database.h"
|
||||
#include "table.h"
|
||||
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
|
||||
DataBase base("test1");
|
||||
DataBase::Table<uint32_t, uint32_t>* table1 = base.addTable<uint32_t, uint32_t>("table1");
|
||||
DataBase::Table<QString, QString>* table2 = base.addTable<QString, QString>("table2");
|
||||
|
||||
base.open();
|
||||
|
||||
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;
|
||||
}
|
@ -47,6 +47,9 @@ private:
|
||||
};
|
||||
|
||||
#include "serializer.hpp"
|
||||
#include "serializer_uint64.hpp"
|
||||
#include "serializer_uint32.hpp"
|
||||
#include "serializer_uint16.hpp"
|
||||
#include "serializer_uint8.hpp"
|
||||
|
||||
#endif // CORE_DATABASE_SERIALIZER_H
|
||||
|
52
serializer_uint16.hpp
Normal file
52
serializer_uint16.hpp
Normal file
@ -0,0 +1,52 @@
|
||||
// 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_UINT16_HPP
|
||||
#define CORE_DATABASE_SERIALIZER_UINT16_HPP
|
||||
|
||||
template<>
|
||||
class DataBase::Serializer<uint16_t>
|
||||
{
|
||||
public:
|
||||
Serializer():value(0) {};
|
||||
Serializer(const uint16_t& p_value):value(p_value) {};
|
||||
~Serializer() {};
|
||||
|
||||
uint16_t deserialize(const MDB_val& data) {
|
||||
std::memcpy(&value, data.mv_data, 2);
|
||||
return value;
|
||||
};
|
||||
MDB_val setData(const uint16_t& data) {
|
||||
value = data;
|
||||
return getData();
|
||||
};
|
||||
MDB_val getData() {
|
||||
MDB_val result;
|
||||
result.mv_data = &value,
|
||||
result.mv_size = 2;
|
||||
return result;
|
||||
};
|
||||
void clear() {}; //not possible;
|
||||
|
||||
private:
|
||||
uint16_t value;
|
||||
};
|
||||
|
||||
|
||||
#endif //CORE_DATABASE_SERIALIZER_UINT16_HPP
|
||||
|
||||
|
51
serializer_uint64.hpp
Normal file
51
serializer_uint64.hpp
Normal file
@ -0,0 +1,51 @@
|
||||
// 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_UINT64_HPP
|
||||
#define CORE_DATABASE_SERIALIZER_UINT64_HPP
|
||||
|
||||
template<>
|
||||
class DataBase::Serializer<uint64_t>
|
||||
{
|
||||
public:
|
||||
Serializer():value(0) {};
|
||||
Serializer(const uint64_t& p_value):value(p_value) {};
|
||||
~Serializer() {};
|
||||
|
||||
uint64_t deserialize(const MDB_val& data) {
|
||||
std::memcpy(&value, data.mv_data, 8);
|
||||
return value;
|
||||
};
|
||||
MDB_val setData(const uint64_t& data) {
|
||||
value = data;
|
||||
return getData();
|
||||
};
|
||||
MDB_val getData() {
|
||||
MDB_val result;
|
||||
result.mv_data = &value,
|
||||
result.mv_size = 8;
|
||||
return result;
|
||||
};
|
||||
void clear() {}; //not possible;
|
||||
|
||||
private:
|
||||
uint64_t value;
|
||||
};
|
||||
|
||||
|
||||
#endif //CORE_DATABASE_SERIALIZER_UINT64_HPP
|
||||
|
53
serializer_uint8.hpp
Normal file
53
serializer_uint8.hpp
Normal file
@ -0,0 +1,53 @@
|
||||
// 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_UINT8_HPP
|
||||
#define CORE_DATABASE_SERIALIZER_UINT8_HPP
|
||||
|
||||
template<>
|
||||
class DataBase::Serializer<uint8_t>
|
||||
{
|
||||
public:
|
||||
Serializer():value(0) {};
|
||||
Serializer(const uint8_t& p_value):value(p_value) {};
|
||||
~Serializer() {};
|
||||
|
||||
uint8_t deserialize(const MDB_val& data) {
|
||||
std::memcpy(&value, data.mv_data, 1);
|
||||
return value;
|
||||
};
|
||||
MDB_val setData(const uint8_t& data) {
|
||||
value = data;
|
||||
return getData();
|
||||
};
|
||||
MDB_val getData() {
|
||||
MDB_val result;
|
||||
result.mv_data = &value,
|
||||
result.mv_size = 1;
|
||||
return result;
|
||||
};
|
||||
void clear() {}; //not possible;
|
||||
|
||||
private:
|
||||
uint8_t value;
|
||||
};
|
||||
|
||||
|
||||
#endif //CORE_DATABASE_SERIALIZER_UINT8_HPP
|
||||
|
||||
|
||||
|
158
storage.h
158
storage.h
@ -1,158 +0,0 @@
|
||||
/*
|
||||
* 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_STORAGE_H
|
||||
#define CORE_STORAGE_H
|
||||
|
||||
#include <QString>
|
||||
#include <lmdb.h>
|
||||
|
||||
|
||||
#include "exception.h"
|
||||
|
||||
namespace Core {
|
||||
|
||||
/**
|
||||
* @todo write docs
|
||||
*/
|
||||
template <class K, class V>
|
||||
class Storage
|
||||
{
|
||||
public:
|
||||
Storage(const QString& name);
|
||||
~Storage();
|
||||
|
||||
void open();
|
||||
void close();
|
||||
|
||||
void addRecord(const K& key, const V& value);
|
||||
void changeRecord(const K& key, const V& value);
|
||||
void removeRecord(const K& key);
|
||||
V getRecord(const K& key) const;
|
||||
QString getName() const;
|
||||
|
||||
|
||||
private:
|
||||
QString name;
|
||||
bool opened;
|
||||
MDB_env* environment;
|
||||
MDB_dbi base;
|
||||
|
||||
public:
|
||||
class Directory:
|
||||
public Utils::Exception
|
||||
{
|
||||
public:
|
||||
Directory(const std::string& p_path):Exception(), path(p_path){}
|
||||
|
||||
std::string getMessage() const{return "Can't create directory for database at " + path;}
|
||||
private:
|
||||
std::string path;
|
||||
};
|
||||
|
||||
class Closed:
|
||||
public Utils::Exception
|
||||
{
|
||||
public:
|
||||
Closed(const std::string& op, const std::string& acc):Exception(), operation(op), account(acc){}
|
||||
|
||||
std::string getMessage() const{return "An attempt to perform operation " + operation + " on closed archive for " + account;}
|
||||
private:
|
||||
std::string operation;
|
||||
std::string account;
|
||||
};
|
||||
|
||||
class NotFound:
|
||||
public Utils::Exception
|
||||
{
|
||||
public:
|
||||
NotFound(const std::string& k, const std::string& acc):Exception(), key(k), account(acc){}
|
||||
|
||||
std::string getMessage() const{return "Element for id " + key + " wasn't found in database " + account;}
|
||||
private:
|
||||
std::string key;
|
||||
std::string account;
|
||||
};
|
||||
|
||||
class 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 Exist:
|
||||
public Utils::Exception
|
||||
{
|
||||
public:
|
||||
Exist(const std::string& acc, const std::string& p_key):Exception(), account(acc), key(p_key){}
|
||||
|
||||
std::string getMessage() const{return "An attempt to insert element " + key + " to database " + account + " but it already has an element with given id";}
|
||||
private:
|
||||
std::string account;
|
||||
std::string key;
|
||||
};
|
||||
|
||||
class 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 Unknown:
|
||||
public Utils::Exception
|
||||
{
|
||||
public:
|
||||
Unknown(const std::string& acc, const std::string& message):Exception(), account(acc), msg(message){}
|
||||
|
||||
std::string getMessage() const{return "Unknown error on database " + account + ": " + msg;}
|
||||
private:
|
||||
std::string account;
|
||||
std::string msg;
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
MDB_val& operator << (MDB_val& data, QString& value);
|
||||
MDB_val& operator >> (MDB_val& data, QString& value);
|
||||
|
||||
MDB_val& operator << (MDB_val& data, uint32_t& value);
|
||||
MDB_val& operator >> (MDB_val& data, uint32_t& value);
|
||||
|
||||
namespace std {
|
||||
std::string to_string(const QString& str);
|
||||
}
|
||||
|
||||
#include "storage.hpp"
|
||||
|
||||
#endif // CORE_STORAGE_H
|
226
storage.hpp
226
storage.hpp
@ -1,226 +0,0 @@
|
||||
/*
|
||||
* 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_STORAGE_HPP
|
||||
#define CORE_STORAGE_HPP
|
||||
|
||||
#include <QStandardPaths>
|
||||
#include <QDir>
|
||||
|
||||
#include "storage.h"
|
||||
#include <cstring>
|
||||
|
||||
template <class K, class V>
|
||||
Core::Storage<K, V>::Storage(const QString& p_name):
|
||||
name(p_name),
|
||||
opened(false),
|
||||
environment(),
|
||||
base()
|
||||
{
|
||||
}
|
||||
|
||||
template <class K, class V>
|
||||
Core::Storage<K, V>::~Storage()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
template <class K, class V>
|
||||
void Core::Storage<K, V>::open()
|
||||
{
|
||||
if (!opened) {
|
||||
mdb_env_create(&environment);
|
||||
QString path(QStandardPaths::writableLocation(QStandardPaths::CacheLocation));
|
||||
path += "/" + name;
|
||||
QDir cache(path);
|
||||
|
||||
if (!cache.exists()) {
|
||||
bool res = cache.mkpath(path);
|
||||
if (!res) {
|
||||
throw Directory(path.toStdString());
|
||||
}
|
||||
}
|
||||
|
||||
mdb_env_set_maxdbs(environment, 1);
|
||||
mdb_env_set_mapsize(environment, 10UL * 1024UL * 1024UL);
|
||||
mdb_env_open(environment, path.toStdString().c_str(), 0, 0664);
|
||||
|
||||
MDB_txn *txn;
|
||||
mdb_txn_begin(environment, NULL, 0, &txn);
|
||||
mdb_dbi_open(txn, "base", MDB_CREATE, &base);
|
||||
mdb_txn_commit(txn);
|
||||
opened = true;
|
||||
}
|
||||
}
|
||||
|
||||
template <class K, class V>
|
||||
void Core::Storage<K, V>::close()
|
||||
{
|
||||
if (opened) {
|
||||
mdb_dbi_close(environment, base);
|
||||
mdb_env_close(environment);
|
||||
opened = false;
|
||||
}
|
||||
}
|
||||
|
||||
template <class K, class V>
|
||||
void Core::Storage<K, V>::addRecord(const K& key, const V& value)
|
||||
{
|
||||
if (!opened) {
|
||||
throw Closed("addRecord", name.toStdString());
|
||||
}
|
||||
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();
|
||||
MDB_txn *txn;
|
||||
mdb_txn_begin(environment, NULL, 0, &txn);
|
||||
int rc;
|
||||
rc = mdb_put(txn, base, &lmdbKey, &lmdbData, MDB_NOOVERWRITE);
|
||||
if (rc != 0) {
|
||||
mdb_txn_abort(txn);
|
||||
if (rc == MDB_KEYEXIST) {
|
||||
throw Exist(name.toStdString(), std::to_string(key));
|
||||
} else {
|
||||
throw Unknown(name.toStdString(), mdb_strerror(rc));
|
||||
}
|
||||
} else {
|
||||
mdb_txn_commit(txn);
|
||||
}
|
||||
}
|
||||
|
||||
template <class K, class V>
|
||||
void Core::Storage<K, V>::changeRecord(const K& key, const V& value)
|
||||
{
|
||||
if (!opened) {
|
||||
throw Closed("changeRecord", name.toStdString());
|
||||
}
|
||||
|
||||
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();
|
||||
MDB_txn *txn;
|
||||
mdb_txn_begin(environment, NULL, 0, &txn);
|
||||
int rc;
|
||||
rc = mdb_put(txn, base, &lmdbKey, &lmdbData, 0);
|
||||
if (rc != 0) {
|
||||
mdb_txn_abort(txn);
|
||||
if (rc) {
|
||||
throw Unknown(name.toStdString(), mdb_strerror(rc));
|
||||
}
|
||||
} else {
|
||||
mdb_txn_commit(txn);
|
||||
}
|
||||
}
|
||||
|
||||
template <class K, class V>
|
||||
V Core::Storage<K, V>::getRecord(const K& key) const
|
||||
{
|
||||
if (!opened) {
|
||||
throw Closed("addElement", name.toStdString());
|
||||
}
|
||||
|
||||
MDB_val lmdbKey, lmdbData;
|
||||
lmdbKey << key;
|
||||
|
||||
MDB_txn *txn;
|
||||
int rc;
|
||||
mdb_txn_begin(environment, NULL, MDB_RDONLY, &txn);
|
||||
rc = mdb_get(txn, base, &lmdbKey, &lmdbData);
|
||||
if (rc) {
|
||||
mdb_txn_abort(txn);
|
||||
if (rc == MDB_NOTFOUND) {
|
||||
throw NotFound(std::to_string(key), name.toStdString());
|
||||
} else {
|
||||
throw Unknown(name.toStdString(), mdb_strerror(rc));
|
||||
}
|
||||
} else {
|
||||
QByteArray ba((char*)lmdbData.mv_data, lmdbData.mv_size);
|
||||
QDataStream ds(&ba, QIODevice::ReadOnly);
|
||||
V value;
|
||||
ds >> value;
|
||||
mdb_txn_abort(txn);
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
template <class K, class V>
|
||||
void Core::Storage<K, V>::removeRecord(const K& key)
|
||||
{
|
||||
if (!opened) {
|
||||
throw Closed("addElement", name.toStdString());
|
||||
}
|
||||
|
||||
MDB_val lmdbKey;
|
||||
lmdbKey << key;
|
||||
|
||||
MDB_txn *txn;
|
||||
int rc;
|
||||
mdb_txn_begin(environment, NULL, 0, &txn);
|
||||
rc = mdb_del(txn, base, &lmdbKey, NULL);
|
||||
if (rc) {
|
||||
mdb_txn_abort(txn);
|
||||
if (rc == MDB_NOTFOUND) {
|
||||
throw NotFound(std::to_string(key), name.toStdString());
|
||||
} else {
|
||||
throw Unknown(name.toStdString(), mdb_strerror(rc));
|
||||
}
|
||||
} else {
|
||||
mdb_txn_commit(txn);
|
||||
}
|
||||
}
|
||||
|
||||
template <class K, class V>
|
||||
QString Core::Storage<K, V>::getName() const {
|
||||
return name;}
|
||||
|
||||
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, 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_STORAGE_HPP
|
20
table.hpp
20
table.hpp
@ -144,11 +144,26 @@ 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<uint64_t>(MDB_txn* transaction) {
|
||||
return mdb_dbi_open(transaction, name.c_str(), MDB_CREATE | MDB_INTEGERKEY, &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<>
|
||||
inline int DataBase::_Table::makeTable<uint16_t>(MDB_txn* transaction) {
|
||||
return mdb_dbi_open(transaction, name.c_str(), MDB_CREATE | MDB_INTEGERKEY, &dbi);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline int DataBase::_Table::makeTable<uint8_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);
|
||||
@ -159,4 +174,9 @@ inline std::string DataBase::_Table::toString(const QString& value) {
|
||||
return value.toStdString();
|
||||
}
|
||||
|
||||
template<>
|
||||
inline std::string DataBase::_Table::toString(const std::string& value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
#endif //CORE_TABLE_HPP
|
||||
|
22
test/CMakeLists.txt
Normal file
22
test/CMakeLists.txt
Normal file
@ -0,0 +1,22 @@
|
||||
enable_testing()
|
||||
find_package(GTest REQUIRED)
|
||||
include_directories(${GTEST_INCLUDE_DIR})
|
||||
|
||||
add_executable(runUnitTests
|
||||
basic.cpp
|
||||
)
|
||||
|
||||
target_compile_options(runUnitTests PRIVATE -fPIC)
|
||||
|
||||
target_include_directories(runUnitTests PRIVATE ${CMAKE_SOURCE_DIR})
|
||||
target_include_directories(runUnitTests PRIVATE ${Qt${QT_VERSION_MAJOR}_INCLUDE_DIRS})
|
||||
target_include_directories(runUnitTests PRIVATE ${Qt${QT_VERSION_MAJOR}Core_INCLUDE_DIRS})
|
||||
|
||||
target_link_libraries(
|
||||
runUnitTests
|
||||
GTest::gtest_main
|
||||
storage
|
||||
)
|
||||
|
||||
include(GoogleTest)
|
||||
gtest_discover_tests(runUnitTests)
|
66
test/basic.cpp
Normal file
66
test/basic.cpp
Normal file
@ -0,0 +1,66 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "database.h"
|
||||
#include "table.h"
|
||||
|
||||
#include <QString>
|
||||
|
||||
class DataBaseTest : public ::testing::Test {
|
||||
protected:
|
||||
DataBaseTest():
|
||||
::testing::Test(),
|
||||
t1(db->getTable<uint32_t, uint32_t>("table1")),
|
||||
t2(db->getTable<QString, QString>("table2")) {}
|
||||
|
||||
~DataBaseTest() {
|
||||
}
|
||||
|
||||
static void SetUpTestSuite() {
|
||||
if (db == nullptr) {
|
||||
db = new DataBase("testBase");
|
||||
db->addTable<uint32_t, uint32_t>("table1");
|
||||
db->addTable<QString, QString>("table2");
|
||||
}
|
||||
}
|
||||
|
||||
static void TearDownTestSuite() {
|
||||
db->close();
|
||||
db->removeDirectory();
|
||||
delete db;
|
||||
db = nullptr;
|
||||
}
|
||||
|
||||
static DataBase* db;
|
||||
|
||||
DataBase::Table<uint32_t, uint32_t>* t1;
|
||||
DataBase::Table<QString, QString>* t2;
|
||||
};
|
||||
|
||||
|
||||
DataBase* DataBaseTest::db = nullptr;
|
||||
|
||||
TEST_F(DataBaseTest, RemovingDirectory) {
|
||||
EXPECT_EQ(db->removeDirectory(), true);
|
||||
}
|
||||
|
||||
TEST_F(DataBaseTest, OpeningDatabase) {
|
||||
db->open();
|
||||
EXPECT_EQ(db->ready(), true);
|
||||
}
|
||||
|
||||
TEST_F(DataBaseTest, AddingIntegerKey) {
|
||||
EXPECT_EQ(db->ready(), true);
|
||||
t1->addRecord(1, 2);
|
||||
EXPECT_EQ(t1->getRecord(1), 2);
|
||||
}
|
||||
|
||||
TEST_F(DataBaseTest, AddingQStringKey) {
|
||||
EXPECT_EQ(db->ready(), true);
|
||||
t2->addRecord("hello", "world");
|
||||
EXPECT_EQ(t2->getRecord("hello"), "world");
|
||||
}
|
||||
|
||||
TEST_F(DataBaseTest, ClosingDatabase) {
|
||||
db->close();
|
||||
EXPECT_EQ(db->ready(), false);
|
||||
}
|
Loading…
Reference in New Issue
Block a user