/*------------------------------------------------------------------------------* * Architecture & Implementation of DBMS * *------------------------------------------------------------------------------* * Copyright 2022 Databases and Information Systems Group TU Dortmund * * Visit us at * * http://dbis.cs.tu-dortmund.de/cms/en/home/ * * * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR * * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * * OTHER DEALINGS IN THE SOFTWARE. * * * * Authors: * * Maximilian Berens * * Roland Kühn * * Jan Mühlig * *------------------------------------------------------------------------------* */ #pragma once #include "type.h" #include #include #include #include #include #include namespace beedb::table { /** * Represents a value within a tuple. * A value contains the raw value and a type. * The raw value will be interpreted as the given type. */ class Value { friend std::ostream &operator<<(std::ostream &stream, const Value &value); public: using value_type = std::variant; Value() = default; Value(const Type type, const value_type &value) : _type(type), _value(value) { } Value(const Type type, value_type &&value) : _type(type), _value(std::move(value)) { } Value(const Value &) = default; Value(Value &&) = default; Value &operator=(const Value &other) { _value = other._value; return *this; } Value &operator=(Value &&other) noexcept { _value = std::move(other._value); return *this; } ~Value() = default; /** * @return Raw value. */ [[nodiscard]] const value_type &value() const { return _value; } [[nodiscard]] value_type &value() { return _value; } /** * @return Value interpreted as given data type. */ template [[nodiscard]] T get() const { return std::get(_value); } /** * Updates the value. * * @param value New value. */ void value(const value_type &value) { _value = value; } /** * @return Type of the value. */ [[nodiscard]] const Type &type() const { return _type; } bool operator==(const Type &type) const { return _type == type; } bool operator==(const Type::Id type_id) const { return _type == type_id; } bool operator==(const Value &other) const { if (_type != other._type) { return false; } if (_type == Type::Id::CHAR) { if (std::holds_alternative(_value) && std::holds_alternative(other._value)) { return std::get(_value) == std::get(other._value); } else if (std::holds_alternative(other._value) && std::holds_alternative(_value)) { return std::get(_value) == std::get(other._value); } } return _value == other._value; } bool operator!=(const Value &other) const { if (_type != other._type) { return true; } if (_type == Type::Id::CHAR) { if (std::holds_alternative(_value) && std::holds_alternative(other._value)) { return std::get(_value) != std::get(other._value); } else if (std::holds_alternative(other._value) && std::holds_alternative(_value)) { return std::get(_value) != std::get(other._value); } } return _value != other._value; } bool operator<=(const Value &other) const { if (_type != other._type) { return false; } if (_type == Type::Id::CHAR) { if (std::holds_alternative(_value) && std::holds_alternative(other._value)) { return std::get(_value) <= std::get(other._value); } else if (std::holds_alternative(other._value) && std::holds_alternative(_value)) { return std::get(_value) <= std::get(other._value); } } return _value <= other._value; } bool operator<(const Value &other) const { if (_type != other._type) { return false; } if (_type == Type::Id::CHAR) { if (std::holds_alternative(_value) && std::holds_alternative(other._value)) { return std::get(_value) < std::get(other._value); } else if (std::holds_alternative(other._value) && std::holds_alternative(_value)) { return std::get(_value) < std::get(other._value); } } return _value < other._value; } bool operator>=(const Value &other) const { if (_type != other._type) { return false; } if (_type == Type::Id::CHAR) { if (std::holds_alternative(_value) && std::holds_alternative(other._value)) { return std::get(_value) >= std::get(other._value); } else if (std::holds_alternative(other._value) && std::holds_alternative(_value)) { return std::get(_value) >= std::get(other._value); } } return _value >= other._value; } bool operator>(const Value &other) const { if (_type != other._type) { return false; } if (_type == Type::Id::CHAR) { if (std::holds_alternative(_value) && std::holds_alternative(other._value)) { return std::get(_value) > std::get(other._value); } else if (std::holds_alternative(other._value) && std::holds_alternative(_value)) { return std::get(_value) > std::get(other._value); } } return _value > other._value; } bool operator==(const std::nullptr_t) const { if (_type == Type::INT) { return std::get(_value) == std::numeric_limits::min(); } else if (_type == Type::LONG) { return std::get(_value) == std::numeric_limits::min(); } else if (_type == Type::DECIMAL) { return std::get(_value) == std::numeric_limits::min(); } else if (_type == Type::CHAR) { if (std::holds_alternative(_value)) { return std::get(_value).empty(); } else if (std::holds_alternative(_value)) { return std::get(_value).empty(); } } else if (_type == Type::DATE) { return std::get(_value) == nullptr; } return true; } /** * @return Maximal value for the stored type. */ [[nodiscard]] Value max() const { if (_type == Type::INT) { return {_type, value_type{std::numeric_limits::max()}}; } else if (_type == Type::LONG) { return {_type, std::numeric_limits::max()}; } else if (_type == Type::DECIMAL) { return {_type, std::numeric_limits::max()}; } else if (_type == Type::CHAR) { return {_type, std::string{""}}; } else { return {_type, 0}; } } /** * @return Minimal value for the stored type. */ [[nodiscard]] [[maybe_unused]] Value min() const { if (_type == Type::INT) { return {_type, std::numeric_limits::min() + 1}; } else if (_type == Type::LONG) { return {_type, std::numeric_limits::min() + 1}; } else if (_type == Type::DECIMAL) { return {_type, std::numeric_limits::min() + 1}; } else if (_type == Type::CHAR) { return {_type, std::string{""}}; } else if (_type == Type::DATE) { return {_type, Date{1, 1, 1}}; } else { return {_type, 0}; } } Value &operator+=(const Value &other) { if (_type == other._type) { if (_type == Type::INT) { _value = std::get(_value) + std::get(other._value); } else if (_type == Type::LONG) { _value = std::get(_value) + std::get(other._value); } else if (_type == Type::DECIMAL) { _value = std::get(_value) + std::get(other._value); } else if (_type == Type::DATE) { // TODO: Implement Date + Date } } return *this; } static table::Value make_zero(const Type type) { if (type == table::Type::LONG) { return {type, std::int64_t{0}}; } else if (type == table::Type::INT) { return {type, std::int32_t{0}}; } else if (type == table::Type::DECIMAL) { return {type, double{0.0}}; } else if (type == Type::DATE) { return {type, Date{}}; } else { return {type, std::string{""}}; } } static table::Value make_null(const Type type) { if (type == table::Type::LONG) { return {type, std::numeric_limits::min()}; } else if (type == table::Type::INT) { return {type, std::numeric_limits::min()}; } else if (type == table::Type::DECIMAL) { return {type, std::numeric_limits::min()}; } else if (type == Type::DATE) { return {type, Date{}}; } else { return {type, std::string{'\0'}}; } } explicit operator std::string() const { if (*this == nullptr) { return "NULL"; } if (_type == Type::INT) { return std::to_string(std::get(_value)); } else if (_type == Type::LONG) { return std::to_string(std::get(_value)); } else if (_type == Type::DECIMAL) { return std::to_string(std::get(_value)); } else if (_type == Type::CHAR) { if (std::holds_alternative(_value)) { return std::get(_value).substr(0, _type.dynamic_length()); } else if (std::holds_alternative(_value)) { return std::string{std::get(_value).data()}.substr(0, _type.dynamic_length()); } } else if (_type == Type::DATE) { return std::get(_value).to_string(); } return ""; } friend table::Value operator+(const table::Value &left, const table::Value &right) { if (left == Type::Id::CHAR || left == Type::Id::DATE || right == Type::Id::CHAR || right == Type::Id::DATE) { return left; } if (left == Type::Id::DECIMAL || right == Type::Id::DECIMAL) { return {Type::Id::DECIMAL, left.try_get_as() + right.try_get_as()}; } else if (left == Type::Id::LONG || right == Type::Id::LONG) { return {Type::Id::LONG, left.try_get_as() + right.try_get_as()}; } else { return {Type::Id::INT, left.try_get_as() + right.try_get_as()}; } } friend table::Value operator-(const table::Value &left, const table::Value &right) { if (left == Type::Id::CHAR || left == Type::Id::DATE || right == Type::Id::CHAR || right == Type::Id::DATE) { return left; } if (left == Type::Id::DECIMAL || right == Type::Id::DECIMAL) { return {Type::Id::DECIMAL, left.try_get_as() - right.try_get_as()}; } else if (left == Type::Id::LONG || right == Type::Id::LONG) { return {Type::Id::LONG, left.try_get_as() - right.try_get_as()}; } else { return {Type::Id::INT, left.try_get_as() - right.try_get_as()}; } } friend table::Value operator/(const table::Value &left, const table::Value &right) { if (left == Type::Id::CHAR || left == Type::Id::DATE || right == Type::Id::CHAR || right == Type::Id::DATE) { return left; } if (left == Type::Id::DECIMAL || right == Type::Id::DECIMAL) { return {Type::Id::DECIMAL, left.try_get_as() / right.try_get_as()}; } else if (left == Type::Id::LONG || right == Type::Id::LONG) { return {Type::Id::LONG, left.try_get_as() / right.try_get_as()}; } else { return {Type::Id::INT, left.try_get_as() / right.try_get_as()}; } } friend table::Value operator*(const table::Value &left, const table::Value &right) { if (left == Type::Id::CHAR || left == Type::Id::DATE || right == Type::Id::CHAR || right == Type::Id::DATE) { return left; } if (left == Type::Id::DECIMAL || right == Type::Id::DECIMAL) { return {Type::Id::DECIMAL, left.try_get_as() * right.try_get_as()}; } else if (left == Type::Id::LONG || right == Type::Id::LONG) { return {Type::Id::LONG, left.try_get_as() * right.try_get_as()}; } else { return {Type::Id::INT, left.try_get_as() * right.try_get_as()}; } } template [[nodiscard]] T try_get_as() const { T value; std::visit( [&value](const auto &v) { using VT = std::decay_t; if constexpr (std::is_same::value) { value = v; } else if constexpr ((std::is_same::value && (std::is_same::value || std::is_same::value)) || (std::is_same::value && (std::is_same::value || std::is_same::value)) || (std::is_same::value && (std::is_same::value || std::is_same::value))) { value = static_cast(v); } }, _value); return value; } private: const Type _type; value_type _value{std::int32_t(0U)}; }; } // namespace beedb::table namespace std { template <> struct hash { public: std::size_t operator()(const beedb::table::Value &value) const { std::size_t h; std::visit( [&h](const auto &v) { using T = std::decay_t; h = std::hash()(v); }, value.value()); return h; } }; } // namespace std