#pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace DB { namespace ErrorCodes { extern const int BAD_TYPE_OF_FIELD; extern const int BAD_GET; extern const int NOT_IMPLEMENTED; extern const int LOGICAL_ERROR; extern const int ILLEGAL_TYPE_OF_ARGUMENT; } constexpr Null NEGATIVE_INFINITY{Null::Value::NegativeInfinity}; constexpr Null POSITIVE_INFINITY{Null::Value::PositiveInfinity}; class Field; using FieldVector = std::vector>; /// Array and Tuple use the same storage type -- FieldVector, but we declare /// distinct types for them, so that the caller can choose whether it wants to /// construct a Field of Array or a Tuple type. An alternative approach would be /// to construct both of these types from FieldVector, and have the caller /// specify the desired Field type explicitly. #define DEFINE_FIELD_VECTOR(X) \ struct X : public FieldVector \ { \ using FieldVector::FieldVector; \ } DEFINE_FIELD_VECTOR(Array); DEFINE_FIELD_VECTOR(Tuple); /// An array with the following structure: [(key1, value1), (key2, value2), ...] DEFINE_FIELD_VECTOR(Map); /// TODO: use map instead of vector. #undef DEFINE_FIELD_VECTOR using FieldMap = std::map, AllocatorWithMemoryTracking>>; #define DEFINE_FIELD_MAP(X) \ struct X : public FieldMap \ { \ using FieldMap::FieldMap; \ } DEFINE_FIELD_MAP(Object); #undef DEFINE_FIELD_MAP struct AggregateFunctionStateData { String name; /// Name with arguments. String data; bool operator < (const AggregateFunctionStateData &) const { throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "Operator < is not implemented for AggregateFunctionStateData."); } bool operator <= (const AggregateFunctionStateData &) const { throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "Operator <= is not implemented for AggregateFunctionStateData."); } bool operator > (const AggregateFunctionStateData &) const { throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "Operator > is not implemented for AggregateFunctionStateData."); } bool operator >= (const AggregateFunctionStateData &) const { throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "Operator >= is not implemented for AggregateFunctionStateData."); } bool operator == (const AggregateFunctionStateData & rhs) const { if (name != rhs.name) throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "Comparing aggregate functions with different types: {} and {}", name, rhs.name); return data == rhs.data; } }; struct CustomType { struct CustomTypeImpl { virtual ~CustomTypeImpl() = default; virtual const char * getTypeName() const = 0; virtual String toString(bool show_secrets) const = 0; virtual bool isSecret() const = 0; virtual bool operator < (const CustomTypeImpl &) const = 0; virtual bool operator <= (const CustomTypeImpl &) const = 0; virtual bool operator > (const CustomTypeImpl &) const = 0; virtual bool operator >= (const CustomTypeImpl &) const = 0; virtual bool operator == (const CustomTypeImpl &) const = 0; }; CustomType() = default; explicit CustomType(std::shared_ptr impl_) : impl(impl_) {} bool isSecret() const { return impl->isSecret(); } const char * getTypeName() const { return impl->getTypeName(); } String toString(bool show_secrets = true) const { return impl->toString(show_secrets); } const CustomTypeImpl & getImpl() { return *impl; } bool operator < (const CustomType & rhs) const { return *impl < *rhs.impl; } bool operator <= (const CustomType & rhs) const { return *impl <= *rhs.impl; } bool operator > (const CustomType & rhs) const { return *impl > *rhs.impl; } bool operator >= (const CustomType & rhs) const { return *impl >= *rhs.impl; } bool operator == (const CustomType & rhs) const { return *impl == *rhs.impl; } std::shared_ptr impl; }; template bool decimalEqual(T x, T y, UInt32 x_scale, UInt32 y_scale); template bool decimalLess(T x, T y, UInt32 x_scale, UInt32 y_scale); template bool decimalLessOrEqual(T x, T y, UInt32 x_scale, UInt32 y_scale); template class DecimalField { public: explicit DecimalField(T value = {}, UInt32 scale_ = 0) : dec(value), scale(scale_) {} operator T() const { return dec; } /// NOLINT T getValue() const { return dec; } T getScaleMultiplier() const { return DecimalUtils::scaleMultiplier(scale); } UInt32 getScale() const { return scale; } template bool operator < (const DecimalField & r) const { using MaxType = std::conditional_t<(sizeof(T) > sizeof(U)), T, U>; return decimalLess(dec, r.getValue(), scale, r.getScale()); } template bool operator <= (const DecimalField & r) const { using MaxType = std::conditional_t<(sizeof(T) > sizeof(U)), T, U>; return decimalLessOrEqual(dec, r.getValue(), scale, r.getScale()); } template bool operator == (const DecimalField & r) const { using MaxType = std::conditional_t<(sizeof(T) > sizeof(U)), T, U>; return decimalEqual(dec, r.getValue(), scale, r.getScale()); } template bool operator > (const DecimalField & r) const { return r < *this; } template bool operator >= (const DecimalField & r) const { return r <= * this; } template bool operator != (const DecimalField & r) const { return !(*this == r); } const DecimalField & operator += (const DecimalField & r) { if (scale != r.getScale()) throw Exception(ErrorCodes::LOGICAL_ERROR, "Add different decimal fields"); dec += r.getValue(); return *this; } const DecimalField & operator -= (const DecimalField & r) { if (scale != r.getScale()) throw Exception(ErrorCodes::LOGICAL_ERROR, "Sub different decimal fields"); dec -= r.getValue(); return *this; } private: T dec; UInt32 scale; }; template constexpr bool is_decimal_field = false; template <> constexpr inline bool is_decimal_field> = true; template <> constexpr inline bool is_decimal_field> = true; template <> constexpr inline bool is_decimal_field> = true; template <> constexpr inline bool is_decimal_field> = true; template struct NearestFieldTypeImpl; template using NearestFieldType = typename NearestFieldTypeImpl::Type; /// char may be signed or unsigned, and behave identically to signed char or unsigned char, /// but they are always three different types. /// signedness of char is different in Linux on x86 and Linux on ARM. template <> struct NearestFieldTypeImpl { using Type = std::conditional_t, Int64, UInt64>; }; template <> struct NearestFieldTypeImpl { using Type = Int64; }; template <> struct NearestFieldTypeImpl { using Type = UInt64; }; #ifdef __cpp_char8_t template <> struct NearestFieldTypeImpl { using Type = UInt64; }; #endif template <> struct NearestFieldTypeImpl { using Type = UInt64; }; template <> struct NearestFieldTypeImpl { using Type = UInt64; }; template <> struct NearestFieldTypeImpl { using Type = UInt64; }; template <> struct NearestFieldTypeImpl { using Type = UUID; }; template <> struct NearestFieldTypeImpl { using Type = IPv4; }; template <> struct NearestFieldTypeImpl { using Type = IPv6; }; template <> struct NearestFieldTypeImpl { using Type = Int64; }; template <> struct NearestFieldTypeImpl { using Type = Int64; }; /// long and long long are always different types that may behave identically or not. /// This is different on Linux and Mac. template <> struct NearestFieldTypeImpl { using Type = Int64; }; /// NOLINT template <> struct NearestFieldTypeImpl { using Type = Int64; }; /// NOLINT template <> struct NearestFieldTypeImpl { using Type = UInt64; }; /// NOLINT template <> struct NearestFieldTypeImpl { using Type = UInt64; }; /// NOLINT template <> struct NearestFieldTypeImpl { using Type = UInt256; }; template <> struct NearestFieldTypeImpl { using Type = Int256; }; template <> struct NearestFieldTypeImpl { using Type = UInt128; }; template <> struct NearestFieldTypeImpl { using Type = Int128; }; template <> struct NearestFieldTypeImpl { using Type = DecimalField; }; template <> struct NearestFieldTypeImpl { using Type = DecimalField; }; template <> struct NearestFieldTypeImpl { using Type = DecimalField; }; template <> struct NearestFieldTypeImpl { using Type = DecimalField; }; template <> struct NearestFieldTypeImpl { using Type = DecimalField; }; template <> struct NearestFieldTypeImpl> { using Type = DecimalField; }; template <> struct NearestFieldTypeImpl> { using Type = DecimalField; }; template <> struct NearestFieldTypeImpl> { using Type = DecimalField; }; template <> struct NearestFieldTypeImpl> { using Type = DecimalField; }; template <> struct NearestFieldTypeImpl> { using Type = DecimalField; }; template <> struct NearestFieldTypeImpl { using Type = Float64; }; template <> struct NearestFieldTypeImpl { using Type = Float64; }; template <> struct NearestFieldTypeImpl { using Type = String; }; template <> struct NearestFieldTypeImpl { using Type = String; }; template <> struct NearestFieldTypeImpl { using Type = String; }; template <> struct NearestFieldTypeImpl { using Type = Array; }; template <> struct NearestFieldTypeImpl { using Type = Tuple; }; template <> struct NearestFieldTypeImpl { using Type = Map; }; template <> struct NearestFieldTypeImpl { using Type = Object; }; template <> struct NearestFieldTypeImpl { using Type = UInt64; }; template <> struct NearestFieldTypeImpl { using Type = Null; }; template <> struct NearestFieldTypeImpl { using Type = AggregateFunctionStateData; }; template <> struct NearestFieldTypeImpl { using Type = CustomType; }; // For enum types, use the field type that corresponds to their underlying type. template requires std::is_enum_v struct NearestFieldTypeImpl { using Type = NearestFieldType>; }; template decltype(auto) castToNearestFieldType(T && x) { using U = NearestFieldType>; if constexpr (std::is_same_v, U>) return std::forward(x); else return U(x); } template concept not_field_or_bool_or_stringlike = (!std::is_same_v, Field> && !std::is_same_v, bool> && !std::is_same_v>, String>); /** 32 is enough. Round number is used for alignment and for better arithmetic inside std::vector. * NOTE: Actually, sizeof(std::string) is 32 when using libc++, so Field is 40 bytes. */ #define DBMS_MIN_FIELD_SIZE 32 /** Discriminated union of several types. * Made for replacement of `boost::variant` * is not generalized, * but somewhat more efficient, and simpler. * * Used to represent a single value of one of several types in memory. * Warning! Prefer to use chunks of columns instead of single values. See IColumn.h */ class Field { public: struct Types { /// Type tag. enum Which { Null = 0, UInt64 = 1, Int64 = 2, Float64 = 3, UInt128 = 4, Int128 = 5, String = 16, Array = 17, Tuple = 18, Decimal32 = 19, Decimal64 = 20, Decimal128 = 21, AggregateFunctionState = 22, Decimal256 = 23, UInt256 = 24, Int256 = 25, Map = 26, UUID = 27, Bool = 28, Object = 29, IPv4 = 30, IPv6 = 31, CustomType = 32, }; }; /// Returns an identifier for the type or vice versa. template struct TypeToEnum; template struct EnumToType; static bool isDecimal(Types::Which which) { return which == Types::Decimal32 || which == Types::Decimal64 || which == Types::Decimal128 || which == Types::Decimal256; } Field() : Field(Null{}) {} /** Despite the presence of a template constructor, this constructor is still needed, * since, in its absence, the compiler will still generate the default constructor. */ Field(const Field & rhs) { create(rhs); } Field(Field && rhs) noexcept { create(std::move(rhs)); } template requires not_field_or_bool_or_stringlike Field(T && rhs); /// NOLINT Field(bool rhs) : Field(castToNearestFieldType(rhs)) /// NOLINT { which = Types::Bool; } /// Create a string inplace. Field(std::string_view str) { create(str.data(), str.size()); } /// NOLINT Field(const String & str) { create(std::string_view{str}); } /// NOLINT Field(String && str) { create(std::move(str)); } /// NOLINT Field(const char * str) { create(std::string_view{str}); } /// NOLINT template Field(const CharT * data, size_t size) { create(data, size); } Field & operator= (const Field & rhs) { if (this != &rhs) { if (which != rhs.which) { destroy(); create(rhs); } else assign(rhs); /// This assigns string or vector without deallocation of existing buffer. } return *this; } Field & operator= (Field && rhs) noexcept { if (this != &rhs) { if (which != rhs.which) { destroy(); create(std::move(rhs)); } else assign(std::move(rhs)); } return *this; } /// Allows expressions like /// Field f = 1; /// Things to note: /// 1. float <--> int needs explicit cast /// 2. customized types needs explicit cast template requires not_field_or_bool_or_stringlike Field & /// NOLINT operator=(T && rhs); Field & operator= (bool rhs) { *this = castToNearestFieldType(rhs); which = Types::Bool; return *this; } Field & operator= (std::string_view str); Field & operator= (const String & str) { return *this = std::string_view{str}; } Field & operator= (String && str); Field & operator= (const char * str) { return *this = std::string_view{str}; } ~Field() { destroy(); } Types::Which getType() const { return which; } std::string_view getTypeName() const; bool isNull() const { return which == Types::Null; } template NearestFieldType> & get(); template const auto & get() const { auto * mutable_this = const_cast *>(this); return mutable_this->get(); } bool isNegativeInfinity() const { return which == Types::Null && get().isNegativeInfinity(); } bool isPositiveInfinity() const { return which == Types::Null && get().isPositiveInfinity(); } template bool tryGet(T & result) { const Types::Which requested = TypeToEnum>::value; if (which != requested) return false; result = get(); return true; } template bool tryGet(T & result) const { const Types::Which requested = TypeToEnum>::value; if (which != requested) return false; result = get(); return true; } template auto & safeGet() const { return const_cast(this)->safeGet(); } template auto & safeGet(); bool operator< (const Field & rhs) const { if (which < rhs.which) return true; if (which > rhs.which) return false; switch (which) { case Types::Null: return false; case Types::Bool: [[fallthrough]]; case Types::UInt64: return get() < rhs.get(); case Types::UInt128: return get() < rhs.get(); case Types::UInt256: return get() < rhs.get(); case Types::Int64: return get() < rhs.get(); case Types::Int128: return get() < rhs.get(); case Types::Int256: return get() < rhs.get(); case Types::UUID: return get() < rhs.get(); case Types::IPv4: return get() < rhs.get(); case Types::IPv6: return get() < rhs.get(); case Types::Float64: return get() < rhs.get(); case Types::String: return get() < rhs.get(); case Types::Array: return get() < rhs.get(); case Types::Tuple: return get() < rhs.get(); case Types::Map: return get() < rhs.get(); case Types::Object: return get() < rhs.get(); case Types::Decimal32: return get>() < rhs.get>(); case Types::Decimal64: return get>() < rhs.get>(); case Types::Decimal128: return get>() < rhs.get>(); case Types::Decimal256: return get>() < rhs.get>(); case Types::AggregateFunctionState: return get() < rhs.get(); case Types::CustomType: return get() < rhs.get(); } throw Exception(ErrorCodes::BAD_TYPE_OF_FIELD, "Bad type of Field"); } bool operator> (const Field & rhs) const { return rhs < *this; } bool operator<= (const Field & rhs) const { if (which < rhs.which) return true; if (which > rhs.which) return false; switch (which) { case Types::Null: return true; case Types::Bool: [[fallthrough]]; case Types::UInt64: return get() <= rhs.get(); case Types::UInt128: return get() <= rhs.get(); case Types::UInt256: return get() <= rhs.get(); case Types::Int64: return get() <= rhs.get(); case Types::Int128: return get() <= rhs.get(); case Types::Int256: return get() <= rhs.get(); case Types::UUID: return get().toUnderType() <= rhs.get().toUnderType(); case Types::IPv4: return get() <= rhs.get(); case Types::IPv6: return get() <= rhs.get(); case Types::Float64: return get() <= rhs.get(); case Types::String: return get() <= rhs.get(); case Types::Array: return get() <= rhs.get(); case Types::Tuple: return get() <= rhs.get(); case Types::Map: return get() <= rhs.get(); case Types::Object: return get() <= rhs.get(); case Types::Decimal32: return get>() <= rhs.get>(); case Types::Decimal64: return get>() <= rhs.get>(); case Types::Decimal128: return get>() <= rhs.get>(); case Types::Decimal256: return get>() <= rhs.get>(); case Types::AggregateFunctionState: return get() <= rhs.get(); case Types::CustomType: return get() <= rhs.get(); } throw Exception(ErrorCodes::BAD_TYPE_OF_FIELD, "Bad type of Field"); } bool operator>= (const Field & rhs) const { return rhs <= *this; } // More like bitwise equality as opposed to semantic equality: // Null equals Null and NaN equals NaN. bool operator== (const Field & rhs) const { if (which != rhs.which) return false; switch (which) { case Types::Null: return true; case Types::Bool: [[fallthrough]]; case Types::UInt64: return get() == rhs.get(); case Types::Int64: return get() == rhs.get(); case Types::Float64: { // Compare as UInt64 so that NaNs compare as equal. return std::bit_cast(get()) == std::bit_cast(rhs.get()); } case Types::UUID: return get() == rhs.get(); case Types::IPv4: return get() == rhs.get(); case Types::IPv6: return get() == rhs.get(); case Types::String: return get() == rhs.get(); case Types::Array: return get() == rhs.get(); case Types::Tuple: return get() == rhs.get(); case Types::Map: return get() == rhs.get(); case Types::Object: return get() == rhs.get(); case Types::UInt128: return get() == rhs.get(); case Types::UInt256: return get() == rhs.get(); case Types::Int128: return get() == rhs.get(); case Types::Int256: return get() == rhs.get(); case Types::Decimal32: return get>() == rhs.get>(); case Types::Decimal64: return get>() == rhs.get>(); case Types::Decimal128: return get>() == rhs.get>(); case Types::Decimal256: return get>() == rhs.get>(); case Types::AggregateFunctionState: return get() == rhs.get(); case Types::CustomType: return get() == rhs.get(); } throw Exception(ErrorCodes::BAD_TYPE_OF_FIELD, "Bad type of Field"); } bool operator!= (const Field & rhs) const { return !(*this == rhs); } /// Field is template parameter, to allow universal reference for field, /// that is useful for const and non-const . template static auto dispatch(F && f, FieldRef && field) { switch (field.which) { case Types::Null: return f(field.template get()); case Types::UInt64: return f(field.template get()); case Types::UInt128: return f(field.template get()); case Types::UInt256: return f(field.template get()); case Types::Int64: return f(field.template get()); case Types::Int128: return f(field.template get()); case Types::Int256: return f(field.template get()); case Types::UUID: return f(field.template get()); case Types::IPv4: return f(field.template get()); case Types::IPv6: return f(field.template get()); case Types::Float64: return f(field.template get()); case Types::String: return f(field.template get()); case Types::Array: return f(field.template get()); case Types::Tuple: return f(field.template get()); case Types::Map: return f(field.template get()); case Types::Bool: { bool value = bool(field.template get()); return f(value); } case Types::Object: return f(field.template get()); case Types::Decimal32: return f(field.template get>()); case Types::Decimal64: return f(field.template get>()); case Types::Decimal128: return f(field.template get>()); case Types::Decimal256: return f(field.template get>()); case Types::AggregateFunctionState: return f(field.template get()); case Types::CustomType: return f(field.template get()); } UNREACHABLE(); } String dump() const; static Field restoreFromDump(std::string_view dump_); private: std::aligned_union_t, DecimalField, DecimalField, DecimalField, AggregateFunctionStateData, CustomType > storage; Types::Which which; /// Assuming there was no allocated state or it was deallocated (see destroy). template void createConcrete(T && x) { using UnqualifiedType = std::decay_t; // In both Field and PODArray, small types may be stored as wider types, // e.g. char is stored as UInt64. Field can return this extended value // with get(). To avoid uninitialized results from get(), // we must initialize the entire wide stored type, and not just the // nominal type. using StorageType = NearestFieldType; new (&storage) StorageType(std::forward(x)); which = TypeToEnum::value; } /// Assuming same types. template void assignConcrete(T && x) { using JustT = std::decay_t; assert(which == TypeToEnum::value); JustT * MAY_ALIAS ptr = reinterpret_cast(&storage); *ptr = std::forward(x); } template requires (sizeof(CharT) == 1) void assignString(const CharT * data, size_t size) { assert(which == Types::String); String * ptr = reinterpret_cast(&storage); ptr->assign(reinterpret_cast(data), size); } void assignString(String && str) { assert(which == Types::String); String * ptr = reinterpret_cast(&storage); ptr->assign(std::move(str)); } void create(const Field & x) { dispatch([this] (auto & value) { createConcrete(value); }, x); } void create(Field && x) { dispatch([this] (auto & value) { createConcrete(std::move(value)); }, x); } void assign(const Field & x) { dispatch([this] (auto & value) { assignConcrete(value); }, x); } void assign(Field && x) { dispatch([this] (auto & value) { assignConcrete(std::move(value)); }, x); } template requires (sizeof(CharT) == 1) void create(const CharT * data, size_t size) { new (&storage) String(reinterpret_cast(data), size); which = Types::String; } void create(String && str) { new (&storage) String(std::move(str)); which = Types::String; } ALWAYS_INLINE void destroy() { switch (which) { case Types::String: destroy(); break; case Types::Array: destroy(); break; case Types::Tuple: destroy(); break; case Types::Map: destroy(); break; case Types::Object: destroy(); break; case Types::AggregateFunctionState: destroy(); break; case Types::CustomType: destroy(); break; default: break; } which = Types::Null; /// for exception safety in subsequent calls to destroy and create, when create fails. } template void destroy() { T * MAY_ALIAS ptr = reinterpret_cast(&storage); ptr->~T(); } }; #undef DBMS_MIN_FIELD_SIZE using Row = std::vector; template <> struct Field::TypeToEnum { static constexpr Types::Which value = Types::Null; }; template <> struct Field::TypeToEnum { static constexpr Types::Which value = Types::UInt64; }; template <> struct Field::TypeToEnum { static constexpr Types::Which value = Types::UInt128; }; template <> struct Field::TypeToEnum { static constexpr Types::Which value = Types::UInt256; }; template <> struct Field::TypeToEnum { static constexpr Types::Which value = Types::Int64; }; template <> struct Field::TypeToEnum { static constexpr Types::Which value = Types::Int128; }; template <> struct Field::TypeToEnum { static constexpr Types::Which value = Types::Int256; }; template <> struct Field::TypeToEnum { static constexpr Types::Which value = Types::UUID; }; template <> struct Field::TypeToEnum { static constexpr Types::Which value = Types::IPv4; }; template <> struct Field::TypeToEnum { static constexpr Types::Which value = Types::IPv6; }; template <> struct Field::TypeToEnum { static constexpr Types::Which value = Types::Float64; }; template <> struct Field::TypeToEnum { static constexpr Types::Which value = Types::String; }; template <> struct Field::TypeToEnum { static constexpr Types::Which value = Types::Array; }; template <> struct Field::TypeToEnum { static constexpr Types::Which value = Types::Tuple; }; template <> struct Field::TypeToEnum { static constexpr Types::Which value = Types::Map; }; template <> struct Field::TypeToEnum { static constexpr Types::Which value = Types::Object; }; template <> struct Field::TypeToEnum>{ static constexpr Types::Which value = Types::Decimal32; }; template <> struct Field::TypeToEnum>{ static constexpr Types::Which value = Types::Decimal64; }; template <> struct Field::TypeToEnum>{ static constexpr Types::Which value = Types::Decimal128; }; template <> struct Field::TypeToEnum>{ static constexpr Types::Which value = Types::Decimal256; }; template <> struct Field::TypeToEnum>{ static constexpr Types::Which value = Types::Decimal64; }; template <> struct Field::TypeToEnum{ static constexpr Types::Which value = Types::AggregateFunctionState; }; template <> struct Field::TypeToEnum{ static constexpr Types::Which value = Types::CustomType; }; template <> struct Field::TypeToEnum{ static constexpr Types::Which value = Types::Bool; }; template <> struct Field::EnumToType { using Type = Null; }; template <> struct Field::EnumToType { using Type = UInt64; }; template <> struct Field::EnumToType { using Type = UInt128; }; template <> struct Field::EnumToType { using Type = UInt256; }; template <> struct Field::EnumToType { using Type = Int64; }; template <> struct Field::EnumToType { using Type = Int128; }; template <> struct Field::EnumToType { using Type = Int256; }; template <> struct Field::EnumToType { using Type = UUID; }; template <> struct Field::EnumToType { using Type = IPv4; }; template <> struct Field::EnumToType { using Type = IPv6; }; template <> struct Field::EnumToType { using Type = Float64; }; template <> struct Field::EnumToType { using Type = String; }; template <> struct Field::EnumToType { using Type = Array; }; template <> struct Field::EnumToType { using Type = Tuple; }; template <> struct Field::EnumToType { using Type = Map; }; template <> struct Field::EnumToType { using Type = Object; }; template <> struct Field::EnumToType { using Type = DecimalField; }; template <> struct Field::EnumToType { using Type = DecimalField; }; template <> struct Field::EnumToType { using Type = DecimalField; }; template <> struct Field::EnumToType { using Type = DecimalField; }; template <> struct Field::EnumToType { using Type = AggregateFunctionStateData; }; template <> struct Field::EnumToType { using Type = CustomType; }; template <> struct Field::EnumToType { using Type = UInt64; }; inline constexpr bool isInt64OrUInt64FieldType(Field::Types::Which t) { return t == Field::Types::Int64 || t == Field::Types::UInt64; } inline constexpr bool isInt64OrUInt64orBoolFieldType(Field::Types::Which t) { return t == Field::Types::Int64 || t == Field::Types::UInt64 || t == Field::Types::Bool; } // Field value getter with type checking in debug builds. template NearestFieldType> & Field::get() { // Before storing the value in the Field, we static_cast it to the field // storage type, so here we return the value of storage type as well. // Otherwise, it is easy to make a mistake of reinterpret_casting the stored // value to a different and incompatible type. // For example, a Float32 value is stored as Float64, and it is incorrect to // return a reference to this value as Float32. using StoredType = NearestFieldType>; #ifndef NDEBUG // Disregard signedness when converting between int64 types. constexpr Field::Types::Which target = TypeToEnum::value; if (target != which && (!isInt64OrUInt64orBoolFieldType(target) || !isInt64OrUInt64orBoolFieldType(which)) && target != Field::Types::IPv4) throw Exception(ErrorCodes::LOGICAL_ERROR, "Invalid Field get from type {} to type {}", which, target); #endif StoredType * MAY_ALIAS ptr = reinterpret_cast(&storage); return *ptr; } template auto & Field::safeGet() { const Types::Which requested = TypeToEnum>>::value; if (which != requested) throw Exception(ErrorCodes::BAD_GET, "Bad get: has {}, requested {}", getTypeName(), requested); return get(); } template requires not_field_or_bool_or_stringlike Field::Field(T && rhs) { auto && val = castToNearestFieldType(std::forward(rhs)); createConcrete(std::forward(val)); } template requires not_field_or_bool_or_stringlike Field & /// NOLINT Field::operator=(T && rhs) { auto && val = castToNearestFieldType(std::forward(rhs)); using U = decltype(val); if (which != TypeToEnum>::value) { destroy(); createConcrete(std::forward(val)); } else assignConcrete(std::forward(val)); return *this; } inline Field & Field::operator=(std::string_view str) { if (which != Types::String) { destroy(); create(str.data(), str.size()); } else assignString(str.data(), str.size()); return *this; } inline Field & Field::operator=(String && str) { if (which != Types::String) { destroy(); create(std::move(str)); } else assignString(std::move(str)); return *this; } class ReadBuffer; class WriteBuffer; /// It is assumed that all elements of the array have the same type. void readBinary(Array & x, ReadBuffer & buf); [[noreturn]] inline void readText(Array &, ReadBuffer &) { throw Exception(ErrorCodes::NOT_IMPLEMENTED, "Cannot read Array."); } [[noreturn]] inline void readQuoted(Array &, ReadBuffer &) { throw Exception(ErrorCodes::NOT_IMPLEMENTED, "Cannot read Array."); } /// It is assumed that all elements of the array have the same type. /// Also write size and type into buf. UInt64 and Int64 is written in variadic size form void writeBinary(const Array & x, WriteBuffer & buf); void writeText(const Array & x, WriteBuffer & buf); [[noreturn]] inline void writeQuoted(const Array &, WriteBuffer &) { throw Exception(ErrorCodes::NOT_IMPLEMENTED, "Cannot write Array quoted."); } void readBinary(Tuple & x, ReadBuffer & buf); [[noreturn]] inline void readText(Tuple &, ReadBuffer &) { throw Exception(ErrorCodes::NOT_IMPLEMENTED, "Cannot read Tuple."); } [[noreturn]] inline void readQuoted(Tuple &, ReadBuffer &) { throw Exception(ErrorCodes::NOT_IMPLEMENTED, "Cannot read Tuple."); } void writeBinary(const Tuple & x, WriteBuffer & buf); void writeText(const Tuple & x, WriteBuffer & buf); [[noreturn]] inline void writeQuoted(const Tuple &, WriteBuffer &) { throw Exception(ErrorCodes::NOT_IMPLEMENTED, "Cannot write Tuple quoted."); } void readBinary(Map & x, ReadBuffer & buf); [[noreturn]] inline void readText(Map &, ReadBuffer &) { throw Exception(ErrorCodes::NOT_IMPLEMENTED, "Cannot read Map."); } [[noreturn]] inline void readQuoted(Map &, ReadBuffer &) { throw Exception(ErrorCodes::NOT_IMPLEMENTED, "Cannot read Map."); } void writeBinary(const Map & x, WriteBuffer & buf); void writeText(const Map & x, WriteBuffer & buf); [[noreturn]] inline void writeQuoted(const Map &, WriteBuffer &) { throw Exception(ErrorCodes::NOT_IMPLEMENTED, "Cannot write Map quoted."); } void readBinary(Object & x, ReadBuffer & buf); [[noreturn]] inline void readText(Object &, ReadBuffer &) { throw Exception(ErrorCodes::NOT_IMPLEMENTED, "Cannot read Object."); } [[noreturn]] inline void readQuoted(Object &, ReadBuffer &) { throw Exception(ErrorCodes::NOT_IMPLEMENTED, "Cannot read Object."); } void writeBinary(const Object & x, WriteBuffer & buf); void writeText(const Object & x, WriteBuffer & buf); void writeBinary(const CustomType & x, WriteBuffer & buf); void writeText(const CustomType & x, WriteBuffer & buf); [[noreturn]] inline void writeQuoted(const Object &, WriteBuffer &) { throw Exception(ErrorCodes::NOT_IMPLEMENTED, "Cannot write Object quoted."); } __attribute__ ((noreturn)) inline void writeText(const AggregateFunctionStateData &, WriteBuffer &) { // This probably doesn't make any sense, but we have to have it for // completeness, so that we can use toString(field_value) in field visitors. throw Exception(ErrorCodes::LOGICAL_ERROR, "Cannot convert a Field of type AggregateFunctionStateData to human-readable text"); } template inline void writeText(const DecimalField & value, WriteBuffer & buf, bool trailing_zeros = false) { writeText(value.getValue(), value.getScale(), buf, trailing_zeros); } template void readQuoted(DecimalField & x, ReadBuffer & buf); void writeFieldText(const Field & x, WriteBuffer & buf); String toString(const Field & x); std::string_view fieldTypeToString(Field::Types::Which type); } template <> struct fmt::formatter { static constexpr auto parse(format_parse_context & ctx) { const auto * it = ctx.begin(); const auto * end = ctx.end(); /// Only support {}. if (it != end && *it != '}') throw fmt::format_error("Invalid format"); return it; } template auto format(const DB::Field & x, FormatContext & ctx) { return fmt::format_to(ctx.out(), "{}", toString(x)); } };