mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-24 16:42:05 +00:00
Initial implementation of XML output format [#METR-20026].
This commit is contained in:
parent
12363ed329
commit
8efb3e5ade
@ -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
|
||||
|
@ -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
|
||||
|
63
dbms/include/DB/DataStreams/XMLRowOutputStream.h
Normal file
63
dbms/include/DB/DataStreams/XMLRowOutputStream.h
Normal 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;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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; }
|
||||
};
|
||||
|
||||
|
@ -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 - какого разделителя ожидать при чтении, если строковое значение не в кавычках (сам разделитель не съедается).
|
||||
*/
|
||||
|
@ -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.
|
||||
|
@ -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>
|
||||
|
@ -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("<", buf);
|
||||
}
|
||||
else if (*next_pos == '&')
|
||||
{
|
||||
buf.write(pos, next_pos - pos);
|
||||
++next_pos;
|
||||
writeCString("&", 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);
|
||||
}
|
||||
|
||||
|
||||
|
20
dbms/include/DB/Interpreters/convertFieldToType.h
Normal file
20
dbms/include/DB/Interpreters/convertFieldToType.h
Normal 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);
|
||||
|
||||
}
|
@ -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>
|
||||
|
@ -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")
|
||||
|
168
dbms/src/DataStreams/XMLRowOutputStream.cpp
Normal file
168
dbms/src/DataStreams/XMLRowOutputStream.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
|
@ -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.
|
||||
|
237
dbms/src/DataTypes/DataTypeEnum.cpp
Normal file
237
dbms/src/DataTypes/DataTypeEnum.cpp
Normal 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>;
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
184
dbms/src/DataTypes/DataTypeTuple.cpp
Normal file
184
dbms/src/DataTypes/DataTypeTuple.cpp
Normal 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(); }));
|
||||
}
|
||||
|
||||
}
|
@ -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
|
||||
|
@ -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]
|
@ -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;
|
Loading…
Reference in New Issue
Block a user