349 lines
No EOL
11 KiB
C++
349 lines
No EOL
11 KiB
C++
|
|
/*************************************************************************
|
|
*
|
|
* Copyright (c) 2023 Fabian Posch
|
|
*
|
|
* 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 2
|
|
* 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, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
* Boston, MA 02110-1301, USA.
|
|
*
|
|
**************************************************************************
|
|
*/
|
|
|
|
#ifndef __DB_TYPES_HPP__
|
|
#define __DB_TYPES_HPP__
|
|
|
|
#include <iomanip>
|
|
#include <pqxx/pqxx>
|
|
|
|
namespace db {
|
|
|
|
// credentials for database access
|
|
struct db_credentials_t {
|
|
std::string server = "";
|
|
bool server_override = false;
|
|
int port = 0;
|
|
bool port_override = false;
|
|
std::string uname = "";
|
|
bool uname_override = false;
|
|
std::string pwd = "";
|
|
bool pwd_override = false;
|
|
std::string dbase = "";
|
|
bool dbase_override = false;
|
|
int version;
|
|
};
|
|
|
|
// create UUID type to store database 128 UUID type
|
|
struct uuid_t {
|
|
uint64_t uuid_upper, uuid_lower;
|
|
|
|
uuid_t& operator=(uint64_t other) {
|
|
this->uuid_lower = other;
|
|
this->uuid_upper = 0;
|
|
return *this;
|
|
}
|
|
};
|
|
|
|
|
|
// override comparison operator to make our life easier
|
|
inline bool operator==(uuid_t const & lhs, uuid_t const & rhs) {
|
|
return lhs.uuid_upper == rhs.uuid_upper && lhs.uuid_lower == rhs.uuid_lower;
|
|
}
|
|
inline bool operator==(uuid_t const & lhs, int const & rhs) {
|
|
return rhs >= 0 && lhs.uuid_lower == (uint64_t)rhs;
|
|
}
|
|
inline bool operator==(int const & lhs, uuid_t const & rhs) {
|
|
return lhs >= 0 && (uint64_t)lhs == rhs.uuid_lower;
|
|
}
|
|
inline bool operator!=(uuid_t const & lhs, uuid_t const & rhs) {
|
|
return lhs.uuid_upper != rhs.uuid_upper || lhs.uuid_lower != rhs.uuid_lower;
|
|
}
|
|
inline bool operator!=(uuid_t const & lhs, int const & rhs) {
|
|
return rhs < 0 || lhs.uuid_lower != (uint64_t)rhs;
|
|
}
|
|
inline bool operator!=(int const & lhs, uuid_t const & rhs) {
|
|
return lhs < 0 || (uint64_t)lhs != rhs.uuid_lower;
|
|
}
|
|
|
|
|
|
// easier conversion to string
|
|
inline std::string to_string(const uuid_t& value) {
|
|
std::ostringstream uuid_s;
|
|
uuid_s << std::hex;
|
|
uuid_s << std::setfill('0') << std::setw(16) << value.uuid_upper;
|
|
uuid_s << std::setfill('0') << std::setw(16) << value.uuid_lower;
|
|
return uuid_s.str();
|
|
}
|
|
|
|
// stream operations
|
|
inline std::ostream& operator<<(std::ostream& os, const uuid_t& value) {
|
|
os << to_string(value);
|
|
return os;
|
|
};
|
|
|
|
/**
|
|
* @brief Representation of the current status of a job.
|
|
*
|
|
*/
|
|
enum class JobStatusType {NOT_STARTED, IN_PROGRESS, FINISHED, HALTED, UNKNOWN};
|
|
|
|
// string mapping for job status type enum
|
|
static std::unordered_map<std::string, JobStatusType> job_status_type_st {
|
|
{"not_started", JobStatusType::NOT_STARTED},
|
|
{"in_progress", JobStatusType::IN_PROGRESS},
|
|
{"finished", JobStatusType::FINISHED},
|
|
{"halted", JobStatusType::HALTED},
|
|
{"unknown", JobStatusType::UNKNOWN}
|
|
};
|
|
|
|
inline std::string to_string(JobStatusType const &value) {
|
|
std::string str = "unknown";
|
|
|
|
for (auto& it : job_status_type_st) {
|
|
if (it.second == value) {
|
|
str = it.first;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return str;
|
|
};
|
|
|
|
inline std::ostream& operator<<(std::ostream& os, JobStatusType const &rhs) {
|
|
os << to_string(rhs);
|
|
return os;
|
|
};
|
|
|
|
// macro for registering enum classes for PQXX
|
|
|
|
template<typename T>
|
|
struct is_enum_class {
|
|
static constexpr bool value = std::is_enum_v<T> && !std::is_convertible_v<T, int>;
|
|
};
|
|
|
|
template<typename T>
|
|
struct is_unordered_map_string {
|
|
static constexpr bool value = false;
|
|
};
|
|
|
|
template<typename T>
|
|
struct is_unordered_map_string<std::unordered_map<std::string, T>> {
|
|
static constexpr bool value = true;
|
|
};
|
|
|
|
template<typename Map, typename Enum>
|
|
struct is_map_key_type_same_as_enum {
|
|
static constexpr bool value = std::is_same_v<typename Map::key_type, std::string> &&
|
|
std::is_same_v<typename Map::mapped_type, Enum>;
|
|
};
|
|
|
|
};
|
|
|
|
// hash operator to be able use uuid_t as a hashmap key
|
|
template<>
|
|
struct std::hash<db::uuid_t> {
|
|
std::size_t operator()(const db::uuid_t& k) const {
|
|
using std::size_t;
|
|
using std::hash;
|
|
|
|
return hash<size_t>()(hash<uint64_t>()(k.uuid_upper) ^ hash<uint64_t>()(k.uuid_lower));
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* @brief Generate type conversion for enum class macros in the libpqxx library
|
|
*
|
|
* Calling this macro will generate the code necessary for converting enum class types in yaml-cpp internally.
|
|
* For this you additionally need a std::unordered_map<std::string, Type)>, mapping string values to your
|
|
* custom enum class type.
|
|
*
|
|
* For more information see https://libpqxx.readthedocs.io/en/7.4.1/a01360.html
|
|
*
|
|
* @param T The type you want to generate the translation for
|
|
* @param lookup_table the unordered map, mapping string values of your type to the internal representation
|
|
*
|
|
*/
|
|
#define DB_REGISTER_ENUM_CLASS(T, lookup_table, name) \
|
|
namespace pqxx { \
|
|
\
|
|
static_assert(db::is_enum_class<T>::value, "DB_REGISTER_ENUM_TYPE: 'T' must be an enum class"); \
|
|
static_assert(db::is_unordered_map_string<decltype(lookup_table)>::value, "DB_REGISTER_ENUM_TYPE: 'lookup_table' must be std::unordered_map<std::string, T>"); \
|
|
static_assert(db::is_map_key_type_same_as_enum<decltype(lookup_table), T>::value, "DB_REGISTER_ENUM_TYPE: 'lookup_table' content must be of type T"); \
|
|
\
|
|
\
|
|
template<> \
|
|
std::string const type_name<T>{name}; \
|
|
\
|
|
template<> \
|
|
struct nullness<T> : pqxx::no_null<T> {}; \
|
|
\
|
|
template<> \
|
|
struct string_traits<T> { \
|
|
\
|
|
static T from_string(std::string_view text) { \
|
|
\
|
|
/* turn the string_view into a string */ \
|
|
std::string str(text); \
|
|
\
|
|
/* make sure the enum value exists */ \
|
|
if (lookup_table.find(str) == lookup_table.end()) throw pqxx::conversion_error("Could not convert " + str + " to " + name); \
|
|
\
|
|
return lookup_table[str]; \
|
|
}; \
|
|
\
|
|
static zview to_buf(char *begin, char *end, T const &value) { \
|
|
std::string str = ""; \
|
|
\
|
|
for (auto& it : lookup_table) { \
|
|
if (it.second == value) { \
|
|
str = it.first; \
|
|
break; \
|
|
} \
|
|
} \
|
|
\
|
|
/* make sure we actually have enough space for the enum */ \
|
|
if (std::distance(begin, end) < static_cast<signed long>(str.size() + 1)) { \
|
|
std::ostringstream str_str; \
|
|
str_str << "Buffer for "; \
|
|
str_str << name; \
|
|
str_str << " to small"; \
|
|
throw conversion_overrun(str_str.str()); \
|
|
}\
|
|
\
|
|
const char* c_str = str.c_str(); \
|
|
\
|
|
std::memcpy(begin, c_str, sizeof(char) * str.length()); \
|
|
\
|
|
return zview(begin, str.length()); \
|
|
}; \
|
|
\
|
|
static char *into_buf(char *begin, char *end, T const &value) { \
|
|
std::string str = ""; \
|
|
\
|
|
for (auto& it : lookup_table) { \
|
|
if (it.second == value) { \
|
|
str = it.first; \
|
|
break; \
|
|
} \
|
|
} \
|
|
\
|
|
/* make sure we actually have enough space for the enum */ \
|
|
if (std::distance(begin, end) < static_cast<signed long>(str.size() + 1)) { \
|
|
std::ostringstream str_str; \
|
|
str_str << "Buffer for "; \
|
|
str_str << name; \
|
|
str_str << " to small"; \
|
|
throw conversion_overrun(str_str.str()); \
|
|
}\
|
|
\
|
|
const char* c_str = str.c_str(); \
|
|
\
|
|
std::memcpy(begin, c_str, sizeof(char) * str.length()); \
|
|
\
|
|
return begin; \
|
|
}; \
|
|
\
|
|
static std::size_t size_buffer(T const &value) noexcept { \
|
|
\
|
|
std::string str; \
|
|
str = ""; \
|
|
\
|
|
for (auto& it : lookup_table) { \
|
|
if (it.second == value) { \
|
|
str = it.first; \
|
|
break; \
|
|
} \
|
|
} \
|
|
\
|
|
return str.size() + 1; \
|
|
}; \
|
|
}; \
|
|
};
|
|
|
|
|
|
/**
|
|
* @brief Conversion extension for uuid_t for the pqxx library
|
|
*
|
|
* For more information see https://libpqxx.readthedocs.io/en/7.4.1/a01360.html
|
|
*
|
|
*/
|
|
namespace pqxx {
|
|
|
|
template<>
|
|
std::string const type_name<db::uuid_t>{"uuid_t"};
|
|
|
|
template<>
|
|
struct nullness<db::uuid_t> : pqxx::no_null<db::uuid_t> {};
|
|
|
|
template<>
|
|
struct string_traits<db::uuid_t> {
|
|
|
|
static db::uuid_t from_string(std::string_view text) {
|
|
|
|
if (text.size() != 36) {
|
|
throw pqxx::conversion_error("Invalid binary string size for uuid_t. Expected: 36; Actual: " + std::to_string(text.size()));
|
|
}
|
|
|
|
// turn the string_view into a string and remove the '-'
|
|
std::string form(text);
|
|
form.erase(std::remove(form.begin(), form.end(), '-'), form.end());
|
|
|
|
// then parse into numbers
|
|
std::istringstream high_stream(form.substr(0, 16));
|
|
std::istringstream low_stream(form.substr(16, 16));
|
|
|
|
uint64_t low, high;
|
|
|
|
high_stream >> std::hex >> high;
|
|
low_stream >> std::hex >> low;
|
|
|
|
return {high, low};
|
|
};
|
|
|
|
static zview to_buf(char *begin, char *end, db::uuid_t const &value) {
|
|
std::string str = db::to_string(value);
|
|
|
|
// make sure we actually have enough space for the UUID
|
|
if (std::distance(begin, end) < static_cast<signed long>(str.size() + 1)) throw conversion_overrun("Buffer for UUID to small");
|
|
|
|
const char* c_str = str.c_str();
|
|
|
|
std::memcpy(begin, c_str, sizeof(char) * str.length());
|
|
|
|
return zview(begin, str.length());
|
|
};
|
|
|
|
static char *into_buf(char *begin, char *end, db::uuid_t const &value) {
|
|
std::string str = db::to_string(value);
|
|
|
|
// make sure we actually have enough space for the UUID
|
|
if (std::distance(begin, end) < static_cast<signed long>(str.size() + 1)) throw conversion_overrun("Buffer for UUID to small");
|
|
|
|
const char* c_str = str.c_str();
|
|
|
|
std::memcpy(begin, c_str, sizeof(char) * str.length());
|
|
|
|
return begin;
|
|
};
|
|
|
|
constexpr static std::size_t size_buffer([[maybe_unused]] db::uuid_t const &value) noexcept { return sizeof(char) * 33; };
|
|
};
|
|
|
|
};
|
|
|
|
DB_REGISTER_ENUM_CLASS(db::JobStatusType, db::job_status_type_st, "job_status_type");
|
|
|
|
|
|
#endif |