2011-08-09 15:57:33 +00:00
|
|
|
#pragma once
|
|
|
|
|
2017-04-01 09:19:00 +00:00
|
|
|
#include <DataTypes/DataTypesNumber.h>
|
2018-08-01 19:50:19 +00:00
|
|
|
#include <DataTypes/DataTypesDecimal.h>
|
2017-04-01 09:19:00 +00:00
|
|
|
#include <DataTypes/DataTypeDate.h>
|
|
|
|
#include <DataTypes/DataTypeDateTime.h>
|
2017-10-30 05:26:31 +00:00
|
|
|
#include <DataTypes/DataTypeInterval.h>
|
2018-09-04 14:08:35 +00:00
|
|
|
#include <DataTypes/DataTypeAggregateFunction.h>
|
2018-04-29 22:43:02 +00:00
|
|
|
#include <DataTypes/Native.h>
|
2017-04-01 09:19:00 +00:00
|
|
|
#include <Columns/ColumnVector.h>
|
2018-08-27 16:16:16 +00:00
|
|
|
#include <Columns/ColumnDecimal.h>
|
2017-04-01 09:19:00 +00:00
|
|
|
#include <Columns/ColumnConst.h>
|
2018-09-04 14:08:35 +00:00
|
|
|
#include <Columns/ColumnAggregateFunction.h>
|
2017-04-01 09:19:00 +00:00
|
|
|
#include <Functions/IFunction.h>
|
2017-07-21 06:35:58 +00:00
|
|
|
#include <Functions/FunctionHelpers.h>
|
2017-10-30 05:26:31 +00:00
|
|
|
#include <Functions/FunctionFactory.h>
|
2017-06-13 04:45:30 +00:00
|
|
|
#include <DataTypes/NumberTraits.h>
|
2017-05-24 20:25:36 +00:00
|
|
|
#include <Core/AccurateComparison.h>
|
2017-11-24 13:55:31 +00:00
|
|
|
#include <Common/FieldVisitors.h>
|
2017-07-13 20:58:19 +00:00
|
|
|
#include <Common/typeid_cast.h>
|
2018-09-06 01:06:30 +00:00
|
|
|
#include <Common/Arena.h>
|
2017-07-21 06:35:58 +00:00
|
|
|
#include <IO/WriteHelpers.h>
|
2017-11-06 01:22:25 +00:00
|
|
|
#include <Interpreters/ExpressionActions.h>
|
2017-07-21 06:35:58 +00:00
|
|
|
#include <ext/range.h>
|
2017-12-21 23:46:34 +00:00
|
|
|
#include <common/intExp.h>
|
2018-08-24 14:20:23 +00:00
|
|
|
#include <common/arithmeticOverflow.h>
|
2018-05-09 04:21:40 +00:00
|
|
|
#include <boost/integer/common_factor.hpp>
|
2011-08-09 15:57:33 +00:00
|
|
|
|
2018-04-29 22:43:02 +00:00
|
|
|
#if USE_EMBEDDED_COMPILER
|
2018-05-06 09:29:57 +00:00
|
|
|
#pragma GCC diagnostic push
|
|
|
|
#pragma GCC diagnostic ignored "-Wunused-parameter"
|
2018-06-21 15:23:20 +00:00
|
|
|
#include <llvm/IR/IRBuilder.h> // Y_IGNORE
|
2018-05-06 09:29:57 +00:00
|
|
|
#pragma GCC diagnostic pop
|
2018-04-29 22:43:02 +00:00
|
|
|
#endif
|
|
|
|
|
2011-08-09 15:57:33 +00:00
|
|
|
|
|
|
|
namespace DB
|
|
|
|
{
|
|
|
|
|
2016-01-12 02:21:15 +00:00
|
|
|
namespace ErrorCodes
|
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
extern const int ILLEGAL_DIVISION;
|
2017-06-13 02:06:53 +00:00
|
|
|
extern const int ILLEGAL_COLUMN;
|
2017-07-21 06:35:58 +00:00
|
|
|
extern const int LOGICAL_ERROR;
|
|
|
|
extern const int TOO_LESS_ARGUMENTS_FOR_FUNCTION;
|
2018-08-08 20:15:49 +00:00
|
|
|
extern const int DECIMAL_OVERFLOW;
|
2018-09-06 18:28:43 +00:00
|
|
|
extern const int CANNOT_ADD_DIFFERENT_AGGREGATE_STATES;
|
2016-01-12 02:21:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-03-20 19:16:02 +00:00
|
|
|
/** Arithmetic operations: +, -, *, /, %,
|
|
|
|
* intDiv (integer division), unary minus.
|
|
|
|
* Bitwise operations: |, &, ^, ~.
|
|
|
|
* Etc.
|
2011-08-09 15:57:33 +00:00
|
|
|
*/
|
|
|
|
|
2017-07-21 06:35:58 +00:00
|
|
|
template <typename A, typename B, typename Op, typename ResultType_ = typename Op::ResultType>
|
2014-08-17 02:13:40 +00:00
|
|
|
struct BinaryOperationImplBase
|
2011-08-09 15:57:33 +00:00
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
using ResultType = ResultType_;
|
|
|
|
|
2017-09-16 22:24:48 +00:00
|
|
|
static void NO_INLINE vector_vector(const PaddedPODArray<A> & a, const PaddedPODArray<B> & b, PaddedPODArray<ResultType> & c)
|
2017-04-01 07:20:54 +00:00
|
|
|
{
|
|
|
|
size_t size = a.size();
|
|
|
|
for (size_t i = 0; i < size; ++i)
|
|
|
|
c[i] = Op::template apply<ResultType>(a[i], b[i]);
|
|
|
|
}
|
|
|
|
|
2017-09-16 22:24:48 +00:00
|
|
|
static void NO_INLINE vector_constant(const PaddedPODArray<A> & a, B b, PaddedPODArray<ResultType> & c)
|
2017-04-01 07:20:54 +00:00
|
|
|
{
|
|
|
|
size_t size = a.size();
|
|
|
|
for (size_t i = 0; i < size; ++i)
|
|
|
|
c[i] = Op::template apply<ResultType>(a[i], b);
|
|
|
|
}
|
|
|
|
|
2017-09-16 22:24:48 +00:00
|
|
|
static void NO_INLINE constant_vector(A a, const PaddedPODArray<B> & b, PaddedPODArray<ResultType> & c)
|
2017-04-01 07:20:54 +00:00
|
|
|
{
|
|
|
|
size_t size = b.size();
|
|
|
|
for (size_t i = 0; i < size; ++i)
|
|
|
|
c[i] = Op::template apply<ResultType>(a, b[i]);
|
|
|
|
}
|
|
|
|
|
2018-04-29 13:01:14 +00:00
|
|
|
static ResultType constant_constant(A a, B b)
|
2017-04-01 07:20:54 +00:00
|
|
|
{
|
2018-04-29 13:01:14 +00:00
|
|
|
return Op::template apply<ResultType>(a, b);
|
2017-04-01 07:20:54 +00:00
|
|
|
}
|
2011-08-09 15:57:33 +00:00
|
|
|
};
|
|
|
|
|
2017-07-21 06:35:58 +00:00
|
|
|
template <typename A, typename B, typename Op, typename ResultType = typename Op::ResultType>
|
2014-09-08 10:46:30 +00:00
|
|
|
struct BinaryOperationImpl : BinaryOperationImplBase<A, B, Op, ResultType>
|
2014-08-17 02:13:40 +00:00
|
|
|
{
|
|
|
|
};
|
|
|
|
|
2014-09-08 10:46:30 +00:00
|
|
|
|
2017-07-21 06:35:58 +00:00
|
|
|
template <typename A, typename Op>
|
2012-12-20 20:01:18 +00:00
|
|
|
struct UnaryOperationImpl
|
2011-08-09 17:24:17 +00:00
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
using ResultType = typename Op::ResultType;
|
2018-09-12 17:50:51 +00:00
|
|
|
using ColVecA = std::conditional_t<IsDecimalNumber<A>, ColumnDecimal<A>, ColumnVector<A>>;
|
|
|
|
using ColVecC = std::conditional_t<IsDecimalNumber<ResultType>, ColumnDecimal<ResultType>, ColumnVector<ResultType>>;
|
|
|
|
using ArrayA = typename ColVecA::Container;
|
|
|
|
using ArrayC = typename ColVecC::Container;
|
2017-04-01 07:20:54 +00:00
|
|
|
|
2018-08-10 14:57:55 +00:00
|
|
|
static void NO_INLINE vector(const ArrayA & a, ArrayC & c)
|
2017-04-01 07:20:54 +00:00
|
|
|
{
|
|
|
|
size_t size = a.size();
|
|
|
|
for (size_t i = 0; i < size; ++i)
|
|
|
|
c[i] = Op::apply(a[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void constant(A a, ResultType & c)
|
|
|
|
{
|
|
|
|
c = Op::apply(a);
|
|
|
|
}
|
2012-12-20 20:01:18 +00:00
|
|
|
};
|
2011-08-09 17:24:17 +00:00
|
|
|
|
2011-08-20 23:56:07 +00:00
|
|
|
|
2017-07-21 06:35:58 +00:00
|
|
|
template <typename A, typename B>
|
2012-12-20 20:01:18 +00:00
|
|
|
struct PlusImpl
|
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
using ResultType = typename NumberTraits::ResultOfAdditionMultiplication<A, B>::Type;
|
2018-08-01 19:50:19 +00:00
|
|
|
static const constexpr bool allow_decimal = true;
|
2017-04-01 07:20:54 +00:00
|
|
|
|
|
|
|
template <typename Result = ResultType>
|
|
|
|
static inline Result apply(A a, B b)
|
|
|
|
{
|
2017-05-13 22:19:04 +00:00
|
|
|
/// Next everywhere, static_cast - so that there is no wrong result in expressions of the form Int64 c = UInt32(a) * Int32(-1).
|
2017-04-01 07:20:54 +00:00
|
|
|
return static_cast<Result>(a) + b;
|
|
|
|
}
|
2018-04-29 22:43:02 +00:00
|
|
|
|
2018-08-22 16:28:02 +00:00
|
|
|
/// Apply operation and check overflow. It's used for Deciamal operations. @returns true if overflowed, false othervise.
|
2018-08-08 20:15:49 +00:00
|
|
|
template <typename Result = ResultType>
|
|
|
|
static inline bool apply(A a, B b, Result & c)
|
|
|
|
{
|
2018-08-09 12:24:03 +00:00
|
|
|
return common::addOverflow(static_cast<Result>(a), b, c);
|
2018-08-08 20:15:49 +00:00
|
|
|
}
|
|
|
|
|
2018-04-29 22:43:02 +00:00
|
|
|
#if USE_EMBEDDED_COMPILER
|
|
|
|
static constexpr bool compilable = true;
|
|
|
|
|
|
|
|
static inline llvm::Value * compile(llvm::IRBuilder<> & b, llvm::Value * left, llvm::Value * right, bool)
|
|
|
|
{
|
|
|
|
return left->getType()->isIntegerTy() ? b.CreateAdd(left, right) : b.CreateFAdd(left, right);
|
|
|
|
}
|
|
|
|
#endif
|
2011-08-09 17:24:17 +00:00
|
|
|
};
|
|
|
|
|
2014-09-08 10:46:30 +00:00
|
|
|
|
2017-07-21 06:35:58 +00:00
|
|
|
template <typename A, typename B>
|
2012-12-20 20:01:18 +00:00
|
|
|
struct MultiplyImpl
|
2011-08-09 17:24:17 +00:00
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
using ResultType = typename NumberTraits::ResultOfAdditionMultiplication<A, B>::Type;
|
2018-08-01 19:50:19 +00:00
|
|
|
static const constexpr bool allow_decimal = true;
|
2011-08-09 17:24:17 +00:00
|
|
|
|
2017-04-01 07:20:54 +00:00
|
|
|
template <typename Result = ResultType>
|
|
|
|
static inline Result apply(A a, B b)
|
|
|
|
{
|
|
|
|
return static_cast<Result>(a) * b;
|
|
|
|
}
|
2018-04-29 22:43:02 +00:00
|
|
|
|
2018-08-22 16:28:02 +00:00
|
|
|
/// Apply operation and check overflow. It's used for Deciamal operations. @returns true if overflowed, false othervise.
|
2018-08-08 20:15:49 +00:00
|
|
|
template <typename Result = ResultType>
|
|
|
|
static inline bool apply(A a, B b, Result & c)
|
|
|
|
{
|
2018-08-09 12:24:03 +00:00
|
|
|
return common::mulOverflow(static_cast<Result>(a), b, c);
|
2018-08-08 20:15:49 +00:00
|
|
|
}
|
|
|
|
|
2018-04-29 22:43:02 +00:00
|
|
|
#if USE_EMBEDDED_COMPILER
|
|
|
|
static constexpr bool compilable = true;
|
|
|
|
|
|
|
|
static inline llvm::Value * compile(llvm::IRBuilder<> & b, llvm::Value * left, llvm::Value * right, bool)
|
|
|
|
{
|
|
|
|
return left->getType()->isIntegerTy() ? b.CreateMul(left, right) : b.CreateFMul(left, right);
|
|
|
|
}
|
|
|
|
#endif
|
2012-12-20 20:01:18 +00:00
|
|
|
};
|
2011-08-09 17:24:17 +00:00
|
|
|
|
2017-07-21 06:35:58 +00:00
|
|
|
template <typename A, typename B>
|
2012-12-20 20:01:18 +00:00
|
|
|
struct MinusImpl
|
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
using ResultType = typename NumberTraits::ResultOfSubtraction<A, B>::Type;
|
2018-08-01 19:50:19 +00:00
|
|
|
static const constexpr bool allow_decimal = true;
|
2011-08-20 23:56:07 +00:00
|
|
|
|
2017-04-01 07:20:54 +00:00
|
|
|
template <typename Result = ResultType>
|
|
|
|
static inline Result apply(A a, B b)
|
|
|
|
{
|
|
|
|
return static_cast<Result>(a) - b;
|
|
|
|
}
|
2018-04-29 22:43:02 +00:00
|
|
|
|
2018-08-22 16:28:02 +00:00
|
|
|
/// Apply operation and check overflow. It's used for Deciamal operations. @returns true if overflowed, false othervise.
|
2018-08-08 20:15:49 +00:00
|
|
|
template <typename Result = ResultType>
|
|
|
|
static inline bool apply(A a, B b, Result & c)
|
|
|
|
{
|
2018-08-09 12:24:03 +00:00
|
|
|
return common::subOverflow(static_cast<Result>(a), b, c);
|
2018-08-08 20:15:49 +00:00
|
|
|
}
|
|
|
|
|
2018-04-29 22:43:02 +00:00
|
|
|
#if USE_EMBEDDED_COMPILER
|
|
|
|
static constexpr bool compilable = true;
|
|
|
|
|
|
|
|
static inline llvm::Value * compile(llvm::IRBuilder<> & b, llvm::Value * left, llvm::Value * right, bool)
|
|
|
|
{
|
|
|
|
return left->getType()->isIntegerTy() ? b.CreateSub(left, right) : b.CreateFSub(left, right);
|
|
|
|
}
|
|
|
|
#endif
|
2011-08-09 17:24:17 +00:00
|
|
|
};
|
|
|
|
|
2017-07-21 06:35:58 +00:00
|
|
|
template <typename A, typename B>
|
2011-08-09 17:24:17 +00:00
|
|
|
struct DivideFloatingImpl
|
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
using ResultType = typename NumberTraits::ResultOfFloatingPointDivision<A, B>::Type;
|
2018-08-01 19:50:19 +00:00
|
|
|
static const constexpr bool allow_decimal = true;
|
2011-08-09 17:24:17 +00:00
|
|
|
|
2017-04-01 07:20:54 +00:00
|
|
|
template <typename Result = ResultType>
|
|
|
|
static inline Result apply(A a, B b)
|
|
|
|
{
|
|
|
|
return static_cast<Result>(a) / b;
|
|
|
|
}
|
2018-04-29 22:43:02 +00:00
|
|
|
|
|
|
|
#if USE_EMBEDDED_COMPILER
|
|
|
|
static constexpr bool compilable = true;
|
|
|
|
|
|
|
|
static inline llvm::Value * compile(llvm::IRBuilder<> & b, llvm::Value * left, llvm::Value * right, bool)
|
|
|
|
{
|
|
|
|
if (left->getType()->isIntegerTy())
|
|
|
|
throw Exception("DivideFloatingImpl expected a floating-point type", ErrorCodes::LOGICAL_ERROR);
|
|
|
|
return b.CreateFDiv(left, right);
|
|
|
|
}
|
|
|
|
#endif
|
2011-08-09 17:24:17 +00:00
|
|
|
};
|
|
|
|
|
2012-12-18 20:07:26 +00:00
|
|
|
|
2013-06-07 22:12:58 +00:00
|
|
|
#pragma GCC diagnostic push
|
|
|
|
#pragma GCC diagnostic ignored "-Wsign-compare"
|
|
|
|
|
2013-02-16 21:23:55 +00:00
|
|
|
template <typename A, typename B>
|
|
|
|
inline void throwIfDivisionLeadsToFPE(A a, B b)
|
2012-12-18 20:07:26 +00:00
|
|
|
{
|
2017-05-13 22:19:04 +00:00
|
|
|
/// Is it better to use siglongjmp instead of checks?
|
2014-06-26 00:58:14 +00:00
|
|
|
|
2017-04-01 07:20:54 +00:00
|
|
|
if (unlikely(b == 0))
|
|
|
|
throw Exception("Division by zero", ErrorCodes::ILLEGAL_DIVISION);
|
2013-02-16 21:23:55 +00:00
|
|
|
|
2017-04-01 07:20:54 +00:00
|
|
|
/// http://avva.livejournal.com/2548306.html
|
2017-12-25 04:01:46 +00:00
|
|
|
if (unlikely(std::is_signed_v<A> && std::is_signed_v<B> && a == std::numeric_limits<A>::min() && b == -1))
|
2017-04-01 07:20:54 +00:00
|
|
|
throw Exception("Division of minimal signed number by minus one", ErrorCodes::ILLEGAL_DIVISION);
|
2013-06-07 22:12:58 +00:00
|
|
|
}
|
2013-06-07 17:32:12 +00:00
|
|
|
|
2014-11-21 14:28:39 +00:00
|
|
|
template <typename A, typename B>
|
|
|
|
inline bool divisionLeadsToFPE(A a, B b)
|
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
if (unlikely(b == 0))
|
|
|
|
return true;
|
2014-11-21 14:28:39 +00:00
|
|
|
|
2017-12-25 04:01:46 +00:00
|
|
|
if (unlikely(std::is_signed_v<A> && std::is_signed_v<B> && a == std::numeric_limits<A>::min() && b == -1))
|
2017-04-01 07:20:54 +00:00
|
|
|
return true;
|
2014-11-21 14:28:39 +00:00
|
|
|
|
2017-04-01 07:20:54 +00:00
|
|
|
return false;
|
2014-11-21 14:28:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-06-07 17:32:12 +00:00
|
|
|
#pragma GCC diagnostic pop
|
2013-06-07 22:12:58 +00:00
|
|
|
|
2012-12-18 20:07:26 +00:00
|
|
|
|
2017-07-21 06:35:58 +00:00
|
|
|
template <typename A, typename B>
|
2011-08-09 17:24:17 +00:00
|
|
|
struct DivideIntegralImpl
|
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
using ResultType = typename NumberTraits::ResultOfIntegerDivision<A, B>::Type;
|
|
|
|
|
|
|
|
template <typename Result = ResultType>
|
|
|
|
static inline Result apply(A a, B b)
|
|
|
|
{
|
|
|
|
throwIfDivisionLeadsToFPE(a, b);
|
|
|
|
return a / b;
|
|
|
|
}
|
2018-04-29 22:43:02 +00:00
|
|
|
|
|
|
|
#if USE_EMBEDDED_COMPILER
|
|
|
|
static constexpr bool compilable = false; /// don't know how to throw from LLVM IR
|
|
|
|
#endif
|
2011-08-09 17:24:17 +00:00
|
|
|
};
|
|
|
|
|
2017-07-21 06:35:58 +00:00
|
|
|
template <typename A, typename B>
|
2014-11-21 14:28:39 +00:00
|
|
|
struct DivideIntegralOrZeroImpl
|
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
using ResultType = typename NumberTraits::ResultOfIntegerDivision<A, B>::Type;
|
2014-11-21 14:28:39 +00:00
|
|
|
|
2017-04-01 07:20:54 +00:00
|
|
|
template <typename Result = ResultType>
|
|
|
|
static inline Result apply(A a, B b)
|
|
|
|
{
|
|
|
|
return unlikely(divisionLeadsToFPE(a, b)) ? 0 : a / b;
|
|
|
|
}
|
2018-04-29 22:43:02 +00:00
|
|
|
|
|
|
|
#if USE_EMBEDDED_COMPILER
|
|
|
|
static constexpr bool compilable = false; /// TODO implement the checks
|
|
|
|
#endif
|
2014-11-21 14:28:39 +00:00
|
|
|
};
|
|
|
|
|
2017-07-21 06:35:58 +00:00
|
|
|
template <typename A, typename B>
|
2011-08-09 17:24:17 +00:00
|
|
|
struct ModuloImpl
|
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
using ResultType = typename NumberTraits::ResultOfModulo<A, B>::Type;
|
|
|
|
|
|
|
|
template <typename Result = ResultType>
|
|
|
|
static inline Result apply(A a, B b)
|
|
|
|
{
|
2017-11-24 08:17:49 +00:00
|
|
|
throwIfDivisionLeadsToFPE(typename NumberTraits::ToInteger<A>::Type(a), typename NumberTraits::ToInteger<B>::Type(b));
|
2018-04-29 22:43:02 +00:00
|
|
|
return typename NumberTraits::ToInteger<A>::Type(a) % typename NumberTraits::ToInteger<B>::Type(b);
|
2017-04-01 07:20:54 +00:00
|
|
|
}
|
2018-04-29 22:43:02 +00:00
|
|
|
|
|
|
|
#if USE_EMBEDDED_COMPILER
|
|
|
|
static constexpr bool compilable = false; /// don't know how to throw from LLVM IR
|
|
|
|
#endif
|
2011-08-09 17:24:17 +00:00
|
|
|
};
|
|
|
|
|
2017-07-21 06:35:58 +00:00
|
|
|
template <typename A, typename B>
|
2013-12-17 16:06:52 +00:00
|
|
|
struct BitAndImpl
|
2012-12-20 13:41:55 +00:00
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
using ResultType = typename NumberTraits::ResultOfBit<A, B>::Type;
|
|
|
|
|
|
|
|
template <typename Result = ResultType>
|
|
|
|
static inline Result apply(A a, B b)
|
|
|
|
{
|
2018-04-29 22:43:02 +00:00
|
|
|
return static_cast<Result>(a) & static_cast<Result>(b);
|
|
|
|
}
|
|
|
|
|
|
|
|
#if USE_EMBEDDED_COMPILER
|
|
|
|
static constexpr bool compilable = true;
|
|
|
|
|
|
|
|
static inline llvm::Value * compile(llvm::IRBuilder<> & b, llvm::Value * left, llvm::Value * right, bool)
|
|
|
|
{
|
|
|
|
if (!left->getType()->isIntegerTy())
|
|
|
|
throw Exception("BitAndImpl expected an integral type", ErrorCodes::LOGICAL_ERROR);
|
|
|
|
return b.CreateAnd(left, right);
|
2017-04-01 07:20:54 +00:00
|
|
|
}
|
2018-04-29 22:43:02 +00:00
|
|
|
#endif
|
2012-12-20 13:41:55 +00:00
|
|
|
};
|
|
|
|
|
2017-07-21 06:35:58 +00:00
|
|
|
template <typename A, typename B>
|
2013-12-17 16:06:52 +00:00
|
|
|
struct BitOrImpl
|
2012-12-20 13:41:55 +00:00
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
using ResultType = typename NumberTraits::ResultOfBit<A, B>::Type;
|
|
|
|
|
|
|
|
template <typename Result = ResultType>
|
|
|
|
static inline Result apply(A a, B b)
|
|
|
|
{
|
2018-04-29 22:43:02 +00:00
|
|
|
return static_cast<Result>(a) | static_cast<Result>(b);
|
2017-04-01 07:20:54 +00:00
|
|
|
}
|
2018-04-29 22:43:02 +00:00
|
|
|
|
|
|
|
#if USE_EMBEDDED_COMPILER
|
|
|
|
static constexpr bool compilable = true;
|
|
|
|
|
|
|
|
static inline llvm::Value * compile(llvm::IRBuilder<> & b, llvm::Value * left, llvm::Value * right, bool)
|
|
|
|
{
|
|
|
|
if (!left->getType()->isIntegerTy())
|
|
|
|
throw Exception("BitOrImpl expected an integral type", ErrorCodes::LOGICAL_ERROR);
|
|
|
|
return b.CreateOr(left, right);
|
|
|
|
}
|
|
|
|
#endif
|
2012-12-20 20:01:18 +00:00
|
|
|
};
|
|
|
|
|
2017-07-21 06:35:58 +00:00
|
|
|
template <typename A, typename B>
|
2013-12-17 16:06:52 +00:00
|
|
|
struct BitXorImpl
|
2012-12-20 20:01:18 +00:00
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
using ResultType = typename NumberTraits::ResultOfBit<A, B>::Type;
|
|
|
|
|
|
|
|
template <typename Result = ResultType>
|
|
|
|
static inline Result apply(A a, B b)
|
|
|
|
{
|
2018-04-29 22:43:02 +00:00
|
|
|
return static_cast<Result>(a) ^ static_cast<Result>(b);
|
2017-04-01 07:20:54 +00:00
|
|
|
}
|
2018-04-29 22:43:02 +00:00
|
|
|
|
|
|
|
#if USE_EMBEDDED_COMPILER
|
|
|
|
static constexpr bool compilable = true;
|
|
|
|
|
|
|
|
static inline llvm::Value * compile(llvm::IRBuilder<> & b, llvm::Value * left, llvm::Value * right, bool)
|
|
|
|
{
|
|
|
|
if (!left->getType()->isIntegerTy())
|
|
|
|
throw Exception("BitXorImpl expected an integral type", ErrorCodes::LOGICAL_ERROR);
|
|
|
|
return b.CreateXor(left, right);
|
|
|
|
}
|
|
|
|
#endif
|
2012-12-20 13:41:55 +00:00
|
|
|
};
|
|
|
|
|
2017-07-21 06:35:58 +00:00
|
|
|
template <typename A, typename B>
|
2013-12-17 16:06:52 +00:00
|
|
|
struct BitShiftLeftImpl
|
2013-12-17 13:49:21 +00:00
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
using ResultType = typename NumberTraits::ResultOfBit<A, B>::Type;
|
|
|
|
|
|
|
|
template <typename Result = ResultType>
|
|
|
|
static inline Result apply(A a, B b)
|
|
|
|
{
|
2018-04-29 22:43:02 +00:00
|
|
|
return static_cast<Result>(a) << static_cast<Result>(b);
|
|
|
|
}
|
|
|
|
|
|
|
|
#if USE_EMBEDDED_COMPILER
|
|
|
|
static constexpr bool compilable = true;
|
|
|
|
|
|
|
|
static inline llvm::Value * compile(llvm::IRBuilder<> & b, llvm::Value * left, llvm::Value * right, bool)
|
|
|
|
{
|
|
|
|
if (!left->getType()->isIntegerTy())
|
|
|
|
throw Exception("BitShiftLeftImpl expected an integral type", ErrorCodes::LOGICAL_ERROR);
|
|
|
|
return b.CreateShl(left, right);
|
2017-04-01 07:20:54 +00:00
|
|
|
}
|
2018-04-29 22:43:02 +00:00
|
|
|
#endif
|
2013-12-17 13:49:21 +00:00
|
|
|
};
|
|
|
|
|
2017-07-21 06:35:58 +00:00
|
|
|
template <typename A, typename B>
|
2013-12-17 16:06:52 +00:00
|
|
|
struct BitShiftRightImpl
|
2013-12-17 13:49:21 +00:00
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
using ResultType = typename NumberTraits::ResultOfBit<A, B>::Type;
|
|
|
|
|
|
|
|
template <typename Result = ResultType>
|
|
|
|
static inline Result apply(A a, B b)
|
|
|
|
{
|
2018-04-29 22:43:02 +00:00
|
|
|
return static_cast<Result>(a) >> static_cast<Result>(b);
|
2017-04-01 07:20:54 +00:00
|
|
|
}
|
2018-04-29 22:43:02 +00:00
|
|
|
|
|
|
|
#if USE_EMBEDDED_COMPILER
|
|
|
|
static constexpr bool compilable = true;
|
|
|
|
|
|
|
|
static inline llvm::Value * compile(llvm::IRBuilder<> & b, llvm::Value * left, llvm::Value * right, bool is_signed)
|
|
|
|
{
|
|
|
|
if (!left->getType()->isIntegerTy())
|
|
|
|
throw Exception("BitShiftRightImpl expected an integral type", ErrorCodes::LOGICAL_ERROR);
|
|
|
|
return is_signed ? b.CreateAShr(left, right) : b.CreateLShr(left, right);
|
|
|
|
}
|
|
|
|
#endif
|
2013-12-17 13:49:21 +00:00
|
|
|
};
|
|
|
|
|
2017-07-21 06:35:58 +00:00
|
|
|
template <typename A, typename B>
|
2017-03-20 19:16:02 +00:00
|
|
|
struct BitRotateLeftImpl
|
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
using ResultType = typename NumberTraits::ResultOfBit<A, B>::Type;
|
|
|
|
|
|
|
|
template <typename Result = ResultType>
|
|
|
|
static inline Result apply(A a, B b)
|
|
|
|
{
|
|
|
|
return (static_cast<Result>(a) << static_cast<Result>(b))
|
|
|
|
| (static_cast<Result>(a) >> ((sizeof(Result) * 8) - static_cast<Result>(b)));
|
|
|
|
}
|
2018-04-29 22:43:02 +00:00
|
|
|
|
|
|
|
#if USE_EMBEDDED_COMPILER
|
|
|
|
static constexpr bool compilable = true;
|
|
|
|
|
|
|
|
static inline llvm::Value * compile(llvm::IRBuilder<> & b, llvm::Value * left, llvm::Value * right, bool)
|
|
|
|
{
|
|
|
|
if (!left->getType()->isIntegerTy())
|
|
|
|
throw Exception("BitRotateLeftImpl expected an integral type", ErrorCodes::LOGICAL_ERROR);
|
|
|
|
auto * size = llvm::ConstantInt::get(left->getType(), left->getType()->getPrimitiveSizeInBits());
|
|
|
|
/// XXX how is this supposed to behave in signed mode?
|
|
|
|
return b.CreateOr(b.CreateShl(left, right), b.CreateLShr(left, b.CreateSub(size, right)));
|
|
|
|
}
|
|
|
|
#endif
|
2017-03-20 19:16:02 +00:00
|
|
|
};
|
|
|
|
|
2017-07-21 06:35:58 +00:00
|
|
|
template <typename A, typename B>
|
2017-03-20 19:16:02 +00:00
|
|
|
struct BitRotateRightImpl
|
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
using ResultType = typename NumberTraits::ResultOfBit<A, B>::Type;
|
|
|
|
|
|
|
|
template <typename Result = ResultType>
|
|
|
|
static inline Result apply(A a, B b)
|
|
|
|
{
|
|
|
|
return (static_cast<Result>(a) >> static_cast<Result>(b))
|
|
|
|
| (static_cast<Result>(a) << ((sizeof(Result) * 8) - static_cast<Result>(b)));
|
|
|
|
}
|
2017-03-20 19:16:02 +00:00
|
|
|
|
2018-04-29 22:43:02 +00:00
|
|
|
#if USE_EMBEDDED_COMPILER
|
|
|
|
static constexpr bool compilable = true;
|
2015-07-10 03:06:00 +00:00
|
|
|
|
2018-04-29 22:43:02 +00:00
|
|
|
static inline llvm::Value * compile(llvm::IRBuilder<> & b, llvm::Value * left, llvm::Value * right, bool)
|
|
|
|
{
|
|
|
|
if (!left->getType()->isIntegerTy())
|
|
|
|
throw Exception("BitRotateRightImpl expected an integral type", ErrorCodes::LOGICAL_ERROR);
|
|
|
|
auto * size = llvm::ConstantInt::get(left->getType(), left->getType()->getPrimitiveSizeInBits());
|
|
|
|
return b.CreateOr(b.CreateLShr(left, right), b.CreateShl(left, b.CreateSub(size, right)));
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
};
|
2017-07-21 06:35:58 +00:00
|
|
|
|
|
|
|
template <typename A, typename B>
|
|
|
|
struct BitTestImpl
|
|
|
|
{
|
|
|
|
using ResultType = UInt8;
|
|
|
|
|
|
|
|
template <typename Result = ResultType>
|
2018-04-29 22:43:02 +00:00
|
|
|
static inline Result apply(A a, B b)
|
|
|
|
{
|
|
|
|
return (typename NumberTraits::ToInteger<A>::Type(a) >> typename NumberTraits::ToInteger<B>::Type(b)) & 1;
|
2018-06-03 20:39:06 +00:00
|
|
|
}
|
2017-07-21 06:35:58 +00:00
|
|
|
|
2018-04-29 22:43:02 +00:00
|
|
|
#if USE_EMBEDDED_COMPILER
|
|
|
|
static constexpr bool compilable = false; /// TODO
|
|
|
|
#endif
|
|
|
|
};
|
2017-07-21 06:35:58 +00:00
|
|
|
|
|
|
|
template <typename A, typename B>
|
2017-01-13 18:15:12 +00:00
|
|
|
struct LeastBaseImpl
|
2015-07-10 02:57:32 +00:00
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
using ResultType = NumberTraits::ResultOfLeast<A, B>;
|
|
|
|
|
|
|
|
template <typename Result = ResultType>
|
|
|
|
static inline Result apply(A a, B b)
|
|
|
|
{
|
2017-05-13 22:19:04 +00:00
|
|
|
/** gcc 4.9.2 successfully vectorizes a loop from this function. */
|
2017-04-01 07:20:54 +00:00
|
|
|
return static_cast<Result>(a) < static_cast<Result>(b) ? static_cast<Result>(a) : static_cast<Result>(b);
|
|
|
|
}
|
2018-04-29 22:43:02 +00:00
|
|
|
|
|
|
|
#if USE_EMBEDDED_COMPILER
|
|
|
|
static constexpr bool compilable = true;
|
|
|
|
|
|
|
|
static inline llvm::Value * compile(llvm::IRBuilder<> & b, llvm::Value * left, llvm::Value * right, bool is_signed)
|
|
|
|
{
|
|
|
|
if (!left->getType()->isIntegerTy())
|
|
|
|
/// XXX minnum is basically fmin(), it may or may not match whatever apply() does
|
|
|
|
return b.CreateMinNum(left, right);
|
|
|
|
return b.CreateSelect(is_signed ? b.CreateICmpSLT(left, right) : b.CreateICmpULT(left, right), left, right);
|
|
|
|
}
|
|
|
|
#endif
|
2015-07-10 02:57:32 +00:00
|
|
|
};
|
|
|
|
|
2017-07-21 06:35:58 +00:00
|
|
|
template <typename A, typename B>
|
2017-01-13 18:15:12 +00:00
|
|
|
struct LeastSpecialImpl
|
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
using ResultType = std::make_signed_t<A>;
|
|
|
|
|
|
|
|
template <typename Result = ResultType>
|
|
|
|
static inline Result apply(A a, B b)
|
|
|
|
{
|
2017-12-25 04:01:46 +00:00
|
|
|
static_assert(std::is_same_v<Result, ResultType>, "ResultType != Result");
|
2017-04-01 07:20:54 +00:00
|
|
|
return accurate::lessOp(a, b) ? static_cast<Result>(a) : static_cast<Result>(b);
|
|
|
|
}
|
2018-04-29 22:43:02 +00:00
|
|
|
|
|
|
|
#if USE_EMBEDDED_COMPILER
|
|
|
|
static constexpr bool compilable = false; /// ???
|
|
|
|
#endif
|
2017-01-13 18:15:12 +00:00
|
|
|
};
|
|
|
|
|
2017-07-21 06:35:58 +00:00
|
|
|
template <typename A, typename B>
|
2017-12-25 04:01:46 +00:00
|
|
|
using LeastImpl = std::conditional_t<!NumberTraits::LeastGreatestSpecialCase<A, B>, LeastBaseImpl<A, B>, LeastSpecialImpl<A, B>>;
|
2017-01-13 18:15:12 +00:00
|
|
|
|
|
|
|
|
2017-07-21 06:35:58 +00:00
|
|
|
template <typename A, typename B>
|
2017-01-13 18:15:12 +00:00
|
|
|
struct GreatestBaseImpl
|
2015-07-10 02:57:32 +00:00
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
using ResultType = NumberTraits::ResultOfGreatest<A, B>;
|
2015-07-10 02:57:32 +00:00
|
|
|
|
2017-04-01 07:20:54 +00:00
|
|
|
template <typename Result = ResultType>
|
|
|
|
static inline Result apply(A a, B b)
|
|
|
|
{
|
|
|
|
return static_cast<Result>(a) > static_cast<Result>(b) ? static_cast<Result>(a) : static_cast<Result>(b);
|
|
|
|
}
|
2018-04-29 22:43:02 +00:00
|
|
|
|
|
|
|
#if USE_EMBEDDED_COMPILER
|
|
|
|
static constexpr bool compilable = true;
|
|
|
|
|
|
|
|
static inline llvm::Value * compile(llvm::IRBuilder<> & b, llvm::Value * left, llvm::Value * right, bool is_signed)
|
|
|
|
{
|
|
|
|
if (!left->getType()->isIntegerTy())
|
|
|
|
/// XXX maxnum is basically fmax(), it may or may not match whatever apply() does
|
2018-04-29 23:07:39 +00:00
|
|
|
/// XXX CreateMaxNum is broken on LLVM 5.0 and 6.0 (generates minnum instead; fixed in 7)
|
|
|
|
return b.CreateBinaryIntrinsic(llvm::Intrinsic::maxnum, left, right);
|
2018-04-29 22:43:02 +00:00
|
|
|
return b.CreateSelect(is_signed ? b.CreateICmpSGT(left, right) : b.CreateICmpUGT(left, right), left, right);
|
|
|
|
}
|
|
|
|
#endif
|
2015-07-10 02:57:32 +00:00
|
|
|
};
|
|
|
|
|
2017-07-21 06:35:58 +00:00
|
|
|
template <typename A, typename B>
|
2017-01-13 18:15:12 +00:00
|
|
|
struct GreatestSpecialImpl
|
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
using ResultType = std::make_unsigned_t<A>;
|
|
|
|
|
|
|
|
template <typename Result = ResultType>
|
|
|
|
static inline Result apply(A a, B b)
|
|
|
|
{
|
2017-12-25 04:01:46 +00:00
|
|
|
static_assert(std::is_same_v<Result, ResultType>, "ResultType != Result");
|
2017-04-01 07:20:54 +00:00
|
|
|
return accurate::greaterOp(a, b) ? static_cast<Result>(a) : static_cast<Result>(b);
|
|
|
|
}
|
2018-04-29 22:43:02 +00:00
|
|
|
|
|
|
|
#if USE_EMBEDDED_COMPILER
|
|
|
|
static constexpr bool compilable = false; /// ???
|
|
|
|
#endif
|
2017-01-13 18:15:12 +00:00
|
|
|
};
|
|
|
|
|
2017-07-21 06:35:58 +00:00
|
|
|
template <typename A, typename B>
|
2017-12-25 04:01:46 +00:00
|
|
|
using GreatestImpl = std::conditional_t<!NumberTraits::LeastGreatestSpecialCase<A, B>, GreatestBaseImpl<A, B>, GreatestSpecialImpl<A, B>>;
|
2017-01-13 18:15:12 +00:00
|
|
|
|
|
|
|
|
2017-07-21 06:35:58 +00:00
|
|
|
template <typename A>
|
2011-09-05 00:32:22 +00:00
|
|
|
struct NegateImpl
|
|
|
|
{
|
2018-08-21 04:31:35 +00:00
|
|
|
using ResultType = std::conditional_t<IsDecimalNumber<A>, A, typename NumberTraits::ResultOfNegate<A>::Type>;
|
2011-09-05 00:32:22 +00:00
|
|
|
|
2017-04-01 07:20:54 +00:00
|
|
|
static inline ResultType apply(A a)
|
|
|
|
{
|
|
|
|
return -static_cast<ResultType>(a);
|
|
|
|
}
|
2018-04-29 22:43:02 +00:00
|
|
|
|
|
|
|
#if USE_EMBEDDED_COMPILER
|
|
|
|
static constexpr bool compilable = true;
|
|
|
|
|
|
|
|
static inline llvm::Value * compile(llvm::IRBuilder<> & b, llvm::Value * arg, bool)
|
|
|
|
{
|
|
|
|
return arg->getType()->isIntegerTy() ? b.CreateNeg(arg) : b.CreateFNeg(arg);
|
|
|
|
}
|
|
|
|
#endif
|
2012-12-20 20:01:18 +00:00
|
|
|
};
|
2011-09-05 00:32:22 +00:00
|
|
|
|
2017-07-21 06:35:58 +00:00
|
|
|
template <typename A>
|
2013-12-17 16:06:52 +00:00
|
|
|
struct BitNotImpl
|
2012-12-20 20:01:18 +00:00
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
using ResultType = typename NumberTraits::ResultOfBitNot<A>::Type;
|
2012-12-20 20:01:18 +00:00
|
|
|
|
2017-04-01 07:20:54 +00:00
|
|
|
static inline ResultType apply(A a)
|
|
|
|
{
|
|
|
|
return ~static_cast<ResultType>(a);
|
|
|
|
}
|
2018-04-29 22:43:02 +00:00
|
|
|
|
|
|
|
#if USE_EMBEDDED_COMPILER
|
|
|
|
static constexpr bool compilable = true;
|
|
|
|
|
|
|
|
static inline llvm::Value * compile(llvm::IRBuilder<> & b, llvm::Value * arg, bool)
|
|
|
|
{
|
|
|
|
if (!arg->getType()->isIntegerTy())
|
|
|
|
throw Exception("BitNotImpl expected an integral type", ErrorCodes::LOGICAL_ERROR);
|
|
|
|
return b.CreateNot(arg);
|
|
|
|
}
|
|
|
|
#endif
|
2011-09-05 00:32:22 +00:00
|
|
|
};
|
|
|
|
|
2017-07-21 06:35:58 +00:00
|
|
|
template <typename A>
|
2014-11-28 16:09:29 +00:00
|
|
|
struct AbsImpl
|
|
|
|
{
|
2018-08-21 04:31:35 +00:00
|
|
|
using ResultType = std::conditional_t<IsDecimalNumber<A>, A, typename NumberTraits::ResultOfAbs<A>::Type>;
|
2017-04-01 07:20:54 +00:00
|
|
|
|
2017-12-01 21:40:58 +00:00
|
|
|
static inline ResultType apply(A a)
|
2017-04-01 07:20:54 +00:00
|
|
|
{
|
2018-08-21 04:31:35 +00:00
|
|
|
if constexpr (IsDecimalNumber<A>)
|
2018-08-07 14:35:21 +00:00
|
|
|
return a < 0 ? A(-a) : a;
|
2018-08-03 16:32:55 +00:00
|
|
|
else if constexpr (std::is_integral_v<A> && std::is_signed_v<A>)
|
2017-12-01 21:40:58 +00:00
|
|
|
return a < 0 ? static_cast<ResultType>(~a) + 1 : a;
|
2017-12-25 04:01:46 +00:00
|
|
|
else if constexpr (std::is_integral_v<A> && std::is_unsigned_v<A>)
|
2017-12-01 21:40:58 +00:00
|
|
|
return static_cast<ResultType>(a);
|
2017-12-25 04:01:46 +00:00
|
|
|
else if constexpr (std::is_floating_point_v<A>)
|
2017-12-01 21:40:58 +00:00
|
|
|
return static_cast<ResultType>(std::abs(a));
|
2017-04-01 07:20:54 +00:00
|
|
|
}
|
2018-04-29 22:43:02 +00:00
|
|
|
|
|
|
|
#if USE_EMBEDDED_COMPILER
|
|
|
|
static constexpr bool compilable = false; /// special type handling, some other time
|
|
|
|
#endif
|
2014-11-28 16:09:29 +00:00
|
|
|
};
|
2011-08-09 15:57:33 +00:00
|
|
|
|
2017-11-22 09:25:36 +00:00
|
|
|
template <typename A, typename B>
|
|
|
|
struct GCDImpl
|
|
|
|
{
|
2017-11-24 08:17:49 +00:00
|
|
|
using ResultType = typename NumberTraits::ResultOfAdditionMultiplication<A, B>::Type;
|
2017-11-22 09:25:36 +00:00
|
|
|
|
|
|
|
template <typename Result = ResultType>
|
|
|
|
static inline Result apply(A a, B b)
|
|
|
|
{
|
2017-11-23 08:40:06 +00:00
|
|
|
throwIfDivisionLeadsToFPE(typename NumberTraits::ToInteger<A>::Type(a), typename NumberTraits::ToInteger<B>::Type(b));
|
|
|
|
throwIfDivisionLeadsToFPE(typename NumberTraits::ToInteger<B>::Type(b), typename NumberTraits::ToInteger<A>::Type(a));
|
2018-05-09 04:21:40 +00:00
|
|
|
return boost::integer::gcd(
|
2017-11-23 08:40:06 +00:00
|
|
|
typename NumberTraits::ToInteger<Result>::Type(a),
|
|
|
|
typename NumberTraits::ToInteger<Result>::Type(b));
|
2017-11-22 09:25:36 +00:00
|
|
|
}
|
2018-04-29 22:43:02 +00:00
|
|
|
|
|
|
|
#if USE_EMBEDDED_COMPILER
|
|
|
|
static constexpr bool compilable = false; /// exceptions (and a non-trivial algorithm)
|
|
|
|
#endif
|
2017-11-22 09:25:36 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
template <typename A, typename B>
|
|
|
|
struct LCMImpl
|
|
|
|
{
|
2017-11-22 09:50:23 +00:00
|
|
|
using ResultType = typename NumberTraits::ResultOfAdditionMultiplication<A, B>::Type;
|
2017-11-22 09:25:36 +00:00
|
|
|
|
|
|
|
template <typename Result = ResultType>
|
|
|
|
static inline Result apply(A a, B b)
|
|
|
|
{
|
2017-11-23 08:40:06 +00:00
|
|
|
throwIfDivisionLeadsToFPE(typename NumberTraits::ToInteger<A>::Type(a), typename NumberTraits::ToInteger<B>::Type(b));
|
|
|
|
throwIfDivisionLeadsToFPE(typename NumberTraits::ToInteger<B>::Type(b), typename NumberTraits::ToInteger<A>::Type(a));
|
2018-05-09 04:21:40 +00:00
|
|
|
return boost::integer::lcm(
|
2017-11-23 08:40:06 +00:00
|
|
|
typename NumberTraits::ToInteger<Result>::Type(a),
|
|
|
|
typename NumberTraits::ToInteger<Result>::Type(b));
|
2017-11-22 09:25:36 +00:00
|
|
|
}
|
2018-04-29 22:43:02 +00:00
|
|
|
|
|
|
|
#if USE_EMBEDDED_COMPILER
|
|
|
|
static constexpr bool compilable = false; /// exceptions (and a non-trivial algorithm)
|
|
|
|
#endif
|
2017-11-22 09:25:36 +00:00
|
|
|
};
|
|
|
|
|
2017-12-21 23:46:34 +00:00
|
|
|
template <typename A>
|
|
|
|
struct IntExp2Impl
|
|
|
|
{
|
|
|
|
using ResultType = UInt64;
|
|
|
|
|
|
|
|
static inline ResultType apply(A a)
|
|
|
|
{
|
|
|
|
return intExp2(a);
|
|
|
|
}
|
2018-04-29 22:43:02 +00:00
|
|
|
|
|
|
|
#if USE_EMBEDDED_COMPILER
|
2018-05-07 13:35:33 +00:00
|
|
|
static constexpr bool compilable = true;
|
|
|
|
|
|
|
|
static inline llvm::Value * compile(llvm::IRBuilder<> & b, llvm::Value * arg, bool)
|
|
|
|
{
|
|
|
|
if (!arg->getType()->isIntegerTy())
|
|
|
|
throw Exception("IntExp2Impl expected an integral type", ErrorCodes::LOGICAL_ERROR);
|
|
|
|
return b.CreateShl(llvm::ConstantInt::get(arg->getType(), 1), arg);
|
|
|
|
}
|
2018-04-29 22:43:02 +00:00
|
|
|
#endif
|
2017-12-21 23:46:34 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
template <typename A>
|
|
|
|
struct IntExp10Impl
|
|
|
|
{
|
|
|
|
using ResultType = UInt64;
|
|
|
|
|
|
|
|
static inline ResultType apply(A a)
|
|
|
|
{
|
|
|
|
return intExp10(a);
|
|
|
|
}
|
2018-04-29 22:43:02 +00:00
|
|
|
|
|
|
|
#if USE_EMBEDDED_COMPILER
|
|
|
|
static constexpr bool compilable = false; /// library function
|
|
|
|
#endif
|
2017-12-21 23:46:34 +00:00
|
|
|
};
|
|
|
|
|
2018-08-01 19:50:19 +00:00
|
|
|
|
2018-08-08 20:15:49 +00:00
|
|
|
template <typename T> struct NativeType { using Type = T; };
|
2018-08-21 04:31:35 +00:00
|
|
|
template <> struct NativeType<Decimal32> { using Type = Int32; };
|
|
|
|
template <> struct NativeType<Decimal64> { using Type = Int64; };
|
|
|
|
template <> struct NativeType<Decimal128> { using Type = Int128; };
|
2018-08-08 20:15:49 +00:00
|
|
|
|
2018-08-01 19:50:19 +00:00
|
|
|
/// Binary operations for Decimals need scale args
|
|
|
|
/// +|- scale one of args (which scale factor is not 1). ScaleR = oneof(Scale1, Scale2);
|
|
|
|
/// * no agrs scale. ScaleR = Scale1 + Scale2;
|
|
|
|
/// / first arg scale. ScaleR = Scale1 (scale_a = DecimalType<B>::getScale()).
|
2018-08-22 13:22:56 +00:00
|
|
|
template <typename A, typename B, template <typename, typename> typename Operation, typename ResultType_, bool _check_overflow = true>
|
2018-08-02 14:28:55 +00:00
|
|
|
struct DecimalBinaryOperation
|
2018-08-01 19:50:19 +00:00
|
|
|
{
|
2018-08-08 20:15:49 +00:00
|
|
|
static constexpr bool is_plus_minus = std::is_same_v<Operation<Int32, Int32>, PlusImpl<Int32, Int32>> ||
|
|
|
|
std::is_same_v<Operation<Int32, Int32>, MinusImpl<Int32, Int32>>;
|
|
|
|
static constexpr bool is_multiply = std::is_same_v<Operation<Int32, Int32>, MultiplyImpl<Int32, Int32>>;
|
|
|
|
static constexpr bool is_division = std::is_same_v<Operation<Int32, Int32>, DivideFloatingImpl<Int32, Int32>>;
|
|
|
|
static constexpr bool is_compare = std::is_same_v<Operation<Int32, Int32>, LeastBaseImpl<Int32, Int32>> ||
|
|
|
|
std::is_same_v<Operation<Int32, Int32>, GreatestBaseImpl<Int32, Int32>>;
|
|
|
|
static constexpr bool is_plus_minus_compare = is_plus_minus || is_compare;
|
|
|
|
static constexpr bool can_overflow = is_plus_minus || is_multiply;
|
2018-08-01 19:50:19 +00:00
|
|
|
|
2018-09-26 11:25:50 +00:00
|
|
|
using ResultType = ResultType_;
|
|
|
|
using NativeResultType = typename NativeType<ResultType>::Type;
|
|
|
|
using Op = std::conditional_t<is_division,
|
|
|
|
DivideIntegralImpl<NativeResultType, NativeResultType>, /// substitute divide by intDiv (throw on division by zero)
|
|
|
|
Operation<NativeResultType, NativeResultType>>;
|
|
|
|
using ColVecA = std::conditional_t<IsDecimalNumber<A>, ColumnDecimal<A>, ColumnVector<A>>;
|
|
|
|
using ColVecB = std::conditional_t<IsDecimalNumber<B>, ColumnDecimal<B>, ColumnVector<B>>;
|
|
|
|
using ArrayA = typename ColVecA::Container;
|
|
|
|
using ArrayB = typename ColVecB::Container;
|
|
|
|
using ArrayC = typename ColumnDecimal<ResultType>::Container;
|
|
|
|
using SelfNoOverflow = DecimalBinaryOperation<A, B, Operation, ResultType_, false>;
|
|
|
|
|
2018-09-03 09:15:44 +00:00
|
|
|
static void vector_vector(const ArrayA & a, const ArrayB & b, ArrayC & c, ResultType scale_a, ResultType scale_b, bool check_overflow)
|
|
|
|
{
|
|
|
|
if (check_overflow)
|
|
|
|
vector_vector(a, b, c, scale_a, scale_b);
|
|
|
|
else
|
|
|
|
SelfNoOverflow::vector_vector(a, b, c, scale_a, scale_b);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void vector_constant(const ArrayA & a, B b, ArrayC & c, ResultType scale_a, ResultType scale_b, bool check_overflow)
|
|
|
|
{
|
|
|
|
if (check_overflow)
|
|
|
|
vector_constant(a, b, c, scale_a, scale_b);
|
|
|
|
else
|
|
|
|
SelfNoOverflow::vector_constant(a, b, c, scale_a, scale_b);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void constant_vector(A a, const ArrayB & b, ArrayC & c, ResultType scale_a, ResultType scale_b, bool check_overflow)
|
|
|
|
{
|
|
|
|
if (check_overflow)
|
|
|
|
constant_vector(a, b, c, scale_a, scale_b);
|
|
|
|
else
|
|
|
|
SelfNoOverflow::constant_vector(a, b, c, scale_a, scale_b);
|
|
|
|
}
|
|
|
|
|
|
|
|
static ResultType constant_constant(A a, B b, ResultType scale_a, ResultType scale_b, bool check_overflow)
|
|
|
|
{
|
|
|
|
if (check_overflow)
|
|
|
|
return constant_constant(a, b, scale_a, scale_b);
|
|
|
|
else
|
|
|
|
return SelfNoOverflow::constant_constant(a, b, scale_a, scale_b);
|
|
|
|
}
|
|
|
|
|
2018-08-10 14:57:55 +00:00
|
|
|
static void NO_INLINE vector_vector(const ArrayA & a, const ArrayB & b, ArrayC & c,
|
2018-08-01 19:50:19 +00:00
|
|
|
ResultType scale_a [[maybe_unused]], ResultType scale_b [[maybe_unused]])
|
|
|
|
{
|
|
|
|
size_t size = a.size();
|
2018-08-08 20:15:49 +00:00
|
|
|
if constexpr (is_plus_minus_compare)
|
2018-08-01 19:50:19 +00:00
|
|
|
{
|
|
|
|
if (scale_a != 1)
|
|
|
|
{
|
|
|
|
for (size_t i = 0; i < size; ++i)
|
2018-08-08 20:15:49 +00:00
|
|
|
c[i] = applyScaled<true>(a[i], b[i], scale_a);
|
2018-08-01 19:50:19 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
else if (scale_b != 1)
|
|
|
|
{
|
|
|
|
for (size_t i = 0; i < size; ++i)
|
2018-08-08 20:15:49 +00:00
|
|
|
c[i] = applyScaled<false>(a[i], b[i], scale_b);
|
2018-08-01 19:50:19 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2018-08-21 04:31:35 +00:00
|
|
|
else if constexpr (is_division && IsDecimalNumber<B>)
|
2018-08-01 19:50:19 +00:00
|
|
|
{
|
|
|
|
for (size_t i = 0; i < size; ++i)
|
2018-08-08 20:15:49 +00:00
|
|
|
c[i] = applyScaledDiv(a[i], b[i], scale_a);
|
2018-08-01 19:50:19 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// default: use it if no return before
|
|
|
|
for (size_t i = 0; i < size; ++i)
|
2018-08-08 20:15:49 +00:00
|
|
|
c[i] = apply(a[i], b[i]);
|
2018-08-01 19:50:19 +00:00
|
|
|
}
|
|
|
|
|
2018-08-10 14:57:55 +00:00
|
|
|
static void NO_INLINE vector_constant(const ArrayA & a, B b, ArrayC & c,
|
2018-08-01 19:50:19 +00:00
|
|
|
ResultType scale_a [[maybe_unused]], ResultType scale_b [[maybe_unused]])
|
|
|
|
{
|
|
|
|
size_t size = a.size();
|
2018-08-08 20:15:49 +00:00
|
|
|
if constexpr (is_plus_minus_compare)
|
2018-08-01 19:50:19 +00:00
|
|
|
{
|
|
|
|
if (scale_a != 1)
|
|
|
|
{
|
|
|
|
for (size_t i = 0; i < size; ++i)
|
2018-08-08 20:15:49 +00:00
|
|
|
c[i] = applyScaled<true>(a[i], b, scale_a);
|
2018-08-01 19:50:19 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
else if (scale_b != 1)
|
|
|
|
{
|
|
|
|
for (size_t i = 0; i < size; ++i)
|
2018-08-08 20:15:49 +00:00
|
|
|
c[i] = applyScaled<false>(a[i], b, scale_b);
|
2018-08-01 19:50:19 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2018-08-21 04:31:35 +00:00
|
|
|
else if constexpr (is_division && IsDecimalNumber<B>)
|
2018-08-01 19:50:19 +00:00
|
|
|
{
|
|
|
|
for (size_t i = 0; i < size; ++i)
|
2018-08-08 20:15:49 +00:00
|
|
|
c[i] = applyScaledDiv(a[i], b, scale_a);
|
2018-08-01 19:50:19 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// default: use it if no return before
|
|
|
|
for (size_t i = 0; i < size; ++i)
|
2018-08-08 20:15:49 +00:00
|
|
|
c[i] = apply(a[i], b);
|
2018-08-01 19:50:19 +00:00
|
|
|
}
|
|
|
|
|
2018-08-10 14:57:55 +00:00
|
|
|
static void NO_INLINE constant_vector(A a, const ArrayB & b, ArrayC & c,
|
2018-08-01 19:50:19 +00:00
|
|
|
ResultType scale_a [[maybe_unused]], ResultType scale_b [[maybe_unused]])
|
|
|
|
{
|
|
|
|
size_t size = b.size();
|
2018-08-08 20:15:49 +00:00
|
|
|
if constexpr (is_plus_minus_compare)
|
2018-08-01 19:50:19 +00:00
|
|
|
{
|
|
|
|
if (scale_a != 1)
|
|
|
|
{
|
|
|
|
for (size_t i = 0; i < size; ++i)
|
2018-08-08 20:15:49 +00:00
|
|
|
c[i] = applyScaled<true>(a, b[i], scale_a);
|
2018-08-01 19:50:19 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
else if (scale_b != 1)
|
|
|
|
{
|
|
|
|
for (size_t i = 0; i < size; ++i)
|
2018-08-08 20:15:49 +00:00
|
|
|
c[i] = applyScaled<false>(a, b[i], scale_b);
|
2018-08-01 19:50:19 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2018-08-21 04:31:35 +00:00
|
|
|
else if constexpr (is_division && IsDecimalNumber<B>)
|
2018-08-01 19:50:19 +00:00
|
|
|
{
|
|
|
|
for (size_t i = 0; i < size; ++i)
|
2018-08-08 20:15:49 +00:00
|
|
|
c[i] = applyScaledDiv(a, b[i], scale_a);
|
2018-08-01 19:50:19 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// default: use it if no return before
|
|
|
|
for (size_t i = 0; i < size; ++i)
|
2018-08-08 20:15:49 +00:00
|
|
|
c[i] = apply(a, b[i]);
|
2018-08-01 19:50:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static ResultType constant_constant(A a, B b, ResultType scale_a [[maybe_unused]], ResultType scale_b [[maybe_unused]])
|
|
|
|
{
|
2018-08-08 20:15:49 +00:00
|
|
|
if constexpr (is_plus_minus_compare)
|
|
|
|
{
|
|
|
|
if (scale_a != 1)
|
|
|
|
return applyScaled<true>(a, b, scale_a);
|
|
|
|
else if (scale_b != 1)
|
|
|
|
return applyScaled<false>(a, b, scale_b);
|
|
|
|
}
|
2018-08-21 04:31:35 +00:00
|
|
|
else if constexpr (is_division && IsDecimalNumber<B>)
|
2018-08-08 20:15:49 +00:00
|
|
|
return applyScaledDiv(a, b, scale_a);
|
|
|
|
return apply(a, b);
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
/// there's implicit type convertion here
|
|
|
|
static NativeResultType apply(NativeResultType a, NativeResultType b)
|
|
|
|
{
|
2018-08-22 13:22:56 +00:00
|
|
|
if constexpr (can_overflow && _check_overflow)
|
2018-08-08 20:15:49 +00:00
|
|
|
{
|
|
|
|
NativeResultType res;
|
|
|
|
if (Op::template apply<NativeResultType>(a, b, res))
|
|
|
|
throw Exception("Decimal math overflow", ErrorCodes::DECIMAL_OVERFLOW);
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return Op::template apply<NativeResultType>(a, b);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <bool scale_left>
|
|
|
|
static NativeResultType applyScaled(NativeResultType a, NativeResultType b, NativeResultType scale)
|
|
|
|
{
|
|
|
|
if constexpr (is_plus_minus_compare)
|
|
|
|
{
|
|
|
|
NativeResultType res;
|
|
|
|
|
2018-08-22 13:22:56 +00:00
|
|
|
if constexpr (_check_overflow)
|
|
|
|
{
|
|
|
|
bool overflow = false;
|
|
|
|
if constexpr (scale_left)
|
|
|
|
overflow |= common::mulOverflow(a, scale, a);
|
|
|
|
else
|
|
|
|
overflow |= common::mulOverflow(b, scale, b);
|
|
|
|
|
|
|
|
if constexpr (can_overflow)
|
|
|
|
overflow |= Op::template apply<NativeResultType>(a, b, res);
|
|
|
|
else
|
|
|
|
res = Op::template apply<NativeResultType>(a, b);
|
2018-08-09 12:24:03 +00:00
|
|
|
|
2018-08-22 13:22:56 +00:00
|
|
|
if (overflow)
|
|
|
|
throw Exception("Decimal math overflow", ErrorCodes::DECIMAL_OVERFLOW);
|
|
|
|
}
|
2018-08-08 20:15:49 +00:00
|
|
|
else
|
2018-08-22 13:22:56 +00:00
|
|
|
{
|
|
|
|
if constexpr (scale_left)
|
|
|
|
a *= scale;
|
|
|
|
else
|
|
|
|
b *= scale;
|
2018-08-08 20:15:49 +00:00
|
|
|
res = Op::template apply<NativeResultType>(a, b);
|
2018-08-22 13:22:56 +00:00
|
|
|
}
|
2018-08-09 12:24:03 +00:00
|
|
|
|
2018-08-08 20:15:49 +00:00
|
|
|
return res;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static NativeResultType applyScaledDiv(NativeResultType a, NativeResultType b, NativeResultType scale)
|
|
|
|
{
|
|
|
|
if constexpr (is_division)
|
|
|
|
{
|
2018-08-22 13:22:56 +00:00
|
|
|
if constexpr (_check_overflow)
|
|
|
|
{
|
|
|
|
bool overflow = false;
|
|
|
|
if constexpr (!IsDecimalNumber<A>)
|
|
|
|
overflow |= common::mulOverflow(scale, scale, scale);
|
|
|
|
overflow |= common::mulOverflow(a, scale, a);
|
|
|
|
if (overflow)
|
|
|
|
throw Exception("Decimal math overflow", ErrorCodes::DECIMAL_OVERFLOW);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if constexpr (!IsDecimalNumber<A>)
|
|
|
|
scale *= scale;
|
|
|
|
a *= scale;
|
|
|
|
}
|
2018-08-08 20:15:49 +00:00
|
|
|
|
|
|
|
return Op::template apply<NativeResultType>(a, b);
|
|
|
|
}
|
2018-08-01 19:50:19 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2014-09-08 10:46:30 +00:00
|
|
|
/// Used to indicate undefined operation
|
2014-09-08 12:22:36 +00:00
|
|
|
struct InvalidType;
|
2014-09-08 10:46:30 +00:00
|
|
|
|
2018-04-29 13:01:14 +00:00
|
|
|
template <bool V, typename T> struct Case : std::bool_constant<V> { using type = T; };
|
2017-03-12 10:13:45 +00:00
|
|
|
|
2018-04-29 13:01:14 +00:00
|
|
|
/// Switch<Case<C0, T0>, ...> -- select the first Ti for which Ci is true; InvalidType if none.
|
|
|
|
template <typename... Ts> using Switch = typename std::disjunction<Ts..., Case<true, InvalidType>>::type;
|
2015-07-10 02:57:32 +00:00
|
|
|
|
2017-12-25 04:01:46 +00:00
|
|
|
template <typename DataType> constexpr bool IsIntegral = false;
|
|
|
|
template <> constexpr bool IsIntegral<DataTypeUInt8> = true;
|
|
|
|
template <> constexpr bool IsIntegral<DataTypeUInt16> = true;
|
|
|
|
template <> constexpr bool IsIntegral<DataTypeUInt32> = true;
|
|
|
|
template <> constexpr bool IsIntegral<DataTypeUInt64> = true;
|
|
|
|
template <> constexpr bool IsIntegral<DataTypeInt8> = true;
|
|
|
|
template <> constexpr bool IsIntegral<DataTypeInt16> = true;
|
|
|
|
template <> constexpr bool IsIntegral<DataTypeInt32> = true;
|
|
|
|
template <> constexpr bool IsIntegral<DataTypeInt64> = true;
|
2014-09-08 10:46:30 +00:00
|
|
|
|
2017-12-25 04:01:46 +00:00
|
|
|
template <typename DataType> constexpr bool IsDateOrDateTime = false;
|
|
|
|
template <> constexpr bool IsDateOrDateTime<DataTypeDate> = true;
|
|
|
|
template <> constexpr bool IsDateOrDateTime<DataTypeDateTime> = true;
|
2014-09-08 10:46:30 +00:00
|
|
|
|
2018-08-01 19:50:19 +00:00
|
|
|
template <typename T0, typename T1> constexpr bool UseLeftDecimal = false;
|
2018-08-21 04:31:35 +00:00
|
|
|
template <> constexpr bool UseLeftDecimal<DataTypeDecimal<Decimal128>, DataTypeDecimal<Decimal32>> = true;
|
|
|
|
template <> constexpr bool UseLeftDecimal<DataTypeDecimal<Decimal128>, DataTypeDecimal<Decimal64>> = true;
|
|
|
|
template <> constexpr bool UseLeftDecimal<DataTypeDecimal<Decimal64>, DataTypeDecimal<Decimal32>> = true;
|
2018-08-01 19:50:19 +00:00
|
|
|
|
2018-04-29 13:01:14 +00:00
|
|
|
template <typename T> using DataTypeFromFieldType = std::conditional_t<std::is_same_v<T, NumberTraits::Error>, InvalidType, DataTypeNumber<T>>;
|
|
|
|
|
2014-09-08 10:46:30 +00:00
|
|
|
template <template <typename, typename> class Operation, typename LeftDataType, typename RightDataType>
|
2018-04-29 13:01:14 +00:00
|
|
|
struct BinaryOperationTraits
|
2014-09-08 10:46:30 +00:00
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
using T0 = typename LeftDataType::FieldType;
|
|
|
|
using T1 = typename RightDataType::FieldType;
|
2018-08-02 14:28:55 +00:00
|
|
|
private: /// it's not correct for Decimal
|
2017-04-01 07:20:54 +00:00
|
|
|
using Op = Operation<T0, T1>;
|
2018-08-02 14:28:55 +00:00
|
|
|
public:
|
|
|
|
|
|
|
|
static constexpr bool allow_decimal =
|
|
|
|
std::is_same_v<Operation<T0, T0>, PlusImpl<T0, T0>> ||
|
|
|
|
std::is_same_v<Operation<T0, T0>, MinusImpl<T0, T0>> ||
|
|
|
|
std::is_same_v<Operation<T0, T0>, MultiplyImpl<T0, T0>> ||
|
2018-08-03 14:46:02 +00:00
|
|
|
std::is_same_v<Operation<T0, T0>, DivideFloatingImpl<T0, T0>> ||
|
|
|
|
std::is_same_v<Operation<T0, T0>, LeastBaseImpl<T0, T0>> ||
|
|
|
|
std::is_same_v<Operation<T0, T0>, GreatestBaseImpl<T0, T0>>;
|
2017-04-01 07:20:54 +00:00
|
|
|
|
2018-04-29 13:01:14 +00:00
|
|
|
/// Appropriate result type for binary operator on numeric types. "Date" can also mean
|
|
|
|
/// DateTime, but if both operands are Dates, their type must be the same (e.g. Date - DateTime is invalid).
|
|
|
|
using ResultDataType = Switch<
|
2018-08-01 19:50:19 +00:00
|
|
|
/// Decimal cases
|
2018-09-10 13:52:18 +00:00
|
|
|
Case<!allow_decimal && (IsDataTypeDecimal<LeftDataType> || IsDataTypeDecimal<RightDataType>), InvalidType>,
|
|
|
|
Case<IsDataTypeDecimal<LeftDataType> && IsDataTypeDecimal<RightDataType> && UseLeftDecimal<LeftDataType, RightDataType>, LeftDataType>,
|
|
|
|
Case<IsDataTypeDecimal<LeftDataType> && IsDataTypeDecimal<RightDataType>, RightDataType>,
|
|
|
|
Case<IsDataTypeDecimal<LeftDataType> && !IsDataTypeDecimal<RightDataType> && IsIntegral<RightDataType>, LeftDataType>,
|
|
|
|
Case<!IsDataTypeDecimal<LeftDataType> && IsDataTypeDecimal<RightDataType> && IsIntegral<LeftDataType>, RightDataType>,
|
2018-08-01 19:50:19 +00:00
|
|
|
/// Decimal <op> Real is not supported (traditional DBs convert Decimal <op> Real to Real)
|
2018-09-10 13:52:18 +00:00
|
|
|
Case<IsDataTypeDecimal<LeftDataType> && !IsDataTypeDecimal<RightDataType> && !IsIntegral<RightDataType>, InvalidType>,
|
|
|
|
Case<!IsDataTypeDecimal<LeftDataType> && IsDataTypeDecimal<RightDataType> && !IsIntegral<LeftDataType>, InvalidType>,
|
2018-04-29 13:01:14 +00:00
|
|
|
/// number <op> number -> see corresponding impl
|
|
|
|
Case<!IsDateOrDateTime<LeftDataType> && !IsDateOrDateTime<RightDataType>,
|
|
|
|
DataTypeFromFieldType<typename Op::ResultType>>,
|
|
|
|
/// Date + Integral -> Date
|
|
|
|
/// Integral + Date -> Date
|
|
|
|
Case<std::is_same_v<Op, PlusImpl<T0, T1>>, Switch<
|
|
|
|
Case<IsIntegral<RightDataType>, LeftDataType>,
|
|
|
|
Case<IsIntegral<LeftDataType>, RightDataType>>>,
|
|
|
|
/// Date - Date -> Int32
|
|
|
|
/// Date - Integral -> Date
|
|
|
|
Case<std::is_same_v<Op, MinusImpl<T0, T1>>, Switch<
|
|
|
|
Case<std::is_same_v<LeftDataType, RightDataType>, DataTypeInt32>,
|
|
|
|
Case<IsDateOrDateTime<LeftDataType> && IsIntegral<RightDataType>, LeftDataType>>>,
|
|
|
|
/// least(Date, Date) -> Date
|
|
|
|
/// greatest(Date, Date) -> Date
|
|
|
|
Case<std::is_same_v<LeftDataType, RightDataType> && (std::is_same_v<Op, LeastImpl<T0, T1>> || std::is_same_v<Op, GreatestImpl<T0, T1>>),
|
|
|
|
LeftDataType>>;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
template <typename... Ts, typename F>
|
|
|
|
static bool castTypeToEither(const IDataType * type, F && f)
|
|
|
|
{
|
|
|
|
/// XXX can't use && here because gcc-7 complains about parentheses around && within ||
|
|
|
|
return ((typeid_cast<const Ts *>(type) ? f(*typeid_cast<const Ts *>(type)) : false) || ...);
|
|
|
|
}
|
2014-09-08 10:46:30 +00:00
|
|
|
|
2012-12-20 20:01:18 +00:00
|
|
|
|
2018-07-19 19:12:48 +00:00
|
|
|
template <template <typename, typename> class Op, typename Name, bool CanBeExecutedOnDefaultArguments = true>
|
2011-08-09 15:57:33 +00:00
|
|
|
class FunctionBinaryArithmetic : public IFunction
|
|
|
|
{
|
2017-10-30 05:26:31 +00:00
|
|
|
const Context & context;
|
2018-08-28 13:59:52 +00:00
|
|
|
bool check_decimal_overflow = true;
|
2017-10-30 05:26:31 +00:00
|
|
|
|
2018-04-29 13:01:14 +00:00
|
|
|
template <typename F>
|
|
|
|
static bool castType(const IDataType * type, F && f)
|
2017-04-01 07:20:54 +00:00
|
|
|
{
|
2018-04-29 13:01:14 +00:00
|
|
|
return castTypeToEither<
|
|
|
|
DataTypeUInt8,
|
|
|
|
DataTypeUInt16,
|
|
|
|
DataTypeUInt32,
|
|
|
|
DataTypeUInt64,
|
|
|
|
DataTypeInt8,
|
|
|
|
DataTypeInt16,
|
|
|
|
DataTypeInt32,
|
|
|
|
DataTypeInt64,
|
|
|
|
DataTypeFloat32,
|
|
|
|
DataTypeFloat64,
|
|
|
|
DataTypeDate,
|
2018-08-01 19:50:19 +00:00
|
|
|
DataTypeDateTime,
|
2018-08-21 04:31:35 +00:00
|
|
|
DataTypeDecimal<Decimal32>,
|
|
|
|
DataTypeDecimal<Decimal64>,
|
|
|
|
DataTypeDecimal<Decimal128>
|
2018-04-29 13:01:14 +00:00
|
|
|
>(type, std::forward<F>(f));
|
2017-04-01 07:20:54 +00:00
|
|
|
}
|
|
|
|
|
2018-04-29 13:01:14 +00:00
|
|
|
template <typename F>
|
|
|
|
static bool castBothTypes(const IDataType * left, const IDataType * right, F && f)
|
2017-04-01 07:20:54 +00:00
|
|
|
{
|
2018-08-27 07:57:42 +00:00
|
|
|
return castType(left, [&](const auto & left_) { return castType(right, [&](const auto & right_) { return f(left_, right_); }); });
|
2017-04-01 07:20:54 +00:00
|
|
|
}
|
2014-09-08 10:46:30 +00:00
|
|
|
|
2018-02-02 08:33:36 +00:00
|
|
|
FunctionBuilderPtr getFunctionForIntervalArithmetic(const DataTypePtr & type0, const DataTypePtr & type1) const
|
2017-10-30 05:26:31 +00:00
|
|
|
{
|
|
|
|
/// Special case when the function is plus or minus, one of arguments is Date/DateTime and another is Interval.
|
|
|
|
/// We construct another function (example: addMonths) and call it.
|
|
|
|
|
2017-12-25 04:01:46 +00:00
|
|
|
bool function_is_plus = std::is_same_v<Op<UInt8, UInt8>, PlusImpl<UInt8, UInt8>>;
|
|
|
|
bool function_is_minus = std::is_same_v<Op<UInt8, UInt8>, MinusImpl<UInt8, UInt8>>;
|
2017-10-30 05:26:31 +00:00
|
|
|
|
|
|
|
if (!function_is_plus && !function_is_minus)
|
|
|
|
return {};
|
|
|
|
|
|
|
|
int interval_arg = 1;
|
|
|
|
const DataTypeInterval * interval_data_type = checkAndGetDataType<DataTypeInterval>(type1.get());
|
|
|
|
if (!interval_data_type)
|
|
|
|
{
|
|
|
|
interval_arg = 0;
|
|
|
|
interval_data_type = checkAndGetDataType<DataTypeInterval>(type0.get());
|
|
|
|
}
|
|
|
|
if (!interval_data_type)
|
|
|
|
return {};
|
|
|
|
|
|
|
|
if (interval_arg == 0 && function_is_minus)
|
|
|
|
throw Exception("Wrong order of arguments for function " + getName() + ": argument of type Interval cannot be first.",
|
|
|
|
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
|
|
|
|
|
|
|
|
const DataTypeDate * date_data_type = checkAndGetDataType<DataTypeDate>(interval_arg == 0 ? type1.get() : type0.get());
|
|
|
|
const DataTypeDateTime * date_time_data_type = nullptr;
|
|
|
|
if (!date_data_type)
|
|
|
|
{
|
|
|
|
date_time_data_type = checkAndGetDataType<DataTypeDateTime>(interval_arg == 0 ? type1.get() : type0.get());
|
|
|
|
if (!date_time_data_type)
|
|
|
|
throw Exception("Wrong argument types for function " + getName() + ": if one argument is Interval, then another must be Date or DateTime.",
|
|
|
|
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::stringstream function_name;
|
|
|
|
function_name << (function_is_plus ? "add" : "subtract") << interval_data_type->kindToString() << 's';
|
|
|
|
|
|
|
|
return FunctionFactory::instance().get(function_name.str(), context);
|
|
|
|
}
|
|
|
|
|
2018-09-06 17:59:23 +00:00
|
|
|
bool isAggregateMultiply(const DataTypePtr & type0, const DataTypePtr & type1) const
|
|
|
|
{
|
|
|
|
if constexpr (!std::is_same_v<Op<UInt8, UInt8>, MultiplyImpl<UInt8, UInt8>>)
|
|
|
|
return false;
|
2018-09-07 22:29:37 +00:00
|
|
|
|
|
|
|
WhichDataType which0(type0);
|
|
|
|
WhichDataType which1(type1);
|
|
|
|
|
|
|
|
return (which0.isAggregateFunction() && which1.isNativeUInt())
|
|
|
|
|| (which0.isNativeUInt() && which1.isAggregateFunction());
|
2018-09-06 17:59:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool isAggregateAddition(const DataTypePtr & type0, const DataTypePtr & type1) const
|
|
|
|
{
|
|
|
|
if constexpr (!std::is_same_v<Op<UInt8, UInt8>, PlusImpl<UInt8, UInt8>>)
|
|
|
|
return false;
|
2018-09-08 01:42:34 +00:00
|
|
|
|
2018-09-08 01:40:23 +00:00
|
|
|
WhichDataType which0(type0);
|
|
|
|
WhichDataType which1(type1);
|
2018-09-08 01:42:34 +00:00
|
|
|
|
2018-09-08 01:40:23 +00:00
|
|
|
return which0.isAggregateFunction() && which1.isAggregateFunction();
|
2018-09-06 17:59:23 +00:00
|
|
|
}
|
|
|
|
|
2018-09-07 12:36:56 +00:00
|
|
|
/// Multiply aggregation state by integer constant: by merging it with itself specified number of times.
|
2018-09-08 01:42:34 +00:00
|
|
|
void executeAggregateMultiply(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count) const
|
2018-09-06 19:13:47 +00:00
|
|
|
{
|
|
|
|
ColumnNumbers new_arguments = arguments;
|
2018-09-07 21:39:34 +00:00
|
|
|
if (WhichDataType(block.getByPosition(new_arguments[1]).type).isAggregateFunction())
|
2018-09-06 19:13:47 +00:00
|
|
|
std::swap(new_arguments[0], new_arguments[1]);
|
|
|
|
|
2018-09-07 12:36:56 +00:00
|
|
|
if (!block.getByPosition(new_arguments[1]).column->isColumnConst())
|
2018-09-08 01:42:34 +00:00
|
|
|
throw Exception{"Illegal column " + block.getByPosition(new_arguments[1]).column->getName()
|
2018-09-07 12:36:56 +00:00
|
|
|
+ " of argument of aggregation state multiply. Should be integer constant", ErrorCodes::ILLEGAL_COLUMN};
|
|
|
|
|
2018-09-06 19:13:47 +00:00
|
|
|
const ColumnAggregateFunction * column = typeid_cast<const ColumnAggregateFunction *>(block.getByPosition(new_arguments[0]).column.get());
|
|
|
|
IAggregateFunction * function = column->getAggregateFunction().get();
|
|
|
|
|
|
|
|
auto arena = std::make_shared<Arena>();
|
|
|
|
|
|
|
|
auto column_to = ColumnAggregateFunction::create(column->getAggregateFunction(), Arenas(1, arena));
|
|
|
|
column_to->reserve(input_rows_count);
|
|
|
|
|
|
|
|
auto column_from = ColumnAggregateFunction::create(column->getAggregateFunction(), Arenas(1, arena));
|
|
|
|
column_from->reserve(input_rows_count);
|
|
|
|
|
|
|
|
for (size_t i = 0; i < input_rows_count; ++i)
|
|
|
|
{
|
|
|
|
column_to->insertDefault();
|
|
|
|
column_from->insertFrom(column->getData()[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
auto & vec_to = column_to->getData();
|
|
|
|
auto & vec_from = column_from->getData();
|
|
|
|
|
2018-09-07 14:06:10 +00:00
|
|
|
UInt64 m = typeid_cast<const ColumnConst *>(block.getByPosition(new_arguments[1]).column.get())->getValue<UInt64>();
|
2018-09-06 19:13:47 +00:00
|
|
|
|
|
|
|
/// We use exponentiation by squaring algorithm to perform multiplying aggregate states by N in O(log(N)) operations
|
|
|
|
/// https://en.wikipedia.org/wiki/Exponentiation_by_squaring
|
|
|
|
while (m)
|
|
|
|
{
|
|
|
|
if (m % 2)
|
|
|
|
{
|
|
|
|
for (size_t i = 0; i < input_rows_count; ++i)
|
|
|
|
function->merge(vec_to[i], vec_from[i], arena.get());
|
|
|
|
--m;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
for (size_t i = 0; i < input_rows_count; ++i)
|
|
|
|
function->merge(vec_from[i], vec_from[i], arena.get());
|
|
|
|
m /= 2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
block.getByPosition(result).column = std::move(column_to);
|
|
|
|
}
|
|
|
|
|
2018-09-07 12:36:56 +00:00
|
|
|
/// Merge two aggregation states together.
|
2018-09-06 19:13:47 +00:00
|
|
|
void executeAggregateAddition(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count) const
|
|
|
|
{
|
|
|
|
const ColumnAggregateFunction * columns[2];
|
|
|
|
for (size_t i = 0; i < 2; ++i)
|
|
|
|
columns[i] = typeid_cast<const ColumnAggregateFunction *>(block.getByPosition(arguments[i]).column.get());
|
|
|
|
|
2018-09-07 12:36:56 +00:00
|
|
|
auto column_to = ColumnAggregateFunction::create(columns[0]->getAggregateFunction());
|
2018-09-08 01:42:34 +00:00
|
|
|
column_to->reserve(input_rows_count);
|
2018-09-06 19:13:47 +00:00
|
|
|
|
|
|
|
for(size_t i = 0; i < input_rows_count; ++i)
|
|
|
|
{
|
|
|
|
column_to->insertFrom(columns[0]->getData()[i]);
|
|
|
|
column_to->insertMergeFrom(columns[1]->getData()[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
block.getByPosition(result).column = std::move(column_to);
|
|
|
|
}
|
|
|
|
|
2018-09-08 01:42:34 +00:00
|
|
|
void executeDateTimeIntervalPlusMinus(Block & block, const ColumnNumbers & arguments,
|
2018-09-06 19:13:47 +00:00
|
|
|
size_t result, size_t input_rows_count, const FunctionBuilderPtr & function_builder) const
|
|
|
|
{
|
|
|
|
ColumnNumbers new_arguments = arguments;
|
|
|
|
|
|
|
|
/// Interval argument must be second.
|
2018-09-07 21:39:34 +00:00
|
|
|
if (WhichDataType(block.getByPosition(arguments[0]).type).isInterval())
|
2018-09-06 19:13:47 +00:00
|
|
|
std::swap(new_arguments[0], new_arguments[1]);
|
|
|
|
|
|
|
|
/// Change interval argument type to its representation
|
|
|
|
Block new_block = block;
|
|
|
|
new_block.getByPosition(new_arguments[1]).type = std::make_shared<DataTypeNumber<DataTypeInterval::FieldType>>();
|
|
|
|
|
|
|
|
ColumnsWithTypeAndName new_arguments_with_type_and_name =
|
|
|
|
{new_block.getByPosition(new_arguments[0]), new_block.getByPosition(new_arguments[1])};
|
|
|
|
auto function = function_builder->build(new_arguments_with_type_and_name);
|
|
|
|
|
|
|
|
function->execute(new_block, new_arguments, result, input_rows_count);
|
|
|
|
block.getByPosition(result).column = new_block.getByPosition(result).column;
|
|
|
|
}
|
|
|
|
|
2011-08-09 15:57:33 +00:00
|
|
|
public:
|
2018-04-29 13:01:14 +00:00
|
|
|
static constexpr auto name = Name::name;
|
|
|
|
static FunctionPtr create(const Context & context) { return std::make_shared<FunctionBinaryArithmetic>(context); }
|
|
|
|
|
2018-08-28 13:59:52 +00:00
|
|
|
FunctionBinaryArithmetic(const Context & context_)
|
|
|
|
: context(context_),
|
|
|
|
check_decimal_overflow(decimalCheckArithmeticOverflow(context))
|
|
|
|
{}
|
2018-04-29 13:01:14 +00:00
|
|
|
|
2017-04-01 07:20:54 +00:00
|
|
|
String getName() const override
|
|
|
|
{
|
|
|
|
return name;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t getNumberOfArguments() const override { return 2; }
|
|
|
|
|
|
|
|
DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override
|
|
|
|
{
|
2018-06-18 12:52:27 +00:00
|
|
|
/// Special case when multiply aggregate function state
|
2018-09-04 14:08:35 +00:00
|
|
|
if (isAggregateMultiply(arguments[0], arguments[1]))
|
|
|
|
{
|
2018-09-07 14:37:26 +00:00
|
|
|
if (WhichDataType(arguments[0]).isAggregateFunction())
|
2018-09-04 14:08:35 +00:00
|
|
|
return arguments[0];
|
|
|
|
return arguments[1];
|
|
|
|
}
|
2018-06-18 12:52:27 +00:00
|
|
|
|
2018-09-06 17:59:23 +00:00
|
|
|
/// Special case - addition of two aggregate functions states
|
|
|
|
if (isAggregateAddition(arguments[0], arguments[1]))
|
|
|
|
{
|
2018-09-07 12:36:56 +00:00
|
|
|
if (!arguments[0]->equals(*arguments[1]))
|
2018-09-08 01:42:34 +00:00
|
|
|
throw Exception("Cannot add aggregate states of different functions: "
|
2018-09-07 12:36:56 +00:00
|
|
|
+ arguments[0]->getName() + " and " + arguments[1]->getName(), ErrorCodes::CANNOT_ADD_DIFFERENT_AGGREGATE_STATES);
|
2018-09-06 17:59:23 +00:00
|
|
|
|
|
|
|
return arguments[0];
|
|
|
|
}
|
|
|
|
|
2017-10-30 05:26:31 +00:00
|
|
|
/// Special case when the function is plus or minus, one of arguments is Date/DateTime and another is Interval.
|
2018-02-02 08:33:36 +00:00
|
|
|
if (auto function_builder = getFunctionForIntervalArithmetic(arguments[0], arguments[1]))
|
2017-10-30 05:26:31 +00:00
|
|
|
{
|
2018-02-06 19:34:53 +00:00
|
|
|
ColumnsWithTypeAndName new_arguments(2);
|
2017-11-06 01:22:25 +00:00
|
|
|
|
|
|
|
for (size_t i = 0; i < 2; ++i)
|
2018-02-06 19:34:53 +00:00
|
|
|
new_arguments[i].type = arguments[i];
|
2017-10-30 05:26:31 +00:00
|
|
|
|
|
|
|
/// Interval argument must be second.
|
2018-09-07 14:37:26 +00:00
|
|
|
if (WhichDataType(new_arguments[0].type).isInterval())
|
2018-02-06 19:34:53 +00:00
|
|
|
std::swap(new_arguments[0], new_arguments[1]);
|
2017-10-30 05:26:31 +00:00
|
|
|
|
|
|
|
/// Change interval argument to its representation
|
2018-02-06 19:34:53 +00:00
|
|
|
new_arguments[1].type = std::make_shared<DataTypeNumber<DataTypeInterval::FieldType>>();
|
2017-10-30 05:26:31 +00:00
|
|
|
|
2018-02-06 19:34:53 +00:00
|
|
|
auto function = function_builder->build(new_arguments);
|
2018-02-02 08:33:36 +00:00
|
|
|
return function->getReturnType();
|
2017-10-30 05:26:31 +00:00
|
|
|
}
|
|
|
|
|
2017-04-01 07:20:54 +00:00
|
|
|
DataTypePtr type_res;
|
2018-04-29 13:01:14 +00:00
|
|
|
bool valid = castBothTypes(arguments[0].get(), arguments[1].get(), [&](const auto & left, const auto & right)
|
|
|
|
{
|
|
|
|
using LeftDataType = std::decay_t<decltype(left)>;
|
|
|
|
using RightDataType = std::decay_t<decltype(right)>;
|
|
|
|
using ResultDataType = typename BinaryOperationTraits<Op, LeftDataType, RightDataType>::ResultDataType;
|
|
|
|
if constexpr (!std::is_same_v<ResultDataType, InvalidType>)
|
|
|
|
{
|
2018-09-10 13:52:18 +00:00
|
|
|
if constexpr (IsDataTypeDecimal<LeftDataType> && IsDataTypeDecimal<RightDataType>)
|
2018-08-01 19:50:19 +00:00
|
|
|
{
|
2018-08-02 14:28:55 +00:00
|
|
|
constexpr bool is_multiply = std::is_same_v<Op<UInt8, UInt8>, MultiplyImpl<UInt8, UInt8>>;
|
|
|
|
constexpr bool is_division = std::is_same_v<Op<UInt8, UInt8>, DivideFloatingImpl<UInt8, UInt8>>;
|
|
|
|
ResultDataType result_type = decimalResultType(left, right, is_multiply, is_division);
|
2018-08-01 19:50:19 +00:00
|
|
|
type_res = std::make_shared<ResultDataType>(result_type.getPrecision(), result_type.getScale());
|
|
|
|
}
|
2018-09-10 13:52:18 +00:00
|
|
|
else if constexpr (IsDataTypeDecimal<LeftDataType>)
|
2018-08-01 19:50:19 +00:00
|
|
|
type_res = std::make_shared<LeftDataType>(left.getPrecision(), left.getScale());
|
2018-09-10 13:52:18 +00:00
|
|
|
else if constexpr (IsDataTypeDecimal<RightDataType>)
|
2018-08-01 19:50:19 +00:00
|
|
|
type_res = std::make_shared<RightDataType>(right.getPrecision(), right.getScale());
|
|
|
|
else
|
|
|
|
type_res = std::make_shared<ResultDataType>();
|
2018-04-29 13:01:14 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
});
|
|
|
|
if (!valid)
|
2017-12-09 14:01:42 +00:00
|
|
|
throw Exception("Illegal types " + arguments[0]->getName() + " and " + arguments[1]->getName() + " of arguments of function " + getName(),
|
2017-04-01 07:20:54 +00:00
|
|
|
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
|
|
|
|
return type_res;
|
|
|
|
}
|
|
|
|
|
2018-04-24 07:16:39 +00:00
|
|
|
void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count) override
|
2017-04-01 07:20:54 +00:00
|
|
|
{
|
2018-09-04 14:08:35 +00:00
|
|
|
/// Special case when multiply aggregate function state
|
|
|
|
if (isAggregateMultiply(block.getByPosition(arguments[0]).type, block.getByPosition(arguments[1]).type))
|
2018-06-18 12:52:27 +00:00
|
|
|
{
|
2018-09-06 19:13:47 +00:00
|
|
|
executeAggregateMultiply(block, arguments, result, input_rows_count);
|
2018-09-06 18:02:41 +00:00
|
|
|
return;
|
2018-09-06 17:59:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Special case - addition of two aggregate functions states
|
|
|
|
if (isAggregateAddition(block.getByPosition(arguments[0]).type, block.getByPosition(arguments[1]).type))
|
|
|
|
{
|
2018-09-08 01:42:34 +00:00
|
|
|
executeAggregateAddition(block, arguments, result, input_rows_count);
|
2018-06-18 12:52:27 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-10-30 05:26:31 +00:00
|
|
|
/// Special case when the function is plus or minus, one of arguments is Date/DateTime and another is Interval.
|
2018-02-06 19:34:53 +00:00
|
|
|
if (auto function_builder = getFunctionForIntervalArithmetic(block.getByPosition(arguments[0]).type, block.getByPosition(arguments[1]).type))
|
2017-10-30 05:26:31 +00:00
|
|
|
{
|
2018-09-06 19:13:47 +00:00
|
|
|
executeDateTimeIntervalPlusMinus(block, arguments, result, input_rows_count, function_builder);
|
2017-10-30 05:26:31 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-08-27 07:57:42 +00:00
|
|
|
auto * left_generic = block.getByPosition(arguments[0]).type.get();
|
|
|
|
auto * right_generic = block.getByPosition(arguments[1]).type.get();
|
|
|
|
bool valid = castBothTypes(left_generic, right_generic, [&](const auto & left, const auto & right)
|
2018-04-29 13:01:14 +00:00
|
|
|
{
|
|
|
|
using LeftDataType = std::decay_t<decltype(left)>;
|
|
|
|
using RightDataType = std::decay_t<decltype(right)>;
|
|
|
|
using ResultDataType = typename BinaryOperationTraits<Op, LeftDataType, RightDataType>::ResultDataType;
|
|
|
|
if constexpr (!std::is_same_v<ResultDataType, InvalidType>)
|
|
|
|
{
|
2018-09-10 13:52:18 +00:00
|
|
|
constexpr bool result_is_decimal = IsDataTypeDecimal<LeftDataType> || IsDataTypeDecimal<RightDataType>;
|
2018-08-02 14:28:55 +00:00
|
|
|
constexpr bool is_multiply = std::is_same_v<Op<UInt8, UInt8>, MultiplyImpl<UInt8, UInt8>>;
|
|
|
|
constexpr bool is_division = std::is_same_v<Op<UInt8, UInt8>, DivideFloatingImpl<UInt8, UInt8>>;
|
|
|
|
|
2018-04-29 13:01:14 +00:00
|
|
|
using T0 = typename LeftDataType::FieldType;
|
|
|
|
using T1 = typename RightDataType::FieldType;
|
|
|
|
using ResultType = typename ResultDataType::FieldType;
|
2018-08-27 16:16:16 +00:00
|
|
|
using ColVecT0 = std::conditional_t<IsDecimalNumber<T0>, ColumnDecimal<T0>, ColumnVector<T0>>;
|
|
|
|
using ColVecT1 = std::conditional_t<IsDecimalNumber<T1>, ColumnDecimal<T1>, ColumnVector<T1>>;
|
|
|
|
using ColVecResult = std::conditional_t<IsDecimalNumber<ResultType>, ColumnDecimal<ResultType>, ColumnVector<ResultType>>;
|
2018-08-01 19:50:19 +00:00
|
|
|
|
2018-08-02 14:28:55 +00:00
|
|
|
/// Decimal operations need scale. Operations are on result type.
|
2018-09-10 13:52:18 +00:00
|
|
|
using OpImpl = std::conditional_t<IsDataTypeDecimal<ResultDataType>,
|
2018-08-08 20:15:49 +00:00
|
|
|
DecimalBinaryOperation<T0, T1, Op, ResultType>,
|
2018-08-01 19:50:19 +00:00
|
|
|
BinaryOperationImpl<T0, T1, Op<T0, T1>, ResultType>>;
|
2018-04-29 13:01:14 +00:00
|
|
|
|
|
|
|
auto col_left_raw = block.getByPosition(arguments[0]).column.get();
|
|
|
|
auto col_right_raw = block.getByPosition(arguments[1]).column.get();
|
2018-08-01 19:50:19 +00:00
|
|
|
if (auto col_left = checkAndGetColumnConst<ColVecT0>(col_left_raw))
|
2018-04-29 13:01:14 +00:00
|
|
|
{
|
2018-08-01 19:50:19 +00:00
|
|
|
if (auto col_right = checkAndGetColumnConst<ColVecT1>(col_right_raw))
|
2018-04-29 13:01:14 +00:00
|
|
|
{
|
|
|
|
/// the only case with a non-vector result
|
2018-08-03 10:20:44 +00:00
|
|
|
if constexpr (result_is_decimal)
|
2018-08-01 19:50:19 +00:00
|
|
|
{
|
2018-08-02 14:28:55 +00:00
|
|
|
ResultDataType type = decimalResultType(left, right, is_multiply, is_division);
|
|
|
|
typename ResultDataType::FieldType scale_a = type.scaleFactorFor(left, is_multiply);
|
|
|
|
typename ResultDataType::FieldType scale_b = type.scaleFactorFor(right, is_multiply || is_division);
|
2018-09-10 13:52:18 +00:00
|
|
|
if constexpr (IsDataTypeDecimal<RightDataType> && is_division)
|
2018-08-01 19:50:19 +00:00
|
|
|
scale_a = right.getScaleMultiplier();
|
2018-09-03 09:15:44 +00:00
|
|
|
|
|
|
|
auto res = OpImpl::constant_constant(col_left->template getValue<T0>(), col_right->template getValue<T1>(),
|
|
|
|
scale_a, scale_b, check_decimal_overflow);
|
|
|
|
block.getByPosition(result).column =
|
|
|
|
ResultDataType(type.getPrecision(), type.getScale()).createColumnConst(
|
|
|
|
col_left->size(), toField(res, type.getScale()));
|
|
|
|
|
2018-08-01 19:50:19 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
auto res = OpImpl::constant_constant(col_left->template getValue<T0>(), col_right->template getValue<T1>());
|
|
|
|
block.getByPosition(result).column = ResultDataType().createColumnConst(col_left->size(), toField(res));
|
|
|
|
}
|
2018-04-29 13:01:14 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-27 16:16:16 +00:00
|
|
|
typename ColVecResult::MutablePtr col_res = nullptr;
|
|
|
|
if constexpr (result_is_decimal)
|
|
|
|
{
|
|
|
|
ResultDataType type = decimalResultType(left, right, is_multiply, is_division);
|
|
|
|
col_res = ColVecResult::create(0, type.getScale());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
col_res = ColVecResult::create();
|
|
|
|
|
2018-04-29 13:01:14 +00:00
|
|
|
auto & vec_res = col_res->getData();
|
|
|
|
vec_res.resize(block.rows());
|
2018-08-27 16:16:16 +00:00
|
|
|
|
2018-08-27 07:57:42 +00:00
|
|
|
if (auto col_left_const = checkAndGetColumnConst<ColVecT0>(col_left_raw))
|
2018-04-29 13:01:14 +00:00
|
|
|
{
|
2018-08-01 19:50:19 +00:00
|
|
|
if (auto col_right = checkAndGetColumn<ColVecT1>(col_right_raw))
|
|
|
|
{
|
2018-08-03 10:20:44 +00:00
|
|
|
if constexpr (result_is_decimal)
|
2018-08-01 19:50:19 +00:00
|
|
|
{
|
2018-08-02 14:28:55 +00:00
|
|
|
ResultDataType type = decimalResultType(left, right, is_multiply, is_division);
|
2018-08-10 14:57:55 +00:00
|
|
|
|
2018-08-02 14:28:55 +00:00
|
|
|
typename ResultDataType::FieldType scale_a = type.scaleFactorFor(left, is_multiply);
|
|
|
|
typename ResultDataType::FieldType scale_b = type.scaleFactorFor(right, is_multiply || is_division);
|
2018-09-10 13:52:18 +00:00
|
|
|
if constexpr (IsDataTypeDecimal<RightDataType> && is_division)
|
2018-08-01 19:50:19 +00:00
|
|
|
scale_a = right.getScaleMultiplier();
|
2018-09-03 09:15:44 +00:00
|
|
|
|
|
|
|
OpImpl::constant_vector(col_left_const->template getValue<T0>(), col_right->getData(), vec_res,
|
|
|
|
scale_a, scale_b, check_decimal_overflow);
|
2018-08-01 19:50:19 +00:00
|
|
|
}
|
|
|
|
else
|
2018-08-27 07:57:42 +00:00
|
|
|
OpImpl::constant_vector(col_left_const->template getValue<T0>(), col_right->getData(), vec_res);
|
2018-08-01 19:50:19 +00:00
|
|
|
}
|
2018-04-29 13:01:14 +00:00
|
|
|
else
|
|
|
|
return false;
|
|
|
|
}
|
2018-08-01 19:50:19 +00:00
|
|
|
else if (auto col_left = checkAndGetColumn<ColVecT0>(col_left_raw))
|
2018-04-29 13:01:14 +00:00
|
|
|
{
|
2018-08-03 10:20:44 +00:00
|
|
|
if constexpr (result_is_decimal)
|
2018-08-01 19:50:19 +00:00
|
|
|
{
|
2018-08-02 14:28:55 +00:00
|
|
|
ResultDataType type = decimalResultType(left, right, is_multiply, is_division);
|
2018-08-10 14:57:55 +00:00
|
|
|
|
2018-08-02 14:28:55 +00:00
|
|
|
typename ResultDataType::FieldType scale_a = type.scaleFactorFor(left, is_multiply);
|
|
|
|
typename ResultDataType::FieldType scale_b = type.scaleFactorFor(right, is_multiply || is_division);
|
2018-09-10 13:52:18 +00:00
|
|
|
if constexpr (IsDataTypeDecimal<RightDataType> && is_division)
|
2018-08-01 19:50:19 +00:00
|
|
|
scale_a = right.getScaleMultiplier();
|
|
|
|
if (auto col_right = checkAndGetColumn<ColVecT1>(col_right_raw))
|
2018-08-22 13:22:56 +00:00
|
|
|
{
|
2018-09-03 09:15:44 +00:00
|
|
|
OpImpl::vector_vector(col_left->getData(), col_right->getData(), vec_res, scale_a, scale_b,
|
|
|
|
check_decimal_overflow);
|
2018-08-22 13:22:56 +00:00
|
|
|
}
|
2018-08-27 07:57:42 +00:00
|
|
|
else if (auto col_right_const = checkAndGetColumnConst<ColVecT1>(col_right_raw))
|
2018-08-22 13:22:56 +00:00
|
|
|
{
|
2018-09-03 09:15:44 +00:00
|
|
|
OpImpl::vector_constant(col_left->getData(), col_right_const->template getValue<T1>(), vec_res,
|
|
|
|
scale_a, scale_b, check_decimal_overflow);
|
2018-08-22 13:22:56 +00:00
|
|
|
}
|
2018-08-01 19:50:19 +00:00
|
|
|
else
|
|
|
|
return false;
|
|
|
|
}
|
2018-04-29 13:01:14 +00:00
|
|
|
else
|
2018-08-01 19:50:19 +00:00
|
|
|
{
|
|
|
|
if (auto col_right = checkAndGetColumn<ColVecT1>(col_right_raw))
|
|
|
|
OpImpl::vector_vector(col_left->getData(), col_right->getData(), vec_res);
|
2018-08-27 07:57:42 +00:00
|
|
|
else if (auto col_right_const = checkAndGetColumnConst<ColVecT1>(col_right_raw))
|
|
|
|
OpImpl::vector_constant(col_left->getData(), col_right_const->template getValue<T1>(), vec_res);
|
2018-08-01 19:50:19 +00:00
|
|
|
else
|
|
|
|
return false;
|
|
|
|
}
|
2018-04-29 13:01:14 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
return false;
|
2018-08-27 07:57:42 +00:00
|
|
|
|
2018-04-29 13:01:14 +00:00
|
|
|
block.getByPosition(result).column = std::move(col_res);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
});
|
|
|
|
if (!valid)
|
|
|
|
throw Exception(getName() + "'s arguments do not match the expected data types", ErrorCodes::LOGICAL_ERROR);
|
2017-04-01 07:20:54 +00:00
|
|
|
}
|
2018-04-29 22:43:02 +00:00
|
|
|
|
|
|
|
#if USE_EMBEDDED_COMPILER
|
|
|
|
bool isCompilableImpl(const DataTypes & arguments) const override
|
|
|
|
{
|
|
|
|
return castBothTypes(arguments[0].get(), arguments[1].get(), [&](const auto & left, const auto & right)
|
|
|
|
{
|
|
|
|
using LeftDataType = std::decay_t<decltype(left)>;
|
|
|
|
using RightDataType = std::decay_t<decltype(right)>;
|
|
|
|
using ResultDataType = typename BinaryOperationTraits<Op, LeftDataType, RightDataType>::ResultDataType;
|
|
|
|
using OpSpec = Op<typename LeftDataType::FieldType, typename RightDataType::FieldType>;
|
2018-09-10 13:52:18 +00:00
|
|
|
return !std::is_same_v<ResultDataType, InvalidType> && !IsDataTypeDecimal<ResultDataType> && OpSpec::compilable;
|
2018-04-29 22:43:02 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
llvm::Value * compileImpl(llvm::IRBuilderBase & builder, const DataTypes & types, ValuePlaceholders values) const override
|
|
|
|
{
|
|
|
|
llvm::Value * result = nullptr;
|
|
|
|
castBothTypes(types[0].get(), types[1].get(), [&](const auto & left, const auto & right)
|
|
|
|
{
|
|
|
|
using LeftDataType = std::decay_t<decltype(left)>;
|
|
|
|
using RightDataType = std::decay_t<decltype(right)>;
|
|
|
|
using ResultDataType = typename BinaryOperationTraits<Op, LeftDataType, RightDataType>::ResultDataType;
|
|
|
|
using OpSpec = Op<typename LeftDataType::FieldType, typename RightDataType::FieldType>;
|
2018-09-10 13:52:18 +00:00
|
|
|
if constexpr (!std::is_same_v<ResultDataType, InvalidType> && !IsDataTypeDecimal<ResultDataType> && OpSpec::compilable)
|
2018-04-29 22:43:02 +00:00
|
|
|
{
|
|
|
|
auto & b = static_cast<llvm::IRBuilder<> &>(builder);
|
2018-05-07 19:21:23 +00:00
|
|
|
auto type = std::make_shared<ResultDataType>();
|
|
|
|
auto * lval = nativeCast(b, types[0], values[0](), type);
|
|
|
|
auto * rval = nativeCast(b, types[1], values[1](), type);
|
2018-04-29 22:43:02 +00:00
|
|
|
result = OpSpec::compile(b, lval, rval, std::is_signed_v<typename ResultDataType::FieldType>);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
});
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
#endif
|
2018-07-19 19:12:48 +00:00
|
|
|
|
|
|
|
bool canBeExecutedOnDefaultArguments() const override { return CanBeExecutedOnDefaultArguments; }
|
2011-08-09 15:57:33 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2015-11-29 08:06:29 +00:00
|
|
|
template <typename FunctionName>
|
|
|
|
struct FunctionUnaryArithmeticMonotonicity;
|
|
|
|
|
|
|
|
|
2017-01-05 16:34:05 +00:00
|
|
|
template <template <typename> class Op, typename Name, bool is_injective>
|
2011-09-05 00:32:22 +00:00
|
|
|
class FunctionUnaryArithmetic : public IFunction
|
|
|
|
{
|
2018-08-07 14:35:21 +00:00
|
|
|
static constexpr bool allow_decimal = std::is_same_v<Op<Int8>, NegateImpl<Int8>> || std::is_same_v<Op<Int8>, AbsImpl<Int8>>;
|
2018-08-03 16:32:55 +00:00
|
|
|
|
2018-04-29 13:01:14 +00:00
|
|
|
template <typename F>
|
|
|
|
static bool castType(const IDataType * type, F && f)
|
2017-04-01 07:20:54 +00:00
|
|
|
{
|
2018-04-29 13:01:14 +00:00
|
|
|
return castTypeToEither<
|
|
|
|
DataTypeUInt8,
|
|
|
|
DataTypeUInt16,
|
|
|
|
DataTypeUInt32,
|
|
|
|
DataTypeUInt64,
|
|
|
|
DataTypeInt8,
|
|
|
|
DataTypeInt16,
|
|
|
|
DataTypeInt32,
|
|
|
|
DataTypeInt64,
|
|
|
|
DataTypeFloat32,
|
2018-08-03 16:32:55 +00:00
|
|
|
DataTypeFloat64,
|
2018-08-21 04:31:35 +00:00
|
|
|
DataTypeDecimal<Decimal32>,
|
|
|
|
DataTypeDecimal<Decimal64>,
|
|
|
|
DataTypeDecimal<Decimal128>
|
2018-04-29 13:01:14 +00:00
|
|
|
>(type, std::forward<F>(f));
|
2017-04-01 07:20:54 +00:00
|
|
|
}
|
2011-09-05 00:32:22 +00:00
|
|
|
|
|
|
|
public:
|
2018-04-29 13:01:14 +00:00
|
|
|
static constexpr auto name = Name::name;
|
|
|
|
static FunctionPtr create(const Context &) { return std::make_shared<FunctionUnaryArithmetic>(); }
|
|
|
|
|
2017-04-01 07:20:54 +00:00
|
|
|
String getName() const override
|
|
|
|
{
|
|
|
|
return name;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t getNumberOfArguments() const override { return 1; }
|
|
|
|
bool isInjective(const Block &) override { return is_injective; }
|
|
|
|
|
2017-07-24 04:14:35 +00:00
|
|
|
bool useDefaultImplementationForConstants() const override { return true; }
|
|
|
|
|
2017-04-01 07:20:54 +00:00
|
|
|
DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override
|
|
|
|
{
|
|
|
|
DataTypePtr result;
|
2018-04-29 13:01:14 +00:00
|
|
|
bool valid = castType(arguments[0].get(), [&](const auto & type)
|
|
|
|
{
|
2018-08-03 16:32:55 +00:00
|
|
|
using DataType = std::decay_t<decltype(type)>;
|
|
|
|
using T0 = typename DataType::FieldType;
|
|
|
|
|
2018-09-10 13:52:18 +00:00
|
|
|
if constexpr (IsDataTypeDecimal<DataType>)
|
2018-08-03 16:32:55 +00:00
|
|
|
{
|
|
|
|
if constexpr (!allow_decimal)
|
|
|
|
return false;
|
|
|
|
result = std::make_shared<DataType>(type.getPrecision(), type.getScale());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
result = std::make_shared<DataTypeNumber<typename Op<T0>::ResultType>>();
|
2018-04-29 13:01:14 +00:00
|
|
|
return true;
|
|
|
|
});
|
|
|
|
if (!valid)
|
2017-04-01 07:20:54 +00:00
|
|
|
throw Exception("Illegal type " + arguments[0]->getName() + " of argument of function " + getName(),
|
|
|
|
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2018-04-24 07:16:39 +00:00
|
|
|
void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t /*input_rows_count*/) override
|
2017-04-01 07:20:54 +00:00
|
|
|
{
|
2018-04-29 13:01:14 +00:00
|
|
|
bool valid = castType(block.getByPosition(arguments[0]).type.get(), [&](const auto & type)
|
|
|
|
{
|
2018-08-03 16:32:55 +00:00
|
|
|
using DataType = std::decay_t<decltype(type)>;
|
|
|
|
using T0 = typename DataType::FieldType;
|
|
|
|
|
2018-09-10 13:52:18 +00:00
|
|
|
if constexpr (IsDataTypeDecimal<DataType>)
|
2018-04-29 13:01:14 +00:00
|
|
|
{
|
2018-08-03 16:32:55 +00:00
|
|
|
if constexpr (allow_decimal)
|
|
|
|
{
|
2018-08-27 16:16:16 +00:00
|
|
|
if (auto col = checkAndGetColumn<ColumnDecimal<T0>>(block.getByPosition(arguments[0]).column.get()))
|
2018-08-03 16:32:55 +00:00
|
|
|
{
|
2018-08-27 16:16:16 +00:00
|
|
|
auto col_res = ColumnDecimal<typename Op<T0>::ResultType>::create(0, type.getScale());
|
2018-08-03 16:32:55 +00:00
|
|
|
auto & vec_res = col_res->getData();
|
|
|
|
vec_res.resize(col->getData().size());
|
|
|
|
UnaryOperationImpl<T0, Op<T0>>::vector(col->getData(), vec_res);
|
|
|
|
block.getByPosition(result).column = std::move(col_res);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
2018-04-29 13:01:14 +00:00
|
|
|
}
|
2018-08-03 16:32:55 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
if (auto col = checkAndGetColumn<ColumnVector<T0>>(block.getByPosition(arguments[0]).column.get()))
|
|
|
|
{
|
|
|
|
auto col_res = ColumnVector<typename Op<T0>::ResultType>::create();
|
|
|
|
auto & vec_res = col_res->getData();
|
|
|
|
vec_res.resize(col->getData().size());
|
|
|
|
UnaryOperationImpl<T0, Op<T0>>::vector(col->getData(), vec_res);
|
|
|
|
block.getByPosition(result).column = std::move(col_res);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-29 13:01:14 +00:00
|
|
|
return false;
|
|
|
|
});
|
|
|
|
if (!valid)
|
|
|
|
throw Exception(getName() + "'s argument does not match the expected data type", ErrorCodes::LOGICAL_ERROR);
|
2017-04-01 07:20:54 +00:00
|
|
|
}
|
|
|
|
|
2018-04-29 22:43:02 +00:00
|
|
|
#if USE_EMBEDDED_COMPILER
|
|
|
|
bool isCompilableImpl(const DataTypes & arguments) const override
|
|
|
|
{
|
|
|
|
return castType(arguments[0].get(), [&](const auto & type)
|
|
|
|
{
|
2018-08-15 13:27:22 +00:00
|
|
|
using DataType = std::decay_t<decltype(type)>;
|
2018-09-10 13:52:18 +00:00
|
|
|
return !IsDataTypeDecimal<DataType> && Op<typename DataType::FieldType>::compilable;
|
2018-04-29 22:43:02 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
llvm::Value * compileImpl(llvm::IRBuilderBase & builder, const DataTypes & types, ValuePlaceholders values) const override
|
|
|
|
{
|
|
|
|
llvm::Value * result = nullptr;
|
|
|
|
castType(types[0].get(), [&](const auto & type)
|
|
|
|
{
|
2018-08-15 13:27:22 +00:00
|
|
|
using DataType = std::decay_t<decltype(type)>;
|
|
|
|
using T0 = typename DataType::FieldType;
|
2018-04-29 22:43:02 +00:00
|
|
|
using T1 = typename Op<T0>::ResultType;
|
2018-09-10 14:26:47 +00:00
|
|
|
if constexpr (!std::is_same_v<T1, InvalidType> && !IsDataTypeDecimal<DataType> && Op<T0>::compilable)
|
2018-04-29 22:43:02 +00:00
|
|
|
{
|
|
|
|
auto & b = static_cast<llvm::IRBuilder<> &>(builder);
|
2018-05-07 19:21:23 +00:00
|
|
|
auto * v = nativeCast(b, types[0], values[0](), std::make_shared<DataTypeNumber<T1>>());
|
2018-04-29 22:43:02 +00:00
|
|
|
result = Op<T0>::compile(b, v, std::is_signed_v<T1>);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
});
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2017-04-01 07:20:54 +00:00
|
|
|
bool hasInformationAboutMonotonicity() const override
|
|
|
|
{
|
|
|
|
return FunctionUnaryArithmeticMonotonicity<Name>::has();
|
|
|
|
}
|
|
|
|
|
2017-12-01 21:40:58 +00:00
|
|
|
Monotonicity getMonotonicityForRange(const IDataType &, const Field & left, const Field & right) const override
|
2017-04-01 07:20:54 +00:00
|
|
|
{
|
|
|
|
return FunctionUnaryArithmeticMonotonicity<Name>::get(left, right);
|
|
|
|
}
|
2011-09-05 00:32:22 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2017-07-21 06:35:58 +00:00
|
|
|
struct NamePlus { static constexpr auto name = "plus"; };
|
|
|
|
struct NameMinus { static constexpr auto name = "minus"; };
|
|
|
|
struct NameMultiply { static constexpr auto name = "multiply"; };
|
|
|
|
struct NameDivideFloating { static constexpr auto name = "divide"; };
|
|
|
|
struct NameDivideIntegral { static constexpr auto name = "intDiv"; };
|
|
|
|
struct NameDivideIntegralOrZero { static constexpr auto name = "intDivOrZero"; };
|
|
|
|
struct NameModulo { static constexpr auto name = "modulo"; };
|
|
|
|
struct NameNegate { static constexpr auto name = "negate"; };
|
|
|
|
struct NameAbs { static constexpr auto name = "abs"; };
|
|
|
|
struct NameBitAnd { static constexpr auto name = "bitAnd"; };
|
|
|
|
struct NameBitOr { static constexpr auto name = "bitOr"; };
|
|
|
|
struct NameBitXor { static constexpr auto name = "bitXor"; };
|
|
|
|
struct NameBitNot { static constexpr auto name = "bitNot"; };
|
|
|
|
struct NameBitShiftLeft { static constexpr auto name = "bitShiftLeft"; };
|
|
|
|
struct NameBitShiftRight { static constexpr auto name = "bitShiftRight"; };
|
|
|
|
struct NameBitRotateLeft { static constexpr auto name = "bitRotateLeft"; };
|
|
|
|
struct NameBitRotateRight { static constexpr auto name = "bitRotateRight"; };
|
|
|
|
struct NameBitTest { static constexpr auto name = "bitTest"; };
|
|
|
|
struct NameBitTestAny { static constexpr auto name = "bitTestAny"; };
|
|
|
|
struct NameBitTestAll { static constexpr auto name = "bitTestAll"; };
|
|
|
|
struct NameLeast { static constexpr auto name = "least"; };
|
|
|
|
struct NameGreatest { static constexpr auto name = "greatest"; };
|
2017-11-22 09:25:36 +00:00
|
|
|
struct NameGCD { static constexpr auto name = "gcd"; };
|
|
|
|
struct NameLCM { static constexpr auto name = "lcm"; };
|
2017-12-21 23:46:34 +00:00
|
|
|
struct NameIntExp2 { static constexpr auto name = "intExp2"; };
|
|
|
|
struct NameIntExp10 { static constexpr auto name = "intExp10"; };
|
2013-12-17 13:49:21 +00:00
|
|
|
|
2017-01-05 16:34:05 +00:00
|
|
|
using FunctionPlus = FunctionBinaryArithmetic<PlusImpl, NamePlus>;
|
|
|
|
using FunctionMinus = FunctionBinaryArithmetic<MinusImpl, NameMinus>;
|
|
|
|
using FunctionMultiply = FunctionBinaryArithmetic<MultiplyImpl, NameMultiply>;
|
|
|
|
using FunctionDivideFloating = FunctionBinaryArithmetic<DivideFloatingImpl, NameDivideFloating>;
|
2018-07-19 19:12:48 +00:00
|
|
|
using FunctionDivideIntegral = FunctionBinaryArithmetic<DivideIntegralImpl, NameDivideIntegral, false>;
|
2016-05-28 10:35:44 +00:00
|
|
|
using FunctionDivideIntegralOrZero = FunctionBinaryArithmetic<DivideIntegralOrZeroImpl, NameDivideIntegralOrZero>;
|
2018-07-19 19:12:48 +00:00
|
|
|
using FunctionModulo = FunctionBinaryArithmetic<ModuloImpl, NameModulo, false>;
|
2017-01-05 16:34:05 +00:00
|
|
|
using FunctionNegate = FunctionUnaryArithmetic<NegateImpl, NameNegate, true>;
|
|
|
|
using FunctionAbs = FunctionUnaryArithmetic<AbsImpl, NameAbs, false>;
|
2017-07-21 06:35:58 +00:00
|
|
|
using FunctionBitAnd = FunctionBinaryArithmetic<BitAndImpl, NameBitAnd>;
|
2017-01-05 16:34:05 +00:00
|
|
|
using FunctionBitOr = FunctionBinaryArithmetic<BitOrImpl, NameBitOr>;
|
|
|
|
using FunctionBitXor = FunctionBinaryArithmetic<BitXorImpl, NameBitXor>;
|
|
|
|
using FunctionBitNot = FunctionUnaryArithmetic<BitNotImpl, NameBitNot, true>;
|
2017-07-21 06:35:58 +00:00
|
|
|
using FunctionBitShiftLeft = FunctionBinaryArithmetic<BitShiftLeftImpl, NameBitShiftLeft>;
|
2017-01-05 16:34:05 +00:00
|
|
|
using FunctionBitShiftRight = FunctionBinaryArithmetic<BitShiftRightImpl, NameBitShiftRight>;
|
2017-07-21 06:35:58 +00:00
|
|
|
using FunctionBitRotateLeft = FunctionBinaryArithmetic<BitRotateLeftImpl, NameBitRotateLeft>;
|
2017-03-20 19:16:02 +00:00
|
|
|
using FunctionBitRotateRight = FunctionBinaryArithmetic<BitRotateRightImpl, NameBitRotateRight>;
|
2017-07-21 06:35:58 +00:00
|
|
|
using FunctionBitTest = FunctionBinaryArithmetic<BitTestImpl, NameBitTest>;
|
2017-01-05 16:34:05 +00:00
|
|
|
using FunctionLeast = FunctionBinaryArithmetic<LeastImpl, NameLeast>;
|
|
|
|
using FunctionGreatest = FunctionBinaryArithmetic<GreatestImpl, NameGreatest>;
|
2018-07-19 19:12:48 +00:00
|
|
|
using FunctionGCD = FunctionBinaryArithmetic<GCDImpl, NameGCD, false>;
|
|
|
|
using FunctionLCM = FunctionBinaryArithmetic<LCMImpl, NameLCM, false>;
|
2017-12-21 23:46:34 +00:00
|
|
|
/// Assumed to be injective for the purpose of query optimization, but in fact it is not injective because of possible overflow.
|
|
|
|
using FunctionIntExp2 = FunctionUnaryArithmetic<IntExp2Impl, NameIntExp2, true>;
|
|
|
|
using FunctionIntExp10 = FunctionUnaryArithmetic<IntExp10Impl, NameIntExp10, true>;
|
|
|
|
|
2011-08-09 15:57:33 +00:00
|
|
|
|
2017-05-13 22:19:04 +00:00
|
|
|
/// Monotonicity properties for some functions.
|
2015-11-29 08:06:29 +00:00
|
|
|
|
|
|
|
template <> struct FunctionUnaryArithmeticMonotonicity<NameNegate>
|
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
static bool has() { return true; }
|
2017-12-01 21:40:58 +00:00
|
|
|
static IFunction::Monotonicity get(const Field &, const Field &)
|
2017-04-01 07:20:54 +00:00
|
|
|
{
|
|
|
|
return { true, false };
|
|
|
|
}
|
2015-11-29 08:06:29 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
template <> struct FunctionUnaryArithmeticMonotonicity<NameAbs>
|
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
static bool has() { return true; }
|
|
|
|
static IFunction::Monotonicity get(const Field & left, const Field & right)
|
|
|
|
{
|
|
|
|
Float64 left_float = left.isNull() ? -std::numeric_limits<Float64>::infinity() : applyVisitor(FieldVisitorConvertToNumber<Float64>(), left);
|
|
|
|
Float64 right_float = right.isNull() ? std::numeric_limits<Float64>::infinity() : applyVisitor(FieldVisitorConvertToNumber<Float64>(), right);
|
2015-11-29 08:06:29 +00:00
|
|
|
|
2017-04-01 07:20:54 +00:00
|
|
|
if ((left_float < 0 && right_float > 0) || (left_float > 0 && right_float < 0))
|
|
|
|
return {};
|
2015-11-29 08:06:29 +00:00
|
|
|
|
2017-04-01 07:20:54 +00:00
|
|
|
return { true, (left_float > 0) };
|
|
|
|
}
|
2015-11-29 08:06:29 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
template <> struct FunctionUnaryArithmeticMonotonicity<NameBitNot>
|
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
static bool has() { return false; }
|
2017-12-01 21:40:58 +00:00
|
|
|
static IFunction::Monotonicity get(const Field &, const Field &)
|
2017-04-01 07:20:54 +00:00
|
|
|
{
|
|
|
|
return {};
|
|
|
|
}
|
2015-11-29 08:06:29 +00:00
|
|
|
};
|
|
|
|
|
2017-12-21 23:46:34 +00:00
|
|
|
template <> struct FunctionUnaryArithmeticMonotonicity<NameIntExp2>
|
|
|
|
{
|
|
|
|
static bool has() { return true; }
|
|
|
|
static IFunction::Monotonicity get(const Field & left, const Field & right)
|
|
|
|
{
|
|
|
|
Float64 left_float = left.isNull() ? -std::numeric_limits<Float64>::infinity() : applyVisitor(FieldVisitorConvertToNumber<Float64>(), left);
|
|
|
|
Float64 right_float = right.isNull() ? std::numeric_limits<Float64>::infinity() : applyVisitor(FieldVisitorConvertToNumber<Float64>(), right);
|
|
|
|
|
|
|
|
if (left_float < 0 || right_float > 63)
|
|
|
|
return {};
|
|
|
|
|
|
|
|
return { true };
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
template <> struct FunctionUnaryArithmeticMonotonicity<NameIntExp10>
|
|
|
|
{
|
|
|
|
static bool has() { return true; }
|
|
|
|
static IFunction::Monotonicity get(const Field & left, const Field & right)
|
|
|
|
{
|
|
|
|
Float64 left_float = left.isNull() ? -std::numeric_limits<Float64>::infinity() : applyVisitor(FieldVisitorConvertToNumber<Float64>(), left);
|
|
|
|
Float64 right_float = right.isNull() ? std::numeric_limits<Float64>::infinity() : applyVisitor(FieldVisitorConvertToNumber<Float64>(), right);
|
|
|
|
|
|
|
|
if (left_float < 0 || right_float > 19)
|
|
|
|
return {};
|
|
|
|
|
|
|
|
return { true };
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2017-03-12 11:09:25 +00:00
|
|
|
}
|
2011-08-09 15:57:33 +00:00
|
|
|
|
2017-05-13 22:19:04 +00:00
|
|
|
/// Optimizations for integer division by a constant.
|
2014-08-17 02:13:40 +00:00
|
|
|
|
2017-01-27 19:55:33 +00:00
|
|
|
#if __SSE2__
|
2017-04-01 07:20:54 +00:00
|
|
|
#define LIBDIVIDE_USE_SSE2 1
|
2016-01-13 21:05:11 +00:00
|
|
|
#endif
|
|
|
|
|
2014-08-17 02:13:40 +00:00
|
|
|
#include <libdivide.h>
|
|
|
|
|
2017-03-12 11:09:25 +00:00
|
|
|
namespace DB
|
|
|
|
{
|
2014-08-17 02:13:40 +00:00
|
|
|
|
|
|
|
template <typename A, typename B>
|
|
|
|
struct DivideIntegralByConstantImpl
|
2017-04-01 07:20:54 +00:00
|
|
|
: BinaryOperationImplBase<A, B, DivideIntegralImpl<A, B>>
|
2014-08-17 02:13:40 +00:00
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
using ResultType = typename DivideIntegralImpl<A, B>::ResultType;
|
2014-08-17 02:13:40 +00:00
|
|
|
|
2017-04-01 07:20:54 +00:00
|
|
|
static void vector_constant(const PaddedPODArray<A> & a, B b, PaddedPODArray<ResultType> & c)
|
|
|
|
{
|
|
|
|
if (unlikely(b == 0))
|
|
|
|
throw Exception("Division by zero", ErrorCodes::ILLEGAL_DIVISION);
|
2014-08-17 02:13:40 +00:00
|
|
|
|
|
|
|
#pragma GCC diagnostic push
|
|
|
|
#pragma GCC diagnostic ignored "-Wsign-compare"
|
|
|
|
|
2017-12-25 04:01:46 +00:00
|
|
|
if (unlikely(std::is_signed_v<B> && b == -1))
|
2017-04-01 07:20:54 +00:00
|
|
|
{
|
|
|
|
size_t size = a.size();
|
|
|
|
for (size_t i = 0; i < size; ++i)
|
|
|
|
c[i] = -c[i];
|
|
|
|
return;
|
|
|
|
}
|
2014-08-17 02:13:40 +00:00
|
|
|
|
|
|
|
#pragma GCC diagnostic pop
|
|
|
|
|
2017-04-01 07:20:54 +00:00
|
|
|
libdivide::divider<A> divider(b);
|
2014-08-17 02:13:40 +00:00
|
|
|
|
2017-04-01 07:20:54 +00:00
|
|
|
size_t size = a.size();
|
2018-09-02 03:00:04 +00:00
|
|
|
const A * a_pos = a.data();
|
2017-04-01 07:20:54 +00:00
|
|
|
const A * a_end = a_pos + size;
|
2018-09-02 03:00:04 +00:00
|
|
|
ResultType * c_pos = c.data();
|
2016-01-13 21:05:11 +00:00
|
|
|
|
2017-01-27 19:55:33 +00:00
|
|
|
#if __SSE2__
|
2017-04-01 07:20:54 +00:00
|
|
|
static constexpr size_t values_per_sse_register = 16 / sizeof(A);
|
|
|
|
const A * a_end_sse = a_pos + size / values_per_sse_register * values_per_sse_register;
|
2014-08-17 02:13:40 +00:00
|
|
|
|
2017-04-01 07:20:54 +00:00
|
|
|
while (a_pos < a_end_sse)
|
|
|
|
{
|
|
|
|
_mm_storeu_si128(reinterpret_cast<__m128i *>(c_pos),
|
|
|
|
_mm_loadu_si128(reinterpret_cast<const __m128i *>(a_pos)) / divider);
|
2014-08-17 02:13:40 +00:00
|
|
|
|
2017-04-01 07:20:54 +00:00
|
|
|
a_pos += values_per_sse_register;
|
|
|
|
c_pos += values_per_sse_register;
|
|
|
|
}
|
2016-01-13 21:05:11 +00:00
|
|
|
#endif
|
2014-08-17 02:13:40 +00:00
|
|
|
|
2017-04-01 07:20:54 +00:00
|
|
|
while (a_pos < a_end)
|
|
|
|
{
|
|
|
|
*c_pos = *a_pos / divider;
|
|
|
|
++a_pos;
|
|
|
|
++c_pos;
|
|
|
|
}
|
|
|
|
}
|
2014-08-17 02:13:40 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
template <typename A, typename B>
|
|
|
|
struct ModuloByConstantImpl
|
2017-04-01 07:20:54 +00:00
|
|
|
: BinaryOperationImplBase<A, B, ModuloImpl<A, B>>
|
2014-08-17 02:13:40 +00:00
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
using ResultType = typename ModuloImpl<A, B>::ResultType;
|
2014-08-17 02:13:40 +00:00
|
|
|
|
2017-04-01 07:20:54 +00:00
|
|
|
static void vector_constant(const PaddedPODArray<A> & a, B b, PaddedPODArray<ResultType> & c)
|
|
|
|
{
|
|
|
|
if (unlikely(b == 0))
|
|
|
|
throw Exception("Division by zero", ErrorCodes::ILLEGAL_DIVISION);
|
2014-08-17 02:13:40 +00:00
|
|
|
|
|
|
|
#pragma GCC diagnostic push
|
|
|
|
#pragma GCC diagnostic ignored "-Wsign-compare"
|
|
|
|
|
2017-12-25 04:01:46 +00:00
|
|
|
if (unlikely((std::is_signed_v<B> && b == -1) || b == 1))
|
2017-04-01 07:20:54 +00:00
|
|
|
{
|
|
|
|
size_t size = a.size();
|
|
|
|
for (size_t i = 0; i < size; ++i)
|
|
|
|
c[i] = 0;
|
|
|
|
return;
|
|
|
|
}
|
2014-08-17 02:13:40 +00:00
|
|
|
|
|
|
|
#pragma GCC diagnostic pop
|
|
|
|
|
2017-04-01 07:20:54 +00:00
|
|
|
libdivide::divider<A> divider(b);
|
2014-08-17 02:13:40 +00:00
|
|
|
|
2017-05-13 22:19:04 +00:00
|
|
|
/// Here we failed to make the SSE variant from libdivide give an advantage.
|
2017-04-01 07:20:54 +00:00
|
|
|
size_t size = a.size();
|
|
|
|
for (size_t i = 0; i < size; ++i)
|
2017-07-21 06:35:58 +00:00
|
|
|
c[i] = a[i] - (a[i] / divider) * b; /// NOTE: perhaps, the division semantics with the remainder of negative numbers is not preserved.
|
2017-04-01 07:20:54 +00:00
|
|
|
}
|
2014-08-17 02:13:40 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2017-05-13 22:19:04 +00:00
|
|
|
/** Specializations are specified for dividing numbers of the type UInt64 and UInt32 by the numbers of the same sign.
|
|
|
|
* Can be expanded to all possible combinations, but more code is needed.
|
2014-08-17 02:13:40 +00:00
|
|
|
*/
|
|
|
|
|
2017-07-21 06:35:58 +00:00
|
|
|
template <> struct BinaryOperationImpl<UInt64, UInt8, DivideIntegralImpl<UInt64, UInt8>> : DivideIntegralByConstantImpl<UInt64, UInt8> {};
|
|
|
|
template <> struct BinaryOperationImpl<UInt64, UInt16, DivideIntegralImpl<UInt64, UInt16>> : DivideIntegralByConstantImpl<UInt64, UInt16> {};
|
|
|
|
template <> struct BinaryOperationImpl<UInt64, UInt32, DivideIntegralImpl<UInt64, UInt32>> : DivideIntegralByConstantImpl<UInt64, UInt32> {};
|
|
|
|
template <> struct BinaryOperationImpl<UInt64, UInt64, DivideIntegralImpl<UInt64, UInt64>> : DivideIntegralByConstantImpl<UInt64, UInt64> {};
|
|
|
|
|
|
|
|
template <> struct BinaryOperationImpl<UInt32, UInt8, DivideIntegralImpl<UInt32, UInt8>> : DivideIntegralByConstantImpl<UInt32, UInt8> {};
|
|
|
|
template <> struct BinaryOperationImpl<UInt32, UInt16, DivideIntegralImpl<UInt32, UInt16>> : DivideIntegralByConstantImpl<UInt32, UInt16> {};
|
|
|
|
template <> struct BinaryOperationImpl<UInt32, UInt32, DivideIntegralImpl<UInt32, UInt32>> : DivideIntegralByConstantImpl<UInt32, UInt32> {};
|
|
|
|
template <> struct BinaryOperationImpl<UInt32, UInt64, DivideIntegralImpl<UInt32, UInt64>> : DivideIntegralByConstantImpl<UInt32, UInt64> {};
|
|
|
|
|
|
|
|
template <> struct BinaryOperationImpl<Int64, Int8, DivideIntegralImpl<Int64, Int8>> : DivideIntegralByConstantImpl<Int64, Int8> {};
|
|
|
|
template <> struct BinaryOperationImpl<Int64, Int16, DivideIntegralImpl<Int64, Int16>> : DivideIntegralByConstantImpl<Int64, Int16> {};
|
|
|
|
template <> struct BinaryOperationImpl<Int64, Int32, DivideIntegralImpl<Int64, Int32>> : DivideIntegralByConstantImpl<Int64, Int32> {};
|
|
|
|
template <> struct BinaryOperationImpl<Int64, Int64, DivideIntegralImpl<Int64, Int64>> : DivideIntegralByConstantImpl<Int64, Int64> {};
|
|
|
|
|
|
|
|
template <> struct BinaryOperationImpl<Int32, Int8, DivideIntegralImpl<Int32, Int8>> : DivideIntegralByConstantImpl<Int32, Int8> {};
|
|
|
|
template <> struct BinaryOperationImpl<Int32, Int16, DivideIntegralImpl<Int32, Int16>> : DivideIntegralByConstantImpl<Int32, Int16> {};
|
|
|
|
template <> struct BinaryOperationImpl<Int32, Int32, DivideIntegralImpl<Int32, Int32>> : DivideIntegralByConstantImpl<Int32, Int32> {};
|
|
|
|
template <> struct BinaryOperationImpl<Int32, Int64, DivideIntegralImpl<Int32, Int64>> : DivideIntegralByConstantImpl<Int32, Int64> {};
|
|
|
|
|
|
|
|
|
|
|
|
template <> struct BinaryOperationImpl<UInt64, UInt8, ModuloImpl<UInt64, UInt8>> : ModuloByConstantImpl<UInt64, UInt8> {};
|
|
|
|
template <> struct BinaryOperationImpl<UInt64, UInt16, ModuloImpl<UInt64, UInt16>> : ModuloByConstantImpl<UInt64, UInt16> {};
|
|
|
|
template <> struct BinaryOperationImpl<UInt64, UInt32, ModuloImpl<UInt64, UInt32>> : ModuloByConstantImpl<UInt64, UInt32> {};
|
|
|
|
template <> struct BinaryOperationImpl<UInt64, UInt64, ModuloImpl<UInt64, UInt64>> : ModuloByConstantImpl<UInt64, UInt64> {};
|
|
|
|
|
|
|
|
template <> struct BinaryOperationImpl<UInt32, UInt8, ModuloImpl<UInt32, UInt8>> : ModuloByConstantImpl<UInt32, UInt8> {};
|
|
|
|
template <> struct BinaryOperationImpl<UInt32, UInt16, ModuloImpl<UInt32, UInt16>> : ModuloByConstantImpl<UInt32, UInt16> {};
|
|
|
|
template <> struct BinaryOperationImpl<UInt32, UInt32, ModuloImpl<UInt32, UInt32>> : ModuloByConstantImpl<UInt32, UInt32> {};
|
|
|
|
template <> struct BinaryOperationImpl<UInt32, UInt64, ModuloImpl<UInt32, UInt64>> : ModuloByConstantImpl<UInt32, UInt64> {};
|
|
|
|
|
|
|
|
template <> struct BinaryOperationImpl<Int64, Int8, ModuloImpl<Int64, Int8>> : ModuloByConstantImpl<Int64, Int8> {};
|
|
|
|
template <> struct BinaryOperationImpl<Int64, Int16, ModuloImpl<Int64, Int16>> : ModuloByConstantImpl<Int64, Int16> {};
|
|
|
|
template <> struct BinaryOperationImpl<Int64, Int32, ModuloImpl<Int64, Int32>> : ModuloByConstantImpl<Int64, Int32> {};
|
|
|
|
template <> struct BinaryOperationImpl<Int64, Int64, ModuloImpl<Int64, Int64>> : ModuloByConstantImpl<Int64, Int64> {};
|
|
|
|
|
|
|
|
template <> struct BinaryOperationImpl<Int32, Int8, ModuloImpl<Int32, Int8>> : ModuloByConstantImpl<Int32, Int8> {};
|
|
|
|
template <> struct BinaryOperationImpl<Int32, Int16, ModuloImpl<Int32, Int16>> : ModuloByConstantImpl<Int32, Int16> {};
|
|
|
|
template <> struct BinaryOperationImpl<Int32, Int32, ModuloImpl<Int32, Int32>> : ModuloByConstantImpl<Int32, Int32> {};
|
|
|
|
template <> struct BinaryOperationImpl<Int32, Int64, ModuloImpl<Int32, Int64>> : ModuloByConstantImpl<Int32, Int64> {};
|
|
|
|
|
|
|
|
|
|
|
|
template <typename Impl, typename Name>
|
|
|
|
struct FunctionBitTestMany : public IFunction
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
static constexpr auto name = Name::name;
|
|
|
|
static FunctionPtr create(const Context &) { return std::make_shared<FunctionBitTestMany>(); }
|
|
|
|
|
|
|
|
String getName() const override { return name; }
|
|
|
|
|
|
|
|
bool isVariadic() const override { return true; }
|
|
|
|
size_t getNumberOfArguments() const override { return 0; }
|
|
|
|
|
|
|
|
DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override
|
|
|
|
{
|
|
|
|
if (arguments.size() < 2)
|
2018-05-07 02:01:11 +00:00
|
|
|
throw Exception{"Number of arguments for function " + getName() + " doesn't match: passed "
|
|
|
|
+ toString(arguments.size()) + ", should be at least 2.", ErrorCodes::TOO_LESS_ARGUMENTS_FOR_FUNCTION};
|
2017-07-21 06:35:58 +00:00
|
|
|
|
2018-09-07 14:37:26 +00:00
|
|
|
const auto & first_arg = arguments.front();
|
2017-07-23 08:40:43 +00:00
|
|
|
|
2018-09-07 14:37:26 +00:00
|
|
|
if (!isInteger(first_arg))
|
2018-05-07 02:01:11 +00:00
|
|
|
throw Exception{"Illegal type " + first_arg->getName() + " of first argument of function " + getName(), ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT};
|
2017-07-21 06:35:58 +00:00
|
|
|
|
|
|
|
|
|
|
|
for (const auto i : ext::range(1, arguments.size()))
|
|
|
|
{
|
2018-09-07 14:37:26 +00:00
|
|
|
const auto & pos_arg = arguments[i];
|
2017-07-21 06:35:58 +00:00
|
|
|
|
2018-09-07 14:37:26 +00:00
|
|
|
if (!isUnsignedInteger(pos_arg))
|
2018-05-07 02:01:11 +00:00
|
|
|
throw Exception{"Illegal type " + pos_arg->getName() + " of " + toString(i) + " argument of function " + getName(), ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT};
|
2017-07-21 06:35:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return std::make_shared<DataTypeUInt8>();
|
|
|
|
}
|
|
|
|
|
2018-04-24 07:16:39 +00:00
|
|
|
void executeImpl(Block & block , const ColumnNumbers & arguments, size_t result, size_t /*input_rows_count*/) override
|
2017-07-21 06:35:58 +00:00
|
|
|
{
|
|
|
|
const auto value_col = block.getByPosition(arguments.front()).column.get();
|
|
|
|
|
2017-07-24 04:14:35 +00:00
|
|
|
if (!execute<UInt8>(block, arguments, result, value_col)
|
|
|
|
&& !execute<UInt16>(block, arguments, result, value_col)
|
|
|
|
&& !execute<UInt32>(block, arguments, result, value_col)
|
|
|
|
&& !execute<UInt64>(block, arguments, result, value_col)
|
|
|
|
&& !execute<Int8>(block, arguments, result, value_col)
|
|
|
|
&& !execute<Int16>(block, arguments, result, value_col)
|
|
|
|
&& !execute<Int32>(block, arguments, result, value_col)
|
|
|
|
&& !execute<Int64>(block, arguments, result, value_col))
|
2018-05-07 02:01:11 +00:00
|
|
|
throw Exception{"Illegal column " + value_col->getName() + " of argument of function " + getName(), ErrorCodes::ILLEGAL_COLUMN};
|
2017-07-21 06:35:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
template <typename T>
|
|
|
|
bool execute(
|
|
|
|
Block & block, const ColumnNumbers & arguments, const size_t result,
|
|
|
|
const IColumn * const value_col_untyped)
|
|
|
|
{
|
|
|
|
if (const auto value_col = checkAndGetColumn<ColumnVector<T>>(value_col_untyped))
|
|
|
|
{
|
|
|
|
const auto size = value_col->size();
|
|
|
|
bool is_const;
|
2018-08-27 07:57:42 +00:00
|
|
|
const auto const_mask = createConstMaskIfConst<T>(block, arguments, is_const);
|
2017-07-21 06:35:58 +00:00
|
|
|
const auto & val = value_col->getData();
|
|
|
|
|
2017-12-16 06:02:54 +00:00
|
|
|
auto out_col = ColumnVector<UInt8>::create(size);
|
2017-07-21 06:35:58 +00:00
|
|
|
auto & out = out_col->getData();
|
|
|
|
|
|
|
|
if (is_const)
|
|
|
|
{
|
|
|
|
for (const auto i : ext::range(0, size))
|
2018-08-27 07:57:42 +00:00
|
|
|
out[i] = Impl::apply(val[i], const_mask);
|
2017-07-21 06:35:58 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
const auto mask = createMask<T>(size, block, arguments);
|
|
|
|
|
|
|
|
for (const auto i : ext::range(0, size))
|
|
|
|
out[i] = Impl::apply(val[i], mask[i]);
|
|
|
|
}
|
|
|
|
|
2017-12-16 05:21:04 +00:00
|
|
|
block.getByPosition(result).column = std::move(out_col);
|
2017-07-21 06:35:58 +00:00
|
|
|
return true;
|
|
|
|
}
|
2018-08-27 16:31:45 +00:00
|
|
|
else if (const auto value_col_const = checkAndGetColumnConst<ColumnVector<T>>(value_col_untyped))
|
2017-07-21 06:35:58 +00:00
|
|
|
{
|
2018-08-27 16:31:45 +00:00
|
|
|
const auto size = value_col_const->size();
|
2017-07-21 06:35:58 +00:00
|
|
|
bool is_const;
|
2018-08-27 07:57:42 +00:00
|
|
|
const auto const_mask = createConstMaskIfConst<T>(block, arguments, is_const);
|
2018-08-27 16:31:45 +00:00
|
|
|
const auto val = value_col_const->template getValue<T>();
|
2017-07-21 06:35:58 +00:00
|
|
|
|
|
|
|
if (is_const)
|
|
|
|
{
|
2018-08-27 07:57:42 +00:00
|
|
|
block.getByPosition(result).column = block.getByPosition(result).type->createColumnConst(size, toField(Impl::apply(val, const_mask)));
|
2017-07-21 06:35:58 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
const auto mask = createMask<T>(size, block, arguments);
|
2017-12-16 06:02:54 +00:00
|
|
|
auto out_col = ColumnVector<UInt8>::create(size);
|
2017-07-21 06:35:58 +00:00
|
|
|
|
|
|
|
auto & out = out_col->getData();
|
|
|
|
|
|
|
|
for (const auto i : ext::range(0, size))
|
|
|
|
out[i] = Impl::apply(val, mask[i]);
|
|
|
|
|
2017-12-16 05:21:04 +00:00
|
|
|
block.getByPosition(result).column = std::move(out_col);
|
2017-07-21 06:35:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename ValueType>
|
2018-08-27 07:57:42 +00:00
|
|
|
ValueType createConstMaskIfConst(const Block & block, const ColumnNumbers & arguments, bool & out_is_const)
|
2017-07-21 06:35:58 +00:00
|
|
|
{
|
2018-08-27 07:57:42 +00:00
|
|
|
out_is_const = true;
|
2017-07-21 06:35:58 +00:00
|
|
|
ValueType mask = 0;
|
|
|
|
|
|
|
|
for (const auto i : ext::range(1, arguments.size()))
|
|
|
|
{
|
|
|
|
if (auto pos_col_const = checkAndGetColumnConst<ColumnVector<ValueType>>(block.getByPosition(arguments[i]).column.get()))
|
|
|
|
{
|
|
|
|
const auto pos = pos_col_const->template getValue<ValueType>();
|
|
|
|
mask = mask | (1 << pos);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2018-08-27 07:57:42 +00:00
|
|
|
out_is_const = false;
|
2017-07-21 06:35:58 +00:00
|
|
|
return {};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return mask;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename ValueType>
|
|
|
|
PaddedPODArray<ValueType> createMask(const size_t size, const Block & block, const ColumnNumbers & arguments)
|
|
|
|
{
|
|
|
|
PaddedPODArray<ValueType> mask(size, ValueType{});
|
|
|
|
|
|
|
|
for (const auto i : ext::range(1, arguments.size()))
|
|
|
|
{
|
|
|
|
const auto pos_col = block.getByPosition(arguments[i]).column.get();
|
|
|
|
|
|
|
|
if (!addToMaskImpl<UInt8>(mask, pos_col)
|
|
|
|
&& !addToMaskImpl<UInt16>(mask, pos_col)
|
|
|
|
&& !addToMaskImpl<UInt32>(mask, pos_col)
|
|
|
|
&& !addToMaskImpl<UInt64>(mask, pos_col))
|
2018-05-07 02:01:11 +00:00
|
|
|
throw Exception{"Illegal column " + pos_col->getName() + " of argument of function " + getName(), ErrorCodes::ILLEGAL_COLUMN};
|
2017-07-21 06:35:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return mask;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename PosType, typename ValueType>
|
|
|
|
bool addToMaskImpl(PaddedPODArray<ValueType> & mask, const IColumn * const pos_col_untyped)
|
|
|
|
{
|
|
|
|
if (const auto pos_col = checkAndGetColumn<ColumnVector<PosType>>(pos_col_untyped))
|
|
|
|
{
|
|
|
|
const auto & pos = pos_col->getData();
|
|
|
|
|
|
|
|
for (const auto i : ext::range(0, mask.size()))
|
|
|
|
mask[i] = mask[i] | (1 << pos[i]);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
2018-08-27 07:57:42 +00:00
|
|
|
else if (const auto pos_col_const = checkAndGetColumnConst<ColumnVector<PosType>>(pos_col_untyped))
|
2017-07-21 06:35:58 +00:00
|
|
|
{
|
2018-08-27 07:57:42 +00:00
|
|
|
const auto & pos = pos_col_const->template getValue<PosType>();
|
2017-07-21 06:35:58 +00:00
|
|
|
const auto new_mask = 1 << pos;
|
|
|
|
|
|
|
|
for (const auto i : ext::range(0, mask.size()))
|
|
|
|
mask[i] = mask[i] | new_mask;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
struct BitTestAnyImpl
|
|
|
|
{
|
|
|
|
template <typename A, typename B>
|
2018-06-03 20:39:06 +00:00
|
|
|
static inline UInt8 apply(A a, B b) { return (a & b) != 0; }
|
2017-07-21 06:35:58 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
struct BitTestAllImpl
|
|
|
|
{
|
|
|
|
template <typename A, typename B>
|
2018-06-03 20:39:06 +00:00
|
|
|
static inline UInt8 apply(A a, B b) { return (a & b) == b; }
|
2017-07-21 06:35:58 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
using FunctionBitTestAny = FunctionBitTestMany<BitTestAnyImpl, NameBitTestAny>;
|
|
|
|
using FunctionBitTestAll = FunctionBitTestMany<BitTestAllImpl, NameBitTestAll>;
|
2014-08-17 02:13:40 +00:00
|
|
|
|
2011-08-09 15:57:33 +00:00
|
|
|
}
|