You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@drill.apache.org by ar...@apache.org on 2018/07/13 12:55:01 UTC

[drill] branch master updated: DRILL-6472: Prevent using zero precision in CAST function

This is an automated email from the ASF dual-hosted git repository.

arina pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/drill.git


The following commit(s) were added to refs/heads/master by this push:
     new c39ba74  DRILL-6472: Prevent using zero precision in CAST function
c39ba74 is described below

commit c39ba74796d1f47887306ae81aa70ccf454effb3
Author: Volodymyr Vysotskyi <vv...@gmail.com>
AuthorDate: Thu Jul 12 19:00:48 2018 +0300

    DRILL-6472: Prevent using zero precision in CAST function
    
    - Add check for the correctness of scale value;
    - Add check for fitting the value to the value with the concrete scale and precision;
    - Implement negative UDF for VarDecimal
    - Add unit tests for new checks and UDF.
---
 .../templates/Decimal/CastDecimalVarDecimal.java   |  13 ++-
 .../templates/Decimal/CastFloatDecimal.java        |  11 +-
 .../codegen/templates/Decimal/CastIntDecimal.java  |   8 +-
 .../templates/Decimal/CastVarCharDecimal.java      |  12 +--
 .../templates/Decimal/DecimalFunctions.java        |   9 +-
 .../drill/exec/planner/sql/SqlConverter.java       |  13 +++
 .../java/org/apache/drill/TestFunctionsQuery.java  |   2 +-
 .../drill/exec/fn/impl/TestCastFunctions.java      | 112 ++++++++++++++++-----
 .../exec/fn/impl/TestVarDecimalFunctions.java      | 108 +++++++++++---------
 .../test/resources/decimal/cast_decimal_int.json   |   2 +-
 .../resources/decimal/cast_decimal_vardecimal.json |   8 +-
 .../test/resources/decimal/cast_int_decimal.json   |   4 +-
 .../resources/decimal/cast_vardecimal_decimal.json |   6 +-
 .../org/apache/drill/exec/util/DecimalUtility.java |  23 +++++
 14 files changed, 227 insertions(+), 104 deletions(-)

diff --git a/exec/java-exec/src/main/codegen/templates/Decimal/CastDecimalVarDecimal.java b/exec/java-exec/src/main/codegen/templates/Decimal/CastDecimalVarDecimal.java
index aeffe5a..68f5475 100644
--- a/exec/java-exec/src/main/codegen/templates/Decimal/CastDecimalVarDecimal.java
+++ b/exec/java-exec/src/main/codegen/templates/Decimal/CastDecimalVarDecimal.java
@@ -64,17 +64,20 @@ public class Cast${type.from}${type.to} implements DrillSimpleFunc {
   public void eval() {
     java.math.BigDecimal bd =
         <#if type.from == "Decimal9" || type.from == "Decimal18">
-        java.math.BigDecimal.valueOf(in.value)
+        java.math.BigDecimal.valueOf(in.value);
         <#else>
         org.apache.drill.exec.util.DecimalUtility
           <#if type.from.contains("Sparse")>
-            .getBigDecimalFromDrillBuf(in.buffer, in.start, in.nDecimalDigits, in.scale, true)
+            .getBigDecimalFromDrillBuf(in.buffer, in.start, in.nDecimalDigits, in.scale, true);
           <#elseif type.from == "VarDecimal">
-            .getBigDecimalFromDrillBuf(in.buffer, in.start, in.end - in.start, in.scale)
+            .getBigDecimalFromDrillBuf(in.buffer, in.start, in.end - in.start, in.scale);
           </#if>
         </#if>
-                .setScale(scale.value, java.math.RoundingMode.HALF_UP)
-                .round(new java.math.MathContext(precision.value, java.math.RoundingMode.HALF_UP));
+
+    org.apache.drill.exec.util.DecimalUtility.checkValueOverflow(bd, precision.value, scale.value);
+
+    bd = bd.setScale(scale.value, java.math.RoundingMode.HALF_UP);
+
     out.scale = scale.value;
     out.precision = precision.value;
     out.start = 0;
diff --git a/exec/java-exec/src/main/codegen/templates/Decimal/CastFloatDecimal.java b/exec/java-exec/src/main/codegen/templates/Decimal/CastFloatDecimal.java
index f051310..245f6d1 100644
--- a/exec/java-exec/src/main/codegen/templates/Decimal/CastFloatDecimal.java
+++ b/exec/java-exec/src/main/codegen/templates/Decimal/CastFloatDecimal.java
@@ -71,12 +71,11 @@ public class Cast${type.from}${type.to} implements DrillSimpleFunc {
 
     out.start = 0;
     java.math.BigDecimal bd =
-        new java.math.BigDecimal(
-            String.valueOf(in.value),
-            new java.math.MathContext(
-                precision.value,
-                java.math.RoundingMode.HALF_UP))
-        .setScale(scale.value, java.math.RoundingMode.HALF_UP);
+        new java.math.BigDecimal(String.valueOf(in.value));
+
+    org.apache.drill.exec.util.DecimalUtility.checkValueOverflow(bd, precision.value, scale.value);
+
+    bd = bd.setScale(scale.value, java.math.RoundingMode.HALF_UP);
 
     byte[] bytes = bd.unscaledValue().toByteArray();
     int len = bytes.length;
diff --git a/exec/java-exec/src/main/codegen/templates/Decimal/CastIntDecimal.java b/exec/java-exec/src/main/codegen/templates/Decimal/CastIntDecimal.java
index 162c562..033c92e 100644
--- a/exec/java-exec/src/main/codegen/templates/Decimal/CastIntDecimal.java
+++ b/exec/java-exec/src/main/codegen/templates/Decimal/CastIntDecimal.java
@@ -67,9 +67,11 @@ public class Cast${type.from}${type.to} implements DrillSimpleFunc {
 
     out.start = 0;
     out.buffer = buffer;
-    java.math.BigDecimal bd = new java.math.BigDecimal(in.value,
-        new java.math.MathContext(precision.value, java.math.RoundingMode.HALF_UP))
-        .setScale(out.scale, java.math.BigDecimal.ROUND_DOWN);
+    java.math.BigDecimal bd = new java.math.BigDecimal(in.value);
+
+    org.apache.drill.exec.util.DecimalUtility.checkValueOverflow(bd, precision.value, scale.value);
+
+    bd = bd.setScale(out.scale, java.math.BigDecimal.ROUND_DOWN);
 
     byte[] bytes = bd.unscaledValue().toByteArray();
     int len = bytes.length;
diff --git a/exec/java-exec/src/main/codegen/templates/Decimal/CastVarCharDecimal.java b/exec/java-exec/src/main/codegen/templates/Decimal/CastVarCharDecimal.java
index a6e209d..ac1fccb 100644
--- a/exec/java-exec/src/main/codegen/templates/Decimal/CastVarCharDecimal.java
+++ b/exec/java-exec/src/main/codegen/templates/Decimal/CastVarCharDecimal.java
@@ -93,12 +93,12 @@ public class CastEmptyString${type.from}To${type.to} implements DrillSimpleFunc
     byte[] buf = new byte[in.end - in.start];
     in.buffer.getBytes(in.start, buf, 0, in.end - in.start);
     String s = new String(buf, com.google.common.base.Charsets.UTF_8);
-    java.math.BigDecimal bd =
-        new java.math.BigDecimal(s,
-            new java.math.MathContext(
-                precision.value,
-                java.math.RoundingMode.HALF_UP))
-        .setScale(scale.value, java.math.RoundingMode.HALF_UP);
+    java.math.BigDecimal bd = new java.math.BigDecimal(s);
+
+    org.apache.drill.exec.util.DecimalUtility.checkValueOverflow(bd, precision.value, scale.value);
+
+    bd = bd.setScale(scale.value, java.math.RoundingMode.HALF_UP);
+
     byte[] bytes = bd.unscaledValue().toByteArray();
     int len = bytes.length;
     out.buffer = buffer.reallocIfNeeded(len);
diff --git a/exec/java-exec/src/main/codegen/templates/Decimal/DecimalFunctions.java b/exec/java-exec/src/main/codegen/templates/Decimal/DecimalFunctions.java
index 059080e..da18cb1 100644
--- a/exec/java-exec/src/main/codegen/templates/Decimal/DecimalFunctions.java
+++ b/exec/java-exec/src/main/codegen/templates/Decimal/DecimalFunctions.java
@@ -193,16 +193,18 @@ public class ${type.name}Functions {
 
 </#list>
 
-<#list ["Abs", "Ceil", "Floor", "Trunc", "Round"] as functionName>
+<#list ["Abs", "Ceil", "Floor", "Trunc", "Round", "Negative"] as functionName>
   <#if functionName == "Ceil">
   @FunctionTemplate(names = {"ceil", "ceiling"},
   <#elseif functionName == "Trunc">
   @FunctionTemplate(names = {"trunc", "truncate"},
+  <#elseif functionName == "Negative">
+  @FunctionTemplate(names = {"negative", "u-", "-"},
   <#else>
   @FunctionTemplate(name = "${functionName?lower_case}",
   </#if>
                     scope = FunctionTemplate.FunctionScope.SIMPLE,
-                  <#if functionName == "Abs">
+                  <#if functionName == "Abs" || functionName == "Negative">
                     returnType = FunctionTemplate.ReturnType.DECIMAL_MAX_SCALE,
                   <#elseif functionName == "Ceil" || functionName == "Floor"
                       || functionName == "Trunc" || functionName == "Round">
@@ -227,6 +229,9 @@ public class ${type.name}Functions {
           <#if functionName == "Abs">
                   .abs();
       result.scale = in.scale;
+          <#elseif functionName == "Negative">
+                  .negate();
+      result.scale = in.scale;
           <#elseif functionName == "Ceil">
                   .setScale(0, java.math.BigDecimal.ROUND_CEILING);
           <#elseif functionName == "Floor">
diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/planner/sql/SqlConverter.java b/exec/java-exec/src/main/java/org/apache/drill/exec/planner/sql/SqlConverter.java
index d6b0951..eee141e 100644
--- a/exec/java-exec/src/main/java/org/apache/drill/exec/planner/sql/SqlConverter.java
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/planner/sql/SqlConverter.java
@@ -86,6 +86,7 @@ import static org.apache.calcite.util.Static.RESOURCE;
 
 import com.google.common.base.Joiner;
 import org.apache.drill.exec.store.ColumnExplorer;
+import org.apache.drill.exec.util.DecimalUtility;
 
 /**
  * Class responsible for managing parsing, validation and toRel conversion for sql statements.
@@ -562,10 +563,22 @@ public class SqlConverter {
       // that differs from the value from specified RelDataType, cast cannot be removed
       // TODO: remove this code when CALCITE-1468 is fixed
       if (type.getSqlTypeName() == SqlTypeName.DECIMAL && exp instanceof RexLiteral) {
+        if (type.getPrecision() < 1) {
+          throw UserException.validationError()
+              .message("Expected precision greater than 0, but was %s.", type.getPrecision())
+              .build(logger);
+        }
+        if (type.getScale() > type.getPrecision()) {
+          throw UserException.validationError()
+              .message("Expected scale less than or equal to precision, " +
+                  "but was scale %s and precision %s.", type.getScale(), type.getPrecision())
+              .build(logger);
+        }
         RexLiteral literal = (RexLiteral) exp;
         Comparable value = literal.getValueAs(Comparable.class);
         if (value instanceof BigDecimal) {
           BigDecimal bigDecimal = (BigDecimal) value;
+          DecimalUtility.checkValueOverflow(bigDecimal, type.getPrecision(), type.getScale());
           if (bigDecimal.scale() != type.getScale() || bigDecimal.precision() != type.getPrecision()) {
             return makeAbstractCast(type, exp);
           }
diff --git a/exec/java-exec/src/test/java/org/apache/drill/TestFunctionsQuery.java b/exec/java-exec/src/test/java/org/apache/drill/TestFunctionsQuery.java
index 8047949..390a6bf 100644
--- a/exec/java-exec/src/test/java/org/apache/drill/TestFunctionsQuery.java
+++ b/exec/java-exec/src/test/java/org/apache/drill/TestFunctionsQuery.java
@@ -953,7 +953,7 @@ public class TestFunctionsQuery extends BaseTestQuery {
             .sqlQuery(query)
             .unOrdered()
             .baselineColumns("col1")
-            .baselineValues(-1.1)
+            .baselineValues(new BigDecimal("-1.1"))
             .go();
   }
 }
diff --git a/exec/java-exec/src/test/java/org/apache/drill/exec/fn/impl/TestCastFunctions.java b/exec/java-exec/src/test/java/org/apache/drill/exec/fn/impl/TestCastFunctions.java
index 01ceb90..eeded5c 100644
--- a/exec/java-exec/src/test/java/org/apache/drill/exec/fn/impl/TestCastFunctions.java
+++ b/exec/java-exec/src/test/java/org/apache/drill/exec/fn/impl/TestCastFunctions.java
@@ -18,18 +18,19 @@
 package org.apache.drill.exec.fn.impl;
 
 import java.math.BigDecimal;
-import java.math.MathContext;
-import java.math.RoundingMode;
 import java.time.LocalDate;
 import java.util.List;
 import java.util.Map;
 
 import org.apache.drill.categories.SqlFunctionTest;
 import org.apache.drill.categories.UnlikelyTest;
+import org.apache.drill.common.exceptions.UserRemoteException;
 import org.apache.drill.exec.planner.physical.PlannerSettings;
 import org.apache.drill.test.BaseTestQuery;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.experimental.categories.Category;
+import org.junit.rules.ExpectedException;
 import org.junit.runner.RunWith;
 
 import com.google.common.collect.Lists;
@@ -37,10 +38,15 @@ import com.google.common.collect.Maps;
 
 import mockit.integration.junit4.JMockit;
 
+import static org.hamcrest.CoreMatchers.containsString;
+
 @RunWith(JMockit.class)
 @Category({UnlikelyTest.class, SqlFunctionTest.class})
 public class TestCastFunctions extends BaseTestQuery {
 
+  @Rule
+  public ExpectedException thrown = ExpectedException.none();
+
   @Test
   public void testVarbinaryToDate() throws Exception {
     testBuilder()
@@ -380,22 +386,14 @@ public class TestCastFunctions extends BaseTestQuery {
           .baselineValues(new BigDecimal(1), new BigDecimal(1), new BigDecimal(1), new BigDecimal(1))
           .baselineValues(new BigDecimal(-1), new BigDecimal(-1), new BigDecimal(-1), new BigDecimal(-1))
 
-          .baselineValues(new BigDecimal(Integer.MAX_VALUE)
-                              .round(new MathContext(9, RoundingMode.HALF_UP))
-                              .setScale(0, RoundingMode.HALF_UP),
+          .baselineValues(new BigDecimal(Integer.MAX_VALUE),
                           new BigDecimal(Integer.MAX_VALUE),
-                          new BigDecimal(Long.MAX_VALUE)
-                              .round(new MathContext(9, RoundingMode.HALF_UP))
-                              .setScale(0, RoundingMode.HALF_UP),
+                          new BigDecimal(Long.MAX_VALUE),
                           new BigDecimal(Long.MAX_VALUE))
 
-          .baselineValues(new BigDecimal(Integer.MIN_VALUE)
-                              .round(new MathContext(9, RoundingMode.HALF_UP))
-                              .setScale(0, RoundingMode.HALF_UP),
+          .baselineValues(new BigDecimal(Integer.MIN_VALUE),
                           new BigDecimal(Integer.MIN_VALUE),
-                          new BigDecimal(Long.MIN_VALUE)
-                              .round(new MathContext(9, RoundingMode.HALF_UP))
-                              .setScale(0, RoundingMode.HALF_UP),
+                          new BigDecimal(Long.MIN_VALUE),
                           new BigDecimal(Long.MIN_VALUE))
 
           .baselineValues(new BigDecimal(123456789),
@@ -421,21 +419,13 @@ public class TestCastFunctions extends BaseTestQuery {
         .baselineValues(0, 0, 0L, 0L)
         .baselineValues(1, 1, 1L, 1L)
         .baselineValues(-1, -1, -1L, -1L)
-        .baselineValues(new BigDecimal(Integer.MAX_VALUE)
-                          .round(new MathContext(9, RoundingMode.HALF_UP))
-                          .setScale(0, RoundingMode.HALF_UP).intValue(),
+        .baselineValues(Integer.MAX_VALUE,
                         (int) Long.MAX_VALUE,
-                        new BigDecimal(Integer.MAX_VALUE)
-                          .round(new MathContext(9, RoundingMode.HALF_UP))
-                          .setScale(0, RoundingMode.HALF_UP).longValue(),
+                        (long) Integer.MAX_VALUE,
                         Long.MAX_VALUE)
-        .baselineValues(new BigDecimal(Integer.MIN_VALUE)
-                          .round(new MathContext(9, RoundingMode.HALF_UP))
-                          .setScale(0, RoundingMode.HALF_UP).intValue(),
+        .baselineValues(Integer.MIN_VALUE,
                         (int) Long.MIN_VALUE,
-                        new BigDecimal(Integer.MIN_VALUE)
-                          .round(new MathContext(9, RoundingMode.HALF_UP))
-                          .setScale(0, RoundingMode.HALF_UP).longValue(),
+                        (long) Integer.MIN_VALUE,
                         Long.MIN_VALUE)
         .baselineValues(123456789, 123456789, 123456789L, 123456789L)
         .go();
@@ -604,4 +594,74 @@ public class TestCastFunctions extends BaseTestQuery {
         .baselineValues(new BigDecimal("100.00"))
         .go();
   }
+
+  @Test
+  public void testCastDecimalZeroPrecision() throws Exception {
+    String query = "select cast('123.0' as decimal(0, 5))";
+
+    thrown.expect(UserRemoteException.class);
+    thrown.expectMessage(containsString("VALIDATION ERROR: Expected precision greater than 0, but was 0"));
+
+    test(query);
+  }
+
+  @Test
+  public void testCastDecimalGreaterScaleThanPrecision() throws Exception {
+    String query = "select cast('123.0' as decimal(3, 5))";
+
+    thrown.expect(UserRemoteException.class);
+    thrown.expectMessage(containsString("VALIDATION ERROR: Expected scale less than or equal to precision, but was scale 5 and precision 3"));
+
+    test(query);
+  }
+
+  @Test
+  public void testCastIntDecimalOverflow() throws Exception {
+    String query = "select cast(i1 as DECIMAL(4, 0)) as s1 from (select cast(123456 as int) as i1)";
+
+    thrown.expect(UserRemoteException.class);
+    thrown.expectMessage(containsString("VALIDATION ERROR: Value 123456 overflows specified precision 4 with scale 0"));
+
+    test(query);
+  }
+
+  @Test
+  public void testCastBigIntDecimalOverflow() throws Exception {
+    String query = "select cast(i1 as DECIMAL(4, 0)) as s1 from (select cast(123456 as bigint) as i1)";
+
+    thrown.expect(UserRemoteException.class);
+    thrown.expectMessage(containsString("VALIDATION ERROR: Value 123456 overflows specified precision 4 with scale 0"));
+
+    test(query);
+  }
+
+  @Test
+  public void testCastFloatDecimalOverflow() throws Exception {
+    String query = "select cast(i1 as DECIMAL(4, 0)) as s1 from (select cast(123456.123 as float) as i1)";
+
+    thrown.expect(UserRemoteException.class);
+    thrown.expectMessage(containsString("VALIDATION ERROR: Value 123456.123 overflows specified precision 4 with scale 0"));
+
+    test(query);
+  }
+
+  @Test
+  public void testCastDoubleDecimalOverflow() throws Exception {
+    String query = "select cast(i1 as DECIMAL(4, 0)) as s1 from (select cast(123456.123 as double) as i1)";
+
+    thrown.expect(UserRemoteException.class);
+    thrown.expectMessage(containsString("VALIDATION ERROR: Value 123456.123 overflows specified precision 4 with scale 0"));
+
+    test(query);
+  }
+
+  @Test
+  public void testCastVarCharDecimalOverflow() throws Exception {
+    String query = "select cast(i1 as DECIMAL(4, 0)) as s1 from (select cast(123456.123 as varchar) as i1)";
+
+    thrown.expect(UserRemoteException.class);
+    thrown.expectMessage(containsString("VALIDATION ERROR: Value 123456.123 overflows specified precision 4 with scale 0"));
+
+    test(query);
+  }
 }
\ No newline at end of file
diff --git a/exec/java-exec/src/test/java/org/apache/drill/exec/fn/impl/TestVarDecimalFunctions.java b/exec/java-exec/src/test/java/org/apache/drill/exec/fn/impl/TestVarDecimalFunctions.java
index b74a860..4087e67 100644
--- a/exec/java-exec/src/test/java/org/apache/drill/exec/fn/impl/TestVarDecimalFunctions.java
+++ b/exec/java-exec/src/test/java/org/apache/drill/exec/fn/impl/TestVarDecimalFunctions.java
@@ -113,7 +113,7 @@ public class TestVarDecimalFunctions extends BaseTestQuery {
             "/ cast('-1.789' as DECIMAL(4, 3)) as s2,\n" +
             "cast('15.02' as DECIMAL(4, 2)) / cast('15.02' as DECIMAL(4, 2)) as s3,\n" +
             "cast('12.93123456789' as DECIMAL(13, 11)) / cast('1' as DECIMAL(1, 0)) as s4,\n" +
-            "cast('0' as DECIMAL(0, 0)) / cast('15.02' as DECIMAL(4, 2)) as s5";
+            "cast('0' as DECIMAL(1, 0)) / cast('15.02' as DECIMAL(4, 2)) as s5";
     testBuilder()
         .sqlQuery(query)
         .ordered()
@@ -297,8 +297,8 @@ public class TestVarDecimalFunctions extends BaseTestQuery {
             "<> cast('1.9999999999999999999999999999234567891' as DECIMAL(38, 37)) as s2,\n" +
             // the same value but different scale and precision
             "cast('1234567.89' as DECIMAL(9, 2)) = cast('1234567.890' as DECIMAL(10, 3)) as s3,\n" +
-            "cast('0' as DECIMAL(4, 2)) = cast('0' as DECIMAL(0, 0)) as s4,\n" +
-            "cast('0' as DECIMAL(4, 2)) <> cast('0' as DECIMAL(0, 0)) as s5,\n" +
+            "cast('0' as DECIMAL(4, 2)) = cast('0' as DECIMAL(1, 0)) as s4,\n" +
+            "cast('0' as DECIMAL(4, 2)) <> cast('0' as DECIMAL(1, 0)) as s5,\n" +
             "cast('12.93123456789' as DECIMAL(13, 11)) = cast('12.93123456788' as DECIMAL(13, 11)) as s6";
     testBuilder()
         .sqlQuery(query)
@@ -318,7 +318,7 @@ public class TestVarDecimalFunctions extends BaseTestQuery {
             "< cast('1.9999999999999999999999999999234567892' as DECIMAL(38, 37)) as s2,\n" +
             // the same value but different scale and precision
             "cast('1234567.89' as DECIMAL(9, 2)) < cast('1234567.890' as DECIMAL(10, 3)) as s3,\n" +
-            "cast('0' as DECIMAL(4, 2)) < cast('0' as DECIMAL(0, 0)) as s4,\n" +
+            "cast('0' as DECIMAL(4, 2)) < cast('0' as DECIMAL(1, 0)) as s4,\n" +
             "cast('12.93123456789' as DECIMAL(13, 11)) < cast('12.93123456788' as DECIMAL(13, 11)) as s5";
     testBuilder()
         .sqlQuery(query)
@@ -338,7 +338,7 @@ public class TestVarDecimalFunctions extends BaseTestQuery {
             "<= cast('1.9999999999999999999999999999234567892' as DECIMAL(38, 37)) as s2,\n" +
             // the same value but different scale and precision
             "cast('1234567.89' as DECIMAL(9, 2)) <= cast('1234567.890' as DECIMAL(10, 3)) as s3,\n" +
-            "cast('0' as DECIMAL(4, 2)) <= cast('0' as DECIMAL(0, 0)) as s4,\n" +
+            "cast('0' as DECIMAL(4, 2)) <= cast('0' as DECIMAL(1, 0)) as s4,\n" +
             "cast('12.93123456789' as DECIMAL(13, 11)) <= cast('12.93123456788' as DECIMAL(13, 11)) as s5";
     testBuilder()
         .sqlQuery(query)
@@ -358,7 +358,7 @@ public class TestVarDecimalFunctions extends BaseTestQuery {
             "> cast('1.9999999999999999999999999999234567892' as DECIMAL(38, 37)) as s2,\n" +
             // the same value but different scale and precision
             "cast('1234567.89' as DECIMAL(9, 2)) > cast('1234567.890' as DECIMAL(10, 3)) as s3,\n" +
-            "cast('0' as DECIMAL(4, 2)) > cast('0' as DECIMAL(0, 0)) as s4,\n" +
+            "cast('0' as DECIMAL(4, 2)) > cast('0' as DECIMAL(1, 0)) as s4,\n" +
             "cast('12.93123456789' as DECIMAL(13, 11)) > cast('12.93123456788' as DECIMAL(13, 11)) as s5";
     testBuilder()
         .sqlQuery(query)
@@ -378,7 +378,7 @@ public class TestVarDecimalFunctions extends BaseTestQuery {
             ">= cast('1.9999999999999999999999999999234567892' as DECIMAL(38, 37)) as s2,\n" +
             // the same value but different scale and precision
             "cast('1234567.89' as DECIMAL(9, 2)) >= cast('1234567.890' as DECIMAL(10, 3)) as s3,\n" +
-            "cast('0' as DECIMAL(4, 2)) >= cast('0' as DECIMAL(0, 0)) as s4,\n" +
+            "cast('0' as DECIMAL(4, 2)) >= cast('0' as DECIMAL(1, 0)) as s4,\n" +
             "cast('12.93123456789' as DECIMAL(13, 11)) >= cast('12.93123456788' as DECIMAL(13, 11)) as s5";
     testBuilder()
         .sqlQuery(query)
@@ -398,8 +398,8 @@ public class TestVarDecimalFunctions extends BaseTestQuery {
             "cast('1.9999999999999999999999999999234567892' as DECIMAL(38, 37))) as s2,\n" +
             // the same value but different scale and precision
             "compare_to_nulls_high(cast('1234567.89' as DECIMAL(9, 2)), cast('1234567.890' as DECIMAL(10, 3))) as s3,\n" +
-            "compare_to_nulls_high(cast('0' as DECIMAL(4, 2)), cast('0' as DECIMAL(0, 0))) as s4,\n" +
-            "compare_to_nulls_high(cast('0' as DECIMAL(4, 2)), cast(null as DECIMAL(0, 0))) as s5,\n" +
+            "compare_to_nulls_high(cast('0' as DECIMAL(4, 2)), cast('0' as DECIMAL(1, 0))) as s4,\n" +
+            "compare_to_nulls_high(cast('0' as DECIMAL(4, 2)), cast(null as DECIMAL(1, 0))) as s5,\n" +
             "compare_to_nulls_high(cast('12.93123456789' as DECIMAL(13, 11)), " +
             "cast('12.93123456788' as DECIMAL(13, 11))) as s6";
     testBuilder()
@@ -420,8 +420,8 @@ public class TestVarDecimalFunctions extends BaseTestQuery {
             "cast('1.9999999999999999999999999999234567892' as DECIMAL(38, 37))) as s2,\n" +
             // the same value but different scale and precision
             "compare_to_nulls_low(cast('1234567.89' as DECIMAL(9, 2)), cast('1234567.890' as DECIMAL(10, 3))) as s3,\n" +
-            "compare_to_nulls_low(cast('0' as DECIMAL(4, 2)), cast('0' as DECIMAL(0, 0))) as s4,\n" +
-            "compare_to_nulls_low(cast('0' as DECIMAL(4, 2)), cast(null as DECIMAL(0, 0))) as s5,\n" +
+            "compare_to_nulls_low(cast('0' as DECIMAL(4, 2)), cast('0' as DECIMAL(1, 0))) as s4,\n" +
+            "compare_to_nulls_low(cast('0' as DECIMAL(4, 2)), cast(null as DECIMAL(1, 0))) as s5,\n" +
             "compare_to_nulls_low(cast('12.93123456789' as DECIMAL(13, 11)), " +
             "cast('12.93123456788' as DECIMAL(13, 11))) as s6";
     testBuilder()
@@ -571,8 +571,7 @@ public class TestVarDecimalFunctions extends BaseTestQuery {
             "cast(i3 as DECIMAL(8, 7)) as s3,\n" +
             "cast(i4 as DECIMAL(6, 6)) as s4,\n" +
             "cast(i5 as DECIMAL(7, 0)) as s5,\n" +
-            "cast(i6 as DECIMAL(7, 46)) as s6,\n" +
-            "cast(i7 as DECIMAL(17, 0)) as s7\n" +
+            "cast(i6 as DECIMAL(38, 38)) as s6\n" +
         "from (" +
             "select\n" +
                 "cast(0 as float) as i1,\n" +
@@ -580,17 +579,15 @@ public class TestVarDecimalFunctions extends BaseTestQuery {
                 "cast(-1.5022222 as float) as i3,\n" +
                 "cast(-0.987654 as float) as i4,\n" +
                 "cast(9999999 as float) as i5,\n" +
-                "cast('%s' as float) as i6,\n" +
-                "cast('%s' as float) as i7)";
+                "cast('%s' as float) as i6)";
 
     testBuilder()
-        .sqlQuery(query, Float.MIN_VALUE, Float.MAX_VALUE)
-        .ordered()
-        .baselineColumns("s1", "s2", "s3", "s4", "s5", "s6", "s7")
+        .sqlQuery(query, Float.MIN_VALUE)
+        .unOrdered()
+        .baselineColumns("s1", "s2", "s3", "s4", "s5", "s6")
         .baselineValues(BigDecimal.valueOf(0), new BigDecimal("1.234567"),
             new BigDecimal("-1.5022222"), new BigDecimal("-0.987654"), BigDecimal.valueOf(9999999),
-            new BigDecimal(String.format("%s", Float.MIN_VALUE)),
-            new BigDecimal("340282350000000000000000000000000000000")) // Float.MAX_VALUE in non-scientific format
+            new BigDecimal(Float.MIN_VALUE).setScale(38, RoundingMode.HALF_UP))
         .go();
   }
 
@@ -603,8 +600,7 @@ public class TestVarDecimalFunctions extends BaseTestQuery {
             "cast(i3 as float) as s3,\n" +
             "cast(i4 as float) as s4,\n" +
             "cast(i5 as float) as s5,\n" +
-            "cast(i6 as float) as s6,\n" +
-            "cast(i7 as float) as s7\n" +
+            "cast(i6 as float) as s6\n" +
         "from (" +
             "select\n" +
                 "cast('999999999999999999999999999.92345678912' as DECIMAL(38, 11)) as i1,\n" +
@@ -612,17 +608,16 @@ public class TestVarDecimalFunctions extends BaseTestQuery {
                 "cast('-1234567891234567891234567891234567.89' as DECIMAL(36, 2)) as i3,\n" +
                 "cast('0' as DECIMAL(36, 3)) as i4,\n" +
                 "cast('15.02' as DECIMAL(4, 2)) as i5,\n" +
-                "cast('%s' as DECIMAL(2, 46)) as i6,\n" +
-                "cast('%s' as DECIMAL(8, 0)) as i7)";
+                "cast('%s' as DECIMAL(38, 38)) as i6)";
 
     testBuilder()
-        .sqlQuery(query, Float.MIN_VALUE, Float.MAX_VALUE)
-        .ordered()
-        .baselineColumns("s1", "s2", "s3", "s4", "s5", "s6", "s7")
+        .sqlQuery(query, Float.MIN_VALUE)
+        .unOrdered()
+        .baselineColumns("s1", "s2", "s3", "s4", "s5", "s6")
         .baselineValues(new BigDecimal("999999999999999999999999999.92345678912").floatValue(),
             new BigDecimal("0.32345678912345678912345678912345678912").floatValue(),
             new BigDecimal("-1234567891234567891234567891234567.89").floatValue(),
-            0f, 15.02f, Float.MIN_VALUE, Float.MAX_VALUE)
+            0f, 15.02f, 0.0f)
         .go();
   }
 
@@ -635,8 +630,7 @@ public class TestVarDecimalFunctions extends BaseTestQuery {
             "cast(i3 as DECIMAL(8, 7)) as s3,\n" +
             "cast(i4 as DECIMAL(6, 6)) as s4,\n" +
             "cast(i5 as DECIMAL(7, 0)) as s5,\n" +
-            "cast(i6 as DECIMAL(17, 325)) as s6,\n" +
-            "cast(i7 as DECIMAL(17, 0)) as s7\n" +
+            "cast(i6 as DECIMAL(38, 38)) as s6\n" +
         "from (" +
             "select\n" +
                 "cast(0 as double) as i1,\n" +
@@ -644,17 +638,15 @@ public class TestVarDecimalFunctions extends BaseTestQuery {
                 "cast(-1.5022222 as double) as i3,\n" +
                 "cast(-0.987654 as double) as i4,\n" +
                 "cast(9999999 as double) as i5,\n" +
-                "cast('%e' as double) as i6,\n" +
-                "cast('%f' as double) as i7)";
+                "cast('%e' as double) as i6)";
 
     testBuilder()
-        .sqlQuery(query, Double.MIN_VALUE, Double.MAX_VALUE)
-        .ordered()
-        .baselineColumns("s1", "s2", "s3", "s4", "s5", "s6", "s7")
+        .sqlQuery(query, Double.MIN_VALUE)
+        .unOrdered()
+        .baselineColumns("s1", "s2", "s3", "s4", "s5", "s6")
         .baselineValues(BigDecimal.valueOf(0), new BigDecimal("1.234567"),
             new BigDecimal("-1.5022222"), new BigDecimal("-0.987654"), BigDecimal.valueOf(9999999),
-            new BigDecimal(String.valueOf(Double.MIN_VALUE)),
-            new BigDecimal(String.format("%1.0f", Double.MAX_VALUE))) // non-scientific format
+            new BigDecimal(String.valueOf(Double.MIN_VALUE)).setScale(38, RoundingMode.HALF_UP))
         .go();
   }
 
@@ -667,8 +659,7 @@ public class TestVarDecimalFunctions extends BaseTestQuery {
             "cast(i3 as double) as s3,\n" +
             "cast(i4 as double) as s4,\n" +
             "cast(i5 as double) as s5,\n" +
-            "cast(i6 as double) as s6,\n" +
-            "cast(i7 as double) as s7\n" +
+            "cast(i6 as double) as s6\n" +
         "from (" +
             "select\n" +
                 "cast('999999999999999999999999999.92345678912' as DECIMAL(38, 11)) as i1,\n" +
@@ -676,17 +667,16 @@ public class TestVarDecimalFunctions extends BaseTestQuery {
                 "cast('-1234567891234567891234567891234567.89' as DECIMAL(36, 2)) as i3,\n" +
                 "cast('0' as DECIMAL(36, 3)) as i4,\n" +
                 "cast('15.02' as DECIMAL(4, 2)) as i5,\n" +
-                "cast('%e' as DECIMAL(17, 325)) as i6,\n" +
-                "cast('%f' as DECIMAL(17, 0)) as i7)";
+                "cast('%e' as DECIMAL(38, 38)) as i6)";
 
     testBuilder()
-        .sqlQuery(query, Double.MIN_VALUE, Double.MAX_VALUE)
-        .ordered()
-        .baselineColumns("s1", "s2", "s3", "s4", "s5", "s6", "s7")
+        .sqlQuery(query, Double.MIN_VALUE)
+        .unOrdered()
+        .baselineColumns("s1", "s2", "s3", "s4", "s5", "s6")
         .baselineValues(new BigDecimal("999999999999999999999999999.92345678912").doubleValue(),
             new BigDecimal("0.32345678912345678912345678912345678912").doubleValue(),
             new BigDecimal("-1234567891234567891234567891234567.89").doubleValue(),
-            0d, 15.02, Double.MIN_VALUE, Double.MAX_VALUE)
+            0d, 15.02, 0.)
         .go();
   }
 
@@ -740,4 +730,32 @@ public class TestVarDecimalFunctions extends BaseTestQuery {
             "-1234567891234567891234567891234567.9", "0", "15")
         .go();
   }
+
+  @Test
+  public void testDecimalNegate() throws Exception {
+    String query =
+        "select\n" +
+            "negative(i1) as s1,\n" +
+            "-i2 as s2,\n" +
+            "negative(i3) as s3,\n" +
+            "-i4 as s4,\n" +
+            "negative(i5) as s5\n" +
+            "from (" +
+            "select\n" +
+            "cast('999999999999999999999999999.92345678912' as DECIMAL(38, 11)) as i1,\n" +
+            "cast('0.32345678912345678912345678912345678912' as DECIMAL(38, 38)) as i2,\n" +
+            "cast('-1234567891234567891234567891234567.89' as DECIMAL(36, 2)) as i3,\n" +
+            "cast('0' as DECIMAL(36, 3)) as i4,\n" +
+            "cast('15.02' as DECIMAL(4, 2)) as i5)";
+    testBuilder()
+        .sqlQuery(query)
+        .unOrdered()
+        .baselineColumns("s1", "s2", "s3", "s4", "s5")
+        .baselineValues(new BigDecimal("-999999999999999999999999999.92345678912"),
+            new BigDecimal("-0.32345678912345678912345678912345678912"),
+            new BigDecimal("1234567891234567891234567891234567.89"),
+            new BigDecimal("0.000"),
+            new BigDecimal("-15.02"))
+        .go();
+  }
 }
diff --git a/exec/java-exec/src/test/resources/decimal/cast_decimal_int.json b/exec/java-exec/src/test/resources/decimal/cast_decimal_int.json
index 27df35a..4e8b700 100644
--- a/exec/java-exec/src/test/resources/decimal/cast_decimal_int.json
+++ b/exec/java-exec/src/test/resources/decimal/cast_decimal_int.json
@@ -19,7 +19,7 @@
       "pop" : "project",
       "@id" : 2,
       "exprs" : [
-        { "ref" : "DEC9_COL", "expr": "(cast(cast(INT_COL as int) as vardecimal(9, 0)))" },
+        { "ref" : "DEC9_COL", "expr": "(cast(cast(INT_COL as int) as vardecimal(10, 0)))" },
         { "ref" : "DEC38_COL", "expr": "(cast(BIGINT_COL as vardecimal(38, 0)))" }
       ],
 
diff --git a/exec/java-exec/src/test/resources/decimal/cast_decimal_vardecimal.json b/exec/java-exec/src/test/resources/decimal/cast_decimal_vardecimal.json
index 92c8237..4f79194 100644
--- a/exec/java-exec/src/test/resources/decimal/cast_decimal_vardecimal.json
+++ b/exec/java-exec/src/test/resources/decimal/cast_decimal_vardecimal.json
@@ -21,8 +21,8 @@
       "exprs" : [
         { "ref" : "DEC28", "expr": "(cast(B as vardecimal(38, 20)))" },
         { "ref" : "DEC38", "expr": "(cast(A as vardecimal(28, 16)))" },
-        { "ref" : "DEC18", "expr": "(cast(B as vardecimal(18, 9)))" },
-        { "ref" : "DEC9", "expr": "(cast(A as vardecimal(9, 0)))" }
+        { "ref" : "DEC18", "expr": "(cast(B as vardecimal(19, 0)))" },
+        { "ref" : "DEC9", "expr": "(cast(A as vardecimal(10, 0)))" }
       ],
 
       "child" : 4
@@ -32,8 +32,8 @@
       "exprs" : [
         {"ref": "DEC28_COL", "expr" : "cast(DEC28 as decimal38sparse(38, 20))"},
         {"ref": "DEC38_COL", "expr" : "cast(DEC38 as decimal28sparse(28, 16))"},
-        {"ref": "DEC18_COL", "expr" : "cast(DEC18 as decimal18(18, 0))"},
-        {"ref": "DEC9_COL", "expr" : "cast(DEC9 as decimal9(9, 0))"}
+        {"ref": "DEC18_COL", "expr" : "cast(DEC18 as decimal18(19, 0))"},
+        {"ref": "DEC9_COL", "expr" : "cast(DEC9 as decimal9(10, 0))"}
       ],
 
       "child" : 3
diff --git a/exec/java-exec/src/test/resources/decimal/cast_int_decimal.json b/exec/java-exec/src/test/resources/decimal/cast_int_decimal.json
index 2f09992..0ea7076 100644
--- a/exec/java-exec/src/test/resources/decimal/cast_int_decimal.json
+++ b/exec/java-exec/src/test/resources/decimal/cast_int_decimal.json
@@ -29,9 +29,9 @@
       "pop" : "project",
       "@id" : 4,
       "exprs" : [
-        {"ref": "DEC9_INT", "expr" : "cast(INT_COL as vardecimal(9, 0))"},
+        {"ref": "DEC9_INT", "expr" : "cast(INT_COL as vardecimal(10, 0))"},
         {"ref": "DEC38_INT", "expr" : "cast(INT_COL as vardecimal(38, 0))"},
-        {"ref": "DEC9_BIGINT", "expr" : "cast(BIGINT_COL as vardecimal(9, 0))"},
+        {"ref": "DEC9_BIGINT", "expr" : "cast(BIGINT_COL as vardecimal(19, 0))"},
         {"ref": "DEC38_BIGINT", "expr" : "cast(BIGINT_COL as vardecimal(38, 0))"}
       ],
 
diff --git a/exec/java-exec/src/test/resources/decimal/cast_vardecimal_decimal.json b/exec/java-exec/src/test/resources/decimal/cast_vardecimal_decimal.json
index 4a4c5c9..1b5252d 100644
--- a/exec/java-exec/src/test/resources/decimal/cast_vardecimal_decimal.json
+++ b/exec/java-exec/src/test/resources/decimal/cast_vardecimal_decimal.json
@@ -21,8 +21,8 @@
       "exprs" : [
         { "ref" : "DEC28", "expr": "(cast(B as vardecimal(38, 16)))" },
         { "ref" : "DEC38", "expr": "(cast(A as vardecimal(28, 16)))" },
-        { "ref" : "DEC18", "expr": "(cast(B as vardecimal(18, 9)))" },
-        { "ref" : "DEC9", "expr": "(cast(A as vardecimal(9, 0)))" }
+        { "ref" : "DEC18", "expr": "(cast(B as vardecimal(18, 0)))" },
+        { "ref" : "DEC9", "expr": "(cast(A as vardecimal(10, 0)))" }
       ],
 
       "child" : 1
@@ -34,7 +34,7 @@
         {"ref": "DEC28_COL", "expr" : "cast(DEC28 as decimal38sparse(38, 16))"},
         {"ref": "DEC38_COL", "expr" : "cast(DEC38 as decimal28sparse(28, 16))"},
         {"ref": "DEC18_COL", "expr" : "cast(DEC18 as decimal18(18, 0))"},
-        {"ref": "DEC9_COL", "expr" : "cast(DEC9 as decimal9(9, 0))"}
+        {"ref": "DEC9_COL", "expr" : "cast(DEC9 as decimal9(10, 0))"}
       ],
 
       "child" : 2
diff --git a/exec/vector/src/main/java/org/apache/drill/exec/util/DecimalUtility.java b/exec/vector/src/main/java/org/apache/drill/exec/util/DecimalUtility.java
index 16ff002..5f2dade 100644
--- a/exec/vector/src/main/java/org/apache/drill/exec/util/DecimalUtility.java
+++ b/exec/vector/src/main/java/org/apache/drill/exec/util/DecimalUtility.java
@@ -26,7 +26,10 @@ import java.math.BigDecimal;
 import java.math.BigInteger;
 import java.math.RoundingMode;
 
+import org.apache.drill.common.exceptions.UserException;
 import org.apache.drill.common.types.TypeProtos;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 @SuppressWarnings("WeakerAccess")
 public class DecimalUtility {
@@ -37,6 +40,8 @@ public class DecimalUtility {
   public final static int DIGITS_BASE = 1000000000;
   public final static int INTEGER_SIZE = Integer.SIZE / 8;
 
+  private static final Logger logger = LoggerFactory.getLogger(DecimalUtility.class);
+
   /**
    * Given the number of actual digits this function returns the
    * number of indexes it will occupy in the array of integers
@@ -417,4 +422,22 @@ public class DecimalUtility {
         return defaultPrecision;
     }
   }
+
+  /**
+   * Checks that the specified value may be fit into the value with specified
+   * {@code desiredPrecision} precision and {@code desiredScale} scale.
+   * Otherwise, the exception is thrown.
+   *
+   * @param value            BigDecimal value to check
+   * @param desiredPrecision precision for the resulting value
+   * @param desiredScale     scale for the resulting value
+   */
+  public static void checkValueOverflow(BigDecimal value, int desiredPrecision, int desiredScale) {
+    if (value.precision() - value.scale() > desiredPrecision - desiredScale) {
+      throw UserException.validationError()
+          .message("Value %s overflows specified precision %s with scale %s.",
+              value, desiredPrecision, desiredScale)
+          .build(logger);
+    }
+  }
 }