This is an automated email from the ASF dual-hosted git repository.
morningman pushed a commit to branch branch-2.0
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/branch-2.0 by this push:
new cf9a2097786 [fix](udf)java udf does not support overloaded evaluate
method (#22681) (#26768)
cf9a2097786 is described below
commit cf9a20977862ec6ec303c5e8a73ebb8cc016c5ec
Author: Mingyu Chen <[email protected]>
AuthorDate: Fri Nov 10 19:27:15 2023 +0800
[fix](udf)java udf does not support overloaded evaluate method (#22681)
(#26768)
Co-authored-by: HB <[email protected]>
---
.../apache/doris/analysis/CreateFunctionStmt.java | 81 +++++++++++---------
.../javaudf_p0/test_javaudf_multi_evaluate.out | 23 ++++++
.../org/apache/doris/udf/MultiEvaluateTest.java | 35 +++++++++
.../javaudf_p0/test_javaudf_multi_evaluate.groovy | 87 ++++++++++++++++++++++
4 files changed, 192 insertions(+), 34 deletions(-)
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 ac1ba1b4e65..54159abb9a9 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
@@ -63,10 +63,12 @@ import java.net.URL;
import java.net.URLClassLoader;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.stream.Collectors;
// create a user define function
public class CreateFunctionStmt extends DdlStmt {
@@ -530,45 +532,56 @@ public class CreateFunctionStmt extends DdlStmt {
URL[] urls = {new URL("jar:" + userFile + "!/")};
try (URLClassLoader cl = URLClassLoader.newInstance(urls)) {
Class udfClass = cl.loadClass(clazz);
-
- Method eval = null;
- for (Method m : udfClass.getMethods()) {
- if (!m.getDeclaringClass().equals(udfClass)) {
- continue;
- }
- String name = m.getName();
- if (EVAL_METHOD_KEY.equals(name) && eval == null) {
- eval = m;
- } else if (EVAL_METHOD_KEY.equals(name)) {
- throw new AnalysisException(String.format(
- "UDF class '%s' has multiple methods with name
'%s' ", udfClass.getCanonicalName(),
- EVAL_METHOD_KEY));
- }
- }
- if (eval == null) {
+ List<Method> evalList = Arrays.stream(udfClass.getMethods())
+ .filter(m -> m.getDeclaringClass().equals(udfClass) &&
EVAL_METHOD_KEY.equals(m.getName()))
+ .collect(Collectors.toList());
+ if (evalList.size() == 0) {
throw new AnalysisException(String.format(
- "No method '%s' in class '%s'!", EVAL_METHOD_KEY,
udfClass.getCanonicalName()));
+ "No method '%s' in class '%s'!", EVAL_METHOD_KEY,
udfClass.getCanonicalName()));
}
- if (Modifier.isStatic(eval.getModifiers())) {
+ List<Method> evalNonStaticAndPublicList = evalList.stream()
+ .filter(m -> !Modifier.isStatic(m.getModifiers()) &&
Modifier.isPublic(m.getModifiers()))
+ .collect(Collectors.toList());
+ if (evalNonStaticAndPublicList.size() == 0) {
throw new AnalysisException(
- String.format("Method '%s' in class '%s' should be
non-static", eval.getName(),
- udfClass.getCanonicalName()));
+ String.format("Method '%s' in class '%s' should be
non-static and public", EVAL_METHOD_KEY,
+ udfClass.getCanonicalName()));
}
- if (!Modifier.isPublic(eval.getModifiers())) {
+ List<Method> evalArgLengthMatchList =
evalNonStaticAndPublicList.stream().filter(
+ m -> m.getParameters().length ==
argsDef.getArgTypes().length).collect(Collectors.toList());
+ if (evalArgLengthMatchList.size() == 0) {
throw new AnalysisException(
- String.format("Method '%s' in class '%s' should be
public", eval.getName(),
- udfClass.getCanonicalName()));
- }
- if (eval.getParameters().length !=
argsDef.getArgTypes().length) {
- throw new AnalysisException(
- String.format("The number of parameters for method
'%s' in class '%s' should be %d",
- eval.getName(),
udfClass.getCanonicalName(), argsDef.getArgTypes().length));
- }
-
- checkUdfType(udfClass, eval, returnType.getType(),
eval.getReturnType(), "return");
- for (int i = 0; i < eval.getParameters().length; i++) {
- Parameter p = eval.getParameters()[i];
- checkUdfType(udfClass, eval, argsDef.getArgTypes()[i],
p.getType(), p.getName());
+ String.format("The number of parameters for method
'%s' in class '%s' should be %d",
+ EVAL_METHOD_KEY, udfClass.getCanonicalName(),
argsDef.getArgTypes().length));
+ } else if (evalArgLengthMatchList.size() == 1) {
+ Method method = evalArgLengthMatchList.get(0);
+ checkUdfType(udfClass, method, returnType.getType(),
method.getReturnType(), "return");
+ for (int i = 0; i < method.getParameters().length; i++) {
+ Parameter p = method.getParameters()[i];
+ checkUdfType(udfClass, method,
argsDef.getArgTypes()[i], p.getType(), p.getName());
+ }
+ } else {
+ // If multiple methods have the same parameters,
+ // the error message returned cannot be as specific as a
single method
+ boolean hasError = false;
+ for (Method method : evalArgLengthMatchList) {
+ try {
+ checkUdfType(udfClass, method,
returnType.getType(), method.getReturnType(), "return");
+ for (int i = 0; i < method.getParameters().length;
i++) {
+ Parameter p = method.getParameters()[i];
+ checkUdfType(udfClass, method,
argsDef.getArgTypes()[i], p.getType(), p.getName());
+ }
+ hasError = false;
+ break;
+ } catch (AnalysisException e) {
+ hasError = true;
+ }
+ }
+ if (hasError) {
+ throw new AnalysisException(String.format(
+ "Multi methods '%s' in class '%s' and no one
passed parameter matching verification",
+ EVAL_METHOD_KEY, udfClass.getCanonicalName()));
+ }
}
} catch (ClassNotFoundException e) {
throw new AnalysisException("Class [" + clazz + "] not found
in file :" + userFile);
diff --git a/regression-test/data/javaudf_p0/test_javaudf_multi_evaluate.out
b/regression-test/data/javaudf_p0/test_javaudf_multi_evaluate.out
new file mode 100644
index 00000000000..204cfba8a1a
--- /dev/null
+++ b/regression-test/data/javaudf_p0/test_javaudf_multi_evaluate.out
@@ -0,0 +1,23 @@
+-- This file is automatically generated. You should know what you did if you
want to edit this
+-- !select_default --
+1 1.11 2 3
+2 \N 3 4
+3 3.33 2 \N
+
+-- !select --
+2.11
+
+-- !select --
+\N
+
+-- !select --
+5
+
+-- !select --
+\N
+
+-- !select --
+7
+
+-- !select --
+\N
diff --git
a/regression-test/java-udf-src/src/main/java/org/apache/doris/udf/MultiEvaluateTest.java
b/regression-test/java-udf-src/src/main/java/org/apache/doris/udf/MultiEvaluateTest.java
new file mode 100644
index 00000000000..8aa12b34393
--- /dev/null
+++
b/regression-test/java-udf-src/src/main/java/org/apache/doris/udf/MultiEvaluateTest.java
@@ -0,0 +1,35 @@
+// 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 MultiEvaluateTest extends UDF {
+
+ public Float evaluate(Float flo) {
+ return flo == null ? null : flo + 1;
+ }
+
+ public Integer evaluate(Integer value) {
+ return value == null ? null : value + 1;
+ }
+
+ public Integer evaluate(Integer value1, Integer value2) {
+ return value1 == null || value2 == null ? null : value1 + value2;
+ }
+
+}
diff --git
a/regression-test/suites/javaudf_p0/test_javaudf_multi_evaluate.groovy
b/regression-test/suites/javaudf_p0/test_javaudf_multi_evaluate.groovy
new file mode 100644
index 00000000000..d8f390bbaac
--- /dev/null
+++ b/regression-test/suites/javaudf_p0/test_javaudf_multi_evaluate.groovy
@@ -0,0 +1,87 @@
+// 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_multi_evaluate") {
+ def tableName = "test_javaudf_multi_evaluate"
+ def jarPath =
"""${context.file.parent}/jars/java-udf-case-jar-with-dependencies.jar"""
+
+ log.info("Jar path: ${jarPath}".toString())
+ try {
+ sql """ DROP TABLE IF EXISTS ${tableName} """
+ sql """
+ CREATE TABLE IF NOT EXISTS ${tableName} (
+ `user_id` INT NOT NULL COMMENT "",
+ `float_1` FLOAT COMMENT "",
+ `int_1` INT COMMENT "",
+ `int_2` INT COMMENT ""
+ )
+ DISTRIBUTED BY HASH(user_id) PROPERTIES("replication_num" = "1");
+ """
+
+
+ sql """ INSERT INTO ${tableName} (`user_id`,`float_1`,`int_1`,`int_2`)
VALUES
+ (1,1.11,2,3),
+ (2,null,3,4),
+ (3,3.33,2,null);
+ """
+ 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_multi_evaluate_test(FLOAT) RETURNS
FLOAT PROPERTIES (
+ "file"="file://${jarPath}",
+ "symbol"="org.apache.doris.udf.MultiEvaluateTest",
+ "type"="JAVA_UDF"
+ ); """
+
+ qt_select """ SELECT java_udf_multi_evaluate_test(float_1) FROM
${tableName} where user_id = 1; """
+ qt_select """ SELECT java_udf_multi_evaluate_test(float_1) FROM
${tableName} where user_id = 2; """
+
+ sql """ CREATE FUNCTION java_udf_multi_evaluate_test(int) RETURNS int
PROPERTIES (
+ "file"="file://${jarPath}",
+ "symbol"="org.apache.doris.udf.MultiEvaluateTest",
+ "type"="JAVA_UDF"
+ ); """
+
+ qt_select """ SELECT java_udf_multi_evaluate_test(int_2) FROM
${tableName} where user_id = 2; """
+ qt_select """ SELECT java_udf_multi_evaluate_test(int_2) FROM
${tableName} where user_id = 3; """
+
+ sql """ CREATE FUNCTION java_udf_multi_evaluate_test(int,int) RETURNS
int PROPERTIES (
+ "file"="file://${jarPath}",
+ "symbol"="org.apache.doris.udf.MultiEvaluateTest",
+ "type"="JAVA_UDF"
+ ); """
+
+ qt_select """ SELECT java_udf_multi_evaluate_test(int_1, int_2) FROM
${tableName} where user_id = 2; """
+ qt_select """ SELECT java_udf_multi_evaluate_test(int_1, int_2) FROM
${tableName} where user_id = 3; """
+
+ } finally {
+ try_sql("DROP FUNCTION IF EXISTS java_udf_multi_evaluate_test(FLOAT);")
+ try_sql("DROP FUNCTION IF EXISTS java_udf_multi_evaluate_test(int);")
+ try_sql("DROP FUNCTION IF EXISTS
java_udf_multi_evaluate_test(int,int);")
+ try_sql("DROP TABLE IF EXISTS ${tableName}")
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]