This is an automated email from the ASF dual-hosted git repository.

morningman pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-doris.git


The following commit(s) were added to refs/heads/master by this push:
     new c5718928df [feature-wip](array-type) support explode and explode_outer 
table function (#8766)
c5718928df is described below

commit c5718928df231861320b7c9fe56acebdbc6125e0
Author: camby <[email protected]>
AuthorDate: Fri Apr 8 12:11:04 2022 +0800

    [feature-wip](array-type) support explode and explode_outer table function 
(#8766)
    
    explode(ArrayColumn) desc:
    > Create a row for each element in the array column.
    
    explode_outer(ArrayColumn) desc:
    > Create a row for each element in the array column. Unlike explode, if the 
array is null or empty, it returns null.
    
    Usage example:
    1. create a table with array column, and insert some data;
    2. open enable_lateral_view and enable_vectorized_engine;
    ```
    set enable_lateral_view = true;
    set enable_vectorized_engine=true;
    ```
    3. use explode_outer
    ```
    > select * from array_test;
    +------+------+--------+
    | k1   | k2   | k3     |
    +------+------+--------+
    |    3 | NULL | NULL   |
    |    1 |    2 | [1, 2] |
    |    2 |    3 | NULL   |
    |    4 | NULL | []     |
    +------+------+--------+
    
    > select k1,explode_column from array_test LATERAL VIEW explode_outer(k3) 
TempExplodeView as explode_column;
    +------+----------------+
    | k1   | explode_column |
    +------+----------------+
    |    1 |              1 |
    |    1 |              2 |
    |    2 |           NULL |
    |    4 |           NULL |
    |    3 |           NULL |
    +------+----------------+
    ```
    4. explode usage example. explode return empty rows while the ARRAY is null 
or empty
    ```
    > select k1,explode_column from array_test LATERAL VIEW explode(k3) 
TempExplodeView as explode_column;
    +------+----------------+
    | k1   | explode_column |
    +------+----------------+
    |    1 |              1 |
    |    1 |              2 |
    +------+----------------+
    ```
---
 .../exprs/table_function/dummy_table_functions.cpp |  11 +
 .../exprs/table_function/dummy_table_functions.h   |   4 +
 be/src/exprs/table_function/table_function.h       |   5 +
 .../table_function/table_function_factory.cpp      |  14 +-
 be/src/vec/CMakeLists.txt                          |   1 +
 be/src/vec/exec/vtable_function_node.cpp           |  18 +-
 be/src/vec/exec/vtable_function_node.h             |   2 +
 be/src/vec/exprs/table_function/vexplode.cpp       | 114 +++++++++
 .../table_function/vexplode.h}                     |  36 +--
 .../vec/exprs/table_function/vexplode_numbers.cpp  |   2 +-
 be/src/vec/exprs/vexpr.h                           |   2 +
 be/src/vec/functions/function_fake.cpp             |   2 +
 be/src/vec/functions/function_fake.h               |  20 ++
 .../function_fake.cpp => test/exprs/mock_vexpr.h}  |  28 ++-
 be/test/vec/function/CMakeLists.txt                |   1 +
 be/test/vec/function/function_test_util.h          | 269 +++++++++++++++------
 be/test/vec/function/table_function_test.cpp       | 174 +++++++++++++
 .../java/org/apache/doris/analysis/TypeDef.java    |   5 -
 .../java/org/apache/doris/catalog/FunctionSet.java |  28 +++
 19 files changed, 629 insertions(+), 107 deletions(-)

diff --git a/be/src/exprs/table_function/dummy_table_functions.cpp 
b/be/src/exprs/table_function/dummy_table_functions.cpp
index 40923bf1cf..8bbf7a35cb 100644
--- a/be/src/exprs/table_function/dummy_table_functions.cpp
+++ b/be/src/exprs/table_function/dummy_table_functions.cpp
@@ -50,4 +50,15 @@ IntVal 
DummyTableFunctions::explode_numbers(doris_udf::FunctionContext* context,
                                             const doris_udf::IntVal& str) {
     return IntVal();
 }
+
+AnyVal DummyTableFunctions::explode(doris_udf::FunctionContext* context,
+                                    const doris_udf::CollectionVal& value) {
+    return AnyVal();
+}
+
+AnyVal DummyTableFunctions::explode_outer(doris_udf::FunctionContext* context,
+                                          const doris_udf::CollectionVal& 
value) {
+    return AnyVal();
+}
+
 } // namespace doris
diff --git a/be/src/exprs/table_function/dummy_table_functions.h 
b/be/src/exprs/table_function/dummy_table_functions.h
index 9fde65cbe5..3f6f879548 100644
--- a/be/src/exprs/table_function/dummy_table_functions.h
+++ b/be/src/exprs/table_function/dummy_table_functions.h
@@ -45,5 +45,9 @@ public:
                                                           const 
doris_udf::StringVal& str);
     static doris_udf::IntVal explode_numbers(doris_udf::FunctionContext* 
context,
                                              const doris_udf::IntVal& value);
+    static doris_udf::AnyVal explode(doris_udf::FunctionContext* context,
+                                     const doris_udf::CollectionVal& value);
+    static doris_udf::AnyVal explode_outer(doris_udf::FunctionContext* context,
+                                           const doris_udf::CollectionVal& 
value);
 };
 } // namespace doris
diff --git a/be/src/exprs/table_function/table_function.h 
b/be/src/exprs/table_function/table_function.h
index c431d361a7..3c2e188950 100644
--- a/be/src/exprs/table_function/table_function.h
+++ b/be/src/exprs/table_function/table_function.h
@@ -98,6 +98,9 @@ public:
         _vexpr_context = vexpr_context;
     }
 
+    bool is_outer() const { return _is_outer; }
+    bool current_empty() const { return _is_current_empty; }
+
 protected:
     std::string _fn_name;
     ExprContext* _expr_context = nullptr;
@@ -111,6 +114,8 @@ protected:
     int64_t _cur_offset = 0;
     // the size of current result
     int64_t _cur_size = 0;
+    // set _is_outer to false for explode function, and should not return 
tuple while array is null or empty
+    bool _is_outer = true;
 };
 
 } // namespace doris
diff --git a/be/src/exprs/table_function/table_function_factory.cpp 
b/be/src/exprs/table_function/table_function_factory.cpp
index 7dcce8c9c5..e44e3e65ed 100644
--- a/be/src/exprs/table_function/table_function_factory.cpp
+++ b/be/src/exprs/table_function/table_function_factory.cpp
@@ -22,6 +22,7 @@
 #include "exprs/table_function/explode_json_array.h"
 #include "exprs/table_function/explode_split.h"
 #include "exprs/table_function/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"
 #include "vec/exprs/table_function/vexplode_numbers.h"
@@ -48,6 +49,12 @@ struct 
TableFunctionCreator<vectorized::VExplodeJsonArrayTableFunction> {
     }
 };
 
+template <>
+struct TableFunctionCreator<vectorized::VExplodeTableFunction> {
+    bool is_outer;
+    TableFunction* operator()() { return new 
vectorized::VExplodeTableFunction(is_outer); }
+};
+
 inline auto ExplodeJsonArrayIntCreator =
         TableFunctionCreator<ExplodeJsonArrayTableFunction> 
{ExplodeJsonArrayType::INT};
 inline auto ExplodeJsonArrayDoubleCreator =
@@ -65,6 +72,9 @@ inline auto VExplodeJsonArrayStringCreator =
         TableFunctionCreator<vectorized::VExplodeJsonArrayTableFunction> {
                 ExplodeJsonArrayType::STRING};
 
+inline auto VExplodeCreator = 
TableFunctionCreator<vectorized::VExplodeTableFunction> {false};
+inline auto VExplodeOuterCreator = 
TableFunctionCreator<vectorized::VExplodeTableFunction> {true};
+
 //{fn_name,is_vectorized}->table_function_creator
 const std::unordered_map<std::pair<std::string, bool>, 
std::function<TableFunction*()>>
         TableFunctionFactory::_function_map {
@@ -81,7 +91,9 @@ const std::unordered_map<std::pair<std::string, bool>, 
std::function<TableFuncti
                 {{"explode_json_array_double", true}, 
VExplodeJsonArrayDoubleCreator},
                 {{"explode_json_array_string", true}, 
VExplodeJsonArrayStringCreator},
                 {{"explode_bitmap", true},
-                 
TableFunctionCreator<vectorized::VExplodeBitmapTableFunction>()}};
+                 
TableFunctionCreator<vectorized::VExplodeBitmapTableFunction>()},
+                {{"explode", true}, VExplodeCreator},
+                {{"explode_outer", true}, VExplodeOuterCreator}}; // namespace 
doris
 
 Status TableFunctionFactory::get_fn(const std::string& fn_name, bool 
is_vectorized,
                                     ObjectPool* pool, TableFunction** fn) {
diff --git a/be/src/vec/CMakeLists.txt b/be/src/vec/CMakeLists.txt
index 886de1326d..3e789b7365 100644
--- a/be/src/vec/CMakeLists.txt
+++ b/be/src/vec/CMakeLists.txt
@@ -111,6 +111,7 @@ set(VEC_FILES
   exprs/vcast_expr.cpp
   exprs/vcase_expr.cpp
   exprs/vinfo_func.cpp
+  exprs/table_function/vexplode.cpp
   exprs/table_function/vexplode_split.cpp
   exprs/table_function/vexplode_numbers.cpp
   exprs/table_function/vexplode_bitmap.cpp
diff --git a/be/src/vec/exec/vtable_function_node.cpp 
b/be/src/vec/exec/vtable_function_node.cpp
index 30dd0097f6..6b25849f77 100644
--- a/be/src/vec/exec/vtable_function_node.cpp
+++ b/be/src/vec/exec/vtable_function_node.cpp
@@ -92,6 +92,16 @@ Status VTableFunctionNode::get_next(RuntimeState* state, 
Block* block, bool* eos
     return Status::OK();
 }
 
+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 (!_fns[i]->is_outer() && _fns[i]->current_empty()) {
+            return true;
+        }
+    }
+    return false;
+}
+
 Status VTableFunctionNode::get_expanded_block(RuntimeState* state, Block* 
output_block, bool* eos) {
     DCHECK(_child_block != nullptr);
 
@@ -128,9 +138,10 @@ Status 
VTableFunctionNode::get_expanded_block(RuntimeState* state, Block* output
             RETURN_IF_ERROR(_process_next_child_row());
         }
 
+        bool skip_child_row = false;
         while (true) {
             int idx = _find_last_fn_eos_idx();
-            if (idx == 0) {
+            if (idx == 0 || skip_child_row) {
                 // all table functions' results are exhausted, process next 
child row.
                 RETURN_IF_ERROR(_process_next_child_row());
                 if (_cur_child_offset == -1) {
@@ -144,6 +155,11 @@ Status 
VTableFunctionNode::get_expanded_block(RuntimeState* state, Block* output
                 }
             }
 
+            // if any table function is not outer and has empty result, go to 
next child row
+            if ((skip_child_row = _is_inner_and_empty()) == true) {
+                continue;
+            }
+
             // get slots from every table function.
             // notice that _fn_values[i] may be null if the table function has 
empty result set.
             for (int i = 0; i < _fn_num; i++) {
diff --git a/be/src/vec/exec/vtable_function_node.h 
b/be/src/vec/exec/vtable_function_node.h
index 1913cd1d35..6192e52ff6 100644
--- a/be/src/vec/exec/vtable_function_node.h
+++ b/be/src/vec/exec/vtable_function_node.h
@@ -37,6 +37,8 @@ private:
 
     Status get_expanded_block(RuntimeState* state, Block* output_block, bool* 
eos);
 
+    bool _is_inner_and_empty();
+
     std::unique_ptr<Block> _child_block;
     std::vector<SlotDescriptor*> _child_slots;
     std::vector<SlotDescriptor*> _output_slots;
diff --git a/be/src/vec/exprs/table_function/vexplode.cpp 
b/be/src/vec/exprs/table_function/vexplode.cpp
new file mode 100644
index 0000000000..58f8e7f2e0
--- /dev/null
+++ b/be/src/vec/exprs/table_function/vexplode.cpp
@@ -0,0 +1,114 @@
+// 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/vexplode.h"
+
+#include "vec/exprs/vexpr.h"
+
+namespace doris::vectorized {
+
+VExplodeTableFunction::VExplodeTableFunction(bool is_outer) {
+    _is_outer = is_outer;
+    if (_is_outer) {
+        _fn_name = "vexplode_outer";
+    } else {
+        _fn_name = "vexplode";
+    }
+}
+
+Status VExplodeTableFunction::process_init(vectorized::Block* block) {
+    CHECK(_vexpr_context->root()->children().size() == 1)
+            << "VExplodeTableFunction must be have 1 children but have "
+            << _vexpr_context->root()->children().size();
+
+    int value_column_idx = -1;
+    _vexpr_context->root()->children()[0]->execute(_vexpr_context, block, 
&value_column_idx);
+
+    if (block->get_by_position(value_column_idx).column->is_nullable()) {
+        auto array_nullable_column = check_and_get_column<ColumnNullable>(
+                *block->get_by_position(value_column_idx).column);
+        _array_null_map = 
array_nullable_column->get_null_map_column().get_data().data();
+        _array_column =
+                
check_and_get_column<ColumnArray>(array_nullable_column->get_nested_column_ptr());
+    } else {
+        _array_null_map = nullptr;
+        _array_column =
+                
check_and_get_column<ColumnArray>(*block->get_by_position(value_column_idx).column);
+    }
+    if (!_array_column) {
+        return Status::NotSupported("column type " +
+                                    
block->get_by_position(value_column_idx).column->get_name() +
+                                    " not supported now");
+    }
+
+    return Status::OK();
+}
+
+Status VExplodeTableFunction::process_row(size_t row_idx) {
+    DCHECK(row_idx < _array_column->size());
+    _is_current_empty = false;
+    _eos = false;
+
+    if (_array_null_map && _array_null_map[row_idx]) {
+        _is_current_empty = true;
+        _cur_size = 0;
+        _cur_offset = 0;
+        _pos = 0;
+    } else {
+        _cur_size =
+                _array_column->get_offsets()[row_idx] - 
_array_column->get_offsets()[row_idx - 1];
+        _cur_offset = 0;
+        _is_current_empty = (_cur_size == 0);
+        _pos = _array_column->get_offsets()[row_idx - 1];
+    }
+    return Status::OK();
+}
+
+Status VExplodeTableFunction::process_close() {
+    _array_column = nullptr;
+    _array_null_map = nullptr;
+    _pos = 0;
+    return Status::OK();
+}
+
+Status VExplodeTableFunction::reset() {
+    _eos = false;
+    _cur_offset = 0;
+    return Status::OK();
+}
+
+Status VExplodeTableFunction::get_value(void** output) {
+    if (_is_current_empty) {
+        *output = nullptr;
+        return Status::OK();
+    }
+
+    *output = const_cast<char*>(_array_column->get_data().get_data_at(_pos + 
_cur_offset).data);
+    return Status::OK();
+}
+
+Status VExplodeTableFunction::get_value_length(int64_t* length) {
+    if (_is_current_empty) {
+        *length = -1;
+        return Status::OK();
+    }
+
+    *length = _array_column->get_data().get_data_at(_pos + _cur_offset).size;
+    return Status::OK();
+}
+
+} // namespace doris::vectorized
diff --git a/be/src/vec/exec/vtable_function_node.h 
b/be/src/vec/exprs/table_function/vexplode.h
similarity index 52%
copy from be/src/vec/exec/vtable_function_node.h
copy to be/src/vec/exprs/table_function/vexplode.h
index 1913cd1d35..b7ee993c0e 100644
--- a/be/src/vec/exec/vtable_function_node.h
+++ b/be/src/vec/exprs/table_function/vexplode.h
@@ -17,29 +17,31 @@
 
 #pragma once
 
-#include "exec/table_function_node.h"
+#include "exprs/table_function/table_function.h"
+#include "vec/common/string_ref.h"
+#include "vec/columns/column.h"
+#include "vec/columns/column_array.h"
+#include "vec/columns/column_nullable.h"
 
 namespace doris::vectorized {
 
-class VTableFunctionNode : public TableFunctionNode {
+class VExplodeTableFunction : public TableFunction {
 public:
-    VTableFunctionNode(ObjectPool* pool, const TPlanNode& tnode, const 
DescriptorTbl& descs);
-    ~VTableFunctionNode() override = default;
+    VExplodeTableFunction(bool is_outer);
 
-    Status init(const TPlanNode& tnode, RuntimeState* state = nullptr) 
override;
-    Status prepare(RuntimeState* state) override;
-    Status get_next(RuntimeState* state, Block* block, bool* eos) override;
+    virtual ~VExplodeTableFunction() = default;
 
-private:
-    Status _process_next_child_row() override;
-
-    using TableFunctionNode::get_next;
+    virtual Status process_init(vectorized::Block* block) override;
+    virtual Status process_row(size_t row_idx) override;
+    virtual Status process_close() override;
+    virtual Status reset() override;
+    virtual Status get_value(void** output) override;
+    virtual Status get_value_length(int64_t* length) override;
 
-    Status get_expanded_block(RuntimeState* state, Block* output_block, bool* 
eos);
-
-    std::unique_ptr<Block> _child_block;
-    std::vector<SlotDescriptor*> _child_slots;
-    std::vector<SlotDescriptor*> _output_slots;
+private:
+    const UInt8* _array_null_map;
+    const ColumnArray* _array_column;
+    size_t _pos;
 };
 
-} // namespace doris::vectorized
\ No newline at end of file
+} // namespace doris::vectorized
diff --git a/be/src/vec/exprs/table_function/vexplode_numbers.cpp 
b/be/src/vec/exprs/table_function/vexplode_numbers.cpp
index 540c7ac5df..c5b6629c06 100644
--- a/be/src/vec/exprs/table_function/vexplode_numbers.cpp
+++ b/be/src/vec/exprs/table_function/vexplode_numbers.cpp
@@ -50,7 +50,7 @@ Status VExplodeNumbersTableFunction::process_row(size_t 
row_idx) {
     } else {
         _cur_size = *reinterpret_cast<const int*>(value.data);
         _cur_offset = 0;
-        _is_current_empty = (_cur_size == 0);
+        _is_current_empty = (_cur_size <= 0);
     }
     return Status::OK();
 }
diff --git a/be/src/vec/exprs/vexpr.h b/be/src/vec/exprs/vexpr.h
index 1a99aee464..26681baf91 100644
--- a/be/src/vec/exprs/vexpr.h
+++ b/be/src/vec/exprs/vexpr.h
@@ -35,6 +35,8 @@ class VExpr {
 public:
     VExpr(const TExprNode& node);
     VExpr(const TypeDescriptor& type, bool is_slotref, bool is_nullable);
+    // only used for test
+    VExpr() {}
     virtual ~VExpr() = default;
 
     virtual VExpr* clone(ObjectPool* pool) const = 0;
diff --git a/be/src/vec/functions/function_fake.cpp 
b/be/src/vec/functions/function_fake.cpp
index 9deacb2afa..b661960517 100644
--- a/be/src/vec/functions/function_fake.cpp
+++ b/be/src/vec/functions/function_fake.cpp
@@ -27,6 +27,8 @@ void register_function_fake(SimpleFunctionFactory& factory) {
     factory.register_function<FunctionFake<FunctionExplodeJsonArrayIntImpl>>();
     
factory.register_function<FunctionFake<FunctionExplodeJsonArrayStringImpl>>();
     factory.register_function<FunctionFake<FunctionExplodeBitmapImpl>>();
+    factory.register_function<FunctionFake<FunctionExplodeImpl>>();
+    factory.register_function<FunctionFake<FunctionExplodeOuterImpl>>();
 }
 
 } // namespace doris::vectorized
diff --git a/be/src/vec/functions/function_fake.h 
b/be/src/vec/functions/function_fake.h
index c7e536d6ea..56648d8877 100644
--- a/be/src/vec/functions/function_fake.h
+++ b/be/src/vec/functions/function_fake.h
@@ -19,8 +19,10 @@
 
 #include "common/status.h"
 #include "vec/core/types.h"
+#include "vec/data_types/data_type_array.h"
 #include "vec/data_types/data_type_number.h"
 #include "vec/data_types/data_type_string.h"
+#include "vec/functions/function_helpers.h"
 #include "vec/functions/simple_function_factory.h"
 #include "vec/utils/util.hpp"
 
@@ -75,6 +77,24 @@ struct FunctionExplodeBitmapImpl {
     }
 };
 
+struct FunctionExplodeImpl {
+    static constexpr auto name = "explode";
+    static DataTypePtr get_return_type_impl(const DataTypes& arguments) {
+        DCHECK(is_array(arguments[0])) << arguments[0]->get_name() << " not 
supported";
+        return make_nullable(
+                
check_and_get_data_type<DataTypeArray>(arguments[0].get())->get_nested_type());
+    }
+};
+
+struct FunctionExplodeOuterImpl {
+    static constexpr auto name = "explode_outer";
+    static DataTypePtr get_return_type_impl(const DataTypes& arguments) {
+        DCHECK(is_array(arguments[0])) << arguments[0]->get_name() << " not 
supported";
+        return make_nullable(
+                
check_and_get_data_type<DataTypeArray>(arguments[0].get())->get_nested_type());
+    }
+};
+
 //FunctionFake is use for some function call expr only work at prepare/open 
phase, do not support execute().
 template <typename Impl>
 class FunctionFake : public IFunction {
diff --git a/be/src/vec/functions/function_fake.cpp b/be/test/exprs/mock_vexpr.h
similarity index 53%
copy from be/src/vec/functions/function_fake.cpp
copy to be/test/exprs/mock_vexpr.h
index 9deacb2afa..4da44ef3b6 100644
--- a/be/src/vec/functions/function_fake.cpp
+++ b/be/test/exprs/mock_vexpr.h
@@ -15,18 +15,22 @@
 // specific language governing permissions and limitations
 // under the License.
 
-#include "vec/functions/function_fake.h"
+#pragma once
 
-namespace doris::vectorized {
+#include <gtest/gtest.h>
+#include <gmock/gmock.h>
 
-void register_function_fake(SimpleFunctionFactory& factory) {
-    factory.register_function<FunctionFake<FunctionEsqueryImpl>>();
-    factory.register_function<FunctionFake<FunctionExplodeSplitImpl>>();
-    factory.register_function<FunctionFake<FunctionExplodeNumbersImpl>>();
-    
factory.register_function<FunctionFake<FunctionExplodeJsonArrayDoubleImpl>>();
-    factory.register_function<FunctionFake<FunctionExplodeJsonArrayIntImpl>>();
-    
factory.register_function<FunctionFake<FunctionExplodeJsonArrayStringImpl>>();
-    factory.register_function<FunctionFake<FunctionExplodeBitmapImpl>>();
-}
+#include "vec/exprs/vexpr.h"
 
-} // namespace doris::vectorized
+namespace doris {
+namespace vectorized {
+
+class MockVExpr : public VExpr {
+public:
+    MOCK_CONST_METHOD1(clone, VExpr*(ObjectPool* pool));
+    MOCK_CONST_METHOD0(expr_name, const std::string&());
+    MOCK_METHOD3(execute, Status(VExprContext* context, vectorized::Block* 
block, int* result_column_id));
+}; // class MockVExpr
+
+} // namespace vectorized
+} // namespace doris
diff --git a/be/test/vec/function/CMakeLists.txt 
b/be/test/vec/function/CMakeLists.txt
index 116030e984..4a68ad1231 100644
--- a/be/test/vec/function/CMakeLists.txt
+++ b/be/test/vec/function/CMakeLists.txt
@@ -32,3 +32,4 @@ ADD_BE_TEST(function_like_test)
 ADD_BE_TEST(function_arithmetic_test)
 ADD_BE_TEST(function_json_test)
 ADD_BE_TEST(function_geo_test)
+ADD_BE_TEST(table_function_test)
diff --git a/be/test/vec/function/function_test_util.h 
b/be/test/vec/function/function_test_util.h
index c345354fbf..16b0aac4be 100644
--- a/be/test/vec/function/function_test_util.h
+++ b/be/test/vec/function/function_test_util.h
@@ -23,6 +23,7 @@
 #include <string>
 
 #include "exec/schema_scanner.h"
+#include "exprs/table_function/table_function.h"
 #include "runtime/row_batch.h"
 #include "runtime/tuple_row.h"
 #include "testutil/function_utils.h"
@@ -37,6 +38,7 @@
 
 namespace doris::vectorized {
 
+using InputDataSet = std::vector<std::vector<std::any>>; // without result
 using DataSet = std::vector<std::pair<std::vector<std::any>, std::any>>;
 using InputTypeSet = std::vector<std::any>;
 
@@ -80,10 +82,9 @@ using UTDataTypeDescs = std::vector<UTDataTypeDesc>;
 
 } // namespace ut_type
 
-size_t type_index_to_data_type(const std::vector<std::any>& input_types, 
size_t index,
-                               doris_udf::FunctionContext::TypeDesc& desc,
-                               DataTypePtr& type) {
-    if(index < 0 || index >= input_types.size()) {
+size_t type_index_to_data_type(const InputTypeSet& input_types, size_t index,
+                               doris_udf::FunctionContext::TypeDesc& desc, 
DataTypePtr& type) {
+    if (index < 0 || index >= input_types.size()) {
         return -1;
     }
 
@@ -156,10 +157,11 @@ size_t type_index_to_data_type(const 
std::vector<std::any>& input_types, size_t
         return 0;
     }
 }
-bool parse_ut_data_type(const std::vector<std::any>& input_types, 
ut_type::UTDataTypeDescs& descs) {
+
+bool parse_ut_data_type(const InputTypeSet& input_types, 
ut_type::UTDataTypeDescs& descs) {
     descs.clear();
     descs.reserve(input_types.size());
-    for (size_t i = 0; i < input_types.size(); ) {
+    for (size_t i = 0; i < input_types.size();) {
         ut_type::UTDataTypeDesc desc;
         if (input_types[i].type() == typeid(Consted)) {
             desc.is_const = true;
@@ -178,11 +180,193 @@ bool parse_ut_data_type(const std::vector<std::any>& 
input_types, ut_type::UTDat
     return true;
 }
 
+bool insert_cell(MutableColumnPtr& column, DataTypePtr type_ptr, const 
std::any& cell) {
+    if (cell.type() == typeid(Null)) {
+        column->insert_data(nullptr, 0);
+        return true;
+    }
+
+    WhichDataType type(type_ptr);
+    if (type.is_string()) {
+        auto str = std::any_cast<ut_type::STRING>(cell);
+        column->insert_data(str.c_str(), str.size());
+    } else if (type.idx == TypeIndex::BitMap) {
+        BitmapValue* bitmap = std::any_cast<BitmapValue*>(cell);
+        column->insert_data((char*)bitmap, sizeof(BitmapValue));
+    } else if (type.is_int8()) {
+        auto value = std::any_cast<ut_type::TINYINT>(cell);
+        column->insert_data(reinterpret_cast<char*>(&value), 0);
+    } else if (type.is_int16()) {
+        auto value = std::any_cast<ut_type::SMALLINT>(cell);
+        column->insert_data(reinterpret_cast<char*>(&value), 0);
+    } else if (type.is_int32()) {
+        auto value = std::any_cast<ut_type::INT>(cell);
+        column->insert_data(reinterpret_cast<char*>(&value), 0);
+    } else if (type.is_int64()) {
+        auto value = std::any_cast<ut_type::BIGINT>(cell);
+        column->insert_data(reinterpret_cast<char*>(&value), 0);
+    } else if (type.is_int128()) {
+        auto value = std::any_cast<ut_type::LARGEINT>(cell);
+        column->insert_data(reinterpret_cast<char*>(&value), 0);
+    } else if (type.is_float64()) {
+        auto value = std::any_cast<ut_type::DOUBLE>(cell);
+        column->insert_data(reinterpret_cast<char*>(&value), 0);
+    } else if (type.is_float64()) {
+        auto value = std::any_cast<ut_type::DOUBLE>(cell);
+        column->insert_data(reinterpret_cast<char*>(&value), 0);
+    } else if (type.is_decimal128()) {
+        auto value = std::any_cast<Decimal<Int128>>(cell);
+        column->insert_data(reinterpret_cast<char*>(&value), 0);
+    } else if (type.is_date_time()) {
+        static std::string date_time_format("%Y-%m-%d %H:%i:%s");
+        auto datetime_str = std::any_cast<std::string>(cell);
+        VecDateTimeValue v;
+        v.from_date_format_str(date_time_format.c_str(), 
date_time_format.size(),
+                               datetime_str.c_str(), datetime_str.size());
+        v.to_datetime();
+        column->insert_data(reinterpret_cast<char*>(&v), 0);
+    } else if (type.is_date()) {
+        static std::string date_time_format("%Y-%m-%d");
+        auto datetime_str = std::any_cast<std::string>(cell);
+        VecDateTimeValue v;
+        v.from_date_format_str(date_time_format.c_str(), 
date_time_format.size(),
+                               datetime_str.c_str(), datetime_str.size());
+        v.cast_to_date();
+        column->insert_data(reinterpret_cast<char*>(&v), 0);
+    } else if (type.is_array()) {
+        auto v = std::any_cast<Array>(cell);
+        column->insert(v);
+    } else {
+        LOG(WARNING) << "dataset not supported for TypeIndex:" << 
(int)type.idx;
+        return false;
+    }
+    return true;
+}
+
+Block* create_block_from_inputset(const InputTypeSet& input_types, const 
InputDataSet& input_set) {
+    // 1.0 create data type
+    ut_type::UTDataTypeDescs descs;
+    if (!parse_ut_data_type(input_types, descs)) {
+        return nullptr;
+    }
+
+    // 1.1 insert data and create block
+    auto row_size = input_set.size();
+    std::unique_ptr<Block> block(new Block());
+    for (size_t i = 0; i < descs.size(); ++i) {
+        auto& desc = descs[i];
+        auto column = desc.data_type->create_column();
+        column->reserve(row_size);
+
+        auto type_ptr = desc.data_type->is_nullable()
+                                ? 
((DataTypeNullable*)(desc.data_type.get()))->get_nested_type()
+                                : desc.data_type;
+        WhichDataType type(type_ptr);
+
+        for (int j = 0; j < row_size; j++) {
+            if (!insert_cell(column, type_ptr, input_set[j][i])) {
+                return nullptr;
+            }
+        }
+
+        if (desc.is_const) {
+            column = ColumnConst::create(std::move(column), row_size);
+        }
+        block->insert({std::move(column), desc.data_type, desc.col_name});
+    }
+    return block.release();
+}
+
+Block* process_table_function(TableFunction* fn, Block* input_block,
+                              const InputTypeSet& output_types) {
+    // pasrse output data types
+    ut_type::UTDataTypeDescs descs;
+    if (!parse_ut_data_type(output_types, descs)) {
+        return nullptr;
+    }
+    if (descs.size() != 1) {
+        LOG(WARNING) << "Now table function test only support return one 
column";
+        return nullptr;
+    }
+
+    // process table function init
+    if (fn->process_init(input_block) != Status::OK()) {
+        LOG(WARNING) << "TableFunction process_init failed";
+        return nullptr;
+    }
+
+    // prepare output column
+    vectorized::MutableColumnPtr column = descs[0].data_type->create_column();
+
+    // process table function for all rows
+    for (size_t row = 0; row < input_block->rows(); ++row) {
+        if (fn->process_row(row) != Status::OK()) {
+            LOG(WARNING) << "TableFunction process_row failed";
+            return nullptr;
+        }
+
+        // consider outer
+        if (!fn->is_outer() && fn->current_empty()) {
+            continue;
+        }
+
+        bool tmp_eos = false;
+        do {
+            void* cell = nullptr;
+            int64_t cell_len = 0;
+            if (fn->get_value(&cell) != Status::OK() ||
+                fn->get_value_length(&cell_len) != Status::OK()) {
+                LOG(WARNING) << "TableFunction get_value or get_value_length 
failed";
+                return nullptr;
+            }
+
+            // copy data from input block
+            if (cell == nullptr) {
+                column->insert_default();
+            } else {
+                column->insert_data(reinterpret_cast<char*>(cell), cell_len);
+            }
+
+            fn->forward(&tmp_eos);
+        } while (!tmp_eos);
+    }
+
+    std::unique_ptr<Block> output_block(new Block());
+    output_block->insert({std::move(column), descs[0].data_type, 
descs[0].col_name});
+    return output_block.release();
+}
+
+void check_vec_table_function(TableFunction* fn, const InputTypeSet& 
input_types,
+                              const InputDataSet& input_set, const 
InputTypeSet& output_types,
+                              const InputDataSet& output_set) {
+    std::unique_ptr<Block> input_block(create_block_from_inputset(input_types, 
input_set));
+    ASSERT_TRUE(input_block != nullptr);
+
+    std::unique_ptr<Block> expect_output_block(
+            create_block_from_inputset(output_types, output_set));
+    ASSERT_TRUE(expect_output_block != nullptr);
+
+    std::unique_ptr<Block> real_output_block(
+            process_table_function(fn, input_block.get(), output_types));
+    ASSERT_TRUE(real_output_block != nullptr);
+
+    // compare real_output_block with expect_output_block
+    ASSERT_EQ(expect_output_block->columns(), real_output_block->columns());
+    ASSERT_EQ(expect_output_block->rows(), real_output_block->rows());
+    for (size_t col = 0; col < expect_output_block->columns(); ++col) {
+        auto left_col = expect_output_block->get_by_position(col).column;
+        auto right_col = real_output_block->get_by_position(col).column;
+        for (size_t row = 0; row < expect_output_block->rows(); ++row) {
+            ASSERT_EQ(left_col->compare_at(row, row, *right_col, 0), 0);
+        }
+    }
+}
+
 // Null values are represented by Null()
 // The type of the constant column is represented as follows: Consted 
{TypeIndex::String}
 // A DataSet with a constant column can only have one row of data
 template <typename ReturnType, bool nullable = false>
-void check_function(const std::string& func_name, const std::vector<std::any>& 
input_types,
+void check_function(const std::string& func_name, const InputTypeSet& 
input_types,
                     const DataSet& data_set) {
     // 1.0 create data type
     ut_type::UTDataTypeDescs descs;
@@ -196,69 +380,12 @@ void check_function(const std::string& func_name, const 
std::vector<std::any>& i
         auto column = desc.data_type->create_column();
         column->reserve(row_size);
 
-        auto type_ptr = desc.data_type->is_nullable() ?
-             ((DataTypeNullable*)(desc.data_type.get()))->get_nested_type() : 
desc.data_type;
-        WhichDataType type(type_ptr);
+        auto type_ptr = desc.data_type->is_nullable()
+                                ? 
((DataTypeNullable*)(desc.data_type.get()))->get_nested_type()
+                                : desc.data_type;
 
         for (int j = 0; j < row_size; j++) {
-            if (data_set[j].first[i].type() == typeid(Null)) {
-                column->insert_data(nullptr, 0);
-                continue;
-            }
-
-            if (type.is_string()) {
-                auto str = 
std::any_cast<ut_type::STRING>(data_set[j].first[i]);
-                column->insert_data(str.c_str(), str.size());
-            }  else if (type.idx == TypeIndex::BitMap) {
-                BitmapValue* bitmap = 
std::any_cast<BitmapValue*>(data_set[j].first[i]);
-                column->insert_data((char*)bitmap, sizeof(BitmapValue));
-            } else if (type.is_int8()) {
-                auto value = 
std::any_cast<ut_type::TINYINT>(data_set[j].first[i]);
-                column->insert_data(reinterpret_cast<char*>(&value), 0);
-            } else if (type.is_int16()) {
-                auto value = 
std::any_cast<ut_type::SMALLINT>(data_set[j].first[i]);
-                column->insert_data(reinterpret_cast<char*>(&value), 0);
-            } else if (type.is_int32()) {
-                auto value = std::any_cast<ut_type::INT>(data_set[j].first[i]);
-                column->insert_data(reinterpret_cast<char*>(&value), 0);
-            } else if (type.is_int64()) {
-                auto value = 
std::any_cast<ut_type::BIGINT>(data_set[j].first[i]);
-                column->insert_data(reinterpret_cast<char*>(&value), 0);
-            } else if (type.is_int128()) {
-                auto value = 
std::any_cast<ut_type::LARGEINT>(data_set[j].first[i]);
-                column->insert_data(reinterpret_cast<char*>(&value), 0);
-            } else if (type.is_float64()) {
-                auto value = 
std::any_cast<ut_type::DOUBLE>(data_set[j].first[i]);
-                column->insert_data(reinterpret_cast<char*>(&value), 0);
-            } else if (type.is_float64()) {
-                auto value = 
std::any_cast<ut_type::DOUBLE>(data_set[j].first[i]);
-                column->insert_data(reinterpret_cast<char*>(&value), 0);
-            } else if (type.is_decimal128()) {
-                auto value = 
std::any_cast<Decimal<Int128>>(data_set[j].first[i]);
-                column->insert_data(reinterpret_cast<char*>(&value), 0);
-            } else if (type.is_date_time()) {
-                static std::string date_time_format("%Y-%m-%d %H:%i:%s");
-                auto datetime_str = 
std::any_cast<std::string>(data_set[j].first[i]);
-                VecDateTimeValue v;
-                v.from_date_format_str(date_time_format.c_str(), 
date_time_format.size(),
-                                       datetime_str.c_str(), 
datetime_str.size());
-                v.to_datetime();
-                column->insert_data(reinterpret_cast<char*>(&v), 0);
-            } else if (type.is_date()) {
-                static std::string date_time_format("%Y-%m-%d");
-                auto datetime_str = 
std::any_cast<std::string>(data_set[j].first[i]);
-                VecDateTimeValue v;
-                v.from_date_format_str(date_time_format.c_str(), 
date_time_format.size(),
-                                       datetime_str.c_str(), 
datetime_str.size());
-                v.cast_to_date();
-                column->insert_data(reinterpret_cast<char*>(&v), 0);
-            } else if (type.is_array()) {
-                auto v = std::any_cast<Array>(data_set[j].first[i]);
-                column->insert(v);
-            } else {
-                LOG(WARNING) << "dataset not supported for TypeIndex:" << 
(int)type.idx;
-                ASSERT_TRUE(false);
-            }
+            ASSERT_TRUE(insert_cell(column, type_ptr, data_set[j].first[i]));
         }
 
         if (desc.is_const) {
@@ -277,7 +404,8 @@ void check_function(const std::string& func_name, const 
std::vector<std::any>& i
         arguments.push_back(i);
         arg_types.push_back(desc.type_desc);
         if (desc.is_const) {
-            
constant_col_ptrs.push_back(std::make_shared<ColumnPtrWrapper>(block.get_by_position(i).column));
+            constant_col_ptrs.push_back(
+                    
std::make_shared<ColumnPtrWrapper>(block.get_by_position(i).column));
             constant_cols.push_back(constant_col_ptrs.back().get());
         } else {
             constant_cols.push_back(nullptr);
@@ -287,7 +415,8 @@ void check_function(const std::string& func_name, const 
std::vector<std::any>& i
     // 2. execute function
     auto return_type = nullable ? make_nullable(std::make_shared<ReturnType>())
                                 : std::make_shared<ReturnType>();
-    auto func = SimpleFunctionFactory::instance().get_function(func_name, 
block.get_columns_with_type_and_name(), return_type);
+    auto func = SimpleFunctionFactory::instance().get_function(
+            func_name, block.get_columns_with_type_and_name(), return_type);
     ASSERT_TRUE(func != nullptr);
 
     doris_udf::FunctionContext::TypeDesc fn_ctx_return;
diff --git a/be/test/vec/function/table_function_test.cpp 
b/be/test/vec/function/table_function_test.cpp
new file mode 100644
index 0000000000..8404892480
--- /dev/null
+++ b/be/test/vec/function/table_function_test.cpp
@@ -0,0 +1,174 @@
+// 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 "exprs/mock_vexpr.h"
+#include "vec/exprs/table_function/vexplode.h"
+#include "vec/exprs/table_function/vexplode_numbers.h"
+#include "vec/exprs/table_function/vexplode_split.h"
+#include "vec/exprs/vexpr_context.h"
+#include "vec/function/function_test_util.h"
+
+namespace doris::vectorized {
+
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::Return;
+using ::testing::SetArgPointee;
+
+class TableFunctionTest : public testing::Test {
+protected:
+    virtual void SetUp() {}
+    virtual void TearDown() {}
+
+    void clear() {
+        _ctx = nullptr;
+        _root = nullptr;
+        _children.clear();
+        _column_ids.clear();
+    }
+
+    void init_expr_context(int child_num) {
+        clear();
+
+        _root = std::make_unique<MockVExpr>();
+        for (int i = 0; i < child_num; ++i) {
+            _column_ids.push_back(i);
+            _children.push_back(std::make_unique<MockVExpr>());
+            EXPECT_CALL(*_children[i], execute(_, _, _))
+                    .WillRepeatedly(DoAll(SetArgPointee<2>(_column_ids[i]), 
Return(Status::OK())));
+            _root->add_child(_children[i].get());
+        }
+        _ctx = std::make_unique<VExprContext>(_root.get());
+    }
+
+private:
+    std::unique_ptr<VExprContext> _ctx;
+    std::unique_ptr<MockVExpr> _root;
+    std::vector<std::unique_ptr<MockVExpr>> _children;
+    std::vector<int> _column_ids;
+};
+
+TEST_F(TableFunctionTest, vexplode_outer) {
+    init_expr_context(1);
+    VExplodeTableFunction explode_outer(true);
+    explode_outer.set_vexpr_context(_ctx.get());
+
+    // explode_outer(Array<Int32>)
+    {
+        InputTypeSet input_types = {TypeIndex::Array, TypeIndex::Int32};
+        Array vec = {Int32(1), Int32(2), Int32(3)};
+        InputDataSet input_set = {{vec}, {Null()}, {Array()}};
+
+        InputTypeSet output_types = {TypeIndex::Int32};
+        InputDataSet output_set = {{Int32(1)}, {Int32(2)}, {Int32(3)}, 
{Null()}, {Null()}};
+
+        check_vec_table_function(&explode_outer, input_types, input_set, 
output_types, output_set);
+    }
+
+    // explode_outer(Array<String>)
+    {
+        InputTypeSet input_types = {TypeIndex::Array, TypeIndex::String};
+        Array vec = {std::string("abc"), std::string(""), std::string("def")};
+        InputDataSet input_set = {{Null()}, {Array()}, {vec}};
+
+        InputTypeSet output_types = {TypeIndex::String};
+        InputDataSet output_set = {
+                {Null()}, {Null()}, {std::string("abc")}, {std::string("")}, 
{std::string("def")}};
+
+        check_vec_table_function(&explode_outer, input_types, input_set, 
output_types, output_set);
+    }
+}
+
+TEST_F(TableFunctionTest, vexplode) {
+    init_expr_context(1);
+    VExplodeTableFunction explode(false);
+    explode.set_vexpr_context(_ctx.get());
+
+    // explode(Array<Int32>)
+    {
+        InputTypeSet input_types = {TypeIndex::Array, TypeIndex::Int32};
+
+        Array vec = {Int32(1), Int32(2), Int32(3)};
+        InputDataSet input_set = {{vec}, {Null()}, {Array()}};
+
+        InputTypeSet output_types = {TypeIndex::Int32};
+        InputDataSet output_set = {{Int32(1)}, {Int32(2)}, {Int32(3)}};
+
+        check_vec_table_function(&explode, input_types, input_set, 
output_types, output_set);
+    }
+
+    // explode(Array<String>)
+    {
+        InputTypeSet input_types = {TypeIndex::Array, TypeIndex::String};
+        Array vec = {std::string("abc"), std::string(""), std::string("def")};
+        InputDataSet input_set = {{Null()}, {Array()}, {vec}};
+
+        InputTypeSet output_types = {TypeIndex::String};
+        InputDataSet output_set = {{std::string("abc")}, {std::string("")}, 
{std::string("def")}};
+
+        check_vec_table_function(&explode, input_types, input_set, 
output_types, output_set);
+    }
+}
+
+TEST_F(TableFunctionTest, vexplode_numbers) {
+    init_expr_context(1);
+    VExplodeNumbersTableFunction tfn;
+    tfn.set_vexpr_context(_ctx.get());
+
+    {
+        InputTypeSet input_types = {TypeIndex::Int32};
+        InputDataSet input_set = {{Int32(2)}, {Int32(3)}, {Null()}, 
{Int32(0)}, {Int32(-2)}};
+
+        InputTypeSet output_types = {TypeIndex::Int32};
+        InputDataSet output_set = {{Int32(0)}, {Int32(1)}, {Int32(0)}, 
{Int32(1)},
+                                   {Int32(2)}, {Null()},   {Null()},   
{Null()}};
+
+        check_vec_table_function(&tfn, input_types, input_set, output_types, 
output_set);
+    }
+}
+
+TEST_F(TableFunctionTest, vexplode_split) {
+    init_expr_context(2);
+    VExplodeSplitTableFunction tfn;
+    tfn.set_vexpr_context(_ctx.get());
+
+    {
+        // Case 1: explode_split(null) --- null
+        // Case 2: explode_split("a,b,c", ",") --> ["a", "b", "c"]
+        // Case 3: explode_split("a,b,c", "a,")) --> ["", "b,c"]
+        // Case 4: explode_split("", ",")) --> [""]
+        InputTypeSet input_types = {TypeIndex::String, TypeIndex::String};
+        InputDataSet input_set = {{Null(), Null()},
+                                  {std::string("a,b,c"), std::string(",")},
+                                  {std::string("a,b,c"), std::string("a,")},
+                                  {std::string(""), std::string(",")}};
+
+        InputTypeSet output_types = {TypeIndex::String};
+        InputDataSet output_set = {{Null()},           {std::string("a")}, 
{std::string("b")},
+                                   {std::string("c")}, {std::string("")},  
{std::string("b,c")},
+                                   {std::string("")}};
+
+        check_vec_table_function(&tfn, input_types, input_set, output_types, 
output_set);
+    }
+}
+
+} // namespace doris::vectorized
+
+int main(int argc, char** argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+    return RUN_ALL_TESTS();
+}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/TypeDef.java 
b/fe/fe-core/src/main/java/org/apache/doris/analysis/TypeDef.java
index d81d63357f..37c8465d20 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/analysis/TypeDef.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/TypeDef.java
@@ -82,11 +82,6 @@ public class TypeDef implements ParseNode {
       analyzeScalarType((ScalarType) type);
     }
 
-    if (type.isArrayType()) {
-      Type itemType = ((ArrayType) type).getItemType();
-      analyze(itemType);
-    }
-
     if (type.isComplexType()) {
       if (!Config.enable_complex_type_support) {
         throw new AnalysisException("Unsupported data type: " + type.toSql());
diff --git a/fe/fe-core/src/main/java/org/apache/doris/catalog/FunctionSet.java 
b/fe/fe-core/src/main/java/org/apache/doris/catalog/FunctionSet.java
index 0bbc83ffd9..b705151d79 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/catalog/FunctionSet.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/FunctionSet.java
@@ -2370,6 +2370,8 @@ public class 
FunctionSet<min_initIN9doris_udf12DecimalV2ValEEEvPNS2_15FunctionCo
     public static final String EXPLODE_JSON_ARRAY_DOUBLE = 
"explode_json_array_double";
     public static final String EXPLODE_JSON_ARRAY_STRING = 
"explode_json_array_string";
     public static final String EXPLODE_NUMBERS = "explode_numbers";
+    public static final String EXPLODE = "explode";
+    public static final String EXPLODE_OUTER = "explode_outer";
 
     private void initTableFunction() {
         List<Function> explodeSplits = Lists.newArrayList();
@@ -2419,5 +2421,31 @@ public class 
FunctionSet<min_initIN9doris_udf12DecimalV2ValEEEvPNS2_15FunctionCo
                 
"_ZN5doris19DummyTableFunctions22explode_numbersEPN9doris_udf15FunctionContextERKNS1_9IntValE",
                 null, null, true));
         tableFunctions.put(EXPLODE_NUMBERS, explodeNumbers);
+
+        List<Function> explodes = Lists.newArrayList();
+        explodes.add(ScalarFunction.createBuiltin(
+                EXPLODE, Type.INT, Function.NullableMode.ALWAYS_NULLABLE,
+                Lists.newArrayList(new ArrayType(Type.INT)), false,
+                
"_ZN5doris19DummyTableFunctions7explodeEPN9doris_udf15FunctionContextERKNS1_13CollectionValE",
+                null, null, true));
+        explodes.add(ScalarFunction.createBuiltin(
+                EXPLODE, Type.VARCHAR, Function.NullableMode.ALWAYS_NULLABLE,
+                Lists.newArrayList(new ArrayType(Type.VARCHAR)), false,
+                
"_ZN5doris19DummyTableFunctions7explodeEPN9doris_udf15FunctionContextERKNS1_13CollectionValE",
+                null, null, true));
+        tableFunctions.put(EXPLODE, explodes);
+
+        List<Function> explodeOuters = Lists.newArrayList();
+        explodeOuters.add(ScalarFunction.createBuiltin(
+                EXPLODE_OUTER, Type.INT, Function.NullableMode.ALWAYS_NULLABLE,
+                Lists.newArrayList(new ArrayType(Type.INT)), false,
+                
"_ZN5doris19DummyTableFunctions13explode_outerEPN9doris_udf15FunctionContextERKNS1_13CollectionValE",
+                null, null, true));
+        explodeOuters.add(ScalarFunction.createBuiltin(
+                EXPLODE_OUTER, Type.VARCHAR, 
Function.NullableMode.ALWAYS_NULLABLE,
+                Lists.newArrayList(new ArrayType(Type.VARCHAR)), false,
+                
"_ZN5doris19DummyTableFunctions13explode_outerEPN9doris_udf15FunctionContextERKNS1_13CollectionValE",
+                null, null, true));
+        tableFunctions.put(EXPLODE_OUTER, explodeOuters);
     }
 }


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

Reply via email to