drill-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From par...@apache.org
Subject [2/7] drill git commit: DRILL-5419: Calculate return string length for literals & some string functions
Date Sat, 13 May 2017 17:39:00 GMT
http://git-wip-us.apache.org/repos/asf/drill/blob/6741e68a/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/FunctionUtils.java
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/FunctionUtils.java b/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/FunctionUtils.java
new file mode 100644
index 0000000..f565b67
--- /dev/null
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/FunctionUtils.java
@@ -0,0 +1,48 @@
+/*
+ * 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.exec.expr.fn;
+
+import org.apache.drill.common.expression.LogicalExpression;
+import org.apache.drill.common.types.TypeProtos;
+import org.apache.drill.exec.expr.annotations.FunctionTemplate;
+
+import java.util.List;
+
+public class FunctionUtils {
+
+  /**
+   * Calculates return type data mode based on give logical expressions.
+   * If null handling strategy is internal, returns return value data mode.
+   * If null handling strategy is null if null and at least one of the input types are nullable,
+   * return nullable data mode.
+   *
+   * @param logicalExpressions logical expressions
+   * @param attributes function attributes
+   * @return data mode
+   */
+  public static TypeProtos.DataMode getReturnTypeDataMode(final List<LogicalExpression> logicalExpressions, FunctionAttributes attributes) {
+    if (attributes.getNullHandling() == FunctionTemplate.NullHandling.NULL_IF_NULL) {
+      for (final LogicalExpression logicalExpression : logicalExpressions) {
+        if (logicalExpression.getMajorType().getMode() == TypeProtos.DataMode.OPTIONAL) {
+          return TypeProtos.DataMode.OPTIONAL;
+        }
+      }
+    }
+    return attributes.getReturnValue().getType().getMode();
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/drill/blob/6741e68a/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/ValueReference.java
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/ValueReference.java b/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/ValueReference.java
new file mode 100644
index 0000000..9fc2151
--- /dev/null
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/ValueReference.java
@@ -0,0 +1,81 @@
+/*
+ * 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.exec.expr.fn;
+
+import com.google.common.base.Preconditions;
+import org.apache.drill.common.types.TypeProtos.MajorType;
+import org.apache.drill.common.types.TypeProtos.MinorType;
+import org.apache.drill.common.types.Types;
+
+public class ValueReference {
+  private final MajorType type;
+  private final String name;
+  private boolean isConstant = false;
+  private boolean isFieldReader = false;
+  private boolean isComplexWriter = false;
+
+  public ValueReference(MajorType type, String name) {
+    Preconditions.checkNotNull(type);
+    Preconditions.checkNotNull(name);
+    this.type = type;
+    this.name = name;
+  }
+
+  public void setConstant(boolean isConstant) {
+    this.isConstant = isConstant;
+  }
+
+  public MajorType getType() {
+    return type;
+  }
+
+  public String getName() {
+    return name;
+  }
+
+  public boolean isConstant() {
+    return isConstant;
+  }
+
+  public boolean isFieldReader() {
+    return isFieldReader;
+  }
+
+  public boolean isComplexWriter() {
+    return isComplexWriter;
+  }
+
+  @Override
+  public String toString() {
+    return "ValueReference [type=" + Types.toString(type) + ", name=" + name + "]";
+  }
+
+  public static ValueReference createFieldReaderRef(String name) {
+    MajorType type = Types.required(MinorType.LATE);
+    ValueReference ref = new ValueReference(type, name);
+    ref.isFieldReader = true;
+    return ref;
+  }
+
+  public static ValueReference createComplexWriterRef(String name) {
+    MajorType type = Types.required(MinorType.LATE);
+    ValueReference ref = new ValueReference(type, name);
+    ref.isComplexWriter = true;
+    return ref;
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/drill/blob/6741e68a/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/WorkspaceReference.java
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/WorkspaceReference.java b/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/WorkspaceReference.java
new file mode 100644
index 0000000..e2ba449
--- /dev/null
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/WorkspaceReference.java
@@ -0,0 +1,64 @@
+/*
+ * 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.exec.expr.fn;
+
+import com.google.common.base.Preconditions;
+import org.apache.drill.common.types.TypeProtos.MajorType;
+import org.apache.drill.common.types.Types;
+
+public class WorkspaceReference {
+
+  private final Class<?> type;
+  private final String name;
+  private final boolean inject;
+  private MajorType majorType;
+
+  public WorkspaceReference(Class<?> type, String name, boolean inject) {
+    Preconditions.checkNotNull(type);
+    Preconditions.checkNotNull(name);
+    this.type = type;
+    this.name = name;
+    this.inject = inject;
+  }
+
+  void setMajorType(MajorType majorType) {
+    this.majorType = majorType;
+  }
+
+  public String getName() {
+    return name;
+  }
+
+  public boolean isInject() {
+    return inject;
+  }
+
+  public Class<?> getType() {
+    return type;
+  }
+
+  public MajorType getMajorType() {
+    return majorType;
+  }
+
+  @Override
+  public String toString() {
+    return "WorkspaceReference [type= " + type +", major type=" + Types.toString(majorType) + ", name=" + name + "]";
+  }
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/drill/blob/6741e68a/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/impl/BitFunctions.java
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/impl/BitFunctions.java b/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/impl/BitFunctions.java
index e19f284..4930aef 100644
--- a/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/impl/BitFunctions.java
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/impl/BitFunctions.java
@@ -35,7 +35,7 @@ import org.apache.drill.exec.expr.holders.IntHolder;
 public class BitFunctions {
 
   @FunctionTemplate(names = {"booleanOr", "or", "||", "orNoShortCircuit"},
-                    scope = FunctionScope.SC_BOOLEAN_OPERATOR,
+                    scope = FunctionScope.SIMPLE,
                     nulls = NullHandling.NULL_IF_NULL)
   public static class BitOr implements DrillSimpleFunc {
 
@@ -51,7 +51,7 @@ public class BitFunctions {
   }
 
   @FunctionTemplate(names = {"booleanAnd", "and", "&&"},
-                    scope = FunctionScope.SC_BOOLEAN_OPERATOR,
+                    scope = FunctionScope.SIMPLE,
                     nulls = NullHandling.NULL_IF_NULL)
   public static class BitAnd implements DrillSimpleFunc {
 

http://git-wip-us.apache.org/repos/asf/drill/blob/6741e68a/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/impl/SimpleCastFunctions.java
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/impl/SimpleCastFunctions.java b/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/impl/SimpleCastFunctions.java
index 346190a..807fbb9 100644
--- a/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/impl/SimpleCastFunctions.java
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/impl/SimpleCastFunctions.java
@@ -1,4 +1,4 @@
-/**
+/*
  * 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
@@ -51,7 +51,10 @@ public class SimpleCastFunctions {
     }
   }
 
-  @FunctionTemplate(name = "castVARCHAR", scope = FunctionTemplate.FunctionScope.SIMPLE, nulls=NullHandling.NULL_IF_NULL)
+  @FunctionTemplate(name = "castVARCHAR",
+      scope = FunctionTemplate.FunctionScope.SIMPLE,
+      returnType = FunctionTemplate.ReturnType.STRING_CAST,
+      nulls = NullHandling.NULL_IF_NULL)
   public static class CastBooleanVarChar implements DrillSimpleFunc {
 
     @Param BitHolder in;

http://git-wip-us.apache.org/repos/asf/drill/blob/6741e68a/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/impl/StringFunctions.java
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/impl/StringFunctions.java b/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/impl/StringFunctions.java
index d90581e..a6fa255 100644
--- a/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/impl/StringFunctions.java
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/impl/StringFunctions.java
@@ -29,6 +29,7 @@ import org.apache.drill.exec.expr.DrillSimpleFunc;
 import org.apache.drill.exec.expr.annotations.FunctionTemplate;
 import org.apache.drill.exec.expr.annotations.FunctionTemplate.FunctionScope;
 import org.apache.drill.exec.expr.annotations.FunctionTemplate.NullHandling;
+import org.apache.drill.exec.expr.annotations.FunctionTemplate.ReturnType;
 import org.apache.drill.exec.expr.annotations.Output;
 import org.apache.drill.exec.expr.annotations.Param;
 import org.apache.drill.exec.expr.annotations.Workspace;
@@ -483,7 +484,10 @@ public class StringFunctions{
   /*
    * Convert string to lower case.
    */
-  @FunctionTemplate(name = "lower", scope = FunctionScope.SIMPLE, nulls = NullHandling.NULL_IF_NULL)
+  @FunctionTemplate(name = "lower",
+      scope = FunctionScope.SIMPLE,
+      returnType = ReturnType.SAME_IN_OUT_LENGTH,
+      nulls = NullHandling.NULL_IF_NULL)
   public static class LowerCase implements DrillSimpleFunc {
     @Param VarCharHolder input;
     @Output VarCharHolder out;
@@ -515,7 +519,10 @@ public class StringFunctions{
   /*
    * Convert string to upper case.
    */
-  @FunctionTemplate(name = "upper", scope = FunctionScope.SIMPLE, nulls = NullHandling.NULL_IF_NULL)
+  @FunctionTemplate(name = "upper",
+      scope = FunctionScope.SIMPLE,
+      returnType = ReturnType.SAME_IN_OUT_LENGTH,
+      nulls = NullHandling.NULL_IF_NULL)
   public static class UpperCase implements DrillSimpleFunc {
 
     @Param VarCharHolder input;
@@ -775,7 +782,10 @@ public class StringFunctions{
   }
 
 
-  @FunctionTemplate(name = "initcap", scope = FunctionScope.SIMPLE, nulls = NullHandling.NULL_IF_NULL)
+  @FunctionTemplate(name = "initcap",
+      scope = FunctionScope.SIMPLE,
+      returnType = ReturnType.SAME_IN_OUT_LENGTH,
+      nulls = NullHandling.NULL_IF_NULL)
   public static class InitCap implements DrillSimpleFunc {
     @Param VarCharHolder input;
     @Output VarCharHolder out;
@@ -860,7 +870,10 @@ public class StringFunctions{
    * Fill up the string to length 'length' by prepending the characters 'fill' in the beginning of 'text'.
    * If the string is already longer than length, then it is truncated (on the right).
    */
-  @FunctionTemplate(name = "lpad", scope = FunctionScope.SIMPLE, nulls = NullHandling.NULL_IF_NULL)
+  @FunctionTemplate(name = "lpad",
+      scope = FunctionScope.SIMPLE,
+      returnType = ReturnType.PAD,
+      nulls = NullHandling.NULL_IF_NULL)
   public static class Lpad implements DrillSimpleFunc {
     @Param  VarCharHolder text;
     @Param  BigIntHolder length;
@@ -935,7 +948,10 @@ public class StringFunctions{
    * Fill up the string to length 'length' by prepending the character ' ' in the beginning of 'text'.
    * If the string is already longer than length, then it is truncated (on the right).
    */
-  @FunctionTemplate(name = "lpad", scope = FunctionScope.SIMPLE, nulls = NullHandling.NULL_IF_NULL)
+  @FunctionTemplate(name = "lpad",
+      scope = FunctionScope.SIMPLE,
+      returnType = ReturnType.PAD,
+      nulls = NullHandling.NULL_IF_NULL)
   public static class LpadTwoArg implements DrillSimpleFunc {
     @Param  VarCharHolder text;
     @Param  BigIntHolder length;
@@ -994,7 +1010,10 @@ public class StringFunctions{
    * Fill up the string to length "length" by appending the characters 'fill' at the end of 'text'
    * If the string is already longer than length then it is truncated.
    */
-  @FunctionTemplate(name = "rpad", scope = FunctionScope.SIMPLE, nulls = NullHandling.NULL_IF_NULL)
+  @FunctionTemplate(name = "rpad",
+      scope = FunctionScope.SIMPLE,
+      returnType = ReturnType.PAD,
+      nulls = NullHandling.NULL_IF_NULL)
   public static class Rpad implements DrillSimpleFunc {
     @Param  VarCharHolder text;
     @Param  BigIntHolder length;
@@ -1072,7 +1091,10 @@ public class StringFunctions{
    * Fill up the string to length "length" by appending the characters ' ' at the end of 'text'
    * If the string is already longer than length then it is truncated.
    */
-  @FunctionTemplate(name = "rpad", scope = FunctionScope.SIMPLE, nulls = NullHandling.NULL_IF_NULL)
+  @FunctionTemplate(name = "rpad",
+      scope = FunctionScope.SIMPLE,
+      returnType = ReturnType.PAD,
+      nulls = NullHandling.NULL_IF_NULL)
   public static class RpadTwoArg implements DrillSimpleFunc {
     @Param  VarCharHolder text;
     @Param  BigIntHolder length;
@@ -1389,7 +1411,10 @@ public class StringFunctions{
 
   }
 
-  @FunctionTemplate(name = "concatOperator", scope = FunctionScope.SIMPLE, nulls = NullHandling.NULL_IF_NULL)
+  @FunctionTemplate(name = "concatOperator",
+      scope = FunctionScope.SIMPLE,
+      returnType = ReturnType.CONCAT,
+      nulls = NullHandling.NULL_IF_NULL)
   public static class ConcatOperator implements DrillSimpleFunc {
     @Param  VarCharHolder left;
     @Param  VarCharHolder right;
@@ -1418,7 +1443,10 @@ public class StringFunctions{
 
   //Concatenate the text representations of the arguments. NULL arguments are ignored.
   //TODO: NullHanding.INTERNAL for DrillSimpleFunc requires change in code generation.
-  @FunctionTemplate(name = "concat", scope = FunctionScope.SIMPLE, nulls = NullHandling.INTERNAL)
+  @FunctionTemplate(name = "concat",
+      scope = FunctionScope.SIMPLE,
+      returnType = ReturnType.CONCAT,
+      nulls = NullHandling.INTERNAL)
   public static class Concat implements DrillSimpleFunc {
     @Param  VarCharHolder left;
     @Param  VarCharHolder right;
@@ -1445,7 +1473,10 @@ public class StringFunctions{
     }
   }
 
-  @FunctionTemplate(name = "concat", scope = FunctionScope.SIMPLE, nulls = NullHandling.INTERNAL)
+  @FunctionTemplate(name = "concat",
+      scope = FunctionScope.SIMPLE,
+      returnType = ReturnType.CONCAT,
+      nulls = NullHandling.INTERNAL)
   public static class ConcatRightNullInput implements DrillSimpleFunc {
     @Param  VarCharHolder left;
     @Param  NullableVarCharHolder right;
@@ -1474,7 +1505,10 @@ public class StringFunctions{
     }
   }
 
-  @FunctionTemplate(name = "concat", scope = FunctionScope.SIMPLE, nulls = NullHandling.INTERNAL)
+  @FunctionTemplate(name = "concat",
+      scope = FunctionScope.SIMPLE,
+      returnType = ReturnType.CONCAT,
+      nulls = NullHandling.INTERNAL)
   public static class ConcatLeftNullInput implements DrillSimpleFunc {
     @Param  NullableVarCharHolder left;
     @Param  VarCharHolder right;
@@ -1503,7 +1537,10 @@ public class StringFunctions{
     }
   }
 
-  @FunctionTemplate(name = "concat", scope = FunctionScope.SIMPLE, nulls = NullHandling.INTERNAL)
+  @FunctionTemplate(name = "concat",
+      scope = FunctionScope.SIMPLE,
+      returnType = ReturnType.CONCAT,
+      nulls = NullHandling.INTERNAL)
   public static class ConcatBothNullInput implements DrillSimpleFunc {
     @Param  NullableVarCharHolder left;
     @Param  NullableVarCharHolder right;
@@ -1682,7 +1719,10 @@ public class StringFunctions{
   /**
   * Returns the reverse string for given input.
   */
-  @FunctionTemplate(name = "reverse", scope = FunctionScope.SIMPLE, nulls = NullHandling.NULL_IF_NULL)
+  @FunctionTemplate(name = "reverse",
+      scope = FunctionScope.SIMPLE,
+      returnType = ReturnType.SAME_IN_OUT_LENGTH,
+      nulls = NullHandling.NULL_IF_NULL)
   public static class ReverseString implements DrillSimpleFunc {
     @Param  VarCharHolder in;
     @Output VarCharHolder out;

http://git-wip-us.apache.org/repos/asf/drill/blob/6741e68a/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/output/ConcatReturnTypeInference.java
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/output/ConcatReturnTypeInference.java b/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/output/ConcatReturnTypeInference.java
new file mode 100644
index 0000000..eea02e7
--- /dev/null
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/output/ConcatReturnTypeInference.java
@@ -0,0 +1,63 @@
+/*
+ * 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.exec.expr.fn.output;
+
+import org.apache.drill.common.expression.LogicalExpression;
+import org.apache.drill.common.types.TypeProtos;
+import org.apache.drill.common.types.Types;
+import org.apache.drill.exec.expr.fn.FunctionAttributes;
+import org.apache.drill.exec.expr.fn.FunctionUtils;
+
+import java.util.List;
+
+/**
+ * Return type calculation implementation for functions with return type set as
+ * {@link org.apache.drill.exec.expr.annotations.FunctionTemplate.ReturnType#CONCAT}.
+ */
+public class ConcatReturnTypeInference implements ReturnTypeInference {
+
+  public static final ConcatReturnTypeInference INSTANCE = new ConcatReturnTypeInference();
+
+  /**
+   * Defines function return type and sets precision if it can be calculated.
+   * Return type precision is sum of input types precisions.
+   * If at least one input type does not have precision, return type will be without precision.
+   * If calculated precision is greater than {@link Types#MAX_VARCHAR_LENGTH},
+   * it is replaced with {@link Types#MAX_VARCHAR_LENGTH}.
+   *
+   * @param logicalExpressions logical expressions
+   * @param attributes function attributes
+   * @return return type
+   */
+  @Override
+  public TypeProtos.MajorType getType(List<LogicalExpression> logicalExpressions, FunctionAttributes attributes) {
+    TypeProtos.MajorType.Builder builder = TypeProtos.MajorType.newBuilder()
+        .setMinorType(attributes.getReturnValue().getType().getMinorType())
+        .setMode(FunctionUtils.getReturnTypeDataMode(logicalExpressions, attributes));
+
+    int totalPrecision = 0;
+    for (LogicalExpression expression : logicalExpressions) {
+      if (expression.getMajorType().hasPrecision()) {
+        totalPrecision += expression.getMajorType().getPrecision();
+      } else {
+        // if at least one expression has unknown precision, return type without precision
+        return builder.build();
+      }
+    }
+    return builder.setPrecision(totalPrecision > Types.MAX_VARCHAR_LENGTH ? Types.MAX_VARCHAR_LENGTH : totalPrecision).build();
+  }
+}

http://git-wip-us.apache.org/repos/asf/drill/blob/6741e68a/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/output/DecimalReturnTypeInference.java
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/output/DecimalReturnTypeInference.java b/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/output/DecimalReturnTypeInference.java
new file mode 100644
index 0000000..ba43b39
--- /dev/null
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/output/DecimalReturnTypeInference.java
@@ -0,0 +1,369 @@
+/*
+ * 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.exec.expr.fn.output;
+
+import org.apache.drill.common.exceptions.DrillRuntimeException;
+import org.apache.drill.common.expression.LogicalExpression;
+import org.apache.drill.common.expression.ValueExpressions;
+import org.apache.drill.common.types.TypeProtos;
+import org.apache.drill.common.util.DecimalScalePrecisionAddFunction;
+import org.apache.drill.common.util.DecimalScalePrecisionDivideFunction;
+import org.apache.drill.common.util.DecimalScalePrecisionModFunction;
+import org.apache.drill.common.util.DecimalScalePrecisionMulFunction;
+import org.apache.drill.exec.expr.annotations.FunctionTemplate;
+import org.apache.drill.exec.expr.fn.FunctionAttributes;
+import org.apache.drill.exec.expr.fn.FunctionUtils;
+import org.apache.drill.exec.util.DecimalUtility;
+
+import java.util.List;
+
+public class DecimalReturnTypeInference {
+
+  /**
+   * Return type calculation implementation for functions with return type set as
+   * {@link org.apache.drill.exec.expr.annotations.FunctionTemplate.ReturnType#DECIMAL_ADD_SCALE}.
+   */
+  public static class DecimalAddReturnTypeInference implements ReturnTypeInference {
+
+    public static final DecimalAddReturnTypeInference INSTANCE = new DecimalAddReturnTypeInference();
+
+    /**
+     * This return type is used by add and subtract functions for decimal data type.
+     * DecimalScalePrecisionAddFunction is used to compute the output types' scale and precision.
+     *
+     * @param logicalExpressions logical expressions
+     * @param attributes function attributes
+     * @return return type
+     */
+    @Override
+    public TypeProtos.MajorType getType(List<LogicalExpression> logicalExpressions, FunctionAttributes attributes) {
+      TypeProtos.DataMode mode = FunctionUtils.getReturnTypeDataMode(logicalExpressions, attributes);
+
+      assert logicalExpressions.size() == 2;
+
+      DecimalScalePrecisionAddFunction outputScalePrec =
+          new DecimalScalePrecisionAddFunction(logicalExpressions.get(0).getMajorType().getPrecision(),
+              logicalExpressions.get(0).getMajorType().getScale(),
+              logicalExpressions.get(1).getMajorType().getPrecision(),
+              logicalExpressions.get(1).getMajorType().getScale());
+      return TypeProtos.MajorType.newBuilder()
+          .setMinorType(DecimalUtility.getDecimalDataType(outputScalePrec.getOutputPrecision()))
+          .setScale(outputScalePrec.getOutputScale())
+          .setPrecision(outputScalePrec.getOutputPrecision())
+          .setMode(mode)
+          .build();
+    }
+  }
+
+  /**
+   * Return type calculation implementation for functions with return type set as
+   * {@link org.apache.drill.exec.expr.annotations.FunctionTemplate.ReturnType#DECIMAL_AGGREGATE}.
+   */
+  public static class DecimalAggReturnTypeInference implements ReturnTypeInference {
+
+    public static final DecimalAggReturnTypeInference INSTANCE = new DecimalAggReturnTypeInference();
+
+    @Override
+    public TypeProtos.MajorType getType(List<LogicalExpression> logicalExpressions, FunctionAttributes attributes) {
+      int scale = 0;
+      int precision = 0;
+
+      // Get the max scale and precision from the inputs
+      for (LogicalExpression e : logicalExpressions) {
+        scale = Math.max(scale, e.getMajorType().getScale());
+        precision = Math.max(precision, e.getMajorType().getPrecision());
+      }
+
+      return TypeProtos.MajorType.newBuilder()
+          .setMinorType(attributes.getReturnValue().getType().getMinorType())
+          .setScale(scale)
+          .setPrecision(precision)
+          .setMode(TypeProtos.DataMode.REQUIRED)
+          .build();
+    }
+  }
+
+  /**
+   * Return type calculation implementation for functions with return type set as
+   * {@link org.apache.drill.exec.expr.annotations.FunctionTemplate.ReturnType#DECIMAL_CAST}.
+   */
+  public static class DecimalCastReturnTypeInference implements ReturnTypeInference {
+
+    public static final DecimalCastReturnTypeInference INSTANCE = new DecimalCastReturnTypeInference();
+
+    @Override
+    public TypeProtos.MajorType getType(List<LogicalExpression> logicalExpressions, FunctionAttributes attributes) {
+      TypeProtos.DataMode mode = FunctionUtils.getReturnTypeDataMode(logicalExpressions, attributes);
+
+      if (logicalExpressions.size() != 3) {
+        StringBuilder err = new StringBuilder();
+        for (int i = 0; i < logicalExpressions.size(); i++) {
+          err.append("arg").append(i).append(": ").append(logicalExpressions.get(i).getMajorType().getMinorType());
+        }
+        throw new DrillRuntimeException("Decimal cast function invoked with incorrect arguments" + err);
+      }
+
+      int scale = (int) ((ValueExpressions.LongExpression)(logicalExpressions.get(logicalExpressions.size() - 1))).getLong();
+      int precision = (int) ((ValueExpressions.LongExpression)(logicalExpressions.get(logicalExpressions.size() - 2))).getLong();
+      return TypeProtos.MajorType.newBuilder()
+          .setMinorType(attributes.getReturnValue().getType().getMinorType())
+          .setScale(scale)
+          .setPrecision(precision)
+          .setMode(mode)
+          .build();
+    }
+  }
+
+  /**
+   * Return type calculation implementation for functions with return type set as
+   * {@link org.apache.drill.exec.expr.annotations.FunctionTemplate.ReturnType#DECIMAL_DIV_SCALE}.
+   */
+  public static class DecimalDivScaleReturnTypeInference implements ReturnTypeInference {
+
+    public static final DecimalDivScaleReturnTypeInference INSTANCE = new DecimalDivScaleReturnTypeInference();
+
+    /**
+     * Return type is used by divide functions for decimal data type.
+     * DecimalScalePrecisionDivideFunction is used to compute the output types' scale and precision.
+     *
+     * @param logicalExpressions logical expressions
+     * @param attributes function attributes
+     * @return return type
+     */
+    @Override
+    public TypeProtos.MajorType getType(List<LogicalExpression> logicalExpressions, FunctionAttributes attributes) {
+      TypeProtos.DataMode mode = FunctionUtils.getReturnTypeDataMode(logicalExpressions, attributes);
+
+      assert logicalExpressions.size() == 2;
+
+      DecimalScalePrecisionDivideFunction outputScalePrec =
+          new DecimalScalePrecisionDivideFunction(logicalExpressions.get(0).getMajorType().getPrecision(),
+              logicalExpressions.get(0).getMajorType().getScale(),
+              logicalExpressions.get(1).getMajorType().getPrecision(),
+              logicalExpressions.get(1).getMajorType().getScale());
+      return TypeProtos.MajorType.newBuilder()
+          .setMinorType(DecimalUtility.getDecimalDataType(outputScalePrec.getOutputPrecision()))
+          .setScale(outputScalePrec.getOutputScale())
+          .setPrecision(outputScalePrec.getOutputPrecision())
+          .setMode(mode)
+          .build();
+    }
+  }
+
+  /**
+   * Return type calculation implementation for functions with return type set as
+   * {@link org.apache.drill.exec.expr.annotations.FunctionTemplate.ReturnType#DECIMAL_MAX_SCALE}.
+   */
+  public static class DecimalMaxScaleReturnTypeInference implements ReturnTypeInference {
+
+    public static final DecimalMaxScaleReturnTypeInference INSTANCE = new DecimalMaxScaleReturnTypeInference();
+
+    @Override
+    public TypeProtos.MajorType getType(List<LogicalExpression> logicalExpressions, FunctionAttributes attributes) {
+
+      TypeProtos.DataMode mode = FunctionUtils.getReturnTypeDataMode(logicalExpressions, attributes);
+      int scale = 0;
+      int precision = 0;
+
+      for (LogicalExpression e : logicalExpressions) {
+        scale = Math.max(scale, e.getMajorType().getScale());
+        precision = Math.max(precision, e.getMajorType().getPrecision());
+      }
+
+      return TypeProtos.MajorType.newBuilder()
+          .setMinorType(attributes.getReturnValue().getType().getMinorType())
+          .setScale(scale)
+          .setPrecision(precision)
+          .setMode(mode)
+          .build();
+    }
+  }
+
+  /**
+   * Return type calculation implementation for functions with return type set as
+   * {@link org.apache.drill.exec.expr.annotations.FunctionTemplate.ReturnType#DECIMAL_MOD_SCALE}.
+   */
+  public static class DecimalModScaleReturnTypeInference implements ReturnTypeInference {
+
+    public static final DecimalModScaleReturnTypeInference INSTANCE = new DecimalModScaleReturnTypeInference();
+
+    /**
+     * Return type is used by divide functions for decimal data type.
+     * DecimalScalePrecisionDivideFunction is used to compute the output types' scale and precision.
+     *
+     * @param logicalExpressions logical expressions
+     * @param attributes function attributes
+     * @return return type
+     */
+    @Override
+    public TypeProtos.MajorType getType(List<LogicalExpression> logicalExpressions, FunctionAttributes attributes) {
+      TypeProtos.DataMode mode = FunctionUtils.getReturnTypeDataMode(logicalExpressions, attributes);
+
+      assert logicalExpressions.size() == 2;
+
+      DecimalScalePrecisionModFunction outputScalePrec =
+          new DecimalScalePrecisionModFunction(logicalExpressions.get(0).getMajorType().getPrecision(),
+              logicalExpressions.get(0).getMajorType().getScale(),
+              logicalExpressions.get(1).getMajorType().getPrecision(),
+              logicalExpressions.get(1).getMajorType().getScale());
+      return TypeProtos.MajorType.newBuilder()
+          .setMinorType(DecimalUtility.getDecimalDataType(outputScalePrec.getOutputPrecision()))
+          .setScale(outputScalePrec.getOutputScale())
+          .setPrecision(outputScalePrec.getOutputPrecision())
+          .setMode(mode)
+          .build();
+    }
+  }
+
+  /**
+   * Return type calculation implementation for functions with return type set as
+   * {@link org.apache.drill.exec.expr.annotations.FunctionTemplate.ReturnType#DECIMAL_SET_SCALE}.
+   */
+  public static class DecimalSetScaleReturnTypeInference implements ReturnTypeInference {
+
+    public static final DecimalSetScaleReturnTypeInference INSTANCE = new DecimalSetScaleReturnTypeInference();
+
+    @Override
+    public TypeProtos.MajorType getType(List<LogicalExpression> logicalExpressions, FunctionAttributes attributes) {
+      TypeProtos.DataMode mode = attributes.getReturnValue().getType().getMode();
+      int scale = 0;
+      int precision = 0;
+
+      if (attributes.getNullHandling() == FunctionTemplate.NullHandling.NULL_IF_NULL) {
+        // if any one of the input types is nullable, then return nullable return type
+        for (LogicalExpression e : logicalExpressions) {
+
+          precision = Math.max(precision, e.getMajorType().getPrecision());
+          if (e.getMajorType().getMode() == TypeProtos.DataMode.OPTIONAL) {
+            mode = TypeProtos.DataMode.OPTIONAL;
+          }
+        }
+
+        // Used by functions like round, truncate which specify the scale for the output as the second argument
+        assert (logicalExpressions.size() == 2) && (logicalExpressions.get(1) instanceof ValueExpressions.IntExpression);
+
+        // Get the scale from the second argument which should be a constant
+        scale = ((ValueExpressions.IntExpression) logicalExpressions.get(1)).getInt();
+      }
+
+      return TypeProtos.MajorType.newBuilder()
+          .setMinorType(attributes.getReturnValue().getType().getMinorType())
+          .setScale(scale)
+          .setPrecision(precision)
+          .setMode(mode)
+          .build();
+    }
+  }
+
+  /**
+   * Return type calculation implementation for functions with return type set as
+   * {@link org.apache.drill.exec.expr.annotations.FunctionTemplate.ReturnType#DECIMAL_SUM_AGGREGATE}.
+   */
+  public static class DecimalSumAggReturnTypeInference implements ReturnTypeInference {
+
+    public static final DecimalSumAggReturnTypeInference INSTANCE = new DecimalSumAggReturnTypeInference();
+
+    @Override
+    public TypeProtos.MajorType getType(List<LogicalExpression> logicalExpressions, FunctionAttributes attributes) {
+      int scale = 0;
+      int precision = 0;
+
+      // Get the max scale and precision from the inputs
+      for (LogicalExpression e : logicalExpressions) {
+        scale = Math.max(scale, e.getMajorType().getScale());
+        precision = Math.max(precision, e.getMajorType().getPrecision());
+      }
+
+      return (TypeProtos.MajorType.newBuilder()
+          .setMinorType(attributes.getReturnValue().getType().getMinorType())
+          .setScale(scale)
+          .setPrecision(38)
+          .setMode(TypeProtos.DataMode.REQUIRED)
+          .build());
+    }
+  }
+
+  /**
+   * Return type calculation implementation for functions with return type set as
+   * {@link org.apache.drill.exec.expr.annotations.FunctionTemplate.ReturnType#DECIMAL_SUM_SCALE}.
+   */
+  public static class DecimalSumScaleReturnTypeInference implements ReturnTypeInference {
+
+    public static final DecimalSumScaleReturnTypeInference INSTANCE = new DecimalSumScaleReturnTypeInference();
+
+    @Override
+    public TypeProtos.MajorType getType(List<LogicalExpression> logicalExpressions, FunctionAttributes attributes) {
+
+      TypeProtos.DataMode mode = FunctionUtils.getReturnTypeDataMode(logicalExpressions, attributes);
+
+      assert logicalExpressions.size() == 2;
+
+      DecimalScalePrecisionMulFunction outputScalePrec =
+          new DecimalScalePrecisionMulFunction(logicalExpressions.get(0).getMajorType().getPrecision(),
+              logicalExpressions.get(0).getMajorType().getScale(),
+              logicalExpressions.get(1).getMajorType().getPrecision(),
+              logicalExpressions.get(1).getMajorType().getScale());
+      return TypeProtos.MajorType.newBuilder()
+          .setMinorType(DecimalUtility.getDecimalDataType(outputScalePrec.getOutputPrecision()))
+          .setScale(outputScalePrec.getOutputScale())
+          .setPrecision(outputScalePrec.getOutputPrecision())
+          .setMode(mode)
+          .build();
+    }
+  }
+
+  /**
+   * Return type calculation implementation for functions with return type set as
+   * {@link org.apache.drill.exec.expr.annotations.FunctionTemplate.ReturnType#DECIMAL_ZERO_SCALE}.
+   */
+  public static class DecimalZeroScaleReturnTypeInference implements ReturnTypeInference {
+
+    public static final DecimalZeroScaleReturnTypeInference INSTANCE = new DecimalZeroScaleReturnTypeInference();
+
+    /**
+     * Return type is used for functions where we need to remove the scale part.
+     * For example, truncate and round functions.
+     *
+     * @param logicalExpressions logical expressions
+     * @param attributes function attributes
+     * @return return type
+     */
+    @Override
+    public TypeProtos.MajorType getType(List<LogicalExpression> logicalExpressions, FunctionAttributes attributes) {
+
+      int precision = 0;
+      TypeProtos.DataMode mode = attributes.getReturnValue().getType().getMode();
+
+      if (attributes.getNullHandling() == FunctionTemplate.NullHandling.NULL_IF_NULL) {
+        // if any one of the input types is nullable, then return nullable return type
+        for (LogicalExpression e : logicalExpressions) {
+          if (e.getMajorType().getMode() == TypeProtos.DataMode.OPTIONAL) {
+            mode = TypeProtos.DataMode.OPTIONAL;
+          }
+          precision = Math.max(precision, e.getMajorType().getPrecision());
+        }
+      }
+
+      return TypeProtos.MajorType.newBuilder()
+          .setMinorType(attributes.getReturnValue().getType().getMinorType())
+          .setScale(0)
+          .setPrecision(precision)
+          .setMode(mode)
+          .build();
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/drill/blob/6741e68a/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/output/DefaultReturnTypeInference.java
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/output/DefaultReturnTypeInference.java b/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/output/DefaultReturnTypeInference.java
new file mode 100644
index 0000000..02e6b1e
--- /dev/null
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/output/DefaultReturnTypeInference.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.exec.expr.fn.output;
+
+import com.google.common.collect.Sets;
+import org.apache.drill.common.expression.LogicalExpression;
+import org.apache.drill.common.types.TypeProtos;
+import org.apache.drill.exec.expr.fn.FunctionAttributes;
+import org.apache.drill.exec.expr.fn.FunctionUtils;
+import org.apache.drill.exec.expr.fn.ValueReference;
+
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Return type calculation implementation for functions with return type set as
+ * {@link org.apache.drill.exec.expr.annotations.FunctionTemplate.ReturnType#DEFAULT}.
+ */
+public class DefaultReturnTypeInference implements ReturnTypeInference {
+
+  public static final DefaultReturnTypeInference INSTANCE = new DefaultReturnTypeInference();
+
+  /**
+   * Calculates return type and its nullability. Precision and scale is not included.
+   *
+   * @param logicalExpressions logical expressions
+   * @param attributes function attributes
+   * @return return type
+   */
+  @Override
+  public TypeProtos.MajorType getType(List<LogicalExpression> logicalExpressions, FunctionAttributes attributes) {
+    if (attributes.getReturnValue().getType().getMinorType() == TypeProtos.MinorType.UNION) {
+      final Set<TypeProtos.MinorType> subTypes = Sets.newHashSet();
+      for (final ValueReference ref : attributes.getParameters()) {
+        subTypes.add(ref.getType().getMinorType());
+      }
+
+      final TypeProtos.MajorType.Builder builder = TypeProtos.MajorType.newBuilder()
+          .setMinorType(TypeProtos.MinorType.UNION)
+          .setMode(TypeProtos.DataMode.OPTIONAL);
+
+      for (final TypeProtos.MinorType subType : subTypes) {
+        builder.addSubType(subType);
+      }
+      return builder.build();
+    }
+    return attributes.getReturnValue().getType().toBuilder()
+        .setMode(FunctionUtils.getReturnTypeDataMode(logicalExpressions, attributes))
+        .build();
+  }
+}

http://git-wip-us.apache.org/repos/asf/drill/blob/6741e68a/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/output/PadReturnTypeInference.java
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/output/PadReturnTypeInference.java b/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/output/PadReturnTypeInference.java
new file mode 100644
index 0000000..aac4703
--- /dev/null
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/output/PadReturnTypeInference.java
@@ -0,0 +1,57 @@
+/*
+ * 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.exec.expr.fn.output;
+
+import org.apache.drill.common.expression.LogicalExpression;
+import org.apache.drill.common.expression.ValueExpressions;
+import org.apache.drill.common.types.TypeProtos;
+import org.apache.drill.exec.expr.fn.FunctionAttributes;
+import org.apache.drill.exec.expr.fn.FunctionUtils;
+
+import java.util.List;
+
+/**
+ * Return type calculation implementation for functions with return type set as
+ * {@link org.apache.drill.exec.expr.annotations.FunctionTemplate.ReturnType#PAD}.
+ */
+public class PadReturnTypeInference implements ReturnTypeInference {
+
+  public static final PadReturnTypeInference INSTANCE = new PadReturnTypeInference();
+
+  /**
+   * Defines function return type and sets precision if it pad length parameter is int expression.
+   * If pad length is less than zero, return type precision is 0.
+   *
+   * @param logicalExpressions logical expressions
+   * @param attributes function attributes
+   * @return return type
+   */
+  @Override
+  public TypeProtos.MajorType getType(List<LogicalExpression> logicalExpressions, FunctionAttributes attributes) {
+    TypeProtos.MajorType.Builder builder = TypeProtos.MajorType.newBuilder()
+        .setMinorType(attributes.getReturnValue().getType().getMinorType())
+        .setMode(FunctionUtils.getReturnTypeDataMode(logicalExpressions, attributes));
+
+    if (logicalExpressions.get(1).iterator().hasNext() &&
+        logicalExpressions.get(1).iterator().next() instanceof ValueExpressions.IntExpression) {
+      int precision = ((ValueExpressions.IntExpression) logicalExpressions.get(1).iterator().next()).getInt();
+      // if pad length is less than zero, output length is 0
+      builder.setPrecision(Math.max(precision, 0));
+    }
+    return builder.build();
+  }
+}

http://git-wip-us.apache.org/repos/asf/drill/blob/6741e68a/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/output/ReturnTypeInference.java
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/output/ReturnTypeInference.java b/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/output/ReturnTypeInference.java
new file mode 100644
index 0000000..05375a0
--- /dev/null
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/output/ReturnTypeInference.java
@@ -0,0 +1,33 @@
+/*
+ * 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.exec.expr.fn.output;
+
+import org.apache.drill.common.expression.LogicalExpression;
+import org.apache.drill.common.types.TypeProtos;
+import org.apache.drill.exec.expr.fn.FunctionAttributes;
+
+import java.util.List;
+
+/**
+ * Return type calculation interface for functions that have return type set as with enum
+ * {@link org.apache.drill.exec.expr.annotations.FunctionTemplate.ReturnType}.
+ */
+public interface ReturnTypeInference {
+
+  TypeProtos.MajorType getType(List<LogicalExpression> logicalExpressions, FunctionAttributes attributes);
+
+}

http://git-wip-us.apache.org/repos/asf/drill/blob/6741e68a/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/output/SameInOutLengthReturnTypeInference.java
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/output/SameInOutLengthReturnTypeInference.java b/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/output/SameInOutLengthReturnTypeInference.java
new file mode 100644
index 0000000..92bfae1
--- /dev/null
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/output/SameInOutLengthReturnTypeInference.java
@@ -0,0 +1,53 @@
+/*
+ * 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.exec.expr.fn.output;
+
+import org.apache.drill.common.expression.LogicalExpression;
+import org.apache.drill.common.types.TypeProtos;
+import org.apache.drill.common.types.Types;
+import org.apache.drill.exec.expr.fn.FunctionAttributes;
+import org.apache.drill.exec.expr.fn.FunctionUtils;
+
+import java.util.List;
+
+/**
+ * Return type calculation implementation for functions with return type set as
+ * {@link org.apache.drill.exec.expr.annotations.FunctionTemplate.ReturnType#SAME_IN_OUT_LENGTH}.
+ */
+public class SameInOutLengthReturnTypeInference implements ReturnTypeInference {
+
+  public static final SameInOutLengthReturnTypeInference INSTANCE = new SameInOutLengthReturnTypeInference();
+
+  /**
+   * Defines function return type and sets precision and scale if input type has them.
+   *
+   * @param logicalExpressions logical expressions
+   * @param attributes function attributes
+   * @return return type
+   */
+  @Override
+  public TypeProtos.MajorType getType(List<LogicalExpression> logicalExpressions, FunctionAttributes attributes) {
+    TypeProtos.MajorType majorType = logicalExpressions.get(0).getMajorType();
+
+    TypeProtos.MajorType.Builder builder = TypeProtos.MajorType.newBuilder()
+        .setMinorType(attributes.getReturnValue().getType().getMinorType())
+        .setMode(FunctionUtils.getReturnTypeDataMode(logicalExpressions, attributes));
+
+    builder = Types.calculateTypePrecisionAndScale(majorType, majorType, builder);
+    return builder.build();
+  }
+}

http://git-wip-us.apache.org/repos/asf/drill/blob/6741e68a/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/output/StringCastReturnTypeInference.java
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/output/StringCastReturnTypeInference.java b/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/output/StringCastReturnTypeInference.java
new file mode 100644
index 0000000..95c30cd
--- /dev/null
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/output/StringCastReturnTypeInference.java
@@ -0,0 +1,57 @@
+/*
+ * 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.exec.expr.fn.output;
+
+import com.google.common.primitives.Ints;
+import org.apache.drill.common.expression.LogicalExpression;
+import org.apache.drill.common.expression.ValueExpressions;
+import org.apache.drill.common.types.TypeProtos;
+import org.apache.drill.exec.expr.fn.FunctionAttributes;
+import org.apache.drill.exec.expr.fn.FunctionUtils;
+
+import java.util.List;
+
+/**
+ * Return type calculation implementation for functions with return type set as
+ * {@link org.apache.drill.exec.expr.annotations.FunctionTemplate.ReturnType#STRING_CAST}.
+ */
+public class StringCastReturnTypeInference implements ReturnTypeInference {
+
+  public static final StringCastReturnTypeInference INSTANCE = new StringCastReturnTypeInference();
+
+  /**
+   * Defines function return type and sets cast length as type precision
+   * if cast length is simple long expression.
+   *
+   * @param logicalExpressions logical expressions
+   * @param attributes function attributes
+   * @return return type
+   */
+  @Override
+  public TypeProtos.MajorType getType(List<LogicalExpression> logicalExpressions, FunctionAttributes attributes) {
+    TypeProtos.MajorType.Builder builder = TypeProtos.MajorType.newBuilder()
+        .setMinorType(attributes.getReturnValue().getType().getMinorType())
+        .setMode(FunctionUtils.getReturnTypeDataMode(logicalExpressions, attributes));
+
+    LogicalExpression logicalExpression = logicalExpressions.get(1);
+    if (logicalExpressions.get(1) instanceof ValueExpressions.LongExpression) {
+      long precision = ((ValueExpressions.LongExpression) logicalExpression).getLong();
+      builder.setPrecision(Ints.checkedCast(precision));
+    }
+    return builder.build();
+  }
+}

http://git-wip-us.apache.org/repos/asf/drill/blob/6741e68a/exec/java-exec/src/main/java/org/apache/drill/exec/physical/impl/project/ProjectRecordBatch.java
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/physical/impl/project/ProjectRecordBatch.java b/exec/java-exec/src/main/java/org/apache/drill/exec/physical/impl/project/ProjectRecordBatch.java
index 1ecdaf5..a35e3f1 100644
--- a/exec/java-exec/src/main/java/org/apache/drill/exec/physical/impl/project/ProjectRecordBatch.java
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/physical/impl/project/ProjectRecordBatch.java
@@ -45,7 +45,6 @@ import org.apache.drill.exec.expr.ClassGenerator.HoldingContainer;
 import org.apache.drill.exec.expr.CodeGenerator;
 import org.apache.drill.exec.expr.DrillFuncHolderExpr;
 import org.apache.drill.exec.expr.ExpressionTreeMaterializer;
-import org.apache.drill.exec.expr.TypeHelper;
 import org.apache.drill.exec.expr.ValueVectorReadExpression;
 import org.apache.drill.exec.expr.ValueVectorWriteExpression;
 import org.apache.drill.exec.expr.fn.DrillComplexWriterFuncHolder;
@@ -517,12 +516,8 @@ public class ProjectRecordBatch extends AbstractSingleRecordBatch<Project> {
         final String castFuncName = CastFunctions.getCastFunc(MinorType.VARCHAR);
         final List<LogicalExpression> castArgs = Lists.newArrayList();
         castArgs.add(convertToJson);  //input_expr
-        /*
-         * We are implicitly casting to VARCHAR so we don't have a max length,
-         * using an arbitrary value. We trim down the size of the stored bytes
-         * to the actual size so this size doesn't really matter.
-         */
-        castArgs.add(new ValueExpressions.LongExpression(TypeHelper.VARCHAR_DEFAULT_CAST_LEN, null)); //
+        // implicitly casting to varchar, since we don't know actual source length, cast to undefined length, which will preserve source length
+        castArgs.add(new ValueExpressions.LongExpression(Types.MAX_VARCHAR_LENGTH, null));
         final FunctionCall castCall = new FunctionCall(castFuncName, castArgs, ExpressionPosition.UNKNOWN);
         exprs.add(new NamedExpression(castCall, new FieldReference(field.getPath())));
       } else {

http://git-wip-us.apache.org/repos/asf/drill/blob/6741e68a/exec/java-exec/src/main/java/org/apache/drill/exec/physical/impl/union/UnionAllRecordBatch.java
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/physical/impl/union/UnionAllRecordBatch.java b/exec/java-exec/src/main/java/org/apache/drill/exec/physical/impl/union/UnionAllRecordBatch.java
index 06b7bdb..985c4ae 100644
--- a/exec/java-exec/src/main/java/org/apache/drill/exec/physical/impl/union/UnionAllRecordBatch.java
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/physical/impl/union/UnionAllRecordBatch.java
@@ -29,13 +29,13 @@ import org.apache.drill.common.expression.SchemaPath;
 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.types.Types;
 import org.apache.drill.exec.exception.ClassTransformationException;
 import org.apache.drill.exec.exception.OutOfMemoryException;
 import org.apache.drill.exec.exception.SchemaChangeException;
 import org.apache.drill.exec.expr.ClassGenerator;
 import org.apache.drill.exec.expr.CodeGenerator;
 import org.apache.drill.exec.expr.ExpressionTreeMaterializer;
-import org.apache.drill.exec.expr.ValueVectorReadExpression;
 import org.apache.drill.exec.expr.ValueVectorWriteExpression;
 import org.apache.drill.exec.ops.FragmentContext;
 import org.apache.drill.exec.physical.config.UnionAll;
@@ -198,16 +198,14 @@ public class UnionAllRecordBatch extends AbstractRecordBatch<UnionAll> {
       // transfer directly,
       // rename columns or
       // cast data types (Minortype or DataMode)
-      if(hasSameTypeAndMode(outputFields.get(index), vw.getValueVector().getField())) {
+      if (hasSameTypeAndMode(outputFields.get(index), vw.getValueVector().getField())) {
         // Transfer column
-        if(outputFields.get(index).getPath().equals(inputPath)) {
-          final LogicalExpression expr = ExpressionTreeMaterializer.materialize(inputPath, current, collector, context.getFunctionRegistry());
-          if (collector.hasErrors()) {
-            throw new SchemaChangeException(String.format("Failure while trying to materialize incoming schema.  Errors:\n %s.", collector.toErrorString()));
-          }
 
-          ValueVectorReadExpression vectorRead = (ValueVectorReadExpression) expr;
-          ValueVector vvOut = container.addOrGet(MaterializedField.create(outputPath.getAsUnescapedPath(), vectorRead.getMajorType()));
+        MajorType outputFieldType = outputFields.get(index).getType();
+        MaterializedField outputField = MaterializedField.create(outputPath.getAsUnescapedPath(), outputFieldType);
+
+        if (outputFields.get(index).getPath().equals(inputPath.getAsUnescapedPath())) {
+          ValueVector vvOut = container.addOrGet(outputField);
           TransferPair tp = vvIn.makeTransferPair(vvOut);
           transfers.add(tp);
         // Copy data in order to rename the column
@@ -217,7 +215,6 @@ public class UnionAllRecordBatch extends AbstractRecordBatch<UnionAll> {
             throw new SchemaChangeException(String.format("Failure while trying to materialize incoming schema.  Errors:\n %s.", collector.toErrorString()));
           }
 
-          MaterializedField outputField = MaterializedField.create(outputPath.getAsUnescapedPath(), expr.getMajorType());
           ValueVector vv = container.addOrGet(outputField, callBack);
           allocationVectors.add(vv);
           TypedFieldId fid = container.getValueVectorId(SchemaPath.getSimplePath(outputField.getPath()));
@@ -571,39 +568,40 @@ public class UnionAllRecordBatch extends AbstractRecordBatch<UnionAll> {
       Iterator<MaterializedField> rightIter = rightSchema.iterator();
 
       int index = 1;
-      while(leftIter.hasNext() && rightIter.hasNext()) {
+      while (leftIter.hasNext() && rightIter.hasNext()) {
         MaterializedField leftField  = leftIter.next();
         MaterializedField rightField = rightIter.next();
 
-        if(hasSameTypeAndMode(leftField, rightField)) {
-          outputFields.add(MaterializedField.create(leftField.getPath(), leftField.getType()));
+        if (hasSameTypeAndMode(leftField, rightField)) {
+          MajorType.Builder builder = MajorType.newBuilder().setMinorType(leftField.getType().getMinorType()).setMode(leftField.getDataMode());
+          builder = Types.calculateTypePrecisionAndScale(leftField.getType(), rightField.getType(), builder);
+          outputFields.add(MaterializedField.create(leftField.getPath(), builder.build()));
         } else {
           // If the output type is not the same,
           // cast the column of one of the table to a data type which is the Least Restrictive
-          MinorType outputMinorType;
-          if(leftField.getType().getMinorType() == rightField.getType().getMinorType()) {
-            outputMinorType = leftField.getType().getMinorType();
+          MajorType.Builder builder = MajorType.newBuilder();
+          if (leftField.getType().getMinorType() == rightField.getType().getMinorType()) {
+            builder.setMinorType(leftField.getType().getMinorType());
+            builder = Types.calculateTypePrecisionAndScale(leftField.getType(), rightField.getType(), builder);
           } else {
             List<MinorType> types = Lists.newLinkedList();
             types.add(leftField.getType().getMinorType());
             types.add(rightField.getType().getMinorType());
-            outputMinorType = TypeCastRules.getLeastRestrictiveType(types);
-            if(outputMinorType == null) {
+            MinorType outputMinorType = TypeCastRules.getLeastRestrictiveType(types);
+            if (outputMinorType == null) {
               throw new DrillRuntimeException("Type mismatch between " + leftField.getType().getMinorType().toString() +
                   " on the left side and " + rightField.getType().getMinorType().toString() +
                   " on the right side in column " + index + " of UNION ALL");
             }
+            builder.setMinorType(outputMinorType);
           }
 
           // The output data mode should be as flexible as the more flexible one from the two input tables
           List<DataMode> dataModes = Lists.newLinkedList();
           dataModes.add(leftField.getType().getMode());
           dataModes.add(rightField.getType().getMode());
-          DataMode dataMode = TypeCastRules.getLeastRestrictiveDataMode(dataModes);
+          builder.setMode(TypeCastRules.getLeastRestrictiveDataMode(dataModes));
 
-          MajorType.Builder builder = MajorType.newBuilder();
-          builder.setMinorType(outputMinorType);
-          builder.setMode(dataMode);
           outputFields.add(MaterializedField.create(leftField.getPath(), builder.build()));
         }
         ++index;

http://git-wip-us.apache.org/repos/asf/drill/blob/6741e68a/exec/java-exec/src/main/java/org/apache/drill/exec/planner/logical/DrillOptiq.java
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/planner/logical/DrillOptiq.java b/exec/java-exec/src/main/java/org/apache/drill/exec/planner/logical/DrillOptiq.java
index 5a90787..db0cfbd 100644
--- a/exec/java-exec/src/main/java/org/apache/drill/exec/planner/logical/DrillOptiq.java
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/planner/logical/DrillOptiq.java
@@ -294,20 +294,19 @@ public class DrillOptiq {
 
     private LogicalExpression getDrillCastFunctionFromOptiq(RexCall call){
       LogicalExpression arg = call.getOperands().get(0).accept(this);
-      MajorType castType = null;
+      MajorType castType;
 
       switch(call.getType().getSqlTypeName().getName()){
       case "VARCHAR":
       case "CHAR":
-        castType = Types.required(MinorType.VARCHAR).toBuilder().setWidth(call.getType().getPrecision()).build();
+        castType = Types.required(MinorType.VARCHAR).toBuilder().setPrecision(call.getType().getPrecision()).build();
         break;
 
       case "INTEGER": castType = Types.required(MinorType.INT); break;
       case "FLOAT": castType = Types.required(MinorType.FLOAT4); break;
       case "DOUBLE": castType = Types.required(MinorType.FLOAT8); break;
       case "DECIMAL":
-        if (context.getPlannerSettings().getOptions().
-            getOption(PlannerSettings.ENABLE_DECIMAL_DATA_TYPE_KEY).bool_val == false ) {
+        if (!context.getPlannerSettings().getOptions().getOption(PlannerSettings.ENABLE_DECIMAL_DATA_TYPE_KEY).bool_val) {
           throw UserException
               .unsupportedError()
               .message(ExecErrorConstants.DECIMAL_DISABLE_ERR_MSG)
@@ -334,7 +333,7 @@ public class DrillOptiq {
         case "INTERVAL_YEAR_MONTH": castType = Types.required(MinorType.INTERVALYEAR); break;
         case "INTERVAL_DAY_TIME": castType = Types.required(MinorType.INTERVALDAY); break;
         case "BOOLEAN": castType = Types.required(MinorType.BIT); break;
-        case "BINARY": castType = Types.required(MinorType.VARBINARY).toBuilder().setWidth(call.getType().getPrecision()).build(); break;
+        case "BINARY": castType = Types.required(MinorType.VARBINARY); break;
         case "ANY": return arg; // Type will be same as argument.
         default: castType = Types.required(MinorType.valueOf(call.getType().getSqlTypeName().getName()));
       }
@@ -422,7 +421,7 @@ public class DrillOptiq {
            * (empty string literal) to the list of arguments.
            */
           List<LogicalExpression> concatArgs = new LinkedList<>(args);
-          concatArgs.add(new QuotedString("", ExpressionPosition.UNKNOWN));
+          concatArgs.add(QuotedString.EMPTY_STRING);
 
           return FunctionCallFactory.createExpression(functionName, concatArgs);
 
@@ -512,9 +511,9 @@ public class DrillOptiq {
         return ValueExpressions.getBit(((Boolean) literal.getValue()));
       case CHAR:
         if (isLiteralNull(literal)) {
-          return createNullExpr(MinorType.VARCHAR);
+          return createStringNullExpr(literal.getType().getPrecision());
         }
-        return ValueExpressions.getChar(((NlsString)literal.getValue()).getValue());
+        return ValueExpressions.getChar(((NlsString)literal.getValue()).getValue(), literal.getType().getPrecision());
       case DOUBLE:
         if (isLiteralNull(literal)){
           return createNullExpr(MinorType.FLOAT8);
@@ -556,14 +555,14 @@ public class DrillOptiq {
         return ValueExpressions.getFloat8(dbl);
       case VARCHAR:
         if (isLiteralNull(literal)) {
-          return createNullExpr(MinorType.VARCHAR);
+          return createStringNullExpr(literal.getType().getPrecision());
         }
-        return ValueExpressions.getChar(((NlsString)literal.getValue()).getValue());
+        return ValueExpressions.getChar(((NlsString)literal.getValue()).getValue(), literal.getType().getPrecision());
       case SYMBOL:
         if (isLiteralNull(literal)) {
-          return createNullExpr(MinorType.VARCHAR);
+          return createStringNullExpr(literal.getType().getPrecision());
         }
-        return ValueExpressions.getChar(literal.getValue().toString());
+        return ValueExpressions.getChar(literal.getValue().toString(), literal.getType().getPrecision());
       case DATE:
         if (isLiteralNull(literal)) {
           return createNullExpr(MinorType.DATE);
@@ -599,10 +598,28 @@ public class DrillOptiq {
         throw new UnsupportedOperationException(String.format("Unable to convert the value of %s and type %s to a Drill constant expression.", literal, literal.getType().getSqlTypeName()));
       }
     }
-  }
 
-  private static final TypedNullConstant createNullExpr(MinorType type) {
-    return new TypedNullConstant(Types.optional(type));
+    /**
+     * Create nullable major type using given minor type
+     * and wraps it in typed null constant.
+     *
+     * @param type minor type
+     * @return typed null constant instance
+     */
+    private TypedNullConstant createNullExpr(MinorType type) {
+      return new TypedNullConstant(Types.optional(type));
+    }
+
+    /**
+     * Create nullable varchar major type with given precision
+     * and wraps it in typed null constant.
+     *
+     * @param precision precision value
+     * @return typed null constant instance
+     */
+    private TypedNullConstant createStringNullExpr(int precision) {
+      return new TypedNullConstant(Types.withPrecision(MinorType.VARCHAR, TypeProtos.DataMode.OPTIONAL, precision));
+    }
   }
 
   public static boolean isLiteralNull(RexLiteral literal) {

http://git-wip-us.apache.org/repos/asf/drill/blob/6741e68a/exec/java-exec/src/main/java/org/apache/drill/exec/planner/sql/SqlConverter.java
----------------------------------------------------------------------
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 845848c..5778041 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
@@ -55,6 +55,7 @@ import org.apache.calcite.sql2rel.RelDecorrelator;
 import org.apache.calcite.sql2rel.SqlToRelConverter;
 import org.apache.drill.common.config.DrillConfig;
 import org.apache.drill.common.exceptions.UserException;
+import org.apache.drill.common.types.Types;
 import org.apache.drill.exec.ExecConstants;
 import org.apache.drill.exec.expr.fn.FunctionImplementationRegistry;
 import org.apache.drill.exec.ops.QueryContext;
@@ -226,7 +227,7 @@ public class SqlConverter {
       case BINARY:
       case VARCHAR:
       case VARBINARY:
-        return 65536;
+        return Types.MAX_VARCHAR_LENGTH;
       default:
         return super.getDefaultPrecision(typeName);
       }

http://git-wip-us.apache.org/repos/asf/drill/blob/6741e68a/exec/java-exec/src/main/java/org/apache/drill/exec/planner/sql/TypeInferenceUtils.java
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/planner/sql/TypeInferenceUtils.java b/exec/java-exec/src/main/java/org/apache/drill/exec/planner/sql/TypeInferenceUtils.java
index b7942ed..523b721 100644
--- a/exec/java-exec/src/main/java/org/apache/drill/exec/planner/sql/TypeInferenceUtils.java
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/planner/sql/TypeInferenceUtils.java
@@ -1,4 +1,4 @@
-/**
+/*
  * 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
@@ -30,9 +30,8 @@ import org.apache.calcite.sql.SqlIntervalQualifier;
 import org.apache.calcite.sql.SqlKind;
 import org.apache.calcite.sql.SqlLiteral;
 import org.apache.calcite.sql.SqlNode;
-import org.apache.calcite.sql.SqlOperator;
+import org.apache.calcite.sql.SqlNumericLiteral;
 import org.apache.calcite.sql.SqlOperatorBinding;
-import org.apache.calcite.sql.SqlRankFunction;
 import org.apache.calcite.sql.fun.SqlAvgAggFunction;
 import org.apache.calcite.sql.parser.SqlParserPos;
 import org.apache.calcite.sql.type.SqlReturnTypeInference;
@@ -46,7 +45,6 @@ import org.apache.drill.common.expression.MajorTypeInLogicalExpression;
 import org.apache.drill.common.exceptions.UserException;
 import org.apache.drill.common.types.TypeProtos;
 import org.apache.drill.common.types.Types;
-import org.apache.drill.exec.expr.TypeHelper;
 import org.apache.drill.exec.expr.fn.DrillFuncHolder;
 import org.apache.drill.exec.resolver.FunctionResolver;
 import org.apache.drill.exec.resolver.FunctionResolverFactory;
@@ -126,14 +124,15 @@ public class TypeInferenceUtils {
       .put("DATE_PART", DrillDatePartSqlReturnTypeInference.INSTANCE)
       .put("SUM", DrillSumSqlReturnTypeInference.INSTANCE)
       .put("COUNT", DrillCountSqlReturnTypeInference.INSTANCE)
-      .put("CONCAT", DrillConcatSqlReturnTypeInference.INSTANCE)
+      .put("CONCAT", DrillConcatSqlReturnTypeInference.INSTANCE_CONCAT)
+      .put("CONCATOPERATOR", DrillConcatSqlReturnTypeInference.INSTANCE_CONCAT_OP)
       .put("LENGTH", DrillLengthSqlReturnTypeInference.INSTANCE)
-      .put("LPAD", DrillPadTrimSqlReturnTypeInference.INSTANCE)
-      .put("RPAD", DrillPadTrimSqlReturnTypeInference.INSTANCE)
-      .put("LTRIM", DrillPadTrimSqlReturnTypeInference.INSTANCE)
-      .put("RTRIM", DrillPadTrimSqlReturnTypeInference.INSTANCE)
-      .put("BTRIM", DrillPadTrimSqlReturnTypeInference.INSTANCE)
-      .put("TRIM", DrillPadTrimSqlReturnTypeInference.INSTANCE)
+      .put("LPAD", DrillPadSqlReturnTypeInference.INSTANCE)
+      .put("RPAD", DrillPadSqlReturnTypeInference.INSTANCE)
+      .put("LTRIM", DrillTrimSqlReturnTypeInference.INSTANCE)
+      .put("RTRIM", DrillTrimSqlReturnTypeInference.INSTANCE)
+      .put("BTRIM", DrillTrimSqlReturnTypeInference.INSTANCE)
+      .put("TRIM", DrillTrimSqlReturnTypeInference.INSTANCE)
       .put("CONVERT_TO", DrillConvertToSqlReturnTypeInference.INSTANCE)
       .put("EXTRACT", DrillExtractSqlReturnTypeInference.INSTANCE)
       .put("SQRT", DrillSqrtSqlReturnTypeInference.INSTANCE)
@@ -142,6 +141,12 @@ public class TypeInferenceUtils {
       .put("KVGEN", DrillDeferToExecSqlReturnTypeInference.INSTANCE)
       .put("CONVERT_FROM", DrillDeferToExecSqlReturnTypeInference.INSTANCE)
 
+      // Functions that return the same type
+      .put("LOWER", DrillSameSqlReturnTypeInference.INSTANCE)
+      .put("UPPER", DrillSameSqlReturnTypeInference.INSTANCE)
+      .put("INITCAP", DrillSameSqlReturnTypeInference.INSTANCE)
+      .put("REVERSE", DrillSameSqlReturnTypeInference.INSTANCE)
+
       // Window Functions
       // RANKING
       .put(SqlKind.CUME_DIST.name(), DrillRankingSqlReturnTypeInference.INSTANCE_DOUBLE)
@@ -158,8 +163,8 @@ public class TypeInferenceUtils {
       .put("LAG", DrillLeadLagSqlReturnTypeInference.INSTANCE)
 
       // FIRST_VALUE, LAST_VALUE
-      .put("FIRST_VALUE", DrillFirstLastValueSqlReturnTypeInference.INSTANCE)
-      .put("LAST_VALUE", DrillFirstLastValueSqlReturnTypeInference.INSTANCE)
+      .put("FIRST_VALUE", DrillSameSqlReturnTypeInference.INSTANCE)
+      .put("LAST_VALUE", DrillSameSqlReturnTypeInference.INSTANCE)
 
       // Functions rely on DrillReduceAggregatesRule for expression simplification as opposed to getting evaluated directly
       .put(SqlAvgAggFunction.Subtype.AVG.name(), DrillAvgAggSqlReturnTypeInference.INSTANCE)
@@ -214,6 +219,16 @@ public class TypeInferenceUtils {
     }
   }
 
+  /**
+   * Checks if given type is string scalar type.
+   *
+   * @param sqlTypeName Calcite's sql type name
+   * @return true if given type is string scalar type
+   */
+  public static boolean isScalarStringType(final SqlTypeName sqlTypeName) {
+    return sqlTypeName == SqlTypeName.VARCHAR || sqlTypeName == SqlTypeName.CHAR;
+  }
+
   private static class DrillDefaultSqlReturnTypeInference implements SqlReturnTypeInference {
     private final List<DrillFuncHolder> functions;
 
@@ -394,31 +409,37 @@ public class TypeInferenceUtils {
   }
 
   private static class DrillConcatSqlReturnTypeInference implements SqlReturnTypeInference {
-    private static final DrillConcatSqlReturnTypeInference INSTANCE = new DrillConcatSqlReturnTypeInference();
+    // Difference between concat function and concat operator ('||') is that concat function resolves nulls internally,
+    // i.e. does not return nulls at all.
+    private static final DrillConcatSqlReturnTypeInference INSTANCE_CONCAT = new DrillConcatSqlReturnTypeInference(false);
+    private static final DrillConcatSqlReturnTypeInference INSTANCE_CONCAT_OP = new DrillConcatSqlReturnTypeInference(true);
+
+    private final boolean isNullIfNull;
+
+    public DrillConcatSqlReturnTypeInference(boolean isNullIfNull) {
+      this.isNullIfNull = isNullIfNull;
+    }
 
     @Override
     public RelDataType inferReturnType(SqlOperatorBinding opBinding) {
-      final RelDataTypeFactory factory = opBinding.getTypeFactory();
 
-      boolean isNullable = true;
-      int precision = 0;
-      for(RelDataType relDataType : opBinding.collectOperandTypes()) {
-        if(!relDataType.isNullable()) {
-          isNullable = false;
-        }
-
-        // If the underlying columns cannot offer information regarding the precision (i.e., the length) of the VarChar,
-        // Drill uses the largest to represent it
-        if(relDataType.getPrecision() == TypeHelper.VARCHAR_DEFAULT_CAST_LEN
-            || relDataType.getPrecision() == RelDataType.PRECISION_NOT_SPECIFIED) {
-          precision = TypeHelper.VARCHAR_DEFAULT_CAST_LEN;
+      // If the underlying columns cannot offer information regarding the precision of the VarChar,
+      // Drill uses the largest to represent it.
+      int totalPrecision = 0;
+      for (RelDataType relDataType : opBinding.collectOperandTypes()) {
+        if (isScalarStringType(relDataType.getSqlTypeName()) && relDataType.getPrecision() != RelDataType.PRECISION_NOT_SPECIFIED) {
+          totalPrecision += relDataType.getPrecision();
         } else {
-          precision += relDataType.getPrecision();
+          totalPrecision = Types.MAX_VARCHAR_LENGTH;
+          break;
         }
       }
 
-      return factory.createTypeWithNullability(
-          factory.createSqlType(SqlTypeName.VARCHAR, precision),
+      totalPrecision = totalPrecision > Types.MAX_VARCHAR_LENGTH ? Types.MAX_VARCHAR_LENGTH : totalPrecision;
+      boolean isNullable = isNullIfNull && isNullable(opBinding.collectOperandTypes());
+
+      return opBinding.getTypeFactory().createTypeWithNullability(
+          opBinding.getTypeFactory().createSqlType(SqlTypeName.VARCHAR, totalPrecision),
           isNullable);
     }
   }
@@ -441,23 +462,56 @@ public class TypeInferenceUtils {
     }
   }
 
-  private static class DrillPadTrimSqlReturnTypeInference implements SqlReturnTypeInference {
-    private static final DrillPadTrimSqlReturnTypeInference INSTANCE = new DrillPadTrimSqlReturnTypeInference();
+  private static class DrillPadSqlReturnTypeInference implements SqlReturnTypeInference {
+    private static final DrillPadSqlReturnTypeInference INSTANCE = new DrillPadSqlReturnTypeInference();
 
     @Override
     public RelDataType inferReturnType(SqlOperatorBinding opBinding) {
-      final RelDataTypeFactory factory = opBinding.getTypeFactory();
-      final SqlTypeName sqlTypeName = SqlTypeName.VARCHAR;
+      if (opBinding instanceof SqlCallBinding && (((SqlCallBinding) opBinding).operand(1) instanceof  SqlNumericLiteral)) {
+        int precision = ((SqlNumericLiteral) ((SqlCallBinding) opBinding).operand(1)).intValue(true);
+        RelDataType sqlType = opBinding.getTypeFactory().createSqlType(SqlTypeName.VARCHAR, Math.max(precision, 0));
+        return opBinding.getTypeFactory().createTypeWithNullability(sqlType, isNullable(opBinding.collectOperandTypes()));
+      }
 
-      for(int i = 0; i < opBinding.getOperandCount(); ++i) {
-        if(opBinding.getOperandType(i).isNullable()) {
-          return createCalciteTypeWithNullability(
-              factory, sqlTypeName, true);
-        }
+      return createCalciteTypeWithNullability(
+          opBinding.getTypeFactory(),
+          SqlTypeName.VARCHAR,
+          isNullable(opBinding.collectOperandTypes()));
+
+    }
+  }
+
+  private static class DrillTrimSqlReturnTypeInference implements SqlReturnTypeInference {
+    private static final DrillTrimSqlReturnTypeInference INSTANCE = new DrillTrimSqlReturnTypeInference();
+
+    @Override
+    public RelDataType inferReturnType(SqlOperatorBinding opBinding) {
+      return createCalciteTypeWithNullability(
+          opBinding.getTypeFactory(),
+          SqlTypeName.VARCHAR,
+          isNullable(opBinding.collectOperandTypes()));
+    }
+  }
+
+  private static class DrillSubstringSqlReturnTypeInference implements SqlReturnTypeInference {
+    private static final DrillSubstringSqlReturnTypeInference INSTANCE = new DrillSubstringSqlReturnTypeInference();
+
+    @Override
+    public RelDataType inferReturnType(SqlOperatorBinding opBinding) {
+      boolean isNullable = isNullable(opBinding.collectOperandTypes());
+
+      boolean isScalarString = isScalarStringType(opBinding.getOperandType(0).getSqlTypeName());
+      int precision = opBinding.getOperandType(0).getPrecision();
+
+      if (isScalarString && precision != RelDataType.PRECISION_NOT_SPECIFIED) {
+        RelDataType sqlType = opBinding.getTypeFactory().createSqlType(SqlTypeName.VARCHAR, precision);
+        return opBinding.getTypeFactory().createTypeWithNullability(sqlType, isNullable);
       }
 
       return createCalciteTypeWithNullability(
-          factory, sqlTypeName, false);
+          opBinding.getTypeFactory(),
+          SqlTypeName.VARCHAR,
+          isNullable);
     }
   }
 
@@ -511,21 +565,20 @@ public class TypeInferenceUtils {
     @Override
     public RelDataType inferReturnType(SqlOperatorBinding opBinding) {
       final RelDataTypeFactory factory = opBinding.getTypeFactory();
+      final boolean isNullable = opBinding.getOperandType(1).isNullable();
 
-      final SqlNode firstOperand = ((SqlCallBinding) opBinding).operand(0);
-      if(!(firstOperand instanceof SqlCharStringLiteral)) {
+      if (!(opBinding instanceof SqlCallBinding) || !(((SqlCallBinding) opBinding).operand(0) instanceof SqlCharStringLiteral)) {
         return createCalciteTypeWithNullability(factory,
             SqlTypeName.ANY,
-            opBinding.getOperandType(1).isNullable());
+            isNullable);
       }
 
-      final String part = ((SqlCharStringLiteral) firstOperand)
+      final String part = ((SqlCharStringLiteral) ((SqlCallBinding) opBinding).operand(0))
           .getNlsString()
           .getValue()
           .toUpperCase();
 
       final SqlTypeName sqlTypeName = getSqlTypeNameForTimeUnit(part);
-      final boolean isNullable = opBinding.getOperandType(1).isNullable();
       return createCalciteTypeWithNullability(
           factory,
           sqlTypeName,
@@ -598,15 +651,12 @@ public class TypeInferenceUtils {
     private static final DrillLeadLagSqlReturnTypeInference INSTANCE = new DrillLeadLagSqlReturnTypeInference();
     @Override
     public RelDataType inferReturnType(SqlOperatorBinding opBinding) {
-      return createCalciteTypeWithNullability(
-          opBinding.getTypeFactory(),
-          opBinding.getOperandType(0).getSqlTypeName(),
-          true);
+      return opBinding.getTypeFactory().createTypeWithNullability(opBinding.getOperandType(0), true);
     }
   }
 
-  private static class DrillFirstLastValueSqlReturnTypeInference implements SqlReturnTypeInference {
-    private static final DrillFirstLastValueSqlReturnTypeInference INSTANCE = new DrillFirstLastValueSqlReturnTypeInference();
+  private static class DrillSameSqlReturnTypeInference implements SqlReturnTypeInference {
+    private static final DrillSameSqlReturnTypeInference INSTANCE = new DrillSameSqlReturnTypeInference();
     @Override
     public RelDataType inferReturnType(SqlOperatorBinding opBinding) {
       return opBinding.getOperandType(0);
@@ -697,7 +747,7 @@ public class TypeInferenceUtils {
               TimeUnit.MONTH,
               SqlParserPos.ZERO));
     } else if (sqlTypeName == SqlTypeName.VARCHAR) {
-      type = typeFactory.createSqlType(sqlTypeName, TypeHelper.VARCHAR_DEFAULT_CAST_LEN);
+      type = typeFactory.createSqlType(sqlTypeName, Types.MAX_VARCHAR_LENGTH);
     } else {
       type = typeFactory.createSqlType(sqlTypeName);
     }
@@ -734,6 +784,21 @@ public class TypeInferenceUtils {
   }
 
   /**
+   * Checks if at least one of the operand types is nullable.
+   *
+   * @param operandTypes operand types
+   * @return true if one of the operands is nullable, false otherwise
+   */
+  private static boolean isNullable(List<RelDataType> operandTypes) {
+    for (RelDataType relDataType : operandTypes) {
+      if (relDataType.isNullable()) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  /**
    * This class is not intended to be instantiated
    */
   private TypeInferenceUtils() {

http://git-wip-us.apache.org/repos/asf/drill/blob/6741e68a/exec/java-exec/src/main/java/org/apache/drill/exec/planner/sql/handlers/FindLimit0Visitor.java
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/planner/sql/handlers/FindLimit0Visitor.java b/exec/java-exec/src/main/java/org/apache/drill/exec/planner/sql/handlers/FindLimit0Visitor.java
index 6df6e0d..d5216e7 100644
--- a/exec/java-exec/src/main/java/org/apache/drill/exec/planner/sql/handlers/FindLimit0Visitor.java
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/planner/sql/handlers/FindLimit0Visitor.java
@@ -1,4 +1,4 @@
-/**
+/*
  * 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
@@ -84,23 +84,27 @@ public class FindLimit0Visitor extends RelShuttleImpl {
    */
   public static DrillRel getDirectScanRelIfFullySchemaed(RelNode rel) {
     final List<RelDataTypeField> fieldList = rel.getRowType().getFieldList();
-    final List<SqlTypeName> columnTypes = Lists.newArrayList();
-    final List<TypeProtos.DataMode> dataModes = Lists.newArrayList();
+    final List<TypeProtos.MajorType> columnTypes = Lists.newArrayList();
+
 
     for (final RelDataTypeField field : fieldList) {
       final SqlTypeName sqlTypeName = field.getType().getSqlTypeName();
       if (!TYPES.contains(sqlTypeName)) {
         return null;
       } else {
-        columnTypes.add(sqlTypeName);
-        dataModes.add(field.getType().isNullable() ?
-            TypeProtos.DataMode.OPTIONAL : TypeProtos.DataMode.REQUIRED);
+        final TypeProtos.MajorType.Builder builder = TypeProtos.MajorType.newBuilder()
+            .setMode(field.getType().isNullable() ? TypeProtos.DataMode.OPTIONAL : TypeProtos.DataMode.REQUIRED)
+            .setMinorType(TypeInferenceUtils.getDrillTypeFromCalciteType(sqlTypeName));
+
+        if (TypeInferenceUtils.isScalarStringType(sqlTypeName)) {
+          builder.setPrecision(field.getType().getPrecision());
+        }
+
+        columnTypes.add(builder.build());
       }
     }
-
     final RelTraitSet traits = rel.getTraitSet().plus(DrillRel.DRILL_LOGICAL);
-    final RelDataTypeReader reader = new RelDataTypeReader(rel.getRowType().getFieldNames(), columnTypes,
-        dataModes);
+    final RelDataTypeReader reader = new RelDataTypeReader(rel.getRowType().getFieldNames(), columnTypes);
     return new DrillDirectScanRel(rel.getCluster(), traits,
         new DirectGroupScan(reader, ScanStats.ZERO_RECORD_TABLE), rel.getRowType());
   }
@@ -197,25 +201,18 @@ public class FindLimit0Visitor extends RelShuttleImpl {
   public static class RelDataTypeReader extends AbstractRecordReader {
 
     public final List<String> columnNames;
-    public final List<SqlTypeName> columnTypes;
-    public final List<TypeProtos.DataMode> dataModes;
+    public final List<TypeProtos.MajorType> columnTypes;
 
-    public RelDataTypeReader(List<String> columnNames, List<SqlTypeName> columnTypes,
-        List<TypeProtos.DataMode> dataModes) {
-      Preconditions.checkArgument(columnNames.size() == columnTypes.size() &&
-          columnTypes.size() == dataModes.size());
+    public RelDataTypeReader(List<String> columnNames, List<TypeProtos.MajorType> columnTypes) {
+      Preconditions.checkArgument(columnNames.size() == columnTypes.size(), "Number of columns and their types should match");
       this.columnNames = columnNames;
       this.columnTypes = columnTypes;
-      this.dataModes = dataModes;
     }
 
     @Override
     public void setup(OperatorContext context, OutputMutator output) throws ExecutionSetupException {
       for (int i = 0; i < columnNames.size(); i++) {
-        final TypeProtos.MajorType type = TypeProtos.MajorType.newBuilder()
-            .setMode(dataModes.get(i))
-            .setMinorType(TypeInferenceUtils.getDrillTypeFromCalciteType(columnTypes.get(i)))
-            .build();
+        final TypeProtos.MajorType type = columnTypes.get(i);
         final MaterializedField field = MaterializedField.create(columnNames.get(i), type);
         final Class vvClass = TypeHelper.getValueVectorClass(type.getMinorType(), type.getMode());
         try {


Mime
View raw message