| // Licensed to the Apache Software Foundation (ASF) under one |
| // or more contributor license agreements. See the NOTICE file |
| // distributed with this work for additional information |
| // regarding copyright ownership. The ASF licenses this file |
| // to you 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. |
| |
| #include <iomanip> |
| #include <random> |
| #include <sstream> |
| #include <math.h> |
| #include <stdint.h> |
| #include <string> |
| #include "exprs/anyval-util.h" |
| #include "exprs/scalar-expr.h" |
| #include "exprs/operators.h" |
| #include "exprs/math-functions.h" |
| #include "util/string-parser.h" |
| #include "runtime/decimal-value.inline.h" |
| #include "runtime/runtime-state.h" |
| #include "runtime/string-value.inline.h" |
| #include "thirdparty/pcg-cpp-0.98/include/pcg_random.hpp" |
| #include "exprs/decimal-operators.h" |
| #include "common/names.h" |
| |
| using std::uppercase; |
| |
| namespace impala { |
| |
| const char* MathFunctions::ALPHANUMERIC_CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; |
| |
| DoubleVal MathFunctions::Pi(FunctionContext* ctx) { |
| return DoubleVal(M_PI); |
| } |
| |
| DoubleVal MathFunctions::E(FunctionContext* ctx) { |
| return DoubleVal(M_E); |
| } |
| |
| // Generates a UDF that always calls FN() on the input val and returns it. |
| #define ONE_ARG_MATH_FN(NAME, RET_TYPE, INPUT_TYPE, FN) \ |
| RET_TYPE MathFunctions::NAME(FunctionContext* ctx, const INPUT_TYPE& v) { \ |
| if (v.is_null) return RET_TYPE::null(); \ |
| return RET_TYPE(FN(v.val)); \ |
| } |
| |
| // Generates a UDF that always calls FN() on the input vals and returns it. |
| #define TWO_ARG_MATH_FN(NAME, RET_TYPE, INPUT_TYPE1, INPUT_TYPE2, FN) \ |
| RET_TYPE MathFunctions::NAME(FunctionContext* ctx, \ |
| const INPUT_TYPE1& v1, const INPUT_TYPE2& v2) { \ |
| if (v1.is_null || v2.is_null) return RET_TYPE::null(); \ |
| return RET_TYPE(FN(v1.val, v2.val)); \ |
| } |
| |
| BigIntVal MathFunctions::Abs(FunctionContext* ctx, const BigIntVal& v) { |
| if (v.is_null) return BigIntVal::null(); |
| if (UNLIKELY(v.val == std::numeric_limits<BigIntVal::underlying_type_t>::min())) { |
| ctx->AddWarning("abs() overflowed, returning NULL"); |
| return BigIntVal::null(); |
| } |
| return BigIntVal(llabs(v.val)); |
| } |
| |
| ONE_ARG_MATH_FN(Abs, BigIntVal, IntVal, llabs); |
| ONE_ARG_MATH_FN(Abs, IntVal, SmallIntVal, abs); |
| ONE_ARG_MATH_FN(Abs, SmallIntVal, TinyIntVal, abs); |
| ONE_ARG_MATH_FN(Abs, DoubleVal, DoubleVal, fabs); |
| ONE_ARG_MATH_FN(Abs, FloatVal, FloatVal, fabs); |
| ONE_ARG_MATH_FN(Sin, DoubleVal, DoubleVal, sin); |
| ONE_ARG_MATH_FN(Asin, DoubleVal, DoubleVal, asin); |
| ONE_ARG_MATH_FN(Cos, DoubleVal, DoubleVal, cos); |
| ONE_ARG_MATH_FN(Acos, DoubleVal, DoubleVal, acos); |
| ONE_ARG_MATH_FN(Tan, DoubleVal, DoubleVal, tan); |
| ONE_ARG_MATH_FN(Atan, DoubleVal, DoubleVal, atan); |
| ONE_ARG_MATH_FN(Cosh, DoubleVal, DoubleVal, cosh); |
| ONE_ARG_MATH_FN(Tanh, DoubleVal, DoubleVal, tanh); |
| ONE_ARG_MATH_FN(Sinh, DoubleVal, DoubleVal, sinh); |
| ONE_ARG_MATH_FN(Sqrt, DoubleVal, DoubleVal, sqrt); |
| ONE_ARG_MATH_FN(Ceil, DoubleVal, DoubleVal, ceil); |
| ONE_ARG_MATH_FN(Floor, DoubleVal, DoubleVal, floor); |
| ONE_ARG_MATH_FN(Truncate, DoubleVal, DoubleVal, trunc); |
| ONE_ARG_MATH_FN(Ln, DoubleVal, DoubleVal, log); |
| ONE_ARG_MATH_FN(Log10, DoubleVal, DoubleVal, log10); |
| ONE_ARG_MATH_FN(Exp, DoubleVal, DoubleVal, exp); |
| |
| TWO_ARG_MATH_FN(Atan2, DoubleVal, DoubleVal, DoubleVal, atan2); |
| |
| DoubleVal MathFunctions::Cot(FunctionContext* ctx, const DoubleVal& v) { |
| if (v.is_null) return DoubleVal::null(); |
| return DoubleVal(tan(M_PI_2 - v.val)); |
| } |
| |
| DoubleVal MathFunctions::Sign(FunctionContext* ctx, const DoubleVal& v) { |
| if (v.is_null) return DoubleVal::null(); |
| return DoubleVal((v.val > 0) ? 1 : ((v.val < 0) ? -1 : 0)); |
| } |
| |
| DoubleVal MathFunctions::Radians(FunctionContext* ctx, const DoubleVal& v) { |
| if (v.is_null) return v; |
| return DoubleVal(v.val * M_PI / 180.0); |
| } |
| |
| DoubleVal MathFunctions::Degrees(FunctionContext* ctx, const DoubleVal& v) { |
| if (v.is_null) return v; |
| return DoubleVal(v.val * 180.0 / M_PI); |
| } |
| |
| DoubleVal MathFunctions::Round(FunctionContext* ctx, const DoubleVal& v) { |
| if (v.is_null) return DoubleVal::null(); |
| return DoubleVal(trunc(v.val + ((v.val < 0) ? -0.5 : 0.5))); |
| } |
| |
| static double GetScaleMultiplier(int64_t scale) { |
| static const double values[] = { |
| 1.0, |
| 10.0, |
| 100.0, |
| 1000.0, |
| 10000.0, |
| 100000.0, |
| 1000000.0, |
| 10000000.0, |
| 100000000.0, |
| 1000000000.0, |
| 10000000000.0, |
| 100000000000.0, |
| 1000000000000.0, |
| 10000000000000.0, |
| 100000000000000.0, |
| 1000000000000000.0, |
| 10000000000000000.0, |
| 100000000000000000.0, |
| 1000000000000000000.0, |
| 10000000000000000000.0}; |
| if (LIKELY(0 <= scale && scale < 20)) return values[scale]; |
| return pow(10.0, scale); |
| } |
| |
| DoubleVal MathFunctions::RoundUpTo(FunctionContext* ctx, const DoubleVal& v, |
| const BigIntVal& scale) { |
| if (v.is_null || scale.is_null) return DoubleVal::null(); |
| return DoubleVal(trunc( |
| v.val * GetScaleMultiplier(scale.val) + ((v.val < 0) ? -0.5 : 0.5)) / |
| GetScaleMultiplier(scale.val)); |
| } |
| |
| DoubleVal MathFunctions::Log2(FunctionContext* ctx, const DoubleVal& v) { |
| if (v.is_null) return DoubleVal::null(); |
| return DoubleVal(log(v.val) / log(2.0)); |
| } |
| |
| DoubleVal MathFunctions::Log(FunctionContext* ctx, const DoubleVal& base, |
| const DoubleVal& v) { |
| if (base.is_null || v.is_null) return DoubleVal::null(); |
| return DoubleVal(log(v.val) / log(base.val)); |
| } |
| |
| DoubleVal MathFunctions::Pow(FunctionContext* ctx, const DoubleVal& base, |
| const DoubleVal& exp) { |
| if (base.is_null || exp.is_null) return DoubleVal::null(); |
| return DoubleVal(pow(base.val, exp.val)); |
| } |
| |
| void MathFunctions::RandPrepare( |
| FunctionContext* ctx, FunctionContext::FunctionStateScope scope) { |
| if (scope == FunctionContext::THREAD_LOCAL) { |
| uint32_t seed = 0; |
| if (ctx->GetNumArgs() == 1) { |
| // This is a call to RandSeed, initialize the seed |
| // TODO: should we support non-constant seed? |
| if (!ctx->IsArgConstant(0)) { |
| ctx->SetError("Seed argument to rand() must be constant"); |
| return; |
| } |
| DCHECK_EQ(ctx->GetArgType(0)->type, FunctionContext::TYPE_BIGINT); |
| BigIntVal* seed_arg = static_cast<BigIntVal*>(ctx->GetConstantArg(0)); |
| if (!seed_arg->is_null) seed = seed_arg->val; |
| } |
| pcg32* generator = ctx->Allocate<pcg32>(); |
| RETURN_IF_NULL(ctx, generator); |
| ctx->SetFunctionState(scope, generator); |
| new (generator) pcg32(seed); |
| } |
| } |
| |
| DoubleVal MathFunctions::Rand(FunctionContext* ctx) { |
| pcg32* const generator = |
| reinterpret_cast<pcg32*>(ctx->GetFunctionState(FunctionContext::THREAD_LOCAL)); |
| DCHECK(generator != nullptr); |
| static const double min = 0, max = 1; |
| std::uniform_real_distribution<double> distribution(min, max); |
| return DoubleVal(distribution(*generator)); |
| } |
| |
| DoubleVal MathFunctions::RandSeed(FunctionContext* ctx, const BigIntVal& seed) { |
| if (seed.is_null) return DoubleVal::null(); |
| return Rand(ctx); |
| } |
| |
| void MathFunctions::RandClose(FunctionContext* ctx, |
| FunctionContext::FunctionStateScope scope) { |
| if (scope == FunctionContext::THREAD_LOCAL) { |
| uint8_t* generator = reinterpret_cast<uint8_t*>( |
| ctx->GetFunctionState(FunctionContext::THREAD_LOCAL)); |
| ctx->Free(generator); |
| ctx->SetFunctionState(FunctionContext::THREAD_LOCAL, nullptr); |
| } |
| } |
| |
| StringVal MathFunctions::Bin(FunctionContext* ctx, const BigIntVal& v) { |
| if (v.is_null) return StringVal::null(); |
| // Cast to an unsigned integer because it is compiler dependent |
| // whether the sign bit will be shifted like a regular bit. |
| // (logical vs. arithmetic shift for signed numbers) |
| uint64_t n = static_cast<uint64_t>(v.val); |
| const size_t max_bits = sizeof(uint64_t) * 8; |
| char result[max_bits]; |
| uint32_t index = max_bits; |
| do { |
| result[--index] = '0' + (n & 1); |
| } while (n >>= 1); |
| return AnyValUtil::FromBuffer(ctx, result + index, max_bits - index); |
| } |
| |
| StringVal MathFunctions::HexInt(FunctionContext* ctx, const BigIntVal& v) { |
| if (v.is_null) return StringVal::null(); |
| // TODO: this is probably unreasonably slow |
| stringstream ss; |
| ss << hex << uppercase << v.val; |
| return AnyValUtil::FromString(ctx, ss.str()); |
| } |
| |
| StringVal MathFunctions::HexString(FunctionContext* ctx, const StringVal& s) { |
| if (s.is_null) return StringVal::null(); |
| stringstream ss; |
| ss << hex << uppercase << setfill('0'); |
| for (int i = 0; i < s.len; ++i) { |
| // setw is not sticky. stringstream only converts integral values, |
| // so a cast to int is required, but only convert the least significant byte to hex. |
| ss << setw(2) << (static_cast<int32_t>(s.ptr[i]) & 0xFF); |
| } |
| return AnyValUtil::FromString(ctx, ss.str()); |
| } |
| |
| StringVal MathFunctions::Unhex(FunctionContext* ctx, const StringVal& s) { |
| if (s.is_null) return StringVal::null(); |
| // For uneven number of chars return empty string like Hive does. |
| if (s.len % 2 != 0) return StringVal(); |
| |
| int result_len = s.len / 2; |
| StringVal result(ctx, result_len); |
| int res_index = 0; |
| int s_index = 0; |
| while (s_index < s.len) { |
| char c = 0; |
| for (int j = 0; j < 2; ++j, ++s_index) { |
| switch(s.ptr[s_index]) { |
| case '0': |
| case '1': |
| case '2': |
| case '3': |
| case '4': |
| case '5': |
| case '6': |
| case '7': |
| case '8': |
| case '9': |
| c += (s.ptr[s_index] - '0') * ((j == 0) ? 16 : 1); |
| break; |
| case 'A': |
| case 'B': |
| case 'C': |
| case 'D': |
| case 'E': |
| case 'F': |
| // Map to decimal values [10, 15] |
| c += (s.ptr[s_index] - 'A' + 10) * ((j == 0) ? 16 : 1); |
| break; |
| case 'a': |
| case 'b': |
| case 'c': |
| case 'd': |
| case 'e': |
| case 'f': |
| // Map to decimal [10, 15] |
| c += (s.ptr[s_index] - 'a' + 10) * ((j == 0) ? 16 : 1); |
| break; |
| default: |
| // Character not in hex alphabet, return empty string. |
| return StringVal(); |
| } |
| } |
| result.ptr[res_index] = c; |
| ++res_index; |
| } |
| return result; |
| } |
| |
| StringVal MathFunctions::ConvInt(FunctionContext* ctx, const BigIntVal& num, |
| const TinyIntVal& src_base, const TinyIntVal& dest_base) { |
| if (num.is_null || src_base.is_null || dest_base.is_null) return StringVal::null(); |
| // As in MySQL and Hive, min base is 2 and max base is 36. |
| // (36 is max base representable by alphanumeric chars) |
| // If a negative target base is given, num should be interpreted in 2's complement. |
| if (abs(src_base.val) < MIN_BASE || abs(src_base.val) > MAX_BASE |
| || abs(dest_base.val) < MIN_BASE || abs(dest_base.val) > MAX_BASE) { |
| // Return NULL like Hive does. |
| return StringVal::null(); |
| } |
| |
| // Invalid input. |
| if (src_base.val < 0 && num.val >= 0) return StringVal::null(); |
| int64_t decimal_num = num.val; |
| if (src_base.val != 10) { |
| // Convert src_num representing a number in src_base but encoded in decimal |
| // into its actual decimal number. |
| if (!DecimalInBaseToDecimal(num.val, src_base.val, &decimal_num)) { |
| // Handle overflow, setting decimal_num appropriately. |
| HandleParseResult(dest_base.val, &decimal_num, StringParser::PARSE_OVERFLOW); |
| } |
| } |
| return DecimalToBase(ctx, decimal_num, dest_base.val); |
| } |
| |
| StringVal MathFunctions::ConvString(FunctionContext* ctx, const StringVal& num_str, |
| const TinyIntVal& src_base, const TinyIntVal& dest_base) { |
| if (num_str.is_null || src_base.is_null || dest_base.is_null) return StringVal::null(); |
| // As in MySQL and Hive, min base is 2 and max base is 36. |
| // (36 is max base representable by alphanumeric chars) |
| // If a negative target base is given, num should be interpreted in 2's complement. |
| if (abs(src_base.val) < MIN_BASE || abs(src_base.val) > MAX_BASE |
| || abs(dest_base.val) < MIN_BASE || abs(dest_base.val) > MAX_BASE) { |
| // Return NULL like Hive does. |
| return StringVal::null(); |
| } |
| // Convert digits in num_str in src_base to decimal. |
| StringParser::ParseResult parse_res; |
| int64_t decimal_num = StringParser::StringToInt<int64_t>( |
| reinterpret_cast<char*>(num_str.ptr), num_str.len, src_base.val, &parse_res); |
| if (src_base.val < 0 && decimal_num >= 0) { |
| // Invalid input. |
| return StringVal::null(); |
| } |
| if (!HandleParseResult(dest_base.val, &decimal_num, parse_res)) { |
| // Return 0 for invalid input strings like Hive does. |
| return StringVal(reinterpret_cast<uint8_t*>(const_cast<char*>("0")), 1); |
| } |
| return DecimalToBase(ctx, decimal_num, dest_base.val); |
| } |
| |
| StringVal MathFunctions::DecimalToBase(FunctionContext* ctx, int64_t src_num, |
| int8_t dest_base) { |
| // Max number of digits of any base (base 2 gives max digits), plus sign. |
| const size_t max_digits = sizeof(uint64_t) * 8 + 1; |
| char buf[max_digits]; |
| int32_t result_len = 0; |
| int32_t buf_index = max_digits - 1; |
| uint64_t temp_num; |
| if (dest_base < 0) { |
| // Dest base is negative, treat src_num as signed. |
| temp_num = abs(src_num); |
| } else { |
| // Dest base is positive. We must interpret src_num in 2's complement. |
| // Convert to an unsigned int to properly deal with 2's complement conversion. |
| temp_num = static_cast<uint64_t>(src_num); |
| } |
| int abs_base = abs(dest_base); |
| do { |
| buf[buf_index] = ALPHANUMERIC_CHARS[temp_num % abs_base]; |
| temp_num /= abs_base; |
| --buf_index; |
| ++result_len; |
| } while (temp_num > 0); |
| // Add optional sign. |
| if (src_num < 0 && dest_base < 0) { |
| buf[buf_index] = '-'; |
| ++result_len; |
| } |
| return AnyValUtil::FromBuffer(ctx, buf + max_digits - result_len, result_len); |
| } |
| |
| bool MathFunctions::DecimalInBaseToDecimal(int64_t src_num, int8_t src_base, |
| int64_t* result) { |
| uint64_t temp_num = abs(src_num); |
| int64_t place = 1; |
| *result = 0; |
| do { |
| int32_t digit = temp_num % 10; |
| // Reset result if digit is not representable in src_base. |
| if (digit >= src_base) { |
| *result = 0; |
| place = 1; |
| } else { |
| *result += |
| ArithmeticUtil::AsUnsigned<std::multiplies>(static_cast<int64_t>(digit), place); |
| // Overflow. |
| if (UNLIKELY(*result < digit)) { |
| return false; |
| } |
| place *= src_base; |
| } |
| temp_num /= 10; |
| } while (temp_num > 0); |
| *result = (src_num < 0) ? -(*result) : *result; |
| return true; |
| } |
| |
| bool MathFunctions::HandleParseResult(int8_t dest_base, int64_t* num, |
| StringParser::ParseResult parse_res) { |
| // On overflow set special value depending on dest_base. |
| // This is consistent with Hive and MySQL's behavior. |
| if (parse_res == StringParser::PARSE_OVERFLOW) { |
| if (dest_base < 0) { |
| *num = -1; |
| } else { |
| *num = numeric_limits<uint64_t>::max(); |
| } |
| } else if (parse_res == StringParser::PARSE_FAILURE) { |
| // Some other error condition. |
| return false; |
| } |
| return true; |
| } |
| |
| BigIntVal MathFunctions::PmodBigInt(FunctionContext* ctx, const BigIntVal& a, |
| const BigIntVal& b) { |
| if (a.is_null || b.is_null) return BigIntVal::null(); |
| return BigIntVal(((a.val % b.val) + b.val) % b.val); |
| } |
| |
| DoubleVal MathFunctions::PmodDouble(FunctionContext* ctx, const DoubleVal& a, |
| const DoubleVal& b) { |
| if (a.is_null || b.is_null) return DoubleVal::null(); |
| return DoubleVal(fmod(fmod(a.val, b.val) + b.val, b.val)); |
| } |
| |
| FloatVal MathFunctions::FmodFloat(FunctionContext* ctx, const FloatVal& a, |
| const FloatVal& b) { |
| if (a.is_null || b.is_null || b.val == 0) return FloatVal::null(); |
| return FloatVal(fmodf(a.val, b.val)); |
| } |
| |
| DoubleVal MathFunctions::FmodDouble(FunctionContext* ctx, const DoubleVal& a, |
| const DoubleVal& b) { |
| if (a.is_null || b.is_null || b.val == 0) return DoubleVal::null(); |
| return DoubleVal(fmod(a.val, b.val)); |
| } |
| |
| // The bucket_number is evaluated using the following formula: |
| // |
| // bucket_number = dist_from_min * num_buckets / range_size + 1 |
| // where - |
| // dist_from_min = expr - min_range |
| // range_size = max_range - min_range |
| // num_buckets = number of buckets |
| // |
| // Since expr, min_range, and max_range are all decimals with the same |
| // byte size, precision, and scale we can interpret them as plain integers |
| // and still calculate the proper bucket. |
| // |
| // There are some possibilities of overflowing during the calculation: |
| // range_size = max_range - min_range |
| // dist_from_min = expr - min_range |
| // dist_from_min * num_buckets |
| // |
| // For all the above cases we use a bigger integer type provided by the |
| // BitUtil::DoubleWidth<> metafunction. |
| template <class T1> |
| BigIntVal MathFunctions::WidthBucketImpl(FunctionContext* ctx, |
| const T1& expr, const T1& min_range, |
| const T1& max_range, const IntVal& num_buckets) { |
| auto expr_val = expr.value(); |
| using ActualType = decltype(expr_val); |
| auto min_range_val = min_range.value(); |
| auto max_range_val = max_range.value(); |
| auto num_buckets_val = static_cast<ActualType>(num_buckets.val); |
| |
| if (UNLIKELY(num_buckets.val <= 0)) { |
| ostringstream error_msg; |
| error_msg << "Number of buckets should be greater than zero:" << num_buckets.val; |
| ctx->SetError(error_msg.str().c_str()); |
| return BigIntVal::null(); |
| } |
| |
| if (UNLIKELY(min_range_val >= max_range_val)) { |
| ctx->SetError("Lower bound cannot be greater than or equal to the upper bound"); |
| return BigIntVal::null(); |
| } |
| |
| if (expr_val < min_range_val) return 0; |
| if (expr_val >= max_range_val) { |
| return BigIntVal(static_cast<int64_t>(num_buckets.val) + 1); |
| } |
| |
| bool bigger_type_needed = false; |
| // It is likely that this if stmt can be evaluated during codegen because |
| // 'max_range' and 'min_range' are almost certainly constant expressions: |
| if (max_range_val >= 0 && min_range_val < 0) { |
| if (static_cast<UnsignedType<ActualType>>(max_range_val) + |
| static_cast<UnsignedType<ActualType>>(abs(min_range_val)) >= |
| static_cast<UnsignedType<ActualType>>(BitUtil::Max<ActualType>())) { |
| bigger_type_needed = true; |
| } |
| } |
| |
| auto MultiplicationOverflows = [](ActualType lhs, ActualType rhs) { |
| DCHECK(lhs > 0 && rhs > 0); |
| using ActualType = decltype(lhs); |
| return BitUtil::CountLeadingZeros(lhs) + BitUtil::CountLeadingZeros(rhs) <= |
| BitUtil::UnsignedWidth<ActualType>() + 1; |
| }; |
| |
| // It is likely that this can be evaluated during codegen: |
| bool multiplication_can_overflow = bigger_type_needed || MultiplicationOverflows( |
| max_range_val - min_range_val, num_buckets_val); |
| // 'expr_val' is not likely to be a constant expression, so this almost certainly |
| // needs runtime evaluation if 'bigger_type_needed' is false and |
| // 'multiplication_can_overflow' is true: |
| bigger_type_needed = bigger_type_needed || ( |
| multiplication_can_overflow && |
| expr_val != min_range_val && |
| MultiplicationOverflows(expr_val - min_range_val, num_buckets_val)); |
| |
| auto BucketFunc = [](auto element, auto min_rng, auto max_rng, auto buckets) { |
| auto range_size = max_rng - min_rng; |
| auto dist_from_min = element - min_rng; |
| auto ret = dist_from_min * buckets / range_size; |
| return BigIntVal(static_cast<int64_t>(ret) + 1); |
| }; |
| |
| if (bigger_type_needed) { |
| using BiggerType = typename DoubleWidth<ActualType>::type; |
| |
| return BucketFunc(static_cast<BiggerType>(expr_val), |
| static_cast<BiggerType>(min_range_val), static_cast<BiggerType>(max_range_val), |
| static_cast<BiggerType>(num_buckets.val)); |
| } else { |
| return BucketFunc(expr_val, min_range_val, max_range_val, num_buckets.val); |
| } |
| } |
| |
| BigIntVal MathFunctions::WidthBucket(FunctionContext* ctx, const DecimalVal& expr, |
| const DecimalVal& min_range, const DecimalVal& max_range, |
| const IntVal& num_buckets) { |
| if (expr.is_null || min_range.is_null || max_range.is_null || num_buckets.is_null) { |
| return BigIntVal::null(); |
| } |
| |
| int arg_size_type = ctx->impl()->GetConstFnAttr(FunctionContextImpl::ARG_TYPE_SIZE, 0); |
| switch (arg_size_type) { |
| case 4: |
| return WidthBucketImpl<Decimal4Value> (ctx, Decimal4Value(expr.val4), |
| Decimal4Value(min_range.val4), Decimal4Value(max_range.val4), num_buckets); |
| case 8: |
| return WidthBucketImpl<Decimal8Value> (ctx, Decimal8Value(expr.val8), |
| Decimal8Value(min_range.val8), Decimal8Value(max_range.val8), num_buckets); |
| case 16: |
| return WidthBucketImpl<Decimal16Value>(ctx, Decimal16Value(expr.val16), |
| Decimal16Value(min_range.val16), Decimal16Value(max_range.val16), num_buckets); |
| default: |
| DCHECK(false); |
| return BigIntVal::null(); |
| } |
| } |
| |
| template <typename T> T MathFunctions::Positive(FunctionContext* ctx, const T& val) { |
| return val; |
| } |
| |
| template <typename T> T MathFunctions::Negative(FunctionContext* ctx, const T& val) { |
| if (val.is_null) return val; |
| return T(-val.val); |
| } |
| |
| template <> |
| DecimalVal MathFunctions::Negative(FunctionContext* ctx, const DecimalVal& val) { |
| if (val.is_null) return val; |
| int type_byte_size = ctx->impl()->GetConstFnAttr(FunctionContextImpl::RETURN_TYPE_SIZE); |
| switch (type_byte_size) { |
| case 4: |
| return DecimalVal(-val.val4); |
| case 8: |
| return DecimalVal(-val.val8); |
| case 16: |
| return DecimalVal(-val.val16); |
| default: |
| DCHECK(false); |
| return DecimalVal::null(); |
| } |
| } |
| |
| BigIntVal MathFunctions::QuotientDouble(FunctionContext* ctx, const DoubleVal& x, |
| const DoubleVal& y) { |
| if (x.is_null || y.is_null || static_cast<int64_t>(y.val) == 0) { |
| return BigIntVal::null(); |
| } |
| return BigIntVal(static_cast<int64_t>(x.val) / static_cast<int64_t>(y.val)); |
| } |
| |
| BigIntVal MathFunctions::QuotientBigInt(FunctionContext* ctx, const BigIntVal& x, |
| const BigIntVal& y) { |
| return Operators::Int_divide_BigIntVal_BigIntVal(ctx, x, y); |
| } |
| |
| template <typename VAL_TYPE, bool ISLEAST> VAL_TYPE MathFunctions::LeastGreatest( |
| FunctionContext* ctx, int num_args, const VAL_TYPE* args) { |
| DCHECK_GT(num_args, 0); |
| if (args[0].is_null) return VAL_TYPE::null(); |
| int result_idx = 0; |
| for (int i = 1; i < num_args; ++i) { |
| if (args[i].is_null) return VAL_TYPE::null(); |
| if (ISLEAST) { |
| if (args[i].val < args[result_idx].val) result_idx = i; |
| } else { |
| if (args[i].val > args[result_idx].val) result_idx = i; |
| } |
| } |
| return VAL_TYPE(args[result_idx].val); |
| } |
| |
| template <bool ISLEAST> StringVal MathFunctions::LeastGreatest( |
| FunctionContext* ctx, int num_args, const StringVal* args) { |
| DCHECK_GT(num_args, 0); |
| if (args[0].is_null) return StringVal::null(); |
| StringValue result_val = StringValue::FromStringVal(args[0]); |
| for (int i = 1; i < num_args; ++i) { |
| if (args[i].is_null) return StringVal::null(); |
| StringValue val = StringValue::FromStringVal(args[i]); |
| if (ISLEAST) { |
| if (val < result_val) result_val = val; |
| } else { |
| if (val > result_val) result_val = val; |
| } |
| } |
| StringVal result; |
| result_val.ToStringVal(&result); |
| return result; |
| } |
| |
| template <bool ISLEAST> TimestampVal MathFunctions::LeastGreatest( |
| FunctionContext* ctx, int num_args, const TimestampVal* args) { |
| DCHECK_GT(num_args, 0); |
| if (args[0].is_null) return TimestampVal::null(); |
| TimestampValue result_val = TimestampValue::FromTimestampVal(args[0]); |
| for (int i = 1; i < num_args; ++i) { |
| if (args[i].is_null) return TimestampVal::null(); |
| TimestampValue val = TimestampValue::FromTimestampVal(args[i]); |
| if (ISLEAST) { |
| if (val < result_val) result_val = val; |
| } else { |
| if (val > result_val) result_val = val; |
| } |
| } |
| TimestampVal result; |
| result_val.ToTimestampVal(&result); |
| return result; |
| } |
| |
| template <bool ISLEAST> DecimalVal MathFunctions::LeastGreatest( |
| FunctionContext* ctx, int num_args, const DecimalVal* args) { |
| DCHECK_GT(num_args, 0); |
| if (args[0].is_null) return DecimalVal::null(); |
| DecimalVal result_val = args[0]; |
| int type_byte_size = ctx->impl()->GetConstFnAttr(FunctionContextImpl::RETURN_TYPE_SIZE); |
| for (int i = 1; i < num_args; ++i) { |
| if (args[i].is_null) return DecimalVal::null(); |
| switch (type_byte_size) { |
| case 4: |
| if (ISLEAST) { |
| if (args[i].val4 < result_val.val4) result_val = args[i]; |
| } else { |
| if (args[i].val4 > result_val.val4) result_val = args[i]; |
| } |
| break; |
| case 8: |
| if (ISLEAST) { |
| if (args[i].val8 < result_val.val8) result_val = args[i]; |
| } else { |
| if (args[i].val8 > result_val.val8) result_val = args[i]; |
| } |
| break; |
| case 16: |
| if (ISLEAST) { |
| if (args[i].val16 < result_val.val16) result_val = args[i]; |
| } else { |
| if (args[i].val16 > result_val.val16) result_val = args[i]; |
| } |
| break; |
| default: |
| DCHECK(false); |
| } |
| } |
| return result_val; |
| } |
| |
| template <bool ISLEAST> DateVal MathFunctions::LeastGreatest( |
| FunctionContext* ctx, int num_args, const DateVal* args) { |
| DCHECK_GT(num_args, 0); |
| if (args[0].is_null) return DateVal::null(); |
| DateValue result_val = DateValue::FromDateVal(args[0]); |
| for (int i = 1; i < num_args; ++i) { |
| if (args[i].is_null) return DateVal::null(); |
| DateValue val = DateValue::FromDateVal(args[i]); |
| if (ISLEAST) { |
| if (val < result_val) result_val = val; |
| } else { |
| if (val > result_val) result_val = val; |
| } |
| } |
| return result_val.ToDateVal(); |
| } |
| |
| template TinyIntVal MathFunctions::Positive<TinyIntVal>( |
| FunctionContext* ctx, const TinyIntVal& val); |
| template SmallIntVal MathFunctions::Positive<SmallIntVal>( |
| FunctionContext* ctx, const SmallIntVal& val); |
| template IntVal MathFunctions::Positive<IntVal>( |
| FunctionContext* ctx, const IntVal& val); |
| template BigIntVal MathFunctions::Positive<BigIntVal>( |
| FunctionContext* ctx, const BigIntVal& val); |
| template FloatVal MathFunctions::Positive<FloatVal>( |
| FunctionContext* ctx, const FloatVal& val); |
| template DoubleVal MathFunctions::Positive<DoubleVal>( |
| FunctionContext* ctx, const DoubleVal& val); |
| template DecimalVal MathFunctions::Positive<DecimalVal>( |
| FunctionContext* ctx, const DecimalVal& val); |
| |
| template TinyIntVal MathFunctions::Negative<TinyIntVal>( |
| FunctionContext* ctx, const TinyIntVal& val); |
| template SmallIntVal MathFunctions::Negative<SmallIntVal>( |
| FunctionContext* ctx, const SmallIntVal& val); |
| template IntVal MathFunctions::Negative<IntVal>( |
| FunctionContext* ctx, const IntVal& val); |
| template BigIntVal MathFunctions::Negative<BigIntVal>( |
| FunctionContext* ctx, const BigIntVal& val); |
| template FloatVal MathFunctions::Negative<FloatVal>( |
| FunctionContext* ctx, const FloatVal& val); |
| template DoubleVal MathFunctions::Negative<DoubleVal>( |
| FunctionContext* ctx, const DoubleVal& val); |
| |
| template TinyIntVal MathFunctions::LeastGreatest<TinyIntVal, true>( |
| FunctionContext* ctx, int num_args, const TinyIntVal* args); |
| template SmallIntVal MathFunctions::LeastGreatest<SmallIntVal, true>( |
| FunctionContext* ctx, int num_args, const SmallIntVal* args); |
| template IntVal MathFunctions::LeastGreatest<IntVal, true>( |
| FunctionContext* ctx, int num_args, const IntVal* args); |
| template BigIntVal MathFunctions::LeastGreatest<BigIntVal, true>( |
| FunctionContext* ctx, int num_args, const BigIntVal* args); |
| template FloatVal MathFunctions::LeastGreatest<FloatVal, true>( |
| FunctionContext* ctx, int num_args, const FloatVal* args); |
| template DoubleVal MathFunctions::LeastGreatest<DoubleVal, true>( |
| FunctionContext* ctx, int num_args, const DoubleVal* args); |
| |
| template TinyIntVal MathFunctions::LeastGreatest<TinyIntVal, false>( |
| FunctionContext* ctx, int num_args, const TinyIntVal* args); |
| template SmallIntVal MathFunctions::LeastGreatest<SmallIntVal, false>( |
| FunctionContext* ctx, int num_args, const SmallIntVal* args); |
| template IntVal MathFunctions::LeastGreatest<IntVal, false>( |
| FunctionContext* ctx, int num_args, const IntVal* args); |
| template BigIntVal MathFunctions::LeastGreatest<BigIntVal, false>( |
| FunctionContext* ctx, int num_args, const BigIntVal* args); |
| template FloatVal MathFunctions::LeastGreatest<FloatVal, false>( |
| FunctionContext* ctx, int num_args, const FloatVal* args); |
| template DoubleVal MathFunctions::LeastGreatest<DoubleVal, false>( |
| FunctionContext* ctx, int num_args, const DoubleVal* args); |
| |
| template StringVal MathFunctions::LeastGreatest<true>( |
| FunctionContext* ctx, int num_args, const StringVal* args); |
| template StringVal MathFunctions::LeastGreatest<false>( |
| FunctionContext* ctx, int num_args, const StringVal* args); |
| |
| template TimestampVal MathFunctions::LeastGreatest<true>( |
| FunctionContext* ctx, int num_args, const TimestampVal* args); |
| template TimestampVal MathFunctions::LeastGreatest<false>( |
| FunctionContext* ctx, int num_args, const TimestampVal* args); |
| |
| template DecimalVal MathFunctions::LeastGreatest<true>( |
| FunctionContext* ctx, int num_args, const DecimalVal* args); |
| template DecimalVal MathFunctions::LeastGreatest<false>( |
| FunctionContext* ctx, int num_args, const DecimalVal* args); |
| |
| template DateVal MathFunctions::LeastGreatest<true>( |
| FunctionContext* ctx, int num_args, const DateVal* args); |
| template DateVal MathFunctions::LeastGreatest<false>( |
| FunctionContext* ctx, int num_args, const DateVal* args); |
| |
| } |