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 f733c1ecb5..351c5c735b 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 @@ -835,13 +835,18 @@ public static boolean fastSetFromBigDecimal( // We trim the trailing zero fraction digits so we don't cause unnecessary precision // overflow later. - if (bigDecimal.signum() == 0) { + int signum = bigDecimal.signum(); + if (signum == 0) { if (bigDecimal.scale() != 0) { // For some strange reason BigDecimal 0 can have a scale. We do not support that. bigDecimal = BigDecimal.ZERO; } - } else { + } else if (bigDecimal.scale() < 0 || bigDecimal.scale() > HiveDecimal.MAX_SCALE + || bigDecimal.precision() > HiveDecimal.MAX_SCALE) { + // Note: low performance to strip trailing zeros + // don't call the bigDecimal.stripTrailingZeros() during the operation, eg, division. + // fastTrailingDecimalZeroCount() and doFastScaleDown() will strip trailing zeros later. BigDecimal bigDecimalStripped = bigDecimal.stripTrailingZeros(); int stripTrailingZerosScale = bigDecimalStripped.scale(); // System.out.println("FAST_SET_FROM_BIG_DECIMAL bigDecimal " + bigDecimal); @@ -863,6 +868,7 @@ public static boolean fastSetFromBigDecimal( // System.out.println("FAST_SET_FROM_BIG_DECIMAL adjusted for zeroes/scale " + bigDecimal + " scale " + bigDecimal.scale()); BigInteger unscaledValue = bigDecimal.unscaledValue(); + int precision = bigDecimal.precision(); // System.out.println("FAST_SET_FROM_BIG_DECIMAL unscaledValue " + unscaledValue + " length " + unscaledValue.toString().length()); final int scale = bigDecimal.scale(); @@ -870,20 +876,40 @@ public static boolean fastSetFromBigDecimal( if (scale < 0 || scale > HiveDecimal.MAX_SCALE) { return false; } - // The digits must fit without rounding. - if (!fastSetFromBigInteger(unscaledValue, fastResult)) { - return false; - } - if (fastResult.fastSignum != 0) { - fastResult.fastIntegerDigitCount = Math.max(0, fastResult.fastIntegerDigitCount - scale); - fastResult.fastScale = scale; - } - return true; + return fastSetFromBigInteger(unscaledValue, fastResult, signum, scale); } // This method will scale down and round to fit, if necessary. - if (!fastSetFromBigInteger(unscaledValue, scale, fastResult)) { + if (!fastSetFromBigInteger(unscaledValue, scale, precision, fastResult)) { + return false; + } + return true; + } + + private static boolean fastSetFromBigInteger(BigInteger bigInteger, + FastHiveDecimal fastResult, int signum, int scale) { + if (!fastSetFromBigInteger(bigInteger, fastResult)) { return false; } + + if (fastResult.fast0 == 0 && fastResult.fast1 == 0 && fastResult.fast2 == 0) { + fastResult.fastSignum = 0; + } else { + fastResult.fastSignum = signum; + fastResult.fastIntegerDigitCount = Math.max(0, fastResult.fastIntegerDigitCount - scale); + fastResult.fastScale = scale; + + final int trailingZeroCount = + fastTrailingDecimalZeroCount( + fastResult.fast0, fastResult.fast1, fastResult.fast2, + fastResult.fastIntegerDigitCount, scale); + if (trailingZeroCount > 0) { + doFastScaleDown( + fastResult, + trailingZeroCount, + fastResult); + fastResult.fastScale -= trailingZeroCount; + } + } return true; } @@ -1128,6 +1154,24 @@ public static boolean fastSetFromBigInteger( */ public static boolean fastSetFromBigInteger( BigInteger bigInteger, int scale, FastHiveDecimal fastResult) { + // Poor performance, because the precision will be calculated by bigInteger.toString() + return fastSetFromBigInteger(bigInteger, scale, -1, fastResult); + } + + /** + * Creates a fast decimal from a BigInteger with a specified scale. + * + * NOTE: The fastSetFromBigInteger method requires the caller to pass a fastResult + * parameter has been reset for better performance. + * + * @param bigInteger the value to set as an integer + * @param scale the scale to use + * @param precision the precision to use + * @param fastResult an object to reuse + * @return True if the BigInteger and scale were successfully converted to a decimal. + */ + public static boolean fastSetFromBigInteger( + BigInteger bigInteger, int scale, int precision, FastHiveDecimal fastResult) { if (scale < 0) { @@ -1150,8 +1194,10 @@ public static boolean fastSetFromBigInteger( bigInteger = bigInteger.negate(); } - // A slow way to get the number of decimal digits. - int precision = bigInteger.toString().length(); + if (precision < 0) { + // A slow way to get the number of decimal digits. + precision = bigInteger.toString().length(); + } // System.out.println("FAST_SET_FROM_BIG_INTEGER adjusted bigInteger " + bigInteger + " precision " + precision); @@ -1212,31 +1258,8 @@ public static boolean fastSetFromBigInteger( } scale = maxScale; } - if (!fastSetFromBigInteger(bigInteger, fastResult)) { - return false; - } - if (fastResult.fast0 == 0 && fastResult.fast1 == 0 && fastResult.fast2 == 0) { - fastResult.fastSignum = 0; - } else { - fastResult.fastSignum = signum; - fastResult.fastIntegerDigitCount = Math.max(0, fastResult.fastIntegerDigitCount - scale); - fastResult.fastScale = scale; - - final int trailingZeroCount = - fastTrailingDecimalZeroCount( - fastResult.fast0, fastResult.fast1, fastResult.fast2, - fastResult.fastIntegerDigitCount, scale); - if (trailingZeroCount > 0) { - doFastScaleDown( - fastResult, - trailingZeroCount, - fastResult); - fastResult.fastScale -= trailingZeroCount; - } - } - - return true; + return fastSetFromBigInteger(bigInteger, fastResult, signum, scale); } //************************************************************************************************