Coverage Report

Created: 2026-07-27 20:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/exprs/table_function/vexplode_v2.cpp
Line
Count
Source
1
// Licensed to the Apache Software Foundation (ASF) under one
2
// or more contributor license agreements.  See the NOTICE file
3
// distributed with this work for additional information
4
// regarding copyright ownership.  The ASF licenses this file
5
// to you under the Apache License, Version 2.0 (the
6
// "License"); you may not use this file except in compliance
7
// with the License.  You may obtain a copy of the License at
8
//
9
//   http://www.apache.org/licenses/LICENSE-2.0
10
//
11
// Unless required by applicable law or agreed to in writing,
12
// software distributed under the License is distributed on an
13
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
// KIND, either express or implied.  See the License for the
15
// specific language governing permissions and limitations
16
// under the License.
17
18
#include "exprs/table_function/vexplode_v2.h"
19
20
#include <glog/logging.h>
21
22
#include <algorithm>
23
#include <cstdint>
24
#include <ostream>
25
26
#include "common/status.h"
27
#include "core/assert_cast.h"
28
#include "core/block/block.h"
29
#include "core/block/column_with_type_and_name.h"
30
#include "core/column/column.h"
31
#include "core/column/column_array.h"
32
#include "core/column/column_nothing.h"
33
#include "core/column/column_struct.h"
34
#include "core/column/column_variant.h"
35
#include "core/data_type/data_type.h"
36
#include "core/data_type/data_type_array.h"
37
#include "core/data_type/data_type_nothing.h"
38
#include "core/data_type/primitive_type.h"
39
#include "exprs/function/function_helpers.h"
40
#include "exprs/vexpr.h"
41
#include "exprs/vexpr_context.h"
42
43
namespace doris {
44
45
26
VExplodeV2TableFunction::VExplodeV2TableFunction() {
46
26
    _fn_name = "vexplode";
47
26
}
48
49
Status VExplodeV2TableFunction::_process_init_variant(Block* block, int value_column_idx,
50
0
                                                      int children_column_idx) {
51
    // explode variant array
52
0
    auto column_without_nullable = remove_nullable(block->get_by_position(value_column_idx).column);
53
0
    auto column = column_without_nullable->convert_to_full_column_if_const();
54
0
    auto variant_column_ptr = IColumn::mutate(std::move(column));
55
0
    auto& variant_column = assert_cast<ColumnVariant&>(*variant_column_ptr);
56
0
    variant_column.finalize();
57
0
    _multi_detail[children_column_idx].output_as_variant = true;
58
0
    _multi_detail[children_column_idx].variant_enable_doc_mode = variant_column.enable_doc_mode();
59
0
    if (!variant_column.is_null_root()) {
60
0
        _array_columns[children_column_idx] = variant_column.get_root();
61
        // We need to wrap the output nested column within a variant column.
62
        // Otherwise the type is missmatched
63
0
        const auto* array_type = check_and_get_data_type<DataTypeArray>(
64
0
                remove_nullable(variant_column.get_root_type()).get());
65
0
        if (array_type == nullptr) {
66
0
            return Status::NotSupported("explode not support none array type {}",
67
0
                                        variant_column.get_root_type()->get_name());
68
0
        }
69
0
        _multi_detail[children_column_idx].nested_type = array_type->get_nested_type();
70
0
    } else {
71
        // null root, use nothing type
72
0
        auto array_column = ColumnNullable::create(ColumnArray::create(ColumnNothing::create(0)),
73
0
                                                   ColumnUInt8::create(0));
74
0
        array_column->insert_many_defaults(variant_column.size());
75
0
        _array_columns[children_column_idx] = std::move(array_column);
76
0
        _multi_detail[children_column_idx].nested_type = std::make_shared<DataTypeNothing>();
77
0
    }
78
0
    return Status::OK();
79
0
}
80
81
32
Status VExplodeV2TableFunction::process_init(Block* block, RuntimeState* state) {
82
32
    auto expr_size = _expr_context->root()->children().size();
83
32
    CHECK(expr_size >= 1) << "VExplodeV2TableFunction support one or more child but has "
84
0
                          << expr_size;
85
86
32
    int value_column_idx = -1;
87
32
    _multi_detail.resize(expr_size);
88
32
    _array_offsets.resize(expr_size);
89
32
    _array_columns.resize(expr_size);
90
91
76
    for (int i = 0; i < expr_size; i++) {
92
44
        RETURN_IF_ERROR(_expr_context->root()->children()[i]->execute(_expr_context.get(), block,
93
44
                                                                      &value_column_idx));
94
44
        if (block->get_by_position(value_column_idx).type->get_primitive_type() == TYPE_VARIANT) {
95
0
            RETURN_IF_ERROR(_process_init_variant(block, value_column_idx, i));
96
44
        } else {
97
44
            _array_columns[i] = block->get_by_position(value_column_idx)
98
44
                                        .column->convert_to_full_column_if_const();
99
44
        }
100
44
        if (!extract_column_array_info(*_array_columns[i], _multi_detail[i])) {
101
0
            return Status::NotSupported(
102
0
                    "column type {} not supported now",
103
0
                    block->get_by_position(value_column_idx).column->get_name());
104
0
        }
105
44
    }
106
107
32
    return Status::OK();
108
32
}
109
110
18
bool VExplodeV2TableFunction::support_block_fast_path() const {
111
18
    return _multi_detail.size() == 1;
112
18
}
113
114
Status VExplodeV2TableFunction::prepare_block_fast_path(Block* /*block*/, RuntimeState* /*state*/,
115
8
                                                        BlockFastPathContext* ctx) {
116
8
    DCHECK(support_block_fast_path());
117
8
    const auto& detail = _multi_detail[0];
118
8
    if (detail.offsets_ptr == nullptr || detail.nested_col.get() == nullptr) {
119
0
        return Status::InternalError("vexplode block fast path not initialized");
120
0
    }
121
8
    ctx->array_nullmap_data = detail.array_nullmap_data;
122
8
    ctx->offsets_ptr = detail.offsets_ptr;
123
8
    ctx->nested_col = detail.nested_col;
124
8
    ctx->nested_nullmap_data = detail.nested_nullmap_data;
125
8
    ctx->generate_row_index = _generate_row_index;
126
8
    return Status::OK();
127
8
}
128
129
54
void VExplodeV2TableFunction::process_row(size_t row_idx) {
130
54
    TableFunction::process_row(row_idx);
131
132
120
    for (int i = 0; i < _multi_detail.size(); i++) {
133
66
        auto& detail = _multi_detail[i];
134
66
        if (!detail.array_nullmap_data || !detail.array_nullmap_data[row_idx]) {
135
52
            _array_offsets[i] = (*detail.offsets_ptr)[row_idx - 1];
136
            // find max size in array
137
52
            auto cur_size = (*detail.offsets_ptr)[row_idx] - _array_offsets[i];
138
52
            _cur_size = std::max<unsigned long>(_cur_size, cur_size);
139
52
        }
140
66
    }
141
54
    _row_idx = row_idx;
142
54
}
143
144
10
void VExplodeV2TableFunction::process_close() {
145
10
    _multi_detail.clear();
146
10
    _array_offsets.clear();
147
10
    _array_columns.clear();
148
10
    _row_idx = 0;
149
10
}
150
151
62
void VExplodeV2TableFunction::get_same_many_values(MutableColumnPtr& column, int length) {
152
62
    if (current_empty()) {
153
12
        column->insert_many_defaults(length);
154
12
        return;
155
12
    }
156
50
    ColumnStruct* struct_column = nullptr;
157
50
    std::vector<IColumn*> columns;
158
159
50
    const bool multi_sub_columns = _multi_detail.size() > 1 || _generate_row_index;
160
161
50
    if (multi_sub_columns) {
162
18
        if (_is_nullable) {
163
18
            auto* nullable_column = assert_cast<ColumnNullable*>(column.get());
164
18
            struct_column =
165
18
                    assert_cast<ColumnStruct*>(nullable_column->get_nested_column_ptr().get());
166
18
            auto* nullmap_column = nullable_column->get_null_map_column_ptr().get();
167
18
            nullmap_column->insert_many_defaults(length);
168
169
18
        } else {
170
0
            struct_column = assert_cast<ColumnStruct*>(column.get());
171
0
        }
172
173
54
        for (size_t i = 0; i != _multi_detail.size(); ++i) {
174
36
            columns.emplace_back(&struct_column->get_column(i + (_generate_row_index ? 1 : 0)));
175
36
        }
176
32
    } else {
177
32
        columns.push_back(column.get());
178
32
    }
179
180
50
    if (_generate_row_index) {
181
0
        auto& pos_column = assert_cast<ColumnInt32&>(struct_column->get_column(0));
182
0
        pos_column.insert_many_vals(static_cast<int32_t>(_cur_offset), length);
183
0
    }
184
185
118
    for (int i = 0; i < _multi_detail.size(); i++) {
186
68
        auto& detail = _multi_detail[i];
187
68
        size_t pos = _array_offsets[i] + _cur_offset;
188
68
        size_t element_size = _multi_detail[i].array_col->size_at(_row_idx);
189
68
        auto& struct_field = *columns.at(i);
190
68
        if ((detail.array_nullmap_data && detail.array_nullmap_data[_row_idx])) {
191
6
            struct_field.insert_many_defaults(length);
192
62
        } else {
193
62
            auto* nullable_column = assert_cast<ColumnNullable*>(struct_field.get_ptr().get());
194
62
            auto* nullmap_column = nullable_column->get_null_map_column_ptr().get();
195
            // only need to check if the value at position pos is null
196
62
            if (element_size < _cur_offset ||
197
62
                (detail.nested_nullmap_data && detail.nested_nullmap_data[pos])) {
198
6
                nullable_column->insert_many_defaults(length);
199
56
            } else {
200
56
                nullable_column->get_nested_column_ptr()->insert_many_from(*detail.nested_col, pos,
201
56
                                                                           length);
202
56
                nullmap_column->insert_many_defaults(length);
203
56
            }
204
62
        }
205
68
    }
206
50
}
207
208
10
int VExplodeV2TableFunction::get_value(MutableColumnPtr& column, int max_step) {
209
10
    max_step = std::min(max_step, (int)(_cur_size - _cur_offset));
210
10
    const bool multi_sub_columns = _multi_detail.size() > 1 || _generate_row_index;
211
212
10
    ColumnStruct* struct_column = nullptr;
213
10
    std::vector<IColumn*> columns;
214
215
10
    if (current_empty()) {
216
0
        column->insert_default();
217
0
        max_step = 1;
218
10
    } else {
219
10
        if (multi_sub_columns) {
220
6
            if (_is_nullable) {
221
6
                auto* nullable_column = assert_cast<ColumnNullable*>(column.get());
222
6
                struct_column =
223
6
                        assert_cast<ColumnStruct*>(nullable_column->get_nested_column_ptr().get());
224
6
                auto* nullmap_column = nullable_column->get_null_map_column_ptr().get();
225
6
                nullmap_column->insert_many_defaults(max_step);
226
227
6
            } else {
228
0
                struct_column = assert_cast<ColumnStruct*>(column.get());
229
0
            }
230
231
18
            for (size_t i = 0; i != _multi_detail.size(); ++i) {
232
12
                columns.emplace_back(&struct_column->get_column(i + (_generate_row_index ? 1 : 0)));
233
12
            }
234
6
        } else {
235
4
            columns.emplace_back(column.get());
236
4
        }
237
238
10
        if (_generate_row_index) {
239
0
            auto& pos_column = assert_cast<ColumnInt32&>(struct_column->get_column(0));
240
0
            pos_column.insert_range_of_integer(static_cast<int32_t>(_cur_offset),
241
0
                                               static_cast<int32_t>(_cur_offset + max_step));
242
0
        }
243
244
26
        for (int i = 0; i < _multi_detail.size(); i++) {
245
16
            auto& detail = _multi_detail[i];
246
16
            size_t pos = _array_offsets[i] + _cur_offset;
247
16
            size_t element_size = _multi_detail[i].array_col->size_at(_row_idx);
248
16
            auto& struct_field = *columns.at(i);
249
16
            if (detail.array_nullmap_data && detail.array_nullmap_data[_row_idx]) {
250
2
                struct_field.insert_many_defaults(max_step);
251
14
            } else {
252
14
                auto* nullable_column = assert_cast<ColumnNullable*>(struct_field.get_ptr().get());
253
14
                auto* nullmap_column = nullable_column->get_null_map_column_ptr().get();
254
14
                if (element_size >= _cur_offset + max_step) {
255
14
                    nullable_column->get_nested_column_ptr()->insert_range_from(*detail.nested_col,
256
14
                                                                                pos, max_step);
257
14
                    if (detail.nested_nullmap_data) {
258
14
                        size_t old_size = nullmap_column->size();
259
14
                        nullmap_column->resize(old_size + max_step);
260
14
                        memcpy(nullmap_column->get_data().data() + old_size,
261
14
                               detail.nested_nullmap_data + pos, max_step * sizeof(UInt8));
262
14
                    } else {
263
0
                        nullmap_column->insert_many_defaults(max_step);
264
0
                    }
265
14
                } else if (element_size > _cur_offset) {
266
0
                    auto current_insert_num = element_size - _cur_offset;
267
0
                    nullable_column->get_nested_column_ptr()->insert_range_from(
268
0
                            *detail.nested_col, pos, current_insert_num);
269
0
                    if (detail.nested_nullmap_data) {
270
0
                        size_t old_size = nullmap_column->size();
271
0
                        nullmap_column->resize(old_size + current_insert_num);
272
0
                        memcpy(nullmap_column->get_data().data() + old_size,
273
0
                               detail.nested_nullmap_data + pos,
274
0
                               current_insert_num * sizeof(UInt8));
275
0
                    } else {
276
0
                        nullmap_column->insert_many_defaults(current_insert_num);
277
0
                    }
278
0
                    nullable_column->insert_many_defaults(max_step - current_insert_num);
279
0
                } else {
280
0
                    nullable_column->insert_many_defaults(max_step);
281
0
                }
282
14
            }
283
16
        }
284
10
    }
285
286
10
    forward(max_step);
287
10
    return max_step;
288
10
}
289
290
} // namespace doris