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 3a4b7bb6b65 [feature](java-udtf) support java-udtf (#33595)
3a4b7bb6b65 is described below

commit 3a4b7bb6b656b0ec3347b603cd8e99e33441eeab
Author: zhangstar333 <[email protected]>
AuthorDate: Fri Apr 19 22:48:31 2024 +0800

    [feature](java-udtf) support java-udtf (#33595)
---
 be/src/pipeline/exec/table_function_operator.cpp   |   12 +-
 be/src/pipeline/exec/table_function_operator.h     |    9 +-
 be/src/vec/columns/column_array.cpp                |    2 +-
 be/src/vec/exec/vtable_function_node.cpp           |    4 +-
 be/src/vec/exec/vtable_function_node.h             |   11 +-
 be/src/vec/exprs/table_function/table_function.h   |    1 +
 .../table_function/table_function_factory.cpp      |   35 +-
 .../exprs/table_function/table_function_factory.h  |    4 +-
 .../exprs/table_function/udf_table_function.cpp    |  199 +++
 .../vec/exprs/table_function/udf_table_function.h  |   97 ++
 be/src/vec/exprs/vectorized_fn_call.cpp            |    9 +-
 be/src/vec/functions/function_fake.h               |    8 +
 .../java/org/apache/doris/udf/BaseExecutor.java    |    4 +
 .../org/apache/doris/common/FeMetaVersion.java     |    4 +-
 fe/fe-core/src/main/cup/sql_parser.cup             |    5 +
 .../apache/doris/analysis/CreateFunctionStmt.java  |   35 +
 .../apache/doris/analysis/FunctionCallExpr.java    |   12 +-
 .../main/java/org/apache/doris/catalog/Env.java    |    9 +
 .../java/org/apache/doris/catalog/Function.java    |   23 +-
 .../org/apache/doris/catalog/FunctionUtil.java     |    7 +-
 .../org/apache/doris/catalog/ScalarFunction.java   |   15 +
 .../glue/translator/ExpressionTranslator.java      |    9 +
 .../trees/expressions/functions/udf/JavaUdtf.java  |  171 +++
 .../expressions/functions/udf/JavaUdtfBuilder.java |   87 ++
 .../visitor/TableGeneratingFunctionVisitor.java    |    4 +
 gensrc/thrift/Types.thrift                         |    1 +
 .../data/javaudf_p0/test_javaudtf_all_types.out    | 1390 ++++++++++++++++++++
 .../data/javaudf_p0/test_javaudtf_arrayint.out     |   25 +
 .../data/javaudf_p0/test_javaudtf_decimal.out      |   15 +
 .../data/javaudf_p0/test_javaudtf_double.out       |   15 +
 .../data/javaudf_p0/test_javaudtf_int.out          |   32 +
 .../data/javaudf_p0/test_javaudtf_string.out       |   34 +
 .../java/org/apache/doris/udf/UDTFAllTypeTest.java |  210 +++
 .../org/apache/doris/udf/UDTFArrayIntTest.java     |   35 +-
 .../java/org/apache/doris/udf/UDTFDecimalTest.java |   35 +-
 .../java/org/apache/doris/udf/UDTFDoubleTest.java  |   33 +-
 .../java/org/apache/doris/udf/UDTFIntTest.java     |   34 +-
 .../java/org/apache/doris/udf/UDTFNullTest.java    |   30 +-
 .../java/org/apache/doris/udf/UDTFStringTest.java  |   35 +-
 .../javaudf_p0/test_javaudtf_all_types.groovy      |  233 ++++
 .../javaudf_p0/test_javaudtf_arrayint.groovy       |   75 ++
 .../suites/javaudf_p0/test_javaudtf_decimal.groovy |   67 +
 .../suites/javaudf_p0/test_javaudtf_double.groovy  |   70 +
 .../suites/javaudf_p0/test_javaudtf_int.groovy     |   74 ++
 .../suites/javaudf_p0/test_javaudtf_string.groovy  |   85 ++
 45 files changed, 3136 insertions(+), 168 deletions(-)

diff --git a/be/src/pipeline/exec/table_function_operator.cpp 
b/be/src/pipeline/exec/table_function_operator.cpp
index b4d993ef035..9256d1deb2b 100644
--- a/be/src/pipeline/exec/table_function_operator.cpp
+++ b/be/src/pipeline/exec/table_function_operator.cpp
@@ -53,13 +53,15 @@ Status TableFunctionLocalState::open(RuntimeState* state) {
     for (size_t i = 0; i < _vfn_ctxs.size(); i++) {
         RETURN_IF_ERROR(p._vfn_ctxs[i]->clone(state, _vfn_ctxs[i]));
 
-        const std::string& tf_name = 
_vfn_ctxs[i]->root()->fn().name.function_name;
         vectorized::TableFunction* fn = nullptr;
-        RETURN_IF_ERROR(vectorized::TableFunctionFactory::get_fn(tf_name, 
state->obj_pool(), &fn));
+        
RETURN_IF_ERROR(vectorized::TableFunctionFactory::get_fn(_vfn_ctxs[i]->root()->fn(),
+                                                                 
state->obj_pool(), &fn));
         fn->set_expr_context(_vfn_ctxs[i]);
         _fns.push_back(fn);
     }
-
+    for (auto* fn : _fns) {
+        RETURN_IF_ERROR(fn->open());
+    }
     _cur_child_offset = -1;
     return Status::OK();
 }
@@ -138,6 +140,7 @@ bool TableFunctionLocalState::_roll_table_functions(int 
last_eos_idx) {
 bool TableFunctionLocalState::_is_inner_and_empty() {
     for (int i = 0; i < _parent->cast<TableFunctionOperatorX>()._fn_num; i++) {
         // if any table function is not outer and has empty result, go to next 
child row
+        // if it's outer function, will be insert into one row NULL
         if (!_fns[i]->is_outer() && _fns[i]->current_empty()) {
             return true;
         }
@@ -269,9 +272,8 @@ Status TableFunctionOperatorX::init(const TPlanNode& tnode, 
RuntimeState* state)
         _vfn_ctxs.push_back(ctx);
 
         auto root = ctx->root();
-        const std::string& tf_name = root->fn().name.function_name;
         vectorized::TableFunction* fn = nullptr;
-        RETURN_IF_ERROR(vectorized::TableFunctionFactory::get_fn(tf_name, 
_pool, &fn));
+        RETURN_IF_ERROR(vectorized::TableFunctionFactory::get_fn(root->fn(), 
_pool, &fn));
         fn->set_expr_context(ctx);
         _fns.push_back(fn);
     }
diff --git a/be/src/pipeline/exec/table_function_operator.h 
b/be/src/pipeline/exec/table_function_operator.h
index 49dd242bfe7..8a7b7bd43d4 100644
--- a/be/src/pipeline/exec/table_function_operator.h
+++ b/be/src/pipeline/exec/table_function_operator.h
@@ -56,6 +56,13 @@ public:
     ~TableFunctionLocalState() override = default;
 
     Status open(RuntimeState* state) override;
+    Status close(RuntimeState* state) override {
+        for (auto* fn : _fns) {
+            RETURN_IF_ERROR(fn->close());
+        }
+        RETURN_IF_ERROR(PipelineXLocalState<>::close(state));
+        return Status::OK();
+    }
     void process_next_child_row();
     Status get_expanded_block(RuntimeState* state, vectorized::Block* 
output_block, bool* eos);
 
@@ -74,7 +81,7 @@ private:
 
     std::vector<vectorized::TableFunction*> _fns;
     vectorized::VExprContextSPtrs _vfn_ctxs;
-    int64_t _cur_child_offset = 0;
+    int64_t _cur_child_offset = -1;
     std::unique_ptr<vectorized::Block> _child_block;
     int _current_row_insert_times = 0;
     bool _child_eos = false;
diff --git a/be/src/vec/columns/column_array.cpp 
b/be/src/vec/columns/column_array.cpp
index 591eb74ca09..aba07557e83 100644
--- a/be/src/vec/columns/column_array.cpp
+++ b/be/src/vec/columns/column_array.cpp
@@ -416,7 +416,7 @@ void ColumnArray::insert(const Field& x) {
 }
 
 void ColumnArray::insert_from(const IColumn& src_, size_t n) {
-    DCHECK(n < src_.size());
+    DCHECK_LT(n, src_.size());
     const ColumnArray& src = assert_cast<const ColumnArray&>(src_);
     size_t size = src.size_at(n);
     size_t offset = src.offset_at(n);
diff --git a/be/src/vec/exec/vtable_function_node.cpp 
b/be/src/vec/exec/vtable_function_node.cpp
index 0c35fae806e..8a04be3b6bb 100644
--- a/be/src/vec/exec/vtable_function_node.cpp
+++ b/be/src/vec/exec/vtable_function_node.cpp
@@ -58,9 +58,8 @@ Status VTableFunctionNode::init(const TPlanNode& tnode, 
RuntimeState* state) {
         _vfn_ctxs.push_back(ctx);
 
         auto root = ctx->root();
-        const std::string& tf_name = root->fn().name.function_name;
         TableFunction* fn = nullptr;
-        RETURN_IF_ERROR(TableFunctionFactory::get_fn(tf_name, _pool, &fn));
+        RETURN_IF_ERROR(TableFunctionFactory::get_fn(root->fn(), _pool, &fn));
         fn->set_expr_context(ctx);
         _fns.push_back(fn);
     }
@@ -93,6 +92,7 @@ Status VTableFunctionNode::_prepare_output_slot_ids(const 
TPlanNode& tnode) {
 bool VTableFunctionNode::_is_inner_and_empty() {
     for (int i = 0; i < _fn_num; i++) {
         // if any table function is not outer and has empty result, go to next 
child row
+        // if it's outer function, will be insert into NULL
         if (!_fns[i]->is_outer() && _fns[i]->current_empty()) {
             return true;
         }
diff --git a/be/src/vec/exec/vtable_function_node.h 
b/be/src/vec/exec/vtable_function_node.h
index 0b64fe47cc5..41dbd8bab64 100644
--- a/be/src/vec/exec/vtable_function_node.h
+++ b/be/src/vec/exec/vtable_function_node.h
@@ -58,7 +58,11 @@ public:
     Status alloc_resource(RuntimeState* state) override {
         SCOPED_TIMER(_exec_timer);
         RETURN_IF_ERROR(ExecNode::alloc_resource(state));
-        return VExpr::open(_vfn_ctxs, state);
+        RETURN_IF_ERROR(VExpr::open(_vfn_ctxs, state));
+        for (auto* fn : _fns) {
+            RETURN_IF_ERROR(fn->open());
+        }
+        return Status::OK();
     }
     Status get_next(RuntimeState* state, Block* block, bool* eos) override;
     bool need_more_input_data() const { return !_child_block->rows() && 
!_child_eos; }
@@ -67,6 +71,9 @@ public:
         if (_num_rows_filtered_counter != nullptr) {
             COUNTER_SET(_num_rows_filtered_counter, 
static_cast<int64_t>(_num_rows_filtered));
         }
+        for (auto* fn : _fns) {
+            static_cast<void>(fn->close());
+        }
         ExecNode::release_resource(state);
     }
 
@@ -145,7 +152,7 @@ private:
     std::shared_ptr<Block> _child_block;
     std::vector<SlotDescriptor*> _child_slots;
     std::vector<SlotDescriptor*> _output_slots;
-    int64_t _cur_child_offset = 0;
+    int64_t _cur_child_offset = -1;
 
     VExprContextSPtrs _vfn_ctxs;
 
diff --git a/be/src/vec/exprs/table_function/table_function.h 
b/be/src/vec/exprs/table_function/table_function.h
index 98d81136439..c817067470a 100644
--- a/be/src/vec/exprs/table_function/table_function.h
+++ b/be/src/vec/exprs/table_function/table_function.h
@@ -58,6 +58,7 @@ public:
     virtual int get_value(MutableColumnPtr& column, int max_step) {
         max_step = std::max(1, std::min(max_step, (int)(_cur_size - 
_cur_offset)));
         int i = 0;
+        // TODO: this for loop maybe could refactor, and call once get_value 
function, it's could insert into max_step value once
         for (; i < max_step && !eos(); i++) {
             get_value(column);
             forward();
diff --git a/be/src/vec/exprs/table_function/table_function_factory.cpp 
b/be/src/vec/exprs/table_function/table_function_factory.cpp
index e42c0a27fd1..29b201b5947 100644
--- a/be/src/vec/exprs/table_function/table_function_factory.cpp
+++ b/be/src/vec/exprs/table_function/table_function_factory.cpp
@@ -17,10 +17,14 @@
 
 #include "vec/exprs/table_function/table_function_factory.h"
 
+#include <gen_cpp/Types_types.h>
+
+#include <string_view>
 #include <utility>
 
 #include "common/object_pool.h"
 #include "vec/exprs/table_function/table_function.h"
+#include "vec/exprs/table_function/udf_table_function.h"
 #include "vec/exprs/table_function/vexplode.h"
 #include "vec/exprs/table_function/vexplode_bitmap.h"
 #include "vec/exprs/table_function/vexplode_json_array.h"
@@ -65,23 +69,30 @@ const std::unordered_map<std::string, 
std::function<std::unique_ptr<TableFunctio
                 {"explode_map", TableFunctionCreator<VExplodeMapTableFunction> 
{}},
                 {"explode", TableFunctionCreator<VExplodeTableFunction> {}}};
 
-Status TableFunctionFactory::get_fn(const std::string& fn_name_raw, 
ObjectPool* pool,
-                                    TableFunction** fn) {
-    bool is_outer = match_suffix(fn_name_raw, COMBINATOR_SUFFIX_OUTER);
-    std::string fn_name_real =
-            is_outer ? remove_suffix(fn_name_raw, COMBINATOR_SUFFIX_OUTER) : 
fn_name_raw;
-
-    auto fn_iterator = _function_map.find(fn_name_real);
-    if (fn_iterator != _function_map.end()) {
-        *fn = pool->add(fn_iterator->second().release());
+Status TableFunctionFactory::get_fn(const TFunction& t_fn, ObjectPool* pool, 
TableFunction** fn) {
+    bool is_outer = match_suffix(t_fn.name.function_name, 
COMBINATOR_SUFFIX_OUTER);
+    if (t_fn.binary_type == TFunctionBinaryType::JAVA_UDF) {
+        *fn = pool->add(UDFTableFunction::create_unique(t_fn).release());
         if (is_outer) {
             (*fn)->set_outer();
         }
-
         return Status::OK();
-    }
+    } else {
+        const std::string& fn_name_raw = t_fn.name.function_name;
+        const std::string& fn_name_real =
+                is_outer ? remove_suffix(fn_name_raw, COMBINATOR_SUFFIX_OUTER) 
: fn_name_raw;
 
-    return Status::NotSupported("Table function {} is not support", 
fn_name_raw);
+        auto fn_iterator = _function_map.find(fn_name_real);
+        if (fn_iterator != _function_map.end()) {
+            *fn = pool->add(fn_iterator->second().release());
+            if (is_outer) {
+                (*fn)->set_outer();
+            }
+
+            return Status::OK();
+        }
+    }
+    return Status::NotSupported("Table function {} is not support", 
t_fn.name.function_name);
 }
 
 } // namespace doris::vectorized
diff --git a/be/src/vec/exprs/table_function/table_function_factory.h 
b/be/src/vec/exprs/table_function/table_function_factory.h
index a68a1763fc4..cd06c202f37 100644
--- a/be/src/vec/exprs/table_function/table_function_factory.h
+++ b/be/src/vec/exprs/table_function/table_function_factory.h
@@ -17,6 +17,8 @@
 
 #pragma once
 
+#include <gen_cpp/Types_types.h>
+
 #include <functional>
 #include <memory>
 #include <string>
@@ -33,7 +35,7 @@ class TableFunction;
 class TableFunctionFactory {
 public:
     TableFunctionFactory() = delete;
-    static Status get_fn(const std::string& fn_name_raw, ObjectPool* pool, 
TableFunction** fn);
+    static Status get_fn(const TFunction& t_fn, ObjectPool* pool, 
TableFunction** fn);
 
     const static std::unordered_map<std::string, 
std::function<std::unique_ptr<TableFunction>()>>
             _function_map;
diff --git a/be/src/vec/exprs/table_function/udf_table_function.cpp 
b/be/src/vec/exprs/table_function/udf_table_function.cpp
new file mode 100644
index 00000000000..bc4c815ceb1
--- /dev/null
+++ b/be/src/vec/exprs/table_function/udf_table_function.cpp
@@ -0,0 +1,199 @@
+// 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.
+
+#include "vec/exprs/table_function/udf_table_function.h"
+
+#include <glog/logging.h>
+
+#include "runtime/user_function_cache.h"
+#include "vec/columns/column_array.h"
+#include "vec/columns/column_nullable.h"
+#include "vec/common/assert_cast.h"
+#include "vec/core/block.h"
+#include "vec/core/types.h"
+#include "vec/data_types/data_type_array.h"
+#include "vec/data_types/data_type_factory.hpp"
+#include "vec/exec/jni_connector.h"
+#include "vec/exprs/vexpr.h"
+#include "vec/exprs/vexpr_context.h"
+
+namespace doris::vectorized {
+const char* EXECUTOR_CLASS = "org/apache/doris/udf/UdfExecutor";
+const char* EXECUTOR_CTOR_SIGNATURE = "([B)V";
+const char* EXECUTOR_EVALUATE_SIGNATURE = "(Ljava/util/Map;Ljava/util/Map;)J";
+const char* EXECUTOR_CLOSE_SIGNATURE = "()V";
+UDFTableFunction::UDFTableFunction(const TFunction& t_fn) : TableFunction(), 
_t_fn(t_fn) {
+    _fn_name = _t_fn.name.function_name;
+    _return_type = DataTypeFactory::instance().create_data_type(
+            TypeDescriptor::from_thrift(t_fn.ret_type));
+    // as the java-utdf function in java code is eg: ArrayList<String>
+    // so we need a array column to save the execute result, and make_nullable 
could help deal with nullmap
+    _return_type = 
make_nullable(std::make_shared<DataTypeArray>(make_nullable(_return_type)));
+}
+
+Status UDFTableFunction::open() {
+    JNIEnv* env = nullptr;
+    RETURN_IF_ERROR(JniUtil::GetJNIEnv(&env));
+    if (env == nullptr) {
+        return Status::InternalError("Failed to get/create JVM");
+    }
+    _jni_ctx = std::make_shared<JniContext>();
+    // Add a scoped cleanup jni reference object. This cleans up local refs 
made below.
+    JniLocalFrame jni_frame;
+    {
+        std::string local_location;
+        auto* function_cache = UserFunctionCache::instance();
+        RETURN_IF_ERROR(function_cache->get_jarpath(_t_fn.id, 
_t_fn.hdfs_location, _t_fn.checksum,
+                                                    &local_location));
+        TJavaUdfExecutorCtorParams ctor_params;
+        ctor_params.__set_fn(_t_fn);
+        ctor_params.__set_location(local_location);
+        jbyteArray ctor_params_bytes;
+        // Pushed frame will be popped when jni_frame goes out-of-scope.
+        RETURN_IF_ERROR(jni_frame.push(env));
+        RETURN_IF_ERROR(SerializeThriftMsg(env, &ctor_params, 
&ctor_params_bytes));
+        RETURN_IF_ERROR(JniUtil::GetGlobalClassRef(env, EXECUTOR_CLASS, 
&_jni_ctx->executor_cl));
+        _jni_ctx->executor_ctor_id =
+                env->GetMethodID(_jni_ctx->executor_cl, "<init>", 
EXECUTOR_CTOR_SIGNATURE);
+        _jni_ctx->executor_evaluate_id =
+                env->GetMethodID(_jni_ctx->executor_cl, "evaluate", 
EXECUTOR_EVALUATE_SIGNATURE);
+        _jni_ctx->executor_close_id =
+                env->GetMethodID(_jni_ctx->executor_cl, "close", 
EXECUTOR_CLOSE_SIGNATURE);
+        _jni_ctx->executor = env->NewObject(_jni_ctx->executor_cl, 
_jni_ctx->executor_ctor_id,
+                                            ctor_params_bytes);
+        jbyte* pBytes = env->GetByteArrayElements(ctor_params_bytes, nullptr);
+        env->ReleaseByteArrayElements(ctor_params_bytes, pBytes, JNI_ABORT);
+        env->DeleteLocalRef(ctor_params_bytes);
+    }
+    RETURN_ERROR_IF_EXC(env);
+    RETURN_IF_ERROR(JniUtil::LocalToGlobalRef(env, _jni_ctx->executor, 
&_jni_ctx->executor));
+    _jni_ctx->open_successes = true;
+    return Status::OK();
+}
+
+Status UDFTableFunction::process_init(Block* block, RuntimeState* state) {
+    auto child_size = _expr_context->root()->children().size();
+    std::vector<size_t> child_column_idxs;
+    child_column_idxs.resize(child_size);
+    for (int i = 0; i < child_size; ++i) {
+        int result_id = -1;
+        
RETURN_IF_ERROR(_expr_context->root()->children()[i]->execute(_expr_context.get(),
 block,
+                                                                      
&result_id));
+        DCHECK_NE(result_id, -1);
+        child_column_idxs[i] = result_id;
+    }
+    JNIEnv* env = nullptr;
+    RETURN_IF_ERROR(JniUtil::GetJNIEnv(&env));
+    std::unique_ptr<long[]> input_table;
+    RETURN_IF_ERROR(
+            JniConnector::to_java_table(block, block->rows(), 
child_column_idxs, input_table));
+    auto input_table_schema = JniConnector::parse_table_schema(block, 
child_column_idxs, true);
+    std::map<String, String> input_params = {
+            {"meta_address", std::to_string((long)input_table.get())},
+            {"required_fields", input_table_schema.first},
+            {"columns_types", input_table_schema.second}};
+
+    jobject input_map = JniUtil::convert_to_java_map(env, input_params);
+    _array_result_column = _return_type->create_column();
+    _result_column_idx = block->columns();
+    block->insert({_array_result_column, _return_type, "res"});
+    auto output_table_schema = JniConnector::parse_table_schema(block, 
{_result_column_idx}, true);
+    std::string output_nullable = _return_type->is_nullable() ? "true" : 
"false";
+    std::map<String, String> output_params = {{"is_nullable", output_nullable},
+                                              {"required_fields", 
output_table_schema.first},
+                                              {"columns_types", 
output_table_schema.second}};
+
+    jobject output_map = JniUtil::convert_to_java_map(env, output_params);
+    DCHECK(_jni_ctx != nullptr);
+    DCHECK(_jni_ctx->executor != nullptr);
+    long output_address = env->CallLongMethod(_jni_ctx->executor, 
_jni_ctx->executor_evaluate_id,
+                                              input_map, output_map);
+    RETURN_IF_ERROR(JniUtil::GetJniExceptionMsg(env));
+    env->DeleteLocalRef(input_map);
+    env->DeleteLocalRef(output_map);
+    RETURN_IF_ERROR(JniConnector::fill_block(block, {_result_column_idx}, 
output_address));
+    block->erase(_result_column_idx);
+    if (!extract_column_array_info(*_array_result_column, 
_array_column_detail)) {
+        return Status::NotSupported("column type {} not supported now",
+                                    
block->get_by_position(_result_column_idx).column->get_name());
+    }
+    return Status::OK();
+}
+
+void UDFTableFunction::process_row(size_t row_idx) {
+    TableFunction::process_row(row_idx);
+    if (!_array_column_detail.array_nullmap_data ||
+        !_array_column_detail.array_nullmap_data[row_idx]) {
+        _array_offset = (*_array_column_detail.offsets_ptr)[row_idx - 1];
+        _cur_size = (*_array_column_detail.offsets_ptr)[row_idx] - 
_array_offset;
+    }
+    // so when it's NULL of row_idx, will not update _cur_size
+    // it's will be _cur_size == 0, and means current_empty.
+    // if the fn is outer, will be continue insert_default
+    // if the fn is not outer function, will be not insert any value.
+}
+
+void UDFTableFunction::process_close() {
+    _array_result_column = nullptr;
+    _array_column_detail.reset();
+    _array_offset = 0;
+}
+
+void UDFTableFunction::get_value(MutableColumnPtr& column) {
+    size_t pos = _array_offset + _cur_offset;
+    if (current_empty() || (_array_column_detail.nested_nullmap_data &&
+                            _array_column_detail.nested_nullmap_data[pos])) {
+        column->insert_default();
+    } else {
+        if (_is_nullable) {
+            auto* nullable_column = assert_cast<ColumnNullable*>(column.get());
+            auto nested_column = nullable_column->get_nested_column_ptr();
+            auto nullmap_column = nullable_column->get_null_map_column_ptr();
+            nested_column->insert_from(*_array_column_detail.nested_col, pos);
+            assert_cast<ColumnUInt8*>(nullmap_column.get())->insert_default();
+        } else {
+            column->insert_from(*_array_column_detail.nested_col, pos);
+        }
+    }
+}
+
+int UDFTableFunction::get_value(MutableColumnPtr& column, int max_step) {
+    max_step = std::min(max_step, (int)(_cur_size - _cur_offset));
+    size_t pos = _array_offset + _cur_offset;
+    if (current_empty()) {
+        column->insert_default();
+        max_step = 1;
+    } else {
+        if (_is_nullable) {
+            auto* nullable_column = assert_cast<ColumnNullable*>(column.get());
+            auto nested_column = nullable_column->get_nested_column_ptr();
+            auto* nullmap_column =
+                    
assert_cast<ColumnUInt8*>(nullable_column->get_null_map_column_ptr().get());
+            nested_column->insert_range_from(*_array_column_detail.nested_col, 
pos, max_step);
+            size_t old_size = nullmap_column->size();
+            nullmap_column->resize(old_size + max_step);
+            memcpy(nullmap_column->get_data().data() + old_size,
+                   _array_column_detail.nested_nullmap_data + pos * 
sizeof(UInt8),
+                   max_step * sizeof(UInt8));
+        } else {
+            column->insert_range_from(*_array_column_detail.nested_col, pos, 
max_step);
+        }
+    }
+    forward(max_step);
+    return max_step;
+}
+} // namespace doris::vectorized
diff --git a/be/src/vec/exprs/table_function/udf_table_function.h 
b/be/src/vec/exprs/table_function/udf_table_function.h
new file mode 100644
index 00000000000..ae6a7c13b35
--- /dev/null
+++ b/be/src/vec/exprs/table_function/udf_table_function.h
@@ -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.
+
+#pragma once
+
+#include "common/status.h"
+#include "jni.h"
+#include "util/jni-util.h"
+#include "vec/columns/column.h"
+#include "vec/data_types/data_type.h"
+#include "vec/exprs/table_function/table_function.h"
+#include "vec/functions/array/function_array_utils.h"
+
+namespace doris::vectorized {
+
+class UDFTableFunction final : public TableFunction {
+    ENABLE_FACTORY_CREATOR(UDFTableFunction);
+
+public:
+    UDFTableFunction(const TFunction& t_fn);
+    ~UDFTableFunction() override = default;
+
+    Status open() override;
+    Status process_init(Block* block, RuntimeState* state) override;
+    void process_row(size_t row_idx) override;
+    void process_close() override;
+    void get_value(MutableColumnPtr& column) override;
+    int get_value(MutableColumnPtr& column, int max_step) override;
+    Status close() override {
+        if (_jni_ctx) {
+            RETURN_IF_ERROR(_jni_ctx->close());
+        }
+        return TableFunction::close();
+    }
+
+private:
+    struct JniContext {
+        // Do not save parent directly, because parent is in VExpr, but jni 
context is in FunctionContext
+        // The deconstruct sequence is not determined, it will core.
+        // JniContext's lifecycle should same with function context, not 
related with expr
+        jclass executor_cl;
+        jmethodID executor_ctor_id;
+        jmethodID executor_evaluate_id;
+        jmethodID executor_close_id;
+        jobject executor = nullptr;
+        bool is_closed = false;
+        bool open_successes = false;
+
+        JniContext() = default;
+
+        Status close() {
+            if (!open_successes) {
+                LOG_WARNING("maybe open failed, need check the reason");
+                return Status::OK(); //maybe open failed, so can't call some 
jni
+            }
+            if (is_closed) {
+                return Status::OK();
+            }
+            JNIEnv* env = nullptr;
+            Status status = JniUtil::GetJNIEnv(&env);
+            if (!status.ok() || env == nullptr) {
+                LOG(WARNING) << "errors while get jni env " << status;
+                return status;
+            }
+            env->CallNonvirtualVoidMethodA(executor, executor_cl, 
executor_close_id, nullptr);
+            env->DeleteGlobalRef(executor);
+            env->DeleteGlobalRef(executor_cl);
+            RETURN_IF_ERROR(JniUtil::GetJniExceptionMsg(env));
+            is_closed = true;
+            return Status::OK();
+        }
+    };
+
+    const TFunction& _t_fn;
+    std::shared_ptr<JniContext> _jni_ctx = nullptr;
+    DataTypePtr _return_type = nullptr;
+    ColumnPtr _array_result_column = nullptr;
+    ColumnArrayExecutionData _array_column_detail;
+    size_t _result_column_idx = 0; // _array_result_column pos in block
+    size_t _array_offset = 0;      // start offset of array[row_idx]
+};
+
+} // namespace doris::vectorized
diff --git a/be/src/vec/exprs/vectorized_fn_call.cpp 
b/be/src/vec/exprs/vectorized_fn_call.cpp
index 1c08b721cf9..b84499653eb 100644
--- a/be/src/vec/exprs/vectorized_fn_call.cpp
+++ b/be/src/vec/exprs/vectorized_fn_call.cpp
@@ -39,6 +39,7 @@
 #include "vec/data_types/data_type_agg_state.h"
 #include "vec/exprs/vexpr_context.h"
 #include "vec/functions/function_agg_state.h"
+#include "vec/functions/function_fake.h"
 #include "vec/functions/function_java_udf.h"
 #include "vec/functions/function_rpc.h"
 #include "vec/functions/simple_function_factory.h"
@@ -67,12 +68,16 @@ Status VectorizedFnCall::prepare(RuntimeState* state, const 
RowDescriptor& desc,
 
     _expr_name = fmt::format("VectorizedFnCall[{}](arguments={},return={})", 
_fn.name.function_name,
                              get_child_names(), _data_type->get_name());
-
     if (_fn.binary_type == TFunctionBinaryType::RPC) {
         _function = FunctionRPC::create(_fn, argument_template, _data_type);
     } else if (_fn.binary_type == TFunctionBinaryType::JAVA_UDF) {
         if (config::enable_java_support) {
-            _function = JavaFunctionCall::create(_fn, argument_template, 
_data_type);
+            if (_fn.is_udtf_function) {
+                // fake function. it's no use and can't execute.
+                _function = FunctionFake<UDTFImpl>::create();
+            } else {
+                _function = JavaFunctionCall::create(_fn, argument_template, 
_data_type);
+            }
         } else {
             return Status::InternalError(
                     "Java UDF is not enabled, you can change be config 
enable_java_support to true "
diff --git a/be/src/vec/functions/function_fake.h 
b/be/src/vec/functions/function_fake.h
index 0dabdfb3c83..b4891ed9392 100644
--- a/be/src/vec/functions/function_fake.h
+++ b/be/src/vec/functions/function_fake.h
@@ -63,4 +63,12 @@ public:
     }
 };
 
+struct UDTFImpl {
+    static DataTypePtr get_return_type_impl(const DataTypes& arguments) {
+        DCHECK(false) << "get_return_type_impl not supported, shouldn't into 
here.";
+        return nullptr;
+    }
+    static std::string get_error_msg() { return "Fake function do not support 
execute"; }
+};
+
 } // namespace doris::vectorized
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 8ad171d6013..2cb8ed5351f 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
@@ -17,6 +17,7 @@
 
 package org.apache.doris.udf;
 
+import org.apache.doris.catalog.ArrayType;
 import org.apache.doris.catalog.Type;
 import org.apache.doris.common.exception.InternalException;
 import org.apache.doris.common.exception.UdfRuntimeException;
@@ -88,6 +89,9 @@ public abstract class BaseExecutor {
         fn = request.fn;
         String jarFile = request.location;
         Type funcRetType = Type.fromThrift(request.fn.ret_type);
+        if (request.fn.is_udtf_function) {
+            funcRetType = ArrayType.create(funcRetType, true);
+        }
         init(request, jarFile, funcRetType, parameterTypes);
     }
 
diff --git 
a/fe/fe-common/src/main/java/org/apache/doris/common/FeMetaVersion.java 
b/fe/fe-common/src/main/java/org/apache/doris/common/FeMetaVersion.java
index ad273b3b2a5..6251632bd29 100644
--- a/fe/fe-common/src/main/java/org/apache/doris/common/FeMetaVersion.java
+++ b/fe/fe-common/src/main/java/org/apache/doris/common/FeMetaVersion.java
@@ -80,9 +80,11 @@ public final class FeMetaVersion {
     public static final int VERSION_129 = 129;
 
     public static final int VERSION_130 = 130;
+    // for java-udtf add a bool field to write
+    public static final int VERSION_131 = 131;
 
     // note: when increment meta version, should assign the latest version to 
VERSION_CURRENT
-    public static final int VERSION_CURRENT = VERSION_130;
+    public static final int VERSION_CURRENT = VERSION_131;
 
     // all logs meta version should >= the minimum version, so that we could 
remove many if clause, for example
     // if (FE_METAVERSION < VERSION_94) ...
diff --git a/fe/fe-core/src/main/cup/sql_parser.cup 
b/fe/fe-core/src/main/cup/sql_parser.cup
index 85e13af50dd..3f3093969a0 100644
--- a/fe/fe-core/src/main/cup/sql_parser.cup
+++ b/fe/fe-core/src/main/cup/sql_parser.cup
@@ -1804,6 +1804,11 @@ create_stmt ::=
     {:
         RESULT = new CreateFunctionStmt(type, ifNotExists, functionName, args, 
parameters, func);
     :}
+    | KW_CREATE opt_var_type:type KW_TABLES KW_FUNCTION 
opt_if_not_exists:ifNotExists function_name:functionName LPAREN 
func_args_def:args RPAREN
+            KW_RETURNS type_def:returnType 
opt_intermediate_type:intermediateType opt_properties:properties
+    {:
+        RESULT = new CreateFunctionStmt(type, ifNotExists, functionName, args, 
returnType, intermediateType, properties);
+    :}
     /* Table */
     | KW_CREATE opt_external:isExternal KW_TABLE opt_if_not_exists:ifNotExists 
table_name:name KW_LIKE table_name:existed_name KW_WITH KW_ROLLUP LPAREN 
ident_list:rollupNames RPAREN
     {:
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 d498d1f75bc..ed618d1603d 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
@@ -104,6 +104,7 @@ public class CreateFunctionStmt extends DdlStmt {
     private final FunctionName functionName;
     private final boolean isAggregate;
     private final boolean isAlias;
+    private boolean isTableFunction;
     private final FunctionArgsDef argsDef;
     private final TypeDef returnType;
     private TypeDef intermediateType;
@@ -140,10 +141,18 @@ public class CreateFunctionStmt extends DdlStmt {
             this.properties = ImmutableSortedMap.copyOf(properties, 
String.CASE_INSENSITIVE_ORDER);
         }
         this.isAlias = false;
+        this.isTableFunction = false;
         this.parameters = ImmutableList.of();
         this.originFunction = null;
     }
 
+    public CreateFunctionStmt(SetType type, boolean ifNotExists, FunctionName 
functionName,
+            FunctionArgsDef argsDef,
+            TypeDef returnType, TypeDef intermediateType, Map<String, String> 
properties) {
+        this(type, ifNotExists, false, functionName, argsDef, returnType, 
intermediateType, properties);
+        this.isTableFunction = true;
+    }
+
     public CreateFunctionStmt(SetType type, boolean ifNotExists, FunctionName 
functionName, FunctionArgsDef argsDef,
             List<String> parameters, Expr originFunction) {
         this.type = type;
@@ -158,6 +167,7 @@ public class CreateFunctionStmt extends DdlStmt {
         }
         this.originFunction = originFunction;
         this.isAggregate = false;
+        this.isTableFunction = false;
         this.returnType = new TypeDef(Type.VARCHAR);
         this.properties = ImmutableSortedMap.of();
     }
@@ -197,6 +207,8 @@ public class CreateFunctionStmt extends DdlStmt {
                     analyzeUda();
                 } else if (isAlias) {
                     analyzeAliasFunction();
+                } else if (isTableFunction) {
+                    analyzeTableFunction();
                 } else {
                     analyzeUdf();
                 }
@@ -208,6 +220,8 @@ public class CreateFunctionStmt extends DdlStmt {
                 analyzeUda();
             } else if (isAlias) {
                 analyzeAliasFunction();
+            } else if (isTableFunction) {
+                analyzeTableFunction();
             } else {
                 analyzeUdf();
             }
@@ -301,6 +315,27 @@ public class CreateFunctionStmt extends DdlStmt {
         }
     }
 
+    private void analyzeTableFunction() throws AnalysisException {
+        String symbol = properties.get(SYMBOL_KEY);
+        if (Strings.isNullOrEmpty(symbol)) {
+            throw new AnalysisException("No 'symbol' in properties");
+        }
+        if (!returnType.getType().isArrayType()) {
+            throw new AnalysisException("JAVA_UDF OF UDTF return type must be 
array type");
+        }
+        analyzeJavaUdf(symbol);
+        URI location = URI.create(userFile);
+        function = ScalarFunction.createUdf(binaryType,
+                functionName, argsDef.getArgTypes(),
+                ((ArrayType) (returnType.getType())).getItemType(), 
argsDef.isVariadic(),
+                location, symbol, null, null);
+        function.setChecksum(checksum);
+        function.setNullableMode(returnNullMode);
+        function.setUDTFunction(true);
+        // Todo: maybe in create tables function, need register two function, 
one is
+        // normal and one is outer as those have different result when result 
is NULL.
+    }
+
     private void analyzeUda() throws AnalysisException {
         AggregateFunction.AggregateFunctionBuilder builder
                 = 
AggregateFunction.AggregateFunctionBuilder.createUdfBuilder();
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/analysis/FunctionCallExpr.java 
b/fe/fe-core/src/main/java/org/apache/doris/analysis/FunctionCallExpr.java
index b703948468c..9cfce9e67de 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/analysis/FunctionCallExpr.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/FunctionCallExpr.java
@@ -1673,7 +1673,7 @@ public class FunctionCallExpr extends Expr {
                     fn = getTableFunction(fnName.getFunction(), 
matchFuncChildTypes,
                             Function.CompareMode.IS_NONSTRICT_SUPERTYPE_OF);
                     if (fn == null) {
-                        throw new 
AnalysisException(getFunctionNotFoundError(argTypes));
+                        throw new 
AnalysisException(getFunctionNotFoundError(argTypes)  + " in table function");
                     }
                     // set param child types
                     fn.setReturnType(((ArrayType) 
childTypes[0]).getItemType());
@@ -1681,6 +1681,16 @@ public class FunctionCallExpr extends Expr {
                     fn = getTableFunction(fnName.getFunction(), childTypes,
                             Function.CompareMode.IS_NONSTRICT_SUPERTYPE_OF);
                 }
+                // find user defined functions
+                if (fn == null) {
+                    fn = findUdf(fnName, analyzer);
+                    if (fn != null) {
+                        FunctionUtil.checkEnableJavaUdf();
+                        if (!fn.isUDTFunction()) {
+                            throw new 
AnalysisException(getFunctionNotFoundError(argTypes)  + " in table function");
+                        }
+                    }
+                }
                 if (fn == null) {
                     throw new 
AnalysisException(getFunctionNotFoundError(argTypes));
                 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/catalog/Env.java 
b/fe/fe-core/src/main/java/org/apache/doris/catalog/Env.java
index b7de2bcc836..aad9963c00f 100755
--- a/fe/fe-core/src/main/java/org/apache/doris/catalog/Env.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/Env.java
@@ -5374,6 +5374,15 @@ public class Env {
         } else {
             Database db = 
getInternalCatalog().getDbOrDdlException(stmt.getFunctionName().getDb());
             db.addFunction(stmt.getFunction(), stmt.isIfNotExists());
+            if (stmt.getFunction().isUDTFunction()) {
+                // all of the table function in doris will have two function
+                // one is the noraml, and another is outer, the different of 
them is deal with
+                // empty: whether need to insert NULL result value
+                Function outerFunction = stmt.getFunction().clone();
+                FunctionName name = outerFunction.getFunctionName();
+                name.setFn(name.getFunction() + "_outer");
+                db.addFunction(outerFunction, stmt.isIfNotExists());
+            }
         }
     }
 
diff --git a/fe/fe-core/src/main/java/org/apache/doris/catalog/Function.java 
b/fe/fe-core/src/main/java/org/apache/doris/catalog/Function.java
index 7dbf3a0ec0a..4d9c97e8dd9 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/catalog/Function.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/Function.java
@@ -138,6 +138,8 @@ public class Function implements Writable {
 
     // If true, this function is global function
     protected boolean isGlobal = false;
+    // If true, this function is table function, mainly used by java-udtf
+    protected boolean isUDTFunction = false;
 
     // Only used for serialization
     protected Function() {
@@ -196,6 +198,8 @@ public class Function implements Writable {
             System.arraycopy(other.argTypes, 0, this.argTypes, 0, 
other.argTypes.length);
         }
         this.checksum = other.checksum;
+        this.isGlobal = other.isGlobal;
+        this.isUDTFunction = other.isUDTFunction;
     }
 
     public void setNestedFunction(Function nestedFunction) {
@@ -563,6 +567,7 @@ public class Function implements Writable {
             fn.setChecksum(checksum);
         }
         fn.setVectorized(vectorized);
+        fn.setIsUdtfFunction(isUDTFunction);
         return fn;
     }
 
@@ -671,6 +676,7 @@ public class Function implements Writable {
         IOUtils.writeOptionString(output, libUrl);
         IOUtils.writeOptionString(output, checksum);
         output.writeUTF(nullableMode.toString());
+        output.writeBoolean(isUDTFunction);
     }
 
     @Override
@@ -708,6 +714,9 @@ public class Function implements Writable {
         if (Env.getCurrentEnvJournalVersion() >= FeMetaVersion.VERSION_126) {
             nullableMode = NullableMode.valueOf(input.readUTF());
         }
+        if (Env.getCurrentEnvJournalVersion() >= FeMetaVersion.VERSION_131) {
+            isUDTFunction = input.readBoolean();
+        }
     }
 
     public static Function read(DataInput input) throws IOException {
@@ -744,7 +753,11 @@ public class Function implements Writable {
             // function type
             // intermediate type
             if (this instanceof ScalarFunction) {
-                row.add("Scalar");
+                if (isUDTFunction()) {
+                    row.add("TABLES");
+                } else {
+                    row.add("Scalar");
+                }
                 row.add("NULL");
             } else if (this instanceof AliasFunction) {
                 row.add("Alias");
@@ -775,6 +788,14 @@ public class Function implements Writable {
         return nullableMode;
     }
 
+    public void setUDTFunction(boolean isUDTFunction) {
+        this.isUDTFunction = isUDTFunction;
+    }
+
+    public boolean isUDTFunction() {
+        return this.isUDTFunction;
+    }
+
     // Try to serialize this function and write to nowhere.
     // Just for checking if we forget to implement write() method for some 
Exprs.
     // To avoid FE exist when writing edit log.
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/catalog/FunctionUtil.java 
b/fe/fe-core/src/main/java/org/apache/doris/catalog/FunctionUtil.java
index e6c7e073579..4c5dd85d4be 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/catalog/FunctionUtil.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/FunctionUtil.java
@@ -28,6 +28,7 @@ import org.apache.doris.common.io.Text;
 import org.apache.doris.nereids.trees.expressions.functions.udf.AliasUdf;
 import org.apache.doris.nereids.trees.expressions.functions.udf.JavaUdaf;
 import org.apache.doris.nereids.trees.expressions.functions.udf.JavaUdf;
+import org.apache.doris.nereids.trees.expressions.functions.udf.JavaUdtf;
 import org.apache.doris.nereids.types.DataType;
 
 import com.google.common.base.Strings;
@@ -238,7 +239,11 @@ public class FunctionUtil {
             if (function instanceof AliasFunction) {
                 AliasUdf.translateToNereidsFunction(dbName, ((AliasFunction) 
function));
             } else if (function instanceof ScalarFunction) {
-                JavaUdf.translateToNereidsFunction(dbName, ((ScalarFunction) 
function));
+                if (function.isUDTFunction()) {
+                    JavaUdtf.translateToNereidsFunction(dbName, 
((ScalarFunction) function));
+                } else {
+                    JavaUdf.translateToNereidsFunction(dbName, 
((ScalarFunction) function));
+                }
             } else if (function instanceof AggregateFunction) {
                 JavaUdaf.translateToNereidsFunction(dbName, 
((AggregateFunction) function));
             }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/catalog/ScalarFunction.java 
b/fe/fe-core/src/main/java/org/apache/doris/catalog/ScalarFunction.java
index 31d97e9b536..7d8368646f2 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/catalog/ScalarFunction.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/ScalarFunction.java
@@ -168,6 +168,21 @@ public class ScalarFunction extends Function {
         return fn;
     }
 
+    public ScalarFunction(ScalarFunction other) {
+        super(other);
+        if (other == null) {
+            return;
+        }
+        symbolName = other.symbolName;
+        prepareFnSymbol = other.prepareFnSymbol;
+        closeFnSymbol = other.closeFnSymbol;
+    }
+
+    @Override
+    public Function clone() {
+        return new ScalarFunction(this);
+    }
+
     public void setSymbolName(String s) {
         symbolName = s;
     }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/glue/translator/ExpressionTranslator.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/glue/translator/ExpressionTranslator.java
index 6c7a1bd82c1..70f1de8c555 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/glue/translator/ExpressionTranslator.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/glue/translator/ExpressionTranslator.java
@@ -96,6 +96,7 @@ import 
org.apache.doris.nereids.trees.expressions.functions.scalar.PushDownToPro
 import 
org.apache.doris.nereids.trees.expressions.functions.scalar.ScalarFunction;
 import org.apache.doris.nereids.trees.expressions.functions.udf.JavaUdaf;
 import org.apache.doris.nereids.trees.expressions.functions.udf.JavaUdf;
+import org.apache.doris.nereids.trees.expressions.functions.udf.JavaUdtf;
 import 
org.apache.doris.nereids.trees.expressions.functions.window.WindowFunction;
 import org.apache.doris.nereids.trees.expressions.literal.Literal;
 import org.apache.doris.nereids.trees.expressions.literal.NullLiteral;
@@ -650,6 +651,14 @@ public class ExpressionTranslator extends 
DefaultExpressionVisitor<Expr, PlanTra
         return new FunctionCallExpr(udf.getCatalogFunction(), exprs);
     }
 
+    @Override
+    public Expr visitJavaUdtf(JavaUdtf udf, PlanTranslatorContext context) {
+        FunctionParams exprs = new FunctionParams(udf.children().stream()
+                .map(expression -> expression.accept(this, context))
+                .collect(Collectors.toList()));
+        return new FunctionCallExpr(udf.getCatalogFunction(), exprs);
+    }
+
     @Override
     public Expr visitJavaUdaf(JavaUdaf udaf, PlanTranslatorContext context) {
         FunctionParams exprs = new FunctionParams(udaf.isDistinct(), 
udaf.children().stream()
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/udf/JavaUdtf.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/udf/JavaUdtf.java
new file mode 100644
index 00000000000..48bf65edc57
--- /dev/null
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/udf/JavaUdtf.java
@@ -0,0 +1,171 @@
+// 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.nereids.trees.expressions.functions.udf;
+
+import org.apache.doris.analysis.FunctionName;
+import org.apache.doris.catalog.Env;
+import org.apache.doris.catalog.Function;
+import org.apache.doris.catalog.Function.NullableMode;
+import org.apache.doris.catalog.FunctionSignature;
+import org.apache.doris.catalog.Type;
+import org.apache.doris.common.util.URI;
+import org.apache.doris.nereids.exceptions.AnalysisException;
+import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.VirtualSlotReference;
+import 
org.apache.doris.nereids.trees.expressions.functions.ExplicitlyCastableSignature;
+import org.apache.doris.nereids.trees.expressions.functions.Udf;
+import 
org.apache.doris.nereids.trees.expressions.functions.generator.TableGeneratingFunction;
+import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
+import org.apache.doris.nereids.types.DataType;
+import org.apache.doris.thrift.TFunctionBinaryType;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+/**
+ * Java UDTF for Nereids
+ */
+public class JavaUdtf extends TableGeneratingFunction implements 
ExplicitlyCastableSignature, Udf {
+    private final String dbName;
+    private final long functionId;
+    private final TFunctionBinaryType binaryType;
+    private final FunctionSignature signature;
+    private final NullableMode nullableMode;
+    private final String objectFile;
+    private final String symbol;
+    private final String prepareFn;
+    private final String closeFn;
+    private final String checkSum;
+
+    /**
+     * Constructor of UDTF
+     */
+    public JavaUdtf(String name, long functionId, String dbName, 
TFunctionBinaryType binaryType,
+            FunctionSignature signature,
+            NullableMode nullableMode, String objectFile, String symbol, 
String prepareFn, String closeFn,
+            String checkSum, Expression... args) {
+        super(name, args);
+        this.dbName = dbName;
+        this.functionId = functionId;
+        this.binaryType = binaryType;
+        this.signature = signature;
+        this.nullableMode = nullableMode;
+        this.objectFile = objectFile;
+        this.symbol = symbol;
+        this.prepareFn = prepareFn;
+        this.closeFn = closeFn;
+        this.checkSum = checkSum;
+    }
+
+    /**
+     * withChildren.
+     */
+    @Override
+    public JavaUdtf withChildren(List<Expression> children) {
+        Preconditions.checkArgument(children.size() == this.children.size());
+        return new JavaUdtf(getName(), functionId, dbName, binaryType, 
signature, nullableMode,
+                objectFile, symbol, prepareFn, closeFn, checkSum, 
children.toArray(new Expression[0]));
+    }
+
+    @Override
+    public List<FunctionSignature> getSignatures() {
+        return ImmutableList.of(signature);
+    }
+
+    @Override
+    public boolean hasVarArguments() {
+        return signature.hasVarArgs;
+    }
+
+    @Override
+    public int arity() {
+        return signature.argumentsTypes.size();
+    }
+
+    @Override
+    public Function getCatalogFunction() {
+        try {
+            org.apache.doris.catalog.ScalarFunction expr = 
org.apache.doris.catalog.ScalarFunction.createUdf(
+                    binaryType,
+                    new FunctionName(dbName, getName()),
+                    
signature.argumentsTypes.stream().map(DataType::toCatalogDataType).toArray(Type[]::new),
+                    signature.returnType.toCatalogDataType(),
+                    signature.hasVarArgs,
+                    URI.create(objectFile),
+                    symbol,
+                    prepareFn,
+                    closeFn
+            );
+            expr.setNullableMode(nullableMode);
+            expr.setChecksum(checkSum);
+            expr.setId(functionId);
+            expr.setUDTFunction(true);
+            return expr;
+        } catch (Exception e) {
+            throw new AnalysisException(e.getMessage(), e.getCause());
+        }
+    }
+
+    /**
+     * translate catalog java udf to nereids java udf
+     */
+    public static void translateToNereidsFunction(String dbName, 
org.apache.doris.catalog.ScalarFunction scalar) {
+        String fnName = scalar.functionName();
+        DataType retType = DataType.fromCatalogType(scalar.getReturnType());
+        List<DataType> argTypes = Arrays.stream(scalar.getArgs())
+                .map(DataType::fromCatalogType)
+                .collect(Collectors.toList());
+
+        FunctionSignature.FuncSigBuilder sigBuilder = 
FunctionSignature.ret(retType);
+        FunctionSignature sig = scalar.hasVarArgs()
+                ? sigBuilder.varArgs(argTypes.toArray(new DataType[0]))
+                : sigBuilder.args(argTypes.toArray(new DataType[0]));
+
+        VirtualSlotReference[] virtualSlots = argTypes.stream()
+                .map(type -> new VirtualSlotReference(type.toString(), type, 
Optional.empty(),
+                        (shape) -> ImmutableList.of()))
+                .toArray(VirtualSlotReference[]::new);
+
+        JavaUdtf udf = new JavaUdtf(fnName, scalar.getId(), dbName, 
scalar.getBinaryType(), sig,
+                scalar.getNullableMode(),
+                scalar.getLocation().getLocation(),
+                scalar.getSymbolName(),
+                scalar.getPrepareFnSymbol(),
+                scalar.getCloseFnSymbol(),
+                scalar.getChecksum(),
+                virtualSlots);
+
+        JavaUdtfBuilder builder = new JavaUdtfBuilder(udf);
+        Env.getCurrentEnv().getFunctionRegistry().addUdf(dbName, fnName, 
builder);
+    }
+
+    @Override
+    public NullableMode getNullableMode() {
+        return nullableMode;
+    }
+
+    @Override
+    public <R, C> R accept(ExpressionVisitor<R, C> visitor, C context) {
+        return visitor.visitJavaUdtf(this, context);
+    }
+}
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/udf/JavaUdtfBuilder.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/udf/JavaUdtfBuilder.java
new file mode 100644
index 00000000000..85114f08e3f
--- /dev/null
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/udf/JavaUdtfBuilder.java
@@ -0,0 +1,87 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.nereids.trees.expressions.functions.udf;
+
+import org.apache.doris.common.Pair;
+import org.apache.doris.common.util.ReflectionUtils;
+import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.functions.BoundFunction;
+import org.apache.doris.nereids.types.DataType;
+import org.apache.doris.nereids.util.TypeCoercionUtils;
+
+import com.google.common.base.Suppliers;
+import com.google.common.collect.Lists;
+
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+/**
+ * function builder for java udtf
+ */
+public class JavaUdtfBuilder extends UdfBuilder {
+    private final JavaUdtf udf;
+    private final int arity;
+    private final boolean isVarArgs;
+
+    public JavaUdtfBuilder(JavaUdtf udf) {
+        this.udf = udf;
+        this.isVarArgs = udf.hasVarArguments();
+        this.arity = udf.arity();
+    }
+
+    @Override
+    public List<DataType> getArgTypes() {
+        return Suppliers.memoize(() -> 
udf.getSignatures().get(0).argumentsTypes.stream()
+                .map(DataType.class::cast)
+                .collect(Collectors.toList())).get();
+    }
+
+    @Override
+    public Class<? extends BoundFunction> functionClass() {
+        return JavaUdtf.class;
+    }
+
+    @Override
+    public boolean canApply(List<?> arguments) {
+        if ((isVarArgs && arity > arguments.size() + 1) || (!isVarArgs && 
arguments.size() != arity)) {
+            return false;
+        }
+        for (Object argument : arguments) {
+            if (!(argument instanceof Expression)) {
+                Optional<Class> primitiveType = 
ReflectionUtils.getPrimitiveType(argument.getClass());
+                if (!primitiveType.isPresent() || 
!Expression.class.isAssignableFrom(primitiveType.get())) {
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    @Override
+    public Pair<JavaUdtf, JavaUdtf> build(String name, List<?> arguments) {
+        List<Expression> exprs = 
arguments.stream().map(Expression.class::cast).collect(Collectors.toList());
+        List<DataType> argTypes = udf.getSignatures().get(0).argumentsTypes;
+
+        List<Expression> processedExprs = Lists.newArrayList();
+        for (int i = 0; i < exprs.size(); ++i) {
+            
processedExprs.add(TypeCoercionUtils.castIfNotSameType(exprs.get(i), 
argTypes.get(i)));
+        }
+        return Pair.ofSame(udf.withChildren(processedExprs));
+    }
+}
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/TableGeneratingFunctionVisitor.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/TableGeneratingFunctionVisitor.java
index 4e4c8ab2bd4..61042283a51 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/TableGeneratingFunctionVisitor.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/TableGeneratingFunctionVisitor.java
@@ -36,6 +36,7 @@ import 
org.apache.doris.nereids.trees.expressions.functions.generator.ExplodeOut
 import 
org.apache.doris.nereids.trees.expressions.functions.generator.ExplodeSplit;
 import 
org.apache.doris.nereids.trees.expressions.functions.generator.ExplodeSplitOuter;
 import 
org.apache.doris.nereids.trees.expressions.functions.generator.TableGeneratingFunction;
+import org.apache.doris.nereids.trees.expressions.functions.udf.JavaUdtf;
 
 /**
  * visitor function for all table generating function.
@@ -115,4 +116,7 @@ public interface TableGeneratingFunctionVisitor<R, C> {
         return visitTableGeneratingFunction(explodeJsonArrayJsonOuter, 
context);
     }
 
+    default R visitJavaUdtf(JavaUdtf udtf, C context) {
+        return visitTableGeneratingFunction(udtf, context);
+    }
 }
diff --git a/gensrc/thrift/Types.thrift b/gensrc/thrift/Types.thrift
index 66694645d74..9404f892052 100644
--- a/gensrc/thrift/Types.thrift
+++ b/gensrc/thrift/Types.thrift
@@ -381,6 +381,7 @@ struct TFunction {
   11: optional i64 id
   12: optional string checksum
   13: optional bool vectorized = false
+  14: optional bool is_udtf_function = false
 }
 
 enum TJdbcOperation {
diff --git a/regression-test/data/javaudf_p0/test_javaudtf_all_types.out 
b/regression-test/data/javaudf_p0/test_javaudtf_all_types.out
new file mode 100644
index 00000000000..96077f7537e
--- /dev/null
+++ b/regression-test/data/javaudf_p0/test_javaudtf_all_types.out
@@ -0,0 +1,1390 @@
+-- This file is automatically generated. You should know what you did if you 
want to edit this
+-- !select --
+\N     \N      \N      \N      \N      \N      \N      \N      \N      \N      
\N      \N      \N      \N
+1      true    1       2       3       4       3.3300  7.77    3.1415  
2023-10-18      2023-10-11T10:11:11.234 row1    [null, "nested1"]       
{"k1":null, "k2":"value1"}
+2      false   2       4       6       8       1.6650  3.885   1.57075 
2023-10-19      2023-10-12T10:12:11.234 row2    [null, "nested2"]       
{"k2":null, "k3":"value2"}
+3      true    3       6       9       12      1.1100  2.59    1.04717 
2023-10-20      2023-10-13T10:13:11.234 row3    [null, "nested3"]       
{"k3":null, "k4":"value3"}
+4      false   4       8       12      16      0.8325  1.943   0.78538 
2023-10-21      2023-10-14T10:14:11.234 row4    [null, "nested4"]       
{"k4":null, "k5":"value4"}
+5      true    5       10      15      20      0.6660  1.554   0.6283  
2023-10-22      2023-10-15T10:15:11.234 row5    [null, "nested5"]       
{"k5":null, "k6":"value5"}
+6      false   6       12      18      24      0.5550  1.295   0.52358 
2023-10-23      2023-10-16T10:16:11.234 row6    [null, "nested6"]       
{"k6":null, "k7":"value6"}
+7      true    7       14      21      28      0.4757  1.11    0.44879 
2023-10-24      2023-10-17T10:17:11.234 row7    [null, "nested7"]       
{"k7":null, "k8":"value7"}
+8      false   8       16      24      32      0.4163  0.971   0.39269 
2023-10-25      2023-10-18T10:18:11.234 row8    [null, "nested8"]       
{"k8":null, "k9":"value8"}
+9      true    9       18      27      36      0.3700  0.863   0.34906 
2023-10-26      2023-10-19T10:19:11.234 row9    [null, "nested9"]       
{"k9":null, "k10":"value9"}
+10     false   \N      20      30      40      \N      0.777   0.31415 \N      
2023-10-20T10:10:11.234 \N      [null, "nested10"]      {"k10":null, 
"k11":"value10"}
+
+-- !select_boolean_col --
+1      true    true
+2      false   false
+2      false   true
+3      true    false
+3      true    true
+3      true    true
+4      false   false
+4      false   false
+4      false   true
+4      false   true
+5      true    false
+5      true    false
+5      true    true
+5      true    true
+5      true    true
+6      false   false
+6      false   false
+6      false   false
+6      false   true
+6      false   true
+6      false   true
+7      true    false
+7      true    false
+7      true    false
+7      true    true
+7      true    true
+7      true    true
+7      true    true
+8      false   false
+8      false   false
+8      false   false
+8      false   false
+8      false   true
+8      false   true
+8      false   true
+8      false   true
+9      true    false
+9      true    false
+9      true    false
+9      true    false
+9      true    true
+9      true    true
+9      true    true
+9      true    true
+9      true    true
+10     false   false
+10     false   false
+10     false   false
+10     false   false
+10     false   false
+10     false   true
+10     false   true
+10     false   true
+10     false   true
+10     false   true
+
+-- !select_tinyint_col --
+1      1       1
+2      2       2
+2      2       3
+3      3       3
+3      3       4
+3      3       5
+4      4       4
+4      4       5
+4      4       6
+4      4       7
+5      5       5
+5      5       6
+5      5       7
+5      5       8
+5      5       9
+6      6       6
+6      6       7
+6      6       8
+6      6       9
+6      6       10
+6      6       11
+7      7       7
+7      7       8
+7      7       9
+7      7       10
+7      7       11
+7      7       12
+7      7       13
+8      8       8
+8      8       9
+8      8       10
+8      8       11
+8      8       12
+8      8       13
+8      8       14
+8      8       15
+9      9       9
+9      9       10
+9      9       11
+9      9       12
+9      9       13
+9      9       14
+9      9       15
+9      9       16
+9      9       17
+
+-- !select_smallint_col --
+1      2       2
+2      4       4
+2      4       6
+3      6       6
+3      6       8
+3      6       10
+4      8       8
+4      8       10
+4      8       12
+4      8       14
+5      10      10
+5      10      12
+5      10      14
+5      10      16
+5      10      18
+6      12      12
+6      12      14
+6      12      16
+6      12      18
+6      12      20
+6      12      22
+7      14      14
+7      14      16
+7      14      18
+7      14      20
+7      14      22
+7      14      24
+7      14      26
+8      16      16
+8      16      18
+8      16      20
+8      16      22
+8      16      24
+8      16      26
+8      16      28
+8      16      30
+9      18      18
+9      18      20
+9      18      22
+9      18      24
+9      18      26
+9      18      28
+9      18      30
+9      18      32
+9      18      34
+10     20      20
+10     20      22
+10     20      24
+10     20      26
+10     20      28
+10     20      30
+10     20      32
+10     20      34
+10     20      36
+10     20      38
+
+-- !select_int_col --
+1      1       1
+2      2       2
+2      2       5
+3      3       3
+3      3       6
+3      3       9
+4      4       4
+4      4       7
+4      4       10
+4      4       13
+5      5       5
+5      5       8
+5      5       11
+5      5       14
+5      5       17
+6      6       6
+6      6       9
+6      6       12
+6      6       15
+6      6       18
+6      6       21
+7      7       7
+7      7       10
+7      7       13
+7      7       16
+7      7       19
+7      7       22
+7      7       25
+8      8       8
+8      8       11
+8      8       14
+8      8       17
+8      8       20
+8      8       23
+8      8       26
+8      8       29
+9      9       9
+9      9       12
+9      9       15
+9      9       18
+9      9       21
+9      9       24
+9      9       27
+9      9       30
+9      9       33
+10     10      10
+10     10      13
+10     10      16
+10     10      19
+10     10      22
+10     10      25
+10     10      28
+10     10      31
+10     10      34
+10     10      37
+
+-- !select_bigint_col --
+1      3       3
+2      6       6
+2      6       10
+3      9       9
+3      9       13
+3      9       17
+4      12      12
+4      12      16
+4      12      20
+4      12      24
+5      15      15
+5      15      19
+5      15      23
+5      15      27
+5      15      31
+6      18      18
+6      18      22
+6      18      26
+6      18      30
+6      18      34
+6      18      38
+7      21      21
+7      21      25
+7      21      29
+7      21      33
+7      21      37
+7      21      41
+7      21      45
+8      24      24
+8      24      28
+8      24      32
+8      24      36
+8      24      40
+8      24      44
+8      24      48
+8      24      52
+9      27      27
+9      27      31
+9      27      35
+9      27      39
+9      27      43
+9      27      47
+9      27      51
+9      27      55
+9      27      59
+10     30      30
+10     30      34
+10     30      38
+10     30      42
+10     30      46
+10     30      50
+10     30      54
+10     30      58
+10     30      62
+10     30      66
+
+-- !select_largeint_col --
+1      4       4
+2      8       8
+2      8       13
+3      12      12
+3      12      17
+3      12      22
+4      16      16
+4      16      21
+4      16      26
+4      16      31
+5      20      20
+5      20      25
+5      20      30
+5      20      35
+5      20      40
+6      24      24
+6      24      29
+6      24      34
+6      24      39
+6      24      44
+6      24      49
+7      28      28
+7      28      33
+7      28      38
+7      28      43
+7      28      48
+7      28      53
+7      28      58
+8      32      32
+8      32      37
+8      32      42
+8      32      47
+8      32      52
+8      32      57
+8      32      62
+8      32      67
+9      36      36
+9      36      41
+9      36      46
+9      36      51
+9      36      56
+9      36      61
+9      36      66
+9      36      71
+9      36      76
+10     40      40
+10     40      45
+10     40      50
+10     40      55
+10     40      60
+10     40      65
+10     40      70
+10     40      75
+10     40      80
+10     40      85
+
+-- !select_decimal_col --
+1      3.3300  3.3300
+2      1.6650  1.6650
+2      1.6650  1.6660
+3      1.1100  1.1100
+3      1.1100  1.1110
+3      1.1100  1.1120
+4      0.8325  0.8325
+4      0.8325  0.8335
+4      0.8325  0.8345
+4      0.8325  0.8355
+5      0.6660  0.6660
+5      0.6660  0.6670
+5      0.6660  0.6680
+5      0.6660  0.6690
+5      0.6660  0.6700
+6      0.5550  0.5550
+6      0.5550  0.5560
+6      0.5550  0.5570
+6      0.5550  0.5580
+6      0.5550  0.5590
+6      0.5550  0.5600
+7      0.4757  0.4757
+7      0.4757  0.4767
+7      0.4757  0.4777
+7      0.4757  0.4787
+7      0.4757  0.4797
+7      0.4757  0.4807
+7      0.4757  0.4817
+8      0.4163  0.4163
+8      0.4163  0.4173
+8      0.4163  0.4183
+8      0.4163  0.4193
+8      0.4163  0.4203
+8      0.4163  0.4213
+8      0.4163  0.4223
+8      0.4163  0.4233
+9      0.3700  0.3700
+9      0.3700  0.3710
+9      0.3700  0.3720
+9      0.3700  0.3730
+9      0.3700  0.3740
+9      0.3700  0.3750
+9      0.3700  0.3760
+9      0.3700  0.3770
+9      0.3700  0.3780
+
+-- !select_float_col --
+1      7.77    7.77
+2      3.885   3.885
+2      3.885   3.985
+3      2.59    2.59
+3      2.59    2.6899998
+3      2.59    2.79
+4      1.943   1.943
+4      1.943   2.043
+4      1.943   2.143
+4      1.943   2.243
+5      1.554   1.554
+5      1.554   1.654
+5      1.554   1.7540001
+5      1.554   1.854
+5      1.554   1.954
+6      1.295   1.295
+6      1.295   1.395
+6      1.295   1.495
+6      1.295   1.5949999
+6      1.295   1.6949999
+6      1.295   1.795
+7      1.11    1.11
+7      1.11    1.21
+7      1.11    1.3100001
+7      1.11    1.41
+7      1.11    1.51
+7      1.11    1.61
+7      1.11    1.71
+8      0.971   0.971
+8      0.971   1.071
+8      0.971   1.171
+8      0.971   1.271
+8      0.971   1.371
+8      0.971   1.471
+8      0.971   1.571
+8      0.971   1.671
+9      0.863   0.863
+9      0.863   0.963
+9      0.863   1.063
+9      0.863   1.163
+9      0.863   1.263
+9      0.863   1.3629999
+9      0.863   1.4629999
+9      0.863   1.563
+9      0.863   1.663
+10     0.777   0.777
+10     0.777   0.87700003
+10     0.777   0.977
+10     0.777   1.077
+10     0.777   1.177
+10     0.777   1.277
+10     0.777   1.377
+10     0.777   1.477
+10     0.777   1.577
+10     0.777   1.677
+
+-- !select_double_col --
+1      3.1415  3.1415
+2      1.57075 1.57075
+2      1.57075 1.58075
+3      1.04717 1.04717
+3      1.04717 1.05717
+3      1.04717 1.06717
+4      0.78538 0.78538
+4      0.78538 0.79538
+4      0.78538 0.80538
+4      0.78538 0.81538
+5      0.6283  0.6283
+5      0.6283  0.6383
+5      0.6283  0.6483
+5      0.6283  0.6583
+5      0.6283  0.6683
+6      0.52358 0.52358
+6      0.52358 0.53358
+6      0.52358 0.5435800000000001
+6      0.52358 0.5535800000000001
+6      0.52358 0.5635800000000001
+6      0.52358 0.5735800000000001
+7      0.44879 0.44879
+7      0.44879 0.45879000000000003
+7      0.44879 0.46879000000000004
+7      0.44879 0.47879000000000005
+7      0.44879 0.48879
+7      0.44879 0.49879
+7      0.44879 0.5087900000000001
+8      0.39269 0.39269
+8      0.39269 0.40269
+8      0.39269 0.41269
+8      0.39269 0.42269
+8      0.39269 0.43268999999999996
+8      0.39269 0.44269
+8      0.39269 0.45269
+8      0.39269 0.46269
+9      0.34906 0.34906
+9      0.34906 0.35906
+9      0.34906 0.36906
+9      0.34906 0.37905999999999995
+9      0.34906 0.38905999999999996
+9      0.34906 0.39905999999999997
+9      0.34906 0.40906
+9      0.34906 0.41906
+9      0.34906 0.42906
+10     0.31415 0.31415
+10     0.31415 0.32415
+10     0.31415 0.33415
+10     0.31415 0.34414999999999996
+10     0.31415 0.35414999999999996
+10     0.31415 0.36415
+10     0.31415 0.37415
+10     0.31415 0.38415
+10     0.31415 0.39415
+10     0.31415 0.40415
+
+-- !select_date_col --
+1      2023-10-18      2023-10-18
+2      2023-10-19      2023-10-19
+2      2023-10-19      2023-11-19
+3      2023-10-20      2023-10-20
+3      2023-10-20      2023-11-20
+3      2023-10-20      2023-12-20
+4      2023-10-21      2023-10-21
+4      2023-10-21      2023-11-21
+4      2023-10-21      2023-12-21
+4      2023-10-21      2024-01-21
+5      2023-10-22      2023-10-22
+5      2023-10-22      2023-11-22
+5      2023-10-22      2023-12-22
+5      2023-10-22      2024-01-22
+5      2023-10-22      2024-02-22
+6      2023-10-23      2023-10-23
+6      2023-10-23      2023-11-23
+6      2023-10-23      2023-12-23
+6      2023-10-23      2024-01-23
+6      2023-10-23      2024-02-23
+6      2023-10-23      2024-03-23
+7      2023-10-24      2023-10-24
+7      2023-10-24      2023-11-24
+7      2023-10-24      2023-12-24
+7      2023-10-24      2024-01-24
+7      2023-10-24      2024-02-24
+7      2023-10-24      2024-03-24
+7      2023-10-24      2024-04-24
+8      2023-10-25      2023-10-25
+8      2023-10-25      2023-11-25
+8      2023-10-25      2023-12-25
+8      2023-10-25      2024-01-25
+8      2023-10-25      2024-02-25
+8      2023-10-25      2024-03-25
+8      2023-10-25      2024-04-25
+8      2023-10-25      2024-05-25
+9      2023-10-26      2023-10-26
+9      2023-10-26      2023-11-26
+9      2023-10-26      2023-12-26
+9      2023-10-26      2024-01-26
+9      2023-10-26      2024-02-26
+9      2023-10-26      2024-03-26
+9      2023-10-26      2024-04-26
+9      2023-10-26      2024-05-26
+9      2023-10-26      2024-06-26
+
+-- !select_datetime_col --
+1      2023-10-11T10:11:11.234 2023-10-11T10:11:11.234
+2      2023-10-12T10:12:11.234 2023-10-12T10:12:11.234
+2      2023-10-12T10:12:11.234 2023-10-13T10:12:11.234
+3      2023-10-13T10:13:11.234 2023-10-13T10:13:11.234
+3      2023-10-13T10:13:11.234 2023-10-14T10:13:11.234
+3      2023-10-13T10:13:11.234 2023-10-15T10:13:11.234
+4      2023-10-14T10:14:11.234 2023-10-14T10:14:11.234
+4      2023-10-14T10:14:11.234 2023-10-15T10:14:11.234
+4      2023-10-14T10:14:11.234 2023-10-16T10:14:11.234
+4      2023-10-14T10:14:11.234 2023-10-17T10:14:11.234
+5      2023-10-15T10:15:11.234 2023-10-15T10:15:11.234
+5      2023-10-15T10:15:11.234 2023-10-16T10:15:11.234
+5      2023-10-15T10:15:11.234 2023-10-17T10:15:11.234
+5      2023-10-15T10:15:11.234 2023-10-18T10:15:11.234
+5      2023-10-15T10:15:11.234 2023-10-19T10:15:11.234
+6      2023-10-16T10:16:11.234 2023-10-16T10:16:11.234
+6      2023-10-16T10:16:11.234 2023-10-17T10:16:11.234
+6      2023-10-16T10:16:11.234 2023-10-18T10:16:11.234
+6      2023-10-16T10:16:11.234 2023-10-19T10:16:11.234
+6      2023-10-16T10:16:11.234 2023-10-20T10:16:11.234
+6      2023-10-16T10:16:11.234 2023-10-21T10:16:11.234
+7      2023-10-17T10:17:11.234 2023-10-17T10:17:11.234
+7      2023-10-17T10:17:11.234 2023-10-18T10:17:11.234
+7      2023-10-17T10:17:11.234 2023-10-19T10:17:11.234
+7      2023-10-17T10:17:11.234 2023-10-20T10:17:11.234
+7      2023-10-17T10:17:11.234 2023-10-21T10:17:11.234
+7      2023-10-17T10:17:11.234 2023-10-22T10:17:11.234
+7      2023-10-17T10:17:11.234 2023-10-23T10:17:11.234
+8      2023-10-18T10:18:11.234 2023-10-18T10:18:11.234
+8      2023-10-18T10:18:11.234 2023-10-19T10:18:11.234
+8      2023-10-18T10:18:11.234 2023-10-20T10:18:11.234
+8      2023-10-18T10:18:11.234 2023-10-21T10:18:11.234
+8      2023-10-18T10:18:11.234 2023-10-22T10:18:11.234
+8      2023-10-18T10:18:11.234 2023-10-23T10:18:11.234
+8      2023-10-18T10:18:11.234 2023-10-24T10:18:11.234
+8      2023-10-18T10:18:11.234 2023-10-25T10:18:11.234
+9      2023-10-19T10:19:11.234 2023-10-19T10:19:11.234
+9      2023-10-19T10:19:11.234 2023-10-20T10:19:11.234
+9      2023-10-19T10:19:11.234 2023-10-21T10:19:11.234
+9      2023-10-19T10:19:11.234 2023-10-22T10:19:11.234
+9      2023-10-19T10:19:11.234 2023-10-23T10:19:11.234
+9      2023-10-19T10:19:11.234 2023-10-24T10:19:11.234
+9      2023-10-19T10:19:11.234 2023-10-25T10:19:11.234
+9      2023-10-19T10:19:11.234 2023-10-26T10:19:11.234
+9      2023-10-19T10:19:11.234 2023-10-27T10:19:11.234
+10     2023-10-20T10:10:11.234 2023-10-20T10:10:11.234
+10     2023-10-20T10:10:11.234 2023-10-21T10:10:11.234
+10     2023-10-20T10:10:11.234 2023-10-22T10:10:11.234
+10     2023-10-20T10:10:11.234 2023-10-23T10:10:11.234
+10     2023-10-20T10:10:11.234 2023-10-24T10:10:11.234
+10     2023-10-20T10:10:11.234 2023-10-25T10:10:11.234
+10     2023-10-20T10:10:11.234 2023-10-26T10:10:11.234
+10     2023-10-20T10:10:11.234 2023-10-27T10:10:11.234
+10     2023-10-20T10:10:11.234 2023-10-28T10:10:11.234
+10     2023-10-20T10:10:11.234 2023-10-29T10:10:11.234
+
+-- !select_string_col --
+1      row1    1
+1      row1    o
+1      row1    r
+1      row1    w
+2      row2    2
+2      row2    o
+2      row2    r
+2      row2    w
+3      row3    3
+3      row3    o
+3      row3    r
+3      row3    w
+4      row4    4
+4      row4    o
+4      row4    r
+4      row4    w
+5      row5    5
+5      row5    o
+5      row5    r
+5      row5    w
+6      row6    6
+6      row6    o
+6      row6    r
+6      row6    w
+7      row7    7
+7      row7    o
+7      row7    r
+7      row7    w
+8      row8    8
+8      row8    o
+8      row8    r
+8      row8    w
+9      row9    9
+9      row9    o
+9      row9    r
+9      row9    w
+
+-- !select_array_col --
+1      [null, "nested1"]       \N
+1      [null, "nested1"]       nested1
+2      [null, "nested2"]       \N
+2      [null, "nested2"]       nested2
+3      [null, "nested3"]       \N
+3      [null, "nested3"]       nested3
+4      [null, "nested4"]       \N
+4      [null, "nested4"]       nested4
+5      [null, "nested5"]       \N
+5      [null, "nested5"]       nested5
+6      [null, "nested6"]       \N
+6      [null, "nested6"]       nested6
+7      [null, "nested7"]       \N
+7      [null, "nested7"]       nested7
+8      [null, "nested8"]       \N
+8      [null, "nested8"]       nested8
+9      [null, "nested9"]       \N
+9      [null, "nested9"]       nested9
+10     [null, "nested10"]      \N
+10     [null, "nested10"]      nested10
+
+-- !select_map_col --
+1      {"k1":null, "k2":"value1"}      k1
+1      {"k1":null, "k2":"value1"}      k2
+2      {"k2":null, "k3":"value2"}      \N
+2      {"k2":null, "k3":"value2"}      value2
+3      {"k3":null, "k4":"value3"}      k3
+3      {"k3":null, "k4":"value3"}      k4
+4      {"k4":null, "k5":"value4"}      \N
+4      {"k4":null, "k5":"value4"}      value4
+5      {"k5":null, "k6":"value5"}      k5
+5      {"k5":null, "k6":"value5"}      k6
+6      {"k6":null, "k7":"value6"}      \N
+6      {"k6":null, "k7":"value6"}      value6
+7      {"k7":null, "k8":"value7"}      k7
+7      {"k7":null, "k8":"value7"}      k8
+8      {"k8":null, "k9":"value8"}      \N
+8      {"k8":null, "k9":"value8"}      value8
+9      {"k9":null, "k10":"value9"}     k10
+9      {"k9":null, "k10":"value9"}     k9
+10     {"k10":null, "k11":"value10"}   \N
+10     {"k10":null, "k11":"value10"}   value10
+
+-- !select_boolean_col_outer --
+\N     \N      \N
+1      true    true
+2      false   false
+2      false   true
+3      true    false
+3      true    true
+3      true    true
+4      false   false
+4      false   false
+4      false   true
+4      false   true
+5      true    false
+5      true    false
+5      true    true
+5      true    true
+5      true    true
+6      false   false
+6      false   false
+6      false   false
+6      false   true
+6      false   true
+6      false   true
+7      true    false
+7      true    false
+7      true    false
+7      true    true
+7      true    true
+7      true    true
+7      true    true
+8      false   false
+8      false   false
+8      false   false
+8      false   false
+8      false   true
+8      false   true
+8      false   true
+8      false   true
+9      true    false
+9      true    false
+9      true    false
+9      true    false
+9      true    true
+9      true    true
+9      true    true
+9      true    true
+9      true    true
+10     false   false
+10     false   false
+10     false   false
+10     false   false
+10     false   false
+10     false   true
+10     false   true
+10     false   true
+10     false   true
+10     false   true
+
+-- !select_tinyint_col_outer --
+\N     \N      \N
+1      1       1
+2      2       2
+2      2       3
+3      3       3
+3      3       4
+3      3       5
+4      4       4
+4      4       5
+4      4       6
+4      4       7
+5      5       5
+5      5       6
+5      5       7
+5      5       8
+5      5       9
+6      6       6
+6      6       7
+6      6       8
+6      6       9
+6      6       10
+6      6       11
+7      7       7
+7      7       8
+7      7       9
+7      7       10
+7      7       11
+7      7       12
+7      7       13
+8      8       8
+8      8       9
+8      8       10
+8      8       11
+8      8       12
+8      8       13
+8      8       14
+8      8       15
+9      9       9
+9      9       10
+9      9       11
+9      9       12
+9      9       13
+9      9       14
+9      9       15
+9      9       16
+9      9       17
+10     \N      \N
+
+-- !select_smallint_col_outer --
+\N     \N      \N
+1      2       2
+2      4       4
+2      4       6
+3      6       6
+3      6       8
+3      6       10
+4      8       8
+4      8       10
+4      8       12
+4      8       14
+5      10      10
+5      10      12
+5      10      14
+5      10      16
+5      10      18
+6      12      12
+6      12      14
+6      12      16
+6      12      18
+6      12      20
+6      12      22
+7      14      14
+7      14      16
+7      14      18
+7      14      20
+7      14      22
+7      14      24
+7      14      26
+8      16      16
+8      16      18
+8      16      20
+8      16      22
+8      16      24
+8      16      26
+8      16      28
+8      16      30
+9      18      18
+9      18      20
+9      18      22
+9      18      24
+9      18      26
+9      18      28
+9      18      30
+9      18      32
+9      18      34
+10     20      20
+10     20      22
+10     20      24
+10     20      26
+10     20      28
+10     20      30
+10     20      32
+10     20      34
+10     20      36
+10     20      38
+
+-- !select_int_col_outer --
+\N     \N      \N
+1      1       1
+2      2       2
+2      2       5
+3      3       3
+3      3       6
+3      3       9
+4      4       4
+4      4       7
+4      4       10
+4      4       13
+5      5       5
+5      5       8
+5      5       11
+5      5       14
+5      5       17
+6      6       6
+6      6       9
+6      6       12
+6      6       15
+6      6       18
+6      6       21
+7      7       7
+7      7       10
+7      7       13
+7      7       16
+7      7       19
+7      7       22
+7      7       25
+8      8       8
+8      8       11
+8      8       14
+8      8       17
+8      8       20
+8      8       23
+8      8       26
+8      8       29
+9      9       9
+9      9       12
+9      9       15
+9      9       18
+9      9       21
+9      9       24
+9      9       27
+9      9       30
+9      9       33
+10     10      10
+10     10      13
+10     10      16
+10     10      19
+10     10      22
+10     10      25
+10     10      28
+10     10      31
+10     10      34
+10     10      37
+
+-- !select_bigint_col_outer --
+\N     \N      \N
+1      3       3
+2      6       6
+2      6       10
+3      9       9
+3      9       13
+3      9       17
+4      12      12
+4      12      16
+4      12      20
+4      12      24
+5      15      15
+5      15      19
+5      15      23
+5      15      27
+5      15      31
+6      18      18
+6      18      22
+6      18      26
+6      18      30
+6      18      34
+6      18      38
+7      21      21
+7      21      25
+7      21      29
+7      21      33
+7      21      37
+7      21      41
+7      21      45
+8      24      24
+8      24      28
+8      24      32
+8      24      36
+8      24      40
+8      24      44
+8      24      48
+8      24      52
+9      27      27
+9      27      31
+9      27      35
+9      27      39
+9      27      43
+9      27      47
+9      27      51
+9      27      55
+9      27      59
+10     30      30
+10     30      34
+10     30      38
+10     30      42
+10     30      46
+10     30      50
+10     30      54
+10     30      58
+10     30      62
+10     30      66
+
+-- !select_largeint_col_outer --
+\N     \N      \N
+1      4       4
+2      8       8
+2      8       13
+3      12      12
+3      12      17
+3      12      22
+4      16      16
+4      16      21
+4      16      26
+4      16      31
+5      20      20
+5      20      25
+5      20      30
+5      20      35
+5      20      40
+6      24      24
+6      24      29
+6      24      34
+6      24      39
+6      24      44
+6      24      49
+7      28      28
+7      28      33
+7      28      38
+7      28      43
+7      28      48
+7      28      53
+7      28      58
+8      32      32
+8      32      37
+8      32      42
+8      32      47
+8      32      52
+8      32      57
+8      32      62
+8      32      67
+9      36      36
+9      36      41
+9      36      46
+9      36      51
+9      36      56
+9      36      61
+9      36      66
+9      36      71
+9      36      76
+10     40      40
+10     40      45
+10     40      50
+10     40      55
+10     40      60
+10     40      65
+10     40      70
+10     40      75
+10     40      80
+10     40      85
+
+-- !select_decimal_col_outer --
+\N     \N      \N
+1      3.3300  3.3300
+2      1.6650  1.6650
+2      1.6650  1.6660
+3      1.1100  1.1100
+3      1.1100  1.1110
+3      1.1100  1.1120
+4      0.8325  0.8325
+4      0.8325  0.8335
+4      0.8325  0.8345
+4      0.8325  0.8355
+5      0.6660  0.6660
+5      0.6660  0.6670
+5      0.6660  0.6680
+5      0.6660  0.6690
+5      0.6660  0.6700
+6      0.5550  0.5550
+6      0.5550  0.5560
+6      0.5550  0.5570
+6      0.5550  0.5580
+6      0.5550  0.5590
+6      0.5550  0.5600
+7      0.4757  0.4757
+7      0.4757  0.4767
+7      0.4757  0.4777
+7      0.4757  0.4787
+7      0.4757  0.4797
+7      0.4757  0.4807
+7      0.4757  0.4817
+8      0.4163  0.4163
+8      0.4163  0.4173
+8      0.4163  0.4183
+8      0.4163  0.4193
+8      0.4163  0.4203
+8      0.4163  0.4213
+8      0.4163  0.4223
+8      0.4163  0.4233
+9      0.3700  0.3700
+9      0.3700  0.3710
+9      0.3700  0.3720
+9      0.3700  0.3730
+9      0.3700  0.3740
+9      0.3700  0.3750
+9      0.3700  0.3760
+9      0.3700  0.3770
+9      0.3700  0.3780
+10     \N      \N
+
+-- !select_float_col_outer --
+\N     \N      \N
+1      7.77    7.77
+2      3.885   3.885
+2      3.885   3.985
+3      2.59    2.59
+3      2.59    2.6899998
+3      2.59    2.79
+4      1.943   1.943
+4      1.943   2.043
+4      1.943   2.143
+4      1.943   2.243
+5      1.554   1.554
+5      1.554   1.654
+5      1.554   1.7540001
+5      1.554   1.854
+5      1.554   1.954
+6      1.295   1.295
+6      1.295   1.395
+6      1.295   1.495
+6      1.295   1.5949999
+6      1.295   1.6949999
+6      1.295   1.795
+7      1.11    1.11
+7      1.11    1.21
+7      1.11    1.3100001
+7      1.11    1.41
+7      1.11    1.51
+7      1.11    1.61
+7      1.11    1.71
+8      0.971   0.971
+8      0.971   1.071
+8      0.971   1.171
+8      0.971   1.271
+8      0.971   1.371
+8      0.971   1.471
+8      0.971   1.571
+8      0.971   1.671
+9      0.863   0.863
+9      0.863   0.963
+9      0.863   1.063
+9      0.863   1.163
+9      0.863   1.263
+9      0.863   1.3629999
+9      0.863   1.4629999
+9      0.863   1.563
+9      0.863   1.663
+10     0.777   0.777
+10     0.777   0.87700003
+10     0.777   0.977
+10     0.777   1.077
+10     0.777   1.177
+10     0.777   1.277
+10     0.777   1.377
+10     0.777   1.477
+10     0.777   1.577
+10     0.777   1.677
+
+-- !select_double_col_outer --
+\N     \N      \N
+1      3.1415  3.1415
+2      1.57075 1.57075
+2      1.57075 1.58075
+3      1.04717 1.04717
+3      1.04717 1.05717
+3      1.04717 1.06717
+4      0.78538 0.78538
+4      0.78538 0.79538
+4      0.78538 0.80538
+4      0.78538 0.81538
+5      0.6283  0.6283
+5      0.6283  0.6383
+5      0.6283  0.6483
+5      0.6283  0.6583
+5      0.6283  0.6683
+6      0.52358 0.52358
+6      0.52358 0.53358
+6      0.52358 0.5435800000000001
+6      0.52358 0.5535800000000001
+6      0.52358 0.5635800000000001
+6      0.52358 0.5735800000000001
+7      0.44879 0.44879
+7      0.44879 0.45879000000000003
+7      0.44879 0.46879000000000004
+7      0.44879 0.47879000000000005
+7      0.44879 0.48879
+7      0.44879 0.49879
+7      0.44879 0.5087900000000001
+8      0.39269 0.39269
+8      0.39269 0.40269
+8      0.39269 0.41269
+8      0.39269 0.42269
+8      0.39269 0.43268999999999996
+8      0.39269 0.44269
+8      0.39269 0.45269
+8      0.39269 0.46269
+9      0.34906 0.34906
+9      0.34906 0.35906
+9      0.34906 0.36906
+9      0.34906 0.37905999999999995
+9      0.34906 0.38905999999999996
+9      0.34906 0.39905999999999997
+9      0.34906 0.40906
+9      0.34906 0.41906
+9      0.34906 0.42906
+10     0.31415 0.31415
+10     0.31415 0.32415
+10     0.31415 0.33415
+10     0.31415 0.34414999999999996
+10     0.31415 0.35414999999999996
+10     0.31415 0.36415
+10     0.31415 0.37415
+10     0.31415 0.38415
+10     0.31415 0.39415
+10     0.31415 0.40415
+
+-- !select_date_col_outer --
+\N     \N      \N
+1      2023-10-18      2023-10-18
+2      2023-10-19      2023-10-19
+2      2023-10-19      2023-11-19
+3      2023-10-20      2023-10-20
+3      2023-10-20      2023-11-20
+3      2023-10-20      2023-12-20
+4      2023-10-21      2023-10-21
+4      2023-10-21      2023-11-21
+4      2023-10-21      2023-12-21
+4      2023-10-21      2024-01-21
+5      2023-10-22      2023-10-22
+5      2023-10-22      2023-11-22
+5      2023-10-22      2023-12-22
+5      2023-10-22      2024-01-22
+5      2023-10-22      2024-02-22
+6      2023-10-23      2023-10-23
+6      2023-10-23      2023-11-23
+6      2023-10-23      2023-12-23
+6      2023-10-23      2024-01-23
+6      2023-10-23      2024-02-23
+6      2023-10-23      2024-03-23
+7      2023-10-24      2023-10-24
+7      2023-10-24      2023-11-24
+7      2023-10-24      2023-12-24
+7      2023-10-24      2024-01-24
+7      2023-10-24      2024-02-24
+7      2023-10-24      2024-03-24
+7      2023-10-24      2024-04-24
+8      2023-10-25      2023-10-25
+8      2023-10-25      2023-11-25
+8      2023-10-25      2023-12-25
+8      2023-10-25      2024-01-25
+8      2023-10-25      2024-02-25
+8      2023-10-25      2024-03-25
+8      2023-10-25      2024-04-25
+8      2023-10-25      2024-05-25
+9      2023-10-26      2023-10-26
+9      2023-10-26      2023-11-26
+9      2023-10-26      2023-12-26
+9      2023-10-26      2024-01-26
+9      2023-10-26      2024-02-26
+9      2023-10-26      2024-03-26
+9      2023-10-26      2024-04-26
+9      2023-10-26      2024-05-26
+9      2023-10-26      2024-06-26
+10     \N      \N
+
+-- !select_datetime_col_outer --
+\N     \N      \N
+1      2023-10-11T10:11:11.234 2023-10-11T10:11:11.234
+2      2023-10-12T10:12:11.234 2023-10-12T10:12:11.234
+2      2023-10-12T10:12:11.234 2023-10-13T10:12:11.234
+3      2023-10-13T10:13:11.234 2023-10-13T10:13:11.234
+3      2023-10-13T10:13:11.234 2023-10-14T10:13:11.234
+3      2023-10-13T10:13:11.234 2023-10-15T10:13:11.234
+4      2023-10-14T10:14:11.234 2023-10-14T10:14:11.234
+4      2023-10-14T10:14:11.234 2023-10-15T10:14:11.234
+4      2023-10-14T10:14:11.234 2023-10-16T10:14:11.234
+4      2023-10-14T10:14:11.234 2023-10-17T10:14:11.234
+5      2023-10-15T10:15:11.234 2023-10-15T10:15:11.234
+5      2023-10-15T10:15:11.234 2023-10-16T10:15:11.234
+5      2023-10-15T10:15:11.234 2023-10-17T10:15:11.234
+5      2023-10-15T10:15:11.234 2023-10-18T10:15:11.234
+5      2023-10-15T10:15:11.234 2023-10-19T10:15:11.234
+6      2023-10-16T10:16:11.234 2023-10-16T10:16:11.234
+6      2023-10-16T10:16:11.234 2023-10-17T10:16:11.234
+6      2023-10-16T10:16:11.234 2023-10-18T10:16:11.234
+6      2023-10-16T10:16:11.234 2023-10-19T10:16:11.234
+6      2023-10-16T10:16:11.234 2023-10-20T10:16:11.234
+6      2023-10-16T10:16:11.234 2023-10-21T10:16:11.234
+7      2023-10-17T10:17:11.234 2023-10-17T10:17:11.234
+7      2023-10-17T10:17:11.234 2023-10-18T10:17:11.234
+7      2023-10-17T10:17:11.234 2023-10-19T10:17:11.234
+7      2023-10-17T10:17:11.234 2023-10-20T10:17:11.234
+7      2023-10-17T10:17:11.234 2023-10-21T10:17:11.234
+7      2023-10-17T10:17:11.234 2023-10-22T10:17:11.234
+7      2023-10-17T10:17:11.234 2023-10-23T10:17:11.234
+8      2023-10-18T10:18:11.234 2023-10-18T10:18:11.234
+8      2023-10-18T10:18:11.234 2023-10-19T10:18:11.234
+8      2023-10-18T10:18:11.234 2023-10-20T10:18:11.234
+8      2023-10-18T10:18:11.234 2023-10-21T10:18:11.234
+8      2023-10-18T10:18:11.234 2023-10-22T10:18:11.234
+8      2023-10-18T10:18:11.234 2023-10-23T10:18:11.234
+8      2023-10-18T10:18:11.234 2023-10-24T10:18:11.234
+8      2023-10-18T10:18:11.234 2023-10-25T10:18:11.234
+9      2023-10-19T10:19:11.234 2023-10-19T10:19:11.234
+9      2023-10-19T10:19:11.234 2023-10-20T10:19:11.234
+9      2023-10-19T10:19:11.234 2023-10-21T10:19:11.234
+9      2023-10-19T10:19:11.234 2023-10-22T10:19:11.234
+9      2023-10-19T10:19:11.234 2023-10-23T10:19:11.234
+9      2023-10-19T10:19:11.234 2023-10-24T10:19:11.234
+9      2023-10-19T10:19:11.234 2023-10-25T10:19:11.234
+9      2023-10-19T10:19:11.234 2023-10-26T10:19:11.234
+9      2023-10-19T10:19:11.234 2023-10-27T10:19:11.234
+10     2023-10-20T10:10:11.234 2023-10-20T10:10:11.234
+10     2023-10-20T10:10:11.234 2023-10-21T10:10:11.234
+10     2023-10-20T10:10:11.234 2023-10-22T10:10:11.234
+10     2023-10-20T10:10:11.234 2023-10-23T10:10:11.234
+10     2023-10-20T10:10:11.234 2023-10-24T10:10:11.234
+10     2023-10-20T10:10:11.234 2023-10-25T10:10:11.234
+10     2023-10-20T10:10:11.234 2023-10-26T10:10:11.234
+10     2023-10-20T10:10:11.234 2023-10-27T10:10:11.234
+10     2023-10-20T10:10:11.234 2023-10-28T10:10:11.234
+10     2023-10-20T10:10:11.234 2023-10-29T10:10:11.234
+
+-- !select_string_col_outer --
+\N     \N      \N
+1      row1    1
+1      row1    o
+1      row1    r
+1      row1    w
+2      row2    2
+2      row2    o
+2      row2    r
+2      row2    w
+3      row3    3
+3      row3    o
+3      row3    r
+3      row3    w
+4      row4    4
+4      row4    o
+4      row4    r
+4      row4    w
+5      row5    5
+5      row5    o
+5      row5    r
+5      row5    w
+6      row6    6
+6      row6    o
+6      row6    r
+6      row6    w
+7      row7    7
+7      row7    o
+7      row7    r
+7      row7    w
+8      row8    8
+8      row8    o
+8      row8    r
+8      row8    w
+9      row9    9
+9      row9    o
+9      row9    r
+9      row9    w
+10     \N      \N
+
+-- !select_array_col_outer --
+\N     \N      \N
+1      [null, "nested1"]       \N
+1      [null, "nested1"]       nested1
+2      [null, "nested2"]       \N
+2      [null, "nested2"]       nested2
+3      [null, "nested3"]       \N
+3      [null, "nested3"]       nested3
+4      [null, "nested4"]       \N
+4      [null, "nested4"]       nested4
+5      [null, "nested5"]       \N
+5      [null, "nested5"]       nested5
+6      [null, "nested6"]       \N
+6      [null, "nested6"]       nested6
+7      [null, "nested7"]       \N
+7      [null, "nested7"]       nested7
+8      [null, "nested8"]       \N
+8      [null, "nested8"]       nested8
+9      [null, "nested9"]       \N
+9      [null, "nested9"]       nested9
+10     [null, "nested10"]      \N
+10     [null, "nested10"]      nested10
+
+-- !select_map_col_outer --
+\N     \N      \N
+1      {"k1":null, "k2":"value1"}      k1
+1      {"k1":null, "k2":"value1"}      k2
+2      {"k2":null, "k3":"value2"}      \N
+2      {"k2":null, "k3":"value2"}      value2
+3      {"k3":null, "k4":"value3"}      k3
+3      {"k3":null, "k4":"value3"}      k4
+4      {"k4":null, "k5":"value4"}      \N
+4      {"k4":null, "k5":"value4"}      value4
+5      {"k5":null, "k6":"value5"}      k5
+5      {"k5":null, "k6":"value5"}      k6
+6      {"k6":null, "k7":"value6"}      \N
+6      {"k6":null, "k7":"value6"}      value6
+7      {"k7":null, "k8":"value7"}      k7
+7      {"k7":null, "k8":"value7"}      k8
+8      {"k8":null, "k9":"value8"}      \N
+8      {"k8":null, "k9":"value8"}      value8
+9      {"k9":null, "k10":"value9"}     k10
+9      {"k9":null, "k10":"value9"}     k9
+10     {"k10":null, "k11":"value10"}   \N
+10     {"k10":null, "k11":"value10"}   value10
+
diff --git a/regression-test/data/javaudf_p0/test_javaudtf_arrayint.out 
b/regression-test/data/javaudf_p0/test_javaudtf_arrayint.out
new file mode 100644
index 00000000000..c7cf111f2e6
--- /dev/null
+++ b/regression-test/data/javaudf_p0/test_javaudtf_arrayint.out
@@ -0,0 +1,25 @@
+-- This file is automatically generated. You should know what you did if you 
want to edit this
+-- !select_default --
+1      2       2022-01-01      2022-01-01T11:11:11     a1b
+2      4       2022-01-01      2022-01-01T11:11:11     a2b
+3      6       2022-01-01      2022-01-01T11:11:11     a3b
+4      8       2022-01-01      2022-01-01T11:11:11     a4b
+5      10      2022-01-01      2022-01-01T11:11:11     a5b
+6      12      2022-01-01      2022-01-01T11:11:11     a6b
+7      14      2022-01-01      2022-01-01T11:11:11     a7b
+8      16      2022-01-01      2022-01-01T11:11:11     a8b
+9      18      2022-01-01      2022-01-01T11:11:11     a9b
+10     20      2022-06-06      2022-01-01T12:12:12     a10b
+
+-- !select1 --
+1      a1b     1
+2      a2b     2
+3      a3b     3
+4      a4b     4
+5      a5b     5
+6      a6b     6
+7      a7b     7
+8      a8b     8
+9      a9b     9
+10     a10b    10
+
diff --git a/regression-test/data/javaudf_p0/test_javaudtf_decimal.out 
b/regression-test/data/javaudf_p0/test_javaudtf_decimal.out
new file mode 100644
index 00000000000..4a0fcd24181
--- /dev/null
+++ b/regression-test/data/javaudf_p0/test_javaudtf_decimal.out
@@ -0,0 +1,15 @@
+-- This file is automatically generated. You should know what you did if you 
want to edit this
+-- !select_default --
+111    11111.111110000 222222.333333300
+112    1234556.111110000       222222.333333300
+113    87654321.111110000      \N
+
+-- !select1 --
+111    11111.111110000 22222.222220000
+112    1234556.111110000       2469112.222220000
+113    87654321.111110000      175308642.222220000
+
+-- !select2 --
+111    222222.333333300        444444.666666600
+112    222222.333333300        444444.666666600
+
diff --git a/regression-test/data/javaudf_p0/test_javaudtf_double.out 
b/regression-test/data/javaudf_p0/test_javaudtf_double.out
new file mode 100644
index 00000000000..0ce1c152d0e
--- /dev/null
+++ b/regression-test/data/javaudf_p0/test_javaudtf_double.out
@@ -0,0 +1,15 @@
+-- This file is automatically generated. You should know what you did if you 
want to edit this
+-- !select_default --
+111    11111.111       222222.33       1.234567834455677E7     1111112.0
+112    1234556.1       222222.33       2.2222222233333334E8    
4.444444444444556E12
+113    8.765432E7      \N      6.666666666666667E9     \N
+
+-- !select1 --
+111    1.234567834455677E7     1.234567834455677E8
+112    2.2222222233333334E8    2.2222222233333335E9
+113    6.666666666666667E9     6.666666666666667E10
+
+-- !select2 --
+111    1111112.0       1.111112E7
+112    4.444444444444556E12    4.4444444444445555E13
+
diff --git a/regression-test/data/javaudf_p0/test_javaudtf_int.out 
b/regression-test/data/javaudf_p0/test_javaudtf_int.out
new file mode 100644
index 00000000000..2fa8be4ac94
--- /dev/null
+++ b/regression-test/data/javaudf_p0/test_javaudtf_int.out
@@ -0,0 +1,32 @@
+-- This file is automatically generated. You should know what you did if you 
want to edit this
+-- !select_default --
+1      1       abc,defg        poiuytre,abcdefg
+2      2       abc,defg        poiuytre,abcdefg
+0      3       abc,defg        poiuytre,abcdefg
+1      4       abc,defg        poiuytre,abcdefg
+2      5       abc,defg        poiuytre,abcdefg
+0      6       abc,defg        poiuytre,abcdefg
+1      7       abc,defg        poiuytre,abcdefg
+2      8       abc,defg        poiuytre,abcdefg
+9      9       ab,cdefg        poiuytreabcde,fg
+
+-- !select1 --
+1      abc,defg        1
+1      abc,defg        1
+1      abc,defg        1
+2      abc,defg        2
+2      abc,defg        2
+2      abc,defg        2
+2      abc,defg        2
+2      abc,defg        2
+2      abc,defg        2
+9      ab,cdefg        9
+9      ab,cdefg        9
+9      ab,cdefg        9
+9      ab,cdefg        9
+9      ab,cdefg        9
+9      ab,cdefg        9
+9      ab,cdefg        9
+9      ab,cdefg        9
+9      ab,cdefg        9
+
diff --git a/regression-test/data/javaudf_p0/test_javaudtf_string.out 
b/regression-test/data/javaudf_p0/test_javaudtf_string.out
new file mode 100644
index 00000000000..7fc0036c5bf
--- /dev/null
+++ b/regression-test/data/javaudf_p0/test_javaudtf_string.out
@@ -0,0 +1,34 @@
+-- This file is automatically generated. You should know what you did if you 
want to edit this
+-- !select_default --
+1      1       abc,defg        poiuytre,abcdefg
+2      2       abc,defg        poiuytre,abcdefg
+0      3       abc,defg        poiuytre,abcdefg
+1      4       abc,defg        poiuytre,abcdefg
+2      5       abc,defg        poiuytre,abcdefg
+0      6       abc,defg        poiuytre,abcdefg
+1      7       abc,defg        poiuytre,abcdefg
+2      8       abc,defg        poiuytre,abcdefg
+9      9       ab,cdefg        poiuytreabcde,fg
+
+-- !select1 --
+0      abc,defg        abc
+0      abc,defg        defg
+0      abc,defg        abc
+0      abc,defg        defg
+1      abc,defg        abc
+1      abc,defg        defg
+1      abc,defg        abc
+1      abc,defg        defg
+1      abc,defg        abc
+1      abc,defg        defg
+2      abc,defg        abc
+2      abc,defg        defg
+2      abc,defg        abc
+2      abc,defg        defg
+2      abc,defg        abc
+2      abc,defg        defg
+9      ab,cdefg        ab
+9      ab,cdefg        cdefg
+
+-- !select2 --
+
diff --git 
a/regression-test/java-udf-src/src/main/java/org/apache/doris/udf/UDTFAllTypeTest.java
 
b/regression-test/java-udf-src/src/main/java/org/apache/doris/udf/UDTFAllTypeTest.java
new file mode 100644
index 00000000000..b3060d1d438
--- /dev/null
+++ 
b/regression-test/java-udf-src/src/main/java/org/apache/doris/udf/UDTFAllTypeTest.java
@@ -0,0 +1,210 @@
+// 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;
+import java.util.Arrays;
+import java.util.Collections;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.util.HashMap;
+
+public class UDTFAllTypeTest {
+    public static class UdtfBoolean {
+        public ArrayList<Boolean> evaluate(Boolean value, Integer count) {
+            if (value == null || count == null) {
+                return null;
+            }
+            ArrayList<Boolean> result = new ArrayList<>();
+            for (int i = 0; i < count; ++i) {
+                result.add((i % 2 == 0));
+            }
+            return result;
+        }
+    }
+
+    public static class UdtfByte {
+        public ArrayList<Byte> evaluate(Byte value, Integer count) {
+            if (value == null || count == null) {
+                return null;
+            }
+            ArrayList<Byte> result = new ArrayList<>();
+            for (int i = 0; i < count; ++i) {
+                result.add((byte) (value + i));
+            }
+            return result;
+        }
+    }
+
+    public static class UdtfShort {
+        public ArrayList<Short> evaluate(Short value, Integer count) {
+            if (value == null || count == null) {
+                return null;
+            }
+            ArrayList<Short> result = new ArrayList<>();
+            for (int i = 0; i < count; ++i) {
+                result.add((short) (value + i * 2));
+            }
+            return result;
+        }
+    }
+
+    public static class UdtfInt {
+        public ArrayList<Integer> evaluate(Integer value, Integer count) {
+            if (value == null || count == null) {
+                return null;
+            }
+            ArrayList<Integer> result = new ArrayList<>();
+            for (int i = 0; i < count; ++i) {
+                result.add(value + i * 3);
+            }
+            return result;
+        }
+    }
+
+    public static class UdtfLong {
+        public ArrayList<Long> evaluate(Long value, Integer count) {
+            if (value == null || count == null) {
+                return null;
+            }
+            ArrayList<Long> result = new ArrayList<>();
+            for (int i = 0; i < count; ++i) {
+                result.add((long) (value + i * 4));
+            }
+            return result;
+        }
+    }
+
+    public static class UdtfLargeInt {
+        public ArrayList<BigInteger> evaluate(BigInteger value, Integer count) 
{
+            if (value == null || count == null) {
+                return null;
+            }
+            ArrayList<BigInteger> result = new ArrayList<>();
+            for (int i = 0; i < count; ++i) {
+                result.add(new BigInteger(String.valueOf(i * 5)).add(value));
+            }
+            return result;
+        }
+    }
+
+    public static class UdtfFloat {
+        public ArrayList<Float> evaluate(Float value, Integer count) {
+            if (value == null || count == null) {
+                return null;
+            }
+            ArrayList<Float> result = new ArrayList<>();
+            for (int i = 0; i < count; ++i) {
+                result.add((float) (value + i * 0.1));
+            }
+            return result;
+        }
+    }
+
+    public static class UdtfDouble {
+        public ArrayList<Double> evaluate(Double value, Integer count) {
+            if (value == null || count == null) {
+                return null;
+            }
+            ArrayList<Double> result = new ArrayList<>();
+            for (int i = 0; i < count; ++i) {
+                result.add(value + i * 0.01);
+            }
+            return result;
+        }
+    }
+
+    public static class UdtfDecimal {
+        public ArrayList<BigDecimal> evaluate(BigDecimal value, Integer count) 
{
+            if (value == null || count == null) {
+                return null;
+            }
+            ArrayList<BigDecimal> result = new ArrayList<>();
+            for (int i = 0; i < count; ++i) {
+                result.add(new BigDecimal(String.valueOf(i * 
0.001)).add(value));
+            }
+            return result;
+        }
+    }
+
+    public static class UdtfDate {
+        public ArrayList<LocalDate> evaluate(LocalDate value, Integer count) {
+            if (value == null || count == null) {
+                return null;
+            }
+            ArrayList<LocalDate> result = new ArrayList<>();
+            for (int i = 0; i < count; ++i) {
+                result.add(value.plusMonths(i));
+            }
+            return result;
+        }
+    }
+
+    public static class UdtfDateTime {
+        public ArrayList<LocalDateTime> evaluate(LocalDateTime value, Integer 
count) {
+            if (value == null || count == null) {
+                return null;
+            }
+            ArrayList<LocalDateTime> result = new ArrayList<>();
+            for (int i = 0; i < count; ++i) {
+                result.add(value.plusDays(i));
+            }
+            return result;
+        }
+    }
+
+    public static class UdtfString {
+        public ArrayList<String> evaluate(String value, String separator) {
+            if (value == null || separator == null) {
+                return null;
+            } else {
+                return new ArrayList<>(Arrays.asList(value.split(separator)));
+            }
+        }
+    }
+
+    public static class UdtfList {
+        public ArrayList<String> evaluate(ArrayList<String> value, Integer 
count) {
+            if (value == null || count == null) {
+                return null;
+            }
+            if (count % 2 == 1) {
+                Collections.reverse(value);
+            }
+            return value;
+        }
+    }
+
+    public static class UdtfMap {
+        public ArrayList<String> evaluate(HashMap<String, String> value, 
Integer count) {
+            if (value == null || count == null) {
+                return null;
+            }
+            ArrayList<String> result;
+            if (count % 2 == 1) {
+                result = new ArrayList<>(value.keySet());
+            } else {
+                result = new ArrayList<>(value.values());
+            }
+            return result;
+        }
+    }
+
+}
diff --git a/be/src/vec/exprs/table_function/table_function_factory.h 
b/regression-test/java-udf-src/src/main/java/org/apache/doris/udf/UDTFArrayIntTest.java
similarity index 59%
copy from be/src/vec/exprs/table_function/table_function_factory.h
copy to 
regression-test/java-udf-src/src/main/java/org/apache/doris/udf/UDTFArrayIntTest.java
index a68a1763fc4..00fb948c826 100644
--- a/be/src/vec/exprs/table_function/table_function_factory.h
+++ 
b/regression-test/java-udf-src/src/main/java/org/apache/doris/udf/UDTFArrayIntTest.java
@@ -15,28 +15,17 @@
 // specific language governing permissions and limitations
 // under the License.
 
-#pragma once
+package org.apache.doris.udf;
 
-#include <functional>
-#include <memory>
-#include <string>
-#include <unordered_map>
+import java.util.ArrayList;
 
-#include "common/status.h"
-
-namespace doris {
-class ObjectPool;
-
-namespace vectorized {
-class TableFunction;
-
-class TableFunctionFactory {
-public:
-    TableFunctionFactory() = delete;
-    static Status get_fn(const std::string& fn_name_raw, ObjectPool* pool, 
TableFunction** fn);
-
-    const static std::unordered_map<std::string, 
std::function<std::unique_ptr<TableFunction>()>>
-            _function_map;
-};
-} // namespace vectorized
-} // namespace doris
+public class UDTFArrayIntTest {
+    public ArrayList<Integer> evaluate(ArrayList<Integer> val) {
+        if (val == null) return null;
+        ArrayList<Integer> result = new ArrayList<>();
+        for (int i = 0; i < val.size(); i = i + 2) {
+            result.add(val.get(i));
+        }
+        return val;
+    }
+}
diff --git a/be/src/vec/exprs/table_function/table_function_factory.h 
b/regression-test/java-udf-src/src/main/java/org/apache/doris/udf/UDTFDecimalTest.java
similarity index 59%
copy from be/src/vec/exprs/table_function/table_function_factory.h
copy to 
regression-test/java-udf-src/src/main/java/org/apache/doris/udf/UDTFDecimalTest.java
index a68a1763fc4..9f8d3c07ddc 100644
--- a/be/src/vec/exprs/table_function/table_function_factory.h
+++ 
b/regression-test/java-udf-src/src/main/java/org/apache/doris/udf/UDTFDecimalTest.java
@@ -15,28 +15,17 @@
 // specific language governing permissions and limitations
 // under the License.
 
-#pragma once
+package org.apache.doris.udf;
 
-#include <functional>
-#include <memory>
-#include <string>
-#include <unordered_map>
+import java.math.BigDecimal;
+import java.util.ArrayList;
 
-#include "common/status.h"
-
-namespace doris {
-class ObjectPool;
-
-namespace vectorized {
-class TableFunction;
-
-class TableFunctionFactory {
-public:
-    TableFunctionFactory() = delete;
-    static Status get_fn(const std::string& fn_name_raw, ObjectPool* pool, 
TableFunction** fn);
-
-    const static std::unordered_map<std::string, 
std::function<std::unique_ptr<TableFunction>()>>
-            _function_map;
-};
-} // namespace vectorized
-} // namespace doris
+public class UDTFDecimalTest {
+    public ArrayList<BigDecimal> evaluate(BigDecimal val) {
+        if (val == null) return null;
+        ArrayList<BigDecimal> result = new ArrayList<>();
+        BigDecimal sum = val.add(val);
+        result.add(sum);
+        return result;
+    }
+}
diff --git a/be/src/vec/exprs/table_function/table_function_factory.h 
b/regression-test/java-udf-src/src/main/java/org/apache/doris/udf/UDTFDoubleTest.java
similarity index 59%
copy from be/src/vec/exprs/table_function/table_function_factory.h
copy to 
regression-test/java-udf-src/src/main/java/org/apache/doris/udf/UDTFDoubleTest.java
index a68a1763fc4..359df11ebbd 100644
--- a/be/src/vec/exprs/table_function/table_function_factory.h
+++ 
b/regression-test/java-udf-src/src/main/java/org/apache/doris/udf/UDTFDoubleTest.java
@@ -15,28 +15,15 @@
 // specific language governing permissions and limitations
 // under the License.
 
-#pragma once
+package org.apache.doris.udf;
 
-#include <functional>
-#include <memory>
-#include <string>
-#include <unordered_map>
+import java.util.ArrayList;
 
-#include "common/status.h"
-
-namespace doris {
-class ObjectPool;
-
-namespace vectorized {
-class TableFunction;
-
-class TableFunctionFactory {
-public:
-    TableFunctionFactory() = delete;
-    static Status get_fn(const std::string& fn_name_raw, ObjectPool* pool, 
TableFunction** fn);
-
-    const static std::unordered_map<std::string, 
std::function<std::unique_ptr<TableFunction>()>>
-            _function_map;
-};
-} // namespace vectorized
-} // namespace doris
+public class UDTFDoubleTest {
+    public ArrayList<Double> evaluate(Double val) {
+        if (val == null) return null;
+        ArrayList<Double> result = new ArrayList<>();
+        result.add(val * 10);
+        return result;
+    }
+}
diff --git a/be/src/vec/exprs/table_function/table_function_factory.h 
b/regression-test/java-udf-src/src/main/java/org/apache/doris/udf/UDTFIntTest.java
similarity index 59%
copy from be/src/vec/exprs/table_function/table_function_factory.h
copy to 
regression-test/java-udf-src/src/main/java/org/apache/doris/udf/UDTFIntTest.java
index a68a1763fc4..27b435727be 100644
--- a/be/src/vec/exprs/table_function/table_function_factory.h
+++ 
b/regression-test/java-udf-src/src/main/java/org/apache/doris/udf/UDTFIntTest.java
@@ -15,28 +15,16 @@
 // specific language governing permissions and limitations
 // under the License.
 
-#pragma once
+package org.apache.doris.udf;
 
-#include <functional>
-#include <memory>
-#include <string>
-#include <unordered_map>
+import java.util.ArrayList;
 
-#include "common/status.h"
-
-namespace doris {
-class ObjectPool;
-
-namespace vectorized {
-class TableFunction;
-
-class TableFunctionFactory {
-public:
-    TableFunctionFactory() = delete;
-    static Status get_fn(const std::string& fn_name_raw, ObjectPool* pool, 
TableFunction** fn);
-
-    const static std::unordered_map<std::string, 
std::function<std::unique_ptr<TableFunction>()>>
-            _function_map;
-};
-} // namespace vectorized
-} // namespace doris
+public class UDTFIntTest {
+    public ArrayList<Integer> evaluate(int count) {
+        ArrayList<Integer> result = new ArrayList<>();
+        for (int i = 0; i < count; i++) {
+            result.add(count);
+        }
+        return result;
+    }
+}
diff --git a/be/src/vec/exprs/table_function/table_function_factory.h 
b/regression-test/java-udf-src/src/main/java/org/apache/doris/udf/UDTFNullTest.java
similarity index 59%
copy from be/src/vec/exprs/table_function/table_function_factory.h
copy to 
regression-test/java-udf-src/src/main/java/org/apache/doris/udf/UDTFNullTest.java
index a68a1763fc4..dc6ac9be90c 100644
--- a/be/src/vec/exprs/table_function/table_function_factory.h
+++ 
b/regression-test/java-udf-src/src/main/java/org/apache/doris/udf/UDTFNullTest.java
@@ -15,28 +15,12 @@
 // specific language governing permissions and limitations
 // under the License.
 
-#pragma once
+package org.apache.doris.udf;
 
-#include <functional>
-#include <memory>
-#include <string>
-#include <unordered_map>
+import java.util.ArrayList;
 
-#include "common/status.h"
-
-namespace doris {
-class ObjectPool;
-
-namespace vectorized {
-class TableFunction;
-
-class TableFunctionFactory {
-public:
-    TableFunctionFactory() = delete;
-    static Status get_fn(const std::string& fn_name_raw, ObjectPool* pool, 
TableFunction** fn);
-
-    const static std::unordered_map<std::string, 
std::function<std::unique_ptr<TableFunction>()>>
-            _function_map;
-};
-} // namespace vectorized
-} // namespace doris
+public class UDTFNullTest {
+    public ArrayList<String> evaluate(String value, String separator) {
+        return null;
+    }
+}
diff --git a/be/src/vec/exprs/table_function/table_function_factory.h 
b/regression-test/java-udf-src/src/main/java/org/apache/doris/udf/UDTFStringTest.java
similarity index 59%
copy from be/src/vec/exprs/table_function/table_function_factory.h
copy to 
regression-test/java-udf-src/src/main/java/org/apache/doris/udf/UDTFStringTest.java
index a68a1763fc4..cb2eb45c9c1 100644
--- a/be/src/vec/exprs/table_function/table_function_factory.h
+++ 
b/regression-test/java-udf-src/src/main/java/org/apache/doris/udf/UDTFStringTest.java
@@ -15,28 +15,17 @@
 // specific language governing permissions and limitations
 // under the License.
 
-#pragma once
+package org.apache.doris.udf;
 
-#include <functional>
-#include <memory>
-#include <string>
-#include <unordered_map>
+import java.util.ArrayList;
+import java.util.Arrays;
 
-#include "common/status.h"
-
-namespace doris {
-class ObjectPool;
-
-namespace vectorized {
-class TableFunction;
-
-class TableFunctionFactory {
-public:
-    TableFunctionFactory() = delete;
-    static Status get_fn(const std::string& fn_name_raw, ObjectPool* pool, 
TableFunction** fn);
-
-    const static std::unordered_map<std::string, 
std::function<std::unique_ptr<TableFunction>()>>
-            _function_map;
-};
-} // namespace vectorized
-} // namespace doris
+public class UDTFStringTest {
+    public ArrayList<String> evaluate(String value, String separator) {
+        if (value == null || separator == null) {
+            return null;
+        } else {
+            return new ArrayList<>(Arrays.asList(value.split(separator)));
+        }
+    }
+}
diff --git a/regression-test/suites/javaudf_p0/test_javaudtf_all_types.groovy 
b/regression-test/suites/javaudf_p0/test_javaudtf_all_types.groovy
new file mode 100644
index 00000000000..aaf9d4d5b42
--- /dev/null
+++ b/regression-test/suites/javaudf_p0/test_javaudtf_all_types.groovy
@@ -0,0 +1,233 @@
+// 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_javaudtf_all_types") {
+    def tableName = "test_javaudtf_all_types"
+    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} (
+            int_col int,
+            boolean_col boolean,
+            tinyint_col tinyint,
+            smallint_col smallint,
+            bigint_col bigint,
+            largeint_col largeint,
+            decimal_col decimal(15, 4),
+            float_col float,
+            double_col double,
+            date_col date,
+            datetime_col datetime(6),
+            string_col string,
+            array_col array<string>,
+            map_col map<string, string>
+            )
+            DISTRIBUTED BY HASH(int_col) PROPERTIES("replication_num" = "1");
+        """
+        StringBuilder sb = new StringBuilder()
+        int i = 1
+        for (; i < 10; i++) {
+            sb.append("""
+                
(${i},${i%2},${i},${i}*2,${i}*3,${i}*4,${3.33/i},${(7.77/i).round(3)},${(3.1415/i).round(5)},"2023-10-${i+17}","2023-10-${i+10}
 10:1${i}:11.234","row${i}",array(null, 
"nested${i}"),{"k${i}":null,"k${i+1}":"value${i}"}),
+            """)
+        }
+        sb.append("""
+                
(${i},${i%2},null,${i}*2,${i}*3,${i}*4,null,${(7.77/i).round(3)},${(3.1415/i).round(5)},null,"2023-10-${i+10}
 10:${i}:11.234",null,array(null, 
"nested${i}"),{"k${i}":null,"k${i+1}":"value${i}"}),
+            """)
+        sb.append("""
+                
(null,null,null,null,null,null,null,null,null,null,null,null,null,null)
+            """)
+        sql """ INSERT INTO ${tableName} VALUES
+             ${sb.toString()}
+            """
+        File path = new File(jarPath)
+        if (!path.exists()) {
+            throw new IllegalStateException("""${jarPath} doesn't exist! """)
+        }
+        qt_select """select * from ${tableName} order by 1,2,3;"""
+
+
+        sql """DROP FUNCTION IF EXISTS udtf_boolean(boolean, int);"""
+        sql """CREATE TABLES FUNCTION udtf_boolean(boolean, int) RETURNS 
array<boolean> PROPERTIES (
+            "file"="file://${jarPath}",
+            "symbol"="org.apache.doris.udf.UDTFAllTypeTest\$UdtfBoolean",
+            "type"="JAVA_UDF"
+        );"""
+
+        sql """DROP FUNCTION IF EXISTS udtf_tinyint(tinyint, int);"""
+        sql """CREATE TABLES FUNCTION udtf_tinyint(tinyint, int) RETURNS 
array<tinyint> PROPERTIES (
+            "file"="file://${jarPath}",
+            "symbol"="org.apache.doris.udf.UDTFAllTypeTest\$UdtfByte",
+            "type"="JAVA_UDF"
+        );"""
+
+        sql """DROP FUNCTION IF EXISTS udtf_short(smallint, int);"""
+        sql """CREATE TABLES FUNCTION udtf_short(smallint, int) RETURNS 
array<smallint> PROPERTIES (
+            "file"="file://${jarPath}",
+            "symbol"="org.apache.doris.udf.UDTFAllTypeTest\$UdtfShort",
+            "type"="JAVA_UDF"
+        );"""
+
+        sql """DROP FUNCTION IF EXISTS udtf_int(int, int);"""
+        sql """CREATE TABLES FUNCTION udtf_int(int, int) RETURNS array<int> 
PROPERTIES (
+            "file"="file://${jarPath}",
+            "symbol"="org.apache.doris.udf.UDTFAllTypeTest\$UdtfInt",
+            "type"="JAVA_UDF"
+        );"""
+
+        sql """DROP FUNCTION IF EXISTS udtf_long(bigint, int);"""
+        sql """CREATE TABLES FUNCTION udtf_long(bigint, int) RETURNS 
array<bigint> PROPERTIES (
+            "file"="file://${jarPath}",
+            "symbol"="org.apache.doris.udf.UDTFAllTypeTest\$UdtfLong",
+            "type"="JAVA_UDF"
+        );"""
+
+        sql """DROP FUNCTION IF EXISTS udtf_largeint(largeint, int);"""
+        sql """CREATE TABLES FUNCTION udtf_largeint(largeint, int) RETURNS 
array<largeint> PROPERTIES (
+            "file"="file://${jarPath}",
+            "symbol"="org.apache.doris.udf.UDTFAllTypeTest\$UdtfLargeInt",
+            "type"="JAVA_UDF"
+        );"""
+
+        sql """DROP FUNCTION IF EXISTS udtf_decimal(decimal(15, 4), int);"""
+        sql """CREATE TABLES FUNCTION udtf_decimal(decimal(15, 4), int) 
RETURNS array<decimal(15, 4)> PROPERTIES (
+            "file"="file://${jarPath}",
+            "symbol"="org.apache.doris.udf.UDTFAllTypeTest\$UdtfDecimal",
+            "type"="JAVA_UDF"
+        );"""
+
+        sql """DROP FUNCTION IF EXISTS udtf_float(float, int);"""
+        sql """CREATE TABLES FUNCTION udtf_float(float, int) RETURNS 
array<float> PROPERTIES (
+            "file"="file://${jarPath}",
+            "symbol"="org.apache.doris.udf.UDTFAllTypeTest\$UdtfFloat",
+            "type"="JAVA_UDF"
+        );"""
+
+        sql """DROP FUNCTION IF EXISTS udtf_double(double, int);"""
+        sql """CREATE TABLES FUNCTION udtf_double(double, int) RETURNS 
array<double> PROPERTIES (
+            "file"="file://${jarPath}",
+            "symbol"="org.apache.doris.udf.UDTFAllTypeTest\$UdtfDouble",
+            "type"="JAVA_UDF"
+        );"""
+
+        sql """DROP FUNCTION IF EXISTS udtf_date(date, int);"""
+        sql """CREATE TABLES FUNCTION udtf_date(date, int) RETURNS array<date> 
PROPERTIES (
+            "file"="file://${jarPath}",
+            "symbol"="org.apache.doris.udf.UDTFAllTypeTest\$UdtfDate",
+            "type"="JAVA_UDF"
+        );"""
+
+        sql """DROP FUNCTION IF EXISTS udtf_datetime(datetime(6), int);"""
+        sql """CREATE TABLES FUNCTION udtf_datetime(datetime(6), int) RETURNS 
array<datetime(6)> PROPERTIES (
+            "file"="file://${jarPath}",
+            "symbol"="org.apache.doris.udf.UDTFAllTypeTest\$UdtfDateTime",
+            "type"="JAVA_UDF"
+        );"""
+
+        sql """DROP FUNCTION IF EXISTS udtf_string(string, string);"""
+        sql """CREATE TABLES FUNCTION udtf_string(string,string) RETURNS 
array<string> PROPERTIES (
+            "file"="file://${jarPath}",
+            "symbol"="org.apache.doris.udf.UDTFAllTypeTest\$UdtfString",
+            "type"="JAVA_UDF"
+        );"""
+
+        sql """DROP FUNCTION IF EXISTS udtf_list(array<string>, int);"""
+        sql """CREATE TABLES FUNCTION udtf_list(array<string>, int) RETURNS 
array<string> PROPERTIES (
+            "file"="file://${jarPath}",
+            "symbol"="org.apache.doris.udf.UDTFAllTypeTest\$UdtfList",
+            "type"="JAVA_UDF"
+        );"""
+
+        sql """DROP FUNCTION IF EXISTS udtf_map(map<string,string>, int);"""
+        sql """CREATE TABLES FUNCTION udtf_map(map<string,string>, int) 
RETURNS array<string> PROPERTIES (
+            "file"="file://${jarPath}",
+            "symbol"="org.apache.doris.udf.UDTFAllTypeTest\$UdtfMap",
+            "type"="JAVA_UDF"
+        );"""
+
+        qt_select_boolean_col  """select int_col, boolean_col, e1 from 
${tableName} lateral view udtf_boolean(boolean_col, int_col) tmp1 as e1 order 
by int_col,2,3;"""
+        qt_select_tinyint_col  """select int_col, tinyint_col, e1 from 
${tableName} lateral view udtf_tinyint(tinyint_col, int_col) tmp1 as e1 order 
by int_col,2,3;"""
+        qt_select_smallint_col """select int_col, smallint_col, e1 from 
${tableName} lateral view udtf_short(smallint_col, int_col) tmp1 as e1 order by 
int_col,2,3;"""
+        qt_select_int_col      """select int_col, int_col, e1 from 
${tableName} lateral view udtf_int(int_col, int_col) tmp1 as e1 order by 
int_col,2,3;"""
+        qt_select_bigint_col   """select int_col, bigint_col, e1 from 
${tableName} lateral view udtf_long(bigint_col, int_col) tmp1 as e1 order by 
int_col,2,3;"""
+        qt_select_largeint_col """select int_col, largeint_col,e1 from 
${tableName} lateral view udtf_largeint(largeint_col, int_col) tmp1 as e1 order 
by int_col,2,3;"""
+        qt_select_decimal_col  """select int_col, decimal_col,e1 from 
${tableName} lateral view udtf_decimal(decimal_col, int_col) tmp1 as e1 order 
by int_col,2,3;"""
+        qt_select_float_col    """select int_col, float_col,e1 from 
${tableName} lateral view udtf_float(float_col, int_col) tmp1 as e1 order by 
int_col,2,3;"""
+        qt_select_double_col   """select int_col, double_col,e1 from 
${tableName} lateral view udtf_double(double_col, int_col) tmp1 as e1 order by 
int_col,2,3;"""
+        qt_select_date_col     """select int_col, date_col,e1 from 
${tableName} lateral view udtf_date(date_col, int_col) tmp1 as e1 order by 
int_col,2,3;"""
+        qt_select_datetime_col """select int_col, datetime_col,e1 from 
${tableName} lateral view udtf_datetime(datetime_col, int_col) tmp1 as e1 order 
by int_col,2,3;"""
+        qt_select_string_col   """select int_col, string_col,e1 from 
${tableName} lateral view udtf_string(string_col, "") tmp1 as e1 order by 
int_col,2,3;"""
+        qt_select_array_col    """select int_col, array_col,e1 from 
${tableName} lateral view udtf_list(array_col, int_col) tmp1 as e1 order by 
int_col,3;"""
+        qt_select_map_col      """select int_col, map_col,e1 from ${tableName} 
lateral view udtf_map(map_col, int_col) tmp1 as e1 order by int_col,3;"""
+        qt_select_boolean_col_outer  """select int_col, boolean_col,e1 from 
${tableName} lateral view udtf_boolean_outer(boolean_col, int_col) tmp1 as e1 
order by int_col,2,3;"""  
+        qt_select_tinyint_col_outer  """select int_col, tinyint_col,e1 from 
${tableName} lateral view udtf_tinyint_outer(tinyint_col, int_col) tmp1 as e1 
order by int_col,2,3;"""  
+        qt_select_smallint_col_outer """select int_col, smallint_col,e1 from 
${tableName} lateral view udtf_short_outer(smallint_col, int_col) tmp1 as e1 
order by int_col,2,3;"""  
+        qt_select_int_col_outer      """select int_col, int_col,e1 from 
${tableName} lateral view udtf_int_outer(int_col, int_col) tmp1 as e1 order by 
int_col,2,3;"""  
+        qt_select_bigint_col_outer   """select int_col, bigint_col,e1 from 
${tableName} lateral view udtf_long_outer(bigint_col, int_col) tmp1 as e1 order 
by int_col,2,3;"""  
+        qt_select_largeint_col_outer """select int_col, largeint_col,e1 from 
${tableName} lateral view udtf_largeint_outer(largeint_col, int_col) tmp1 as e1 
order by int_col,2,3;"""  
+        qt_select_decimal_col_outer  """select int_col, decimal_col,e1 from 
${tableName} lateral view udtf_decimal_outer(decimal_col, int_col) tmp1 as e1 
order by int_col,2,3;"""  
+        qt_select_float_col_outer    """select int_col, float_col,e1 from 
${tableName} lateral view udtf_float_outer(float_col, int_col) tmp1 as e1 order 
by int_col,2,3;"""  
+        qt_select_double_col_outer   """select int_col, double_col,e1 from 
${tableName} lateral view udtf_double_outer(double_col, int_col) tmp1 as e1 
order by int_col,2,3;"""  
+        qt_select_date_col_outer     """select int_col, date_col,e1 from 
${tableName} lateral view udtf_date_outer(date_col, int_col) tmp1 as e1 order 
by int_col,2,3;"""  
+        qt_select_datetime_col_outer """select int_col, datetime_col,e1 from 
${tableName} lateral view udtf_datetime_outer(datetime_col, int_col) tmp1 as e1 
order by int_col,2,3;"""  
+        qt_select_string_col_outer   """select int_col, string_col,e1 from 
${tableName} lateral view udtf_string_outer(string_col, "") tmp1 as e1 order by 
int_col,2,3;"""  
+        qt_select_array_col_outer    """select int_col, array_col,e1 from 
${tableName} lateral view udtf_list_outer(array_col, int_col) tmp1 as e1 order 
by int_col,3;"""
+        qt_select_map_col_outer      """select int_col, map_col,e1 from 
${tableName} lateral view udtf_map_outer(map_col, int_col) tmp1 as e1 order by 
int_col,3;"""
+        // qt_java_udf_all_types """select
+        //     int_col,
+        //     udtf_boolean(boolean_col),
+        //     udtf_tinyint(tinyint_col),
+        //     udtf_short(smallint_col),
+        //     udtf_int(int_col),
+        //     udtf_long(bigint_col),
+        //     udtf_largeint(largeint_col),
+        //     udtf_decimal(decimal_col),
+        //     udtf_float(float_col),
+        //     udtf_double(double_col),
+        //     udtf_date(date_col),
+        //     udtf_datetime(datetime_col),
+        //     udtf_string(string_col),
+        //     udtf_list(array_col),
+        //     udtf_map(map_col)
+        //     from ${tableName} order by int_col;"""
+    } finally {
+        try_sql """DROP FUNCTION IF EXISTS udtf_boolean_outer(boolean, int);"""
+        try_sql """DROP FUNCTION IF EXISTS udtf_tinyint_outer(tinyint, int);"""
+        try_sql """DROP FUNCTION IF EXISTS udtf_short_outer(smallint, int);"""
+        try_sql """DROP FUNCTION IF EXISTS udtf_int_outer(int, int);"""
+        try_sql """DROP FUNCTION IF EXISTS udtf_long_outer(bigint, int);"""
+        try_sql """DROP FUNCTION IF EXISTS udtf_largeint_outer(largeint, 
int);"""
+        try_sql """DROP FUNCTION IF EXISTS udtf_decimal_outer(decimal(15, 4), 
int);"""
+        try_sql """DROP FUNCTION IF EXISTS udtf_float_outer(float, int);"""
+        try_sql """DROP FUNCTION IF EXISTS udtf_double_outer(double, int);"""
+        try_sql """DROP FUNCTION IF EXISTS udtf_date_outer(date, int);"""
+        try_sql """DROP FUNCTION IF EXISTS udtf_datetime_outer(datetime(6), 
int);"""
+        try_sql """DROP FUNCTION IF EXISTS udtf_string_outer(string, 
string);"""
+        try_sql """DROP FUNCTION IF EXISTS udtf_list_outer(array<string>, 
int);"""
+        try_sql """DROP FUNCTION IF EXISTS udtf_map_outer(map<string,string>, 
int);"""
+        // try_sql("""DROP TABLE IF EXISTS ${tableName};""")
+    }
+}
diff --git a/regression-test/suites/javaudf_p0/test_javaudtf_arrayint.groovy 
b/regression-test/suites/javaudf_p0/test_javaudtf_arrayint.groovy
new file mode 100644
index 00000000000..c9140c689eb
--- /dev/null
+++ b/regression-test/suites/javaudf_p0/test_javaudtf_arrayint.groovy
@@ -0,0 +1,75 @@
+// 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_javaudtf_arrayint") {
+    def tableName = "test_javaudtf_arrayint"
+    def jarPath = 
"""${context.file.parent}/jars/java-udf-case-jar-with-dependencies.jar"""
+
+    log.info("Jar path: ${jarPath}".toString())
+    try {
+        sql """ DROP TABLE IF EXISTS ${tableName} """
+        sql """
+        CREATE TABLE IF NOT EXISTS ${tableName} (
+            `user_id`      INT      NOT NULL COMMENT "",
+            `tinyint_col`  TINYINT  NOT NULL COMMENT "",
+            `datev2_col`   datev2 NOT NULL COMMENT "",
+            `datetimev2_col` datetimev2 NOT NULL COMMENT "", 
+            `string_col`   STRING   NOT NULL COMMENT ""
+            )
+            DISTRIBUTED BY HASH(user_id) PROPERTIES("replication_num" = "1");
+        """
+        StringBuilder sb = new StringBuilder()
+        int i = 1
+        for (; i < 10; i ++) {
+            sb.append("""
+                (${i},${i}*2,'2022-01-01','2022-01-01 11:11:11','a${i}b'),
+            """)
+        }
+        sb.append("""
+                (${i},${i}*2,'2022-06-06','2022-01-01 12:12:12','a${i}b')
+            """)
+        sql """ INSERT INTO ${tableName} VALUES
+             ${sb.toString()}
+            """
+        qt_select_default """ SELECT * FROM ${tableName} t ORDER BY user_id; 
"""
+
+        File path = new File(jarPath)
+        if (!path.exists()) {
+            throw new IllegalStateException("""${jarPath} doesn't exist! """)
+        }
+
+        sql """DROP FUNCTION IF EXISTS udtf_arrayint_outer(array<int>);"""
+        sql """ CREATE TABLES FUNCTION udtf_arrayint(array<int>) RETURNS 
array<int> PROPERTIES (
+            "file"="file://${jarPath}",
+            "symbol"="org.apache.doris.udf.UDTFArrayIntTest",
+            "always_nullable"="true",
+            "type"="JAVA_UDF"
+        ); """
+
+        qt_select1 """ SELECT user_id, string_col, e1 FROM ${tableName} 
lateral view  udtf_arrayint(array(user_id)) temp as e1 order by user_id; """
+
+    } finally {
+        try_sql("DROP FUNCTION IF EXISTS udtf_arrayint(array<int>);")
+        try_sql("DROP TABLE IF EXISTS ${tableName}")
+    }
+}
diff --git a/regression-test/suites/javaudf_p0/test_javaudtf_decimal.groovy 
b/regression-test/suites/javaudf_p0/test_javaudtf_decimal.groovy
new file mode 100644
index 00000000000..ae3cacb246a
--- /dev/null
+++ b/regression-test/suites/javaudf_p0/test_javaudtf_decimal.groovy
@@ -0,0 +1,67 @@
+// 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_javaudtf_decimal") {
+    def tableName = "test_javaudtf_decimal"
+    def jarPath = 
"""${context.file.parent}/jars/java-udf-case-jar-with-dependencies.jar"""
+
+    log.info("Jar path: ${jarPath}".toString())
+    try {
+        sql """ DROP TABLE IF EXISTS ${tableName} """
+        sql """
+        CREATE TABLE IF NOT EXISTS ${tableName} (
+            `user_id` INT NOT NULL COMMENT "",
+            `cost_1` decimal(27,9) NOT NULL COMMENT "",
+            `cost_2` decimal(27,9) COMMENT ""
+            )
+            DISTRIBUTED BY HASH(user_id) PROPERTIES("replication_num" = "1");
+        """
+        
+        
+        sql """ INSERT INTO ${tableName} (`user_id`,`cost_1`,`cost_2`) VALUES
+                (111,11111.11111,222222.3333333),
+                (112,1234556.11111,222222.3333333),
+                (113,87654321.11111,null)
+            """
+        qt_select_default """ SELECT * FROM ${tableName} t ORDER BY user_id; 
"""
+
+        File path = new File(jarPath)
+        if (!path.exists()) {
+            throw new IllegalStateException("""${jarPath} doesn't exist! """)
+        }
+        sql """DROP FUNCTION IF EXISTS udtf_decimal_outer(decimal(27,9));"""
+        sql """ CREATE TABLES FUNCTION udtf_decimal(decimal(27,9)) RETURNS 
array<decimal(27,9)> PROPERTIES (
+            "file"="file://${jarPath}",
+            "symbol"="org.apache.doris.udf.UDTFDecimalTest",
+            "always_nullable"="true",
+            "type"="JAVA_UDF"
+        ); """
+
+        qt_select1 """ SELECT user_id, cost_1, e1 FROM ${tableName} lateral 
view  udtf_decimal(cost_1) temp as e1 order by user_id; """
+        qt_select2 """ SELECT user_id, cost_2, e1 FROM ${tableName} lateral 
view  udtf_decimal(cost_2) temp as e1 order by user_id; """
+
+    } finally {
+        try_sql("DROP FUNCTION IF EXISTS udtf_decimal(decimal);")
+        try_sql("DROP TABLE IF EXISTS ${tableName}")
+    }
+}
diff --git a/regression-test/suites/javaudf_p0/test_javaudtf_double.groovy 
b/regression-test/suites/javaudf_p0/test_javaudtf_double.groovy
new file mode 100644
index 00000000000..22d54f81b8a
--- /dev/null
+++ b/regression-test/suites/javaudf_p0/test_javaudtf_double.groovy
@@ -0,0 +1,70 @@
+// 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_javaudtf_double") {
+    def tableName = "test_javaudtf_double"
+    def jarPath = 
"""${context.file.parent}/jars/java-udf-case-jar-with-dependencies.jar"""
+
+    log.info("Jar path: ${jarPath}".toString())
+    try {
+        sql """ DROP TABLE IF EXISTS ${tableName} """
+        sql """
+        CREATE TABLE IF NOT EXISTS ${tableName} (
+            `user_id`  INT    NOT NULL COMMENT "",
+            `float_1`  FLOAT  NOT NULL COMMENT "",
+            `float_2`  FLOAT           COMMENT "",
+            `double_1` DOUBLE NOT NULL COMMENT "",
+            `double_2` DOUBLE          COMMENT ""
+            )
+            DISTRIBUTED BY HASH(user_id) PROPERTIES("replication_num" = "1");
+        """
+        
+        
+        sql """ INSERT INTO ${tableName} 
(`user_id`,`float_1`,`float_2`,double_1,double_2) VALUES
+                
(111,11111.11111,222222.3333333,12345678.34455677,1111111.999999999999),
+                
(112,1234556.11111,222222.3333333,222222222.3333333333333,4444444444444.555555555555),
+                (113,87654321.11111,null,6666666666.6666666666,null)
+            """
+        qt_select_default """ SELECT * FROM ${tableName} t ORDER BY user_id; 
"""
+
+        File path = new File(jarPath)
+        if (!path.exists()) {
+            throw new IllegalStateException("""${jarPath} doesn't exist! """)
+        }
+
+        sql """DROP FUNCTION IF EXISTS udtf_double_outer(double);"""
+        sql """ CREATE TABLES FUNCTION udtf_double(double) RETURNS 
array<double> PROPERTIES (
+            "file"="file://${jarPath}",
+            "symbol"="org.apache.doris.udf.UDTFDoubleTest",
+            "always_nullable"="true",
+            "type"="JAVA_UDF"
+        ); """
+
+        qt_select1 """ SELECT user_id, double_1, e1 FROM ${tableName} lateral 
view  udtf_double(double_1) temp as e1 order by user_id; """
+        qt_select2 """ SELECT user_id, double_2, e1 FROM ${tableName} lateral 
view  udtf_double(double_2) temp as e1 order by user_id; """
+
+    } finally {
+        try_sql("DROP FUNCTION IF EXISTS udtf_double(double);")
+        try_sql("DROP TABLE IF EXISTS ${tableName}")
+    }
+}
diff --git a/regression-test/suites/javaudf_p0/test_javaudtf_int.groovy 
b/regression-test/suites/javaudf_p0/test_javaudtf_int.groovy
new file mode 100644
index 00000000000..a1d85c99cba
--- /dev/null
+++ b/regression-test/suites/javaudf_p0/test_javaudtf_int.groovy
@@ -0,0 +1,74 @@
+// 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_javaudtf_int") {
+    def tableName = "test_javaudtf_int"
+    def jarPath = 
"""${context.file.parent}/jars/java-udf-case-jar-with-dependencies.jar"""
+
+    log.info("Jar path: ${jarPath}".toString())
+    try {
+        sql """ DROP TABLE IF EXISTS ${tableName} """
+        sql """
+        CREATE TABLE IF NOT EXISTS ${tableName} (
+            `user_id`     INT         NOT NULL COMMENT "用户id",
+            `char_col`    CHAR        NOT NULL COMMENT "",
+            `varchar_col` VARCHAR(10) NOT NULL COMMENT "",
+            `string_col`  STRING      NOT NULL COMMENT ""
+            )
+            DISTRIBUTED BY HASH(user_id) PROPERTIES("replication_num" = "1");
+        """
+        StringBuilder sb = new StringBuilder()
+        int i = 1
+        for (; i < 9; i ++) {
+            sb.append("""
+                (${i % 3}, '${i}','abc,defg','poiuytre,abcdefg'),
+            """)
+        }
+        sb.append("""
+                (${i}, '${i}','ab,cdefg','poiuytreabcde,fg')
+            """)
+        sql """ INSERT INTO ${tableName} VALUES
+             ${sb.toString()}
+            """
+        qt_select_default """ SELECT * FROM ${tableName} t ORDER BY char_col; 
"""
+
+        File path = new File(jarPath)
+        if (!path.exists()) {
+            throw new IllegalStateException("""${jarPath} doesn't exist! """)
+        }
+
+        sql """DROP FUNCTION IF EXISTS udtf_int_outer(int);"""
+        sql """ CREATE TABLES FUNCTION udtf_int(int) RETURNS array<int> 
PROPERTIES (
+            "file"="file://${jarPath}",
+            "symbol"="org.apache.doris.udf.UDTFIntTest",
+            "always_nullable"="true",
+            "type"="JAVA_UDF"
+        ); """
+
+        qt_select1 """ SELECT user_id, varchar_col, e1 FROM ${tableName} 
lateral view  udtf_int(user_id) temp as e1 order by user_id; """
+
+    } finally {
+        try_sql("DROP FUNCTION IF EXISTS udtf_int(int);")
+        try_sql("DROP TABLE IF EXISTS ${tableName}")
+    }
+}
diff --git a/regression-test/suites/javaudf_p0/test_javaudtf_string.groovy 
b/regression-test/suites/javaudf_p0/test_javaudtf_string.groovy
new file mode 100644
index 00000000000..b3f034937fa
--- /dev/null
+++ b/regression-test/suites/javaudf_p0/test_javaudtf_string.groovy
@@ -0,0 +1,85 @@
+// 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_javaudtf_string") {
+    def tableName = "test_javaudtf_string"
+    def jarPath = 
"""${context.file.parent}/jars/java-udf-case-jar-with-dependencies.jar"""
+
+    log.info("Jar path: ${jarPath}".toString())
+    try {
+        sql """ DROP TABLE IF EXISTS ${tableName} """
+        sql """
+        CREATE TABLE IF NOT EXISTS ${tableName} (
+            `user_id`     INT         NOT NULL COMMENT "用户id",
+            `char_col`    CHAR        NOT NULL COMMENT "",
+            `varchar_col` VARCHAR(10) NOT NULL COMMENT "",
+            `string_col`  STRING      NOT NULL COMMENT ""
+            )
+            DISTRIBUTED BY HASH(user_id) PROPERTIES("replication_num" = "1");
+        """
+        StringBuilder sb = new StringBuilder()
+        int i = 1
+        for (; i < 9; i ++) {
+            sb.append("""
+                (${i % 3}, '${i}','abc,defg','poiuytre,abcdefg'),
+            """)
+        }
+        sb.append("""
+                (${i}, '${i}','ab,cdefg','poiuytreabcde,fg')
+            """)
+        sql """ INSERT INTO ${tableName} VALUES
+             ${sb.toString()}
+            """
+        qt_select_default """ SELECT * FROM ${tableName} t ORDER BY char_col; 
"""
+
+        File path = new File(jarPath)
+        if (!path.exists()) {
+            throw new IllegalStateException("""${jarPath} doesn't exist! """)
+        }
+
+        sql """DROP FUNCTION IF EXISTS udtf_string_split_outer(string, 
string);"""
+        sql """ CREATE TABLES FUNCTION udtf_string_split(string, string) 
RETURNS array<string> PROPERTIES (
+            "file"="file://${jarPath}",
+            "symbol"="org.apache.doris.udf.UDTFStringTest",
+            "always_nullable"="true",
+            "type"="JAVA_UDF"
+        ); """
+
+        qt_select1 """ SELECT user_id, varchar_col, e1 FROM ${tableName} 
lateral view  udtf_string_split(varchar_col, ",") temp as e1 order by user_id; 
"""
+        
+        sql """DROP FUNCTION IF EXISTS udtf_null_outer(string, string);"""
+        sql """ CREATE TABLES FUNCTION udtf_null(string, string) RETURNS 
array<string> PROPERTIES (
+            "file"="file://${jarPath}",
+            "symbol"="org.apache.doris.udf.UDTFNullTest",
+            "always_nullable"="true",
+            "type"="JAVA_UDF"
+        ); """
+
+        qt_select2 """ SELECT user_id, varchar_col, e1 FROM ${tableName} 
lateral view  udtf_null(varchar_col, ",") temp as e1 order by user_id; """
+
+    } finally {
+        try_sql("DROP FUNCTION IF EXISTS udtf_string_split(string, string);")
+        try_sql("DROP FUNCTION IF EXISTS udtf_null(string, string);")
+        try_sql("DROP TABLE IF EXISTS ${tableName}")
+    }
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]


Reply via email to