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 <87313068+zhangstar...@users.noreply.github.com>
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: commits-unsubscr...@doris.apache.org
For additional commands, e-mail: commits-h...@doris.apache.org


Reply via email to