/************************************************************************* * * 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 #include 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(uuid_t const &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, 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 job_status_type_st { {"not_started", JobStatusType::NOT_STARTED}, {"in_progress", JobStatusType::IN_PROGRESS}, {"finished", JobStatusType::FINISHED}, {"halted", JobStatusType::HALTED}, {"unknown", JobStatusType::UNKNOWN} }; ENUM_CLASS_OPS(JobStatusType, job_status_type_st); // macro for registering enum classes for PQXX template struct is_enum_class { static constexpr bool value = std::is_enum_v && !std::is_convertible_v; }; template struct is_unordered_map_string { static constexpr bool value = false; }; template struct is_unordered_map_string> { static constexpr bool value = true; }; template struct is_map_key_type_same_as_enum { static constexpr bool value = std::is_same_v && std::is_same_v; }; }; // hash operator to be able use uuid_t as a hashmap key template<> struct std::hash { std::size_t operator()(const db::uuid_t& k) const { using std::size_t; using std::hash; return hash()(hash()(k.uuid_upper) ^ hash()(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, 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::value, "DB_REGISTER_ENUM_TYPE: 'T' must be an enum class"); \ static_assert(db::is_unordered_map_string::value, "DB_REGISTER_ENUM_TYPE: 'lookup_table' must be std::unordered_map"); \ static_assert(db::is_map_key_type_same_as_enum::value, "DB_REGISTER_ENUM_TYPE: 'lookup_table' content must be of type T"); \ \ \ template<> \ std::string const type_name{name}; \ \ template<> \ struct nullness : pqxx::no_null {}; \ \ template<> \ struct string_traits { \ \ 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(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(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{"uuid_t"}; template<> struct nullness : pqxx::no_null {}; template<> struct string_traits { 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(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(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