This is an automated email from the ASF dual-hosted git repository. gabriellee pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/master by this push: new 0a228a68d6 [Improvement](javaudf) support different date argument for date/datetime type (#13920) 0a228a68d6 is described below commit 0a228a68d68721ba273841b22399ee758ae44d1a Author: Gabriel <gabrielleeb...@gmail.com> AuthorDate: Thu Nov 3 20:33:20 2022 +0800 [Improvement](javaudf) support different date argument for date/datetime type (#13920) --- be/src/vec/functions/function_java_udf.cpp | 27 +- .../apache/doris/analysis/CreateFunctionStmt.java | 12 +- .../java/org/apache/doris/udf/JdbcExecutor.java | 4 +- .../java/org/apache/doris/udf/UdafExecutor.java | 49 +++- .../java/org/apache/doris/udf/UdfExecutor.java | 133 +++------ .../main/java/org/apache/doris/udf/UdfUtils.java | 318 ++++++++++++++++++++- .../data/javaudf_p0/test_javaudf_date.out | 276 ++++++++++++++++++ .../main/java/org/apache/doris/udf/DateTest1.java | 28 ++ .../main/java/org/apache/doris/udf/DateTest2.java | 28 ++ .../main/java/org/apache/doris/udf/DateTest3.java | 26 ++ .../java/org/apache/doris/udf/DateTimeTest1.java | 28 ++ .../java/org/apache/doris/udf/DateTimeTest2.java | 28 ++ .../java/org/apache/doris/udf/DateTimeTest3.java | 28 ++ .../main/java/org/apache/doris/udf/DoubleTest.java | 26 ++ .../suites/javaudf_p0/test_javaudf_date.groovy | 195 +++++++++++++ 15 files changed, 1057 insertions(+), 149 deletions(-) diff --git a/be/src/vec/functions/function_java_udf.cpp b/be/src/vec/functions/function_java_udf.cpp index 467e9e3c66..1a2a52a515 100644 --- a/be/src/vec/functions/function_java_udf.cpp +++ b/be/src/vec/functions/function_java_udf.cpp @@ -112,34 +112,37 @@ Status JavaFunctionCall::execute(FunctionContext* context, Block& block, JniContext* jni_ctx = reinterpret_cast<JniContext*>( context->get_function_state(FunctionContext::THREAD_LOCAL)); int arg_idx = 0; - ColumnPtr args[arguments.size()]; + ColumnPtr data_cols[arguments.size()]; + ColumnPtr null_cols[arguments.size()]; for (size_t col_idx : arguments) { ColumnWithTypeAndName& column = block.get_by_position(col_idx); - args[arg_idx] = column.column->convert_to_full_column_if_const(); + data_cols[arg_idx] = column.column->convert_to_full_column_if_const(); if (!_argument_types[arg_idx]->equals(*column.type)) { return Status::InvalidArgument(strings::Substitute( "$0-th input column's type $1 does not equal to required type $2", arg_idx, column.type->get_name(), _argument_types[arg_idx]->get_name())); } - if (auto* nullable = check_and_get_column<const ColumnNullable>(*args[arg_idx])) { - args[arg_idx] = nullable->get_nested_column_ptr(); - auto null_col = - check_and_get_column<ColumnVector<UInt8>>(nullable->get_null_map_column_ptr()); - jni_ctx->input_nulls_buffer_ptr.get()[arg_idx] = - reinterpret_cast<int64_t>(null_col->get_data().data()); + if (auto* nullable = check_and_get_column<const ColumnNullable>(*data_cols[arg_idx])) { + null_cols[arg_idx] = nullable->get_null_map_column_ptr(); + jni_ctx->input_nulls_buffer_ptr.get()[arg_idx] = reinterpret_cast<int64_t>( + check_and_get_column<ColumnVector<UInt8>>(null_cols[arg_idx]) + ->get_data() + .data()); + data_cols[arg_idx] = nullable->get_nested_column_ptr(); } else { jni_ctx->input_nulls_buffer_ptr.get()[arg_idx] = -1; } - if (args[arg_idx]->is_column_string()) { - const ColumnString* str_col = assert_cast<const ColumnString*>(args[arg_idx].get()); + if (data_cols[arg_idx]->is_column_string()) { + const ColumnString* str_col = + assert_cast<const ColumnString*>(data_cols[arg_idx].get()); jni_ctx->input_values_buffer_ptr.get()[arg_idx] = reinterpret_cast<int64_t>(str_col->get_chars().data()); jni_ctx->input_offsets_ptrs.get()[arg_idx] = reinterpret_cast<int64_t>(str_col->get_offsets().data()); - } else if (args[arg_idx]->is_numeric() || args[arg_idx]->is_column_decimal()) { + } else if (data_cols[arg_idx]->is_numeric() || data_cols[arg_idx]->is_column_decimal()) { jni_ctx->input_values_buffer_ptr.get()[arg_idx] = - reinterpret_cast<int64_t>(args[arg_idx]->get_raw_data().data); + reinterpret_cast<int64_t>(data_cols[arg_idx]->get_raw_data().data); } else { return Status::InvalidArgument( strings::Substitute("Java UDF doesn't support type $0 now !", diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/CreateFunctionStmt.java b/fe/fe-core/src/main/java/org/apache/doris/analysis/CreateFunctionStmt.java index 3c47a4bc16..39fccef1dc 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/analysis/CreateFunctionStmt.java +++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/CreateFunctionStmt.java @@ -513,6 +513,10 @@ public class CreateFunctionStmt extends DdlStmt { } } + public static final Set<Class> DATE_SUPPORTED_JAVA_TYPE = Sets.newHashSet(LocalDate.class, java.util.Date.class, + org.joda.time.LocalDate.class); + public static final Set<Class> DATETIME_SUPPORTED_JAVA_TYPE = Sets.newHashSet(LocalDateTime.class, + org.joda.time.DateTime.class, org.joda.time.LocalDateTime.class); private static final ImmutableMap<PrimitiveType, Set<Class>> PrimitiveTypeToJavaClassType = new ImmutableMap.Builder<PrimitiveType, Set<Class>>() .put(PrimitiveType.BOOLEAN, Sets.newHashSet(Boolean.class, boolean.class)) @@ -525,10 +529,10 @@ public class CreateFunctionStmt extends DdlStmt { .put(PrimitiveType.CHAR, Sets.newHashSet(String.class)) .put(PrimitiveType.VARCHAR, Sets.newHashSet(String.class)) .put(PrimitiveType.STRING, Sets.newHashSet(String.class)) - .put(PrimitiveType.DATE, Sets.newHashSet(LocalDate.class)) - .put(PrimitiveType.DATEV2, Sets.newHashSet(LocalDate.class)) - .put(PrimitiveType.DATETIME, Sets.newHashSet(LocalDateTime.class)) - .put(PrimitiveType.DATETIMEV2, Sets.newHashSet(LocalDateTime.class)) + .put(PrimitiveType.DATE, DATE_SUPPORTED_JAVA_TYPE) + .put(PrimitiveType.DATEV2, DATE_SUPPORTED_JAVA_TYPE) + .put(PrimitiveType.DATETIME, DATETIME_SUPPORTED_JAVA_TYPE) + .put(PrimitiveType.DATETIMEV2, DATETIME_SUPPORTED_JAVA_TYPE) .put(PrimitiveType.LARGEINT, Sets.newHashSet(BigInteger.class)) .put(PrimitiveType.DECIMALV2, Sets.newHashSet(BigDecimal.class)) .put(PrimitiveType.DECIMAL32, Sets.newHashSet(BigDecimal.class)) diff --git a/fe/java-udf/src/main/java/org/apache/doris/udf/JdbcExecutor.java b/fe/java-udf/src/main/java/org/apache/doris/udf/JdbcExecutor.java index 49da4332c7..ffb905b7a1 100644 --- a/fe/java-udf/src/main/java/org/apache/doris/udf/JdbcExecutor.java +++ b/fe/java-udf/src/main/java/org/apache/doris/udf/JdbcExecutor.java @@ -175,7 +175,7 @@ public class JdbcExecutor { public long convertDateToLong(Object obj) { LocalDate date = ((Date) obj).toLocalDate(); - long time = UdfUtils.convertDateTimeToLong(date.getYear(), date.getMonthValue(), date.getDayOfMonth(), + long time = UdfUtils.convertToDateTime(date.getYear(), date.getMonthValue(), date.getDayOfMonth(), 0, 0, 0, true); return time; } @@ -188,7 +188,7 @@ public class JdbcExecutor { } else { date = ((Timestamp) obj).toLocalDateTime(); } - long time = UdfUtils.convertDateTimeToLong(date.getYear(), date.getMonthValue(), date.getDayOfMonth(), + long time = UdfUtils.convertToDateTime(date.getYear(), date.getMonthValue(), date.getDayOfMonth(), date.getHour(), date.getMinute(), date.getSecond(), false); return time; } diff --git a/fe/java-udf/src/main/java/org/apache/doris/udf/UdafExecutor.java b/fe/java-udf/src/main/java/org/apache/doris/udf/UdafExecutor.java index b0ec36882d..3e77b60f38 100644 --- a/fe/java-udf/src/main/java/org/apache/doris/udf/UdafExecutor.java +++ b/fe/java-udf/src/main/java/org/apache/doris/udf/UdafExecutor.java @@ -20,7 +20,7 @@ package org.apache.doris.udf; import org.apache.doris.catalog.Type; import org.apache.doris.common.Pair; import org.apache.doris.thrift.TJavaUdfExecutorCtorParams; -import org.apache.doris.udf.UdfExecutor.JavaUdfDataType; +import org.apache.doris.udf.UdfUtils.JavaUdfDataType; import com.google.common.base.Joiner; import com.google.common.collect.Lists; @@ -40,8 +40,6 @@ import java.math.BigInteger; import java.net.MalformedURLException; import java.net.URLClassLoader; import java.nio.charset.StandardCharsets; -import java.time.LocalDate; -import java.time.LocalDateTime; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -73,6 +71,8 @@ public class UdafExecutor { private URLClassLoader classLoader; private JavaUdfDataType[] argTypes; private JavaUdfDataType retType; + private Class[] argClass; + private Class retClass; /** * Constructor to create an object. @@ -275,16 +275,22 @@ public class UdafExecutor { return true; } case DATE: { - LocalDate date = (LocalDate) obj; - long time = UdfUtils.convertDateTimeToLong(date.getYear(), date.getMonthValue(), date.getDayOfMonth(), - 0, 0, 0, true); + long time = UdfUtils.convertToDate(obj, retClass); UdfUtils.UNSAFE.putLong(UdfUtils.UNSAFE.getLong(null, outputBufferPtr) + row * retType.getLen(), time); return true; } case DATETIME: { - LocalDateTime date = (LocalDateTime) obj; - long time = UdfUtils.convertDateTimeToLong(date.getYear(), date.getMonthValue(), date.getDayOfMonth(), - date.getHour(), date.getMinute(), date.getSecond(), false); + long time = UdfUtils.convertToDateTime(obj, retClass); + UdfUtils.UNSAFE.putLong(UdfUtils.UNSAFE.getLong(null, outputBufferPtr) + row * retType.getLen(), time); + return true; + } + case DATEV2: { + long time = UdfUtils.convertToDateV2(obj, retClass); + UdfUtils.UNSAFE.putLong(UdfUtils.UNSAFE.getLong(null, outputBufferPtr) + row * retType.getLen(), time); + return true; + } + case DATETIMEV2: { + long time = UdfUtils.convertToDateTimeV2(obj, retClass); UdfUtils.UNSAFE.putLong(UdfUtils.UNSAFE.getLong(null, outputBufferPtr) + row * retType.getLen(), time); return true; } @@ -388,13 +394,25 @@ public class UdafExecutor { case DATE: { long data = UdfUtils.UNSAFE.getLong(null, UdfUtils.UNSAFE.getLong(null, UdfUtils.getAddressAtOffset(inputBufferPtrs, i)) + 8L * row); - inputObjects[i] = UdfUtils.convertToDate(data); + inputObjects[i] = UdfUtils.convertDateToJavaDate(data, argClass[i]); break; } case DATETIME: { long data = UdfUtils.UNSAFE.getLong(null, UdfUtils.UNSAFE.getLong(null, UdfUtils.getAddressAtOffset(inputBufferPtrs, i)) + 8L * row); - inputObjects[i] = UdfUtils.convertToDateTime(data); + inputObjects[i] = UdfUtils.convertDateTimeToJavaDateTime(data, argClass[i]); + break; + } + case DATEV2: { + int data = UdfUtils.UNSAFE.getInt(null, + UdfUtils.UNSAFE.getLong(null, UdfUtils.getAddressAtOffset(inputBufferPtrs, i)) + 4L * row); + inputObjects[i] = UdfUtils.convertDateV2ToJavaDate(data, argClass[i]); + break; + } + case DATETIMEV2: { + long data = UdfUtils.UNSAFE.getLong(null, + UdfUtils.UNSAFE.getLong(null, UdfUtils.getAddressAtOffset(inputBufferPtrs, i)) + 8L * row); + inputObjects[i] = UdfUtils.convertDateTimeV2ToJavaDateTime(data, argClass[i]); break; } case LARGEINT: { @@ -476,20 +494,21 @@ public class UdafExecutor { LOG.debug("result function set return parameterTypes has error"); } else { retType = returnType.second; + retClass = methods[idx].getReturnType(); } break; } case UDAF_ADD_FUNCTION: { allMethods.put(methods[idx].getName(), methods[idx]); - Class<?>[] methodTypes = methods[idx].getParameterTypes(); - if (methodTypes.length != parameterTypes.length + 1) { - LOG.debug("add function parameterTypes length not equal " + methodTypes.length + " " + argClass = methods[idx].getParameterTypes(); + if (argClass.length != parameterTypes.length + 1) { + LOG.debug("add function parameterTypes length not equal " + argClass.length + " " + parameterTypes.length + " " + methods[idx].getName()); } if (!(parameterTypes.length == 0)) { Pair<Boolean, JavaUdfDataType[]> inputType = UdfUtils.setArgTypes(parameterTypes, - methodTypes, true); + argClass, true); if (!inputType.first) { LOG.debug("add function set arg parameterTypes has error"); } else { diff --git a/fe/java-udf/src/main/java/org/apache/doris/udf/UdfExecutor.java b/fe/java-udf/src/main/java/org/apache/doris/udf/UdfExecutor.java index fe35aec515..6f45707142 100644 --- a/fe/java-udf/src/main/java/org/apache/doris/udf/UdfExecutor.java +++ b/fe/java-udf/src/main/java/org/apache/doris/udf/UdfExecutor.java @@ -20,7 +20,7 @@ package org.apache.doris.udf; import org.apache.doris.catalog.Type; import org.apache.doris.common.Pair; import org.apache.doris.thrift.TJavaUdfExecutorCtorParams; -import org.apache.doris.thrift.TPrimitiveType; +import org.apache.doris.udf.UdfUtils.JavaUdfDataType; import com.google.common.base.Joiner; import com.google.common.collect.Lists; @@ -37,8 +37,6 @@ import java.math.BigInteger; import java.net.MalformedURLException; import java.net.URLClassLoader; import java.nio.charset.StandardCharsets; -import java.time.LocalDate; -import java.time.LocalDateTime; import java.util.ArrayList; import java.util.Arrays; @@ -85,91 +83,7 @@ public class UdfExecutor { private long rowIdx; private final long batchSizePtr; - - // Data types that are supported as return or argument types in Java UDFs. - public enum JavaUdfDataType { - INVALID_TYPE("INVALID_TYPE", TPrimitiveType.INVALID_TYPE, 0), - BOOLEAN("BOOLEAN", TPrimitiveType.BOOLEAN, 1), - TINYINT("TINYINT", TPrimitiveType.TINYINT, 1), - SMALLINT("SMALLINT", TPrimitiveType.SMALLINT, 2), - INT("INT", TPrimitiveType.INT, 4), - BIGINT("BIGINT", TPrimitiveType.BIGINT, 8), - FLOAT("FLOAT", TPrimitiveType.FLOAT, 4), - DOUBLE("DOUBLE", TPrimitiveType.DOUBLE, 8), - CHAR("CHAR", TPrimitiveType.CHAR, 0), - VARCHAR("VARCHAR", TPrimitiveType.VARCHAR, 0), - STRING("STRING", TPrimitiveType.STRING, 0), - DATE("DATE", TPrimitiveType.DATE, 8), - DATETIME("DATETIME", TPrimitiveType.DATETIME, 8), - LARGEINT("LARGEINT", TPrimitiveType.LARGEINT, 16), - DECIMALV2("DECIMALV2", TPrimitiveType.DECIMALV2, 16); - - private final String description; - private final TPrimitiveType thriftType; - private final int len; - - JavaUdfDataType(String description, TPrimitiveType thriftType, int len) { - this.description = description; - this.thriftType = thriftType; - this.len = len; - } - - @Override - public String toString() { - return description; - } - - public TPrimitiveType getPrimitiveType() { - return thriftType; - } - - public int getLen() { - return len; - } - - public static JavaUdfDataType getType(Class<?> c) { - if (c == boolean.class || c == Boolean.class) { - return JavaUdfDataType.BOOLEAN; - } else if (c == byte.class || c == Byte.class) { - return JavaUdfDataType.TINYINT; - } else if (c == short.class || c == Short.class) { - return JavaUdfDataType.SMALLINT; - } else if (c == int.class || c == Integer.class) { - return JavaUdfDataType.INT; - } else if (c == long.class || c == Long.class) { - return JavaUdfDataType.BIGINT; - } else if (c == float.class || c == Float.class) { - return JavaUdfDataType.FLOAT; - } else if (c == double.class || c == Double.class) { - return JavaUdfDataType.DOUBLE; - } else if (c == char.class || c == Character.class) { - return JavaUdfDataType.CHAR; - } else if (c == String.class) { - return JavaUdfDataType.STRING; - } else if (c == LocalDate.class) { - return JavaUdfDataType.DATE; - } else if (c == LocalDateTime.class) { - return JavaUdfDataType.DATETIME; - } else if (c == BigInteger.class) { - return JavaUdfDataType.LARGEINT; - } else if (c == BigDecimal.class) { - return JavaUdfDataType.DECIMALV2; - } - return JavaUdfDataType.INVALID_TYPE; - } - - public static boolean isSupported(Type t) { - for (JavaUdfDataType javaType : JavaUdfDataType.values()) { - if (javaType == JavaUdfDataType.INVALID_TYPE) { - continue; - } - if (javaType.getPrimitiveType() == t.getPrimitiveType().toThrift()) { - return true; - } - } - return false; - } - } + private Class[] argClass; /** * Create a UdfExecutor, using parameters from a serialized thrift object. Used by @@ -350,17 +264,22 @@ public class UdfExecutor { return true; } case DATE: { - LocalDate date = (LocalDate) obj; - long time = - UdfUtils.convertDateTimeToLong(date.getYear(), date.getMonthValue(), date.getDayOfMonth(), 0, 0, - 0, true); + long time = UdfUtils.convertToDate(obj, method.getReturnType()); UdfUtils.UNSAFE.putLong(UdfUtils.UNSAFE.getLong(null, outputBufferPtr) + row * retType.getLen(), time); return true; } case DATETIME: { - LocalDateTime date = (LocalDateTime) obj; - long time = UdfUtils.convertDateTimeToLong(date.getYear(), date.getMonthValue(), date.getDayOfMonth(), - date.getHour(), date.getMinute(), date.getSecond(), false); + long time = UdfUtils.convertToDateTime(obj, method.getReturnType()); + UdfUtils.UNSAFE.putLong(UdfUtils.UNSAFE.getLong(null, outputBufferPtr) + row * retType.getLen(), time); + return true; + } + case DATEV2: { + int time = UdfUtils.convertToDateV2(obj, method.getReturnType()); + UdfUtils.UNSAFE.putInt(UdfUtils.UNSAFE.getLong(null, outputBufferPtr) + row * retType.getLen(), time); + return true; + } + case DATETIMEV2: { + long time = UdfUtils.convertToDateTimeV2(obj, method.getReturnType()); UdfUtils.UNSAFE.putLong(UdfUtils.UNSAFE.getLong(null, outputBufferPtr) + row * retType.getLen(), time); return true; } @@ -459,13 +378,25 @@ public class UdfExecutor { case DATE: { long data = UdfUtils.UNSAFE.getLong(null, UdfUtils.UNSAFE.getLong(null, UdfUtils.getAddressAtOffset(inputBufferPtrs, i)) + 8L * row); - inputObjects[i] = UdfUtils.convertToDate(data); + inputObjects[i] = UdfUtils.convertDateToJavaDate(data, argClass[i]); break; } case DATETIME: { long data = UdfUtils.UNSAFE.getLong(null, UdfUtils.UNSAFE.getLong(null, UdfUtils.getAddressAtOffset(inputBufferPtrs, i)) + 8L * row); - inputObjects[i] = UdfUtils.convertToDateTime(data); + inputObjects[i] = UdfUtils.convertDateTimeToJavaDateTime(data, argClass[i]); + break; + } + case DATEV2: { + int data = UdfUtils.UNSAFE.getInt(null, + UdfUtils.UNSAFE.getLong(null, UdfUtils.getAddressAtOffset(inputBufferPtrs, i)) + 4L * row); + inputObjects[i] = UdfUtils.convertDateV2ToJavaDate(data, argClass[i]); + break; + } + case DATETIMEV2: { + long data = UdfUtils.UNSAFE.getLong(null, + UdfUtils.UNSAFE.getLong(null, UdfUtils.getAddressAtOffset(inputBufferPtrs, i)) + 8L * row); + inputObjects[i] = UdfUtils.convertDateTimeV2ToJavaDateTime(data, argClass[i]); break; } case LARGEINT: { @@ -534,15 +465,15 @@ public class UdfExecutor { continue; } signatures.add(m.toGenericString()); - Class<?>[] methodTypes = m.getParameterTypes(); + argClass = m.getParameterTypes(); // Try to match the arguments - if (methodTypes.length != parameterTypes.length) { + if (argClass.length != parameterTypes.length) { continue; } method = m; Pair<Boolean, JavaUdfDataType> returnType; - if (methodTypes.length == 0 && parameterTypes.length == 0) { + if (argClass.length == 0 && parameterTypes.length == 0) { // Special case where the UDF doesn't take any input args returnType = UdfUtils.setReturnType(funcRetType, m.getReturnType()); if (!returnType.first) { @@ -560,7 +491,7 @@ public class UdfExecutor { } else { retType = returnType.second; } - Pair<Boolean, JavaUdfDataType[]> inputType = UdfUtils.setArgTypes(parameterTypes, methodTypes, false); + Pair<Boolean, JavaUdfDataType[]> inputType = UdfUtils.setArgTypes(parameterTypes, argClass, false); if (!inputType.first) { continue; } else { diff --git a/fe/java-udf/src/main/java/org/apache/doris/udf/UdfUtils.java b/fe/java-udf/src/main/java/org/apache/doris/udf/UdfUtils.java index dbca62a64e..00df57ccdc 100644 --- a/fe/java-udf/src/main/java/org/apache/doris/udf/UdfUtils.java +++ b/fe/java-udf/src/main/java/org/apache/doris/udf/UdfUtils.java @@ -17,6 +17,7 @@ package org.apache.doris.udf; +import org.apache.doris.analysis.CreateFunctionStmt; import org.apache.doris.catalog.PrimitiveType; import org.apache.doris.catalog.ScalarType; import org.apache.doris.catalog.Type; @@ -25,22 +26,28 @@ import org.apache.doris.thrift.TPrimitiveType; import org.apache.doris.thrift.TScalarType; import org.apache.doris.thrift.TTypeDesc; import org.apache.doris.thrift.TTypeNode; -import org.apache.doris.udf.UdfExecutor.JavaUdfDataType; import com.google.common.base.Preconditions; +import com.google.common.collect.Sets; +import org.apache.log4j.Logger; import sun.misc.Unsafe; import java.io.File; import java.lang.reflect.Field; +import java.math.BigDecimal; +import java.math.BigInteger; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; import java.security.AccessController; import java.security.PrivilegedAction; +import java.time.DateTimeException; import java.time.LocalDate; import java.time.LocalDateTime; +import java.util.Set; public class UdfUtils { + private static final Logger LOG = Logger.getLogger(UdfUtils.class); public static final Unsafe UNSAFE; private static final long UNSAFE_COPY_THRESHOLD = 1024L * 1024L; public static final long BYTE_ARRAY_OFFSET; @@ -59,6 +66,93 @@ public class UdfUtils { BYTE_ARRAY_OFFSET = UNSAFE.arrayBaseOffset(byte[].class); } + // Data types that are supported as return or argument types in Java UDFs. + public enum JavaUdfDataType { + INVALID_TYPE("INVALID_TYPE", TPrimitiveType.INVALID_TYPE, 0), + BOOLEAN("BOOLEAN", TPrimitiveType.BOOLEAN, 1), + TINYINT("TINYINT", TPrimitiveType.TINYINT, 1), + SMALLINT("SMALLINT", TPrimitiveType.SMALLINT, 2), + INT("INT", TPrimitiveType.INT, 4), + BIGINT("BIGINT", TPrimitiveType.BIGINT, 8), + FLOAT("FLOAT", TPrimitiveType.FLOAT, 4), + DOUBLE("DOUBLE", TPrimitiveType.DOUBLE, 8), + CHAR("CHAR", TPrimitiveType.CHAR, 0), + VARCHAR("VARCHAR", TPrimitiveType.VARCHAR, 0), + STRING("STRING", TPrimitiveType.STRING, 0), + DATE("DATE", TPrimitiveType.DATE, 8), + DATETIME("DATETIME", TPrimitiveType.DATETIME, 8), + LARGEINT("LARGEINT", TPrimitiveType.LARGEINT, 16), + DECIMALV2("DECIMALV2", TPrimitiveType.DECIMALV2, 16), + DATEV2("DATEV2", TPrimitiveType.DATEV2, 4), + DATETIMEV2("DATETIMEV2", TPrimitiveType.DATETIMEV2, 8); + + private final String description; + private final TPrimitiveType thriftType; + private final int len; + + JavaUdfDataType(String description, TPrimitiveType thriftType, int len) { + this.description = description; + this.thriftType = thriftType; + this.len = len; + } + + @Override + public String toString() { + return description; + } + + public TPrimitiveType getPrimitiveType() { + return thriftType; + } + + public int getLen() { + return len; + } + + public static Set<JavaUdfDataType> getCandidateTypes(Class<?> c) { + if (c == boolean.class || c == Boolean.class) { + return Sets.newHashSet(JavaUdfDataType.BOOLEAN); + } else if (c == byte.class || c == Byte.class) { + return Sets.newHashSet(JavaUdfDataType.TINYINT); + } else if (c == short.class || c == Short.class) { + return Sets.newHashSet(JavaUdfDataType.SMALLINT); + } else if (c == int.class || c == Integer.class) { + return Sets.newHashSet(JavaUdfDataType.INT); + } else if (c == long.class || c == Long.class) { + return Sets.newHashSet(JavaUdfDataType.BIGINT); + } else if (c == float.class || c == Float.class) { + return Sets.newHashSet(JavaUdfDataType.FLOAT); + } else if (c == double.class || c == Double.class) { + return Sets.newHashSet(JavaUdfDataType.DOUBLE); + } else if (c == char.class || c == Character.class) { + return Sets.newHashSet(JavaUdfDataType.CHAR); + } else if (c == String.class) { + return Sets.newHashSet(JavaUdfDataType.STRING); + } else if (CreateFunctionStmt.DATE_SUPPORTED_JAVA_TYPE.contains(c)) { + return Sets.newHashSet(JavaUdfDataType.DATE, JavaUdfDataType.DATEV2); + } else if (CreateFunctionStmt.DATETIME_SUPPORTED_JAVA_TYPE.contains(c)) { + return Sets.newHashSet(JavaUdfDataType.DATETIME, JavaUdfDataType.DATETIMEV2); + } else if (c == BigInteger.class) { + return Sets.newHashSet(JavaUdfDataType.LARGEINT); + } else if (c == BigDecimal.class) { + return Sets.newHashSet(JavaUdfDataType.DECIMALV2); + } + return Sets.newHashSet(JavaUdfDataType.INVALID_TYPE); + } + + public static boolean isSupported(Type t) { + for (JavaUdfDataType javaType : JavaUdfDataType.values()) { + if (javaType == JavaUdfDataType.INVALID_TYPE) { + continue; + } + if (javaType.getPrimitiveType() == t.getPrimitiveType().toThrift()) { + return true; + } + } + return false; + } + } + protected static Pair<Type, Integer> fromThrift(TTypeDesc typeDesc, int nodeIdx) throws InternalException { TTypeNode node = typeDesc.getTypes().get(nodeIdx); Type type = null; @@ -134,14 +228,16 @@ public class UdfUtils { if (!JavaUdfDataType.isSupported(retType)) { throw new InternalException("Unsupported return type: " + retType.toSql()); } - JavaUdfDataType javaType = JavaUdfDataType.getType(udfReturnType); + Set<JavaUdfDataType> javaTypes = JavaUdfDataType.getCandidateTypes(udfReturnType); // Check if the evaluate method return type is compatible with the return type from // the function definition. This happens when both of them map to the same primitive // type. - if (retType.getPrimitiveType().toThrift() != javaType.getPrimitiveType()) { - return Pair.of(false, javaType); + Object[] res = javaTypes.stream().filter( + t -> t.getPrimitiveType() == retType.getPrimitiveType().toThrift()).toArray(); + if (res.length == 0) { + return Pair.of(false, (JavaUdfDataType) javaTypes.toArray()[0]); } - return Pair.of(true, javaType); + return Pair.of(true, (JavaUdfDataType) res[0]); } /** @@ -154,18 +250,48 @@ public class UdfUtils { JavaUdfDataType[] inputArgTypes = new JavaUdfDataType[parameterTypes.length]; int firstPos = isUdaf ? 1 : 0; for (int i = 0; i < parameterTypes.length; ++i) { - inputArgTypes[i] = JavaUdfDataType.getType(udfArgTypes[i + firstPos]); - if (inputArgTypes[i].getPrimitiveType() != parameterTypes[i].getPrimitiveType().toThrift()) { + Set<JavaUdfDataType> javaTypes = JavaUdfDataType.getCandidateTypes(udfArgTypes[i + firstPos]); + int finalI = i; + Object[] res = javaTypes.stream().filter( + t -> t.getPrimitiveType() == parameterTypes[finalI].getPrimitiveType().toThrift()).toArray(); + if (res.length == 0) { + inputArgTypes[i] = (JavaUdfDataType) javaTypes.toArray()[0]; return Pair.of(false, inputArgTypes); + } else { + inputArgTypes[i] = (JavaUdfDataType) res[0]; } } return Pair.of(true, inputArgTypes); } + public static Object convertDateTimeV2ToJavaDateTime(long date, Class clz) { + int year = (int) (date >> 46); + int yearMonth = (int) (date >> 42); + int yearMonthDay = (int) (date >> 37); + + int month = (yearMonth & 0XF); + int day = (yearMonthDay & 0X1F); + + int hour = (int) ((date >> 32) & 0X1F); + int minute = (int) ((date >> 26) & 0X3F); + int second = (int) ((date >> 20) & 0X3F); + //here don't need those bits are type = ((minus_type_neg >> 1) & 0x7); + + if (LocalDateTime.class.equals(clz)) { + return convertToLocalDateTime(year, month, day, hour, minute, second); + } else if (org.joda.time.DateTime.class.equals(clz)) { + return convertToJodaDateTime(year, month, day, hour, minute, second); + } else if (org.joda.time.LocalDateTime.class.equals(clz)) { + return convertToJodaLocalDateTime(year, month, day, hour, minute, second); + } else { + return null; + } + } + /** * input is a 64bit num from backend, and then get year, month, day, hour, minus, second by the order of bits. */ - public static LocalDateTime convertToDateTime(long date) { + public static Object convertDateTimeToJavaDateTime(long date, Class clz) { int year = (int) (date >> 48); int yearMonth = (int) (date >> 40); int yearMonthDay = (int) (date >> 32); @@ -181,29 +307,149 @@ public class UdfUtils { int second = (minuteTypeNeg >> 4); //here don't need those bits are type = ((minus_type_neg >> 1) & 0x7); - LocalDateTime value = LocalDateTime.of(year, month, day, hour, minute, second); + if (LocalDateTime.class.equals(clz)) { + return convertToLocalDateTime(year, month, day, hour, minute, second); + } else if (org.joda.time.DateTime.class.equals(clz)) { + return convertToJodaDateTime(year, month, day, hour, minute, second); + } else if (org.joda.time.LocalDateTime.class.equals(clz)) { + return convertToJodaLocalDateTime(year, month, day, hour, minute, second); + } else { + return null; + } + } + + public static Object convertDateV2ToJavaDate(int date, Class clz) { + int year = date >> 9; + int month = (date >> 5) & 0XF; + int day = date & 0X1F; + if (LocalDate.class.equals(clz)) { + return convertToLocalDate(year, month, day); + } else if (java.util.Date.class.equals(clz)) { + return convertToJavaDate(year, month, day); + } else if (org.joda.time.LocalDate.class.equals(clz)) { + return convertToJodaDate(year, month, day); + } else { + return null; + } + } + + public static LocalDateTime convertToLocalDateTime(int year, int month, int day, + int hour, int minute, int second) { + LocalDateTime value = null; + try { + value = LocalDateTime.of(year, month, day, hour, minute, second); + } catch (DateTimeException e) { + LOG.warn("Error occurs when parsing date time value: {}", e); + } return value; } - /** - * a 64bit num convertToDate. - */ - public static LocalDate convertToDate(long date) { + public static org.joda.time.DateTime convertToJodaDateTime(int year, int month, int day, + int hour, int minute, int second) { + try { + return new org.joda.time.DateTime(year, month, day, hour, minute, second); + } catch (Exception e) { + return null; + } + } + + public static org.joda.time.LocalDateTime convertToJodaLocalDateTime(int year, int month, int day, + int hour, int minute, int second) { + try { + return new org.joda.time.LocalDateTime(year, month, day, hour, minute, second); + } catch (Exception e) { + return null; + } + } + + public static Object convertDateToJavaDate(long date, Class clz) { int year = (int) (date >> 48); int yearMonth = (int) (date >> 40); int yearMonthDay = (int) (date >> 32); int month = (yearMonth & 0XFF); int day = (yearMonthDay & 0XFF); - LocalDate value = LocalDate.of(year, month, day); + if (LocalDate.class.equals(clz)) { + return convertToLocalDate(year, month, day); + } else if (java.util.Date.class.equals(clz)) { + return convertToJavaDate(year, month, day); + } else if (org.joda.time.LocalDate.class.equals(clz)) { + return convertToJodaDate(year, month, day); + } else { + return null; + } + } + + /** + * a 64bit num convertToDate. + */ + public static LocalDate convertToLocalDate(int year, int month, int day) { + LocalDate value = null; + try { + value = LocalDate.of(year, month, day); + } catch (DateTimeException e) { + LOG.warn("Error occurs when parsing date value: {}", e); + } return value; } + public static org.joda.time.LocalDate convertToJodaDate(int year, int month, int day) { + try { + return new org.joda.time.LocalDate(year, month, day); + } catch (Exception e) { + return null; + } + } + + public static java.util.Date convertToJavaDate(int year, int month, int day) { + try { + return new java.util.Date(year - 1900, month - 1, day); + } catch (Exception e) { + return null; + } + } + /** * input is the second, minute, hours, day , month and year respectively. * and then combining all num to a 64bit value return to backend; */ - public static long convertDateTimeToLong(int year, int month, int day, int hour, int minute, int second, + public static long convertToDateTime(Object obj, Class clz) { + if (LocalDateTime.class.equals(clz)) { + LocalDateTime date = (LocalDateTime) obj; + return convertToDateTime(date.getYear(), date.getMonthValue(), date.getDayOfMonth(), date.getHour(), + date.getMinute(), date.getSecond(), false); + } else if (org.joda.time.DateTime.class.equals(clz)) { + org.joda.time.DateTime date = (org.joda.time.DateTime) obj; + return convertToDateTime(date.getYear(), date.getMonthOfYear(), date.getDayOfMonth(), date.getHourOfDay(), + date.getMinuteOfHour(), date.getSecondOfMinute(), false); + } else if (org.joda.time.LocalDateTime.class.equals(clz)) { + org.joda.time.LocalDateTime date = (org.joda.time.LocalDateTime) obj; + return convertToDateTime(date.getYear(), date.getMonthOfYear(), date.getDayOfMonth(), date.getHourOfDay(), + date.getMinuteOfHour(), date.getSecondOfMinute(), false); + } else { + return 0; + } + } + + public static long convertToDate(Object obj, Class clz) { + if (LocalDate.class.equals(clz)) { + LocalDate date = (LocalDate) obj; + return convertToDateTime(date.getYear(), date.getMonthValue(), date.getDayOfMonth(), 0, + 0, 0, true); + } else if (java.util.Date.class.equals(clz)) { + java.util.Date date = (java.util.Date) obj; + return convertToDateTime(date.getYear() + 1900, date.getMonth(), date.getDay(), 0, + 0, 0, true); + } else if (org.joda.time.LocalDate.class.equals(clz)) { + org.joda.time.LocalDate date = (org.joda.time.LocalDate) obj; + return convertToDateTime(date.getYear(), date.getDayOfMonth(), date.getDayOfMonth(), 0, + 0, 0, true); + } else { + return 0; + } + } + + public static long convertToDateTime(int year, int month, int day, int hour, int minute, int second, boolean isDate) { long time = 0; time = time + year; @@ -219,6 +465,48 @@ public class UdfUtils { return time; } + public static long convertToDateTimeV2(int year, int month, int day, int hour, int minute, int second) { + return (long) second << 20 | (long) minute << 26 | (long) hour << 32 + | (long) day << 37 | (long) month << 42 | (long) year << 46; + } + + public static long convertToDateTimeV2(Object obj, Class clz) { + if (LocalDateTime.class.equals(clz)) { + LocalDateTime date = (LocalDateTime) obj; + return convertToDateTimeV2(date.getYear(), date.getMonthValue(), date.getDayOfMonth(), date.getHour(), + date.getMinute(), date.getSecond()); + } else if (org.joda.time.DateTime.class.equals(clz)) { + org.joda.time.DateTime date = (org.joda.time.DateTime) obj; + return convertToDateTimeV2(date.getYear(), date.getMonthOfYear(), date.getDayOfMonth(), date.getHourOfDay(), + date.getMinuteOfHour(), date.getSecondOfMinute()); + } else if (org.joda.time.LocalDateTime.class.equals(clz)) { + org.joda.time.LocalDateTime date = (org.joda.time.LocalDateTime) obj; + return convertToDateTimeV2(date.getYear(), date.getMonthOfYear(), date.getDayOfMonth(), date.getHourOfDay(), + date.getMinuteOfHour(), date.getSecondOfMinute()); + } else { + return 0; + } + } + + public static int convertToDateV2(int year, int month, int day) { + return (int) (day | (long) month << 5 | (long) year << 9); + } + + public static int convertToDateV2(Object obj, Class clz) { + if (LocalDate.class.equals(clz)) { + LocalDate date = (LocalDate) obj; + return convertToDateV2(date.getYear(), date.getMonthValue(), date.getDayOfMonth()); + } else if (java.util.Date.class.equals(clz)) { + java.util.Date date = (java.util.Date) obj; + return convertToDateV2(date.getYear(), date.getMonth(), date.getDay()); + } else if (org.joda.time.LocalDate.class.equals(clz)) { + org.joda.time.LocalDate date = (org.joda.time.LocalDate) obj; + return convertToDateV2(date.getYear(), date.getDayOfMonth(), date.getDayOfMonth()); + } else { + return 0; + } + } + /** * Change the order of the bytes, Because JVM is Big-Endian , x86 is Little-Endian. */ diff --git a/regression-test/data/javaudf_p0/test_javaudf_date.out b/regression-test/data/javaudf_p0/test_javaudf_date.out new file mode 100644 index 0000000000..4fb1082ba4 --- /dev/null +++ b/regression-test/data/javaudf_p0/test_javaudf_date.out @@ -0,0 +1,276 @@ +-- This file is automatically generated. You should know what you did if you want to edit this +-- !select_default -- +1 2022-01-01 2022-01-01T11:11:11 2022-01-01 2022-01-01T11:11:11 +1 2022-01-01 2022-01-01T11:11:11 2022-01-01 2022-01-01T11:11:11 +1 2022-01-01 2022-01-01T11:11:11 2022-01-01 2022-01-01T11:11:11 +1 2022-01-01 2022-01-01T11:11:11 2022-01-01 2022-01-01T11:11:11 +1 2022-01-01 2022-01-01T11:11:11 2022-01-01 2022-01-01T11:11:11 +1 2022-01-01 2022-01-01T11:11:11 2022-01-01 2022-01-01T11:11:11 +1 2022-01-01 2022-01-01T11:11:11 2022-01-01 2022-01-01T11:11:11 +1 2022-01-01 2022-01-01T11:11:11 2022-01-01 2022-01-01T11:11:11 +1 2022-01-01 2022-01-01T11:11:11 2022-01-01 2022-01-01T11:11:11 + +-- !select -- +true +true +true +true +true +true +true +true +true + +-- !select -- +false +false +false +false +false +false +false +false +false + +-- !select -- +\N +\N +\N +\N +\N +\N +\N +\N +\N + +-- !select -- +true +true +true +true +true +true +true +true +true + +-- !select -- +false +false +false +false +false +false +false +false +false + +-- !select -- +\N +\N +\N +\N +\N +\N +\N +\N +\N + +-- !select -- +true +true +true +true +true +true +true +true +true + +-- !select -- +false +false +false +false +false +false +false +false +false + +-- !select -- +\N +\N +\N +\N +\N +\N +\N +\N +\N + +-- !select -- +true +true +true +true +true +true +true +true +true + +-- !select -- +false +false +false +false +false +false +false +false +false + +-- !select -- +\N +\N +\N +\N +\N +\N +\N +\N +\N + +-- !select -- +true +true +true +true +true +true +true +true +true + +-- !select -- +false +false +false +false +false +false +false +false +false + +-- !select -- +\N +\N +\N +\N +\N +\N +\N +\N +\N + +-- !select -- +true +true +true +true +true +true +true +true +true + +-- !select -- +false +false +false +false +false +false +false +false +false + +-- !select -- +\N +\N +\N +\N +\N +\N +\N +\N +\N + +-- !select -- +true +true +true +true +true +true +true +true +true + +-- !select -- +true +true +true +true +true +true +true +true +true + +-- !select -- +true +true +true +true +true +true +true +true +true + +-- !select -- +true +true +true +true +true +true +true +true +true + +-- !select -- +true +true +true +true +true +true +true +true +true + +-- !select -- +true +true +true +true +true +true +true +true +true + diff --git a/regression-test/java-udf-src/src/main/java/org/apache/doris/udf/DateTest1.java b/regression-test/java-udf-src/src/main/java/org/apache/doris/udf/DateTest1.java new file mode 100644 index 0000000000..217bc6d48f --- /dev/null +++ b/regression-test/java-udf-src/src/main/java/org/apache/doris/udf/DateTest1.java @@ -0,0 +1,28 @@ +// 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.doris.udf; + +import org.apache.hadoop.hive.ql.exec.UDF; + +import java.time.LocalDate; + +public class DateTest1 extends UDF { + public Boolean evaluate(LocalDate date, LocalDate date2) { + return date == null || date2 == null ? null : date.isAfter(date2); + } +} diff --git a/regression-test/java-udf-src/src/main/java/org/apache/doris/udf/DateTest2.java b/regression-test/java-udf-src/src/main/java/org/apache/doris/udf/DateTest2.java new file mode 100644 index 0000000000..05560cb474 --- /dev/null +++ b/regression-test/java-udf-src/src/main/java/org/apache/doris/udf/DateTest2.java @@ -0,0 +1,28 @@ +// 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.doris.udf; + +import org.apache.hadoop.hive.ql.exec.UDF; + +import java.time.LocalDate; + +public class DateTest2 extends UDF { + public Boolean evaluate(java.util.Date date, java.util.Date date2) { + return date == null || date2 == null ? null : date.after(date2); + } +} diff --git a/regression-test/java-udf-src/src/main/java/org/apache/doris/udf/DateTest3.java b/regression-test/java-udf-src/src/main/java/org/apache/doris/udf/DateTest3.java new file mode 100644 index 0000000000..278427f549 --- /dev/null +++ b/regression-test/java-udf-src/src/main/java/org/apache/doris/udf/DateTest3.java @@ -0,0 +1,26 @@ +// 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.doris.udf; + +import org.apache.hadoop.hive.ql.exec.UDF; + +public class DateTest3 extends UDF { + public Boolean evaluate(org.joda.time.LocalDate date, org.joda.time.LocalDate date2) { + return date == null || date2 == null ? null : date.isAfter(date2); + } +} diff --git a/regression-test/java-udf-src/src/main/java/org/apache/doris/udf/DateTimeTest1.java b/regression-test/java-udf-src/src/main/java/org/apache/doris/udf/DateTimeTest1.java new file mode 100644 index 0000000000..ecbbacfd7b --- /dev/null +++ b/regression-test/java-udf-src/src/main/java/org/apache/doris/udf/DateTimeTest1.java @@ -0,0 +1,28 @@ +// 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.doris.udf; + +import org.apache.hadoop.hive.ql.exec.UDF; + +import java.time.LocalDateTime; + +public class DateTimeTest1 extends UDF { + public Boolean evaluate(LocalDateTime date, LocalDateTime date2) { + return date == null || date2 == null ? null : date.isAfter(date2); + } +} diff --git a/regression-test/java-udf-src/src/main/java/org/apache/doris/udf/DateTimeTest2.java b/regression-test/java-udf-src/src/main/java/org/apache/doris/udf/DateTimeTest2.java new file mode 100644 index 0000000000..83544313a7 --- /dev/null +++ b/regression-test/java-udf-src/src/main/java/org/apache/doris/udf/DateTimeTest2.java @@ -0,0 +1,28 @@ +// 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.doris.udf; + +import org.apache.hadoop.hive.ql.exec.UDF; + +import java.time.LocalDateTime; + +public class DateTimeTest2 extends UDF { + public Boolean evaluate(org.joda.time.LocalDateTime date, org.joda.time.LocalDateTime date2) { + return date == null || date2 == null ? null : date.isAfter(date2); + } +} diff --git a/regression-test/java-udf-src/src/main/java/org/apache/doris/udf/DateTimeTest3.java b/regression-test/java-udf-src/src/main/java/org/apache/doris/udf/DateTimeTest3.java new file mode 100644 index 0000000000..eeadadff6b --- /dev/null +++ b/regression-test/java-udf-src/src/main/java/org/apache/doris/udf/DateTimeTest3.java @@ -0,0 +1,28 @@ +// 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.doris.udf; + +import org.apache.hadoop.hive.ql.exec.UDF; + +import java.time.LocalDateTime; + +public class DateTimeTest3 extends UDF { + public Boolean evaluate(org.joda.time.DateTime date, org.joda.time.DateTime date2) { + return date == null || date2 == null ? null : date.isAfter(date2); + } +} diff --git a/regression-test/java-udf-src/src/main/java/org/apache/doris/udf/DoubleTest.java b/regression-test/java-udf-src/src/main/java/org/apache/doris/udf/DoubleTest.java new file mode 100644 index 0000000000..150956d8c9 --- /dev/null +++ b/regression-test/java-udf-src/src/main/java/org/apache/doris/udf/DoubleTest.java @@ -0,0 +1,26 @@ +// 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.doris.udf; + +import org.apache.hadoop.hive.ql.exec.UDF; + +public class DoubleTest extends UDF { + public Double evaluate(Double dou,Double dou2) { + return dou == null || dou2 == null ? null : dou + dou2; + } +} diff --git a/regression-test/suites/javaudf_p0/test_javaudf_date.groovy b/regression-test/suites/javaudf_p0/test_javaudf_date.groovy new file mode 100644 index 0000000000..a5e87b8e46 --- /dev/null +++ b/regression-test/suites/javaudf_p0/test_javaudf_date.groovy @@ -0,0 +1,195 @@ +// 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. + +import org.codehaus.groovy.runtime.IOGroovyMethods + +import java.nio.charset.StandardCharsets +import java.nio.file.Files +import java.nio.file.Paths + +suite("test_javaudf_date") { + def tableName = "test_javaudf_date" + def jarPath = """${context.file.parent}/jars/java-udf-case-jar-with-dependencies.jar""" + + try { + sql """ DROP TABLE IF EXISTS ${tableName} """ + sql """ + CREATE TABLE IF NOT EXISTS ${tableName} ( + `user_id` INT NOT NULL COMMENT "用户id", + `date_col` date NOT NULL, + `datetime_col` datetime NOT NULL, + `datev2_col` datev2 NOT NULL, + `datetimev2_col` datetimev2 NOT NULL + ) + DISTRIBUTED BY HASH(user_id) PROPERTIES("replication_num" = "1"); + """ + StringBuilder sb = new StringBuilder() + int i = 1 + for (; i < 9; i ++) { + sb.append(""" + (1, '2022-01-01', '2022-01-01 11:11:11', '2022-01-01', '2022-01-01 11:11:11'), + """) + } + sb.append(""" + (1, '2022-01-01', '2022-01-01 11:11:11', '2022-01-01', '2022-01-01 11:11:11') + """) + sql """ INSERT INTO ${tableName} VALUES + ${sb.toString()} + """ + qt_select_default """ SELECT * FROM ${tableName} t ORDER BY user_id; """ + + File path = new File(jarPath) + if (!path.exists()) { + throw new IllegalStateException("""${jarPath} doesn't exist! """) + } + + sql """ CREATE FUNCTION java_udf_date_test1(date, date) RETURNS boolean PROPERTIES ( + "file"="file://${jarPath}", + "symbol"="org.apache.doris.udf.DateTest1", + "type"="JAVA_UDF" + ); """ + + qt_select """ SELECT java_udf_date_test1(date_col, '2011-01-01') result FROM ${tableName} ORDER BY result; """ + qt_select """ SELECT java_udf_date_test1('2011-01-01', '2011-01-01') result FROM ${tableName} ORDER BY result; """ + qt_select """ SELECT java_udf_date_test1(null, '2011-01-01') result FROM ${tableName} ORDER BY result; """ + + sql """ DROP FUNCTION java_udf_date_test1(date, date); """ + + sql """ CREATE FUNCTION java_udf_date_test2(date, date) RETURNS boolean PROPERTIES ( + "file"="file://${jarPath}", + "symbol"="org.apache.doris.udf.DateTest2", + "type"="JAVA_UDF" + ); """ + + qt_select """ SELECT java_udf_date_test2(date_col, '2011-01-01') result FROM ${tableName} ORDER BY result; """ + qt_select """ SELECT java_udf_date_test2('2011-01-01', '2011-01-01') result FROM ${tableName} ORDER BY result; """ + qt_select """ SELECT java_udf_date_test2(null, '2011-01-01') result FROM ${tableName} ORDER BY result; """ + + sql """ DROP FUNCTION java_udf_date_test2(date, date); """ + + sql """ CREATE FUNCTION java_udf_date_test3(date, date) RETURNS boolean PROPERTIES ( + "file"="file://${jarPath}", + "symbol"="org.apache.doris.udf.DateTest3", + "type"="JAVA_UDF" + ); """ + + qt_select """ SELECT java_udf_date_test3(date_col, '2011-01-01') result FROM ${tableName} ORDER BY result; """ + qt_select """ SELECT java_udf_date_test3('2011-01-01', '2011-01-01') result FROM ${tableName} ORDER BY result; """ + qt_select """ SELECT java_udf_date_test3(null, '2011-01-01') result FROM ${tableName} ORDER BY result; """ + + sql """ DROP FUNCTION java_udf_date_test3(date, date); """ + + sql """ CREATE FUNCTION java_udf_datetime_test1(datetime, datetime) RETURNS boolean PROPERTIES ( + "file"="file://${jarPath}", + "symbol"="org.apache.doris.udf.DateTimeTest1", + "type"="JAVA_UDF" + ); """ + + qt_select """ SELECT java_udf_datetime_test1(datetime_col, '2011-01-01') result FROM ${tableName} ORDER BY result; """ + qt_select """ SELECT java_udf_datetime_test1('2011-01-01', '2011-01-01') result FROM ${tableName} ORDER BY result; """ + qt_select """ SELECT java_udf_datetime_test1(null, '2011-01-01') result FROM ${tableName} ORDER BY result; """ + + sql """ DROP FUNCTION java_udf_datetime_test1(datetime, datetime); """ + + sql """ CREATE FUNCTION java_udf_datetime_test2(datetime, datetime) RETURNS boolean PROPERTIES ( + "file"="file://${jarPath}", + "symbol"="org.apache.doris.udf.DateTimeTest2", + "type"="JAVA_UDF" + ); """ + + qt_select """ SELECT java_udf_datetime_test2(datetime_col, '2011-01-01') result FROM ${tableName} ORDER BY result; """ + qt_select """ SELECT java_udf_datetime_test2('2011-01-01', '2011-01-01') result FROM ${tableName} ORDER BY result; """ + qt_select """ SELECT java_udf_datetime_test2(null, '2011-01-01') result FROM ${tableName} ORDER BY result; """ + + sql """ DROP FUNCTION java_udf_datetime_test2(datetime, datetime); """ + + sql """ CREATE FUNCTION java_udf_datetime_test3(datetime, datetime) RETURNS boolean PROPERTIES ( + "file"="file://${jarPath}", + "symbol"="org.apache.doris.udf.DateTimeTest3", + "type"="JAVA_UDF" + ); """ + + qt_select """ SELECT java_udf_datetime_test3(datetime_col, '2011-01-01') result FROM ${tableName} ORDER BY result; """ + qt_select """ SELECT java_udf_datetime_test3('2011-01-01', '2011-01-01') result FROM ${tableName} ORDER BY result; """ + qt_select """ SELECT java_udf_datetime_test3(null, '2011-01-01') result FROM ${tableName} ORDER BY result; """ + + sql """ DROP FUNCTION java_udf_datetime_test3(datetime, datetime); """ + + + sql """ CREATE FUNCTION java_udf_datev2_test1(datev2, datev2) RETURNS boolean PROPERTIES ( + "file"="file://${jarPath}", + "symbol"="org.apache.doris.udf.DateTest1", + "type"="JAVA_UDF" + ); """ + + qt_select """ SELECT java_udf_datev2_test1(datev2_col, '2011-01-01') result FROM ${tableName} ORDER BY result; """ + + sql """ DROP FUNCTION java_udf_datev2_test1(datev2, datev2); """ + + sql """ CREATE FUNCTION java_udf_datev2_test2(datev2, datev2) RETURNS boolean PROPERTIES ( + "file"="file://${jarPath}", + "symbol"="org.apache.doris.udf.DateTest2", + "type"="JAVA_UDF" + ); """ + + qt_select """ SELECT java_udf_datev2_test2(datev2_col, '2011-01-01') result FROM ${tableName} ORDER BY result; """ + + sql """ DROP FUNCTION java_udf_datev2_test2(datev2, datev2); """ + + sql """ CREATE FUNCTION java_udf_datev2_test3(datev2, datev2) RETURNS boolean PROPERTIES ( + "file"="file://${jarPath}", + "symbol"="org.apache.doris.udf.DateTest3", + "type"="JAVA_UDF" + ); """ + + qt_select """ SELECT java_udf_datev2_test3(datev2_col, '2011-01-01') result FROM ${tableName} ORDER BY result; """ + + sql """ DROP FUNCTION java_udf_datev2_test3(datev2, datev2); """ + + sql """ CREATE FUNCTION java_udf_datetimev2_test1(datetimev2, datetimev2) RETURNS boolean PROPERTIES ( + "file"="file://${jarPath}", + "symbol"="org.apache.doris.udf.DateTimeTest1", + "type"="JAVA_UDF" + ); """ + + qt_select """ SELECT java_udf_datetimev2_test1(datetimev2_col, '2011-01-01') result FROM ${tableName} ORDER BY result; """ + + sql """ DROP FUNCTION java_udf_datetimev2_test1(datetimev2, datetimev2); """ + + sql """ CREATE FUNCTION java_udf_datetimev2_test2(datetimev2, datetimev2) RETURNS boolean PROPERTIES ( + "file"="file://${jarPath}", + "symbol"="org.apache.doris.udf.DateTimeTest2", + "type"="JAVA_UDF" + ); """ + + qt_select """ SELECT java_udf_datetimev2_test2(datetimev2_col, '2011-01-01') result FROM ${tableName} ORDER BY result; """ + + sql """ DROP FUNCTION java_udf_datetimev2_test2(datetimev2, datetimev2); """ + + sql """ CREATE FUNCTION java_udf_datetimev2_test3(datetimev2, datetimev2) RETURNS boolean PROPERTIES ( + "file"="file://${jarPath}", + "symbol"="org.apache.doris.udf.DateTimeTest3", + "type"="JAVA_UDF" + ); """ + + qt_select """ SELECT java_udf_datetimev2_test3(datetimev2_col, '2011-01-01') result FROM ${tableName} ORDER BY result; """ + + sql """ DROP FUNCTION java_udf_datetimev2_test3(datetimev2, datetimev2); """ + } finally { + try_sql("DROP TABLE IF EXISTS ${tableName}") + } +} --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@doris.apache.org For additional commands, e-mail: commits-h...@doris.apache.org