ClickHouse/dbms/include/DB/Functions/FunctionsConditional.h

1212 lines
42 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#pragma once
#include <DB/DataTypes/DataTypesNumberFixed.h>
#include <DB/DataTypes/DataTypeArray.h>
#include <DB/Columns/ColumnVector.h>
#include <DB/Columns/ColumnString.h>
#include <DB/Columns/ColumnConst.h>
#include <DB/Columns/ColumnArray.h>
#include <DB/Functions/IFunction.h>
#include <DB/Functions/NumberTraits.h>
namespace DB
{
/** Функция выбора по условию: if(cond, then, else).
* cond - UInt8
* then, else - числовые типы, для которых есть общий тип, либо даты, даты-с-временем, либо строки, либо массивы таких типов.
*/
template <typename A, typename B, typename ResultType>
struct NumIfImpl
{
private:
static PODArray<ResultType> & result_vector(Block & block, size_t result, size_t size)
{
ColumnVector<ResultType> * col_res = new ColumnVector<ResultType>;
block.getByPosition(result).column = col_res;
typename ColumnVector<ResultType>::Container_t & vec_res = col_res->getData();
vec_res.resize(size);
return vec_res;
}
public:
static void vector_vector(
const PODArray<UInt8> & cond,
const PODArray<A> & a, const PODArray<B> & b,
Block & block,
size_t result)
{
size_t size = cond.size();
PODArray<ResultType> & res = result_vector(block, result, size);
for (size_t i = 0; i < size; ++i)
res[i] = cond[i] ? static_cast<ResultType>(a[i]) : static_cast<ResultType>(b[i]);
}
static void vector_constant(
const PODArray<UInt8> & cond,
const PODArray<A> & a, B b,
Block & block,
size_t result)
{
size_t size = cond.size();
PODArray<ResultType> & res = result_vector(block, result, size);
for (size_t i = 0; i < size; ++i)
res[i] = cond[i] ? static_cast<ResultType>(a[i]) : static_cast<ResultType>(b);
}
static void constant_vector(
const PODArray<UInt8> & cond,
A a, const PODArray<B> & b,
Block & block,
size_t result)
{
size_t size = cond.size();
PODArray<ResultType> & res = result_vector(block, result, size);
for (size_t i = 0; i < size; ++i)
res[i] = cond[i] ? static_cast<ResultType>(a) : static_cast<ResultType>(b[i]);
}
static void constant_constant(
const PODArray<UInt8> & cond,
A a, B b,
Block & block,
size_t result)
{
size_t size = cond.size();
PODArray<ResultType> & res = result_vector(block, result, size);
for (size_t i = 0; i < size; ++i)
res[i] = cond[i] ? static_cast<ResultType>(a) : static_cast<ResultType>(b);
}
};
template <typename A, typename B>
struct NumIfImpl<A, B, NumberTraits::Error>
{
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<UInt8> & cond,
const PODArray<A> & a, const PODArray<B> & b,
Block & block,
size_t result)
{
throw_error();
}
static void vector_constant(
const PODArray<UInt8> & cond,
const PODArray<A> & a, B b,
Block & block,
size_t result)
{
throw_error();
}
static void constant_vector(
const PODArray<UInt8> & cond,
A a, const PODArray<B> & b,
Block & block,
size_t result)
{
throw_error();
}
static void constant_constant(
const PODArray<UInt8> & cond,
A a, B b,
Block & block,
size_t result)
{
throw_error();
}
};
struct StringIfImpl
{
static void vector_vector(
const PODArray<UInt8> & 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_constant(
const PODArray<UInt8> & 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 (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 constant_vector(
const PODArray<UInt8> & 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)
{
size_t size = cond.size();
c_offsets.resize(size);
c_data.reserve(b_data.size());
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.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_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;
}
b_prev_offset = b_offsets[i];
}
}
static void constant_constant(
const PODArray<UInt8> & 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 <typename A, typename B, typename ResultType>
struct NumArrayIfImpl
{
template <typename FromT>
static ALWAYS_INLINE void copy_from_vector(
size_t i,
const PODArray<FromT> & from_data, const ColumnArray::Offsets_t & from_offsets, ColumnArray::Offset_t from_prev_offset,
PODArray<ResultType> & 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<ResultType>(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<ResultType> & from_data,
PODArray<ResultType> & 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<ResultType> ** c_data, ColumnArray::Offsets_t ** c_offsets)
{
ColumnVector<ResultType> * col_res_vec = new ColumnVector<ResultType>;
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<UInt8> & cond,
const PODArray<A> & a_data, const ColumnArray::Offsets_t & a_offsets,
const PODArray<B> & b_data, const ColumnArray::Offsets_t & b_offsets,
Block & block, size_t result)
{
PODArray<ResultType> * 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<UInt8> & cond,
const PODArray<A> & a_data, const ColumnArray::Offsets_t & a_offsets,
const Array & b,
Block & block, size_t result)
{
PODArray<ResultType> * c_data = nullptr;
ColumnArray::Offsets_t * c_offsets = nullptr;
create_result_column(block, result, &c_data, &c_offsets);
PODArray<ResultType> b_converted(b.size());
for (size_t i = 0, size = b.size(); i < size; ++i)
b_converted[i] = b[i].get<typename NearestFieldType<B>::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<UInt8> & cond,
const Array & a,
const PODArray<B> & b_data, const ColumnArray::Offsets_t & b_offsets,
Block & block, size_t result)
{
PODArray<ResultType> * c_data = nullptr;
ColumnArray::Offsets_t * c_offsets = nullptr;
create_result_column(block, result, &c_data, &c_offsets);
PODArray<ResultType> a_converted(a.size());
for (size_t i = 0, size = a.size(); i < size; ++i)
a_converted[i] = a[i].get<typename NearestFieldType<A>::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<UInt8> & cond,
const Array & a, const Array & b,
Block & block, size_t result)
{
PODArray<ResultType> * c_data = nullptr;
ColumnArray::Offsets_t * c_offsets = nullptr;
create_result_column(block, result, &c_data, &c_offsets);
PODArray<ResultType> a_converted(a.size());
for (size_t i = 0, size = a.size(); i < size; ++i)
a_converted[i] = a[i].get<typename NearestFieldType<A>::Type>();
PODArray<ResultType> b_converted(b.size());
for (size_t i = 0, size = b.size(); i < size; ++i)
b_converted[i] = b[i].get<typename NearestFieldType<B>::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 <typename A, typename B>
struct NumArrayIfImpl<A, B, NumberTraits::Error>
{
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<UInt8> & cond,
const PODArray<A> & a_data, const ColumnArray::Offsets_t & a_offsets,
const PODArray<B> & b_data, const ColumnArray::Offsets_t & b_offsets,
Block & block, size_t result)
{
throw_error();
}
static void vector_constant(
const PODArray<UInt8> & cond,
const PODArray<A> & a_data, const ColumnArray::Offsets_t & a_offsets,
const Array & b,
Block & block, size_t result)
{
throw_error();
}
static void constant_vector(
const PODArray<UInt8> & cond,
const Array & a,
const PODArray<B> & b_data, const ColumnArray::Offsets_t & b_offsets,
Block & block, size_t result)
{
throw_error();
}
static void constant_constant(
const PODArray<UInt8> & cond,
const Array & a, const Array & b,
Block & block, size_t result)
{
throw_error();
}
};
/** Реализация для массивов строк.
* NOTE: Код слишком сложный, потому что он работает в внутренностями массивов строк.
*/
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<const String &>();
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<UInt8> & 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 <bool reverse>
static void vector_constant_impl(
const PODArray<UInt8> & 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<UInt8> & 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<false>(cond, a_data, a_string_offsets, a_array_offsets, b, c_data, c_string_offsets, c_array_offsets);
}
static void constant_vector(
const PODArray<UInt8> & 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<true>(cond, b_data, b_string_offsets, b_array_offsets, a, c_data, c_string_offsets, c_array_offsets);
}
static void constant_constant(
const PODArray<UInt8> & 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<const String &>().size() + 1;
size_t sum_size_b = 0;
for (const auto & s : b)
sum_size_b += s.get<const String &>().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 <typename T>
struct DataTypeFromFieldTypeOrError
{
static DataTypePtr getDataType()
{
return new typename DataTypeFromFieldType<T>::Type;
}
};
template <>
struct DataTypeFromFieldTypeOrError<NumberTraits::Error>
{
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 <typename T0, typename T1>
bool checkRightType(const DataTypes & arguments, DataTypePtr & type_res) const
{
if (typeid_cast<const T1 *>(&*arguments[2]))
{
typedef typename NumberTraits::ResultOfIf<typename T0::FieldType, typename T1::FieldType>::Type ResultType;
type_res = DataTypeFromFieldTypeOrError<ResultType>::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 <typename T0>
bool checkLeftType(const DataTypes & arguments, DataTypePtr & type_res) const
{
if (typeid_cast<const T0 *>(&*arguments[1]))
{
if ( checkRightType<T0, DataTypeUInt8>(arguments, type_res)
|| checkRightType<T0, DataTypeUInt16>(arguments, type_res)
|| checkRightType<T0, DataTypeUInt32>(arguments, type_res)
|| checkRightType<T0, DataTypeUInt64>(arguments, type_res)
|| checkRightType<T0, DataTypeInt8>(arguments, type_res)
|| checkRightType<T0, DataTypeInt16>(arguments, type_res)
|| checkRightType<T0, DataTypeInt32>(arguments, type_res)
|| checkRightType<T0, DataTypeInt64>(arguments, type_res)
|| checkRightType<T0, DataTypeFloat32>(arguments, type_res)
|| checkRightType<T0, DataTypeFloat64>(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 <typename T0, typename T1>
bool executeRightType(
const ColumnVector<UInt8> * cond_col,
Block & block,
const ColumnNumbers & arguments,
size_t result,
const ColumnVector<T0> * col_left)
{
const ColumnVector<T1> * col_right_vec = typeid_cast<const ColumnVector<T1> *>(&*block.getByPosition(arguments[2]).column);
const ColumnConst<T1> * col_right_const = typeid_cast<const ColumnConst<T1> *>(&*block.getByPosition(arguments[2]).column);
if (!col_right_vec && !col_right_const)
return false;
typedef typename NumberTraits::ResultOfIf<T0, T1>::Type ResultType;
if (col_right_vec)
NumIfImpl<T0, T1, ResultType>::vector_vector(cond_col->getData(), col_left->getData(), col_right_vec->getData(), block, result);
else
NumIfImpl<T0, T1, ResultType>::vector_constant(cond_col->getData(), col_left->getData(), col_right_const->getData(), block, result);
return true;
}
template <typename T0, typename T1>
bool executeConstRightType(
const ColumnVector<UInt8> * cond_col,
Block & block,
const ColumnNumbers & arguments,
size_t result,
const ColumnConst<T0> * col_left)
{
const ColumnVector<T1> * col_right_vec = typeid_cast<const ColumnVector<T1> *>(&*block.getByPosition(arguments[2]).column);
const ColumnConst<T1> * col_right_const = typeid_cast<const ColumnConst<T1> *>(&*block.getByPosition(arguments[2]).column);
if (!col_right_vec && !col_right_const)
return false;
typedef typename NumberTraits::ResultOfIf<T0, T1>::Type ResultType;
if (col_right_vec)
NumIfImpl<T0, T1, ResultType>::constant_vector(cond_col->getData(), col_left->getData(), col_right_vec->getData(), block, result);
else
NumIfImpl<T0, T1, ResultType>::constant_constant(cond_col->getData(), col_left->getData(), col_right_const->getData(), block, result);
return true;
}
template <typename T0, typename T1>
bool executeRightTypeArray(
const ColumnVector<UInt8> * cond_col,
Block & block,
const ColumnNumbers & arguments,
size_t result,
const ColumnArray * col_left_array,
const ColumnVector<T0> * col_left)
{
const IColumn * col_right_untyped = block.getByPosition(arguments[2]).column.get();
const ColumnArray * col_right_array = typeid_cast<const ColumnArray *>(col_right_untyped);
const ColumnConstArray * col_right_const_array = typeid_cast<const ColumnConstArray *>(col_right_untyped);
if (!col_right_array && !col_right_const_array)
return false;
typedef typename NumberTraits::ResultOfIf<T0, T1>::Type ResultType;
if (col_right_array)
{
const ColumnVector<T1> * col_right_vec = typeid_cast<const ColumnVector<T1> *>(&col_right_array->getData());
if (!col_right_vec)
return false;
NumArrayIfImpl<T0, T1, ResultType>::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<const typename DataTypeFromFieldType<T1>::Type *>(
typeid_cast<const DataTypeArray &>(*col_right_const_array->getDataType()).getNestedType().get()))
return false;
NumArrayIfImpl<T0, T1, ResultType>::vector_constant(
cond_col->getData(),
col_left->getData(), col_left_array->getOffsets(),
col_right_const_array->getData(),
block, result);
}
return true;
}
template <typename T0, typename T1>
bool executeConstRightTypeArray(
const ColumnVector<UInt8> * 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<const ColumnArray *>(col_right_untyped);
const ColumnConstArray * col_right_const_array = typeid_cast<const ColumnConstArray *>(col_right_untyped);
if (!col_right_array && !col_right_const_array)
return false;
typedef typename NumberTraits::ResultOfIf<T0, T1>::Type ResultType;
if (col_right_array)
{
const ColumnVector<T1> * col_right_vec = typeid_cast<const ColumnVector<T1> *>(&col_right_array->getData());
if (!col_right_vec)
return false;
NumArrayIfImpl<T0, T1, ResultType>::constant_vector(
cond_col->getData(),
col_left_const_array->getData(),
col_right_vec->getData(), col_right_array->getOffsets(),
block, result);
}
else
{
if (!typeid_cast<const typename DataTypeFromFieldType<T1>::Type *>(
typeid_cast<const DataTypeArray &>(*col_right_const_array->getDataType()).getNestedType().get()))
return false;
NumArrayIfImpl<T0, T1, ResultType>::constant_constant(
cond_col->getData(),
col_left_const_array->getData(),
col_right_const_array->getData(),
block, result);
}
return true;
}
template <typename T0>
bool executeLeftType(const ColumnVector<UInt8> * cond_col, Block & block, const ColumnNumbers & arguments, size_t result)
{
const IColumn * col_left_untyped = block.getByPosition(arguments[1]).column.get();
const ColumnVector<T0> * col_left = nullptr;
const ColumnConst<T0> * col_const_left = nullptr;
const ColumnArray * col_arr_left = nullptr;
const ColumnVector<T0> * col_arr_left_elems = nullptr;
const ColumnConstArray * col_const_arr_left = nullptr;
col_left = typeid_cast<const ColumnVector<T0> *>(col_left_untyped);
if (!col_left)
{
col_const_left = typeid_cast<const ColumnConst<T0> *>(col_left_untyped);
if (!col_const_left)
{
col_arr_left = typeid_cast<const ColumnArray *>(col_left_untyped);
if (col_arr_left)
col_arr_left_elems = typeid_cast<const ColumnVector<T0> *>(&col_arr_left->getData());
else
col_const_arr_left = typeid_cast<const ColumnConstArray *>(col_left_untyped);
}
}
if (col_left)
{
if ( executeRightType<T0, UInt8>(cond_col, block, arguments, result, col_left)
|| executeRightType<T0, UInt16>(cond_col, block, arguments, result, col_left)
|| executeRightType<T0, UInt32>(cond_col, block, arguments, result, col_left)
|| executeRightType<T0, UInt64>(cond_col, block, arguments, result, col_left)
|| executeRightType<T0, Int8>(cond_col, block, arguments, result, col_left)
|| executeRightType<T0, Int16>(cond_col, block, arguments, result, col_left)
|| executeRightType<T0, Int32>(cond_col, block, arguments, result, col_left)
|| executeRightType<T0, Int64>(cond_col, block, arguments, result, col_left)
|| executeRightType<T0, Float32>(cond_col, block, arguments, result, col_left)
|| executeRightType<T0, Float64>(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<T0, UInt8>(cond_col, block, arguments, result, col_const_left)
|| executeConstRightType<T0, UInt16>(cond_col, block, arguments, result, col_const_left)
|| executeConstRightType<T0, UInt32>(cond_col, block, arguments, result, col_const_left)
|| executeConstRightType<T0, UInt64>(cond_col, block, arguments, result, col_const_left)
|| executeConstRightType<T0, Int8>(cond_col, block, arguments, result, col_const_left)
|| executeConstRightType<T0, Int16>(cond_col, block, arguments, result, col_const_left)
|| executeConstRightType<T0, Int32>(cond_col, block, arguments, result, col_const_left)
|| executeConstRightType<T0, Int64>(cond_col, block, arguments, result, col_const_left)
|| executeConstRightType<T0, Float32>(cond_col, block, arguments, result, col_const_left)
|| executeConstRightType<T0, Float64>(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<T0, UInt8>(cond_col, block, arguments, result, col_arr_left, col_arr_left_elems)
|| executeRightTypeArray<T0, UInt16>(cond_col, block, arguments, result, col_arr_left, col_arr_left_elems)
|| executeRightTypeArray<T0, UInt32>(cond_col, block, arguments, result, col_arr_left, col_arr_left_elems)
|| executeRightTypeArray<T0, UInt64>(cond_col, block, arguments, result, col_arr_left, col_arr_left_elems)
|| executeRightTypeArray<T0, Int8>(cond_col, block, arguments, result, col_arr_left, col_arr_left_elems)
|| executeRightTypeArray<T0, Int16>(cond_col, block, arguments, result, col_arr_left, col_arr_left_elems)
|| executeRightTypeArray<T0, Int32>(cond_col, block, arguments, result, col_arr_left, col_arr_left_elems)
|| executeRightTypeArray<T0, Int64>(cond_col, block, arguments, result, col_arr_left, col_arr_left_elems)
|| executeRightTypeArray<T0, Float32>(cond_col, block, arguments, result, col_arr_left, col_arr_left_elems)
|| executeRightTypeArray<T0, Float64>(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<const typename DataTypeFromFieldType<T0>::Type *>(
typeid_cast<const DataTypeArray &>(*col_const_arr_left->getDataType()).getNestedType().get()))
{
if ( executeConstRightTypeArray<T0, UInt8>(cond_col, block, arguments, result, col_const_arr_left)
|| executeConstRightTypeArray<T0, UInt16>(cond_col, block, arguments, result, col_const_arr_left)
|| executeConstRightTypeArray<T0, UInt32>(cond_col, block, arguments, result, col_const_arr_left)
|| executeConstRightTypeArray<T0, UInt64>(cond_col, block, arguments, result, col_const_arr_left)
|| executeConstRightTypeArray<T0, Int8>(cond_col, block, arguments, result, col_const_arr_left)
|| executeConstRightTypeArray<T0, Int16>(cond_col, block, arguments, result, col_const_arr_left)
|| executeConstRightTypeArray<T0, Int32>(cond_col, block, arguments, result, col_const_arr_left)
|| executeConstRightTypeArray<T0, Int64>(cond_col, block, arguments, result, col_const_arr_left)
|| executeConstRightTypeArray<T0, Float32>(cond_col, block, arguments, result, col_const_arr_left)
|| executeConstRightTypeArray<T0, Float64>(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<UInt8> * 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<const ColumnString *>(col_then_untyped);
const ColumnString * col_else = typeid_cast<const ColumnString *>(col_else_untyped);
const ColumnConstString * col_then_const = typeid_cast<const ColumnConstString *>(col_then_untyped);
const ColumnConstString * col_else_const = typeid_cast<const ColumnConstString *>(col_else_untyped);
if ((col_then || col_then_const) && (col_else || col_else_const))
{
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
return false;
return true;
}
const ColumnArray * col_arr_then = typeid_cast<const ColumnArray *>(col_then_untyped);
const ColumnArray * col_arr_else = typeid_cast<const ColumnArray *>(col_else_untyped);
const ColumnConstArray * col_arr_then_const = typeid_cast<const ColumnConstArray *>(col_then_untyped);
const ColumnConstArray * col_arr_else_const = typeid_cast<const ColumnConstArray *>(col_else_untyped);
const ColumnString * col_then_elements = col_arr_then ? typeid_cast<const ColumnString *>(&col_arr_then->getData()) : nullptr;
const ColumnString * col_else_elements = col_arr_else ? typeid_cast<const ColumnString *>(&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
{
return name;
}
/// Получить типы результата по типам аргументов. Если функция неприменима для данных аргументов - кинуть исключение.
DataTypePtr getReturnType(const DataTypes & arguments) const
{
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<const DataTypeUInt8 *>(&*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<const DataTypeArray *>(arguments[1].get());
const DataTypeArray * type_arr2 = typeid_cast<const DataTypeArray *>(arguments[2].get());
if (arguments[1]->behavesAsNumber() && arguments[2]->behavesAsNumber())
{
DataTypePtr type_res;
if (!( checkLeftType<DataTypeUInt8>(arguments, type_res)
|| checkLeftType<DataTypeUInt16>(arguments, type_res)
|| checkLeftType<DataTypeUInt32>(arguments, type_res)
|| checkLeftType<DataTypeUInt64>(arguments, type_res)
|| checkLeftType<DataTypeInt8>(arguments, type_res)
|| checkLeftType<DataTypeInt16>(arguments, type_res)
|| checkLeftType<DataTypeInt32>(arguments, type_res)
|| checkLeftType<DataTypeInt64>(arguments, type_res)
|| checkLeftType<DataTypeFloat32>(arguments, type_res)
|| checkLeftType<DataTypeFloat64>(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())
{
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)
{
const ColumnVector<UInt8> * cond_col = typeid_cast<const ColumnVector<UInt8> *>(&*block.getByPosition(arguments[0]).column);
const ColumnConst<UInt8> * cond_const_col = typeid_cast<const ColumnConst<UInt8> *>(&*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<const ColumnVector<UInt8> *>(&*materialized_cond_col);
}
}
if (cond_col)
{
if (!( executeLeftType<UInt8>(cond_col, block, arguments, result)
|| executeLeftType<UInt16>(cond_col, block, arguments, result)
|| executeLeftType<UInt32>(cond_col, block, arguments, result)
|| executeLeftType<UInt64>(cond_col, block, arguments, result)
|| executeLeftType<Int8>(cond_col, block, arguments, result)
|| executeLeftType<Int16>(cond_col, block, arguments, result)
|| executeLeftType<Int32>(cond_col, block, arguments, result)
|| executeLeftType<Int64>(cond_col, block, arguments, result)
|| executeLeftType<Float32>(cond_col, block, arguments, result)
|| executeLeftType<Float64>(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);
}
};
}