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

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


The following commit(s) were added to refs/heads/master by this push:
     new 360d354bcaf [feature](table-function)support posexplode table function 
(#43221)
360d354bcaf is described below

commit 360d354bcaf0073342ad8ac5cd75ca2f96dc8246
Author: zhangstar333 <[email protected]>
AuthorDate: Wed Nov 6 15:29:34 2024 +0800

    [feature](table-function)support posexplode table function (#43221)
    
    ### What problem does this PR solve?
    <!--
    You need to clearly describe your PR in this part:
    
    1. What problem was fixed (it's best to include specific error reporting
    information). How it was fixed.
    2. Which behaviors were modified. What was the previous behavior, what
    is it now, why was it modified, and what possible impacts might there
    be.
    3. What features were added. Why this function was added.
    4. Which codes were refactored and why this part of the code was
    refactored.
    5. Which functions were optimized and what is the difference before and
    after the optimization.
    
    The description of the PR needs to enable reviewers to quickly and
    clearly understand the logic of the code modification.
    -->
    
    <!--
    If there are related issues, please fill in the issue number.
    - If you want the issue to be closed after the PR is merged, please use
    "close #12345". Otherwise, use "ref #12345"
    -->
    Issue Number: close #xxx
    
    <!--
    If this PR is followup a preivous PR, for example, fix the bug that
    introduced by a related PR,
    link the PR here
    -->
    Related PR: #xxx
    
    Problem Summary:
    support posexplode table function
    doc: https://github.com/apache/doris-website/pull/1283
---
 .../table_function/table_function_factory.cpp      |   2 +
 be/src/vec/exprs/table_function/vposexplode.cpp    | 155 +++++++++++++++++++
 be/src/vec/exprs/table_function/vposexplode.h      |  50 +++++++
 be/src/vec/functions/function_fake.cpp             |  29 ++++
 .../catalog/BuiltinTableGeneratingFunctions.java   |   6 +-
 .../functions/generator/PosExplode.java            |  80 ++++++++++
 .../functions/generator/PosExplodeOuter.java       |  80 ++++++++++
 .../visitor/TableGeneratingFunctionVisitor.java    |  10 ++
 .../sql_functions/table_function/posexplode.out    | 166 +++++++++++++++++++++
 .../sql_functions/table_function/posexplode.groovy |  82 ++++++++++
 10 files changed, 659 insertions(+), 1 deletion(-)

diff --git a/be/src/vec/exprs/table_function/table_function_factory.cpp 
b/be/src/vec/exprs/table_function/table_function_factory.cpp
index 0bef185351d..332eaed37d4 100644
--- a/be/src/vec/exprs/table_function/table_function_factory.cpp
+++ b/be/src/vec/exprs/table_function/table_function_factory.cpp
@@ -33,6 +33,7 @@
 #include "vec/exprs/table_function/vexplode_map.h"
 #include "vec/exprs/table_function/vexplode_numbers.h"
 #include "vec/exprs/table_function/vexplode_split.h"
+#include "vec/exprs/table_function/vposexplode.h"
 #include "vec/utils/util.hpp"
 
 namespace doris::vectorized {
@@ -61,6 +62,7 @@ const std::unordered_map<std::string, 
std::function<std::unique_ptr<TableFunctio
                 {"explode_bitmap", 
TableFunctionCreator<VExplodeBitmapTableFunction>()},
                 {"explode_map", TableFunctionCreator<VExplodeMapTableFunction> 
{}},
                 {"explode_json_object", 
TableFunctionCreator<VExplodeJsonObjectTableFunction> {}},
+                {"posexplode", TableFunctionCreator<VPosExplodeTableFunction> 
{}},
                 {"explode", TableFunctionCreator<VExplodeTableFunction> {}}};
 
 Status TableFunctionFactory::get_fn(const TFunction& t_fn, ObjectPool* pool, 
TableFunction** fn) {
diff --git a/be/src/vec/exprs/table_function/vposexplode.cpp 
b/be/src/vec/exprs/table_function/vposexplode.cpp
new file mode 100644
index 00000000000..20d04a219f8
--- /dev/null
+++ b/be/src/vec/exprs/table_function/vposexplode.cpp
@@ -0,0 +1,155 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+#include "vec/exprs/table_function/vposexplode.h"
+
+#include <glog/logging.h>
+
+#include <ostream>
+#include <vector>
+
+#include "common/status.h"
+#include "vec/columns/column.h"
+#include "vec/columns/column_nullable.h"
+#include "vec/columns/columns_number.h"
+#include "vec/common/assert_cast.h"
+#include "vec/common/string_ref.h"
+#include "vec/core/block.h"
+#include "vec/core/column_with_type_and_name.h"
+#include "vec/exprs/vexpr.h"
+#include "vec/exprs/vexpr_context.h"
+
+namespace doris::vectorized {
+
+VPosExplodeTableFunction::VPosExplodeTableFunction() {
+    _fn_name = "posexplode";
+}
+
+Status VPosExplodeTableFunction::process_init(Block* block, RuntimeState* 
state) {
+    CHECK(_expr_context->root()->children().size() == 1)
+            << "VPosExplodeTableFunction only support 1 child but has "
+            << _expr_context->root()->children().size();
+
+    int value_column_idx = -1;
+    
RETURN_IF_ERROR(_expr_context->root()->children()[0]->execute(_expr_context.get(),
 block,
+                                                                  
&value_column_idx));
+
+    _collection_column =
+            
block->get_by_position(value_column_idx).column->convert_to_full_column_if_const();
+
+    if (!extract_column_array_info(*_collection_column, _array_detail)) {
+        return Status::NotSupported("column type {} not supported now, only 
support array",
+                                    
block->get_by_position(value_column_idx).column->get_name());
+    }
+    if (is_column_nullable(*_collection_column)) {
+        _array_data_column =
+                assert_cast<const ColumnArray&>(
+                        assert_cast<const 
ColumnNullable&>(*_collection_column).get_nested_column())
+                        .get_data_ptr();
+    } else {
+        _array_data_column = assert_cast<const 
ColumnArray&>(*_collection_column).get_data_ptr();
+    }
+    return Status::OK();
+}
+
+void VPosExplodeTableFunction::process_row(size_t row_idx) {
+    DCHECK(row_idx < _collection_column->size());
+    TableFunction::process_row(row_idx);
+
+    if (!_array_detail.array_nullmap_data || 
!_array_detail.array_nullmap_data[row_idx]) {
+        _collection_offset = (*_array_detail.offsets_ptr)[row_idx - 1];
+        _cur_size = (*_array_detail.offsets_ptr)[row_idx] - _collection_offset;
+    }
+}
+
+void VPosExplodeTableFunction::process_close() {
+    _collection_column = nullptr;
+    _array_data_column = nullptr;
+    _array_detail.reset();
+    _collection_offset = 0;
+}
+
+void VPosExplodeTableFunction::get_same_many_values(MutableColumnPtr& column, 
int length) {
+    // now we only support array column explode to struct column
+    size_t pos = _collection_offset + _cur_offset;
+    // if current is empty array row, also append a default value
+    if (current_empty()) {
+        column->insert_many_defaults(length);
+        return;
+    }
+    ColumnStruct* ret = nullptr;
+    // this _is_nullable is whole output column's nullable
+    if (_is_nullable) {
+        ret = assert_cast<ColumnStruct*>(
+                
assert_cast<ColumnNullable*>(column.get())->get_nested_column_ptr().get());
+        assert_cast<ColumnUInt8*>(
+                
assert_cast<ColumnNullable*>(column.get())->get_null_map_column_ptr().get())
+                ->insert_many_defaults(length);
+    } else if (column->is_column_struct()) {
+        ret = assert_cast<ColumnStruct*>(column.get());
+    } else {
+        throw Exception(ErrorCode::INTERNAL_ERROR,
+                        "only support array column explode to struct column");
+    }
+    if (!ret || ret->tuple_size() != 2) {
+        throw Exception(
+                ErrorCode::INTERNAL_ERROR,
+                "only support array column explode to two column, but given:  
", ret->tuple_size());
+    }
+    auto& pose_column_nullable = 
assert_cast<ColumnNullable&>(ret->get_column(0));
+    pose_column_nullable.get_null_map_column().insert_many_defaults(length);
+    assert_cast<ColumnInt32&>(pose_column_nullable.get_nested_column())
+            .insert_many_vals(_cur_offset, length);
+    ret->get_column(1).insert_many_from(*_array_data_column, pos, length);
+}
+
+int VPosExplodeTableFunction::get_value(MutableColumnPtr& column, int 
max_step) {
+    max_step = std::min(max_step, (int)(_cur_size - _cur_offset));
+    size_t pos = _collection_offset + _cur_offset;
+    if (current_empty()) {
+        column->insert_default();
+        max_step = 1;
+    } else {
+        ColumnStruct* struct_column = nullptr;
+        if (_is_nullable) {
+            auto* nullable_column = assert_cast<ColumnNullable*>(column.get());
+            struct_column =
+                    
assert_cast<ColumnStruct*>(nullable_column->get_nested_column_ptr().get());
+            auto* nullmap_column =
+                    
assert_cast<ColumnUInt8*>(nullable_column->get_null_map_column_ptr().get());
+            // here nullmap_column insert max_step many defaults as if 
array[row_idx] is NULL
+            // will be not update value, _cur_size = 0, means current_empty;
+            // so here could insert directly
+            nullmap_column->insert_many_defaults(max_step);
+        } else {
+            struct_column = assert_cast<ColumnStruct*>(column.get());
+        }
+        if (!struct_column || struct_column->tuple_size() != 2) {
+            throw Exception(ErrorCode::INTERNAL_ERROR,
+                            "only support array column explode to two column, 
but given:  ",
+                            struct_column->tuple_size());
+        }
+        auto& pose_column_nullable = 
assert_cast<ColumnNullable&>(struct_column->get_column(0));
+        
pose_column_nullable.get_null_map_column().insert_many_defaults(max_step);
+        assert_cast<ColumnInt32&>(pose_column_nullable.get_nested_column())
+                .insert_range_of_integer(_cur_offset, _cur_offset + max_step);
+        struct_column->get_column(1).insert_range_from(*_array_data_column, 
pos, max_step);
+    }
+    forward(max_step);
+    return max_step;
+}
+} // namespace doris::vectorized
diff --git a/be/src/vec/exprs/table_function/vposexplode.h 
b/be/src/vec/exprs/table_function/vposexplode.h
new file mode 100644
index 00000000000..4e021fd58da
--- /dev/null
+++ b/be/src/vec/exprs/table_function/vposexplode.h
@@ -0,0 +1,50 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+#pragma once
+
+#include "common/status.h"
+#include "vec/columns/column_map.h"
+#include "vec/data_types/data_type.h"
+#include "vec/data_types/data_type_array.h"
+#include "vec/exprs/table_function/table_function.h"
+#include "vec/functions/array/function_array_utils.h"
+
+namespace doris::vectorized {
+
+class VPosExplodeTableFunction : public TableFunction {
+    ENABLE_FACTORY_CREATOR(VPosExplodeTableFunction);
+
+public:
+    VPosExplodeTableFunction();
+
+    ~VPosExplodeTableFunction() override = default;
+
+    Status process_init(Block* block, RuntimeState* state) override;
+    void process_row(size_t row_idx) override;
+    void process_close() override;
+    void get_same_many_values(MutableColumnPtr& column, int length) override;
+    int get_value(MutableColumnPtr& column, int max_step) override;
+
+private:
+    ColumnPtr _collection_column;
+    ColumnPtr _array_data_column;
+    ColumnArrayExecutionData _array_detail;
+    size_t _collection_offset; // start offset of array[row_idx]
+};
+
+} // namespace doris::vectorized
diff --git a/be/src/vec/functions/function_fake.cpp 
b/be/src/vec/functions/function_fake.cpp
index 6a4f6275e1a..646da600b50 100644
--- a/be/src/vec/functions/function_fake.cpp
+++ b/be/src/vec/functions/function_fake.cpp
@@ -21,6 +21,7 @@
 
 #include <algorithm>
 #include <boost/iterator/iterator_facade.hpp>
+#include <memory>
 #include <ostream>
 #include <string>
 
@@ -83,6 +84,25 @@ struct FunctionExplodeMap {
     static std::string get_error_msg() { return "Fake function do not support 
execute"; }
 };
 
+template <bool AlwaysNullable = false>
+struct FunctionPoseExplode {
+    static DataTypePtr get_return_type_impl(const DataTypes& arguments) {
+        DCHECK(is_array(arguments[0])) << arguments[0]->get_name() << " not 
supported";
+        DataTypes fieldTypes(2);
+        fieldTypes[0] = make_nullable(std::make_shared<DataTypeInt32>());
+        fieldTypes[1] =
+                
check_and_get_data_type<DataTypeArray>(arguments[0].get())->get_nested_type();
+        auto struct_type = 
std::make_shared<vectorized::DataTypeStruct>(fieldTypes);
+        if constexpr (AlwaysNullable) {
+            return make_nullable(struct_type);
+        } else {
+            return arguments[0]->is_nullable() ? make_nullable(struct_type) : 
struct_type;
+        }
+    }
+    static DataTypes get_variadic_argument_types() { return {}; }
+    static std::string get_error_msg() { return "Fake function do not support 
execute"; }
+};
+
 // explode json-object: expands json-object to struct with a pair of key and 
value in column string
 struct FunctionExplodeJsonObject {
     static DataTypePtr get_return_type_impl(const DataTypes& arguments) {
@@ -138,6 +158,12 @@ void 
register_table_function_expand_outer_default(SimpleFunctionFactory& factory
                                                                  
COMBINATOR_SUFFIX_OUTER);
 };
 
+template <typename FunctionImpl>
+void register_table_function_with_impl(SimpleFunctionFactory& factory, const 
std::string& name,
+                                       const std::string& suffix = "") {
+    factory.register_function<FunctionFake<FunctionImpl>>(name + suffix);
+};
+
 void register_function_fake(SimpleFunctionFactory& factory) {
     register_function<FunctionEsquery>(factory, "esquery");
 
@@ -158,6 +184,9 @@ void register_function_fake(SimpleFunctionFactory& factory) 
{
     register_table_function_expand_outer_default<DataTypeFloat64, false>(
             factory, "explode_json_array_double");
     register_table_function_expand_outer_default<DataTypeInt64, 
false>(factory, "explode_bitmap");
+    register_table_function_with_impl<FunctionPoseExplode<false>>(factory, 
"posexplode");
+    register_table_function_with_impl<FunctionPoseExplode<true>>(factory, 
"posexplode",
+                                                                 
COMBINATOR_SUFFIX_OUTER);
     register_table_function_expand_outer_default<DataTypeObject, 
false>(factory,
                                                                         
"explode_variant_array");
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/catalog/BuiltinTableGeneratingFunctions.java
 
b/fe/fe-core/src/main/java/org/apache/doris/catalog/BuiltinTableGeneratingFunctions.java
index 8f9679a0b51..d342b01ff7e 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/catalog/BuiltinTableGeneratingFunctions.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/catalog/BuiltinTableGeneratingFunctions.java
@@ -38,6 +38,8 @@ import 
org.apache.doris.nereids.trees.expressions.functions.generator.ExplodeOut
 import 
org.apache.doris.nereids.trees.expressions.functions.generator.ExplodeSplit;
 import 
org.apache.doris.nereids.trees.expressions.functions.generator.ExplodeSplitOuter;
 import 
org.apache.doris.nereids.trees.expressions.functions.generator.ExplodeVariantArray;
+import 
org.apache.doris.nereids.trees.expressions.functions.generator.PosExplode;
+import 
org.apache.doris.nereids.trees.expressions.functions.generator.PosExplodeOuter;
 
 import com.google.common.collect.ImmutableList;
 
@@ -71,7 +73,9 @@ public class BuiltinTableGeneratingFunctions implements 
FunctionHelper {
             tableGenerating(ExplodeJsonArrayStringOuter.class, 
"explode_json_array_string_outer"),
             tableGenerating(ExplodeJsonArrayJson.class, 
"explode_json_array_json"),
             tableGenerating(ExplodeJsonArrayJsonOuter.class, 
"explode_json_array_json_outer"),
-            tableGenerating(ExplodeVariantArray.class, "explode_variant_array")
+            tableGenerating(ExplodeVariantArray.class, 
"explode_variant_array"),
+            tableGenerating(PosExplode.class, "posexplode"),
+            tableGenerating(PosExplodeOuter.class, "posexplode_outer")
     );
 
     public static final BuiltinTableGeneratingFunctions INSTANCE = new 
BuiltinTableGeneratingFunctions();
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/generator/PosExplode.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/generator/PosExplode.java
new file mode 100644
index 00000000000..16f8232606f
--- /dev/null
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/generator/PosExplode.java
@@ -0,0 +1,80 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.nereids.trees.expressions.functions.generator;
+
+import org.apache.doris.catalog.FunctionSignature;
+import org.apache.doris.nereids.exceptions.AnalysisException;
+import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.functions.PropagateNullable;
+import org.apache.doris.nereids.trees.expressions.shape.UnaryExpression;
+import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
+import org.apache.doris.nereids.types.ArrayType;
+import org.apache.doris.nereids.types.IntegerType;
+import org.apache.doris.nereids.types.StructField;
+import org.apache.doris.nereids.types.StructType;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+
+import java.util.List;
+
+/**
+ * PosExplode(array('a','b','c')) generate two columns and three rows with:
+ * pose column: 0, 1, 2
+ * value column: 'a', 'b', 'c'
+ */
+public class PosExplode extends TableGeneratingFunction implements 
UnaryExpression, PropagateNullable {
+
+    /**
+     * constructor with 1 argument.
+     */
+    public PosExplode(Expression arg) {
+        super("posexplode", arg);
+    }
+
+    /**
+     * withChildren.
+     */
+    @Override
+    public PosExplode withChildren(List<Expression> children) {
+        Preconditions.checkArgument(children.size() == 1);
+        return new PosExplode(children.get(0));
+    }
+
+    @Override
+    public void checkLegalityBeforeTypeCoercion() {
+        if (!(child().getDataType() instanceof ArrayType)) {
+            throw new AnalysisException("only support array type for 
posexplode function but got "
+                    + child().getDataType());
+        }
+    }
+
+    @Override
+    public List<FunctionSignature> getSignatures() {
+        return ImmutableList.of(
+                FunctionSignature.ret(new StructType(ImmutableList.of(
+                        new StructField("pos", IntegerType.INSTANCE, true, ""),
+                        new StructField("col", ((ArrayType) 
child().getDataType()).getItemType(), true, ""))))
+                        .args(child().getDataType()));
+    }
+
+    @Override
+    public <R, C> R accept(ExpressionVisitor<R, C> visitor, C context) {
+        return visitor.visitPosExplode(this, context);
+    }
+}
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/generator/PosExplodeOuter.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/generator/PosExplodeOuter.java
new file mode 100644
index 00000000000..6d181354f41
--- /dev/null
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/generator/PosExplodeOuter.java
@@ -0,0 +1,80 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.nereids.trees.expressions.functions.generator;
+
+import org.apache.doris.catalog.FunctionSignature;
+import org.apache.doris.nereids.exceptions.AnalysisException;
+import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.functions.AlwaysNullable;
+import org.apache.doris.nereids.trees.expressions.literal.StructLiteral;
+import org.apache.doris.nereids.trees.expressions.shape.UnaryExpression;
+import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
+import org.apache.doris.nereids.types.ArrayType;
+import org.apache.doris.nereids.types.IntegerType;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+
+import java.util.List;
+
+/**
+ * PosExplode(array('a','b','c')) generate two columns and three rows with:
+ * pose column: 0, 1, 2
+ * value column: 'a', 'b', 'c'
+ */
+public class PosExplodeOuter extends TableGeneratingFunction implements 
UnaryExpression, AlwaysNullable {
+
+    /**
+     * constructor with 1 argument.
+     */
+    public PosExplodeOuter(Expression arg) {
+        super("posexplode_outer", arg);
+    }
+
+    /**
+     * withChildren.
+     */
+    @Override
+    public PosExplodeOuter withChildren(List<Expression> children) {
+        Preconditions.checkArgument(children.size() == 1);
+        return new PosExplodeOuter(children.get(0));
+    }
+
+    @Override
+    public void checkLegalityBeforeTypeCoercion() {
+        if (!(child().getDataType() instanceof ArrayType)) {
+            throw new AnalysisException("only support array type for 
posexplode_outer function but got "
+                    + child().getDataType());
+        }
+    }
+
+    @Override
+    public List<FunctionSignature> getSignatures() {
+        return ImmutableList.of(
+                FunctionSignature.ret(StructLiteral.constructStructType(
+                        Lists.newArrayList(IntegerType.INSTANCE,
+                                ((ArrayType) 
child().getDataType()).getItemType())))
+                        .args(child().getDataType()));
+    }
+
+    @Override
+    public <R, C> R accept(ExpressionVisitor<R, C> visitor, C context) {
+        return visitor.visitPosExplodeOuter(this, context);
+    }
+}
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/TableGeneratingFunctionVisitor.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/TableGeneratingFunctionVisitor.java
index 94839b21fe7..9fae7c397ca 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/TableGeneratingFunctionVisitor.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/TableGeneratingFunctionVisitor.java
@@ -38,6 +38,8 @@ import 
org.apache.doris.nereids.trees.expressions.functions.generator.ExplodeOut
 import 
org.apache.doris.nereids.trees.expressions.functions.generator.ExplodeSplit;
 import 
org.apache.doris.nereids.trees.expressions.functions.generator.ExplodeSplitOuter;
 import 
org.apache.doris.nereids.trees.expressions.functions.generator.ExplodeVariantArray;
+import 
org.apache.doris.nereids.trees.expressions.functions.generator.PosExplode;
+import 
org.apache.doris.nereids.trees.expressions.functions.generator.PosExplodeOuter;
 import 
org.apache.doris.nereids.trees.expressions.functions.generator.TableGeneratingFunction;
 import org.apache.doris.nereids.trees.expressions.functions.udf.JavaUdtf;
 
@@ -134,4 +136,12 @@ public interface TableGeneratingFunctionVisitor<R, C> {
     default R visitJavaUdtf(JavaUdtf udtf, C context) {
         return visitTableGeneratingFunction(udtf, context);
     }
+
+    default R visitPosExplode(PosExplode posExplode, C context) {
+        return visitTableGeneratingFunction(posExplode, context);
+    }
+
+    default R visitPosExplodeOuter(PosExplodeOuter posExplodeOuter, C context) 
{
+        return visitTableGeneratingFunction(posExplodeOuter, context);
+    }
 }
diff --git 
a/regression-test/data/nereids_p0/sql_functions/table_function/posexplode.out 
b/regression-test/data/nereids_p0/sql_functions/table_function/posexplode.out
new file mode 100644
index 00000000000..393e13a2b54
--- /dev/null
+++ 
b/regression-test/data/nereids_p0/sql_functions/table_function/posexplode.out
@@ -0,0 +1,166 @@
+-- This file is automatically generated. You should know what you did if you 
want to edit this
+-- !sql --
+0      zhangsan        ["Chinese", "Math", "English"]
+1      lisi    ["null"]
+2      wangwu  ["88a", "90b", "96c"]
+3      lisi2   [null]
+4      amory   \N
+
+-- !explode_sql --
+0      zhangsan        ["Chinese", "Math", "English"]  0       Chinese
+0      zhangsan        ["Chinese", "Math", "English"]  1       Math
+0      zhangsan        ["Chinese", "Math", "English"]  2       English
+1      lisi    ["null"]        0       null
+2      wangwu  ["88a", "90b", "96c"]   0       88a
+2      wangwu  ["88a", "90b", "96c"]   1       90b
+2      wangwu  ["88a", "90b", "96c"]   2       96c
+3      lisi2   [null]  0       \N
+
+-- !explode_outer_sql --
+0      zhangsan        ["Chinese", "Math", "English"]  0       Chinese
+0      zhangsan        ["Chinese", "Math", "English"]  1       Math
+0      zhangsan        ["Chinese", "Math", "English"]  2       English
+1      lisi    ["null"]        0       null
+2      wangwu  ["88a", "90b", "96c"]   0       88a
+2      wangwu  ["88a", "90b", "96c"]   1       90b
+2      wangwu  ["88a", "90b", "96c"]   2       96c
+3      lisi2   [null]  0       \N
+4      amory   \N      \N      \N
+
+-- !explode_sql_multi --
+0      zhangsan        ["Chinese", "Math", "English"]  0       Chinese 0       
Chinese
+0      zhangsan        ["Chinese", "Math", "English"]  0       Chinese 1       
Math
+0      zhangsan        ["Chinese", "Math", "English"]  0       Chinese 2       
English
+0      zhangsan        ["Chinese", "Math", "English"]  1       Math    0       
Chinese
+0      zhangsan        ["Chinese", "Math", "English"]  1       Math    1       
Math
+0      zhangsan        ["Chinese", "Math", "English"]  1       Math    2       
English
+0      zhangsan        ["Chinese", "Math", "English"]  2       English 0       
Chinese
+0      zhangsan        ["Chinese", "Math", "English"]  2       English 1       
Math
+0      zhangsan        ["Chinese", "Math", "English"]  2       English 2       
English
+1      lisi    ["null"]        0       null    0       null
+2      wangwu  ["88a", "90b", "96c"]   0       88a     0       88a
+2      wangwu  ["88a", "90b", "96c"]   0       88a     1       90b
+2      wangwu  ["88a", "90b", "96c"]   0       88a     2       96c
+2      wangwu  ["88a", "90b", "96c"]   1       90b     0       88a
+2      wangwu  ["88a", "90b", "96c"]   1       90b     1       90b
+2      wangwu  ["88a", "90b", "96c"]   1       90b     2       96c
+2      wangwu  ["88a", "90b", "96c"]   2       96c     0       88a
+2      wangwu  ["88a", "90b", "96c"]   2       96c     1       90b
+2      wangwu  ["88a", "90b", "96c"]   2       96c     2       96c
+3      lisi2   [null]  0       \N      0       \N
+
+-- !explode_sql_alias --
+0      zhangsan        ["Chinese", "Math", "English"]  0       Chinese
+0      zhangsan        ["Chinese", "Math", "English"]  1       Math
+0      zhangsan        ["Chinese", "Math", "English"]  2       English
+1      lisi    ["null"]        0       null
+2      wangwu  ["88a", "90b", "96c"]   0       88a
+2      wangwu  ["88a", "90b", "96c"]   1       90b
+2      wangwu  ["88a", "90b", "96c"]   2       96c
+3      lisi2   [null]  0       \N
+
+-- !explode_outer_sql_alias --
+0      zhangsan        ["Chinese", "Math", "English"]  0       Chinese
+0      zhangsan        ["Chinese", "Math", "English"]  1       Math
+0      zhangsan        ["Chinese", "Math", "English"]  2       English
+1      lisi    ["null"]        0       null
+2      wangwu  ["88a", "90b", "96c"]   0       88a
+2      wangwu  ["88a", "90b", "96c"]   1       90b
+2      wangwu  ["88a", "90b", "96c"]   2       96c
+3      lisi2   [null]  0       \N
+4      amory   \N      \N      \N
+
+-- !explode_sql_alias_multi --
+0      zhangsan        ["Chinese", "Math", "English"]  0       Chinese 0       
Chinese
+0      zhangsan        ["Chinese", "Math", "English"]  0       Chinese 1       
Math
+0      zhangsan        ["Chinese", "Math", "English"]  0       Chinese 2       
English
+0      zhangsan        ["Chinese", "Math", "English"]  1       Math    0       
Chinese
+0      zhangsan        ["Chinese", "Math", "English"]  1       Math    1       
Math
+0      zhangsan        ["Chinese", "Math", "English"]  1       Math    2       
English
+0      zhangsan        ["Chinese", "Math", "English"]  2       English 0       
Chinese
+0      zhangsan        ["Chinese", "Math", "English"]  2       English 1       
Math
+0      zhangsan        ["Chinese", "Math", "English"]  2       English 2       
English
+1      lisi    ["null"]        0       null    0       null
+2      wangwu  ["88a", "90b", "96c"]   0       88a     0       88a
+2      wangwu  ["88a", "90b", "96c"]   0       88a     1       90b
+2      wangwu  ["88a", "90b", "96c"]   0       88a     2       96c
+2      wangwu  ["88a", "90b", "96c"]   1       90b     0       88a
+2      wangwu  ["88a", "90b", "96c"]   1       90b     1       90b
+2      wangwu  ["88a", "90b", "96c"]   1       90b     2       96c
+2      wangwu  ["88a", "90b", "96c"]   2       96c     0       88a
+2      wangwu  ["88a", "90b", "96c"]   2       96c     1       90b
+2      wangwu  ["88a", "90b", "96c"]   2       96c     2       96c
+3      lisi2   [null]  0       \N      0       \N
+
+-- !sql --
+0      zhangsan        ["Chinese", "Math", "English"]
+1      lisi    ["null"]
+2      wangwu  ["88a", "90b", "96c"]
+3      lisi2   [null]
+4      liuba   []
+
+-- !explode_sql_not --
+0      zhangsan        ["Chinese", "Math", "English"]  0       Chinese
+0      zhangsan        ["Chinese", "Math", "English"]  1       Math
+0      zhangsan        ["Chinese", "Math", "English"]  2       English
+1      lisi    ["null"]        0       null
+2      wangwu  ["88a", "90b", "96c"]   0       88a
+2      wangwu  ["88a", "90b", "96c"]   1       90b
+2      wangwu  ["88a", "90b", "96c"]   2       96c
+3      lisi2   [null]  0       \N
+
+-- !explode_outer_sql_not --
+0      zhangsan        ["Chinese", "Math", "English"]  0       Chinese
+0      zhangsan        ["Chinese", "Math", "English"]  1       Math
+0      zhangsan        ["Chinese", "Math", "English"]  2       English
+1      lisi    ["null"]        0       null
+2      wangwu  ["88a", "90b", "96c"]   0       88a
+2      wangwu  ["88a", "90b", "96c"]   1       90b
+2      wangwu  ["88a", "90b", "96c"]   2       96c
+3      lisi2   [null]  0       \N
+4      liuba   []      \N      \N
+
+-- !explode_sql_alias_multi2 --
+0      zhangsan        ["Chinese", "Math", "English"]  {"pos":0, 
"col":"Chinese"}      {"pos":0, "col":"Chinese"}
+0      zhangsan        ["Chinese", "Math", "English"]  {"pos":0, 
"col":"Chinese"}      {"pos":1, "col":"Math"}
+0      zhangsan        ["Chinese", "Math", "English"]  {"pos":0, 
"col":"Chinese"}      {"pos":2, "col":"English"}
+0      zhangsan        ["Chinese", "Math", "English"]  {"pos":1, "col":"Math"} 
{"pos":0, "col":"Chinese"}
+0      zhangsan        ["Chinese", "Math", "English"]  {"pos":1, "col":"Math"} 
{"pos":1, "col":"Math"}
+0      zhangsan        ["Chinese", "Math", "English"]  {"pos":1, "col":"Math"} 
{"pos":2, "col":"English"}
+0      zhangsan        ["Chinese", "Math", "English"]  {"pos":2, 
"col":"English"}      {"pos":0, "col":"Chinese"}
+0      zhangsan        ["Chinese", "Math", "English"]  {"pos":2, 
"col":"English"}      {"pos":1, "col":"Math"}
+0      zhangsan        ["Chinese", "Math", "English"]  {"pos":2, 
"col":"English"}      {"pos":2, "col":"English"}
+1      lisi    ["null"]        {"pos":0, "col":"null"} {"pos":0, "col":"null"}
+2      wangwu  ["88a", "90b", "96c"]   {"pos":0, "col":"88a"}  {"pos":0, 
"col":"88a"}
+2      wangwu  ["88a", "90b", "96c"]   {"pos":0, "col":"88a"}  {"pos":1, 
"col":"90b"}
+2      wangwu  ["88a", "90b", "96c"]   {"pos":0, "col":"88a"}  {"pos":2, 
"col":"96c"}
+2      wangwu  ["88a", "90b", "96c"]   {"pos":1, "col":"90b"}  {"pos":0, 
"col":"88a"}
+2      wangwu  ["88a", "90b", "96c"]   {"pos":1, "col":"90b"}  {"pos":1, 
"col":"90b"}
+2      wangwu  ["88a", "90b", "96c"]   {"pos":1, "col":"90b"}  {"pos":2, 
"col":"96c"}
+2      wangwu  ["88a", "90b", "96c"]   {"pos":2, "col":"96c"}  {"pos":0, 
"col":"88a"}
+2      wangwu  ["88a", "90b", "96c"]   {"pos":2, "col":"96c"}  {"pos":1, 
"col":"90b"}
+2      wangwu  ["88a", "90b", "96c"]   {"pos":2, "col":"96c"}  {"pos":2, 
"col":"96c"}
+3      lisi2   [null]  {"pos":0, "col":null}   {"pos":0, "col":null}
+
+-- !explode_sql_alias_multi3 --
+0      zhangsan        ["Chinese", "Math", "English"]  {"pos":0, 
"col":"Chinese"}      {"pos":0, "col":"Chinese"}
+0      zhangsan        ["Chinese", "Math", "English"]  {"pos":0, 
"col":"Chinese"}      {"pos":1, "col":"Math"}
+0      zhangsan        ["Chinese", "Math", "English"]  {"pos":0, 
"col":"Chinese"}      {"pos":2, "col":"English"}
+0      zhangsan        ["Chinese", "Math", "English"]  {"pos":1, "col":"Math"} 
{"pos":0, "col":"Chinese"}
+0      zhangsan        ["Chinese", "Math", "English"]  {"pos":1, "col":"Math"} 
{"pos":1, "col":"Math"}
+0      zhangsan        ["Chinese", "Math", "English"]  {"pos":1, "col":"Math"} 
{"pos":2, "col":"English"}
+0      zhangsan        ["Chinese", "Math", "English"]  {"pos":2, 
"col":"English"}      {"pos":0, "col":"Chinese"}
+0      zhangsan        ["Chinese", "Math", "English"]  {"pos":2, 
"col":"English"}      {"pos":1, "col":"Math"}
+0      zhangsan        ["Chinese", "Math", "English"]  {"pos":2, 
"col":"English"}      {"pos":2, "col":"English"}
+1      lisi    ["null"]        {"pos":0, "col":"null"} {"pos":0, "col":"null"}
+2      wangwu  ["88a", "90b", "96c"]   {"pos":0, "col":"88a"}  {"pos":0, 
"col":"88a"}
+2      wangwu  ["88a", "90b", "96c"]   {"pos":0, "col":"88a"}  {"pos":1, 
"col":"90b"}
+2      wangwu  ["88a", "90b", "96c"]   {"pos":0, "col":"88a"}  {"pos":2, 
"col":"96c"}
+2      wangwu  ["88a", "90b", "96c"]   {"pos":1, "col":"90b"}  {"pos":0, 
"col":"88a"}
+2      wangwu  ["88a", "90b", "96c"]   {"pos":1, "col":"90b"}  {"pos":1, 
"col":"90b"}
+2      wangwu  ["88a", "90b", "96c"]   {"pos":1, "col":"90b"}  {"pos":2, 
"col":"96c"}
+2      wangwu  ["88a", "90b", "96c"]   {"pos":2, "col":"96c"}  {"pos":0, 
"col":"88a"}
+2      wangwu  ["88a", "90b", "96c"]   {"pos":2, "col":"96c"}  {"pos":1, 
"col":"90b"}
+2      wangwu  ["88a", "90b", "96c"]   {"pos":2, "col":"96c"}  {"pos":2, 
"col":"96c"}
+3      lisi2   [null]  {"pos":0, "col":null}   {"pos":0, "col":null}
+
diff --git 
a/regression-test/suites/nereids_p0/sql_functions/table_function/posexplode.groovy
 
b/regression-test/suites/nereids_p0/sql_functions/table_function/posexplode.groovy
new file mode 100644
index 00000000000..8320af92f48
--- /dev/null
+++ 
b/regression-test/suites/nereids_p0/sql_functions/table_function/posexplode.groovy
@@ -0,0 +1,82 @@
+// 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.
+
+suite("posexplode") {
+    sql "SET enable_nereids_planner=true"
+    sql "SET enable_fallback_to_original_planner=false"
+
+    sql """ DROP TABLE IF EXISTS table_test """
+    sql """
+        CREATE TABLE IF NOT EXISTS `table_test`(
+                   `id` INT NULL,
+                   `name` TEXT NULL,
+                   `score` array<string> NULL
+                 ) ENGINE=OLAP
+                 DUPLICATE KEY(`id`)
+                 COMMENT 'OLAP'
+                 DISTRIBUTED BY HASH(`id`) BUCKETS 1
+                 PROPERTIES ("replication_allocation" = "tag.location.default: 
1");
+    """
+
+    // insert values
+    sql """ insert into table_test values (0, "zhangsan", 
["Chinese","Math","English"]); """
+    sql """ insert into table_test values (1, "lisi", ["null"]); """
+    sql """ insert into table_test values (2, "wangwu", ["88a","90b","96c"]); 
"""
+    sql """ insert into table_test values (3, "lisi2", [null]); """
+    sql """ insert into table_test values (4, "amory", NULL); """
+
+    qt_sql """ select * from table_test order by id; """
+    order_qt_explode_sql """ select id,name,score, k,v from table_test lateral 
view posexplode(score) tmp as k,v order by id;"""
+    order_qt_explode_outer_sql """ select id,name,score, k,v from table_test 
lateral view posexplode_outer(score) tmp as k,v order by id; """
+
+    // multi lateral view
+    order_qt_explode_sql_multi """ select id,name,score, k,v,k1,v1 from 
table_test lateral view posexplode_outer(score) tmp as k,v lateral view 
posexplode(score) tmp2 as k1,v1 order by id;"""
+
+    // test with alias
+    order_qt_explode_sql_alias """ select id,name,score, tmp.k, tmp.v from 
table_test lateral view posexplode(score) tmp as k,v order by id;"""
+    order_qt_explode_outer_sql_alias """ select id,name,score, tmp.k, tmp.v 
from table_test lateral view posexplode_outer(score) tmp as k,v order by id; """
+
+    order_qt_explode_sql_alias_multi """ select id,name,score, tmp.k, tmp.v, 
tmp2.k, tmp2.v from table_test lateral view posexplode_outer(score) tmp as k,v 
lateral view posexplode(score) tmp2 as k,v order by id;"""
+
+    sql """ DROP TABLE IF EXISTS table_test_not """
+    sql """
+        CREATE TABLE IF NOT EXISTS `table_test_not`(
+                   `id` INT NULL,
+                   `name` TEXT NULL,
+                   `score` array<string> not NULL
+                 ) ENGINE=OLAP
+                 DUPLICATE KEY(`id`)
+                 COMMENT 'OLAP'
+                 DISTRIBUTED BY HASH(`id`) BUCKETS 1
+                 PROPERTIES ("replication_allocation" = "tag.location.default: 
1");
+    """
+
+    // insert values
+    sql """ insert into table_test_not values (0, "zhangsan", 
["Chinese","Math","English"]); """
+    sql """ insert into table_test_not values (1, "lisi", ["null"]); """
+    sql """ insert into table_test_not values (2, "wangwu", 
["88a","90b","96c"]); """
+    sql """ insert into table_test_not values (3, "lisi2", [null]); """
+    sql """ insert into table_test_not values (4, "liuba", []); """
+
+    qt_sql """ select * from table_test_not order by id; """
+    order_qt_explode_sql_not """ select id,name,score, k,v from table_test_not 
lateral view posexplode(score) tmp as k,v order by id;"""
+    order_qt_explode_outer_sql_not """ select id,name,score, k,v from 
table_test_not lateral view posexplode_outer(score) tmp as k,v order by id; """
+    order_qt_explode_sql_alias_multi2 """ select * from table_test_not lateral 
view posexplode(score) tmp as e1 lateral view posexplode(score) tmp2 as e2 
order by id;"""
+    sql """ set batch_size = 1; """
+    order_qt_explode_sql_alias_multi3 """ select * from table_test_not lateral 
view posexplode(score) tmp as e1 lateral view posexplode(score) tmp2 as e2 
order by id;"""
+
+}


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


Reply via email to