You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@doris.apache.org by mo...@apache.org on 2021/12/16 02:46:29 UTC

[incubator-doris] branch master updated: [feat](lateral-view) Support execution of lateral view stmt (#7255)

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 0499b22  [feat](lateral-view) Support execution of lateral view stmt (#7255)
0499b22 is described below

commit 0499b2211b7ad685a6aae17508b3120b3490bcec
Author: Mingyu Chen <mo...@gmail.com>
AuthorDate: Thu Dec 16 10:46:15 2021 +0800

    [feat](lateral-view) Support execution of lateral view stmt (#7255)
    
    1. Add table function node
    2. Add 3 table functions: explode_split, explode_bitmap and explode_json_array
---
 be/src/common/daemon.cpp                           |   2 +
 be/src/exec/CMakeLists.txt                         |   1 +
 be/src/exec/exec_node.cpp                          |   5 +
 be/src/exec/table_function_node.cpp                | 340 +++++++++++++++++++++
 be/src/exec/table_function_node.h                  |  77 +++++
 be/src/exprs/CMakeLists.txt                        |   9 +-
 be/src/exprs/scalar_fn_call.cpp                    |  53 +---
 be/src/exprs/scalar_fn_call.h                      |   5 +
 be/src/exprs/slot_ref.cpp                          |   5 +-
 .../exprs/table_function/dummy_table_functions.cpp |  49 +++
 .../exprs/table_function/dummy_table_functions.h   |  47 +++
 be/src/exprs/table_function/explode_bitmap.cpp     | 124 ++++++++
 be/src/exprs/table_function/explode_bitmap.h       |  56 ++++
 be/src/exprs/table_function/explode_json_array.cpp | 202 ++++++++++++
 be/src/exprs/table_function/explode_json_array.h   | 130 ++++++++
 be/src/exprs/table_function/explode_split.cpp      | 115 +++++++
 be/src/exprs/table_function/explode_split.h        |  56 ++++
 be/src/exprs/table_function/table_function.h       |  65 ++++
 .../table_function/table_function_factory.cpp      |  48 +++
 .../exprs/table_function/table_function_factory.h  |  36 +++
 be/src/runtime/descriptors.h                       |   1 -
 be/src/util/bitmap_value.h                         | 130 ++++++++
 be/test/util/bitmap_value_test.cpp                 |  37 +++
 docs/.vuepress/sidebar/en.js                       |  10 +
 docs/.vuepress/sidebar/zh-CN.js                    |  10 +
 .../table-functions/explode-bitmap.md              | 157 ++++++++++
 .../table-functions/explode-json-array.md          | 286 +++++++++++++++++
 .../sql-functions/table-functions/explode-split.md | 112 +++++++
 .../Data Manipulation/lateral-view.md              |  94 ++++++
 .../table-functions/explode-bitmap.md              | 157 ++++++++++
 .../table-functions/explode-json-array.md          | 286 +++++++++++++++++
 .../sql-functions/table-functions/explode-split.md | 112 +++++++
 .../Data Manipulation/lateral-view.md              |  94 ++++++
 .../apache/doris/analysis/FunctionCallExpr.java    | 147 +++++----
 .../org/apache/doris/analysis/LateralViewRef.java  |  30 +-
 .../java/org/apache/doris/analysis/SlotRef.java    |  14 +
 .../java/org/apache/doris/catalog/FunctionSet.java |  50 ++-
 .../apache/doris/planner/DistributedPlanner.java   |   1 +
 .../main/java/org/apache/doris/qe/Coordinator.java |   2 +
 .../doris/planner/TableFunctionPlanTest.java       |  61 +++-
 40 files changed, 3046 insertions(+), 170 deletions(-)

diff --git a/be/src/common/daemon.cpp b/be/src/common/daemon.cpp
index ac4e645..b6863b5 100644
--- a/be/src/common/daemon.cpp
+++ b/be/src/common/daemon.cpp
@@ -40,6 +40,7 @@
 #include "exprs/new_in_predicate.h"
 #include "exprs/operators.h"
 #include "exprs/string_functions.h"
+#include "exprs/table_function/dummy_table_functions.h"
 #include "exprs/time_operators.h"
 #include "exprs/timestamp_functions.h"
 #include "exprs/topn_function.h"
@@ -264,6 +265,7 @@ void Daemon::init(int argc, char** argv, const std::vector<StorePath>& paths) {
     HllFunctions::init();
     HashFunctions::init();
     TopNFunctions::init();
+    DummyTableFunctions::init();
 
     LOG(INFO) << CpuInfo::debug_string();
     LOG(INFO) << DiskInfo::debug_string();
diff --git a/be/src/exec/CMakeLists.txt b/be/src/exec/CMakeLists.txt
index e33e30d..f3856ec 100644
--- a/be/src/exec/CMakeLists.txt
+++ b/be/src/exec/CMakeLists.txt
@@ -58,6 +58,7 @@ set(EXEC_FILES
     plain_text_line_reader.cpp
     csv_scan_node.cpp
     csv_scanner.cpp
+    table_function_node.cpp
     es_scan_node.cpp
     es_http_scan_node.cpp
     es_http_scanner.cpp
diff --git a/be/src/exec/exec_node.cpp b/be/src/exec/exec_node.cpp
index cdd4917..d692a79 100644
--- a/be/src/exec/exec_node.cpp
+++ b/be/src/exec/exec_node.cpp
@@ -48,6 +48,7 @@
 #include "exec/schema_scan_node.h"
 #include "exec/select_node.h"
 #include "exec/spill_sort_node.h"
+#include "exec/table_function_node.h"
 #include "exec/topn_node.h"
 #include "exec/union_node.h"
 #include "exprs/expr_context.h"
@@ -472,6 +473,10 @@ Status ExecNode::create_node(RuntimeState* state, ObjectPool* pool, const TPlanN
         *node = pool->add(new AssertNumRowsNode(pool, tnode, descs));
         return Status::OK();
 
+    case TPlanNodeType::TABLE_FUNCTION_NODE:
+        *node = pool->add(new TableFunctionNode(pool, tnode, descs));
+        return Status::OK();
+
     default:
         map<int, const char*>::const_iterator i =
                 _TPlanNodeType_VALUES_TO_NAMES.find(tnode.node_type);
diff --git a/be/src/exec/table_function_node.cpp b/be/src/exec/table_function_node.cpp
new file mode 100644
index 0000000..d192354
--- /dev/null
+++ b/be/src/exec/table_function_node.cpp
@@ -0,0 +1,340 @@
+// 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 "exec/table_function_node.h"
+
+#include "exprs/expr.h"
+#include "exprs/expr_context.h"
+#include "runtime/descriptors.h"
+#include "runtime/raw_value.h"
+#include "runtime/row_batch.h"
+#include "runtime/runtime_state.h"
+#include "runtime/tuple_row.h"
+#include "exprs/table_function/table_function_factory.h"
+
+namespace doris {
+
+TableFunctionNode::TableFunctionNode(ObjectPool* pool, const TPlanNode& tnode, const DescriptorTbl& descs)
+    : ExecNode(pool, tnode, descs) {
+
+}
+
+TableFunctionNode::~TableFunctionNode() {
+
+}
+
+Status TableFunctionNode::init(const TPlanNode& tnode, RuntimeState* state) {
+    RETURN_IF_ERROR(ExecNode::init(tnode, state));
+
+    for (const TExpr& texpr : tnode.table_function_node.fnCallExprList) {
+        ExprContext* ctx = nullptr;
+        RETURN_IF_ERROR(Expr::create_expr_tree(_pool, texpr, &ctx));
+        _fn_ctxs.push_back(ctx);
+
+        Expr* root = ctx->root();
+        const std::string& tf_name = root->fn().name.function_name; 
+        TableFunction* fn;
+        RETURN_IF_ERROR(TableFunctionFactory::get_fn(tf_name, _pool, &fn));
+        fn->set_expr_context(ctx);
+        _fns.push_back(fn);
+    }
+    _fn_num = _fns.size();
+    _fn_values.resize(_fn_num);
+
+    // Prepare output slot ids
+    RETURN_IF_ERROR(_prepare_output_slot_ids(tnode));
+    return Status::OK();
+}
+
+Status TableFunctionNode::_prepare_output_slot_ids(const TPlanNode& tnode) {
+    // Prepare output slot ids
+    if (tnode.table_function_node.outputSlotIds.empty()) {
+        return Status::InternalError("Output slots of table function node is empty");
+    }
+    SlotId max_id = -1;
+    for (auto slot_id : tnode.table_function_node.outputSlotIds) {
+        if (slot_id > max_id) {
+            max_id = slot_id;
+        }
+    }
+    _output_slot_ids = std::vector<bool>(max_id + 1, false);
+    for (auto slot_id : tnode.table_function_node.outputSlotIds) {
+        _output_slot_ids[slot_id] = true;
+    }
+
+    return Status::OK();
+}
+
+Status TableFunctionNode::prepare(RuntimeState* state) {
+    RETURN_IF_ERROR(ExecNode::prepare(state));
+    
+    RETURN_IF_ERROR(Expr::prepare(_fn_ctxs, state, _row_descriptor, expr_mem_tracker()));
+    for (auto fn : _fns) {
+        RETURN_IF_ERROR(fn->prepare());
+    }
+    return Status::OK();
+}
+
+Status TableFunctionNode::open(RuntimeState* state) {
+    SCOPED_TIMER(_runtime_profile->total_time_counter());
+    RETURN_IF_CANCELLED(state);
+    RETURN_IF_ERROR(ExecNode::open(state));
+
+    RETURN_IF_ERROR(Expr::open(_fn_ctxs, state));
+    for (auto fn : _fns) {
+        RETURN_IF_ERROR(fn->open());
+    }
+
+    RETURN_IF_ERROR(_children[0]->open(state));
+    return Status::OK();
+}
+
+Status TableFunctionNode::_process_next_child_row() {
+    if (_cur_child_offset == _cur_child_batch->num_rows()) {
+        _child_batch_exhausted = true;
+        return Status::OK();
+    }
+    _cur_child_tuple_row = _cur_child_batch->get_row(_cur_child_offset++);
+    for (TableFunction* fn : _fns) {
+        RETURN_IF_ERROR(fn->process(_cur_child_tuple_row));
+    }
+
+    _child_batch_exhausted = false;
+    return Status::OK();
+}
+
+// Returns the index of fn of the last eos counted from back to front
+// eg: there are 3 functions in `_fns`
+//      eos:    false, true, true
+//      return: 1
+//
+//      eos:    false, false, true
+//      return: 2
+//
+//      eos:    false, false, false
+//      return: -1
+//
+//      eos:    true, true, true
+//      return: 0
+//
+// return:
+//  0: all fns are eos
+// -1: all fns are not eos
+// >0: some of fns are eos
+int TableFunctionNode::_find_last_fn_eos_idx() {
+    for (int i = _fn_num - 1; i >=0; --i) {
+        if (!_fns[i]->eos()) {
+            if (i == _fn_num - 1) {
+                return -1;
+            } else {
+                return i + 1;
+            }
+        }
+    }
+    // all eos
+    return 0;
+}
+
+// Roll to reset the table function.
+// Eg:
+//  There are 3 functions f1, f2 and f3 in `_fns`.
+//  If `last_eos_idx` is 1, which means f2 and f3 are eos.
+//  So we need to forward f1, and reset f2 and f3.
+bool TableFunctionNode::_roll_table_functions(int last_eos_idx) {
+    bool fn_eos = false;
+    int i = last_eos_idx - 1;
+    for (; i >= 0; --i) {
+        _fns[i]->forward(&fn_eos);
+        if (!fn_eos) {
+            break;
+        }
+    }
+    if (i == -1) {
+        // after forward, all functions are eos.
+        // we should process next child row to get more table function results.
+        return false;
+    }
+
+    for (int j = i + 1; j < _fn_num; ++j) {
+        _fns[j]->reset();
+    }
+
+    return true;
+}
+
+// There are 2 while loops in this method.
+// The outer loop is to get the next batch from child node.
+// And the inner loop is to expand the row by table functions, and output row by row.
+Status TableFunctionNode::get_next(RuntimeState* state, RowBatch* row_batch, bool* eos) {
+    RETURN_IF_ERROR(exec_debug_action(TExecNodePhase::GETNEXT));
+    SCOPED_TIMER(_runtime_profile->total_time_counter());
+
+    const RowDescriptor& parent_rowdesc = row_batch->row_desc();
+    const RowDescriptor& child_rowdesc = _children[0]->row_desc();
+    if (_parent_tuple_desc_size == -1) {
+        _parent_tuple_desc_size = parent_rowdesc.tuple_descriptors().size();
+        _child_tuple_desc_size = child_rowdesc.tuple_descriptors().size();
+        for (int i = 0; i < _child_tuple_desc_size; ++i) {
+            _child_slot_sizes.push_back(child_rowdesc.tuple_descriptors()[i]->slots().size());
+        }    
+    }
+
+    uint8_t* tuple_buffer = nullptr;
+    Tuple* tuple_ptr = nullptr;
+    Tuple* pre_tuple_ptr = nullptr;
+    int row_idx = 0;
+
+    while (true) {
+        RETURN_IF_CANCELLED(state);
+        RETURN_IF_ERROR(state->check_query_state("TableFunctionNode, while getting next batch."));
+
+        if (_cur_child_batch == nullptr) {
+            _cur_child_batch.reset(new RowBatch(child_rowdesc, state->batch_size(), mem_tracker().get()));
+        }
+        if (_child_batch_exhausted) {
+            // current child batch is exhausted, get next batch from child
+            RETURN_IF_ERROR(_children[0]->get_next(state, _cur_child_batch.get(), eos));
+            if (*eos) {
+                break;
+            }
+            _cur_child_offset = 0;
+            RETURN_IF_ERROR(_process_next_child_row());
+            if (_child_batch_exhausted) {
+                _cur_child_batch->reset();
+                continue;
+            }
+        }
+
+        while (true) {
+            int idx = _find_last_fn_eos_idx();
+            if (idx == 0) {
+                // all table functions' results are exhausted, process next child row
+                RETURN_IF_ERROR(_process_next_child_row());
+                if (_child_batch_exhausted) {
+                    break;
+                }
+            } else if (idx < _fn_num && idx != -1) {
+                // some of table functions' results are exhausted
+                if (!_roll_table_functions(idx)) {
+                    // continue to process next child row
+                    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++) {
+                RETURN_IF_ERROR(_fns[i]->get_value(&_fn_values[i]));
+            }
+
+            // allocate memory for row batch for the first time
+            if (tuple_buffer == nullptr) {
+                int64_t tuple_buffer_size;
+                RETURN_IF_ERROR(
+                        row_batch->resize_and_allocate_tuple_buffer(state, &tuple_buffer_size, &tuple_buffer));
+                tuple_ptr = reinterpret_cast<Tuple*>(tuple_buffer);
+            }
+            
+            pre_tuple_ptr = tuple_ptr;
+            // The tuples order in parent row batch should be
+            //      child1, child2, tf1, tf2, ...
+            TupleRow* parent_tuple_row = row_batch->get_row(row_idx++);
+            // 1. copy child tuples
+            int tuple_idx = 0;
+            for (int i = 0; i < _child_tuple_desc_size; tuple_idx++, i++) {
+                TupleDescriptor* child_tuple_desc = child_rowdesc.tuple_descriptors()[tuple_idx];
+                TupleDescriptor* parent_tuple_desc = parent_rowdesc.tuple_descriptors()[tuple_idx];
+
+                for (int j = 0; j < _child_slot_sizes[i]; ++j) {
+                    SlotDescriptor* child_slot_desc = child_tuple_desc->slots()[j];
+                    SlotDescriptor* parent_slot_desc = parent_tuple_desc->slots()[j];
+
+                    if (_output_slot_ids[parent_slot_desc->id()]) {
+                        Tuple* child_tuple = _cur_child_tuple_row->get_tuple(child_rowdesc.get_tuple_idx(child_tuple_desc->id()));
+                        void* dest_slot = tuple_ptr->get_slot(parent_slot_desc->tuple_offset());
+                        RawValue::write(child_tuple->get_slot(child_slot_desc->tuple_offset()), dest_slot, parent_slot_desc->type(), row_batch->tuple_data_pool());
+                        tuple_ptr->set_not_null(parent_slot_desc->null_indicator_offset());
+                    } else {
+                        tuple_ptr->set_null(parent_slot_desc->null_indicator_offset());
+                    }
+                }
+                parent_tuple_row->set_tuple(tuple_idx, tuple_ptr);
+                tuple_ptr = reinterpret_cast<Tuple*>(reinterpret_cast<uint8_t*>(tuple_ptr) + parent_tuple_desc->byte_size());
+            }
+
+            // 2. copy funtion result
+            for (int i = 0; tuple_idx < _parent_tuple_desc_size; tuple_idx++, i++) {
+                TupleDescriptor* parent_tuple_desc = parent_rowdesc.tuple_descriptors()[tuple_idx];
+                SlotDescriptor* parent_slot_desc = parent_tuple_desc->slots()[0];
+                void* dest_slot = tuple_ptr->get_slot(parent_slot_desc->tuple_offset());
+                // if (_fn_values[i] != nullptr && _output_slot_ids[parent_slot_desc->id()]) {
+                if (_fn_values[i] != nullptr) {
+                    RawValue::write(_fn_values[i], dest_slot, parent_slot_desc->type(), row_batch->tuple_data_pool());
+                    tuple_ptr->set_not_null(parent_slot_desc->null_indicator_offset());
+                } else {
+                    tuple_ptr->set_null(parent_slot_desc->null_indicator_offset());
+                }
+                parent_tuple_row->set_tuple(tuple_idx, tuple_ptr);
+
+                tuple_ptr = reinterpret_cast<Tuple*>(reinterpret_cast<uint8_t*>(tuple_ptr) + parent_tuple_desc->byte_size());
+            }
+
+            // 3. eval conjuncts
+            if (eval_conjuncts(&_conjunct_ctxs[0], _conjunct_ctxs.size(), parent_tuple_row)) {
+                row_batch->commit_last_row();
+                ++_num_rows_returned;
+            } else {
+                tuple_ptr = pre_tuple_ptr;
+            }
+
+            // Forward after write success.
+            // Because data in `_fn_values` points to the data saved in functions.
+            // And `forward` will change the data in functions.
+            bool tmp;
+            _fns[_fn_num - 1]->forward(&tmp);
+
+            if (row_batch->at_capacity()) {
+                *eos = false;
+                break;
+            }
+        }
+
+        if (row_batch->at_capacity()) {
+            break;
+        } 
+    }
+    if (reached_limit()) {
+        int num_rows_over = _num_rows_returned - _limit;
+        row_batch->set_num_rows(row_batch->num_rows() - num_rows_over);
+        _num_rows_returned -= num_rows_over;
+        COUNTER_SET(_rows_returned_counter, _num_rows_returned);
+        *eos = true;
+    }
+
+    return Status::OK();
+}
+
+Status TableFunctionNode::close(RuntimeState* state) {
+    if (is_closed()) {
+        return Status::OK();
+    }
+    RETURN_IF_ERROR(exec_debug_action(TExecNodePhase::CLOSE));
+    Expr::close(_fn_ctxs, state);
+    return ExecNode::close(state);
+}
+
+}; // namespace doris
diff --git a/be/src/exec/table_function_node.h b/be/src/exec/table_function_node.h
new file mode 100644
index 0000000..f91577e
--- /dev/null
+++ b/be/src/exec/table_function_node.h
@@ -0,0 +1,77 @@
+// 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 "exec/exec_node.h"
+
+namespace doris {
+
+class MemPool;
+class RowBatch;
+class TableFunction;
+class TupleRow;
+
+// TableFunctionNode
+class TableFunctionNode : public ExecNode {
+public:
+    TableFunctionNode(ObjectPool* pool, const TPlanNode& tnode, const DescriptorTbl& descs);
+    ~TableFunctionNode();
+
+    virtual Status init(const TPlanNode& tnode, RuntimeState* state = nullptr);
+    virtual Status prepare(RuntimeState* state);
+    virtual Status open(RuntimeState* state);
+    virtual Status get_next(RuntimeState* state, RowBatch* row_batch, bool* eos);
+    virtual Status close(RuntimeState* state);
+
+private:
+
+    Status _prepare_output_slot_ids(const TPlanNode& tnode);
+
+    // return:
+    //  0: all fns are eos
+    // -1: all fns are not eos
+    // >0: some of fns are eos
+    int _find_last_fn_eos_idx();
+
+    Status _process_next_child_row();
+
+    bool _roll_table_functions(int last_eos_idx);
+
+private:
+
+    int64_t _cur_child_offset = 0;
+    TupleRow* _cur_child_tuple_row = nullptr;
+    std::shared_ptr<RowBatch> _cur_child_batch;
+    // true means current child batch is completely consumed.
+    // we should get next batch from child node.
+    bool _child_batch_exhausted = true;
+
+    std::vector<ExprContext*> _fn_ctxs;
+    std::vector<TableFunction*> _fns;
+    std::vector<void*> _fn_values;
+    int _fn_num = 0;
+
+    // std::unordered_set<SlotId> _output_slot_ids;
+    std::vector<bool> _output_slot_ids;
+
+    int _parent_tuple_desc_size = -1;
+    int _child_tuple_desc_size = -1;
+    std::vector<int> _child_slot_sizes;
+};
+
+}; // namespace doris
diff --git a/be/src/exprs/CMakeLists.txt b/be/src/exprs/CMakeLists.txt
index 99ebbf4..5ebf1d1 100644
--- a/be/src/exprs/CMakeLists.txt
+++ b/be/src/exprs/CMakeLists.txt
@@ -70,4 +70,11 @@ add_library(Exprs
   bitmap_function.cpp
   hll_function.cpp
   grouping_sets_functions.cpp
-  topn_function.cpp)
+  topn_function.cpp
+
+  table_function/explode_split.cpp
+  table_function/explode_bitmap.cpp
+  table_function/explode_json_array.cpp
+  table_function/table_function_factory.cpp
+  table_function/dummy_table_functions.cpp
+)
diff --git a/be/src/exprs/scalar_fn_call.cpp b/be/src/exprs/scalar_fn_call.cpp
index 2a7fc82..5e1d081 100644
--- a/be/src/exprs/scalar_fn_call.cpp
+++ b/be/src/exprs/scalar_fn_call.cpp
@@ -72,7 +72,6 @@ Status ScalarFnCall::prepare(RuntimeState* state, const RowDescriptor& desc, Exp
     }
 
     _fn_context_index = context->register_func(state, return_type, arg_types, varargs_buffer_size);
-    // _scalar_fn = OpcodeRegistry::instance()->get_function_ptr(_opcode);
     Status status = Status::OK();
     if (_scalar_fn == nullptr) {
         if (SymbolsUtil::is_mangled(_fn.scalar_fn.symbol)) {
@@ -88,61 +87,11 @@ Status ScalarFnCall::prepare(RuntimeState* state, const RowDescriptor& desc, Exp
             // ret_type = ColumnType(thrift_to_type(_fn.ret_type));
             std::string symbol = SymbolsUtil::mangle_user_function(_fn.scalar_fn.symbol, arg_types,
                                                                    _fn.has_var_args, nullptr);
+
             status = UserFunctionCache::instance()->get_function_ptr(
                     _fn.id, symbol, _fn.hdfs_location, _fn.checksum, &_scalar_fn, &_cache_entry);
         }
     }
-#if 0
-    // If the codegen object hasn't been created yet and we're calling a builtin or native
-    // UDF with <= 8 non-variadic arguments, we can use the interpreted path and call the
-    // builtin without codegen. This saves us the overhead of creating the codegen object
-    // when it's not necessary (i.e., in plan fragments with no codegen-enabled operators).
-    // In addition, we can never codegen char arguments.
-    // TODO: codegen for char arguments
-    if (char_arg || (!state->codegen_created() && num_fixed_args() <= 8 &&
-                     (_fn.binary_type == TFunctionBinaryType::BUILTIN ||
-                      _fn.binary_type == TFunctionBinaryType::NATIVE))) {
-        // Builtins with char arguments must still have <= 8 arguments.
-        // TODO: delete when we have codegen for char arguments
-        if (char_arg) {
-            DCHECK(num_fixed_args() <= 8 && _fn.binary_type == TFunctionBinaryType::BUILTIN);
-        }
-        Status status = UserFunctionCache::instance()->GetSoFunctionPtr(
-            _fn.hdfs_location, _fn.scalar_fn.symbol, &_scalar_fn, &cache_entry_);
-        if (!status.ok()) {
-            if (_fn.binary_type == TFunctionBinaryType::BUILTIN) {
-                // Builtins symbols should exist unless there is a version mismatch.
-                status.SetErrorMsg(ErrorMsg(TErrorCode::MISSING_BUILTIN,
-                                            _fn.name.function_name, _fn.scalar_fn.symbol));
-                return status;
-            } else {
-                DCHECK_EQ(_fn.binary_type, TFunctionBinaryType::NATIVE);
-                return Status::InternalError(strings::Substitute("Problem loading UDF '$0':\n$1",
-                                         _fn.name.function_name, status.GetDetail()));
-                return status;
-            }
-        }
-    } else {
-        // If we got here, either codegen is enabled or we need codegen to run this function.
-        LlvmCodeGen* codegen;
-        RETURN_IF_ERROR(state->GetCodegen(&codegen));
-
-        if (_fn.binary_type == TFunctionBinaryType::IR) {
-            std::string local_path;
-            RETURN_IF_ERROR(UserFunctionCache::instance()->GetLocalLibPath(
-                    _fn.hdfs_location, UserFunctionCache::TYPE_IR, &local_path));
-            // Link the UDF module into this query's main module (essentially copy the UDF
-            // module into the main module) so the UDF's functions are available in the main
-            // module.
-            RETURN_IF_ERROR(codegen->LinkModule(local_path));
-        }
-
-        Function* ir_udf_wrapper;
-        RETURN_IF_ERROR(GetCodegendComputeFn(state, &ir_udf_wrapper));
-        // TODO: don't do this for child exprs
-        codegen->AddFunctionToJit(ir_udf_wrapper, &_scalar_fn_wrapper);
-    }
-#endif
     if (_fn.scalar_fn.__isset.prepare_fn_symbol) {
         RETURN_IF_ERROR(get_function(state, _fn.scalar_fn.prepare_fn_symbol,
                                      reinterpret_cast<void**>(&_prepare_fn)));
diff --git a/be/src/exprs/scalar_fn_call.h b/be/src/exprs/scalar_fn_call.h
index e94e9d0..cf8f1f7 100644
--- a/be/src/exprs/scalar_fn_call.h
+++ b/be/src/exprs/scalar_fn_call.h
@@ -54,6 +54,11 @@ public:
         return pool->add(new ScalarFnCall(*this));
     }
 
+    // TODO: just for table function.
+    // It is not good to expose this field to public.
+    // We should refactor it after implementing real table functions.
+    int get_fn_context_index() const { return _fn_context_index; }
+
 protected:
     friend class Expr;
 
diff --git a/be/src/exprs/slot_ref.cpp b/be/src/exprs/slot_ref.cpp
index 0068c03..204d20c 100644
--- a/be/src/exprs/slot_ref.cpp
+++ b/be/src/exprs/slot_ref.cpp
@@ -20,6 +20,7 @@
 #include <sstream>
 
 #include "gen_cpp/Exprs_types.h"
+#include "gutil/strings/substitute.h"
 #include "runtime/runtime_state.h"
 #include "util/types.h"
 
@@ -62,7 +63,7 @@ Status SlotRef::prepare(const SlotDescriptor* slot_desc, const RowDescriptor& ro
     }
     _tuple_idx = row_desc.get_tuple_idx(slot_desc->parent());
     if (_tuple_idx == RowDescriptor::INVALID_IDX) {
-        return Status::InternalError("can't support");
+        return Status::InternalError(strings::Substitute("failed to get tuple idx with tuple id: $0, slot id: $1", slot_desc->parent(), _slot_id));
     }
     _tuple_is_nullable = row_desc.tuple_is_nullable(_tuple_idx);
     _slot_offset = slot_desc->tuple_offset();
@@ -94,7 +95,7 @@ Status SlotRef::prepare(RuntimeState* state, const RowDescriptor& row_desc, Expr
     // TODO(marcel): get from runtime state
     _tuple_idx = row_desc.get_tuple_idx(slot_desc->parent());
     if (_tuple_idx == RowDescriptor::INVALID_IDX) {
-        return Status::InternalError("can't support");
+        return Status::InternalError(strings::Substitute("failed to get tuple idx when prepare with tuple id: $0, slot id: $1", slot_desc->parent(), _slot_id));
     }
     DCHECK(_tuple_idx != RowDescriptor::INVALID_IDX);
     _tuple_is_nullable = row_desc.tuple_is_nullable(_tuple_idx);
diff --git a/be/src/exprs/table_function/dummy_table_functions.cpp b/be/src/exprs/table_function/dummy_table_functions.cpp
new file mode 100644
index 0000000..bb64100
--- /dev/null
+++ b/be/src/exprs/table_function/dummy_table_functions.cpp
@@ -0,0 +1,49 @@
+// 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/table_function/dummy_table_functions.h"
+
+namespace doris {
+
+void DummyTableFunctions::init() {}
+
+StringVal DummyTableFunctions::explode_split(FunctionContext* context, const StringVal& str,
+        const StringVal& sep) {
+    return StringVal();
+}
+
+BigIntVal DummyTableFunctions::explode_bitmap(doris_udf::FunctionContext* context,
+        const doris_udf::StringVal& bitmap) {
+    return BigIntVal();
+}
+
+BigIntVal DummyTableFunctions::explode_json_array_int(doris_udf::FunctionContext* context,
+        const doris_udf::StringVal& str) {
+    return BigIntVal();
+}
+
+DoubleVal DummyTableFunctions::explode_json_array_double(doris_udf::FunctionContext* context,
+        const doris_udf::StringVal& str) {
+    return DoubleVal();
+}
+
+StringVal DummyTableFunctions::explode_json_array_string(doris_udf::FunctionContext* context,
+        const doris_udf::StringVal& str) {
+    return StringVal();
+}
+
+} // namespace doris
diff --git a/be/src/exprs/table_function/dummy_table_functions.h b/be/src/exprs/table_function/dummy_table_functions.h
new file mode 100644
index 0000000..4723a52
--- /dev/null
+++ b/be/src/exprs/table_function/dummy_table_functions.h
@@ -0,0 +1,47 @@
+// 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 "exprs/anyval_util.h"
+
+namespace doris {
+
+// Currently Doris does not support array types, so the definition of table function
+// is still using the definition of the scalar function. 
+// The definition here is just to facilitate the query planning stage and the query execution preparation stage
+// to make smooth use of the existing function framework
+// But the execution logic of the table function is not here. So the function names here are prefixed with "dummy".
+// TODO: refactor here after we support real array type.
+class DummyTableFunctions {
+public:
+    static void init();
+
+    static doris_udf::StringVal explode_split(doris_udf::FunctionContext* context,
+                                          const doris_udf::StringVal& str,
+                                          const doris_udf::StringVal& sep);
+    static doris_udf::BigIntVal explode_bitmap(doris_udf::FunctionContext* context,
+                                          const doris_udf::StringVal& bitmap);
+    static doris_udf::BigIntVal explode_json_array_int(doris_udf::FunctionContext* context,
+                                          const doris_udf::StringVal& str);
+    static doris_udf::DoubleVal explode_json_array_double(doris_udf::FunctionContext* context,
+                                          const doris_udf::StringVal& str);
+    static doris_udf::StringVal explode_json_array_string(doris_udf::FunctionContext* context,
+                                          const doris_udf::StringVal& str);
+};
+} // namespace doris
+
diff --git a/be/src/exprs/table_function/explode_bitmap.cpp b/be/src/exprs/table_function/explode_bitmap.cpp
new file mode 100644
index 0000000..67fe561
--- /dev/null
+++ b/be/src/exprs/table_function/explode_bitmap.cpp
@@ -0,0 +1,124 @@
+// 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/table_function/explode_bitmap.h"
+
+#include "exprs/expr_context.h"
+#include "exprs/scalar_fn_call.h"
+
+namespace doris {
+
+ExplodeBitmapTableFunction::ExplodeBitmapTableFunction() {}
+
+ExplodeBitmapTableFunction::~ExplodeBitmapTableFunction() {
+    if (_cur_iter != nullptr) {
+        delete _cur_iter;
+        _cur_iter = nullptr;
+    }
+    if (_cur_bitmap_owned && _cur_bitmap != nullptr) {
+        delete _cur_bitmap;
+        _cur_bitmap = nullptr;
+    }
+}
+
+Status ExplodeBitmapTableFunction::prepare() {
+    return Status::OK();
+}
+
+Status ExplodeBitmapTableFunction::open() {
+    return Status::OK();
+}
+
+Status ExplodeBitmapTableFunction::process(TupleRow* tuple_row) {
+    CHECK(1 == _expr_context->root()->get_num_children()) << _expr_context->root()->get_num_children();
+    _eos = false;
+    _is_current_empty = false;
+    _cur_size = 0;
+    _cur_offset = 0;
+
+    StringVal bitmap_str = _expr_context->root()->get_child(0)->get_string_val(_expr_context, tuple_row);
+    if (bitmap_str.is_null) {
+       _is_current_empty = true;
+    } else {
+        if (bitmap_str.len == 0) {
+            _cur_bitmap = reinterpret_cast<BitmapValue*>(bitmap_str.ptr);
+            _cur_bitmap_owned = false;
+        } else {
+            _cur_bitmap = new BitmapValue((char*) bitmap_str.ptr);
+            _cur_bitmap_owned = true;
+        }
+        _cur_size = _cur_bitmap->cardinality();
+        if (_cur_size == 0) {
+            _is_current_empty = true;
+        } else {
+            _reset_iterator();
+        }
+    }
+
+    return Status::OK();
+}
+
+void ExplodeBitmapTableFunction::_reset_iterator() {
+    DCHECK(_cur_bitmap->cardinality() > 0) << _cur_bitmap->cardinality();
+    if (_cur_iter != nullptr) {
+        delete _cur_iter;
+        _cur_iter = nullptr;
+    }
+    _cur_iter = new BitmapValueIterator(*_cur_bitmap);
+    _cur_value = **_cur_iter;
+}
+
+Status ExplodeBitmapTableFunction::reset() {
+    _eos = false;
+    if (!_is_current_empty) {
+        _reset_iterator();
+    }
+    return Status::OK();
+}
+
+Status ExplodeBitmapTableFunction::get_value(void** output) {
+    if (_is_current_empty) {
+        *output = nullptr;
+    } else {
+        *output = &_cur_value;
+    }
+    return Status::OK();
+}
+
+Status ExplodeBitmapTableFunction::close() {
+    return Status::OK();
+}
+
+Status ExplodeBitmapTableFunction::forward(bool* eos) {
+    if (_is_current_empty) {
+        *eos = true;
+        _eos = true;
+    } else {
+        ++(*_cur_iter);
+        ++_cur_offset;
+        if (_cur_offset == _cur_size) {
+            *eos = true;
+            _eos = true;
+        } else {
+            _cur_value = **_cur_iter;
+            *eos = false;
+        }
+    }
+    return Status::OK();
+}
+
+} // namespace doris
diff --git a/be/src/exprs/table_function/explode_bitmap.h b/be/src/exprs/table_function/explode_bitmap.h
new file mode 100644
index 0000000..b491eea
--- /dev/null
+++ b/be/src/exprs/table_function/explode_bitmap.h
@@ -0,0 +1,56 @@
+// 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 "exprs/table_function/table_function.h"
+
+#include "util/bitmap_value.h"
+
+namespace doris {
+
+class ExplodeBitmapTableFunction : public TableFunction {
+public:
+    ExplodeBitmapTableFunction();
+    virtual ~ExplodeBitmapTableFunction();
+
+    virtual Status prepare() override;
+    virtual Status open() override;
+    virtual Status process(TupleRow* tuple_row) override;
+    virtual Status reset() override;
+    virtual Status get_value(void** output) override;
+    virtual Status close() override;
+
+    virtual Status forward(bool* eos) override;
+
+private:
+    void _reset_iterator();
+
+private:
+
+    // Read from tuple row.
+    // if _cur_bitmap_owned is true, need to delete it when deconstruction
+    BitmapValue* _cur_bitmap = nullptr;
+    bool _cur_bitmap_owned = false;
+    // iterator of _cur_bitmap
+    BitmapValueIterator* _cur_iter = nullptr;
+    // current value read from bitmap, it will be referenced by
+    // table function scan node.
+    uint64_t _cur_value = 0;
+};
+
+} // namespace doris
diff --git a/be/src/exprs/table_function/explode_json_array.cpp b/be/src/exprs/table_function/explode_json_array.cpp
new file mode 100644
index 0000000..e82c9ec
--- /dev/null
+++ b/be/src/exprs/table_function/explode_json_array.cpp
@@ -0,0 +1,202 @@
+// 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/table_function/explode_json_array.h"
+
+#include "exprs/expr_context.h"
+#include "exprs/scalar_fn_call.h"
+
+namespace doris {
+
+std::string ParsedData::true_value = "true";
+std::string ParsedData::false_value = "false";
+
+int ParsedData::set_output(ExplodeJsonArrayType type, rapidjson::Document& document) {
+    int size = document.GetArray().Size();
+    switch (type) {
+        case ExplodeJsonArrayType::INT: {
+            _data.resize(size);
+            _backup_int.resize(size);
+            int i = 0;
+            for (auto& v : document.GetArray()) {
+                if (v.IsInt64()) {
+                    _backup_int[i] = v.GetInt64();
+                    _data[i] = &_backup_int[i];
+                } else {
+                    _data[i] = nullptr;
+                }
+                ++i;
+            }
+            break;
+        }
+        case ExplodeJsonArrayType::DOUBLE: {
+            _data.resize(size);
+            _backup_double.resize(size);
+            int i = 0;
+            for (auto& v : document.GetArray()) {
+                if (v.IsDouble()) {
+                    _backup_double[i] = v.GetDouble();
+                    _data[i] = &_backup_double[i];
+                } else {
+                    _data[i] = nullptr;
+                }
+                ++i;
+            }
+            break;
+        }
+        case ExplodeJsonArrayType::STRING: {
+            _data_string.clear();
+            _backup_string.clear();
+            _string_nulls.clear();
+            int32_t wbytes = 0;
+            int i = 0;
+            for (auto& v : document.GetArray()) {
+                switch (v.GetType()) {
+                    case rapidjson::Type::kStringType:
+                        _backup_string.emplace_back(v.GetString(), v.GetStringLength());
+                        _data_string.emplace_back(_backup_string.back());
+                        _string_nulls.push_back(false);
+                        break;
+                    case rapidjson::Type::kNumberType:
+                        if (v.IsUint()) {
+                            wbytes = sprintf(tmp_buf, "%u", v.GetUint());
+                        } else if (v.IsInt()) {
+                            wbytes = sprintf(tmp_buf, "%d", v.GetInt());
+                        } else if (v.IsUint64()) {
+                            wbytes = sprintf(tmp_buf, "%lu", v.GetUint64());
+                        } else if (v.IsInt64()) {
+                            wbytes = sprintf(tmp_buf, "%ld", v.GetInt64());
+                        } else {
+                            wbytes = sprintf(tmp_buf, "%f", v.GetDouble());
+                        }
+                        _backup_string.emplace_back(tmp_buf, wbytes);
+                        _data_string.emplace_back(_backup_string.back());
+                        _string_nulls.push_back(false);
+                        break;
+                    case rapidjson::Type::kFalseType:
+                        _data_string.emplace_back(true_value);
+                        _string_nulls.push_back(false);
+                        break;
+                    case rapidjson::Type::kTrueType:
+                        _data_string.emplace_back(false_value);
+                        _string_nulls.push_back(false);
+                        break;
+                    case rapidjson::Type::kNullType:
+                        _data_string.push_back({});
+                        _string_nulls.push_back(true);
+                        break;
+                    default:
+                        _data_string.push_back({});
+                        _string_nulls.push_back(true);
+                        break;
+                }
+                ++i;
+            }
+            break;
+        }
+        default:
+            CHECK(false) << type;
+            break;
+    }
+    return size;
+}
+
+/////////////////////////
+ExplodeJsonArrayTableFunction::ExplodeJsonArrayTableFunction(ExplodeJsonArrayType type)
+    : _type(type) {
+    
+}
+
+ExplodeJsonArrayTableFunction::~ExplodeJsonArrayTableFunction() {
+}
+
+Status ExplodeJsonArrayTableFunction::prepare() {
+    return Status::OK();
+}
+
+Status ExplodeJsonArrayTableFunction::open() {
+    return Status::OK();
+}
+
+Status ExplodeJsonArrayTableFunction::process(TupleRow* tuple_row) {
+    CHECK(1 == _expr_context->root()->get_num_children()) << _expr_context->root()->get_num_children();
+    _is_current_empty = false;
+    _eos = false;
+
+    StringVal text = _expr_context->root()->get_child(0)->get_string_val(_expr_context, tuple_row);
+    if (text.is_null || text.len == 0) {
+        // _set_null_output();
+        _is_current_empty = true;
+    } else {
+        rapidjson::Document document;
+        document.Parse((char*) text.ptr, text.len);
+        if (UNLIKELY(document.HasParseError()) || !document.IsArray() || document.GetArray().Size() == 0) {
+            // _set_null_output();
+            _is_current_empty = true;
+        } else {
+            _cur_size = _parsed_data.set_output(_type, document);
+            _cur_offset = 0;
+            // _eos = _cur_size == 0;
+        }
+    }
+    // _is_current_empty = _eos;
+    return Status::OK();
+}
+
+void ExplodeJsonArrayTableFunction::_set_null_output() {
+    _parsed_data.set_null_output(_type);
+    _cur_size = 1;
+    _cur_offset = 0;
+    _eos = false;
+}
+
+Status ExplodeJsonArrayTableFunction::reset() {
+    _eos = false;
+    _cur_offset = 0;
+    return Status::OK();
+}
+
+Status ExplodeJsonArrayTableFunction::get_value(void** output) {
+    if (_is_current_empty) {
+        *output = nullptr;
+    } else {
+        _parsed_data.get_value(_type, _cur_offset, output);
+    }
+    return Status::OK();
+}
+
+Status ExplodeJsonArrayTableFunction::close() {
+    return Status::OK();
+}
+
+Status ExplodeJsonArrayTableFunction::forward(bool* eos) {
+    if (_is_current_empty) {
+        *eos = true;
+        _eos = true;
+    } else {
+        ++_cur_offset;
+        if (_cur_offset == _cur_size) {
+            *eos = true;
+            _eos = true;
+        } else {
+            *eos = false;
+        }
+    }
+    return Status::OK();
+}
+
+} // namespace doris
diff --git a/be/src/exprs/table_function/explode_json_array.h b/be/src/exprs/table_function/explode_json_array.h
new file mode 100644
index 0000000..ae5c657
--- /dev/null
+++ b/be/src/exprs/table_function/explode_json_array.h
@@ -0,0 +1,130 @@
+// 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 "exprs/table_function/table_function.h"
+
+#include <rapidjson/document.h>
+#include <rapidjson/stringbuffer.h>
+#include "gutil/strings/stringpiece.h"
+#include "runtime/string_value.h"
+
+namespace doris {
+
+enum ExplodeJsonArrayType {
+    INT = 0,
+    DOUBLE,
+    STRING
+};
+
+struct ParsedData {
+    static std::string true_value;
+    static std::string false_value;
+
+    // The number parsed from json array
+    // the `_backup` saved the real number entity.
+    std::vector<void*> _data;
+    std::vector<StringValue> _data_string;
+    std::vector<int64_t> _backup_int;
+    std::vector<double> _backup_double;
+    std::vector<std::string> _backup_string;
+    std::vector<bool> _string_nulls;
+    char tmp_buf[128] = {0};
+
+    void reset(ExplodeJsonArrayType type) {
+        switch (type) {
+            case ExplodeJsonArrayType::INT:
+                _data.clear();
+                _backup_int.clear();
+                break;
+            case ExplodeJsonArrayType::DOUBLE:
+                _data.clear();
+                _backup_double.clear();
+                break;
+            case ExplodeJsonArrayType::STRING:
+                _data_string.clear();
+                _backup_string.clear();
+                _string_nulls.clear();
+                break;
+            default:
+                CHECK(false) << type;
+                break;
+        }
+    }
+
+    void set_null_output(ExplodeJsonArrayType type) {
+        switch (type) {
+            case ExplodeJsonArrayType::INT:
+            case ExplodeJsonArrayType::DOUBLE:
+                _data.resize(1);
+                _data[0] = nullptr;
+                break;
+            case ExplodeJsonArrayType::STRING:
+                _string_nulls.resize(1);
+                _string_nulls[0] = true;
+                break;
+            default:
+                CHECK(false) << type;
+                break;
+        }
+    }
+
+    void get_value(ExplodeJsonArrayType type, int64_t offset, void** output) {
+        switch(type) {
+            case ExplodeJsonArrayType::INT:
+            case ExplodeJsonArrayType::DOUBLE:
+                *output = _data[offset];
+                break;
+            case ExplodeJsonArrayType::STRING:
+                // LOG(INFO) << "cmy get_value offset: " << offset << ", is null: " << _string_nulls[offset] << ", data: " << (_string_nulls[offset] ? "null2" : _backup_string[offset]);
+                *output = _string_nulls[offset] ? nullptr : &_data_string[offset];
+                break;
+            default:
+                CHECK(false) << type;
+        }
+    }
+
+    int set_output(ExplodeJsonArrayType type, rapidjson::Document& document);
+};
+
+// Input:  json array: [1,2,3,4,5];
+// Output: rows: 1,2,3,4,5
+// If json array contains non-numeric type, or is not a json array, will return null
+class ExplodeJsonArrayTableFunction : public TableFunction {
+public:
+    ExplodeJsonArrayTableFunction(ExplodeJsonArrayType type);
+    virtual ~ExplodeJsonArrayTableFunction();
+
+    virtual Status prepare() override;
+    virtual Status open() override;
+    virtual Status process(TupleRow* tuple_row) override;
+    virtual Status reset() override;
+    virtual Status get_value(void** output) override;
+    virtual Status close() override;
+    virtual Status forward(bool* eos) override;
+
+private:
+    void _set_null_output();
+
+private:
+    ParsedData _parsed_data;
+
+    ExplodeJsonArrayType _type;
+};
+
+} // namespace doris
diff --git a/be/src/exprs/table_function/explode_split.cpp b/be/src/exprs/table_function/explode_split.cpp
new file mode 100644
index 0000000..959dad4
--- /dev/null
+++ b/be/src/exprs/table_function/explode_split.cpp
@@ -0,0 +1,115 @@
+// 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/table_function/explode_split.h"
+
+#include "exprs/expr_context.h"
+#include "exprs/scalar_fn_call.h"
+#include "gutil/strings/split.h"
+
+namespace doris {
+
+ExplodeSplitTableFunction::ExplodeSplitTableFunction() {
+}
+
+ExplodeSplitTableFunction::~ExplodeSplitTableFunction() {
+}
+
+Status ExplodeSplitTableFunction::prepare() {
+    return Status::OK();
+}
+
+Status ExplodeSplitTableFunction::open() {
+    ScalarFnCall* fn_call = reinterpret_cast<ScalarFnCall*>(_expr_context->root());
+    FunctionContext* fn_ctx = _expr_context->fn_context(fn_call->get_fn_context_index());
+    CHECK(2 == fn_ctx->get_num_constant_args()) << fn_ctx->get_num_constant_args();
+    // check if the delimiter argument(the 2nd arg) is constant.
+    // if yes, cache it
+    if (fn_ctx->is_arg_constant(1)) {
+        _is_delimiter_constant = true; 
+        StringVal* delimiter = reinterpret_cast<StringVal*>(fn_ctx->get_constant_arg(1));
+        _const_delimter = StringPiece((char*) delimiter->ptr, delimiter->len);
+    }
+    return Status::OK();
+}
+
+Status ExplodeSplitTableFunction::process(TupleRow* tuple_row) {
+    CHECK(2 == _expr_context->root()->get_num_children()) << _expr_context->root()->get_num_children();
+    _is_current_empty = false;
+    _eos = false;
+
+    _data.clear();
+    StringVal text = _expr_context->root()->get_child(0)->get_string_val(_expr_context, tuple_row);
+    if (text.is_null) {
+        _is_current_empty = true;
+        _cur_size = 0;
+        _cur_offset = 0;
+    } else {
+        if (_is_delimiter_constant) {
+            _backup = strings::Split(StringPiece((char*) text.ptr, text.len), _const_delimter);
+        } else {
+            StringVal delimiter = _expr_context->root()->get_child(1)->get_string_val(_expr_context, tuple_row);
+            _backup = strings::Split(StringPiece((char*) text.ptr, text.len), StringPiece((char*) delimiter.ptr, delimiter.len));
+        }
+        for (const std::string str : _backup) {
+            _data.emplace_back(str);
+        }
+        _cur_size = _backup.size();
+        _cur_offset = 0;
+        _is_current_empty = (_cur_size == 0);
+    }
+    return Status::OK();
+}
+
+Status ExplodeSplitTableFunction::reset() {
+    _eos = false;
+    if (!_is_current_empty) {
+        _cur_offset = 0;
+    }
+    return Status::OK();
+}
+
+Status ExplodeSplitTableFunction::get_value(void** output) {
+    if (_is_current_empty) {
+        *output = nullptr;
+    } else {
+        *output = &_data[_cur_offset];
+    }
+    return Status::OK();
+}
+
+Status ExplodeSplitTableFunction::close() {
+    return Status::OK();
+}
+
+Status ExplodeSplitTableFunction::forward(bool* eos) {
+    if (_is_current_empty) {
+        *eos = true;
+        _eos = true;
+    } else {
+        ++_cur_offset;
+        if (_cur_offset == _cur_size) {
+            *eos = true;
+            _eos = true;
+        } else {
+            *eos = false;
+        }
+    }
+    return Status::OK();
+}
+
+} // namespace doris
diff --git a/be/src/exprs/table_function/explode_split.h b/be/src/exprs/table_function/explode_split.h
new file mode 100644
index 0000000..ad80671
--- /dev/null
+++ b/be/src/exprs/table_function/explode_split.h
@@ -0,0 +1,56 @@
+// 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 "exprs/table_function/table_function.h"
+
+#include "gutil/strings/stringpiece.h"
+#include "runtime/string_value.h"
+
+namespace doris {
+
+class ExplodeSplitTableFunction : public TableFunction {
+public:
+    ExplodeSplitTableFunction();
+    virtual ~ExplodeSplitTableFunction();
+
+    virtual Status prepare() override;
+    virtual Status open() override;
+    virtual Status process(TupleRow* tuple_row) override;
+    virtual Status reset() override;
+    virtual Status get_value(void** output) override;
+    virtual Status close() override;
+
+    virtual Status forward(bool* eos) override;
+
+private:
+
+    // The string value splitted from source, and will be referenced by
+    // table function scan node.
+    // the `_backup` saved the real string entity.
+    std::vector<StringValue> _data;
+    std::vector<std::string> _backup;
+
+    // indicate whether the delimiter is constant.
+    // if true, the constant delimiter will be saved in `_const_delimter`
+    bool _is_delimiter_constant = false;
+    StringPiece _const_delimter;
+
+};
+
+} // namespace doris
diff --git a/be/src/exprs/table_function/table_function.h b/be/src/exprs/table_function/table_function.h
new file mode 100644
index 0000000..a2b8b00
--- /dev/null
+++ b/be/src/exprs/table_function/table_function.h
@@ -0,0 +1,65 @@
+// 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"
+
+namespace doris {
+
+// TODO: think about how to manager memeory consumption of table functions.
+// Currently, the memory allocated from table function is from malloc directly.
+class TableFunctionState {
+};
+
+class ExprContext;
+class TupleRow;
+class TableFunction {
+public:
+    virtual ~TableFunction() {}
+
+    virtual Status prepare() = 0;
+    virtual Status open() = 0;
+    virtual Status process(TupleRow* tuple_row) = 0;
+    virtual Status reset() = 0;
+    virtual Status get_value(void** output) = 0;
+    virtual Status close() = 0;
+
+    virtual Status forward(bool *eos) = 0;
+
+public:
+    bool eos() const { return _eos; }
+
+    void set_expr_context(ExprContext* expr_context) {
+        _expr_context = expr_context;
+    }
+
+protected:
+    std::string _fn_name;
+    ExprContext* _expr_context;
+    // true if there is no more data can be read from this function.
+    bool _eos = false;
+    // true means the function result set from current row is empty(eg, source value is null or empty).
+    // so that when calling reset(), we can do nothing and keep eos as true.
+    bool _is_current_empty = false;
+    // the position of current cursor
+    int64_t _cur_offset = 0;
+    // the size of current result
+    int64_t _cur_size = 0;
+};
+
+} // namespace doris
diff --git a/be/src/exprs/table_function/table_function_factory.cpp b/be/src/exprs/table_function/table_function_factory.cpp
new file mode 100644
index 0000000..fc6ead5
--- /dev/null
+++ b/be/src/exprs/table_function/table_function_factory.cpp
@@ -0,0 +1,48 @@
+// 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/table_function/table_function_factory.h"
+
+#include "common/object_pool.h"
+#include "exprs/table_function/explode_bitmap.h"
+#include "exprs/table_function/explode_json_array.h"
+#include "exprs/table_function/explode_split.h"
+
+namespace doris {
+
+Status TableFunctionFactory::get_fn(const std::string& fn_name, ObjectPool* pool, TableFunction** fn) {
+    if (fn_name == "explode_split") {
+        *fn = pool->add(new ExplodeSplitTableFunction());
+        return Status::OK(); 
+    } else if (fn_name == "explode_bitmap") {
+        *fn = pool->add(new ExplodeBitmapTableFunction());
+        return Status::OK(); 
+    } else if (fn_name == "explode_json_array_int") {
+        *fn = pool->add(new ExplodeJsonArrayTableFunction(ExplodeJsonArrayType::INT));
+        return Status::OK(); 
+    } else if (fn_name == "explode_json_array_double") {
+        *fn = pool->add(new ExplodeJsonArrayTableFunction(ExplodeJsonArrayType::DOUBLE));
+        return Status::OK(); 
+    } else if (fn_name == "explode_json_array_string") {
+        *fn = pool->add(new ExplodeJsonArrayTableFunction(ExplodeJsonArrayType::STRING));
+        return Status::OK(); 
+    } else {
+        return Status::NotSupported("Unknown table function: " + fn_name);
+    }
+}
+
+} // namespace doris
diff --git a/be/src/exprs/table_function/table_function_factory.h b/be/src/exprs/table_function/table_function_factory.h
new file mode 100644
index 0000000..ca5bd05
--- /dev/null
+++ b/be/src/exprs/table_function/table_function_factory.h
@@ -0,0 +1,36 @@
+// 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 "exprs/table_function/table_function_factory.h"
+#include "exprs/table_function/explode_split.h"
+
+#include "common/status.h"
+
+namespace doris {
+
+class ObjectPool;
+class TableFunction;
+class TableFunctionFactory {
+public:
+    TableFunctionFactory() {}
+    ~TableFunctionFactory() {}
+    static Status get_fn(const std::string& fn_name, ObjectPool* pool, TableFunction** fn); 
+};
+
+} // namespace doris
diff --git a/be/src/runtime/descriptors.h b/be/src/runtime/descriptors.h
index e201d84..d668d42 100644
--- a/be/src/runtime/descriptors.h
+++ b/be/src/runtime/descriptors.h
@@ -108,7 +108,6 @@ private:
     friend class TupleDescriptor;
     friend class SchemaScanner;
     friend class OlapTableSchemaParam;
-    friend class TupleDescriptor;
 
     const SlotId _id;
     const TypeDescriptor _type;
diff --git a/be/src/util/bitmap_value.h b/be/src/util/bitmap_value.h
index a3c54e0..7259685 100644
--- a/be/src/util/bitmap_value.h
+++ b/be/src/util/bitmap_value.h
@@ -1134,6 +1134,7 @@ inline Roaring64MapSetBitForwardIterator Roaring64Map::end() const {
 // Represent the in-memory and on-disk structure of Doris's BITMAP data type.
 // Optimize for the case where the bitmap contains 0 or 1 element which is common
 // for streaming load scenario.
+class BitmapValueIterator;
 class BitmapValue {
 public:
     // Construct an empty bitmap.
@@ -1692,6 +1693,13 @@ public:
         return count;
     }
 
+    // Implement an iterator for convenience
+    friend class BitmapValueIterator;
+    typedef BitmapValueIterator b_iterator;
+
+    b_iterator begin() const;
+    b_iterator end() const;
+
 private:
     void _convert_to_smaller_type() {
         if (_type == BITMAP) {
@@ -1717,6 +1725,128 @@ private:
     BitmapDataType _type;
 };
 
+// A simple implement of bitmap value iterator(Read only)
+// Usage:
+//  BitmapValueIterator iter = bitmap_value.begin();
+//  BitmapValueIterator end = bitmap_value.end();
+//  for (; iter != end(); ++iter) {
+//      uint64_t v = *iter;
+//      ... do something with "v" ... 
+//  }
+class BitmapValueIterator {
+public:
+    BitmapValueIterator()
+       : _bitmap(BitmapValue()) {
+    }
+
+    BitmapValueIterator(const BitmapValue& bitmap, bool end = false)
+        : _bitmap(bitmap), _end(end) {
+
+        switch (_bitmap._type) {
+            case BitmapValue::BitmapDataType::EMPTY:
+                _end = true;
+                break;
+            case BitmapValue::BitmapDataType::SINGLE:
+                _sv = _bitmap._sv;
+                break;
+            case BitmapValue::BitmapDataType::BITMAP:
+                _iter = new detail::Roaring64MapSetBitForwardIterator(_bitmap._bitmap, _end);
+                break;
+            default:
+                CHECK(false) << _bitmap._type;
+        }
+    }
+
+    BitmapValueIterator(const BitmapValueIterator& other)
+        : _bitmap(other._bitmap), _iter(other._iter), _sv(other._sv), _end(other._end) {
+    }
+
+    ~BitmapValueIterator() {
+        if (_iter != nullptr) {
+            delete _iter;
+            _iter = nullptr;
+        }
+    }
+
+    uint64_t operator*() const {
+        CHECK(!_end) << "should not get value of end iterator";
+        switch (_bitmap._type) {
+            case BitmapValue::BitmapDataType::SINGLE:
+                return _sv;
+            case BitmapValue::BitmapDataType::BITMAP:
+                return *(*_iter);
+            default:
+                CHECK(false) << _bitmap._type;
+        }
+        return 0;
+    }
+
+    BitmapValueIterator& operator++() { // ++i, must returned inc. value
+        CHECK(!_end) << "should not forward when iterator ends";
+        switch (_bitmap._type) {
+            case BitmapValue::BitmapDataType::SINGLE:
+                _end = true;
+                break;
+            case BitmapValue::BitmapDataType::BITMAP:
+                ++(*_iter);
+                break;
+            default:
+                CHECK(false) << _bitmap._type;
+        }
+        return *this;
+    }
+
+    BitmapValueIterator operator++(int) { // i++, must return orig. value
+        CHECK(!_end) << "should not forward when iterator ends";
+        BitmapValueIterator orig(*this);
+        switch (_bitmap._type) {
+            case BitmapValue::BitmapDataType::SINGLE:
+                _end = true;
+                break;
+            case BitmapValue::BitmapDataType::BITMAP:
+                ++(*_iter);
+                break;
+            default:
+                CHECK(false) << _bitmap._type;
+        }
+        return orig;
+    }
+
+    bool operator==(const BitmapValueIterator& other) const {
+        if (_end && other._end) return true;
+
+        switch (_bitmap._type) {
+            case BitmapValue::BitmapDataType::EMPTY:
+                return other._bitmap._type == BitmapValue::BitmapDataType::EMPTY;
+            case BitmapValue::BitmapDataType::SINGLE:
+                return _sv == other._sv;
+            case BitmapValue::BitmapDataType::BITMAP:
+                return *_iter == *(other._iter);
+            default:
+                CHECK(false) << _bitmap._type;
+        }
+        return false;
+    }
+
+    bool operator!=(const BitmapValueIterator& other) const {
+        return !(*this == other);
+    }
+
+private:
+    const BitmapValue& _bitmap;
+    detail::Roaring64MapSetBitForwardIterator* _iter = nullptr;
+    uint64_t _sv = 0;
+    bool _end = false;
+};
+
+inline BitmapValueIterator BitmapValue::begin() const {
+    return BitmapValueIterator(*this);
+}
+
+inline BitmapValueIterator BitmapValue::end() const {
+    return BitmapValueIterator(*this, true);
+}
+
 } // namespace doris
 
 #endif //DORIS_BE_SRC_UTIL_BITMAP_VALUE_H
diff --git a/be/test/util/bitmap_value_test.cpp b/be/test/util/bitmap_value_test.cpp
index 9973205..cb2083d 100644
--- a/be/test/util/bitmap_value_test.cpp
+++ b/be/test/util/bitmap_value_test.cpp
@@ -346,6 +346,43 @@ TEST(BitmapValueTest, bitmap_single_convert) {
     bitmap |= bitmap_u;
     ASSERT_EQ(BitmapValue::BITMAP, bitmap._type);
 }
+
+TEST(BitmapValueTest, bitmap_value_iterator_test) {
+    BitmapValue empty;
+    for (auto iter = empty.begin(); iter != empty.end(); ++iter) {
+        // should not goes here
+        ASSERT_TRUE(false);
+    }
+
+    BitmapValue single(1024);
+    for (auto iter = single.begin(); iter != single.end(); ++iter) {
+        ASSERT_EQ(1024, *iter);
+    }
+
+    int i = 0;
+    BitmapValue bitmap({0, 1025, 1026, UINT32_MAX, UINT64_MAX});
+    for (auto iter = bitmap.begin(); iter != bitmap.end(); ++iter, ++i) {
+        switch (i) {
+            case 0:
+                ASSERT_EQ(0, *iter);
+                break;
+            case 1:
+                ASSERT_EQ(1025, *iter);
+                break;
+            case 2:
+                ASSERT_EQ(1026, *iter);
+                break;
+            case 3:
+                ASSERT_EQ(UINT32_MAX, *iter);
+                break;
+            case 4:
+                ASSERT_EQ(UINT64_MAX, *iter);
+                break;
+            default:
+                ASSERT_TRUE(false); 
+        }
+    }
+}
 } // namespace doris
 
 int main(int argc, char** argv) {
diff --git a/docs/.vuepress/sidebar/en.js b/docs/.vuepress/sidebar/en.js
index 1001833..93966f2 100644
--- a/docs/.vuepress/sidebar/en.js
+++ b/docs/.vuepress/sidebar/en.js
@@ -435,6 +435,15 @@ module.exports = [
             directoryPath: "hash-functions/",
             children: ["murmur_hash3_32"],
           },
+          {
+            title: "table functions",
+            directoryPath: "table-functions/",
+            children: [
+              "explode-bitmap",
+              "explode-split",
+              "explode-json-array"
+            ],
+          },
           "window-function",
           "cast",
           "digital-masking",
@@ -585,6 +594,7 @@ module.exports = [
               "alter-routine-load",
               "insert",
               "UPDATE",
+              "lateral-view",
             ],
           },
           {
diff --git a/docs/.vuepress/sidebar/zh-CN.js b/docs/.vuepress/sidebar/zh-CN.js
index a3dda8e..9ef56f1 100644
--- a/docs/.vuepress/sidebar/zh-CN.js
+++ b/docs/.vuepress/sidebar/zh-CN.js
@@ -439,6 +439,15 @@ module.exports = [
             directoryPath: "hash-functions/",
             children: ["murmur_hash3_32"],
           },
+          {
+            title: "table functions",
+            directoryPath: "table-functions/",
+            children: [
+              "explode-bitmap",
+              "explode-split",
+              "explode-json-array"
+            ],
+          },
           "window-function",
           "cast",
           "digital-masking",
@@ -588,6 +597,7 @@ module.exports = [
               "alter-routine-load",
               "insert",
               "UPDATE",
+              "lateral-view",
             ],
           },
           {
diff --git a/docs/en/sql-reference/sql-functions/table-functions/explode-bitmap.md b/docs/en/sql-reference/sql-functions/table-functions/explode-bitmap.md
new file mode 100644
index 0000000..8863928
--- /dev/null
+++ b/docs/en/sql-reference/sql-functions/table-functions/explode-bitmap.md
@@ -0,0 +1,157 @@
+---
+{
+    "title": "explode_bitmap",
+    "language": "en"
+}
+---
+
+<!--
+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.
+-->
+
+# explode_bitmap
+
+## description
+
+Table functions must be used in conjunction with Lateral View.
+
+Expand a bitmap type.
+
+grammar:
+
+```
+explode_bitmap(bitmap)
+```
+
+## example
+
+Original table data:
+
+```
+mysql> select k1 from example1 order by k1;
++------+
+| k1   |
++------+
+|    1 |
+|    2 |
+|    3 |
+|    4 |
+|    5 |
+|    6 |
++------+
+```
+
+Lateral View:
+
+```
+mysql> select k1, e1 from example1 lateral view explode_bitmap(bitmap_empty()) tmp1 as e1 order by k1, e1;
++------+------+
+| k1   | e1   |
++------+------+
+|    1 | NULL |
+|    2 | NULL |
+|    3 | NULL |
+|    4 | NULL |
+|    5 | NULL |
+|    6 | NULL |
++------+------+
+
+mysql> select k1, e1 from example1 lateral view explode_bitmap(bitmap_from_string("1")) tmp1 as e1 order by k1, e1;
++------+------+
+| k1   | e1   |
++------+------+
+|    1 |    1 |
+|    2 |    1 |
+|    3 |    1 |
+|    4 |    1 |
+|    5 |    1 |
+|    6 |    1 |
++------+------+
+
+mysql> select k1, e1 from example1 lateral view explode_bitmap(bitmap_from_string("1,2")) tmp1 as e1 order by k1, e1;
++------+------+
+| k1   | e1   |
++------+------+
+|    1 |    1 |
+|    1 |    2 |
+|    2 |    1 |
+|    2 |    2 |
+|    3 |    1 |
+|    3 |    2 |
+|    4 |    1 |
+|    4 |    2 |
+|    5 |    1 |
+|    5 |    2 |
+|    6 |    1 |
+|    6 |    2 |
++------+------+
+
+mysql> select k1, e1 from example1 lateral view explode_bitmap(bitmap_from_string("1,1000")) tmp1 as e1 order by k1, e1;
++------+------+
+| k1   | e1   |
++------+------+
+|    1 |    1 |
+|    1 | 1000 |
+|    2 |    1 |
+|    2 | 1000 |
+|    3 |    1 |
+|    3 | 1000 |
+|    4 |    1 |
+|    4 | 1000 |
+|    5 |    1 |
+|    5 | 1000 |
+|    6 |    1 |
+|    6 | 1000 |
++------+------+
+
+mysql> select k1, e1, e2 from example1
+lateral view explode_bitmap(bitmap_from_string("1,1000")) tmp1 as e1
+lateral view explode_split("a,b", ",") tmp2 as e2 order by k1, e1, e2;
++------+------+------+
+| k1   | e1   | e2   |
++------+------+------+
+|    1 |    1 | a    |
+|    1 |    1 | b    |
+|    1 | 1000 | a    |
+|    1 | 1000 | b    |
+|    2 |    1 | a    |
+|    2 |    1 | b    |
+|    2 | 1000 | a    |
+|    2 | 1000 | b    |
+|    3 |    1 | a    |
+|    3 |    1 | b    |
+|    3 | 1000 | a    |
+|    3 | 1000 | b    |
+|    4 |    1 | a    |
+|    4 |    1 | b    |
+|    4 | 1000 | a    |
+|    4 | 1000 | b    |
+|    5 |    1 | a    |
+|    5 |    1 | b    |
+|    5 | 1000 | a    |
+|    5 | 1000 | b    |
+|    6 |    1 | a    |
+|    6 |    1 | b    |
+|    6 | 1000 | a    |
+|    6 | 1000 | b    |
++------+------+------+
+```
+
+## keyword
+
+    explode_bitmap
\ No newline at end of file
diff --git a/docs/en/sql-reference/sql-functions/table-functions/explode-json-array.md b/docs/en/sql-reference/sql-functions/table-functions/explode-json-array.md
new file mode 100644
index 0000000..e9c136d
--- /dev/null
+++ b/docs/en/sql-reference/sql-functions/table-functions/explode-json-array.md
@@ -0,0 +1,286 @@
+---
+{
+    "title": "explode_json_array",
+    "language": "en"
+}
+---
+
+<!--
+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.
+-->
+
+# `explode_json_array`
+
+## description
+
+Table functions must be used in conjunction with Lateral View.
+
+Expand a json array. According to the array element type, there are three function names. Corresponding to integer, floating point and string arrays respectively.
+
+grammar:
+
+```
+explode_json_array_int(json_str)
+explode_json_array_double(json_str)
+explode_json_array_string(json_str)
+```
+
+## example
+
+Original table data:
+
+```
+mysql> select k1 from example1 order by k1;
++------+
+| k1   |
++------+
+|    1 |
+|    2 |
+|    3 |
+|    4 |
+|    5 |
+|    6 |
++------+
+```
+
+Lateral View:
+
+```
+mysql> select k1, e1 from example1 lateral view explode_json_array_int('[]') tmp1 as e1 order by k1, e1;
++------+------+
+| k1   | e1   |
++------+------+
+|    1 | NULL |
+|    2 | NULL |
+|    3 | NULL |
++------+------+
+
+mysql> select k1, e1 from example1 lateral view explode_json_array_int('[1,2,3]') tmp1 as e1 order by k1, e1;
++------+------+
+| k1   | e1   |
++------+------+
+|    1 |    1 |
+|    1 |    2 |
+|    1 |    3 |
+|    2 |    1 |
+|    2 |    2 |
+|    2 |    3 |
+|    3 |    1 |
+|    3 |    2 |
+|    3 |    3 |
++------+------+
+
+mysql> select k1, e1 from example1 lateral view explode_json_array_int('[1,"b",3]') tmp1 as e1 order by k1, e1;
++------+------+
+| k1   | e1   |
++------+------+
+|    1 | NULL |
+|    1 |    1 |
+|    1 |    3 |
+|    2 | NULL |
+|    2 |    1 |
+|    2 |    3 |
+|    3 | NULL |
+|    3 |    1 |
+|    3 |    3 |
++------+------+
+
+mysql> select k1, e1 from example1 lateral view explode_json_array_int('["a","b","c"]') tmp1 as e1 order by k1, e1;
++------+------+
+| k1   | e1   |
++------+------+
+|    1 | NULL |
+|    1 | NULL |
+|    1 | NULL |
+|    2 | NULL |
+|    2 | NULL |
+|    2 | NULL |
+|    3 | NULL |
+|    3 | NULL |
+|    3 | NULL |
++------+------+
+
+mysql> select k1, e1 from example1 lateral view explode_json_array_int('{"a": 3}') tmp1 as e1 order by k1, e1;
++------+------+
+| k1   | e1   |
++------+------+
+|    1 | NULL |
+|    2 | NULL |
+|    3 | NULL |
++------+------+
+
+mysql> select k1, e1 from example1 lateral view explode_json_array_double('[]') tmp1 as e1 order by k1, e1;
++------+------+
+| k1   | e1   |
++------+------+
+|    1 | NULL |
+|    2 | NULL |
+|    3 | NULL |
++------+------+
+
+mysql> select k1, e1 from example1 lateral view explode_json_array_double('[1,2,3]') tmp1 as e1 order by k1, e1;
++------+------+
+| k1   | e1   |
++------+------+
+|    1 | NULL |
+|    1 | NULL |
+|    1 | NULL |
+|    2 | NULL |
+|    2 | NULL |
+|    2 | NULL |
+|    3 | NULL |
+|    3 | NULL |
+|    3 | NULL |
++------+------+
+
+mysql> select k1, e1 from example1 lateral view explode_json_array_double('[1,"b",3]') tmp1 as e1 order by k1, e1;
++------+------+
+| k1   | e1   |
++------+------+
+|    1 | NULL |
+|    1 | NULL |
+|    1 | NULL |
+|    2 | NULL |
+|    2 | NULL |
+|    2 | NULL |
+|    3 | NULL |
+|    3 | NULL |
+|    3 | NULL |
++------+------+
+
+mysql> select k1, e1 from example1 lateral view explode_json_array_double('[1.0,2.0,3.0]') tmp1 as e1 order by k1, e1;
++------+------+
+| k1   | e1   |
++------+------+
+|    1 |    1 |
+|    1 |    2 |
+|    1 |    3 |
+|    2 |    1 |
+|    2 |    2 |
+|    2 |    3 |
+|    3 |    1 |
+|    3 |    2 |
+|    3 |    3 |
++------+------+
+
+mysql> select k1, e1 from example1 lateral view explode_json_array_double('[1,"b",3]') tmp1 as e1 order by k1, e1;
++------+------+
+| k1   | e1   |
++------+------+
+|    1 | NULL |
+|    1 | NULL |
+|    1 | NULL |
+|    2 | NULL |
+|    2 | NULL |
+|    2 | NULL |
+|    3 | NULL |
+|    3 | NULL |
+|    3 | NULL |
++------+------+
+
+mysql> select k1, e1 from example1 lateral view explode_json_array_double('["a","b","c"]') tmp1 as e1 order by k1, e1;
++------+------+
+| k1   | e1   |
++------+------+
+|    1 | NULL |
+|    1 | NULL |
+|    1 | NULL |
+|    2 | NULL |
+|    2 | NULL |
+|    2 | NULL |
+|    3 | NULL |
+|    3 | NULL |
+|    3 | NULL |
++------+------+
+
+mysql> select k1, e1 from example1 lateral view explode_json_array_double('{"a": 3}') tmp1 as e1 order by k1, e1;
++------+------+
+| k1   | e1   |
++------+------+
+|    1 | NULL |
+|    2 | NULL |
+|    3 | NULL |
++------+------+
+
+mysql> select k1, e1 from example1 lateral view explode_json_array_string('[]') tmp1 as e1 order by k1, e1;
++------+------+
+| k1   | e1   |
++------+------+
+|    1 | NULL |
+|    2 | NULL |
+|    3 | NULL |
++------+------+
+
+mysql> select k1, e1 from example1 lateral view explode_json_array_string('[1.0,2.0,3.0]') tmp1 as e1 order by k1, e1;
++------+----------+
+| k1   | e1       |
++------+----------+
+|    1 | 1.000000 |
+|    1 | 2.000000 |
+|    1 | 3.000000 |
+|    2 | 1.000000 |
+|    2 | 2.000000 |
+|    2 | 3.000000 |
+|    3 | 1.000000 |
+|    3 | 2.000000 |
+|    3 | 3.000000 |
++------+----------+
+
+mysql> select k1, e1 from example1 lateral view explode_json_array_string('[1,"b",3]') tmp1 as e1 order by k1, e1;
++------+------+
+| k1   | e1   |
++------+------+
+|    1 | 1    |
+|    1 | 3    |
+|    1 | b    |
+|    2 | 1    |
+|    2 | 3    |
+|    2 | b    |
+|    3 | 1    |
+|    3 | 3    |
+|    3 | b    |
++------+------+
+
+mysql> select k1, e1 from example1 lateral view explode_json_array_string('["a","b","c"]') tmp1 as e1 order by k1, e1;
++------+------+
+| k1   | e1   |
++------+------+
+|    1 | a    |
+|    1 | b    |
+|    1 | c    |
+|    2 | a    |
+|    2 | b    |
+|    2 | c    |
+|    3 | a    |
+|    3 | b    |
+|    3 | c    |
++------+------+
+
+mysql> select k1, e1 from example1 lateral view explode_json_array_string('{"a": 3}') tmp1 as e1 order by k1, e1;
++------+------+
+| k1   | e1   |
++------+------+
+|    1 | NULL |
+|    2 | NULL |
+|    3 | NULL |
++------+------+
+```
+
+## keyword
+
+    explode_json_array
\ No newline at end of file
diff --git a/docs/en/sql-reference/sql-functions/table-functions/explode-split.md b/docs/en/sql-reference/sql-functions/table-functions/explode-split.md
new file mode 100644
index 0000000..0557b90
--- /dev/null
+++ b/docs/en/sql-reference/sql-functions/table-functions/explode-split.md
@@ -0,0 +1,112 @@
+---
+{
+    "title": "explode_split",
+    "language": "en"
+}
+---
+
+<!--
+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.
+-->
+
+# explode_split
+
+## description
+
+Table functions must be used in conjunction with Lateral View.
+
+Split a string into multiple substrings according to the specified delimiter.
+
+grammar:
+
+```
+explode_split(str, delimiter)
+```
+
+## example
+
+Original table data:
+
+```
+mysql> select * from example1 order by k1;
++------+---------+
+| k1   | k2      |
++------+---------+
+|    1 |         |
+|    2 | NULL    |
+|    3 | ,       |
+|    4 | 1       |
+|    5 | 1,2,3   |
+|    6 | a, b, c |
++------+---------+
+```
+
+Lateral View:
+
+```
+mysql> select k1, e1 from example1 lateral view explode_split(k2, ',') tmp1 as e1 where k1 = 1 order by k1, e1;
++------+------+
+| k1   | e1   |
++------+------+
+|    1 |      |
++------+------+
+
+mysql> select k1, e1 from example1 lateral view explode_split(k2, ',') tmp1 as e1 where k1 = 2 order by k1, e1;
++------+------+
+| k1   | e1   |
++------+------+
+|    2 | NULL |
++------+------+
+
+mysql> select k1, e1 from example1 lateral view explode_split(k2, ',') tmp1 as e1 where k1 = 3 order by k1, e1;
++------+------+
+| k1   | e1   |
++------+------+
+|    3 |      |
+|    3 |      |
++------+------+
+
+mysql> select k1, e1 from example1 lateral view explode_split(k2, ',') tmp1 as e1 where k1 = 4 order by k1, e1;
++------+------+
+| k1   | e1   |
++------+------+
+|    4 | 1    |
++------+------+
+
+mysql> select k1, e1 from example1 lateral view explode_split(k2, ',') tmp1 as e1 where k1 = 5 order by k1, e1;
++------+------+
+| k1   | e1   |
++------+------+
+|    5 | 1    |
+|    5 | 2    |
+|    5 | 3    |
++------+------+
+
+mysql> select k1, e1 from example1 lateral view explode_split(k2, ',') tmp1 as e1 where k1 = 6 order by k1, e1;
++------+------+
+| k1   | e1   |
++------+------+
+|    6 |  b   |
+|    6 |  c   |
+|    6 | a    |
++------+------+
+```
+
+## keyword
+
+    explode_split
\ No newline at end of file
diff --git a/docs/en/sql-reference/sql-statements/Data Manipulation/lateral-view.md b/docs/en/sql-reference/sql-statements/Data Manipulation/lateral-view.md
new file mode 100644
index 0000000..1adacea
--- /dev/null
+++ b/docs/en/sql-reference/sql-statements/Data Manipulation/lateral-view.md	
@@ -0,0 +1,94 @@
+---
+{
+    "title": "Lateral View",
+    "language": "en"
+}
+---
+
+<!--
+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.
+-->
+
+# Lateral View
+
+## description
+
+Lateral view syntax can be used with Table Function to fulfill the requirement of expanding one row of data into multiple rows (column to rows).
+
+grammar:
+
+```
+...
+FROM table_name
+lateral_view_ref[ lateral_view_ref ...]
+
+lateral_view_ref:
+
+LATERAL VIEW table_function(...) view_alias as col_name
+```
+    
+The Lateral view clause must follow the table name or subquery. Can contain multiple Lateral view clauses. `view_alias` is the name of the corresponding Lateral View. `col_name` is the name of the column produced by the table function `table_function`.
+
+Table functions currently supported:
+
+1. `explode_split`
+2. `explode_bitmap`
+3. `explode_json_array`
+
+For specific function descriptions, please refer to the corresponding syntax help documentation.
+
+The data in the table will be Cartesian product with the result set produced by each Lateral View and then return to the upper level.
+
+## example
+
+Here, only the syntax example of Lateral View is given. For the specific meaning and output result description, please refer to the help document of the corresponding table function.
+
+1.
+
+```
+select k1, e1 from tbl1
+lateral view explode_split(v1,',') tmp1 as e1 where e1 = "abc";
+```
+
+2.
+
+```
+select k1, e1, e2 from tbl2
+lateral view explode_split(v1,',') tmp1 as e1
+lateral view explode_bitmap(bitmap1) tmp2 as e2
+where e2> 3;
+```
+
+3.
+
+```
+select k1, e1, e2 from tbl3
+lateral view explode_json_array_int("[1,2,3]") tmp1 as e1
+lateral view explode_bitmap(bitmap_from_string("4,5,6")) tmp2 as e2;
+```
+
+4.
+
+```
+select k1, e1 from (select k1, bitmap_union(members) as x from tbl1 where k1=10000 group by k1)tmp1
+lateral view explode_bitmap(x) tmp2 as e1;
+```
+
+## keyword
+
+    lateral view
diff --git a/docs/zh-CN/sql-reference/sql-functions/table-functions/explode-bitmap.md b/docs/zh-CN/sql-reference/sql-functions/table-functions/explode-bitmap.md
new file mode 100644
index 0000000..99f7971
--- /dev/null
+++ b/docs/zh-CN/sql-reference/sql-functions/table-functions/explode-bitmap.md
@@ -0,0 +1,157 @@
+---
+{
+    "title": "explode_bitmap",
+    "language": "zh-CN"
+}
+---
+
+<!--
+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.
+-->
+
+# explode_bitmap
+
+## description
+
+表函数,需配合 Lateral View 使用。
+
+展开一个bitmap类型。
+
+语法:
+
+```
+explode_bitmap(bitmap)
+```
+
+## example
+
+原表数据:
+
+```
+mysql> select k1 from example1 order by k1;
++------+
+| k1   |
++------+
+|    1 |
+|    2 |
+|    3 |
+|    4 |
+|    5 |
+|    6 |
++------+
+```
+
+Lateral View:
+
+```
+mysql> select k1, e1 from example1 lateral view explode_bitmap(bitmap_empty()) tmp1 as e1 order by k1, e1;
++------+------+
+| k1   | e1   |
++------+------+
+|    1 | NULL |
+|    2 | NULL |
+|    3 | NULL |
+|    4 | NULL |
+|    5 | NULL |
+|    6 | NULL |
++------+------+
+
+mysql> select k1, e1 from example1 lateral view explode_bitmap(bitmap_from_string("1")) tmp1 as e1 order by k1, e1;
++------+------+
+| k1   | e1   |
++------+------+
+|    1 |    1 |
+|    2 |    1 |
+|    3 |    1 |
+|    4 |    1 |
+|    5 |    1 |
+|    6 |    1 |
++------+------+
+
+mysql> select k1, e1 from example1 lateral view explode_bitmap(bitmap_from_string("1,2")) tmp1 as e1 order by k1, e1;
++------+------+
+| k1   | e1   |
++------+------+
+|    1 |    1 |
+|    1 |    2 |
+|    2 |    1 |
+|    2 |    2 |
+|    3 |    1 |
+|    3 |    2 |
+|    4 |    1 |
+|    4 |    2 |
+|    5 |    1 |
+|    5 |    2 |
+|    6 |    1 |
+|    6 |    2 |
++------+------+
+
+mysql> select k1, e1 from example1 lateral view explode_bitmap(bitmap_from_string("1,1000")) tmp1 as e1 order by k1, e1;
++------+------+
+| k1   | e1   |
++------+------+
+|    1 |    1 |
+|    1 | 1000 |
+|    2 |    1 |
+|    2 | 1000 |
+|    3 |    1 |
+|    3 | 1000 |
+|    4 |    1 |
+|    4 | 1000 |
+|    5 |    1 |
+|    5 | 1000 |
+|    6 |    1 |
+|    6 | 1000 |
++------+------+
+
+mysql> select k1, e1, e2 from example1
+lateral view explode_bitmap(bitmap_from_string("1,1000")) tmp1 as e1
+lateral view explode_split("a,b", ",") tmp2 as e2 order by k1, e1, e2;
++------+------+------+
+| k1   | e1   | e2   |
++------+------+------+
+|    1 |    1 | a    |
+|    1 |    1 | b    |
+|    1 | 1000 | a    |
+|    1 | 1000 | b    |
+|    2 |    1 | a    |
+|    2 |    1 | b    |
+|    2 | 1000 | a    |
+|    2 | 1000 | b    |
+|    3 |    1 | a    |
+|    3 |    1 | b    |
+|    3 | 1000 | a    |
+|    3 | 1000 | b    |
+|    4 |    1 | a    |
+|    4 |    1 | b    |
+|    4 | 1000 | a    |
+|    4 | 1000 | b    |
+|    5 |    1 | a    |
+|    5 |    1 | b    |
+|    5 | 1000 | a    |
+|    5 | 1000 | b    |
+|    6 |    1 | a    |
+|    6 |    1 | b    |
+|    6 | 1000 | a    |
+|    6 | 1000 | b    |
++------+------+------+
+```
+
+## keyword
+
+    explode_bitmap
\ No newline at end of file
diff --git a/docs/zh-CN/sql-reference/sql-functions/table-functions/explode-json-array.md b/docs/zh-CN/sql-reference/sql-functions/table-functions/explode-json-array.md
new file mode 100644
index 0000000..8332d6a
--- /dev/null
+++ b/docs/zh-CN/sql-reference/sql-functions/table-functions/explode-json-array.md
@@ -0,0 +1,286 @@
+---
+{
+    "title": "explode_json_array",
+    "language": "zh-CN"
+}
+---
+
+<!--
+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.
+-->
+
+# `explode_json_array`
+
+## description
+
+表函数,需配合 Lateral View 使用。
+
+展开一个 json 数组。根据数组元素类型,有三种函数名称。分别对应整型、浮点和字符串数组。
+
+语法:
+
+```
+explode_json_array_int(json_str)
+explode_json_array_double(json_str)
+explode_json_array_string(json_str)
+```
+
+## example
+
+原表数据:
+
+```
+mysql> select k1 from example1 order by k1;
++------+
+| k1   |
++------+
+|    1 |
+|    2 |
+|    3 |
+|    4 |
+|    5 |
+|    6 |
++------+
+```
+
+Lateral View:
+
+```
+mysql> select k1, e1 from example1 lateral view explode_json_array_int('[]') tmp1 as e1 order by k1, e1;
++------+------+
+| k1   | e1   |
++------+------+
+|    1 | NULL |
+|    2 | NULL |
+|    3 | NULL |
++------+------+
+
+mysql> select k1, e1 from example1 lateral view explode_json_array_int('[1,2,3]') tmp1 as e1 order by k1, e1;
++------+------+
+| k1   | e1   |
++------+------+
+|    1 |    1 |
+|    1 |    2 |
+|    1 |    3 |
+|    2 |    1 |
+|    2 |    2 |
+|    2 |    3 |
+|    3 |    1 |
+|    3 |    2 |
+|    3 |    3 |
++------+------+
+
+mysql> select k1, e1 from example1 lateral view explode_json_array_int('[1,"b",3]') tmp1 as e1 order by k1, e1;
++------+------+
+| k1   | e1   |
++------+------+
+|    1 | NULL |
+|    1 |    1 |
+|    1 |    3 |
+|    2 | NULL |
+|    2 |    1 |
+|    2 |    3 |
+|    3 | NULL |
+|    3 |    1 |
+|    3 |    3 |
++------+------+
+
+mysql> select k1, e1 from example1 lateral view explode_json_array_int('["a","b","c"]') tmp1 as e1 order by k1, e1;
++------+------+
+| k1   | e1   |
++------+------+
+|    1 | NULL |
+|    1 | NULL |
+|    1 | NULL |
+|    2 | NULL |
+|    2 | NULL |
+|    2 | NULL |
+|    3 | NULL |
+|    3 | NULL |
+|    3 | NULL |
++------+------+
+
+mysql> select k1, e1 from example1 lateral view explode_json_array_int('{"a": 3}') tmp1 as e1 order by k1, e1;
++------+------+
+| k1   | e1   |
++------+------+
+|    1 | NULL |
+|    2 | NULL |
+|    3 | NULL |
++------+------+
+
+mysql> select k1, e1 from example1 lateral view explode_json_array_double('[]') tmp1 as e1 order by k1, e1;
++------+------+
+| k1   | e1   |
++------+------+
+|    1 | NULL |
+|    2 | NULL |
+|    3 | NULL |
++------+------+
+
+mysql> select k1, e1 from example1 lateral view explode_json_array_double('[1,2,3]') tmp1 as e1 order by k1, e1;
++------+------+
+| k1   | e1   |
++------+------+
+|    1 | NULL |
+|    1 | NULL |
+|    1 | NULL |
+|    2 | NULL |
+|    2 | NULL |
+|    2 | NULL |
+|    3 | NULL |
+|    3 | NULL |
+|    3 | NULL |
++------+------+
+
+mysql> select k1, e1 from example1 lateral view explode_json_array_double('[1,"b",3]') tmp1 as e1 order by k1, e1;
++------+------+
+| k1   | e1   |
++------+------+
+|    1 | NULL |
+|    1 | NULL |
+|    1 | NULL |
+|    2 | NULL |
+|    2 | NULL |
+|    2 | NULL |
+|    3 | NULL |
+|    3 | NULL |
+|    3 | NULL |
++------+------+
+
+mysql> select k1, e1 from example1 lateral view explode_json_array_double('[1.0,2.0,3.0]') tmp1 as e1 order by k1, e1;
++------+------+
+| k1   | e1   |
++------+------+
+|    1 |    1 |
+|    1 |    2 |
+|    1 |    3 |
+|    2 |    1 |
+|    2 |    2 |
+|    2 |    3 |
+|    3 |    1 |
+|    3 |    2 |
+|    3 |    3 |
++------+------+
+
+mysql> select k1, e1 from example1 lateral view explode_json_array_double('[1,"b",3]') tmp1 as e1 order by k1, e1;
++------+------+
+| k1   | e1   |
++------+------+
+|    1 | NULL |
+|    1 | NULL |
+|    1 | NULL |
+|    2 | NULL |
+|    2 | NULL |
+|    2 | NULL |
+|    3 | NULL |
+|    3 | NULL |
+|    3 | NULL |
++------+------+
+
+mysql> select k1, e1 from example1 lateral view explode_json_array_double('["a","b","c"]') tmp1 as e1 order by k1, e1;
++------+------+
+| k1   | e1   |
++------+------+
+|    1 | NULL |
+|    1 | NULL |
+|    1 | NULL |
+|    2 | NULL |
+|    2 | NULL |
+|    2 | NULL |
+|    3 | NULL |
+|    3 | NULL |
+|    3 | NULL |
++------+------+
+
+mysql> select k1, e1 from example1 lateral view explode_json_array_double('{"a": 3}') tmp1 as e1 order by k1, e1;
++------+------+
+| k1   | e1   |
++------+------+
+|    1 | NULL |
+|    2 | NULL |
+|    3 | NULL |
++------+------+
+
+mysql> select k1, e1 from example1 lateral view explode_json_array_string('[]') tmp1 as e1 order by k1, e1;
++------+------+
+| k1   | e1   |
++------+------+
+|    1 | NULL |
+|    2 | NULL |
+|    3 | NULL |
++------+------+
+
+mysql> select k1, e1 from example1 lateral view explode_json_array_string('[1.0,2.0,3.0]') tmp1 as e1 order by k1, e1;
++------+----------+
+| k1   | e1       |
++------+----------+
+|    1 | 1.000000 |
+|    1 | 2.000000 |
+|    1 | 3.000000 |
+|    2 | 1.000000 |
+|    2 | 2.000000 |
+|    2 | 3.000000 |
+|    3 | 1.000000 |
+|    3 | 2.000000 |
+|    3 | 3.000000 |
++------+----------+
+
+mysql> select k1, e1 from example1 lateral view explode_json_array_string('[1,"b",3]') tmp1 as e1 order by k1, e1;
++------+------+
+| k1   | e1   |
++------+------+
+|    1 | 1    |
+|    1 | 3    |
+|    1 | b    |
+|    2 | 1    |
+|    2 | 3    |
+|    2 | b    |
+|    3 | 1    |
+|    3 | 3    |
+|    3 | b    |
++------+------+
+
+mysql> select k1, e1 from example1 lateral view explode_json_array_string('["a","b","c"]') tmp1 as e1 order by k1, e1;
++------+------+
+| k1   | e1   |
++------+------+
+|    1 | a    |
+|    1 | b    |
+|    1 | c    |
+|    2 | a    |
+|    2 | b    |
+|    2 | c    |
+|    3 | a    |
+|    3 | b    |
+|    3 | c    |
++------+------+
+
+mysql> select k1, e1 from example1 lateral view explode_json_array_string('{"a": 3}') tmp1 as e1 order by k1, e1;
++------+------+
+| k1   | e1   |
++------+------+
+|    1 | NULL |
+|    2 | NULL |
+|    3 | NULL |
++------+------+
+```
+
+## keyword
+
+    explode_json_array
\ No newline at end of file
diff --git a/docs/zh-CN/sql-reference/sql-functions/table-functions/explode-split.md b/docs/zh-CN/sql-reference/sql-functions/table-functions/explode-split.md
new file mode 100644
index 0000000..d7c4031
--- /dev/null
+++ b/docs/zh-CN/sql-reference/sql-functions/table-functions/explode-split.md
@@ -0,0 +1,112 @@
+---
+{
+    "title": "explode_split",
+    "language": "zh-CN"
+}
+---
+
+<!--
+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.
+-->
+
+# explode_split
+
+## description
+
+表函数,需配合 Lateral View 使用。
+
+将一个字符串按指定的分隔符分割成多个子串。
+
+语法:
+
+```
+explode_split(str, delimiter)
+```
+
+## example
+
+原表数据:
+
+```
+mysql> select * from example1 order by k1;
++------+---------+
+| k1   | k2      |
++------+---------+
+|    1 |         |
+|    2 | NULL    |
+|    3 | ,       |
+|    4 | 1       |
+|    5 | 1,2,3   |
+|    6 | a, b, c |
++------+---------+
+```
+
+Lateral View:
+
+```
+mysql> select k1, e1 from example1 lateral view explode_split(k2, ',') tmp1 as e1 where k1 = 1 order by k1, e1;
++------+------+
+| k1   | e1   |
++------+------+
+|    1 |      |
++------+------+
+
+mysql> select k1, e1 from example1 lateral view explode_split(k2, ',') tmp1 as e1 where k1 = 2 order by k1, e1;
++------+------+
+| k1   | e1   |
++------+------+
+|    2 | NULL |
++------+------+
+
+mysql> select k1, e1 from example1 lateral view explode_split(k2, ',') tmp1 as e1 where k1 = 3 order by k1, e1;
++------+------+
+| k1   | e1   |
++------+------+
+|    3 |      |
+|    3 |      |
++------+------+
+
+mysql> select k1, e1 from example1 lateral view explode_split(k2, ',') tmp1 as e1 where k1 = 4 order by k1, e1;
++------+------+
+| k1   | e1   |
++------+------+
+|    4 | 1    |
++------+------+
+
+mysql> select k1, e1 from example1 lateral view explode_split(k2, ',') tmp1 as e1 where k1 = 5 order by k1, e1;
++------+------+
+| k1   | e1   |
++------+------+
+|    5 | 1    |
+|    5 | 2    |
+|    5 | 3    |
++------+------+
+
+mysql> select k1, e1 from example1 lateral view explode_split(k2, ',') tmp1 as e1 where k1 = 6 order by k1, e1;
++------+------+
+| k1   | e1   |
++------+------+
+|    6 |  b   |
+|    6 |  c   |
+|    6 | a    |
++------+------+
+```
+
+## keyword
+
+    explode_split
\ No newline at end of file
diff --git a/docs/zh-CN/sql-reference/sql-statements/Data Manipulation/lateral-view.md b/docs/zh-CN/sql-reference/sql-statements/Data Manipulation/lateral-view.md
new file mode 100644
index 0000000..2b6798e
--- /dev/null
+++ b/docs/zh-CN/sql-reference/sql-statements/Data Manipulation/lateral-view.md	
@@ -0,0 +1,94 @@
+---
+{
+    "title": "Lateral View",
+    "language": "zh-CN"
+}
+---
+
+<!--
+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.
+-->
+
+# Lateral View
+
+## description
+
+Lateral view 语法可以搭配 Table Function,完成将一行数据扩展成多行(列转行)的需求。
+
+语法:
+
+```
+...
+FROM table_name
+lateral_view_ref[ lateral_view_ref ...]
+
+lateral_view_ref:
+
+LATERAL VIEW table_function(...) view_alias as col_name
+```
+    
+Lateral view 子句必须跟随在表名或子查询之后。可以包含多个 Lateral view 子句。`view_alias` 是对应 Lateral View 的名称。`col_name` 是表函数 `table_function` 产出的列名。
+
+目前支持的表函数:
+
+1. `explode_split`
+2. `explode_bitmap`
+3. `explode_json_array`
+
+具体函数说明可参阅对应语法帮助文档。
+
+table 中的数据会和各个 Lateral View 产生的结果集做笛卡尔积后返回上层。
+
+## example
+
+这里只给出 Lateral View 的语法示例,具体含义和产出的结果说明,可参阅对应表函数帮助文档。
+
+1.
+
+```
+select k1, e1 from tbl1
+lateral view explode_split(v1, ',') tmp1 as e1 where e1 = "abc";
+```
+
+2.
+
+```
+select k1, e1, e2 from tbl2
+lateral view explode_split(v1, ',') tmp1 as e1
+lateral view explode_bitmap(bitmap1) tmp2 as e2
+where e2 > 3;
+```
+
+3.
+
+```
+select k1, e1, e2 from tbl3
+lateral view explode_json_array_int("[1,2,3]") tmp1 as e1
+lateral view explode_bitmap(bitmap_from_string("4,5,6")) tmp2 as e2;
+```
+
+4.
+
+```
+select k1, e1 from (select k1, bitmap_union(members) as x from tbl1 where k1=10000 group by k1)tmp1
+lateral view explode_bitmap(x) tmp2 as e1;
+```
+
+## keyword
+
+    lateral view
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 76a81c6..87f8919 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
@@ -55,6 +55,7 @@ import java.io.IOException;
 import java.text.StringCharacterIterator;
 import java.util.Arrays;
 import java.util.List;
+
 // TODO: for aggregations, we need to unify the code paths for builtins and UDAs.
 public class FunctionCallExpr extends Expr {
     private static final Logger LOG = LogManager.getLogger(FunctionCallExpr.class);
@@ -84,7 +85,10 @@ public class FunctionCallExpr extends Expr {
     private Expr originStmtFnExpr;
 
     private boolean isRewrote = false;
-    
+
+    public static final String UNKNOWN_TABLE_FUNCTION_MSG = "Currently only support `explode_split`, `explode_bitmap` " +
+            "and `explode_json_array_xx` table functions";
+
     public void setIsAnalyticFnCall(boolean v) {
         isAnalyticFnCall = v;
     }
@@ -127,7 +131,7 @@ public class FunctionCallExpr extends Expr {
     }
 
     private FunctionCallExpr(
-        FunctionName fnName, FunctionParams params, boolean isMergeAggFn) {
+            FunctionName fnName, FunctionParams params, boolean isMergeAggFn) {
         super();
         this.fnName = fnName;
         fnParams = params;
@@ -158,7 +162,7 @@ public class FunctionCallExpr extends Expr {
         super(other);
         fnName = other.fnName;
         isAnalyticFnCall = other.isAnalyticFnCall;
-     //   aggOp = other.aggOp;
+        //   aggOp = other.aggOp;
         // fnParams = other.fnParams;
         // Clone the params in a way that keeps the children_ and the params.exprs()
         // in sync. The children have already been cloned in the super c'tor.
@@ -245,12 +249,12 @@ public class FunctionCallExpr extends Expr {
         }
         if (((FunctionCallExpr) expr).fnParams.isDistinct()) {
             sb.append("DISTINCT ");
-        }  
+        }
         boolean isJsonFunction = false;
         int len = children.size();
         List<String> result = Lists.newArrayList();
         if ((fnName.getFunction().equalsIgnoreCase("json_array")) ||
-            (fnName.getFunction().equalsIgnoreCase("json_object"))) {
+                (fnName.getFunction().equalsIgnoreCase("json_object"))) {
             len = len - 1;
             isJsonFunction = true;
         }
@@ -277,7 +281,7 @@ public class FunctionCallExpr extends Expr {
 
     public boolean isScalarFunction() {
         Preconditions.checkState(fn != null);
-        return fn instanceof ScalarFunction ;
+        return fn instanceof ScalarFunction;
     }
 
     public boolean isAggregateFunction() {
@@ -289,6 +293,7 @@ public class FunctionCallExpr extends Expr {
         Preconditions.checkState(fn != null);
         return fn instanceof BuiltinAggregateFunction && !isAnalyticFnCall;
     }
+
     /**
      * Returns true if this is a call to an aggregate function that returns
      * non-null on an empty input (e.g. count).
@@ -296,7 +301,7 @@ public class FunctionCallExpr extends Expr {
     public boolean returnsNonNullOnEmpty() {
         Preconditions.checkNotNull(fn);
         return fn instanceof AggregateFunction
-            && ((AggregateFunction) fn).returnsNonNullOnEmpty();
+                && ((AggregateFunction) fn).returnsNonNullOnEmpty();
     }
 
     public boolean isDistinct() {
@@ -372,8 +377,8 @@ public class FunctionCallExpr extends Expr {
             }
             return;
         }
-        
-        if(fnName.getFunction().equalsIgnoreCase("json_array")) {
+
+        if (fnName.getFunction().equalsIgnoreCase("json_array")) {
             String res = parseJsonDataType(false);
             if (children.size() == originChildSize) {
                 children.add(new StringLiteral(res));
@@ -381,8 +386,8 @@ public class FunctionCallExpr extends Expr {
             return;
         }
 
-        if(fnName.getFunction().equalsIgnoreCase("json_object")) {
-            if ((children.size()&1) == 1 && (originChildSize == children.size())) {
+        if (fnName.getFunction().equalsIgnoreCase("json_object")) {
+            if ((children.size() & 1) == 1 && (originChildSize == children.size())) {
                 throw new AnalysisException("json_object can't be odd parameters, need even parameters: " + this.toSql());
             }
             String res = parseJsonDataType(true);
@@ -395,7 +400,7 @@ public class FunctionCallExpr extends Expr {
         if (fnName.getFunction().equalsIgnoreCase("group_concat")) {
             if (children.size() > 2 || children.isEmpty()) {
                 throw new AnalysisException(
-                         "group_concat requires one or two parameters: " + this.toSql());
+                        "group_concat requires one or two parameters: " + this.toSql());
             }
 
             if (fnParams.isDistinct()) {
@@ -405,7 +410,7 @@ public class FunctionCallExpr extends Expr {
             Expr arg0 = getChild(0);
             if (!arg0.type.isStringType() && !arg0.type.isNull()) {
                 throw new AnalysisException(
-                         "group_concat requires first parameter to be of type STRING: " + this.toSql());
+                        "group_concat requires first parameter to be of type STRING: " + this.toSql());
             }
 
             if (children.size() == 2) {
@@ -490,7 +495,7 @@ public class FunctionCallExpr extends Expr {
                 throw new AnalysisException("intersect_count function first argument should be of BITMAP type, but was " + inputType);
             }
 
-            for(int i = 2; i < children.size(); i++) {
+            for (int i = 2; i < children.size(); i++) {
                 if (!getChild(i).isConstant()) {
                     throw new AnalysisException("intersect_count function filter_values arg must be constant");
                 }
@@ -667,7 +672,7 @@ public class FunctionCallExpr extends Expr {
                 type = getChild(0).type.getMaxResolutionType();
             }
             fn = getBuiltinFunction(analyzer, fnName.getFunction(), new Type[]{type},
-                Function.CompareMode.IS_NONSTRICT_SUPERTYPE_OF);
+                    Function.CompareMode.IS_NONSTRICT_SUPERTYPE_OF);
         } else if (fnName.getFunction().equalsIgnoreCase("count_distinct")) {
             Type compatibleType = this.children.get(0).getType();
             for (int i = 1; i < this.children.size(); ++i) {
@@ -688,36 +693,36 @@ public class FunctionCallExpr extends Expr {
                 fn = getTableFunction(fnName.getFunction(), childTypes,
                         Function.CompareMode.IS_NONSTRICT_SUPERTYPE_OF);
                 if (fn == null) {
-                    throw new AnalysisException("Doris only support `explode_split(varchar, varchar)` table function");
+                    throw new AnalysisException(UNKNOWN_TABLE_FUNCTION_MSG);
                 }
-                return;
-            }
-            // now first find function in built-in functions
-            if (Strings.isNullOrEmpty(fnName.getDb())) {
-                Type[] childTypes = collectChildReturnTypes();
-                fn = getBuiltinFunction(analyzer, fnName.getFunction(), childTypes,
-                        Function.CompareMode.IS_NONSTRICT_SUPERTYPE_OF);
-            }
-
-            // find user defined functions
-            if (fn == null) {
-                if (!analyzer.isUDFAllowed()) {
-                    throw new AnalysisException(
-                            "Does not support non-builtin functions, or function does not exist: " + this.toSqlImpl());
+            } else {
+                // now first find function in built-in functions
+                if (Strings.isNullOrEmpty(fnName.getDb())) {
+                    Type[] childTypes = collectChildReturnTypes();
+                    fn = getBuiltinFunction(analyzer, fnName.getFunction(), childTypes,
+                            Function.CompareMode.IS_NONSTRICT_SUPERTYPE_OF);
                 }
 
-                String dbName = fnName.analyzeDb(analyzer);
-                if (!Strings.isNullOrEmpty(dbName)) {
-                    // check operation privilege
-                    if (!Catalog.getCurrentCatalog().getAuth().checkDbPriv(
-                            ConnectContext.get(), dbName, PrivPredicate.SELECT)) {
-                        ErrorReport.reportAnalysisException(ErrorCode.ERR_SPECIFIC_ACCESS_DENIED_ERROR, "SELECT");
+                // find user defined functions
+                if (fn == null) {
+                    if (!analyzer.isUDFAllowed()) {
+                        throw new AnalysisException(
+                                "Does not support non-builtin functions, or function does not exist: " + this.toSqlImpl());
                     }
-                    Database db = Catalog.getCurrentCatalog().getDbNullable(dbName);
-                    if (db != null) {
-                        Function searchDesc = new Function(
-                                fnName, Arrays.asList(collectChildReturnTypes()), Type.INVALID, false);
-                        fn = db.getFunction(searchDesc, Function.CompareMode.IS_NONSTRICT_SUPERTYPE_OF);
+
+                    String dbName = fnName.analyzeDb(analyzer);
+                    if (!Strings.isNullOrEmpty(dbName)) {
+                        // check operation privilege
+                        if (!Catalog.getCurrentCatalog().getAuth().checkDbPriv(
+                                ConnectContext.get(), dbName, PrivPredicate.SELECT)) {
+                            ErrorReport.reportAnalysisException(ErrorCode.ERR_SPECIFIC_ACCESS_DENIED_ERROR, "SELECT");
+                        }
+                        Database db = Catalog.getCurrentCatalog().getDbNullable(dbName);
+                        if (db != null) {
+                            Function searchDesc = new Function(
+                                    fnName, Arrays.asList(collectChildReturnTypes()), Type.INVALID, false);
+                            fn = db.getFunction(searchDesc, Function.CompareMode.IS_NONSTRICT_SUPERTYPE_OF);
+                        }
                     }
                 }
             }
@@ -729,7 +734,7 @@ public class FunctionCallExpr extends Expr {
         }
 
         if (fnName.getFunction().equalsIgnoreCase("from_unixtime")
-        || fnName.getFunction().equalsIgnoreCase("date_format")) {
+                || fnName.getFunction().equalsIgnoreCase("date_format")) {
             // if has only one child, it has default time format: yyyy-MM-dd HH:mm:ss.SSSSSS
             if (children.size() > 1) {
                 final StringLiteral fmtLiteral = (StringLiteral) children.get(1);
@@ -994,40 +999,34 @@ public class FunctionCallExpr extends Expr {
         result = 31 * result + Objects.hashCode(fnParams);
         return result;
     }
-    public String forJSON(String str){
+
+    public String forJSON(String str) {
         final StringBuilder result = new StringBuilder();
         StringCharacterIterator iterator = new StringCharacterIterator(str);
         char character = iterator.current();
-        while (character != StringCharacterIterator.DONE){
-          if( character == '\"' ){
-            result.append("\\\"");
-          }
-          else if(character == '\\'){
-            result.append("\\\\");
-          }
-          else if(character == '/'){
-            result.append("\\/");
-          }
-          else if(character == '\b'){
-            result.append("\\b");
-          }
-          else if(character == '\f'){
-            result.append("\\f");
-          }
-          else if(character == '\n'){
-            result.append("\\n");
-          }
-          else if(character == '\r'){
-            result.append("\\r");
-          }
-          else if(character == '\t'){
-            result.append("\\t");
-          }
-          else {
-            result.append(character);
-          }
-          character = iterator.next();
+        while (character != StringCharacterIterator.DONE) {
+            if (character == '\"') {
+                result.append("\\\"");
+            } else if (character == '\\') {
+                result.append("\\\\");
+            } else if (character == '/') {
+                result.append("\\/");
+            } else if (character == '\b') {
+                result.append("\\b");
+            } else if (character == '\f') {
+                result.append("\\f");
+            } else if (character == '\n') {
+                result.append("\\n");
+            } else if (character == '\r') {
+                result.append("\\r");
+            } else if (character == '\t') {
+                result.append("\\t");
+            } else {
+                result.append(character);
+            }
+            character = iterator.next();
         }
-        return result.toString();    
-      }
+        return result.toString();
+    }
 }
+
diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/LateralViewRef.java b/fe/fe-core/src/main/java/org/apache/doris/analysis/LateralViewRef.java
index 41baabc..8326854 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/analysis/LateralViewRef.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/LateralViewRef.java
@@ -18,9 +18,7 @@
 package org.apache.doris.analysis;
 
 import org.apache.doris.catalog.Column;
-import org.apache.doris.catalog.FunctionSet;
 import org.apache.doris.catalog.InlineView;
-import org.apache.doris.catalog.Type;
 import org.apache.doris.common.AnalysisException;
 import org.apache.doris.common.Config;
 import org.apache.doris.common.UserException;
@@ -77,32 +75,31 @@ public class LateralViewRef extends TableRef {
         if (!(expr instanceof FunctionCallExpr)) {
             throw new AnalysisException("Only support function call expr in lateral view");
         }
+
+        analyzeFunctionExpr(analyzer);
+
+        // analyze lateral view
+        desc = analyzer.registerTableRef(this);
+        explodeSlotRef = new SlotRef(new TableName(null, viewName), columnName);
+        explodeSlotRef.analyze(analyzer);
+        isAnalyzed = true;  // true now that we have assigned desc
+    }
+
+    private void analyzeFunctionExpr(Analyzer analyzer) throws AnalysisException {
         fnExpr = (FunctionCallExpr) expr;
         fnExpr.setTableFnCall(true);
         checkAndSupplyDefaultTableName(fnExpr);
         fnExpr.analyze(analyzer);
-        if (!fnExpr.getFnName().getFunction().equals(FunctionSet.EXPLODE_SPLIT)) {
-            throw new AnalysisException("Only support explode function in lateral view");
-        }
         checkScalarFunction(fnExpr.getChild(0));
-        if (!(fnExpr.getChild(1) instanceof StringLiteral)) {
-            throw new AnalysisException("Split separator of explode must be a string const");
-        }
         fnExpr.getChild(0).collect(SlotRef.class, originSlotRefList);
-        // analyze lateral view
-        desc = analyzer.registerTableRef(this);
-        explodeSlotRef = new SlotRef(new TableName(null, viewName), columnName);
-        explodeSlotRef.analyze(analyzer);
-        isAnalyzed = true;  // true now that we have assigned desc
     }
 
     @Override
     public TupleDescriptor createTupleDescriptor(Analyzer analyzer) throws AnalysisException {
         // Create a fake catalog table for the lateral view
         List<Column> columnList = Lists.newArrayList();
-        columnList.add(new Column(columnName, Type.VARCHAR,
-                false, null, true,
-                null, ""));
+        columnList.add(new Column(columnName, fnExpr.getFn().getReturnType(),
+                false, null, true, null, ""));
         view = new InlineView(viewName, columnList);
 
         // Create the non-materialized tuple and set the fake table in it.
@@ -180,3 +177,4 @@ public class LateralViewRef extends TableRef {
         }
     }
 }
+
diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/SlotRef.java b/fe/fe-core/src/main/java/org/apache/doris/analysis/SlotRef.java
index 8c7d169..c7b451d 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/analysis/SlotRef.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/SlotRef.java
@@ -30,6 +30,7 @@ import org.apache.doris.thrift.TSlotRef;
 import com.google.common.base.MoreObjects;
 import com.google.common.base.Objects;
 import com.google.common.base.Preconditions;
+import com.google.common.collect.Lists;
 
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
@@ -374,6 +375,19 @@ public class SlotRef extends Expr {
         }
     }
 
+    @Override
+    protected Expr substituteImpl(ExprSubstitutionMap smap, Analyzer analyzer) throws AnalysisException {
+        if (isAnalyzed && desc != null && desc.getParent().getTable() == null && desc.getSourceExprs() != null) {
+            List<Expr> newSourceExprs = Lists.newArrayList();
+            for (int i = 0; i < desc.getSourceExprs().size(); ++i) {
+                newSourceExprs.add(desc.getSourceExprs().get(i).substituteImpl(smap, analyzer));
+            }
+            desc.setSourceExprs(newSourceExprs);
+            return this;
+        }
+        return super.substituteImpl(smap, analyzer);
+    }
+
     public Table getTable() {
         Preconditions.checkState(desc != null);
         Table table = desc.getParent().getTable();
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 c74da9f..03327ce 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
@@ -2140,19 +2140,51 @@ public class FunctionSet<min_initIN9doris_udf12DecimalV2ValEEEvPNS2_15FunctionCo
         return builtinFunctions;
     }
 
-
     public static final String EXPLODE_SPLIT = "explode_split";
+    public static final String EXPLODE_BITMAP = "explode_bitmap";
+    public static final String EXPLODE_JSON_ARRAY_INT = "explode_json_array_int";
+    public static final String EXPLODE_JSON_ARRAY_DOUBLE = "explode_json_array_double";
+    public static final String EXPLODE_JSON_ARRAY_STRING = "explode_json_array_string";
 
     private void initTableFunction() {
-        // init explode_split function
-        ArrayList<Type> argsType = Lists.newArrayList();
-        argsType.add(Type.VARCHAR);
-        argsType.add(Type.VARCHAR);
-        Function explodeSplit = ScalarFunction.createBuiltin(
-                EXPLODE_SPLIT, Type.VARCHAR, Function.NullableMode.DEPEND_ON_ARGUMENT, argsType, false,
-                "", "", "", true);
         List<Function> explodeSplits = Lists.newArrayList();
-        explodeSplits.add(explodeSplit);
+        explodeSplits.add(ScalarFunction.createBuiltin(
+                EXPLODE_SPLIT, Type.VARCHAR, Function.NullableMode.DEPEND_ON_ARGUMENT,
+                Lists.newArrayList(Type.VARCHAR, Type.VARCHAR), false,
+                "_ZN5doris19DummyTableFunctions13explode_splitEPN9doris_udf15FunctionContextERKNS1_9StringValES6_",
+                null, null, true));
         tableFunctions.put(EXPLODE_SPLIT, explodeSplits);
+
+        List<Function> explodeBitmaps = Lists.newArrayList();
+        explodeBitmaps.add(ScalarFunction.createBuiltin(
+                EXPLODE_BITMAP, Type.BIGINT, Function.NullableMode.DEPEND_ON_ARGUMENT,
+                Lists.newArrayList(Type.BITMAP), false,
+                "_ZN5doris19DummyTableFunctions14explode_bitmapEPN9doris_udf15FunctionContextERKNS1_9StringValE",
+                null, null, true));
+        tableFunctions.put(EXPLODE_BITMAP, explodeBitmaps);
+
+        List<Function> explodeJsonArrayInts = Lists.newArrayList();
+        explodeJsonArrayInts.add(ScalarFunction.createBuiltin(
+                EXPLODE_JSON_ARRAY_INT, Type.BIGINT, Function.NullableMode.DEPEND_ON_ARGUMENT,
+                Lists.newArrayList(Type.VARCHAR), false,
+                "_ZN5doris19DummyTableFunctions22explode_json_array_intEPN9doris_udf15FunctionContextERKNS1_9StringValE",
+                null, null, true));
+        tableFunctions.put(EXPLODE_JSON_ARRAY_INT, explodeJsonArrayInts);
+
+        List<Function> explodeJsonArrayDoubles = Lists.newArrayList();
+        explodeJsonArrayDoubles.add(ScalarFunction.createBuiltin(
+                EXPLODE_JSON_ARRAY_DOUBLE, Type.DOUBLE, Function.NullableMode.DEPEND_ON_ARGUMENT,
+                Lists.newArrayList(Type.VARCHAR), false,
+                "_ZN5doris19DummyTableFunctions25explode_json_array_doubleEPN9doris_udf15FunctionContextERKNS1_9StringValE",
+                null, null, true));
+        tableFunctions.put(EXPLODE_JSON_ARRAY_DOUBLE, explodeJsonArrayDoubles);
+
+        List<Function> explodeJsonArrayStrings = Lists.newArrayList();
+        explodeJsonArrayStrings.add(ScalarFunction.createBuiltin(
+                EXPLODE_JSON_ARRAY_STRING, Type.VARCHAR, Function.NullableMode.DEPEND_ON_ARGUMENT,
+                Lists.newArrayList(Type.VARCHAR), false,
+                "_ZN5doris19DummyTableFunctions25explode_json_array_stringEPN9doris_udf15FunctionContextERKNS1_9StringValE",
+                null, null, true));
+        tableFunctions.put(EXPLODE_JSON_ARRAY_STRING, explodeJsonArrayStrings);
     }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/planner/DistributedPlanner.java b/fe/fe-core/src/main/java/org/apache/doris/planner/DistributedPlanner.java
index a77a3ce..4e02aa8 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/planner/DistributedPlanner.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/planner/DistributedPlanner.java
@@ -293,6 +293,7 @@ public class DistributedPlanner {
     private PlanFragment createTableFunctionFragment(PlanNode node, PlanFragment childFragment) {
         Preconditions.checkState(node instanceof TableFunctionNode);
         node.setChild(0, childFragment.getPlanRoot());
+        node.setNumInstances(childFragment.getPlanRoot().getNumInstances());
         childFragment.addPlanRoot(node);
         return childFragment;
     }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/qe/Coordinator.java b/fe/fe-core/src/main/java/org/apache/doris/qe/Coordinator.java
index 5e72189..a792231 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/qe/Coordinator.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/qe/Coordinator.java
@@ -573,6 +573,7 @@ public class Coordinator {
                         LOG.warn("catch a execute exception", e);
                         exception = e;
                         code = TStatusCode.THRIFT_RPC_ERROR;
+                        BackendServiceProxy.getInstance().removeProxy(pair.first.brpcAddress);
                     } catch (InterruptedException e) {
                         LOG.warn("catch a interrupt exception", e);
                         exception = e;
@@ -2229,3 +2230,4 @@ public class Coordinator {
 }
 
 
+
diff --git a/fe/fe-core/src/test/java/org/apache/doris/planner/TableFunctionPlanTest.java b/fe/fe-core/src/test/java/org/apache/doris/planner/TableFunctionPlanTest.java
index 2752467..8617077 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/planner/TableFunctionPlanTest.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/planner/TableFunctionPlanTest.java
@@ -19,20 +19,20 @@ package org.apache.doris.planner;
 
 import org.apache.doris.analysis.CreateDbStmt;
 import org.apache.doris.analysis.CreateTableStmt;
+import org.apache.doris.analysis.FunctionCallExpr;
 import org.apache.doris.catalog.Catalog;
 import org.apache.doris.qe.ConnectContext;
 import org.apache.doris.utframe.UtFrameUtils;
 
 import org.apache.commons.io.FileUtils;
-
-import java.io.File;
-import java.util.UUID;
-
 import org.junit.After;
 import org.junit.Assert;
 import org.junit.BeforeClass;
 import org.junit.Test;
 
+import java.io.File;
+import java.util.UUID;
+
 public class TableFunctionPlanTest {
     private static String runningDir = "fe/mocked/TableFunctionPlanTest/" + UUID.randomUUID().toString() + "/";
     private static ConnectContext ctx;
@@ -55,6 +55,11 @@ public class TableFunctionPlanTest {
                 + "DUPLICATE KEY(k1) distributed by hash(k1) buckets 3 properties('replication_num' = '1');";
         CreateTableStmt createTableStmt = (CreateTableStmt) UtFrameUtils.parseAndAnalyzeStmt(createTblStmtStr, ctx);
         Catalog.getCurrentCatalog().createTable(createTableStmt);
+
+        createTblStmtStr = "create table db1.tbl2(k1 int, k2 varchar, v1 bitmap bitmap_union) "
+                + "distributed by hash(k1) buckets 3 properties('replication_num' = '1');";
+        createTableStmt = (CreateTableStmt) UtFrameUtils.parseAndAnalyzeStmt(createTblStmtStr, ctx);
+        Catalog.getCurrentCatalog().createTable(createTableStmt);
     }
 
     // test planner
@@ -172,15 +177,11 @@ public class TableFunctionPlanTest {
     public void errorParam() throws Exception {
         String sql = "explain select k1, e1 from db1.tbl1 lateral view explode_split(k2) tmp as e1;";
         String explainString = UtFrameUtils.getSQLPlanOrErrorMsg(ctx, sql);
-        Assert.assertTrue(explainString.contains("Doris only support `explode_split(varchar, varchar)` table function"));
+        Assert.assertTrue(explainString.contains(FunctionCallExpr.UNKNOWN_TABLE_FUNCTION_MSG));
 
         sql = "explain select k1, e1 from db1.tbl1 lateral view explode_split(k1) tmp as e1;";
         explainString = UtFrameUtils.getSQLPlanOrErrorMsg(ctx, sql);
-        Assert.assertTrue(explainString.contains("Doris only support `explode_split(varchar, varchar)` table function"));
-
-        sql = "explain select k1, e1 from db1.tbl1 lateral view explode_split(k1, k2) tmp as e1;";
-        explainString = UtFrameUtils.getSQLPlanOrErrorMsg(ctx, sql);
-        Assert.assertTrue(explainString.contains("Split separator of explode must be a string const"));
+        Assert.assertTrue(explainString.contains(FunctionCallExpr.UNKNOWN_TABLE_FUNCTION_MSG));
     }
 
     /* Case2 table function in where stmt
@@ -385,29 +386,59 @@ public class TableFunctionPlanTest {
         Assert.assertTrue(explainString.contains("tuple ids: 1 3"));
         String formatString = explainString.replaceAll(" ", "");
         Assert.assertTrue(formatString.contains(
-                          "SlotDescriptor{id=0,col=k1,type=INT}\n"
+                "SlotDescriptor{id=0,col=k1,type=INT}\n"
                         + "parent=0\n"
                         + "materialized=true"
         ));
         Assert.assertTrue(formatString.contains(
-                          "SlotDescriptor{id=1,col=k2,type=VARCHAR(*)}\n"
+                "SlotDescriptor{id=1,col=k2,type=VARCHAR(*)}\n"
                         + "parent=0\n"
                         + "materialized=true"
         ));
         Assert.assertTrue(formatString.contains(
-                          "SlotDescriptor{id=2,col=null,type=INT}\n"
+                "SlotDescriptor{id=2,col=null,type=INT}\n"
                         + "parent=1\n"
                         + "materialized=true"
         ));
         Assert.assertTrue(formatString.contains(
-                          "SlotDescriptor{id=3,col=null,type=VARCHAR(*)}\n"
+                "SlotDescriptor{id=3,col=null,type=VARCHAR(*)}\n"
                         + "parent=1\n"
                         + "materialized=true"
         ));
         Assert.assertTrue(formatString.contains(
-                          "SlotDescriptor{id=6,col=e1,type=VARCHAR(*)}\n"
+                "SlotDescriptor{id=6,col=e1,type=VARCHAR(*)}\n"
                         + "parent=3\n"
                         + "materialized=true"
         ));
     }
+
+    @Test
+    public void testExplodeBitmap() throws Exception {
+        String sql = "desc select k1, e1 from db1.tbl2 lateral view explode_bitmap(v1) tmp1 as e1 ";
+        String explainString = UtFrameUtils.getSQLPlanOrErrorMsg(ctx, sql, true);
+        System.out.println(explainString);
+        Assert.assertTrue(explainString.contains("table function: explode_bitmap(`default_cluster:db1`.`tbl2`.`v1`)"));
+        Assert.assertTrue(explainString.contains("output slot id: 1 2"));
+    }
+
+    @Test
+    public void testExplodeJsonArray() throws Exception {
+        String sql = "desc select k1, e1 from db1.tbl2 lateral view explode_json_array_int('[1,2,3]') tmp1 as e1 ";
+        String explainString = UtFrameUtils.getSQLPlanOrErrorMsg(ctx, sql, true);
+        System.out.println(explainString);
+        Assert.assertTrue(explainString.contains("table function: explode_json_array_int('[1,2,3]')"));
+        Assert.assertTrue(explainString.contains("output slot id: 0 1"));
+
+        sql = "desc select k1, e1 from db1.tbl2 lateral view explode_json_array_string('[\"a\",\"b\",\"c\"]') tmp1 as e1 ";
+        explainString = UtFrameUtils.getSQLPlanOrErrorMsg(ctx, sql, true);
+        System.out.println(explainString);
+        Assert.assertTrue(explainString.contains("table function: explode_json_array_string('[\"a\",\"b\",\"c\"]')"));
+        Assert.assertTrue(explainString.contains("output slot id: 0 1"));
+
+        sql = "desc select k1, e1 from db1.tbl2 lateral view explode_json_array_double('[1.1, 2.2, 3.3]') tmp1 as e1 ";
+        explainString = UtFrameUtils.getSQLPlanOrErrorMsg(ctx, sql, true);
+        System.out.println(explainString);
+        Assert.assertTrue(explainString.contains("table function: explode_json_array_double('[1.1, 2.2, 3.3]')"));
+        Assert.assertTrue(explainString.contains("output slot id: 0 1"));
+    }
 }

---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org