ClickHouse/dbms/include/DB/Core/Field.h

472 lines
14 KiB
C++
Raw Normal View History

2011-08-28 00:31:30 +00:00
#pragma once
2010-03-01 16:59:51 +00:00
#include <vector>
#include <algorithm>
2014-01-08 16:33:28 +00:00
#include <type_traits>
#include <functional>
2010-03-01 16:59:51 +00:00
#include <boost/static_assert.hpp>
2010-03-01 16:59:51 +00:00
2015-10-05 01:35:28 +00:00
#include <DB/Common/Exception.h>
#include <DB/Core/Types.h>
#include <common/strong_typedef.h>
2010-03-01 16:59:51 +00:00
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<Field>; /// Значение типа "массив"
using TupleBackend = std::vector<Field>;
STRONG_TYPEDEF(TupleBackend, Tuple); /// Значение типа "кортеж"
2010-03-01 16:59:51 +00:00
2013-01-08 21:32:16 +00:00
/** 32 хватает с запасом (достаточно 28), но выбрано круглое число,
* чтобы арифметика при использовании массивов из Field была проще (не содержала умножения).
*/
#define DBMS_MIN_FIELD_SIZE 32
2013-01-08 21:32:16 +00:00
/** Discriminated union из нескольких типов.
* Сделан для замены boost::variant:
* является не обобщённым,
* зато несколько более эффективным, и более простым.
*
* Используется для представления единичного значения одного из нескольких типов в оперативке.
2010-03-01 16:59:51 +00:00
* Внимание! Предпочтительно вместо единичных значений хранить кусочки столбцов. См. Column.h
*/
class Field
{
public:
struct Types
{
/// Идентификатор типа.
enum Which
{
Null = 0,
UInt64 = 1,
Int64 = 2,
Float64 = 3,
2013-02-09 02:20:26 +00:00
/// не 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);
}
}
};
2010-03-01 16:59:51 +00:00
/// Позволяет получить идентификатор для типа или наоборот.
template <typename T> struct TypeToEnum;
template <Types::Which which> struct EnumToType;
2010-03-01 16:59:51 +00:00
2012-08-26 06:48:39 +00:00
Field()
: which(Types::Null)
{
}
2010-03-01 16:59:51 +00:00
/** Не смотря на наличие шаблонного конструктора, этот конструктор всё-равно нужен,
* так как при его отсутствии, компилятор всё-равно сгенерирует конструктор по-умолчанию.
*/
Field(const Field & rhs)
{
create(rhs);
}
Field & operator= (const Field & rhs)
{
destroy();
create(rhs);
return *this;
}
template <typename T>
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);
}
template <typename T>
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 <typename T> T & get()
{
using TWithoutRef = typename std::remove_reference<T>::type;
TWithoutRef * __attribute__((__may_alias__)) ptr = reinterpret_cast<TWithoutRef*>(storage);
return *ptr;
};
2013-02-25 23:08:25 +00:00
template <typename T> const T & get() const
{
using TWithoutRef = typename std::remove_reference<T>::type;
const TWithoutRef * __attribute__((__may_alias__)) ptr = reinterpret_cast<const TWithoutRef*>(storage);
return *ptr;
2010-03-01 16:59:51 +00:00
};
template <typename T> T & safeGet()
{
2014-01-08 16:33:28 +00:00
const Types::Which requested = TypeToEnum<typename std::remove_cv<typename std::remove_reference<T>::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<T>();
}
template <typename T> const T & safeGet() const
{
2014-01-08 16:33:28 +00:00
const Types::Which requested = TypeToEnum<typename std::remove_cv<typename std::remove_reference<T>::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<T>();
}
bool operator< (const Field & rhs) const
{
if (which < rhs.which)
return true;
if (which > rhs.which)
return false;
2013-02-25 23:08:25 +00:00
switch (which)
{
case Types::Null: return false;
case Types::UInt64: return get<UInt64>() < rhs.get<UInt64>();
case Types::Int64: return get<Int64>() < rhs.get<Int64>();
case Types::Float64: return get<Float64>() < rhs.get<Float64>();
case Types::String: return get<String>() < rhs.get<String>();
case Types::Array: return get<Array>() < rhs.get<Array>();
case Types::Tuple: return get<Tuple>() < rhs.get<Tuple>();
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<UInt64>() <= rhs.get<UInt64>();
case Types::Int64: return get<Int64>() <= rhs.get<Int64>();
case Types::Float64: return get<Float64>() <= rhs.get<Float64>();
case Types::String: return get<String>() <= rhs.get<String>();
case Types::Array: return get<Array>() <= rhs.get<Array>();
case Types::Tuple: return get<Tuple>() <= rhs.get<Tuple>();
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<UInt64>() == rhs.get<UInt64>();
case Types::String: return get<String>() == rhs.get<String>();
case Types::Array: return get<Array>() == rhs.get<Array>();
case Types::Tuple: return get<Tuple>() == rhs.get<Tuple>();
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 <typename T>
void create(const T & x)
{
which = TypeToEnum<T>::value;
2013-01-05 21:57:11 +00:00
T * __attribute__((__may_alias__)) ptr = reinterpret_cast<T*>(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<UInt64>()); break;
case Types::Int64: create(x.get<Int64>()); break;
case Types::Float64: create(x.get<Float64>()); break;
case Types::String: create(x.get<String>()); break;
case Types::Array: create(x.get<Array>()); break;
case Types::Tuple: create(x.get<Tuple>()); break;
}
}
void create(const char * data, size_t size)
{
which = Types::String;
String * __attribute__((__may_alias__)) ptr = reinterpret_cast<String*>(storage);
new (ptr) String(data, size);
}
void create(const unsigned char * data, size_t size)
{
create(reinterpret_cast<const char *>(data), size);
}
2013-01-07 08:27:39 +00:00
__attribute__((__always_inline__)) void destroy()
{
if (which < Types::MIN_NON_POD)
return;
2013-02-25 23:08:25 +00:00
switch (which)
{
case Types::String:
destroy<String>();
break;
case Types::Array:
destroy<Array>();
break;
case Types::Tuple:
destroy<Tuple>();
break;
default:
break;
}
}
template <typename T>
void destroy()
{
T * __attribute__((__may_alias__)) ptr = reinterpret_cast<T*>(storage);
ptr->~T();
}
};
#undef DBMS_MIN_FIELD_SIZE
template <> struct Field::TypeToEnum<Null> { static const Types::Which value = Types::Null; };
template <> struct Field::TypeToEnum<UInt64> { static const Types::Which value = Types::UInt64; };
template <> struct Field::TypeToEnum<Int64> { static const Types::Which value = Types::Int64; };
template <> struct Field::TypeToEnum<Float64> { static const Types::Which value = Types::Float64; };
template <> struct Field::TypeToEnum<String> { static const Types::Which value = Types::String; };
template <> struct Field::TypeToEnum<Array> { static const Types::Which value = Types::Array; };
template <> struct Field::TypeToEnum<Tuple> { static const Types::Which value = Types::Tuple; };
template <> struct Field::EnumToType<Field::Types::Null> { using Type = Null ; };
template <> struct Field::EnumToType<Field::Types::UInt64> { using Type = UInt64 ; };
template <> struct Field::EnumToType<Field::Types::Int64> { using Type = Int64 ; };
template <> struct Field::EnumToType<Field::Types::Float64> { using Type = Float64 ; };
template <> struct Field::EnumToType<Field::Types::String> { using Type = String ; };
template <> struct Field::EnumToType<Field::Types::Array> { using Type = Array ; };
template <> struct Field::EnumToType<Field::Types::Tuple> { using Type = Tuple ; };
template <typename T>
T get(const Field & field)
{
return field.template get<T>();
2010-03-01 16:59:51 +00:00
}
template <typename T>
T get(Field & field)
{
return field.template get<T>();
}
2010-03-01 16:59:51 +00:00
template <typename T>
T safeGet(const Field & field)
2010-03-01 16:59:51 +00:00
{
return field.template safeGet<T>();
}
2010-03-01 16:59:51 +00:00
template <typename T>
T safeGet(Field & field)
2010-03-01 16:59:51 +00:00
{
return field.template safeGet<T>();
}
template <> struct TypeName<Array> { static std::string get() { return "Array"; } };
template <> struct TypeName<Tuple> { static std::string get() { return "Tuple"; } };
2010-03-18 19:32:14 +00:00
template <typename T> struct NearestFieldType;
template <> struct NearestFieldType<UInt8> { using Type = UInt64 ; };
template <> struct NearestFieldType<UInt16> { using Type = UInt64 ; };
template <> struct NearestFieldType<UInt32> { using Type = UInt64 ; };
template <> struct NearestFieldType<UInt64> { using Type = UInt64 ; };
template <> struct NearestFieldType<Int8> { using Type = Int64 ; };
template <> struct NearestFieldType<Int16> { using Type = Int64 ; };
template <> struct NearestFieldType<Int32> { using Type = Int64 ; };
template <> struct NearestFieldType<Int64> { using Type = Int64 ; };
template <> struct NearestFieldType<Float32> { using Type = Float64 ; };
template <> struct NearestFieldType<Float64> { using Type = Float64 ; };
template <> struct NearestFieldType<String> { using Type = String ; };
template <> struct NearestFieldType<Array> { using Type = Array ; };
template <> struct NearestFieldType<Tuple> { using Type = Tuple ; };
template <> struct NearestFieldType<bool> { using Type = UInt64 ; };
template <typename T>
typename NearestFieldType<T>::Type nearestFieldType(const T & x)
{
return typename NearestFieldType<T>::Type(x);
}
2010-03-01 16:59:51 +00:00
}
/// Заглушки, чтобы DBObject-ы с полем типа Array компилировались.
#include <mysqlxx/Manip.h>
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); }
}