Coverage Report

Created: 2026-07-09 01:02

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