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

lihaopeng 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 2d83167e50 [Feature] [Lateral-View] support outer combinator of table 
function (#9147)
2d83167e50 is described below

commit 2d83167e50069ee575ab8b6f3a9a6cb9657ab3f9
Author: Pxl <952130...@qq.com>
AuthorDate: Sun Apr 24 12:09:40 2022 +0800

    [Feature] [Lateral-View] support outer combinator of table function (#9147)
---
 be/src/exprs/table_function/table_function.h       |  13 ++-
 .../table_function/table_function_factory.cpp      |  50 ++++-----
 .../exprs/table_function/table_function_factory.h  |  12 +-
 be/src/vec/exprs/table_function/vexplode.cpp       |   9 +-
 be/src/vec/exprs/table_function/vexplode.h         |   4 +-
 be/src/vec/functions/function_fake.cpp             |  54 +++++++--
 be/src/vec/functions/function_fake.h               |  70 +-----------
 be/src/vec/functions/simple_function_factory.h     |  13 ++-
 be/test/vec/function/table_function_test.cpp       |  13 +--
 docs/.vuepress/sidebar/en.js                       |   3 +-
 docs/.vuepress/sidebar/zh-CN.js                    |   3 +-
 .../table-functions/outer-combinator.md            |  48 ++++++++
 .../table-functions/explode-bitmap.md              |  11 +-
 .../table-functions/explode-json-array.md          | 121 ++-------------------
 .../sql-functions/table-functions/explode-split.md |   8 +-
 .../table-functions/outer-combinator.md            |  48 ++++++++
 .../java/org/apache/doris/catalog/FunctionSet.java | 104 ++++++++----------
 17 files changed, 269 insertions(+), 315 deletions(-)

diff --git a/be/src/exprs/table_function/table_function.h 
b/be/src/exprs/table_function/table_function.h
index 3c2e188950..901572996b 100644
--- a/be/src/exprs/table_function/table_function.h
+++ b/be/src/exprs/table_function/table_function.h
@@ -30,6 +30,8 @@ namespace doris {
 // Currently, the memory allocated from table function is from malloc directly.
 class TableFunctionState {};
 
+constexpr auto COMBINATOR_SUFFIX_OUTER = "_outer";
+
 class ExprContext;
 class TupleRow;
 class TableFunction {
@@ -99,6 +101,14 @@ public:
     }
 
     bool is_outer() const { return _is_outer; }
+    void set_outer() {
+        if (is_outer()) {
+            return;
+        }
+        _is_outer = true;
+        _fn_name += COMBINATOR_SUFFIX_OUTER;
+    }
+
     bool current_empty() const { return _is_current_empty; }
 
 protected:
@@ -115,7 +125,6 @@ protected:
     // the size of current result
     int64_t _cur_size = 0;
     // set _is_outer to false for explode function, and should not return 
tuple while array is null or empty
-    bool _is_outer = true;
+    bool _is_outer = false;
 };
-
 } // namespace doris
diff --git a/be/src/exprs/table_function/table_function_factory.cpp 
b/be/src/exprs/table_function/table_function_factory.cpp
index e44e3e65ed..2415627ccd 100644
--- a/be/src/exprs/table_function/table_function_factory.cpp
+++ b/be/src/exprs/table_function/table_function_factory.cpp
@@ -17,17 +17,6 @@
 
 #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"
-#include "exprs/table_function/table_function.h"
-#include "vec/exprs/table_function/vexplode.h"
-#include "vec/exprs/table_function/vexplode_bitmap.h"
-#include "vec/exprs/table_function/vexplode_json_array.h"
-#include "vec/exprs/table_function/vexplode_numbers.h"
-#include "vec/exprs/table_function/vexplode_split.h"
-
 namespace doris {
 
 template <typename TableFunctionType>
@@ -49,12 +38,6 @@ struct 
TableFunctionCreator<vectorized::VExplodeJsonArrayTableFunction> {
     }
 };
 
-template <>
-struct TableFunctionCreator<vectorized::VExplodeTableFunction> {
-    bool is_outer;
-    TableFunction* operator()() { return new 
vectorized::VExplodeTableFunction(is_outer); }
-};
-
 inline auto ExplodeJsonArrayIntCreator =
         TableFunctionCreator<ExplodeJsonArrayTableFunction> 
{ExplodeJsonArrayType::INT};
 inline auto ExplodeJsonArrayDoubleCreator =
@@ -72,10 +55,7 @@ inline auto VExplodeJsonArrayStringCreator =
         TableFunctionCreator<vectorized::VExplodeJsonArrayTableFunction> {
                 ExplodeJsonArrayType::STRING};
 
-inline auto VExplodeCreator = 
TableFunctionCreator<vectorized::VExplodeTableFunction> {false};
-inline auto VExplodeOuterCreator = 
TableFunctionCreator<vectorized::VExplodeTableFunction> {true};
-
-//{fn_name,is_vectorized}->table_function_creator
+// {fn_name, is_vectorized} -> table_function_creator
 const std::unordered_map<std::pair<std::string, bool>, 
std::function<TableFunction*()>>
         TableFunctionFactory::_function_map {
                 {{"explode_split", false}, 
TableFunctionCreator<ExplodeSplitTableFunction> {}},
@@ -92,19 +72,37 @@ const std::unordered_map<std::pair<std::string, bool>, 
std::function<TableFuncti
                 {{"explode_json_array_string", true}, 
VExplodeJsonArrayStringCreator},
                 {{"explode_bitmap", true},
                  
TableFunctionCreator<vectorized::VExplodeBitmapTableFunction>()},
-                {{"explode", true}, VExplodeCreator},
-                {{"explode_outer", true}, VExplodeOuterCreator}}; // namespace 
doris
+                {{"explode", true}, 
TableFunctionCreator<vectorized::VExplodeTableFunction> {}}};
 
-Status TableFunctionFactory::get_fn(const std::string& fn_name, bool 
is_vectorized,
+Status TableFunctionFactory::get_fn(const std::string& fn_name_raw, bool 
is_vectorized,
                                     ObjectPool* pool, TableFunction** fn) {
-    auto fn_iterator = _function_map.find({fn_name, is_vectorized});
+    auto match_suffix = [](const std::string& name, const std::string& suffix) 
-> bool {
+        if (name.length() < suffix.length()) {
+            return false;
+        }
+        return name.substr(name.length() - suffix.length()) == suffix;
+    };
+
+    auto remove_suffix = [](const std::string& name, const std::string& 
suffix) -> std::string {
+        return name.substr(0, name.length() - suffix.length());
+    };
+
+    bool is_outer = match_suffix(fn_name_raw, COMBINATOR_SUFFIX_OUTER);
+    std::string fn_name_real =
+            is_outer ? remove_suffix(fn_name_raw, COMBINATOR_SUFFIX_OUTER) : 
fn_name_raw;
+
+    auto fn_iterator = _function_map.find({fn_name_real, is_vectorized});
     if (fn_iterator != _function_map.end()) {
         *fn = pool->add(fn_iterator->second());
+        if (is_outer) {
+            (*fn)->set_outer();
+        }
+
         return Status::OK();
     }
 
     return Status::NotSupported(std::string(is_vectorized ? "vectorized " : 
"") +
-                                "table function " + fn_name + " not support");
+                                "table function " + fn_name_raw + " not 
support");
 }
 
 } // namespace doris
diff --git a/be/src/exprs/table_function/table_function_factory.h 
b/be/src/exprs/table_function/table_function_factory.h
index eaa1b86c63..05d4fa2e74 100644
--- a/be/src/exprs/table_function/table_function_factory.h
+++ b/be/src/exprs/table_function/table_function_factory.h
@@ -20,9 +20,17 @@
 #include <functional>
 #include <unordered_map>
 
+#include "common/object_pool.h"
 #include "common/status.h"
+#include "exprs/table_function/explode_bitmap.h"
+#include "exprs/table_function/explode_json_array.h"
 #include "exprs/table_function/explode_split.h"
-#include "exprs/table_function/table_function_factory.h"
+#include "exprs/table_function/table_function.h"
+#include "vec/exprs/table_function/vexplode.h"
+#include "vec/exprs/table_function/vexplode_bitmap.h"
+#include "vec/exprs/table_function/vexplode_json_array.h"
+#include "vec/exprs/table_function/vexplode_numbers.h"
+#include "vec/exprs/table_function/vexplode_split.h"
 
 namespace doris {
 
@@ -32,7 +40,7 @@ class TableFunctionFactory {
 public:
     TableFunctionFactory() {}
     ~TableFunctionFactory() {}
-    static Status get_fn(const std::string& fn_name, bool is_vectorized, 
ObjectPool* pool,
+    static Status get_fn(const std::string& fn_name_raw, bool is_vectorized, 
ObjectPool* pool,
                          TableFunction** fn);
 
     const static std::unordered_map<std::pair<std::string, bool>, 
std::function<TableFunction*()>>
diff --git a/be/src/vec/exprs/table_function/vexplode.cpp 
b/be/src/vec/exprs/table_function/vexplode.cpp
index 58f8e7f2e0..adb0db076d 100644
--- a/be/src/vec/exprs/table_function/vexplode.cpp
+++ b/be/src/vec/exprs/table_function/vexplode.cpp
@@ -21,13 +21,8 @@
 
 namespace doris::vectorized {
 
-VExplodeTableFunction::VExplodeTableFunction(bool is_outer) {
-    _is_outer = is_outer;
-    if (_is_outer) {
-        _fn_name = "vexplode_outer";
-    } else {
-        _fn_name = "vexplode";
-    }
+VExplodeTableFunction::VExplodeTableFunction() {
+    _fn_name = "vexplode";
 }
 
 Status VExplodeTableFunction::process_init(vectorized::Block* block) {
diff --git a/be/src/vec/exprs/table_function/vexplode.h 
b/be/src/vec/exprs/table_function/vexplode.h
index b7ee993c0e..da4909994b 100644
--- a/be/src/vec/exprs/table_function/vexplode.h
+++ b/be/src/vec/exprs/table_function/vexplode.h
@@ -18,16 +18,16 @@
 #pragma once
 
 #include "exprs/table_function/table_function.h"
-#include "vec/common/string_ref.h"
 #include "vec/columns/column.h"
 #include "vec/columns/column_array.h"
 #include "vec/columns/column_nullable.h"
+#include "vec/common/string_ref.h"
 
 namespace doris::vectorized {
 
 class VExplodeTableFunction : public TableFunction {
 public:
-    VExplodeTableFunction(bool is_outer);
+    VExplodeTableFunction();
 
     virtual ~VExplodeTableFunction() = default;
 
diff --git a/be/src/vec/functions/function_fake.cpp 
b/be/src/vec/functions/function_fake.cpp
index b661960517..1f52297806 100644
--- a/be/src/vec/functions/function_fake.cpp
+++ b/be/src/vec/functions/function_fake.cpp
@@ -17,18 +17,54 @@
 
 #include "vec/functions/function_fake.h"
 
+#include <boost/metaparse/string.hpp>
+#include <string_view>
+#include <type_traits>
+
 namespace doris::vectorized {
 
+// We can use std::basic_fixed_string with c++20 in the future
+template <const char* Name, typename ReturnType>
+struct FakeFunctionBaseImpl {
+    static constexpr auto name = Name;
+    static DataTypePtr get_return_type_impl(const DataTypes& arguments) {
+        return std::make_shared<ReturnType>();
+    }
+};
+
+#define C_STR(str_) boost::mpl::c_str<BOOST_METAPARSE_STRING(str_)>::value
+
+using FunctionEsquery = FakeFunctionBaseImpl<C_STR("esquery"), DataTypeUInt8>;
+
+using FunctionExplodeSplit = FakeFunctionBaseImpl<C_STR("explode_split"), 
DataTypeString>;
+using FunctionExplodeNumbers = FakeFunctionBaseImpl<C_STR("explode_numbers"), 
DataTypeInt32>;
+using FunctionExplodeJsonArrayInt =
+        FakeFunctionBaseImpl<C_STR("explode_json_array_int"), DataTypeInt64>;
+using FunctionExplodeJsonArrayString =
+        FakeFunctionBaseImpl<C_STR("explode_json_array_string"), 
DataTypeString>;
+using FunctionExplodeJsonArrayDouble =
+        FakeFunctionBaseImpl<C_STR("explode_json_array_double"), 
DataTypeFloat64>;
+using FunctionExplodeBitmap = FakeFunctionBaseImpl<C_STR("explode_bitmap"), 
DataTypeInt64>;
+
+struct FunctionExplode {
+    static constexpr auto name = "explode";
+    static DataTypePtr get_return_type_impl(const DataTypes& arguments) {
+        DCHECK(is_array(arguments[0])) << arguments[0]->get_name() << " not 
supported";
+        return make_nullable(
+                
check_and_get_data_type<DataTypeArray>(arguments[0].get())->get_nested_type());
+    }
+};
+
 void register_function_fake(SimpleFunctionFactory& factory) {
-    factory.register_function<FunctionFake<FunctionEsqueryImpl>>();
-    factory.register_function<FunctionFake<FunctionExplodeSplitImpl>>();
-    factory.register_function<FunctionFake<FunctionExplodeNumbersImpl>>();
-    
factory.register_function<FunctionFake<FunctionExplodeJsonArrayDoubleImpl>>();
-    factory.register_function<FunctionFake<FunctionExplodeJsonArrayIntImpl>>();
-    
factory.register_function<FunctionFake<FunctionExplodeJsonArrayStringImpl>>();
-    factory.register_function<FunctionFake<FunctionExplodeBitmapImpl>>();
-    factory.register_function<FunctionFake<FunctionExplodeImpl>>();
-    factory.register_function<FunctionFake<FunctionExplodeOuterImpl>>();
+    factory.register_function<FunctionFake<FunctionEsquery>>();
+
+    factory.register_table_function<FunctionFake<FunctionExplodeSplit>>();
+    factory.register_table_function<FunctionFake<FunctionExplodeNumbers>>();
+    
factory.register_table_function<FunctionFake<FunctionExplodeJsonArrayDouble>>();
+    
factory.register_table_function<FunctionFake<FunctionExplodeJsonArrayInt>>();
+    
factory.register_table_function<FunctionFake<FunctionExplodeJsonArrayString>>();
+    factory.register_table_function<FunctionFake<FunctionExplodeBitmap>>();
+    factory.register_table_function<FunctionFake<FunctionExplode>>();
 }
 
 } // namespace doris::vectorized
diff --git a/be/src/vec/functions/function_fake.h 
b/be/src/vec/functions/function_fake.h
index 56648d8877..eaaecaf843 100644
--- a/be/src/vec/functions/function_fake.h
+++ b/be/src/vec/functions/function_fake.h
@@ -27,75 +27,7 @@
 #include "vec/utils/util.hpp"
 
 namespace doris::vectorized {
-
-struct FunctionEsqueryImpl {
-    static constexpr auto name = "esquery";
-    static DataTypePtr get_return_type_impl(const DataTypes& arguments) {
-        return std::make_shared<DataTypeUInt8>();
-    }
-};
-
-struct FunctionExplodeSplitImpl {
-    static constexpr auto name = "explode_split";
-    static DataTypePtr get_return_type_impl(const DataTypes& arguments) {
-        return std::make_shared<DataTypeString>();
-    }
-};
-
-struct FunctionExplodeNumbersImpl {
-    static constexpr auto name = "explode_numbers";
-    static DataTypePtr get_return_type_impl(const DataTypes& arguments) {
-        return std::make_shared<DataTypeInt32>();
-    }
-};
-
-struct FunctionExplodeJsonArrayIntImpl {
-    static constexpr auto name = "explode_json_array_int";
-    static DataTypePtr get_return_type_impl(const DataTypes& arguments) {
-        return std::make_shared<DataTypeInt64>();
-    }
-};
-
-struct FunctionExplodeJsonArrayStringImpl {
-    static constexpr auto name = "explode_json_array_string";
-    static DataTypePtr get_return_type_impl(const DataTypes& arguments) {
-        return std::make_shared<DataTypeString>();
-    }
-};
-
-struct FunctionExplodeJsonArrayDoubleImpl {
-    static constexpr auto name = "explode_json_array_double";
-    static DataTypePtr get_return_type_impl(const DataTypes& arguments) {
-        return std::make_shared<DataTypeFloat64>();
-    }
-};
-
-struct FunctionExplodeBitmapImpl {
-    static constexpr auto name = "explode_bitmap";
-    static DataTypePtr get_return_type_impl(const DataTypes& arguments) {
-        return std::make_shared<DataTypeInt64>();
-    }
-};
-
-struct FunctionExplodeImpl {
-    static constexpr auto name = "explode";
-    static DataTypePtr get_return_type_impl(const DataTypes& arguments) {
-        DCHECK(is_array(arguments[0])) << arguments[0]->get_name() << " not 
supported";
-        return make_nullable(
-                
check_and_get_data_type<DataTypeArray>(arguments[0].get())->get_nested_type());
-    }
-};
-
-struct FunctionExplodeOuterImpl {
-    static constexpr auto name = "explode_outer";
-    static DataTypePtr get_return_type_impl(const DataTypes& arguments) {
-        DCHECK(is_array(arguments[0])) << arguments[0]->get_name() << " not 
supported";
-        return make_nullable(
-                
check_and_get_data_type<DataTypeArray>(arguments[0].get())->get_nested_type());
-    }
-};
-
-//FunctionFake is use for some function call expr only work at prepare/open 
phase, do not support execute().
+// FunctionFake is use for some function call expr only work at prepare/open 
phase, do not support execute().
 template <typename Impl>
 class FunctionFake : public IFunction {
 public:
diff --git a/be/src/vec/functions/simple_function_factory.h 
b/be/src/vec/functions/simple_function_factory.h
index 48266e303b..033682212a 100644
--- a/be/src/vec/functions/simple_function_factory.h
+++ b/be/src/vec/functions/simple_function_factory.h
@@ -23,6 +23,7 @@
 #include <mutex>
 #include <string>
 
+#include "exprs/table_function/table_function.h"
 #include "vec/functions/function.h"
 
 namespace doris::vectorized {
@@ -103,10 +104,18 @@ public:
 
     template <class Function>
     void register_function() {
-        if constexpr (std::is_base_of<IFunction, Function>::value)
+        if constexpr (std::is_base_of<IFunction, Function>::value) {
             register_function(Function::name, 
&createDefaultFunction<Function>);
-        else
+        } else {
             register_function(Function::name, &Function::create);
+        }
+    }
+
+    template <class Function>
+    void register_table_function() {
+        function_creators[Function::name] = &createDefaultFunction<Function>;
+        function_creators[std::string(Function::name) + 
COMBINATOR_SUFFIX_OUTER] =
+                &createDefaultFunction<Function>;
     }
 
     void register_alias(const std::string& name, const std::string& alias) {
diff --git a/be/test/vec/function/table_function_test.cpp 
b/be/test/vec/function/table_function_test.cpp
index f525138d65..19e53dc8a4 100644
--- a/be/test/vec/function/table_function_test.cpp
+++ b/be/test/vec/function/table_function_test.cpp
@@ -64,7 +64,8 @@ private:
 
 TEST_F(TableFunctionTest, vexplode_outer) {
     init_expr_context(1);
-    VExplodeTableFunction explode_outer(true);
+    VExplodeTableFunction explode_outer;
+    explode_outer.set_outer();
     explode_outer.set_vexpr_context(_ctx.get());
 
     // explode_outer(Array<Int32>)
@@ -95,7 +96,7 @@ TEST_F(TableFunctionTest, vexplode_outer) {
 
 TEST_F(TableFunctionTest, vexplode) {
     init_expr_context(1);
-    VExplodeTableFunction explode(false);
+    VExplodeTableFunction explode;
     explode.set_vexpr_context(_ctx.get());
 
     // explode(Array<Int32>)
@@ -134,8 +135,7 @@ TEST_F(TableFunctionTest, vexplode_numbers) {
         InputDataSet input_set = {{Int32(2)}, {Int32(3)}, {Null()}, 
{Int32(0)}, {Int32(-2)}};
 
         InputTypeSet output_types = {TypeIndex::Int32};
-        InputDataSet output_set = {{Int32(0)}, {Int32(1)}, {Int32(0)}, 
{Int32(1)},
-                                   {Int32(2)}, {Null()},   {Null()},   
{Null()}};
+        InputDataSet output_set = {{Int32(0)}, {Int32(1)}, {Int32(0)}, 
{Int32(1)}, {Int32(2)}};
 
         check_vec_table_function(&tfn, input_types, input_set, output_types, 
output_set);
     }
@@ -158,9 +158,8 @@ TEST_F(TableFunctionTest, vexplode_split) {
                                   {std::string(""), std::string(",")}};
 
         InputTypeSet output_types = {TypeIndex::String};
-        InputDataSet output_set = {{Null()},           {std::string("a")}, 
{std::string("b")},
-                                   {std::string("c")}, {std::string("")},  
{std::string("b,c")},
-                                   {std::string("")}};
+        InputDataSet output_set = {{std::string("a")}, {std::string("b")},   
{std::string("c")},
+                                   {std::string("")},  {std::string("b,c")}, 
{std::string("")}};
 
         check_vec_table_function(&tfn, input_types, input_set, output_types, 
output_set);
     }
diff --git a/docs/.vuepress/sidebar/en.js b/docs/.vuepress/sidebar/en.js
index e28083d851..6a3d7ffda3 100644
--- a/docs/.vuepress/sidebar/en.js
+++ b/docs/.vuepress/sidebar/en.js
@@ -544,7 +544,8 @@ module.exports = [
               "explode-bitmap",
               "explode-split",
               "explode-json-array",
-              "explode-numbers"
+              "explode-numbers",
+              "outer-combinator"
             ],
           },
           "window-function",
diff --git a/docs/.vuepress/sidebar/zh-CN.js b/docs/.vuepress/sidebar/zh-CN.js
index 83e7f22f9b..13ef8c0daf 100644
--- a/docs/.vuepress/sidebar/zh-CN.js
+++ b/docs/.vuepress/sidebar/zh-CN.js
@@ -558,7 +558,8 @@ module.exports = [
               "explode-bitmap",
               "explode-split",
               "explode-json-array",
-              "explode-numbers"
+              "explode-numbers",
+              "outer-combinator"
             ],
           },
           "window-function",
diff --git 
a/docs/en/sql-reference/sql-functions/table-functions/outer-combinator.md 
b/docs/en/sql-reference/sql-functions/table-functions/outer-combinator.md
new file mode 100644
index 0000000000..83bed087d9
--- /dev/null
+++ b/docs/en/sql-reference/sql-functions/table-functions/outer-combinator.md
@@ -0,0 +1,48 @@
+---
+{
+    "title": "outer combinator",
+    "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.
+-->
+
+# outer combinator
+
+## description
+
+Adding the `_outer` suffix after the function name of the table function 
changes the function behavior from `non-outer` to `outer`, and adds a row of 
`Null` data when the table function generates 0 rows of data.
+
+## example
+
+```
+mysql> select e1 from (select 1 k1) as t lateral view explode_numbers(0) tmp1 
as e1;
+Empty set
+
+mysql> select e1 from (select 1 k1) as t lateral view explode_numbers_outer(0) 
tmp1 as e1;
++------+
+| e1   |
++------+
+| NULL |
++------+
+```
+## keyword
+
+    outer
\ No newline at end of file
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
index 99f79711ac..e6302f46f3 100644
--- 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
@@ -60,16 +60,7 @@ 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 |
-+------+------+
+Empty set
 
 mysql> select k1, e1 from example1 lateral view 
explode_bitmap(bitmap_from_string("1")) tmp1 as e1 order by k1, e1;
 +------+------+
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
index 8332d6a8e4..d5a08f8140 100644
--- 
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
@@ -62,13 +62,7 @@ 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 |
-+------+------+
+Empty set
 
 mysql> select k1, e1 from example1 lateral view 
explode_json_array_int('[1,2,3]') tmp1 as e1 order by k1, e1;
 +------+------+
@@ -89,79 +83,28 @@ mysql> select k1, e1 from example1 lateral view 
explode_json_array_int('[1,"b",3
 +------+------+
 | 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 |
-+------+------+
+Empty set
 
 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 |
-+------+------+
+Empty set
 
 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 |
-+------+------+
+Empty set
 
 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 |
-+------+------+
+Empty set
 
 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 |
-+------+------+
+Empty set
 
 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;
 +------+------+
@@ -179,52 +122,16 @@ mysql> select k1, e1 from example1 lateral view 
explode_json_array_double('[1.0,
 +------+------+
 
 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 |
-+------+------+
+Empty set
 
 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 |
-+------+------+
+Empty set
 
 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 |
-+------+------+
+Empty set
 
 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 |
-+------+------+
+Empty set
 
 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;
 +------+----------+
@@ -272,13 +179,7 @@ mysql> select k1, e1 from example1 lateral view 
explode_json_array_string('["a",
 +------+------+
 
 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 |
-+------+------+
+Empty set
 ```
 
 ## keyword
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
index d7c403186e..6bc0ea3fe7 100644
--- 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
@@ -67,11 +67,7 @@ mysql> select k1, e1 from example1 lateral view 
explode_split(k2, ',') tmp1 as e
 +------+------+
 
 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 |
-+------+------+
+Empty set
 
 mysql> select k1, e1 from example1 lateral view explode_split(k2, ',') tmp1 as 
e1 where k1 = 3 order by k1, e1;
 +------+------+
@@ -103,7 +99,7 @@ mysql> select k1, e1 from example1 lateral view 
explode_split(k2, ',') tmp1 as e
 +------+------+
 |    6 |  b   |
 |    6 |  c   |
-|    6 | a    |
+|    6 |  a   |
 +------+------+
 ```
 
diff --git 
a/docs/zh-CN/sql-reference/sql-functions/table-functions/outer-combinator.md 
b/docs/zh-CN/sql-reference/sql-functions/table-functions/outer-combinator.md
new file mode 100644
index 0000000000..ef9f97047e
--- /dev/null
+++ b/docs/zh-CN/sql-reference/sql-functions/table-functions/outer-combinator.md
@@ -0,0 +1,48 @@
+---
+{
+    "title": "outer组合器",
+    "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.
+-->
+
+# outer组合器
+
+## description
+
+在table 
function的函数名后面添加`_outer`后缀使得函数行为从`non-outer`变为`outer`,在表函数生成0行数据时添加一行`Null`数据。
+
+## example
+
+```
+mysql> select e1 from (select 1 k1) as t lateral view explode_numbers(0) tmp1 
as e1;
+Empty set
+
+mysql> select e1 from (select 1 k1) as t lateral view explode_numbers_outer(0) 
tmp1 as e1;
++------+
+| e1   |
++------+
+| NULL |
++------+
+```
+## keyword
+
+    outer
\ No newline at end of file
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 e95b1c66bb..b6c08cba93 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
@@ -25,6 +25,7 @@ import org.apache.doris.analysis.InPredicate;
 import org.apache.doris.analysis.IsNullPredicate;
 import org.apache.doris.analysis.LikePredicate;
 import org.apache.doris.builtins.ScalarBuiltins;
+import org.apache.doris.catalog.Function.NullableMode;
 
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableMap;
@@ -2482,81 +2483,62 @@ public class 
FunctionSet<min_initIN9doris_udf12DecimalV2ValEEEvPNS2_15FunctionCo
     public static final String EXPLODE_JSON_ARRAY_STRING = 
"explode_json_array_string";
     public static final String EXPLODE_NUMBERS = "explode_numbers";
     public static final String EXPLODE = "explode";
-    public static final String EXPLODE_OUTER = "explode_outer";
+
+    private void addTableFunction(String name, Type retType, NullableMode 
nullableMode, ArrayList<Type> argTypes,
+            boolean hasVarArgs, String symbol) {
+        List<Function> functionList = tableFunctions.get(name);
+        functionList.add(ScalarFunction.createBuiltin(name, retType, 
nullableMode, argTypes, hasVarArgs, symbol, null,
+                null, true));
+    }
+
+    private void addTableFunctionWithCombinator(String name, Type retType, 
NullableMode nullableMode,
+            ArrayList<Type> argTypes, boolean hasVarArgs, String symbol) {
+        addTableFunction(name, retType, nullableMode, argTypes, hasVarArgs, 
symbol);
+        addTableFunction(name + "_outer", retType, 
Function.NullableMode.ALWAYS_NULLABLE, argTypes, hasVarArgs, symbol);
+    }
+
+    private void initTableFunctionListWithCombinator(String name) {
+        tableFunctions.put(name, Lists.newArrayList());
+        tableFunctions.put(name + "_outer", Lists.newArrayList());
+    }
 
     private void initTableFunction() {
-        List<Function> explodeSplits = Lists.newArrayList();
-        explodeSplits.add(ScalarFunction.createBuiltin(
-                EXPLODE_SPLIT, Type.VARCHAR, 
Function.NullableMode.DEPEND_ON_ARGUMENT,
+        initTableFunctionListWithCombinator(EXPLODE_SPLIT);
+        addTableFunctionWithCombinator(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);
+                
"_ZN5doris19DummyTableFunctions13explode_splitEPN9doris_udf15FunctionContextERKNS1_9StringValES6_");
 
-        List<Function> explodeBitmaps = Lists.newArrayList();
-        explodeBitmaps.add(ScalarFunction.createBuiltin(
-                EXPLODE_BITMAP, Type.BIGINT, 
Function.NullableMode.DEPEND_ON_ARGUMENT,
+        initTableFunctionListWithCombinator(EXPLODE_BITMAP);
+        addTableFunctionWithCombinator(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);
+                
"_ZN5doris19DummyTableFunctions14explode_bitmapEPN9doris_udf15FunctionContextERKNS1_9StringValE");
 
-        List<Function> explodeJsonArrayInts = Lists.newArrayList();
-        explodeJsonArrayInts.add(ScalarFunction.createBuiltin(
-                EXPLODE_JSON_ARRAY_INT, Type.BIGINT, 
Function.NullableMode.DEPEND_ON_ARGUMENT,
+        initTableFunctionListWithCombinator(EXPLODE_JSON_ARRAY_INT);
+        addTableFunctionWithCombinator(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);
+                
"_ZN5doris19DummyTableFunctions22explode_json_array_intEPN9doris_udf15FunctionContextERKNS1_9StringValE");
 
-        List<Function> explodeJsonArrayDoubles = Lists.newArrayList();
-        explodeJsonArrayDoubles.add(ScalarFunction.createBuiltin(
-                EXPLODE_JSON_ARRAY_DOUBLE, Type.DOUBLE, 
Function.NullableMode.DEPEND_ON_ARGUMENT,
+        initTableFunctionListWithCombinator(EXPLODE_JSON_ARRAY_DOUBLE);
+        addTableFunctionWithCombinator(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);
+                
"_ZN5doris19DummyTableFunctions25explode_json_array_doubleEPN9doris_udf15FunctionContextERKNS1_9StringValE");
 
-        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);
+        initTableFunctionListWithCombinator(EXPLODE_JSON_ARRAY_STRING);
+        addTableFunctionWithCombinator(EXPLODE_JSON_ARRAY_STRING, Type.VARCHAR,
+                Function.NullableMode.DEPEND_ON_ARGUMENT, 
Lists.newArrayList(Type.VARCHAR), false,
+                
"_ZN5doris19DummyTableFunctions25explode_json_array_stringEPN9doris_udf15FunctionContextERKNS1_9StringValE");
 
-        List<Function> explodeNumbers = Lists.newArrayList();
-        explodeNumbers.add(ScalarFunction.createBuiltin(
-                EXPLODE_NUMBERS, Type.INT, 
Function.NullableMode.DEPEND_ON_ARGUMENT,
+        initTableFunctionListWithCombinator(EXPLODE_NUMBERS);
+        addTableFunctionWithCombinator(EXPLODE_NUMBERS, Type.INT, 
Function.NullableMode.DEPEND_ON_ARGUMENT,
                 Lists.newArrayList(Type.INT), false,
-                
"_ZN5doris19DummyTableFunctions22explode_numbersEPN9doris_udf15FunctionContextERKNS1_9IntValE",
-                null, null, true));
-        tableFunctions.put(EXPLODE_NUMBERS, explodeNumbers);
-
-        List<Function> explodes = Lists.newArrayList();
-        explodes.add(ScalarFunction.createBuiltin(
-                EXPLODE, Type.INT, Function.NullableMode.ALWAYS_NULLABLE,
-                Lists.newArrayList(new ArrayType(Type.INT)), false,
-                
"_ZN5doris19DummyTableFunctions7explodeEPN9doris_udf15FunctionContextERKNS1_13CollectionValE",
-                null, null, true));
-        explodes.add(ScalarFunction.createBuiltin(
-                EXPLODE, Type.VARCHAR, Function.NullableMode.ALWAYS_NULLABLE,
-                Lists.newArrayList(new ArrayType(Type.VARCHAR)), false,
-                
"_ZN5doris19DummyTableFunctions7explodeEPN9doris_udf15FunctionContextERKNS1_13CollectionValE",
-                null, null, true));
-        tableFunctions.put(EXPLODE, explodes);
+                
"_ZN5doris19DummyTableFunctions22explode_numbersEPN9doris_udf15FunctionContextERKNS1_9IntValE");
 
-        List<Function> explodeOuters = Lists.newArrayList();
-        explodeOuters.add(ScalarFunction.createBuiltin(
-                EXPLODE_OUTER, Type.INT, Function.NullableMode.ALWAYS_NULLABLE,
+        initTableFunctionListWithCombinator(EXPLODE);
+        addTableFunctionWithCombinator(EXPLODE, Type.INT, 
Function.NullableMode.ALWAYS_NULLABLE,
                 Lists.newArrayList(new ArrayType(Type.INT)), false,
-                
"_ZN5doris19DummyTableFunctions13explode_outerEPN9doris_udf15FunctionContextERKNS1_13CollectionValE",
-                null, null, true));
-        explodeOuters.add(ScalarFunction.createBuiltin(
-                EXPLODE_OUTER, Type.VARCHAR, 
Function.NullableMode.ALWAYS_NULLABLE,
+                
"_ZN5doris19DummyTableFunctions7explodeEPN9doris_udf15FunctionContextERKNS1_13CollectionValE");
+        addTableFunctionWithCombinator(EXPLODE, Type.VARCHAR, 
Function.NullableMode.ALWAYS_NULLABLE,
                 Lists.newArrayList(new ArrayType(Type.VARCHAR)), false,
-                
"_ZN5doris19DummyTableFunctions13explode_outerEPN9doris_udf15FunctionContextERKNS1_13CollectionValE",
-                null, null, true));
-        tableFunctions.put(EXPLODE_OUTER, explodeOuters);
+                
"_ZN5doris19DummyTableFunctions7explodeEPN9doris_udf15FunctionContextERKNS1_13CollectionValE");
     }
 }


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

Reply via email to