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

Reply via email to