2017-04-01 09:19:00 +00:00
|
|
|
#include <Functions/FunctionFactory.h>
|
|
|
|
#include <Functions/FunctionsLogical.h>
|
2014-08-22 00:57:20 +00:00
|
|
|
|
2019-07-16 20:57:11 +00:00
|
|
|
#include <Columns/ColumnConst.h>
|
|
|
|
#include <Columns/ColumnNullable.h>
|
2021-06-22 16:21:23 +00:00
|
|
|
#include <Columns/ColumnVector.h>
|
|
|
|
#include <Columns/ColumnsNumber.h>
|
2021-06-14 04:13:35 +00:00
|
|
|
#include <Common/FieldVisitorConvertToNumber.h>
|
2021-04-27 13:12:57 +00:00
|
|
|
#include <Columns/MaskOperations.h>
|
2019-07-16 20:57:11 +00:00
|
|
|
#include <Common/typeid_cast.h>
|
2021-06-22 16:21:23 +00:00
|
|
|
#include <Columns/IColumn.h>
|
2019-07-16 20:57:11 +00:00
|
|
|
#include <DataTypes/DataTypeNullable.h>
|
|
|
|
#include <DataTypes/DataTypesNumber.h>
|
|
|
|
#include <Functions/FunctionHelpers.h>
|
2021-06-22 16:21:23 +00:00
|
|
|
#include <Common/FieldVisitors.h>
|
2019-07-16 20:57:11 +00:00
|
|
|
|
|
|
|
#include <algorithm>
|
|
|
|
|
|
|
|
|
2014-08-22 00:57:20 +00:00
|
|
|
namespace DB
|
|
|
|
{
|
|
|
|
|
|
|
|
void registerFunctionsLogical(FunctionFactory & factory)
|
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
factory.registerFunction<FunctionAnd>();
|
|
|
|
factory.registerFunction<FunctionOr>();
|
|
|
|
factory.registerFunction<FunctionXor>();
|
2020-07-10 06:48:05 +00:00
|
|
|
factory.registerFunction<FunctionNot>(FunctionFactory::CaseInsensitive); /// Operator NOT(x) can be parsed as a function.
|
2014-08-22 00:57:20 +00:00
|
|
|
}
|
|
|
|
|
2019-07-16 20:57:11 +00:00
|
|
|
namespace ErrorCodes
|
|
|
|
{
|
|
|
|
extern const int LOGICAL_ERROR;
|
|
|
|
extern const int ILLEGAL_TYPE_OF_ARGUMENT;
|
|
|
|
extern const int TOO_FEW_ARGUMENTS_FOR_FUNCTION;
|
|
|
|
extern const int ILLEGAL_COLUMN;
|
|
|
|
}
|
|
|
|
|
|
|
|
namespace
|
|
|
|
{
|
|
|
|
using namespace FunctionsLogicalDetail;
|
|
|
|
|
|
|
|
using UInt8Container = ColumnUInt8::Container;
|
|
|
|
using UInt8ColumnPtrs = std::vector<const ColumnUInt8 *>;
|
|
|
|
|
|
|
|
|
2020-06-23 13:42:52 +00:00
|
|
|
MutableColumnPtr buildColumnFromTernaryData(const UInt8Container & ternary_data, const bool make_nullable)
|
2019-07-16 20:57:11 +00:00
|
|
|
{
|
|
|
|
const size_t rows_count = ternary_data.size();
|
|
|
|
|
|
|
|
auto new_column = ColumnUInt8::create(rows_count);
|
|
|
|
std::transform(
|
|
|
|
ternary_data.cbegin(), ternary_data.cend(), new_column->getData().begin(),
|
|
|
|
[](const auto x) { return x == Ternary::True; });
|
|
|
|
|
|
|
|
if (!make_nullable)
|
|
|
|
return new_column;
|
|
|
|
|
|
|
|
auto null_column = ColumnUInt8::create(rows_count);
|
|
|
|
std::transform(
|
|
|
|
ternary_data.cbegin(), ternary_data.cend(), null_column->getData().begin(),
|
|
|
|
[](const auto x) { return x == Ternary::Null; });
|
|
|
|
|
|
|
|
return ColumnNullable::create(std::move(new_column), std::move(null_column));
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T>
|
2020-06-23 13:42:52 +00:00
|
|
|
bool tryConvertColumnToBool(const IColumn * column, UInt8Container & res)
|
2019-07-16 20:57:11 +00:00
|
|
|
{
|
|
|
|
const auto col = checkAndGetColumn<ColumnVector<T>>(column);
|
|
|
|
if (!col)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
std::transform(
|
|
|
|
col->getData().cbegin(), col->getData().cend(), res.begin(),
|
2020-07-07 10:26:11 +00:00
|
|
|
[](const auto x) { return !!x; });
|
2019-07-16 20:57:11 +00:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-06-23 13:42:52 +00:00
|
|
|
void convertAnyColumnToBool(const IColumn * column, UInt8Container & res)
|
2019-07-16 20:57:11 +00:00
|
|
|
{
|
2020-06-23 13:42:52 +00:00
|
|
|
if (!tryConvertColumnToBool<Int8>(column, res) &&
|
|
|
|
!tryConvertColumnToBool<Int16>(column, res) &&
|
|
|
|
!tryConvertColumnToBool<Int32>(column, res) &&
|
|
|
|
!tryConvertColumnToBool<Int64>(column, res) &&
|
|
|
|
!tryConvertColumnToBool<UInt16>(column, res) &&
|
|
|
|
!tryConvertColumnToBool<UInt32>(column, res) &&
|
|
|
|
!tryConvertColumnToBool<UInt64>(column, res) &&
|
|
|
|
!tryConvertColumnToBool<Float32>(column, res) &&
|
|
|
|
!tryConvertColumnToBool<Float64>(column, res))
|
2019-07-16 20:57:11 +00:00
|
|
|
throw Exception("Unexpected type of column: " + column->getName(), ErrorCodes::ILLEGAL_COLUMN);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-07-17 16:10:37 +00:00
|
|
|
template <class Op, typename Func>
|
|
|
|
static bool extractConstColumns(ColumnRawPtrs & in, UInt8 & res, Func && func)
|
2019-07-16 21:06:23 +00:00
|
|
|
{
|
2019-07-17 16:10:37 +00:00
|
|
|
bool has_res = false;
|
2019-07-22 22:39:42 +00:00
|
|
|
|
2019-07-17 16:10:37 +00:00
|
|
|
for (int i = static_cast<int>(in.size()) - 1; i >= 0; --i)
|
2019-07-16 21:06:23 +00:00
|
|
|
{
|
2020-08-01 19:10:59 +00:00
|
|
|
UInt8 x;
|
|
|
|
|
|
|
|
if (in[i]->onlyNull())
|
|
|
|
x = func(Null());
|
|
|
|
else if (isColumnConst(*in[i]))
|
|
|
|
x = func((*in[i])[0]);
|
|
|
|
else
|
2019-07-17 16:10:37 +00:00
|
|
|
continue;
|
|
|
|
|
|
|
|
if (has_res)
|
2019-07-16 21:06:23 +00:00
|
|
|
{
|
2019-07-17 16:10:37 +00:00
|
|
|
res = Op::apply(res, x);
|
2019-07-16 21:06:23 +00:00
|
|
|
}
|
|
|
|
else
|
2019-07-17 16:10:37 +00:00
|
|
|
{
|
|
|
|
res = x;
|
|
|
|
has_res = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
in.erase(in.begin() + i);
|
2019-07-16 21:06:23 +00:00
|
|
|
}
|
2019-07-22 22:39:42 +00:00
|
|
|
|
2019-07-17 16:10:37 +00:00
|
|
|
return has_res;
|
|
|
|
}
|
2019-07-16 21:06:23 +00:00
|
|
|
|
2019-07-17 16:10:37 +00:00
|
|
|
template <class Op>
|
2020-06-23 13:42:52 +00:00
|
|
|
inline bool extractConstColumnsAsBool(ColumnRawPtrs & in, UInt8 & res)
|
2019-07-16 21:06:23 +00:00
|
|
|
{
|
2019-07-17 16:10:37 +00:00
|
|
|
return extractConstColumns<Op>(
|
|
|
|
in, res,
|
|
|
|
[](const Field & value)
|
|
|
|
{
|
|
|
|
return !value.isNull() && applyVisitor(FieldVisitorConvertToNumber<bool>(), value);
|
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
2019-07-16 21:06:23 +00:00
|
|
|
|
2019-07-17 16:10:37 +00:00
|
|
|
template <class Op>
|
2020-06-23 13:42:52 +00:00
|
|
|
inline bool extractConstColumnsAsTernary(ColumnRawPtrs & in, UInt8 & res_3v)
|
2019-07-17 16:10:37 +00:00
|
|
|
{
|
|
|
|
return extractConstColumns<Op>(
|
|
|
|
in, res_3v,
|
|
|
|
[](const Field & value)
|
|
|
|
{
|
|
|
|
return value.isNull()
|
|
|
|
? Ternary::makeValue(false, true)
|
|
|
|
: Ternary::makeValue(applyVisitor(FieldVisitorConvertToNumber<bool>(), value));
|
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
2019-07-16 21:06:23 +00:00
|
|
|
|
|
|
|
|
2020-07-07 10:26:11 +00:00
|
|
|
/// N.B. This class calculates result only for non-nullable types
|
2019-07-16 20:57:11 +00:00
|
|
|
template <typename Op, size_t N>
|
|
|
|
class AssociativeApplierImpl
|
|
|
|
{
|
|
|
|
using ResultValueType = typename Op::ResultType;
|
|
|
|
|
|
|
|
public:
|
|
|
|
/// Remembers the last N columns from `in`.
|
2020-03-18 03:27:32 +00:00
|
|
|
explicit AssociativeApplierImpl(const UInt8ColumnPtrs & in)
|
2019-07-16 20:57:11 +00:00
|
|
|
: vec(in[in.size() - N]->getData()), next(in) {}
|
|
|
|
|
|
|
|
/// Returns a combination of values in the i-th row of all columns stored in the constructor.
|
2019-07-17 16:10:37 +00:00
|
|
|
inline ResultValueType apply(const size_t i) const
|
2019-07-16 20:57:11 +00:00
|
|
|
{
|
2020-07-07 10:26:11 +00:00
|
|
|
const auto a = !!vec[i];
|
2019-07-16 20:57:11 +00:00
|
|
|
if constexpr (Op::isSaturable())
|
2019-07-17 16:10:37 +00:00
|
|
|
return Op::isSaturatedValue(a) ? a : Op::apply(a, next.apply(i));
|
2019-07-16 20:57:11 +00:00
|
|
|
else
|
2019-07-17 16:10:37 +00:00
|
|
|
return Op::apply(a, next.apply(i));
|
2019-07-16 20:57:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
const UInt8Container & vec;
|
|
|
|
const AssociativeApplierImpl<Op, N - 1> next;
|
|
|
|
};
|
|
|
|
|
|
|
|
template <typename Op>
|
|
|
|
class AssociativeApplierImpl<Op, 1>
|
|
|
|
{
|
|
|
|
using ResultValueType = typename Op::ResultType;
|
|
|
|
|
|
|
|
public:
|
2020-03-18 03:27:32 +00:00
|
|
|
explicit AssociativeApplierImpl(const UInt8ColumnPtrs & in)
|
2019-07-16 20:57:11 +00:00
|
|
|
: vec(in[in.size() - 1]->getData()) {}
|
|
|
|
|
2020-07-07 10:26:11 +00:00
|
|
|
inline ResultValueType apply(const size_t i) const { return !!vec[i]; }
|
2019-07-16 20:57:11 +00:00
|
|
|
|
|
|
|
private:
|
|
|
|
const UInt8Container & vec;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2019-07-17 16:10:37 +00:00
|
|
|
/// A helper class used by AssociativeGenericApplierImpl
|
|
|
|
/// Allows for on-the-fly conversion of any data type into intermediate ternary representation
|
2020-06-23 13:42:52 +00:00
|
|
|
using TernaryValueGetter = std::function<Ternary::ResultType (size_t)>;
|
2019-07-16 20:57:11 +00:00
|
|
|
|
2019-07-17 16:10:37 +00:00
|
|
|
template <typename ... Types>
|
|
|
|
struct ValueGetterBuilderImpl;
|
2019-07-16 20:57:11 +00:00
|
|
|
|
2019-07-17 16:10:37 +00:00
|
|
|
template <typename Type, typename ...Types>
|
|
|
|
struct ValueGetterBuilderImpl<Type, Types...>
|
|
|
|
{
|
2020-06-23 13:42:52 +00:00
|
|
|
static TernaryValueGetter build(const IColumn * x)
|
2019-07-17 16:10:37 +00:00
|
|
|
{
|
2020-08-01 18:52:30 +00:00
|
|
|
if (x->onlyNull())
|
|
|
|
{
|
|
|
|
return [](size_t){ return Ternary::Null; };
|
|
|
|
}
|
|
|
|
else if (const auto * nullable_column = typeid_cast<const ColumnNullable *>(x))
|
2019-07-16 20:57:11 +00:00
|
|
|
{
|
2020-04-22 08:31:10 +00:00
|
|
|
if (const auto * nested_column = typeid_cast<const ColumnVector<Type> *>(nullable_column->getNestedColumnPtr().get()))
|
2019-07-17 16:10:37 +00:00
|
|
|
{
|
2020-08-01 18:52:30 +00:00
|
|
|
return [
|
|
|
|
&null_data = nullable_column->getNullMapData(),
|
|
|
|
&column_data = nested_column->getData()](size_t i)
|
|
|
|
{
|
|
|
|
return Ternary::makeValue(column_data[i], null_data[i]);
|
|
|
|
};
|
2019-07-17 16:10:37 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
return ValueGetterBuilderImpl<Types...>::build(x);
|
2019-07-16 20:57:11 +00:00
|
|
|
}
|
2019-07-17 16:10:37 +00:00
|
|
|
else if (const auto column = typeid_cast<const ColumnVector<Type> *>(x))
|
|
|
|
return [&column_data = column->getData()](size_t i) { return Ternary::makeValue(column_data[i]); };
|
2019-07-16 20:57:11 +00:00
|
|
|
else
|
2019-07-17 16:10:37 +00:00
|
|
|
return ValueGetterBuilderImpl<Types...>::build(x);
|
|
|
|
}
|
|
|
|
};
|
2019-07-16 20:57:11 +00:00
|
|
|
|
2019-07-17 16:10:37 +00:00
|
|
|
template <>
|
|
|
|
struct ValueGetterBuilderImpl<>
|
|
|
|
{
|
2020-06-23 13:42:52 +00:00
|
|
|
static TernaryValueGetter build(const IColumn * x)
|
2019-07-17 16:10:37 +00:00
|
|
|
{
|
|
|
|
throw Exception(
|
2020-07-16 18:04:16 +00:00
|
|
|
std::string("Unknown numeric column of type: ") + demangle(typeid(*x).name()),
|
2019-07-17 16:10:37 +00:00
|
|
|
ErrorCodes::LOGICAL_ERROR);
|
2019-07-16 20:57:11 +00:00
|
|
|
}
|
2019-07-17 16:10:37 +00:00
|
|
|
};
|
2019-07-16 20:57:11 +00:00
|
|
|
|
2019-07-17 16:10:37 +00:00
|
|
|
using ValueGetterBuilder =
|
|
|
|
ValueGetterBuilderImpl<UInt8, UInt16, UInt32, UInt64, Int8, Int16, Int32, Int64, Float32, Float64>;
|
2019-07-16 20:57:11 +00:00
|
|
|
|
2019-07-17 16:10:37 +00:00
|
|
|
/// This class together with helper class ValueGetterBuilder can be used with columns of arbitrary data type
|
|
|
|
/// Allows for on-the-fly conversion of any type of data into intermediate ternary representation
|
|
|
|
/// and eliminates the need to materialize data columns in intermediate representation
|
2019-07-16 20:57:11 +00:00
|
|
|
template <typename Op, size_t N>
|
|
|
|
class AssociativeGenericApplierImpl
|
|
|
|
{
|
|
|
|
using ResultValueType = typename Op::ResultType;
|
|
|
|
|
|
|
|
public:
|
|
|
|
/// Remembers the last N columns from `in`.
|
2020-03-18 03:27:32 +00:00
|
|
|
explicit AssociativeGenericApplierImpl(const ColumnRawPtrs & in)
|
2019-07-16 20:57:11 +00:00
|
|
|
: val_getter{ValueGetterBuilder::build(in[in.size() - N])}, next{in} {}
|
|
|
|
|
|
|
|
/// Returns a combination of values in the i-th row of all columns stored in the constructor.
|
2019-07-17 16:10:37 +00:00
|
|
|
inline ResultValueType apply(const size_t i) const
|
2019-07-16 20:57:11 +00:00
|
|
|
{
|
2019-07-17 16:10:37 +00:00
|
|
|
const auto a = val_getter(i);
|
2019-07-16 20:57:11 +00:00
|
|
|
if constexpr (Op::isSaturable())
|
2020-07-07 10:26:11 +00:00
|
|
|
return Op::isSaturatedValueTernary(a) ? a : Op::apply(a, next.apply(i));
|
2019-07-16 20:57:11 +00:00
|
|
|
else
|
|
|
|
return Op::apply(a, next.apply(i));
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
2020-06-23 13:42:52 +00:00
|
|
|
const TernaryValueGetter val_getter;
|
2019-07-16 20:57:11 +00:00
|
|
|
const AssociativeGenericApplierImpl<Op, N - 1> next;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
template <typename Op>
|
|
|
|
class AssociativeGenericApplierImpl<Op, 1>
|
|
|
|
{
|
|
|
|
using ResultValueType = typename Op::ResultType;
|
|
|
|
|
|
|
|
public:
|
|
|
|
/// Remembers the last N columns from `in`.
|
2020-03-18 03:27:32 +00:00
|
|
|
explicit AssociativeGenericApplierImpl(const ColumnRawPtrs & in)
|
2019-07-16 20:57:11 +00:00
|
|
|
: val_getter{ValueGetterBuilder::build(in[in.size() - 1])} {}
|
|
|
|
|
|
|
|
inline ResultValueType apply(const size_t i) const { return val_getter(i); }
|
|
|
|
|
|
|
|
private:
|
2020-06-23 13:42:52 +00:00
|
|
|
const TernaryValueGetter val_getter;
|
2019-07-16 20:57:11 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2019-07-18 09:09:29 +00:00
|
|
|
/// Apply target function by feeding it "batches" of N columns
|
2020-10-24 20:52:32 +00:00
|
|
|
/// Combining 8 columns per pass is the fastest method, because it's the maximum when clang vectorizes a loop.
|
2019-07-16 20:57:11 +00:00
|
|
|
template <
|
2020-10-24 20:52:32 +00:00
|
|
|
typename Op, template <typename, size_t> typename OperationApplierImpl, size_t N = 8>
|
2019-07-16 20:57:11 +00:00
|
|
|
struct OperationApplier
|
|
|
|
{
|
2020-01-18 21:02:43 +00:00
|
|
|
template <typename Columns, typename ResultData>
|
2020-01-19 06:07:30 +00:00
|
|
|
static void apply(Columns & in, ResultData & result_data, bool use_result_data_as_input = false)
|
2019-07-18 08:07:24 +00:00
|
|
|
{
|
2020-01-19 06:07:30 +00:00
|
|
|
if (!use_result_data_as_input)
|
2020-10-25 01:43:06 +00:00
|
|
|
doBatchedApply<false>(in, result_data.data(), result_data.size());
|
2020-03-09 03:38:43 +00:00
|
|
|
while (!in.empty())
|
2020-10-25 01:43:06 +00:00
|
|
|
doBatchedApply<true>(in, result_data.data(), result_data.size());
|
2019-07-18 08:07:24 +00:00
|
|
|
}
|
|
|
|
|
2020-10-25 01:43:06 +00:00
|
|
|
template <bool CarryResult, typename Columns, typename Result>
|
|
|
|
static void NO_INLINE doBatchedApply(Columns & in, Result * __restrict result_data, size_t size)
|
2019-07-16 20:57:11 +00:00
|
|
|
{
|
|
|
|
if (N > in.size())
|
|
|
|
{
|
2020-01-18 21:02:43 +00:00
|
|
|
OperationApplier<Op, OperationApplierImpl, N - 1>
|
2020-10-25 01:43:06 +00:00
|
|
|
::template doBatchedApply<CarryResult>(in, result_data, size);
|
2019-07-16 20:57:11 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-03-23 02:12:31 +00:00
|
|
|
const OperationApplierImpl<Op, N> operation_applier_impl(in);
|
2020-10-25 01:43:06 +00:00
|
|
|
for (size_t i = 0; i < size; ++i)
|
|
|
|
{
|
2020-01-19 06:07:30 +00:00
|
|
|
if constexpr (CarryResult)
|
2020-10-25 01:43:06 +00:00
|
|
|
result_data[i] = Op::apply(result_data[i], operation_applier_impl.apply(i));
|
2020-01-18 21:02:43 +00:00
|
|
|
else
|
2020-10-25 01:43:06 +00:00
|
|
|
result_data[i] = operation_applier_impl.apply(i);
|
|
|
|
}
|
2019-07-16 20:57:11 +00:00
|
|
|
|
|
|
|
in.erase(in.end() - N, in.end());
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
template <
|
2019-07-18 08:07:24 +00:00
|
|
|
typename Op, template <typename, size_t> typename OperationApplierImpl>
|
2020-01-18 21:02:43 +00:00
|
|
|
struct OperationApplier<Op, OperationApplierImpl, 0>
|
2019-07-16 20:57:11 +00:00
|
|
|
{
|
2020-01-18 21:02:43 +00:00
|
|
|
template <bool, typename Columns, typename Result>
|
2020-10-25 01:43:06 +00:00
|
|
|
static void NO_INLINE doBatchedApply(Columns &, Result &, size_t)
|
2019-07-16 20:57:11 +00:00
|
|
|
{
|
|
|
|
throw Exception(
|
2019-07-18 09:09:29 +00:00
|
|
|
"OperationApplier<...>::apply(...): not enough arguments to run this method",
|
2019-07-16 20:57:11 +00:00
|
|
|
ErrorCodes::LOGICAL_ERROR);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
template <class Op>
|
2020-10-18 19:00:13 +00:00
|
|
|
static ColumnPtr executeForTernaryLogicImpl(ColumnRawPtrs arguments, const DataTypePtr & result_type, size_t input_rows_count)
|
2019-07-16 20:57:11 +00:00
|
|
|
{
|
|
|
|
/// Combine all constant columns into a single constant value.
|
|
|
|
UInt8 const_3v_value = 0;
|
2020-07-07 10:26:11 +00:00
|
|
|
const bool has_consts = extractConstColumnsAsTernary<Op>(arguments, const_3v_value);
|
2019-07-16 20:57:11 +00:00
|
|
|
|
|
|
|
/// If the constant value uniquely determines the result, return it.
|
2020-07-07 20:56:40 +00:00
|
|
|
if (has_consts && (arguments.empty() || Op::isSaturatedValueTernary(const_3v_value)))
|
2019-07-16 20:57:11 +00:00
|
|
|
{
|
2020-10-18 19:00:13 +00:00
|
|
|
return ColumnConst::create(
|
|
|
|
buildColumnFromTernaryData(UInt8Container({const_3v_value}), result_type->isNullable()),
|
2019-07-16 20:57:11 +00:00
|
|
|
input_rows_count
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2020-01-19 06:22:01 +00:00
|
|
|
const auto result_column = has_consts ?
|
|
|
|
ColumnUInt8::create(input_rows_count, const_3v_value) : ColumnUInt8::create(input_rows_count);
|
2019-07-16 20:57:11 +00:00
|
|
|
|
2020-01-19 06:22:01 +00:00
|
|
|
OperationApplier<Op, AssociativeGenericApplierImpl>::apply(arguments, result_column->getData(), has_consts);
|
2019-07-16 20:57:11 +00:00
|
|
|
|
2020-10-18 19:00:13 +00:00
|
|
|
return buildColumnFromTernaryData(result_column->getData(), result_type->isNullable());
|
2019-07-16 20:57:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-07-18 08:07:24 +00:00
|
|
|
template <typename Op, typename ... Types>
|
|
|
|
struct TypedExecutorInvoker;
|
|
|
|
|
|
|
|
template <typename Op>
|
2019-07-18 09:09:29 +00:00
|
|
|
using FastApplierImpl =
|
2019-07-18 08:07:24 +00:00
|
|
|
TypedExecutorInvoker<Op, UInt8, UInt16, UInt32, UInt64, Int8, Int16, Int32, Int64, Float32, Float64>;
|
|
|
|
|
|
|
|
template <typename Op, typename Type, typename ... Types>
|
|
|
|
struct TypedExecutorInvoker<Op, Type, Types ...>
|
|
|
|
{
|
|
|
|
template <typename T, typename Result>
|
2019-07-18 09:09:29 +00:00
|
|
|
static void apply(const ColumnVector<T> & x, const IColumn & y, Result & result)
|
2019-07-18 08:07:24 +00:00
|
|
|
{
|
|
|
|
if (const auto column = typeid_cast<const ColumnVector<Type> *>(&y))
|
|
|
|
std::transform(
|
2019-07-18 09:09:29 +00:00
|
|
|
x.getData().cbegin(), x.getData().cend(),
|
|
|
|
column->getData().cbegin(), result.begin(),
|
2019-07-22 13:56:38 +00:00
|
|
|
[](const auto a, const auto b) { return Op::apply(!!a, !!b); });
|
2019-07-18 08:07:24 +00:00
|
|
|
else
|
2019-07-18 09:09:29 +00:00
|
|
|
TypedExecutorInvoker<Op, Types ...>::template apply<T>(x, y, result);
|
2019-07-18 08:07:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
template <typename Result>
|
2019-07-18 09:09:29 +00:00
|
|
|
static void apply(const IColumn & x, const IColumn & y, Result & result)
|
2019-07-18 08:07:24 +00:00
|
|
|
{
|
|
|
|
if (const auto column = typeid_cast<const ColumnVector<Type> *>(&x))
|
2019-07-18 09:09:29 +00:00
|
|
|
FastApplierImpl<Op>::template apply<Type>(*column, y, result);
|
2019-07-18 08:07:24 +00:00
|
|
|
else
|
2019-07-18 09:09:29 +00:00
|
|
|
TypedExecutorInvoker<Op, Types ...>::apply(x, y, result);
|
2019-07-18 08:07:24 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
template <typename Op>
|
|
|
|
struct TypedExecutorInvoker<Op>
|
|
|
|
{
|
|
|
|
template <typename T, typename Result>
|
2019-07-18 09:09:29 +00:00
|
|
|
static void apply(const ColumnVector<T> &, const IColumn & y, Result &)
|
2019-07-18 08:07:24 +00:00
|
|
|
{
|
|
|
|
throw Exception(std::string("Unknown numeric column y of type: ") + demangle(typeid(y).name()), ErrorCodes::LOGICAL_ERROR);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename Result>
|
2019-07-18 09:09:29 +00:00
|
|
|
static void apply(const IColumn & x, const IColumn &, Result &)
|
2019-07-18 08:07:24 +00:00
|
|
|
{
|
|
|
|
throw Exception(std::string("Unknown numeric column x of type: ") + demangle(typeid(x).name()), ErrorCodes::LOGICAL_ERROR);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2020-07-07 10:26:11 +00:00
|
|
|
/// Types of all of the arguments are guaranteed to be non-nullable here
|
2019-07-16 20:57:11 +00:00
|
|
|
template <class Op>
|
2020-10-18 19:00:13 +00:00
|
|
|
static ColumnPtr basicExecuteImpl(ColumnRawPtrs arguments, size_t input_rows_count)
|
2019-07-16 20:57:11 +00:00
|
|
|
{
|
|
|
|
/// Combine all constant columns into a single constant value.
|
|
|
|
UInt8 const_val = 0;
|
2020-07-07 10:26:11 +00:00
|
|
|
bool has_consts = extractConstColumnsAsBool<Op>(arguments, const_val);
|
2019-07-16 20:57:11 +00:00
|
|
|
|
|
|
|
/// If the constant value uniquely determines the result, return it.
|
|
|
|
if (has_consts && (arguments.empty() || Op::apply(const_val, 0) == Op::apply(const_val, 1)))
|
|
|
|
{
|
|
|
|
if (!arguments.empty())
|
|
|
|
const_val = Op::apply(const_val, 0);
|
2020-10-18 19:00:13 +00:00
|
|
|
return DataTypeUInt8().createColumnConst(input_rows_count, toField(const_val));
|
2019-07-16 20:57:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// If the constant value is a neutral element, let's forget about it.
|
|
|
|
if (has_consts && Op::apply(const_val, 0) == 0 && Op::apply(const_val, 1) == 1)
|
|
|
|
has_consts = false;
|
|
|
|
|
2020-01-19 06:22:01 +00:00
|
|
|
auto col_res = has_consts ?
|
|
|
|
ColumnUInt8::create(input_rows_count, const_val) : ColumnUInt8::create(input_rows_count);
|
2019-07-16 20:57:11 +00:00
|
|
|
|
2019-07-18 09:09:29 +00:00
|
|
|
/// FastPath detection goes in here
|
|
|
|
if (arguments.size() == (has_consts ? 1 : 2))
|
|
|
|
{
|
|
|
|
if (has_consts)
|
2020-01-19 06:22:01 +00:00
|
|
|
FastApplierImpl<Op>::apply(*arguments[0], *col_res, col_res->getData());
|
2019-07-18 09:09:29 +00:00
|
|
|
else
|
2020-01-19 06:22:01 +00:00
|
|
|
FastApplierImpl<Op>::apply(*arguments[0], *arguments[1], col_res->getData());
|
2019-07-18 09:09:29 +00:00
|
|
|
|
2020-10-18 19:00:13 +00:00
|
|
|
return col_res;
|
2019-07-18 09:09:29 +00:00
|
|
|
}
|
|
|
|
|
2019-07-16 20:57:11 +00:00
|
|
|
/// Convert all columns to UInt8
|
2020-01-19 06:22:01 +00:00
|
|
|
UInt8ColumnPtrs uint8_args;
|
|
|
|
Columns converted_columns_holder;
|
2019-07-16 20:57:11 +00:00
|
|
|
for (const IColumn * column : arguments)
|
|
|
|
{
|
2020-04-22 08:31:10 +00:00
|
|
|
if (const auto * uint8_column = checkAndGetColumn<ColumnUInt8>(column))
|
2019-07-16 20:57:11 +00:00
|
|
|
uint8_args.push_back(uint8_column);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
auto converted_column = ColumnUInt8::create(input_rows_count);
|
2020-06-23 13:42:52 +00:00
|
|
|
convertAnyColumnToBool(column, converted_column->getData());
|
2019-07-16 20:57:11 +00:00
|
|
|
uint8_args.push_back(converted_column.get());
|
2020-01-19 06:22:01 +00:00
|
|
|
converted_columns_holder.emplace_back(std::move(converted_column));
|
2019-07-16 20:57:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-19 06:22:01 +00:00
|
|
|
OperationApplier<Op, AssociativeApplierImpl>::apply(uint8_args, col_res->getData(), has_consts);
|
2019-07-16 20:57:11 +00:00
|
|
|
|
2020-10-18 19:00:13 +00:00
|
|
|
return col_res;
|
2019-07-16 20:57:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename Impl, typename Name>
|
2021-04-27 13:12:57 +00:00
|
|
|
DataTypePtr FunctionAnyArityLogical<Impl, Name>::getReturnTypeImpl(const DataTypes & arguments) const
|
2019-07-16 20:57:11 +00:00
|
|
|
{
|
|
|
|
if (arguments.size() < 2)
|
|
|
|
throw Exception("Number of arguments for function \"" + getName() + "\" should be at least 2: passed "
|
|
|
|
+ toString(arguments.size()),
|
|
|
|
ErrorCodes::TOO_FEW_ARGUMENTS_FOR_FUNCTION);
|
|
|
|
|
|
|
|
bool has_nullable_arguments = false;
|
|
|
|
for (size_t i = 0; i < arguments.size(); ++i)
|
|
|
|
{
|
|
|
|
const auto & arg_type = arguments[i];
|
|
|
|
|
|
|
|
if (!has_nullable_arguments)
|
|
|
|
{
|
|
|
|
has_nullable_arguments = arg_type->isNullable();
|
|
|
|
if (has_nullable_arguments && !Impl::specialImplementationForNulls())
|
|
|
|
throw Exception("Logical error: Unexpected type of argument for function \"" + getName() + "\": "
|
|
|
|
" argument " + toString(i + 1) + " is of type " + arg_type->getName(), ErrorCodes::LOGICAL_ERROR);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(isNativeNumber(arg_type)
|
|
|
|
|| (Impl::specialImplementationForNulls() && (arg_type->onlyNull() || isNativeNumber(removeNullable(arg_type))))))
|
|
|
|
throw Exception("Illegal type ("
|
|
|
|
+ arg_type->getName()
|
|
|
|
+ ") of " + toString(i + 1) + " argument of function " + getName(),
|
|
|
|
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
|
|
|
|
}
|
|
|
|
|
|
|
|
auto result_type = std::make_shared<DataTypeUInt8>();
|
|
|
|
return has_nullable_arguments
|
|
|
|
? makeNullable(result_type)
|
|
|
|
: result_type;
|
|
|
|
}
|
|
|
|
|
2021-06-22 16:21:23 +00:00
|
|
|
template <bool inverted>
|
|
|
|
static void applyTernaryLogicImpl(const IColumn::Filter & mask, IColumn::Filter & null_bytemap)
|
2021-06-07 10:55:55 +00:00
|
|
|
{
|
|
|
|
for (size_t i = 0; i != mask.size(); ++i)
|
|
|
|
{
|
2021-06-22 16:21:23 +00:00
|
|
|
UInt8 value = mask[i];
|
|
|
|
if constexpr (inverted)
|
|
|
|
value = !value;
|
|
|
|
|
|
|
|
if (null_bytemap[i] && value)
|
2021-06-07 10:55:55 +00:00
|
|
|
null_bytemap[i] = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-22 16:21:23 +00:00
|
|
|
template <typename Name>
|
|
|
|
static void applyTernaryLogic(const IColumn::Filter & mask, IColumn::Filter & null_bytemap)
|
|
|
|
{
|
|
|
|
if (Name::name == NameAnd::name)
|
|
|
|
applyTernaryLogicImpl<true>(mask, null_bytemap);
|
|
|
|
else if (Name::name == NameOr::name)
|
|
|
|
applyTernaryLogicImpl<false>(mask, null_bytemap);
|
|
|
|
}
|
|
|
|
|
2021-04-20 15:55:33 +00:00
|
|
|
template <typename Impl, typename Name>
|
2021-06-07 10:55:55 +00:00
|
|
|
ColumnPtr FunctionAnyArityLogical<Impl, Name>::executeShortCircuit(ColumnsWithTypeAndName & arguments, const DataTypePtr & result_type) const
|
2021-04-20 15:55:33 +00:00
|
|
|
{
|
|
|
|
if (Name::name != NameAnd::name && Name::name != NameOr::name)
|
2021-04-22 15:14:58 +00:00
|
|
|
throw Exception("Function " + getName() + " doesn't support short circuit execution", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
|
2021-04-20 15:55:33 +00:00
|
|
|
|
2021-06-22 16:21:23 +00:00
|
|
|
/// Let's denote x_i' = maskedExecute(x_i, mask).
|
|
|
|
/// 1) AND(x_0, x_1, x_2, ..., x_n)
|
|
|
|
/// We will support mask_i = x_0 & x_1 & ... & x_i.
|
|
|
|
/// Base:
|
|
|
|
/// mask_0 is 1 everywhere, x_0' = x_0.
|
|
|
|
/// Iteration:
|
|
|
|
/// mask_i = extractMask(mask_{i - 1}, x_{i - 1}')
|
|
|
|
/// x_i' = maskedExecute(x_i, mask)
|
|
|
|
/// Also we will treat NULL as 1 if x_i' is Nullable
|
|
|
|
/// to support ternary logic.
|
|
|
|
/// The result is mask_n.
|
|
|
|
///
|
|
|
|
/// 1) OR(x_0, x_1, x_2, ..., x_n)
|
|
|
|
/// We will support mask_i = !x_0 & !x_1 & ... & !x_i.
|
|
|
|
/// mask_0 is 1 everywhere, x_0' = x_0.
|
|
|
|
/// mask = extractMask(mask, !x_{i - 1}')
|
|
|
|
/// x_i' = maskedExecute(x_i, mask)
|
|
|
|
/// Also we will treat NULL as 0 if x_i' is Nullable
|
|
|
|
/// to support ternary logic.
|
|
|
|
/// The result is !mask_n.
|
2021-05-18 13:05:55 +00:00
|
|
|
|
2021-06-07 10:55:55 +00:00
|
|
|
bool inverted = Name::name != NameAnd::name;
|
2021-06-22 16:21:23 +00:00
|
|
|
UInt8 null_value = UInt8(Name::name == NameAnd::name);
|
|
|
|
IColumn::Filter mask(arguments[0].column->size(), 1);
|
2021-06-07 10:55:55 +00:00
|
|
|
|
|
|
|
/// If result is nullable, we need to create null bytemap of the resulting column.
|
|
|
|
/// We will fill it while extracting mask from arguments.
|
|
|
|
std::unique_ptr<IColumn::Filter> nulls;
|
2021-06-10 17:47:34 +00:00
|
|
|
if (result_type->isNullable())
|
2021-06-07 10:55:55 +00:00
|
|
|
nulls = std::make_unique<IColumn::Filter>(arguments[0].column->size(), 0);
|
|
|
|
|
2021-06-22 16:21:23 +00:00
|
|
|
MaskInfo mask_info;
|
|
|
|
for (size_t i = 1; i <= arguments.size(); ++i)
|
2021-04-20 15:55:33 +00:00
|
|
|
{
|
2021-06-22 16:21:23 +00:00
|
|
|
if (inverted)
|
2021-08-10 11:31:15 +00:00
|
|
|
mask_info = extractInvertedMask(mask, arguments[i - 1].column, nulls.get(), null_value);
|
2021-06-22 16:21:23 +00:00
|
|
|
else
|
2021-08-10 11:31:15 +00:00
|
|
|
mask_info = extractMask(mask, arguments[i - 1].column, nulls.get(), null_value);
|
2021-06-22 16:21:23 +00:00
|
|
|
|
|
|
|
/// If mask doesn't have ones, we don't need to execute the rest arguments,
|
2021-06-07 10:55:55 +00:00
|
|
|
/// because the result won't change.
|
2021-06-22 16:21:23 +00:00
|
|
|
if (!mask_info.has_ones || i == arguments.size())
|
2021-06-07 10:55:55 +00:00
|
|
|
break;
|
2021-06-22 16:21:23 +00:00
|
|
|
|
2021-08-10 11:31:15 +00:00
|
|
|
maskedExecute(arguments[i], mask, mask_info);
|
2021-04-20 15:55:33 +00:00
|
|
|
}
|
2021-06-22 16:21:23 +00:00
|
|
|
/// For OR function we need to inverse mask to get the resulting column.
|
|
|
|
if (inverted)
|
2021-08-10 11:31:15 +00:00
|
|
|
inverseMask(mask, mask_info);
|
2021-06-07 10:55:55 +00:00
|
|
|
|
2021-06-10 17:47:34 +00:00
|
|
|
if (nulls)
|
2021-06-07 10:55:55 +00:00
|
|
|
applyTernaryLogic<Name>(mask, *nulls);
|
|
|
|
|
|
|
|
MutableColumnPtr res = ColumnUInt8::create();
|
|
|
|
typeid_cast<ColumnUInt8 *>(res.get())->getData() = std::move(mask);
|
|
|
|
|
2021-06-10 17:47:34 +00:00
|
|
|
if (!nulls)
|
2021-06-07 10:55:55 +00:00
|
|
|
return res;
|
|
|
|
|
|
|
|
MutableColumnPtr bytemap = ColumnUInt8::create();
|
|
|
|
typeid_cast<ColumnUInt8 *>(bytemap.get())->getData() = std::move(*nulls);
|
|
|
|
return ColumnNullable::create(std::move(res), std::move(bytemap));
|
2021-04-20 15:55:33 +00:00
|
|
|
}
|
|
|
|
|
2019-07-16 20:57:11 +00:00
|
|
|
template <typename Impl, typename Name>
|
2020-10-18 19:00:13 +00:00
|
|
|
ColumnPtr FunctionAnyArityLogical<Impl, Name>::executeImpl(
|
2021-06-07 10:55:55 +00:00
|
|
|
const ColumnsWithTypeAndName & args, const DataTypePtr & result_type, size_t input_rows_count) const
|
2019-07-16 20:57:11 +00:00
|
|
|
{
|
2021-06-07 10:55:55 +00:00
|
|
|
ColumnsWithTypeAndName arguments = std::move(args);
|
|
|
|
|
|
|
|
/// Special implementation for short-circuit arguments.
|
|
|
|
if (checkShirtCircuitArguments(arguments) != -1)
|
|
|
|
return executeShortCircuit(arguments, result_type);
|
|
|
|
|
2019-07-16 20:57:11 +00:00
|
|
|
ColumnRawPtrs args_in;
|
2020-10-18 19:00:13 +00:00
|
|
|
for (const auto & arg_index : arguments)
|
|
|
|
args_in.push_back(arg_index.column.get());
|
2019-07-16 20:57:11 +00:00
|
|
|
|
2020-10-18 19:00:13 +00:00
|
|
|
if (result_type->isNullable())
|
|
|
|
return executeForTernaryLogicImpl<Impl>(std::move(args_in), result_type, input_rows_count);
|
2019-07-16 20:57:11 +00:00
|
|
|
else
|
2020-10-18 19:00:13 +00:00
|
|
|
return basicExecuteImpl<Impl>(std::move(args_in), input_rows_count);
|
2019-07-16 20:57:11 +00:00
|
|
|
}
|
|
|
|
|
2021-05-23 22:06:38 +00:00
|
|
|
template <typename Impl, typename Name>
|
2021-05-24 11:25:02 +00:00
|
|
|
ColumnPtr FunctionAnyArityLogical<Impl, Name>::getConstantResultForNonConstArguments(const ColumnsWithTypeAndName & arguments, const DataTypePtr & result_type) const
|
2021-05-23 22:06:38 +00:00
|
|
|
{
|
|
|
|
/** Try to perform optimization for saturable functions (AndFunction, OrFunction) in case some arguments are
|
2021-05-24 09:30:28 +00:00
|
|
|
* constants.
|
|
|
|
* If function is not saturable (XorFunction) we cannot perform such optimization.
|
|
|
|
* If function is AndFunction and in arguments there is constant false, result is false.
|
|
|
|
* If function is OrFunction and in arguments there is constant true, result is true.
|
|
|
|
*/
|
2021-05-23 22:06:38 +00:00
|
|
|
if constexpr (!Impl::isSaturable())
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
bool has_true_constant = false;
|
|
|
|
bool has_false_constant = false;
|
|
|
|
|
|
|
|
for (const auto & argument : arguments)
|
|
|
|
{
|
|
|
|
ColumnPtr column = argument.column;
|
|
|
|
|
|
|
|
if (!column || !isColumnConst(*column))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
DataTypePtr non_nullable_type = removeNullable(argument.type);
|
|
|
|
TypeIndex data_type_index = non_nullable_type->getTypeId();
|
|
|
|
|
|
|
|
if (!isNativeNumber(data_type_index))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
const ColumnConst * const_column = static_cast<const ColumnConst *>(column.get());
|
|
|
|
|
2021-05-24 09:30:28 +00:00
|
|
|
Field constant_field_value = const_column->getField();
|
|
|
|
if (constant_field_value.isNull())
|
|
|
|
continue;
|
|
|
|
|
|
|
|
auto field_type = constant_field_value.getType();
|
2021-05-23 22:06:38 +00:00
|
|
|
|
2021-05-24 09:30:28 +00:00
|
|
|
bool constant_value_bool = false;
|
2021-05-23 22:06:38 +00:00
|
|
|
|
2021-05-24 09:30:28 +00:00
|
|
|
if (field_type == Field::Types::Float64)
|
|
|
|
constant_value_bool = static_cast<bool>(constant_field_value.get<Float64>());
|
|
|
|
else if (field_type == Field::Types::Int64)
|
|
|
|
constant_value_bool = static_cast<bool>(constant_field_value.get<Int64>());
|
|
|
|
else if (field_type == Field::Types::UInt64)
|
|
|
|
constant_value_bool = static_cast<bool>(constant_field_value.get<UInt64>());
|
2021-05-23 22:06:38 +00:00
|
|
|
|
2021-05-24 09:30:28 +00:00
|
|
|
has_true_constant = has_true_constant || constant_value_bool;
|
|
|
|
has_false_constant = has_false_constant || !constant_value_bool;
|
2021-05-23 22:06:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ColumnPtr result_column;
|
|
|
|
|
|
|
|
if constexpr (std::is_same_v<Impl, AndImpl>)
|
|
|
|
{
|
|
|
|
if (has_false_constant)
|
2021-07-06 09:36:44 +00:00
|
|
|
result_column = result_type->createColumnConst(0, static_cast<UInt8>(false));
|
2021-05-23 22:06:38 +00:00
|
|
|
}
|
|
|
|
else if constexpr (std::is_same_v<Impl, OrImpl>)
|
|
|
|
{
|
|
|
|
if (has_true_constant)
|
2021-07-06 09:36:44 +00:00
|
|
|
result_column = result_type->createColumnConst(0, static_cast<UInt8>(true));
|
2021-05-23 22:06:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return result_column;
|
|
|
|
}
|
2019-07-16 20:57:11 +00:00
|
|
|
|
|
|
|
template <typename A, typename Op>
|
|
|
|
struct UnaryOperationImpl
|
|
|
|
{
|
|
|
|
using ResultType = typename Op::ResultType;
|
|
|
|
using ArrayA = typename ColumnVector<A>::Container;
|
|
|
|
using ArrayC = typename ColumnVector<ResultType>::Container;
|
|
|
|
|
|
|
|
static void NO_INLINE vector(const ArrayA & a, ArrayC & c)
|
|
|
|
{
|
|
|
|
std::transform(
|
|
|
|
a.cbegin(), a.cend(), c.begin(),
|
|
|
|
[](const auto x) { return Op::apply(x); });
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
template <template <typename> class Impl, typename Name>
|
|
|
|
DataTypePtr FunctionUnaryLogical<Impl, Name>::getReturnTypeImpl(const DataTypes & arguments) const
|
|
|
|
{
|
|
|
|
if (!isNativeNumber(arguments[0]))
|
|
|
|
throw Exception("Illegal type ("
|
|
|
|
+ arguments[0]->getName()
|
|
|
|
+ ") of argument of function " + getName(),
|
|
|
|
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
|
|
|
|
|
|
|
|
return std::make_shared<DataTypeUInt8>();
|
|
|
|
}
|
|
|
|
|
|
|
|
template <template <typename> class Impl, typename T>
|
2020-11-17 13:24:45 +00:00
|
|
|
ColumnPtr functionUnaryExecuteType(const ColumnsWithTypeAndName & arguments)
|
2019-07-16 20:57:11 +00:00
|
|
|
{
|
2020-10-18 19:00:13 +00:00
|
|
|
if (auto col = checkAndGetColumn<ColumnVector<T>>(arguments[0].column.get()))
|
2019-07-16 20:57:11 +00:00
|
|
|
{
|
|
|
|
auto col_res = ColumnUInt8::create();
|
|
|
|
|
|
|
|
typename ColumnUInt8::Container & vec_res = col_res->getData();
|
|
|
|
vec_res.resize(col->getData().size());
|
|
|
|
UnaryOperationImpl<T, Impl<T>>::vector(col->getData(), vec_res);
|
|
|
|
|
2020-10-18 19:00:13 +00:00
|
|
|
return col_res;
|
2019-07-16 20:57:11 +00:00
|
|
|
}
|
|
|
|
|
2020-10-18 19:00:13 +00:00
|
|
|
return nullptr;
|
2019-07-16 20:57:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
template <template <typename> class Impl, typename Name>
|
2020-11-17 13:24:45 +00:00
|
|
|
ColumnPtr FunctionUnaryLogical<Impl, Name>::executeImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr &, size_t /*input_rows_count*/) const
|
2019-07-16 20:57:11 +00:00
|
|
|
{
|
2020-10-18 19:00:13 +00:00
|
|
|
ColumnPtr res;
|
2020-10-20 13:11:57 +00:00
|
|
|
if (!((res = functionUnaryExecuteType<Impl, UInt8>(arguments))
|
2020-10-18 19:00:13 +00:00
|
|
|
|| (res = functionUnaryExecuteType<Impl, UInt16>(arguments))
|
|
|
|
|| (res = functionUnaryExecuteType<Impl, UInt32>(arguments))
|
|
|
|
|| (res = functionUnaryExecuteType<Impl, UInt64>(arguments))
|
|
|
|
|| (res = functionUnaryExecuteType<Impl, Int8>(arguments))
|
|
|
|
|| (res = functionUnaryExecuteType<Impl, Int16>(arguments))
|
|
|
|
|| (res = functionUnaryExecuteType<Impl, Int32>(arguments))
|
|
|
|
|| (res = functionUnaryExecuteType<Impl, Int64>(arguments))
|
|
|
|
|| (res = functionUnaryExecuteType<Impl, Float32>(arguments))
|
|
|
|
|| (res = functionUnaryExecuteType<Impl, Float64>(arguments))))
|
|
|
|
throw Exception("Illegal column " + arguments[0].column->getName()
|
2019-07-16 20:57:11 +00:00
|
|
|
+ " of argument of function " + getName(),
|
|
|
|
ErrorCodes::ILLEGAL_COLUMN);
|
2020-10-18 19:00:13 +00:00
|
|
|
|
|
|
|
return res;
|
2019-07-16 20:57:11 +00:00
|
|
|
}
|
|
|
|
|
2014-08-22 00:57:20 +00:00
|
|
|
}
|