This is an automated email from the ASF dual-hosted git repository.
zhangstar333 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 6001223968b [Feature](udf) support struct type in java udf (#34586)
6001223968b is described below
commit 6001223968b03b4198be790adfc745b0e978ed94
Author: zhangstar333 <[email protected]>
AuthorDate: Sat May 11 14:28:47 2024 +0800
[Feature](udf) support struct type in java udf (#34586)
* [Feature](udf) support struct type in java udf
---
.../doris/common/jni/utils/JavaUdfDataType.java | 19 ++++-
.../apache/doris/common/jni/utils/UdfUtils.java | 10 +++
.../apache/doris/common/jni/vec/VectorColumn.java | 31 +++++--
.../java/org/apache/doris/udf/BaseExecutor.java | 40 ++++++++-
.../java/org/apache/doris/udf/UdafExecutor.java | 2 +-
.../java/org/apache/doris/udf/UdfExecutor.java | 12 +--
.../org/apache/doris/jdbc/BaseJdbcExecutor.java | 2 +-
.../org/apache/doris/jdbc/DefaultJdbcExecutor.java | 2 +-
.../main/java/org/apache/doris/catalog/Type.java | 1 +
.../apache/doris/analysis/CreateFunctionStmt.java | 4 +
.../java/org/apache/doris/catalog/ColumnType.java | 30 ++++++-
.../data/javaudf_p0/test_javaudf_struct_type.out | 56 +++++++++++++
.../java/org/apache/doris/udf/StructTypeTest.java | 30 +++++++
.../apache/doris/udf/StructTypeTestDecimal.java | 33 ++++++++
.../apache/doris/udf/StructTypeTestReturnInt.java | 30 +++++++
.../apache/doris/udf/StructTypeTestStringInt.java | 33 ++++++++
.../javaudf_p0/test_javaudf_struct_type.groovy | 97 ++++++++++++++++++++++
17 files changed, 405 insertions(+), 27 deletions(-)
diff --git
a/fe/be-java-extensions/java-common/src/main/java/org/apache/doris/common/jni/utils/JavaUdfDataType.java
b/fe/be-java-extensions/java-common/src/main/java/org/apache/doris/common/jni/utils/JavaUdfDataType.java
index 846c4bb172d..18bb90ddb99 100644
---
a/fe/be-java-extensions/java-common/src/main/java/org/apache/doris/common/jni/utils/JavaUdfDataType.java
+++
b/fe/be-java-extensions/java-common/src/main/java/org/apache/doris/common/jni/utils/JavaUdfDataType.java
@@ -17,6 +17,7 @@
package org.apache.doris.common.jni.utils;
+import org.apache.doris.catalog.StructField;
import org.apache.doris.catalog.Type;
import org.apache.doris.common.exception.InternalException;
import org.apache.doris.thrift.TPrimitiveType;
@@ -26,6 +27,7 @@ import org.apache.log4j.Logger;
import java.math.BigDecimal;
import java.math.BigInteger;
+import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
@@ -57,6 +59,7 @@ public class JavaUdfDataType {
16);
public static final JavaUdfDataType ARRAY_TYPE = new
JavaUdfDataType("ARRAY_TYPE", TPrimitiveType.ARRAY, 0);
public static final JavaUdfDataType MAP_TYPE = new
JavaUdfDataType("MAP_TYPE", TPrimitiveType.MAP, 0);
+ public static final JavaUdfDataType STRUCT_TYPE = new
JavaUdfDataType("STRUCT_TYPE", TPrimitiveType.STRUCT, 0);
private static Set<JavaUdfDataType> JavaUdfDataTypeSet = new HashSet<>();
@@ -83,6 +86,7 @@ public class JavaUdfDataType {
JavaUdfDataTypeSet.add(DECIMAL128);
JavaUdfDataTypeSet.add(ARRAY_TYPE);
JavaUdfDataTypeSet.add(MAP_TYPE);
+ JavaUdfDataTypeSet.add(STRUCT_TYPE);
}
private final String description;
@@ -95,6 +99,7 @@ public class JavaUdfDataType {
private Type valueType;
private int keyScale;
private int valueScale;
+ private ArrayList<StructField> fields = new ArrayList<>();
public JavaUdfDataType(String description, TPrimitiveType thriftType, int
len) {
this.description = description;
@@ -150,7 +155,7 @@ public class JavaUdfDataType {
return Sets.newHashSet(JavaUdfDataType.DECIMALV2,
JavaUdfDataType.DECIMAL32, JavaUdfDataType.DECIMAL64,
JavaUdfDataType.DECIMAL128);
} else if (c == java.util.ArrayList.class) {
- return Sets.newHashSet(JavaUdfDataType.ARRAY_TYPE);
+ return Sets.newHashSet(JavaUdfDataType.ARRAY_TYPE,
JavaUdfDataType.STRUCT_TYPE);
} else if (c == java.util.HashMap.class) {
return Sets.newHashSet(JavaUdfDataType.MAP_TYPE);
}
@@ -232,4 +237,16 @@ public class JavaUdfDataType {
public int getValueScale() {
return valueScale;
}
+
+ public void setFields(ArrayList<StructField> fields) {
+ this.fields = fields;
+ }
+
+ public ArrayList<String> getFieldNames() {
+ ArrayList<String> names = new ArrayList<>();
+ for (StructField filed : fields) {
+ names.add(filed.getName());
+ }
+ return names;
+ }
}
diff --git
a/fe/be-java-extensions/java-common/src/main/java/org/apache/doris/common/jni/utils/UdfUtils.java
b/fe/be-java-extensions/java-common/src/main/java/org/apache/doris/common/jni/utils/UdfUtils.java
index 5ebafb57084..e1a6a86d4d0 100644
---
a/fe/be-java-extensions/java-common/src/main/java/org/apache/doris/common/jni/utils/UdfUtils.java
+++
b/fe/be-java-extensions/java-common/src/main/java/org/apache/doris/common/jni/utils/UdfUtils.java
@@ -20,6 +20,8 @@ package org.apache.doris.common.jni.utils;
import org.apache.doris.catalog.ArrayType;
import org.apache.doris.catalog.MapType;
import org.apache.doris.catalog.ScalarType;
+import org.apache.doris.catalog.StructField;
+import org.apache.doris.catalog.StructType;
import org.apache.doris.catalog.Type;
import org.apache.doris.common.Pair;
import org.apache.doris.common.exception.InternalException;
@@ -40,6 +42,7 @@ import java.security.AccessController;
import java.security.PrivilegedAction;
import java.time.LocalDate;
import java.time.LocalDateTime;
+import java.util.ArrayList;
import java.util.Set;
public class UdfUtils {
@@ -141,6 +144,9 @@ public class UdfUtils {
if (valuType.isDatetimeV2() || valuType.isDecimalV3()) {
result.setValueScale(((ScalarType) valuType).getScalarScale());
}
+ } else if (retType.isStructType()) {
+ StructType structType = (StructType) retType;
+ result.setFields(structType.getFields());
}
return Pair.of(res.length != 0, result);
}
@@ -185,6 +191,10 @@ public class UdfUtils {
if (valuType.isDatetimeV2() || valuType.isDecimalV3()) {
inputArgTypes[i].setValueScale(((ScalarType)
valuType).getScalarScale());
}
+ } else if (parameterTypes[finalI].isStructType()) {
+ StructType structType = (StructType) parameterTypes[finalI];
+ ArrayList<StructField> fields = structType.getFields();
+ inputArgTypes[i].setFields(fields);
}
if (res.length == 0) {
return Pair.of(false, inputArgTypes);
diff --git
a/fe/be-java-extensions/java-common/src/main/java/org/apache/doris/common/jni/vec/VectorColumn.java
b/fe/be-java-extensions/java-common/src/main/java/org/apache/doris/common/jni/vec/VectorColumn.java
index 4bb4db86d3b..bf929ed9e2b 100644
---
a/fe/be-java-extensions/java-common/src/main/java/org/apache/doris/common/jni/vec/VectorColumn.java
+++
b/fe/be-java-extensions/java-common/src/main/java/org/apache/doris/common/jni/vec/VectorColumn.java
@@ -22,6 +22,7 @@ import org.apache.doris.common.jni.utils.TypeNativeBytes;
import org.apache.doris.common.jni.vec.ColumnType.Type;
import org.apache.doris.common.jni.vec.NativeColumnValue.NativeValue;
+import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import java.math.BigDecimal;
@@ -212,10 +213,14 @@ public class VectorColumn {
return offsets;
}
- public ColumnType.Type getColumnTyp() {
+ public ColumnType.Type getColumnPrimitiveType() {
return columnType.getType();
}
+ public ColumnType getColumnType() {
+ return columnType;
+ }
+
/**
* Release columns and meta information
*/
@@ -1127,7 +1132,8 @@ public class VectorColumn {
OffHeap.putLong(null, offsets + 8L * appendIndex, offset);
appendIndex++;
}
- Object[] nested =
newObjectContainerArray(childColumns[0].getColumnTyp(), offset -
childColumns[0].appendIndex);
+ Object[] nested =
newObjectContainerArray(childColumns[0].getColumnPrimitiveType(),
+ offset - childColumns[0].appendIndex);
int index = 0;
for (List<Object> v : batch) {
if (v != null) {
@@ -1186,8 +1192,10 @@ public class VectorColumn {
OffHeap.putLong(null, offsets + 8L * appendIndex, offset);
appendIndex++;
}
- Object[] keys =
newObjectContainerArray(childColumns[0].getColumnTyp(), offset -
childColumns[0].appendIndex);
- Object[] values =
newObjectContainerArray(childColumns[1].getColumnTyp(), offset -
childColumns[0].appendIndex);
+ Object[] keys =
newObjectContainerArray(childColumns[0].getColumnPrimitiveType(),
+ offset - childColumns[0].appendIndex);
+ Object[] values =
newObjectContainerArray(childColumns[1].getColumnPrimitiveType(),
+ offset - childColumns[0].appendIndex);
int index = 0;
for (Map<Object, Object> v : batch) {
if (v != null) {
@@ -1241,8 +1249,9 @@ public class VectorColumn {
public void appendStruct(Map<String, Object>[] batch, boolean isNullable) {
reserve(appendIndex + batch.length);
Object[][] columnData = new Object[childColumns.length][];
+
Preconditions.checkArgument(this.getColumnType().getChildNames().size() ==
childColumns.length);
for (int j = 0; j < childColumns.length; ++j) {
- columnData[j] =
newObjectContainerArray(childColumns[j].getColumnTyp(), batch.length);
+ columnData[j] =
newObjectContainerArray(childColumns[j].getColumnPrimitiveType(), batch.length);
}
int index = 0;
for (Map<String, Object> v : batch) {
@@ -1252,8 +1261,8 @@ public class VectorColumn {
columnData[j][index] = null;
}
} else {
- for (int j = 0; j < childColumns.length; ++j) {
- columnData[j][index] =
v.get(childColumns[j].getColumnTyp().name());
+ for (int j = 0; j <
this.getColumnType().getChildNames().size(); ++j) {
+ columnData[j][index] =
v.get(this.getColumnType().getChildNames().get(j));
}
}
index++;
@@ -1266,8 +1275,12 @@ public class VectorColumn {
public HashMap<String, Object> getStruct(int rowId) {
HashMap<String, Object> result = new HashMap<>();
- for (VectorColumn column : childColumns) {
- result.put(column.getColumnTyp().name(),
column.getObjectColumn(rowId, rowId + 1)[0]);
+
Preconditions.checkArgument(this.getColumnType().getChildNames().size() ==
childColumns.length);
+ for (int i = 0; i < childColumns.length; ++i) {
+ // here use the hashmap to return struct data, the key is the
nested_column name
+ // and value is the nested_column data
+ result.put(this.getColumnType().getChildNames().get(i),
+ childColumns[i].getObjectColumn(rowId, rowId + 1)[0]);
}
return result;
}
diff --git
a/fe/be-java-extensions/java-udf/src/main/java/org/apache/doris/udf/BaseExecutor.java
b/fe/be-java-extensions/java-udf/src/main/java/org/apache/doris/udf/BaseExecutor.java
index 2cb8ed5351f..a67b259be52 100644
---
a/fe/be-java-extensions/java-udf/src/main/java/org/apache/doris/udf/BaseExecutor.java
+++
b/fe/be-java-extensions/java-udf/src/main/java/org/apache/doris/udf/BaseExecutor.java
@@ -37,6 +37,9 @@ import java.io.IOException;
import java.net.URLClassLoader;
import java.time.LocalDate;
import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map.Entry;
public abstract class BaseExecutor {
private static final Logger LOG = Logger.getLogger(BaseExecutor.class);
@@ -218,14 +221,30 @@ public abstract class BaseExecutor {
}
break;
}
+ case STRUCT: {
+ return (Object[] columnData) -> {
+ Object[] result = new ArrayList[columnData.length];
+ for (int i = 0; i < columnData.length; ++i) {
+ if (columnData[i] != null) {
+ HashMap<String, Object> value = (HashMap<String,
Object>) columnData[i];
+ ArrayList<Object> elements = new ArrayList<>();
+ for (Entry<String, Object> entry :
value.entrySet()) {
+ elements.add(entry.getValue());
+ }
+ result[i] = elements;
+ }
+ }
+ return result;
+ };
+ }
default:
break;
}
return null;
}
- protected ColumnValueConverter getOutputConverter(TPrimitiveType
primitiveType, Class clz) {
- switch (primitiveType) {
+ protected ColumnValueConverter getOutputConverter(JavaUdfDataType
returnType, Class clz) {
+ switch (returnType.getPrimitiveType()) {
case DATE:
case DATEV2: {
if (java.util.Date.class.equals(clz)) {
@@ -288,6 +307,23 @@ public abstract class BaseExecutor {
}
break;
}
+ case STRUCT: {
+ return (Object[] columnData) -> {
+ Object[] result = (HashMap<String, Object>[]) new
HashMap<?, ?>[columnData.length];
+ ArrayList<String> names = returnType.getFieldNames();
+ for (int i = 0; i < columnData.length; ++i) {
+ HashMap<String, Object> elements = new HashMap<String,
Object>();
+ if (columnData[i] != null) {
+ ArrayList<Object> v = (ArrayList<Object>)
columnData[i];
+ for (int k = 0; k < v.size(); ++k) {
+ elements.put(names.get(k), v.get(k));
+ }
+ result[i] = elements;
+ }
+ }
+ return result;
+ };
+ }
default:
break;
}
diff --git
a/fe/be-java-extensions/java-udf/src/main/java/org/apache/doris/udf/UdafExecutor.java
b/fe/be-java-extensions/java-udf/src/main/java/org/apache/doris/udf/UdafExecutor.java
index cf0021c7db0..30a0fe116d4 100644
---
a/fe/be-java-extensions/java-udf/src/main/java/org/apache/doris/udf/UdafExecutor.java
+++
b/fe/be-java-extensions/java-udf/src/main/java/org/apache/doris/udf/UdafExecutor.java
@@ -87,7 +87,7 @@ public class UdafExecutor extends BaseExecutor {
}
private ColumnValueConverter getOutputConverter() {
- return getOutputConverter(retType.getPrimitiveType(), retClass);
+ return getOutputConverter(retType, retClass);
}
public void addBatch(boolean isSinglePlace, int rowStart, int rowEnd, long
placeAddr, int offset,
diff --git
a/fe/be-java-extensions/java-udf/src/main/java/org/apache/doris/udf/UdfExecutor.java
b/fe/be-java-extensions/java-udf/src/main/java/org/apache/doris/udf/UdfExecutor.java
index 7e44cd3e423..8d3b79fadff 100644
---
a/fe/be-java-extensions/java-udf/src/main/java/org/apache/doris/udf/UdfExecutor.java
+++
b/fe/be-java-extensions/java-udf/src/main/java/org/apache/doris/udf/UdfExecutor.java
@@ -85,7 +85,7 @@ public class UdfExecutor extends BaseExecutor {
}
private ColumnValueConverter getOutputConverter() {
- return getOutputConverter(retType.getPrimitiveType(),
method.getReturnType());
+ return getOutputConverter(retType, method.getReturnType());
}
public long evaluate(Map<String, String> inputParams, Map<String, String>
outputParams) throws UdfRuntimeException {
@@ -188,9 +188,6 @@ public class UdfExecutor extends BaseExecutor {
retType = returnType.second;
}
argTypes = new JavaUdfDataType[0];
- if (LOG.isDebugEnabled()) {
- LOG.debug("Loaded UDF '" + className + "' from " +
jarPath);
- }
return;
}
returnType = UdfUtils.setReturnType(funcRetType,
m.getReturnType());
@@ -199,19 +196,12 @@ public class UdfExecutor extends BaseExecutor {
} else {
retType = returnType.second;
}
- Type keyType = retType.getKeyType();
- Type valueType = retType.getValueType();
Pair<Boolean, JavaUdfDataType[]> inputType =
UdfUtils.setArgTypes(parameterTypes, argClass, false);
if (!inputType.first) {
continue;
} else {
argTypes = inputType.second;
}
- if (LOG.isDebugEnabled()) {
- LOG.debug("Loaded UDF '" + className + "' from " +
jarPath);
- }
- retType.setKeyType(keyType);
- retType.setValueType(valueType);
return;
}
diff --git
a/fe/be-java-extensions/jdbc-scanner/src/main/java/org/apache/doris/jdbc/BaseJdbcExecutor.java
b/fe/be-java-extensions/jdbc-scanner/src/main/java/org/apache/doris/jdbc/BaseJdbcExecutor.java
index a1ddb5ab404..6b4b72682fa 100644
---
a/fe/be-java-extensions/jdbc-scanner/src/main/java/org/apache/doris/jdbc/BaseJdbcExecutor.java
+++
b/fe/be-java-extensions/jdbc-scanner/src/main/java/org/apache/doris/jdbc/BaseJdbcExecutor.java
@@ -433,7 +433,7 @@ public abstract class BaseJdbcExecutor implements
JdbcExecutor {
private void insertColumn(int rowIdx, int colIdx, VectorColumn column)
throws SQLException {
int parameterIndex = colIdx + 1;
- ColumnType.Type dorisType = column.getColumnTyp();
+ ColumnType.Type dorisType = column.getColumnPrimitiveType();
if (column.isNullAt(rowIdx)) {
insertNullColumn(parameterIndex, dorisType);
return;
diff --git
a/fe/be-java-extensions/jdbc-scanner/src/main/java/org/apache/doris/jdbc/DefaultJdbcExecutor.java
b/fe/be-java-extensions/jdbc-scanner/src/main/java/org/apache/doris/jdbc/DefaultJdbcExecutor.java
index 6530ea54c5e..a6c6efea7cb 100644
---
a/fe/be-java-extensions/jdbc-scanner/src/main/java/org/apache/doris/jdbc/DefaultJdbcExecutor.java
+++
b/fe/be-java-extensions/jdbc-scanner/src/main/java/org/apache/doris/jdbc/DefaultJdbcExecutor.java
@@ -1018,7 +1018,7 @@ public class DefaultJdbcExecutor {
private void insertColumn(int rowIdx, int colIdx, VectorColumn column)
throws SQLException {
int parameterIndex = colIdx + 1;
- ColumnType.Type dorisType = column.getColumnTyp();
+ ColumnType.Type dorisType = column.getColumnPrimitiveType();
if (column.isNullAt(rowIdx)) {
insertNullColumn(parameterIndex, dorisType);
return;
diff --git a/fe/fe-common/src/main/java/org/apache/doris/catalog/Type.java
b/fe/fe-common/src/main/java/org/apache/doris/catalog/Type.java
index 46cda54753e..ef1ead2a153 100644
--- a/fe/fe-common/src/main/java/org/apache/doris/catalog/Type.java
+++ b/fe/fe-common/src/main/java/org/apache/doris/catalog/Type.java
@@ -336,6 +336,7 @@ public abstract class Type {
.put(PrimitiveType.DECIMAL128,
Sets.newHashSet(BigDecimal.class))
.put(PrimitiveType.ARRAY, Sets.newHashSet(ArrayList.class))
.put(PrimitiveType.MAP, Sets.newHashSet(HashMap.class))
+ .put(PrimitiveType.STRUCT,
Sets.newHashSet(ArrayList.class))
.build();
public static ArrayList<ScalarType> getIntegerTypes() {
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 ed618d1603d..d5ce0d80424 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
@@ -27,6 +27,7 @@ import org.apache.doris.catalog.FunctionUtil;
import org.apache.doris.catalog.MapType;
import org.apache.doris.catalog.ScalarFunction;
import org.apache.doris.catalog.ScalarType;
+import org.apache.doris.catalog.StructType;
import org.apache.doris.catalog.Type;
import org.apache.doris.common.AnalysisException;
import org.apache.doris.common.Config;
@@ -640,6 +641,9 @@ public class CreateFunctionStmt extends DdlStmt {
} else if (expType instanceof MapType) {
MapType mapType = (MapType) expType;
javaTypes =
Type.PrimitiveTypeToJavaClassType.get(mapType.getPrimitiveType());
+ } else if (expType instanceof StructType) {
+ StructType structType = (StructType) expType;
+ javaTypes =
Type.PrimitiveTypeToJavaClassType.get(structType.getPrimitiveType());
} else {
throw new AnalysisException(
String.format("Method '%s' in class '%s' does not support
type '%s'",
diff --git a/fe/fe-core/src/main/java/org/apache/doris/catalog/ColumnType.java
b/fe/fe-core/src/main/java/org/apache/doris/catalog/ColumnType.java
index d4813dbc820..374e17becfd 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/catalog/ColumnType.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/ColumnType.java
@@ -24,6 +24,7 @@ import com.google.common.base.Preconditions;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
+import java.util.ArrayList;
/**
* 这个是对Column类型的一个封装,对于大多数类型,primitive type足够了,这里有两个例外需要用到这个信息
@@ -166,7 +167,8 @@ public abstract class ColumnType {
}
public static void write(DataOutput out, Type type) throws IOException {
- Preconditions.checkArgument(type.isScalarType() || type.isArrayType()
|| type.isMapType(),
+ Preconditions.checkArgument(
+ type.isScalarType() || type.isArrayType() || type.isMapType()
|| type.isStructType(),
"only support scalar type and array serialization");
if (type.isScalarType()) {
ScalarType scalarType = (ScalarType) type;
@@ -188,6 +190,18 @@ public abstract class ColumnType {
write(out, mapType.getValueType());
out.writeBoolean(mapType.getIsKeyContainsNull());
out.writeBoolean(mapType.getIsValueContainsNull());
+ } else if (type.isStructType()) {
+ StructType structType = (StructType) type;
+ Text.writeString(out, structType.getPrimitiveType().name());
+ out.writeInt(structType.getFields().size());
+ for (int i = 0; i < structType.getFields().size(); ++i) {
+ StructField structField = structType.getFields().get(i);
+ Text.writeString(out, structField.getName());
+ write(out, structField.getType());
+ Text.writeString(out, structField.getComment());
+ out.writeInt(structField.getPosition());
+ out.writeBoolean(structField.getContainsNull());
+ }
}
}
@@ -203,6 +217,20 @@ public abstract class ColumnType {
boolean keyContainsNull = in.readBoolean();
boolean valueContainsNull = in.readBoolean();
return new MapType(keyType, valueType, keyContainsNull,
valueContainsNull);
+ } else if (primitiveType == PrimitiveType.STRUCT) {
+ int size = in.readInt();
+ ArrayList<StructField> fields = new ArrayList<>();
+ for (int i = 0; i < size; ++i) {
+ String name = Text.readString(in);
+ Type type = read(in);
+ String comment = Text.readString(in);
+ int pos = in.readInt();
+ boolean containsNull = in.readBoolean();
+ StructField field = new StructField(name, type, comment,
containsNull);
+ field.setPosition(pos);
+ fields.add(field);
+ }
+ return new StructType(fields);
} else {
int scale = in.readInt();
int precision = in.readInt();
diff --git a/regression-test/data/javaudf_p0/test_javaudf_struct_type.out
b/regression-test/data/javaudf_p0/test_javaudf_struct_type.out
new file mode 100644
index 00000000000..4f54496f1a3
--- /dev/null
+++ b/regression-test/data/javaudf_p0/test_javaudf_struct_type.out
@@ -0,0 +1,56 @@
+-- This file is automatically generated. You should know what you did if you
want to edit this
+-- !select_default --
+1 asd 123.456 {"s_id": 1, "s_name": "sn11"}
+2 qwe 546.677 {"s_id": 2, "s_name": "sn22"}
+3 kljkl 8347.121 {"s_id": 3, "s_name": "sn333"}
+4 nvblk 999.123 {"s_id": 4, "s_name": "sn444"}
+5 uyuy 1.784 {"s_id": 5, "s_name": "sn3423"}
+6 ghjhj 2.784 {"s_id": 6, "s_name": "sne543"}
+7 rtut 3.784 {"s_id": 7, "s_name": "sn6878"}
+8 vnvc 4.784 {"s_id": 8, "s_name": "sn7989"}
+9 asdzdf 5.784 {"s_id": 9, "s_name": "sn242"}
+
+-- !select_1 --
+{"s_id": 1, "s_name": "sn11"} {"s_id": 1, "s_name": "sn11"}
+{"s_id": 2, "s_name": "sn22"} {"s_id": 2, "s_name": "sn22"}
+{"s_id": 3, "s_name": "sn333"} {"s_id": 3, "s_name": "sn333"}
+{"s_id": 4, "s_name": "sn444"} {"s_id": 4, "s_name": "sn444"}
+{"s_id": 5, "s_name": "sn3423"} {"s_id": 5, "s_name": "sn3423"}
+{"s_id": 6, "s_name": "sne543"} {"s_id": 6, "s_name": "sne543"}
+{"s_id": 7, "s_name": "sn6878"} {"s_id": 7, "s_name": "sn6878"}
+{"s_id": 8, "s_name": "sn7989"} {"s_id": 8, "s_name": "sn7989"}
+{"s_id": 9, "s_name": "sn242"} {"s_id": 9, "s_name": "sn242"}
+
+-- !select_2 --
+1 asd {"s_id": 1, "s_name": "asd"}
+2 qwe {"s_id": 2, "s_name": "qwe"}
+3 kljkl {"s_id": 3, "s_name": "kljkl"}
+4 nvblk {"s_id": 4, "s_name": "nvblk"}
+5 uyuy {"s_id": 5, "s_name": "uyuy"}
+6 ghjhj {"s_id": 6, "s_name": "ghjhj"}
+7 rtut {"s_id": 7, "s_name": "rtut"}
+8 vnvc {"s_id": 8, "s_name": "vnvc"}
+9 asdzdf {"s_id": 9, "s_name": "asdzdf"}
+
+-- !select_3 --
+{"s_id": 1, "s_name": "sn11"} 1
+{"s_id": 2, "s_name": "sn22"} 2
+{"s_id": 3, "s_name": "sn333"} 3
+{"s_id": 4, "s_name": "sn444"} 4
+{"s_id": 5, "s_name": "sn3423"} 5
+{"s_id": 6, "s_name": "sne543"} 6
+{"s_id": 7, "s_name": "sn6878"} 7
+{"s_id": 8, "s_name": "sn7989"} 8
+{"s_id": 9, "s_name": "sn242"} 9
+
+-- !select_4 --
+123.456 {"s_id": 123.456}
+546.677 {"s_id": 546.677}
+8347.121 {"s_id": 8347.121}
+999.123 {"s_id": 999.123}
+1.784 {"s_id": 1.784}
+2.784 {"s_id": 2.784}
+3.784 {"s_id": 3.784}
+4.784 {"s_id": 4.784}
+5.784 {"s_id": 5.784}
+
diff --git
a/regression-test/java-udf-src/src/main/java/org/apache/doris/udf/StructTypeTest.java
b/regression-test/java-udf-src/src/main/java/org/apache/doris/udf/StructTypeTest.java
new file mode 100644
index 00000000000..64c9eba6194
--- /dev/null
+++
b/regression-test/java-udf-src/src/main/java/org/apache/doris/udf/StructTypeTest.java
@@ -0,0 +1,30 @@
+// 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 java.util.ArrayList;
+
+public class StructTypeTest {
+ public ArrayList<Object> evaluate(ArrayList<Object> res) {
+ if (res == null) {
+ return null;
+ }
+ return res;
+ }
+}
diff --git
a/regression-test/java-udf-src/src/main/java/org/apache/doris/udf/StructTypeTestDecimal.java
b/regression-test/java-udf-src/src/main/java/org/apache/doris/udf/StructTypeTestDecimal.java
new file mode 100644
index 00000000000..b198e99d43c
--- /dev/null
+++
b/regression-test/java-udf-src/src/main/java/org/apache/doris/udf/StructTypeTestDecimal.java
@@ -0,0 +1,33 @@
+// 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 java.math.BigDecimal;
+import java.util.ArrayList;
+
+public class StructTypeTestDecimal {
+ public ArrayList<Object> evaluate(BigDecimal val1) {
+ if (val1 == null) {
+ return null;
+ }
+ ArrayList<Object> res = new ArrayList<>();
+ res.add(val1);
+ return res;
+ }
+}
diff --git
a/regression-test/java-udf-src/src/main/java/org/apache/doris/udf/StructTypeTestReturnInt.java
b/regression-test/java-udf-src/src/main/java/org/apache/doris/udf/StructTypeTestReturnInt.java
new file mode 100644
index 00000000000..01ec31dea6b
--- /dev/null
+++
b/regression-test/java-udf-src/src/main/java/org/apache/doris/udf/StructTypeTestReturnInt.java
@@ -0,0 +1,30 @@
+// 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 java.util.ArrayList;
+
+public class StructTypeTestReturnInt {
+ public Integer evaluate(ArrayList<Object> value) {
+ if (value == null) {
+ return null;
+ }
+ return (Integer) value.get(0);
+ }
+}
diff --git
a/regression-test/java-udf-src/src/main/java/org/apache/doris/udf/StructTypeTestStringInt.java
b/regression-test/java-udf-src/src/main/java/org/apache/doris/udf/StructTypeTestStringInt.java
new file mode 100644
index 00000000000..f5e76c7e2db
--- /dev/null
+++
b/regression-test/java-udf-src/src/main/java/org/apache/doris/udf/StructTypeTestStringInt.java
@@ -0,0 +1,33 @@
+// 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 java.util.ArrayList;
+
+public class StructTypeTestStringInt {
+ public ArrayList<Object> evaluate(Integer val1, String val2) {
+ if (val1 == null && val2 == null) {
+ return null;
+ }
+ ArrayList<Object> res = new ArrayList<>();
+ res.add(val1);
+ res.add(val2);
+ return res;
+ }
+}
diff --git a/regression-test/suites/javaudf_p0/test_javaudf_struct_type.groovy
b/regression-test/suites/javaudf_p0/test_javaudf_struct_type.groovy
new file mode 100644
index 00000000000..e8ff5fb594f
--- /dev/null
+++ b/regression-test/suites/javaudf_p0/test_javaudf_struct_type.groovy
@@ -0,0 +1,97 @@
+// 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_struct_type") {
+ def tableName = "test_javaudf_struct_type"
+ 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,
+ `name` string,
+ `decimal_col` decimal(9,3),
+ `s_info` STRUCT<s_id:int(11), s_name:string> NULL
+ )
+ DISTRIBUTED BY HASH(user_id) PROPERTIES("replication_num" = "1");
+ """
+ sql """ INSERT INTO ${tableName} VALUES (1, "asd", 123.456, {1,
'sn11'}); """
+ sql """ INSERT INTO ${tableName} VALUES (2, "qwe", 546.677, {2,
'sn22'}); """
+ sql """ INSERT INTO ${tableName} VALUES (3, "kljkl", 8347.121, {3,
'sn333'}); """
+ sql """ INSERT INTO ${tableName} VALUES (4, "nvblk", 999.123, {4,
'sn444'}); """
+ sql """ INSERT INTO ${tableName} VALUES (5, "uyuy", 1.784, {5,
'sn3423'}); """
+ sql """ INSERT INTO ${tableName} VALUES (6, "ghjhj", 2.784, {6,
'sne543'}); """
+ sql """ INSERT INTO ${tableName} VALUES (7, "rtut", 3.784, {7,
'sn6878'}); """
+ sql """ INSERT INTO ${tableName} VALUES (8, "vnvc", 4.784, {8,
'sn7989'}); """
+ sql """ INSERT INTO ${tableName} VALUES (9, "asdzdf", 5.784, {9,
'sn242'}); """
+ 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_struct_test(STRUCT<s_id:int,
s_name:string>) RETURNS STRUCT<s_id:int, s_name:string> PROPERTIES (
+ "file"="file://${jarPath}",
+ "symbol"="org.apache.doris.udf.StructTypeTest",
+ "type"="JAVA_UDF"
+ ); """
+
+ qt_select_1 """ SELECT s_info, java_udf_struct_test(s_info) result
FROM ${tableName} ORDER BY user_id; """
+
+
+ sql """ CREATE FUNCTION java_udf_struct_string_int_test(int,string)
RETURNS STRUCT<s_id:int, s_name:string> PROPERTIES (
+ "file"="file://${jarPath}",
+ "symbol"="org.apache.doris.udf.StructTypeTestStringInt",
+ "type"="JAVA_UDF"
+ ); """
+
+ qt_select_2 """ SELECT user_id, name,
java_udf_struct_string_int_test(user_id, name) result FROM ${tableName} ORDER
BY user_id; """
+
+ sql """ CREATE FUNCTION
java_udf_struct_return_int_test(STRUCT<s_id:int, s_name:string>) RETURNS int
PROPERTIES (
+ "file"="file://${jarPath}",
+ "symbol"="org.apache.doris.udf.StructTypeTestReturnInt",
+ "type"="JAVA_UDF"
+ ); """
+
+ qt_select_3 """ SELECT s_info, java_udf_struct_return_int_test(s_info)
result FROM ${tableName} ORDER BY user_id; """
+
+
+ sql """ CREATE FUNCTION java_udf_struct_decimal_test(decimal(9,3))
RETURNS STRUCT<s_id:decimal(9,3)> PROPERTIES (
+ "file"="file://${jarPath}",
+ "symbol"="org.apache.doris.udf.StructTypeTestDecimal",
+ "type"="JAVA_UDF"
+ ); """
+
+ qt_select_4 """ SELECT decimal_col,
java_udf_struct_decimal_test(decimal_col) result FROM ${tableName} ORDER BY
user_id; """
+
+ } finally {
+ try_sql("DROP FUNCTION IF EXISTS java_udf_struct_test(STRUCT<s_id:int,
s_name:string>);")
+ try_sql("DROP FUNCTION IF EXISTS
java_udf_struct_string_int_test(int,string);")
+ try_sql("DROP FUNCTION IF EXISTS
java_udf_struct_return_int_test(STRUCT<s_id:int, s_name:string>);")
+ try_sql("DROP FUNCTION IF EXISTS
java_udf_struct_decimal_test(decimal(9,3));")
+ try_sql("DROP TABLE IF EXISTS ${tableName}")
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]