From a92c1514a9a26715c53f67ba9b4c693c5df040be Mon Sep 17 00:00:00 2001 From: Xiaobing Zhou Date: Wed, 4 Feb 2015 13:52:17 -0800 Subject: [PATCH] HIVE-9518: Implement MONTHS_BETWEEN aligned with Oracle one --- .../hadoop/hive/ql/exec/FunctionRegistry.java | 1 + .../hive/ql/udf/generic/GenericUDFDateDiff.java | 136 +---------- .../ql/udf/generic/GenericUDFDateDiffBase.java | 162 +++++++++++++ .../ql/udf/generic/GenericUDFMonthsBetween.java | 106 +++++++++ .../hive/ql/udf/TestGenericUDFMonthsBetween.java | 261 +++++++++++++++++++++ .../clientnegative/udf_months_between_error1.q | 1 + .../clientnegative/udf_months_between_error2.q | 1 + .../clientnegative/udf_months_between_error3.q | 1 + .../clientnegative/udf_months_between_error4.q | 1 + .../clientnegative/udf_months_between_error5.q | 1 + .../clientnegative/udf_months_between_error6.q | 1 + .../clientnegative/udf_months_between_error7.q | 1 + .../clientnegative/udf_months_between_error8.q | 1 + .../clientnegative/udf_months_between_error9.q | 1 + .../queries/clientpositive/udf_months_between.q | 70 ++++++ .../clientnegative/udf_months_between_error1.q.out | 1 + .../clientnegative/udf_months_between_error2.q.out | 1 + .../clientnegative/udf_months_between_error3.q.out | 1 + .../clientnegative/udf_months_between_error4.q.out | 1 + .../clientnegative/udf_months_between_error5.q.out | 1 + .../clientnegative/udf_months_between_error6.q.out | 1 + .../clientnegative/udf_months_between_error7.q.out | 1 + .../clientnegative/udf_months_between_error8.q.out | 1 + .../clientnegative/udf_months_between_error9.q.out | 1 + .../results/clientpositive/show_functions.q.out | 2 + .../clientpositive/udf_months_between.q.out | 216 +++++++++++++++++ 26 files changed, 849 insertions(+), 123 deletions(-) create mode 100644 ql/src/java/org/apache/hadoop/hive/ql/udf/generic/GenericUDFDateDiffBase.java create mode 100644 ql/src/java/org/apache/hadoop/hive/ql/udf/generic/GenericUDFMonthsBetween.java create mode 100644 ql/src/test/org/apache/hadoop/hive/ql/udf/TestGenericUDFMonthsBetween.java create mode 100644 ql/src/test/queries/clientnegative/udf_months_between_error1.q create mode 100644 ql/src/test/queries/clientnegative/udf_months_between_error2.q create mode 100644 ql/src/test/queries/clientnegative/udf_months_between_error3.q create mode 100644 ql/src/test/queries/clientnegative/udf_months_between_error4.q create mode 100644 ql/src/test/queries/clientnegative/udf_months_between_error5.q create mode 100644 ql/src/test/queries/clientnegative/udf_months_between_error6.q create mode 100644 ql/src/test/queries/clientnegative/udf_months_between_error7.q create mode 100644 ql/src/test/queries/clientnegative/udf_months_between_error8.q create mode 100644 ql/src/test/queries/clientnegative/udf_months_between_error9.q create mode 100644 ql/src/test/queries/clientpositive/udf_months_between.q create mode 100644 ql/src/test/results/clientnegative/udf_months_between_error1.q.out create mode 100644 ql/src/test/results/clientnegative/udf_months_between_error2.q.out create mode 100644 ql/src/test/results/clientnegative/udf_months_between_error3.q.out create mode 100644 ql/src/test/results/clientnegative/udf_months_between_error4.q.out create mode 100644 ql/src/test/results/clientnegative/udf_months_between_error5.q.out create mode 100644 ql/src/test/results/clientnegative/udf_months_between_error6.q.out create mode 100644 ql/src/test/results/clientnegative/udf_months_between_error7.q.out create mode 100644 ql/src/test/results/clientnegative/udf_months_between_error8.q.out create mode 100644 ql/src/test/results/clientnegative/udf_months_between_error9.q.out create mode 100644 ql/src/test/results/clientpositive/udf_months_between.q.out diff --git a/ql/src/java/org/apache/hadoop/hive/ql/exec/FunctionRegistry.java b/ql/src/java/org/apache/hadoop/hive/ql/exec/FunctionRegistry.java index bfb4dc2..ee76d14 100644 --- a/ql/src/java/org/apache/hadoop/hive/ql/exec/FunctionRegistry.java +++ b/ql/src/java/org/apache/hadoop/hive/ql/exec/FunctionRegistry.java @@ -277,6 +277,7 @@ registerGenericUDF("date_add", GenericUDFDateAdd.class); registerGenericUDF("date_sub", GenericUDFDateSub.class); registerGenericUDF("datediff", GenericUDFDateDiff.class); + registerGenericUDF("months_between", GenericUDFMonthsBetween.class); registerGenericUDF("add_months", GenericUDFAddMonths.class); registerUDF("get_json_object", UDFJson.class, false); diff --git a/ql/src/java/org/apache/hadoop/hive/ql/udf/generic/GenericUDFDateDiff.java b/ql/src/java/org/apache/hadoop/hive/ql/udf/generic/GenericUDFDateDiff.java index 1ecd835..358a30c 100644 --- a/ql/src/java/org/apache/hadoop/hive/ql/udf/generic/GenericUDFDateDiff.java +++ b/ql/src/java/org/apache/hadoop/hive/ql/udf/generic/GenericUDFDateDiff.java @@ -17,31 +17,18 @@ */ package org.apache.hadoop.hive.ql.udf.generic; -import java.sql.Timestamp; -import java.text.ParseException; -import java.text.SimpleDateFormat; import java.util.Date; -import java.util.TimeZone; import org.apache.hadoop.hive.ql.exec.Description; import org.apache.hadoop.hive.ql.exec.UDFArgumentException; -import org.apache.hadoop.hive.ql.exec.UDFArgumentLengthException; -import org.apache.hadoop.hive.ql.exec.UDFArgumentTypeException; import org.apache.hadoop.hive.ql.exec.vector.VectorizedExpressions; import org.apache.hadoop.hive.ql.exec.vector.expressions.VectorUDFDateDiffColCol; import org.apache.hadoop.hive.ql.exec.vector.expressions.VectorUDFDateDiffColScalar; import org.apache.hadoop.hive.ql.exec.vector.expressions.VectorUDFDateDiffScalarCol; -import org.apache.hadoop.hive.ql.metadata.HiveException; -import org.apache.hadoop.hive.serde2.io.DateWritable; -import org.apache.hadoop.hive.serde2.io.TimestampWritable; import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector; -import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspectorConverters; -import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspectorConverters.Converter; -import org.apache.hadoop.hive.serde2.objectinspector.PrimitiveObjectInspector; -import org.apache.hadoop.hive.serde2.objectinspector.PrimitiveObjectInspector.PrimitiveCategory; import org.apache.hadoop.hive.serde2.objectinspector.primitive.PrimitiveObjectInspectorFactory; -import org.apache.hadoop.hive.serde2.objectinspector.primitive.PrimitiveObjectInspectorConverter.TimestampConverter; import org.apache.hadoop.io.IntWritable; +import org.apache.hadoop.io.WritableComparable; /** * UDFDateDiff. @@ -60,132 +47,35 @@ + " > SELECT _FUNC_('2009-07-30', '2009-07-31') FROM src LIMIT 1;\n" + " 1") @VectorizedExpressions({VectorUDFDateDiffColScalar.class, VectorUDFDateDiffColCol.class, VectorUDFDateDiffScalarCol.class}) -public class GenericUDFDateDiff extends GenericUDF { - private transient SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd"); - private transient Converter inputConverter1; - private transient Converter inputConverter2; - private IntWritable output = new IntWritable(); - private transient PrimitiveCategory inputType1; - private transient PrimitiveCategory inputType2; - private IntWritable result = new IntWritable(); +public class GenericUDFDateDiff extends GenericUDFDateDiffBase { + private transient IntWritable output; public GenericUDFDateDiff() { - formatter.setTimeZone(TimeZone.getTimeZone("UTC")); + udfName = "datediff"; } @Override public ObjectInspector initialize(ObjectInspector[] arguments) throws UDFArgumentException { - if (arguments.length != 2) { - throw new UDFArgumentLengthException( - "datediff() requires 2 argument, got " + arguments.length); - } - inputConverter1 = checkArguments(arguments, 0); - inputConverter2 = checkArguments(arguments, 1); - inputType1 = ((PrimitiveObjectInspector) arguments[0]).getPrimitiveCategory(); - inputType2 = ((PrimitiveObjectInspector) arguments[1]).getPrimitiveCategory(); - ObjectInspector outputOI = PrimitiveObjectInspectorFactory.writableIntObjectInspector; - return outputOI; + output = new IntWritable(); + return super.initialize(arguments); } @Override - public IntWritable evaluate(DeferredObject[] arguments) throws HiveException { - output = evaluate(convertToDate(inputType1, inputConverter1, arguments[0]), - convertToDate(inputType2, inputConverter2, arguments[1])); - return output; + protected ObjectInspector getOutputOI() { + return PrimitiveObjectInspectorFactory.writableIntObjectInspector; } @Override - public String getDisplayString(String[] children) { - StringBuilder sb = new StringBuilder(); - sb.append("datediff("); - if (children.length > 0) { - sb.append(children[0]); - for (int i = 1; i < children.length; i++) { - sb.append(", "); - sb.append(children[i]); - } - } - sb.append(")"); - return sb.toString(); - } + protected WritableComparable evaluate(Date date1, Date date2) { - private Date convertToDate(PrimitiveCategory inputType, Converter converter, DeferredObject argument) - throws HiveException { - assert(converter != null); - assert(argument != null); - if (argument.get() == null) { - return null; - } - Date date = new Date(); - switch (inputType) { - case STRING: - case VARCHAR: - case CHAR: - String dateString = converter.convert(argument.get()).toString(); - try { - date = formatter.parse(dateString); - } catch (ParseException e) { - return null; - } - break; - case TIMESTAMP: - Timestamp ts = ((TimestampWritable) converter.convert(argument.get())) - .getTimestamp(); - date.setTime(ts.getTime()); - break; - case DATE: - DateWritable dw = (DateWritable) converter.convert(argument.get()); - date = dw.get(); - break; - default: - throw new UDFArgumentException( - "TO_DATE() only takes STRING/TIMESTAMP/DATEWRITABLE types, got " + inputType); - } - return date; - } - - private Converter checkArguments(ObjectInspector[] arguments, int i) throws UDFArgumentException { - if (arguments[i].getCategory() != ObjectInspector.Category.PRIMITIVE) { - throw new UDFArgumentTypeException(0, - "Only primitive type arguments are accepted but " - + arguments[i].getTypeName() + " is passed. as first arguments"); - } - PrimitiveCategory inputType = ((PrimitiveObjectInspector) arguments[i]).getPrimitiveCategory(); - Converter converter; - switch (inputType) { - case STRING: - case VARCHAR: - case CHAR: - converter = ObjectInspectorConverters.getConverter( - (PrimitiveObjectInspector) arguments[i], - PrimitiveObjectInspectorFactory.writableStringObjectInspector); - break; - case TIMESTAMP: - converter = new TimestampConverter((PrimitiveObjectInspector) arguments[i], - PrimitiveObjectInspectorFactory.writableTimestampObjectInspector); - break; - case DATE: - converter = ObjectInspectorConverters.getConverter((PrimitiveObjectInspector)arguments[i], - PrimitiveObjectInspectorFactory.writableDateObjectInspector); - break; - default: - throw new UDFArgumentException( - " DATEDIFF() only takes STRING/TIMESTAMP/DATEWRITABLE types as " + (i + 1) - + "-th argument, got " + inputType); - } - return converter; - } - - private IntWritable evaluate(Date date, Date date2) { - - if (date == null || date2 == null) { + if (date1 == null || date2 == null) { return null; } // NOTE: This implementation avoids the extra-second problem // by comparing with UTC epoch and integer division. // 86400 is the number of seconds in a day - long diffInMilliSeconds = date.getTime() - date2.getTime(); - result.set((int) (diffInMilliSeconds / (86400 * 1000))); - return result; + long diffInMilliSeconds = date1.getTime() - date2.getTime(); + output.set((int) (diffInMilliSeconds / (86400 * 1000))); + return output; } } diff --git a/ql/src/java/org/apache/hadoop/hive/ql/udf/generic/GenericUDFDateDiffBase.java b/ql/src/java/org/apache/hadoop/hive/ql/udf/generic/GenericUDFDateDiffBase.java new file mode 100644 index 0000000..3c2dbfe --- /dev/null +++ b/ql/src/java/org/apache/hadoop/hive/ql/udf/generic/GenericUDFDateDiffBase.java @@ -0,0 +1,162 @@ +/** + * 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.udf.generic; + +import java.sql.Timestamp; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.TimeZone; + +import org.apache.hadoop.hive.ql.exec.UDFArgumentException; +import org.apache.hadoop.hive.ql.exec.UDFArgumentLengthException; +import org.apache.hadoop.hive.ql.exec.UDFArgumentTypeException; +import org.apache.hadoop.hive.ql.metadata.HiveException; +import org.apache.hadoop.hive.serde2.io.DateWritable; +import org.apache.hadoop.hive.serde2.io.TimestampWritable; +import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector; +import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspectorConverters; +import org.apache.hadoop.hive.serde2.objectinspector.PrimitiveObjectInspector; +import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspectorConverters.Converter; +import org.apache.hadoop.hive.serde2.objectinspector.PrimitiveObjectInspector.PrimitiveCategory; +import org.apache.hadoop.hive.serde2.objectinspector.primitive.PrimitiveObjectInspectorFactory; +import org.apache.hadoop.hive.serde2.objectinspector.primitive.PrimitiveObjectInspectorConverter.TimestampConverter; +import org.apache.hadoop.io.WritableComparable; + +public abstract class GenericUDFDateDiffBase extends GenericUDF { + + private transient SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd"); + private transient Converter inputConverter1; + private transient Converter inputConverter2; + private transient PrimitiveCategory inputType1; + private transient PrimitiveCategory inputType2; + protected String udfName = ""; + + public GenericUDFDateDiffBase() { + formatter.setTimeZone(TimeZone.getTimeZone("UTC")); + } + + @Override + public ObjectInspector initialize(ObjectInspector[] arguments) throws UDFArgumentException { + if (arguments.length != 2) { + throw new UDFArgumentLengthException(udfName + "() requires 2 argument, got " + + arguments.length); + } + inputConverter1 = checkArguments(arguments, 0); + inputConverter2 = checkArguments(arguments, 1); + inputType1 = ((PrimitiveObjectInspector) arguments[0]).getPrimitiveCategory(); + inputType2 = ((PrimitiveObjectInspector) arguments[1]).getPrimitiveCategory(); + return getOutputOI(); + } + + protected abstract ObjectInspector getOutputOI(); + + @Override + public WritableComparable evaluate(DeferredObject[] arguments) throws HiveException { + WritableComparable output = evaluate( + convertToDate(inputType1, inputConverter1, arguments[0]), + convertToDate(inputType2, inputConverter2, arguments[1])); + return output; + } + + protected abstract WritableComparable evaluate(Date date1, Date date2); + + @Override + public String getDisplayString(String[] children) { + StringBuilder sb = new StringBuilder(); + sb.append(udfName + "("); + if (children.length > 0) { + sb.append(children[0]); + for (int i = 1; i < children.length; i++) { + sb.append(", "); + sb.append(children[i]); + } + } + sb.append(")"); + return sb.toString(); + } + + private Date convertToDate(PrimitiveCategory inputType, Converter converter, + DeferredObject argument) throws HiveException { + assert (converter != null); + assert (argument != null); + if (argument.get() == null) { + return null; + } + Date date = new Date(); + switch (inputType) { + case STRING: + case VARCHAR: + case CHAR: + String dateString = converter.convert(argument.get()).toString(); + try { + date = formatter.parse(dateString); + } catch (ParseException e) { + return null; + } + break; + case TIMESTAMP: + Timestamp ts = ((TimestampWritable) converter.convert(argument.get())).getTimestamp(); + date.setTime(ts.getTime()); + break; + case DATE: + DateWritable dw = (DateWritable) converter.convert(argument.get()); + date = dw.get(); + break; + default: + throw new UDFArgumentException( + "TO_DATE() only takes STRING/TIMESTAMP/DATEWRITABLE types, got " + inputType); + } + return date; + } + + private Converter checkArguments(ObjectInspector[] arguments, int i) + throws UDFArgumentException { + if (arguments[i].getCategory() != ObjectInspector.Category.PRIMITIVE) { + throw new UDFArgumentTypeException(0, + "Only primitive type arguments are accepted but " + + arguments[i].getTypeName() + " is passed. as first arguments"); + } + PrimitiveCategory inputType = ((PrimitiveObjectInspector) arguments[i]).getPrimitiveCategory(); + Converter converter; + switch (inputType) { + case STRING: + case VARCHAR: + case CHAR: + converter = ObjectInspectorConverters.getConverter( + (PrimitiveObjectInspector) arguments[i], + PrimitiveObjectInspectorFactory.writableStringObjectInspector); + break; + case TIMESTAMP: + converter = new TimestampConverter((PrimitiveObjectInspector) arguments[i], + PrimitiveObjectInspectorFactory.writableTimestampObjectInspector); + break; + case DATE: + converter = ObjectInspectorConverters.getConverter((PrimitiveObjectInspector)arguments[i], + PrimitiveObjectInspectorFactory.writableDateObjectInspector); + break; + default: + throw new UDFArgumentException( + " " + udfName.toUpperCase() + "() only takes STRING/TIMESTAMP/DATEWRITABLE types as " + (i + 1) + + "-th argument, got " + inputType); + } + return converter; + } + +} diff --git a/ql/src/java/org/apache/hadoop/hive/ql/udf/generic/GenericUDFMonthsBetween.java b/ql/src/java/org/apache/hadoop/hive/ql/udf/generic/GenericUDFMonthsBetween.java new file mode 100644 index 0000000..5035ed3 --- /dev/null +++ b/ql/src/java/org/apache/hadoop/hive/ql/udf/generic/GenericUDFMonthsBetween.java @@ -0,0 +1,106 @@ +/** + * 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.udf.generic; + +import java.math.BigDecimal; +import java.util.Calendar; +import java.util.Date; +import java.util.TimeZone; + +import org.apache.hadoop.hive.ql.exec.Description; +import org.apache.hadoop.hive.ql.exec.UDFArgumentException; +import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector; +import org.apache.hadoop.hive.serde2.objectinspector.primitive.PrimitiveObjectInspectorFactory; +import org.apache.hadoop.hive.serde2.io.DoubleWritable; +import org.apache.hadoop.io.WritableComparable; + +/** + * UDFMonthsBetween. + * + * Calculate number of months between dates date1 and date2. The time part of + * the string will be ignored. If dateString1 is earlier than dateString2, then + * the result can be negative. + * + */ +@Description(name = "months_between", +value = "_FUNC_(date1, date2) - returns number of months between dates date1 and date2", +extended = "If date1 is later than date2, then the result is positive. " + + "If date1 is earlier than date2, then the result is negative." + + "If date1 and date2 are either the same days of the month or both last days of months, " + + "then the result is always an integer." + + "otherwise, it will calculate the fractional portion of the result based on a 31-day " + + "month and considers the difference in time components date1 and date2.\n" + + "date1 and date2 are strings in the format " + + "'yyyy-MM-dd HH:mm:ss' or 'yyyy-MM-dd'. The time parts are ignored.\n" + + "Example:\n " + + " > SELECT _FUNC_('02-02-1995', '01-01-1995') FROM src LIMIT 1;\n" + " 1.03225806") +public class GenericUDFMonthsBetween extends GenericUDFDateDiffBase { + private transient DoubleWritable output; + + public GenericUDFMonthsBetween() { + udfName = "months_between"; + } + + @Override + public ObjectInspector initialize(ObjectInspector[] arguments) throws UDFArgumentException { + output = new DoubleWritable(); + return super.initialize(arguments); + } + + @Override + protected ObjectInspector getOutputOI() { + return PrimitiveObjectInspectorFactory.writableDoubleObjectInspector; + } + + @Override + protected WritableComparable evaluate(Date date1, Date date2) { + + if (date1 == null || date2 == null) { + return null; + } + + Calendar cal1 = Calendar.getInstance(TimeZone.getTimeZone("UTC")); + cal1.setTime(date1); + + Calendar cal2 = Calendar.getInstance(TimeZone.getTimeZone("UTC")); + cal2.setTime(date2); + + double monthsBetween = 0; + // difference in month for years + monthsBetween = (cal1.get(Calendar.YEAR) - cal2.get(Calendar.YEAR)) * 12; + // difference in month for months + monthsBetween += cal1.get(Calendar.MONTH) - cal2.get(Calendar.MONTH); + + // difference in month for days + //if both date1 and date2 are the same days or the last days of the month + if (cal1.get(Calendar.DAY_OF_MONTH) == cal2.get(Calendar.DAY_OF_MONTH) || + (cal1.get(Calendar.DAY_OF_MONTH) == cal1.getActualMaximum(Calendar.DAY_OF_MONTH) + && cal2.get(Calendar.DAY_OF_MONTH) == cal2.getActualMaximum(Calendar.DAY_OF_MONTH))) { + monthsBetween += 0; + } else { + int dayDiff = cal1.get(Calendar.DAY_OF_MONTH) - cal2.get(Calendar.DAY_OF_MONTH); + monthsBetween += (dayDiff / 31d); + } + + Double result = new BigDecimal(monthsBetween).setScale(8, BigDecimal.ROUND_HALF_UP) + .doubleValue(); + output.set(result); + return output; + } +} diff --git a/ql/src/test/org/apache/hadoop/hive/ql/udf/TestGenericUDFMonthsBetween.java b/ql/src/test/org/apache/hadoop/hive/ql/udf/TestGenericUDFMonthsBetween.java new file mode 100644 index 0000000..19f245e --- /dev/null +++ b/ql/src/test/org/apache/hadoop/hive/ql/udf/TestGenericUDFMonthsBetween.java @@ -0,0 +1,261 @@ +/** + * 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.udf; + +import java.sql.Date; +import java.sql.Timestamp; + +import org.apache.hadoop.hive.ql.metadata.HiveException; +import org.apache.hadoop.hive.ql.udf.generic.GenericUDF.DeferredJavaObject; +import org.apache.hadoop.hive.ql.udf.generic.GenericUDF.DeferredObject; +import org.apache.hadoop.hive.ql.udf.generic.GenericUDFMonthsBetween; +import org.apache.hadoop.hive.serde2.io.DateWritable; +import org.apache.hadoop.hive.serde2.io.TimestampWritable; +import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector; +import org.apache.hadoop.hive.serde2.objectinspector.primitive.PrimitiveObjectInspectorFactory; +import org.apache.hadoop.io.DoubleWritable; +import org.apache.hadoop.io.Text; + +import junit.framework.TestCase; + +public class TestGenericUDFMonthsBetween extends TestCase { + public void testStringToDate() throws HiveException { + GenericUDFMonthsBetween udf = new GenericUDFMonthsBetween(); + ObjectInspector valueOI1 = PrimitiveObjectInspectorFactory.javaStringObjectInspector; + ObjectInspector valueOI2 = PrimitiveObjectInspectorFactory.javaStringObjectInspector; + ObjectInspector[] arguments = { valueOI1, valueOI2 }; + + // test month diff with fraction considering time components + udf.initialize(arguments); + DeferredObject valueObj1 = new DeferredJavaObject(new Text("1995-02-02")); + DeferredObject valueObj2 = new DeferredJavaObject(new Text("1995-01-01")); + DeferredObject[] args = { valueObj1, valueObj2 }; + DoubleWritable output = (DoubleWritable) udf.evaluate(args); + assertEquals("months_between() test for STRING failed ", "1.03225806", output.toString()); + + valueObj1 = new DeferredJavaObject(new Text("2003-07-17")); + valueObj2 = new DeferredJavaObject(new Text("2005-07-06")); + args = new DeferredObject[] { valueObj1, valueObj2 }; + output = (DoubleWritable) udf.evaluate(args); + assertEquals("months_between() test for STRING failed ", "-23.64516129", output.toString()); + + // test the last day of month + valueObj1 = new DeferredJavaObject(new Text("2001-06-30")); + valueObj2 = new DeferredJavaObject(new Text("2000-05-31")); + args = new DeferredObject[] { valueObj1, valueObj2 }; + output = (DoubleWritable) udf.evaluate(args); + assertEquals("months_between() test for STRING failed ", "13.0", output.toString()); + + // test the same day of month + valueObj1 = new DeferredJavaObject(new Text("2000-06-01")); + valueObj2 = new DeferredJavaObject(new Text("2004-07-01")); + args = new DeferredObject[] { valueObj1, valueObj2 }; + output = (DoubleWritable) udf.evaluate(args); + assertEquals("months_between() test for STRING failed ", "-49.0", output.toString()); + + // test february of non-leap year, 2/28 + valueObj1 = new DeferredJavaObject(new Text("2002-02-28")); + valueObj2 = new DeferredJavaObject(new Text("2002-03-01")); + args = new DeferredObject[] { valueObj1, valueObj2 }; + output = (DoubleWritable) udf.evaluate(args); + assertEquals("months_between() test for STRING failed ", "-0.12903226", output.toString()); + + // test february of non-leap year, 2/31 is viewd as 3/3 due to 3 days diff + // from 2/31 to 2/28 + valueObj1 = new DeferredJavaObject(new Text("2002-02-31")); + valueObj2 = new DeferredJavaObject(new Text("2002-03-01")); + args = new DeferredObject[] { valueObj1, valueObj2 }; + output = (DoubleWritable) udf.evaluate(args); + assertEquals("months_between() test for STRING failed ", "0.06451613", output.toString()); + + // test Feb of leap year, 2/29 + valueObj1 = new DeferredJavaObject(new Text("2012-02-29")); + valueObj2 = new DeferredJavaObject(new Text("2012-03-01")); + args = new DeferredObject[] { valueObj1, valueObj2 }; + output = (DoubleWritable) udf.evaluate(args); + assertEquals("months_between() test for STRING failed ", "-0.09677419", output.toString()); + + // test february of leap year, 2/31 is viewed as 3/2 due to 2 days diff from + // 2/31 to 2/29 + valueObj1 = new DeferredJavaObject(new Text("2012-02-31")); + valueObj2 = new DeferredJavaObject(new Text("2012-03-01")); + args = new DeferredObject[] { valueObj1, valueObj2 }; + output = (DoubleWritable) udf.evaluate(args); + assertEquals("months_between() test for STRING failed ", "0.03225806", output.toString()); + + // Test with null args + args = new DeferredObject[] { new DeferredJavaObject(null), valueObj2 }; + assertNull("months_between() 1st arg null", udf.evaluate(args)); + + args = new DeferredObject[] { valueObj1, new DeferredJavaObject(null) }; + assertNull("months_between() 2nd arg null", udf.evaluate(args)); + + args = new DeferredObject[] { new DeferredJavaObject(null), new DeferredJavaObject(null) }; + assertNull("months_between() both args null", udf.evaluate(args)); + } + + public void testTimestampToDate() throws HiveException { + GenericUDFMonthsBetween udf = new GenericUDFMonthsBetween(); + ObjectInspector valueOI1 = PrimitiveObjectInspectorFactory.writableTimestampObjectInspector; + ObjectInspector valueOI2 = PrimitiveObjectInspectorFactory.writableTimestampObjectInspector; + ObjectInspector[] arguments = { valueOI1, valueOI2 }; + + // test month diff with fraction considering time components + udf.initialize(arguments); + DeferredObject valueObj1 = new DeferredJavaObject(new TimestampWritable(Timestamp.valueOf("1995-02-02 00:00:00"))); + DeferredObject valueObj2 = new DeferredJavaObject(new TimestampWritable(Timestamp.valueOf("1995-01-01 00:00:00"))); + DeferredObject[] args = { valueObj1, valueObj2 }; + DoubleWritable output = (DoubleWritable) udf.evaluate(args); + assertEquals("months_between() test for TIMESTAMP failed ", "1.03225806", output.toString()); + + valueObj1 = new DeferredJavaObject(new TimestampWritable(Timestamp.valueOf("2003-07-17 00:00:00"))); + valueObj2 = new DeferredJavaObject(new TimestampWritable(Timestamp.valueOf("2005-07-06 00:00:00"))); + args = new DeferredObject[] { valueObj1, valueObj2 }; + output = (DoubleWritable) udf.evaluate(args); + assertEquals("months_between() test for TIMESTAMP failed ", "-23.64516129", output.toString()); + + // test the last day of month + valueObj1 = new DeferredJavaObject(new TimestampWritable(Timestamp.valueOf("2001-06-30 00:00:00"))); + valueObj2 = new DeferredJavaObject(new TimestampWritable(Timestamp.valueOf("2000-05-31 00:00:00"))); + args = new DeferredObject[] { valueObj1, valueObj2 }; + output = (DoubleWritable) udf.evaluate(args); + assertEquals("months_between() test for TIMESTAMP failed ", "13.0", output.toString()); + + // test the same day of month + valueObj1 = new DeferredJavaObject(new TimestampWritable(Timestamp.valueOf("2000-06-01 00:00:00"))); + valueObj2 = new DeferredJavaObject(new TimestampWritable(Timestamp.valueOf("2004-07-01 00:00:00"))); + args = new DeferredObject[] { valueObj1, valueObj2 }; + output = (DoubleWritable) udf.evaluate(args); + assertEquals("months_between() test for TIMESTAMP failed ", "-49.0", output.toString()); + + // test february of non-leap year, 2/28 + valueObj1 = new DeferredJavaObject(new TimestampWritable(Timestamp.valueOf("2002-02-28 00:00:00"))); + valueObj2 = new DeferredJavaObject(new TimestampWritable(Timestamp.valueOf("2002-03-01 00:00:00"))); + args = new DeferredObject[] { valueObj1, valueObj2 }; + output = (DoubleWritable) udf.evaluate(args); + assertEquals("months_between() test for TIMESTAMP failed ", "-0.12903226", output.toString()); + + // test february of non-leap year, 2/31 is viewd as 3/3 due to 3 days diff + // from 2/31 to 2/28 + valueObj1 = new DeferredJavaObject(new TimestampWritable(Timestamp.valueOf("2002-02-31 00:00:00"))); + valueObj2 = new DeferredJavaObject(new TimestampWritable(Timestamp.valueOf("2002-03-01 00:00:00"))); + args = new DeferredObject[] { valueObj1, valueObj2 }; + output = (DoubleWritable) udf.evaluate(args); + assertEquals("months_between() test for TIMESTAMP failed ", "0.06451613", output.toString()); + + // test Feb of leap year, 2/29 + valueObj1 = new DeferredJavaObject(new TimestampWritable(Timestamp.valueOf("2012-02-29 00:00:00"))); + valueObj2 = new DeferredJavaObject(new TimestampWritable(Timestamp.valueOf("2012-03-01 00:00:00"))); + args = new DeferredObject[] { valueObj1, valueObj2 }; + output = (DoubleWritable) udf.evaluate(args); + assertEquals("months_between() test for TIMESTAMP failed ", "-0.09677419", output.toString()); + + // test february of leap year, 2/31 is viewed as 3/2 due to 2 days diff from + // 2/31 to 2/29 + valueObj1 = new DeferredJavaObject(new TimestampWritable(Timestamp.valueOf("2012-02-31 00:00:00"))); + valueObj2 = new DeferredJavaObject(new TimestampWritable(Timestamp.valueOf("2012-03-01 00:00:00"))); + args = new DeferredObject[] { valueObj1, valueObj2 }; + output = (DoubleWritable) udf.evaluate(args); + assertEquals("months_between() test for TIMESTAMP failed ", "0.03225806", output.toString()); + + // Test with null args + args = new DeferredObject[] { new DeferredJavaObject(null), valueObj2 }; + assertNull("months_between() 1st arg null", udf.evaluate(args)); + + args = new DeferredObject[] { valueObj1, new DeferredJavaObject(null) }; + assertNull("months_between() 2nd arg null", udf.evaluate(args)); + + args = new DeferredObject[] { new DeferredJavaObject(null), new DeferredJavaObject(null) }; + assertNull("months_between() both args null", udf.evaluate(args)); + } + + public void testDateWritablepToDate() throws HiveException { + GenericUDFMonthsBetween udf = new GenericUDFMonthsBetween(); + ObjectInspector valueOI1 = PrimitiveObjectInspectorFactory.writableDateObjectInspector; + ObjectInspector valueOI2 = PrimitiveObjectInspectorFactory.writableDateObjectInspector; + ObjectInspector[] arguments = { valueOI1, valueOI2 }; + + // test month diff with fraction considering time components + udf.initialize(arguments); + DeferredObject valueObj1 = new DeferredJavaObject(new DateWritable(Date.valueOf("1995-02-02"))); + DeferredObject valueObj2 = new DeferredJavaObject(new DateWritable(Date.valueOf("1995-01-01"))); + DeferredObject[] args = { valueObj1, valueObj2 }; + DoubleWritable output = (DoubleWritable) udf.evaluate(args); + assertEquals("months_between() test for TIMESTAMP failed ", "1.03225806", output.toString()); + + valueObj1 = new DeferredJavaObject(new DateWritable(Date.valueOf("2003-07-17"))); + valueObj2 = new DeferredJavaObject(new DateWritable(Date.valueOf("2005-07-06"))); + args = new DeferredObject[] { valueObj1, valueObj2 }; + output = (DoubleWritable) udf.evaluate(args); + assertEquals("months_between() test for TIMESTAMP failed ", "-23.64516129", output.toString()); + + // test the last day of month + valueObj1 = new DeferredJavaObject(new DateWritable(Date.valueOf("2001-06-30"))); + valueObj2 = new DeferredJavaObject(new DateWritable(Date.valueOf("2000-05-31"))); + args = new DeferredObject[] { valueObj1, valueObj2 }; + output = (DoubleWritable) udf.evaluate(args); + assertEquals("months_between() test for TIMESTAMP failed ", "13.0", output.toString()); + + // test the same day of month + valueObj1 = new DeferredJavaObject(new DateWritable(Date.valueOf("2000-06-01"))); + valueObj2 = new DeferredJavaObject(new DateWritable(Date.valueOf("2004-07-01"))); + args = new DeferredObject[] { valueObj1, valueObj2 }; + output = (DoubleWritable) udf.evaluate(args); + assertEquals("months_between() test for TIMESTAMP failed ", "-49.0", output.toString()); + + // test february of non-leap year, 2/28 + valueObj1 = new DeferredJavaObject(new DateWritable(Date.valueOf("2002-02-28"))); + valueObj2 = new DeferredJavaObject(new DateWritable(Date.valueOf("2002-03-01"))); + args = new DeferredObject[] { valueObj1, valueObj2 }; + output = (DoubleWritable) udf.evaluate(args); + assertEquals("months_between() test for TIMESTAMP failed ", "-0.12903226", output.toString()); + + // test february of non-leap year, 2/31 is viewd as 3/3 due to 3 days diff + // from 2/31 to 2/28 + valueObj1 = new DeferredJavaObject(new DateWritable(Date.valueOf("2002-02-31"))); + valueObj2 = new DeferredJavaObject(new DateWritable(Date.valueOf("2002-03-01"))); + args = new DeferredObject[] { valueObj1, valueObj2 }; + output = (DoubleWritable) udf.evaluate(args); + assertEquals("months_between() test for TIMESTAMP failed ", "0.06451613", output.toString()); + + // test Feb of leap year, 2/29 + valueObj1 = new DeferredJavaObject(new DateWritable(Date.valueOf("2012-02-29"))); + valueObj2 = new DeferredJavaObject(new DateWritable(Date.valueOf("2012-03-01"))); + args = new DeferredObject[] { valueObj1, valueObj2 }; + output = (DoubleWritable) udf.evaluate(args); + assertEquals("months_between() test for TIMESTAMP failed ", "-0.09677419", output.toString()); + + // test february of leap year, 2/31 is viewed as 3/2 due to 2 days diff from + // 2/31 to 2/29 + valueObj1 = new DeferredJavaObject(new DateWritable(Date.valueOf("2012-02-31"))); + valueObj2 = new DeferredJavaObject(new DateWritable(Date.valueOf("2012-03-01"))); + args = new DeferredObject[] { valueObj1, valueObj2 }; + output = (DoubleWritable) udf.evaluate(args); + assertEquals("months_between() test for TIMESTAMP failed ", "0.03225806", output.toString()); + + // Test with null args + args = new DeferredObject[] { new DeferredJavaObject(null), valueObj2 }; + assertNull("months_between() 1st arg null", udf.evaluate(args)); + + args = new DeferredObject[] { valueObj1, new DeferredJavaObject(null) }; + assertNull("months_between() 2nd arg null", udf.evaluate(args)); + + args = new DeferredObject[] { new DeferredJavaObject(null), new DeferredJavaObject(null) }; + assertNull("months_between() both args null", udf.evaluate(args)); + } +} diff --git a/ql/src/test/queries/clientnegative/udf_months_between_error1.q b/ql/src/test/queries/clientnegative/udf_months_between_error1.q new file mode 100644 index 0000000..9b25386 --- /dev/null +++ b/ql/src/test/queries/clientnegative/udf_months_between_error1.q @@ -0,0 +1 @@ +SELECT MONTHS_BETWEEN(null, '2012-03-01') FROM src tablesample (1 rows); diff --git a/ql/src/test/queries/clientnegative/udf_months_between_error2.q b/ql/src/test/queries/clientnegative/udf_months_between_error2.q new file mode 100644 index 0000000..747cacd --- /dev/null +++ b/ql/src/test/queries/clientnegative/udf_months_between_error2.q @@ -0,0 +1 @@ +SELECT MONTHS_BETWEEN('2012-02-31', null) FROM src tablesample (1 rows); diff --git a/ql/src/test/queries/clientnegative/udf_months_between_error3.q b/ql/src/test/queries/clientnegative/udf_months_between_error3.q new file mode 100644 index 0000000..8768d06 --- /dev/null +++ b/ql/src/test/queries/clientnegative/udf_months_between_error3.q @@ -0,0 +1 @@ +SELECT MONTHS_BETWEEN(null, null) FROM src tablesample (1 rows); diff --git a/ql/src/test/queries/clientnegative/udf_months_between_error4.q b/ql/src/test/queries/clientnegative/udf_months_between_error4.q new file mode 100644 index 0000000..91e7277 --- /dev/null +++ b/ql/src/test/queries/clientnegative/udf_months_between_error4.q @@ -0,0 +1 @@ +SELECT MONTHS_BETWEEN(null, CAST('2012-03-01 00:00:00' AS TIMESTAMP)) FROM src tablesample (1 rows); diff --git a/ql/src/test/queries/clientnegative/udf_months_between_error5.q b/ql/src/test/queries/clientnegative/udf_months_between_error5.q new file mode 100644 index 0000000..e4be9f5 --- /dev/null +++ b/ql/src/test/queries/clientnegative/udf_months_between_error5.q @@ -0,0 +1 @@ +SELECT MONTHS_BETWEEN(CAST('2012-02-31 00:00:00' AS TIMESTAMP), null) FROM src tablesample (1 rows); diff --git a/ql/src/test/queries/clientnegative/udf_months_between_error6.q b/ql/src/test/queries/clientnegative/udf_months_between_error6.q new file mode 100644 index 0000000..2b86c9f --- /dev/null +++ b/ql/src/test/queries/clientnegative/udf_months_between_error6.q @@ -0,0 +1 @@ +SELECT MONTHS_BETWEEN(null, CAST('2012-03-01' AS DATE)) FROM src tablesample (1 rows); diff --git a/ql/src/test/queries/clientnegative/udf_months_between_error7.q b/ql/src/test/queries/clientnegative/udf_months_between_error7.q new file mode 100644 index 0000000..effe69e --- /dev/null +++ b/ql/src/test/queries/clientnegative/udf_months_between_error7.q @@ -0,0 +1 @@ +SELECT MONTHS_BETWEEN(CAST('2012-02-31' AS DATE), null) FROM src tablesample (1 rows); diff --git a/ql/src/test/queries/clientnegative/udf_months_between_error8.q b/ql/src/test/queries/clientnegative/udf_months_between_error8.q new file mode 100644 index 0000000..01a4455 --- /dev/null +++ b/ql/src/test/queries/clientnegative/udf_months_between_error8.q @@ -0,0 +1 @@ +SELECT MONTHS_BETWEEN(1, '2001-01-01') FROM src tablesample (1 rows); diff --git a/ql/src/test/queries/clientnegative/udf_months_between_error9.q b/ql/src/test/queries/clientnegative/udf_months_between_error9.q new file mode 100644 index 0000000..4b6e73a --- /dev/null +++ b/ql/src/test/queries/clientnegative/udf_months_between_error9.q @@ -0,0 +1 @@ +SELECT MONTHS_BETWEEN(1.0, 2) FROM src tablesample (1 rows); diff --git a/ql/src/test/queries/clientpositive/udf_months_between.q b/ql/src/test/queries/clientpositive/udf_months_between.q new file mode 100644 index 0000000..fda41d8 --- /dev/null +++ b/ql/src/test/queries/clientpositive/udf_months_between.q @@ -0,0 +1,70 @@ +DESCRIBE FUNCTION months_between; +DESCRIBE FUNCTION EXTENDED months_between; + +--test string format +EXPLAIN +SELECT MONTHS_BETWEEN('1995-02-02', '1995-01-01'), + MONTHS_BETWEEN('2003-07-17', '2005-07-06'), + MONTHS_BETWEEN('2001-06-30', '2000-05-31'), + MONTHS_BETWEEN('2000-06-01', '2004-07-01'), + MONTHS_BETWEEN('2002-02-28', '2002-03-01'), + MONTHS_BETWEEN('2002-02-31', '2002-03-01'), + MONTHS_BETWEEN('2012-02-29', '2012-03-01'), + MONTHS_BETWEEN('2012-02-31', '2012-03-01') +FROM src tablesample (1 rows); + + +SELECT MONTHS_BETWEEN('1995-02-02', '1995-01-01'), + MONTHS_BETWEEN('2003-07-17', '2005-07-06'), + MONTHS_BETWEEN('2001-06-30', '2000-05-31'), + MONTHS_BETWEEN('2000-06-01', '2004-07-01'), + MONTHS_BETWEEN('2002-02-28', '2002-03-01'), + MONTHS_BETWEEN('2002-02-31', '2002-03-01'), + MONTHS_BETWEEN('2012-02-29', '2012-03-01'), + MONTHS_BETWEEN('2012-02-31', '2012-03-01') +FROM src tablesample (1 rows); + +--test timestamp format +EXPLAIN +SELECT MONTHS_BETWEEN(CAST('1995-02-02 00:00:00' AS TIMESTAMP), CAST('1995-01-01 00:00:00' AS TIMESTAMP)), + MONTHS_BETWEEN(CAST('2003-07-17 00:00:00' AS TIMESTAMP), CAST('2005-07-06 00:00:00' AS TIMESTAMP)), + MONTHS_BETWEEN(CAST('2001-06-30 00:00:00' AS TIMESTAMP), CAST('2000-05-31 00:00:00' AS TIMESTAMP)), + MONTHS_BETWEEN(CAST('2000-06-01 00:00:00' AS TIMESTAMP), CAST('2004-07-01 00:00:00' AS TIMESTAMP)), + MONTHS_BETWEEN(CAST('2002-02-28 00:00:00' AS TIMESTAMP), CAST('2002-03-01 00:00:00' AS TIMESTAMP)), + MONTHS_BETWEEN(CAST('2002-02-31 00:00:00' AS TIMESTAMP), CAST('2002-03-01 00:00:00' AS TIMESTAMP)), + MONTHS_BETWEEN(CAST('2012-02-29 00:00:00' AS TIMESTAMP), CAST('2012-03-01 00:00:00' AS TIMESTAMP)), + MONTHS_BETWEEN(CAST('2012-02-31 00:00:00' AS TIMESTAMP), CAST('2012-03-01 00:00:00' AS TIMESTAMP)) +FROM src tablesample (1 rows); + + +SELECT MONTHS_BETWEEN(CAST('1995-02-02 00:00:00' AS TIMESTAMP), CAST('1995-01-01 00:00:00' AS TIMESTAMP)), + MONTHS_BETWEEN(CAST('2003-07-17 00:00:00' AS TIMESTAMP), CAST('2005-07-06 00:00:00' AS TIMESTAMP)), + MONTHS_BETWEEN(CAST('2001-06-30 00:00:00' AS TIMESTAMP), CAST('2000-05-31 00:00:00' AS TIMESTAMP)), + MONTHS_BETWEEN(CAST('2000-06-01 00:00:00' AS TIMESTAMP), CAST('2004-07-01 00:00:00' AS TIMESTAMP)), + MONTHS_BETWEEN(CAST('2002-02-28 00:00:00' AS TIMESTAMP), CAST('2002-03-01 00:00:00' AS TIMESTAMP)), + MONTHS_BETWEEN(CAST('2002-02-31 00:00:00' AS TIMESTAMP), CAST('2002-03-01 00:00:00' AS TIMESTAMP)), + MONTHS_BETWEEN(CAST('2012-02-29 00:00:00' AS TIMESTAMP), CAST('2012-03-01 00:00:00' AS TIMESTAMP)), + MONTHS_BETWEEN(CAST('2012-02-31 00:00:00' AS TIMESTAMP), CAST('2012-03-01 00:00:00' AS TIMESTAMP)) +FROM src tablesample (1 rows); + +--test date format +EXPLAIN +SELECT MONTHS_BETWEEN(CAST('1995-02-02' AS DATE), CAST('1995-01-01' AS DATE)), + MONTHS_BETWEEN(CAST('2003-07-17' AS DATE), CAST('2005-07-06' AS DATE)), + MONTHS_BETWEEN(CAST('2001-06-30' AS DATE), CAST('2000-05-31' AS DATE)), + MONTHS_BETWEEN(CAST('2000-06-01' AS DATE), CAST('2004-07-01' AS DATE)), + MONTHS_BETWEEN(CAST('2002-02-28' AS DATE), CAST('2002-03-01' AS DATE)), + MONTHS_BETWEEN(CAST('2002-02-31' AS DATE), CAST('2002-03-01' AS DATE)), + MONTHS_BETWEEN(CAST('2012-02-29' AS DATE), CAST('2012-03-01' AS DATE)), + MONTHS_BETWEEN(CAST('2012-02-31' AS DATE), CAST('2012-03-01' AS DATE)) +FROM src tablesample (1 rows); + +SELECT MONTHS_BETWEEN(CAST('1995-02-02' AS DATE), CAST('1995-01-01' AS DATE)), + MONTHS_BETWEEN(CAST('2003-07-17' AS DATE), CAST('2005-07-06' AS DATE)), + MONTHS_BETWEEN(CAST('2001-06-30' AS DATE), CAST('2000-05-31' AS DATE)), + MONTHS_BETWEEN(CAST('2000-06-01' AS DATE), CAST('2004-07-01' AS DATE)), + MONTHS_BETWEEN(CAST('2002-02-28' AS DATE), CAST('2002-03-01' AS DATE)), + MONTHS_BETWEEN(CAST('2002-02-31' AS DATE), CAST('2002-03-01' AS DATE)), + MONTHS_BETWEEN(CAST('2012-02-29' AS DATE), CAST('2012-03-01' AS DATE)), + MONTHS_BETWEEN(CAST('2012-02-31' AS DATE), CAST('2012-03-01' AS DATE)) +FROM src tablesample (1 rows); diff --git a/ql/src/test/results/clientnegative/udf_months_between_error1.q.out b/ql/src/test/results/clientnegative/udf_months_between_error1.q.out new file mode 100644 index 0000000..899836d --- /dev/null +++ b/ql/src/test/results/clientnegative/udf_months_between_error1.q.out @@ -0,0 +1 @@ +FAILED: SemanticException [Error 10014]: Line 1:7 Wrong arguments ''2012-03-01'': MONTHS_BETWEEN() only takes STRING/TIMESTAMP/DATEWRITABLE types as 1-th argument, got VOID diff --git a/ql/src/test/results/clientnegative/udf_months_between_error2.q.out b/ql/src/test/results/clientnegative/udf_months_between_error2.q.out new file mode 100644 index 0000000..106610a --- /dev/null +++ b/ql/src/test/results/clientnegative/udf_months_between_error2.q.out @@ -0,0 +1 @@ +FAILED: SemanticException [Error 10014]: Line 1:7 Wrong arguments 'TOK_NULL': MONTHS_BETWEEN() only takes STRING/TIMESTAMP/DATEWRITABLE types as 2-th argument, got VOID diff --git a/ql/src/test/results/clientnegative/udf_months_between_error3.q.out b/ql/src/test/results/clientnegative/udf_months_between_error3.q.out new file mode 100644 index 0000000..d4422c4 --- /dev/null +++ b/ql/src/test/results/clientnegative/udf_months_between_error3.q.out @@ -0,0 +1 @@ +FAILED: SemanticException [Error 10014]: Line 1:7 Wrong arguments 'TOK_NULL': MONTHS_BETWEEN() only takes STRING/TIMESTAMP/DATEWRITABLE types as 1-th argument, got VOID diff --git a/ql/src/test/results/clientnegative/udf_months_between_error4.q.out b/ql/src/test/results/clientnegative/udf_months_between_error4.q.out new file mode 100644 index 0000000..d8b5a0d --- /dev/null +++ b/ql/src/test/results/clientnegative/udf_months_between_error4.q.out @@ -0,0 +1 @@ +FAILED: SemanticException [Error 10014]: Line 1:7 Wrong arguments ''2012-03-01 00:00:00'': MONTHS_BETWEEN() only takes STRING/TIMESTAMP/DATEWRITABLE types as 1-th argument, got VOID diff --git a/ql/src/test/results/clientnegative/udf_months_between_error5.q.out b/ql/src/test/results/clientnegative/udf_months_between_error5.q.out new file mode 100644 index 0000000..106610a --- /dev/null +++ b/ql/src/test/results/clientnegative/udf_months_between_error5.q.out @@ -0,0 +1 @@ +FAILED: SemanticException [Error 10014]: Line 1:7 Wrong arguments 'TOK_NULL': MONTHS_BETWEEN() only takes STRING/TIMESTAMP/DATEWRITABLE types as 2-th argument, got VOID diff --git a/ql/src/test/results/clientnegative/udf_months_between_error6.q.out b/ql/src/test/results/clientnegative/udf_months_between_error6.q.out new file mode 100644 index 0000000..899836d --- /dev/null +++ b/ql/src/test/results/clientnegative/udf_months_between_error6.q.out @@ -0,0 +1 @@ +FAILED: SemanticException [Error 10014]: Line 1:7 Wrong arguments ''2012-03-01'': MONTHS_BETWEEN() only takes STRING/TIMESTAMP/DATEWRITABLE types as 1-th argument, got VOID diff --git a/ql/src/test/results/clientnegative/udf_months_between_error7.q.out b/ql/src/test/results/clientnegative/udf_months_between_error7.q.out new file mode 100644 index 0000000..106610a --- /dev/null +++ b/ql/src/test/results/clientnegative/udf_months_between_error7.q.out @@ -0,0 +1 @@ +FAILED: SemanticException [Error 10014]: Line 1:7 Wrong arguments 'TOK_NULL': MONTHS_BETWEEN() only takes STRING/TIMESTAMP/DATEWRITABLE types as 2-th argument, got VOID diff --git a/ql/src/test/results/clientnegative/udf_months_between_error8.q.out b/ql/src/test/results/clientnegative/udf_months_between_error8.q.out new file mode 100644 index 0000000..b1153b1 --- /dev/null +++ b/ql/src/test/results/clientnegative/udf_months_between_error8.q.out @@ -0,0 +1 @@ +FAILED: SemanticException [Error 10014]: Line 1:7 Wrong arguments ''2001-01-01'': MONTHS_BETWEEN() only takes STRING/TIMESTAMP/DATEWRITABLE types as 1-th argument, got INT diff --git a/ql/src/test/results/clientnegative/udf_months_between_error9.q.out b/ql/src/test/results/clientnegative/udf_months_between_error9.q.out new file mode 100644 index 0000000..5ce19f9 --- /dev/null +++ b/ql/src/test/results/clientnegative/udf_months_between_error9.q.out @@ -0,0 +1 @@ +FAILED: SemanticException [Error 10014]: Line 1:7 Wrong arguments '2': MONTHS_BETWEEN() only takes STRING/TIMESTAMP/DATEWRITABLE types as 1-th argument, got DOUBLE diff --git a/ql/src/test/results/clientpositive/show_functions.q.out b/ql/src/test/results/clientpositive/show_functions.q.out index e21b54b..e7f247b 100644 --- a/ql/src/test/results/clientpositive/show_functions.q.out +++ b/ql/src/test/results/clientpositive/show_functions.q.out @@ -121,6 +121,7 @@ max min minute month +months_between named_struct negative ngrams @@ -314,6 +315,7 @@ max min minute month +months_between xpath xpath_boolean xpath_double diff --git a/ql/src/test/results/clientpositive/udf_months_between.q.out b/ql/src/test/results/clientpositive/udf_months_between.q.out new file mode 100644 index 0000000..fb7f379 --- /dev/null +++ b/ql/src/test/results/clientpositive/udf_months_between.q.out @@ -0,0 +1,216 @@ +PREHOOK: query: DESCRIBE FUNCTION months_between +PREHOOK: type: DESCFUNCTION +POSTHOOK: query: DESCRIBE FUNCTION months_between +POSTHOOK: type: DESCFUNCTION +months_between(date1, date2) - returns number of months between dates date1 and date2 +PREHOOK: query: DESCRIBE FUNCTION EXTENDED months_between +PREHOOK: type: DESCFUNCTION +POSTHOOK: query: DESCRIBE FUNCTION EXTENDED months_between +POSTHOOK: type: DESCFUNCTION +months_between(date1, date2) - returns number of months between dates date1 and date2 +If date1 is later than date2, then the result is positive. If date1 is earlier than date2, then the result is negative.If date1 and date2 are either the same days of the month or both last days of months, then the result is always an integer.otherwise, it will calculate the fractional portion of the result based on a 31-day month and considers the difference in time components date1 and date2. +date1 and date2 are strings in the format 'yyyy-MM-dd HH:mm:ss' or 'yyyy-MM-dd'. The time parts are ignored. +Example: + > SELECT months_between('02-02-1995', '01-01-1995') FROM src LIMIT 1; + 1.03225806 +PREHOOK: query: --test string format +EXPLAIN +SELECT MONTHS_BETWEEN('1995-02-02', '1995-01-01'), + MONTHS_BETWEEN('2003-07-17', '2005-07-06'), + MONTHS_BETWEEN('2001-06-30', '2000-05-31'), + MONTHS_BETWEEN('2000-06-01', '2004-07-01'), + MONTHS_BETWEEN('2002-02-28', '2002-03-01'), + MONTHS_BETWEEN('2002-02-31', '2002-03-01'), + MONTHS_BETWEEN('2012-02-29', '2012-03-01'), + MONTHS_BETWEEN('2012-02-31', '2012-03-01') +FROM src tablesample (1 rows) +PREHOOK: type: QUERY +POSTHOOK: query: --test string format +EXPLAIN +SELECT MONTHS_BETWEEN('1995-02-02', '1995-01-01'), + MONTHS_BETWEEN('2003-07-17', '2005-07-06'), + MONTHS_BETWEEN('2001-06-30', '2000-05-31'), + MONTHS_BETWEEN('2000-06-01', '2004-07-01'), + MONTHS_BETWEEN('2002-02-28', '2002-03-01'), + MONTHS_BETWEEN('2002-02-31', '2002-03-01'), + MONTHS_BETWEEN('2012-02-29', '2012-03-01'), + MONTHS_BETWEEN('2012-02-31', '2012-03-01') +FROM src tablesample (1 rows) +POSTHOOK: type: QUERY +STAGE DEPENDENCIES: + Stage-0 is a root stage + +STAGE PLANS: + Stage: Stage-0 + Fetch Operator + limit: -1 + Processor Tree: + TableScan + alias: src + Row Limit Per Split: 1 + Statistics: Num rows: 500 Data size: 5312 Basic stats: COMPLETE Column stats: COMPLETE + Select Operator + expressions: 1.03225806 (type: double), -23.64516129 (type: double), 13.0 (type: double), -49.0 (type: double), -0.12903226 (type: double), 0.06451613 (type: double), -0.09677419 (type: double), 0.03225806 (type: double) + outputColumnNames: _col0, _col1, _col2, _col3, _col4, _col5, _col6, _col7 + Statistics: Num rows: 500 Data size: 32000 Basic stats: COMPLETE Column stats: COMPLETE + ListSink + +PREHOOK: query: SELECT MONTHS_BETWEEN('1995-02-02', '1995-01-01'), + MONTHS_BETWEEN('2003-07-17', '2005-07-06'), + MONTHS_BETWEEN('2001-06-30', '2000-05-31'), + MONTHS_BETWEEN('2000-06-01', '2004-07-01'), + MONTHS_BETWEEN('2002-02-28', '2002-03-01'), + MONTHS_BETWEEN('2002-02-31', '2002-03-01'), + MONTHS_BETWEEN('2012-02-29', '2012-03-01'), + MONTHS_BETWEEN('2012-02-31', '2012-03-01') +FROM src tablesample (1 rows) +PREHOOK: type: QUERY +PREHOOK: Input: default@src +#### A masked pattern was here #### +POSTHOOK: query: SELECT MONTHS_BETWEEN('1995-02-02', '1995-01-01'), + MONTHS_BETWEEN('2003-07-17', '2005-07-06'), + MONTHS_BETWEEN('2001-06-30', '2000-05-31'), + MONTHS_BETWEEN('2000-06-01', '2004-07-01'), + MONTHS_BETWEEN('2002-02-28', '2002-03-01'), + MONTHS_BETWEEN('2002-02-31', '2002-03-01'), + MONTHS_BETWEEN('2012-02-29', '2012-03-01'), + MONTHS_BETWEEN('2012-02-31', '2012-03-01') +FROM src tablesample (1 rows) +POSTHOOK: type: QUERY +POSTHOOK: Input: default@src +#### A masked pattern was here #### +1.03225806 -23.64516129 13.0 -49.0 -0.12903226 0.06451613 -0.09677419 0.03225806 +PREHOOK: query: --test timestamp format +EXPLAIN +SELECT MONTHS_BETWEEN(CAST('1995-02-02 00:00:00' AS TIMESTAMP), CAST('1995-01-01 00:00:00' AS TIMESTAMP)), + MONTHS_BETWEEN(CAST('2003-07-17 00:00:00' AS TIMESTAMP), CAST('2005-07-06 00:00:00' AS TIMESTAMP)), + MONTHS_BETWEEN(CAST('2001-06-30 00:00:00' AS TIMESTAMP), CAST('2000-05-31 00:00:00' AS TIMESTAMP)), + MONTHS_BETWEEN(CAST('2000-06-01 00:00:00' AS TIMESTAMP), CAST('2004-07-01 00:00:00' AS TIMESTAMP)), + MONTHS_BETWEEN(CAST('2002-02-28 00:00:00' AS TIMESTAMP), CAST('2002-03-01 00:00:00' AS TIMESTAMP)), + MONTHS_BETWEEN(CAST('2002-02-31 00:00:00' AS TIMESTAMP), CAST('2002-03-01 00:00:00' AS TIMESTAMP)), + MONTHS_BETWEEN(CAST('2012-02-29 00:00:00' AS TIMESTAMP), CAST('2012-03-01 00:00:00' AS TIMESTAMP)), + MONTHS_BETWEEN(CAST('2012-02-31 00:00:00' AS TIMESTAMP), CAST('2012-03-01 00:00:00' AS TIMESTAMP)) +FROM src tablesample (1 rows) +PREHOOK: type: QUERY +POSTHOOK: query: --test timestamp format +EXPLAIN +SELECT MONTHS_BETWEEN(CAST('1995-02-02 00:00:00' AS TIMESTAMP), CAST('1995-01-01 00:00:00' AS TIMESTAMP)), + MONTHS_BETWEEN(CAST('2003-07-17 00:00:00' AS TIMESTAMP), CAST('2005-07-06 00:00:00' AS TIMESTAMP)), + MONTHS_BETWEEN(CAST('2001-06-30 00:00:00' AS TIMESTAMP), CAST('2000-05-31 00:00:00' AS TIMESTAMP)), + MONTHS_BETWEEN(CAST('2000-06-01 00:00:00' AS TIMESTAMP), CAST('2004-07-01 00:00:00' AS TIMESTAMP)), + MONTHS_BETWEEN(CAST('2002-02-28 00:00:00' AS TIMESTAMP), CAST('2002-03-01 00:00:00' AS TIMESTAMP)), + MONTHS_BETWEEN(CAST('2002-02-31 00:00:00' AS TIMESTAMP), CAST('2002-03-01 00:00:00' AS TIMESTAMP)), + MONTHS_BETWEEN(CAST('2012-02-29 00:00:00' AS TIMESTAMP), CAST('2012-03-01 00:00:00' AS TIMESTAMP)), + MONTHS_BETWEEN(CAST('2012-02-31 00:00:00' AS TIMESTAMP), CAST('2012-03-01 00:00:00' AS TIMESTAMP)) +FROM src tablesample (1 rows) +POSTHOOK: type: QUERY +STAGE DEPENDENCIES: + Stage-0 is a root stage + +STAGE PLANS: + Stage: Stage-0 + Fetch Operator + limit: -1 + Processor Tree: + TableScan + alias: src + Row Limit Per Split: 1 + Statistics: Num rows: 500 Data size: 5312 Basic stats: COMPLETE Column stats: COMPLETE + Select Operator + expressions: 1.03225806 (type: double), -23.64516129 (type: double), 13.0 (type: double), -49.0 (type: double), -0.12903226 (type: double), 0.06451613 (type: double), -0.09677419 (type: double), 0.03225806 (type: double) + outputColumnNames: _col0, _col1, _col2, _col3, _col4, _col5, _col6, _col7 + Statistics: Num rows: 500 Data size: 32000 Basic stats: COMPLETE Column stats: COMPLETE + ListSink + +PREHOOK: query: SELECT MONTHS_BETWEEN(CAST('1995-02-02 00:00:00' AS TIMESTAMP), CAST('1995-01-01 00:00:00' AS TIMESTAMP)), + MONTHS_BETWEEN(CAST('2003-07-17 00:00:00' AS TIMESTAMP), CAST('2005-07-06 00:00:00' AS TIMESTAMP)), + MONTHS_BETWEEN(CAST('2001-06-30 00:00:00' AS TIMESTAMP), CAST('2000-05-31 00:00:00' AS TIMESTAMP)), + MONTHS_BETWEEN(CAST('2000-06-01 00:00:00' AS TIMESTAMP), CAST('2004-07-01 00:00:00' AS TIMESTAMP)), + MONTHS_BETWEEN(CAST('2002-02-28 00:00:00' AS TIMESTAMP), CAST('2002-03-01 00:00:00' AS TIMESTAMP)), + MONTHS_BETWEEN(CAST('2002-02-31 00:00:00' AS TIMESTAMP), CAST('2002-03-01 00:00:00' AS TIMESTAMP)), + MONTHS_BETWEEN(CAST('2012-02-29 00:00:00' AS TIMESTAMP), CAST('2012-03-01 00:00:00' AS TIMESTAMP)), + MONTHS_BETWEEN(CAST('2012-02-31 00:00:00' AS TIMESTAMP), CAST('2012-03-01 00:00:00' AS TIMESTAMP)) +FROM src tablesample (1 rows) +PREHOOK: type: QUERY +PREHOOK: Input: default@src +#### A masked pattern was here #### +POSTHOOK: query: SELECT MONTHS_BETWEEN(CAST('1995-02-02 00:00:00' AS TIMESTAMP), CAST('1995-01-01 00:00:00' AS TIMESTAMP)), + MONTHS_BETWEEN(CAST('2003-07-17 00:00:00' AS TIMESTAMP), CAST('2005-07-06 00:00:00' AS TIMESTAMP)), + MONTHS_BETWEEN(CAST('2001-06-30 00:00:00' AS TIMESTAMP), CAST('2000-05-31 00:00:00' AS TIMESTAMP)), + MONTHS_BETWEEN(CAST('2000-06-01 00:00:00' AS TIMESTAMP), CAST('2004-07-01 00:00:00' AS TIMESTAMP)), + MONTHS_BETWEEN(CAST('2002-02-28 00:00:00' AS TIMESTAMP), CAST('2002-03-01 00:00:00' AS TIMESTAMP)), + MONTHS_BETWEEN(CAST('2002-02-31 00:00:00' AS TIMESTAMP), CAST('2002-03-01 00:00:00' AS TIMESTAMP)), + MONTHS_BETWEEN(CAST('2012-02-29 00:00:00' AS TIMESTAMP), CAST('2012-03-01 00:00:00' AS TIMESTAMP)), + MONTHS_BETWEEN(CAST('2012-02-31 00:00:00' AS TIMESTAMP), CAST('2012-03-01 00:00:00' AS TIMESTAMP)) +FROM src tablesample (1 rows) +POSTHOOK: type: QUERY +POSTHOOK: Input: default@src +#### A masked pattern was here #### +1.03225806 -23.64516129 13.0 -49.0 -0.12903226 0.06451613 -0.09677419 0.03225806 +PREHOOK: query: --test date format +EXPLAIN +SELECT MONTHS_BETWEEN(CAST('1995-02-02' AS DATE), CAST('1995-01-01' AS DATE)), + MONTHS_BETWEEN(CAST('2003-07-17' AS DATE), CAST('2005-07-06' AS DATE)), + MONTHS_BETWEEN(CAST('2001-06-30' AS DATE), CAST('2000-05-31' AS DATE)), + MONTHS_BETWEEN(CAST('2000-06-01' AS DATE), CAST('2004-07-01' AS DATE)), + MONTHS_BETWEEN(CAST('2002-02-28' AS DATE), CAST('2002-03-01' AS DATE)), + MONTHS_BETWEEN(CAST('2002-02-31' AS DATE), CAST('2002-03-01' AS DATE)), + MONTHS_BETWEEN(CAST('2012-02-29' AS DATE), CAST('2012-03-01' AS DATE)), + MONTHS_BETWEEN(CAST('2012-02-31' AS DATE), CAST('2012-03-01' AS DATE)) +FROM src tablesample (1 rows) +PREHOOK: type: QUERY +POSTHOOK: query: --test date format +EXPLAIN +SELECT MONTHS_BETWEEN(CAST('1995-02-02' AS DATE), CAST('1995-01-01' AS DATE)), + MONTHS_BETWEEN(CAST('2003-07-17' AS DATE), CAST('2005-07-06' AS DATE)), + MONTHS_BETWEEN(CAST('2001-06-30' AS DATE), CAST('2000-05-31' AS DATE)), + MONTHS_BETWEEN(CAST('2000-06-01' AS DATE), CAST('2004-07-01' AS DATE)), + MONTHS_BETWEEN(CAST('2002-02-28' AS DATE), CAST('2002-03-01' AS DATE)), + MONTHS_BETWEEN(CAST('2002-02-31' AS DATE), CAST('2002-03-01' AS DATE)), + MONTHS_BETWEEN(CAST('2012-02-29' AS DATE), CAST('2012-03-01' AS DATE)), + MONTHS_BETWEEN(CAST('2012-02-31' AS DATE), CAST('2012-03-01' AS DATE)) +FROM src tablesample (1 rows) +POSTHOOK: type: QUERY +STAGE DEPENDENCIES: + Stage-0 is a root stage + +STAGE PLANS: + Stage: Stage-0 + Fetch Operator + limit: -1 + Processor Tree: + TableScan + alias: src + Row Limit Per Split: 1 + Statistics: Num rows: 500 Data size: 5312 Basic stats: COMPLETE Column stats: COMPLETE + Select Operator + expressions: 1.03225806 (type: double), -23.64516129 (type: double), 13.0 (type: double), -49.0 (type: double), -0.12903226 (type: double), 0.06451613 (type: double), -0.09677419 (type: double), 0.03225806 (type: double) + outputColumnNames: _col0, _col1, _col2, _col3, _col4, _col5, _col6, _col7 + Statistics: Num rows: 500 Data size: 32000 Basic stats: COMPLETE Column stats: COMPLETE + ListSink + +PREHOOK: query: SELECT MONTHS_BETWEEN(CAST('1995-02-02' AS DATE), CAST('1995-01-01' AS DATE)), + MONTHS_BETWEEN(CAST('2003-07-17' AS DATE), CAST('2005-07-06' AS DATE)), + MONTHS_BETWEEN(CAST('2001-06-30' AS DATE), CAST('2000-05-31' AS DATE)), + MONTHS_BETWEEN(CAST('2000-06-01' AS DATE), CAST('2004-07-01' AS DATE)), + MONTHS_BETWEEN(CAST('2002-02-28' AS DATE), CAST('2002-03-01' AS DATE)), + MONTHS_BETWEEN(CAST('2002-02-31' AS DATE), CAST('2002-03-01' AS DATE)), + MONTHS_BETWEEN(CAST('2012-02-29' AS DATE), CAST('2012-03-01' AS DATE)), + MONTHS_BETWEEN(CAST('2012-02-31' AS DATE), CAST('2012-03-01' AS DATE)) +FROM src tablesample (1 rows) +PREHOOK: type: QUERY +PREHOOK: Input: default@src +#### A masked pattern was here #### +POSTHOOK: query: SELECT MONTHS_BETWEEN(CAST('1995-02-02' AS DATE), CAST('1995-01-01' AS DATE)), + MONTHS_BETWEEN(CAST('2003-07-17' AS DATE), CAST('2005-07-06' AS DATE)), + MONTHS_BETWEEN(CAST('2001-06-30' AS DATE), CAST('2000-05-31' AS DATE)), + MONTHS_BETWEEN(CAST('2000-06-01' AS DATE), CAST('2004-07-01' AS DATE)), + MONTHS_BETWEEN(CAST('2002-02-28' AS DATE), CAST('2002-03-01' AS DATE)), + MONTHS_BETWEEN(CAST('2002-02-31' AS DATE), CAST('2002-03-01' AS DATE)), + MONTHS_BETWEEN(CAST('2012-02-29' AS DATE), CAST('2012-03-01' AS DATE)), + MONTHS_BETWEEN(CAST('2012-02-31' AS DATE), CAST('2012-03-01' AS DATE)) +FROM src tablesample (1 rows) +POSTHOOK: type: QUERY +POSTHOOK: Input: default@src +#### A masked pattern was here #### +1.03225806 -23.64516129 13.0 -49.0 -0.12903226 0.06451613 -0.09677419 0.03225806 -- 1.9.3 (Apple Git-50)