Index: ql/src/test/queries/clientpositive/num_op_type_conv.q =================================================================== --- ql/src/test/queries/clientpositive/num_op_type_conv.q (revision 0) +++ ql/src/test/queries/clientpositive/num_op_type_conv.q (revision 0) @@ -0,0 +1,10 @@ +EXPLAIN SELECT null + 7, 1.0 - null, null + null, + CAST(21 AS BIGINT) % CAST(5 AS TINYINT), + CAST(21 AS BIGINT) % CAST(21 AS BIGINT), + 9 % "3" FROM src LIMIT 1; + +SELECT null + 7, 1.0 - null, null + null, + CAST(21 AS BIGINT) % CAST(5 AS TINYINT), + CAST(21 AS BIGINT) % CAST(21 AS BIGINT), + 9 % "3" FROM src LIMIT 1; + \ No newline at end of file Index: ql/src/test/results/clientpositive/num_op_type_conv.q.out =================================================================== --- ql/src/test/results/clientpositive/num_op_type_conv.q.out (revision 0) +++ ql/src/test/results/clientpositive/num_op_type_conv.q.out (revision 0) @@ -0,0 +1,67 @@ +PREHOOK: query: EXPLAIN SELECT null + 7, 1.0 - null, null + null, + CAST(21 AS BIGINT) % CAST(5 AS TINYINT), + CAST(21 AS BIGINT) % CAST(21 AS BIGINT), + 9 % "3" FROM src LIMIT 1 +PREHOOK: type: QUERY +POSTHOOK: query: EXPLAIN SELECT null + 7, 1.0 - null, null + null, + CAST(21 AS BIGINT) % CAST(5 AS TINYINT), + CAST(21 AS BIGINT) % CAST(21 AS BIGINT), + 9 % "3" FROM src LIMIT 1 +POSTHOOK: type: QUERY +ABSTRACT SYNTAX TREE: + (TOK_QUERY (TOK_FROM (TOK_TABREF src)) (TOK_INSERT (TOK_DESTINATION (TOK_DIR TOK_TMP_FILE)) (TOK_SELECT (TOK_SELEXPR (+ TOK_NULL 7)) (TOK_SELEXPR (- 1.0 TOK_NULL)) (TOK_SELEXPR (+ TOK_NULL TOK_NULL)) (TOK_SELEXPR (% (TOK_FUNCTION TOK_BIGINT 21) (TOK_FUNCTION TOK_TINYINT 5))) (TOK_SELEXPR (% (TOK_FUNCTION TOK_BIGINT 21) (TOK_FUNCTION TOK_BIGINT 21))) (TOK_SELEXPR (% 9 "3"))) (TOK_LIMIT 1))) + +STAGE DEPENDENCIES: + Stage-1 is a root stage + Stage-0 is a root stage + +STAGE PLANS: + Stage: Stage-1 + Map Reduce + Alias -> Map Operator Tree: + src + TableScan + alias: src + Select Operator + expressions: + expr: (null + 7) + type: int + expr: (1.0 - null) + type: double + expr: (null + null) + type: tinyint + expr: (UDFToLong(21) % UDFToByte(5)) + type: bigint + expr: (UDFToLong(21) % UDFToLong(21)) + type: bigint + expr: (9 % '3') + type: double + outputColumnNames: _col0, _col1, _col2, _col3, _col4, _col5 + Limit + File Output Operator + compressed: false + GlobalTableId: 0 + table: + input format: org.apache.hadoop.mapred.TextInputFormat + output format: org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat + + Stage: Stage-0 + Fetch Operator + limit: 1 + + +PREHOOK: query: SELECT null + 7, 1.0 - null, null + null, + CAST(21 AS BIGINT) % CAST(5 AS TINYINT), + CAST(21 AS BIGINT) % CAST(21 AS BIGINT), + 9 % "3" FROM src LIMIT 1 +PREHOOK: type: QUERY +PREHOOK: Input: default@src +PREHOOK: Output: file:/data/users/pyang/task/trunk/VENDOR.hive/trunk/build/ql/tmp/125386453/10000 +POSTHOOK: query: SELECT null + 7, 1.0 - null, null + null, + CAST(21 AS BIGINT) % CAST(5 AS TINYINT), + CAST(21 AS BIGINT) % CAST(21 AS BIGINT), + 9 % "3" FROM src LIMIT 1 +POSTHOOK: type: QUERY +POSTHOOK: Input: default@src +POSTHOOK: Output: file:/data/users/pyang/task/trunk/VENDOR.hive/trunk/build/ql/tmp/125386453/10000 +NULL NULL NULL 1 0 0.0 Index: ql/src/java/org/apache/hadoop/hive/ql/exec/NumericOpMethodResolver.java =================================================================== --- ql/src/java/org/apache/hadoop/hive/ql/exec/NumericOpMethodResolver.java (revision 4576) +++ ql/src/java/org/apache/hadoop/hive/ql/exec/NumericOpMethodResolver.java (working copy) @@ -29,10 +29,17 @@ import org.apache.hadoop.hive.serde2.typeinfo.TypeInfoUtils; /** - * The class implements the method resolution for operators like (+, -, *, /, %, |, &, ^). The - * resolution logic is as follows: - * 1. The resolver first tries to find an exact parameter match. - * 2. If 1 fails then it returns the evaluate(Double, Double) method. + * 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 { @@ -53,27 +60,44 @@ */ @Override public Method getEvalMethod(List argTypeInfos) - throws AmbiguousMethodException { + throws AmbiguousMethodException, UDFArgumentException { assert(argTypeInfos.size() == 2); List pTypeInfos = null; - if (argTypeInfos.get(0).equals(TypeInfoFactory.voidTypeInfo) || - argTypeInfos.get(1).equals(TypeInfoFactory.voidTypeInfo)) { - pTypeInfos = new ArrayList(); - pTypeInfos.add(TypeInfoFactory.doubleTypeInfo); - pTypeInfos.add(TypeInfoFactory.doubleTypeInfo); - } else if (argTypeInfos.get(0).equals(TypeInfoFactory.stringTypeInfo) || + List modArgTypeInfos = new ArrayList(); + + // If either argument is a string, we convert to a double because a number + // in string form should always be convertible into a double + if (argTypeInfos.get(0).equals(TypeInfoFactory.stringTypeInfo) || argTypeInfos.get(1).equals(TypeInfoFactory.stringTypeInfo) ) { - pTypeInfos = new ArrayList(); - pTypeInfos.add(TypeInfoFactory.doubleTypeInfo); - pTypeInfos.add(TypeInfoFactory.doubleTypeInfo); - } else if (argTypeInfos.get(0) == argTypeInfos.get(1)) { - pTypeInfos = argTypeInfos; + modArgTypeInfos.add(TypeInfoFactory.doubleTypeInfo); + modArgTypeInfos.add(TypeInfoFactory.doubleTypeInfo); } else { - pTypeInfos = new ArrayList(); - pTypeInfos.add(TypeInfoFactory.doubleTypeInfo); - pTypeInfos.add(TypeInfoFactory.doubleTypeInfo); + // 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(); + pTypeInfos.add(commonType); + pTypeInfos.add(commonType); Method udfMethod = null; Index: ql/src/java/org/apache/hadoop/hive/ql/udf/UDFOPDivide.java =================================================================== --- ql/src/java/org/apache/hadoop/hive/ql/udf/UDFOPDivide.java (revision 4576) +++ ql/src/java/org/apache/hadoop/hive/ql/udf/UDFOPDivide.java (working copy) @@ -37,12 +37,17 @@ " > SELECT 3 _FUNC_ 2 FROM src LIMIT 1;\n" + " 1.5" ) +/** + * Note that in SQL, the return type of divide is not necessarily the same + * as the parameters. For example, 3 / 2 = 1.5, not 1. To follow SQL, we always + * return a double for divide. + */ public class UDFOPDivide extends UDF { private static Log LOG = LogFactory.getLog("org.apache.hadoop.hive.ql.udf.UDFOPDivide"); protected DoubleWritable doubleWritable = new DoubleWritable(); - + public DoubleWritable evaluate(DoubleWritable a, DoubleWritable b) { // LOG.info("Get input " + a.getClass() + ":" + a + " " + b.getClass() + ":" + b); if ((a == null) || (b == null)) Index: ql/src/java/org/apache/hadoop/hive/ql/exec/ComparisonOpMethodResolver.java =================================================================== --- ql/src/java/org/apache/hadoop/hive/ql/exec/ComparisonOpMethodResolver.java (revision 4576) +++ ql/src/java/org/apache/hadoop/hive/ql/exec/ComparisonOpMethodResolver.java (working copy) @@ -29,11 +29,13 @@ import org.apache.hadoop.hive.serde2.typeinfo.TypeInfoUtils; /** - * The class implements the method resolution for overloaded comparison operators. The - * resolution logic is as follows: - * 1. The resolver first tries to find an exact parameter match. - * 2. If 1 fails and any of the parameters is a date, it converts the other to the date. - * 3. If 1 and 3 fail then it returns the evaluate(Double, Double) method. + * The class implements the method resolution for operators like + * (> < <= >= = <>). The resolution logic is as follows: + * 1. If one of the parameters is null, then it resolves to + * evaluate(Double, Double) + * 2. If both of the parameters are of type T, then it resolves to + * evaluate(T, T) + * 3. If 1 and 2 fails then it resolves to evaluate(Double, Double). */ public class ComparisonOpMethodResolver implements UDFMethodResolver { Index: ql/src/java/org/apache/hadoop/hive/ql/exec/UDFMethodResolver.java =================================================================== --- ql/src/java/org/apache/hadoop/hive/ql/exec/UDFMethodResolver.java (revision 4576) +++ ql/src/java/org/apache/hadoop/hive/ql/exec/UDFMethodResolver.java (working copy) @@ -43,5 +43,5 @@ * function signature. */ public Method getEvalMethod(List argClasses) - throws AmbiguousMethodException; + throws AmbiguousMethodException, UDFArgumentException; }