#pragma once #include #include #include #include #include #include #include #include #include #include #include namespace DB { /** Функция выбора по условию: if(cond, then, else). * cond - UInt8 * then, else - числовые типы, для которых есть общий тип, либо даты, даты-с-временем, либо строки, либо массивы таких типов. */ template struct NumIfImpl { private: static PODArray & result_vector(Block & block, size_t result, size_t size) { ColumnVector * col_res = new ColumnVector; block.getByPosition(result).column = col_res; typename ColumnVector::Container_t & vec_res = col_res->getData(); vec_res.resize(size); return vec_res; } public: static void vector_vector( const PODArray & cond, const PODArray & a, const PODArray & b, Block & block, size_t result) { size_t size = cond.size(); PODArray & res = result_vector(block, result, size); for (size_t i = 0; i < size; ++i) res[i] = cond[i] ? static_cast(a[i]) : static_cast(b[i]); } static void vector_constant( const PODArray & cond, const PODArray & a, B b, Block & block, size_t result) { size_t size = cond.size(); PODArray & res = result_vector(block, result, size); for (size_t i = 0; i < size; ++i) res[i] = cond[i] ? static_cast(a[i]) : static_cast(b); } static void constant_vector( const PODArray & cond, A a, const PODArray & b, Block & block, size_t result) { size_t size = cond.size(); PODArray & res = result_vector(block, result, size); for (size_t i = 0; i < size; ++i) res[i] = cond[i] ? static_cast(a) : static_cast(b[i]); } static void constant_constant( const PODArray & cond, A a, B b, Block & block, size_t result) { size_t size = cond.size(); PODArray & res = result_vector(block, result, size); for (size_t i = 0; i < size; ++i) res[i] = cond[i] ? static_cast(a) : static_cast(b); } }; template struct NumIfImpl { private: static void throw_error() { throw Exception("Internal logic error: invalid types of arguments 2 and 3 of if", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); } public: static void vector_vector( const PODArray & cond, const PODArray & a, const PODArray & b, Block & block, size_t result) { throw_error(); } static void vector_constant( const PODArray & cond, const PODArray & a, B b, Block & block, size_t result) { throw_error(); } static void constant_vector( const PODArray & cond, A a, const PODArray & b, Block & block, size_t result) { throw_error(); } static void constant_constant( const PODArray & cond, A a, B b, Block & block, size_t result) { throw_error(); } }; struct StringIfImpl { static void vector_vector( const PODArray & cond, const ColumnString::Chars_t & a_data, const ColumnString::Offsets_t & a_offsets, const ColumnString::Chars_t & b_data, const ColumnString::Offsets_t & b_offsets, ColumnString::Chars_t & c_data, ColumnString::Offsets_t & c_offsets) { size_t size = cond.size(); c_offsets.resize(size); c_data.reserve(std::max(a_data.size(), b_data.size())); ColumnString::Offset_t a_prev_offset = 0; ColumnString::Offset_t b_prev_offset = 0; ColumnString::Offset_t c_prev_offset = 0; for (size_t i = 0; i < size; ++i) { if (cond[i]) { size_t size_to_write = a_offsets[i] - a_prev_offset; c_data.resize(c_data.size() + size_to_write); memcpy(&c_data[c_prev_offset], &a_data[a_prev_offset], size_to_write); c_prev_offset += size_to_write; c_offsets[i] = c_prev_offset; } else { size_t size_to_write = b_offsets[i] - b_prev_offset; c_data.resize(c_data.size() + size_to_write); memcpy(&c_data[c_prev_offset], &b_data[b_prev_offset], size_to_write); c_prev_offset += size_to_write; c_offsets[i] = c_prev_offset; } a_prev_offset = a_offsets[i]; b_prev_offset = b_offsets[i]; } } static void vector_fixed_vector_fixed( const PODArray & cond, const ColumnFixedString::Chars_t & a_data, const ColumnFixedString::Chars_t & b_data, const size_t N, ColumnFixedString::Chars_t & c_data) { size_t size = cond.size(); c_data.resize(a_data.size()); for (size_t i = 0; i < size; ++i) { if (cond[i]) memcpy(&c_data[i * N], &a_data[i * N], N); else memcpy(&c_data[i * N], &b_data[i * N], N); } } template static void vector_vector_fixed_impl( const PODArray & cond, const ColumnString::Chars_t & a_data, const ColumnString::Offsets_t & a_offsets, const ColumnFixedString::Chars_t & b_data, const size_t b_N, ColumnString::Chars_t & c_data, ColumnString::Offsets_t & c_offsets) { size_t size = cond.size(); c_offsets.resize(size); c_data.reserve(std::max(a_data.size(), b_data.size() + size)); ColumnString::Offset_t a_prev_offset = 0; ColumnString::Offset_t c_prev_offset = 0; for (size_t i = 0; i < size; ++i) { if (negative != cond[i]) { size_t size_to_write = a_offsets[i] - a_prev_offset; c_data.resize(c_data.size() + size_to_write); memcpy(&c_data[c_prev_offset], &a_data[a_prev_offset], size_to_write); c_prev_offset += size_to_write; c_offsets[i] = c_prev_offset; } else { size_t size_to_write = b_N; c_data.resize(c_data.size() + size_to_write + 1); memcpy(&c_data[c_prev_offset], &b_data[i * b_N], size_to_write); c_data.back() = 0; c_prev_offset += size_to_write + 1; c_offsets[i] = c_prev_offset; } a_prev_offset = a_offsets[i]; } } static void vector_vector_fixed( const PODArray & cond, const ColumnString::Chars_t & a_data, const ColumnString::Offsets_t & a_offsets, const ColumnFixedString::Chars_t & b_data, const size_t b_N, ColumnString::Chars_t & c_data, ColumnString::Offsets_t & c_offsets) { vector_vector_fixed_impl(cond, a_data, a_offsets, b_data, b_N, c_data, c_offsets); } static void vector_fixed_vector( const PODArray & cond, const ColumnFixedString::Chars_t & a_data, const size_t a_N, const ColumnString::Chars_t & b_data, const ColumnString::Offsets_t & b_offsets, ColumnString::Chars_t & c_data, ColumnString::Offsets_t & c_offsets) { vector_vector_fixed_impl(cond, b_data, b_offsets, a_data, a_N, c_data, c_offsets); } template static void vector_constant_impl( const PODArray & cond, const ColumnString::Chars_t & a_data, const ColumnString::Offsets_t & a_offsets, const String & b, ColumnString::Chars_t & c_data, ColumnString::Offsets_t & c_offsets) { size_t size = cond.size(); c_offsets.resize(size); c_data.reserve(a_data.size()); ColumnString::Offset_t a_prev_offset = 0; ColumnString::Offset_t c_prev_offset = 0; for (size_t i = 0; i < size; ++i) { if (negative != cond[i]) { size_t size_to_write = a_offsets[i] - a_prev_offset; c_data.resize(c_data.size() + size_to_write); memcpy(&c_data[c_prev_offset], &a_data[a_prev_offset], size_to_write); c_prev_offset += size_to_write; c_offsets[i] = c_prev_offset; } else { size_t size_to_write = b.size() + 1; c_data.resize(c_data.size() + size_to_write); memcpy(&c_data[c_prev_offset], b.data(), size_to_write); c_prev_offset += size_to_write; c_offsets[i] = c_prev_offset; } a_prev_offset = a_offsets[i]; } } static void vector_constant( const PODArray & cond, const ColumnString::Chars_t & a_data, const ColumnString::Offsets_t & a_offsets, const String & b, ColumnString::Chars_t & c_data, ColumnString::Offsets_t & c_offsets) { return vector_constant_impl(cond, a_data, a_offsets, b, c_data, c_offsets); } static void constant_vector( const PODArray & cond, const String & a, const ColumnString::Chars_t & b_data, const ColumnString::Offsets_t & b_offsets, ColumnString::Chars_t & c_data, ColumnString::Offsets_t & c_offsets) { return vector_constant_impl(cond, b_data, b_offsets, a, c_data, c_offsets); } template static void vector_fixed_constant_impl( const PODArray & cond, const ColumnFixedString::Chars_t & a_data, const size_t a_N, const String & b, ColumnString::Chars_t & c_data, ColumnString::Offsets_t & c_offsets) { size_t size = cond.size(); c_offsets.resize(size); c_data.reserve(a_data.size()); ColumnString::Offset_t c_prev_offset = 0; for (size_t i = 0; i < size; ++i) { if (negative != cond[i]) { size_t size_to_write = a_N; c_data.resize(c_data.size() + size_to_write + 1); memcpy(&c_data[c_prev_offset], &a_data[i * a_N], size_to_write); c_data.back() = 0; c_prev_offset += size_to_write + 1; c_offsets[i] = c_prev_offset; } else { size_t size_to_write = b.size() + 1; c_data.resize(c_data.size() + size_to_write); memcpy(&c_data[c_prev_offset], b.data(), size_to_write); c_prev_offset += size_to_write; c_offsets[i] = c_prev_offset; } } } static void vector_fixed_constant( const PODArray & cond, const ColumnFixedString::Chars_t & a_data, const size_t N, const String & b, ColumnString::Chars_t & c_data, ColumnString::Offsets_t & c_offsets) { vector_fixed_constant_impl(cond, a_data, N, b, c_data, c_offsets); } static void constant_vector_fixed( const PODArray & cond, const String & a, const ColumnFixedString::Chars_t & b_data, const size_t N, ColumnString::Chars_t & c_data, ColumnString::Offsets_t & c_offsets) { vector_fixed_constant_impl(cond, b_data, N, a, c_data, c_offsets); } static void constant_constant( const PODArray & cond, const String & a, const String & b, ColumnString::Chars_t & c_data, ColumnString::Offsets_t & c_offsets) { size_t size = cond.size(); c_offsets.resize(size); c_data.reserve((std::max(a.size(), b.size()) + 1) * size); ColumnString::Offset_t c_prev_offset = 0; for (size_t i = 0; i < size; ++i) { if (cond[i]) { size_t size_to_write = a.size() + 1; c_data.resize(c_data.size() + size_to_write); memcpy(&c_data[c_prev_offset], a.data(), size_to_write); c_prev_offset += size_to_write; c_offsets[i] = c_prev_offset; } else { size_t size_to_write = b.size() + 1; c_data.resize(c_data.size() + size_to_write); memcpy(&c_data[c_prev_offset], b.data(), size_to_write); c_prev_offset += size_to_write; c_offsets[i] = c_prev_offset; } } } }; template struct NumArrayIfImpl { template static ALWAYS_INLINE void copy_from_vector( size_t i, const PODArray & from_data, const ColumnArray::Offsets_t & from_offsets, ColumnArray::Offset_t from_prev_offset, PODArray & to_data, ColumnArray::Offsets_t & to_offsets, ColumnArray::Offset_t & to_prev_offset) { size_t size_to_write = from_offsets[i] - from_prev_offset; to_data.resize(to_data.size() + size_to_write); for (size_t i = 0; i < size_to_write; ++i) to_data[to_prev_offset + i] = static_cast(from_data[from_prev_offset + i]); to_prev_offset += size_to_write; to_offsets[i] = to_prev_offset; } static ALWAYS_INLINE void copy_from_constant( size_t i, const PODArray & from_data, PODArray & to_data, ColumnArray::Offsets_t & to_offsets, ColumnArray::Offset_t & to_prev_offset) { size_t size_to_write = from_data.size(); to_data.resize(to_data.size() + size_to_write); memcpy(&to_data[to_prev_offset], from_data.data(), size_to_write * sizeof(from_data[0])); to_prev_offset += size_to_write; to_offsets[i] = to_prev_offset; } static void create_result_column( Block & block, size_t result, PODArray ** c_data, ColumnArray::Offsets_t ** c_offsets) { ColumnVector * col_res_vec = new ColumnVector; ColumnArray * col_res_array = new ColumnArray(col_res_vec); block.getByPosition(result).column = col_res_array; *c_data = &col_res_vec->getData(); *c_offsets = &col_res_array->getOffsets(); } static void vector_vector( const PODArray & cond, const PODArray & a_data, const ColumnArray::Offsets_t & a_offsets, const PODArray & b_data, const ColumnArray::Offsets_t & b_offsets, Block & block, size_t result) { PODArray * c_data = nullptr; ColumnArray::Offsets_t * c_offsets = nullptr; create_result_column(block, result, &c_data, &c_offsets); size_t size = cond.size(); c_offsets->resize(size); c_data->reserve(std::max(a_data.size(), b_data.size())); ColumnArray::Offset_t a_prev_offset = 0; ColumnArray::Offset_t b_prev_offset = 0; ColumnArray::Offset_t c_prev_offset = 0; for (size_t i = 0; i < size; ++i) { if (cond[i]) copy_from_vector(i, a_data, a_offsets, a_prev_offset, *c_data, *c_offsets, c_prev_offset); else copy_from_vector(i, b_data, b_offsets, b_prev_offset, *c_data, *c_offsets, c_prev_offset); a_prev_offset = a_offsets[i]; b_prev_offset = b_offsets[i]; } } static void vector_constant( const PODArray & cond, const PODArray & a_data, const ColumnArray::Offsets_t & a_offsets, const Array & b, Block & block, size_t result) { PODArray * c_data = nullptr; ColumnArray::Offsets_t * c_offsets = nullptr; create_result_column(block, result, &c_data, &c_offsets); PODArray b_converted(b.size()); for (size_t i = 0, size = b.size(); i < size; ++i) b_converted[i] = b[i].get::Type>(); size_t size = cond.size(); c_offsets->resize(size); c_data->reserve(a_data.size()); ColumnArray::Offset_t a_prev_offset = 0; ColumnArray::Offset_t c_prev_offset = 0; for (size_t i = 0; i < size; ++i) { if (cond[i]) copy_from_vector(i, a_data, a_offsets, a_prev_offset, *c_data, *c_offsets, c_prev_offset); else copy_from_constant(i, b_converted, *c_data, *c_offsets, c_prev_offset); a_prev_offset = a_offsets[i]; } } static void constant_vector( const PODArray & cond, const Array & a, const PODArray & b_data, const ColumnArray::Offsets_t & b_offsets, Block & block, size_t result) { PODArray * c_data = nullptr; ColumnArray::Offsets_t * c_offsets = nullptr; create_result_column(block, result, &c_data, &c_offsets); PODArray a_converted(a.size()); for (size_t i = 0, size = a.size(); i < size; ++i) a_converted[i] = a[i].get::Type>(); size_t size = cond.size(); c_offsets->resize(size); c_data->reserve(b_data.size()); ColumnArray::Offset_t b_prev_offset = 0; ColumnArray::Offset_t c_prev_offset = 0; for (size_t i = 0; i < size; ++i) { if (cond[i]) copy_from_constant(i, a_converted, *c_data, *c_offsets, c_prev_offset); else copy_from_vector(i, b_data, b_offsets, b_prev_offset, *c_data, *c_offsets, c_prev_offset); b_prev_offset = b_offsets[i]; } } static void constant_constant( const PODArray & cond, const Array & a, const Array & b, Block & block, size_t result) { PODArray * c_data = nullptr; ColumnArray::Offsets_t * c_offsets = nullptr; create_result_column(block, result, &c_data, &c_offsets); PODArray a_converted(a.size()); for (size_t i = 0, size = a.size(); i < size; ++i) a_converted[i] = a[i].get::Type>(); PODArray b_converted(b.size()); for (size_t i = 0, size = b.size(); i < size; ++i) b_converted[i] = b[i].get::Type>(); size_t size = cond.size(); c_offsets->resize(size); c_data->reserve((std::max(a.size(), b.size())) * size); ColumnArray::Offset_t c_prev_offset = 0; for (size_t i = 0; i < size; ++i) { if (cond[i]) copy_from_constant(i, a_converted, *c_data, *c_offsets, c_prev_offset); else copy_from_constant(i, b_converted, *c_data, *c_offsets, c_prev_offset); } } }; template struct NumArrayIfImpl { private: static void throw_error() { throw Exception("Internal logic error: invalid types of arguments 2 and 3 of if", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); } public: static void vector_vector( const PODArray & cond, const PODArray & a_data, const ColumnArray::Offsets_t & a_offsets, const PODArray & b_data, const ColumnArray::Offsets_t & b_offsets, Block & block, size_t result) { throw_error(); } static void vector_constant( const PODArray & cond, const PODArray & a_data, const ColumnArray::Offsets_t & a_offsets, const Array & b, Block & block, size_t result) { throw_error(); } static void constant_vector( const PODArray & cond, const Array & a, const PODArray & b_data, const ColumnArray::Offsets_t & b_offsets, Block & block, size_t result) { throw_error(); } static void constant_constant( const PODArray & cond, const Array & a, const Array & b, Block & block, size_t result) { throw_error(); } }; /** Реализация для массивов строк. * NOTE: Код слишком сложный, потому что он работает в внутренностями массивов строк. * NOTE: Массивы из FixedString не поддерживаются. */ struct StringArrayIfImpl { static ALWAYS_INLINE void copy_from_vector( size_t i, const ColumnString::Chars_t & from_data, const ColumnString::Offsets_t & from_string_offsets, const ColumnArray::Offsets_t & from_array_offsets, const ColumnArray::Offset_t & from_array_prev_offset, const ColumnString::Offset_t & from_string_prev_offset, ColumnString::Chars_t & to_data, ColumnString::Offsets_t & to_string_offsets, ColumnArray::Offsets_t & to_array_offsets, ColumnArray::Offset_t & to_array_prev_offset, ColumnString::Offset_t & to_string_prev_offset) { size_t array_size = from_array_offsets[i] - from_array_prev_offset; size_t bytes_to_copy = 0; size_t from_string_prev_offset_local = from_string_prev_offset; for (size_t j = 0; j < array_size; ++j) { size_t string_size = from_string_offsets[from_array_prev_offset + j] - from_string_prev_offset_local; to_string_prev_offset += string_size; to_string_offsets.push_back(to_string_prev_offset); from_string_prev_offset_local += string_size; bytes_to_copy += string_size; } size_t to_data_old_size = to_data.size(); to_data.resize(to_data_old_size + bytes_to_copy); memcpy(&to_data[to_data_old_size], &from_data[from_string_prev_offset], bytes_to_copy); to_array_prev_offset += array_size; to_array_offsets[i] = to_array_prev_offset; } static ALWAYS_INLINE void copy_from_constant( size_t i, const Array & from_data, ColumnString::Chars_t & to_data, ColumnString::Offsets_t & to_string_offsets, ColumnArray::Offsets_t & to_array_offsets, ColumnArray::Offset_t & to_array_prev_offset, ColumnString::Offset_t & to_string_prev_offset) { size_t array_size = from_data.size(); for (size_t j = 0; j < array_size; ++j) { const String & str = from_data[j].get(); size_t string_size = str.size() + 1; /// Включая 0 на конце. to_data.resize(to_string_prev_offset + string_size); memcpy(&to_data[to_string_prev_offset], str.data(), string_size); to_string_prev_offset += string_size; to_string_offsets.push_back(to_string_prev_offset); } to_array_prev_offset += array_size; to_array_offsets[i] = to_array_prev_offset; } static void vector_vector( const PODArray & cond, const ColumnString::Chars_t & a_data, const ColumnString::Offsets_t & a_string_offsets, const ColumnArray::Offsets_t & a_array_offsets, const ColumnString::Chars_t & b_data, const ColumnString::Offsets_t & b_string_offsets, const ColumnArray::Offsets_t & b_array_offsets, ColumnString::Chars_t & c_data, ColumnString::Offsets_t & c_string_offsets, ColumnArray::Offsets_t & c_array_offsets) { size_t size = cond.size(); c_array_offsets.resize(size); c_string_offsets.reserve(std::max(a_string_offsets.size(), b_string_offsets.size())); c_data.reserve(std::max(a_data.size(), b_data.size())); ColumnArray::Offset_t a_array_prev_offset = 0; ColumnArray::Offset_t b_array_prev_offset = 0; ColumnArray::Offset_t c_array_prev_offset = 0; ColumnString::Offset_t a_string_prev_offset = 0; ColumnString::Offset_t b_string_prev_offset = 0; ColumnString::Offset_t c_string_prev_offset = 0; for (size_t i = 0; i < size; ++i) { if (cond[i]) copy_from_vector(i, a_data, a_string_offsets, a_array_offsets, a_array_prev_offset, a_string_prev_offset, c_data, c_string_offsets, c_array_offsets, c_array_prev_offset, c_string_prev_offset); else copy_from_vector(i, b_data, b_string_offsets, b_array_offsets, b_array_prev_offset, b_string_prev_offset, c_data, c_string_offsets, c_array_offsets, c_array_prev_offset, c_string_prev_offset); a_array_prev_offset = a_array_offsets[i]; b_array_prev_offset = b_array_offsets[i]; if (a_array_prev_offset) a_string_prev_offset = a_string_offsets[a_array_prev_offset - 1]; if (b_array_prev_offset) b_string_prev_offset = b_string_offsets[b_array_prev_offset - 1]; } } template static void vector_constant_impl( const PODArray & cond, const ColumnString::Chars_t & a_data, const ColumnString::Offsets_t & a_string_offsets, const ColumnArray::Offsets_t & a_array_offsets, const Array & b, ColumnString::Chars_t & c_data, ColumnString::Offsets_t & c_string_offsets, ColumnArray::Offsets_t & c_array_offsets) { size_t size = cond.size(); c_array_offsets.resize(size); c_string_offsets.reserve(a_string_offsets.size()); c_data.reserve(a_data.size()); ColumnArray::Offset_t a_array_prev_offset = 0; ColumnArray::Offset_t c_array_prev_offset = 0; ColumnString::Offset_t a_string_prev_offset = 0; ColumnString::Offset_t c_string_prev_offset = 0; for (size_t i = 0; i < size; ++i) { if (reverse != cond[i]) copy_from_vector(i, a_data, a_string_offsets, a_array_offsets, a_array_prev_offset, a_string_prev_offset, c_data, c_string_offsets, c_array_offsets, c_array_prev_offset, c_string_prev_offset); else copy_from_constant(i, b, c_data, c_string_offsets, c_array_offsets, c_array_prev_offset, c_string_prev_offset); a_array_prev_offset = a_array_offsets[i]; if (a_array_prev_offset) a_string_prev_offset = a_string_offsets[a_array_prev_offset - 1]; } } static void vector_constant( const PODArray & cond, const ColumnString::Chars_t & a_data, const ColumnString::Offsets_t & a_string_offsets, const ColumnArray::Offsets_t & a_array_offsets, const Array & b, ColumnString::Chars_t & c_data, ColumnString::Offsets_t & c_string_offsets, ColumnArray::Offsets_t & c_array_offsets) { vector_constant_impl(cond, a_data, a_string_offsets, a_array_offsets, b, c_data, c_string_offsets, c_array_offsets); } static void constant_vector( const PODArray & cond, const Array & a, const ColumnString::Chars_t & b_data, const ColumnString::Offsets_t & b_string_offsets, const ColumnArray::Offsets_t & b_array_offsets, ColumnString::Chars_t & c_data, ColumnString::Offsets_t & c_string_offsets, ColumnArray::Offsets_t & c_array_offsets) { vector_constant_impl(cond, b_data, b_string_offsets, b_array_offsets, a, c_data, c_string_offsets, c_array_offsets); } static void constant_constant( const PODArray & cond, const Array & a, const Array & b, ColumnString::Chars_t & c_data, ColumnString::Offsets_t & c_string_offsets, ColumnArray::Offsets_t & c_array_offsets) { size_t size = cond.size(); c_array_offsets.resize(size); c_string_offsets.reserve(std::max(a.size(), b.size()) * size); size_t sum_size_a = 0; for (const auto & s : a) sum_size_a += s.get().size() + 1; size_t sum_size_b = 0; for (const auto & s : b) sum_size_b += s.get().size() + 1; c_data.reserve(std::max(sum_size_a, sum_size_b) * size); ColumnArray::Offset_t c_array_prev_offset = 0; ColumnString::Offset_t c_string_prev_offset = 0; for (size_t i = 0; i < size; ++i) { if (cond[i]) copy_from_constant(i, a, c_data, c_string_offsets, c_array_offsets, c_array_prev_offset, c_string_prev_offset); else copy_from_constant(i, b, c_data, c_string_offsets, c_array_offsets, c_array_prev_offset, c_string_prev_offset); } } }; template struct DataTypeFromFieldTypeOrError { static DataTypePtr getDataType() { return new typename DataTypeFromFieldType::Type; } }; template <> struct DataTypeFromFieldTypeOrError { static DataTypePtr getDataType() { return nullptr; } }; class FunctionIf : public IFunction { public: static constexpr auto name = "if"; static IFunction * create(const Context & context) { return new FunctionIf; } private: template bool checkRightType(const DataTypes & arguments, DataTypePtr & type_res) const { if (typeid_cast(&*arguments[2])) { typedef typename NumberTraits::ResultOfIf::Type ResultType; type_res = DataTypeFromFieldTypeOrError::getDataType(); if (!type_res) throw Exception("Arguments 2 and 3 of function " + getName() + " are not upscalable to a common type without loss of precision: " + arguments[1]->getName() + " and " + arguments[2]->getName(), ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); return true; } return false; } template bool checkLeftType(const DataTypes & arguments, DataTypePtr & type_res) const { if (typeid_cast(&*arguments[1])) { if ( checkRightType(arguments, type_res) || checkRightType(arguments, type_res) || checkRightType(arguments, type_res) || checkRightType(arguments, type_res) || checkRightType(arguments, type_res) || checkRightType(arguments, type_res) || checkRightType(arguments, type_res) || checkRightType(arguments, type_res) || checkRightType(arguments, type_res) || checkRightType(arguments, type_res)) return true; else throw Exception("Illegal type " + arguments[2]->getName() + " of third argument of function " + getName(), ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); } return false; } template bool executeRightType( const ColumnVector * cond_col, Block & block, const ColumnNumbers & arguments, size_t result, const ColumnVector * col_left) { const ColumnVector * col_right_vec = typeid_cast *>(&*block.getByPosition(arguments[2]).column); const ColumnConst * col_right_const = typeid_cast *>(&*block.getByPosition(arguments[2]).column); if (!col_right_vec && !col_right_const) return false; typedef typename NumberTraits::ResultOfIf::Type ResultType; if (col_right_vec) NumIfImpl::vector_vector(cond_col->getData(), col_left->getData(), col_right_vec->getData(), block, result); else NumIfImpl::vector_constant(cond_col->getData(), col_left->getData(), col_right_const->getData(), block, result); return true; } template bool executeConstRightType( const ColumnVector * cond_col, Block & block, const ColumnNumbers & arguments, size_t result, const ColumnConst * col_left) { const ColumnVector * col_right_vec = typeid_cast *>(&*block.getByPosition(arguments[2]).column); const ColumnConst * col_right_const = typeid_cast *>(&*block.getByPosition(arguments[2]).column); if (!col_right_vec && !col_right_const) return false; typedef typename NumberTraits::ResultOfIf::Type ResultType; if (col_right_vec) NumIfImpl::constant_vector(cond_col->getData(), col_left->getData(), col_right_vec->getData(), block, result); else NumIfImpl::constant_constant(cond_col->getData(), col_left->getData(), col_right_const->getData(), block, result); return true; } template bool executeRightTypeArray( const ColumnVector * cond_col, Block & block, const ColumnNumbers & arguments, size_t result, const ColumnArray * col_left_array, const ColumnVector * col_left) { const IColumn * col_right_untyped = block.getByPosition(arguments[2]).column.get(); const ColumnArray * col_right_array = typeid_cast(col_right_untyped); const ColumnConstArray * col_right_const_array = typeid_cast(col_right_untyped); if (!col_right_array && !col_right_const_array) return false; typedef typename NumberTraits::ResultOfIf::Type ResultType; if (col_right_array) { const ColumnVector * col_right_vec = typeid_cast *>(&col_right_array->getData()); if (!col_right_vec) return false; NumArrayIfImpl::vector_vector( cond_col->getData(), col_left->getData(), col_left_array->getOffsets(), col_right_vec->getData(), col_right_array->getOffsets(), block, result); } else { if (!typeid_cast::Type *>( typeid_cast(*col_right_const_array->getDataType()).getNestedType().get())) return false; NumArrayIfImpl::vector_constant( cond_col->getData(), col_left->getData(), col_left_array->getOffsets(), col_right_const_array->getData(), block, result); } return true; } template bool executeConstRightTypeArray( const ColumnVector * cond_col, Block & block, const ColumnNumbers & arguments, size_t result, const ColumnConstArray * col_left_const_array) { const IColumn * col_right_untyped = block.getByPosition(arguments[2]).column.get(); const ColumnArray * col_right_array = typeid_cast(col_right_untyped); const ColumnConstArray * col_right_const_array = typeid_cast(col_right_untyped); if (!col_right_array && !col_right_const_array) return false; typedef typename NumberTraits::ResultOfIf::Type ResultType; if (col_right_array) { const ColumnVector * col_right_vec = typeid_cast *>(&col_right_array->getData()); if (!col_right_vec) return false; NumArrayIfImpl::constant_vector( cond_col->getData(), col_left_const_array->getData(), col_right_vec->getData(), col_right_array->getOffsets(), block, result); } else { if (!typeid_cast::Type *>( typeid_cast(*col_right_const_array->getDataType()).getNestedType().get())) return false; NumArrayIfImpl::constant_constant( cond_col->getData(), col_left_const_array->getData(), col_right_const_array->getData(), block, result); } return true; } template bool executeLeftType(const ColumnVector * cond_col, Block & block, const ColumnNumbers & arguments, size_t result) { const IColumn * col_left_untyped = block.getByPosition(arguments[1]).column.get(); const ColumnVector * col_left = nullptr; const ColumnConst * col_const_left = nullptr; const ColumnArray * col_arr_left = nullptr; const ColumnVector * col_arr_left_elems = nullptr; const ColumnConstArray * col_const_arr_left = nullptr; col_left = typeid_cast *>(col_left_untyped); if (!col_left) { col_const_left = typeid_cast *>(col_left_untyped); if (!col_const_left) { col_arr_left = typeid_cast(col_left_untyped); if (col_arr_left) col_arr_left_elems = typeid_cast *>(&col_arr_left->getData()); else col_const_arr_left = typeid_cast(col_left_untyped); } } if (col_left) { if ( executeRightType(cond_col, block, arguments, result, col_left) || executeRightType(cond_col, block, arguments, result, col_left) || executeRightType(cond_col, block, arguments, result, col_left) || executeRightType(cond_col, block, arguments, result, col_left) || executeRightType(cond_col, block, arguments, result, col_left) || executeRightType(cond_col, block, arguments, result, col_left) || executeRightType(cond_col, block, arguments, result, col_left) || executeRightType(cond_col, block, arguments, result, col_left) || executeRightType(cond_col, block, arguments, result, col_left) || executeRightType(cond_col, block, arguments, result, col_left)) return true; else throw Exception("Illegal column " + block.getByPosition(arguments[2]).column->getName() + " of third argument of function " + getName(), ErrorCodes::ILLEGAL_COLUMN); } else if (col_const_left) { if ( executeConstRightType(cond_col, block, arguments, result, col_const_left) || executeConstRightType(cond_col, block, arguments, result, col_const_left) || executeConstRightType(cond_col, block, arguments, result, col_const_left) || executeConstRightType(cond_col, block, arguments, result, col_const_left) || executeConstRightType(cond_col, block, arguments, result, col_const_left) || executeConstRightType(cond_col, block, arguments, result, col_const_left) || executeConstRightType(cond_col, block, arguments, result, col_const_left) || executeConstRightType(cond_col, block, arguments, result, col_const_left) || executeConstRightType(cond_col, block, arguments, result, col_const_left) || executeConstRightType(cond_col, block, arguments, result, col_const_left)) return true; else throw Exception("Illegal column " + block.getByPosition(arguments[2]).column->getName() + " of third argument of function " + getName(), ErrorCodes::ILLEGAL_COLUMN); } else if (col_arr_left && col_arr_left_elems) { if ( executeRightTypeArray(cond_col, block, arguments, result, col_arr_left, col_arr_left_elems) || executeRightTypeArray(cond_col, block, arguments, result, col_arr_left, col_arr_left_elems) || executeRightTypeArray(cond_col, block, arguments, result, col_arr_left, col_arr_left_elems) || executeRightTypeArray(cond_col, block, arguments, result, col_arr_left, col_arr_left_elems) || executeRightTypeArray(cond_col, block, arguments, result, col_arr_left, col_arr_left_elems) || executeRightTypeArray(cond_col, block, arguments, result, col_arr_left, col_arr_left_elems) || executeRightTypeArray(cond_col, block, arguments, result, col_arr_left, col_arr_left_elems) || executeRightTypeArray(cond_col, block, arguments, result, col_arr_left, col_arr_left_elems) || executeRightTypeArray(cond_col, block, arguments, result, col_arr_left, col_arr_left_elems) || executeRightTypeArray(cond_col, block, arguments, result, col_arr_left, col_arr_left_elems)) return true; else throw Exception("Illegal column " + block.getByPosition(arguments[2]).column->getName() + " of third argument of function " + getName(), ErrorCodes::ILLEGAL_COLUMN); } else if (col_const_arr_left && typeid_cast::Type *>( typeid_cast(*col_const_arr_left->getDataType()).getNestedType().get())) { if ( executeConstRightTypeArray(cond_col, block, arguments, result, col_const_arr_left) || executeConstRightTypeArray(cond_col, block, arguments, result, col_const_arr_left) || executeConstRightTypeArray(cond_col, block, arguments, result, col_const_arr_left) || executeConstRightTypeArray(cond_col, block, arguments, result, col_const_arr_left) || executeConstRightTypeArray(cond_col, block, arguments, result, col_const_arr_left) || executeConstRightTypeArray(cond_col, block, arguments, result, col_const_arr_left) || executeConstRightTypeArray(cond_col, block, arguments, result, col_const_arr_left) || executeConstRightTypeArray(cond_col, block, arguments, result, col_const_arr_left) || executeConstRightTypeArray(cond_col, block, arguments, result, col_const_arr_left) || executeConstRightTypeArray(cond_col, block, arguments, result, col_const_arr_left)) return true; else throw Exception("Illegal column " + block.getByPosition(arguments[2]).column->getName() + " of third argument of function " + getName(), ErrorCodes::ILLEGAL_COLUMN); } return false; } bool executeString(const ColumnVector * cond_col, Block & block, const ColumnNumbers & arguments, size_t result) { const IColumn * col_then_untyped = block.getByPosition(arguments[1]).column.get(); const IColumn * col_else_untyped = block.getByPosition(arguments[2]).column.get(); const ColumnString * col_then = typeid_cast(col_then_untyped); const ColumnString * col_else = typeid_cast(col_else_untyped); const ColumnFixedString * col_then_fixed = typeid_cast(col_then_untyped); const ColumnFixedString * col_else_fixed = typeid_cast(col_else_untyped); const ColumnConstString * col_then_const = typeid_cast(col_then_untyped); const ColumnConstString * col_else_const = typeid_cast(col_else_untyped); if ((col_then || col_then_const || col_then_fixed) && (col_else || col_else_const || col_else_fixed)) { if (col_then_fixed && col_else_fixed) { /// Результат - FixedString. if (col_then_fixed->getN() != col_else_fixed->getN()) throw Exception("FixedString columns as 'then' and 'else' arguments of function 'if' has different sizes", ErrorCodes::ILLEGAL_COLUMN); size_t N = col_then_fixed->getN(); ColumnFixedString * col_res = new ColumnFixedString(N); block.getByPosition(result).column = col_res; ColumnFixedString::Chars_t & res_vec = col_res->getChars(); StringIfImpl::vector_fixed_vector_fixed( cond_col->getData(), col_then_fixed->getChars(), col_else_fixed->getChars(), N, res_vec); } else { /// Результат - String. ColumnString * col_res = new ColumnString; block.getByPosition(result).column = col_res; ColumnString::Chars_t & res_vec = col_res->getChars(); ColumnString::Offsets_t & res_offsets = col_res->getOffsets(); if (col_then && col_else) StringIfImpl::vector_vector( cond_col->getData(), col_then->getChars(), col_then->getOffsets(), col_else->getChars(), col_else->getOffsets(), res_vec, res_offsets); else if (col_then && col_else_const) StringIfImpl::vector_constant( cond_col->getData(), col_then->getChars(), col_then->getOffsets(), col_else_const->getData(), res_vec, res_offsets); else if (col_then_const && col_else) StringIfImpl::constant_vector( cond_col->getData(), col_then_const->getData(), col_else->getChars(), col_else->getOffsets(), res_vec, res_offsets); else if (col_then_const && col_else_const) StringIfImpl::constant_constant( cond_col->getData(), col_then_const->getData(), col_else_const->getData(), res_vec, res_offsets); else if (col_then && col_else_fixed) StringIfImpl::vector_vector_fixed( cond_col->getData(), col_then->getChars(), col_then->getOffsets(), col_else_fixed->getChars(), col_else_fixed->getN(), res_vec, res_offsets); else if (col_then_fixed && col_else) StringIfImpl::vector_fixed_vector( cond_col->getData(), col_then_fixed->getChars(), col_then_fixed->getN(), col_else->getChars(), col_else->getOffsets(), res_vec, res_offsets); else if (col_then_const && col_else_fixed) StringIfImpl::constant_vector_fixed( cond_col->getData(), col_then_const->getData(), col_else_fixed->getChars(), col_else_fixed->getN(), res_vec, res_offsets); else if (col_then_fixed && col_else_const) StringIfImpl::vector_fixed_constant( cond_col->getData(), col_then_fixed->getChars(), col_then_fixed->getN(), col_else_const->getData(), res_vec, res_offsets); else return false; } return true; } const ColumnArray * col_arr_then = typeid_cast(col_then_untyped); const ColumnArray * col_arr_else = typeid_cast(col_else_untyped); const ColumnConstArray * col_arr_then_const = typeid_cast(col_then_untyped); const ColumnConstArray * col_arr_else_const = typeid_cast(col_else_untyped); const ColumnString * col_then_elements = col_arr_then ? typeid_cast(&col_arr_then->getData()) : nullptr; const ColumnString * col_else_elements = col_arr_else ? typeid_cast(&col_arr_else->getData()) : nullptr; if (((col_arr_then && col_then_elements) || col_arr_then_const) && ((col_arr_else && col_else_elements) || col_arr_else_const)) { ColumnString * col_res_elements = new ColumnString; ColumnArray * col_res = new ColumnArray(col_res_elements); block.getByPosition(result).column = col_res; ColumnString::Chars_t & res_chars = col_res_elements->getChars(); ColumnString::Offsets_t & res_string_offsets = col_res_elements->getOffsets(); ColumnArray::Offsets_t & res_array_offsets = col_res->getOffsets(); if (col_then_elements && col_else_elements) StringArrayIfImpl::vector_vector( cond_col->getData(), col_then_elements->getChars(), col_then_elements->getOffsets(), col_arr_then->getOffsets(), col_else_elements->getChars(), col_else_elements->getOffsets(), col_arr_else->getOffsets(), res_chars, res_string_offsets, res_array_offsets); else if (col_then_elements && col_arr_else_const) StringArrayIfImpl::vector_constant( cond_col->getData(), col_then_elements->getChars(), col_then_elements->getOffsets(), col_arr_then->getOffsets(), col_arr_else_const->getData(), res_chars, res_string_offsets, res_array_offsets); else if (col_arr_then_const && col_else_elements) StringArrayIfImpl::constant_vector( cond_col->getData(), col_arr_then_const->getData(), col_else_elements->getChars(), col_else_elements->getOffsets(), col_arr_else->getOffsets(), res_chars, res_string_offsets, res_array_offsets); else if (col_arr_then_const && col_arr_else_const) StringArrayIfImpl::constant_constant( cond_col->getData(), col_arr_then_const->getData(), col_arr_else_const->getData(), res_chars, res_string_offsets, res_array_offsets); else return false; return true; } return false; } public: /// Получить имя функции. String getName() const override { return name; } /// Получить типы результата по типам аргументов. Если функция неприменима для данных аргументов - кинуть исключение. DataTypePtr getReturnType(const DataTypes & arguments) const override { if (arguments.size() != 3) throw Exception("Number of arguments for function " + getName() + " doesn't match: passed " + toString(arguments.size()) + ", should be 3.", ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); if (!typeid_cast(&*arguments[0])) throw Exception("Illegal type of first argument (condition) of function if. Must be UInt8.", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); const DataTypeArray * type_arr1 = typeid_cast(arguments[1].get()); const DataTypeArray * type_arr2 = typeid_cast(arguments[2].get()); if (arguments[1]->behavesAsNumber() && arguments[2]->behavesAsNumber()) { DataTypePtr type_res; if (!( checkLeftType(arguments, type_res) || checkLeftType(arguments, type_res) || checkLeftType(arguments, type_res) || checkLeftType(arguments, type_res) || checkLeftType(arguments, type_res) || checkLeftType(arguments, type_res) || checkLeftType(arguments, type_res) || checkLeftType(arguments, type_res) || checkLeftType(arguments, type_res) || checkLeftType(arguments, type_res))) throw Exception("Internal error: unexpected type " + arguments[1]->getName() + " of first argument of function " + getName(), ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); return type_res; } else if (type_arr1 && type_arr2) { /// NOTE Сообщения об ошибках будут относится к типам элементов массивов, что немного некорректно. return new DataTypeArray(getReturnType({arguments[0], type_arr1->getNestedType(), type_arr2->getNestedType()})); } else if (arguments[1]->getName() != arguments[2]->getName()) { const DataTypeString * type_string1 = typeid_cast(arguments[1].get()); const DataTypeString * type_string2 = typeid_cast(arguments[2].get()); const DataTypeFixedString * type_fixed_string1 = typeid_cast(arguments[1].get()); const DataTypeFixedString * type_fixed_string2 = typeid_cast(arguments[2].get()); if (type_fixed_string1 && type_fixed_string2) { if (type_fixed_string1->getN() != type_fixed_string2->getN()) throw Exception("FixedString types as 'then' and 'else' arguments of function 'if' has different sizes", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); return new DataTypeFixedString(type_fixed_string1->getN()); } else if ((type_string1 || type_fixed_string1) && (type_string2 || type_fixed_string2)) { return new DataTypeString; } throw Exception{ "Incompatible second and third arguments for function " + getName() + ": " + arguments[1]->getName() + " and " + arguments[2]->getName(), ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT }; } return arguments[1]; } /// Выполнить функцию над блоком. void execute(Block & block, const ColumnNumbers & arguments, size_t result) override { const ColumnVector * cond_col = typeid_cast *>(&*block.getByPosition(arguments[0]).column); const ColumnConst * cond_const_col = typeid_cast *>(&*block.getByPosition(arguments[0]).column); ColumnPtr materialized_cond_col; if (cond_const_col) { if (block.getByPosition(arguments[1]).type->getName() == block.getByPosition(arguments[2]).type->getName()) { block.getByPosition(result).column = cond_const_col->getData() ? block.getByPosition(arguments[1]).column : block.getByPosition(arguments[2]).column; return; } else { materialized_cond_col = cond_const_col->convertToFullColumn(); cond_col = typeid_cast *>(&*materialized_cond_col); } } if (cond_col) { if (!( executeLeftType(cond_col, block, arguments, result) || executeLeftType(cond_col, block, arguments, result) || executeLeftType(cond_col, block, arguments, result) || executeLeftType(cond_col, block, arguments, result) || executeLeftType(cond_col, block, arguments, result) || executeLeftType(cond_col, block, arguments, result) || executeLeftType(cond_col, block, arguments, result) || executeLeftType(cond_col, block, arguments, result) || executeLeftType(cond_col, block, arguments, result) || executeLeftType(cond_col, block, arguments, result) || executeString(cond_col, block, arguments, result))) throw Exception("Illegal columns " + block.getByPosition(arguments[1]).column->getName() + " and " + block.getByPosition(arguments[2]).column->getName() + " of second (then) and third (else) arguments of function " + getName(), ErrorCodes::ILLEGAL_COLUMN); } else throw Exception("Illegal column " + cond_col->getName() + " of first argument of function " + getName() + ". Must be ColumnUInt8 or ColumnConstUInt8.", ErrorCodes::ILLEGAL_COLUMN); } }; }