| /* |
| * 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.hadoop.hive.ql.exec; |
| |
| import java.lang.reflect.Method; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.List; |
| |
| import org.apache.hadoop.hive.serde2.typeinfo.TypeInfo; |
| import org.apache.hadoop.hive.serde2.typeinfo.TypeInfoFactory; |
| import org.apache.hadoop.hive.serde2.typeinfo.TypeInfoUtils; |
| |
| /** |
| * The class implements the method resolution for operators like (+, -, *, %). |
| * The resolution logic is as follows: |
| * |
| * 1. If one of the parameters is a string, then it resolves to evaluate(double, |
| * double) 2. If one of the parameters is null, then it resolves to evaluate(T, |
| * T) where T is the other non-null parameter type. 3. If both of the parameters |
| * are null, then it resolves to evaluate(byte, byte) 4. Otherwise, it resolves |
| * to evaluate(T, T), where T is the type resulting from calling |
| * FunctionRegistry.getCommonClass() on the two arguments. |
| */ |
| public class NumericOpMethodResolver implements UDFMethodResolver { |
| |
| /** |
| * The udfclass for which resolution is needed. |
| */ |
| Class<? extends UDF> udfClass; |
| |
| /** |
| * Constuctor. |
| */ |
| public NumericOpMethodResolver(Class<? extends UDF> udfClass) { |
| this.udfClass = udfClass; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see |
| * org.apache.hadoop.hive.ql.exec.UDFMethodResolver#getEvalMethod(java.util |
| * .List) |
| */ |
| @Override |
| public Method getEvalMethod(List<TypeInfo> argTypeInfos) throws UDFArgumentException { |
| assert (argTypeInfos.size() == 2); |
| |
| List<TypeInfo> pTypeInfos = null; |
| List<TypeInfo> modArgTypeInfos = new ArrayList<TypeInfo>(); |
| |
| // If either argument is a string, we convert to a double or decimal because a number |
| // in string form should always be convertible into either of those |
| if (argTypeInfos.get(0).equals(TypeInfoFactory.stringTypeInfo) |
| || argTypeInfos.get(1).equals(TypeInfoFactory.stringTypeInfo)) { |
| |
| // Default is double, but if one of the sides is already in decimal we |
| // complete the operation in that type. |
| if (argTypeInfos.get(0).equals(TypeInfoFactory.decimalTypeInfo) |
| || argTypeInfos.get(1).equals(TypeInfoFactory.decimalTypeInfo)) { |
| modArgTypeInfos.add(TypeInfoFactory.decimalTypeInfo); |
| modArgTypeInfos.add(TypeInfoFactory.decimalTypeInfo); |
| } else { |
| modArgTypeInfos.add(TypeInfoFactory.doubleTypeInfo); |
| modArgTypeInfos.add(TypeInfoFactory.doubleTypeInfo); |
| } |
| } else { |
| // If it's a void, we change the type to a byte because once the types |
| // are run through getCommonClass(), a byte and any other type T will |
| // resolve to type T |
| for (int i = 0; i < 2; i++) { |
| if (argTypeInfos.get(i).equals(TypeInfoFactory.voidTypeInfo)) { |
| modArgTypeInfos.add(TypeInfoFactory.byteTypeInfo); |
| } else { |
| modArgTypeInfos.add(argTypeInfos.get(i)); |
| } |
| } |
| } |
| |
| TypeInfo commonType = FunctionRegistry.getCommonClass(modArgTypeInfos |
| .get(0), modArgTypeInfos.get(1)); |
| |
| if (commonType == null) { |
| throw new UDFArgumentException("Unable to find a common class between" |
| + "types " + modArgTypeInfos.get(0).getTypeName() + " and " |
| + modArgTypeInfos.get(1).getTypeName()); |
| } |
| |
| pTypeInfos = new ArrayList<TypeInfo>(); |
| pTypeInfos.add(commonType); |
| pTypeInfos.add(commonType); |
| |
| Method udfMethod = null; |
| |
| for (Method m : Arrays.asList(udfClass.getMethods())) { |
| if (m.getName().equals("evaluate")) { |
| |
| List<TypeInfo> argumentTypeInfos = TypeInfoUtils.getParameterTypeInfos( |
| m, pTypeInfos.size()); |
| if (argumentTypeInfos == null) { |
| // null means the method does not accept number of arguments passed. |
| continue; |
| } |
| |
| boolean match = (argumentTypeInfos.size() == pTypeInfos.size()); |
| |
| for (int i = 0; i < pTypeInfos.size() && match; i++) { |
| TypeInfo accepted = argumentTypeInfos.get(i); |
| if (!accepted.accept(pTypeInfos.get(i))) { |
| match = false; |
| } |
| } |
| |
| if (match) { |
| if (udfMethod != null) { |
| throw new AmbiguousMethodException(udfClass, argTypeInfos, |
| Arrays.asList(new Method[]{udfMethod, m})); |
| } else { |
| udfMethod = m; |
| } |
| } |
| } |
| } |
| |
| if (udfMethod == null) { |
| throw new NoMatchingMethodException(udfClass, argTypeInfos, null); |
| } |
| |
| return udfMethod; |
| } |
| } |