DRILL-793: Fix output type's scale and precision for math functions.

Project: http://git-wip-us.apache.org/repos/asf/incubator-drill/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-drill/commit/1465e11c
Tree: http://git-wip-us.apache.org/repos/asf/incubator-drill/tree/1465e11c
Diff: http://git-wip-us.apache.org/repos/asf/incubator-drill/diff/1465e11c

Branch: refs/heads/master
Commit: 1465e11ca58fc139ae2f444b236c2db514276687
Parents: 5079f8b
Author: Mehant Baid <[email protected]>
Authored: Thu Jun 12 18:09:57 2014 -0700
Committer: Jacques Nadeau <[email protected]>
Committed: Tue Jun 17 16:04:09 2014 -0700

----------------------------------------------------------------------
 .../DecimalScalePrecisionDivideFunction.java    | 65 +++++++++++++
 .../util/DecimalScalePrecisionMulFunction.java  | 52 +++++++++++
 .../drill/common/util/DecimalUtility.java       | 38 ++++++++
 .../templates/Decimal/DecimalFunctions.java     | 97 +++++++++++++++-----
 .../expr/fn/DrillDecimalDivScaleFuncHolder.java | 28 ++++--
 .../expr/fn/DrillDecimalSumScaleFuncHolder.java | 25 ++++-
 .../drill/exec/expr/fn/DrillFuncHolder.java     |  2 +-
 .../drill/exec/resolver/TypeCastRules.java      | 15 +--
 .../drill/exec/physical/impl/TestDecimal.java   |  5 +-
 .../resources/decimal/test_decimal_complex.json |  8 +-
 .../drill/jdbc/test/TestFunctionsQuery.java     | 15 +++
 11 files changed, 299 insertions(+), 51 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-drill/blob/1465e11c/common/src/main/java/org/apache/drill/common/util/DecimalScalePrecisionDivideFunction.java
----------------------------------------------------------------------
diff --git 
a/common/src/main/java/org/apache/drill/common/util/DecimalScalePrecisionDivideFunction.java
 
b/common/src/main/java/org/apache/drill/common/util/DecimalScalePrecisionDivideFunction.java
new file mode 100644
index 0000000..5a53603
--- /dev/null
+++ 
b/common/src/main/java/org/apache/drill/common/util/DecimalScalePrecisionDivideFunction.java
@@ -0,0 +1,65 @@
+/**
+ * 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.drill.common.util;
+
+/*
+ * Here we compute the scale and precision of the output decimal data type
+ * based on the input scale and precision. Since division operation can be
+ * a multiplication operation we compute the scale to be the sum of the inputs.
+ * The precision is computed by getting the sum of integer digits of the input
+ * and adding it with scale. The scale is further expanded to occupy the 
remaining
+ * digits in the given precision range
+ *
+ * Eg: Input1 : precision = 5, scale = 3 ==> max integer digits = 2
+ *     Input2 : precision = 7, scale = 4 ==> max integer digits = 3
+ *
+ *     Output: max integer digits ==> 2 + 3 = 5
+ *             max scale          ==> 3 + 4 = 7
+ *
+ *             Minimum precision required ==> 5 + 7 = 12
+ *
+ * Since our minimum precision required is 12, we will use DECIMAL18 as the 
output type
+ * but since this is divide we will grant the remaining digits in DECIMAL18 to 
scale
+ * so we have the following
+ *    output scale      ==> 7 + (18 - 12) = 13
+ *    output precision  ==> 18
+ */
+public class DecimalScalePrecisionDivideFunction {
+  private int outputScale = 0;
+  private int outputPrecision = 0;
+
+  public DecimalScalePrecisionDivideFunction(int leftPrecision, int leftScale, 
int rightPrecision, int rightScale) {
+    // compute the output scale and precision here
+    outputScale = leftScale + rightScale;
+    int integerDigits = (leftPrecision - leftScale) + (rightPrecision - 
rightScale);
+
+    outputPrecision = DecimalUtility.getPrecisionRange(outputScale + 
integerDigits);
+
+    // Try and increase the scale if we have any room
+    outputScale = (outputPrecision - integerDigits >= 0) ? (outputPrecision - 
integerDigits) : 0;
+  }
+
+  public int getOutputScale() {
+    return outputScale;
+  }
+
+  public int getOutputPrecision() {
+    return outputPrecision;
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-drill/blob/1465e11c/common/src/main/java/org/apache/drill/common/util/DecimalScalePrecisionMulFunction.java
----------------------------------------------------------------------
diff --git 
a/common/src/main/java/org/apache/drill/common/util/DecimalScalePrecisionMulFunction.java
 
b/common/src/main/java/org/apache/drill/common/util/DecimalScalePrecisionMulFunction.java
new file mode 100644
index 0000000..1fd3427
--- /dev/null
+++ 
b/common/src/main/java/org/apache/drill/common/util/DecimalScalePrecisionMulFunction.java
@@ -0,0 +1,52 @@
+/**
+ * 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.drill.common.util;
+
+/*
+ * Here we compute the output scale and precision of the multiply function.
+ * We simply add the input scale and precision to determine the output's scale
+ * and precision
+ */
+public class DecimalScalePrecisionMulFunction {
+  private int outputScale = 0;
+  private int outputPrecision = 0;
+
+  public DecimalScalePrecisionMulFunction(int leftPrecision, int leftScale, 
int rightPrecision, int rightScale) {
+    // compute the output scale and precision here
+    outputScale = leftScale + rightScale;
+    int integerDigits = (leftPrecision - leftScale) + (rightPrecision - 
rightScale);
+
+    outputPrecision = integerDigits + outputScale;
+
+    // If we are beyond the maximum precision range, cut down the fractional 
part
+    if (outputPrecision > 38) {
+      outputPrecision = 38;
+      outputScale = (outputPrecision - integerDigits >= 0) ? (outputPrecision 
- integerDigits) : 0;
+    }
+  }
+
+  public int getOutputScale() {
+    return outputScale;
+  }
+
+  public int getOutputPrecision() {
+    return outputPrecision;
+  }
+}
+

http://git-wip-us.apache.org/repos/asf/incubator-drill/blob/1465e11c/common/src/main/java/org/apache/drill/common/util/DecimalUtility.java
----------------------------------------------------------------------
diff --git 
a/common/src/main/java/org/apache/drill/common/util/DecimalUtility.java 
b/common/src/main/java/org/apache/drill/common/util/DecimalUtility.java
index 4cc80ea..7f1a4a0 100644
--- a/common/src/main/java/org/apache/drill/common/util/DecimalUtility.java
+++ b/common/src/main/java/org/apache/drill/common/util/DecimalUtility.java
@@ -19,6 +19,7 @@ package org.apache.drill.common.util;
 
 import io.netty.buffer.ByteBuf;
 import io.netty.buffer.Unpooled;
+import org.apache.drill.common.types.TypeProtos;
 
 import java.math.BigDecimal;
 import java.math.BigInteger;
@@ -646,5 +647,42 @@ public class DecimalUtility {
     }
   }
 
+  /*
+   * Function returns the Minor decimal type given the precision
+   */
+  public static TypeProtos.MinorType getDecimalDataType(int precision) {
+    if (precision <= 9) {
+      return TypeProtos.MinorType.DECIMAL9;
+    } else if (precision <= 18) {
+      return TypeProtos.MinorType.DECIMAL18;
+    } else if (precision <= 28) {
+      return TypeProtos.MinorType.DECIMAL28SPARSE;
+    } else {
+      return TypeProtos.MinorType.DECIMAL38SPARSE;
+    }
+  }
+
+  public static int getMaxPrecision(TypeProtos.MinorType decimalType) {
+    if (decimalType == TypeProtos.MinorType.DECIMAL9) {
+      return 9;
+    } else if (decimalType == TypeProtos.MinorType.DECIMAL18) {
+      return 18;
+    } else if (decimalType == TypeProtos.MinorType.DECIMAL28SPARSE) {
+      return 28;
+    } else if (decimalType == TypeProtos.MinorType.DECIMAL38SPARSE) {
+      return 38;
+    }
+    return 0;
+  }
+
+
+  /*
+   * Given a precision it provides the max precision of that decimal data type;
+   * For eg: given the precision 12, we would use DECIMAL18 to store the data
+   * which has a max precision range of 18 digits
+   */
+  public static int getPrecisionRange(int precision) {
+    return getMaxPrecision(getDecimalDataType(precision));
+  }
 }
 

http://git-wip-us.apache.org/repos/asf/incubator-drill/blob/1465e11c/exec/java-exec/src/main/codegen/templates/Decimal/DecimalFunctions.java
----------------------------------------------------------------------
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 b294396..a41fb20 100644
--- a/exec/java-exec/src/main/codegen/templates/Decimal/DecimalFunctions.java
+++ b/exec/java-exec/src/main/codegen/templates/Decimal/DecimalFunctions.java
@@ -379,6 +379,8 @@ public class ${type.name}Functions {
         @Param ${type.name}Holder right;
         @Workspace ByteBuf buffer;
         @Workspace int[] tempResult;
+        @Workspace int outputScale;
+        @Workspace int outputPrecision;
         @Output ${type.name}Holder result;
 
         public void setup(RecordBatch incoming) {
@@ -386,10 +388,20 @@ public class ${type.name}Functions {
             buffer = io.netty.buffer.Unpooled.wrappedBuffer(new byte[size]);
             buffer = new io.netty.buffer.SwappedByteBuf(buffer);
             tempResult = new int[${type.storage} * ${type.storage}];
+            outputPrecision = Integer.MIN_VALUE;
         }
 
         public void eval() {
 
+            if (outputPrecision == Integer.MIN_VALUE) {
+                org.apache.drill.common.util.DecimalScalePrecisionMulFunction 
resultScalePrec =
+                new 
org.apache.drill.common.util.DecimalScalePrecisionMulFunction((int) 
left.precision, (int) left.scale, (int) right.precision, (int) right.scale);
+                outputScale = resultScalePrec.getOutputScale();
+                outputPrecision = resultScalePrec.getOutputPrecision();
+            }
+            // Set the scale and precision
+            result.scale = outputScale;
+            result.precision = outputPrecision;
             result.buffer = buffer;
             result.start = 0;
 
@@ -474,10 +486,6 @@ public class ${type.name}Functions {
               result.setInteger(outputIndex--, 0);
             }
 
-            // Set the scale and precision
-            result.scale = left.scale + right.scale;
-            result.precision = result.maxPrecision;
-
             result.sign = (left.sign == right.sign) ? false : true;
         }
     }
@@ -489,17 +497,25 @@ public class ${type.name}Functions {
         @Param ${type.name}Holder right;
         @Output ${type.name}Holder result;
         @Workspace ByteBuf buffer;
+        @Workspace int outputScale;
+        @Workspace int outputPrecision;
 
         public void setup(RecordBatch incoming) {
             int size = (${type.storage} * 
(org.apache.drill.common.util.DecimalUtility.integerSize));
             buffer = io.netty.buffer.Unpooled.wrappedBuffer(new byte[size]);
             buffer = new io.netty.buffer.SwappedByteBuf(buffer);
+            outputPrecision = Integer.MIN_VALUE;
         }
 
         public void eval() {
-
-            result.scale = left.scale;
-            result.precision = left.precision;
+            if (outputPrecision == Integer.MIN_VALUE) {
+                
org.apache.drill.common.util.DecimalScalePrecisionDivideFunction 
resultScalePrec =
+                new 
org.apache.drill.common.util.DecimalScalePrecisionDivideFunction((int) 
left.precision, (int) left.scale, (int) right.precision, (int) right.scale);
+                outputScale = resultScalePrec.getOutputScale();
+                outputPrecision = resultScalePrec.getOutputPrecision();
+            }
+            result.scale = outputScale;
+            result.precision = outputPrecision;
             result.buffer = buffer;
             result.start = 0;
 
@@ -524,17 +540,25 @@ public class ${type.name}Functions {
         @Param ${type.name}Holder right;
         @Output ${type.name}Holder result;
         @Workspace ByteBuf buffer;
+        @Workspace int outputScale;
+        @Workspace int outputPrecision;
 
         public void setup(RecordBatch incoming) {
             int size = (${type.storage} * 
(org.apache.drill.common.util.DecimalUtility.integerSize));
             buffer = io.netty.buffer.Unpooled.wrappedBuffer(new byte[size]);
             buffer = new io.netty.buffer.SwappedByteBuf(buffer);
+            outputPrecision = Integer.MIN_VALUE;
         }
 
         public void eval() {
-
-            result.scale = left.scale;
-            result.precision = left.precision;
+            if (outputPrecision == Integer.MIN_VALUE) {
+                
org.apache.drill.common.util.DecimalScalePrecisionDivideFunction 
resultScalePrec =
+                new 
org.apache.drill.common.util.DecimalScalePrecisionDivideFunction((int) 
left.precision, (int) left.scale, (int) right.precision, (int) right.scale);
+                outputScale = resultScalePrec.getOutputScale();
+                outputPrecision = resultScalePrec.getOutputPrecision();
+            }
+            result.scale = outputScale;
+            result.precision = outputPrecision;
             result.buffer = buffer;
             result.start = 0;
 
@@ -542,7 +566,7 @@ public class ${type.name}Functions {
             java.math.BigDecimal denominator = 
org.apache.drill.common.util.DecimalUtility.getBigDecimalFromByteBuf(right.buffer,
 right.start, right.nDecimalDigits, right.scale, true);
 
             java.math.BigDecimal output = numerator.remainder(denominator);
-            output.setScale(left.scale, java.math.BigDecimal.ROUND_DOWN);
+            output.setScale(result.scale, java.math.BigDecimal.ROUND_DOWN);
 
             // Initialize the result buffer
             for (int i = 0; i < ${type.storage}; i++) {
@@ -1204,15 +1228,24 @@ public class ${type.name}Functions {
 
         @Param ${type.name}Holder left;
         @Param ${type.name}Holder right;
+        @Workspace int outputScale;
+        @Workspace int outputPrecision;
         @Output ${type.name}Holder result;
 
-        public void setup(RecordBatch incoming) {}
+        public void setup(RecordBatch incoming) {
+            outputPrecision = Integer.MIN_VALUE;
+        }
 
         public void eval() {
-
+            if (outputPrecision == Integer.MIN_VALUE) {
+                org.apache.drill.common.util.DecimalScalePrecisionMulFunction 
resultScalePrec =
+                new 
org.apache.drill.common.util.DecimalScalePrecisionMulFunction((int) 
left.precision, (int) left.scale, (int) right.precision, (int) right.scale);
+                outputScale = resultScalePrec.getOutputScale();
+                outputPrecision = resultScalePrec.getOutputPrecision();
+            }
             result.value = left.value * right.value;
-            result.precision = result.maxPrecision;
-            result.scale = left.scale + right.scale;
+            result.precision = outputPrecision;
+            result.scale = outputScale;
         }
     }
 
@@ -1242,12 +1275,23 @@ public class ${type.name}Functions {
         @Param ${type.name}Holder left;
         @Param ${type.name}Holder right;
         @Output ${type.name}Holder result;
+        @Workspace int outputScale;
+        @Workspace int outputPrecision;
 
-        public void setup(RecordBatch incoming) {}
+        public void setup(RecordBatch incoming) {
+            outputPrecision = Integer.MIN_VALUE;
+        }
 
         public void eval() {
 
-            result.scale = left.scale;
+            if (outputPrecision == Integer.MIN_VALUE) {
+                
org.apache.drill.common.util.DecimalScalePrecisionDivideFunction 
resultScalePrec =
+                new 
org.apache.drill.common.util.DecimalScalePrecisionDivideFunction((int) 
left.precision, (int) left.scale, (int) right.precision, (int) right.scale);
+                outputScale = resultScalePrec.getOutputScale();
+                outputPrecision = resultScalePrec.getOutputPrecision();
+            }
+            result.scale = outputScale;
+            result.precision = outputPrecision;
 
             java.math.BigDecimal numerator = new 
java.math.BigDecimal(java.math.BigInteger.valueOf(left.value), left.scale);
             java.math.BigDecimal denominator = new 
java.math.BigDecimal(java.math.BigInteger.valueOf(right.value), right.scale);
@@ -1263,21 +1307,30 @@ public class ${type.name}Functions {
 
         @Param ${type.name}Holder left;
         @Param ${type.name}Holder right;
+        @Workspace int outputScale;
+        @Workspace int outputPrecision;
         @Output ${type.name}Holder result;
 
-        public void setup(RecordBatch incoming) {}
+        public void setup(RecordBatch incoming) {
+            outputPrecision = Integer.MIN_VALUE;
+        }
 
         public void eval() {
-
+            if (outputPrecision == Integer.MIN_VALUE) {
+                
org.apache.drill.common.util.DecimalScalePrecisionDivideFunction 
resultScalePrec =
+                new 
org.apache.drill.common.util.DecimalScalePrecisionDivideFunction((int) 
left.precision, (int) left.scale, (int) right.precision, (int) right.scale);
+                outputScale = resultScalePrec.getOutputScale();
+                outputPrecision = resultScalePrec.getOutputPrecision();
+            }
+            result.precision = outputPrecision;
+            result.scale = outputScale;
             java.math.BigDecimal numerator = new 
java.math.BigDecimal(java.math.BigInteger.valueOf(left.value), left.scale);
             java.math.BigDecimal denominator = new 
java.math.BigDecimal(java.math.BigInteger.valueOf(right.value), right.scale);
 
             java.math.BigDecimal output = numerator.remainder(denominator);
-            output.setScale(left.scale, java.math.BigDecimal.ROUND_DOWN);
+            output.setScale(result.scale, java.math.BigDecimal.ROUND_DOWN);
 
             result.value = output.unscaledValue().${type.storage}Value();
-            result.precision = result.maxPrecision;
-            result.scale = left.scale;
         }
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-drill/blob/1465e11c/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/DrillDecimalDivScaleFuncHolder.java
----------------------------------------------------------------------
diff --git 
a/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/DrillDecimalDivScaleFuncHolder.java
 
b/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/DrillDecimalDivScaleFuncHolder.java
index af239e4..941dc49 100644
--- 
a/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/DrillDecimalDivScaleFuncHolder.java
+++ 
b/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/DrillDecimalDivScaleFuncHolder.java
@@ -24,6 +24,8 @@ import org.apache.drill.common.expression.LogicalExpression;
 import org.apache.drill.common.types.TypeProtos;
 import org.apache.drill.common.types.TypeProtos.MajorType;
 
+import org.apache.drill.common.util.DecimalScalePrecisionDivideFunction;
+import org.apache.drill.common.util.DecimalUtility;
 import org.apache.drill.exec.expr.annotations.FunctionTemplate.FunctionScope;
 import org.apache.drill.exec.expr.annotations.FunctionTemplate.NullHandling;
 
@@ -36,12 +38,15 @@ public class DrillDecimalDivScaleFuncHolder extends 
DrillSimpleFuncHolder{
     super(scope, nullHandling, isBinaryCommutative, isRandom, registeredNames, 
parameters, returnValue, workspaceVars, methods, imports);
   }
 
+  /*
+   * This function scope is used by divide functions for decimal data type.
+   * DecimalScalePrecisionDivideFunction is used to compute the output types'
+   * scale and precision
+   */
   @Override
   public MajorType getReturnType(List<LogicalExpression> args) {
 
     TypeProtos.DataMode mode = returnValue.type.getMode();
-    int scale = 0;
-    int precision = 0;
 
     if (nullHandling == NullHandling.NULL_IF_NULL) {
       // if any one of the input types is nullable, then return nullable 
return type
@@ -53,12 +58,21 @@ public class DrillDecimalDivScaleFuncHolder extends 
DrillSimpleFuncHolder{
       }
     }
 
-    /* Set the scale to be the same as the fist input's scale
-     * Used by divide and modulo functions
+
+    /* Get the result's scale and precision. This is a function scope for 
Divide function, assert we have
+     * only two inputs
      */
-    scale = args.get(0).getMajorType().getScale();
-    precision = args.get(0).getMajorType().getPrecision();
+    assert args.size() == 2;
 
-    return 
(TypeProtos.MajorType.newBuilder().setMinorType(returnValue.type.getMinorType()).setScale(scale).setPrecision(precision).setMode(mode).build());
+    DecimalScalePrecisionDivideFunction outputScalePrec =
+      new 
DecimalScalePrecisionDivideFunction(args.get(0).getMajorType().getPrecision(), 
args.get(0).getMajorType().getScale(),
+                                              
args.get(1).getMajorType().getPrecision(), 
args.get(1).getMajorType().getScale());
+    return 
(TypeProtos.MajorType.newBuilder().setMinorType(DecimalUtility.getDecimalDataType(outputScalePrec.getOutputPrecision()))
+        
.setScale(outputScalePrec.getOutputScale()).setPrecision(outputScalePrec.getOutputPrecision()).setMode(mode).build());
+  }
+  
+  @Override
+  public boolean checkPrecisionRange() {
+    return true;
   }
 }

http://git-wip-us.apache.org/repos/asf/incubator-drill/blob/1465e11c/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/DrillDecimalSumScaleFuncHolder.java
----------------------------------------------------------------------
diff --git 
a/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/DrillDecimalSumScaleFuncHolder.java
 
b/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/DrillDecimalSumScaleFuncHolder.java
index 2e82966..6c04afc 100644
--- 
a/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/DrillDecimalSumScaleFuncHolder.java
+++ 
b/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/DrillDecimalSumScaleFuncHolder.java
@@ -24,6 +24,8 @@ import org.apache.drill.common.expression.LogicalExpression;
 import org.apache.drill.common.types.TypeProtos;
 import org.apache.drill.common.types.TypeProtos.MajorType;
 
+import org.apache.drill.common.util.DecimalScalePrecisionMulFunction;
+import org.apache.drill.common.util.DecimalUtility;
 import org.apache.drill.exec.expr.annotations.FunctionTemplate.FunctionScope;
 import org.apache.drill.exec.expr.annotations.FunctionTemplate.NullHandling;
 
@@ -40,19 +42,32 @@ public class DrillDecimalSumScaleFuncHolder extends 
DrillSimpleFuncHolder{
     public MajorType getReturnType(List<LogicalExpression> args) {
 
         TypeProtos.DataMode mode = returnValue.type.getMode();
-        int scale = 0;
-        int precision = 0;
 
         if (nullHandling == NullHandling.NULL_IF_NULL) {
             // if any one of the input types is nullable, then return nullable 
return type
             for (LogicalExpression e : args) {
                 if (e.getMajorType().getMode() == 
TypeProtos.DataMode.OPTIONAL) {
                     mode = TypeProtos.DataMode.OPTIONAL;
+                    break;
                 }
-                scale += e.getMajorType().getScale();
-                precision = Math.max(precision, 
e.getMajorType().getPrecision());
             }
         }
-        return 
(TypeProtos.MajorType.newBuilder().setMinorType(returnValue.type.getMinorType()).setScale(scale).setPrecision(precision).setMode(mode).build());
+
+    /* Get the result's scale and precision. This is a function scope for 
Multiply function, assert we have
+     * only two inputs
+     */
+    assert args.size() == 2;
+
+    DecimalScalePrecisionMulFunction outputScalePrec =
+      new 
DecimalScalePrecisionMulFunction(args.get(0).getMajorType().getPrecision(), 
args.get(0).getMajorType().getScale(),
+                                              
args.get(1).getMajorType().getPrecision(), 
args.get(1).getMajorType().getScale());
+        return 
(TypeProtos.MajorType.newBuilder().setMinorType(DecimalUtility.getDecimalDataType(outputScalePrec.getOutputPrecision()))
+            
.setScale(outputScalePrec.getOutputScale()).setPrecision(outputScalePrec.getOutputPrecision()).setMode(mode).build());
+    }
+
+    @Override
+    public boolean checkPrecisionRange() {
+        return true;
     }
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-drill/blob/1465e11c/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/DrillFuncHolder.java
----------------------------------------------------------------------
diff --git 
a/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/DrillFuncHolder.java
 
b/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/DrillFuncHolder.java
index fd687af..fc8dc00 100644
--- 
a/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/DrillFuncHolder.java
+++ 
b/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/DrillFuncHolder.java
@@ -294,7 +294,7 @@ public abstract class DrillFuncHolder {
 
   }
 
-  public boolean matchInputOutputType() {
+  public boolean checkPrecisionRange() {
     return false;
   }
 

http://git-wip-us.apache.org/repos/asf/incubator-drill/blob/1465e11c/exec/java-exec/src/main/java/org/apache/drill/exec/resolver/TypeCastRules.java
----------------------------------------------------------------------
diff --git 
a/exec/java-exec/src/main/java/org/apache/drill/exec/resolver/TypeCastRules.java
 
b/exec/java-exec/src/main/java/org/apache/drill/exec/resolver/TypeCastRules.java
index 854342c..bf202c8 100644
--- 
a/exec/java-exec/src/main/java/org/apache/drill/exec/resolver/TypeCastRules.java
+++ 
b/exec/java-exec/src/main/java/org/apache/drill/exec/resolver/TypeCastRules.java
@@ -28,6 +28,7 @@ import org.apache.drill.common.types.Types;
 import org.apache.drill.common.types.TypeProtos.DataMode;
 import org.apache.drill.common.types.TypeProtos.MajorType;
 import org.apache.drill.common.types.TypeProtos.MinorType;
+import org.apache.drill.common.util.DecimalUtility;
 import org.apache.drill.exec.expr.annotations.FunctionTemplate.NullHandling;
 import org.apache.drill.exec.expr.fn.DrillFuncHolder;
 
@@ -794,13 +795,13 @@ public class TypeCastRules {
     // number of arguments that could implicitly casts using precedence map or 
didn't require casting at all
     int nCasts = 0;
 
-    // Check if the function holder requires the input type and output type to 
match
-    if (holder.matchInputOutputType() == true) {
-      MinorType outputType = holder.getReturnType(call.args).getMinorType();
-      for (int i = 0; i < holder.getParamCount(); i++) {
-        if (call.args.get(i).getMajorType().getMinorType() != outputType) {
-          return -1;
-        }
+    /*
+     * If we are determining function holder for decimal data type, we need to 
make sure the output type of
+     * the function can fit the precision that we need based on the input 
types.
+     */
+    if (holder.checkPrecisionRange() == true) {
+      if 
(DecimalUtility.getMaxPrecision(holder.getReturnType().getMinorType()) < 
holder.getReturnType(call.args).getPrecision()) {
+        return -1;
       }
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-drill/blob/1465e11c/exec/java-exec/src/test/java/org/apache/drill/exec/physical/impl/TestDecimal.java
----------------------------------------------------------------------
diff --git 
a/exec/java-exec/src/test/java/org/apache/drill/exec/physical/impl/TestDecimal.java
 
b/exec/java-exec/src/test/java/org/apache/drill/exec/physical/impl/TestDecimal.java
index 2df4d18..f485378 100644
--- 
a/exec/java-exec/src/test/java/org/apache/drill/exec/physical/impl/TestDecimal.java
+++ 
b/exec/java-exec/src/test/java/org/apache/drill/exec/physical/impl/TestDecimal.java
@@ -34,6 +34,7 @@ import org.apache.drill.exec.rpc.user.QueryResultBatch;
 import org.apache.drill.exec.server.Drillbit;
 import org.apache.drill.exec.server.RemoteServiceSet;
 import org.apache.drill.exec.vector.ValueVector;
+import org.junit.Ignore;
 import org.junit.Test;
 
 import com.google.common.base.Charsets;
@@ -208,22 +209,18 @@ public class TestDecimal extends PopUnitTestBase{
 
             String addOutput[] = {"-99999998877.700000000", "11.423456789", 
"123456789.100000000", "-0.119998000", "100000000112.423456789" , 
"-99999999879.907000000", "123456789123456801.300000000"};
             String subtractOutput[] = {"-100000001124.300000000", 
"10.823456789", "-123456788.900000000", "-0.120002000", 
"99999999889.823456789", "-100000000122.093000000", 
"123456789123456776.700000000"};
-            String multiplyOutput[] = {"-112330000001123.300000000000000000", 
"3.337037036700000000" , "12345678.900000000000000000", "-0.000000240000000000" 
, "11130000000125.040740615700000000" , "-12109300000121.093000000000000000", 
"1518518506218518504.700000000000000000" };
 
             Iterator<VectorWrapper<?>> itr = batchLoader.iterator();
 
             ValueVector.Accessor addAccessor = 
itr.next().getValueVector().getAccessor();
             ValueVector.Accessor subAccessor = 
itr.next().getValueVector().getAccessor();
-            ValueVector.Accessor mulAccessor = 
itr.next().getValueVector().getAccessor();
 
             for (int i = 0; i < addAccessor.getValueCount(); i++) {
                 assertEquals(addAccessor.getObject(i).toString(), 
addOutput[i]);
                 assertEquals(subAccessor.getObject(i).toString(), 
subtractOutput[i]);
-                assertEquals(mulAccessor.getObject(i).toString(), 
multiplyOutput[i]);
             }
             assertEquals(7, addAccessor.getValueCount());
             assertEquals(7, subAccessor.getValueCount());
-            assertEquals(7, mulAccessor.getValueCount());
 
             batchLoader.clear();
             for (QueryResultBatch result : results) {

http://git-wip-us.apache.org/repos/asf/incubator-drill/blob/1465e11c/exec/java-exec/src/test/resources/decimal/test_decimal_complex.json
----------------------------------------------------------------------
diff --git 
a/exec/java-exec/src/test/resources/decimal/test_decimal_complex.json 
b/exec/java-exec/src/test/resources/decimal/test_decimal_complex.json
index b2f1929..3f98174 100644
--- a/exec/java-exec/src/test/resources/decimal/test_decimal_complex.json
+++ b/exec/java-exec/src/test/resources/decimal/test_decimal_complex.json
@@ -22,7 +22,7 @@
           "ref" : "DE",
           "expr" : " (cast(B as decimal38sparse(38, 9))) "
         },
-        {"ref" : "DE1", "expr": " cast(A as decimal38sparse(38, 9))" }
+        {"ref" : "DE1", "expr": " cast(A as decimal18(15, 6))" }
         ],
 
         "child" : 1
@@ -34,8 +34,7 @@
           "ref" : "DEC38ADD",
           "expr" : " (DE + DE1)  "
         },
-        {"ref" : "DEC38SUB" , "expr" : " (DE - DE1) " },
-        {"ref" : "DEC38MUL" , "expr" : " (DE * DE1) " }
+        {"ref" : "DEC38SUB" , "expr" : " (DE - DE1) " }
         ],
 
         "child" : 2
@@ -47,8 +46,7 @@
           "ref" : "DEC38ADD",
           "expr" : " cast(DEC38ADD as varchar(100))  "
         },
-        {"ref" : "DEC38SUB" , "expr" : " cast(DEC38SUB as varchar(100)) " },
-        {"ref" : "DEC38MUL" , "expr" : " cast(DEC38MUL as varchar(100)) " }
+        {"ref" : "DEC38SUB" , "expr" : " cast(DEC38SUB as varchar(100)) " }
         ],
 
         "child" : 3

http://git-wip-us.apache.org/repos/asf/incubator-drill/blob/1465e11c/exec/jdbc/src/test/java/org/apache/drill/jdbc/test/TestFunctionsQuery.java
----------------------------------------------------------------------
diff --git 
a/exec/jdbc/src/test/java/org/apache/drill/jdbc/test/TestFunctionsQuery.java 
b/exec/jdbc/src/test/java/org/apache/drill/jdbc/test/TestFunctionsQuery.java
index 60b8f82..c2d90fe 100644
--- a/exec/jdbc/src/test/java/org/apache/drill/jdbc/test/TestFunctionsQuery.java
+++ b/exec/jdbc/src/test/java/org/apache/drill/jdbc/test/TestFunctionsQuery.java
@@ -523,4 +523,19 @@ public class TestFunctionsQuery {
         .returns(
             "DECIMAL_DOUBLE_CAST=1.0001\n");
   }
+
+  @Test
+  public void testCastDecimalDivide() throws Exception {
+    String query = "select  (cast('9' as decimal(9, 1)) / cast('2' as 
decimal(4, 1))) as DEC9_DIV, " +
+        "cast('123456789.123456789' as decimal(18, 9)) * 
cast('123456789.123456789' as decimal(18, 9)) as DEC18_MUL " +
+        "from cp.`employee.json` where employee_id = 1";
+
+    JdbcAssert.withNoDefaultSchema()
+        .sql(query)
+        .returns(
+            "DEC9_DIV=4.5000000; " +
+            "DEC18_MUL=15241578780673678.515622620750190521\n");
+  }
+
+
 }

Reply via email to