#pragma once #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; } class Field; using Array = std::vector; /// Значение типа "массив" using TupleBackend = std::vector; STRONG_TYPEDEF(TupleBackend, Tuple); /// Значение типа "кортеж" /** 32 хватает с запасом (достаточно 28), но выбрано круглое число, * чтобы арифметика при использовании массивов из Field была проще (не содержала умножения). */ #define DBMS_MIN_FIELD_SIZE 32 /** Discriminated union из нескольких типов. * Сделан для замены boost::variant: * является не обобщённым, * зато несколько более эффективным, и более простым. * * Используется для представления единичного значения одного из нескольких типов в оперативке. * Внимание! Предпочтительно вместо единичных значений хранить кусочки столбцов. См. Column.h */ class Field { public: struct Types { /// Идентификатор типа. enum Which { Null = 0, UInt64 = 1, Int64 = 2, Float64 = 3, /// не POD типы. Для них предполагается relocatable. String = 16, Array = 17, Tuple = 18, }; static const int MIN_NON_POD = 16; static const char * toString(Which which) { switch (which) { case Null: return "Null"; case UInt64: return "UInt64"; case Int64: return "Int64"; case Float64: return "Float64"; case String: return "String"; case Array: return "Array"; case Tuple: return "Tuple"; default: throw Exception("Bad type of Field", ErrorCodes::BAD_TYPE_OF_FIELD); } } }; /// Позволяет получить идентификатор для типа или наоборот. template struct TypeToEnum; template struct EnumToType; Field() : which(Types::Null) { } /** Не смотря на наличие шаблонного конструктора, этот конструктор всё-равно нужен, * так как при его отсутствии, компилятор всё-равно сгенерирует конструктор по-умолчанию. */ Field(const Field & rhs) { create(rhs); } Field(Field && rhs) { move(std::move(rhs)); } template Field(const T & rhs) { create(rhs); } /// Создать строку inplace. Field(const char * data, size_t size) { create(data, size); } Field(const unsigned char * data, size_t size) { create(data, size); } void assignString(const char * data, size_t size) { destroy(); create(data, size); } void assignString(const unsigned char * data, size_t size) { destroy(); create(data, size); } Field & operator= (const Field & rhs) { if (this != &rhs) { destroy(); create(rhs); } return *this; } Field & operator= (Field && rhs) { if (this != &rhs) { move(std::move(rhs)); } return *this; } template Field & operator= (const T & rhs) { destroy(); create(rhs); return *this; } ~Field() { destroy(); } Types::Which getType() const { return which; } const char * getTypeName() const { return Types::toString(which); } bool isNull() const { return which == Types::Null; } template T & get() { using TWithoutRef = typename std::remove_reference::type; TWithoutRef * __attribute__((__may_alias__)) ptr = reinterpret_cast(storage); return *ptr; }; template const T & get() const { using TWithoutRef = typename std::remove_reference::type; const TWithoutRef * __attribute__((__may_alias__)) ptr = reinterpret_cast(storage); return *ptr; }; template T & safeGet() { const Types::Which requested = TypeToEnum::type>::type>::value; if (which != requested) throw Exception("Bad get: has " + std::string(getTypeName()) + ", requested " + std::string(Types::toString(requested)), ErrorCodes::BAD_GET); return get(); } template const T & safeGet() const { const Types::Which requested = TypeToEnum::type>::type>::value; if (which != requested) throw Exception("Bad get: has " + std::string(getTypeName()) + ", requested " + std::string(Types::toString(requested)), ErrorCodes::BAD_GET); return get(); } 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::UInt64: return get() < rhs.get(); case Types::Int64: 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(); default: throw Exception("Bad type of Field", ErrorCodes::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::UInt64: return get() <= rhs.get(); case Types::Int64: 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(); default: throw Exception("Bad type of Field", ErrorCodes::BAD_TYPE_OF_FIELD); } } bool operator>= (const Field & rhs) const { return rhs <= *this; } bool operator== (const Field & rhs) const { if (which != rhs.which) return false; switch (which) { case Types::Null: return true; case Types::UInt64: case Types::Int64: 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(); default: throw Exception("Bad type of Field", ErrorCodes::BAD_TYPE_OF_FIELD); } } bool operator!= (const Field & rhs) const { return !(*this == rhs); } private: static const size_t storage_size = std::max({ DBMS_MIN_FIELD_SIZE - sizeof(Types::Which), sizeof(Null), sizeof(UInt64), sizeof(Int64), sizeof(Float64), sizeof(String), sizeof(Array), sizeof(Tuple)}); char storage[storage_size] __attribute__((aligned(8))); Types::Which which; template void create(const T & x) { which = TypeToEnum::value; T * __attribute__((__may_alias__)) ptr = reinterpret_cast(storage); new (ptr) T(x); } void create(const Null & x) { which = Types::Null; } void create(const Field & x) { switch (x.which) { case Types::Null: create(Null()); break; case Types::UInt64: create(x.get()); break; case Types::Int64: create(x.get()); break; case Types::Float64: create(x.get()); break; case Types::String: create(x.get()); break; case Types::Array: create(x.get()); break; case Types::Tuple: create(x.get()); break; } } void create(const char * data, size_t size) { which = Types::String; String * __attribute__((__may_alias__)) ptr = reinterpret_cast(storage); new (ptr) String(data, size); } void create(const unsigned char * data, size_t size) { create(reinterpret_cast(data), size); } __attribute__((__always_inline__)) void destroy() { if (which < Types::MIN_NON_POD) return; switch (which) { case Types::String: destroy(); break; case Types::Array: destroy(); break; case Types::Tuple: destroy(); break; default: break; } } template void destroy() { T * __attribute__((__may_alias__)) ptr = reinterpret_cast(storage); ptr->~T(); } template void moveValue(Field && x) { T * __attribute__((__may_alias__)) ptr_this = reinterpret_cast(storage); T * __attribute__((__may_alias__)) ptr_x = reinterpret_cast(x.storage); new (ptr_this) T(std::move(*ptr_x)); } void move(Field && x) { which = x.which; switch (x.which) { case Types::Null: create(Null()); break; case Types::UInt64: create(x.get()); break; case Types::Int64: create(x.get()); break; case Types::Float64: create(x.get()); break; case Types::String: moveValue(std::move(x)); break; case Types::Array: moveValue(std::move(x)); break; case Types::Tuple: moveValue(std::move(x)); break; } } }; #undef DBMS_MIN_FIELD_SIZE template <> struct Field::TypeToEnum { static const Types::Which value = Types::Null; }; template <> struct Field::TypeToEnum { static const Types::Which value = Types::UInt64; }; template <> struct Field::TypeToEnum { static const Types::Which value = Types::Int64; }; template <> struct Field::TypeToEnum { static const Types::Which value = Types::Float64; }; template <> struct Field::TypeToEnum { static const Types::Which value = Types::String; }; template <> struct Field::TypeToEnum { static const Types::Which value = Types::Array; }; template <> struct Field::TypeToEnum { static const Types::Which value = Types::Tuple; }; template <> struct Field::EnumToType { using Type = Null ; }; template <> struct Field::EnumToType { using Type = UInt64 ; }; template <> struct Field::EnumToType { using Type = Int64 ; }; 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 T get(const Field & field) { return field.template get(); } template T get(Field & field) { return field.template get(); } template T safeGet(const Field & field) { return field.template safeGet(); } template T safeGet(Field & field) { return field.template safeGet(); } template <> struct TypeName { static std::string get() { return "Array"; } }; template <> struct TypeName { static std::string get() { return "Tuple"; } }; template struct NearestFieldType; template <> struct NearestFieldType { using Type = UInt64 ; }; template <> struct NearestFieldType { using Type = UInt64 ; }; template <> struct NearestFieldType { using Type = UInt64 ; }; template <> struct NearestFieldType { using Type = UInt64 ; }; template <> struct NearestFieldType { using Type = Int64 ; }; template <> struct NearestFieldType { using Type = Int64 ; }; template <> struct NearestFieldType { using Type = Int64 ; }; template <> struct NearestFieldType { using Type = Int64 ; }; template <> struct NearestFieldType { using Type = Float64 ; }; template <> struct NearestFieldType { using Type = Float64 ; }; template <> struct NearestFieldType { using Type = String ; }; template <> struct NearestFieldType { using Type = Array ; }; template <> struct NearestFieldType { using Type = Tuple ; }; template <> struct NearestFieldType { using Type = UInt64 ; }; template <> struct NearestFieldType { using Type = Null; }; template typename NearestFieldType::Type nearestFieldType(const T & x) { return typename NearestFieldType::Type(x); } } /// Заглушки, чтобы DBObject-ы с полем типа Array компилировались. #include namespace mysqlxx { std::ostream & operator<< (mysqlxx::EscapeManipResult res, const DB::Array & value); std::ostream & operator<< (mysqlxx::QuoteManipResult res, const DB::Array & value); std::istream & operator>> (mysqlxx::UnEscapeManipResult res, DB::Array & value); std::istream & operator>> (mysqlxx::UnQuoteManipResult res, DB::Array & value); std::ostream & operator<< (mysqlxx::EscapeManipResult res, const DB::Tuple & value); std::ostream & operator<< (mysqlxx::QuoteManipResult res, const DB::Tuple & value); std::istream & operator>> (mysqlxx::UnEscapeManipResult res, DB::Tuple & value); std::istream & operator>> (mysqlxx::UnQuoteManipResult res, DB::Tuple & value); } namespace DB { class ReadBuffer; class WriteBuffer; /// Предполагается что у всех элементов массива одинаковый тип. void readBinary(Array & x, ReadBuffer & buf); inline void readText(Array & x, ReadBuffer & buf) { throw Exception("Cannot read Array.", ErrorCodes::NOT_IMPLEMENTED); } inline void readQuoted(Array & x, ReadBuffer & buf) { throw Exception("Cannot read Array.", ErrorCodes::NOT_IMPLEMENTED); } /// Предполагается что у всех элементов массива одинаковый тип. void writeBinary(const Array & x, WriteBuffer & buf); void writeText(const Array & x, WriteBuffer & buf); inline void writeQuoted(const Array & x, WriteBuffer & buf) { throw Exception("Cannot write Array quoted.", ErrorCodes::NOT_IMPLEMENTED); } } namespace DB { void readBinary(Tuple & x, ReadBuffer & buf); inline void readText(Tuple & x, ReadBuffer & buf) { throw Exception("Cannot read Tuple.", ErrorCodes::NOT_IMPLEMENTED); } inline void readQuoted(Tuple & x, ReadBuffer & buf) { throw Exception("Cannot read Tuple.", ErrorCodes::NOT_IMPLEMENTED); } void writeBinary(const Tuple & x, WriteBuffer & buf); void writeText(const Tuple & x, WriteBuffer & buf); inline void writeQuoted(const Tuple & x, WriteBuffer & buf) { throw Exception("Cannot write Tuple quoted.", ErrorCodes::NOT_IMPLEMENTED); } }