Coverage Report

Created: 2026-07-27 16:12

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/storage/iterator/block_reader.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 "storage/iterator/block_reader.h"
19
20
#include <gen_cpp/olap_file.pb.h>
21
#include <glog/logging.h>
22
#include <stdint.h>
23
24
#include <algorithm>
25
#include <boost/iterator/iterator_facade.hpp>
26
#include <iterator>
27
#include <memory>
28
#include <ostream>
29
#include <string>
30
31
// IWYU pragma: no_include <opentelemetry/common/threadlocal.h>
32
#include "cloud/config.h"
33
#include "common/compiler_util.h" // IWYU pragma: keep
34
#include "common/config.h"
35
#include "common/status.h"
36
#include "core/block/column_with_type_and_name.h"
37
#include "core/column/column_nullable.h"
38
#include "core/column/column_string.h"
39
#include "core/column/column_vector.h"
40
#include "core/data_type/data_type_number.h"
41
#include "exprs/aggregate/aggregate_function_reader.h"
42
#include "exprs/function_filter.h"
43
#include "runtime/runtime_state.h"
44
#include "storage/binlog.h"
45
#include "storage/iterator/binlog_block_reader_utils.h"
46
#include "storage/iterator/vcollect_iterator.h"
47
#include "storage/olap_common.h"
48
#include "storage/olap_define.h"
49
#include "storage/predicate/like_column_predicate.h"
50
#include "storage/row_ttl.h"
51
#include "storage/rowset/rowset.h"
52
#include "storage/rowset/rowset_reader_context.h"
53
#include "storage/tablet/tablet.h"
54
#include "storage/tablet/tablet_schema.h"
55
#include "storage/utils.h"
56
#include "util/defer_op.h"
57
58
namespace doris {
59
class ColumnPredicate;
60
} // namespace doris
61
62
namespace doris {
63
using namespace ErrorCode;
64
65
static constexpr int32_t BLOCK_SIZE_CHECK_INTERVAL_ROWS = 64;
66
67
96
BlockReader::~BlockReader() {
68
99
    for (int i = 0; i < _agg_functions.size(); ++i) {
69
3
        _agg_functions[i]->destroy(_agg_places[i]);
70
3
        delete[] _agg_places[i];
71
3
    }
72
96
}
73
74
580
Status BlockReader::next_block_with_aggregation(Block* block, bool* eof) {
75
580
    if (_remove_row_ttl_output) {
76
2
        const auto& ttl_column = _tablet_schema->column(_tablet_schema->ttl_col_idx());
77
2
        const auto ttl_type = ttl_column.get_vec_type();
78
2
        block->insert({ttl_type->create_column(), ttl_type, TTL_COL});
79
2
    }
80
580
    Defer remove_internal_ttl([&] {
81
580
        if (_remove_row_ttl_output) {
82
2
            DORIS_CHECK_EQ(_row_ttl_output_pos, block->columns() - 1);
83
2
            block->erase(_row_ttl_output_pos);
84
2
        }
85
580
    });
86
580
    auto res = (this->*_next_block_func)(block, eof);
87
580
    if (res.ok() && (_filter_delete_sign || _filter_row_ttl) && block->rows() > 0) {
88
285
        RowVisibilityFilter filter;
89
285
        RETURN_IF_ERROR(build_row_visibility_filter(*block, *_tablet_schema, _filter_delete_sign,
90
285
                                                    _filter_row_ttl, _row_ttl_now_us, &filter));
91
285
        if (UNLIKELY(_reader_context.record_rowids)) {
92
285
            DORIS_CHECK_EQ(_block_row_locations.size(), filter.selection.size());
93
280k
            for (size_t row = 0; row < filter.selection.size(); ++row) {
94
280k
                if (!filter.selection[row]) {
95
1
                    _block_row_locations[row].row_id = -1;
96
1
                }
97
280k
            }
98
285
        }
99
285
        RETURN_IF_ERROR(filter_block_by_row_visibility(block, filter.selection));
100
285
        _stats.rows_del_filtered += filter.rows_deleted;
101
285
    }
102
580
    if (!config::is_cloud_mode()) {
103
579
        if (!res.ok()) [[unlikely]] {
104
0
            static_cast<Tablet*>(_tablet.get())->report_error(res);
105
0
        }
106
579
    }
107
580
    return res;
108
580
}
109
110
// Lazily resolves the positions of the binlog meta columns (tso / lsn / op) inside the
111
// merged source block, and builds _before_column_idx mapping each non-meta column to its
112
// __BEFORE__ mirror. The resolved positions are reused across blocks; if the column
113
// layout changes (detected via _binlog_op_pos sanity check), they are re-resolved.
114
14
Status BlockReader::_ensure_binlog_column_pos(const Block& src_block) {
115
14
    if (_binlog_column_pos_inited) {
116
2
        if (_binlog_op_pos >= 0 && _binlog_op_pos < src_block.columns() &&
117
2
            src_block.get_by_position(_binlog_op_pos).name == BINLOG_OP_COL) {
118
2
            return Status::OK();
119
2
        }
120
0
        _binlog_tso_pos = -1;
121
0
        _binlog_lsn_pos = -1;
122
0
        _binlog_op_pos = -1;
123
0
        _binlog_column_pos_inited = false;
124
0
    }
125
126
12
    const uint32_t col_num = src_block.columns();
127
12
    _before_column_idx.resize(col_num);
128
84
    for (uint32_t i = 0; i < col_num; ++i) {
129
72
        const auto& name = src_block.get_by_position(i).name;
130
72
        if (name == BINLOG_TSO_COL) {
131
12
            _binlog_tso_pos = static_cast<int>(i);
132
60
        } else if (name == BINLOG_LSN_COL) {
133
12
            _binlog_lsn_pos = static_cast<int>(i);
134
48
        } else if (name == BINLOG_OP_COL) {
135
12
            _binlog_op_pos = static_cast<int>(i);
136
36
        } else {
137
36
            std::string before_name = binlog::build_before_column_name(name);
138
36
            int tmp_idx = src_block.get_position_by_name(before_name);
139
36
            _before_column_idx[i] = tmp_idx < 0 ? i : tmp_idx;
140
36
        }
141
72
    }
142
12
    _binlog_column_pos_inited = true;
143
12
    return Status::OK();
144
14
}
145
146
27
int64_t BlockReader::_read_binlog_op(const IColumn& col, size_t row) const {
147
27
    const IColumn* cur = &col;
148
27
    if (const auto* nullable = check_and_get_column<ColumnNullable>(*cur)) {
149
0
        if (nullable->is_null_at(row)) {
150
0
            return binlog::ROW_BINLOG_UNKNOWN;
151
0
        }
152
0
        cur = &nullable->get_nested_column();
153
0
    }
154
155
27
    if (const auto* int64_col = check_and_get_column<ColumnInt64>(*cur)) {
156
27
        return int64_col->get_element(row);
157
27
    }
158
159
0
    return binlog::ROW_BINLOG_UNKNOWN;
160
27
}
161
162
21
Status BlockReader::_write_binlog_op(IColumn& col, int64_t op) const {
163
21
    IColumn* cur = &col;
164
21
    ColumnNullable* nullable = nullptr;
165
21
    if (auto* n = typeid_cast<ColumnNullable*>(cur)) {
166
0
        nullable = n;
167
0
        cur = &nullable->get_nested_column();
168
0
    }
169
170
21
    if (auto* int64_col = typeid_cast<ColumnInt64*>(cur)) {
171
21
        int64_col->insert_value(op);
172
21
    } else {
173
0
        return Status::InternalError("invalid column type");
174
0
    }
175
176
21
    if (nullable != nullptr) {
177
0
        nullable->get_null_map_data().push_back(0);
178
0
    }
179
21
    return Status::OK();
180
21
}
181
182
41
bool BlockReader::_is_binlog_meta_column(int idx) const {
183
41
    return idx == _binlog_tso_pos || idx == _binlog_lsn_pos || idx == _binlog_op_pos;
184
41
}
185
186
// Resolves which source-block column to read from for a given binlog row position.
187
// When use_before is true and idx is a regular data column, return the index of its
188
// __BEFORE__ mirror (built in _before_column_idx); otherwise return idx itself.
189
// Binlog meta columns (tso / lsn / op) have no BEFORE mirror, so they always pass through.
190
76
int BlockReader::_resolve_source_column_index(int idx, bool use_before) const {
191
76
    if (!use_before || _is_binlog_meta_column(idx)) {
192
49
        return idx;
193
49
    }
194
195
27
    return _before_column_idx[idx];
196
76
}
197
198
14
void BlockReader::_init_pending_row_columns(const Block& block) {
199
14
    if (!_pending_row_columns.empty()) {
200
2
        return;
201
2
    }
202
12
    _pending_row_columns = block.clone_empty_columns();
203
12
}
204
205
// Drains the carry-over row produced on the previous batch boundary into the current
206
// output block. Returns true if a row was emitted, false if no pending row exists.
207
39
bool BlockReader::_emit_pending_row(MutableColumns& target_columns, size_t& output_row_count) {
208
39
    if (!_has_pending_row) {
209
37
        return false;
210
37
    }
211
14
    for (size_t i = 0; i < _pending_row_columns.size(); ++i) {
212
12
        target_columns[i]->insert_from(*_pending_row_columns[i], 0);
213
12
        _pending_row_columns[i]->clear();
214
12
    }
215
2
    _has_pending_row = false;
216
2
    output_row_count++;
217
2
    return true;
218
39
}
219
220
// Copies one source row into target_columns with the given output op code, picking BEFORE
221
// or AFTER values per column according to use_before. Used by _detail_change_next_block to
222
// materialize the BEFORE / AFTER halves of an UPDATE pair (and INSERT / DELETE singletons).
223
Status BlockReader::_append_change_row(MutableColumns& target_columns, const Block& src_block,
224
12
                                       size_t row_pos, int64_t output_op, bool use_before) {
225
72
    for (auto idx : _normal_columns_idx) {
226
72
        int target_col_idx = _return_columns_loc[idx];
227
72
        if (target_col_idx < 0) {
228
0
            continue;
229
0
        }
230
72
        if (idx == _binlog_op_pos) {
231
12
            RETURN_IF_ERROR(_write_binlog_op(*target_columns[target_col_idx], output_op));
232
12
            continue;
233
12
        }
234
60
        int source_idx = _resolve_source_column_index(idx, use_before);
235
60
        target_columns[target_col_idx]->insert_from(*src_block.get_by_position(source_idx).column,
236
60
                                                    row_pos);
237
60
    }
238
12
    return Status::OK();
239
12
}
240
241
// MIN_DELTA reader: groups consecutive rows sharing the same primary key in
242
// _stored_data_columns, then collapses the group into the minimum equivalent change
243
// (SKIP / INSERT / DELETE / UPDATE_BEFORE+AFTER) via AggregateFunctionMinDelta.
244
7
Status BlockReader::_min_delta_next_block(Block* block, bool* eof) {
245
7
    if (UNLIKELY(_eof && !_has_pending_row)) {
246
0
        *eof = true;
247
0
        return Status::OK();
248
0
    }
249
250
7
    if (_stored_data_columns.empty()) {
251
6
        _stored_data_columns = _next_row.block->clone_empty_columns();
252
6
    }
253
254
7
    auto target_columns_guard = block->mutate_columns_scoped();
255
7
    auto& target_columns = target_columns_guard.mutable_columns();
256
7
    size_t output_row_count = 0;
257
7
    _init_pending_row_columns(*block);
258
7
    RETURN_IF_ERROR(_ensure_binlog_column_pos(*_next_row.block));
259
24
    while (output_row_count < batch_max_rows()) {
260
23
        if (_emit_pending_row(target_columns, output_row_count)) {
261
1
            continue;
262
1
        }
263
22
        if (_eof) {
264
6
            break;
265
6
        }
266
16
        bool need_pop = _stored_data_columns[0]->size() > 1;
267
112
        for (size_t i = 0; i < _stored_data_columns.size(); ++i) {
268
96
            if (need_pop) {
269
0
                _stored_data_columns[i]->pop_back(1);
270
0
            }
271
96
            _stored_data_columns[i]->insert_from(*_next_row.block->get_by_position(i).column,
272
96
                                                 _next_row.row_pos);
273
96
        }
274
16
        auto res = _vcollect_iter.next(&_next_row);
275
16
        if (UNLIKELY(res.is<END_OF_FILE>())) {
276
6
            _eof = true;
277
10
        } else if (UNLIKELY(!res.ok())) {
278
0
            return res;
279
0
        }
280
281
16
        if (!_eof && _next_row.is_same) {
282
7
            continue;
283
7
        }
284
9
        size_t group_size = _stored_data_columns[0]->size();
285
9
        auto first_op = _read_binlog_op(*_stored_data_columns[_binlog_op_pos], 0);
286
9
        auto last_op = _read_binlog_op(*_stored_data_columns[_binlog_op_pos], group_size - 1);
287
9
        auto result = binlog::AggregateFunctionMinDelta::calculate_result(first_op, last_op);
288
9
        switch (result) {
289
2
        case binlog::AggregateFunctionMinDelta::ResultType::SKIP:
290
2
            break;
291
3
        case binlog::AggregateFunctionMinDelta::ResultType::INSERT:
292
18
            for (auto idx : _normal_columns_idx) {
293
18
                int target_col_idx = _return_columns_loc[idx];
294
18
                if (idx == _binlog_op_pos) {
295
3
                    RETURN_IF_ERROR(_write_binlog_op(*target_columns[target_col_idx],
296
3
                                                     binlog::STREAM_CHANGE_INSERT));
297
15
                } else {
298
                    // insert should use most recent value
299
15
                    target_columns[target_col_idx]->insert_from(*_stored_data_columns[idx],
300
15
                                                                group_size - 1);
301
15
                }
302
18
            }
303
3
            output_row_count++;
304
3
            break;
305
2
        case binlog::AggregateFunctionMinDelta::ResultType::DELETE:
306
12
            for (auto idx : _normal_columns_idx) {
307
12
                int target_col_idx = _return_columns_loc[idx];
308
12
                if (idx == _binlog_op_pos) {
309
2
                    RETURN_IF_ERROR(_write_binlog_op(*target_columns[target_col_idx],
310
2
                                                     binlog::STREAM_CHANGE_DELETE));
311
10
                } else if (idx == _binlog_lsn_pos || idx == _binlog_tso_pos) {
312
4
                    target_columns[target_col_idx]->insert_from(*_stored_data_columns[idx],
313
4
                                                                group_size - 1);
314
6
                } else {
315
                    // delete should use first op value
316
6
                    int source_idx = _resolve_source_column_index(idx, true);
317
6
                    target_columns[target_col_idx]->insert_from(*_stored_data_columns[source_idx],
318
6
                                                                0);
319
6
                }
320
12
            }
321
2
            output_row_count++;
322
2
            break;
323
2
        case binlog::AggregateFunctionMinDelta::ResultType::UPDATE_BEFORE_AFTER:
324
12
            for (auto idx : _normal_columns_idx) {
325
12
                int target_col_idx = _return_columns_loc[idx];
326
12
                if (idx == _binlog_op_pos) {
327
2
                    RETURN_IF_ERROR(_write_binlog_op(*target_columns[target_col_idx],
328
2
                                                     binlog::STREAM_CHANGE_UPDATE_BEFORE));
329
10
                } else {
330
10
                    int source_idx = _resolve_source_column_index(idx, true);
331
10
                    target_columns[target_col_idx]->insert_from(*_stored_data_columns[source_idx],
332
10
                                                                0);
333
10
                }
334
12
            }
335
2
            output_row_count++;
336
2
            if (output_row_count >= batch_max_rows()) {
337
6
                for (auto idx : _normal_columns_idx) {
338
6
                    int target_col_idx = _return_columns_loc[idx];
339
6
                    if (idx == _binlog_op_pos) {
340
1
                        RETURN_IF_ERROR(_write_binlog_op(*_pending_row_columns[target_col_idx],
341
1
                                                         binlog::STREAM_CHANGE_UPDATE_AFTER));
342
5
                    } else {
343
5
                        _pending_row_columns[target_col_idx]->insert_from(
344
5
                                *_stored_data_columns[idx], group_size - 1);
345
5
                    }
346
6
                }
347
1
                _has_pending_row = true;
348
1
            } else {
349
6
                for (auto idx : _normal_columns_idx) {
350
6
                    int target_col_idx = _return_columns_loc[idx];
351
6
                    if (idx == _binlog_op_pos) {
352
1
                        RETURN_IF_ERROR(_write_binlog_op(*target_columns[target_col_idx],
353
1
                                                         binlog::STREAM_CHANGE_UPDATE_AFTER));
354
5
                    } else {
355
5
                        target_columns[target_col_idx]->insert_from(*_stored_data_columns[idx],
356
5
                                                                    group_size - 1);
357
5
                    }
358
6
                }
359
1
                output_row_count++;
360
1
            }
361
2
            break;
362
9
        }
363
364
54
        for (auto& col : _stored_data_columns) {
365
54
            col->clear();
366
54
        }
367
9
    }
368
7
    *eof = _eof && !_has_pending_row;
369
7
    return Status::OK();
370
7
}
371
372
// DETAIL reader: emits every recorded binlog change verbatim. APPEND -> single INSERT row,
373
// DELETE -> single DELETE row, UPDATE -> a BEFORE+AFTER pair. When the AFTER row would
374
// overflow batch_max_rows(), it is parked in _pending_row_columns and flushed next call.
375
8
Status BlockReader::_detail_change_next_block(Block* block, bool* eof) {
376
8
    if (UNLIKELY(_eof && !_has_pending_row)) {
377
1
        *eof = true;
378
1
        return Status::OK();
379
1
    }
380
7
    auto target_columns_guard = block->mutate_columns_scoped();
381
7
    auto& target_columns = target_columns_guard.mutable_columns();
382
7
    size_t output_row_count = 0;
383
7
    _init_pending_row_columns(*block);
384
7
    RETURN_IF_ERROR(_ensure_binlog_column_pos(*_next_row.block));
385
17
    while (output_row_count < batch_max_rows()) {
386
16
        if (_emit_pending_row(target_columns, output_row_count)) {
387
1
            continue;
388
1
        }
389
15
        if (_eof) {
390
6
            break;
391
6
        }
392
9
        if (UNLIKELY(_next_row.block == nullptr)) {
393
0
            return Status::InternalError("invalid row reference in detail change reader");
394
0
        }
395
9
        const Block& source_block = *_next_row.block;
396
9
        const size_t row = _next_row.row_pos;
397
9
        int64_t op = _read_binlog_op(*source_block.get_by_position(_binlog_op_pos).column, row);
398
9
        if (op == ROW_BINLOG_UPDATE) {
399
3
            RETURN_IF_ERROR(_append_change_row(target_columns, source_block, row,
400
3
                                               binlog::STREAM_CHANGE_UPDATE_BEFORE, true));
401
3
            output_row_count++;
402
3
            if (output_row_count >= batch_max_rows()) {
403
1
                RETURN_IF_ERROR(_append_change_row(_pending_row_columns, source_block, row,
404
1
                                                   binlog::STREAM_CHANGE_UPDATE_AFTER, false));
405
1
                _has_pending_row = true;
406
2
            } else {
407
2
                RETURN_IF_ERROR(_append_change_row(target_columns, source_block, row,
408
2
                                                   binlog::STREAM_CHANGE_UPDATE_AFTER, false));
409
2
                output_row_count++;
410
2
            }
411
6
        } else if (op == ROW_BINLOG_APPEND) {
412
4
            RETURN_IF_ERROR(_append_change_row(target_columns, source_block, row,
413
4
                                               binlog::STREAM_CHANGE_INSERT, false));
414
4
            output_row_count++;
415
4
        } else if (op == ROW_BINLOG_DELETE) {
416
2
            RETURN_IF_ERROR(_append_change_row(target_columns, source_block, row,
417
2
                                               binlog::STREAM_CHANGE_DELETE, true));
418
2
            output_row_count++;
419
2
        }
420
421
9
        auto res = _vcollect_iter.next(&_next_row);
422
9
        if (UNLIKELY(res.is<END_OF_FILE>())) {
423
6
            _eof = true;
424
6
        } else if (UNLIKELY(!res.ok())) {
425
0
            return res;
426
0
        }
427
9
    }
428
7
    *eof = _eof && !_has_pending_row;
429
7
    return Status::OK();
430
7
}
431
432
67
bool BlockReader::_rowsets_not_mono_asc_disjoint(const ReaderParams& read_params) {
433
67
    std::string pre_rs_last_key;
434
67
    bool pre_rs_key_bounds_truncated {false};
435
67
    const std::vector<RowSetSplits>& rs_splits = read_params.rs_splits;
436
162
    for (const auto& rs_split : rs_splits) {
437
162
        if (rs_split.rs_reader->rowset()->num_rows() == 0) {
438
0
            continue;
439
0
        }
440
162
        if (rs_split.rs_reader->rowset()->is_segments_overlapping()) {
441
0
            return true;
442
0
        }
443
162
        std::string rs_first_key;
444
162
        bool has_first_key = rs_split.rs_reader->rowset()->first_key(&rs_first_key);
445
162
        if (!has_first_key) {
446
0
            return true;
447
0
        }
448
162
        bool cur_rs_key_bounds_truncated {
449
162
                rs_split.rs_reader->rowset()->is_segments_key_bounds_truncated()};
450
162
        if (!Slice::lhs_is_strictly_less_than_rhs(Slice {pre_rs_last_key},
451
162
                                                  pre_rs_key_bounds_truncated, Slice {rs_first_key},
452
162
                                                  cur_rs_key_bounds_truncated)) {
453
58
            return true;
454
58
        }
455
104
        bool has_last_key = rs_split.rs_reader->rowset()->last_key(&pre_rs_last_key);
456
104
        pre_rs_key_bounds_truncated = cur_rs_key_bounds_truncated;
457
104
        CHECK(has_last_key);
458
104
    }
459
9
    return false;
460
67
}
461
462
48
Status BlockReader::_init_collect_iter(const ReaderParams& read_params) {
463
48
    auto res = _capture_rs_readers(read_params);
464
48
    if (!res.ok()) {
465
0
        LOG(WARNING) << "fail to init reader when _capture_rs_readers. res:" << res
466
0
                     << ", tablet_id:" << read_params.tablet->tablet_id()
467
0
                     << ", schema_hash:" << read_params.tablet->schema_hash()
468
0
                     << ", reader_type:" << int(read_params.reader_type)
469
0
                     << ", version:" << read_params.version;
470
0
        return res;
471
0
    }
472
    // check if rowsets are noneoverlapping
473
48
    {
474
48
        SCOPED_RAW_TIMER(&_stats.block_reader_vcollect_iter_init_timer_ns);
475
48
        _is_rowsets_overlapping = _rowsets_not_mono_asc_disjoint(read_params);
476
48
        const bool is_min_delta_stream = read_params.binlog_scan_type == TBinlogScanType::MIN_DELTA;
477
48
        const bool force_merge = read_params.read_orderby_key || is_min_delta_stream;
478
48
        const bool is_reverse = !is_min_delta_stream && read_params.read_orderby_key_reverse;
479
48
        _vcollect_iter.init(this, _is_rowsets_overlapping, force_merge, is_reverse);
480
48
    }
481
482
48
    std::vector<RowsetReaderSharedPtr> valid_rs_readers;
483
48
    RuntimeState* runtime_state = read_params.runtime_state;
484
485
48
    {
486
48
        SCOPED_RAW_TIMER(&_stats.block_reader_rs_readers_init_timer_ns);
487
192
        for (int i = 0; i < read_params.rs_splits.size(); ++i) {
488
144
            if (runtime_state != nullptr && runtime_state->is_cancelled()) {
489
0
                return runtime_state->cancel_reason();
490
0
            }
491
492
144
            auto& rs_split = read_params.rs_splits[i];
493
494
            // _vcollect_iter.topn_next() will init rs_reader by itself
495
144
            if (!_vcollect_iter.use_topn_next()) {
496
144
                RETURN_IF_ERROR(rs_split.rs_reader->init(&_reader_context, rs_split));
497
144
            }
498
499
144
            Status res1 = _vcollect_iter.add_child(rs_split);
500
144
            if (!res1.ok() && !res1.is<END_OF_FILE>()) {
501
0
                LOG(WARNING) << "failed to add child to iterator, err=" << res1;
502
0
                return res1;
503
0
            }
504
144
            if (res1.ok()) {
505
144
                valid_rs_readers.push_back(rs_split.rs_reader);
506
144
            }
507
144
        }
508
48
    }
509
48
    {
510
48
        SCOPED_RAW_TIMER(&_stats.block_reader_build_heap_init_timer_ns);
511
48
        RETURN_IF_ERROR(_vcollect_iter.build_heap(valid_rs_readers));
512
        // _vcollect_iter.topn_next() can not use current_row
513
48
        if (!_vcollect_iter.use_topn_next()) {
514
48
            auto status = _vcollect_iter.current_row(&_next_row);
515
48
            _eof = status.is<END_OF_FILE>();
516
48
        }
517
48
    }
518
519
0
    return Status::OK();
520
48
}
521
522
0
Status BlockReader::_init_agg_state(const ReaderParams& read_params) {
523
0
    if (_eof) {
524
0
        return Status::OK();
525
0
    }
526
527
0
    auto stored_block = _next_row.block->create_same_struct_block(batch_max_rows());
528
0
    _stored_data_columns = std::move(*stored_block).mutate_columns();
529
530
0
    _stored_has_null_tag.resize(_stored_data_columns.size());
531
0
    _stored_has_variable_length_tag.resize(_stored_data_columns.size());
532
533
0
    auto& tablet_schema = *_tablet_schema;
534
0
    for (auto idx : _agg_columns_idx) {
535
0
        auto column = tablet_schema.column(
536
0
                read_params.origin_return_columns->at(_return_columns_loc[idx]));
537
0
        AggregateFunctionPtr function =
538
0
                column.get_aggregate_function(AGG_READER_SUFFIX, read_params.get_be_exec_version());
539
540
        // to avoid coredump when something goes wrong(i.e. column missmatch)
541
0
        if (!function) {
542
0
            return Status::InternalError(
543
0
                    "Failed to init reader when init agg state: "
544
0
                    "tablet_id: {}, schema_hash: {}, reader_type: {}, version: {}",
545
0
                    read_params.tablet->tablet_id(), read_params.tablet->schema_hash(),
546
0
                    int(read_params.reader_type), read_params.version.to_string());
547
0
        }
548
0
        const auto* column_ptr = _stored_data_columns[idx].get();
549
0
        const IColumn* columns[] = {column_ptr};
550
0
        function->check_input_columns_type(columns);
551
0
        function->check_result_column_type(*column_ptr);
552
0
        _agg_functions.push_back(function);
553
        // create aggregate data
554
0
        AggregateDataPtr place = new char[function->size_of_data()];
555
0
        SAFE_CREATE(function->create(place), {
556
0
            _agg_functions.pop_back();
557
0
            delete[] place;
558
0
        });
559
0
        _agg_places.push_back(place);
560
561
        // calculate `_has_variable_length_tag` tag. like string, array, map
562
0
        _stored_has_variable_length_tag[idx] = _stored_data_columns[idx]->is_variable_length();
563
0
    }
564
565
0
    return Status::OK();
566
0
}
567
568
48
Status BlockReader::init(const ReaderParams& read_params) {
569
48
    SCOPED_RAW_TIMER(&_stats.tablet_reader_init_timer_ns);
570
48
    RETURN_IF_ERROR(TabletReader::init(read_params));
571
48
    _filter_row_ttl = should_gc_row_ttl(*_tablet_schema,
572
48
                                        _tablet->enable_unique_key_merge_on_write(),
573
48
                                        read_params.reader_type, read_params.version);
574
48
    if (_filter_row_ttl) {
575
0
        _row_ttl_now_us =
576
0
                read_params.row_ttl_gc_now_us > 0 ? read_params.row_ttl_gc_now_us : UnixMicros();
577
0
    }
578
579
48
    auto return_column_size = read_params.origin_return_columns->size();
580
48
    _return_columns_loc.resize(read_params.return_columns.size(), -1);
581
48
    std::unordered_map<int32_t /*cid*/, int32_t /*pos*/> pos_map;
582
176
    for (int i = 0; i < return_column_size; ++i) {
583
128
        auto cid = read_params.origin_return_columns->at(i);
584
        // For each original cid, find the index in return_columns
585
240
        for (int j = 0; j < read_params.return_columns.size(); ++j) {
586
240
            if (read_params.return_columns[j] == cid) {
587
128
                if (j < _tablet->num_key_columns() || _tablet->keys_type() != AGG_KEYS) {
588
128
                    pos_map[cid] = (int32_t)_normal_columns_idx.size();
589
128
                    _normal_columns_idx.emplace_back(j);
590
128
                } else {
591
0
                    _agg_columns_idx.emplace_back(j);
592
0
                }
593
128
                _return_columns_loc[j] = i;
594
128
                break;
595
128
            }
596
240
        }
597
128
    }
598
599
48
    if (_filter_row_ttl) {
600
0
        const int32_t ttl_cid = _tablet_schema->ttl_col_idx();
601
0
        DORIS_CHECK(ttl_cid >= 0);
602
0
        auto ttl_it = std::find(read_params.return_columns.begin(),
603
0
                                read_params.return_columns.end(), ttl_cid);
604
0
        if (ttl_it == read_params.return_columns.end()) {
605
0
            return Status::InternalError("row ttl column is missing from reader columns");
606
0
        }
607
0
        const size_t ttl_read_pos = std::distance(read_params.return_columns.begin(), ttl_it);
608
0
        if (_return_columns_loc[ttl_read_pos] == -1) {
609
0
            _row_ttl_output_pos = return_column_size;
610
0
            _remove_row_ttl_output = true;
611
0
            _return_columns_loc[ttl_read_pos] = static_cast<int>(_row_ttl_output_pos);
612
0
            _normal_columns_idx.emplace_back(ttl_read_pos);
613
0
            pos_map[ttl_cid] = static_cast<int32_t>(_normal_columns_idx.size() - 1);
614
0
        }
615
0
    }
616
617
48
    if (_tablet_schema->has_seq_map()) {
618
0
        if (_tablet_schema->has_sequence_col()) {
619
0
            auto msg = "sequence columns conflict, both seq_col and seq_map are true!";
620
0
            LOG(WARNING) << msg;
621
0
            return Status::InternalError(msg);
622
0
        }
623
0
        _has_seq_map = true;
624
0
        for (auto seq_val_iter = _tablet_schema->seq_col_idx_to_value_cols_idx().cbegin();
625
0
             seq_val_iter != _tablet_schema->seq_col_idx_to_value_cols_idx().cend();
626
0
             ++seq_val_iter) {
627
0
            int seq_loc = -1;
628
0
            for (int i = 0; i < read_params.return_columns.size(); ++i) {
629
0
                if (read_params.return_columns[i] == seq_val_iter->first) {
630
0
                    seq_loc = i;
631
0
                    break;
632
0
                }
633
0
            }
634
0
            if (seq_loc == -1) {
635
                // don't need to deal with this seq col
636
0
                continue;
637
0
            }
638
639
0
            std::vector<uint32_t> pos_vec;
640
0
            for (auto agg_cid : seq_val_iter->second) {
641
0
                const auto& val_pos_iter = pos_map.find(agg_cid);
642
0
                if (val_pos_iter == pos_map.end()) {
643
0
                    continue;
644
0
                }
645
0
                pos_vec.emplace_back(val_pos_iter->second);
646
0
            }
647
0
            if (_return_columns_loc[seq_loc] == -1) {
648
0
                _seq_map_not_in_origin_block.emplace(seq_loc, pos_vec);
649
0
            } else {
650
0
                _seq_map_in_origin_block.emplace(seq_loc, pos_vec);
651
0
            }
652
0
        }
653
0
    }
654
655
48
    auto status = _init_collect_iter(read_params);
656
48
    if (!status.ok()) [[unlikely]] {
657
0
        if (!config::is_cloud_mode()) {
658
0
            static_cast<Tablet*>(_tablet.get())->report_error(status);
659
0
        }
660
0
        return status;
661
0
    }
662
663
    // MIN_DELTA: collapse consecutive same-key changes to the minimum equivalent change set
664
    // (e.g. INSERT+DELETE -> SKIP, INSERT+UPDATE -> INSERT). Reduces downstream traffic.
665
48
    if (read_params.binlog_scan_type == TBinlogScanType::MIN_DELTA) {
666
0
        _next_block_func = &BlockReader::_min_delta_next_block;
667
0
        return Status::OK();
668
0
    }
669
    // DETAIL: emit every recorded change as-is, with BEFORE+AFTER rows for UPDATE.
670
    // Used when the consumer needs full change history rather than the net delta.
671
48
    if (read_params.binlog_scan_type == TBinlogScanType::DETAIL) {
672
0
        _next_block_func = &BlockReader::_detail_change_next_block;
673
0
        return Status::OK();
674
0
    }
675
676
48
    if (_direct_mode) {
677
0
        _next_block_func = &BlockReader::_direct_next_block;
678
0
        return Status::OK();
679
0
    }
680
48
    if (_has_seq_map && !_eof) {
681
0
        for (auto it = _seq_map_not_in_origin_block.cbegin();
682
0
             it != _seq_map_not_in_origin_block.cend(); ++it) {
683
0
            auto seq_idx = it->first;
684
0
            _seq_columns.insert(
685
0
                    {seq_idx, _next_row.block->get_by_position(seq_idx).column->clone_empty()});
686
0
        }
687
0
    }
688
689
48
    switch (_tablet_schema->keys_type()) {
690
16
    case KeysType::DUP_KEYS:
691
16
        _next_block_func = &BlockReader::_direct_next_block;
692
16
        break;
693
32
    case KeysType::UNIQUE_KEYS:
694
32
        if (read_params.reader_type == ReaderType::READER_QUERY &&
695
32
            _reader_context.enable_unique_key_merge_on_write) {
696
0
            _next_block_func = &BlockReader::_direct_next_block;
697
32
        } else if (_has_seq_map) {
698
0
            _next_block_func = &BlockReader::_replace_key_next_block;
699
32
        } else {
700
32
            _next_block_func = &BlockReader::_unique_key_next_block;
701
32
        }
702
32
        break;
703
0
    case KeysType::AGG_KEYS:
704
0
        _next_block_func = &BlockReader::_agg_key_next_block;
705
0
        RETURN_IF_ERROR(_init_agg_state(read_params));
706
0
        break;
707
0
    default:
708
0
        DCHECK(false) << "No next row function for type:" << _tablet_schema->keys_type();
709
0
        break;
710
48
    }
711
48
    _filter_delete_sign = _next_block_func == &BlockReader::_unique_key_next_block &&
712
48
                          _delete_sign_available;
713
714
48
    return Status::OK();
715
48
}
716
717
296
Status BlockReader::_direct_next_block(Block* block, bool* eof) {
718
296
    auto res = _vcollect_iter.next(block);
719
296
    if (UNLIKELY(!res.ok() && !res.is<END_OF_FILE>())) {
720
1
        return res;
721
1
    }
722
295
    *eof = res.is<END_OF_FILE>();
723
295
    _eof = *eof;
724
295
    if (UNLIKELY(_reader_context.record_rowids)) {
725
295
        res = _vcollect_iter.current_block_row_locations(&_block_row_locations);
726
295
        if (UNLIKELY(!res.ok() && res != Status::Error<END_OF_FILE>(""))) {
727
0
            return res;
728
0
        }
729
295
        DCHECK_EQ(_block_row_locations.size(), block->rows());
730
295
    }
731
295
    return Status::OK();
732
295
}
733
734
0
Status BlockReader::_direct_agg_key_next_block(Block* block, bool* eof) {
735
0
    return Status::OK();
736
0
}
737
738
0
Status BlockReader::_replace_key_next_block(Block* block, bool* eof) {
739
0
    if (UNLIKELY(_eof)) {
740
0
        *eof = true;
741
0
        return Status::OK();
742
0
    }
743
744
0
    auto target_block_row = 0;
745
0
    auto target_columns_guard = block->mutate_columns_scoped();
746
0
    auto& target_columns = target_columns_guard.mutable_columns();
747
    // currently seq mapping only support mor table
748
    // so this will not be executed for the time being
749
0
    if (UNLIKELY(_reader_context.record_rowids)) {
750
0
        _block_row_locations.resize(batch_max_rows());
751
0
    }
752
0
    auto merged_row = 0;
753
0
    while (target_block_row < batch_max_rows() && !_eof) {
754
0
        RETURN_IF_ERROR(_insert_data_normal(target_columns));
755
        // use the first line to init _seq_columns
756
0
        for (auto it = _seq_map_not_in_origin_block.cbegin();
757
0
             it != _seq_map_not_in_origin_block.cend(); ++it) {
758
0
            auto seq_idx = it->first;
759
0
            _update_last_mutil_seq(seq_idx);
760
0
        }
761
0
        if (UNLIKELY(_reader_context.record_rowids)) {
762
0
            _block_row_locations[target_block_row] = _vcollect_iter.current_row_location();
763
0
        }
764
0
        target_block_row++;
765
766
0
        while (!_eof) {
767
            // the version is in reverse order, the first row is the highest version,
768
            // in UNIQUE_KEY highest version is the final result
769
0
            auto res = _vcollect_iter.next(&_next_row);
770
0
            if (UNLIKELY(res.is<END_OF_FILE>())) {
771
0
                _eof = true;
772
0
                *eof = true;
773
0
                if (UNLIKELY(_reader_context.record_rowids)) {
774
0
                    _block_row_locations.resize(target_block_row);
775
0
                }
776
0
                break;
777
0
            }
778
779
0
            if (UNLIKELY(!res.ok())) {
780
0
                LOG(WARNING) << "next failed: " << res;
781
0
                return res;
782
0
            }
783
784
0
            if (_next_row.is_same) {
785
0
                merged_row++;
786
0
                _compare_sequence_map_and_replace(target_columns);
787
0
            } else {
788
0
                break;
789
0
            }
790
0
        }
791
        // Byte-budget check: after the inner loop _next_row is either EOF or the next different
792
        // key, so it is safe to stop accumulating here without repeating any row.
793
0
        if (target_block_row % BLOCK_SIZE_CHECK_INTERVAL_ROWS == 0 &&
794
0
            _reached_byte_budget(target_columns)) {
795
0
            if (UNLIKELY(_reader_context.record_rowids)) {
796
0
                _block_row_locations.resize(target_block_row);
797
0
            }
798
0
            break;
799
0
        }
800
0
    }
801
0
    _merged_rows += merged_row;
802
0
    return Status::OK();
803
0
}
804
805
4.36k
bool BlockReader::_reached_byte_budget(const MutableColumns& columns) const {
806
4.36k
    return config::enable_adaptive_batch_size && _reader_context.preferred_block_size_bytes > 0 &&
807
4.36k
           Block::columns_byte_size(columns) >= _reader_context.preferred_block_size_bytes;
808
4.36k
}
809
810
0
void BlockReader::_compare_sequence_map_and_replace(MutableColumns& columns) {
811
0
    auto src_block = _next_row.block.get();
812
0
    auto src_pos = _next_row.row_pos;
813
814
    // use seq column in origin block to compare and replace
815
0
    for (auto it = _seq_map_in_origin_block.cbegin(); it != _seq_map_in_origin_block.cend(); ++it) {
816
0
        auto seq_idx = it->first;
817
0
        auto dst_seq_column = columns[_return_columns_loc[seq_idx]].get();
818
0
        auto dst_pos = dst_seq_column->size() - 1;
819
0
        auto src_seq_column = src_block->get_by_position(seq_idx).column;
820
        // the rowset version of dst is higher .
821
0
        auto res = dst_seq_column->compare_at(dst_pos, src_pos, *src_seq_column, -1);
822
0
        if (res >= 0) {
823
0
            continue;
824
0
        }
825
826
        // update value and seq column
827
0
        for (auto& p : it->second) {
828
0
            auto val_idx = _normal_columns_idx[p];
829
0
            auto src_column = src_block->get_by_position(val_idx).column;
830
0
            auto dst_column = columns[_return_columns_loc[val_idx]].get();
831
0
            dst_column->pop_back(1);
832
0
            dst_column->insert_from(*src_column, src_pos);
833
0
        }
834
835
0
        dst_seq_column->pop_back(1);
836
0
        dst_seq_column->insert_from(*src_seq_column, src_pos);
837
0
    }
838
839
    // use temp seq block to compare and replace because origin block not contains these seq columns
840
0
    for (auto it = _seq_map_not_in_origin_block.cbegin(); it != _seq_map_not_in_origin_block.cend();
841
0
         ++it) {
842
0
        auto seq_idx = it->first;
843
0
        auto dst_seq_column = _seq_columns[seq_idx].get();
844
0
        auto src_seq_column = src_block->get_by_position(seq_idx).column;
845
        // the rowset version of dst is higher .
846
0
        auto res = dst_seq_column->compare_at(0, src_pos, *src_seq_column, -1);
847
0
        if (res >= 0) {
848
0
            continue;
849
0
        }
850
851
        // update value and seq column (if need to return)
852
0
        for (auto& p : it->second) {
853
0
            auto val_idx = _normal_columns_idx[p];
854
0
            auto src_column = src_block->get_by_position(val_idx).column;
855
0
            auto dst_column = columns[_return_columns_loc[val_idx]].get();
856
0
            dst_column->pop_back(1);
857
0
            dst_column->insert_from(*src_column, src_pos);
858
0
        }
859
860
0
        _update_last_mutil_seq(seq_idx);
861
0
    }
862
0
}
863
864
0
void BlockReader::_update_last_mutil_seq(int seq_idx) {
865
0
    auto block = _next_row.block.get();
866
0
    _seq_columns[seq_idx]->clear();
867
0
    _seq_columns[seq_idx]->insert_from(*block->get_by_position(seq_idx).column, _next_row.row_pos);
868
0
}
869
870
0
Status BlockReader::_agg_key_next_block(Block* block, bool* eof) {
871
0
    if (UNLIKELY(_eof)) {
872
0
        *eof = true;
873
0
        return Status::OK();
874
0
    }
875
876
0
    auto target_block_row = 0;
877
0
    auto merged_row = 0;
878
0
    auto target_columns_guard = block->mutate_columns_scoped();
879
0
    auto& target_columns = target_columns_guard.mutable_columns();
880
0
    RETURN_IF_ERROR(_insert_data_normal(target_columns));
881
0
    target_block_row++;
882
0
    _append_agg_data(target_columns);
883
884
0
    while (true) {
885
0
        auto res = _vcollect_iter.next(&_next_row);
886
0
        if (UNLIKELY(res.is<END_OF_FILE>())) {
887
0
            _eof = true;
888
0
            *eof = true;
889
0
            break;
890
0
        }
891
0
        if (UNLIKELY(!res.ok())) {
892
0
            LOG(WARNING) << "next failed: " << res;
893
0
            return res;
894
0
        }
895
896
0
        if (!_next_row.is_same) {
897
0
            if (target_block_row == batch_max_rows()) {
898
0
                break;
899
0
            }
900
            // Byte-budget check at group boundary: _next_row is the first row of the new group
901
            // and is still pending (not yet inserted), so stopping here is safe.
902
0
            if (target_block_row % BLOCK_SIZE_CHECK_INTERVAL_ROWS == 0 &&
903
0
                _reached_byte_budget(target_columns)) {
904
0
                break;
905
0
            }
906
907
0
            _agg_data_counters.push_back(_last_agg_data_counter);
908
0
            _last_agg_data_counter = 0;
909
910
0
            RETURN_IF_ERROR(_insert_data_normal(target_columns));
911
912
0
            target_block_row++;
913
0
        } else {
914
0
            merged_row++;
915
0
        }
916
917
0
        _append_agg_data(target_columns);
918
0
    }
919
920
0
    _agg_data_counters.push_back(_last_agg_data_counter);
921
0
    _last_agg_data_counter = 0;
922
0
    _update_agg_data(target_columns);
923
924
0
    _merged_rows += merged_row;
925
0
    return Status::OK();
926
0
}
927
928
284
Status BlockReader::_unique_key_next_block(Block* block, bool* eof) {
929
284
    if (UNLIKELY(_eof)) {
930
0
        *eof = true;
931
0
        return Status::OK();
932
0
    }
933
934
284
    auto target_block_row = 0;
935
284
    auto target_columns_guard = block->mutate_columns_scoped();
936
284
    auto& target_columns = target_columns_guard.mutable_columns();
937
284
    if (UNLIKELY(_reader_context.record_rowids)) {
938
284
        _block_row_locations.resize(batch_max_rows());
939
284
    }
940
941
280k
    do {
942
280k
        RETURN_IF_ERROR(_insert_data_normal(target_columns));
943
944
280k
        if (UNLIKELY(_reader_context.record_rowids)) {
945
280k
            _block_row_locations[target_block_row] = _vcollect_iter.current_row_location();
946
280k
        }
947
280k
        target_block_row++;
948
949
        // the version is in reverse order, the first row is the highest version,
950
        // in UNIQUE_KEY highest version is the final result, there is no need to
951
        // merge the lower versions
952
280k
        auto res = _vcollect_iter.next(&_next_row);
953
280k
        if (UNLIKELY(res.is<END_OF_FILE>())) {
954
32
            _eof = true;
955
32
            *eof = true;
956
32
            if (UNLIKELY(_reader_context.record_rowids)) {
957
32
                _block_row_locations.resize(target_block_row);
958
32
            }
959
32
            break;
960
32
        }
961
962
280k
        if (UNLIKELY(!res.ok())) {
963
0
            LOG(WARNING) << "next failed: " << res;
964
0
            return res;
965
0
        }
966
        // Byte-budget check: _next_row is already saved so stopping here is safe.
967
280k
        if (target_block_row % BLOCK_SIZE_CHECK_INTERVAL_ROWS == 0 &&
968
280k
            _reached_byte_budget(target_columns)) {
969
0
            if (UNLIKELY(_reader_context.record_rowids)) {
970
0
                _block_row_locations.resize(target_block_row);
971
0
            }
972
0
            break;
973
0
        }
974
280k
    } while (target_block_row < batch_max_rows());
975
976
284
    return Status::OK();
977
284
}
978
979
280k
Status BlockReader::_insert_data_normal(MutableColumns& columns) {
980
280k
    auto block = _next_row.block.get();
981
982
280k
    RETURN_IF_CATCH_EXCEPTION({
983
280k
        for (auto idx : _normal_columns_idx) {
984
280k
            columns[_return_columns_loc[idx]]->insert_from(*block->get_by_position(idx).column,
985
280k
                                                           _next_row.row_pos);
986
280k
        }
987
280k
    });
988
280k
    return Status::OK();
989
280k
}
990
991
115
void BlockReader::_append_agg_data(MutableColumns& columns) {
992
115
    _stored_row_ref.push_back(_next_row);
993
115
    _last_agg_data_counter++;
994
995
    // execute aggregate when accumulated `batch_max_rows()` rows or some ref invalid soon
996
    // `_stored_data_columns` is sized to `batch_max_rows()`,
997
    // this flush keeps the number of rows in `_stored_row_ref` within `batch_max_rows()`.
998
115
    bool is_last = (_next_row.block->rows() == _next_row.row_pos + 1);
999
115
    if (is_last || _stored_row_ref.size() == batch_max_rows()) {
1000
29
        _update_agg_data(columns);
1001
29
    }
1002
115
}
1003
1004
32
void BlockReader::_update_agg_data(MutableColumns& columns) {
1005
    // copy data to stored block
1006
32
    size_t copy_size = _copy_agg_data();
1007
1008
    // calculate has_null_tag
1009
32
    for (auto idx : _agg_columns_idx) {
1010
32
        _stored_has_null_tag[idx] = _stored_data_columns[idx]->has_null(0, copy_size);
1011
32
    }
1012
1013
    // calculate aggregate and insert
1014
32
    int counter_sum = 0;
1015
32
    for (int counter : _agg_data_counters) {
1016
3
        _update_agg_value(columns, counter_sum, counter_sum + counter - 1);
1017
3
        counter_sum += counter;
1018
3
    }
1019
1020
    // some key still has value at next block, so do not insert
1021
32
    if (_last_agg_data_counter) {
1022
29
        _update_agg_value(columns, counter_sum, counter_sum + _last_agg_data_counter - 1, false);
1023
29
        _last_agg_data_counter = 0;
1024
29
    }
1025
1026
32
    _agg_data_counters.clear();
1027
32
}
1028
1029
32
size_t BlockReader::_copy_agg_data() {
1030
32
    size_t copy_size = _stored_row_ref.size();
1031
1032
147
    for (size_t i = 0; i < copy_size; i++) {
1033
115
        auto& ref = _stored_row_ref[i];
1034
115
        _temp_ref_map[ref.block.get()].emplace_back(ref.row_pos, i);
1035
115
    }
1036
1037
32
    for (auto idx : _agg_columns_idx) {
1038
32
        auto& dst_column = _stored_data_columns[idx];
1039
32
        if (_stored_has_variable_length_tag[idx]) {
1040
            //variable length type should replace ordered
1041
0
            dst_column->clear();
1042
0
            for (size_t i = 0; i < copy_size; i++) {
1043
0
                auto& ref = _stored_row_ref[i];
1044
0
                dst_column->insert_from(*ref.block->get_by_position(idx).column, ref.row_pos);
1045
0
            }
1046
32
        } else {
1047
32
            for (auto& it : _temp_ref_map) {
1048
32
                if (!it.second.empty()) {
1049
29
                    auto& src_column = *it.first->get_by_position(idx).column;
1050
115
                    for (auto& pos : it.second) {
1051
115
                        dst_column->replace_column_data(src_column, pos.first, pos.second);
1052
115
                    }
1053
29
                }
1054
32
            }
1055
32
        }
1056
32
    }
1057
1058
32
    for (auto& it : _temp_ref_map) {
1059
32
        it.second.clear();
1060
32
    }
1061
32
    _stored_row_ref.clear();
1062
1063
32
    return copy_size;
1064
32
}
1065
1066
32
void BlockReader::_update_agg_value(MutableColumns& columns, int begin, int end, bool is_close) {
1067
64
    for (int i = 0; i < _agg_columns_idx.size(); i++) {
1068
32
        auto idx = _agg_columns_idx[i];
1069
1070
32
        AggregateFunctionPtr function = _agg_functions[i];
1071
32
        AggregateDataPtr place = _agg_places[i];
1072
32
        auto* column_ptr = _stored_data_columns[idx].get();
1073
1074
32
        if (begin <= end) {
1075
29
            function->add_batch_range(begin, end, place, const_cast<const IColumn**>(&column_ptr),
1076
29
                                      _arena, _stored_has_null_tag[idx]);
1077
29
        }
1078
1079
32
        if (is_close) {
1080
3
            function->insert_result_into(place, *columns[_return_columns_loc[idx]]);
1081
            // reset aggregate data
1082
3
            function->reset(place);
1083
3
        }
1084
32
    }
1085
32
    if (is_close) {
1086
3
        _arena.clear();
1087
3
    }
1088
32
}
1089
1090
} // namespace doris