| /* |
| * 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 java.util.ArrayList; |
| import java.util.List; |
| |
| import com.google.common.collect.Lists; |
| import org.apache.drill.common.FunctionNames; |
| import org.apache.drill.common.expression.ErrorCollector; |
| import org.apache.drill.common.expression.ErrorCollectorImpl; |
| import org.apache.drill.common.expression.ExpressionPosition; |
| import org.apache.drill.common.expression.FunctionCall; |
| import org.apache.drill.common.expression.IfExpression; |
| import org.apache.drill.common.expression.IfExpression.IfCondition; |
| import org.apache.drill.common.expression.LogicalExpression; |
| import org.apache.drill.common.expression.ValueExpressions.IntExpression; |
| import org.apache.drill.common.types.TypeProtos; |
| import org.apache.drill.common.types.TypeProtos.MajorType; |
| import org.apache.drill.common.types.TypeProtos.MinorType; |
| import org.apache.drill.exec.expr.ClassGenerator.HoldingContainer; |
| import org.apache.drill.exec.expr.ExpressionTreeMaterializer; |
| import org.apache.drill.exec.expr.HoldingContainerExpression; |
| |
| public class FunctionGenerationHelper implements FunctionNames { |
| |
| /** |
| * Finds ordering comparator ("compare_to...") FunctionHolderExpression with |
| * a specified ordering for NULL (and considering NULLS <i>equal</i>). |
| * @param null_high whether NULL should compare as the lowest value (if |
| * {@code false}) or the highest value (if {@code true}) |
| * @param left ... |
| * @param right ... |
| * @param functionLookupContext ... |
| * @return |
| * FunctionHolderExpression containing the found function implementation |
| */ |
| public static LogicalExpression getOrderingComparator( |
| boolean null_high, |
| HoldingContainer left, |
| HoldingContainer right, |
| FunctionLookupContext functionLookupContext) { |
| final String comparator_name = |
| null_high ? COMPARE_TO_NULLS_HIGH : COMPARE_TO_NULLS_LOW; |
| |
| if ( ! isComparableType(left.getMajorType() ) |
| || ! isComparableType(right.getMajorType() ) |
| ){ |
| throw new UnsupportedOperationException( |
| formatCanNotCompareMsg(left.getMajorType(), right.getMajorType())); |
| } |
| LogicalExpression comparisonFunctionExpression = getFunctionExpression(comparator_name, left, right); |
| |
| ErrorCollector collector = new ErrorCollectorImpl(); |
| if (!isUnionType(left.getMajorType()) && !isUnionType(right.getMajorType())) { |
| return ExpressionTreeMaterializer.materialize(comparisonFunctionExpression, null, collector, functionLookupContext); |
| } else { |
| LogicalExpression typeComparisonFunctionExpression = getTypeComparisonFunction(comparisonFunctionExpression, left, right); |
| return ExpressionTreeMaterializer.materialize(typeComparisonFunctionExpression, null, collector, functionLookupContext); |
| } |
| } |
| |
| private static boolean isUnionType(MajorType majorType) { |
| return majorType.getMinorType() == MinorType.UNION; |
| } |
| |
| /** |
| * Finds ordering comparator ("compare_to...") FunctionHolderExpression with |
| * a "NULL high" ordering (and considering NULLS <i>equal</i>). |
| * @param left ... |
| * @param right ... |
| * @param registry ... |
| * @return FunctionHolderExpression containing the function implementation |
| */ |
| public static LogicalExpression getOrderingComparatorNullsHigh( |
| HoldingContainer left, |
| HoldingContainer right, |
| FunctionLookupContext registry) { |
| return getOrderingComparator(true, left, right, registry); |
| } |
| |
| private static LogicalExpression getFunctionExpression(String name, HoldingContainer... args) { |
| List<LogicalExpression> argExpressions = new ArrayList<LogicalExpression>(args.length); |
| for (HoldingContainer c : args) { |
| argExpressions.add(new HoldingContainerExpression(c)); |
| } |
| |
| return new FunctionCall(name, argExpressions, ExpressionPosition.UNKNOWN); |
| } |
| |
| /** |
| * Wraps the comparison function in an If-statement which compares the types first, evaluating the comaprison function only |
| * if the types are equivialent |
| * |
| * @param comparisonFunction |
| * @param args |
| * @return |
| */ |
| private static LogicalExpression getTypeComparisonFunction(LogicalExpression comparisonFunction, HoldingContainer... args) { |
| List<LogicalExpression> argExpressions = Lists.newArrayList(); |
| List<MajorType> argTypes = Lists.newArrayList(); |
| for (HoldingContainer c : args) { |
| argTypes.add(c.getMajorType()); |
| argExpressions.add(new HoldingContainerExpression(c)); |
| } |
| FunctionCall call = new FunctionCall("compareType", argExpressions, ExpressionPosition.UNKNOWN); |
| |
| List<LogicalExpression> newArgs = Lists.newArrayList(); |
| newArgs.add(call); |
| newArgs.add(new IntExpression(0, ExpressionPosition.UNKNOWN)); |
| FunctionCall notEqual = new FunctionCall(FunctionNames.NE, newArgs, ExpressionPosition.UNKNOWN); |
| |
| IfExpression.IfCondition ifCondition = new IfCondition(notEqual, call); |
| IfExpression ifExpression = IfExpression.newBuilder().setIfCondition(ifCondition).setElse(comparisonFunction).build(); |
| return ifExpression; |
| } |
| |
| private static final void appendType(MajorType mt, StringBuilder sb) { |
| sb.append(mt.getMinorType().name()); |
| sb.append(":"); |
| sb.append(mt.getMode().name()); |
| } |
| |
| private static boolean isComparableType(MajorType type) { |
| if (type.getMinorType() == MinorType.MAP || |
| type.getMinorType() == MinorType.LIST || |
| type.getMode() == TypeProtos.DataMode.REPEATED ) { |
| return false; |
| } else { |
| return true; |
| } |
| } |
| |
| private static String formatCanNotCompareMsg(MajorType left, MajorType right) { |
| StringBuilder sb = new StringBuilder(); |
| sb.append("Map, Array, Union or repeated scalar type should not be used in group by, order by or in a comparison operator. Drill does not support compare between "); |
| |
| appendType(left, sb); |
| sb.append(" and "); |
| appendType(right, sb); |
| sb.append("."); |
| |
| return sb.toString(); |
| } |
| } |