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