#include #include #include #include #include #include #include #include #include #include #include namespace DB { namespace ErrorCodes { extern const int NO_COMMON_TYPE; } namespace { String getExceptionMessagePrefix(const DataTypes & types) { WriteBufferFromOwnString res; res << "There is no subtype for types "; bool first = true; for (const auto & type : types) { if (!first) res << ", "; first = false; res << type->getName(); } return res.str(); } } DataTypePtr getMostSubtype(const DataTypes & types, bool throw_if_result_is_nothing, bool force_support_conversion) { auto get_nothing_or_throw = [throw_if_result_is_nothing, & types](const std::string & reason) { if (throw_if_result_is_nothing) throw Exception(getExceptionMessagePrefix(types) + reason, ErrorCodes::NO_COMMON_TYPE); return std::make_shared(); }; /// Trivial cases if (types.empty()) { if (throw_if_result_is_nothing) throw Exception("There is no common type for empty type list", ErrorCodes::NO_COMMON_TYPE); return std::make_shared(); } if (types.size() == 1) { if (throw_if_result_is_nothing && typeid_cast(types[0].get())) throw Exception("There is no common type for type Nothing", ErrorCodes::NO_COMMON_TYPE); return types[0]; } /// All types are equal { bool all_equal = true; for (size_t i = 1, size = types.size(); i < size; ++i) { if (!types[i]->equals(*types[0])) { all_equal = false; break; } } if (all_equal) return types[0]; } /// Recursive rules /// If there are Nothing types, result is Nothing { for (const auto & type : types) if (typeid_cast(type.get())) return get_nothing_or_throw(" because some of them are Nothing"); } /// For Arrays { bool have_array = false; bool all_arrays = true; DataTypes nested_types; nested_types.reserve(types.size()); for (const auto & type : types) { if (const auto * type_array = typeid_cast(type.get())) { have_array = true; nested_types.emplace_back(type_array->getNestedType()); } else all_arrays = false; } if (have_array) { if (!all_arrays) return get_nothing_or_throw(" because some of them are Array and some of them are not"); return std::make_shared(getMostSubtype(nested_types, false, force_support_conversion)); } } /// For tuples { bool have_tuple = false; bool all_tuples = true; size_t tuple_size = 0; std::vector nested_types; for (const auto & type : types) { if (const auto * type_tuple = typeid_cast(type.get())) { if (!have_tuple) { tuple_size = type_tuple->getElements().size(); nested_types.resize(tuple_size); for (size_t elem_idx = 0; elem_idx < tuple_size; ++elem_idx) nested_types[elem_idx].reserve(types.size()); } else if (tuple_size != type_tuple->getElements().size()) return get_nothing_or_throw(" because Tuples have different sizes"); have_tuple = true; for (size_t elem_idx = 0; elem_idx < tuple_size; ++elem_idx) nested_types[elem_idx].emplace_back(type_tuple->getElements()[elem_idx]); } else all_tuples = false; } if (have_tuple) { if (!all_tuples) return get_nothing_or_throw(" because some of them are Tuple and some of them are not"); DataTypes common_tuple_types(tuple_size); for (size_t elem_idx = 0; elem_idx < tuple_size; ++elem_idx) common_tuple_types[elem_idx] = getMostSubtype(nested_types[elem_idx], throw_if_result_is_nothing, force_support_conversion); return std::make_shared(common_tuple_types); } } /// For Nullable { bool all_nullable = true; bool have_nullable = false; DataTypes nested_types; nested_types.reserve(types.size()); for (const auto & type : types) { if (const auto * type_nullable = typeid_cast(type.get())) { have_nullable = true; nested_types.emplace_back(type_nullable->getNestedType()); } else { all_nullable = false; nested_types.emplace_back(type); } } if (have_nullable) { if (all_nullable || force_support_conversion) return std::make_shared(getMostSubtype(nested_types, false, force_support_conversion)); return getMostSubtype(nested_types, throw_if_result_is_nothing, force_support_conversion); } } /// Non-recursive rules /// For String and FixedString, the common type is FixedString. /// For different FixedStrings, the common type is Nothing. /// No other types are compatible with Strings. TODO Enums? { bool have_string = false; bool all_strings = true; DataTypePtr fixed_string_type = nullptr; for (const auto & type : types) { if (isFixedString(type)) { have_string = true; if (!fixed_string_type) fixed_string_type = type; else if (!type->equals(*fixed_string_type)) return get_nothing_or_throw(" because some of them are FixedStrings with different length"); } else if (isString(type)) have_string = true; else all_strings = false; } if (have_string) { if (!all_strings) return get_nothing_or_throw(" because some of them are String/FixedString and some of them are not"); return fixed_string_type ? fixed_string_type : std::make_shared(); } } /// For Date and DateTime, the common type is Date. No other types are compatible. { bool have_date_or_datetime = false; bool all_date_or_datetime = true; for (const auto & type : types) { if (isDateOrDateTime(type)) have_date_or_datetime = true; else all_date_or_datetime = false; } if (have_date_or_datetime) { if (!all_date_or_datetime) return get_nothing_or_throw(" because some of them are Date/DateTime and some of them are not"); return std::make_shared(); } } /// For numeric types, the most complicated part. { bool all_numbers = true; size_t min_bits_of_signed_integer = 0; size_t min_bits_of_unsigned_integer = 0; size_t min_mantissa_bits_of_floating = 0; auto minimize = [](size_t & what, size_t value) { if (what == 0 || value < what) what = value; }; for (const auto & type : types) { if (typeid_cast(type.get())) minimize(min_bits_of_unsigned_integer, 8); else if (typeid_cast(type.get())) minimize(min_bits_of_unsigned_integer, 16); else if (typeid_cast(type.get())) minimize(min_bits_of_unsigned_integer, 32); else if (typeid_cast(type.get())) minimize(min_bits_of_unsigned_integer, 64); else if (typeid_cast(type.get())) minimize(min_bits_of_signed_integer, 8); else if (typeid_cast(type.get())) minimize(min_bits_of_signed_integer, 16); else if (typeid_cast(type.get())) minimize(min_bits_of_signed_integer, 32); else if (typeid_cast(type.get())) minimize(min_bits_of_signed_integer, 64); else if (typeid_cast(type.get())) minimize(min_mantissa_bits_of_floating, 24); else if (typeid_cast(type.get())) minimize(min_mantissa_bits_of_floating, 53); else all_numbers = false; } if (min_bits_of_signed_integer || min_bits_of_unsigned_integer || min_mantissa_bits_of_floating) { if (!all_numbers) return get_nothing_or_throw(" because some of them are numbers and some of them are not"); /// If the result must be floating. if (!min_bits_of_signed_integer && !min_bits_of_unsigned_integer) { if (min_mantissa_bits_of_floating <= 24) return std::make_shared(); else if (min_mantissa_bits_of_floating <= 53) return std::make_shared(); else throw Exception("Logical error: " + getExceptionMessagePrefix(types) + " but as all data types are floats, we must have found maximum float type", ErrorCodes::NO_COMMON_TYPE); } /// If there are signed and unsigned types of same bit-width, the result must be unsigned number. if (min_bits_of_unsigned_integer && (min_bits_of_signed_integer == 0 || min_bits_of_unsigned_integer <= min_bits_of_signed_integer)) { if (min_bits_of_unsigned_integer <= 8) return std::make_shared(); else if (min_bits_of_unsigned_integer <= 16) return std::make_shared(); else if (min_bits_of_unsigned_integer <= 32) return std::make_shared(); else if (min_bits_of_unsigned_integer <= 64) return std::make_shared(); else throw Exception("Logical error: " + getExceptionMessagePrefix(types) + " but as all data types are integers, we must have found maximum unsigned integer type", ErrorCodes::NO_COMMON_TYPE); } /// All signed. { if (min_bits_of_signed_integer <= 8) return std::make_shared(); else if (min_bits_of_signed_integer <= 16) return std::make_shared(); else if (min_bits_of_signed_integer <= 32) return std::make_shared(); else if (min_bits_of_signed_integer <= 64) return std::make_shared(); else throw Exception("Logical error: " + getExceptionMessagePrefix(types) + " but as all data types are integers, we must have found maximum signed integer type", ErrorCodes::NO_COMMON_TYPE); } } } /// All other data types (UUID, AggregateFunction, Enum...) are compatible only if they are the same (checked in trivial cases). return get_nothing_or_throw(""); } }