Initial implementation of XML output format [#METR-20026].

This commit is contained in:
Alexey Milovidov 2016-02-14 05:37:42 +03:00
parent 12363ed329
commit 8efb3e5ade
27 changed files with 846 additions and 373 deletions

View File

@ -211,6 +211,7 @@ add_library (dbms
include/DB/DataStreams/RemoteBlockOutputStream.h
include/DB/DataStreams/MergingSortedBlockInputStream.h
include/DB/DataStreams/JSONRowOutputStream.h
include/DB/DataStreams/XMLRowOutputStream.h
include/DB/DataStreams/TSKVRowOutputStream.h
include/DB/DataStreams/ODBCDriverBlockOutputStream.h
include/DB/DataStreams/MergeSortingBlockInputStream.h
@ -676,6 +677,7 @@ add_library (dbms
src/DataStreams/FormatFactory.cpp
src/DataStreams/copyData.cpp
src/DataStreams/JSONRowOutputStream.cpp
src/DataStreams/XMLRowOutputStream.cpp
src/DataStreams/TSKVRowOutputStream.cpp
src/DataStreams/ODBCDriverBlockOutputStream.cpp
src/DataStreams/JSONCompactRowOutputStream.cpp
@ -695,6 +697,8 @@ add_library (dbms
src/DataTypes/DataTypeNested.cpp
src/DataTypes/DataTypeFactory.cpp
src/DataTypes/DataTypeAggregateFunction.cpp
src/DataTypes/DataTypeEnum.cpp
src/DataTypes/DataTypeTuple.cpp
src/DataTypes/FieldToDataType.cpp
src/Parsers/ASTSelectQuery.cpp

View File

@ -1,11 +1,8 @@
#pragma once
#include <Poco/SharedPtr.h>
#include <DB/Core/Block.h>
#include <DB/IO/WriteBuffer.h>
#include <DB/DataStreams/IRowOutputStream.h>
#include <DB/DataStreams/IProfilingBlockInputStream.h>
namespace DB

View File

@ -0,0 +1,63 @@
#pragma once
#include <DB/Core/Block.h>
#include <DB/IO/WriteBuffer.h>
#include <DB/DataStreams/IRowOutputStream.h>
namespace DB
{
/** Поток для вывода данных в формате XML.
*/
class XMLRowOutputStream : public IRowOutputStream
{
public:
XMLRowOutputStream(WriteBuffer & ostr_, const Block & sample_);
void writeField(const Field & field) override;
void writeRowStartDelimiter() override;
void writeRowEndDelimiter() override;
void writePrefix() override;
void writeSuffix() override;
void flush() override
{
ostr->next();
if (validating_ostr)
dst_ostr.next();
}
void setRowsBeforeLimit(size_t rows_before_limit_) override
{
applied_limit = true;
rows_before_limit = rows_before_limit_;
}
void setTotals(const Block & totals_) override { totals = totals_; }
void setExtremes(const Block & extremes_) override { extremes = extremes_; }
String getContentType() const override { return "application/xml; charset=UTF-8"; }
protected:
void writeRowsBeforeLimitAtLeast();
virtual void writeTotals();
virtual void writeExtremes();
WriteBuffer & dst_ostr;
std::unique_ptr<WriteBuffer> validating_ostr; /// Валидирует UTF-8 последовательности.
WriteBuffer * ostr;
size_t field_number = 0;
size_t row_count = 0;
bool applied_limit = false;
size_t rows_before_limit = 0;
NamesAndTypes fields;
Block totals;
Block extremes;
};
}

View File

@ -53,6 +53,7 @@ public:
void serializeTextQuoted(const Field & field, WriteBuffer & ostr) const override;
void deserializeTextQuoted(Field & field, ReadBuffer & istr) const override;
void serializeTextJSON(const Field & field, WriteBuffer & ostr) const override;
void serializeTextXML(const Field & field, WriteBuffer & ostr) const override;
void serializeTextCSV(const Field & field, WriteBuffer & ostr) const override;
void deserializeTextCSV(Field & field, ReadBuffer & istr, const char delimiter) const override;

View File

@ -43,6 +43,7 @@ public:
void deserializeTextQuoted(Field & field, ReadBuffer & istr) const override;
void serializeTextJSON(const Field & field, WriteBuffer & ostr) const override;
void serializeTextXML(const Field & field, WriteBuffer & ostr) const override;
void serializeTextCSV(const Field & field, WriteBuffer & ostr) const override;
void deserializeTextCSV(Field & field, ReadBuffer & istr, const char delimiter) const override;

View File

@ -1,6 +1,8 @@
#pragma once
#include <DB/DataTypes/IDataType.h>
#include <DB/Columns/ColumnVector.h>
#include <DB/Columns/ColumnConst.h>
#include <DB/Common/HashTable/HashMap.h>
#include <vector>
#include <unordered_map>
@ -11,16 +13,10 @@ namespace DB
namespace ErrorCodes
{
extern const int SYNTAX_ERROR;
extern const int LOGICAL_ERROR;
extern const int EMPTY_DATA_PASSED;
}
template <typename FieldType> struct EnumName;
template <> struct EnumName<Int8> { static constexpr auto value = "Enum8"; };
template <> struct EnumName<Int16> { static constexpr auto value = "Enum16"; };
template <typename Type>
class DataTypeEnum final : public IDataType
{
@ -39,90 +35,16 @@ private:
ValueToNameMap value_to_name_map;
std::string name;
static std::string generateName(const Values & values)
{
std::string name;
{
WriteBufferFromString out{name};
writeString(EnumName<FieldType>::value, out);
writeChar('(', out);
auto first = true;
for (const auto & name_and_value : values)
{
if (!first)
writeString(", ", out);
first = false;
writeQuotedString(name_and_value.first, out);
writeString(" = ", out);
writeText(name_and_value.second, out);
}
writeChar(')', out);
}
return name;
}
void fillMaps()
{
for (const auto & name_and_value : values )
{
const auto name_to_value_pair = name_to_value_map.insert(
{ StringRef{name_and_value.first}, name_and_value.second });
if (!name_to_value_pair.second)
throw Exception{
"Duplicate names in enum: '" + name_and_value.first + "' = " + toString(name_and_value.second)
+ " and '" + name_to_value_pair.first->first.toString() + "' = " + toString(
name_to_value_pair.first->second),
ErrorCodes::SYNTAX_ERROR
};
const auto value_to_name_pair = value_to_name_map.insert(
{ name_and_value.second, StringRef{name_and_value.first} });
if (!value_to_name_pair.second)
throw Exception{
"Duplicate values in enum: '" + name_and_value.first + "' = " + toString(name_and_value.second)
+ " and '" + value_to_name_pair.first->second.toString() + "' = " + toString(
value_to_name_pair.first->first),
ErrorCodes::SYNTAX_ERROR
};
}
}
static std::string generateName(const Values & values);
void fillMaps();
public:
DataTypeEnum(const Values & values_) : values{values_}
{
if (values.empty())
throw Exception{
"DataTypeEnum enumeration cannot be empty",
ErrorCodes::EMPTY_DATA_PASSED
};
fillMaps();
std::sort(std::begin(values), std::end(values), [] (auto & left, auto & right) {
return left.second < right.second;
});
name = generateName(values);
}
DataTypeEnum(const DataTypeEnum & other) : values{other.values}, name{other.name}
{
fillMaps();
}
DataTypeEnum(const Values & values_);
DataTypeEnum(const DataTypeEnum & other);
const Values & getValues() const { return values; }
std::string getName() const override { return name; }
bool isNumeric() const override { return true; }
bool behavesAsNumber() const override { return true; }
const StringRef & getNameForValue(const FieldType & value) const
@ -148,125 +70,30 @@ public:
return it->second;
}
DataTypePtr clone() const override
{
return new DataTypeEnum(*this);
}
DataTypePtr clone() const override;
void serializeBinary(const Field & field, WriteBuffer & ostr) const override
{
const FieldType x = get<typename NearestFieldType<FieldType>::Type>(field);
writeBinary(x, ostr);
}
void deserializeBinary(Field & field, ReadBuffer & istr) const override
{
FieldType x;
readBinary(x, istr);
field = nearestFieldType(x);
}
void serializeBinary(const Field & field, WriteBuffer & ostr) const override;
void deserializeBinary(Field & field, ReadBuffer & istr) const override;
void serializeText(const Field & field, WriteBuffer & ostr) const override;
void deserializeText(Field & field, ReadBuffer & istr) const override;
void serializeTextEscaped(const Field & field, WriteBuffer & ostr) const override;
void deserializeTextEscaped(Field & field, ReadBuffer & istr) const override;
void serializeTextQuoted(const Field & field, WriteBuffer & ostr) const override;
void deserializeTextQuoted(Field & field, ReadBuffer & istr) const override;
void serializeTextJSON(const Field & field, WriteBuffer & ostr) const override;
void serializeTextXML(const Field & field, WriteBuffer & ostr) const override;
void serializeTextCSV(const Field & field, WriteBuffer & ostr) const override;
void deserializeTextCSV(Field & field, ReadBuffer & istr, const char delimiter) const override;
void serializeText(const Field & field, WriteBuffer & ostr) const override
{
const FieldType x = get<typename NearestFieldType<FieldType>::Type>(field);
writeString(getNameForValue(x), ostr);
}
void deserializeText(Field & field, ReadBuffer & istr) const override
{
std::string name;
readString(name, istr);
field = nearestFieldType(getValue(StringRef(name)));
}
void serializeTextEscaped(const Field & field, WriteBuffer & ostr) const override
{
const FieldType x = get<typename NearestFieldType<FieldType>::Type>(field);
writeEscapedString(getNameForValue(x), ostr);
}
void deserializeTextEscaped(Field & field, ReadBuffer & istr) const override
{
std::string name;
readEscapedString(name, istr);
field = nearestFieldType(getValue(StringRef(name)));
}
void serializeTextQuoted(const Field & field, WriteBuffer & ostr) const override
{
const FieldType x = get<typename NearestFieldType<FieldType>::Type>(field);
writeQuotedString(getNameForValue(x), ostr);
}
void deserializeTextQuoted(Field & field, ReadBuffer & istr) const override
{
std::string name;
readQuotedString(name, istr);
field = nearestFieldType(getValue(StringRef(name)));
}
void serializeTextJSON(const Field & field, WriteBuffer & ostr) const override
{
const FieldType x = get<typename NearestFieldType<FieldType>::Type>(field);
writeJSONString(getNameForValue(x), ostr);
}
void serializeTextCSV(const Field & field, WriteBuffer & ostr) const override
{
const FieldType x = get<typename NearestFieldType<FieldType>::Type>(field);
writeCSVString<>(getNameForValue(x), ostr);
}
void deserializeTextCSV(Field & field, ReadBuffer & istr, const char delimiter) const override
{
std::string name;
readCSVString(name, istr, delimiter);
field = nearestFieldType(getValue(StringRef(name)));
}
/** Потоковая сериализация массивов устроена по-особенному:
* - записываются/читаются элементы, уложенные подряд, без размеров массивов;
* - размеры записываются/читаются в отдельный столбец,
* и о записи/чтении размеров должна позаботиться вызывающая сторона.
* Это нужно, так как при реализации вложенных структур, несколько массивов могут иметь общие размеры.
*/
/** Записать только значения, без размеров. Вызывающая сторона также должна куда-нибудь записать смещения. */
void serializeBinary(
const IColumn & column, WriteBuffer & ostr, const size_t offset = 0, size_t limit = 0) const override
{
const auto & x = typeid_cast<const ColumnType &>(column).getData();
const auto size = x.size();
if (limit == 0 || offset + limit > size)
limit = size - offset;
ostr.write(reinterpret_cast<const char *>(&x[offset]), sizeof(FieldType) * limit);
}
/** Прочитать только значения, без размеров.
* При этом, в column уже заранее должны быть считаны все размеры.
*/
void deserializeBinary(
IColumn & column, ReadBuffer & istr, const size_t limit, const double avg_value_size_hint) const override
{
auto & x = typeid_cast<ColumnType &>(column).getData();
const auto initial_size = x.size();
x.resize(initial_size + limit);
const auto size = istr.readBig(reinterpret_cast<char*>(&x[initial_size]), sizeof(FieldType) * limit);
x.resize(initial_size + size / sizeof(FieldType));
}
void serializeBinary(const IColumn & column, WriteBuffer & ostr, const size_t offset = 0, size_t limit = 0) const override;
void deserializeBinary(IColumn & column, ReadBuffer & istr, const size_t limit, const double avg_value_size_hint) const override;
size_t getSizeOfField() const override { return sizeof(FieldType); }
ColumnPtr createColumn() const override { return new ColumnType; }
ColumnPtr createConstColumn(const size_t size, const Field & field) const override
{
return new ConstColumnType(size, get<typename NearestFieldType<FieldType>::Type>(field));
}
ColumnPtr createConstColumn(const size_t size, const Field & field) const override;
Field getDefault() const override
{
return typename NearestFieldType<FieldType>::Type(values.front().second);
}
Field getDefault() const override;
};

View File

@ -57,6 +57,7 @@ public:
void deserializeTextQuoted(Field & field, ReadBuffer & istr) const override;
void serializeTextJSON(const Field & field, WriteBuffer & ostr) const override;
void serializeTextXML(const Field & field, WriteBuffer & ostr) const override;
void serializeTextCSV(const Field & field, WriteBuffer & ostr) const override;
void deserializeTextCSV(Field & field, ReadBuffer & istr, const char delimiter) const override;

View File

@ -43,6 +43,7 @@ public:
void deserializeTextQuoted(Field & field, ReadBuffer & istr) const override;
void serializeTextJSON(const Field & field, WriteBuffer & ostr) const override;
void serializeTextXML(const Field & field, WriteBuffer & ostr) const override;
void serializeTextCSV(const Field & field, WriteBuffer & ostr) const override;
void deserializeTextCSV(Field & field, ReadBuffer & istr, const char delimiter) const override;

View File

@ -1,13 +1,6 @@
#pragma once
#include <DB/DataTypes/IDataType.h>
#include <DB/Columns/ColumnTuple.h>
#include <DB/Columns/ColumnConst.h>
#include <DB/DataStreams/NativeBlockInputStream.h>
#include <DB/DataStreams/NativeBlockOutputStream.h>
#include <ext/map.hpp>
#include <ext/enumerate.hpp>
#include <ext/range.hpp>
namespace DB
@ -25,169 +18,36 @@ private:
public:
DataTypeTuple(DataTypes elems_) : elems(elems_) {}
std::string getName() const override
{
std::stringstream s;
s << "Tuple(";
for (DataTypes::const_iterator it = elems.begin(); it != elems.end(); ++it)
s << (it == elems.begin() ? "" : ", ") << (*it)->getName();
s << ")";
return s.str();
}
std::string getName() const override;
SharedPtr<IDataType> clone() const override { return new DataTypeTuple(elems); }
void serializeBinary(const Field & field, WriteBuffer & ostr) const override
{
const auto & tuple = get<const Tuple &>(field).t;
for (const auto & idx_elem : ext::enumerate(elems))
idx_elem.second->serializeBinary(tuple[idx_elem.first], ostr);
}
void deserializeBinary(Field & field, ReadBuffer & istr) const override
{
const size_t size = elems.size();
field = Tuple(TupleBackend(size));
TupleBackend & tuple = get<Tuple &>(field).t;
for (const auto i : ext::range(0, size))
elems[i]->deserializeBinary(tuple[i], istr);
}
void serializeText(const Field & field, WriteBuffer & ostr) const override
{
const TupleBackend & tuple = get<const Tuple &>(field).t;
writeChar('(', ostr);
for (const auto i : ext::range(0, ext::size(elems)))
{
if (i != 0)
writeChar(',', ostr);
elems[i]->serializeTextQuoted(tuple[i], ostr);
}
writeChar(')', ostr);
}
void deserializeText(Field & field, ReadBuffer & istr) const override
{
const size_t size = elems.size();
field = Tuple(TupleBackend(size));
TupleBackend & tuple = get<Tuple &>(field).t;
assertChar('(', istr);
for (const auto i : ext::range(0, size))
{
skipWhitespaceIfAny(istr);
if (i != 0)
assertChar(',', istr);
elems[i]->deserializeTextQuoted(tuple[i], istr);
}
skipWhitespaceIfAny(istr);
assertChar(')', istr);
}
void serializeTextEscaped(const Field & field, WriteBuffer & ostr) const override
{
serializeText(field, ostr);
}
void deserializeTextEscaped(Field & field, ReadBuffer & istr) const override
{
deserializeText(field, istr);
}
void serializeTextQuoted(const Field & field, WriteBuffer & ostr) const override
{
serializeText(field, ostr);
}
void deserializeTextQuoted(Field & field, ReadBuffer & istr) const override
{
deserializeText(field, istr);
}
void serializeTextJSON(const Field & field, WriteBuffer & ostr) const override
{
const TupleBackend & tuple = get<const Tuple &>(field).t;
writeChar('[', ostr);
for (const auto i : ext::range(0, ext::size(elems)))
{
if (i != 0)
writeChar(',', ostr);
elems[i]->serializeTextJSON(tuple[i], ostr);
}
writeChar(']', ostr);
}
void serializeBinary(const Field & field, WriteBuffer & ostr) const override;
void deserializeBinary(Field & field, ReadBuffer & istr) const override;
void serializeText(const Field & field, WriteBuffer & ostr) const override;
void deserializeText(Field & field, ReadBuffer & istr) const override;
void serializeTextEscaped(const Field & field, WriteBuffer & ostr) const override;
void deserializeTextEscaped(Field & field, ReadBuffer & istr) const override;
void serializeTextQuoted(const Field & field, WriteBuffer & ostr) const override;
void deserializeTextQuoted(Field & field, ReadBuffer & istr) const override;
void serializeTextJSON(const Field & field, WriteBuffer & ostr) const override;
void serializeTextXML(const Field & field, WriteBuffer & ostr) const override;
/// Кортежи в формате CSV будем сериализовать, как отдельные столбцы (то есть, теряя их вложенность в кортеж).
void serializeTextCSV(const Field & field, WriteBuffer & ostr) const override
{
const TupleBackend & tuple = get<const Tuple &>(field).t;
for (const auto i : ext::range(0, ext::size(elems)))
{
if (i != 0)
writeChar(',', ostr);
elems[i]->serializeTextCSV(tuple[i], ostr);
}
}
void serializeTextCSV(const Field & field, WriteBuffer & ostr) const override;
void deserializeTextCSV(Field & field, ReadBuffer & istr, const char delimiter) const override;
void deserializeTextCSV(Field & field, ReadBuffer & istr, const char delimiter) const override
{
const size_t size = elems.size();
field = Tuple(TupleBackend(size));
TupleBackend & tuple = get<Tuple &>(field).t;
for (const auto i : ext::range(0, size))
{
if (i != 0)
{
skipWhitespaceIfAny(istr);
assertChar(delimiter, istr);
skipWhitespaceIfAny(istr);
}
elems[i]->deserializeTextCSV(tuple[i], istr, delimiter);
}
}
void serializeBinary(const IColumn & column, WriteBuffer & ostr, size_t offset = 0, size_t limit = 0) const override
{
const ColumnTuple & real_column = static_cast<const ColumnTuple &>(column);
for (size_t i = 0, size = elems.size(); i < size; ++i)
NativeBlockOutputStream::writeData(*elems[i], real_column.getData().getByPosition(i).column, ostr, offset, limit);
}
void serializeBinary(const IColumn & column, WriteBuffer & ostr, size_t offset = 0, size_t limit = 0) const override;
/** limit обязательно должен быть в точности равен количеству сериализованных значений.
* Именно из-за этого (невозможности читать меньший кусок записанных данных), Tuple не могут быть использованы для хранения данных в таблицах.
* (Хотя могут быть использованы для передачи данных по сети в Native формате.)
*/
void deserializeBinary(IColumn & column, ReadBuffer & istr, size_t limit, double avg_value_size_hint) const override
{
ColumnTuple & real_column = static_cast<ColumnTuple &>(column);
for (size_t i = 0, size = elems.size(); i < size; ++i)
NativeBlockInputStream::readData(*elems[i], *real_column.getData().getByPosition(i).column, istr, limit);
}
void deserializeBinary(IColumn & column, ReadBuffer & istr, size_t limit, double avg_value_size_hint) const override;
ColumnPtr createColumn() const override
{
Block tuple_block;
for (size_t i = 0, size = elems.size(); i < size; ++i)
{
ColumnWithTypeAndName col;
col.column = elems[i]->createColumn();
col.type = elems[i]->clone();
tuple_block.insert(col);
}
return new ColumnTuple(tuple_block);
}
ColumnPtr createConstColumn(size_t size, const Field & field) const override
{
return new ColumnConstTuple(size, get<const Tuple &>(field), new DataTypeTuple(elems));
}
Field getDefault() const override
{
return Tuple(ext::map<TupleBackend>(elems, [] (const DataTypePtr & elem) { return elem->getDefault(); }));
}
ColumnPtr createColumn() const override;
ColumnPtr createConstColumn(size_t size, const Field & field) const override;
Field getDefault() const override;
const DataTypes & getElements() const { return elems; }
};

View File

@ -73,6 +73,13 @@ public:
*/
virtual void serializeTextJSON(const Field & field, WriteBuffer & ostr) const = 0;
/** Текстовая сериализация для подстановки в формат XML.
*/
virtual void serializeTextXML(const Field & field, WriteBuffer & ostr) const
{
serializeText(field, ostr);
}
/** Текстовая сериализация для формата CSV.
* delimiter - какого разделителя ожидать при чтении, если строковое значение не в кавычках (сам разделитель не съедается).
*/

View File

@ -4,6 +4,7 @@
#include <DB/Columns/ColumnConst.h>
#include <DB/Columns/ColumnString.h>
#include <DB/Columns/ColumnFixedString.h>
#include <DB/Columns/ColumnTuple.h>
#include <DB/DataTypes/DataTypesNumberFixed.h>
#include <DB/DataTypes/DataTypeDateTime.h>
@ -28,11 +29,12 @@ namespace DB
* - строки и фиксированные строки;
* - даты;
* - даты-с-временем;
* внутри каждой группы, но не из разных групп.
* внутри каждой группы, но не из разных групп;
* - кортежи (сравнение лексикографическое).
*
* Исключение: можно сравнивать дату и дату-с-временем с константной строкой. Пример: EventDate = '2015-01-01'.
*
* TODO Массивы, кортежи.
* TODO Массивы.
*/
/** Игнорируем warning о сравнении signed и unsigned.

View File

@ -10,6 +10,7 @@
#include <DB/Columns/ColumnVector.h>
#include <DB/Columns/ColumnArray.h>
#include <DB/Columns/ColumnString.h>
#include <DB/Columns/ColumnTuple.h>
#include <DB/Interpreters/Context.h>
#include <DB/Interpreters/Dictionaries.h>

View File

@ -348,6 +348,25 @@ inline void writeBackQuotedString(const String & s, WriteBuffer & buf)
writeAnyQuotedString<'`'>(s, buf);
}
/// То же самое, но обратные кавычки применяются только при наличии символов, не подходящих для идентификатора без обратных кавычек.
inline void writeProbablyBackQuotedString(const String & s, WriteBuffer & buf)
{
if (s.empty() || !((s[0] >= 'a' && s[0] <= 'z') || (s[0] >= 'A' && s[0] <= 'Z') || s[0] == '_'))
writeBackQuotedString(s, buf);
else
{
const char * pos = s.data() + 1;
const char * end = s.data() + s.size();
for (; pos < end; ++pos)
if (!((*pos >= 'a' && *pos <= 'z') || (*pos >= 'A' && *pos <= 'Z') || (*pos >= '0' && *pos <= '9') || *pos == '_'))
break;
if (pos != end)
writeBackQuotedString(s, buf);
else
writeString(s, buf);
}
}
/** Выводит строку в для формата CSV.
* Правила:
@ -400,23 +419,50 @@ void writeCSVString(const StringRef & s, WriteBuffer & buf)
}
/// То же самое, но обратные кавычки применяются только при наличии символов, не подходящих для идентификатора без обратных кавычек.
inline void writeProbablyBackQuotedString(const String & s, WriteBuffer & buf)
/// Запись строки в текстовый узел в XML (не в атрибут - иначе нужно больше эскейпинга).
inline void writeXMLString(const char * begin, const char * end, WriteBuffer & buf)
{
if (s.empty() || !((s[0] >= 'a' && s[0] <= 'z') || (s[0] >= 'A' && s[0] <= 'Z') || s[0] == '_'))
writeBackQuotedString(s, buf);
else
const char * pos = begin;
while (true)
{
const char * pos = s.data() + 1;
const char * end = s.data() + s.size();
for (; pos < end; ++pos)
if (!((*pos >= 'a' && *pos <= 'z') || (*pos >= 'A' && *pos <= 'Z') || (*pos >= '0' && *pos <= '9') || *pos == '_'))
const char * next_pos = strpbrk(pos, "<&");
if (next_pos == nullptr)
{
buf.write(pos, end - pos);
break;
if (pos != end)
writeBackQuotedString(s, buf);
else
writeString(s, buf);
}
else if (*next_pos == 0) /// Нулевой байт до конца строки.
{
++next_pos;
buf.write(pos, next_pos - pos);
}
else if (*next_pos == '<')
{
buf.write(pos, next_pos - pos);
++next_pos;
writeCString("&lt;", buf);
}
else if (*next_pos == '&')
{
buf.write(pos, next_pos - pos);
++next_pos;
writeCString("&amp;", buf);
}
/// NOTE Возможно, для некоторых парсеров XML нужно ещё эскейпить нулевой байт и некоторые control characters.
pos = next_pos;
}
}
inline void writeXMLString(const String & s, WriteBuffer & buf)
{
writeXMLString(s.data(), s.data() + s.size(), buf);
}
inline void writeXMLString(const StringRef & s, WriteBuffer & buf)
{
writeXMLString(s.data, s.data + s.size, buf);
}

View File

@ -0,0 +1,20 @@
#pragma once
#include <DB/Core/Field.h>
namespace DB
{
class IDataType;
/** Используется для интерпретации выражений в множестве в IN,
* а также в запросе вида INSERT ... VALUES ...
*
* Чтобы корректно работали выражения вида 1.0 IN (1) или чтобы 1 IN (1, 2.0, 2.5, -1) работало так же, как 1 IN (1, 2).
* Проверяет совместимость типов, проверяет попадание значений в диапазон допустимых значений типа, делает преобразование типа.
* Если значение не попадает в диапазон - возвращает Null.
*/
Field convertFieldToType(const Field & src, const IDataType & type);
}

View File

@ -5,6 +5,7 @@
#include <DB/Columns/ColumnString.h>
#include <DB/Columns/ColumnConst.h>
#include <DB/Columns/ColumnArray.h>
#include <DB/Columns/ColumnTuple.h>
#include <DB/Columns/ColumnFixedString.h>
#include <DB/DataTypes/DataTypeTuple.h>
#include <ext/enumerate.hpp>

View File

@ -17,6 +17,7 @@
#include <DB/DataStreams/BlockOutputStreamFromRowOutputStream.h>
#include <DB/DataStreams/JSONRowOutputStream.h>
#include <DB/DataStreams/JSONCompactRowOutputStream.h>
#include <DB/DataStreams/XMLRowOutputStream.h>
#include <DB/DataStreams/TSKVRowOutputStream.h>
#include <DB/DataStreams/PrettyCompactMonoBlockOutputStream.h>
#include <DB/DataStreams/ODBCDriverBlockOutputStream.h>
@ -68,6 +69,7 @@ BlockInputStreamPtr FormatFactory::getInput(const String & name, ReadBuffer & bu
|| name == "Null"
|| name == "JSON"
|| name == "JSONCompact"
|| name == "XML"
|| name == "TSKV"
|| name == "ODBCDriver")
throw Exception("Format " + name + " is not suitable for input", ErrorCodes::FORMAT_IS_NOT_SUITABLE_FOR_INPUT);
@ -121,6 +123,8 @@ BlockOutputStreamPtr FormatFactory::getOutput(const String & name, WriteBuffer &
return new BlockOutputStreamFromRowOutputStream(new JSONRowOutputStream(buf, sample));
else if (name == "JSONCompact")
return new BlockOutputStreamFromRowOutputStream(new JSONCompactRowOutputStream(buf, sample));
else if (name == "XML")
return new BlockOutputStreamFromRowOutputStream(new XMLRowOutputStream(buf, sample));
else if (name == "TSKV")
return new BlockOutputStreamFromRowOutputStream(new TSKVRowOutputStream(buf, sample));
else if (name == "ODBCDriver")

View File

@ -0,0 +1,168 @@
#include <DB/IO/WriteHelpers.h>
#include <DB/IO/WriteBufferValidUTF8.h>
#include <DB/DataStreams/XMLRowOutputStream.h>
namespace DB
{
using Poco::SharedPtr;
XMLRowOutputStream::XMLRowOutputStream(WriteBuffer & ostr_, const Block & sample_)
: dst_ostr(ostr_)
{
NamesAndTypesList columns(sample_.getColumnsList());
fields.assign(columns.begin(), columns.end());
bool have_non_numeric_columns = false;
for (size_t i = 0; i < sample_.columns(); ++i)
{
if (!sample_.unsafeGetByPosition(i).type->isNumeric())
{
have_non_numeric_columns = true;
break;
}
}
if (have_non_numeric_columns)
{
validating_ostr.reset(new WriteBufferValidUTF8(dst_ostr));
ostr = validating_ostr.get();
}
else
ostr = &dst_ostr;
}
void XMLRowOutputStream::writePrefix()
{
writeCString("<?xml version='1.0' encoding='UTF-8' ?>\n", *ostr);
writeCString("<result>\n", *ostr);
writeCString("\t<meta>\n", *ostr);
writeCString("\t\t<columns>\n", *ostr);
for (size_t i = 0; i < fields.size(); ++i)
{
writeCString("\t\t\t<column>\n", *ostr);
writeCString("\t\t\t\t<name>", *ostr);
writeXMLString(fields[i].name, *ostr);
writeCString("</name>\n", *ostr);
writeCString("\t\t\t\t<type>", *ostr);
writeXMLString(fields[i].type->getName(), *ostr);
writeCString("</type>\n", *ostr);
writeCString("\t\t\t</column>\n", *ostr);
}
writeCString("\t\t</columns>\n", *ostr);
writeCString("\t</meta>\n", *ostr);
writeCString("\t<data>\n", *ostr);
}
void XMLRowOutputStream::writeField(const Field & field)
{
writeCString("\t\t\t<field>", *ostr);
fields[field_number].type->serializeTextXML(field, *ostr);
writeCString("</field>\n", *ostr);
++field_number;
}
void XMLRowOutputStream::writeRowStartDelimiter()
{
writeCString("\t\t<row>\n", *ostr);
}
void XMLRowOutputStream::writeRowEndDelimiter()
{
writeCString("\t\t</row>\n", *ostr);
field_number = 0;
++row_count;
}
void XMLRowOutputStream::writeSuffix()
{
writeCString("\t</data>\n", *ostr);
writeTotals();
writeExtremes();
writeCString("\t<rows>", *ostr);
writeIntText(row_count, *ostr);
writeCString("</rows>\n", *ostr);
writeRowsBeforeLimitAtLeast();
writeCString("</result>\n", *ostr);
ostr->next();
}
void XMLRowOutputStream::writeRowsBeforeLimitAtLeast()
{
if (applied_limit)
{
writeCString("\t<rows_before_limit_at_least>", *ostr);
writeIntText(rows_before_limit, *ostr);
writeCString("</rows_before_limit_at_least>\n", *ostr);
}
}
void XMLRowOutputStream::writeTotals()
{
if (totals)
{
writeCString("\t<totals>\n", *ostr);
size_t totals_columns = totals.columns();
for (size_t i = 0; i < totals_columns; ++i)
{
const ColumnWithTypeAndName & column = totals.getByPosition(i);
writeCString("\t\t<field>", *ostr);
column.type->serializeTextXML((*column.column)[0], *ostr);
writeCString("</field>\n", *ostr);
}
writeCString("\t</totals>\n", *ostr);
}
}
static void writeExtremesElement(const char * title, const Block & extremes, size_t row_num, WriteBuffer & ostr)
{
writeCString("\t\t<", ostr);
writeCString(title, ostr);
writeCString(">\n", ostr);
size_t extremes_columns = extremes.columns();
for (size_t i = 0; i < extremes_columns; ++i)
{
const ColumnWithTypeAndName & column = extremes.getByPosition(i);
writeCString("\t\t\t<field>", ostr);
column.type->serializeTextXML((*column.column)[row_num], ostr);
writeCString("</field>\n", ostr);
}
writeCString("\t\t</", ostr);
writeCString(title, ostr);
writeCString(">\n", ostr);
}
void XMLRowOutputStream::writeExtremes()
{
if (extremes)
{
writeCString("\t<extremes>\n", *ostr);
writeExtremesElement("min", extremes, 0, *ostr);
writeExtremesElement("max", extremes, 1, *ostr);
writeCString("\t</extremes>\n", *ostr);
}
}
}

View File

@ -142,6 +142,12 @@ void DataTypeAggregateFunction::serializeTextJSON(const Field & field, WriteBuff
}
void DataTypeAggregateFunction::serializeTextXML(const Field & field, WriteBuffer & ostr) const
{
writeXMLString(get<const String &>(field), ostr);
}
void DataTypeAggregateFunction::serializeTextCSV(const Field & field, WriteBuffer & ostr) const
{
writeCSV(get<const String &>(field), ostr);

View File

@ -226,6 +226,21 @@ void DataTypeArray::serializeTextJSON(const Field & field, WriteBuffer & ostr) c
}
void DataTypeArray::serializeTextXML(const Field & field, WriteBuffer & ostr) const
{
const Array & arr = get<const Array &>(field);
writeCString("<array>", ostr);
for (size_t i = 0, size = arr.size(); i < size; ++i)
{
writeCString("<elem>", ostr);
nested->serializeTextXML(arr[i], ostr);
writeCString("</elem>", ostr);
}
writeCString("</array>", ostr);
}
void DataTypeArray::serializeTextCSV(const Field & field, WriteBuffer & ostr) const
{
/// Хорошего способа сериализовать массив в CSV нет. Поэтому сериализуем его в строку, а затем полученную строку запишем в CSV.

View File

@ -0,0 +1,237 @@
#include <DB/IO/WriteBufferFromString.h>
#include <DB/DataTypes/DataTypeEnum.h>
namespace DB
{
namespace ErrorCodes
{
extern const int SYNTAX_ERROR;
extern const int EMPTY_DATA_PASSED;
}
template <typename FieldType> struct EnumName;
template <> struct EnumName<Int8> { static constexpr auto value = "Enum8"; };
template <> struct EnumName<Int16> { static constexpr auto value = "Enum16"; };
template <typename Type>
std::string DataTypeEnum<Type>::generateName(const Values & values)
{
std::string name;
{
WriteBufferFromString out{name};
writeString(EnumName<FieldType>::value, out);
writeChar('(', out);
auto first = true;
for (const auto & name_and_value : values)
{
if (!first)
writeString(", ", out);
first = false;
writeQuotedString(name_and_value.first, out);
writeString(" = ", out);
writeText(name_and_value.second, out);
}
writeChar(')', out);
}
return name;
}
template <typename Type>
void DataTypeEnum<Type>::fillMaps()
{
for (const auto & name_and_value : values )
{
const auto name_to_value_pair = name_to_value_map.insert(
{ StringRef{name_and_value.first}, name_and_value.second });
if (!name_to_value_pair.second)
throw Exception{
"Duplicate names in enum: '" + name_and_value.first + "' = " + toString(name_and_value.second)
+ " and '" + name_to_value_pair.first->first.toString() + "' = " + toString(
name_to_value_pair.first->second),
ErrorCodes::SYNTAX_ERROR
};
const auto value_to_name_pair = value_to_name_map.insert(
{ name_and_value.second, StringRef{name_and_value.first} });
if (!value_to_name_pair.second)
throw Exception{
"Duplicate values in enum: '" + name_and_value.first + "' = " + toString(name_and_value.second)
+ " and '" + value_to_name_pair.first->second.toString() + "' = " + toString(
value_to_name_pair.first->first),
ErrorCodes::SYNTAX_ERROR
};
}
}
template <typename Type>
DataTypeEnum<Type>::DataTypeEnum(const Values & values_) : values{values_}
{
if (values.empty())
throw Exception{
"DataTypeEnum enumeration cannot be empty",
ErrorCodes::EMPTY_DATA_PASSED
};
fillMaps();
std::sort(std::begin(values), std::end(values), [] (auto & left, auto & right) {
return left.second < right.second;
});
name = generateName(values);
}
template <typename Type>
DataTypeEnum<Type>::DataTypeEnum(const DataTypeEnum & other) : values{other.values}, name{other.name}
{
fillMaps();
}
template <typename Type>
DataTypePtr DataTypeEnum<Type>::clone() const
{
return new DataTypeEnum(*this);
}
template <typename Type>
void DataTypeEnum<Type>::serializeBinary(const Field & field, WriteBuffer & ostr) const
{
const FieldType x = get<typename NearestFieldType<FieldType>::Type>(field);
writeBinary(x, ostr);
}
template <typename Type>
void DataTypeEnum<Type>::deserializeBinary(Field & field, ReadBuffer & istr) const
{
FieldType x;
readBinary(x, istr);
field = nearestFieldType(x);
}
template <typename Type>
void DataTypeEnum<Type>::serializeText(const Field & field, WriteBuffer & ostr) const
{
const FieldType x = get<typename NearestFieldType<FieldType>::Type>(field);
writeString(getNameForValue(x), ostr);
}
template <typename Type>
void DataTypeEnum<Type>::deserializeText(Field & field, ReadBuffer & istr) const
{
std::string name;
readString(name, istr);
field = nearestFieldType(getValue(StringRef(name)));
}
template <typename Type>
void DataTypeEnum<Type>::serializeTextEscaped(const Field & field, WriteBuffer & ostr) const
{
const FieldType x = get<typename NearestFieldType<FieldType>::Type>(field);
writeEscapedString(getNameForValue(x), ostr);
}
template <typename Type>
void DataTypeEnum<Type>::deserializeTextEscaped(Field & field, ReadBuffer & istr) const
{
std::string name;
readEscapedString(name, istr);
field = nearestFieldType(getValue(StringRef(name)));
}
template <typename Type>
void DataTypeEnum<Type>::serializeTextQuoted(const Field & field, WriteBuffer & ostr) const
{
const FieldType x = get<typename NearestFieldType<FieldType>::Type>(field);
writeQuotedString(getNameForValue(x), ostr);
}
template <typename Type>
void DataTypeEnum<Type>::deserializeTextQuoted(Field & field, ReadBuffer & istr) const
{
std::string name;
readQuotedString(name, istr);
field = nearestFieldType(getValue(StringRef(name)));
}
template <typename Type>
void DataTypeEnum<Type>::serializeTextJSON(const Field & field, WriteBuffer & ostr) const
{
const FieldType x = get<typename NearestFieldType<FieldType>::Type>(field);
writeJSONString(getNameForValue(x), ostr);
}
template <typename Type>
void DataTypeEnum<Type>::serializeTextXML(const Field & field, WriteBuffer & ostr) const
{
const FieldType x = get<typename NearestFieldType<FieldType>::Type>(field);
writeXMLString(getNameForValue(x), ostr);
}
template <typename Type>
void DataTypeEnum<Type>::serializeTextCSV(const Field & field, WriteBuffer & ostr) const
{
const FieldType x = get<typename NearestFieldType<FieldType>::Type>(field);
writeCSVString<>(getNameForValue(x), ostr);
}
template <typename Type>
void DataTypeEnum<Type>::deserializeTextCSV(Field & field, ReadBuffer & istr, const char delimiter) const
{
std::string name;
readCSVString(name, istr, delimiter);
field = nearestFieldType(getValue(StringRef(name)));
}
template <typename Type>
void DataTypeEnum<Type>::serializeBinary(
const IColumn & column, WriteBuffer & ostr, const size_t offset, size_t limit) const
{
const auto & x = typeid_cast<const ColumnType &>(column).getData();
const auto size = x.size();
if (limit == 0 || offset + limit > size)
limit = size - offset;
ostr.write(reinterpret_cast<const char *>(&x[offset]), sizeof(FieldType) * limit);
}
template <typename Type>
void DataTypeEnum<Type>::deserializeBinary(
IColumn & column, ReadBuffer & istr, const size_t limit, const double avg_value_size_hint) const
{
auto & x = typeid_cast<ColumnType &>(column).getData();
const auto initial_size = x.size();
x.resize(initial_size + limit);
const auto size = istr.readBig(reinterpret_cast<char*>(&x[initial_size]), sizeof(FieldType) * limit);
x.resize(initial_size + size / sizeof(FieldType));
}
template <typename Type>
ColumnPtr DataTypeEnum<Type>::createConstColumn(const size_t size, const Field & field) const
{
return new ConstColumnType(size, get<typename NearestFieldType<FieldType>::Type>(field));
}
template <typename Type>
Field DataTypeEnum<Type>::getDefault() const
{
return typename NearestFieldType<FieldType>::Type(values.front().second);
}
/// Явные инстанцирования.
template class DataTypeEnum<Int8>;
template class DataTypeEnum<Int16>;
}

View File

@ -22,6 +22,8 @@
#include <DB/Parsers/parseQuery.h>
#include <DB/DataTypes/DataTypeEnum.h>
#include <ext/map.hpp>
namespace DB
{
@ -32,6 +34,7 @@ namespace ErrorCodes
extern const int UNKNOWN_TYPE;
extern const int NESTED_TYPE_TOO_DEEP;
extern const int PARAMETERS_TO_AGGREGATE_FUNCTIONS_MUST_BE_LITERALS;
extern const int SYNTAX_ERROR;
}

View File

@ -125,6 +125,12 @@ void DataTypeFixedString::serializeTextJSON(const Field & field, WriteBuffer & o
}
void DataTypeFixedString::serializeTextXML(const Field & field, WriteBuffer & ostr) const
{
writeXMLString(get<const String &>(field), ostr);
}
void DataTypeFixedString::serializeTextCSV(const Field & field, WriteBuffer & ostr) const
{
writeCSV(get<const String &>(field), ostr);

View File

@ -224,6 +224,12 @@ void DataTypeString::serializeTextJSON(const Field & field, WriteBuffer & ostr)
}
void DataTypeString::serializeTextXML(const Field & field, WriteBuffer & ostr) const
{
writeXMLString(get<const String &>(field), ostr);
}
void DataTypeString::serializeTextCSV(const Field & field, WriteBuffer & ostr) const
{
writeCSV(get<const String &>(field), ostr);

View File

@ -0,0 +1,184 @@
#include <DB/Columns/ColumnTuple.h>
#include <DB/Columns/ColumnConst.h>
#include <DB/DataStreams/NativeBlockInputStream.h>
#include <DB/DataStreams/NativeBlockOutputStream.h>
#include <DB/DataTypes/DataTypeTuple.h>
#include <ext/map.hpp>
#include <ext/enumerate.hpp>
#include <ext/range.hpp>
namespace DB
{
std::string DataTypeTuple::getName() const
{
std::stringstream s;
s << "Tuple(";
for (DataTypes::const_iterator it = elems.begin(); it != elems.end(); ++it)
s << (it == elems.begin() ? "" : ", ") << (*it)->getName();
s << ")";
return s.str();
}
void DataTypeTuple::serializeBinary(const Field & field, WriteBuffer & ostr) const
{
const auto & tuple = get<const Tuple &>(field).t;
for (const auto & idx_elem : ext::enumerate(elems))
idx_elem.second->serializeBinary(tuple[idx_elem.first], ostr);
}
void DataTypeTuple::deserializeBinary(Field & field, ReadBuffer & istr) const
{
const size_t size = elems.size();
field = Tuple(TupleBackend(size));
TupleBackend & tuple = get<Tuple &>(field).t;
for (const auto i : ext::range(0, size))
elems[i]->deserializeBinary(tuple[i], istr);
}
void DataTypeTuple::serializeText(const Field & field, WriteBuffer & ostr) const
{
const TupleBackend & tuple = get<const Tuple &>(field).t;
writeChar('(', ostr);
for (const auto i : ext::range(0, ext::size(elems)))
{
if (i != 0)
writeChar(',', ostr);
elems[i]->serializeTextQuoted(tuple[i], ostr);
}
writeChar(')', ostr);
}
void DataTypeTuple::deserializeText(Field & field, ReadBuffer & istr) const
{
const size_t size = elems.size();
field = Tuple(TupleBackend(size));
TupleBackend & tuple = get<Tuple &>(field).t;
assertChar('(', istr);
for (const auto i : ext::range(0, size))
{
skipWhitespaceIfAny(istr);
if (i != 0)
assertChar(',', istr);
elems[i]->deserializeTextQuoted(tuple[i], istr);
}
skipWhitespaceIfAny(istr);
assertChar(')', istr);
}
void DataTypeTuple::serializeTextEscaped(const Field & field, WriteBuffer & ostr) const
{
serializeText(field, ostr);
}
void DataTypeTuple::deserializeTextEscaped(Field & field, ReadBuffer & istr) const
{
deserializeText(field, istr);
}
void DataTypeTuple::serializeTextQuoted(const Field & field, WriteBuffer & ostr) const
{
serializeText(field, ostr);
}
void DataTypeTuple::deserializeTextQuoted(Field & field, ReadBuffer & istr) const
{
deserializeText(field, istr);
}
void DataTypeTuple::serializeTextJSON(const Field & field, WriteBuffer & ostr) const
{
const TupleBackend & tuple = get<const Tuple &>(field).t;
writeChar('[', ostr);
for (const auto i : ext::range(0, ext::size(elems)))
{
if (i != 0)
writeChar(',', ostr);
elems[i]->serializeTextJSON(tuple[i], ostr);
}
writeChar(']', ostr);
}
void DataTypeTuple::serializeTextXML(const Field & field, WriteBuffer & ostr) const
{
const TupleBackend & tuple = get<const Tuple &>(field).t;
writeCString("<tuple>", ostr);
for (const auto i : ext::range(0, ext::size(elems)))
{
writeCString("<elem>", ostr);
elems[i]->serializeTextXML(tuple[i], ostr);
writeCString("</elem>", ostr);
}
writeCString("</tuple>", ostr);
}
void DataTypeTuple::serializeTextCSV(const Field & field, WriteBuffer & ostr) const
{
const TupleBackend & tuple = get<const Tuple &>(field).t;
for (const auto i : ext::range(0, ext::size(elems)))
{
if (i != 0)
writeChar(',', ostr);
elems[i]->serializeTextCSV(tuple[i], ostr);
}
}
void DataTypeTuple::deserializeTextCSV(Field & field, ReadBuffer & istr, const char delimiter) const
{
const size_t size = elems.size();
field = Tuple(TupleBackend(size));
TupleBackend & tuple = get<Tuple &>(field).t;
for (const auto i : ext::range(0, size))
{
if (i != 0)
{
skipWhitespaceIfAny(istr);
assertChar(delimiter, istr);
skipWhitespaceIfAny(istr);
}
elems[i]->deserializeTextCSV(tuple[i], istr, delimiter);
}
}
void DataTypeTuple::serializeBinary(const IColumn & column, WriteBuffer & ostr, size_t offset, size_t limit) const
{
const ColumnTuple & real_column = static_cast<const ColumnTuple &>(column);
for (size_t i = 0, size = elems.size(); i < size; ++i)
NativeBlockOutputStream::writeData(*elems[i], real_column.getData().getByPosition(i).column, ostr, offset, limit);
}
void DataTypeTuple::deserializeBinary(IColumn & column, ReadBuffer & istr, size_t limit, double avg_value_size_hint) const
{
ColumnTuple & real_column = static_cast<ColumnTuple &>(column);
for (size_t i = 0, size = elems.size(); i < size; ++i)
NativeBlockInputStream::readData(*elems[i], *real_column.getData().getByPosition(i).column, istr, limit);
}
ColumnPtr DataTypeTuple::createColumn() const
{
Block tuple_block;
for (size_t i = 0, size = elems.size(); i < size; ++i)
{
ColumnWithTypeAndName col;
col.column = elems[i]->createColumn();
col.type = elems[i]->clone();
tuple_block.insert(col);
}
return new ColumnTuple(tuple_block);
}
ColumnPtr DataTypeTuple::createConstColumn(size_t size, const Field & field) const
{
return new ColumnConstTuple(size, get<const Tuple &>(field), new DataTypeTuple(elems));
}
Field DataTypeTuple::getDefault() const
{
return Tuple(ext::map<TupleBackend>(elems, [] (const DataTypePtr & elem) { return elem->getDefault(); }));
}
}

View File

@ -1,6 +1,7 @@
#include <DB/Core/FieldVisitors.h>
#include <DB/DataTypes/FieldToDataType.h>
#include <DB/DataTypes/DataTypeTuple.h>
#include <ext/size.hpp>
namespace DB

View File

@ -0,0 +1,4 @@
1 Hello 2016-01-01 2016-01-02 03:04:05 [1,2,3]
2 Hello, world 2016-01-02 2016-01-02 03:04:00 [0,1]
3 hello, world! 2016-01-03 2016-01-02 03:00:00 []
4 World 2016-01-04 2016-12-11 10:09:08 [3,2,1]

View File

@ -0,0 +1,7 @@
DROP TABLE IF EXISTS test.insert;
CREATE TABLE test.insert (i UInt64, s String, d Date, t DateTime, a Array(UInt32)) ENGINE = Memory;
INSERT INTO test.insert VALUES (1, 'Hello', '2016-01-01', '2016-01-02 03:04:05', [1, 2, 3]), (1 + 1, concat('Hello', ', world'), toDate('2016-01-01') + 1, toStartOfMinute(toDateTime('2016-01-02 03:04:05')), [[0,1],[2]][1]), (round(pi()), concat('hello', ', world!'), toDate(toDateTime('2016-01-03 03:04:05')), toStartOfHour(toDateTime('2016-01-02 03:04:05')), []), (4, 'World', '2016-01-04', '2016-12-11 10:09:08', [3,2,1]);
SELECT * FROM test.insert ORDER BY i;
DROP TABLE test.insert;