ClickHouse/dbms/src/Core/Field.h

546 lines
17 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 <Common/Exception.h>
#include <Common/UInt128.h>
#include <Core/Types.h>
#include <Core/Defines.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;
Squashed commit of the following: commit c567d4e1fe8d54e6363e47548f1e3927cc5ee78f Author: Alexey Milovidov <milovidov@yandex-team.ru> Date: Fri Jan 6 20:35:01 2017 +0300 Style [#METR-2944]. commit 26bf3e1228e03f46c29b13edb0e3770bd453e3f1 Author: Alexey Milovidov <milovidov@yandex-team.ru> Date: Fri Jan 6 20:33:11 2017 +0300 Miscellaneous [#METR-2944]. commit eb946f4c6fd4bb0e9e5c7fb1468d36be3dfca5a5 Author: Alexey Milovidov <milovidov@yandex-team.ru> Date: Fri Jan 6 20:30:19 2017 +0300 Miscellaneous [#METR-2944]. commit 78c867a14744b5af2db8d37caf7804fc2057ea51 Author: Alexey Milovidov <milovidov@yandex-team.ru> Date: Fri Jan 6 20:11:41 2017 +0300 Miscellaneous [#METR-2944]. commit 6604c5c83cfcedc81c8da4da026711920d5963b4 Author: Alexey Milovidov <milovidov@yandex-team.ru> Date: Fri Jan 6 19:56:15 2017 +0300 Miscellaneous [#METR-2944]. commit 23fbf05c1d4bead636458ec21b05a101b1152e33 Author: Alexey Milovidov <milovidov@yandex-team.ru> Date: Fri Jan 6 19:47:52 2017 +0300 Miscellaneous [#METR-2944]. commit 98772faf11a7d450d473f7fa84f8a9ae24f7b59b Author: Alexey Milovidov <milovidov@yandex-team.ru> Date: Fri Jan 6 19:46:05 2017 +0300 Miscellaneous [#METR-2944]. commit 3dc636ab9f9359dbeac2e8d997ae563d4ca147e2 Author: Alexey Milovidov <milovidov@yandex-team.ru> Date: Fri Jan 6 19:39:46 2017 +0300 Miscellaneous [#METR-2944]. commit 3e16aee95482f374ee3eda1a4dbe9ba5cdce02e8 Author: Alexey Milovidov <milovidov@yandex-team.ru> Date: Fri Jan 6 19:38:03 2017 +0300 Miscellaneous [#METR-2944]. commit ae7e7e90eb1f82bd0fe0f887708d08b9e7755612 Author: Alexey Milovidov <milovidov@yandex-team.ru> Date: Fri Jan 6 19:34:15 2017 +0300 Miscellaneous [#METR-2944].
2017-01-06 17:41:19 +00:00
using Array = std::vector<Field>;
using TupleBackend = std::vector<Field>;
Squashed commit of the following: commit c567d4e1fe8d54e6363e47548f1e3927cc5ee78f Author: Alexey Milovidov <milovidov@yandex-team.ru> Date: Fri Jan 6 20:35:01 2017 +0300 Style [#METR-2944]. commit 26bf3e1228e03f46c29b13edb0e3770bd453e3f1 Author: Alexey Milovidov <milovidov@yandex-team.ru> Date: Fri Jan 6 20:33:11 2017 +0300 Miscellaneous [#METR-2944]. commit eb946f4c6fd4bb0e9e5c7fb1468d36be3dfca5a5 Author: Alexey Milovidov <milovidov@yandex-team.ru> Date: Fri Jan 6 20:30:19 2017 +0300 Miscellaneous [#METR-2944]. commit 78c867a14744b5af2db8d37caf7804fc2057ea51 Author: Alexey Milovidov <milovidov@yandex-team.ru> Date: Fri Jan 6 20:11:41 2017 +0300 Miscellaneous [#METR-2944]. commit 6604c5c83cfcedc81c8da4da026711920d5963b4 Author: Alexey Milovidov <milovidov@yandex-team.ru> Date: Fri Jan 6 19:56:15 2017 +0300 Miscellaneous [#METR-2944]. commit 23fbf05c1d4bead636458ec21b05a101b1152e33 Author: Alexey Milovidov <milovidov@yandex-team.ru> Date: Fri Jan 6 19:47:52 2017 +0300 Miscellaneous [#METR-2944]. commit 98772faf11a7d450d473f7fa84f8a9ae24f7b59b Author: Alexey Milovidov <milovidov@yandex-team.ru> Date: Fri Jan 6 19:46:05 2017 +0300 Miscellaneous [#METR-2944]. commit 3dc636ab9f9359dbeac2e8d997ae563d4ca147e2 Author: Alexey Milovidov <milovidov@yandex-team.ru> Date: Fri Jan 6 19:39:46 2017 +0300 Miscellaneous [#METR-2944]. commit 3e16aee95482f374ee3eda1a4dbe9ba5cdce02e8 Author: Alexey Milovidov <milovidov@yandex-team.ru> Date: Fri Jan 6 19:38:03 2017 +0300 Miscellaneous [#METR-2944]. commit ae7e7e90eb1f82bd0fe0f887708d08b9e7755612 Author: Alexey Milovidov <milovidov@yandex-team.ru> Date: Fri Jan 6 19:34:15 2017 +0300 Miscellaneous [#METR-2944].
2017-01-06 17:41:19 +00:00
STRONG_TYPEDEF(TupleBackend, Tuple); /// Array and Tuple are different types with equal representation inside Field.
2010-03-01 16:59:51 +00:00
Squashed commit of the following: commit c567d4e1fe8d54e6363e47548f1e3927cc5ee78f Author: Alexey Milovidov <milovidov@yandex-team.ru> Date: Fri Jan 6 20:35:01 2017 +0300 Style [#METR-2944]. commit 26bf3e1228e03f46c29b13edb0e3770bd453e3f1 Author: Alexey Milovidov <milovidov@yandex-team.ru> Date: Fri Jan 6 20:33:11 2017 +0300 Miscellaneous [#METR-2944]. commit eb946f4c6fd4bb0e9e5c7fb1468d36be3dfca5a5 Author: Alexey Milovidov <milovidov@yandex-team.ru> Date: Fri Jan 6 20:30:19 2017 +0300 Miscellaneous [#METR-2944]. commit 78c867a14744b5af2db8d37caf7804fc2057ea51 Author: Alexey Milovidov <milovidov@yandex-team.ru> Date: Fri Jan 6 20:11:41 2017 +0300 Miscellaneous [#METR-2944]. commit 6604c5c83cfcedc81c8da4da026711920d5963b4 Author: Alexey Milovidov <milovidov@yandex-team.ru> Date: Fri Jan 6 19:56:15 2017 +0300 Miscellaneous [#METR-2944]. commit 23fbf05c1d4bead636458ec21b05a101b1152e33 Author: Alexey Milovidov <milovidov@yandex-team.ru> Date: Fri Jan 6 19:47:52 2017 +0300 Miscellaneous [#METR-2944]. commit 98772faf11a7d450d473f7fa84f8a9ae24f7b59b Author: Alexey Milovidov <milovidov@yandex-team.ru> Date: Fri Jan 6 19:46:05 2017 +0300 Miscellaneous [#METR-2944]. commit 3dc636ab9f9359dbeac2e8d997ae563d4ca147e2 Author: Alexey Milovidov <milovidov@yandex-team.ru> Date: Fri Jan 6 19:39:46 2017 +0300 Miscellaneous [#METR-2944]. commit 3e16aee95482f374ee3eda1a4dbe9ba5cdce02e8 Author: Alexey Milovidov <milovidov@yandex-team.ru> Date: Fri Jan 6 19:38:03 2017 +0300 Miscellaneous [#METR-2944]. commit ae7e7e90eb1f82bd0fe0f887708d08b9e7755612 Author: Alexey Milovidov <milovidov@yandex-team.ru> Date: Fri Jan 6 19:34:15 2017 +0300 Miscellaneous [#METR-2944].
2017-01-06 17:41:19 +00:00
/** 32 is enough. Round number is used for alignment and for better arithmetic inside std::vector.
2013-01-08 21:32:16 +00:00
*/
#define DBMS_MIN_FIELD_SIZE 32
2013-01-08 21:32:16 +00:00
2017-04-30 13:50:16 +00:00
/** Discriminated union of several types.
* Made for replacement of `boost::variant`
* is not generalized,
* but somewhat more efficient, and simpler.
*
2017-08-29 19:07:54 +00:00
* 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 Column.h
2010-03-01 16:59:51 +00:00
*/
class Field
{
public:
struct Types
{
/// Type tag.
enum Which
{
2017-05-11 19:48:46 +00:00
Null = 0,
UInt64 = 1,
Int64 = 2,
Float64 = 3,
UInt128 = 4,
/// Non-POD types.
2017-05-11 19:48:46 +00:00
String = 16,
Array = 17,
Tuple = 18,
};
static const int MIN_NON_POD = 16;
static const char * toString(Which which)
{
switch (which)
{
2017-05-11 19:48:46 +00:00
case Null: return "Null";
case UInt64: return "UInt64";
2017-07-06 14:42:27 +00:00
case UInt128: return "UInt128";
2017-05-11 19:48:46 +00:00
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);
}
}
};
2017-04-30 13:50:16 +00:00
/// Returns an identifier for the type or vice versa.
template <typename T> struct TypeToEnum;
template <Types::Which which> struct EnumToType;
Field()
: which(Types::Null)
{
}
2017-04-30 13:50:16 +00:00
/** 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)
{
create(std::move(rhs));
}
template <typename T>
2017-12-25 04:01:46 +00:00
Field(T && rhs, std::integral_constant<int, Field::TypeToEnum<std::decay_t<T>>::value> * = nullptr)
{
createConcrete(std::forward<T>(rhs));
}
2017-04-30 13:50:16 +00:00
/// Create a string inplace.
Field(const char * data, size_t size)
{
create(data, size);
}
Field(const unsigned char * data, size_t size)
{
create(data, size);
}
/// NOTE In case when field already has string type, more direct assign is possible.
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)
{
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)
{
if (this != &rhs)
{
if (which != rhs.which)
{
destroy();
create(std::move(rhs));
}
else
assign(std::move(rhs));
}
return *this;
}
template <typename T>
2017-12-25 04:01:46 +00:00
std::enable_if_t<!std::is_same_v<std::decay_t<T>, Field>, Field &>
operator= (T && rhs)
{
2017-12-25 04:01:46 +00:00
if (which != TypeToEnum<std::decay_t<T>>::value)
{
destroy();
createConcrete(std::forward<T>(rhs));
}
else
assignConcrete(std::forward<T>(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()
{
2017-12-25 04:01:46 +00:00
using TWithoutRef = std::remove_reference_t<T>;
TWithoutRef * __attribute__((__may_alias__)) ptr = reinterpret_cast<TWithoutRef*>(&storage);
return *ptr;
};
template <typename T> const T & get() const
{
2017-12-25 04:01:46 +00:00
using TWithoutRef = std::remove_reference_t<T>;
const TWithoutRef * __attribute__((__may_alias__)) ptr = reinterpret_cast<const TWithoutRef*>(&storage);
return *ptr;
};
template <typename T> bool tryGet(T & result)
{
2017-12-25 04:01:46 +00:00
const Types::Which requested = TypeToEnum<std::decay_t<T>>::value;
if (which != requested)
return false;
result = get<T>();
return true;
}
template <typename T> bool tryGet(T & result) const
{
2017-12-25 04:01:46 +00:00
const Types::Which requested = TypeToEnum<std::decay_t<T>>::value;
if (which != requested)
return false;
result = get<T>();
return true;
}
template <typename T> T & safeGet()
{
2017-12-25 04:01:46 +00:00
const Types::Which requested = TypeToEnum<std::decay_t<T>>::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
{
2017-12-25 04:01:46 +00:00
const Types::Which requested = TypeToEnum<std::decay_t<T>>::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;
switch (which)
{
2017-05-11 19:48:46 +00:00
case Types::Null: return false;
case Types::UInt64: return get<UInt64>() < rhs.get<UInt64>();
2017-07-06 14:42:27 +00:00
case Types::UInt128: return get<UInt128>() < rhs.get<UInt128>();
2017-05-11 19:48:46 +00:00
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)
{
2017-05-11 19:48:46 +00:00
case Types::Null: return true;
case Types::UInt64: return get<UInt64>() <= rhs.get<UInt64>();
2017-07-06 14:42:27 +00:00
case Types::UInt128: return get<UInt128>() <= rhs.get<UInt128>();
2017-05-11 19:48:46 +00:00
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)
{
2017-05-11 19:48:46 +00:00
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>();
case Types::UInt128: return get<UInt128>() == rhs.get<UInt128>();
default:
throw Exception("Bad type of Field", ErrorCodes::BAD_TYPE_OF_FIELD);
}
}
bool operator!= (const Field & rhs) const
{
return !(*this == rhs);
}
private:
2017-12-25 04:01:46 +00:00
std::aligned_union_t<DBMS_MIN_FIELD_SIZE - sizeof(Types::Which),
Null, UInt64, UInt128, Int64, Float64, String, Array, Tuple
2017-12-25 04:01:46 +00:00
> storage;
Types::Which which;
/// Assuming there was no allocated state or it was deallocated (see destroy).
template <typename T>
void createConcrete(T && x)
{
2017-12-25 04:01:46 +00:00
using JustT = std::decay_t<T>;
JustT * __attribute__((__may_alias__)) ptr = reinterpret_cast<JustT *>(&storage);
new (ptr) JustT(std::forward<T>(x));
which = TypeToEnum<JustT>::value;
}
/// Assuming same types.
template <typename T>
void assignConcrete(T && x)
{
2017-12-25 04:01:46 +00:00
using JustT = std::decay_t<T>;
JustT * __attribute__((__may_alias__)) ptr = reinterpret_cast<JustT *>(&storage);
*ptr = std::forward<T>(x);
}
template <typename F, typename Field> /// Field template parameter may be const or non-const Field.
static void dispatch(F && f, Field & field)
{
switch (field.which)
{
2017-05-11 19:48:46 +00:00
case Types::Null: f(field.template get<Null>()); return;
case Types::UInt64: f(field.template get<UInt64>()); return;
2017-07-06 14:42:27 +00:00
case Types::UInt128: f(field.template get<UInt128>()); return;
2017-05-11 19:48:46 +00:00
case Types::Int64: f(field.template get<Int64>()); return;
case Types::Float64: f(field.template get<Float64>()); return;
case Types::String: f(field.template get<String>()); return;
case Types::Array: f(field.template get<Array>()); return;
case Types::Tuple: f(field.template get<Tuple>()); return;
default:
throw Exception("Bad type of Field", ErrorCodes::BAD_TYPE_OF_FIELD);
}
}
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);
}
void create(const char * data, size_t size)
{
String * __attribute__((__may_alias__)) ptr = reinterpret_cast<String*>(&storage);
new (ptr) String(data, size);
which = Types::String;
}
void create(const unsigned char * data, size_t size)
{
create(reinterpret_cast<const char *>(data), size);
}
ALWAYS_INLINE void destroy()
{
if (which < Types::MIN_NON_POD)
return;
switch (which)
{
case Types::String:
destroy<String>();
break;
case Types::Array:
destroy<Array>();
break;
case Types::Tuple:
destroy<Tuple>();
break;
default:
break;
}
which = Types::Null; /// for exception safety in subsequent calls to destroy and create, when create fails.
}
template <typename T>
void destroy()
{
T * __attribute__((__may_alias__)) ptr = reinterpret_cast<T*>(&storage);
ptr->~T();
}
};
#undef DBMS_MIN_FIELD_SIZE
2017-05-11 19:48:46 +00:00
template <> struct Field::TypeToEnum<Null> { static const Types::Which value = Types::Null; };
template <> struct Field::TypeToEnum<UInt64> { static const Types::Which value = Types::UInt64; };
2017-07-06 14:42:27 +00:00
template <> struct Field::TypeToEnum<UInt128> { static const Types::Which value = Types::UInt128; };
2017-05-11 19:48:46 +00:00
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; };
2017-05-11 19:48:46 +00:00
template <> struct Field::EnumToType<Field::Types::Null> { using Type = Null; };
template <> struct Field::EnumToType<Field::Types::UInt64> { using Type = UInt64; };
2017-07-06 14:42:27 +00:00
template <> struct Field::EnumToType<Field::Types::UInt128> { using Type = UInt128; };
2017-05-11 19:48:46 +00:00
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;
2017-05-11 19:48:46 +00:00
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<UInt128> { using Type = UInt128; };
2017-05-11 19:48:46 +00:00
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 <> struct NearestFieldType<Null> { using Type = Null; };
template <typename T>
typename NearestFieldType<T>::Type nearestFieldType(const T & x)
{
return typename NearestFieldType<T>::Type(x);
}
class ReadBuffer;
class WriteBuffer;
2017-04-30 13:50:16 +00:00
/// It is assumed that all elements of the array have the same type.
void readBinary(Array & x, ReadBuffer & buf);
2017-12-01 17:49:12 +00:00
inline void readText(Array &, ReadBuffer &) { throw Exception("Cannot read Array.", ErrorCodes::NOT_IMPLEMENTED); }
inline void readQuoted(Array &, ReadBuffer &) { throw Exception("Cannot read Array.", ErrorCodes::NOT_IMPLEMENTED); }
2017-04-30 13:50:16 +00:00
/// It is assumed that all elements of the array have the same type.
void writeBinary(const Array & x, WriteBuffer & buf);
void writeText(const Array & x, WriteBuffer & buf);
2017-12-01 17:49:12 +00:00
inline void writeQuoted(const Array &, WriteBuffer &) { throw Exception("Cannot write Array quoted.", ErrorCodes::NOT_IMPLEMENTED); }
void readBinary(Tuple & x, ReadBuffer & buf);
2017-12-01 17:49:12 +00:00
inline void readText(Tuple &, ReadBuffer &) { throw Exception("Cannot read Tuple.", ErrorCodes::NOT_IMPLEMENTED); }
inline void readQuoted(Tuple &, ReadBuffer &) { throw Exception("Cannot read Tuple.", ErrorCodes::NOT_IMPLEMENTED); }
void writeBinary(const Tuple & x, WriteBuffer & buf);
void writeText(const Tuple & x, WriteBuffer & buf);
2017-12-01 17:49:12 +00:00
inline void writeQuoted(const Tuple &, WriteBuffer &) { throw Exception("Cannot write Tuple quoted.", ErrorCodes::NOT_IMPLEMENTED); }
}