This is an automated email from the ASF dual-hosted git repository. ravindra pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/arrow.git
The following commit(s) were added to refs/heads/master by this push: new 2b52844 ARROW-4205: [Gandiva] Support for decimal multiply 2b52844 is described below commit 2b52844d4597a3ec3526aa62787164228f64c4d0 Author: Pindikura Ravindra <ravin...@dremio.com> AuthorDate: Sat Mar 2 09:12:50 2019 +0530 ARROW-4205: [Gandiva] Support for decimal multiply - fastpath : safe multiply the two values (when result precision is < 38) - if large values, convert to 256-bit (boost), multiply and then, scale down - avoid converting to 256-bit wherever possible - track overflow (TODO: generate errors on overflow) Author: Pindikura Ravindra <ravin...@dremio.com> Closes #3745 from pravindra/gmult and squashes the following commits: 85e1e9dc <Pindikura Ravindra> ARROW-4205: fix typo cbbfd346 <Pindikura Ravindra> ARROW-4205: fix manylinux build d290c34e <Pindikura Ravindra> ARROW-4205: fix lint issues 52671696 <Pindikura Ravindra> ARROW-4205: Address review comments 53015a10 <Pindikura Ravindra> ARROW-4205: minor fixes f39d075e <Pindikura Ravindra> ARROW-4205: move boost using fns to cpp. b8483e6c <Pindikura Ravindra> ARROW-4205: Add java test ad5f4c1a <Pindikura Ravindra> ARROW-4205: Add to registry c26cc261 <Pindikura Ravindra> ARROW-4205: add unit tests 2db30b7a <Pindikura Ravindra> ARROW-4205: Support for decimal multiply --- ci/travis_script_manylinux.sh | 2 +- cpp/src/arrow/util/basic_decimal.cc | 15 ++ cpp/src/arrow/util/basic_decimal.h | 6 + cpp/src/arrow/util/decimal-test.cc | 8 + cpp/src/gandiva/CMakeLists.txt | 1 + cpp/src/gandiva/basic_decimal_scalar.h | 13 +- cpp/src/gandiva/decimal_ir.cc | 60 ++++++- cpp/src/gandiva/decimal_ir.h | 3 + cpp/src/gandiva/decimal_scalar.h | 4 + cpp/src/gandiva/decimal_xlarge.cc | 158 ++++++++++++++++++ .../decimal_ops.h => decimal_xlarge.h} | 25 +-- cpp/src/gandiva/exported_funcs.h | 8 +- cpp/src/gandiva/function_registry_arithmetic.cc | 13 +- cpp/src/gandiva/function_registry_string.cc | 10 ++ cpp/src/gandiva/precompiled/CMakeLists.txt | 3 +- cpp/src/gandiva/precompiled/decimal_ops.cc | 113 +++++++++++++ cpp/src/gandiva/precompiled/decimal_ops.h | 5 + cpp/src/gandiva/precompiled/decimal_ops_test.cc | 185 +++++++++++++++++++-- cpp/src/gandiva/precompiled/decimal_wrapper.cc | 18 ++ cpp/src/gandiva/tests/decimal_single_test.cc | 18 ++ .../gandiva/evaluator/ProjectorDecimalTest.java | 59 +++++++ python/manylinux1/scripts/build_boost.sh | 2 +- 22 files changed, 676 insertions(+), 53 deletions(-) diff --git a/ci/travis_script_manylinux.sh b/ci/travis_script_manylinux.sh index 8f2bd88..9606e54 100755 --- a/ci/travis_script_manylinux.sh +++ b/ci/travis_script_manylinux.sh @@ -53,7 +53,7 @@ for PYTHON_TUPLE in ${PYTHON_VERSIONS}; do -e UNICODE_WIDTH=$UNICODE_WIDTH \ -v $PWD:/io \ -v $PWD/../../:/arrow \ - quay.io/xhochy/arrow_manylinux1_x86_64_base:latest \ + quay.io/pravindra/arrow_manylinux1_x86_64_base:latest \ /io/build_arrow.sh # create a testing conda environment diff --git a/cpp/src/arrow/util/basic_decimal.cc b/cpp/src/arrow/util/basic_decimal.cc index f8d3a72..1af1a5b 100644 --- a/cpp/src/arrow/util/basic_decimal.cc +++ b/cpp/src/arrow/util/basic_decimal.cc @@ -122,6 +122,10 @@ static const BasicDecimal128 ScaleMultipliersHalf[] = { static constexpr uint64_t kIntMask = 0xFFFFFFFF; static constexpr auto kCarryBit = static_cast<uint64_t>(1) << static_cast<uint64_t>(32); +// same as ScaleMultipliers[38] - 1 +static constexpr BasicDecimal128 kMaxValue = + BasicDecimal128(5421010862427522170LL, 687399551400673280ULL - 1); + BasicDecimal128::BasicDecimal128(const uint8_t* bytes) : BasicDecimal128( BitUtil::FromLittleEndian(reinterpret_cast<const int64_t*>(bytes)[1]), @@ -150,6 +154,11 @@ BasicDecimal128& BasicDecimal128::Negate() { BasicDecimal128& BasicDecimal128::Abs() { return *this < 0 ? Negate() : *this; } +BasicDecimal128 BasicDecimal128::Abs(const BasicDecimal128& in) { + BasicDecimal128 result(in); + return result.Abs(); +} + BasicDecimal128& BasicDecimal128::operator+=(const BasicDecimal128& right) { const uint64_t sum = low_bits_ + right.low_bits_; high_bits_ = SafeSignedAdd<int64_t>(high_bits_, right.high_bits_); @@ -649,6 +658,8 @@ const BasicDecimal128& BasicDecimal128::GetScaleMultiplier(int32_t scale) { return ScaleMultipliers[scale]; } +const BasicDecimal128& BasicDecimal128::GetMaxValue() { return kMaxValue; } + BasicDecimal128 BasicDecimal128::IncreaseScaleBy(int32_t increase_by) const { DCHECK_GE(increase_by, 0); DCHECK_LE(increase_by, 38); @@ -660,6 +671,10 @@ BasicDecimal128 BasicDecimal128::ReduceScaleBy(int32_t reduce_by, bool round) co DCHECK_GE(reduce_by, 0); DCHECK_LE(reduce_by, 38); + if (reduce_by == 0) { + return *this; + } + BasicDecimal128 divisor(ScaleMultipliers[reduce_by]); BasicDecimal128 result; BasicDecimal128 remainder; diff --git a/cpp/src/arrow/util/basic_decimal.h b/cpp/src/arrow/util/basic_decimal.h index e19cb14..7929b11 100644 --- a/cpp/src/arrow/util/basic_decimal.h +++ b/cpp/src/arrow/util/basic_decimal.h @@ -66,6 +66,9 @@ class ARROW_EXPORT BasicDecimal128 { /// \brief Absolute value (in-place) BasicDecimal128& Abs(); + /// \brief Absolute value + static BasicDecimal128 Abs(const BasicDecimal128& left); + /// \brief Add a number to this one. The result is truncated to 128 bits. BasicDecimal128& operator+=(const BasicDecimal128& right); @@ -138,6 +141,9 @@ class ARROW_EXPORT BasicDecimal128 { /// \brief count the number of leading binary zeroes. int32_t CountLeadingBinaryZeros() const; + /// \brief Get the maximum valid unscaled decimal value. + static const BasicDecimal128& GetMaxValue(); + private: uint64_t low_bits_; int64_t high_bits_; diff --git a/cpp/src/arrow/util/decimal-test.cc b/cpp/src/arrow/util/decimal-test.cc index 3d6e23d..db4d35f 100644 --- a/cpp/src/arrow/util/decimal-test.cc +++ b/cpp/src/arrow/util/decimal-test.cc @@ -548,6 +548,10 @@ TEST(Decimal128Test, IncreaseScale) { Decimal128 result; int32_t out; + result = Decimal128("1234").IncreaseScaleBy(0); + ASSERT_OK(result.ToInteger(&out)); + ASSERT_EQ(1234, out); + result = Decimal128("1234").IncreaseScaleBy(3); ASSERT_OK(result.ToInteger(&out)); ASSERT_EQ(1234000, out); @@ -561,6 +565,10 @@ TEST(Decimal128Test, ReduceScaleAndRound) { Decimal128 result; int32_t out; + result = Decimal128("123456").ReduceScaleBy(0); + ASSERT_OK(result.ToInteger(&out)); + ASSERT_EQ(123456, out); + result = Decimal128("123456").ReduceScaleBy(1, false); ASSERT_OK(result.ToInteger(&out)); ASSERT_EQ(12345, out); diff --git a/cpp/src/gandiva/CMakeLists.txt b/cpp/src/gandiva/CMakeLists.txt index 9d03ef1..1885c49 100644 --- a/cpp/src/gandiva/CMakeLists.txt +++ b/cpp/src/gandiva/CMakeLists.txt @@ -44,6 +44,7 @@ set(SRC_FILES context_helper.cc decimal_ir.cc decimal_type_util.cc + decimal_xlarge.cc engine.cc date_utils.cc expr_decomposer.cc diff --git a/cpp/src/gandiva/basic_decimal_scalar.h b/cpp/src/gandiva/basic_decimal_scalar.h index fab8227..578fa23 100644 --- a/cpp/src/gandiva/basic_decimal_scalar.h +++ b/cpp/src/gandiva/basic_decimal_scalar.h @@ -27,14 +27,15 @@ using arrow::BasicDecimal128; /// Represents a 128-bit decimal value along with its precision and scale. class BasicDecimalScalar128 { public: - BasicDecimalScalar128(int64_t high_bits, uint64_t low_bits, int32_t precision, - int32_t scale) + constexpr BasicDecimalScalar128(int64_t high_bits, uint64_t low_bits, int32_t precision, + int32_t scale) : value_(high_bits, low_bits), precision_(precision), scale_(scale) {} - BasicDecimalScalar128(const BasicDecimal128& value, int32_t precision, int32_t scale) + constexpr BasicDecimalScalar128(const BasicDecimal128& value, int32_t precision, + int32_t scale) : value_(value), precision_(precision), scale_(scale) {} - BasicDecimalScalar128(int32_t precision, int32_t scale) + constexpr BasicDecimalScalar128(int32_t precision, int32_t scale) : precision_(precision), scale_(scale) {} int32_t scale() const { return scale_; } @@ -55,4 +56,8 @@ inline bool operator==(const BasicDecimalScalar128& left, left.scale() == right.scale(); } +inline BasicDecimalScalar128 operator-(const BasicDecimalScalar128& operand) { + return BasicDecimalScalar128{-operand.value(), operand.precision(), operand.scale()}; +} + } // namespace gandiva diff --git a/cpp/src/gandiva/decimal_ir.cc b/cpp/src/gandiva/decimal_ir.cc index f51f512..53727bb 100644 --- a/cpp/src/gandiva/decimal_ir.cc +++ b/cpp/src/gandiva/decimal_ir.cc @@ -353,6 +353,59 @@ Status DecimalIR::BuildSubtract() { return Status::OK(); } +Status DecimalIR::BuildMultiply() { + // Create fn prototype : + // int128_t + // multiply_decimal128_decimal128(int128_t x_value, int32_t x_precision, int32_t + // x_scale, + // int128_t y_value, int32_t y_precision, int32_t y_scale + // int32_t out_precision, int32_t out_scale) + auto i32 = types()->i32_type(); + auto i128 = types()->i128_type(); + auto function = BuildFunction("multiply_decimal128_decimal128", i128, + { + {"x_value", i128}, + {"x_precision", i32}, + {"x_scale", i32}, + {"y_value", i128}, + {"y_precision", i32}, + {"y_scale", i32}, + {"out_precision", i32}, + {"out_scale", i32}, + }); + + auto arg_iter = function->arg_begin(); + ValueFull x(&arg_iter[0], &arg_iter[1], &arg_iter[2]); + ValueFull y(&arg_iter[3], &arg_iter[4], &arg_iter[5]); + ValueFull out(nullptr, &arg_iter[6], &arg_iter[7]); + + auto entry = llvm::BasicBlock::Create(*context(), "entry", function); + ir_builder()->SetInsertPoint(entry); + + // Make call to pre-compiled IR function. + auto block = ir_builder()->GetInsertBlock(); + auto out_high_ptr = new llvm::AllocaInst(types()->i64_type(), 0, "out_hi", block); + auto out_low_ptr = new llvm::AllocaInst(types()->i64_type(), 0, "out_low", block); + auto x_split = ValueSplit::MakeFromInt128(this, x.value()); + auto y_split = ValueSplit::MakeFromInt128(this, y.value()); + + std::vector<llvm::Value*> args = { + x_split.high(), x_split.low(), x.precision(), x.scale(), + y_split.high(), y_split.low(), y.precision(), y.scale(), + out.precision(), out.scale(), out_high_ptr, out_low_ptr, + }; + ir_builder()->CreateCall( + module()->getFunction("multiply_internal_decimal128_decimal128"), args); + + auto out_high = ir_builder()->CreateLoad(out_high_ptr); + auto out_low = ir_builder()->CreateLoad(out_low_ptr); + auto result = ValueSplit(out_high, out_low).AsInt128(this); + ADD_TRACE_128("Multiply : result", result); + + ir_builder()->CreateRet(result); + return Status::OK(); +} + Status DecimalIR::AddFunctions(Engine* engine) { auto decimal_ir = std::make_shared<DecimalIR>(engine); @@ -362,11 +415,10 @@ Status DecimalIR::AddFunctions(Engine* engine) { // Lookup intrinsic functions decimal_ir->InitializeIntrinsics(); - // build "add" ARROW_RETURN_NOT_OK(decimal_ir->BuildAdd()); - - // build "subtract" - return decimal_ir->BuildSubtract(); + ARROW_RETURN_NOT_OK(decimal_ir->BuildSubtract()); + ARROW_RETURN_NOT_OK(decimal_ir->BuildMultiply()); + return Status::OK(); } // Do an bitwise-or of all the overflow bits. diff --git a/cpp/src/gandiva/decimal_ir.h b/cpp/src/gandiva/decimal_ir.h index fb9fe70..e552cf1 100644 --- a/cpp/src/gandiva/decimal_ir.h +++ b/cpp/src/gandiva/decimal_ir.h @@ -146,6 +146,9 @@ class DecimalIR : public FunctionIRBuilder { // Build the function for decimal subtraction. Status BuildSubtract(); + // Build the function for decimal multiplication. + Status BuildMultiply(); + // Add a trace in IR code. void AddTrace(const std::string& fmt, std::vector<llvm::Value*> args); diff --git a/cpp/src/gandiva/decimal_scalar.h b/cpp/src/gandiva/decimal_scalar.h index 5b38770..37dc6df 100644 --- a/cpp/src/gandiva/decimal_scalar.h +++ b/cpp/src/gandiva/decimal_scalar.h @@ -39,6 +39,10 @@ class DecimalScalar128 : public BasicDecimalScalar128 { DecimalScalar128(const std::string& value, int32_t precision, int32_t scale) : BasicDecimalScalar128(Decimal128(value), precision, scale) {} + /// \brief constructor creates a DecimalScalar128 from a BasicDecimalScalar128. + constexpr DecimalScalar128(const BasicDecimalScalar128& scalar) noexcept + : BasicDecimalScalar128(scalar) {} + inline std::string ToString() const { Decimal128 dvalue(value()); return dvalue.ToString(0) + "," + std::to_string(precision()) + "," + diff --git a/cpp/src/gandiva/decimal_xlarge.cc b/cpp/src/gandiva/decimal_xlarge.cc new file mode 100644 index 0000000..570cd88 --- /dev/null +++ b/cpp/src/gandiva/decimal_xlarge.cc @@ -0,0 +1,158 @@ +// 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. + +// Operations that can deal with very large values (256-bit). +// +// The intermediate results with decimal can be larger than what can fit into 128-bit, +// but the final results can fit in 128-bit after scaling down. These functions deal +// with operations on the intermediate values. +// + +#include "gandiva/decimal_xlarge.h" + +#include <limits> +#include <vector> +#include "boost/multiprecision/cpp_int.hpp" + +#include "arrow/util/basic_decimal.h" +#include "gandiva/decimal_type_util.h" +#include "gandiva/logging.h" + +#ifndef GANDIVA_UNIT_TEST +#include "gandiva/engine.h" +#include "gandiva/exported_funcs.h" + +namespace gandiva { + +void ExportedDecimalFunctions::AddMappings(Engine* engine) const { + std::vector<llvm::Type*> args; + auto types = engine->types(); + + // gdv_multiply_and_scale_down + args = {types->i64_type(), // int64_t x_high + types->i64_type(), // uint64_t x_low + types->i64_type(), // int64_t y_high + types->i64_type(), // uint64_t x_low + types->i32_type(), // int32_t reduce_scale_by + types->i64_ptr_type(), // int64_t* out_high + types->i64_ptr_type(), // uint64_t* out_low + types->i8_ptr_type()}; // bool* overflow + + engine->AddGlobalMappingForFunc( + "gdv_xlarge_multiply_and_scale_down", types->void_type() /*return_type*/, args, + reinterpret_cast<void*>(gdv_xlarge_multiply_and_scale_down)); +} +} // namespace gandiva + +#endif // !GANDIVA_UNIT_TEST + +using arrow::BasicDecimal128; +using boost::multiprecision::int256_t; + +namespace gandiva { +namespace internal { + +// Convert to 256-bit integer from 128-bit decimal. +static int256_t ConvertToInt256(BasicDecimal128 in) { + int256_t v = in.high_bits(); + v <<= 64; + v |= in.low_bits(); + return v; +} + +// Convert to 128-bit decimal from 256-bit integer. +// If there is an overflow, the output is undefined. +static BasicDecimal128 ConvertToDecimal128(int256_t in, bool* overflow) { + BasicDecimal128 result; + constexpr int256_t UINT64_MASK = std::numeric_limits<uint64_t>::max(); + + int256_t in_abs = abs(in); + bool is_negative = in < 0; + + uint64_t low = (in_abs & UINT64_MASK).convert_to<uint64_t>(); + in_abs >>= 64; + uint64_t high = (in_abs & UINT64_MASK).convert_to<uint64_t>(); + in_abs >>= 64; + + if (in_abs > 0) { + // we've shifted in by 128-bit, so nothing should be left. + *overflow = true; + } else if (high > INT64_MAX) { + // the high-bit must not be set (signed 128-bit). + *overflow = true; + } else { + result = BasicDecimal128(static_cast<int64_t>(high), low); + if (result > BasicDecimal128::GetMaxValue()) { + *overflow = true; + } + } + return is_negative ? -result : result; +} + +// divide input by 10^reduce_by, and round up the fractional part. +static int256_t ReduceScaleBy(int256_t in, int32_t reduce_by) { + DCHECK_GE(reduce_by, 0); + DCHECK_LE(reduce_by, 2 * DecimalTypeUtil::kMaxPrecision); + + if (reduce_by == 0) { + // nothing to do. + return in; + } + + int256_t divisor; + if (reduce_by <= DecimalTypeUtil::kMaxPrecision) { + divisor = ConvertToInt256(BasicDecimal128::GetScaleMultiplier(reduce_by)); + } else { + divisor = ConvertToInt256( + BasicDecimal128::GetScaleMultiplier(DecimalTypeUtil::kMaxPrecision)); + for (auto i = DecimalTypeUtil::kMaxPrecision; i < reduce_by; i++) { + divisor *= 10; + } + } + + DCHECK_GT(divisor, 0); + DCHECK_EQ(divisor % 2, 0); // multiple of 10. + auto result = in / divisor; + auto remainder = in % divisor; + // round up (same as BasicDecimal128::ReduceScaleBy) + if (abs(remainder) >= (divisor >> 1)) { + result += (in > 0 ? 1 : -1); + } + return result; +} + +} // namespace internal +} // namespace gandiva + +extern "C" { + +void gdv_xlarge_multiply_and_scale_down(int64_t x_high, uint64_t x_low, int64_t y_high, + uint64_t y_low, int32_t reduce_scale_by, + int64_t* out_high, uint64_t* out_low, + bool* overflow) { + BasicDecimal128 x{x_high, x_low}; + BasicDecimal128 y{y_high, y_low}; + auto intermediate_result = + gandiva::internal::ConvertToInt256(x) * gandiva::internal::ConvertToInt256(y); + intermediate_result = + gandiva::internal::ReduceScaleBy(intermediate_result, reduce_scale_by); + auto result = gandiva::internal::ConvertToDecimal128(intermediate_result, overflow); + *out_high = result.high_bits(); + *out_low = result.low_bits(); +} + +} // extern "C" diff --git a/cpp/src/gandiva/precompiled/decimal_ops.h b/cpp/src/gandiva/decimal_xlarge.h similarity index 53% copy from cpp/src/gandiva/precompiled/decimal_ops.h copy to cpp/src/gandiva/decimal_xlarge.h index 5a6c94b..9d48937 100644 --- a/cpp/src/gandiva/precompiled/decimal_ops.h +++ b/cpp/src/gandiva/decimal_xlarge.h @@ -18,22 +18,13 @@ #pragma once #include <cstdint> -#include <string> -#include "gandiva/basic_decimal_scalar.h" -namespace gandiva { -namespace decimalops { +/// Stub functions to deal with extra large decimals that can be accessed from LLVM-IR +/// code. +extern "C" { -/// Return the sum of 'x' and 'y'. -/// out_precision and out_scale are passed along for efficiency, they must match -/// the rules in DecimalTypeSql::GetResultType. -arrow::BasicDecimal128 Add(const BasicDecimalScalar128& x, const BasicDecimalScalar128& y, - int32_t out_precision, int32_t out_scale); - -/// Subtract 'y' from 'x', and return the result. -arrow::BasicDecimal128 Subtract(const BasicDecimalScalar128& x, - const BasicDecimalScalar128& y, int32_t out_precision, - int32_t out_scale); - -} // namespace decimalops -} // namespace gandiva +void gdv_xlarge_multiply_and_scale_down(int64_t x_high, uint64_t x_low, int64_t y_high, + uint64_t y_low, int32_t reduce_scale_by, + int64_t* out_high, uint64_t* out_low, + bool* overflow); +} diff --git a/cpp/src/gandiva/exported_funcs.h b/cpp/src/gandiva/exported_funcs.h index 4e028be..66259b2 100644 --- a/cpp/src/gandiva/exported_funcs.h +++ b/cpp/src/gandiva/exported_funcs.h @@ -45,12 +45,18 @@ class ExportedContextFunctions : public ExportedFuncsBase { }; REGISTER_EXPORTED_FUNCS(ExportedContextFunctions); -// Class for exporting Context functions +// Class for exporting Time functions class ExportedTimeFunctions : public ExportedFuncsBase { void AddMappings(Engine* engine) const override; }; REGISTER_EXPORTED_FUNCS(ExportedTimeFunctions); +// Class for exporting Decimal functions +class ExportedDecimalFunctions : public ExportedFuncsBase { + void AddMappings(Engine* engine) const override; +}; +REGISTER_EXPORTED_FUNCS(ExportedDecimalFunctions); + } // namespace gandiva #endif // GANDIVA_EXPORTED_FUNCS_H diff --git a/cpp/src/gandiva/function_registry_arithmetic.cc b/cpp/src/gandiva/function_registry_arithmetic.cc index 0a2ac93..921f91c 100644 --- a/cpp/src/gandiva/function_registry_arithmetic.cc +++ b/cpp/src/gandiva/function_registry_arithmetic.cc @@ -28,9 +28,6 @@ namespace gandiva { #define BINARY_RELATIONAL_BOOL_DATE_FN(name) \ NUMERIC_DATE_TYPES(BINARY_RELATIONAL_SAFE_NULL_IF_NULL, name) -#define UNARY_OCTET_LEN_FN(name) \ - UNARY_SAFE_NULL_IF_NULL(name, utf8, int32), UNARY_SAFE_NULL_IF_NULL(name, binary, int32) - #define UNARY_CAST_TO_FLOAT64(name) UNARY_SAFE_NULL_IF_NULL(castFLOAT8, name, float64) #define UNARY_CAST_TO_FLOAT32(name) UNARY_SAFE_NULL_IF_NULL(castFLOAT4, name, float32) @@ -59,6 +56,7 @@ std::vector<NativeFunction> GetArithmeticFunctionRegistry() { BINARY_SYMMETRIC_SAFE_NULL_IF_NULL(add, decimal128), BINARY_SYMMETRIC_SAFE_NULL_IF_NULL(subtract, decimal128), + BINARY_SYMMETRIC_SAFE_NULL_IF_NULL(multiply, decimal128), BINARY_RELATIONAL_BOOL_FN(equal), BINARY_RELATIONAL_BOOL_FN(not_equal), @@ -66,14 +64,7 @@ std::vector<NativeFunction> GetArithmeticFunctionRegistry() { BINARY_RELATIONAL_BOOL_DATE_FN(less_than), BINARY_RELATIONAL_BOOL_DATE_FN(less_than_or_equal_to), BINARY_RELATIONAL_BOOL_DATE_FN(greater_than), - BINARY_RELATIONAL_BOOL_DATE_FN(greater_than_or_equal_to), - - UNARY_OCTET_LEN_FN(octet_length), - UNARY_OCTET_LEN_FN(bit_length), - - UNARY_UNSAFE_NULL_IF_NULL(char_length, utf8, int32), - UNARY_UNSAFE_NULL_IF_NULL(length, utf8, int32), - UNARY_UNSAFE_NULL_IF_NULL(lengthUtf8, binary, int32)}; + BINARY_RELATIONAL_BOOL_DATE_FN(greater_than_or_equal_to)}; return arithmetic_fn_registry_; } diff --git a/cpp/src/gandiva/function_registry_string.cc b/cpp/src/gandiva/function_registry_string.cc index c97925a..bc78ec6 100644 --- a/cpp/src/gandiva/function_registry_string.cc +++ b/cpp/src/gandiva/function_registry_string.cc @@ -26,6 +26,9 @@ namespace gandiva { #define BINARY_RELATIONAL_SAFE_NULL_IF_NULL_UTF8_FN(name) \ BINARY_RELATIONAL_SAFE_NULL_IF_NULL(name, utf8) +#define UNARY_OCTET_LEN_FN(name) \ + UNARY_SAFE_NULL_IF_NULL(name, utf8, int32), UNARY_SAFE_NULL_IF_NULL(name, binary, int32) + std::vector<NativeFunction> GetStringFunctionRegistry() { static std::vector<NativeFunction> string_fn_registry_ = { BINARY_RELATIONAL_SAFE_NULL_IF_NULL_FN(equal), @@ -38,6 +41,13 @@ std::vector<NativeFunction> GetStringFunctionRegistry() { BINARY_RELATIONAL_SAFE_NULL_IF_NULL_UTF8_FN(starts_with), BINARY_RELATIONAL_SAFE_NULL_IF_NULL_UTF8_FN(ends_with), + UNARY_OCTET_LEN_FN(octet_length), + UNARY_OCTET_LEN_FN(bit_length), + + UNARY_UNSAFE_NULL_IF_NULL(char_length, utf8, int32), + UNARY_UNSAFE_NULL_IF_NULL(length, utf8, int32), + UNARY_UNSAFE_NULL_IF_NULL(lengthUtf8, binary, int32), + NativeFunction("upper", DataTypeVector{utf8()}, utf8(), kResultNullIfNull, "upper_utf8", NativeFunction::kNeedsContext), diff --git a/cpp/src/gandiva/precompiled/CMakeLists.txt b/cpp/src/gandiva/precompiled/CMakeLists.txt index 1249000..3ad0e09 100644 --- a/cpp/src/gandiva/precompiled/CMakeLists.txt +++ b/cpp/src/gandiva/precompiled/CMakeLists.txt @@ -128,5 +128,6 @@ if(ARROW_BUILD_TESTS) add_precompiled_unit_test(arithmetic_ops_test.cc arithmetic_ops.cc ../context_helper.cc) add_precompiled_unit_test(extended_math_ops_test.cc extended_math_ops.cc ../context_helper.cc) - add_precompiled_unit_test(decimal_ops_test.cc decimal_ops.cc ../decimal_type_util.cc) + add_precompiled_unit_test(decimal_ops_test.cc decimal_ops.cc ../decimal_type_util.cc + ../decimal_xlarge.cc) endif() diff --git a/cpp/src/gandiva/precompiled/decimal_ops.cc b/cpp/src/gandiva/precompiled/decimal_ops.cc index 887f42d..9aa1f41 100644 --- a/cpp/src/gandiva/precompiled/decimal_ops.cc +++ b/cpp/src/gandiva/precompiled/decimal_ops.cc @@ -22,6 +22,7 @@ #include <algorithm> #include "gandiva/decimal_type_util.h" +#include "gandiva/decimal_xlarge.h" #include "gandiva/logging.h" namespace gandiva { @@ -226,5 +227,117 @@ BasicDecimal128 Subtract(const BasicDecimalScalar128& x, const BasicDecimalScala return Add(x, {-y.value(), y.precision(), y.scale()}, out_precision, out_scale); } +// Multiply when the out_precision is 38, and there is no trimming of the scale i.e +// the intermediate value is the same as the final value. +static BasicDecimal128 MultiplyMaxPrecisionNoScaleDown(const BasicDecimalScalar128& x, + const BasicDecimalScalar128& y, + int32_t out_scale, + bool* overflow) { + DCHECK_EQ(x.scale() + y.scale(), out_scale); + + BasicDecimal128 result; + auto x_abs = BasicDecimal128::Abs(x.value()); + auto y_abs = BasicDecimal128::Abs(y.value()); + + if (x_abs > BasicDecimal128::GetMaxValue() / y_abs) { + *overflow = true; + } else { + // We've verified that the result will fit into 128 bits. + *overflow = false; + result = x.value() * y.value(); + } + return result; +} + +// Multiply when the out_precision is 38, and there is trimming of the scale i.e +// the intermediate value could be larger than the final value. +static BasicDecimal128 MultiplyMaxPrecisionAndScaleDown(const BasicDecimalScalar128& x, + const BasicDecimalScalar128& y, + int32_t out_scale, + bool* overflow) { + auto delta_scale = x.scale() + y.scale() - out_scale; + DCHECK_GT(delta_scale, 0); + + *overflow = false; + BasicDecimal128 result; + auto x_abs = BasicDecimal128::Abs(x.value()); + auto y_abs = BasicDecimal128::Abs(y.value()); + + // It's possible that the intermediate value does not fit in 128-bits, but the + // final value will (after scaling down). + bool needs_int256 = false; + int32_t total_leading_zeros = + x_abs.CountLeadingBinaryZeros() + y_abs.CountLeadingBinaryZeros(); + // This check is quick, but conservative. In some cases it will indicate that + // converting to 256 bits is necessary, when it's not actually the case. + needs_int256 = total_leading_zeros <= 128; + if (ARROW_PREDICT_FALSE(needs_int256)) { + int64_t result_high; + uint64_t result_low; + + // This requires converting to 256-bit, and we use the boost library for that. To + // avoid references to boost from the precompiled-to-ir code (this causes issues + // with symbol resolution at runtime), we use a wrapper exported from the CPP code. + gdv_xlarge_multiply_and_scale_down(x.value().high_bits(), x.value().low_bits(), + y.value().high_bits(), y.value().low_bits(), + delta_scale, &result_high, &result_low, overflow); + result = BasicDecimal128(result_high, result_low); + } else { + if (ARROW_PREDICT_TRUE(delta_scale <= 38)) { + // The largest value that result can have here is (2^64 - 1) * (2^63 - 1), which is + // greater than BasicDecimal128::kMaxValue. + result = x.value() * y.value(); + // Since delta_scale is greater than zero, result can now be at most + // ((2^64 - 1) * (2^63 - 1)) / 10, which is less than BasicDecimal128::kMaxValue, so + // there cannot be any overflow. + result = result.ReduceScaleBy(delta_scale); + } else { + // We are multiplying decimal(38, 38) by decimal(38, 38). The result should be a + // decimal(38, 37), so delta scale = 38 + 38 - 37 = 39. Since we are not in the + // 256 bit intermediate value case and we are scaling down by 39, then we are + // guaranteed that the result is 0 (even if we try to round). The largest possible + // intermediate result is 38 "9"s. If we scale down by 39, the leftmost 9 is now + // two digits to the right of the rightmost "visible" one. The reason why we have + // to handle this case separately is because a scale multiplier with a delta_scale + // 39 does not fit into 128 bit. + DCHECK_EQ(delta_scale, 39); + result = 0; + } + } + return result; +} + +// Multiply when the out_precision is 38. +static BasicDecimal128 MultiplyMaxPrecision(const BasicDecimalScalar128& x, + const BasicDecimalScalar128& y, + int32_t out_scale, bool* overflow) { + auto delta_scale = x.scale() + y.scale() - out_scale; + DCHECK_GE(delta_scale, 0); + if (delta_scale == 0) { + return MultiplyMaxPrecisionNoScaleDown(x, y, out_scale, overflow); + } else { + return MultiplyMaxPrecisionAndScaleDown(x, y, out_scale, overflow); + } +} + +BasicDecimal128 Multiply(const BasicDecimalScalar128& x, const BasicDecimalScalar128& y, + int32_t out_precision, int32_t out_scale, bool* overflow) { + BasicDecimal128 result; + *overflow = false; + if (out_precision < DecimalTypeUtil::kMaxPrecision) { + // fast-path multiply + result = x.value() * y.value(); + DCHECK_EQ(x.scale() + y.scale(), out_scale); + DCHECK_LE(BasicDecimal128::Abs(result), BasicDecimal128::GetMaxValue()); + } else if (x.value() == 0 || y.value() == 0) { + // Handle this separately to avoid divide-by-zero errors. + result = BasicDecimal128(0, 0); + } else { + result = MultiplyMaxPrecision(x, y, out_scale, overflow); + } + DCHECK(*overflow || BasicDecimal128::Abs(result) <= BasicDecimal128::GetMaxValue()); + return result; +} + } // namespace decimalops } // namespace gandiva diff --git a/cpp/src/gandiva/precompiled/decimal_ops.h b/cpp/src/gandiva/precompiled/decimal_ops.h index 5a6c94b..f45bc78 100644 --- a/cpp/src/gandiva/precompiled/decimal_ops.h +++ b/cpp/src/gandiva/precompiled/decimal_ops.h @@ -35,5 +35,10 @@ arrow::BasicDecimal128 Subtract(const BasicDecimalScalar128& x, const BasicDecimalScalar128& y, int32_t out_precision, int32_t out_scale); +/// Multiply 'x' from 'y', and return the result. +arrow::BasicDecimal128 Multiply(const BasicDecimalScalar128& x, + const BasicDecimalScalar128& y, int32_t out_precision, + int32_t out_scale, bool* overflow); + } // namespace decimalops } // namespace gandiva diff --git a/cpp/src/gandiva/precompiled/decimal_ops_test.cc b/cpp/src/gandiva/precompiled/decimal_ops_test.cc index ef2c402..f6d0b02 100644 --- a/cpp/src/gandiva/precompiled/decimal_ops_test.cc +++ b/cpp/src/gandiva/precompiled/decimal_ops_test.cc @@ -30,49 +30,84 @@ namespace gandiva { class TestDecimalSql : public ::testing::Test { protected: static void Verify(DecimalTypeUtil::Op op, const DecimalScalar128& x, - const DecimalScalar128& y, const DecimalScalar128& expected); + const DecimalScalar128& y, const DecimalScalar128& expected_result, + bool expected_overflow); void AddAndVerify(const DecimalScalar128& x, const DecimalScalar128& y, - const DecimalScalar128& expected) { - return Verify(DecimalTypeUtil::kOpAdd, x, y, expected); + const DecimalScalar128& expected_result) { + // TODO: overflow checks + return Verify(DecimalTypeUtil::kOpAdd, x, y, expected_result, false); } void SubtractAndVerify(const DecimalScalar128& x, const DecimalScalar128& y, - const DecimalScalar128& expected) { - return Verify(DecimalTypeUtil::kOpSubtract, x, y, expected); + const DecimalScalar128& expected_result) { + // TODO: overflow checks + return Verify(DecimalTypeUtil::kOpSubtract, x, y, expected_result, false); } + + void MultiplyAndVerify(const DecimalScalar128& x, const DecimalScalar128& y, + const DecimalScalar128& expected_result, + bool expected_overflow) { + return Verify(DecimalTypeUtil::kOpMultiply, x, y, expected_result, expected_overflow); + } + + void MultiplyAndVerifyAllSign(const DecimalScalar128& x, const DecimalScalar128& y, + const DecimalScalar128& expected_result, + bool expected_overflow); }; -#define EXPECT_DECIMAL_EQ(x, y, expected, actual) \ - EXPECT_EQ(expected, actual) << (x).ToString() << " + " << (y).ToString() \ - << " expected : " << expected.ToString() << " actual " \ - << actual.ToString() +#define EXPECT_DECIMAL_EQ(op, x, y, expected_result, expected_overflow, actual_result, \ + actual_overflow) \ + { \ + EXPECT_TRUE(expected_overflow == actual_overflow) \ + << op << "(" << (x).ToString() << " and " << (y).ToString() << ")" \ + << " expected overflow : " << expected_overflow \ + << " actual overflow : " << actual_overflow; \ + if (!expected_overflow) { \ + EXPECT_TRUE(expected_result == actual_result) \ + << op << "(" << (x).ToString() << " and " << (y).ToString() << ")" \ + << " expected : " << expected_result.ToString() \ + << " actual : " << actual_result.ToString(); \ + } \ + } void TestDecimalSql::Verify(DecimalTypeUtil::Op op, const DecimalScalar128& x, - const DecimalScalar128& y, const DecimalScalar128& expected) { + const DecimalScalar128& y, + const DecimalScalar128& expected_result, + bool expected_overflow) { auto t1 = std::make_shared<arrow::Decimal128Type>(x.precision(), x.scale()); auto t2 = std::make_shared<arrow::Decimal128Type>(y.precision(), y.scale()); + bool overflow = false; Decimal128TypePtr out_type; EXPECT_OK(DecimalTypeUtil::GetResultType(op, {t1, t2}, &out_type)); arrow::BasicDecimal128 out_value; + std::string op_name; switch (op) { case DecimalTypeUtil::kOpAdd: + op_name = "add"; out_value = decimalops::Add(x, y, out_type->precision(), out_type->scale()); break; case DecimalTypeUtil::kOpSubtract: + op_name = "subtract"; out_value = decimalops::Subtract(x, y, out_type->precision(), out_type->scale()); break; + case DecimalTypeUtil::kOpMultiply: + op_name = "multiply"; + out_value = + decimalops::Multiply(x, y, out_type->precision(), out_type->scale(), &overflow); + break; + default: // not implemented. ASSERT_FALSE(true); } - EXPECT_DECIMAL_EQ( - x, y, expected, - DecimalScalar128(out_value, out_type->precision(), out_type->scale())); + EXPECT_DECIMAL_EQ(op_name, x, y, expected_result, expected_overflow, + DecimalScalar128(out_value, out_type->precision(), out_type->scale()), + overflow); } TEST_F(TestDecimalSql, Add) { @@ -121,4 +156,128 @@ TEST_F(TestDecimalSql, Subtract) { DecimalScalar128{"-99999999999999999999999999999989999990", 38, 6}); } +void TestDecimalSql::MultiplyAndVerifyAllSign(const DecimalScalar128& left, + const DecimalScalar128& right, + const DecimalScalar128& expected_output, + bool expected_overflow) { + // both +ve + MultiplyAndVerify(left, right, expected_output, expected_overflow); + + // left -ve + MultiplyAndVerify(-left, right, -expected_output, expected_overflow); + + // right -ve + MultiplyAndVerify(left, -right, -expected_output, expected_overflow); + + // both -ve + MultiplyAndVerify(-left, -right, expected_output, expected_overflow); +} + +TEST_F(TestDecimalSql, Multiply) { + const std::string thirty_five_9s(35, '9'); + const std::string thirty_six_9s(36, '9'); + const std::string thirty_eight_9s(38, '9'); + + // fast-path : out_precision < 38 + MultiplyAndVerifyAllSign(DecimalScalar128{"201", 10, 3}, // x + DecimalScalar128{"301", 10, 2}, // y + DecimalScalar128{"60501", 21, 5}, // expected + false); // overflow + + // right 0 + MultiplyAndVerify(DecimalScalar128{"201", 20, 3}, // x + DecimalScalar128{"0", 20, 2}, // y + DecimalScalar128{"0", 38, 5}, // expected + false); // overflow + + // left 0 + MultiplyAndVerify(DecimalScalar128{"0", 20, 3}, // x + DecimalScalar128{"301", 20, 2}, // y + DecimalScalar128{"0", 38, 5}, // expected + false); // overflow + + // out_precision == 38, small input values, no trimming of scale (scale <= 6 doesn't + // get trimmed). + MultiplyAndVerify(DecimalScalar128{"201", 20, 3}, // x + DecimalScalar128{"301", 20, 2}, // y + DecimalScalar128{"60501", 38, 5}, // expected + false); // overflow + + // out_precision == 38, large values, no trimming of scale (scale <= 6 doesn't + // get trimmed). + MultiplyAndVerifyAllSign( + DecimalScalar128{"201", 20, 3}, // x + DecimalScalar128{thirty_five_9s, 35, 2}, // y + DecimalScalar128{"20099999999999999999999999999999999799", 38, 5}, // expected + false); // overflow + + // out_precision == 38, very large values, no trimming of scale (scale <= 6 doesn't + // get trimmed). overflow expected. + MultiplyAndVerifyAllSign(DecimalScalar128{"201", 20, 3}, // x + DecimalScalar128{thirty_six_9s, 35, 2}, // y + DecimalScalar128{"0", 38, 5}, // expected + true); // overflow + + MultiplyAndVerifyAllSign(DecimalScalar128{"201", 20, 3}, // x + DecimalScalar128{thirty_eight_9s, 35, 2}, // y + DecimalScalar128{"0", 38, 5}, // expected + true); // overflow + + // out_precision == 38, small input values, trimming of scale. + MultiplyAndVerifyAllSign(DecimalScalar128{"201", 20, 5}, // x + DecimalScalar128{"301", 20, 5}, // y + DecimalScalar128{"61", 38, 7}, // expected + false); // overflow + + // out_precision == 38, large values, trimming of scale. + MultiplyAndVerifyAllSign( + DecimalScalar128{"201", 20, 5}, // x + DecimalScalar128{thirty_five_9s, 35, 5}, // y + DecimalScalar128{"2010000000000000000000000000000000", 38, 6}, // expected + false); // overflow + + // out_precision == 38, very large values, trimming of scale (requires convert to 256). + MultiplyAndVerifyAllSign( + DecimalScalar128{thirty_five_9s, 38, 20}, // x + DecimalScalar128{thirty_six_9s, 38, 20}, // y + DecimalScalar128{"9999999999999999999999999999999999890", 38, 6}, // expected + false); // overflow + + // out_precision == 38, very large values, trimming of scale (requires convert to 256). + // should cause overflow. + MultiplyAndVerifyAllSign(DecimalScalar128{thirty_five_9s, 38, 4}, // x + DecimalScalar128{thirty_six_9s, 38, 4}, // y + DecimalScalar128{"0", 38, 6}, // expected + true); // overflow + + // corner cases. + MultiplyAndVerifyAllSign( + DecimalScalar128{0, UINT64_MAX, 38, 4}, // x + DecimalScalar128{0, UINT64_MAX, 38, 4}, // y + DecimalScalar128{"3402823669209384634264811192843491082", 38, 6}, // expected + false); // overflow + + MultiplyAndVerifyAllSign( + DecimalScalar128{0, UINT64_MAX, 38, 4}, // x + DecimalScalar128{0, INT64_MAX, 38, 4}, // y + DecimalScalar128{"1701411834604692317040171876053197783", 38, 6}, // expected + false); // overflow + + MultiplyAndVerifyAllSign(DecimalScalar128{"201", 38, 38}, // x + DecimalScalar128{"301", 38, 38}, // y + DecimalScalar128{"0", 38, 37}, // expected + false); // overflow + + MultiplyAndVerifyAllSign(DecimalScalar128{0, UINT64_MAX, 38, 38}, // x + DecimalScalar128{0, UINT64_MAX, 38, 38}, // y + DecimalScalar128{"0", 38, 37}, // expected + false); // overflow + + MultiplyAndVerifyAllSign( + DecimalScalar128{thirty_five_9s, 38, 38}, // x + DecimalScalar128{thirty_six_9s, 38, 38}, // y + DecimalScalar128{"100000000000000000000000000000000", 38, 37}, // expected + false); // overflow +} + } // namespace gandiva diff --git a/cpp/src/gandiva/precompiled/decimal_wrapper.cc b/cpp/src/gandiva/precompiled/decimal_wrapper.cc index f327a50..1066c5c 100644 --- a/cpp/src/gandiva/precompiled/decimal_wrapper.cc +++ b/cpp/src/gandiva/precompiled/decimal_wrapper.cc @@ -34,4 +34,22 @@ void add_large_decimal128_decimal128(int64_t x_high, uint64_t x_low, int32_t x_p *out_low = out.low_bits(); } +FORCE_INLINE +void multiply_internal_decimal128_decimal128(int64_t x_high, uint64_t x_low, + int32_t x_precision, int32_t x_scale, + int64_t y_high, uint64_t y_low, + int32_t y_precision, int32_t y_scale, + int32_t out_precision, int32_t out_scale, + int64_t* out_high, uint64_t* out_low) { + gandiva::BasicDecimalScalar128 x(x_high, x_low, x_precision, x_scale); + gandiva::BasicDecimalScalar128 y(y_high, y_low, y_precision, y_scale); + bool overflow; + + // TODO ravindra: generate error on overflows (ARROW-4570). + arrow::BasicDecimal128 out = + gandiva::decimalops::Multiply(x, y, out_precision, out_scale, &overflow); + *out_high = out.high_bits(); + *out_low = out.low_bits(); +} + } // extern "C" diff --git a/cpp/src/gandiva/tests/decimal_single_test.cc b/cpp/src/gandiva/tests/decimal_single_test.cc index a83137f..0cc93e7 100644 --- a/cpp/src/gandiva/tests/decimal_single_test.cc +++ b/cpp/src/gandiva/tests/decimal_single_test.cc @@ -61,6 +61,11 @@ class TestDecimalOps : public ::testing::Test { Verify(DecimalTypeUtil::kOpSubtract, "subtract", x, y, expected); } + void MultiplyAndVerify(const DecimalScalar128& x, const DecimalScalar128& y, + const DecimalScalar128& expected) { + Verify(DecimalTypeUtil::kOpMultiply, "multiply", x, y, expected); + } + protected: arrow::MemoryPool* pool_; }; @@ -253,4 +258,17 @@ TEST_F(TestDecimalOps, TestSubtract) { decimal_literal("-3211", 32, 3)); // expected } +// Lots of unit tests for multiply in decimal_ops_test.cc. So, keeping this basic. +TEST_F(TestDecimalOps, TestMultiply) { + // fast-path + MultiplyAndVerify(decimal_literal("201", 10, 3), // x + decimal_literal("301", 10, 2), // y + decimal_literal("60501", 21, 5)); // expected + + // max precision + MultiplyAndVerify(DecimalScalar128(std::string(35, '9'), 38, 20), // x + DecimalScalar128(std::string(36, '9'), 38, 20), // x + DecimalScalar128("9999999999999999999999999999999999890", 38, 6)); +} + } // namespace gandiva diff --git a/java/gandiva/src/test/java/org/apache/arrow/gandiva/evaluator/ProjectorDecimalTest.java b/java/gandiva/src/test/java/org/apache/arrow/gandiva/evaluator/ProjectorDecimalTest.java index a3a0b48..e4a7cc3 100644 --- a/java/gandiva/src/test/java/org/apache/arrow/gandiva/evaluator/ProjectorDecimalTest.java +++ b/java/gandiva/src/test/java/org/apache/arrow/gandiva/evaluator/ProjectorDecimalTest.java @@ -154,4 +154,63 @@ public class ProjectorDecimalTest extends org.apache.arrow.gandiva.evaluator.Bas releaseValueVectors(output); eval.close(); } + + @Test + public void test_multiply() throws GandivaException { + int precision = 38; + int scale = 8; + ArrowType.Decimal decimal = new ArrowType.Decimal(precision, scale); + Field a = Field.nullable("a", decimal); + Field b = Field.nullable("b", decimal); + List<Field> args = Lists.newArrayList(a, b); + + ArrowType.Decimal outputType = DecimalTypeUtil.getResultTypeForOperation(DecimalTypeUtil + .OperationType.MULTIPLY, decimal, decimal); + Field retType = Field.nullable("c", outputType); + ExpressionTree root = TreeBuilder.makeExpression("multiply", args, retType); + + List<ExpressionTree> exprs = Lists.newArrayList(root); + + Schema schema = new Schema(args); + Projector eval = Projector.make(schema, exprs); + + int numRows = 4; + byte[] validity = new byte[]{(byte) 255}; + String[] aValues = new String[]{"1.12345678","2.12345678","3.12345678", "999999999999.99999999"}; + String[] bValues = new String[]{"2.12345678","3.12345678","4.12345678", "999999999999.99999999"}; + + DecimalVector valuesa = decimalVector(aValues, precision, scale); + DecimalVector valuesb = decimalVector(bValues, precision, scale); + ArrowRecordBatch batch = + new ArrowRecordBatch( + numRows, + Lists.newArrayList(new ArrowFieldNode(numRows, 0), new ArrowFieldNode(numRows, 0)), + Lists.newArrayList(valuesa.getValidityBuffer(), valuesa.getDataBuffer(), + valuesb.getValidityBuffer(), valuesb.getDataBuffer())); + + DecimalVector outVector = new DecimalVector("decimal_output", allocator, outputType.getPrecision(), + outputType.getScale()); + outVector.allocateNew(numRows); + + List<ValueVector> output = new ArrayList<ValueVector>(); + output.add(outVector); + eval.evaluate(batch, output); + + // should have scaled down. + BigDecimal[] expOutput = new BigDecimal[]{BigDecimal.valueOf(2.385612), + BigDecimal.valueOf(6.632525), + BigDecimal.valueOf(12.879439), + new BigDecimal("999999999999999999980000.000000")}; + + for (int i = 0; i < 4; i++) { + assertFalse(outVector.isNull(i)); + assertTrue("index : " + i + " failed compare", expOutput[i].compareTo(outVector.getObject(i) + ) == 0); + } + + // free buffers + releaseRecordBatch(batch); + releaseValueVectors(output); + eval.close(); + } } diff --git a/python/manylinux1/scripts/build_boost.sh b/python/manylinux1/scripts/build_boost.sh index 43eeab2..fbf8002 100755 --- a/python/manylinux1/scripts/build_boost.sh +++ b/python/manylinux1/scripts/build_boost.sh @@ -25,7 +25,7 @@ mkdir /arrow_boost pushd /boost_${BOOST_VERSION_UNDERSCORE} ./bootstrap.sh ./b2 tools/bcp -./dist/bin/bcp --namespace=arrow_boost --namespace-alias filesystem date_time system regex build algorithm locale format variant multi_precision/cpp_int /arrow_boost +./dist/bin/bcp --namespace=arrow_boost --namespace-alias filesystem date_time system regex build algorithm locale format variant multiprecision/cpp_int /arrow_boost popd pushd /arrow_boost