#pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include namespace DB { class Field; typedef std::vector Array; /// Значение типа "массив" using Poco::SharedPtr; /** 32 хватает с запасом (достаточно 28), но выбрано круглое число, * чтобы арифметика при использовании массивов из Field была проще (не содержала умножения). */ #define DBMS_TOTAL_FIELD_SIZE 32 /** Discriminated union из нескольких типов. * Сделан для замены boost::variant: * является не обобщённым, * зато несколько более эффективным, и более простым. * * Используется для представления единичного значения одного из нескольких типов в оперативке. * Внимание! Предпочтительно вместо единичных значений хранить кусочки столбцов. См. Column.h */ class __attribute__((aligned(DBMS_TOTAL_FIELD_SIZE))) Field { public: struct Types { /// Идентификатор типа. enum Which { Null = 0, UInt64 = 1, Int64 = 2, Float64 = 3, /// не POD типы. Для них предполагается relocatable. String = 16, Array = 17, }; 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"; default: throw Exception("Bad type of Field", ErrorCodes::BAD_TYPE_OF_FIELD); } } }; /// Позволяет получить идентификатор для типа или наоборот. template struct TypeToEnum; template struct EnumToType; Field() : which(Types::Null) { // std::cerr << "Field()" << std::endl; } /** Не смотря на наличие шаблонного конструктора, этот конструктор всё-равно нужен, * так как при его отсутствии, компилятор всё-равно сгенерирует конструктор по-умолчанию. */ Field(const Field & rhs) { // std::cerr << this << " Field::Field(const Field &)" << std::endl; create(rhs); } Field & operator= (const Field & rhs) { // std::cerr << this << " Field::operator=(const Field &)" << std::endl; destroy(); create(rhs); return *this; } template Field(const T & rhs) { // std::cerr << this << " Field::Field(" << Types::toString(TypeToEnum::value) << ")" << std::endl; 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); } template Field & operator= (const T & rhs) { // std::cerr << this << " Field::operator=(" << Types::toString(TypeToEnum::value) << ")" << std::endl; destroy(); create(rhs); return *this; } ~Field() { // std::cerr << this << " Field::~Field()" << std::endl; 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() { typedef typename std::remove_reference::type TWithoutRef; TWithoutRef * __attribute__((__may_alias__)) ptr = reinterpret_cast(storage); return *ptr; }; template const T & get() const { typedef typename std::remove_reference::type TWithoutRef; 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(); 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(); 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(); 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 = DBMS_TOTAL_FIELD_SIZE - sizeof(Types::Which); BOOST_STATIC_ASSERT(storage_size >= sizeof(Null)); BOOST_STATIC_ASSERT(storage_size >= sizeof(UInt64)); BOOST_STATIC_ASSERT(storage_size >= sizeof(Int64)); BOOST_STATIC_ASSERT(storage_size >= sizeof(Float64)); BOOST_STATIC_ASSERT(storage_size >= sizeof(String)); BOOST_STATIC_ASSERT(storage_size >= sizeof(Array)); char storage[storage_size] __attribute__((aligned(8))); Types::Which which; template void create(const T & x) { which = TypeToEnum::value; // std::cerr << this << " Creating " << getTypeName() << std::endl; T * __attribute__((__may_alias__)) ptr = reinterpret_cast(storage); new (ptr) T(x); } void create(const Null & x) { which = Types::Null; // std::cerr << this << " Creating " << getTypeName() << std::endl; } void create(const Field & x) { // std::cerr << this << " Creating Field" << std::endl; 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; } } 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() { // std::cerr << this << " Destroying " << getTypeName() << std::endl; if (which < Types::MIN_NON_POD) return; switch (which) { case Types::String: destroy(); break; case Types::Array: destroy(); break; default: break; } } template void destroy() { T * __attribute__((__may_alias__)) ptr = reinterpret_cast(storage); ptr->~T(); } }; 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::EnumToType { typedef Null Type; }; template <> struct Field::EnumToType { typedef UInt64 Type; }; template <> struct Field::EnumToType { typedef Int64 Type; }; template <> struct Field::EnumToType { typedef Float64 Type; }; template <> struct Field::EnumToType { typedef String Type; }; template <> struct Field::EnumToType { typedef Array Type; }; 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(); } /** StaticVisitor (его наследники) - класс с перегруженными для разных типов операторами (). * Вызвать visitor для field можно с помощью функции apply_visitor. * Также поддерживается visitor, в котором оператор () принимает два аргумента. */ template struct StaticVisitor { typedef R ResultType; }; template typename Visitor::ResultType apply_visitor_impl(Visitor & visitor, F & field) { switch (field.getType()) { case Field::Types::Null: return visitor(field.template get()); case Field::Types::UInt64: return visitor(field.template get()); case Field::Types::Int64: return visitor(field.template get()); case Field::Types::Float64: return visitor(field.template get()); case Field::Types::String: return visitor(field.template get()); case Field::Types::Array: return visitor(field.template get()); default: throw Exception("Bad type of Field", ErrorCodes::BAD_TYPE_OF_FIELD); } } /** Эти штуки нужны, чтобы принимать временный объект по константной ссылке. * В шаблон выше, типы форвардятся уже с const-ом. */ template typename Visitor::ResultType apply_visitor(const Visitor & visitor, Field & field) { return apply_visitor_impl(visitor, field); } template typename Visitor::ResultType apply_visitor(const Visitor & visitor, const Field & field) { return apply_visitor_impl(visitor, field); } template typename Visitor::ResultType apply_visitor(Visitor & visitor, Field & field) { return apply_visitor_impl(visitor, field); } template typename Visitor::ResultType apply_visitor(Visitor & visitor, const Field & field) { return apply_visitor_impl(visitor, field); } template typename Visitor::ResultType apply_binary_visitor_impl2(Visitor & visitor, F1 & field1, F2 & field2) { switch (field2.getType()) { case Field::Types::Null: return visitor(field1, field2.template get()); case Field::Types::UInt64: return visitor(field1, field2.template get()); case Field::Types::Int64: return visitor(field1, field2.template get()); case Field::Types::Float64: return visitor(field1, field2.template get()); case Field::Types::String: return visitor(field1, field2.template get()); case Field::Types::Array: return visitor(field1, field2.template get()); default: throw Exception("Bad type of Field", ErrorCodes::BAD_TYPE_OF_FIELD); } } template typename Visitor::ResultType apply_binary_visitor_impl1(Visitor & visitor, F1 & field1, F2 & field2) { switch (field1.getType()) { case Field::Types::Null: return apply_binary_visitor_impl2(visitor, field1.template get(), field2); case Field::Types::UInt64: return apply_binary_visitor_impl2(visitor, field1.template get(), field2); case Field::Types::Int64: return apply_binary_visitor_impl2(visitor, field1.template get(), field2); case Field::Types::Float64: return apply_binary_visitor_impl2(visitor, field1.template get(), field2); case Field::Types::String: return apply_binary_visitor_impl2(visitor, field1.template get(), field2); case Field::Types::Array: return apply_binary_visitor_impl2(visitor, field1.template get(), field2); default: throw Exception("Bad type of Field", ErrorCodes::BAD_TYPE_OF_FIELD); } } template typename Visitor::ResultType apply_visitor(Visitor & visitor, Field & field1, Field & field2) { return apply_binary_visitor_impl1(visitor, field1, field2); } template typename Visitor::ResultType apply_visitor(Visitor & visitor, Field & field1, const Field & field2) { return apply_binary_visitor_impl1(visitor, field1, field2); } template typename Visitor::ResultType apply_visitor(Visitor & visitor, const Field & field1, Field & field2) { return apply_binary_visitor_impl1(visitor, field1, field2); } template typename Visitor::ResultType apply_visitor(Visitor & visitor, const Field & field1, const Field & field2) { return apply_binary_visitor_impl1(visitor, field1, field2); } template typename Visitor::ResultType apply_visitor(const Visitor & visitor, Field & field1, Field & field2) { return apply_binary_visitor_impl1(visitor, field1, field2); } template typename Visitor::ResultType apply_visitor(const Visitor & visitor, Field & field1, const Field & field2) { return apply_binary_visitor_impl1(visitor, field1, field2); } template typename Visitor::ResultType apply_visitor(const Visitor & visitor, const Field & field1, Field & field2) { return apply_binary_visitor_impl1(visitor, field1, field2); } template typename Visitor::ResultType apply_visitor(const Visitor & visitor, const Field & field1, const Field & field2) { return apply_binary_visitor_impl1(visitor, field1, field2); } template <> struct TypeName { static std::string get() { return "Array"; } }; /** Возвращает строковый дамп типа */ class FieldVisitorDump : public StaticVisitor { private: template static inline String formatQuotedWithPrefix(T x, const char * prefix) { String res; WriteBufferFromString wb(res); wb.write(prefix, strlen(prefix)); writeQuoted(x, wb); return res; } public: String operator() (const Null & x) const { return "NULL"; } String operator() (const UInt64 & x) const { return formatQuotedWithPrefix(x, "UInt64_"); } String operator() (const Int64 & x) const { return formatQuotedWithPrefix(x, "Int64_"); } String operator() (const Float64 & x) const { return formatQuotedWithPrefix(x, "Float64_"); } String operator() (const String & x) const { String res; WriteBufferFromString wb(res); writeQuoted(x, wb); return res; } String operator() (const Array & x) const { String res; WriteBufferFromString wb(res); FieldVisitorDump visitor; wb.write("Array_[", 7); for (Array::const_iterator it = x.begin(); it != x.end(); ++it) { if (it != x.begin()) wb.write(", ", 2); writeString(apply_visitor(visitor, *it), wb); } writeChar(']', wb); return res; } }; /** Выводит текстовое представление типа, как литерала в SQL запросе */ class FieldVisitorToString : public StaticVisitor { private: template static inline String formatQuoted(T x) { String res; WriteBufferFromString wb(res); writeQuoted(x, wb); return res; } public: String operator() (const Null & x) const { return "NULL"; } String operator() (const UInt64 & x) const { return formatQuoted(x); } String operator() (const Int64 & x) const { return formatQuoted(x); } String operator() (const Float64 & x) const { return formatQuoted(x); } String operator() (const String & x) const { return formatQuoted(x); } String operator() (const Array & x) const { String res; WriteBufferFromString wb(res); FieldVisitorToString visitor; writeChar('[', wb); for (Array::const_iterator it = x.begin(); it != x.end(); ++it) { if (it != x.begin()) wb.write(", ", 2); writeString(apply_visitor(visitor, *it), wb); } writeChar(']', wb); return res; } }; /** Числовой тип преобразует в указанный. */ template class FieldVisitorConvertToNumber : public StaticVisitor { public: T operator() (const Null & x) const { throw Exception("Cannot convert NULL to " + TypeName::get(), ErrorCodes::CANNOT_CONVERT_TYPE); } T operator() (const String & x) const { throw Exception("Cannot convert String to " + TypeName::get(), ErrorCodes::CANNOT_CONVERT_TYPE); } T operator() (const Array & x) const { throw Exception("Cannot convert Array to " + TypeName::get(), ErrorCodes::CANNOT_CONVERT_TYPE); } T operator() (const UInt64 & x) const { return x; } T operator() (const Int64 & x) const { return x; } T operator() (const Float64 & x) const { return x; } }; template struct NearestFieldType; template <> struct NearestFieldType { typedef UInt64 Type; }; template <> struct NearestFieldType { typedef UInt64 Type; }; template <> struct NearestFieldType { typedef UInt64 Type; }; template <> struct NearestFieldType { typedef UInt64 Type; }; template <> struct NearestFieldType { typedef Int64 Type; }; template <> struct NearestFieldType { typedef Int64 Type; }; template <> struct NearestFieldType { typedef Int64 Type; }; template <> struct NearestFieldType { typedef Int64 Type; }; template <> struct NearestFieldType { typedef Float64 Type; }; template <> struct NearestFieldType { typedef Float64 Type; }; template <> struct NearestFieldType { typedef String Type; }; template <> struct NearestFieldType { typedef Array Type; }; template <> struct NearestFieldType { typedef UInt64 Type; }; template typename NearestFieldType::Type nearestFieldType(const T & x) { return typename NearestFieldType::Type(x); } } /// Заглушки, чтобы DBObject-ы с полем типа Array компилировались. namespace mysqlxx { inline std::ostream & operator<< (mysqlxx::EscapeManipResult res, const DB::Array & value) { return res.ostr << apply_visitor(DB::FieldVisitorToString(), value); } inline std::ostream & operator<< (mysqlxx::QuoteManipResult res, const DB::Array & value) { throw Poco::Exception("Cannot quote Array with mysqlxx::quote."); } inline std::istream & operator>> (mysqlxx::UnEscapeManipResult res, DB::Array & value) { throw Poco::Exception("Cannot unescape Array with mysqlxx::unescape."); } inline std::istream & operator>> (mysqlxx::UnQuoteManipResult res, DB::Array & value) { throw Poco::Exception("Cannot unquote Array with mysqlxx::unquote."); } } namespace DB { class ReadBuffer; class WriteBuffer; /// Предполагается что у всех элементов массива одинаковый тип. inline void readBinary(Array & x, ReadBuffer & buf) { size_t size; UInt8 type; DB::readBinary(type, buf); DB::readBinary(size, buf); for (size_t index = 0; index < size; ++index) { switch (type) { case Field::Types::Null: { x.push_back(DB::Field()); break; } case Field::Types::UInt64: { UInt64 value; DB::readVarUInt(value, buf); x.push_back(value); break; } case Field::Types::Int64: { Int64 value; DB::readVarInt(value, buf); x.push_back(value); break; } case Field::Types::Float64: { Float64 value; DB::readFloatBinary(value, buf); x.push_back(value); break; } case Field::Types::String: { std::string value; DB::readStringBinary(value, buf); x.push_back(value); break; } case Field::Types::Array: { Array value; DB::readBinary(value, buf); x.push_back(value); break; } }; } } 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); } /// Предполагается что у всех элементов массива одинаковый тип. inline void writeBinary(const Array & x, WriteBuffer & buf) { UInt8 type = Field::Types::Null; size_t size = x.size(); if (size) type = x.front().getType(); DB::writeBinary(type, buf); DB::writeBinary(size, buf); for (Array::const_iterator it = x.begin(); it != x.end(); ++it) { switch (type) { case Field::Types::Null: break; case Field::Types::UInt64: { DB::writeVarUInt(get(*it), buf); break; } case Field::Types::Int64: { DB::writeVarInt(get(*it), buf); break; } case Field::Types::Float64: { DB::writeFloatBinary(get(*it), buf); break; } case Field::Types::String: { DB::writeStringBinary(get(*it), buf); break; } case Field::Types::Array: { DB::writeBinary(get(*it), buf); break; } }; } } inline void writeText(const Array & x, WriteBuffer & buf) { DB::String res = apply_visitor(DB::FieldVisitorToString(), DB::Field(x)); buf.write(res.data(), res.size()); } inline void writeQuoted(const Array & x, WriteBuffer & buf) { throw Exception("Cannot write Array quoted.", ErrorCodes::NOT_IMPLEMENTED); } } #undef DBMS_TOTAL_FIELD_SIZE