Repository: impala
Updated Branches:
  refs/heads/master ea8d2ba7f -> d428c16f1


IMPALA-5017: Error on decimal overflow

Before this patch, decimal operations would either silently overflow (in
the case of sum() and avg()), or produce a warning.

In this patch, the behaviour is changed so that an error is produced in
the case of overflow when DECIMAL_v2 is enabled. Decimal v1 behaviour is
unchanged.

We introduce overflow checks when computing sum() and avg(). This
results in a ~30% performance regression when we are in decimal v2 mode
compared to decimal v1.

Benchmarks:
  Query:
  select sum(dec_38_19) from decimal_tbl
    Decimal v1: 11.57s
    Decimal v2: 16.58s

  Query:
  select avg(dec_38_19) from decimal_tbl
    Decimal v1: 12.08s
    Decimal v2: 17.08s

The performance regression is not as bad if we are computing the sum or
average of decimal column with a lower precision:

  Query:
  select sum(dec_9_5) from decimal_tbl
    Decimal v1: 11.06s
    Decimal v2: 13.08s

  Query:
  select avg(dec_9_5) from decimal_tbl
    Decimal v1: 11.56s
    Decimal v2: 13.57s

Testing:
- Added several end to end tests.
- Updated Expr tests to check for error in case of overflow.

Change-Id: Id98a92c9a9469ec8cf14e518c741a2dab7053019
Reviewed-on: http://gerrit.cloudera.org:8080/8404
Reviewed-by: Taras Bobrovytsky <tbobrovyt...@cloudera.com>
Tested-by: Impala Public Jenkins


Project: http://git-wip-us.apache.org/repos/asf/impala/repo
Commit: http://git-wip-us.apache.org/repos/asf/impala/commit/575b5a20
Tree: http://git-wip-us.apache.org/repos/asf/impala/tree/575b5a20
Diff: http://git-wip-us.apache.org/repos/asf/impala/diff/575b5a20

Branch: refs/heads/master
Commit: 575b5a20e6ddc72b7cefcdd27ae722a8ad0530ee
Parents: ea8d2ba
Author: Taras Bobrovytsky <tbobrovyt...@cloudera.com>
Authored: Thu Oct 19 16:50:17 2017 -0700
Committer: Impala Public Jenkins <impala-public-jenk...@gerrit.cloudera.org>
Committed: Fri Dec 1 23:23:01 2017 +0000

----------------------------------------------------------------------
 be/src/exprs/aggregate-functions-ir.cc          |  49 ++++++-
 be/src/exprs/decimal-operators-ir.cc            |  14 +-
 be/src/exprs/expr-codegen-test.cc               |   2 +-
 be/src/exprs/expr-test.cc                       | 131 +++++++++++--------
 .../queries/QueryTest/decimal-exprs.test        | 100 ++++++++++++++
 .../queries/QueryTest/decimal.test              |  11 --
 tests/hs2/test_hs2.py                           |   2 +-
 7 files changed, 231 insertions(+), 78 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/impala/blob/575b5a20/be/src/exprs/aggregate-functions-ir.cc
----------------------------------------------------------------------
diff --git a/be/src/exprs/aggregate-functions-ir.cc 
b/be/src/exprs/aggregate-functions-ir.cc
index 049344e..d5f946a 100644
--- a/be/src/exprs/aggregate-functions-ir.cc
+++ b/be/src/exprs/aggregate-functions-ir.cc
@@ -402,6 +402,7 @@ IR_ALWAYS_INLINE void 
AggregateFunctions::DecimalAvgAddOrRemove(FunctionContext*
   DCHECK(dst->ptr != NULL);
   DCHECK_EQ(sizeof(DecimalAvgState), dst->len);
   DecimalAvgState* avg = reinterpret_cast<DecimalAvgState*>(dst->ptr);
+  bool decimal_v2 = 
ctx->impl()->GetConstFnAttr(FunctionContextImpl::DECIMAL_V2);
 
   // Since the src and dst are guaranteed to be the same scale, we can just
   // do a simple add.
@@ -409,11 +410,25 @@ IR_ALWAYS_INLINE void 
AggregateFunctions::DecimalAvgAddOrRemove(FunctionContext*
   switch (ctx->impl()->GetConstFnAttr(FunctionContextImpl::ARG_TYPE_SIZE, 0)) {
     case 4:
       avg->sum_val16 += m * src.val4;
+      if (UNLIKELY(decimal_v2 &&
+          abs(avg->sum_val16) > DecimalUtil::MAX_UNSCALED_DECIMAL16)) {
+        ctx->SetError("Avg computation overflowed");
+      }
       break;
     case 8:
       avg->sum_val16 += m * src.val8;
+      if (UNLIKELY(decimal_v2 &&
+          abs(avg->sum_val16) > DecimalUtil::MAX_UNSCALED_DECIMAL16)) {
+        ctx->SetError("Avg computation overflowed");
+      }
       break;
     case 16:
+      if (UNLIKELY(decimal_v2 && (avg->sum_val16 >= 0) == (src.val16 >= 0) &&
+          abs(avg->sum_val16) > DecimalUtil::MAX_UNSCALED_DECIMAL16 - 
abs(src.val16))) {
+        // We can't check for overflow after performing the addition like in 
the other
+        // cases because the result may not fit into int128.
+        ctx->SetError("Avg computation overflowed");
+      }
       avg->sum_val16 += m * src.val16;
       break;
     default:
@@ -434,6 +449,11 @@ void AggregateFunctions::DecimalAvgMerge(FunctionContext* 
ctx,
   DCHECK(dst->ptr != NULL);
   DCHECK_EQ(sizeof(DecimalAvgState), dst->len);
   DecimalAvgState* dst_struct = reinterpret_cast<DecimalAvgState*>(dst->ptr);
+  bool decimal_v2 = 
ctx->impl()->GetConstFnAttr(FunctionContextImpl::DECIMAL_V2);
+  bool overflow = decimal_v2 &&
+      abs(dst_struct->sum_val16) >
+      DecimalUtil::MAX_UNSCALED_DECIMAL16 - abs(src_struct->sum_val16);
+  if (UNLIKELY(overflow)) ctx->SetError("Avg computation overflowed");
   dst_struct->sum_val16 += src_struct->sum_val16;
   dst_struct->count += src_struct->count;
 }
@@ -452,12 +472,16 @@ DecimalVal 
AggregateFunctions::DecimalAvgGetValue(FunctionContext* ctx,
   int sum_scale = 
ctx->impl()->GetConstFnAttr(FunctionContextImpl::ARG_TYPE_SCALE, 0);
   bool is_nan = false;
   bool overflow = false;
-  bool round = ctx->impl()->GetConstFnAttr(FunctionContextImpl::DECIMAL_V2);
+  bool decimal_v2 = 
ctx->impl()->GetConstFnAttr(FunctionContextImpl::DECIMAL_V2);
   Decimal16Value result = sum.Divide<int128_t>(sum_scale, count, 0 /* count's 
scale */,
-      output_precision, output_scale, round, &is_nan, &overflow);
+      output_precision, output_scale, decimal_v2, &is_nan, &overflow);
   if (UNLIKELY(is_nan)) return DecimalVal::null();
   if (UNLIKELY(overflow)) {
-    ctx->AddWarning("Avg computation overflowed, returning NULL");
+    if (decimal_v2) {
+      ctx->SetError("Avg computation overflowed");
+    } else {
+      ctx->AddWarning("Avg computation overflowed, returning NULL");
+    }
     return DecimalVal::null();
   }
   return DecimalVal(result.value());
@@ -516,14 +540,29 @@ IR_ALWAYS_INLINE void 
AggregateFunctions::SumDecimalAddOrSubtract(FunctionContex
   if (src.is_null) return;
   if (dst->is_null) InitZero<DecimalVal>(ctx, dst);
   int precision = 
ctx->impl()->GetConstFnAttr(FunctionContextImpl::ARG_TYPE_PRECISION, 0);
+  bool decimal_v2 = 
ctx->impl()->GetConstFnAttr(FunctionContextImpl::DECIMAL_V2);
   // Since the src and dst are guaranteed to be the same scale, we can just
   // do a simple add.
   int m = subtract ? -1 : 1;
   if (precision <= 9) {
     dst->val16 += m * src.val4;
+    if (UNLIKELY(decimal_v2 &&
+        abs(dst->val16) > DecimalUtil::MAX_UNSCALED_DECIMAL16)) {
+      ctx->SetError("Sum computation overflowed");
+    }
   } else if (precision <= 19) {
     dst->val16 += m * src.val8;
+    if (UNLIKELY(decimal_v2 &&
+        abs(dst->val16) > DecimalUtil::MAX_UNSCALED_DECIMAL16)) {
+      ctx->SetError("Sum computation overflowed");
+    }
   } else {
+    if (UNLIKELY(decimal_v2 && (dst->val16 >= 0) == (src.val16 >= 0) &&
+        abs(dst->val16) > DecimalUtil::MAX_UNSCALED_DECIMAL16 - 
abs(src.val16))) {
+      // We can't check for overflow after performing the addition like in the 
other
+      // cases because the result may not fit into int128.
+      ctx->SetError("Sum computation overflowed");
+    }
     dst->val16 += m * src.val16;
   }
 }
@@ -532,6 +571,10 @@ void AggregateFunctions::SumDecimalMerge(FunctionContext* 
ctx,
     const DecimalVal& src, DecimalVal* dst) {
   if (src.is_null) return;
   if (dst->is_null) InitZero<DecimalVal>(ctx, dst);
+  bool decimal_v2 = 
ctx->impl()->GetConstFnAttr(FunctionContextImpl::DECIMAL_V2);
+  bool overflow = decimal_v2 &&
+      abs(dst->val16) > DecimalUtil::MAX_UNSCALED_DECIMAL16 - abs(src.val16);
+  if (UNLIKELY(overflow)) ctx->SetError("Sum computation overflowed");
   dst->val16 += src.val16;
 }
 

http://git-wip-us.apache.org/repos/asf/impala/blob/575b5a20/be/src/exprs/decimal-operators-ir.cc
----------------------------------------------------------------------
diff --git a/be/src/exprs/decimal-operators-ir.cc 
b/be/src/exprs/decimal-operators-ir.cc
index 0133317..bb54a0f 100644
--- a/be/src/exprs/decimal-operators-ir.cc
+++ b/be/src/exprs/decimal-operators-ir.cc
@@ -33,11 +33,15 @@
 namespace impala {
 
 #define RETURN_IF_OVERFLOW(ctx, overflow, return_type) \
-  do {\
-    if (UNLIKELY(overflow)) {\
-      ctx->AddWarning("Expression overflowed, returning NULL");\
-      return return_type::null();\
-    }\
+  do { \
+    if (UNLIKELY(overflow)) { \
+      if (ctx->impl()->GetConstFnAttr(FunctionContextImpl::DECIMAL_V2)) { \
+        ctx->SetError("Decimal expression overflowed"); \
+      } else { \
+        ctx->AddWarning("Decimal expression overflowed, returning NULL"); \
+      } \
+      return return_type::null(); \
+    } \
   } while (false)
 
 // Inline in IR module so branches can be optimised out.

http://git-wip-us.apache.org/repos/asf/impala/blob/575b5a20/be/src/exprs/expr-codegen-test.cc
----------------------------------------------------------------------
diff --git a/be/src/exprs/expr-codegen-test.cc 
b/be/src/exprs/expr-codegen-test.cc
index 781e457..e1c5c50 100644
--- a/be/src/exprs/expr-codegen-test.cc
+++ b/be/src/exprs/expr-codegen-test.cc
@@ -324,7 +324,7 @@ TEST_F(ExprCodegenTest, TestInlineConstFnAttrs) {
 
   // Call InlineConstFnAttrs() and rerun verification
   int replaced = InlineConstFnAttrs(expr, codegen.get(), fn);
-  EXPECT_EQ(replaced, 9);
+  EXPECT_EQ(replaced, 19);
   ResetVerification(codegen.get());
   verification_succeeded = VerifyFunction(codegen.get(), fn);
   EXPECT_TRUE(verification_succeeded) << CodeGenUtil::Print(fn);

http://git-wip-us.apache.org/repos/asf/impala/blob/575b5a20/be/src/exprs/expr-test.cc
----------------------------------------------------------------------
diff --git a/be/src/exprs/expr-test.cc b/be/src/exprs/expr-test.cc
index 341a6e2..4b7bcd0 100644
--- a/be/src/exprs/expr-test.cc
+++ b/be/src/exprs/expr-test.cc
@@ -1569,10 +1569,10 @@ DecimalTestCase decimal_cases[] = {
   { "cast(1 as decimal(38,0)) + cast(0.2 as decimal(38,1))", {{ false, false, 
12, 38, 1 }}},
   { "cast(100000000000000000000000000000000 as decimal(38,0)) + cast(0 as 
decimal(38,38))",
     {{ false, true, 0, 38, 38 },
-     { false, true, 0, 38, 6 }}},
+     { true, false, 0, 38, 6 }}},
   { "cast(-100000000000000000000000000000000 as decimal(38,0)) + cast(0 as 
decimal(38,38))",
     {{ false, true, 0, 38, 38 },
-     { false, true, 0, 38, 6 }}},
+     { true, false, 0, 38, 6 }}},
   { "cast(99999999999999999999999999999999 as decimal(38,0)) + cast(0 as 
decimal(38,38))",
     {{ false, true, 0, 38, 38 },
      { false, false, StringToInt128("99999999999999999999999999999999000000"), 
38, 6 }}},
@@ -1614,10 +1614,12 @@ DecimalTestCase decimal_cases[] = {
     {{ false, false, StringToInt128("1"), 38, 0 }}},
   { "cast(99999999999999999999999999999999.999999 as decimal(38,6)) + "
     "cast(8899999999999999999999999999999.999999 as decimal(38,6))",
-    {{ false, true, 0, 38, 6 }}},
+    {{ false, true, 0, 38, 6 },
+     { true, false, 0, 38, 6 }}},
   { "cast(-99999999999999999999999999999999.999999 as decimal(38,6)) + "
     "cast(-8899999999999999999999999999999.999999 as decimal(38,6))",
-    {{ false, true, 0, 38, 6 }}},
+    {{ false, true, 0, 38, 6 },
+     { true, false, 0, 38, 6 }}},
   { "cast(-99999999999999999999999999999999.999999 as decimal(38,6)) + "
     "cast(8899999999999999999999999999999.999999 as decimal(38,6))",
     {{ false, false, 
StringToInt128("-91100000000000000000000000000000000000"), 38, 6 }}},
@@ -1634,10 +1636,12 @@ DecimalTestCase decimal_cases[] = {
   // Smallest result that overflows.
   { "cast(77777777777777777777777777777777.777777 as decimal(38,6)) + "
     "cast(22222222222222222222222222222222.222223 as decimal(38,6))",
-    {{ false, true, 0, 38, 6 }}},
+    {{ false, true, 0, 38, 6 },
+     { true, false, 0, 38, 6 }}},
   { "cast(-77777777777777777777777777777777.777777 as decimal(38,6)) + "
     "cast(-22222222222222222222222222222222.222223 as decimal(38,6))",
-    {{ false, true, 0, 38, 6 }}},
+    {{ false, true, 0, 38, 6 },
+     { true, false, 0, 38, 6 }}},
   { "cast(11111111111111111111111111111111.777777 as decimal(38,6)) + "
     "cast(11111111111111111111111111111111.555555 as decimal(38,6))",
     {{ false, false, StringToInt128("22222222222222222222222222222223333332"), 
38, 6 }}},
@@ -1670,11 +1674,11 @@ DecimalTestCase decimal_cases[] = {
   { "cast(99999999999999999999999999999999.999999 as decimal(38,6)) + "
     "cast(0.0000005 as decimal(38,7))",
     {{ false, true, 0, 38, 7 },
-     { false, true, 0, 38, 6 }}},
+     { true, false, 0, 38, 6 }}},
   { "cast(-99999999999999999999999999999999.999999 as decimal(38,6)) + "
     "cast(-0.0000005 as decimal(38,7))",
     {{ false, true, 0, 38, 7 },
-     { false, true, 0, 38, 6 }}},
+     { true, false, 0, 38, 6 }}},
   { "cast(99999999999999999999999999999999.999999 as decimal(38,6)) + "
     "cast(-0.0000006 as decimal(38,7))",
     {{ false, true, 0, 38, 7 },
@@ -1698,11 +1702,11 @@ DecimalTestCase decimal_cases[] = {
   { "cast(99999999999999999999999999999999 as decimal(38,0)) + "
     "cast(0.9999995 as decimal(38,38))",
     {{ false, true, 0, 38, 38 },
-     { false, true, 0, 38, 6 }}},
+     { true, false, 0, 38, 6 }}},
   { "cast(-99999999999999999999999999999999 as decimal(38,0)) + "
     "cast(-0.9999995 as decimal(38,38))",
     {{ false, true, 0, 38, 38 },
-     { false, true, 0, 38, 6 }}},
+     { true, false, 0, 38, 6 }}},
   { "cast(0.99999999999999999999999999999999999999 as decimal(38,38)) + "
     "cast(-5e-38 as decimal(38,38))",
     {{ false, false, StringToInt128("99999999999999999999999999999999999994"), 
38, 38 },
@@ -1832,17 +1836,21 @@ DecimalTestCase decimal_cases[] = {
   // MAX_UNSCALED_DECIMAL16.
   { "cast(18446744073709551615 as decimal(38,0)) * "
     "cast(9223372036854775807 as decimal(38,0))",
-    {{ false, true, 0, 38, 0 }}},
+    {{ false, true, 0, 38, 0 },
+     { true, false, 0, 38, 0 }}},
   { "cast(-18446744073709551615 as decimal(38,0)) * "
     "cast(9223372036854775807 as decimal(38,0))",
-    {{ false, true, 0, 38, 0 }}},
+    {{ false, true, 0, 38, 0 },
+     { true, false, 0, 38, 0 }}},
   // int256 is required. We are multiplying (2^64 - 1) * (2^63) here.
   { "cast(18446744073709551615 as decimal(38,0)) * "
     "cast(9223372036854775808 as decimal(38,0))",
-    {{ false, true, 0, 38, 0 }}},
+    {{ false, true, 0, 38, 0 },
+     { true, false, 0, 38, 0 }}},
   { "cast(-18446744073709551615 as decimal(38,0)) * "
     "cast(9223372036854775808 as decimal(38,0))",
-    {{ false, true, 0, 38, 0 }}},
+    {{ false, true, 0, 38, 0 },
+     { true, false, 0, 38, 0 }}},
   // Largest intermediate result that does not require int256.
   { "cast(1844674407370.9551615 as decimal(38,7)) * "
     "cast(9223372036854775807 as decimal(38,0))",
@@ -1879,7 +1887,8 @@ DecimalTestCase decimal_cases[] = {
      { false, false, StringToInt128("1000000000000000000000000000000000000"), 
38, 37 }}},
   { "cast(10000000000000000000 as decimal(21,0)) * "
       "cast(10000000000000000000 as decimal(21,0))",
-    {{ false, true, 0, 38, 0 }}},
+    {{ false, true, 0, 38, 0 },
+     { true, false, 0, 38, 0 }}},
   { "cast(1000000000000000.0000 as decimal(38,4)) * "
       "cast(1000000000000000.0000 as decimal(38,4))",
     {{ false, true, 0, 38, 8 },
@@ -1892,11 +1901,11 @@ DecimalTestCase decimal_cases[] = {
   { "cast(10000000000000000.0000 as decimal(38,4)) * "
       "cast(10000000000000000.0000 as decimal(38,4))",
     {{ false, true, 0, 38, 8 },
-     { false, true, 0, 38, 6 }}},
+     { true, false, 0, 38, 6 }}},
   { "cast(-10000000000000000.0000 as decimal(38,4)) * "
       "cast(10000000000000000.0000 as decimal(38,4))",
     {{ false, true, 0, 38, 8 },
-     { false, true, 0, 38, 6 }}},
+     { true, false, 0, 38, 6 }}},
   // The reason why the result of (38,38) * (38,38) is (38,37).
   { "cast(0.99999999999999999999999999999999999999 as decimal(38,38)) * "
       "cast(0.99999999999999999999999999999999999999 as decimal(38,38))",
@@ -1944,7 +1953,7 @@ DecimalTestCase decimal_cases[] = {
   { "cast(99999999999999999999999999999999999999 as decimal(38,0)) / "
     "cast(0.00000000000000000000000000000000000001 as decimal(38,38))",
     {{ false, true, 0, 38, 38 },
-     { false, true, 0, 38,  6 }}},
+     { true, false, 0, 38,  6 }}},
   { "cast(0.00000000000000000000000000000000000001 as decimal(38,38)) / "
     "cast(99999999999999999999999999999999999999 as decimal(38,0))",
     {{ false, false, 0, 38, 38 }}},
@@ -2485,7 +2494,8 @@ DecimalTestCase decimal_cases[] = {
   { "cast('10' as decimal(2,0)) / cast('0' as decimal(38,19))",
     {{ false, true, 0, 38, 19 },
      { true, false, 0, 38, 19 }}},
-  { "mod(cast(NULL as decimal(2,0)), NULL)", {{ false, true, 0, 2, 0 }}},
+  { "mod(cast(NULL as decimal(2,0)), NULL)",
+    {{ false, true, 0, 2, 0 }}},
   // Test CAST DECIMAL -> DECIMAL
   { "cast(cast(0.12344 as decimal(6,5)) as decimal(6,4))",
     {{ false, false, 1234, 6, 4 }}},
@@ -2497,10 +2507,10 @@ DecimalTestCase decimal_cases[] = {
      { false, false, 1, 1, 0 }}},
   { "cast(cast(999999999.99 as DECIMAL(11,2)) as DECIMAL(9,0))",
     {{ false, false, 999999999, 9, 0 },
-     { false, true, 0, 9, 0 }}},
+     { true, false, 0, 9, 0 }}},
   { "cast(cast(-999999999.99 as DECIMAL(11,2)) as DECIMAL(9,0))",
     {{ false, false, -999999999, 9, 0 },
-     { false, true, 0, 9, 0 }}},
+     { true, false, 0, 9, 0 }}},
   // IMPALA-2233: Test that implicit casts do not lose precision.
   // The overload greatest(decimal(*,*)) is available and should be used.
   { "greatest(0, cast('99999.1111' as decimal(30,10)))",
@@ -2511,7 +2521,7 @@ DecimalTestCase decimal_cases[] = {
     "as d))) as t",
     {{ false, false, static_cast<int128_t>(10000000ll) *
        10000000000ll * 10000000000ll * 10000000000ll, 38, 5 },
-     { false, true, 0, 38, 6}}},
+     { true, false, 0, 38, 6}}},
   { "avg(d) from (values((cast(1234567890 as DECIMAL(10,0)) as d))) as t",
     {{ false, false, 1234567890, 10, 0},
      { false, false, 1234567890000000, 16, 6}}},
@@ -2528,17 +2538,17 @@ DecimalTestCase decimal_cases[] = {
     "as d))) as t",
     {{ false, false, static_cast<int128_t>(100) *
       10000000000ll * 10000000000ll * 10000000000ll, 33, 0},
-     { false, true, 0, 38, 6}}},
+     { true, false, 0, 38, 6}}},
   { "avg(d) from (values((cast(100000000000000000000000000000000.0 as 
DECIMAL(34,1)) "
     "as d))) as t",
     {{ false, false, static_cast<int128_t>(1000) *
       10000000000ll * 10000000000ll * 10000000000ll, 34, 1},
-     { false, true, 0, 38, 6}}},
+     { true, false, 0, 38, 6}}},
   { "avg(d) from (values((cast(100000000000000000000000000000000.00000 as 
DECIMAL(38,5)) "
     "as d))) as t",
     {{ false, false, static_cast<int128_t>(10000000) *
       10000000000ll * 10000000000ll * 10000000000ll, 38, 5},
-     { false, true, 0, 38, 6}}},
+     { true, false, 0, 38, 6}}},
   { "avg(d) from (values((cast(10000000000000000000000000000000.000000 as 
DECIMAL(38,6)) "
     "as d))) as t",
     {{ false, false, static_cast<int128_t>(10000000) *
@@ -2555,7 +2565,7 @@ DecimalTestCase decimal_cases[] = {
     {{ false, false, 9999999900, 10, 2 }}},
   { "cast(cast(99999999.5999999 AS int) AS decimal(10,2))",
     {{ false, false, 9999999900, 10, 2 },
-     { false, true, 0, 10, 2 }}},
+     { true, false, 0, 10, 2 }}},
   { "cast(cast(10000.5999999 as int) as decimal(30,6))",
     {{ false, false, 10000000000, 30, 6 },
      { false, false, 10001000000, 30, 6 }}},
@@ -2567,76 +2577,76 @@ DecimalTestCase decimal_cases[] = {
      { false, false, -100010, 6, 1 }}},
   { "cast(cast(9999.5 AS int) AS decimal(4,0))",
     {{ false, false, 9999, 4, 0 },
-     { false, true, 0, 4, 0 }}},
+     { true, false, 0, 4, 0 }}},
   { "cast(cast(-9999.5 AS int) AS decimal(4,0))",
     {{ false, false, -9999, 4, 0 },
-     { false, true, 0, 4, 0 }}},
+     { true, false, 0, 4, 0 }}},
   { "cast(cast(127.4999 AS tinyint) AS decimal(30,0))",
     {{ false, false, 127, 30, 0 }}},
   { "cast(cast(127.5 AS tinyint) AS decimal(30,0))",
     {{ false, false, 127, 30, 0 },
-     { false, true, 0, 30, 0 }}},
+     { true, false, 0, 30, 0 }}},
   { "cast(cast(128.0 AS tinyint) AS decimal(30,0))",
     {{ false, false, -128, 30, 0 }, // BUG: JIRA: IMPALA-865
-     { false, true, 0, 30, 0 }}},
+     { true, false, 0, 30, 0 }}},
   { "cast(cast(-128.4999 AS tinyint) AS decimal(30,0))",
     {{ false, false, -128, 30, 0 }}},
   { "cast(cast(-128.5 AS tinyint) AS decimal(30,0))",
     {{ false, false, -128, 30, 0 },
-     { false, true, 0, 30, 0 }}},
+     { true, false, 0, 30, 0 }}},
   { "cast(cast(-129.0 AS tinyint) AS decimal(30,0))",
     {{ false, false, 127, 30, 0 }, // BUG: JIRA: IMPALA-865
-     { false, true, 0, 30, 0 }}},
+     { true, false, 0, 30, 0 }}},
   { "cast(cast(32767.4999 AS smallint) AS decimal(30,0))",
     {{ false, false, 32767, 30, 0 }}},
   { "cast(cast(32767.5 AS smallint) AS decimal(30,0))",
     {{ false, false, 32767, 30, 0 },
-     { false, true, 0, 30, 0 }}},
+     { true, false, 0, 30, 0 }}},
   { "cast(cast(32768.0 AS smallint) AS decimal(30,0))",
     {{ false, false, -32768, 30, 0 }, // BUG: JIRA: IMPALA-865
-     { false, true, 0, 30, 0 }}},
+     { true, false, 0, 30, 0 }}},
   { "cast(cast(-32768.4999 AS smallint) AS decimal(30,0))",
     {{ false, false, -32768, 30, 0 }}},
   { "cast(cast(-32768.5 AS smallint) AS decimal(30,0))",
     {{ false, false, -32768, 30, 0 },
-     { false, true, 0, 30, 0 }}},
+     { true, false, 0, 30, 0 }}},
   { "cast(cast(-32769.0 AS smallint) AS decimal(30,0))",
     {{ false, false, 32767, 30, 0 }, // BUG: JIRA: IMPALA-865
-     { false, true, 0, 30, 0 }}},
+     { true, false, 0, 30, 0 }}},
   { "cast(cast(2147483647.4999 AS int) AS decimal(30,0))",
     {{ false, false, 2147483647, 30, 0 }}},
   { "cast(cast(2147483647.5 AS int) AS decimal(30,0))",
     {{ false, false, 2147483647, 30, 0 },
-     { false, true, 0, 30, 0 }}},
+     { true, false, 0, 30, 0 }}},
   { "cast(cast(2147483648.0 AS int) AS decimal(30,0))",
     {{ false, false, -2147483648, 30, 0 }, // BUG: JIRA: IMPALA-865
-     { false, true, 0, 30, 0 }}},
+     { true, false, 0, 30, 0 }}},
   { "cast(cast(-2147483648.4999 AS int) AS decimal(30,0))",
     {{ false, false, -2147483648, 30, 0 }}},
   { "cast(cast(-2147483648.5 AS int) AS decimal(30,0))",
     {{ false, false, -2147483648, 30, 0 },
-     { false, true, 0, 30, 0 }}},
+     { true, false, 0, 30, 0 }}},
   { "cast(cast(-2147483649.0 AS int) AS decimal(30,0))",
     {{ false, false, 2147483647, 30, 0 }, // BUG: JIRA: IMPALA-865
-     { false, true, 0, 30, 0 }}},
+     { true, false, 0, 30, 0 }}},
   { "cast(cast(9223372036854775807.4999 AS bigint) AS decimal(30,0))",
     {{ false, false, 9223372036854775807, 30, 0 }}},
   { "cast(cast(9223372036854775807.5 AS bigint) AS decimal(30,0))",
     {{ false, false, 9223372036854775807, 30, 0 },
-     { false, true, 0, 30, 0 }}},
+     { true, false, 0, 30, 0 }}},
   { "cast(cast(9223372036854775808.0 AS bigint) AS decimal(30,0))",
     // BUG; also GCC workaround with -1
     {{ false, false, -9223372036854775807 - 1, 30, 0 },
      // error: integer constant is so large that it is unsigned
-     { false, true, 0, 30, 0 }}},
+     { true, false, 0, 30, 0 }}},
   { "cast(cast(-9223372036854775808.4999 AS bigint) AS decimal(30,0))",
     {{ false, false, -9223372036854775807 - 1, 30, 0 }}},
   { "cast(cast(-9223372036854775808.5 AS bigint) AS decimal(30,0))",
     {{ false, false, -9223372036854775807 - 1, 30, 0 },
-     { false, true, 0, 30, 0 }}},
+     { true, false, 0, 30, 0 }}},
   { "cast(cast(-9223372036854775809.0 AS bigint) AS decimal(30,0))",
     {{ false, false, 9223372036854775807, 30, 0 }, // BUG: JIRA: IMPALA-865
-     { false, true, 0, 30, 0 }}},
+     { true, false, 0, 30, 0 }}},
   { "cast(cast(cast(pow(1, -38) as decimal(38,38)) as bigint) as 
decimal(18,10))",
     {{ false, false, 0, 18, 10 },
      { false, false, 10000000000, 18, 10 }}},
@@ -2651,7 +2661,7 @@ DecimalTestCase decimal_cases[] = {
      { false, false, 10000, 5, 1 }}},
   { "cast(cast(power(10, 3) - power(10, -2) as float) as decimal(4,1))",
     {{ false, false, 9999, 4, 1 },
-     { false, true, 0, 4, 1 }}},
+     { true, false, 0, 4, 1 }}},
   { "cast(cast(-power(10, 3) + power(10, -1) as float) as decimal(4,1))",
     {{ false, false, -9999, 4, 1 }}},
   { "cast(cast(-power(10, 3) + power(10, -2) as float) as decimal(5,1))",
@@ -2659,7 +2669,7 @@ DecimalTestCase decimal_cases[] = {
      { false, false, -10000, 5, 1 }}},
   { "cast(cast(-power(10, 3) + power(10, -2) as float) as decimal(4,1))",
     {{ false, false, -9999, 4, 1 },
-     { false, true, 0, 4, 1 }}},
+     { true, false, 0, 4, 1 }}},
   { "cast(cast(power(10, 3) - 0.45 as double) as decimal(4,1))",
     {{ false, false, 9995, 4, 1 },
      { false, false, 9996, 4, 1 }}},
@@ -2670,7 +2680,7 @@ DecimalTestCase decimal_cases[] = {
      { false, false, 1000, 5, 0 }}},
   { "cast(cast(power(10, 3) - 0.45 as double) as decimal(3,0))",
     {{ false, false, 999, 3, 0 },
-     { false, true, 0, 3, 0 }}},
+     { true, false, 0, 3, 0 }}},
   { "cast(cast(-power(10, 3) + 0.45 as double) as decimal(4,1))",
     {{ false, false, -9995, 4, 1 },
      { false, false, -9996, 4, 1 }}},
@@ -2681,7 +2691,7 @@ DecimalTestCase decimal_cases[] = {
      { false, false, -1000, 5, 0 }}},
   { "cast(cast(-power(10, 3) + 0.45 as double) as decimal(3,0))",
     {{ false, false, -999, 3, 0 },
-     { false, true, 0, 3, 0 }}},
+     { true, false, 0, 3, 0 }}},
   { "cast(cast(power(10, 3) - 0.5 as double) as decimal(4,1))",
     {{ false, false, 9995, 4, 1 }}},
   { "cast(cast(power(10, 3) - 0.5 as double) as decimal(5,2))",
@@ -2691,7 +2701,7 @@ DecimalTestCase decimal_cases[] = {
      { false, false, 1000, 5, 0 }}},
   { "cast(cast(power(10, 3) - 0.5 as double) as decimal(3,0))",
     {{ false, false, 999, 3, 0 },
-     { false, true, 0, 3, 0 }}},
+     { true, false, 0, 3, 0 }}},
   { "cast(cast(-power(10, 3) + 0.5 as double) as decimal(4,1))",
     {{ false, false, -9995, 4, 1 }}},
   { "cast(cast(-power(10, 3) + 0.5 as double) as decimal(5,2))",
@@ -2701,7 +2711,7 @@ DecimalTestCase decimal_cases[] = {
      { false, false, -1000, 5, 0 }}},
   { "cast(cast(-power(10, 3) + 0.5 as double) as decimal(3,0))",
     {{ false, false, -999, 3, 0 },
-     { false, true, 0, 3, 0 }}},
+     { true, false, 0, 3, 0 }}},
   { "cast(cast(power(10, 3) - 0.55 as double) as decimal(4,1))",
     {{ false, false, 9994, 4, 1 },
      { false, false, 9995, 4, 1 }}},
@@ -2721,20 +2731,27 @@ DecimalTestCase decimal_cases[] = {
   { "cast(cast(-power(10, 3) + 0.55 as double) as decimal(3,0))",
     {{ false, false, -999, 3, 0 }}},
   { "cast(power(2, 1023) * 100 as decimal(38,0))",
-    {{ false, true, 0, 38, 0 }}},
+    {{ false, true, 0, 38, 0 },
+     { true, false, 0, 38, 0 }}},
   { "cast(power(2, 1023) * 100 as decimal(18,0))",
-    {{ false, true, 0, 18, 0 }}},
+    {{ false, true, 0, 18, 0 },
+     { true, false, 0, 18, 0 }}},
   { "cast(power(2, 1023) * 100 as decimal(9,0))",
-    {{ false, true, 0, 9, 0 }}},
+    {{ false, true, 0, 9, 0 },
+     { true, false, 0, 9, 0 }}},
   { "cast(0/0 as decimal(38,0))",
-    {{ false, true, 0, 38, 0 }}},
+    {{ false, true, 0, 38, 0 },
+     { true, false, 0, 38, 0 }}},
   { "cast(0/0 as decimal(18,0))",
-    {{ false, true, 0, 18, 0 }}},
+    {{ false, true, 0, 18, 0 },
+     { true, false, 0, 18, 0 }}},
   { "cast(0/0 as decimal(9,0))",
-    {{ false, true, 0, 9, 0 }}},
+    {{ false, true, 0, 9, 0 },
+     { true, false, 0, 9, 0 }}},
   // 39 5's - legal double but will overflow in decimal
   { "cast(555555555555555555555555555555555555555 as decimal(38,0))",
-    {{ false, true, 0, 38, 0 }}},
+    {{ false, true, 0, 38, 0 },
+     { true, false, 0, 38, 0 }}},
 };
 
 TEST_F(ExprTest, DecimalArithmeticExprs) {

http://git-wip-us.apache.org/repos/asf/impala/blob/575b5a20/testdata/workloads/functional-query/queries/QueryTest/decimal-exprs.test
----------------------------------------------------------------------
diff --git 
a/testdata/workloads/functional-query/queries/QueryTest/decimal-exprs.test 
b/testdata/workloads/functional-query/queries/QueryTest/decimal-exprs.test
index b34cb78..328fbaf 100644
--- a/testdata/workloads/functional-query/queries/QueryTest/decimal-exprs.test
+++ b/testdata/workloads/functional-query/queries/QueryTest/decimal-exprs.test
@@ -94,6 +94,106 @@ select 10.0 % 0 from decimal_tbl;
 UDF ERROR: Cannot divide decimal by zero
 ====
 ---- QUERY
+# Test DECIMAL V1 decimal overflow
+set decimal_v2=false;
+select
+  cast(9999999999999999999999999999 as decimal(38, 6)) *
+  cast(9999999999999999999999999999 as decimal(38, 6))
+---- RESULTS
+NULL
+---- TYPES
+DECIMAL
+---- ERRORS
+UDF WARNING: Decimal expression overflowed, returning NULL
+====
+---- QUERY
+# Test DECIMAL V2 decimal overflow
+set decimal_v2=true;
+select
+  cast(9999999999999999999999999999 as decimal(38, 6)) *
+  cast(9999999999999999999999999999 as decimal(38, 6))
+---- CATCH
+UDF ERROR: Decimal expression overflowed
+====
+---- QUERY
+# IMPALA-1837: Handle loss of precision when implicitly casting a literal to a 
decimal.
+# Here "1.8" will be implicitly cast to a decimal(38,38), losing precision.
+set decimal_v2=false;
+select coalesce(1.8, cast(0 as decimal(38,38)));
+---- TYPES
+DECIMAL
+---- RESULTS
+0.00000000000000000000000000000000000000
+---- ERRORS
+UDF WARNING: Decimal expression overflowed, returning NULL
+====
+---- QUERY
+set decimal_v2=true;
+select coalesce(1.8, cast(0 as decimal(38,38)));
+---- CATCH
+UDF ERROR: Decimal expression overflowed
+====
+---- QUERY
+# DECIMAL v1 sum() overflow. A negative number is incorrectly returned due to 
overflow.
+set decimal_v2=false;
+select sum(d6 * cast(4e37 as decimal(38,0))) from decimal_tbl;
+---- RESULTS
+-40282366920938463463374607431768211456
+---- TYPES
+DECIMAL
+====
+---- QUERY
+# DECIMAL v2 sum() overflow.
+set decimal_v2=true;
+select sum(d6 * cast(4e37 as decimal(38,0))) from decimal_tbl;
+---- CATCH
+UDF ERROR: Sum computation overflowed
+====
+---- QUERY
+# DECIMAL v1 avg() overflow. A negative number is incorrectly returned due to 
overflow.
+set decimal_v2=false;
+select avg(d6 * cast(4e37 as decimal(38,0))) from decimal_tbl;
+---- RESULTS
+-28056473384187692692674921486353642291
+---- TYPES
+DECIMAL
+====
+---- QUERY
+# DECIMAL v2 avg() overflow.
+set decimal_v2=true;
+select avg(d6 * cast(4e37 as decimal(38,0))) from decimal_tbl;
+---- CATCH
+UDF ERROR: Avg computation overflowed
+====
+---- QUERY
+# DECIMAL v1 avg() and sum() could incorrecly indicate an overflow error if
+# only the absolute values are considered.
+set decimal_v2=false;
+with t as (
+  select cast(99999999999999999999999999999999999999 as decimal(38, 0)) as c
+  union all
+  select cast(-99999999999999999999999999999999999999 as decimal(38, 0)) as c)
+select sum(c), avg(c) from t;
+---- RESULTS
+0,0
+---- TYPES
+DECIMAL,DECIMAL
+====
+---- QUERY
+# DECIMAL v2 avg() and sum() could incorrecly indicate an overflow error if
+# only the absolute values are considered.
+set decimal_v2=true;
+with t as (
+  select cast(99999999999999999999999999999999999999 as decimal(38, 0)) as c
+  union all
+  select cast(-99999999999999999999999999999999999999 as decimal(38, 0)) as c)
+select sum(c), avg(c) from t;
+---- RESULTS
+0,0.000000
+---- TYPES
+DECIMAL,DECIMAL
+====
+---- QUERY
 # Test casting behavior without decimal_v2 query option set.
 set decimal_v2=false;
 select cast(d3 as decimal(20, 3)) from decimal_tbl;

http://git-wip-us.apache.org/repos/asf/impala/blob/575b5a20/testdata/workloads/functional-query/queries/QueryTest/decimal.test
----------------------------------------------------------------------
diff --git a/testdata/workloads/functional-query/queries/QueryTest/decimal.test 
b/testdata/workloads/functional-query/queries/QueryTest/decimal.test
index 1f0fa4c..e2958df 100644
--- a/testdata/workloads/functional-query/queries/QueryTest/decimal.test
+++ b/testdata/workloads/functional-query/queries/QueryTest/decimal.test
@@ -368,17 +368,6 @@ STRING
 ---- QUERY
 # IMPALA-1837: Handle loss of precision when implicitly casting a literal to a 
decimal.
 # Here "1.8" will be implicitly cast to a decimal(38,38), losing precision.
-select coalesce(1.8, cast(0 as decimal(38,38)))
----- TYPES
-DECIMAL
----- RESULTS
-0.00000000000000000000000000000000000000
----- ERRORS
-UDF WARNING: Expression overflowed, returning NULL
-====
----- QUERY
-# IMPALA-1837: Handle loss of precision when implicitly casting a literal to a 
decimal.
-# Here "1.8" will be implicitly cast to a decimal(38,38), losing precision.
 # No error is reported because the overflowed expr is never evaluated.
 select coalesce(cast(0 as decimal(38,38)), 1.8)
 ---- TYPES

http://git-wip-us.apache.org/repos/asf/impala/blob/575b5a20/tests/hs2/test_hs2.py
----------------------------------------------------------------------
diff --git a/tests/hs2/test_hs2.py b/tests/hs2/test_hs2.py
index 7d7f9e9..5fa8fda 100644
--- a/tests/hs2/test_hs2.py
+++ b/tests/hs2/test_hs2.py
@@ -441,7 +441,7 @@ class TestHS2(HS2TestSuite):
 
     # Test overflow warning
     log = self.get_log("select cast(1000 as decimal(2, 1))")
-    assert "Expression overflowed, returning NULL" in log
+    assert "Decimal expression overflowed, returning NULL" in log
 
   @needs_session()
   def test_get_exec_summary(self):

Reply via email to