#include #include #include namespace DB { namespace ErrorCodes { extern const int SYNTAX_ERROR; extern const int EMPTY_DATA_PASSED; } template struct EnumName; template <> struct EnumName { static constexpr auto value = "Enum8"; }; template <> struct EnumName { static constexpr auto value = "Enum16"; }; template std::string DataTypeEnum::generateName(const Values & values) { std::string name; { WriteBufferFromString out{name}; writeString(EnumName::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 void DataTypeEnum::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 DataTypeEnum::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 DataTypeEnum::DataTypeEnum(const DataTypeEnum & other) : values{other.values}, name{other.name} { fillMaps(); } template DataTypePtr DataTypeEnum::clone() const { return std::make_shared(*this); } template void DataTypeEnum::serializeBinary(const Field & field, WriteBuffer & ostr) const { const FieldType x = get::Type>(field); writeBinary(x, ostr); } template void DataTypeEnum::deserializeBinary(Field & field, ReadBuffer & istr) const { FieldType x; readBinary(x, istr); field = nearestFieldType(x); } template void DataTypeEnum::serializeBinary(const IColumn & column, size_t row_num, WriteBuffer & ostr) const { writeBinary(static_cast(column).getData()[row_num], ostr); } template void DataTypeEnum::deserializeBinary(IColumn & column, ReadBuffer & istr) const { typename ColumnType::value_type x; readBinary(x, istr); static_cast(column).getData().push_back(x); } template void DataTypeEnum::serializeText(const IColumn & column, size_t row_num, WriteBuffer & ostr) const { writeString(getNameForValue(static_cast(column).getData()[row_num]), ostr); } template void DataTypeEnum::serializeTextEscaped(const IColumn & column, size_t row_num, WriteBuffer & ostr) const { writeEscapedString(getNameForValue(static_cast(column).getData()[row_num]), ostr); } template void DataTypeEnum::deserializeTextEscaped(IColumn & column, ReadBuffer & istr) const { /// NOTE It would be nice to do without creating a temporary object - at least extract std::string out. std::string name; readEscapedString(name, istr); static_cast(column).getData().push_back(getValue(StringRef(name))); } template void DataTypeEnum::serializeTextQuoted(const IColumn & column, size_t row_num, WriteBuffer & ostr) const { writeQuotedString(getNameForValue(static_cast(column).getData()[row_num]), ostr); } template void DataTypeEnum::deserializeTextQuoted(IColumn & column, ReadBuffer & istr) const { std::string name; readQuotedStringWithSQLStyle(name, istr); static_cast(column).getData().push_back(getValue(StringRef(name))); } template void DataTypeEnum::serializeTextJSON(const IColumn & column, size_t row_num, WriteBuffer & ostr, bool) const { writeJSONString(getNameForValue(static_cast(column).getData()[row_num]), ostr); } template void DataTypeEnum::serializeTextXML(const IColumn & column, size_t row_num, WriteBuffer & ostr) const { writeXMLString(getNameForValue(static_cast(column).getData()[row_num]), ostr); } template void DataTypeEnum::deserializeTextJSON(IColumn & column, ReadBuffer & istr) const { std::string name; readJSONString(name, istr); static_cast(column).getData().push_back(getValue(StringRef(name))); } template void DataTypeEnum::serializeTextCSV(const IColumn & column, size_t row_num, WriteBuffer & ostr) const { writeCSVString(getNameForValue(static_cast(column).getData()[row_num]), ostr); } template void DataTypeEnum::deserializeTextCSV(IColumn & column, ReadBuffer & istr, const char delimiter) const { std::string name; readCSVString(name, istr, delimiter); static_cast(column).getData().push_back(getValue(StringRef(name))); } template void DataTypeEnum::serializeBinaryBulk( const IColumn & column, WriteBuffer & ostr, const size_t offset, size_t limit) const { const auto & x = typeid_cast(column).getData(); const auto size = x.size(); if (limit == 0 || offset + limit > size) limit = size - offset; ostr.write(reinterpret_cast(&x[offset]), sizeof(FieldType) * limit); } template void DataTypeEnum::deserializeBinaryBulk( IColumn & column, ReadBuffer & istr, const size_t limit, const double avg_value_size_hint) const { auto & x = typeid_cast(column).getData(); const auto initial_size = x.size(); x.resize(initial_size + limit); const auto size = istr.readBig(reinterpret_cast(&x[initial_size]), sizeof(FieldType) * limit); x.resize(initial_size + size / sizeof(FieldType)); } template ColumnPtr DataTypeEnum::createConstColumn(const size_t size, const Field & field) const { return std::make_shared(size, get::Type>(field)); } template Field DataTypeEnum::getDefault() const { return typename NearestFieldType::Type(values.front().second); } template static void checkOverflow(Int64 value) { if (!(std::numeric_limits::min() <= value && value <= std::numeric_limits::max())) throw Exception("DataTypeEnum: Unexpected value " + toString(value), ErrorCodes::BAD_TYPE_OF_FIELD); } template Field DataTypeEnum::castToName(const Field & value_or_name) const { if (value_or_name.getType() == Field::Types::String) { getValue(value_or_name.get()); /// Check correctness return value_or_name.get(); } else if (value_or_name.getType() == Field::Types::Int64) { Int64 value = value_or_name.get(); checkOverflow(value); return getNameForValue(static_cast(value)).toString(); } else throw Exception(String("DataTypeEnum: Unsupported type of field ") + value_or_name.getTypeName(), ErrorCodes::BAD_TYPE_OF_FIELD); } template Field DataTypeEnum::castToValue(const Field & value_or_name) const { if (value_or_name.getType() == Field::Types::String) { return static_cast(getValue(value_or_name.get())); } else if (value_or_name.getType() == Field::Types::Int64 || value_or_name.getType() == Field::Types::UInt64) { Int64 value = value_or_name.get(); checkOverflow(value); getNameForValue(static_cast(value)); /// Check correctness return value; } else throw Exception(String("DataTypeEnum: Unsupported type of field ") + value_or_name.getTypeName(), ErrorCodes::BAD_TYPE_OF_FIELD); } /// Explicit instantiations. template class DataTypeEnum; template class DataTypeEnum; }