This is an automated email from the ASF dual-hosted git repository.
deniskuzZ pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/hive.git
The following commit(s) were added to refs/heads/master by this push:
new 32efdcd9e6c HIVE-26731: UDF trunc produces incorrect result for higher
values (#6878)
32efdcd9e6c is described below
commit 32efdcd9e6cfdfbecd30a4bf454c66f76be6281d
Author: gopigowtham <[email protected]>
AuthorDate: Mon Oct 19 21:19:30 2026 +0530
HIVE-26731: UDF trunc produces incorrect result for higher values (#6878)
---
.../ql/exec/vector/expressions/TruncDecimal.java | 24 +--
.../ql/exec/vector/expressions/TruncFloat.java | 24 +--
.../hive/ql/udf/generic/GenericUDFTrunc.java | 81 +++------
.../hadoop/hive/ql/udf/generic/TruncUtils.java | 101 ++++++++++++
.../expressions/TestVectorMathFunctions.java | 101 ++++++++++++
.../hive/ql/udf/generic/TestGenericUDFTrunc.java | 182 +++++++++++++++++++++
.../clientpositive/udf_trunc_large_number.q | 34 ++++
.../llap/udf_trunc_large_number.q.out | 171 +++++++++++++++++++
.../hive/common/type/FastHiveDecimalImpl.java | 10 ++
.../hadoop/hive/common/type/TestHiveDecimal.java | 11 ++
10 files changed, 640 insertions(+), 99 deletions(-)
diff --git
a/ql/src/java/org/apache/hadoop/hive/ql/exec/vector/expressions/TruncDecimal.java
b/ql/src/java/org/apache/hadoop/hive/ql/exec/vector/expressions/TruncDecimal.java
index 8dbd4d8a56c..b16f268cb22 100644
---
a/ql/src/java/org/apache/hadoop/hive/ql/exec/vector/expressions/TruncDecimal.java
+++
b/ql/src/java/org/apache/hadoop/hive/ql/exec/vector/expressions/TruncDecimal.java
@@ -19,11 +19,11 @@
package org.apache.hadoop.hive.ql.exec.vector.expressions;
-import org.apache.hadoop.hive.common.type.HiveDecimal;
import org.apache.hadoop.hive.ql.exec.vector.ColumnVector;
import org.apache.hadoop.hive.ql.exec.vector.DecimalColumnVector;
import org.apache.hadoop.hive.ql.exec.vector.VectorExpressionDescriptor;
import
org.apache.hadoop.hive.ql.exec.vector.VectorExpressionDescriptor.ArgumentType;
+import org.apache.hadoop.hive.ql.udf.generic.TruncUtils;
import org.apache.hadoop.hive.serde2.io.HiveDecimalWritable;
/**
@@ -45,26 +45,8 @@ public TruncDecimal(int colNum, int scale, int
outputColumnNum) {
@Override
protected void trunc(ColumnVector inputColVector, ColumnVector
outputColVector, int i) {
- HiveDecimal input = ((DecimalColumnVector)
inputColVector).vector[i].getHiveDecimal();
-
- HiveDecimal output = trunc(input);
- ((DecimalColumnVector) outputColVector).vector[i] = new
HiveDecimalWritable(output);
- }
-
- protected HiveDecimal trunc(HiveDecimal input) {
- HiveDecimal pow = HiveDecimal.create(Math.pow(10, Math.abs(scale)));
-
- if (scale >= 0) {
- if (scale != 0) {
- long longValue = input.multiply(pow).longValue();
- return HiveDecimal.create(longValue).divide(pow);
- } else {
- return HiveDecimal.create(input.longValue());
- }
- } else {
- long longValue2 = input.divide(pow).longValue();
- return HiveDecimal.create(longValue2).multiply(pow);
- }
+ HiveDecimalWritable input = ((DecimalColumnVector)
inputColVector).vector[i];
+ TruncUtils.trunc(input, scale, ((DecimalColumnVector)
outputColVector).vector[i]);
}
protected ArgumentType getInputColumnType() {
diff --git
a/ql/src/java/org/apache/hadoop/hive/ql/exec/vector/expressions/TruncFloat.java
b/ql/src/java/org/apache/hadoop/hive/ql/exec/vector/expressions/TruncFloat.java
index 5870b440440..622e0690481 100644
---
a/ql/src/java/org/apache/hadoop/hive/ql/exec/vector/expressions/TruncFloat.java
+++
b/ql/src/java/org/apache/hadoop/hive/ql/exec/vector/expressions/TruncFloat.java
@@ -19,7 +19,6 @@
package org.apache.hadoop.hive.ql.exec.vector.expressions;
-import java.math.BigDecimal;
import java.util.Arrays;
import org.apache.hadoop.hive.ql.exec.vector.ColumnVector;
@@ -29,6 +28,7 @@
import
org.apache.hadoop.hive.ql.exec.vector.VectorExpressionDescriptor.Descriptor;
import org.apache.hadoop.hive.ql.exec.vector.VectorizedRowBatch;
import org.apache.hadoop.hive.ql.metadata.HiveException;
+import org.apache.hadoop.hive.ql.udf.generic.TruncUtils;
/**
* Vectorized implementation of trunc(number, scale) function for float/double
input
@@ -127,26 +127,8 @@ public void evaluate(VectorizedRowBatch batch) throws
HiveException {
}
protected void trunc(ColumnVector inputColVector, ColumnVector
outputColVector, int i) {
- BigDecimal input = BigDecimal.valueOf(((DoubleColumnVector)
inputColVector).vector[i]);
-
- double output = trunc(input).doubleValue();
- ((DoubleColumnVector) outputColVector).vector[i] = output;
- }
-
- protected BigDecimal trunc(BigDecimal input) {
- BigDecimal pow = BigDecimal.valueOf(Math.pow(10, Math.abs(scale)));
-
- if (scale >= 0) {
- if (scale != 0) {
- long longValue = input.multiply(pow).longValue();
- return BigDecimal.valueOf(longValue).divide(pow);
- } else {
- return BigDecimal.valueOf(input.longValue());
- }
- } else {
- long longValue2 = input.divide(pow).longValue();
- return BigDecimal.valueOf(longValue2).multiply(pow);
- }
+ double input = ((DoubleColumnVector) inputColVector).vector[i];
+ ((DoubleColumnVector) outputColVector).vector[i] = TruncUtils.trunc(input,
scale);
}
@Override
diff --git
a/ql/src/java/org/apache/hadoop/hive/ql/udf/generic/GenericUDFTrunc.java
b/ql/src/java/org/apache/hadoop/hive/ql/udf/generic/GenericUDFTrunc.java
index bccb81b4a1b..0f1e6020c8f 100644
--- a/ql/src/java/org/apache/hadoop/hive/ql/udf/generic/GenericUDFTrunc.java
+++ b/ql/src/java/org/apache/hadoop/hive/ql/udf/generic/GenericUDFTrunc.java
@@ -19,10 +19,7 @@
package org.apache.hadoop.hive.ql.udf.generic;
-import java.math.BigDecimal;
-
import org.apache.hadoop.hive.common.type.Date;
-import org.apache.hadoop.hive.common.type.HiveDecimal;
import org.apache.hadoop.hive.common.type.Timestamp;
import org.apache.hadoop.hive.ql.exec.Description;
import org.apache.hadoop.hive.ql.exec.UDFArgumentException;
@@ -97,6 +94,15 @@ public class GenericUDFTrunc extends GenericUDF {
private transient PrimitiveCategory inputType2;
private final Date date = new Date();
private final Text output = new Text();
+
+ private final ByteWritable byteOutput = new ByteWritable();
+ private final ShortWritable shortOutput = new ShortWritable();
+ private final IntWritable intOutput = new IntWritable();
+ private final LongWritable longOutput = new LongWritable();
+ private final FloatWritable floatOutput = new FloatWritable();
+ private final DoubleWritable doubleOutput = new DoubleWritable();
+ private final HiveDecimalWritable decimalOutput = new HiveDecimalWritable();
+
private transient String fmtInput;
private transient PrimitiveObjectInspector inputOI;
private transient PrimitiveObjectInspector inputScaleOI;
@@ -388,44 +394,44 @@ private Object evaluateNumber(DeferredObject[] arguments)
case DECIMAL:
HiveDecimalWritable decimalWritable =
(HiveDecimalWritable) inputOI.getPrimitiveWritableObject(input);
- HiveDecimal dec = trunc(decimalWritable.getHiveDecimal(), scale);
- if (dec == null) {
- return null;
- }
- return new HiveDecimalWritable(dec);
+ TruncUtils.trunc(decimalWritable, scale, decimalOutput);
+ return decimalOutput;
case BYTE:
ByteWritable byteWritable = (ByteWritable)
inputOI.getPrimitiveWritableObject(input);
if (scale >= 0) {
return byteWritable;
- } else {
- return new ByteWritable((byte) trunc(byteWritable.get(), scale));
}
+ byteOutput.set((byte) TruncUtils.trunc(byteWritable.get(), scale));
+ return byteOutput;
case SHORT:
ShortWritable shortWritable = (ShortWritable)
inputOI.getPrimitiveWritableObject(input);
if (scale >= 0) {
return shortWritable;
- } else {
- return new ShortWritable((short) trunc(shortWritable.get(), scale));
}
+ shortOutput.set((short) TruncUtils.trunc(shortWritable.get(), scale));
+ return shortOutput;
case INT:
IntWritable intWritable = (IntWritable)
inputOI.getPrimitiveWritableObject(input);
if (scale >= 0) {
return intWritable;
- } else {
- return new IntWritable((int) trunc(intWritable.get(), scale));
}
+ intOutput.set((int) TruncUtils.trunc(intWritable.get(), scale));
+ return intOutput;
case LONG:
LongWritable longWritable = (LongWritable)
inputOI.getPrimitiveWritableObject(input);
if (scale >= 0) {
return longWritable;
- } else {
- return new LongWritable(trunc(longWritable.get(), scale));
}
+ longOutput.set(TruncUtils.trunc(longWritable.get(), scale));
+ return longOutput;
case FLOAT:
float f = ((FloatWritable)
inputOI.getPrimitiveWritableObject(input)).get();
- return new FloatWritable((float) trunc(f, scale));
+ floatOutput.set((float) TruncUtils.trunc(f, scale));
+ return floatOutput;
case DOUBLE:
- return trunc(((DoubleWritable)
inputOI.getPrimitiveWritableObject(input)), scale);
+ double d = ((DoubleWritable)
inputOI.getPrimitiveWritableObject(input)).get();
+ doubleOutput.set(TruncUtils.trunc(d, scale));
+ return doubleOutput;
default:
throw new UDFArgumentTypeException(0,
"Only numeric or string group data types are allowed for TRUNC
function. Got "
@@ -458,43 +464,4 @@ private Date evalDate(Date d) throws UDFArgumentException {
return null;
}
}
-
- protected HiveDecimal trunc(HiveDecimal input, int scale) {
- BigDecimal bigDecimal = trunc(input.bigDecimalValue(), scale);
- return HiveDecimal.create(bigDecimal);
- }
-
- protected long trunc(long input, int scale) {
- return trunc(BigDecimal.valueOf(input), scale).longValue();
- }
-
- protected double trunc(double input, int scale) {
- return trunc(BigDecimal.valueOf(input), scale).doubleValue();
- }
-
- protected DoubleWritable trunc(DoubleWritable input, int scale) {
- BigDecimal bigDecimal = new BigDecimal(input.get());
- BigDecimal trunc = trunc(bigDecimal, scale);
- DoubleWritable doubleWritable = new DoubleWritable(trunc.doubleValue());
- return doubleWritable;
- }
-
- protected BigDecimal trunc(BigDecimal input, int scale) {
- BigDecimal output = new BigDecimal(0);
- BigDecimal pow = BigDecimal.valueOf(Math.pow(10, Math.abs(scale)));
- if (scale >= 0) {
- pow = BigDecimal.valueOf(Math.pow(10, scale));
- if (scale != 0) {
- long longValue = input.multiply(pow).longValue();
- output = BigDecimal.valueOf(longValue).divide(pow);
- } else {
- output = BigDecimal.valueOf(input.longValue());
- }
- } else {
- long longValue2 = input.divide(pow).longValue();
- output = BigDecimal.valueOf(longValue2).multiply(pow);
- }
- return output;
- }
-
}
diff --git a/ql/src/java/org/apache/hadoop/hive/ql/udf/generic/TruncUtils.java
b/ql/src/java/org/apache/hadoop/hive/ql/udf/generic/TruncUtils.java
new file mode 100644
index 00000000000..b3774499726
--- /dev/null
+++ b/ql/src/java/org/apache/hadoop/hive/ql/udf/generic/TruncUtils.java
@@ -0,0 +1,101 @@
+/*
+ * 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.
+ */
+
+package org.apache.hadoop.hive.ql.udf.generic;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+
+import org.apache.hadoop.hive.common.type.FastHiveDecimalImpl;
+import org.apache.hadoop.hive.common.type.HiveDecimal;
+import org.apache.hadoop.hive.serde2.io.HiveDecimalWritable;
+
+/**
+ * Utility class for generic trunc UDF and its vectorized implementations.
+ * <p>
+ * Truncation drops the digits after the given scale, rounding towards zero. A
negative scale
+ * zeroes digits left of the decimal point. The input is never scaled into a
long, so the result
+ * is exact over the whole range of the input type.
+ */
+public final class TruncUtils {
+
+ // HiveDecimal rounding takes the legacy BigDecimal rounding mode constants.
+ @SuppressWarnings("deprecation")
+ private static final int DECIMAL_ROUND_DOWN = BigDecimal.ROUND_DOWN;
+
+ // Every decimal is smaller than 10^38 in magnitude, so lower scales also
truncate it to zero.
+ private static final int MIN_DECIMAL_SCALE = -HiveDecimal.MAX_PRECISION;
+
+ // Doubles of at least this magnitude have no fractional part.
+ private static final double MIN_INTEGRAL_DOUBLE = 0x1p52;
+
+ // Double.MAX_VALUE is about 1.8E308, so lower scales truncate every double
to zero.
+ private static final int MIN_DOUBLE_SCALE = -309;
+
+ private TruncUtils() {
+ }
+
+ /**
+ * Truncates the input decimal into the output writable. The input is not
modified.
+ */
+ public static void trunc(HiveDecimalWritable input, int scale,
HiveDecimalWritable output) {
+ output.set(input);
+ output.mutateSetScale(Math.max(scale, MIN_DECIMAL_SCALE),
DECIMAL_ROUND_DOWN);
+ }
+
+ /**
+ * Truncating a double is approximate, as the double value itself is
approximate. Like
+ * {@link RoundUtils#round(double, int)}, this truncates the shortest
decimal representation
+ * of the double, so trunc(0.3, 1) is 0.3 even though the double nearest to
0.3 is below it.
+ *
+ * @param input input value
+ * @param scale decimal place
+ * @return truncated value
+ */
+ public static double trunc(double input, int scale) {
+ if (Double.isNaN(input) || Double.isInfinite(input)) {
+ return input;
+ }
+ if (scale == 0) {
+ // Common trunc(x) case, no BigDecimal needed: the cast truncates
towards zero.
+ return Math.abs(input) < MIN_INTEGRAL_DOUBLE ? (double) (long) input :
input;
+ }
+ if (scale < MIN_DOUBLE_SCALE) {
+ return 0.0;
+ }
+ BigDecimal value = BigDecimal.valueOf(input);
+ // Nothing to drop. Checked first since setScale would pad the value up to
the given scale.
+ if (scale >= value.scale()) {
+ return input;
+ }
+ return value.setScale(scale, RoundingMode.DOWN).doubleValue();
+ }
+
+ public static long trunc(long input, int scale) {
+ if (scale >= 0) {
+ return input;
+ }
+ if (scale < -FastHiveDecimalImpl.MAX_LONG_POWER_OF_TEN) {
+ // Every long is smaller than 10^19 in magnitude.
+ return 0;
+ }
+ // The remainder has the sign of the input, so this truncates towards zero.
+ return input - input % FastHiveDecimalImpl.powerOfTen(-scale);
+ }
+}
diff --git
a/ql/src/test/org/apache/hadoop/hive/ql/exec/vector/expressions/TestVectorMathFunctions.java
b/ql/src/test/org/apache/hadoop/hive/ql/exec/vector/expressions/TestVectorMathFunctions.java
index 0fe2f6ea514..7a152be2fb1 100644
---
a/ql/src/test/org/apache/hadoop/hive/ql/exec/vector/expressions/TestVectorMathFunctions.java
+++
b/ql/src/test/org/apache/hadoop/hive/ql/exec/vector/expressions/TestVectorMathFunctions.java
@@ -24,12 +24,14 @@
import java.util.Random;
import org.apache.hadoop.hive.common.type.Date;
+import org.apache.hadoop.hive.common.type.HiveDecimal;
import org.apache.hadoop.hive.conf.HiveConf;
import org.junit.Assert;
import org.apache.hadoop.hive.serde2.RandomTypeUtil;
import org.apache.hadoop.hive.common.type.Timestamp;
import org.apache.hadoop.hive.ql.exec.vector.BytesColumnVector;
+import org.apache.hadoop.hive.ql.exec.vector.DecimalColumnVector;
import org.apache.hadoop.hive.ql.exec.vector.DoubleColumnVector;
import org.apache.hadoop.hive.ql.exec.vector.LongColumnVector;
import org.apache.hadoop.hive.ql.exec.vector.TimestampColumnVector;
@@ -120,6 +122,105 @@ public void testRoundToDecimalPlaces() throws
HiveException {
Assert.assertEquals(1.2346d, resultV.vector[7], Double.MIN_VALUE);
}
+ @Test
+ public void testTruncDecimal() throws HiveException {
+ // HIVE-26731: the values must not be scaled into a long
+ String[] input = { "123456711889.122345897", "-123456711889.122345897",
+ "12345678901234567890123456789.987654321", null };
+ verifyTruncDecimal(new TruncDecimal(0, 8, 1), input,
"123456711889.12234589",
+ "-123456711889.12234589", "12345678901234567890123456789.98765432",
null);
+ verifyTruncDecimal(new TruncDecimal(0, 19, 1), input, input);
+ verifyTruncDecimal(new TruncDecimal(0, -4, 1), input, "123456710000",
"-123456710000",
+ "12345678901234567890123450000", null);
+ verifyTruncDecimal(new TruncDecimal(0, Integer.MIN_VALUE, 1), input, "0",
"0", "0", null);
+ verifyTruncDecimal(new TruncDecimalNoScale(0, 1), input, "123456711889",
"-123456711889",
+ "12345678901234567890123456789", null);
+ }
+
+ private static void verifyTruncDecimal(VectorExpression expr, String[] input,
+ String... expected) throws HiveException {
+ VectorizedRowBatch batch = new VectorizedRowBatch(2, input.length);
+ DecimalColumnVector inV = new DecimalColumnVector(input.length, 38, 9);
+ DecimalColumnVector outV = new DecimalColumnVector(input.length, 38, 9);
+ for (int i = 0; i < input.length; i++) {
+ if (input[i] == null) {
+ inV.noNulls = false;
+ inV.isNull[i] = true;
+ } else {
+ inV.vector[i].set(HiveDecimal.create(input[i]));
+ }
+ }
+ batch.cols[0] = inV;
+ batch.cols[1] = outV;
+ batch.size = input.length;
+
+ expr.evaluate(batch);
+
+ for (int i = 0; i < input.length; i++) {
+ Assert.assertEquals(expected[i] == null, outV.isNull[i]);
+ if (expected[i] != null) {
+ Assert.assertEquals(expr + " row " + i,
HiveDecimal.create(expected[i]),
+ outV.vector[i].getHiveDecimal());
+ Assert.assertEquals("input must not be modified",
HiveDecimal.create(input[i]),
+ inV.vector[i].getHiveDecimal());
+ }
+ }
+ }
+
+ @Test
+ public void testTruncDouble() throws HiveException {
+ Double[] input = { 123456711889.12234, -123456711889.12234, 0.3, 1.0E300,
Double.NaN, null };
+ verifyTruncDouble(new TruncFloat(0, 8, 1), input,
+ 123456711889.12234, -123456711889.12234, 0.3, 1.0E300, Double.NaN,
null);
+ verifyTruncDouble(new TruncFloat(0, 3, 1), input,
+ 123456711889.122, -123456711889.122, 0.3, 1.0E300, Double.NaN, null);
+ verifyTruncDouble(new TruncFloat(0, -5, 1), input,
+ 123456700000.0, -123456700000.0, 0.0, 1.0E300, Double.NaN, null);
+ verifyTruncDouble(new TruncFloatNoScale(0, 1), input,
+ 123456711889.0, -123456711889.0, 0.0, 1.0E300, Double.NaN, null);
+ }
+
+ @Test
+ public void testTruncDoubleRepeating() throws HiveException {
+ VectorizedRowBatch batch = getVectorizedRowBatchDoubleInDoubleOut();
+ DoubleColumnVector inV = (DoubleColumnVector) batch.cols[0];
+ inV.vector[0] = 123456711889.12234;
+ inV.isRepeating = true;
+
+ new TruncFloat(0, 3, 1).evaluate(batch);
+
+ DoubleColumnVector outV = (DoubleColumnVector) batch.cols[1];
+ Assert.assertTrue(outV.isRepeating);
+ Assert.assertEquals(123456711889.122, outV.vector[0], 0.0);
+ }
+
+ private static void verifyTruncDouble(VectorExpression expr, Double[] input,
+ Double... expected) throws HiveException {
+ VectorizedRowBatch batch = new VectorizedRowBatch(2, input.length);
+ DoubleColumnVector inV = new DoubleColumnVector(input.length);
+ DoubleColumnVector outV = new DoubleColumnVector(input.length);
+ for (int i = 0; i < input.length; i++) {
+ if (input[i] == null) {
+ inV.noNulls = false;
+ inV.isNull[i] = true;
+ } else {
+ inV.vector[i] = input[i];
+ }
+ }
+ batch.cols[0] = inV;
+ batch.cols[1] = outV;
+ batch.size = input.length;
+
+ expr.evaluate(batch);
+
+ for (int i = 0; i < input.length; i++) {
+ Assert.assertEquals(expected[i] == null, outV.isNull[i]);
+ if (expected[i] != null) {
+ Assert.assertEquals(expr + " row " + i, expected[i], outV.vector[i],
0.0);
+ }
+ }
+ }
+
private static final int DAYS_LIMIT = 365 * 9999;
//approximate, so we get some negative values:
private static final int SMALLEST_EPOCH_DAY = -365 * 1969;
diff --git
a/ql/src/test/org/apache/hadoop/hive/ql/udf/generic/TestGenericUDFTrunc.java
b/ql/src/test/org/apache/hadoop/hive/ql/udf/generic/TestGenericUDFTrunc.java
index a1f21f80d71..14dca9f9681 100644
--- a/ql/src/test/org/apache/hadoop/hive/ql/udf/generic/TestGenericUDFTrunc.java
+++ b/ql/src/test/org/apache/hadoop/hive/ql/udf/generic/TestGenericUDFTrunc.java
@@ -20,18 +20,29 @@
package org.apache.hadoop.hive.ql.udf.generic;
import org.apache.hadoop.hive.common.type.Date;
+import org.apache.hadoop.hive.common.type.HiveDecimal;
import org.apache.hadoop.hive.common.type.Timestamp;
import org.apache.hadoop.hive.ql.metadata.HiveException;
import org.apache.hadoop.hive.ql.udf.generic.GenericUDF.DeferredJavaObject;
import org.apache.hadoop.hive.ql.udf.generic.GenericUDF.DeferredObject;
+import org.apache.hadoop.hive.serde2.io.ByteWritable;
import org.apache.hadoop.hive.serde2.io.DateWritableV2;
+import org.apache.hadoop.hive.serde2.io.DoubleWritable;
+import org.apache.hadoop.hive.serde2.io.HiveDecimalWritable;
+import org.apache.hadoop.hive.serde2.io.ShortWritable;
import org.apache.hadoop.hive.serde2.io.TimestampWritableV2;
import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector;
import
org.apache.hadoop.hive.serde2.objectinspector.primitive.PrimitiveObjectInspectorFactory;
+import org.apache.hadoop.hive.serde2.typeinfo.TypeInfoFactory;
+import org.apache.hadoop.io.FloatWritable;
+import org.apache.hadoop.io.IntWritable;
+import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
+import org.apache.hadoop.io.Writable;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
import org.junit.Test;
/**
@@ -765,6 +776,177 @@ public void testDateWritableToDateWithYearFormat() throws
HiveException {
runAndVerify("2016-01-01", udf, initArgs, evalArgs);
}
+ @Test
+ public void testDecimalLargerThanLongWhenScaled() throws HiveException {
+ // HIVE-26731: 123456711889.122345897 * 10^8 does not fit in a long
+ verifyDecimal("123456711889.12234589", "123456711889.122345897", 21, 9, 8);
+ verifyDecimal("-123456711889.12234589", "-123456711889.122345897", 21, 9,
8);
+ verifyDecimal("123456711889.122345897", "123456711889.122345897", 21, 9,
19);
+ verifyDecimal("0.0000000001", "0.00000000012345678901234567890123456789",
38, 38, 10);
+ }
+
+ @Test
+ public void testDecimalIntegerPartLargerThanLong() throws HiveException {
+ String input = "12345678901234567890123456789.987654321";
+ verifyDecimal("12345678901234567890123456789.98765", input, 38, 9, 5);
+ verifyDecimal("12345678901234567890123456789", input, 38, 9, 0);
+ verifyDecimal("12345678901234567890123450000", input, 38, 9, -4);
+ verifyDecimal("-12345678901234567890123450000", "-" + input, 38, 9, -4);
+ verifyDecimal("0", input, 38, 9, -29);
+ verifyDecimal("0", input, 38, 9, Integer.MIN_VALUE);
+
+ String max = "99999999999999999999999999999999999999";
+ verifyDecimal("99999999999999999999999999999999999990", max, 38, 0, -1);
+ verifyDecimal("0", max, 38, 0, -38);
+ verifyDecimal("0", "-" + max, 38, 0, -39);
+ verifyDecimal("0", max, 38, 0, Integer.MIN_VALUE);
+ }
+
+ @Test
+ public void testDecimalWithoutScale() throws HiveException {
+ HiveDecimalWritable input = new
HiveDecimalWritable("-12345678901234567890123456789.987654321");
+ Object result = evaluate(
+ PrimitiveObjectInspectorFactory.getPrimitiveWritableObjectInspector(
+ TypeInfoFactory.getDecimalTypeInfo(38, 9)), input);
+ assertEquals(new HiveDecimalWritable("-12345678901234567890123456789"),
result);
+ assertEquals("input must not be modified",
+ new HiveDecimalWritable("-12345678901234567890123456789.987654321"),
input);
+ }
+
+ @Test
+ public void testDouble() throws HiveException {
+ verifyDouble(123456711889.12234, 123456711889.12234, 8);
+ verifyDouble(123456711889.122, 123456711889.12234, 3);
+ verifyDouble(-123456711889.0, -123456711889.12234, 0);
+ verifyDouble(-123456700000.0, -123456711889.12234, -5);
+ verifyDouble(1.0E300, 1.0E300, 30);
+ verifyDouble(9.9E299, 9.99E299, -298);
+ verifyDouble(0.0, 0.5, -1);
+ // The shortest decimal representation is truncated, not the exact binary
value
+ verifyDouble(0.3, 0.3, 1);
+ verifyDouble(0.29, 0.29, 2);
+ verifyDouble(0.0, -0.5, 0);
+ verifyDouble(Double.NaN, Double.NaN, 2);
+ verifyDouble(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, 2);
+ verifyDouble(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, 0);
+ }
+
+ @Test(timeout = 10000)
+ public void testDoubleExtremeScale() throws HiveException {
+ verifyDouble(Double.MIN_VALUE, Double.MIN_VALUE, 325);
+ verifyDouble(0.0, Double.MIN_VALUE, 323);
+ verifyDouble(1.0E308, 1.7976931348623157E308, -308);
+ verifyDouble(0.0, -Double.MAX_VALUE, -309);
+ // Neither the result nor the work may depend on the magnitude of the scale
+ verifyDouble(12.5, 12.5, Integer.MAX_VALUE);
+ verifyDouble(0.0, 12.5, Integer.MIN_VALUE);
+ for (int i = 0; i < 1000; i++) {
+ verifyDouble(12.5, 12.5, 100000000);
+ verifyDouble(0.0, 12.5, -100000000);
+ }
+ }
+
+ @Test
+ public void testFloat() throws HiveException {
+ verifyFloat(12345.123f, 12345.1234f, 3);
+ verifyFloat(12345.1234f, 12345.1234f, 10);
+ verifyFloat(-12345f, -12345.1234f, 0);
+ verifyFloat(-12300f, -12345.1234f, -2);
+ }
+
+ @Test
+ public void testIntegerTypes() throws HiveException {
+ verifyLong(Long.MAX_VALUE, Long.MAX_VALUE, 2);
+ verifyLong(9223372036854775000L, Long.MAX_VALUE, -3);
+ verifyLong(-9000000000000000000L, Long.MIN_VALUE, -18);
+ verifyLong(0L, Long.MIN_VALUE, -19);
+ verifyLong(0L, Long.MAX_VALUE, Integer.MIN_VALUE);
+
+ assertEquals(new IntWritable(-2147483000), evaluate(
+ PrimitiveObjectInspectorFactory.writableIntObjectInspector, new
IntWritable(-2147483648), -3));
+ assertEquals(new IntWritable(2147483647), evaluate(
+ PrimitiveObjectInspectorFactory.writableIntObjectInspector, new
IntWritable(2147483647), 3));
+ assertEquals(new ShortWritable((short) -32700), evaluate(
+ PrimitiveObjectInspectorFactory.writableShortObjectInspector,
+ new ShortWritable((short) -32768), -2));
+ assertEquals(new ByteWritable((byte) 120), evaluate(
+ PrimitiveObjectInspectorFactory.writableByteObjectInspector, new
ByteWritable((byte) 127), -1));
+ }
+
+ @Test
+ public void testNonConstantScale() throws HiveException {
+ GenericUDFTrunc udf = new GenericUDFTrunc();
+ udf.initialize(new ObjectInspector[] {
+ PrimitiveObjectInspectorFactory.getPrimitiveWritableObjectInspector(
+ TypeInfoFactory.getDecimalTypeInfo(21, 9)),
+ PrimitiveObjectInspectorFactory.writableIntObjectInspector });
+
+ HiveDecimalWritable input = new
HiveDecimalWritable("123456711889.122345897");
+ int[] scales = { 8, 0, -4, 19 };
+ String[] expected = { "123456711889.12234589", "123456711889",
"123456710000",
+ "123456711889.122345897" };
+ for (int i = 0; i < scales.length; i++) {
+ Object result = udf.evaluate(new DeferredObject[] {
+ new DeferredJavaObject(input), new DeferredJavaObject(new
IntWritable(scales[i])) });
+ assertEquals("scale " + scales[i], new HiveDecimalWritable(expected[i]),
result);
+ }
+ assertEquals("input must not be modified", new
HiveDecimalWritable("123456711889.122345897"),
+ input);
+ }
+
+ @Test
+ public void testNullInput() throws HiveException {
+
assertNull(evaluate(PrimitiveObjectInspectorFactory.writableDoubleObjectInspector,
null, 2));
+ }
+
+ private static void verifyDecimal(String expected, String input, int
precision, int scale,
+ int truncScale) throws HiveException {
+ Object result = evaluate(
+ PrimitiveObjectInspectorFactory.getPrimitiveWritableObjectInspector(
+ TypeInfoFactory.getDecimalTypeInfo(precision, scale)),
+ new HiveDecimalWritable(input), truncScale);
+ assertEquals("trunc(" + input + ", " + truncScale + ")",
+ HiveDecimal.create(expected), ((HiveDecimalWritable)
result).getHiveDecimal());
+ }
+
+ private static void verifyDouble(double expected, double input, int scale)
+ throws HiveException {
+ Object result =
evaluate(PrimitiveObjectInspectorFactory.writableDoubleObjectInspector,
+ new DoubleWritable(input), scale);
+ assertEquals("trunc(" + input + ", " + scale + ")", expected,
+ ((DoubleWritable) result).get(), 0.0);
+ }
+
+ private static void verifyFloat(float expected, float input, int scale)
throws HiveException {
+ Object result =
evaluate(PrimitiveObjectInspectorFactory.writableFloatObjectInspector,
+ new FloatWritable(input), scale);
+ assertEquals("trunc(" + input + ", " + scale + ")", expected,
+ ((FloatWritable) result).get(), 0.0f);
+ }
+
+ private static void verifyLong(long expected, long input, int scale) throws
HiveException {
+ Object result =
evaluate(PrimitiveObjectInspectorFactory.writableLongObjectInspector,
+ new LongWritable(input), scale);
+ assertEquals("trunc(" + input + ", " + scale + ")", new
LongWritable(expected), result);
+ }
+
+ private static Object evaluate(ObjectInspector valueOI, Writable value, int
scale)
+ throws HiveException {
+ GenericUDFTrunc udf = new GenericUDFTrunc();
+ IntWritable scaleWritable = new IntWritable(scale);
+ udf.initialize(new ObjectInspector[] { valueOI,
+
PrimitiveObjectInspectorFactory.getPrimitiveWritableConstantObjectInspector(
+ TypeInfoFactory.intTypeInfo, scaleWritable) });
+ return udf.evaluate(new DeferredObject[] {
+ new DeferredJavaObject(value), new DeferredJavaObject(scaleWritable)
});
+ }
+
+ private static Object evaluate(ObjectInspector valueOI, Writable value)
throws HiveException {
+ GenericUDFTrunc udf = new GenericUDFTrunc();
+ udf.initialize(new ObjectInspector[] { valueOI });
+ return udf.evaluate(new DeferredObject[] { new DeferredJavaObject(value)
});
+ }
+
private void runAndVerify(String expResult, GenericUDF udf,
ObjectInspector[] initArgs,
DeferredObject[] evalArgs) throws HiveException {
udf.initialize(initArgs);
diff --git a/ql/src/test/queries/clientpositive/udf_trunc_large_number.q
b/ql/src/test/queries/clientpositive/udf_trunc_large_number.q
new file mode 100644
index 00000000000..35204ddbbcc
--- /dev/null
+++ b/ql/src/test/queries/clientpositive/udf_trunc_large_number.q
@@ -0,0 +1,34 @@
+-- SORT_QUERY_RESULTS
+-- HIVE-26731: trunc must stay exact when the input, scaled to the requested
+-- number of decimal places, no longer fits in a long.
+
+SELECT trunc(123456711889.122345897, 8), trunc(-123456711889.122345897, 8),
+ trunc(123456711889.122345897, 19), trunc(123456711889.122345897),
+ trunc(123456711889.122345897, -4);
+
+CREATE TABLE trunc_large_number (d DECIMAL(38,9), c DOUBLE, b BIGINT) STORED
AS ORC;
+
+INSERT INTO trunc_large_number VALUES
+ (123456711889.122345897, 123456711889.12234, 9223372036854775807),
+ (-123456711889.122345897, -123456711889.12234, -9223372036854775807),
+ (12345678901234567890123456789.987654321, 0.3, 12345),
+ (NULL, NULL, NULL);
+
+set hive.fetch.task.conversion=none;
+set hive.vectorized.execution.enabled=true;
+
+EXPLAIN VECTORIZATION ONLY EXPRESSION
+SELECT trunc(d, 8), trunc(d), trunc(c, 8), trunc(c) FROM trunc_large_number;
+
+SELECT d, trunc(d, 8), trunc(d, -4), trunc(d), trunc(d, -30) FROM
trunc_large_number;
+SELECT c, trunc(c, 8), trunc(c, 1), trunc(c), trunc(c, -5) FROM
trunc_large_number;
+SELECT b, trunc(b, -3), trunc(b, -19) FROM trunc_large_number;
+
+-- Row mode must give the same results as vectorized execution
+set hive.vectorized.execution.enabled=false;
+
+SELECT d, trunc(d, 8), trunc(d, -4), trunc(d), trunc(d, -30) FROM
trunc_large_number;
+SELECT c, trunc(c, 8), trunc(c, 1), trunc(c), trunc(c, -5) FROM
trunc_large_number;
+SELECT b, trunc(b, -3), trunc(b, -19) FROM trunc_large_number;
+
+DROP TABLE trunc_large_number;
diff --git
a/ql/src/test/results/clientpositive/llap/udf_trunc_large_number.q.out
b/ql/src/test/results/clientpositive/llap/udf_trunc_large_number.q.out
new file mode 100644
index 00000000000..5aacaae695b
--- /dev/null
+++ b/ql/src/test/results/clientpositive/llap/udf_trunc_large_number.q.out
@@ -0,0 +1,171 @@
+PREHOOK: query: SELECT trunc(123456711889.122345897, 8),
trunc(-123456711889.122345897, 8),
+ trunc(123456711889.122345897, 19), trunc(123456711889.122345897),
+ trunc(123456711889.122345897, -4)
+PREHOOK: type: QUERY
+PREHOOK: Input: _dummy_database@_dummy_table
+#### A masked pattern was here ####
+POSTHOOK: query: SELECT trunc(123456711889.122345897, 8),
trunc(-123456711889.122345897, 8),
+ trunc(123456711889.122345897, 19), trunc(123456711889.122345897),
+ trunc(123456711889.122345897, -4)
+POSTHOOK: type: QUERY
+POSTHOOK: Input: _dummy_database@_dummy_table
+#### A masked pattern was here ####
+123456711889.12234589 -123456711889.12234589
123456711889.1223458970000000000 123456711889 123456710000
+PREHOOK: query: CREATE TABLE trunc_large_number (d DECIMAL(38,9), c DOUBLE, b
BIGINT) STORED AS ORC
+PREHOOK: type: CREATETABLE
+PREHOOK: Output: database:default
+PREHOOK: Output: default@trunc_large_number
+POSTHOOK: query: CREATE TABLE trunc_large_number (d DECIMAL(38,9), c DOUBLE, b
BIGINT) STORED AS ORC
+POSTHOOK: type: CREATETABLE
+POSTHOOK: Output: database:default
+POSTHOOK: Output: default@trunc_large_number
+PREHOOK: query: INSERT INTO trunc_large_number VALUES
+ (123456711889.122345897, 123456711889.12234, 9223372036854775807),
+ (-123456711889.122345897, -123456711889.12234, -9223372036854775807),
+ (12345678901234567890123456789.987654321, 0.3, 12345),
+ (NULL, NULL, NULL)
+PREHOOK: type: QUERY
+PREHOOK: Input: _dummy_database@_dummy_table
+PREHOOK: Output: default@trunc_large_number
+POSTHOOK: query: INSERT INTO trunc_large_number VALUES
+ (123456711889.122345897, 123456711889.12234, 9223372036854775807),
+ (-123456711889.122345897, -123456711889.12234, -9223372036854775807),
+ (12345678901234567890123456789.987654321, 0.3, 12345),
+ (NULL, NULL, NULL)
+POSTHOOK: type: QUERY
+POSTHOOK: Input: _dummy_database@_dummy_table
+POSTHOOK: Output: default@trunc_large_number
+POSTHOOK: Lineage: trunc_large_number.b SCRIPT []
+POSTHOOK: Lineage: trunc_large_number.c SCRIPT []
+POSTHOOK: Lineage: trunc_large_number.d SCRIPT []
+PREHOOK: query: EXPLAIN VECTORIZATION ONLY EXPRESSION
+SELECT trunc(d, 8), trunc(d), trunc(c, 8), trunc(c) FROM trunc_large_number
+PREHOOK: type: QUERY
+PREHOOK: Input: default@trunc_large_number
+#### A masked pattern was here ####
+POSTHOOK: query: EXPLAIN VECTORIZATION ONLY EXPRESSION
+SELECT trunc(d, 8), trunc(d), trunc(c, 8), trunc(c) FROM trunc_large_number
+POSTHOOK: type: QUERY
+POSTHOOK: Input: default@trunc_large_number
+#### A masked pattern was here ####
+PLAN VECTORIZATION:
+ enabled: true
+ enabledConditionsMet: [hive.vectorized.execution.enabled IS true]
+
+STAGE DEPENDENCIES:
+ Stage-1 is a root stage
+ Stage-0 depends on stages: Stage-1
+
+STAGE PLANS:
+ Stage: Stage-1
+ Tez
+ Vertices:
+ Map 1
+ Map Operator Tree:
+ TableScan Vectorization:
+ native: true
+ Select Vectorization:
+ className: VectorSelectOperator
+ native: true
+ projectedOutputColumnNums: [5, 6, 7, 8]
+ selectExpressions: TruncDecimal(col 0, scale 8) ->
5:decimal(37,8), TruncDecimalNoScale(col 0, scale 0) -> 6:decimal(29,0),
TruncFloat(col 1, scale 8) -> 7:double, TruncFloatNoScale(col 1, scale 0) ->
8:double
+ File Sink Vectorization:
+ className: VectorFileSinkOperator
+ native: false
+ Execution mode: vectorized, llap
+ LLAP IO: all inputs
+ Map Vectorization:
+ enabled: true
+ enabledConditionsMet:
hive.vectorized.use.vectorized.input.format IS true
+ inputFormatFeatureSupport: [DECIMAL_64]
+ featureSupportInUse: [DECIMAL_64]
+ inputFileFormats:
org.apache.hadoop.hive.ql.io.orc.OrcInputFormat
+ allNative: false
+ usesVectorUDFAdaptor: false
+ vectorized: true
+
+ Stage: Stage-0
+ Fetch Operator
+
+PREHOOK: query: SELECT d, trunc(d, 8), trunc(d, -4), trunc(d), trunc(d, -30)
FROM trunc_large_number
+PREHOOK: type: QUERY
+PREHOOK: Input: default@trunc_large_number
+#### A masked pattern was here ####
+POSTHOOK: query: SELECT d, trunc(d, 8), trunc(d, -4), trunc(d), trunc(d, -30)
FROM trunc_large_number
+POSTHOOK: type: QUERY
+POSTHOOK: Input: default@trunc_large_number
+#### A masked pattern was here ####
+-123456711889.122345897 -123456711889.12234589 -123456710000
-123456711889 0
+123456711889.122345897 123456711889.12234589 123456710000 123456711889 0
+12345678901234567890123456789.987654321
12345678901234567890123456789.98765432 12345678901234567890123450000
12345678901234567890123456789 0
+NULL NULL NULL NULL NULL
+PREHOOK: query: SELECT c, trunc(c, 8), trunc(c, 1), trunc(c), trunc(c, -5)
FROM trunc_large_number
+PREHOOK: type: QUERY
+PREHOOK: Input: default@trunc_large_number
+#### A masked pattern was here ####
+POSTHOOK: query: SELECT c, trunc(c, 8), trunc(c, 1), trunc(c), trunc(c, -5)
FROM trunc_large_number
+POSTHOOK: type: QUERY
+POSTHOOK: Input: default@trunc_large_number
+#### A masked pattern was here ####
+-1.2345671188912234E11 -1.2345671188912234E11 -1.234567118891E11
-1.23456711889E11 -1.234567E11
+0.3 0.3 0.3 0.0 0.0
+1.2345671188912234E11 1.2345671188912234E11 1.234567118891E11
1.23456711889E11 1.234567E11
+NULL NULL NULL NULL NULL
+PREHOOK: query: SELECT b, trunc(b, -3), trunc(b, -19) FROM trunc_large_number
+PREHOOK: type: QUERY
+PREHOOK: Input: default@trunc_large_number
+#### A masked pattern was here ####
+POSTHOOK: query: SELECT b, trunc(b, -3), trunc(b, -19) FROM trunc_large_number
+POSTHOOK: type: QUERY
+POSTHOOK: Input: default@trunc_large_number
+#### A masked pattern was here ####
+-9223372036854775807 -9223372036854775000 0
+12345 12000 0
+9223372036854775807 9223372036854775000 0
+NULL NULL NULL
+PREHOOK: query: SELECT d, trunc(d, 8), trunc(d, -4), trunc(d), trunc(d, -30)
FROM trunc_large_number
+PREHOOK: type: QUERY
+PREHOOK: Input: default@trunc_large_number
+#### A masked pattern was here ####
+POSTHOOK: query: SELECT d, trunc(d, 8), trunc(d, -4), trunc(d), trunc(d, -30)
FROM trunc_large_number
+POSTHOOK: type: QUERY
+POSTHOOK: Input: default@trunc_large_number
+#### A masked pattern was here ####
+-123456711889.122345897 -123456711889.12234589 -123456710000
-123456711889 0
+123456711889.122345897 123456711889.12234589 123456710000 123456711889 0
+12345678901234567890123456789.987654321
12345678901234567890123456789.98765432 12345678901234567890123450000
12345678901234567890123456789 0
+NULL NULL NULL NULL NULL
+PREHOOK: query: SELECT c, trunc(c, 8), trunc(c, 1), trunc(c), trunc(c, -5)
FROM trunc_large_number
+PREHOOK: type: QUERY
+PREHOOK: Input: default@trunc_large_number
+#### A masked pattern was here ####
+POSTHOOK: query: SELECT c, trunc(c, 8), trunc(c, 1), trunc(c), trunc(c, -5)
FROM trunc_large_number
+POSTHOOK: type: QUERY
+POSTHOOK: Input: default@trunc_large_number
+#### A masked pattern was here ####
+-1.2345671188912234E11 -1.2345671188912234E11 -1.234567118891E11
-1.23456711889E11 -1.234567E11
+0.3 0.3 0.3 0.0 0.0
+1.2345671188912234E11 1.2345671188912234E11 1.234567118891E11
1.23456711889E11 1.234567E11
+NULL NULL NULL NULL NULL
+PREHOOK: query: SELECT b, trunc(b, -3), trunc(b, -19) FROM trunc_large_number
+PREHOOK: type: QUERY
+PREHOOK: Input: default@trunc_large_number
+#### A masked pattern was here ####
+POSTHOOK: query: SELECT b, trunc(b, -3), trunc(b, -19) FROM trunc_large_number
+POSTHOOK: type: QUERY
+POSTHOOK: Input: default@trunc_large_number
+#### A masked pattern was here ####
+-9223372036854775807 -9223372036854775000 0
+12345 12000 0
+9223372036854775807 9223372036854775000 0
+NULL NULL NULL
+PREHOOK: query: DROP TABLE trunc_large_number
+PREHOOK: type: DROPTABLE
+PREHOOK: Input: default@trunc_large_number
+PREHOOK: Output: database:default
+PREHOOK: Output: default@trunc_large_number
+POSTHOOK: query: DROP TABLE trunc_large_number
+POSTHOOK: type: DROPTABLE
+POSTHOOK: Input: default@trunc_large_number
+POSTHOOK: Output: database:default
+POSTHOOK: Output: default@trunc_large_number
diff --git
a/storage-api/src/java/org/apache/hadoop/hive/common/type/FastHiveDecimalImpl.java
b/storage-api/src/java/org/apache/hadoop/hive/common/type/FastHiveDecimalImpl.java
index 23d1e9eaacf..20dbecc9bbc 100644
---
a/storage-api/src/java/org/apache/hadoop/hive/common/type/FastHiveDecimalImpl.java
+++
b/storage-api/src/java/org/apache/hadoop/hive/common/type/FastHiveDecimalImpl.java
@@ -143,6 +143,15 @@ public class FastHiveDecimalImpl extends FastHiveDecimal {
1000000000000000000L, // 18
};
+ public static final int MAX_LONG_POWER_OF_TEN = powerOfTenTable.length - 1;
+
+ /**
+ * Returns 10^exponent, for 0 <= exponent <= {@link
#MAX_LONG_POWER_OF_TEN}.
+ */
+ public static long powerOfTen(int exponent) {
+ return powerOfTenTable[exponent];
+ }
+
public static final int MAX_DECIMAL_DIGITS = 38;
/**
@@ -5136,6 +5145,7 @@ public static boolean fastRoundIntegerDown(
fastResult);
if (fastResult.fast0 == 0 && fastResult.fast1 == 0 && fastResult.fast2 ==
0) {
+ fastResult.fastSignum = 0;
fastResult.fastIntegerDigitCount = 0;
fastResult.fastScale = 0;
} else {
diff --git
a/storage-api/src/test/org/apache/hadoop/hive/common/type/TestHiveDecimal.java
b/storage-api/src/test/org/apache/hadoop/hive/common/type/TestHiveDecimal.java
index ec567ad4426..4034f66ce56 100644
---
a/storage-api/src/test/org/apache/hadoop/hive/common/type/TestHiveDecimal.java
+++
b/storage-api/src/test/org/apache/hadoop/hive/common/type/TestHiveDecimal.java
@@ -2953,6 +2953,17 @@ public void testRoundHalfEvenSpecial() {
}
}
+ @Test
+ public void testRoundDownAllIntegerDigitsInPlace() {
+ String[] values = { "12345.678", "-12345.678", "12345",
"99999999999999999999999999999999999999" };
+ for (String value : values) {
+ HiveDecimalWritable writable = new HiveDecimalWritable(value);
+ writable.mutateSetScale(-writable.getHiveDecimal().integerDigitCount(),
BigDecimal.ROUND_DOWN);
+ assertEquals(value, 0, writable.signum());
+ assertEquals(value, HiveDecimal.ZERO, writable.getHiveDecimal());
+ }
+ }
+
//------------------------------------------------------------------------------------------------
@Test