// Copyright 2023 PingCAP, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace DB { namespace ErrorCodes { extern const int ILLEGAL_DIVISION; extern const int ILLEGAL_COLUMN; extern const int LOGICAL_ERROR; extern const int TOO_LESS_ARGUMENTS_FOR_FUNCTION; extern const int DECIMAL_OVERFLOW; } // namespace ErrorCodes // /// this one is just for convenience template using If = std::conditional_t; /** Arithmetic operations: +, -, *, /, %, * intDiv (integer division). * Bitwise operations: |, &, ^, ~. * Etc. */ template struct BinaryOperationImplBase { using ResultType = OpResultType; using ColVecA = std::conditional_t, ColumnDecimal, ColumnVector>; using ColVecB = std::conditional_t, ColumnDecimal, ColumnVector>; using ArrayA = typename ColVecA::Container; using ArrayB = typename ColVecB::Container; static void NO_INLINE vectorVector(const ArrayA & a, const ArrayB & b, PaddedPODArray & c) { size_t size = a.size(); for (size_t i = 0; i < size; ++i) if constexpr (IsDecimal && IsDecimal) c[i] = Op::template apply( DecimalField(a[i], a.getScale()), DecimalField(b[i], b.getScale())); else if constexpr (IsDecimal) c[i] = Op::template apply(DecimalField(a[i], a.getScale()), b[i]); else if constexpr (IsDecimal) c[i] = Op::template apply(a[i], DecimalField(b[i], b.getScale())); else c[i] = Op::template apply(a[i], b[i]); } static void NO_INLINE vectorVectorNullable( const ArrayA & a, const ColumnUInt8 * a_nullmap, const ArrayB & b, const ColumnUInt8 * b_nullmap, PaddedPODArray & c, typename ColumnUInt8::Container & res_null) { size_t size = a.size(); if (a_nullmap != nullptr && b_nullmap != nullptr) { const auto & a_nullmap_data = a_nullmap->getData(); const auto & b_nullmap_data = b_nullmap->getData(); for (size_t i = 0; i < size; i++) res_null[i] = a_nullmap_data[i] || b_nullmap_data[i]; } else if (a_nullmap != nullptr || b_nullmap != nullptr) { const auto & nullmap_data = a_nullmap != nullptr ? a_nullmap->getData() : b_nullmap->getData(); for (size_t i = 0; i < size; i++) res_null[i] = nullmap_data[i]; } for (size_t i = 0; i < size; ++i) { if constexpr (IsDecimal && IsDecimal) c[i] = Op::template apply( DecimalField(a[i], a.getScale()), DecimalField(b[i], b.getScale()), res_null[i]); else if constexpr (IsDecimal) c[i] = Op::template apply(DecimalField(a[i], a.getScale()), b[i], res_null[i]); else if constexpr (IsDecimal) c[i] = Op::template apply(a[i], DecimalField(b[i], b.getScale()), res_null[i]); else c[i] = Op::template apply(a[i], b[i], res_null[i]); } } static void NO_INLINE vectorConstant(const ArrayA & a, typename NearestFieldType::Type b, PaddedPODArray & c) { size_t size = a.size(); for (size_t i = 0; i < size; ++i) if constexpr (IsDecimal) c[i] = Op::template apply(DecimalField(a[i], a.getScale()), b); else c[i] = Op::template apply(a[i], b); } static void NO_INLINE vectorConstantNullable( const ArrayA & a, const ColumnUInt8 * a_nullmap, typename NearestFieldType::Type b, PaddedPODArray & c, typename ColumnUInt8::Container & res_null) { size_t size = a.size(); if (a_nullmap != nullptr) { const auto & nullmap_data = a_nullmap->getData(); for (size_t i = 0; i < size; ++i) res_null[i] = nullmap_data[i]; } for (size_t i = 0; i < size; ++i) if constexpr (IsDecimal) c[i] = Op::template apply(DecimalField(a[i], a.getScale()), b, res_null[i]); else c[i] = Op::template apply(a[i], b, res_null[i]); } static void NO_INLINE constantVector(typename NearestFieldType::Type a, const ArrayB & b, PaddedPODArray & c) { size_t size = b.size(); for (size_t i = 0; i < size; ++i) { if constexpr (IsDecimal) c[i] = Op::template apply(a, DecimalField(b[i], b.getScale())); else c[i] = Op::template apply(a, b[i]); } } static void NO_INLINE constantVectorNullable( typename NearestFieldType::Type a, const ArrayB & b, const ColumnUInt8 * b_nullmap, PaddedPODArray & c, typename ColumnUInt8::Container & res_null) { size_t size = b.size(); if (b_nullmap != nullptr) { const auto & nullmap_data = b_nullmap->getData(); for (size_t i = 0; i < size; i++) res_null[i] = nullmap_data[i]; } for (size_t i = 0; i < size; ++i) { if constexpr (IsDecimal) c[i] = Op::template apply(a, DecimalField(b[i], b.getScale()), res_null[i]); else c[i] = Op::template apply(a, b[i], res_null[i]); } } static ResultType constantConstant(typename NearestFieldType::Type a, typename NearestFieldType::Type b) { return Op::template apply(a, b); } static ResultType constantConstantNullable( typename NearestFieldType::Type a, typename NearestFieldType::Type b, UInt8 & res_null) { return Op::template apply(a, b, res_null); } }; template struct BinaryOperationImpl : BinaryOperationImplBase { }; /// these ones for better semantics template using Then = T; template using Else = T; /// 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::getScale()). template typename Operation, typename OpResultType> struct DecimalBinaryOperation { //static_assert((IsDecimal || IsDecimal) && IsDecimal); //static_assert(IsDecimal || std::is_integral_v); //static_assert(IsDecimal || std::is_integral_v); static constexpr bool is_plus_minus = IsOperation::plus || IsOperation::minus; static constexpr bool is_multiply = IsOperation::multiply; static constexpr bool is_modulo = IsOperation::modulo; static constexpr bool is_float_division = IsOperation::div_floating; static constexpr bool is_int_division = IsOperation::div_int; static constexpr bool is_division = is_float_division || is_int_division; static constexpr bool is_compare = IsOperation::least || IsOperation::greatest; static constexpr bool is_plus_minus_compare = is_plus_minus || is_compare; static constexpr bool can_overflow = is_plus_minus || is_multiply; static constexpr bool need_promote_type = (std::is_same_v || std::is_same_v)&&( is_plus_minus_compare || is_division || is_multiply || is_modulo); // And is multiple / division / modulo static constexpr bool check_overflow = need_promote_type && std::is_same_v; // Check if exceeds 10 * 66; using ResultType = OpResultType; using NativeResultType = typename ResultType::NativeType; using ColVecA = std::conditional_t, ColumnDecimal, ColumnVector>; using ColVecB = std::conditional_t, ColumnDecimal, ColumnVector>; using ArrayA = typename ColVecA::Container; using ArrayB = typename ColVecB::Container; using ArrayC = typename ColumnDecimal::Container; using PromoteResultType = typename PromoteType::Type; using InputType = std::conditional_t; using Op = Operation; static void inline evaluateNullmap( size_t size, const ColumnUInt8 * a_nullmap, const ColumnUInt8 * b_nullmap, typename ColumnUInt8::Container & res_null) { if (a_nullmap != nullptr && b_nullmap != nullptr) { const auto & a_nullmap_data = a_nullmap->getData(); const auto & b_nullmap_data = b_nullmap->getData(); for (size_t i = 0; i < size; ++i) res_null[i] = a_nullmap_data[i] || b_nullmap_data[i]; } else if (a_nullmap != nullptr || b_nullmap != nullptr) { const auto & nullmap_data = a_nullmap != nullptr ? a_nullmap->getData() : b_nullmap->getData(); for (size_t i = 0; i < size; ++i) res_null[i] = nullmap_data[i]; } } static void NO_INLINE vectorVector( const ArrayA & a, const ArrayB & b, ArrayC & c, NativeResultType scale_a [[maybe_unused]], NativeResultType scale_b [[maybe_unused]], NativeResultType scale_result [[maybe_unused]]) { size_t size = a.size(); if constexpr (is_plus_minus_compare) { if (scale_a != 1) { for (size_t i = 0; i < size; ++i) c[i] = applyScaled(a[i], b[i], scale_a); return; } else if (scale_b != 1) { for (size_t i = 0; i < size; ++i) c[i] = applyScaled(a[i], b[i], scale_b); return; } } else if constexpr (is_multiply) { for (size_t i = 0; i < size; ++i) c[i] = applyScaledMul(a[i], b[i], scale_result); return; } else if constexpr (is_division) { for (size_t i = 0; i < size; ++i) c[i] = applyScaled(a[i], b[i], scale_a); return; } /// default: use it if no return before for (size_t i = 0; i < size; ++i) c[i] = apply(a[i], b[i]); } static void NO_INLINE vectorVectorNullable( const ArrayA & a, const ColumnUInt8 * a_nullmap, const ArrayB & b, const ColumnUInt8 * b_nullmap, ArrayC & c, typename ColumnUInt8::Container & res_null, NativeResultType scale_a [[maybe_unused]], NativeResultType scale_b [[maybe_unused]], NativeResultType scale_result [[maybe_unused]]) { size_t size = a.size(); evaluateNullmap(size, a_nullmap, b_nullmap, res_null); if constexpr (is_division) { for (size_t i = 0; i < size; ++i) c[i] = applyScaled(a[i], b[i], scale_a, res_null[i]); return; } else if constexpr (is_modulo) { if (scale_a != 1) { for (size_t i = 0; i < size; ++i) c[i] = applyScaled(a[i], b[i], scale_a, res_null[i]); } else { for (size_t i = 0; i < size; ++i) c[i] = applyScaled(a[i], b[i], scale_b, res_null[i]); } return; } throw Exception("Should not reach here"); } static void NO_INLINE vectorConstant( const ArrayA & a, B b, ArrayC & c, NativeResultType scale_a [[maybe_unused]], NativeResultType scale_b [[maybe_unused]], NativeResultType scale_result [[maybe_unused]]) { size_t size = a.size(); if constexpr (is_plus_minus_compare) { if (scale_a != 1) { for (size_t i = 0; i < size; ++i) c[i] = applyScaled(a[i], b, scale_a); return; } else if (scale_b != 1) { for (size_t i = 0; i < size; ++i) c[i] = applyScaled(a[i], b, scale_b); return; } } else if constexpr (is_multiply) { for (size_t i = 0; i < size; ++i) c[i] = applyScaledMul(a[i], b, scale_result); return; } else if constexpr (is_division) { for (size_t i = 0; i < size; ++i) c[i] = applyScaled(a[i], b, scale_a); return; } /// default: use it if no return before for (size_t i = 0; i < size; ++i) c[i] = apply(a[i], b); } static void NO_INLINE vectorConstantNullable( const ArrayA & a, const ColumnUInt8 * a_nullmap, B b, ArrayC & c, typename ColumnUInt8::Container & res_null, NativeResultType scale_a [[maybe_unused]], NativeResultType scale_b [[maybe_unused]], NativeResultType scale_result [[maybe_unused]]) { size_t size = a.size(); evaluateNullmap(size, a_nullmap, nullptr, res_null); if constexpr (is_division) { for (size_t i = 0; i < size; ++i) c[i] = applyScaled(a[i], b, scale_a, res_null[i]); return; } else if constexpr (is_modulo) { if (scale_a != 1) { for (size_t i = 0; i < size; ++i) c[i] = applyScaled(a[i], b, scale_a, res_null[i]); } else { for (size_t i = 0; i < size; ++i) c[i] = applyScaled(a[i], b, scale_b, res_null[i]); } return; } throw Exception("Should not reach here"); } static void NO_INLINE constantVector( A a, const ArrayB & b, ArrayC & c, NativeResultType scale_a [[maybe_unused]], NativeResultType scale_b [[maybe_unused]], NativeResultType scale_result [[maybe_unused]]) { size_t size = b.size(); if constexpr (is_plus_minus_compare) { if (scale_a != 1) { for (size_t i = 0; i < size; ++i) c[i] = applyScaled(a, b[i], scale_a); return; } else if (scale_b != 1) { for (size_t i = 0; i < size; ++i) c[i] = applyScaled(a, b[i], scale_b); return; } } else if constexpr (is_multiply) { for (size_t i = 0; i < size; ++i) c[i] = applyScaledMul(a, b[i], scale_result); return; } else if constexpr (is_division) { for (size_t i = 0; i < size; ++i) c[i] = applyScaled(a, b[i], scale_a); return; } /// default: use it if no return before for (size_t i = 0; i < size; ++i) c[i] = apply(a, b[i]); } static void NO_INLINE constantVectorNullable( A a, const ArrayB & b, const ColumnUInt8 * b_nullmap, ArrayC & c, typename ColumnUInt8::Container & res_null, NativeResultType scale_a [[maybe_unused]], NativeResultType scale_b [[maybe_unused]], NativeResultType scale_result [[maybe_unused]]) { size_t size = b.size(); evaluateNullmap(size, nullptr, b_nullmap, res_null); if constexpr (is_division) { for (size_t i = 0; i < size; ++i) c[i] = applyScaled(a, b[i], scale_a, res_null[i]); return; } else if constexpr (is_modulo) { if (scale_a != 1) { for (size_t i = 0; i < size; ++i) c[i] = applyScaled(a, b[i], scale_a, res_null[i]); } else { for (size_t i = 0; i < size; ++i) c[i] = applyScaled(a, b[i], scale_b, res_null[i]); } return; } throw Exception("Should not reach here"); } static ResultType constantConstant( A a, B b, NativeResultType scale_a [[maybe_unused]], NativeResultType scale_b [[maybe_unused]], NativeResultType scale_result [[maybe_unused]]) { if constexpr (is_plus_minus_compare) { if (scale_a != 1) return applyScaled(a, b, scale_a); else if (scale_b != 1) return applyScaled(a, b, scale_b); } else if constexpr (is_multiply) { return applyScaledMul(a, b, scale_result); } else if constexpr (is_division) return applyScaled(a, b, scale_a); return apply(a, b); } static ResultType constantConstantNullable( A a, B b, NativeResultType scale_a [[maybe_unused]], NativeResultType scale_b [[maybe_unused]], NativeResultType scale_result [[maybe_unused]], UInt8 & res_null) { if constexpr (is_division) { return applyScaled(a, b, scale_a, res_null); } else if constexpr (is_modulo) { if (scale_a != 1) return applyScaled(a, b, scale_a, res_null); else return applyScaled(a, b, scale_b, res_null); } throw Exception("Should not reach here"); } private: static NativeResultType applyScaledMul(NativeResultType a, NativeResultType b, NativeResultType scale) { if constexpr (is_multiply) { if constexpr (need_promote_type) { PromoteResultType res = Op::template apply(a, b); res = res / scale; if constexpr (check_overflow) { if (res > DecimalMaxValue::maxValue()) { throw Exception("Decimal math overflow", ErrorCodes::DECIMAL_OVERFLOW); } } return static_cast(res); } else { NativeResultType res = Op::template apply(a, b); res = res / scale; return res; } } } /// there's implicit type convertion here static NativeResultType apply(NativeResultType a, NativeResultType b) { if constexpr (need_promote_type) { auto res = Op::template apply(a, b); if constexpr (check_overflow) { if (res > DecimalMaxValue::maxValue()) { throw Exception("Decimal math overflow", ErrorCodes::DECIMAL_OVERFLOW); } } return static_cast(res); } else { return Op::template apply(a, b); } } template static NativeResultType applyScaled(InputType a, InputType b, InputType scale) { if constexpr (is_plus_minus_compare || is_division || is_modulo) { InputType res; if constexpr (scale_left) a = a * scale; else b = b * scale; res = Op::template apply(a, b); if constexpr (check_overflow) { if (res > DecimalMaxValue::maxValue()) { throw Exception("Decimal math overflow", ErrorCodes::DECIMAL_OVERFLOW); } } return static_cast(res); } } template static NativeResultType applyScaled(InputType a, InputType b, InputType scale, UInt8 & res_null) { if constexpr (is_division || is_modulo) { InputType res; if constexpr (scale_left) a = a * scale; else b = b * scale; res = Op::template apply(a, b, res_null); if constexpr (check_overflow) { if (res > DecimalMaxValue::maxValue()) { throw Exception("Decimal math overflow", ErrorCodes::DECIMAL_OVERFLOW); } } return static_cast(res); } throw Exception("Should not reach here"); } }; template constexpr bool IsIntegral = false; template <> inline constexpr bool IsIntegral = true; template <> inline constexpr bool IsIntegral = true; template <> inline constexpr bool IsIntegral = true; template <> inline constexpr bool IsIntegral = true; template <> inline constexpr bool IsIntegral = true; template <> inline constexpr bool IsIntegral = true; template <> inline constexpr bool IsIntegral = true; template <> inline constexpr bool IsIntegral = true; template constexpr bool IsDateOrDateTime = false; template <> inline constexpr bool IsDateOrDateTime = true; template <> inline constexpr bool IsDateOrDateTime = true; /** Returns appropriate result type for binary operator on dates (or datetimes): * Date + Integral -> Date * Integral + Date -> Date * Date - Date -> Int32 * Date - Integral -> Date * least(Date, Date) -> Date * greatest(Date, Date) -> Date * All other operations are not defined and return InvalidType, operations on * distinct date types are also undefined (e.g. DataTypeDate - DataTypeDateTime) */ template