diff --git a/include/matador/sql/field.hpp b/include/matador/sql/field.hpp index 6a54329..434f1f5 100644 --- a/include/matador/sql/field.hpp +++ b/include/matador/sql/field.hpp @@ -42,6 +42,7 @@ public: [[nodiscard]] utils::constraints type() const; [[nodiscard]] size_t size() const; [[nodiscard]] int index() const; + [[nodiscard]] utils::value value() const; template std::optional as() const { diff --git a/include/matador/utils/errors.hpp b/include/matador/utils/errors.hpp index d3cad5b..cee2138 100644 --- a/include/matador/utils/errors.hpp +++ b/include/matador/utils/errors.hpp @@ -9,6 +9,7 @@ namespace matador::utils { enum class utils_error { InvalidVersionString, + IdentifierTypeMismatch }; class utils_category_impl final : public std::error_category diff --git a/include/matador/utils/identifier.hpp b/include/matador/utils/identifier.hpp index 5b5b836..b7f04f9 100644 --- a/include/matador/utils/identifier.hpp +++ b/include/matador/utils/identifier.hpp @@ -16,6 +16,7 @@ #include namespace matador::utils { +class value; class identifier_serializer { public: virtual ~identifier_serializer() = default; @@ -94,7 +95,6 @@ public: >; identifier(); - template, int> = 0> explicit identifier(Type &&id) : value_(std::forward(id)) { @@ -117,17 +117,20 @@ public: } identifier& operator=(const std::string &value); - identifier& operator=(const char *value); - template && std::is_same_v, bool>, int> = 0> - identifier &operator=(Type &&value) { + identifier& operator=(Type &&value) { value_ = std::forward(value); return *this; } ~identifier() = default; + result assign(const value &val); + + static result from_value(const value &val); + [[nodiscard]] database_type to_database_type() const; + bool operator==(const identifier &x) const; bool operator!=(const identifier &x) const; bool operator<(const identifier &x) const; diff --git a/source/core/utils/errors.cpp b/source/core/utils/errors.cpp index e1e6236..5d0e133 100644 --- a/source/core/utils/errors.cpp +++ b/source/core/utils/errors.cpp @@ -10,6 +10,8 @@ std::string utils_category_impl::message(const int ev) const { switch (static_cast(ev)) { case (utils_error::InvalidVersionString): return "Invalid version string"; + case utils_error::IdentifierTypeMismatch: + return "Identifier type mismatch"; default: return "Unknown error"; } diff --git a/source/core/utils/identifier.cpp b/source/core/utils/identifier.cpp index 49b8e6c..9168641 100644 --- a/source/core/utils/identifier.cpp +++ b/source/core/utils/identifier.cpp @@ -1,7 +1,9 @@ #include "matador/utils/identifier.hpp" +#include "matador/utils/value.hpp" +#include "matador/utils/errors.hpp" + #include -#include #include namespace matador::utils { @@ -121,6 +123,73 @@ identifier & identifier::operator=(const char *value) { return *this; } +result identifier::from_value(const value &val) { + identifier id; + if (const auto result = id.assign(val); result.is_error()) { + return failure(result.err()); + } + + return ok(id); +} + +database_type identifier::to_database_type() const { + return std::visit([](const auto &v) -> database_type { + using T = std::decay_t; + + if constexpr (std::is_same_v) { + return nullptr; + } else if constexpr (std::is_same_v) { + return v; + } else { + return static_cast(v); + } + }, value_); +} + +result identifier::assign(const value &val) { + switch (val.type()) { + case basic_type::Null: + value_ = std::monostate{}; + return ok{}; + + case basic_type::Int8: + if (auto v = val.as()) { value_ = *v; return ok{}; } + break; + case basic_type::Int16: + if (auto v = val.as()) { value_ = *v; return ok{}; } + break; + case basic_type::Int32: + if (auto v = val.as()) { value_ = *v; return ok{}; } + break; + case basic_type::Int64: + if (auto v = val.as()) { value_ = *v; return ok{}; } + break; + + case basic_type::UInt8: + if (auto v = val.as()) { value_ = *v; return ok{}; } + break; + case basic_type::UInt16: + if (auto v = val.as()) { value_ = *v; return ok{}; } + break; + case basic_type::UInt32: + if (auto v = val.as()) { value_ = *v; return ok{}; } + break; + case basic_type::UInt64: + if (auto v = val.as()) { value_ = *v; return ok{}; } + break; + + case basic_type::Text: + case basic_type::Varchar: + if (auto v = val.as()) { value_ = *v; return ok{}; } + break; + + default: + break; + } + + return failure(error{utils_error::IdentifierTypeMismatch}); +} + bool identifier::operator==(const identifier &x) const { return value_ == x.value_; } diff --git a/source/orm/sql/field.cpp b/source/orm/sql/field.cpp index da1349e..fbaa155 100644 --- a/source/orm/sql/field.cpp +++ b/source/orm/sql/field.cpp @@ -62,6 +62,10 @@ int field::index() const { return index_; } +utils::value field::value() const { + return value_; +} + std::ostream &operator<<(std::ostream &out, const field &col) { out << col.str(); return out; diff --git a/test/core/utils/IdentifierTest.cpp b/test/core/utils/IdentifierTest.cpp index 0d5a110..4d746f4 100644 --- a/test/core/utils/IdentifierTest.cpp +++ b/test/core/utils/IdentifierTest.cpp @@ -2,6 +2,7 @@ #include "matador/utils/identifier.hpp" #include "matador/utils/default_type_traits.hpp" +#include "matador/utils/value.hpp" using namespace matador::utils; @@ -279,3 +280,87 @@ TEST_CASE("identifier as() and convert() behave consistently for same integer ty REQUIRE(conv_u64.is_ok()); REQUIRE(*conv_u64 == 77); } + +TEST_CASE("identifier assign from value", "[utils][identifier][assign]") { + identifier id; + + value v1{int32_t{17}}; + const auto r1 = id.assign(v1); + REQUIRE(r1.is_ok()); + REQUIRE(id.is_integer()); + REQUIRE(id.str() == "17"); + + value v2{std::string{"abc"}}; + const auto r2 = id.assign(v2); + REQUIRE(r2.is_ok()); + REQUIRE(id.is_varchar()); + REQUIRE(id.str() == "abc"); + + value v3{basic_type::Double, 0}; + const auto r3 = id.assign(v3); + REQUIRE(r3.is_error()); +} + +TEST_CASE("identifier assign from value with exact type mapping", "[utils][identifier][assign]") { + identifier id; + + SECTION("integer types") { + value v{int16_t{12}}; + const auto r = id.assign(v); + REQUIRE(r.is_ok()); + REQUIRE(id.is_integer()); + REQUIRE(id.str() == "12"); + } + + SECTION("string types") { + value v{std::string{"abc"}}; + const auto r = id.assign(v); + REQUIRE(r.is_ok()); + REQUIRE(id.is_varchar()); + REQUIRE(id.str() == "abc"); + } + + SECTION("null") { + value v{basic_type::Null, 0}; + const auto r = id.assign(v); + REQUIRE(r.is_ok()); + REQUIRE(id.is_null()); + } +} + +TEST_CASE("identifier::to_database_type converts internal value to database_type", "[identifier]") { + SECTION("null identifier becomes nullptr") { + identifier id{nullptr}; + + const auto db_value = id.to_database_type(); + + REQUIRE(std::holds_alternative(db_value)); + } + + SECTION("signed integral identifier becomes same signed database type") { + identifier id{int32_t{42}}; + + const auto db_value = id.to_database_type(); + + REQUIRE(std::holds_alternative(db_value)); + REQUIRE(std::get(db_value) == 42); + } + + SECTION("unsigned integral identifier becomes same unsigned database type") { + identifier id{uint64_t{123456789ULL}}; + + const auto db_value = id.to_database_type(); + + REQUIRE(std::holds_alternative(db_value)); + REQUIRE(std::get(db_value) == 123456789ULL); + } + + SECTION("string identifier becomes std::string database type") { + identifier id{std::string{"customer_1"}}; + + const auto db_value = id.to_database_type(); + + REQUIRE(std::holds_alternative(db_value)); + REQUIRE(std::get(db_value) == "customer_1"); + } +} \ No newline at end of file