Return-Path: X-Original-To: archive-asf-public-internal@cust-asf2.ponee.io Delivered-To: archive-asf-public-internal@cust-asf2.ponee.io Received: from cust-asf.ponee.io (cust-asf.ponee.io [163.172.22.183]) by cust-asf2.ponee.io (Postfix) with ESMTP id 9F491200C83 for ; Sat, 13 May 2017 19:39:04 +0200 (CEST) Received: by cust-asf.ponee.io (Postfix) id 9DCCC160BB2; Sat, 13 May 2017 17:39:04 +0000 (UTC) Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by cust-asf.ponee.io (Postfix) with SMTP id 5907D160BBB for ; Sat, 13 May 2017 19:39:02 +0200 (CEST) Received: (qmail 85713 invoked by uid 500); 13 May 2017 17:38:59 -0000 Mailing-List: contact commits-help@drill.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: commits@drill.apache.org Delivered-To: mailing list commits@drill.apache.org Received: (qmail 85690 invoked by uid 99); 13 May 2017 17:38:59 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Sat, 13 May 2017 17:38:59 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id AF34EE0061; Sat, 13 May 2017 17:38:59 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: parthc@apache.org To: commits@drill.apache.org Date: Sat, 13 May 2017 17:39:00 -0000 Message-Id: <5f82c2f37ce74f6b9e2338fe08db0d42@git.apache.org> In-Reply-To: <1a9c05ec8b9b4196ade2b44de95d26bf@git.apache.org> References: <1a9c05ec8b9b4196ade2b44de95d26bf@git.apache.org> X-Mailer: ASF-Git Admin Mailer Subject: [2/7] drill git commit: DRILL-5419: Calculate return string length for literals & some string functions archived-at: Sat, 13 May 2017 17:39:04 -0000 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 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 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 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 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 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 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 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 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 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 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 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 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 logicalExpressions, FunctionAttributes attributes) { + if (attributes.getReturnValue().getType().getMinorType() == TypeProtos.MinorType.UNION) { + final Set 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 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 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 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 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 { final String castFuncName = CastFunctions.getCastFunc(MinorType.VARCHAR); final List 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 { // 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 { 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 { Iterator 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 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 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 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 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 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 fieldList = rel.getRowType().getFieldList(); - final List columnTypes = Lists.newArrayList(); - final List dataModes = Lists.newArrayList(); + final List 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 columnNames; - public final List columnTypes; - public final List dataModes; + public final List columnTypes; - public RelDataTypeReader(List columnNames, List columnTypes, - List dataModes) { - Preconditions.checkArgument(columnNames.size() == columnTypes.size() && - columnTypes.size() == dataModes.size()); + public RelDataTypeReader(List columnNames, List 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 {