Coverage Report

Created: 2026-05-20 02:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/storage/segment/segment_iterator.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/segment/segment_iterator.h"
19
20
#include <assert.h>
21
#include <gen_cpp/Exprs_types.h>
22
#include <gen_cpp/Opcodes_types.h>
23
#include <gen_cpp/Types_types.h>
24
#include <gen_cpp/olap_file.pb.h>
25
26
#include <algorithm>
27
#include <boost/iterator/iterator_facade.hpp>
28
#include <cstdint>
29
#include <memory>
30
#include <numeric>
31
#include <set>
32
#include <unordered_map>
33
#include <utility>
34
#include <vector>
35
36
#include "cloud/config.h"
37
#include "common/compiler_util.h" // IWYU pragma: keep
38
#include "common/config.h"
39
#include "common/consts.h"
40
#include "common/exception.h"
41
#include "common/logging.h"
42
#include "common/metrics/doris_metrics.h"
43
#include "common/object_pool.h"
44
#include "common/status.h"
45
#include "core/assert_cast.h"
46
#include "core/block/column_with_type_and_name.h"
47
#include "core/column/column.h"
48
#include "core/column/column_const.h"
49
#include "core/column/column_nothing.h"
50
#include "core/column/column_nullable.h"
51
#include "core/column/column_string.h"
52
#include "core/column/column_variant.h"
53
#include "core/column/column_vector.h"
54
#include "core/column/predicate_column.h"
55
#include "core/data_type/data_type.h"
56
#include "core/data_type/data_type_factory.hpp"
57
#include "core/data_type/data_type_number.h"
58
#include "core/data_type/define_primitive_type.h"
59
#include "core/field.h"
60
#include "core/string_ref.h"
61
#include "core/typeid_cast.h"
62
#include "core/types.h"
63
#include "exprs/function/array/function_array_index.h"
64
#include "exprs/vexpr.h"
65
#include "exprs/vexpr_context.h"
66
#include "exprs/virtual_slot_ref.h"
67
#include "exprs/vliteral.h"
68
#include "exprs/vslot_ref.h"
69
#include "io/cache/cached_remote_file_reader.h"
70
#include "io/fs/file_reader.h"
71
#include "io/io_common.h"
72
#include "runtime/descriptors.h"
73
#include "runtime/query_context.h"
74
#include "runtime/runtime_predicate.h"
75
#include "runtime/runtime_state.h"
76
#include "runtime/thread_context.h"
77
#include "storage/binlog.h"
78
#include "storage/compaction/collection_similarity.h"
79
#include "storage/field.h"
80
#include "storage/id_manager.h"
81
#include "storage/index/ann/ann_index.h"
82
#include "storage/index/ann/ann_index_iterator.h"
83
#include "storage/index/ann/ann_index_reader.h"
84
#include "storage/index/ann/ann_topn_runtime.h"
85
#include "storage/index/index_file_reader.h"
86
#include "storage/index/index_iterator.h"
87
#include "storage/index/index_query_context.h"
88
#include "storage/index/index_reader_helper.h"
89
#include "storage/index/indexed_column_reader.h"
90
#include "storage/index/inverted/inverted_index_reader.h"
91
#include "storage/index/ordinal_page_index.h"
92
#include "storage/index/primary_key_index.h"
93
#include "storage/index/short_key_index.h"
94
#include "storage/iterators.h"
95
#include "storage/olap_common.h"
96
#include "storage/predicate/bloom_filter_predicate.h"
97
#include "storage/predicate/column_predicate.h"
98
#include "storage/predicate/like_column_predicate.h"
99
#include "storage/schema.h"
100
#include "storage/segment/column_reader.h"
101
#include "storage/segment/column_reader_cache.h"
102
#include "storage/segment/condition_cache.h"
103
#include "storage/segment/row_ranges.h"
104
#include "storage/segment/segment.h"
105
#include "storage/segment/segment_prefetcher.h"
106
#include "storage/segment/variant/variant_column_reader.h"
107
#include "storage/segment/virtual_column_iterator.h"
108
#include "storage/tablet/tablet_schema.h"
109
#include "storage/types.h"
110
#include "storage/utils.h"
111
#include "util/concurrency_stats.h"
112
#include "util/defer_op.h"
113
#include "util/json/path_in_data.h"
114
#include "util/simd/bits.h"
115
116
namespace doris {
117
using namespace ErrorCode;
118
namespace segment_v2 {
119
namespace {
120
121
Status tablet_column_id_by_slot(const TabletSchemaSPtr& tablet_schema, const SlotDescriptor* slot,
122
0
                                ColumnId* cid) {
123
0
    int32_t field_index = -1;
124
0
    if (slot->type()->get_primitive_type() == PrimitiveType::TYPE_VARIANT) {
125
0
        field_index = tablet_schema->field_index(
126
0
                PathInData(tablet_schema->column_by_uid(slot->col_unique_id()).name_lower_case(),
127
0
                           slot->column_paths()));
128
0
    } else {
129
0
        field_index = slot->col_unique_id() >= 0 ? tablet_schema->field_index(slot->col_unique_id())
130
0
                                                 : tablet_schema->field_index(slot->col_name());
131
0
    }
132
0
    if (field_index < 0) {
133
0
        return Status::InternalError(
134
0
                "field name is invalid. field={}, field_name_to_index={}, col_unique_id={}",
135
0
                slot->col_name(), tablet_schema->get_all_field_names(), slot->col_unique_id());
136
0
    }
137
0
    *cid = field_index;
138
0
    return Status::OK();
139
0
}
140
141
Status rebind_storage_expr_to_reader_schema(
142
        const StorageReadOptions& opts, const VExprSPtr& expr,
143
0
        const std::unordered_map<ColumnId, size_t>& cid_to_pos) {
144
0
    DORIS_CHECK(expr != nullptr);
145
146
0
    if (expr->is_slot_ref()) {
147
0
        auto slot_ref = std::static_pointer_cast<VSlotRef>(expr);
148
0
        auto* slot = opts.runtime_state->desc_tbl().get_slot_descriptor(slot_ref->slot_id());
149
0
        if (slot == nullptr) {
150
0
            return Status::InternalError("slot {} is not found in descriptor table",
151
0
                                         slot_ref->slot_id());
152
0
        }
153
154
0
        ColumnId cid = 0;
155
0
        RETURN_IF_ERROR(tablet_column_id_by_slot(opts.tablet_schema, slot, &cid));
156
0
        auto pos_it = cid_to_pos.find(cid);
157
0
        if (pos_it == cid_to_pos.end()) {
158
0
            return Status::InternalError("slot {} column {} with cid {} is not in reader schema",
159
0
                                         slot_ref->slot_id(), slot->col_name(), cid);
160
0
        }
161
0
        slot_ref->set_column_id(cast_set<int>(pos_it->second));
162
0
    } else if (expr->is_virtual_slot_ref()) {
163
0
        auto virtual_slot_ref = std::static_pointer_cast<VirtualSlotRef>(expr);
164
0
        auto* slot =
165
0
                opts.runtime_state->desc_tbl().get_slot_descriptor(virtual_slot_ref->slot_id());
166
0
        if (slot == nullptr) {
167
0
            return Status::InternalError("slot {} is not found in descriptor table",
168
0
                                         virtual_slot_ref->slot_id());
169
0
        }
170
171
0
        ColumnId cid = 0;
172
0
        RETURN_IF_ERROR(tablet_column_id_by_slot(opts.tablet_schema, slot, &cid));
173
0
        auto pos_it = cid_to_pos.find(cid);
174
0
        if (pos_it == cid_to_pos.end()) {
175
0
            return Status::InternalError(
176
0
                    "virtual slot {} column {} with cid {} is not in reader schema",
177
0
                    virtual_slot_ref->slot_id(), slot->col_name(), cid);
178
0
        }
179
0
        virtual_slot_ref->set_column_id(cast_set<int>(pos_it->second));
180
        // A virtual slot has its own output position in the reader block, and its
181
        // materialization expression may also contain real slot refs. Rebind both
182
        // sides so evaluating the virtual expression reads from the same block
183
        // layout used by SegmentIterator.
184
0
        RETURN_IF_ERROR(rebind_storage_expr_to_reader_schema(
185
0
                opts, virtual_slot_ref->get_virtual_column_expr(), cid_to_pos));
186
0
    }
187
188
0
    for (const auto& child : expr->children()) {
189
0
        RETURN_IF_ERROR(rebind_storage_expr_to_reader_schema(opts, child, cid_to_pos));
190
0
    }
191
0
    return Status::OK();
192
0
}
193
194
Status rebind_storage_exprs_to_reader_schema(const StorageReadOptions& opts, const Schema& schema,
195
                                             const VExprContextSPtrs& common_exprs,
196
2.82k
                                             std::map<ColumnId, VExprContextSPtr>& virtual_exprs) {
197
2.82k
    if (common_exprs.empty() && virtual_exprs.empty()) {
198
2.82k
        return Status::OK();
199
2.82k
    }
200
0
    DORIS_CHECK(opts.runtime_state != nullptr);
201
0
    DORIS_CHECK(opts.tablet_schema != nullptr);
202
203
0
    const auto keys_type = opts.tablet_schema->keys_type();
204
0
    if (keys_type == KeysType::DUP_KEYS ||
205
0
        (keys_type == KeysType::UNIQUE_KEYS && opts.enable_unique_key_merge_on_write)) {
206
0
        return Status::OK();
207
0
    }
208
209
    // Storage exprs are prepared with RowDescriptor, so VSlotRef/VirtualSlotRef column_id points to
210
    // the scan tuple column ordinal. SegmentIterator evaluates cloned exprs on a block built from
211
    // the reader schema instead. AGG_KEYS and non-MOW UNIQUE_KEYS readers may expand the reader
212
    // schema, for example by filling all key columns before merging/aggregating rows, so the scan
213
    // tuple ordinal is not always the same as the runtime block ordinal.
214
    //
215
    // DUP_KEYS and UNIQUE_KEYS MOW use direct readers for query scans, so their reader block keeps
216
    // the scan tuple layout and can skip this per-segment expression-tree traversal. For merge/agg
217
    // readers, the reader schema is the source of truth: map tablet column id to reader-block
218
    // position and rebind every storage expr slot to that position.
219
0
    std::unordered_map<ColumnId, size_t> cid_to_pos;
220
0
    for (size_t pos = 0; pos < schema.num_column_ids(); ++pos) {
221
0
        cid_to_pos.emplace(schema.column_id(cast_set<int>(pos)), pos);
222
0
    }
223
224
0
    for (const auto& ctx : common_exprs) {
225
0
        RETURN_IF_ERROR(rebind_storage_expr_to_reader_schema(opts, ctx->root(), cid_to_pos));
226
0
    }
227
0
    for (const auto& [_, ctx] : virtual_exprs) {
228
0
        RETURN_IF_ERROR(rebind_storage_expr_to_reader_schema(opts, ctx->root(), cid_to_pos));
229
0
    }
230
0
    return Status::OK();
231
0
}
232
233
} // namespace
234
235
2.83k
SegmentIterator::~SegmentIterator() = default;
236
237
2.82k
void SegmentIterator::_init_row_bitmap_by_condition_cache() {
238
    // Only dispose need column predicate and expr cal in condition cache
239
2.82k
    if (!_col_predicates.empty() ||
240
2.82k
        (_enable_common_expr_pushdown && !_remaining_conjunct_roots.empty())) {
241
0
        if (_opts.condition_cache_digest) {
242
0
            auto* condition_cache = ConditionCache::instance();
243
0
            ConditionCache::CacheKey cache_key(_opts.rowset_id, _segment->id(),
244
0
                                               _opts.condition_cache_digest);
245
246
            // Increment search count when digest != 0
247
0
            DorisMetrics::instance()->condition_cache_search_count->increment(1);
248
249
0
            ConditionCacheHandle handle;
250
0
            _find_condition_cache = condition_cache->lookup(cache_key, &handle);
251
252
            // Increment hit count if cache lookup is successful
253
0
            if (_find_condition_cache) {
254
0
                DorisMetrics::instance()->condition_cache_hit_count->increment(1);
255
0
                if (_opts.runtime_state) {
256
0
                    VLOG_DEBUG << "Condition cache hit, query id: "
257
0
                               << print_id(_opts.runtime_state->query_id())
258
0
                               << ", segment id: " << _segment->id()
259
0
                               << ", cache digest: " << _opts.condition_cache_digest
260
0
                               << ", rowset id: " << _opts.rowset_id.to_string();
261
0
                }
262
0
            }
263
264
0
            auto num_rows = _segment->num_rows();
265
0
            if (_find_condition_cache) {
266
0
                const auto& filter_result = *(handle.get_filter_result());
267
0
                int64_t filtered_blocks = 0;
268
0
                for (int i = 0; i < filter_result.size(); i++) {
269
0
                    if (!filter_result[i]) {
270
0
                        _row_bitmap.removeRange(
271
0
                                i * CONDITION_CACHE_OFFSET,
272
0
                                i * CONDITION_CACHE_OFFSET + CONDITION_CACHE_OFFSET);
273
0
                        filtered_blocks++;
274
0
                    }
275
0
                }
276
                // Record condition_cache hit segment number
277
0
                _opts.stats->condition_cache_hit_seg_nums++;
278
                // Record rows filtered by condition cache hit
279
0
                _opts.stats->condition_cache_filtered_rows +=
280
0
                        filtered_blocks * SegmentIterator::CONDITION_CACHE_OFFSET;
281
0
            } else {
282
0
                _condition_cache = std::make_shared<std::vector<bool>>(
283
0
                        num_rows / CONDITION_CACHE_OFFSET + 1, false);
284
0
            }
285
0
        }
286
2.82k
    } else {
287
2.82k
        _opts.condition_cache_digest = 0;
288
2.82k
    }
289
2.82k
}
290
291
// A fast range iterator for roaring bitmap. Output ranges use closed-open form, like [from, to).
292
// Example:
293
//   input bitmap:  [0 1 4 5 6 7 10 15 16 17 18 19]
294
//   output ranges: [0,2), [4,8), [10,11), [15,20) (when max_range_size=10)
295
//   output ranges: [0,2), [4,7), [7,8), [10,11), [15,18), [18,20) (when max_range_size=3)
296
class SegmentIterator::BitmapRangeIterator {
297
public:
298
0
    BitmapRangeIterator() = default;
299
2.82k
    virtual ~BitmapRangeIterator() = default;
300
301
2.82k
    explicit BitmapRangeIterator(const roaring::Roaring& bitmap) {
302
2.82k
        roaring_init_iterator(&bitmap.roaring, &_iter);
303
2.82k
    }
304
305
0
    bool has_more_range() const { return !_eof; }
306
307
6.50k
    [[nodiscard]] static uint32_t get_batch_size() { return kBatchSize; }
308
309
    // read next range into [*from, *to) whose size <= max_range_size.
310
    // return false when there is no more range.
311
0
    virtual bool next_range(const uint32_t max_range_size, uint32_t* from, uint32_t* to) {
312
0
        if (_eof) {
313
0
            return false;
314
0
        }
315
316
0
        *from = _buf[_buf_pos];
317
0
        uint32_t range_size = 0;
318
0
        uint32_t expect_val = _buf[_buf_pos]; // this initial value just make first batch valid
319
320
        // if array is contiguous sequence then the following conditions need to be met :
321
        // a_0: x
322
        // a_1: x+1
323
        // a_2: x+2
324
        // ...
325
        // a_p: x+p
326
        // so we can just use (a_p-a_0)-p to check conditions
327
        // and should notice the previous batch needs to be continuous with the current batch
328
0
        while (!_eof && range_size + _buf_size - _buf_pos <= max_range_size &&
329
0
               expect_val == _buf[_buf_pos] &&
330
0
               _buf[_buf_size - 1] - _buf[_buf_pos] == _buf_size - 1 - _buf_pos) {
331
0
            range_size += _buf_size - _buf_pos;
332
0
            expect_val = _buf[_buf_size - 1] + 1;
333
0
            _read_next_batch();
334
0
        }
335
336
        // promise remain range not will reach next batch
337
0
        if (!_eof && range_size < max_range_size && expect_val == _buf[_buf_pos]) {
338
0
            do {
339
0
                _buf_pos++;
340
0
                range_size++;
341
0
            } while (range_size < max_range_size && _buf[_buf_pos] == _buf[_buf_pos - 1] + 1);
342
0
        }
343
0
        *to = *from + range_size;
344
0
        return true;
345
0
    }
346
347
    // read batch_size of rowids from roaring bitmap into buf array
348
12.9k
    virtual uint32_t read_batch_rowids(rowid_t* buf, uint32_t batch_size) {
349
12.9k
        return roaring::api::roaring_read_uint32_iterator(&_iter, buf, batch_size);
350
12.9k
    }
351
352
private:
353
0
    void _read_next_batch() {
354
0
        _buf_pos = 0;
355
0
        _buf_size = roaring::api::roaring_read_uint32_iterator(&_iter, _buf, kBatchSize);
356
0
        _eof = (_buf_size == 0);
357
0
    }
358
359
    static const uint32_t kBatchSize = 256;
360
    roaring::api::roaring_uint32_iterator_t _iter;
361
    uint32_t _buf[kBatchSize];
362
    uint32_t _buf_pos = 0;
363
    uint32_t _buf_size = 0;
364
    bool _eof = false;
365
};
366
367
// A backward range iterator for roaring bitmap. Output ranges use closed-open form, like [from, to).
368
// Example:
369
//   input bitmap:  [0 1 4 5 6 7 10 15 16 17 18 19]
370
//   output ranges: , [15,20), [10,11), [4,8), [0,2) (when max_range_size=10)
371
//   output ranges: [17,20), [15,17), [10,11), [5,8), [4, 5), [0,2) (when max_range_size=3)
372
class SegmentIterator::BackwardBitmapRangeIterator : public SegmentIterator::BitmapRangeIterator {
373
public:
374
0
    explicit BackwardBitmapRangeIterator(const roaring::Roaring& bitmap) {
375
0
        roaring_init_iterator_last(&bitmap.roaring, &_riter);
376
0
        _rowid_count = cast_set<uint32_t>(roaring_bitmap_get_cardinality(&bitmap.roaring));
377
0
        _rowid_left = _rowid_count;
378
0
    }
379
380
0
    bool has_more_range() const { return !_riter.has_value; }
381
382
    // read next range into [*from, *to) whose size <= max_range_size.
383
    // return false when there is no more range.
384
0
    bool next_range(const uint32_t max_range_size, uint32_t* from, uint32_t* to) override {
385
0
        if (!_riter.has_value) {
386
0
            return false;
387
0
        }
388
389
0
        uint32_t range_size = 0;
390
0
        *to = _riter.current_value + 1;
391
392
0
        do {
393
0
            *from = _riter.current_value;
394
0
            range_size++;
395
0
            roaring_previous_uint32_iterator(&_riter);
396
0
        } while (range_size < max_range_size && _riter.has_value &&
397
0
                 _riter.current_value + 1 == *from);
398
399
0
        return true;
400
0
    }
401
    /**
402
     * Reads a batch of row IDs from a roaring bitmap, starting from the end and moving backwards.
403
     * This function retrieves the last `batch_size` row IDs from the bitmap and stores them in the provided buffer.
404
     * It updates the internal state to track how many row IDs are left to read in subsequent calls.
405
     *
406
     * The row IDs are read in reverse order, but stored in the buffer maintaining their original order in the bitmap.
407
     *
408
     * Example:
409
     *   input bitmap: [0 1 4 5 6 7 10 15 16 17 18 19]
410
     *   If the bitmap has 12 elements and batch_size is set to 5, the function will first read [15, 16, 17, 18, 19]
411
     *   into the buffer, leaving 7 elements left. In the next call with batch_size 5, it will read [4, 5, 6, 7, 10].
412
     *
413
     */
414
0
    uint32_t read_batch_rowids(rowid_t* buf, uint32_t batch_size) override {
415
0
        if (!_riter.has_value || _rowid_left == 0) {
416
0
            return 0;
417
0
        }
418
419
0
        if (_rowid_count <= batch_size) {
420
0
            roaring_bitmap_to_uint32_array(_riter.parent,
421
0
                                           buf); // Fill 'buf' with '_rowid_count' elements.
422
0
            uint32_t num_read = _rowid_left;     // Save the number of row IDs read.
423
0
            _rowid_left = 0;                     // No row IDs left after this operation.
424
0
            return num_read;                     // Return the number of row IDs read.
425
0
        }
426
427
0
        uint32_t read_size = std::min(batch_size, _rowid_left);
428
0
        uint32_t num_read = 0; // Counter for the number of row IDs read.
429
430
        // Read row IDs into the buffer in reverse order.
431
0
        while (num_read < read_size && _riter.has_value) {
432
0
            buf[read_size - num_read - 1] = _riter.current_value;
433
0
            num_read++;
434
0
            _rowid_left--; // Decrement the count of remaining row IDs.
435
0
            roaring_previous_uint32_iterator(&_riter);
436
0
        }
437
438
        // Return the actual number of row IDs read.
439
0
        return num_read;
440
0
    }
441
442
private:
443
    roaring::api::roaring_uint32_iterator_t _riter;
444
    uint32_t _rowid_count;
445
    uint32_t _rowid_left;
446
};
447
448
SegmentIterator::SegmentIterator(std::shared_ptr<Segment> segment, SchemaSPtr schema)
449
2.83k
        : _segment(std::move(segment)),
450
2.83k
          _schema(schema),
451
2.83k
          _column_iterators(_schema->num_columns()),
452
2.83k
          _index_iterators(_schema->num_columns()),
453
2.83k
          _cur_rowid(0),
454
2.83k
          _lazy_materialization_read(false),
455
2.83k
          _lazy_inited(false),
456
2.83k
          _inited(false),
457
2.83k
          _pool(new ObjectPool) {}
458
459
5.50k
Status SegmentIterator::init(const StorageReadOptions& opts) {
460
5.50k
    auto status = _init_impl(opts);
461
5.50k
    if (!status.ok()) {
462
0
        _segment->update_healthy_status(status);
463
0
    }
464
5.50k
    return status;
465
5.50k
}
466
467
2.82k
std::unique_ptr<AdaptiveBlockSizePredictor> SegmentIterator::_make_block_size_predictor() const {
468
2.82k
    if (!config::enable_adaptive_batch_size || _opts.preferred_block_size_bytes == 0) {
469
0
        return nullptr;
470
0
    }
471
472
    // Collect per-column raw byte metadata from the segment footer for the columns
473
    // this iterator will actually output (defined by _schema, which is built from
474
    // _opts.return_columns).
475
2.82k
    std::vector<AdaptiveBlockSizePredictor::ColumnMetadata> col_metadata;
476
2.82k
    uint32_t seg_rows = _segment->num_rows();
477
2.82k
    uint64_t total_raw_bytes = 0;
478
2.82k
    double metadata_hint_bytes_per_row = 0.0;
479
2.82k
    if (seg_rows > 0) {
480
2.82k
        const auto& ts = _segment->tablet_schema();
481
2.82k
        if (ts) {
482
7.05k
            for (ColumnId cid : _schema->column_ids()) {
483
7.05k
                if (static_cast<size_t>(cid) < ts->num_columns()) {
484
7.05k
                    int32_t uid = ts->column(cid).unique_id();
485
7.05k
                    uint64_t raw_bytes = _segment->column_raw_data_bytes(uid);
486
7.05k
                    if (uid >= 0 && raw_bytes > 0) {
487
6.94k
                        total_raw_bytes += raw_bytes;
488
6.94k
                    }
489
7.05k
                }
490
7.05k
            }
491
2.82k
            metadata_hint_bytes_per_row = total_raw_bytes / static_cast<double>(seg_rows);
492
2.82k
        }
493
2.82k
    }
494
495
2.82k
    return std::make_unique<AdaptiveBlockSizePredictor>(
496
2.82k
            _opts.preferred_block_size_bytes, metadata_hint_bytes_per_row,
497
2.82k
            AdaptiveBlockSizePredictor::kDefaultProbeRows, _opts.block_row_max);
498
2.82k
}
499
500
5.50k
Status SegmentIterator::_init_impl(const StorageReadOptions& opts) {
501
    // get file handle from file descriptor of segment
502
5.50k
    if (_inited) {
503
2.68k
        return Status::OK();
504
2.68k
    }
505
2.82k
    _opts = opts;
506
2.82k
    SCOPED_RAW_TIMER(&_opts.stats->segment_iterator_init_timer_ns);
507
2.82k
    _inited = true;
508
2.82k
    _file_reader = _segment->_file_reader;
509
2.82k
    _col_predicates.clear();
510
511
2.82k
    for (const auto& predicate : opts.column_predicates) {
512
0
        if (!_segment->can_apply_predicate_safely(predicate->column_id(), *_schema,
513
0
                                                  _opts.target_cast_type_for_variants, _opts)) {
514
0
            continue;
515
0
        }
516
0
        _col_predicates.emplace_back(predicate);
517
0
    }
518
2.82k
    _tablet_id = opts.tablet_id;
519
    // Read options will not change, so that just resize here
520
2.82k
    _block_rowids.resize(_opts.block_row_max);
521
522
    // Adaptive batch size: snapshot the initial row limit and create predictor if enabled.
523
2.82k
    _initial_block_row_max = _opts.block_row_max;
524
2.82k
    _block_size_predictor = _make_block_size_predictor();
525
526
2.82k
    _remaining_conjunct_roots = opts.remaining_conjunct_roots;
527
528
2.82k
    if (_schema->rowid_col_idx() > 0) {
529
0
        _record_rowids = true;
530
0
    }
531
532
2.82k
    _virtual_column_exprs = _opts.virtual_column_exprs;
533
2.82k
    _vir_cid_to_idx_in_block = _opts.vir_cid_to_idx_in_block;
534
2.82k
    _score_runtime = _opts.score_runtime;
535
2.82k
    _ann_topn_runtime = _opts.ann_topn_runtime;
536
537
2.82k
    if (opts.output_columns != nullptr) {
538
1.30k
        _output_columns = *(opts.output_columns);
539
1.30k
    }
540
541
2.82k
    _storage_name_and_type.resize(_schema->columns().size());
542
2.82k
    auto storage_format = _opts.tablet_schema->get_inverted_index_storage_format();
543
25.8k
    for (int i = 0; i < _schema->columns().size(); ++i) {
544
23.0k
        const StorageField* col = _schema->column(i);
545
23.0k
        if (col) {
546
7.05k
            auto storage_type = _segment->get_data_type_of(col->get_desc(), _opts);
547
7.05k
            if (storage_type == nullptr) {
548
0
                storage_type = DataTypeFactory::instance().create_data_type(col->get_desc(),
549
0
                                                                            col->is_nullable());
550
0
            }
551
            // Currently, when writing a lucene index, the field of the document is column_name, and the column name is
552
            // bound to the index field. Since version 1.2, the data file storage has been changed from column_name to
553
            // column_unique_id, allowing the column name to be changed. Due to current limitations, previous inverted
554
            // index data cannot be used after Doris changes the column name. Column names also support Unicode
555
            // characters, which may cause other problems with indexing in non-ASCII characters.
556
            // After consideration, it was decided to change the field name from column_name to column_unique_id in
557
            // format V2, while format V1 continues to use column_name.
558
7.05k
            std::string field_name;
559
7.05k
            if (storage_format == InvertedIndexStorageFormatPB::V1) {
560
4.37k
                field_name = col->name();
561
4.37k
            } else {
562
2.68k
                if (col->is_extracted_column()) {
563
                    // variant sub col
564
                    // field_name format: parent_unique_id.sub_col_name
565
0
                    field_name = std::to_string(col->parent_unique_id()) + "." + col->name();
566
2.68k
                } else {
567
2.68k
                    field_name = std::to_string(col->unique_id());
568
2.68k
                }
569
2.68k
            }
570
7.05k
            _storage_name_and_type[i] = std::make_pair(field_name, storage_type);
571
7.05k
            if (int32_t uid = col->get_unique_id(); !_variant_sparse_column_cache.contains(uid)) {
572
7.05k
                DCHECK(uid >= 0);
573
7.05k
                _variant_sparse_column_cache.emplace(uid,
574
7.05k
                                                     std::make_unique<PathToBinaryColumnCache>());
575
7.05k
            }
576
7.05k
        }
577
23.0k
    }
578
579
2.82k
    RETURN_IF_ERROR(init_iterators());
580
581
2.82k
    RETURN_IF_ERROR(_construct_compound_expr_context());
582
2.82k
    _enable_common_expr_pushdown = !_common_expr_ctxs_push_down.empty();
583
2.82k
    VLOG_DEBUG << fmt::format(
584
0
            "Segment iterator init, virtual_column_exprs size: {}, "
585
0
            "_vir_cid_to_idx_in_block size: {}, common_expr_pushdown size: {}",
586
0
            _opts.virtual_column_exprs.size(), _opts.vir_cid_to_idx_in_block.size(),
587
0
            _common_expr_ctxs_push_down.size());
588
2.82k
    _initialize_predicate_results();
589
2.82k
    return Status::OK();
590
2.82k
}
591
592
2.82k
void SegmentIterator::_initialize_predicate_results() {
593
    // Initialize from _col_predicates
594
2.82k
    for (auto pred : _col_predicates) {
595
0
        int cid = pred->column_id();
596
0
        _column_predicate_index_exec_status[cid][pred] = false;
597
0
    }
598
599
2.82k
    _calculate_expr_in_remaining_conjunct_root();
600
2.82k
}
601
602
2.82k
Status SegmentIterator::init_iterators() {
603
2.82k
    RETURN_IF_ERROR(_init_return_column_iterators());
604
2.82k
    RETURN_IF_ERROR(_init_index_iterators());
605
2.82k
    return Status::OK();
606
2.82k
}
607
608
12.9k
Status SegmentIterator::_lazy_init(Block* block) {
609
12.9k
    if (_lazy_inited) {
610
10.1k
        return Status::OK();
611
10.1k
    }
612
2.82k
    SCOPED_RAW_TIMER(&_opts.stats->block_init_ns);
613
2.82k
    DorisMetrics::instance()->segment_read_total->increment(1);
614
2.82k
    _row_bitmap.addRange(0, _segment->num_rows());
615
2.82k
    _init_row_bitmap_by_condition_cache();
616
617
    // z-order can not use prefix index
618
2.82k
    if (_segment->_tablet_schema->sort_type() != SortType::ZORDER &&
619
2.82k
        _segment->_tablet_schema->cluster_key_uids().empty()) {
620
2.82k
        RETURN_IF_ERROR(_get_row_ranges_by_keys());
621
2.82k
    }
622
2.82k
    RETURN_IF_ERROR(_get_row_ranges_by_column_conditions());
623
2.82k
    RETURN_IF_ERROR(_vec_init_lazy_materialization());
624
    // Remove rows that have been marked deleted
625
2.82k
    if (_opts.delete_bitmap.count(segment_id()) > 0 &&
626
2.82k
        _opts.delete_bitmap.at(segment_id()) != nullptr) {
627
25
        size_t pre_size = _row_bitmap.cardinality();
628
25
        _row_bitmap -= *(_opts.delete_bitmap.at(segment_id()));
629
25
        _opts.stats->rows_del_by_bitmap += (pre_size - _row_bitmap.cardinality());
630
25
        VLOG_DEBUG << "read on segment: " << segment_id() << ", delete bitmap cardinality: "
631
0
                   << _opts.delete_bitmap.at(segment_id())->cardinality() << ", "
632
0
                   << _opts.stats->rows_del_by_bitmap << " rows deleted by bitmap";
633
25
    }
634
635
2.82k
    if (!_opts.row_ranges.is_empty()) {
636
0
        _row_bitmap &= RowRanges::ranges_to_roaring(_opts.row_ranges);
637
0
    }
638
639
2.82k
    _prepare_score_column_materialization();
640
641
2.82k
    RETURN_IF_ERROR(_apply_ann_topn_predicate());
642
643
2.82k
    if (_opts.read_orderby_key_reverse) {
644
0
        _range_iter.reset(new BackwardBitmapRangeIterator(_row_bitmap));
645
2.82k
    } else {
646
2.82k
        _range_iter.reset(new BitmapRangeIterator(_row_bitmap));
647
2.82k
    }
648
649
    // Reserve columns for _initial_block_row_max (the original max before any adaptive
650
    // prediction) because the predictor may increase block_row_max on subsequent batches
651
    // up to this ceiling. Using the current (possibly reduced) _opts.block_row_max would
652
    // cause heap-buffer-overflow if a later prediction is larger.
653
2.82k
    auto nrows_reserve_limit =
654
2.82k
            std::min(_row_bitmap.cardinality(), uint64_t(_initial_block_row_max));
655
2.82k
    if (_lazy_materialization_read || _opts.record_rowids || _is_need_expr_eval) {
656
896
        _block_rowids.resize(_initial_block_row_max);
657
896
    }
658
2.82k
    _current_return_columns.resize(_schema->columns().size());
659
660
2.82k
    _vec_init_char_column_id(block);
661
9.87k
    for (size_t i = 0; i < _schema->column_ids().size(); i++) {
662
7.05k
        ColumnId cid = _schema->column_ids()[i];
663
7.05k
        const auto* column_desc = _schema->column(cid);
664
7.05k
        if (_is_pred_column[cid]) {
665
467
            auto storage_column_type = _storage_name_and_type[cid].second;
666
            // Char type is special , since char type's computational datatype is same with string,
667
            // both are DataTypeString, but DataTypeString only return FieldType::OLAP_FIELD_TYPE_STRING
668
            // in get_storage_field_type.
669
467
            RETURN_IF_CATCH_EXCEPTION(
670
                    // Here, cid will not go out of bounds
671
                    // because the size of _current_return_columns equals _schema->tablet_columns().size()
672
467
                    _current_return_columns[cid] = Schema::get_predicate_column_ptr(
673
467
                            _is_char_type[cid] ? FieldType::OLAP_FIELD_TYPE_CHAR
674
467
                                               : storage_column_type->get_storage_field_type(),
675
467
                            storage_column_type->is_nullable(), _opts.io_ctx.reader_type));
676
467
            _current_return_columns[cid]->set_rowset_segment_id(
677
467
                    {_segment->rowset_id(), _segment->id()});
678
467
            _current_return_columns[cid]->reserve(nrows_reserve_limit);
679
6.58k
        } else if (i >= block->columns()) {
680
            // This column needs to be scanned, but doesn't need to be returned upward. (delete sign)
681
            // if i >= block->columns means the column and not the pred_column means `column i` is
682
            // a delete condition column. but the column is not effective in the segment. so we just
683
            // create a column to hold the data.
684
            // a. origin data -> b. delete condition -> c. new load data
685
            // the segment of c do not effective delete condition, but it still need read the column
686
            // to match the schema.
687
            // TODO: skip read the not effective delete column to speed up segment read.
688
0
            _current_return_columns[cid] = Schema::get_data_type_ptr(*column_desc)->create_column();
689
0
            _current_return_columns[cid]->reserve(nrows_reserve_limit);
690
0
        }
691
7.05k
    }
692
693
    // Additional deleted filter condition will be materialized column be at the end of the block,
694
    // after _output_column_by_sel_idx  will be erase, we not need to filter it,
695
    // so erase it from _columns_to_filter in the first next_batch.
696
    // Eg:
697
    //      `delete from table where a = 10;`
698
    //      `select b from table;`
699
    // a column only effective in segment iterator, the block from query engine only contain the b column,
700
    // so no need to filter a column by expr.
701
2.82k
    for (auto it = _columns_to_filter.begin(); it != _columns_to_filter.end();) {
702
0
        if (*it >= block->columns()) {
703
0
            it = _columns_to_filter.erase(it);
704
0
        } else {
705
0
            ++it;
706
0
        }
707
0
    }
708
709
2.82k
    _lazy_inited = true;
710
711
2.82k
    _init_segment_prefetchers();
712
713
2.82k
    return Status::OK();
714
2.82k
}
715
716
2.82k
void SegmentIterator::_init_segment_prefetchers() {
717
2.82k
    SCOPED_RAW_TIMER(&_opts.stats->segment_iterator_init_segment_prefetchers_timer_ns);
718
2.82k
    if (!config::is_cloud_mode()) {
719
2.82k
        return;
720
2.82k
    }
721
0
    static std::vector<ReaderType> supported_reader_types {
722
0
            ReaderType::READER_QUERY, ReaderType::READER_BASE_COMPACTION,
723
0
            ReaderType::READER_CUMULATIVE_COMPACTION, ReaderType::READER_FULL_COMPACTION};
724
0
    if (std::ranges::none_of(supported_reader_types,
725
0
                             [&](ReaderType t) { return _opts.io_ctx.reader_type == t; })) {
726
0
        return;
727
0
    }
728
    // Initialize segment prefetcher for predicate and non-predicate columns
729
0
    bool is_query = (_opts.io_ctx.reader_type == ReaderType::READER_QUERY);
730
0
    bool enable_prefetch = is_query ? config::enable_query_segment_file_cache_prefetch
731
0
                                    : config::enable_compaction_segment_file_cache_prefetch;
732
0
    LOG_IF(INFO, config::enable_segment_prefetch_verbose_log) << fmt::format(
733
0
            "[verbose] SegmentIterator _init_segment_prefetchers, is_query={}, enable_prefetch={}, "
734
0
            "_row_bitmap.isEmpty()={}, row_bitmap.cardinality()={}, tablet={}, rowset={}, "
735
0
            "segment={}, predicate_column_ids={}, common_expr_column_ids={}",
736
0
            is_query, enable_prefetch, _row_bitmap.isEmpty(), _row_bitmap.cardinality(),
737
0
            _opts.tablet_id, _opts.rowset_id.to_string(), segment_id(),
738
0
            fmt::join(_predicate_column_ids, ","), fmt::join(_common_expr_column_ids, ","));
739
0
    if (enable_prefetch && !_row_bitmap.isEmpty()) {
740
0
        int window_size =
741
0
                1 + (is_query ? config::query_segment_file_cache_prefetch_block_size
742
0
                              : config::compaction_segment_file_cache_prefetch_block_size);
743
0
        LOG_IF(INFO, config::enable_segment_prefetch_verbose_log) << fmt::format(
744
0
                "[verbose] SegmentIterator prefetch config: window_size={}", window_size);
745
0
        if (window_size > 0 &&
746
0
            !_column_iterators.empty()) { // ensure init_iterators has been called
747
0
            SegmentPrefetcherConfig prefetch_config(window_size,
748
0
                                                    config::file_cache_each_block_size);
749
0
            for (auto cid : _schema->column_ids()) {
750
0
                auto& column_iter = _column_iterators[cid];
751
0
                if (column_iter == nullptr) {
752
0
                    continue;
753
0
                }
754
0
                const auto* tablet_column = _schema->column(cid);
755
0
                SegmentPrefetchParams params {
756
0
                        .config = prefetch_config,
757
0
                        .read_options = _opts,
758
0
                };
759
0
                LOG_IF(INFO, config::enable_segment_prefetch_verbose_log) << fmt::format(
760
0
                        "[verbose] SegmentIterator init_segment_prefetchers, "
761
0
                        "tablet={}, rowset={}, segment={}, column_id={}, col_name={}, type={}",
762
0
                        _opts.tablet_id, _opts.rowset_id.to_string(), segment_id(), cid,
763
0
                        tablet_column->name(), tablet_column->type());
764
0
                Status st = column_iter->init_prefetcher(params);
765
0
                if (!st.ok()) {
766
0
                    LOG_IF(WARNING, config::enable_segment_prefetch_verbose_log) << fmt::format(
767
0
                            "[verbose] failed to init prefetcher for column_id={}, "
768
0
                            "tablet={}, rowset={}, segment={}, error={}",
769
0
                            cid, _opts.tablet_id, _opts.rowset_id.to_string(), segment_id(),
770
0
                            st.to_string());
771
0
                }
772
0
            }
773
774
            // for compaction, it's guaranteed that all rows are read, so we can prefetch all data blocks
775
0
            PrefetcherInitMethod init_method = (is_query && _row_bitmap.cardinality() < num_rows())
776
0
                                                       ? PrefetcherInitMethod::FROM_ROWIDS
777
0
                                                       : PrefetcherInitMethod::ALL_DATA_BLOCKS;
778
0
            std::map<PrefetcherInitMethod, std::vector<SegmentPrefetcher*>> prefetchers;
779
0
            for (const auto& column_iter : _column_iterators) {
780
0
                if (column_iter != nullptr) {
781
0
                    column_iter->collect_prefetchers(prefetchers, init_method);
782
0
                }
783
0
            }
784
0
            for (auto& [method, prefetcher_vec] : prefetchers) {
785
0
                if (method == PrefetcherInitMethod::ALL_DATA_BLOCKS) {
786
0
                    for (auto* prefetcher : prefetcher_vec) {
787
0
                        prefetcher->build_all_data_blocks();
788
0
                    }
789
0
                } else if (method == PrefetcherInitMethod::FROM_ROWIDS && !prefetcher_vec.empty()) {
790
0
                    SegmentPrefetcher::build_blocks_by_rowids(_row_bitmap, prefetcher_vec);
791
0
                }
792
0
            }
793
0
        }
794
0
    }
795
0
}
796
797
2.82k
Status SegmentIterator::_get_row_ranges_by_keys() {
798
2.82k
    SCOPED_RAW_TIMER(&_opts.stats->generate_row_ranges_by_keys_ns);
799
2.82k
    DorisMetrics::instance()->segment_row_total->increment(num_rows());
800
801
    // fast path for empty segment or empty key ranges
802
2.82k
    if (_row_bitmap.isEmpty() || _opts.key_ranges.empty()) {
803
2.82k
        return Status::OK();
804
2.82k
    }
805
806
    // Read & seek key columns is a waste of time when no key column in _schema
807
0
    if (std::none_of(
808
0
                _schema->columns().begin(), _schema->columns().end(), [&](const StorageField* col) {
809
0
                    return col && _opts.tablet_schema->column_by_uid(col->unique_id()).is_key();
810
0
                })) {
811
0
        return Status::OK();
812
0
    }
813
814
0
    RowRanges result_ranges;
815
0
    for (auto& key_range : _opts.key_ranges) {
816
0
        rowid_t lower_rowid = 0;
817
0
        rowid_t upper_rowid = num_rows();
818
0
        RETURN_IF_ERROR(_prepare_seek(key_range));
819
0
        if (key_range.upper_key != nullptr) {
820
            // If client want to read upper_bound, the include_upper is true. So we
821
            // should get the first ordinal at which key is larger than upper_bound.
822
            // So we call _lookup_ordinal with include_upper's negate
823
0
            RETURN_IF_ERROR(_lookup_ordinal(*key_range.upper_key, !key_range.include_upper,
824
0
                                            num_rows(), &upper_rowid));
825
0
        }
826
0
        if (upper_rowid > 0 && key_range.lower_key != nullptr) {
827
0
            RETURN_IF_ERROR(_lookup_ordinal(*key_range.lower_key, key_range.include_lower,
828
0
                                            upper_rowid, &lower_rowid));
829
0
        }
830
0
        auto row_range = RowRanges::create_single(lower_rowid, upper_rowid);
831
0
        RowRanges::ranges_union(result_ranges, row_range, &result_ranges);
832
0
    }
833
0
    size_t pre_size = _row_bitmap.cardinality();
834
0
    _row_bitmap &= RowRanges::ranges_to_roaring(result_ranges);
835
0
    _opts.stats->rows_key_range_filtered += (pre_size - _row_bitmap.cardinality());
836
837
0
    return Status::OK();
838
0
}
839
840
// Set up environment for the following seek.
841
0
Status SegmentIterator::_prepare_seek(const StorageReadOptions::KeyRange& key_range) {
842
0
    std::vector<const StorageField*> key_fields;
843
0
    std::set<uint32_t> column_set;
844
0
    if (key_range.lower_key != nullptr) {
845
0
        for (auto cid : key_range.lower_key->schema()->column_ids()) {
846
0
            column_set.emplace(cid);
847
0
            key_fields.emplace_back(key_range.lower_key->column_schema(cid));
848
0
        }
849
0
    }
850
0
    if (key_range.upper_key != nullptr) {
851
0
        for (auto cid : key_range.upper_key->schema()->column_ids()) {
852
0
            if (column_set.count(cid) == 0) {
853
0
                key_fields.emplace_back(key_range.upper_key->column_schema(cid));
854
0
                column_set.emplace(cid);
855
0
            }
856
0
        }
857
0
    }
858
0
    if (!_seek_schema) {
859
        // Schema constructors accept a vector of TabletColumnPtr. Convert
860
        // StorageField pointers to TabletColumnPtr by copying their descriptors.
861
0
        std::vector<TabletColumnPtr> cols;
862
0
        cols.reserve(key_fields.size());
863
0
        for (const StorageField* f : key_fields) {
864
0
            cols.emplace_back(std::make_shared<TabletColumn>(f->get_desc()));
865
0
        }
866
0
        _seek_schema = std::make_unique<Schema>(cols, cols.size());
867
0
    }
868
    // todo(wb) need refactor here, when using pk to search, _seek_block is useless
869
0
    if (_seek_block.size() == 0) {
870
0
        _seek_block.resize(_seek_schema->num_column_ids());
871
0
        int i = 0;
872
0
        for (auto cid : _seek_schema->column_ids()) {
873
0
            auto column_desc = _seek_schema->column(cid);
874
0
            _seek_block[i] = Schema::get_column_by_field(*column_desc);
875
0
            i++;
876
0
        }
877
0
    }
878
879
    // create used column iterator
880
0
    for (auto cid : _seek_schema->column_ids()) {
881
0
        if (_column_iterators[cid] == nullptr) {
882
            // TODO: Do we need this?
883
0
            if (_virtual_column_exprs.contains(cid)) {
884
0
                _column_iterators[cid] = std::make_unique<VirtualColumnIterator>();
885
0
                continue;
886
0
            }
887
888
0
            RETURN_IF_ERROR(_segment->new_column_iterator(_opts.tablet_schema->column(cid),
889
0
                                                          &_column_iterators[cid], &_opts,
890
0
                                                          &_variant_sparse_column_cache));
891
0
            ColumnIteratorOptions iter_opts {
892
0
                    .use_page_cache = _opts.use_page_cache,
893
0
                    .file_reader = _file_reader.get(),
894
0
                    .stats = _opts.stats,
895
0
                    .io_ctx = _opts.io_ctx,
896
0
            };
897
0
            RETURN_IF_ERROR(_column_iterators[cid]->init(iter_opts));
898
0
        }
899
0
    }
900
901
0
    return Status::OK();
902
0
}
903
904
2.82k
Status SegmentIterator::_get_row_ranges_by_column_conditions() {
905
2.82k
    SCOPED_RAW_TIMER(&_opts.stats->generate_row_ranges_by_column_conditions_ns);
906
2.82k
    if (_row_bitmap.isEmpty()) {
907
0
        return Status::OK();
908
0
    }
909
910
2.82k
    {
911
2.82k
        if (_opts.runtime_state &&
912
2.82k
            _opts.runtime_state->query_options().enable_inverted_index_query &&
913
2.82k
            (has_index_in_iterators() || !_common_expr_ctxs_push_down.empty())) {
914
0
            SCOPED_RAW_TIMER(&_opts.stats->inverted_index_filter_timer);
915
0
            size_t input_rows = _row_bitmap.cardinality();
916
            // Only apply column-level inverted index if we have iterators
917
0
            if (has_index_in_iterators()) {
918
0
                RETURN_IF_ERROR(_apply_inverted_index());
919
0
            }
920
            // Always apply expr-level index (e.g., search expressions) if we have common_expr_pushdown
921
            // This allows search expressions with variant subcolumns to be evaluated even when
922
            // the segment doesn't have all subcolumns
923
0
            RETURN_IF_ERROR(_apply_index_expr());
924
0
            for (auto it = _common_expr_ctxs_push_down.begin();
925
0
                 it != _common_expr_ctxs_push_down.end();) {
926
0
                if ((*it)->all_expr_inverted_index_evaluated()) {
927
0
                    const auto* result = (*it)->get_index_context()->get_index_result_for_expr(
928
0
                            (*it)->root().get());
929
0
                    if (result != nullptr) {
930
0
                        _row_bitmap &= *result->get_data_bitmap();
931
0
                        auto root = (*it)->root();
932
0
                        auto iter_find = std::find(_remaining_conjunct_roots.begin(),
933
0
                                                   _remaining_conjunct_roots.end(), root);
934
0
                        if (iter_find != _remaining_conjunct_roots.end()) {
935
0
                            _remaining_conjunct_roots.erase(iter_find);
936
0
                        }
937
0
                        it = _common_expr_ctxs_push_down.erase(it);
938
0
                    }
939
0
                } else {
940
0
                    ++it;
941
0
                }
942
0
            }
943
0
            _opts.condition_cache_digest =
944
0
                    _common_expr_ctxs_push_down.empty() ? 0 : _opts.condition_cache_digest;
945
0
            _opts.stats->rows_inverted_index_filtered += (input_rows - _row_bitmap.cardinality());
946
0
            for (auto cid : _schema->column_ids()) {
947
0
                bool result_true = _check_all_conditions_passed_inverted_index_for_column(cid);
948
0
                if (result_true) {
949
0
                    _need_read_data_indices[cid] = false;
950
0
                }
951
0
            }
952
0
        }
953
2.82k
    }
954
955
2.82k
    DBUG_EXECUTE_IF("segment_iterator.inverted_index.filtered_rows", {
956
2.82k
        LOG(INFO) << "Debug Point: segment_iterator.inverted_index.filtered_rows: "
957
2.82k
                  << _opts.stats->rows_inverted_index_filtered;
958
2.82k
        auto filtered_rows = DebugPoints::instance()->get_debug_param_or_default<int32_t>(
959
2.82k
                "segment_iterator.inverted_index.filtered_rows", "filtered_rows", -1);
960
2.82k
        if (filtered_rows != _opts.stats->rows_inverted_index_filtered) {
961
2.82k
            return Status::Error<ErrorCode::INTERNAL_ERROR>(
962
2.82k
                    "filtered_rows: {} not equal to expected: {}",
963
2.82k
                    _opts.stats->rows_inverted_index_filtered, filtered_rows);
964
2.82k
        }
965
2.82k
    })
966
967
2.82k
    DBUG_EXECUTE_IF("segment_iterator.apply_inverted_index", {
968
2.82k
        LOG(INFO) << "Debug Point: segment_iterator.apply_inverted_index";
969
2.82k
        if (!_common_expr_ctxs_push_down.empty() || !_col_predicates.empty()) {
970
2.82k
            return Status::Error<ErrorCode::INTERNAL_ERROR>(
971
2.82k
                    "it is failed to apply inverted index, common_expr_ctxs_push_down: {}, "
972
2.82k
                    "col_predicates: {}",
973
2.82k
                    _common_expr_ctxs_push_down.size(), _col_predicates.size());
974
2.82k
        }
975
2.82k
    })
976
977
2.82k
    if (!_row_bitmap.isEmpty() &&
978
2.82k
        (!_opts.topn_filter_source_node_ids.empty() || !_opts.col_id_to_predicates.empty() ||
979
2.82k
         _opts.delete_condition_predicates->num_of_column_predicate() > 0)) {
980
467
        RowRanges condition_row_ranges = RowRanges::create_single(_segment->num_rows());
981
467
        RETURN_IF_ERROR(_get_row_ranges_from_conditions(&condition_row_ranges));
982
467
        size_t pre_size = _row_bitmap.cardinality();
983
467
        _row_bitmap &= RowRanges::ranges_to_roaring(condition_row_ranges);
984
467
        _opts.stats->rows_conditions_filtered += (pre_size - _row_bitmap.cardinality());
985
467
    }
986
987
2.82k
    DBUG_EXECUTE_IF("bloom_filter_must_filter_data", {
988
2.82k
        if (_opts.stats->rows_bf_filtered == 0) {
989
2.82k
            return Status::Error<ErrorCode::INTERNAL_ERROR>(
990
2.82k
                    "Bloom filter did not filter the data.");
991
2.82k
        }
992
2.82k
    })
993
994
    // TODO(hkp): calculate filter rate to decide whether to
995
    // use zone map/bloom filter/secondary index or not.
996
2.82k
    return Status::OK();
997
2.82k
}
998
999
0
bool SegmentIterator::_column_has_ann_index(int32_t cid) {
1000
0
    bool has_ann_index = _index_iterators[cid] != nullptr &&
1001
0
                         _index_iterators[cid]->get_reader(AnnIndexReaderType::ANN);
1002
1003
0
    return has_ann_index;
1004
0
}
1005
1006
2.82k
Status SegmentIterator::_apply_ann_topn_predicate() {
1007
2.82k
    if (_ann_topn_runtime == nullptr) {
1008
2.82k
        return Status::OK();
1009
2.82k
    }
1010
1011
0
    VLOG_DEBUG << fmt::format("Try apply ann topn: {}", _ann_topn_runtime->debug_string());
1012
0
    size_t src_col_idx = _ann_topn_runtime->get_src_column_idx();
1013
0
    ColumnId src_cid = _schema->column_id(src_col_idx);
1014
0
    IndexIterator* ann_index_iterator = _index_iterators[src_cid].get();
1015
0
    bool has_ann_index = _column_has_ann_index(src_cid);
1016
0
    bool has_common_expr_push_down = !_common_expr_ctxs_push_down.empty();
1017
0
    bool has_column_predicate = std::any_of(_is_pred_column.begin(), _is_pred_column.end(),
1018
0
                                            [](bool is_pred) { return is_pred; });
1019
0
    if (!has_ann_index || has_common_expr_push_down || has_column_predicate) {
1020
0
        VLOG_DEBUG << fmt::format(
1021
0
                "Ann topn can not be evaluated by ann index, has_ann_index: {}, "
1022
0
                "has_common_expr_push_down: {}, has_column_predicate: {}",
1023
0
                has_ann_index, has_common_expr_push_down, has_column_predicate);
1024
        // Disable index-only scan on ann indexed column.
1025
0
        _need_read_data_indices[src_cid] = true;
1026
0
        _opts.stats->ann_fall_back_brute_force_cnt += 1;
1027
0
        return Status::OK();
1028
0
    }
1029
1030
    // Process asc & desc according to the type of metric
1031
0
    auto index_reader = ann_index_iterator->get_reader(AnnIndexReaderType::ANN);
1032
0
    auto ann_index_reader = dynamic_cast<AnnIndexReader*>(index_reader.get());
1033
0
    DCHECK(ann_index_reader != nullptr);
1034
0
    if (ann_index_reader->get_metric_type() == AnnIndexMetric::IP) {
1035
0
        if (_ann_topn_runtime->is_asc()) {
1036
0
            VLOG_DEBUG << fmt::format(
1037
0
                    "Asc topn for inner product can not be evaluated by ann index");
1038
            // Disable index-only scan on ann indexed column.
1039
0
            _need_read_data_indices[src_cid] = true;
1040
0
            _opts.stats->ann_fall_back_brute_force_cnt += 1;
1041
0
            return Status::OK();
1042
0
        }
1043
0
    } else {
1044
0
        if (!_ann_topn_runtime->is_asc()) {
1045
0
            VLOG_DEBUG << fmt::format("Desc topn for l2/cosine can not be evaluated by ann index");
1046
            // Disable index-only scan on ann indexed column.
1047
0
            _need_read_data_indices[src_cid] = true;
1048
0
            _opts.stats->ann_fall_back_brute_force_cnt += 1;
1049
0
            return Status::OK();
1050
0
        }
1051
0
    }
1052
1053
0
    if (ann_index_reader->get_metric_type() != _ann_topn_runtime->get_metric_type()) {
1054
0
        VLOG_DEBUG << fmt::format(
1055
0
                "Ann topn metric type {} not match index metric type {}, can not be evaluated by "
1056
0
                "ann index",
1057
0
                metric_to_string(_ann_topn_runtime->get_metric_type()),
1058
0
                metric_to_string(ann_index_reader->get_metric_type()));
1059
        // Disable index-only scan on ann indexed column.
1060
0
        _need_read_data_indices[src_cid] = true;
1061
0
        _opts.stats->ann_fall_back_brute_force_cnt += 1;
1062
0
        return Status::OK();
1063
0
    }
1064
1065
0
    size_t pre_size = _row_bitmap.cardinality();
1066
0
    size_t rows_of_segment = _segment->num_rows();
1067
0
    if (static_cast<double>(pre_size) < static_cast<double>(rows_of_segment) * 0.3) {
1068
0
        VLOG_DEBUG << fmt::format(
1069
0
                "Ann topn predicate input rows {} < 30% of segment rows {}, will not use ann index "
1070
0
                "to "
1071
0
                "filter",
1072
0
                pre_size, rows_of_segment);
1073
        // Disable index-only scan on ann indexed column.
1074
0
        _need_read_data_indices[src_cid] = true;
1075
0
        _opts.stats->ann_fall_back_brute_force_cnt += 1;
1076
0
        return Status::OK();
1077
0
    }
1078
0
    IColumn::MutablePtr result_column;
1079
0
    std::shared_ptr<std::vector<uint64_t>> result_row_ids;
1080
0
    segment_v2::AnnIndexStats ann_index_stats;
1081
1082
    // Try to load ANN index before search
1083
0
    auto ann_index_iterator_casted =
1084
0
            dynamic_cast<segment_v2::AnnIndexIterator*>(ann_index_iterator);
1085
0
    if (ann_index_iterator_casted == nullptr) {
1086
0
        VLOG_DEBUG << "Failed to cast index iterator to AnnIndexIterator, fallback to brute force";
1087
0
        _need_read_data_indices[src_cid] = true;
1088
0
        _opts.stats->ann_fall_back_brute_force_cnt += 1;
1089
0
        return Status::OK();
1090
0
    }
1091
1092
    // Track load index timing
1093
0
    {
1094
0
        SCOPED_TIMER(&(ann_index_stats.load_index_costs_ns));
1095
0
        if (!ann_index_iterator_casted->try_load_index()) {
1096
0
            VLOG_DEBUG << "Failed to load ANN index, fallback to brute force search";
1097
0
            _need_read_data_indices[src_cid] = true;
1098
0
            _opts.stats->ann_fall_back_brute_force_cnt += 1;
1099
0
            return Status::OK();
1100
0
        }
1101
0
        double load_costs_ms =
1102
0
                static_cast<double>(ann_index_stats.load_index_costs_ns.value()) / 1000000.0;
1103
0
        DorisMetrics::instance()->ann_index_load_costs_ms->increment(
1104
0
                static_cast<int64_t>(load_costs_ms));
1105
0
    }
1106
1107
0
    bool enable_ann_index_result_cache =
1108
0
            !_opts.runtime_state ||
1109
0
            !_opts.runtime_state->query_options().__isset.enable_ann_index_result_cache ||
1110
0
            _opts.runtime_state->query_options().enable_ann_index_result_cache;
1111
0
    RETURN_IF_ERROR(_ann_topn_runtime->evaluate_vector_ann_search(
1112
0
            ann_index_iterator_casted, &_row_bitmap, rows_of_segment, enable_ann_index_result_cache,
1113
0
            result_column, result_row_ids, ann_index_stats));
1114
1115
0
    VLOG_DEBUG << fmt::format("Ann topn filtered {} - {} = {} rows", pre_size,
1116
0
                              _row_bitmap.cardinality(), pre_size - _row_bitmap.cardinality());
1117
1118
0
    int64_t rows_filterd = pre_size - _row_bitmap.cardinality();
1119
0
    _opts.stats->rows_ann_index_topn_filtered += rows_filterd;
1120
0
    _opts.stats->ann_index_load_ns += ann_index_stats.load_index_costs_ns.value();
1121
0
    _opts.stats->ann_topn_search_ns += ann_index_stats.search_costs_ns.value();
1122
0
    _opts.stats->ann_ivf_on_disk_load_ns += ann_index_stats.ivf_on_disk_load_costs_ns.value();
1123
0
    _opts.stats->ann_ivf_on_disk_cache_hit_cnt += ann_index_stats.ivf_on_disk_cache_hit_cnt.value();
1124
0
    _opts.stats->ann_ivf_on_disk_cache_miss_cnt +=
1125
0
            ann_index_stats.ivf_on_disk_cache_miss_cnt.value();
1126
0
    _opts.stats->ann_index_topn_engine_search_ns += ann_index_stats.engine_search_ns.value();
1127
0
    _opts.stats->ann_index_topn_result_process_ns +=
1128
0
            ann_index_stats.result_process_costs_ns.value();
1129
0
    _opts.stats->ann_index_topn_engine_convert_ns += ann_index_stats.engine_convert_ns.value();
1130
0
    _opts.stats->ann_index_topn_engine_prepare_ns += ann_index_stats.engine_prepare_ns.value();
1131
0
    _opts.stats->ann_index_topn_search_cnt += 1;
1132
0
    _opts.stats->ann_index_cache_hits += ann_index_stats.topn_cache_hits.value();
1133
0
    const size_t dst_col_idx = _ann_topn_runtime->get_dest_column_idx();
1134
0
    ColumnIterator* column_iter = _column_iterators[_schema->column_id(dst_col_idx)].get();
1135
0
    DCHECK(column_iter != nullptr);
1136
0
    VirtualColumnIterator* virtual_column_iter = dynamic_cast<VirtualColumnIterator*>(column_iter);
1137
0
    DCHECK(virtual_column_iter != nullptr);
1138
0
    VLOG_DEBUG << fmt::format(
1139
0
            "Virtual column iterator, column_idx {}, is materialized with {} rows", dst_col_idx,
1140
0
            result_row_ids->size());
1141
    // reference count of result_column should be 1, so move will not issue any data copy.
1142
0
    virtual_column_iter->prepare_materialization(std::move(result_column), result_row_ids);
1143
1144
0
    _need_read_data_indices[src_cid] = false;
1145
0
    VLOG_DEBUG << fmt::format(
1146
0
            "Enable ANN index-only scan for src column cid {} (skip reading data pages)", src_cid);
1147
1148
0
    return Status::OK();
1149
0
}
1150
1151
467
Status SegmentIterator::_get_row_ranges_from_conditions(RowRanges* condition_row_ranges) {
1152
467
    std::set<int32_t> cids;
1153
467
    for (auto& entry : _opts.col_id_to_predicates) {
1154
0
        cids.insert(entry.first);
1155
0
    }
1156
1157
467
    {
1158
467
        SCOPED_RAW_TIMER(&_opts.stats->generate_row_ranges_by_dict_ns);
1159
        /// Low cardinality optimization is currently not very stable, so to prevent data corruption,
1160
        /// we are temporarily disabling its use in data compaction.
1161
        // TODO: enable it in not only ReaderTyper::READER_QUERY but also other reader types.
1162
467
        if (_opts.io_ctx.reader_type == ReaderType::READER_QUERY) {
1163
0
            RowRanges dict_row_ranges = RowRanges::create_single(num_rows());
1164
0
            for (auto cid : cids) {
1165
0
                if (!_segment->can_apply_predicate_safely(
1166
0
                            cid, *_schema, _opts.target_cast_type_for_variants, _opts)) {
1167
0
                    continue;
1168
0
                }
1169
0
                DCHECK(_opts.col_id_to_predicates.count(cid) > 0);
1170
0
                RETURN_IF_ERROR(_column_iterators[cid]->get_row_ranges_by_dict(
1171
0
                        _opts.col_id_to_predicates.at(cid).get(), &dict_row_ranges));
1172
1173
0
                if (dict_row_ranges.is_empty()) {
1174
0
                    break;
1175
0
                }
1176
0
            }
1177
1178
0
            if (dict_row_ranges.is_empty()) {
1179
0
                RowRanges::ranges_intersection(*condition_row_ranges, dict_row_ranges,
1180
0
                                               condition_row_ranges);
1181
0
                _opts.stats->segment_dict_filtered++;
1182
0
                _opts.stats->filtered_segment_number++;
1183
0
                return Status::OK();
1184
0
            }
1185
0
        }
1186
467
    }
1187
1188
467
    size_t pre_size = 0;
1189
467
    {
1190
467
        SCOPED_RAW_TIMER(&_opts.stats->generate_row_ranges_by_bf_ns);
1191
        // first filter data by bloom filter index
1192
        // bloom filter index only use CondColumn
1193
467
        RowRanges bf_row_ranges = RowRanges::create_single(num_rows());
1194
467
        for (auto& cid : cids) {
1195
0
            DCHECK(_opts.col_id_to_predicates.count(cid) > 0);
1196
0
            if (!_segment->can_apply_predicate_safely(cid, *_schema,
1197
0
                                                      _opts.target_cast_type_for_variants, _opts)) {
1198
0
                continue;
1199
0
            }
1200
            // get row ranges by bf index of this column,
1201
0
            RowRanges column_bf_row_ranges = RowRanges::create_single(num_rows());
1202
0
            RETURN_IF_ERROR(_column_iterators[cid]->get_row_ranges_by_bloom_filter(
1203
0
                    _opts.col_id_to_predicates.at(cid).get(), &column_bf_row_ranges));
1204
0
            RowRanges::ranges_intersection(bf_row_ranges, column_bf_row_ranges, &bf_row_ranges);
1205
0
        }
1206
1207
467
        pre_size = condition_row_ranges->count();
1208
467
        RowRanges::ranges_intersection(*condition_row_ranges, bf_row_ranges, condition_row_ranges);
1209
467
        _opts.stats->rows_bf_filtered += (pre_size - condition_row_ranges->count());
1210
467
    }
1211
1212
0
    {
1213
467
        SCOPED_RAW_TIMER(&_opts.stats->generate_row_ranges_by_zonemap_ns);
1214
467
        RowRanges zone_map_row_ranges = RowRanges::create_single(num_rows());
1215
        // second filter data by zone map
1216
467
        for (const auto& cid : cids) {
1217
0
            DCHECK(_opts.col_id_to_predicates.count(cid) > 0);
1218
0
            if (!_segment->can_apply_predicate_safely(cid, *_schema,
1219
0
                                                      _opts.target_cast_type_for_variants, _opts)) {
1220
0
                continue;
1221
0
            }
1222
            // do not check zonemap if predicate does not support zonemap
1223
0
            if (!_opts.col_id_to_predicates.at(cid)->support_zonemap()) {
1224
0
                VLOG_DEBUG << "skip zonemap for column " << cid;
1225
0
                continue;
1226
0
            }
1227
            // get row ranges by zone map of this column,
1228
0
            RowRanges column_row_ranges = RowRanges::create_single(num_rows());
1229
0
            RETURN_IF_ERROR(_column_iterators[cid]->get_row_ranges_by_zone_map(
1230
0
                    _opts.col_id_to_predicates.at(cid).get(),
1231
0
                    _opts.del_predicates_for_zone_map.count(cid) > 0
1232
0
                            ? &(_opts.del_predicates_for_zone_map.at(cid))
1233
0
                            : nullptr,
1234
0
                    &column_row_ranges));
1235
            // intersect different columns's row ranges to get final row ranges by zone map
1236
0
            RowRanges::ranges_intersection(zone_map_row_ranges, column_row_ranges,
1237
0
                                           &zone_map_row_ranges);
1238
0
        }
1239
1240
467
        pre_size = condition_row_ranges->count();
1241
467
        RowRanges::ranges_intersection(*condition_row_ranges, zone_map_row_ranges,
1242
467
                                       condition_row_ranges);
1243
1244
467
        size_t pre_size2 = condition_row_ranges->count();
1245
467
        RowRanges::ranges_intersection(*condition_row_ranges, zone_map_row_ranges,
1246
467
                                       condition_row_ranges);
1247
467
        _opts.stats->rows_stats_rp_filtered += (pre_size2 - condition_row_ranges->count());
1248
467
        _opts.stats->rows_stats_filtered += (pre_size - condition_row_ranges->count());
1249
467
    }
1250
1251
0
    return Status::OK();
1252
467
}
1253
1254
0
bool SegmentIterator::_is_literal_node(const TExprNodeType::type& node_type) {
1255
0
    switch (node_type) {
1256
0
    case TExprNodeType::BOOL_LITERAL:
1257
0
    case TExprNodeType::INT_LITERAL:
1258
0
    case TExprNodeType::LARGE_INT_LITERAL:
1259
0
    case TExprNodeType::FLOAT_LITERAL:
1260
0
    case TExprNodeType::DECIMAL_LITERAL:
1261
0
    case TExprNodeType::STRING_LITERAL:
1262
0
    case TExprNodeType::DATE_LITERAL:
1263
0
    case TExprNodeType::TIMEV2_LITERAL:
1264
0
        return true;
1265
0
    default:
1266
0
        return false;
1267
0
    }
1268
0
}
1269
1270
0
Status SegmentIterator::_extract_common_expr_columns(const VExprSPtr& expr) {
1271
0
    auto& children = expr->children();
1272
0
    for (int i = 0; i < children.size(); ++i) {
1273
0
        RETURN_IF_ERROR(_extract_common_expr_columns(children[i]));
1274
0
    }
1275
1276
0
    auto node_type = expr->node_type();
1277
0
    if (node_type == TExprNodeType::SLOT_REF) {
1278
0
        auto slot_expr = std::dynamic_pointer_cast<doris::VSlotRef>(expr);
1279
0
        _is_common_expr_column[_schema->column_id(slot_expr->column_id())] = true;
1280
0
        _common_expr_columns.insert(_schema->column_id(slot_expr->column_id()));
1281
0
    } else if (node_type == TExprNodeType::VIRTUAL_SLOT_REF) {
1282
0
        std::shared_ptr<VirtualSlotRef> virtual_slot_ref =
1283
0
                std::dynamic_pointer_cast<VirtualSlotRef>(expr);
1284
0
        RETURN_IF_ERROR(_extract_common_expr_columns(virtual_slot_ref->get_virtual_column_expr()));
1285
0
    }
1286
1287
0
    return Status::OK();
1288
0
}
1289
1290
0
bool SegmentIterator::_check_apply_by_inverted_index(std::shared_ptr<ColumnPredicate> pred) {
1291
0
    if (_opts.runtime_state && !_opts.runtime_state->query_options().enable_inverted_index_query) {
1292
0
        return false;
1293
0
    }
1294
0
    auto pred_column_id = pred->column_id();
1295
0
    if (_index_iterators[pred_column_id] == nullptr) {
1296
        //this column without inverted index
1297
0
        return false;
1298
0
    }
1299
1300
0
    if (_inverted_index_not_support_pred_type(pred->type())) {
1301
0
        return false;
1302
0
    }
1303
1304
0
    if (pred->type() == PredicateType::IN_LIST || pred->type() == PredicateType::NOT_IN_LIST) {
1305
        // in_list or not_in_list predicate produced by runtime filter
1306
0
        if (pred->is_runtime_filter()) {
1307
0
            return false;
1308
0
        }
1309
0
    }
1310
1311
    // UNTOKENIZED strings exceed ignore_above, they are written as null, causing range query errors
1312
0
    if (PredicateTypeTraits::is_range(pred->type()) &&
1313
0
        !IndexReaderHelper::has_bkd_index(_index_iterators[pred_column_id].get())) {
1314
0
        return false;
1315
0
    }
1316
1317
    // Function filter no apply inverted index
1318
0
    if (dynamic_cast<LikeColumnPredicate<TYPE_CHAR>*>(pred.get()) != nullptr ||
1319
0
        dynamic_cast<LikeColumnPredicate<TYPE_STRING>*>(pred.get()) != nullptr) {
1320
0
        return false;
1321
0
    }
1322
1323
0
    bool handle_by_fulltext = _column_has_fulltext_index(pred_column_id);
1324
0
    if (handle_by_fulltext) {
1325
        // when predicate is leafNode of andNode,
1326
        // can apply 'match query' and 'equal query' and 'list query' for fulltext index.
1327
0
        return pred->type() == PredicateType::MATCH || pred->type() == PredicateType::IS_NULL ||
1328
0
               pred->type() == PredicateType::IS_NOT_NULL ||
1329
0
               PredicateTypeTraits::is_equal_or_list(pred->type());
1330
0
    }
1331
1332
0
    return true;
1333
0
}
1334
1335
// TODO: optimization when all expr can not evaluate by inverted/ann index,
1336
0
Status SegmentIterator::_apply_index_expr() {
1337
0
    bool enable_ann_index_result_cache =
1338
0
            !_opts.runtime_state ||
1339
0
            !_opts.runtime_state->query_options().__isset.enable_ann_index_result_cache ||
1340
0
            _opts.runtime_state->query_options().enable_ann_index_result_cache;
1341
1342
0
    for (const auto& expr_ctx : _common_expr_ctxs_push_down) {
1343
0
        if (Status st = expr_ctx->evaluate_inverted_index(num_rows()); !st.ok()) {
1344
0
            if (_downgrade_without_index(st) || st.code() == ErrorCode::NOT_IMPLEMENTED_ERROR) {
1345
0
                continue;
1346
0
            } else {
1347
                // other code is not to be handled, we should just break
1348
0
                LOG(WARNING) << "failed to evaluate inverted index for expr_ctx: "
1349
0
                             << expr_ctx->root()->debug_string()
1350
0
                             << ", error msg: " << st.to_string();
1351
0
                return st;
1352
0
            }
1353
0
        }
1354
0
    }
1355
1356
    // Evaluate inverted index for virtual column MATCH expressions (projections).
1357
    // Unlike common exprs which filter rows, these only compute index result bitmaps
1358
    // for later materialization via fast_execute().
1359
0
    for (auto& [cid, expr_ctx] : _virtual_column_exprs) {
1360
0
        if (expr_ctx->get_index_context() == nullptr) {
1361
0
            continue;
1362
0
        }
1363
0
        if (Status st = expr_ctx->evaluate_inverted_index(num_rows()); !st.ok()) {
1364
0
            if (_downgrade_without_index(st) || st.code() == ErrorCode::NOT_IMPLEMENTED_ERROR) {
1365
0
                continue;
1366
0
            } else {
1367
0
                LOG(WARNING) << "failed to evaluate inverted index for virtual column expr: "
1368
0
                             << expr_ctx->root()->debug_string()
1369
0
                             << ", error msg: " << st.to_string();
1370
0
                return st;
1371
0
            }
1372
0
        }
1373
0
    }
1374
1375
    // Apply ann range search
1376
0
    for (const auto& expr_ctx : _common_expr_ctxs_push_down) {
1377
0
        segment_v2::AnnIndexStats ann_index_stats;
1378
0
        size_t origin_rows = _row_bitmap.cardinality();
1379
0
        RETURN_IF_ERROR(expr_ctx->evaluate_ann_range_search(
1380
0
                _index_iterators, _schema->column_ids(), _column_iterators,
1381
0
                _common_expr_to_slotref_map, _row_bitmap, ann_index_stats,
1382
0
                enable_ann_index_result_cache));
1383
0
        _opts.stats->rows_ann_index_range_filtered += (origin_rows - _row_bitmap.cardinality());
1384
0
        _opts.stats->ann_index_load_ns += ann_index_stats.load_index_costs_ns.value();
1385
0
        _opts.stats->ann_index_range_search_ns += ann_index_stats.search_costs_ns.value();
1386
0
        _opts.stats->ann_ivf_on_disk_load_ns += ann_index_stats.ivf_on_disk_load_costs_ns.value();
1387
0
        _opts.stats->ann_ivf_on_disk_cache_hit_cnt +=
1388
0
                ann_index_stats.ivf_on_disk_cache_hit_cnt.value();
1389
0
        _opts.stats->ann_ivf_on_disk_cache_miss_cnt +=
1390
0
                ann_index_stats.ivf_on_disk_cache_miss_cnt.value();
1391
0
        _opts.stats->ann_range_engine_search_ns += ann_index_stats.engine_search_ns.value();
1392
0
        _opts.stats->ann_range_result_convert_ns += ann_index_stats.result_process_costs_ns.value();
1393
0
        _opts.stats->ann_range_engine_convert_ns += ann_index_stats.engine_convert_ns.value();
1394
0
        _opts.stats->ann_range_pre_process_ns += ann_index_stats.engine_prepare_ns.value();
1395
0
        _opts.stats->ann_fall_back_brute_force_cnt += ann_index_stats.fall_back_brute_force_cnt;
1396
0
        _opts.stats->ann_index_range_cache_hits += ann_index_stats.range_cache_hits.value();
1397
0
    }
1398
1399
0
    for (auto it = _common_expr_ctxs_push_down.begin(); it != _common_expr_ctxs_push_down.end();) {
1400
0
        if ((*it)->root()->ann_range_search_executedd()) {
1401
0
            _opts.stats->ann_index_range_search_cnt++;
1402
0
            it = _common_expr_ctxs_push_down.erase(it);
1403
0
        } else {
1404
0
            ++it;
1405
0
        }
1406
0
    }
1407
    // TODO:Do we need to remove these expr root from _remaining_conjunct_roots?
1408
1409
0
    return Status::OK();
1410
0
}
1411
1412
0
bool SegmentIterator::_downgrade_without_index(Status res, bool need_remaining) {
1413
0
    bool is_fallback =
1414
0
            _opts.runtime_state->query_options().enable_fallback_on_missing_inverted_index;
1415
0
    if ((res.code() == ErrorCode::INVERTED_INDEX_FILE_NOT_FOUND && is_fallback) ||
1416
0
        res.code() == ErrorCode::INVERTED_INDEX_BYPASS ||
1417
0
        res.code() == ErrorCode::INVERTED_INDEX_EVALUATE_SKIPPED ||
1418
0
        (res.code() == ErrorCode::INVERTED_INDEX_NO_TERMS && need_remaining) ||
1419
0
        res.code() == ErrorCode::INVERTED_INDEX_FILE_CORRUPTED) {
1420
        // 1. INVERTED_INDEX_FILE_NOT_FOUND means index file has not been built,
1421
        //    usually occurs when creating a new index, queries can be downgraded
1422
        //    without index.
1423
        // 2. INVERTED_INDEX_BYPASS means the hit of condition by index
1424
        //    has reached the optimal limit, downgrade without index query can
1425
        //    improve query performance.
1426
        // 3. INVERTED_INDEX_EVALUATE_SKIPPED means the inverted index is not
1427
        //    suitable for executing this predicate, skipped it and filter data
1428
        //    by function later.
1429
        // 4. INVERTED_INDEX_NO_TERMS means the column has fulltext index,
1430
        //    but the column condition value no terms in specified parser,
1431
        //    such as: where A = '' and B = ','
1432
        //    the predicate of A and B need downgrade without index query.
1433
        // 5. INVERTED_INDEX_FILE_CORRUPTED means the index file is corrupted,
1434
        //    such as when index segment files are not generated
1435
        // above case can downgrade without index query
1436
0
        _opts.stats->inverted_index_downgrade_count++;
1437
0
        if (!res.is<ErrorCode::INVERTED_INDEX_BYPASS>()) {
1438
0
            LOG(INFO) << "will downgrade without index to evaluate predicate, because of res: "
1439
0
                      << res;
1440
0
        } else {
1441
0
            VLOG_DEBUG << "will downgrade without index to evaluate predicate, because of res: "
1442
0
                       << res;
1443
0
        }
1444
0
        return true;
1445
0
    }
1446
0
    return false;
1447
0
}
1448
1449
0
bool SegmentIterator::_column_has_fulltext_index(int32_t cid) {
1450
0
    bool has_fulltext_index =
1451
0
            _index_iterators[cid] != nullptr &&
1452
0
            _index_iterators[cid]->get_reader(InvertedIndexReaderType::FULLTEXT) &&
1453
0
            _index_iterators[cid]->get_reader(InvertedIndexReaderType::STRING_TYPE) == nullptr;
1454
1455
0
    return has_fulltext_index;
1456
0
}
1457
1458
0
inline bool SegmentIterator::_inverted_index_not_support_pred_type(const PredicateType& type) {
1459
0
    return type == PredicateType::BF || type == PredicateType::BITMAP_FILTER;
1460
0
}
1461
1462
Status SegmentIterator::_apply_inverted_index_on_column_predicate(
1463
        std::shared_ptr<ColumnPredicate> pred,
1464
0
        std::vector<std::shared_ptr<ColumnPredicate>>& remaining_predicates, bool* continue_apply) {
1465
0
    if (!_check_apply_by_inverted_index(pred)) {
1466
0
        remaining_predicates.emplace_back(pred);
1467
0
    } else {
1468
0
        bool need_remaining_after_evaluate = _column_has_fulltext_index(pred->column_id()) &&
1469
0
                                             PredicateTypeTraits::is_equal_or_list(pred->type());
1470
0
        Status res =
1471
0
                pred->evaluate(_storage_name_and_type[pred->column_id()],
1472
0
                               _index_iterators[pred->column_id()].get(), num_rows(), &_row_bitmap);
1473
0
        if (!res.ok()) {
1474
0
            if (_downgrade_without_index(res, need_remaining_after_evaluate)) {
1475
0
                remaining_predicates.emplace_back(pred);
1476
0
                return Status::OK();
1477
0
            }
1478
0
            LOG(WARNING) << "failed to evaluate index"
1479
0
                         << ", column predicate type: " << pred->pred_type_string(pred->type())
1480
0
                         << ", error msg: " << res;
1481
0
            return res;
1482
0
        }
1483
1484
0
        if (_row_bitmap.isEmpty()) {
1485
            // all rows have been pruned, no need to process further predicates
1486
0
            *continue_apply = false;
1487
0
        }
1488
1489
0
        if (need_remaining_after_evaluate) {
1490
0
            remaining_predicates.emplace_back(pred);
1491
0
            return Status::OK();
1492
0
        }
1493
0
        if (!pred->is_runtime_filter()) {
1494
0
            _column_predicate_index_exec_status[pred->column_id()][pred] = true;
1495
0
        }
1496
0
    }
1497
0
    return Status::OK();
1498
0
}
1499
1500
27.1k
bool SegmentIterator::_need_read_data(ColumnId cid) {
1501
27.1k
    if (_opts.runtime_state && !_opts.runtime_state->query_options().enable_no_need_read_data_opt) {
1502
0
        return true;
1503
0
    }
1504
    // only support DUP_KEYS and UNIQUE_KEYS with MOW
1505
27.1k
    if (!((_opts.tablet_schema->keys_type() == KeysType::DUP_KEYS ||
1506
27.1k
           (_opts.tablet_schema->keys_type() == KeysType::UNIQUE_KEYS &&
1507
11.1k
            _opts.enable_unique_key_merge_on_write)))) {
1508
7.58k
        return true;
1509
7.58k
    }
1510
    // this is a virtual column, we always need to read data
1511
19.5k
    if (this->_vir_cid_to_idx_in_block.contains(cid)) {
1512
0
        return true;
1513
0
    }
1514
1515
    // if there is a delete predicate, we always need to read data
1516
19.5k
    if (_has_delete_predicate(cid)) {
1517
1.74k
        return true;
1518
1.74k
    }
1519
17.8k
    if (_output_columns.count(-1)) {
1520
        // if _output_columns contains -1, it means that the light
1521
        // weight schema change may not be enabled or other reasons
1522
        // caused the column unique_id not be set, to prevent errors
1523
        // occurring, return true here that column data needs to be read
1524
0
        return true;
1525
0
    }
1526
    // Check the following conditions:
1527
    // 1. If the column represented by the unique ID is an inverted index column (indicated by '_need_read_data_indices.count(unique_id) > 0 && !_need_read_data_indices[unique_id]')
1528
    //    and it's not marked for projection in '_output_columns'.
1529
    // 2. Or, if the column is an inverted index column and it's marked for projection in '_output_columns',
1530
    //    and the operation is a push down of the 'COUNT_ON_INDEX' aggregation function.
1531
    // If any of the above conditions are met, log a debug message indicating that there's no need to read data for the indexed column.
1532
    // Then, return false.
1533
17.8k
    const auto& column = _opts.tablet_schema->column(cid);
1534
    // Different subcolumns may share the same parent_unique_id, so we choose to abandon this optimization.
1535
17.8k
    if (column.is_extracted_column() &&
1536
17.8k
        _opts.push_down_agg_type_opt != TPushAggOp::COUNT_ON_INDEX) {
1537
13
        return true;
1538
13
    }
1539
17.8k
    int32_t unique_id = column.unique_id();
1540
17.8k
    if (unique_id < 0) {
1541
1
        unique_id = column.parent_unique_id();
1542
1
    }
1543
17.8k
    if ((_need_read_data_indices.contains(cid) && !_need_read_data_indices[cid] &&
1544
17.8k
         !_output_columns.contains(unique_id)) ||
1545
17.8k
        (_need_read_data_indices.contains(cid) && !_need_read_data_indices[cid] &&
1546
17.8k
         _output_columns.count(unique_id) == 1 &&
1547
17.8k
         _opts.push_down_agg_type_opt == TPushAggOp::COUNT_ON_INDEX)) {
1548
1
        VLOG_DEBUG << "SegmentIterator no need read data for column: "
1549
0
                   << _opts.tablet_schema->column_by_uid(unique_id).name();
1550
1
        return false;
1551
1
    }
1552
17.8k
    return true;
1553
17.8k
}
1554
1555
0
Status SegmentIterator::_apply_inverted_index() {
1556
0
    std::vector<std::shared_ptr<ColumnPredicate>> remaining_predicates;
1557
0
    std::set<std::shared_ptr<ColumnPredicate>> no_need_to_pass_column_predicate_set;
1558
1559
0
    for (auto pred : _col_predicates) {
1560
0
        if (no_need_to_pass_column_predicate_set.count(pred) > 0) {
1561
0
            continue;
1562
0
        } else {
1563
0
            bool continue_apply = true;
1564
0
            RETURN_IF_ERROR(_apply_inverted_index_on_column_predicate(pred, remaining_predicates,
1565
0
                                                                      &continue_apply));
1566
0
            if (!continue_apply) {
1567
0
                break;
1568
0
            }
1569
0
        }
1570
0
    }
1571
1572
0
    _col_predicates = std::move(remaining_predicates);
1573
0
    return Status::OK();
1574
0
}
1575
1576
/**
1577
 * @brief Checks if all conditions related to a specific column have passed in both
1578
 * `_column_predicate_inverted_index_status` and `_common_expr_inverted_index_status`.
1579
 *
1580
 * This function first checks the conditions in `_column_predicate_inverted_index_status`
1581
 * for the given `ColumnId`. If all conditions pass, it sets `default_return` to `true`.
1582
 * It then checks the conditions in `_common_expr_inverted_index_status` for the same column.
1583
 *
1584
 * The function returns `true` if all conditions in both maps pass. If any condition fails
1585
 * in either map, the function immediately returns `false`. If the column does not exist
1586
 * in one of the maps, the function returns `default_return`.
1587
 *
1588
 * @param cid The ColumnId of the column to check.
1589
 * @param default_return The default value to return if the column is not found in the status maps.
1590
 * @return true if all conditions in both status maps pass, or if the column is not found
1591
 *         and `default_return` is true.
1592
 * @return false if any condition in either status map fails, or if the column is not found
1593
 *         and `default_return` is false.
1594
 */
1595
bool SegmentIterator::_check_all_conditions_passed_inverted_index_for_column(ColumnId cid,
1596
3
                                                                             bool default_return) {
1597
3
    auto pred_it = _column_predicate_index_exec_status.find(cid);
1598
3
    if (pred_it != _column_predicate_index_exec_status.end()) {
1599
2
        const auto& pred_map = pred_it->second;
1600
2
        bool pred_passed = std::all_of(pred_map.begin(), pred_map.end(),
1601
2
                                       [](const auto& pred_entry) { return pred_entry.second; });
1602
2
        if (!pred_passed) {
1603
1
            return false;
1604
1
        } else {
1605
1
            default_return = true;
1606
1
        }
1607
2
    }
1608
1609
2
    auto expr_it = _common_expr_index_exec_status.find(cid);
1610
2
    if (expr_it != _common_expr_index_exec_status.end()) {
1611
0
        const auto& expr_map = expr_it->second;
1612
0
        return std::all_of(expr_map.begin(), expr_map.end(),
1613
0
                           [](const auto& expr_entry) { return expr_entry.second; });
1614
0
    }
1615
2
    return default_return;
1616
2
}
1617
1618
2.82k
Status SegmentIterator::_init_return_column_iterators() {
1619
2.82k
    SCOPED_RAW_TIMER(&_opts.stats->segment_iterator_init_return_column_iterators_timer_ns);
1620
2.82k
    if (_cur_rowid >= num_rows()) {
1621
0
        return Status::OK();
1622
0
    }
1623
1624
7.05k
    for (auto cid : _schema->column_ids()) {
1625
7.05k
        if (_schema->column(cid)->name() == BeConsts::ROWID_COL) {
1626
0
            _column_iterators[cid].reset(
1627
0
                    new RowIdColumnIterator(_opts.tablet_id, _opts.rowset_id, _segment->id()));
1628
0
            continue;
1629
0
        }
1630
1631
7.05k
        if (_schema->column(cid)->name().starts_with(BeConsts::GLOBAL_ROWID_COL)) {
1632
0
            auto& id_file_map = _opts.runtime_state->get_id_file_map();
1633
0
            uint32_t file_id = id_file_map->get_file_mapping_id(std::make_shared<FileMapping>(
1634
0
                    _opts.tablet_id, _opts.rowset_id, _segment->id()));
1635
0
            _column_iterators[cid].reset(new RowIdColumnIteratorV2(
1636
0
                    IdManager::ID_VERSION, BackendOptions::get_backend_id(), file_id));
1637
0
            continue;
1638
0
        }
1639
1640
7.05k
        if (_schema->column(cid)->name().starts_with(BeConsts::VIRTUAL_COLUMN_PREFIX)) {
1641
0
            _column_iterators[cid] = std::make_unique<VirtualColumnIterator>();
1642
0
            continue;
1643
0
        }
1644
1645
7.05k
        std::set<ColumnId> del_cond_id_set;
1646
7.05k
        _opts.delete_condition_predicates->get_all_column_ids(del_cond_id_set);
1647
7.05k
        std::vector<bool> tmp_is_pred_column;
1648
7.05k
        tmp_is_pred_column.resize(_schema->columns().size(), false);
1649
7.05k
        for (auto predicate : _col_predicates) {
1650
0
            auto p_cid = predicate->column_id();
1651
0
            tmp_is_pred_column[p_cid] = true;
1652
0
        }
1653
        // handle delete_condition
1654
7.05k
        for (auto d_cid : del_cond_id_set) {
1655
1.32k
            tmp_is_pred_column[d_cid] = true;
1656
1.32k
        }
1657
1658
7.05k
        if (_column_iterators[cid] == nullptr) {
1659
7.05k
            RETURN_IF_ERROR(_segment->new_column_iterator(_opts.tablet_schema->column(cid),
1660
7.05k
                                                          &_column_iterators[cid], &_opts,
1661
7.05k
                                                          &_variant_sparse_column_cache));
1662
7.05k
            ColumnIteratorOptions iter_opts {
1663
7.05k
                    .use_page_cache = _opts.use_page_cache,
1664
                    // If the col is predicate column, then should read the last page to check
1665
                    // if the column is full dict encoding
1666
7.05k
                    .is_predicate_column = tmp_is_pred_column[cid],
1667
7.05k
                    .file_reader = _file_reader.get(),
1668
7.05k
                    .stats = _opts.stats,
1669
7.05k
                    .io_ctx = _opts.io_ctx,
1670
7.05k
            };
1671
7.05k
            RETURN_IF_ERROR(_column_iterators[cid]->init(iter_opts));
1672
7.05k
        }
1673
7.05k
    }
1674
1675
2.82k
#ifndef NDEBUG
1676
2.82k
    for (auto pair : _vir_cid_to_idx_in_block) {
1677
0
        ColumnId vir_col_cid = pair.first;
1678
0
        DCHECK(_column_iterators[vir_col_cid] != nullptr)
1679
0
                << "Virtual column iterator for " << vir_col_cid << " should not be null";
1680
0
        ColumnIterator* column_iter = _column_iterators[vir_col_cid].get();
1681
0
        DCHECK(dynamic_cast<VirtualColumnIterator*>(column_iter) != nullptr)
1682
0
                << "Virtual column iterator for " << vir_col_cid
1683
0
                << " should be VirtualColumnIterator";
1684
0
    }
1685
2.82k
#endif
1686
2.82k
    return Status::OK();
1687
2.82k
}
1688
1689
2.82k
Status SegmentIterator::_init_index_iterators() {
1690
2.82k
    SCOPED_RAW_TIMER(&_opts.stats->segment_iterator_init_index_iterators_timer_ns);
1691
2.82k
    if (_cur_rowid >= num_rows()) {
1692
0
        return Status::OK();
1693
0
    }
1694
1695
2.82k
    _index_query_context = std::make_shared<IndexQueryContext>();
1696
2.82k
    _index_query_context->io_ctx = &_opts.io_ctx;
1697
2.82k
    _index_query_context->stats = _opts.stats;
1698
2.82k
    _index_query_context->runtime_state = _opts.runtime_state;
1699
1700
2.82k
    if (_score_runtime) {
1701
0
        _index_query_context->collection_statistics = _opts.collection_statistics;
1702
0
        _index_query_context->collection_similarity = std::make_shared<CollectionSimilarity>();
1703
0
        _index_query_context->query_limit = _score_runtime->get_limit();
1704
0
        _index_query_context->is_asc = _score_runtime->is_asc();
1705
0
    }
1706
1707
    // Inverted index iterators
1708
7.05k
    for (auto cid : _schema->column_ids()) {
1709
        // Use segment’s own index_meta, for compatibility with future indexing needs to default to lowercase.
1710
7.05k
        if (_index_iterators[cid] == nullptr) {
1711
            // In the _opts.tablet_schema, the sub-column type information for the variant is FieldType::OLAP_FIELD_TYPE_VARIANT.
1712
            // This is because the sub-column is created in create_materialized_variant_column.
1713
            // We use this column to locate the metadata for the inverted index, which requires a unique_id and path.
1714
7.05k
            const auto& column = _opts.tablet_schema->column(cid);
1715
7.05k
            std::vector<const TabletIndex*> inverted_indexs;
1716
            // Keep shared_ptr alive to prevent use-after-free when accessing raw pointers
1717
7.05k
            TabletIndexes inverted_indexs_holder;
1718
            // If the column is an extracted column, we need to find the sub-column in the parent column reader.
1719
7.05k
            std::shared_ptr<ColumnReader> column_reader;
1720
7.05k
            if (column.is_extracted_column()) {
1721
6
                if (!_segment->_column_reader_cache->get_column_reader(
1722
6
                            column.parent_unique_id(), &column_reader, _opts.stats) ||
1723
6
                    column_reader == nullptr) {
1724
0
                    continue;
1725
0
                }
1726
6
                auto* variant_reader = assert_cast<VariantColumnReader*>(column_reader.get());
1727
6
                DataTypePtr data_type = _storage_name_and_type[cid].second;
1728
6
                if (data_type != nullptr &&
1729
6
                    data_type->get_primitive_type() == PrimitiveType::TYPE_VARIANT) {
1730
0
                    DataTypePtr inferred_type;
1731
0
                    Status st = variant_reader->infer_data_type_for_path(
1732
0
                            &inferred_type, column, _opts, _segment->_column_reader_cache.get());
1733
0
                    if (st.ok() && inferred_type != nullptr) {
1734
0
                        data_type = inferred_type;
1735
0
                    }
1736
0
                }
1737
6
                inverted_indexs_holder =
1738
6
                        variant_reader->find_subcolumn_tablet_indexes(column, data_type);
1739
                // Extract raw pointers from shared_ptr for iteration
1740
6
                for (const auto& index_ptr : inverted_indexs_holder) {
1741
0
                    inverted_indexs.push_back(index_ptr.get());
1742
0
                }
1743
6
            }
1744
            // If the column is not an extracted column, we can directly get the inverted index metadata from the tablet schema.
1745
7.05k
            else {
1746
7.05k
                inverted_indexs = _segment->_tablet_schema->inverted_indexs(column);
1747
7.05k
            }
1748
7.05k
            for (const auto& inverted_index : inverted_indexs) {
1749
2.56k
                RETURN_IF_ERROR(_segment->new_index_iterator(column, inverted_index, _opts,
1750
2.56k
                                                             &_index_iterators[cid]));
1751
2.56k
            }
1752
7.05k
            if (_index_iterators[cid] != nullptr) {
1753
2.55k
                _index_iterators[cid]->set_context(_index_query_context);
1754
2.55k
            }
1755
7.05k
        }
1756
7.05k
    }
1757
1758
    // Ann index iterators
1759
7.05k
    for (auto cid : _schema->column_ids()) {
1760
7.05k
        if (_index_iterators[cid] == nullptr) {
1761
4.50k
            const auto& column = _opts.tablet_schema->column(cid);
1762
4.50k
            const auto* index_meta = _segment->_tablet_schema->ann_index(column);
1763
4.50k
            if (index_meta) {
1764
1
                RETURN_IF_ERROR(_segment->new_index_iterator(column, index_meta, _opts,
1765
1
                                                             &_index_iterators[cid]));
1766
1767
1
                if (_index_iterators[cid] != nullptr) {
1768
1
                    _index_iterators[cid]->set_context(_index_query_context);
1769
1
                }
1770
1
            }
1771
4.50k
        }
1772
7.05k
    }
1773
1774
2.82k
    return Status::OK();
1775
2.82k
}
1776
1777
Status SegmentIterator::_lookup_ordinal(const RowCursor& key, bool is_include, rowid_t upper_bound,
1778
0
                                        rowid_t* rowid) {
1779
0
    if (_segment->_tablet_schema->keys_type() == UNIQUE_KEYS &&
1780
0
        _segment->get_primary_key_index() != nullptr) {
1781
0
        return _lookup_ordinal_from_pk_index(key, is_include, rowid);
1782
0
    }
1783
0
    return _lookup_ordinal_from_sk_index(key, is_include, upper_bound, rowid);
1784
0
}
1785
1786
// look up one key to get its ordinal at which can get data by using short key index.
1787
// 'upper_bound' is defined the max ordinal the function will search.
1788
// We use upper_bound to reduce search times.
1789
// If we find a valid ordinal, it will be set in rowid and with Status::OK()
1790
// If we can not find a valid key in this segment, we will set rowid to upper_bound
1791
// Otherwise return error.
1792
// 1. get [start, end) ordinal through short key index
1793
// 2. binary search to find exact ordinal that match the input condition
1794
// Make is_include template to reduce branch
1795
Status SegmentIterator::_lookup_ordinal_from_sk_index(const RowCursor& key, bool is_include,
1796
0
                                                      rowid_t upper_bound, rowid_t* rowid) {
1797
0
    const ShortKeyIndexDecoder* sk_index_decoder = _segment->get_short_key_index();
1798
0
    DCHECK(sk_index_decoder != nullptr);
1799
1800
0
    std::string index_key;
1801
0
    key.encode_key_with_padding(&index_key, _segment->_tablet_schema->num_short_key_columns(),
1802
0
                                is_include);
1803
1804
0
    const auto& key_col_ids = key.schema()->column_ids();
1805
1806
    // Clone the key once and pad CHAR fields to storage format before the binary search.
1807
    // _seek_block holds storage-format data where CHAR is zero-padded to column length,
1808
    // while RowCursor holds CHAR in compute format (unpadded). Padding once here avoids
1809
    // repeated allocation inside the comparison loop.
1810
0
    RowCursor padded_key = key.clone();
1811
0
    padded_key.pad_char_fields();
1812
1813
0
    ssize_t start_block_id = 0;
1814
0
    auto start_iter = sk_index_decoder->lower_bound(index_key);
1815
0
    if (start_iter.valid()) {
1816
        // Because previous block may contain this key, so we should set rowid to
1817
        // last block's first row.
1818
0
        start_block_id = start_iter.ordinal();
1819
0
        if (start_block_id > 0) {
1820
0
            start_block_id--;
1821
0
        }
1822
0
    } else {
1823
        // When we don't find a valid index item, which means all short key is
1824
        // smaller than input key, this means that this key may exist in the last
1825
        // row block. so we set the rowid to first row of last row block.
1826
0
        start_block_id = sk_index_decoder->num_items() - 1;
1827
0
    }
1828
0
    rowid_t start = cast_set<rowid_t>(start_block_id) * sk_index_decoder->num_rows_per_block();
1829
1830
0
    rowid_t end = upper_bound;
1831
0
    auto end_iter = sk_index_decoder->upper_bound(index_key);
1832
0
    if (end_iter.valid()) {
1833
0
        end = cast_set<rowid_t>(end_iter.ordinal()) * sk_index_decoder->num_rows_per_block();
1834
0
    }
1835
1836
    // binary search to find the exact key
1837
0
    while (start < end) {
1838
0
        rowid_t mid = (start + end) / 2;
1839
0
        RETURN_IF_ERROR(_seek_and_peek(mid));
1840
0
        int cmp = _compare_short_key_with_seek_block(padded_key, key_col_ids);
1841
0
        if (cmp > 0) {
1842
0
            start = mid + 1;
1843
0
        } else if (cmp == 0) {
1844
0
            if (is_include) {
1845
                // lower bound
1846
0
                end = mid;
1847
0
            } else {
1848
                // upper bound
1849
0
                start = mid + 1;
1850
0
            }
1851
0
        } else {
1852
0
            end = mid;
1853
0
        }
1854
0
    }
1855
1856
0
    *rowid = start;
1857
0
    return Status::OK();
1858
0
}
1859
1860
Status SegmentIterator::_lookup_ordinal_from_pk_index(const RowCursor& key, bool is_include,
1861
0
                                                      rowid_t* rowid) {
1862
0
    DCHECK(_segment->_tablet_schema->keys_type() == UNIQUE_KEYS);
1863
0
    const PrimaryKeyIndexReader* pk_index_reader = _segment->get_primary_key_index();
1864
0
    DCHECK(pk_index_reader != nullptr);
1865
1866
0
    std::string index_key;
1867
0
    key.encode_key_with_padding<true>(&index_key, _segment->_tablet_schema->num_key_columns(),
1868
0
                                      is_include);
1869
0
    if (index_key < _segment->min_key()) {
1870
0
        *rowid = 0;
1871
0
        return Status::OK();
1872
0
    } else if (index_key > _segment->max_key()) {
1873
0
        *rowid = num_rows();
1874
0
        return Status::OK();
1875
0
    }
1876
0
    bool exact_match = false;
1877
1878
0
    std::unique_ptr<segment_v2::IndexedColumnIterator> index_iterator;
1879
0
    RETURN_IF_ERROR(pk_index_reader->new_iterator(&index_iterator, _opts.stats));
1880
1881
0
    Status status = index_iterator->seek_at_or_after(&index_key, &exact_match);
1882
0
    if (UNLIKELY(!status.ok())) {
1883
0
        *rowid = num_rows();
1884
0
        if (status.is<ENTRY_NOT_FOUND>()) {
1885
0
            return Status::OK();
1886
0
        }
1887
0
        return status;
1888
0
    }
1889
0
    *rowid = cast_set<rowid_t>(index_iterator->get_current_ordinal());
1890
1891
    // The sequence column needs to be removed from primary key index when comparing key
1892
0
    bool has_seq_col = _segment->_tablet_schema->has_sequence_col();
1893
    // Used to get key range from primary key index,
1894
    // for mow with cluster key table, we should get key range from short key index.
1895
0
    DCHECK(_segment->_tablet_schema->cluster_key_uids().empty());
1896
1897
    // if full key is exact_match, the primary key without sequence column should also the same
1898
0
    if (has_seq_col && !exact_match) {
1899
0
        size_t seq_col_length =
1900
0
                _segment->_tablet_schema->column(_segment->_tablet_schema->sequence_col_idx())
1901
0
                        .length() +
1902
0
                1;
1903
0
        auto index_type = DataTypeFactory::instance().create_data_type(
1904
0
                _segment->_pk_index_reader->type(), 1, 0);
1905
0
        auto index_column = index_type->create_column();
1906
0
        size_t num_to_read = 1;
1907
0
        size_t num_read = num_to_read;
1908
0
        RETURN_IF_ERROR(index_iterator->next_batch(&num_read, index_column));
1909
0
        DCHECK(num_to_read == num_read);
1910
1911
0
        Slice sought_key =
1912
0
                Slice(index_column->get_data_at(0).data, index_column->get_data_at(0).size);
1913
0
        Slice sought_key_without_seq =
1914
0
                Slice(sought_key.get_data(), sought_key.get_size() - seq_col_length);
1915
1916
        // compare key
1917
0
        if (Slice(index_key).compare(sought_key_without_seq) == 0) {
1918
0
            exact_match = true;
1919
0
        }
1920
0
    }
1921
1922
    // find the key in primary key index, and the is_include is false, so move
1923
    // to the next row.
1924
0
    if (exact_match && !is_include) {
1925
0
        *rowid += 1;
1926
0
    }
1927
0
    return Status::OK();
1928
0
}
1929
1930
// seek to the row and load that row to _key_cursor
1931
0
Status SegmentIterator::_seek_and_peek(rowid_t rowid) {
1932
0
    {
1933
0
        _opts.stats->block_init_seek_num += 1;
1934
0
        SCOPED_RAW_TIMER(&_opts.stats->block_init_seek_ns);
1935
0
        RETURN_IF_ERROR(_seek_columns(_seek_schema->column_ids(), rowid));
1936
0
    }
1937
0
    size_t num_rows = 1;
1938
1939
    //note(wb) reset _seek_block for memory reuse
1940
    // it is easier to use row based memory layout for clear memory
1941
0
    for (int i = 0; i < _seek_block.size(); i++) {
1942
0
        _seek_block[i]->clear();
1943
0
    }
1944
0
    RETURN_IF_ERROR(_read_columns(_seek_schema->column_ids(), _seek_block, num_rows));
1945
0
    return Status::OK();
1946
0
}
1947
1948
0
Status SegmentIterator::_seek_columns(const std::vector<ColumnId>& column_ids, rowid_t pos) {
1949
0
    for (auto cid : column_ids) {
1950
0
        if (!_need_read_data(cid)) {
1951
0
            continue;
1952
0
        }
1953
0
        RETURN_IF_ERROR(_column_iterators[cid]->seek_to_ordinal(pos));
1954
0
    }
1955
0
    return Status::OK();
1956
0
}
1957
1958
/* ---------------------- for vectorization implementation  ---------------------- */
1959
1960
/**
1961
 *  For storage layer data type, can be measured from two perspectives:
1962
 *  1 Whether the type can be read in a fast way(batch read using SIMD)
1963
 *    Such as integer type and float type, this type can be read in SIMD way.
1964
 *    For the type string/bitmap/hll, they can not be read in batch way, so read this type data is slow.
1965
 *   If a type can be read fast, we can try to eliminate Lazy Materialization, because we think for this type, seek cost > read cost.
1966
 *   This is an estimate, if we want more precise cost, statistics collection is necessary(this is a todo).
1967
 *   In short, when returned non-pred columns contains string/hll/bitmap, we using Lazy Materialization.
1968
 *   Otherwise, we disable it.
1969
 *
1970
 *   When Lazy Materialization enable, we need to read column at least two times.
1971
 *   First time to read Pred col, second time to read non-pred.
1972
 *   Here's an interesting question to research, whether read Pred col once is the best plan.
1973
 *   (why not read Pred col twice or more?)
1974
 *
1975
 *   When Lazy Materialization disable, we just need to read once.
1976
 *
1977
 *
1978
 *  2 Whether the predicate type can be evaluate in a fast way(using SIMD to eval pred)
1979
 *    Such as integer type and float type, they can be eval fast.
1980
 *    But for BloomFilter/string/date, they eval slow.
1981
 *    If a type can be eval fast, we use vectorization to eval it.
1982
 *    Otherwise, we use short-circuit to eval it.
1983
 *
1984
 *
1985
 */
1986
1987
// todo(wb) need a UT here
1988
2.82k
Status SegmentIterator::_vec_init_lazy_materialization() {
1989
2.82k
    _is_pred_column.resize(_schema->columns().size(), false);
1990
1991
    // including short/vec/delete pred
1992
2.82k
    std::set<ColumnId> pred_column_ids;
1993
2.82k
    _lazy_materialization_read = false;
1994
1995
2.82k
    std::set<ColumnId> del_cond_id_set;
1996
2.82k
    _opts.delete_condition_predicates->get_all_column_ids(del_cond_id_set);
1997
1998
2.82k
    std::set<std::shared_ptr<const ColumnPredicate>> delete_predicate_set {};
1999
2.82k
    _opts.delete_condition_predicates->get_all_column_predicate(delete_predicate_set);
2000
2.82k
    for (auto predicate : delete_predicate_set) {
2001
467
        if (PredicateTypeTraits::is_range(predicate->type())) {
2002
327
            _delete_range_column_ids.push_back(predicate->column_id());
2003
327
        } else if (PredicateTypeTraits::is_bloom_filter(predicate->type())) {
2004
0
            _delete_bloom_filter_column_ids.push_back(predicate->column_id());
2005
0
        }
2006
467
    }
2007
2008
    // Step1: extract columns that can be lazy materialization
2009
2.82k
    if (!_col_predicates.empty() || !del_cond_id_set.empty()) {
2010
467
        std::set<ColumnId> short_cir_pred_col_id_set; // using set for distinct cid
2011
467
        std::set<ColumnId> vec_pred_col_id_set;
2012
2013
467
        for (auto predicate : _col_predicates) {
2014
0
            auto cid = predicate->column_id();
2015
0
            _is_pred_column[cid] = true;
2016
0
            pred_column_ids.insert(cid);
2017
2018
            // check pred using short eval or vec eval
2019
0
            if (_can_evaluated_by_vectorized(predicate)) {
2020
0
                vec_pred_col_id_set.insert(cid);
2021
0
                _pre_eval_block_predicate.push_back(predicate);
2022
0
            } else {
2023
0
                short_cir_pred_col_id_set.insert(cid);
2024
0
                _short_cir_eval_predicate.push_back(predicate);
2025
0
            }
2026
0
            if (predicate->is_runtime_filter()) {
2027
0
                _filter_info_id.push_back(predicate);
2028
0
            }
2029
0
        }
2030
2031
        // handle delete_condition
2032
467
        if (!del_cond_id_set.empty()) {
2033
467
            short_cir_pred_col_id_set.insert(del_cond_id_set.begin(), del_cond_id_set.end());
2034
467
            pred_column_ids.insert(del_cond_id_set.begin(), del_cond_id_set.end());
2035
2036
467
            for (auto cid : del_cond_id_set) {
2037
467
                _is_pred_column[cid] = true;
2038
467
            }
2039
467
        }
2040
2041
467
        _vec_pred_column_ids.assign(vec_pred_col_id_set.cbegin(), vec_pred_col_id_set.cend());
2042
467
        _short_cir_pred_column_ids.assign(short_cir_pred_col_id_set.cbegin(),
2043
467
                                          short_cir_pred_col_id_set.cend());
2044
467
    }
2045
2046
2.82k
    if (!_vec_pred_column_ids.empty()) {
2047
0
        _is_need_vec_eval = true;
2048
0
    }
2049
2.82k
    if (!_short_cir_pred_column_ids.empty()) {
2050
467
        _is_need_short_eval = true;
2051
467
    }
2052
2053
    // ColumnId to column index in block
2054
    // ColumnId will contail all columns in tablet schema, including virtual columns and global rowid column,
2055
2.82k
    _schema_block_id_map.resize(_schema->columns().size(), -1);
2056
    // Use cols read by query to initialize _schema_block_id_map.
2057
    // We need to know the index of each column in the block.
2058
    // There is an assumption here that the columns in the block are in the same order as in the read schema.
2059
    // TODO: A probelm is that, delete condition columns will exist in _schema->column_ids but not in block if
2060
    // delete column is not read by the query.
2061
9.87k
    for (int i = 0; i < _schema->num_column_ids(); i++) {
2062
7.05k
        auto cid = _schema->column_id(i);
2063
7.05k
        _schema_block_id_map[cid] = i;
2064
7.05k
    }
2065
2066
    // Step2: extract columns that can execute expr context
2067
2.82k
    _is_common_expr_column.resize(_schema->columns().size(), false);
2068
2.82k
    if (_enable_common_expr_pushdown && !_remaining_conjunct_roots.empty()) {
2069
0
        for (auto expr : _remaining_conjunct_roots) {
2070
0
            RETURN_IF_ERROR(_extract_common_expr_columns(expr));
2071
0
        }
2072
0
        if (!_common_expr_columns.empty()) {
2073
0
            _is_need_expr_eval = true;
2074
0
            for (auto cid : _schema->column_ids()) {
2075
                // pred column also needs to be filtered by expr, exclude additional delete condition column.
2076
                // if delete condition column not in the block, no filter is needed
2077
                // and will be removed from _columns_to_filter in the first next_batch.
2078
0
                if (_is_common_expr_column[cid] || _is_pred_column[cid]) {
2079
0
                    auto loc = _schema_block_id_map[cid];
2080
0
                    _columns_to_filter.push_back(loc);
2081
0
                }
2082
0
            }
2083
2084
0
            for (auto pair : _vir_cid_to_idx_in_block) {
2085
0
                _columns_to_filter.push_back(cast_set<ColumnId>(pair.second));
2086
0
            }
2087
0
        }
2088
0
    }
2089
2090
    // Step 3: fill non predicate columns and second read column
2091
    // if _schema columns size equal to pred_column_ids size, lazy_materialization_read is false,
2092
    // all columns are lazy materialization columns without non predicte column.
2093
    // If common expr pushdown exists, and expr column is not contained in lazy materialization columns,
2094
    // add to second read column, which will be read after lazy materialization
2095
2.82k
    if (_schema->column_ids().size() > pred_column_ids.size()) {
2096
        // pred_column_ids maybe empty, so that could not set _lazy_materialization_read = true here
2097
        // has to check there is at least one predicate column
2098
6.99k
        for (auto cid : _schema->column_ids()) {
2099
6.99k
            if (!_is_pred_column[cid]) {
2100
6.58k
                if (_is_need_vec_eval || _is_need_short_eval) {
2101
862
                    _lazy_materialization_read = true;
2102
862
                }
2103
6.58k
                if (_is_common_expr_column[cid]) {
2104
0
                    _common_expr_column_ids.push_back(cid);
2105
6.58k
                } else {
2106
6.58k
                    _non_predicate_columns.push_back(cid);
2107
6.58k
                }
2108
6.58k
            }
2109
6.99k
        }
2110
2.76k
    }
2111
2112
    // Step 4: fill first read columns
2113
2.82k
    if (_lazy_materialization_read) {
2114
        // insert pred cid to first_read_columns
2115
410
        for (auto cid : pred_column_ids) {
2116
410
            _predicate_column_ids.push_back(cid);
2117
410
        }
2118
2.41k
    } else if (!_is_need_vec_eval && !_is_need_short_eval && !_is_need_expr_eval) {
2119
8.08k
        for (int i = 0; i < _schema->num_column_ids(); i++) {
2120
5.72k
            auto cid = _schema->column_id(i);
2121
5.72k
            _predicate_column_ids.push_back(cid);
2122
5.72k
        }
2123
2.35k
    } else {
2124
57
        if (_is_need_vec_eval || _is_need_short_eval) {
2125
            // TODO To refactor, because we suppose lazy materialization is better performance.
2126
            // pred exits, but we can eliminate lazy materialization
2127
            // insert pred/non-pred cid to first read columns
2128
57
            std::set<ColumnId> pred_id_set;
2129
57
            pred_id_set.insert(_short_cir_pred_column_ids.begin(),
2130
57
                               _short_cir_pred_column_ids.end());
2131
57
            pred_id_set.insert(_vec_pred_column_ids.begin(), _vec_pred_column_ids.end());
2132
2133
57
            DCHECK(_common_expr_column_ids.empty());
2134
            // _non_predicate_column_ids must be empty. Otherwise _lazy_materialization_read must not false.
2135
114
            for (int i = 0; i < _schema->num_column_ids(); i++) {
2136
57
                auto cid = _schema->column_id(i);
2137
57
                if (pred_id_set.find(cid) != pred_id_set.end()) {
2138
57
                    _predicate_column_ids.push_back(cid);
2139
57
                }
2140
57
            }
2141
57
        } else if (_is_need_expr_eval) {
2142
0
            DCHECK(!_is_need_vec_eval && !_is_need_short_eval);
2143
0
            for (auto cid : _common_expr_columns) {
2144
0
                _predicate_column_ids.push_back(cid);
2145
0
            }
2146
0
        }
2147
57
    }
2148
2149
2.82k
    VLOG_DEBUG << fmt::format(
2150
0
            "Laze materialization init end. "
2151
0
            "lazy_materialization_read: {}, "
2152
0
            "_col_predicates size: {}, "
2153
0
            "_cols_read_by_column_predicate: [{}], "
2154
0
            "_non_predicate_columns: [{}], "
2155
0
            "_cols_read_by_common_expr: [{}], "
2156
0
            "columns_to_filter: [{}], "
2157
0
            "_schema_block_id_map: [{}]",
2158
0
            _lazy_materialization_read, _col_predicates.size(),
2159
0
            fmt::join(_predicate_column_ids, ","), fmt::join(_non_predicate_columns, ","),
2160
0
            fmt::join(_common_expr_column_ids, ","), fmt::join(_columns_to_filter, ","),
2161
0
            fmt::join(_schema_block_id_map, ","));
2162
2.82k
    return Status::OK();
2163
2.82k
}
2164
2165
0
bool SegmentIterator::_can_evaluated_by_vectorized(std::shared_ptr<ColumnPredicate> predicate) {
2166
0
    auto cid = predicate->column_id();
2167
0
    FieldType field_type = _schema->column(cid)->type();
2168
0
    if (field_type == FieldType::OLAP_FIELD_TYPE_VARIANT) {
2169
        // Use variant cast dst type
2170
0
        field_type = _opts.target_cast_type_for_variants[_schema->column(cid)->name()]
2171
0
                             ->get_storage_field_type();
2172
0
    }
2173
0
    switch (predicate->type()) {
2174
0
    case PredicateType::EQ:
2175
0
    case PredicateType::NE:
2176
0
    case PredicateType::LE:
2177
0
    case PredicateType::LT:
2178
0
    case PredicateType::GE:
2179
0
    case PredicateType::GT: {
2180
0
        if (field_type == FieldType::OLAP_FIELD_TYPE_VARCHAR ||
2181
0
            field_type == FieldType::OLAP_FIELD_TYPE_CHAR ||
2182
0
            field_type == FieldType::OLAP_FIELD_TYPE_STRING) {
2183
0
            return config::enable_low_cardinality_optimize &&
2184
0
                   _opts.io_ctx.reader_type == ReaderType::READER_QUERY &&
2185
0
                   _column_iterators[cid]->is_all_dict_encoding();
2186
0
        } else if (field_type == FieldType::OLAP_FIELD_TYPE_DECIMAL) {
2187
0
            return false;
2188
0
        }
2189
0
        return true;
2190
0
    }
2191
0
    default:
2192
0
        return false;
2193
0
    }
2194
0
}
2195
2196
7.06k
bool SegmentIterator::_has_char_type(const StorageField& column_desc) {
2197
7.06k
    switch (column_desc.type()) {
2198
0
    case FieldType::OLAP_FIELD_TYPE_CHAR:
2199
0
        return true;
2200
2
    case FieldType::OLAP_FIELD_TYPE_ARRAY:
2201
2
        return _has_char_type(*column_desc.get_sub_field(0));
2202
2
    case FieldType::OLAP_FIELD_TYPE_MAP:
2203
2
        return _has_char_type(*column_desc.get_sub_field(0)) ||
2204
2
               _has_char_type(*column_desc.get_sub_field(1));
2205
0
    case FieldType::OLAP_FIELD_TYPE_STRUCT:
2206
0
        for (int idx = 0; idx < column_desc.get_sub_field_count(); ++idx) {
2207
0
            if (_has_char_type(*column_desc.get_sub_field(idx))) {
2208
0
                return true;
2209
0
            }
2210
0
        }
2211
0
        return false;
2212
7.05k
    default:
2213
7.05k
        return false;
2214
7.06k
    }
2215
7.06k
};
2216
2217
2.82k
void SegmentIterator::_vec_init_char_column_id(Block* block) {
2218
2.82k
    if (!_char_type_idx.empty()) {
2219
0
        return;
2220
0
    }
2221
2.82k
    _is_char_type.resize(_schema->columns().size(), false);
2222
9.87k
    for (size_t i = 0; i < _schema->num_column_ids(); i++) {
2223
7.05k
        auto cid = _schema->column_id(i);
2224
7.05k
        const StorageField* column_desc = _schema->column(cid);
2225
2226
        // The additional deleted filter condition will be in the materialized column at the end of the block.
2227
        // After _output_column_by_sel_idx, it will be erased, so we do not need to shrink it.
2228
7.05k
        if (i < block->columns()) {
2229
7.05k
            if (_has_char_type(*column_desc)) {
2230
0
                _char_type_idx.emplace_back(i);
2231
0
            }
2232
7.05k
        }
2233
2234
7.05k
        if (column_desc->type() == FieldType::OLAP_FIELD_TYPE_CHAR) {
2235
0
            _is_char_type[cid] = true;
2236
0
        }
2237
7.05k
    }
2238
2.82k
}
2239
2240
bool SegmentIterator::_prune_column(ColumnId cid, MutableColumnPtr& column, bool fill_defaults,
2241
27.1k
                                    size_t num_of_defaults) {
2242
27.1k
    if (_need_read_data(cid)) {
2243
27.1k
        return false;
2244
27.1k
    }
2245
0
    if (!fill_defaults) {
2246
0
        return true;
2247
0
    }
2248
0
    if (column->is_nullable()) {
2249
0
        auto nullable_col_ptr = reinterpret_cast<ColumnNullable*>(column.get());
2250
0
        nullable_col_ptr->get_null_map_column().insert_many_defaults(num_of_defaults);
2251
0
        nullable_col_ptr->get_nested_column_ptr()->insert_many_defaults(num_of_defaults);
2252
0
    } else {
2253
        // assert(column->is_const());
2254
0
        column->insert_many_defaults(num_of_defaults);
2255
0
    }
2256
0
    return true;
2257
0
}
2258
2259
Status SegmentIterator::_read_columns(const std::vector<ColumnId>& column_ids,
2260
0
                                      MutableColumns& column_block, size_t nrows) {
2261
0
    for (auto cid : column_ids) {
2262
0
        auto& column = column_block[cid];
2263
0
        size_t rows_read = nrows;
2264
0
        if (_prune_column(cid, column, true, rows_read)) {
2265
0
            continue;
2266
0
        }
2267
0
        RETURN_IF_ERROR(_column_iterators[cid]->next_batch(&rows_read, column));
2268
0
        if (nrows != rows_read) {
2269
0
            return Status::Error<ErrorCode::INTERNAL_ERROR>("nrows({}) != rows_read({})", nrows,
2270
0
                                                            rows_read);
2271
0
        }
2272
0
    }
2273
0
    return Status::OK();
2274
0
}
2275
2276
Status SegmentIterator::_init_current_block(Block* block,
2277
                                            std::vector<MutableColumnPtr>& current_columns,
2278
12.9k
                                            uint32_t nrows_read_limit) {
2279
12.9k
    block->clear_column_data(_schema->num_column_ids());
2280
2281
40.9k
    for (size_t i = 0; i < _schema->num_column_ids(); i++) {
2282
28.0k
        auto cid = _schema->column_id(i);
2283
28.0k
        const auto* column_desc = _schema->column(cid);
2284
2285
28.0k
        auto file_column_type = _storage_name_and_type[cid].second;
2286
28.0k
        auto expected_type = Schema::get_data_type_ptr(*column_desc);
2287
28.0k
        if (!_is_pred_column[cid] && !file_column_type->equals(*expected_type)) {
2288
            // The storage layer type is different from schema needed type, so we use storage
2289
            // type to read columns instead of schema type for safety
2290
0
            VLOG_DEBUG << fmt::format(
2291
0
                    "Recreate column with expected type {}, file column type {}, col_name {}, "
2292
0
                    "col_path {}",
2293
0
                    block->get_by_position(i).type->get_name(), file_column_type->get_name(),
2294
0
                    column_desc->name(),
2295
0
                    column_desc->path() == nullptr ? "" : column_desc->path()->get_path());
2296
            // TODO reuse
2297
0
            current_columns[cid] = file_column_type->create_column();
2298
0
            current_columns[cid]->reserve(nrows_read_limit);
2299
28.0k
        } else {
2300
            // the column in block must clear() here to insert new data
2301
28.0k
            if (_is_pred_column[cid] ||
2302
28.0k
                i >= block->columns()) { //todo(wb) maybe we can release it after output block
2303
2.16k
                if (current_columns[cid].get() == nullptr) {
2304
0
                    return Status::InternalError(
2305
0
                            "SegmentIterator meet invalid column, id={}, name={}", cid,
2306
0
                            _schema->column(cid)->name());
2307
0
                }
2308
2.16k
                current_columns[cid]->clear();
2309
25.9k
            } else { // non-predicate column
2310
25.9k
                current_columns[cid] = std::move(*block->get_by_position(i).column).mutate();
2311
25.9k
                current_columns[cid]->reserve(nrows_read_limit);
2312
25.9k
            }
2313
28.0k
        }
2314
28.0k
    }
2315
2316
12.9k
    for (auto entry : _virtual_column_exprs) {
2317
0
        auto cid = entry.first;
2318
0
        current_columns[cid] = ColumnNothing::create(0);
2319
0
        current_columns[cid]->reserve(nrows_read_limit);
2320
0
    }
2321
2322
12.9k
    return Status::OK();
2323
12.9k
}
2324
2325
10.1k
Status SegmentIterator::_output_non_pred_columns(Block* block) {
2326
10.1k
    SCOPED_RAW_TIMER(&_opts.stats->output_col_ns);
2327
10.1k
    VLOG_DEBUG << fmt::format(
2328
0
            "Output non-predicate columns, _non_predicate_columns: [{}], "
2329
0
            "_schema_block_id_map: [{}]",
2330
0
            fmt::join(_non_predicate_columns, ","), fmt::join(_schema_block_id_map, ","));
2331
10.1k
    RETURN_IF_ERROR(_convert_to_expected_type(_non_predicate_columns));
2332
19.3k
    for (auto cid : _non_predicate_columns) {
2333
19.3k
        auto loc = _schema_block_id_map[cid];
2334
        // Whether a delete predicate column gets output depends on how the caller builds
2335
        // the block passed to next_batch(). Both calling paths now build the block with
2336
        // only the output schema (return_columns), so delete predicate columns are skipped:
2337
        //
2338
        // 1) VMergeIterator path: block_reset() builds _block using the output schema
2339
        //    (return_columns only), e.g. block has 2 columns {c1, c2}.
2340
        //    Here loc=2 for delete predicate c3, block->columns()=2, so loc < block->columns()
2341
        //    is false, and c3 is skipped.
2342
        //
2343
        // 2) VUnionIterator path: the caller's block is built with only return_columns
2344
        //    (output schema), e.g. block has 2 columns {c1, c2}.
2345
        //    Here loc=2 for c3, block->columns()=2, so loc < block->columns() is false,
2346
        //    and c3 is skipped — same behavior as the VMergeIterator path.
2347
19.3k
        if (loc < block->columns()) {
2348
19.3k
            bool column_in_block_is_nothing = check_and_get_column<const ColumnNothing>(
2349
19.3k
                    block->get_by_position(loc).column.get());
2350
19.3k
            bool column_is_normal = !_vir_cid_to_idx_in_block.contains(cid);
2351
19.3k
            bool return_column_is_nothing =
2352
19.3k
                    check_and_get_column<const ColumnNothing>(_current_return_columns[cid].get());
2353
19.3k
            VLOG_DEBUG << fmt::format(
2354
0
                    "Cid {} loc {}, column_in_block_is_nothing {}, column_is_normal {}, "
2355
0
                    "return_column_is_nothing {}",
2356
0
                    cid, loc, column_in_block_is_nothing, column_is_normal,
2357
0
                    return_column_is_nothing);
2358
2359
19.3k
            if (column_in_block_is_nothing || column_is_normal) {
2360
19.3k
                block->replace_by_position(loc, std::move(_current_return_columns[cid]));
2361
19.3k
                VLOG_DEBUG << fmt::format(
2362
0
                        "Output non-predicate column, cid: {}, loc: {}, col_name: {}, rows {}", cid,
2363
0
                        loc, _schema->column(cid)->name(),
2364
0
                        block->get_by_position(loc).column->size());
2365
19.3k
            }
2366
            // Means virtual column in block has been materialized(maybe by common expr).
2367
            // so do nothing here.
2368
19.3k
        }
2369
19.3k
    }
2370
10.1k
    return Status::OK();
2371
10.1k
}
2372
2373
/**
2374
 * Reads columns by their index, handling both continuous and discontinuous rowid scenarios.
2375
 *
2376
 * This function is designed to read a specified number of rows (up to nrows_read_limit)
2377
 * from the segment iterator, dealing with both continuous and discontinuous rowid arrays.
2378
 * It operates as follows:
2379
 *
2380
 * 1. Reads a batch of rowids (up to the specified limit), and checks if they are continuous.
2381
 *    Continuous here means that the rowids form an unbroken sequence (e.g., 1, 2, 3, 4...).
2382
 *
2383
 * 2. For each column that needs to be read (identified by _predicate_column_ids):
2384
 *    - If the rowids are continuous, the function uses seek_to_ordinal and next_batch
2385
 *      for efficient reading.
2386
 *    - If the rowids are not continuous, the function processes them in smaller batches
2387
 *      (each of size up to 256). Each batch is checked for internal continuity:
2388
 *        a. If a batch is continuous, uses seek_to_ordinal and next_batch for that batch.
2389
 *        b. If a batch is not continuous, uses read_by_rowids for individual rowids in the batch.
2390
 *
2391
 * This approach optimizes reading performance by leveraging batch processing for continuous
2392
 * rowid sequences and handling discontinuities gracefully in smaller chunks.
2393
 */
2394
12.9k
Status SegmentIterator::_read_columns_by_index(uint32_t nrows_read_limit, uint16_t& nrows_read) {
2395
12.9k
    SCOPED_RAW_TIMER(&_opts.stats->predicate_column_read_ns);
2396
2397
12.9k
    nrows_read = (uint16_t)_range_iter->read_batch_rowids(_block_rowids.data(), nrows_read_limit);
2398
12.9k
    bool is_continuous = (nrows_read > 1) &&
2399
12.9k
                         (_block_rowids[nrows_read - 1] - _block_rowids[0] == nrows_read - 1);
2400
12.9k
    VLOG_DEBUG << fmt::format(
2401
0
            "nrows_read from range iterator: {}, is_continus {}, _cols_read_by_column_predicate "
2402
0
            "[{}]",
2403
0
            nrows_read, is_continuous, fmt::join(_predicate_column_ids, ","));
2404
2405
12.9k
    LOG_IF(INFO, config::enable_segment_prefetch_verbose_log) << fmt::format(
2406
0
            "[verbose] SegmentIterator::_read_columns_by_index read {} rowids, continuous: {}, "
2407
0
            "rowids: [{}...{}]",
2408
0
            nrows_read, is_continuous, nrows_read > 0 ? _block_rowids[0] : 0,
2409
0
            nrows_read > 0 ? _block_rowids[nrows_read - 1] : 0);
2410
25.0k
    for (auto cid : _predicate_column_ids) {
2411
25.0k
        auto& column = _current_return_columns[cid];
2412
25.0k
        VLOG_DEBUG << fmt::format("Reading column {}, col_name {}", cid,
2413
0
                                  _schema->column(cid)->name());
2414
25.0k
        if (!_virtual_column_exprs.contains(cid)) {
2415
25.0k
            if (_no_need_read_key_data(cid, column, nrows_read)) {
2416
0
                VLOG_DEBUG << fmt::format("Column {} no need to read.", cid);
2417
0
                continue;
2418
0
            }
2419
25.0k
            if (_prune_column(cid, column, true, nrows_read)) {
2420
0
                VLOG_DEBUG << fmt::format("Column {} is pruned. No need to read data.", cid);
2421
0
                continue;
2422
0
            }
2423
25.0k
            DBUG_EXECUTE_IF("segment_iterator._read_columns_by_index", {
2424
25.0k
                auto col_name = _opts.tablet_schema->column(cid).name();
2425
25.0k
                auto debug_col_name =
2426
25.0k
                        DebugPoints::instance()->get_debug_param_or_default<std::string>(
2427
25.0k
                                "segment_iterator._read_columns_by_index", "column_name", "");
2428
25.0k
                if (debug_col_name.empty() && col_name != "__DORIS_DELETE_SIGN__") {
2429
25.0k
                    return Status::Error<ErrorCode::INTERNAL_ERROR>(
2430
25.0k
                            "does not need to read data, {}", col_name);
2431
25.0k
                }
2432
25.0k
                if (debug_col_name.find(col_name) != std::string::npos) {
2433
25.0k
                    return Status::Error<ErrorCode::INTERNAL_ERROR>(
2434
25.0k
                            "does not need to read data, {}", col_name);
2435
25.0k
                }
2436
25.0k
            })
2437
25.0k
        }
2438
2439
25.0k
        if (is_continuous) {
2440
18.5k
            size_t rows_read = nrows_read;
2441
18.5k
            _opts.stats->predicate_column_read_seek_num += 1;
2442
18.5k
            if (_opts.runtime_state && _opts.runtime_state->enable_profile()) {
2443
0
                SCOPED_RAW_TIMER(&_opts.stats->predicate_column_read_seek_ns);
2444
0
                RETURN_IF_ERROR(_column_iterators[cid]->seek_to_ordinal(_block_rowids[0]));
2445
18.5k
            } else {
2446
18.5k
                RETURN_IF_ERROR(_column_iterators[cid]->seek_to_ordinal(_block_rowids[0]));
2447
18.5k
            }
2448
18.5k
            RETURN_IF_ERROR(_column_iterators[cid]->next_batch(&rows_read, column));
2449
18.5k
            if (rows_read != nrows_read) {
2450
0
                return Status::Error<ErrorCode::INTERNAL_ERROR>("nrows({}) != rows_read({})",
2451
0
                                                                nrows_read, rows_read);
2452
0
            }
2453
18.5k
        } else {
2454
6.50k
            const uint32_t batch_size = _range_iter->get_batch_size();
2455
6.50k
            uint32_t processed = 0;
2456
7.69k
            while (processed < nrows_read) {
2457
1.19k
                uint32_t current_batch_size = std::min(batch_size, nrows_read - processed);
2458
1.19k
                bool batch_continuous = (current_batch_size > 1) &&
2459
1.19k
                                        (_block_rowids[processed + current_batch_size - 1] -
2460
1.17k
                                                 _block_rowids[processed] ==
2461
1.17k
                                         current_batch_size - 1);
2462
2463
1.19k
                if (batch_continuous) {
2464
0
                    size_t rows_read = current_batch_size;
2465
0
                    _opts.stats->predicate_column_read_seek_num += 1;
2466
0
                    if (_opts.runtime_state && _opts.runtime_state->enable_profile()) {
2467
0
                        SCOPED_RAW_TIMER(&_opts.stats->predicate_column_read_seek_ns);
2468
0
                        RETURN_IF_ERROR(
2469
0
                                _column_iterators[cid]->seek_to_ordinal(_block_rowids[processed]));
2470
0
                    } else {
2471
0
                        RETURN_IF_ERROR(
2472
0
                                _column_iterators[cid]->seek_to_ordinal(_block_rowids[processed]));
2473
0
                    }
2474
0
                    RETURN_IF_ERROR(_column_iterators[cid]->next_batch(&rows_read, column));
2475
0
                    if (rows_read != current_batch_size) {
2476
0
                        return Status::Error<ErrorCode::INTERNAL_ERROR>(
2477
0
                                "batch nrows({}) != rows_read({})", current_batch_size, rows_read);
2478
0
                    }
2479
1.19k
                } else {
2480
1.19k
                    RETURN_IF_ERROR(_column_iterators[cid]->read_by_rowids(
2481
1.19k
                            &_block_rowids[processed], current_batch_size, column));
2482
1.19k
                }
2483
1.19k
                processed += current_batch_size;
2484
1.19k
            }
2485
6.50k
        }
2486
25.0k
    }
2487
2488
12.9k
    return Status::OK();
2489
12.9k
}
2490
void SegmentIterator::_replace_version_col_if_needed(const std::vector<ColumnId>& column_ids,
2491
14.3k
                                                     size_t num_rows) {
2492
    // Only the rowset with single version need to replace the version column.
2493
    // Doris can't determine the version before publish_version finished, so
2494
    // we can't write data to __DORIS_VERSION_COL__ in segment writer, the value
2495
    // is 0 by default.
2496
    // So we need to replace the value to real version while reading.
2497
14.3k
    if (_opts.version.first != _opts.version.second) {
2498
6.49k
        return;
2499
6.49k
    }
2500
7.81k
    int32_t version_idx = _schema->version_col_idx();
2501
7.81k
    if (std::ranges::find(column_ids, version_idx) == column_ids.end()) {
2502
7.81k
        return;
2503
7.81k
    }
2504
2505
0
    const auto* column_desc = _schema->column(version_idx);
2506
0
    auto column = Schema::get_data_type_ptr(*column_desc)->create_column();
2507
0
    DCHECK(_schema->column(version_idx)->type() == FieldType::OLAP_FIELD_TYPE_BIGINT);
2508
0
    auto* col_ptr = assert_cast<ColumnInt64*>(column.get());
2509
0
    for (size_t j = 0; j < num_rows; j++) {
2510
0
        col_ptr->insert_value(_opts.version.second);
2511
0
    }
2512
0
    _current_return_columns[version_idx] = std::move(column);
2513
0
    VLOG_DEBUG << "replaced version column in segment iterator, version_col_idx:" << version_idx;
2514
0
}
2515
2516
void SegmentIterator::_update_lsn_col_if_needed(const std::vector<ColumnId>& column_ids,
2517
14.3k
                                                size_t num_rows) {
2518
    // | commit tso(64) | auto-inc row_id(64) |
2519
14.3k
    if (_opts.version.first != _opts.version.second) {
2520
6.49k
        return;
2521
6.49k
    }
2522
2523
7.81k
    if (_opts.io_ctx.reader_type != ReaderType::READER_BINLOG &&
2524
7.81k
        _opts.io_ctx.reader_type != ReaderType::READER_BINLOG_COMPACTION) {
2525
7.81k
        return;
2526
7.81k
    }
2527
2528
0
    int32_t lsn_col_idx = _schema->lsn_col_idx();
2529
0
    if (lsn_col_idx < 0 || std::ranges::find(column_ids, lsn_col_idx) == column_ids.end()) {
2530
0
        return;
2531
0
    }
2532
2533
0
    DCHECK_EQ(_opts.commit_tso.start_tso(), _opts.commit_tso.end_tso());
2534
0
    const Int64 commit_tso = _opts.commit_tso.end_tso() == -1 ? 0 : _opts.commit_tso.end_tso();
2535
2536
0
    if (_is_pred_column[lsn_col_idx]) {
2537
0
        auto* lsn_column = assert_cast<PredicateColumnType<TYPE_LARGEINT>*>(
2538
0
                _current_return_columns[lsn_col_idx].get());
2539
0
        std::vector<Int128> binlog_lsns;
2540
0
        binlog_lsns.reserve(num_rows);
2541
0
        for (size_t j = 0; j < num_rows; j++) {
2542
0
            const Int128 row_id = lsn_column->get_data()[j];
2543
0
            binlog_lsns.emplace_back(make_row_binlog_lsn(commit_tso, row_id));
2544
0
        }
2545
0
        _current_return_columns[lsn_col_idx]->clear();
2546
0
        for (const auto& binlog_lsn : binlog_lsns) {
2547
0
            lsn_column->insert_data(reinterpret_cast<const char*>(&binlog_lsn), 0);
2548
0
        }
2549
0
        return;
2550
0
    }
2551
2552
0
    auto* lsn_column = assert_cast<ColumnInt128*>(_current_return_columns[lsn_col_idx].get());
2553
0
    const auto* column_desc = _schema->column(lsn_col_idx);
2554
0
    auto column = Schema::get_data_type_ptr(*column_desc)->create_column();
2555
0
    DCHECK(column_desc->type() == FieldType::OLAP_FIELD_TYPE_LARGEINT);
2556
0
    auto* col_ptr = assert_cast<ColumnInt128*>(column.get());
2557
2558
0
    for (size_t j = 0; j < num_rows; j++) {
2559
0
        const Int128 row_id = lsn_column->get_element(j);
2560
0
        col_ptr->insert_value(make_row_binlog_lsn(commit_tso, row_id));
2561
0
    }
2562
0
    _current_return_columns[lsn_col_idx] = std::move(column);
2563
0
}
2564
2565
void SegmentIterator::_update_tso_col_if_needed(const std::vector<ColumnId>& column_ids,
2566
14.3k
                                                size_t num_rows) {
2567
    // use physical time part of commit tso to replace timestamp col
2568
14.3k
    if (_opts.version.first != _opts.version.second) {
2569
6.49k
        return;
2570
6.49k
    }
2571
2572
7.81k
    if (_opts.io_ctx.reader_type != ReaderType::READER_BINLOG &&
2573
7.81k
        _opts.io_ctx.reader_type != ReaderType::READER_BINLOG_COMPACTION) {
2574
7.81k
        return;
2575
7.81k
    }
2576
2577
0
    int32_t tso_col_idx = _schema->tso_col_idx();
2578
0
    if (tso_col_idx < 0 || std::ranges::find(column_ids, tso_col_idx) == column_ids.end()) {
2579
0
        return;
2580
0
    }
2581
2582
0
    DCHECK_EQ(_opts.commit_tso.start_tso(), _opts.commit_tso.end_tso());
2583
0
    Int64 commit_tso = _opts.commit_tso.end_tso() == -1 ? 0 : _opts.commit_tso.end_tso();
2584
0
    Int64 commit_time = extract_tso_physical_time(commit_tso);
2585
2586
0
    if (_is_pred_column[tso_col_idx]) {
2587
        // Nullable predicate column is represented as ColumnNullable(predicate_col)
2588
0
        if (auto* tso_nullable =
2589
0
                    typeid_cast<ColumnNullable*>(_current_return_columns[tso_col_idx].get())) {
2590
0
            _current_return_columns[tso_col_idx]->clear();
2591
0
            auto value = commit_time;
2592
0
            for (size_t j = 0; j < num_rows; j++) {
2593
0
                tso_nullable->get_nested_column_ptr()->insert_data(
2594
0
                        reinterpret_cast<const char*>(&value), 0);
2595
0
                tso_nullable->get_null_map_data().emplace_back(0);
2596
0
            }
2597
0
            return;
2598
0
        }
2599
2600
0
        auto* tso_column = assert_cast<PredicateColumnType<TYPE_BIGINT>*>(
2601
0
                _current_return_columns[tso_col_idx].get());
2602
0
        _current_return_columns[tso_col_idx]->clear();
2603
0
        auto value = commit_time;
2604
0
        for (size_t j = 0; j < num_rows; j++) {
2605
0
            tso_column->insert_data(reinterpret_cast<const char*>(&value), 0);
2606
0
        }
2607
0
        return;
2608
0
    }
2609
2610
0
    const auto* column_desc = _schema->column(tso_col_idx);
2611
0
    auto column = Schema::get_data_type_ptr(*column_desc)->create_column();
2612
0
    DCHECK(column_desc->type() == FieldType::OLAP_FIELD_TYPE_BIGINT);
2613
2614
0
    if (auto* tso_nullable = typeid_cast<ColumnNullable*>(column.get())) {
2615
0
        auto* col_ptr = assert_cast<ColumnInt64*>(&tso_nullable->get_nested_column());
2616
0
        for (size_t j = 0; j < num_rows; j++) {
2617
0
            col_ptr->insert_value(commit_time);
2618
0
            tso_nullable->get_null_map_data().emplace_back(0);
2619
0
        }
2620
0
    } else {
2621
0
        auto* col_ptr = assert_cast<ColumnInt64*>(column.get());
2622
0
        for (size_t j = 0; j < num_rows; j++) {
2623
0
            col_ptr->insert_value(commit_time);
2624
0
        }
2625
0
    }
2626
0
    _current_return_columns[tso_col_idx] = std::move(column);
2627
0
}
2628
2629
uint16_t SegmentIterator::_evaluate_vectorization_predicate(uint16_t* sel_rowid_idx,
2630
1.69k
                                                            uint16_t selected_size) {
2631
1.69k
    SCOPED_RAW_TIMER(&_opts.stats->vec_cond_ns);
2632
1.69k
    bool all_pred_always_true = true;
2633
1.69k
    for (const auto& pred : _pre_eval_block_predicate) {
2634
0
        if (!pred->always_true()) {
2635
0
            all_pred_always_true = false;
2636
0
        } else {
2637
0
            pred->update_filter_info(0, 0, selected_size);
2638
0
        }
2639
0
    }
2640
2641
1.69k
    const uint16_t original_size = selected_size;
2642
    //If all predicates are always_true, then return directly.
2643
1.69k
    if (all_pred_always_true || !_is_need_vec_eval) {
2644
3.90M
        for (uint16_t i = 0; i < original_size; ++i) {
2645
3.90M
            sel_rowid_idx[i] = i;
2646
3.90M
        }
2647
        // All preds are always_true, so return immediately and update the profile statistics here.
2648
1.69k
        _opts.stats->vec_cond_input_rows += original_size;
2649
1.69k
        return original_size;
2650
1.69k
    }
2651
2652
0
    _ret_flags.resize(original_size);
2653
0
    DCHECK(!_pre_eval_block_predicate.empty());
2654
0
    bool is_first = true;
2655
0
    for (auto& pred : _pre_eval_block_predicate) {
2656
0
        if (pred->always_true()) {
2657
0
            continue;
2658
0
        }
2659
0
        auto column_id = pred->column_id();
2660
0
        auto& column = _current_return_columns[column_id];
2661
0
        if (is_first) {
2662
0
            pred->evaluate_vec(*column, original_size, (bool*)_ret_flags.data());
2663
0
            is_first = false;
2664
0
        } else {
2665
0
            pred->evaluate_and_vec(*column, original_size, (bool*)_ret_flags.data());
2666
0
        }
2667
0
    }
2668
2669
0
    uint16_t new_size = 0;
2670
2671
0
    uint16_t sel_pos = 0;
2672
0
    const uint16_t sel_end = sel_pos + selected_size;
2673
0
    static constexpr size_t SIMD_BYTES = simd::bits_mask_length();
2674
0
    const uint16_t sel_end_simd = sel_pos + selected_size / SIMD_BYTES * SIMD_BYTES;
2675
2676
0
    while (sel_pos < sel_end_simd) {
2677
0
        auto mask = simd::bytes_mask_to_bits_mask(_ret_flags.data() + sel_pos);
2678
0
        if (0 == mask) {
2679
            //pass
2680
0
        } else if (simd::bits_mask_all() == mask) {
2681
0
            for (uint16_t i = 0; i < SIMD_BYTES; i++) {
2682
0
                sel_rowid_idx[new_size++] = sel_pos + i;
2683
0
            }
2684
0
        } else {
2685
0
            simd::iterate_through_bits_mask(
2686
0
                    [&](const int bit_pos) {
2687
0
                        sel_rowid_idx[new_size++] = sel_pos + (uint16_t)bit_pos;
2688
0
                    },
2689
0
                    mask);
2690
0
        }
2691
0
        sel_pos += SIMD_BYTES;
2692
0
    }
2693
2694
0
    for (; sel_pos < sel_end; sel_pos++) {
2695
0
        if (_ret_flags[sel_pos]) {
2696
0
            sel_rowid_idx[new_size++] = sel_pos;
2697
0
        }
2698
0
    }
2699
2700
0
    _opts.stats->vec_cond_input_rows += original_size;
2701
0
    _opts.stats->rows_vec_cond_filtered += original_size - new_size;
2702
0
    return new_size;
2703
1.69k
}
2704
2705
uint16_t SegmentIterator::_evaluate_short_circuit_predicate(uint16_t* vec_sel_rowid_idx,
2706
1.69k
                                                            uint16_t selected_size) {
2707
1.69k
    SCOPED_RAW_TIMER(&_opts.stats->short_cond_ns);
2708
1.69k
    if (!_is_need_short_eval) {
2709
0
        return selected_size;
2710
0
    }
2711
2712
1.69k
    uint16_t original_size = selected_size;
2713
1.69k
    for (auto predicate : _short_cir_eval_predicate) {
2714
0
        auto column_id = predicate->column_id();
2715
0
        auto& short_cir_column = _current_return_columns[column_id];
2716
0
        selected_size = predicate->evaluate(*short_cir_column, vec_sel_rowid_idx, selected_size);
2717
0
    }
2718
2719
1.69k
    _opts.stats->short_circuit_cond_input_rows += original_size;
2720
1.69k
    _opts.stats->rows_short_circuit_cond_filtered += original_size - selected_size;
2721
2722
    // evaluate delete condition
2723
1.69k
    original_size = selected_size;
2724
1.69k
    selected_size = _opts.delete_condition_predicates->evaluate(_current_return_columns,
2725
1.69k
                                                                vec_sel_rowid_idx, selected_size);
2726
1.69k
    _opts.stats->rows_vec_del_cond_filtered += original_size - selected_size;
2727
1.69k
    return selected_size;
2728
1.69k
}
2729
2730
1
static void shrink_materialized_block_columns(Block* block, size_t rows) {
2731
2
    for (auto& entry : *block) {
2732
2
        if (entry.column && entry.column->size() > rows) {
2733
1
            entry.column = entry.column->shrink(rows);
2734
1
        }
2735
2
    }
2736
1
}
2737
2738
static void slice_materialized_block_columns(Block* block, size_t offset, size_t rows,
2739
1
                                             size_t original_rows) {
2740
1
    for (auto& entry : *block) {
2741
1
        if (!entry.column || entry.column->size() == 0) {
2742
0
            continue;
2743
0
        }
2744
1
        DORIS_CHECK(entry.column->size() == original_rows);
2745
1
        entry.column = entry.column->cut(offset, rows);
2746
1
    }
2747
1
}
2748
2749
1.69k
Status SegmentIterator::_apply_read_limit_to_selected_rows(Block* block, uint16_t& selected_size) {
2750
1.69k
    if (_opts.read_limit == 0) {
2751
1.69k
        return Status::OK();
2752
1.69k
    }
2753
2
    DORIS_CHECK(_rows_returned <= _opts.read_limit);
2754
2
    size_t remaining = _opts.read_limit - _rows_returned;
2755
2
    if (remaining == 0) {
2756
0
        selected_size = 0;
2757
0
        shrink_materialized_block_columns(block, 0);
2758
0
        return Status::OK();
2759
0
    }
2760
2
    if (selected_size > remaining) {
2761
2
        if (_opts.read_orderby_key_reverse) {
2762
1
            const auto original_size = selected_size;
2763
1
            const auto offset = original_size - remaining;
2764
21
            for (size_t i = 0; i < remaining; ++i) {
2765
20
                _sel_rowid_idx[i] = _sel_rowid_idx[offset + i];
2766
20
            }
2767
1
            selected_size = cast_set<uint16_t>(remaining);
2768
1
            slice_materialized_block_columns(block, offset, remaining, original_size);
2769
1
            return Status::OK();
2770
1
        }
2771
1
        selected_size = cast_set<uint16_t>(remaining);
2772
1
        shrink_materialized_block_columns(block, selected_size);
2773
1
    }
2774
1
    return Status::OK();
2775
2
}
2776
2777
Status SegmentIterator::_read_columns_by_rowids(std::vector<ColumnId>& read_column_ids,
2778
                                                std::vector<rowid_t>& rowid_vector,
2779
                                                uint16_t* sel_rowid_idx, size_t select_size,
2780
                                                MutableColumns* mutable_columns,
2781
1.38k
                                                bool init_condition_cache) {
2782
1.38k
    SCOPED_RAW_TIMER(&_opts.stats->lazy_read_ns);
2783
1.38k
    std::vector<rowid_t> rowids(select_size);
2784
2785
1.38k
    if (init_condition_cache) {
2786
0
        DCHECK(_condition_cache);
2787
0
        auto& condition_cache = *_condition_cache;
2788
0
        for (size_t i = 0; i < select_size; ++i) {
2789
0
            rowids[i] = rowid_vector[sel_rowid_idx[i]];
2790
0
            condition_cache[rowids[i] / SegmentIterator::CONDITION_CACHE_OFFSET] = true;
2791
0
        }
2792
1.38k
    } else {
2793
2.74M
        for (size_t i = 0; i < select_size; ++i) {
2794
2.74M
            rowids[i] = rowid_vector[sel_rowid_idx[i]];
2795
2.74M
        }
2796
1.38k
    }
2797
2798
2.10k
    for (auto cid : read_column_ids) {
2799
2.10k
        auto& colunm = (*mutable_columns)[cid];
2800
2.10k
        if (_no_need_read_key_data(cid, colunm, select_size)) {
2801
0
            continue;
2802
0
        }
2803
2.10k
        if (_prune_column(cid, colunm, true, select_size)) {
2804
0
            continue;
2805
0
        }
2806
2807
2.10k
        DBUG_EXECUTE_IF("segment_iterator._read_columns_by_index", {
2808
2.10k
            auto debug_col_name = DebugPoints::instance()->get_debug_param_or_default<std::string>(
2809
2.10k
                    "segment_iterator._read_columns_by_index", "column_name", "");
2810
2.10k
            if (debug_col_name.empty()) {
2811
2.10k
                return Status::Error<ErrorCode::INTERNAL_ERROR>("does not need to read data");
2812
2.10k
            }
2813
2.10k
            auto col_name = _opts.tablet_schema->column(cid).name();
2814
2.10k
            if (debug_col_name.find(col_name) != std::string::npos) {
2815
2.10k
                return Status::Error<ErrorCode::INTERNAL_ERROR>("does not need to read data, {}",
2816
2.10k
                                                                debug_col_name);
2817
2.10k
            }
2818
2.10k
        })
2819
2820
2.10k
        if (_current_return_columns[cid].get() == nullptr) {
2821
0
            return Status::InternalError(
2822
0
                    "SegmentIterator meet invalid column, return columns size {}, cid {}",
2823
0
                    _current_return_columns.size(), cid);
2824
0
        }
2825
2.10k
        RETURN_IF_ERROR(_column_iterators[cid]->read_by_rowids(rowids.data(), select_size,
2826
2.10k
                                                               _current_return_columns[cid]));
2827
2.10k
    }
2828
2829
1.38k
    return Status::OK();
2830
1.38k
}
2831
2832
12.9k
Status SegmentIterator::next_batch(Block* block) {
2833
    // Replace virtual columns with ColumnNothing at the begining of each next_batch call.
2834
12.9k
    _init_virtual_columns(block);
2835
12.9k
    auto status = [&]() {
2836
12.9k
        RETURN_IF_CATCH_EXCEPTION({
2837
            // Adaptive batch size: predict how many rows this batch should read.
2838
12.9k
            if (_block_size_predictor) {
2839
12.9k
                auto predicted = static_cast<uint32_t>(_block_size_predictor->predict_next_rows());
2840
12.9k
                _opts.block_row_max = std::min(predicted, _initial_block_row_max);
2841
12.9k
                _opts.stats->adaptive_batch_size_predict_min_rows =
2842
12.9k
                        std::min(_opts.stats->adaptive_batch_size_predict_min_rows,
2843
12.9k
                                 static_cast<int64_t>(predicted));
2844
12.9k
                _opts.stats->adaptive_batch_size_predict_max_rows =
2845
12.9k
                        std::max(_opts.stats->adaptive_batch_size_predict_max_rows,
2846
12.9k
                                 static_cast<int64_t>(predicted));
2847
12.9k
            } else {
2848
                // No predictor — record the fixed batch size using min/max so we don't
2849
                // clobber values already accumulated by other segment iterators that
2850
                // share the same OlapReaderStatistics.
2851
12.9k
                _opts.stats->adaptive_batch_size_predict_min_rows =
2852
12.9k
                        std::min(_opts.stats->adaptive_batch_size_predict_min_rows,
2853
12.9k
                                 static_cast<int64_t>(_opts.block_row_max));
2854
12.9k
                _opts.stats->adaptive_batch_size_predict_max_rows =
2855
12.9k
                        std::max(_opts.stats->adaptive_batch_size_predict_max_rows,
2856
12.9k
                                 static_cast<int64_t>(_opts.block_row_max));
2857
12.9k
            }
2858
2859
12.9k
            auto res = _next_batch_internal(block);
2860
2861
12.9k
            if (res.is<END_OF_FILE>()) {
2862
                // Since we have a type check at the caller.
2863
                // So a replacement of nothing column with real column is needed.
2864
12.9k
                const auto& idx_to_datatype = _opts.vir_col_idx_to_type;
2865
12.9k
                for (const auto& pair : _vir_cid_to_idx_in_block) {
2866
12.9k
                    size_t idx = pair.second;
2867
12.9k
                    auto type = idx_to_datatype.find(idx)->second;
2868
12.9k
                    block->replace_by_position(idx, type->create_column());
2869
12.9k
                }
2870
2871
12.9k
                if (_opts.condition_cache_digest && !_find_condition_cache) {
2872
12.9k
                    auto* condition_cache = ConditionCache::instance();
2873
12.9k
                    ConditionCache::CacheKey cache_key(_opts.rowset_id, _segment->id(),
2874
12.9k
                                                       _opts.condition_cache_digest);
2875
12.9k
                    VLOG_DEBUG << "Condition cache insert, query id: "
2876
12.9k
                               << print_id(_opts.runtime_state->query_id())
2877
12.9k
                               << ", rowset id: " << _opts.rowset_id.to_string()
2878
12.9k
                               << ", segment id: " << _segment->id()
2879
12.9k
                               << ", cache digest: " << _opts.condition_cache_digest;
2880
12.9k
                    condition_cache->insert(cache_key, std::move(_condition_cache));
2881
12.9k
                }
2882
12.9k
                return res;
2883
12.9k
            }
2884
2885
12.9k
            RETURN_IF_ERROR(res);
2886
            // reverse block row order if read_orderby_key_reverse is true for key topn
2887
            // it should be processed for all success _next_batch_internal
2888
12.9k
            if (_opts.read_orderby_key_reverse) {
2889
12.9k
                size_t num_rows = block->rows();
2890
12.9k
                if (num_rows == 0) {
2891
12.9k
                    return Status::OK();
2892
12.9k
                }
2893
12.9k
                size_t num_columns = block->columns();
2894
12.9k
                IColumn::Permutation permutation;
2895
12.9k
                for (size_t i = 0; i < num_rows; ++i) permutation.emplace_back(num_rows - 1 - i);
2896
2897
12.9k
                for (size_t i = 0; i < num_columns; ++i)
2898
12.9k
                    block->get_by_position(i).column =
2899
12.9k
                            block->get_by_position(i).column->permute(permutation, num_rows);
2900
12.9k
            }
2901
2902
12.9k
            RETURN_IF_ERROR(block->check_type_and_column());
2903
2904
            // Adaptive batch size: update EWMA estimate from the completed batch.
2905
            // block->bytes() is accurate here: predicates have been applied and non-predicate
2906
            // columns have been filled for surviving rows by _next_batch_internal.
2907
12.9k
            if (_block_size_predictor && block->rows() > 0) {
2908
12.9k
                _block_size_predictor->update(*block);
2909
12.9k
            }
2910
2911
12.9k
            return Status::OK();
2912
12.9k
        });
2913
12.9k
    }();
2914
2915
    // if rows read by batch is 0, will return end of file, we should not remove segment cache in this situation.
2916
12.9k
    if (!status.ok() && !status.is<END_OF_FILE>()) {
2917
0
        _segment->update_healthy_status(status);
2918
0
    }
2919
12.9k
    return status;
2920
12.9k
}
2921
2922
12.9k
Status SegmentIterator::_convert_to_expected_type(const std::vector<ColumnId>& col_ids) {
2923
26.3k
    for (ColumnId i : col_ids) {
2924
26.3k
        if (!_current_return_columns[i] || _converted_column_ids[i] || _is_pred_column[i]) {
2925
467
            continue;
2926
467
        }
2927
25.9k
        const StorageField* field_type = _schema->column(i);
2928
25.9k
        DataTypePtr expected_type = Schema::get_data_type_ptr(*field_type);
2929
25.9k
        DataTypePtr file_column_type = _storage_name_and_type[i].second;
2930
25.9k
        if (!file_column_type->equals(*expected_type)) {
2931
0
            ColumnPtr expected;
2932
0
            ColumnPtr original = _current_return_columns[i]->assume_mutable()->get_ptr();
2933
0
            RETURN_IF_ERROR(variant_util::cast_column({original, file_column_type, ""},
2934
0
                                                      expected_type, &expected));
2935
0
            _current_return_columns[i] = expected->assume_mutable();
2936
0
            _converted_column_ids[i] = true;
2937
0
            VLOG_DEBUG << fmt::format(
2938
0
                    "Convert {} fom file column type {} to {}, num_rows {}",
2939
0
                    field_type->path() == nullptr ? "" : field_type->path()->get_path(),
2940
0
                    file_column_type->get_name(), expected_type->get_name(),
2941
0
                    _current_return_columns[i]->size());
2942
0
        }
2943
25.9k
    }
2944
12.9k
    return Status::OK();
2945
12.9k
}
2946
2947
Status SegmentIterator::copy_column_data_by_selector(IColumn* input_col_ptr,
2948
                                                     MutableColumnPtr& output_col,
2949
                                                     uint16_t* sel_rowid_idx, uint16_t select_size,
2950
1.39k
                                                     size_t batch_size) {
2951
1.39k
    if (output_col->is_nullable() != input_col_ptr->is_nullable()) {
2952
0
        LOG(WARNING) << "nullable mismatch for output_column: " << output_col->dump_structure()
2953
0
                     << " input_column: " << input_col_ptr->dump_structure()
2954
0
                     << " select_size: " << select_size;
2955
0
        return Status::RuntimeError("copy_column_data_by_selector nullable mismatch");
2956
0
    }
2957
1.39k
    output_col->reserve(select_size);
2958
1.39k
    return input_col_ptr->filter_by_selector(sel_rowid_idx, select_size, output_col.get());
2959
1.39k
}
2960
2961
12.9k
Status SegmentIterator::_next_batch_internal(Block* block) {
2962
12.9k
    SCOPED_CONCURRENCY_COUNT(ConcurrencyStatsManager::instance().segment_iterator_next_batch);
2963
2964
12.9k
    bool is_mem_reuse = block->mem_reuse();
2965
12.9k
    DCHECK(is_mem_reuse);
2966
2967
12.9k
    RETURN_IF_ERROR(_lazy_init(block));
2968
2969
12.9k
    SCOPED_RAW_TIMER(&_opts.stats->block_load_ns);
2970
2971
12.9k
    if (_opts.read_limit > 0 && _rows_returned >= _opts.read_limit) {
2972
0
        return _process_eof(block);
2973
0
    }
2974
2975
    // If the row bitmap size is smaller than nrows_read_limit, there's no need to reserve that many column rows.
2976
12.9k
    uint32_t nrows_read_limit =
2977
12.9k
            std::min(cast_set<uint32_t>(_row_bitmap.cardinality()), _opts.block_row_max);
2978
12.9k
    if (_can_opt_limit_reads()) {
2979
        // No SegmentIterator-side conjunct remains to be evaluated, so LIMIT is equivalent before
2980
        // and after filtering. Cap the first read directly; this is the no-conjunct fast path that
2981
        // avoids reading rows past the pushed-down local LIMIT.
2982
0
        size_t cap = (_opts.read_limit > _rows_returned) ? (_opts.read_limit - _rows_returned) : 0;
2983
0
        if (cap < nrows_read_limit) {
2984
0
            nrows_read_limit = static_cast<uint32_t>(cap);
2985
0
        }
2986
0
    }
2987
12.9k
    DBUG_EXECUTE_IF("segment_iterator.topn_opt_1", {
2988
12.9k
        if (nrows_read_limit != 1) {
2989
12.9k
            return Status::Error<ErrorCode::INTERNAL_ERROR>(
2990
12.9k
                    "topn opt 1 execute failed: nrows_read_limit={}, "
2991
12.9k
                    "_opts.read_limit={}",
2992
12.9k
                    nrows_read_limit, _opts.read_limit);
2993
12.9k
        }
2994
12.9k
    })
2995
2996
12.9k
    RETURN_IF_ERROR(_init_current_block(block, _current_return_columns, nrows_read_limit));
2997
12.9k
    _converted_column_ids.assign(_schema->columns().size(), false);
2998
2999
12.9k
    _selected_size = 0;
3000
12.9k
    RETURN_IF_ERROR(_read_columns_by_index(nrows_read_limit, _selected_size));
3001
12.9k
    _replace_version_col_if_needed(_predicate_column_ids, _selected_size);
3002
12.9k
    _update_lsn_col_if_needed(_predicate_column_ids, _selected_size);
3003
12.9k
    _update_tso_col_if_needed(_predicate_column_ids, _selected_size);
3004
3005
12.9k
    _opts.stats->blocks_load += 1;
3006
12.9k
    _opts.stats->raw_rows_read += _selected_size;
3007
3008
12.9k
    if (_selected_size == 0) {
3009
2.81k
        return _process_eof(block);
3010
2.81k
    }
3011
3012
10.1k
    if (_is_need_vec_eval || _is_need_short_eval || _is_need_expr_eval) {
3013
1.69k
        _sel_rowid_idx.resize(_selected_size);
3014
3015
1.69k
        if (_is_need_vec_eval || _is_need_short_eval) {
3016
1.69k
            _convert_dict_code_for_predicate_if_necessary();
3017
3018
            // step 1: evaluate vectorization predicate
3019
1.69k
            _selected_size =
3020
1.69k
                    _evaluate_vectorization_predicate(_sel_rowid_idx.data(), _selected_size);
3021
3022
            // step 2: evaluate short circuit predicate
3023
            // todo(wb) research whether need to read short predicate after vectorization evaluation
3024
            //          to reduce cost of read short circuit columns.
3025
            //          In SSB test, it make no difference; So need more scenarios to test
3026
1.69k
            _selected_size =
3027
1.69k
                    _evaluate_short_circuit_predicate(_sel_rowid_idx.data(), _selected_size);
3028
1.69k
            VLOG_DEBUG << fmt::format("After evaluate predicates, selected size: {} ",
3029
0
                                      _selected_size);
3030
1.69k
            if (_selected_size > 0) {
3031
                // step 3.1: output short circuit and predicate column
3032
                // when lazy materialization enables, _predicate_column_ids = distinct(_short_cir_pred_column_ids + _vec_pred_column_ids)
3033
                // see _vec_init_lazy_materialization
3034
                // todo(wb) need to tell input columnids from output columnids
3035
1.66k
                RETURN_IF_ERROR(_output_column_by_sel_idx(block, _predicate_column_ids,
3036
1.66k
                                                          _sel_rowid_idx.data(), _selected_size));
3037
3038
                // step 3.2: read remaining expr column and evaluate it.
3039
1.66k
                if (_is_need_expr_eval) {
3040
                    // The predicate column contains the remaining expr column, no need second read.
3041
0
                    if (_common_expr_column_ids.size() > 0) {
3042
0
                        SCOPED_RAW_TIMER(&_opts.stats->non_predicate_read_ns);
3043
0
                        RETURN_IF_ERROR(_read_columns_by_rowids(
3044
0
                                _common_expr_column_ids, _block_rowids, _sel_rowid_idx.data(),
3045
0
                                _selected_size, &_current_return_columns));
3046
0
                        _replace_version_col_if_needed(_common_expr_column_ids, _selected_size);
3047
0
                        _update_lsn_col_if_needed(_common_expr_column_ids, _selected_size);
3048
0
                        _update_tso_col_if_needed(_common_expr_column_ids, _selected_size);
3049
0
                        RETURN_IF_ERROR(_process_columns(_common_expr_column_ids, block));
3050
0
                    }
3051
3052
0
                    DCHECK(block->columns() > _schema_block_id_map[*_common_expr_columns.begin()]);
3053
0
                    RETURN_IF_ERROR(
3054
0
                            _process_common_expr(_sel_rowid_idx.data(), _selected_size, block));
3055
0
                }
3056
1.66k
            } else {
3057
30
                _fill_column_nothing();
3058
30
                if (_is_need_expr_eval) {
3059
0
                    RETURN_IF_ERROR(_process_columns(_common_expr_column_ids, block));
3060
0
                }
3061
30
            }
3062
1.69k
        } else if (_is_need_expr_eval) {
3063
0
            DCHECK(!_predicate_column_ids.empty());
3064
0
            RETURN_IF_ERROR(_process_columns(_predicate_column_ids, block));
3065
            // first read all rows are insert block, initialize sel_rowid_idx to all rows.
3066
0
            for (uint16_t i = 0; i < _selected_size; ++i) {
3067
0
                _sel_rowid_idx[i] = i;
3068
0
            }
3069
0
            RETURN_IF_ERROR(_process_common_expr(_sel_rowid_idx.data(), _selected_size, block));
3070
0
        }
3071
3072
1.69k
        RETURN_IF_ERROR(_apply_read_limit_to_selected_rows(block, _selected_size));
3073
3074
        // step4: read non_predicate column
3075
1.69k
        if (_selected_size > 0) {
3076
1.66k
            if (!_non_predicate_columns.empty()) {
3077
1.38k
                RETURN_IF_ERROR(_read_columns_by_rowids(
3078
1.38k
                        _non_predicate_columns, _block_rowids, _sel_rowid_idx.data(),
3079
1.38k
                        _selected_size, &_current_return_columns,
3080
1.38k
                        _opts.condition_cache_digest && !_find_condition_cache));
3081
1.38k
                _replace_version_col_if_needed(_non_predicate_columns, _selected_size);
3082
1.38k
                _update_lsn_col_if_needed(_non_predicate_columns, _selected_size);
3083
1.38k
                _update_tso_col_if_needed(_non_predicate_columns, _selected_size);
3084
1.38k
            } else {
3085
286
                if (_opts.condition_cache_digest && !_find_condition_cache) {
3086
0
                    auto& condition_cache = *_condition_cache;
3087
0
                    for (size_t i = 0; i < _selected_size; ++i) {
3088
0
                        auto rowid = _block_rowids[_sel_rowid_idx[i]];
3089
0
                        condition_cache[rowid / SegmentIterator::CONDITION_CACHE_OFFSET] = true;
3090
0
                    }
3091
0
                }
3092
286
            }
3093
1.66k
        }
3094
1.69k
    }
3095
3096
    // step5: output columns
3097
10.1k
    RETURN_IF_ERROR(_output_non_pred_columns(block));
3098
    // Convert inverted index bitmaps to result columns for virtual column exprs
3099
    // (e.g., MATCH projections). This must run before _materialization_of_virtual_column
3100
    // so that fast_execute() can find the pre-computed result columns.
3101
10.1k
    if (!_virtual_column_exprs.empty()) {
3102
0
        bool use_sel = _is_need_vec_eval || _is_need_short_eval || _is_need_expr_eval;
3103
0
        uint16_t* sel_rowid_idx = use_sel ? _sel_rowid_idx.data() : nullptr;
3104
0
        std::vector<VExprContext*> vir_ctxs;
3105
0
        vir_ctxs.reserve(_virtual_column_exprs.size());
3106
0
        for (auto& [cid, ctx] : _virtual_column_exprs) {
3107
0
            vir_ctxs.push_back(ctx.get());
3108
0
        }
3109
0
        _output_index_result_column(vir_ctxs, sel_rowid_idx, _selected_size, block);
3110
0
    }
3111
10.1k
    RETURN_IF_ERROR(_materialization_of_virtual_column(block));
3112
    // shrink char_type suffix zero data
3113
10.1k
    block->shrink_char_type_column_suffix_zero(_char_type_idx);
3114
10.1k
    if (_opts.read_limit > 0) {
3115
0
        _rows_returned += block->rows();
3116
0
    }
3117
10.1k
    return _check_output_block(block);
3118
10.1k
}
3119
3120
0
Status SegmentIterator::_process_columns(const std::vector<ColumnId>& column_ids, Block* block) {
3121
0
    RETURN_IF_ERROR(_convert_to_expected_type(column_ids));
3122
0
    for (auto cid : column_ids) {
3123
0
        auto loc = _schema_block_id_map[cid];
3124
0
        block->replace_by_position(loc, std::move(_current_return_columns[cid]));
3125
0
    }
3126
0
    return Status::OK();
3127
0
}
3128
3129
30
void SegmentIterator::_fill_column_nothing() {
3130
    // If column_predicate filters out all rows, the corresponding column in _current_return_columns[cid] must be a ColumnNothing.
3131
    // Because:
3132
    // 1. Before each batch, _init_return_columns is called to initialize _current_return_columns, and virtual columns in _current_return_columns are initialized as ColumnNothing.
3133
    // 2. When select_size == 0, the read method of VirtualColumnIterator will definitely not be called, so the corresponding Column remains a ColumnNothing
3134
30
    for (const auto pair : _vir_cid_to_idx_in_block) {
3135
0
        auto cid = pair.first;
3136
0
        auto pos = pair.second;
3137
0
        [[maybe_unused]] const auto* nothing_col =
3138
0
                assert_cast<const ColumnNothing*>(_current_return_columns[cid].get());
3139
0
        _current_return_columns[cid] = _opts.vir_col_idx_to_type[pos]->create_column();
3140
0
    }
3141
30
}
3142
3143
10.1k
Status SegmentIterator::_check_output_block(Block* block) {
3144
10.1k
#ifndef NDEBUG
3145
10.1k
    size_t rows = block->rows();
3146
10.1k
    size_t idx = 0;
3147
20.7k
    for (const auto& entry : *block) {
3148
20.7k
        if (!entry.column) {
3149
0
            return Status::InternalError(
3150
0
                    "Column in idx {} is null, block columns {}, normal_columns {}, "
3151
0
                    "virtual_columns {}",
3152
0
                    idx, block->columns(), _schema->num_column_ids(), _virtual_column_exprs.size());
3153
20.7k
        } else if (check_and_get_column<ColumnNothing>(entry.column.get())) {
3154
0
            if (rows > 0) {
3155
0
                std::vector<std::string> vcid_to_idx;
3156
0
                for (const auto& pair : _vir_cid_to_idx_in_block) {
3157
0
                    vcid_to_idx.push_back(fmt::format("{}-{}", pair.first, pair.second));
3158
0
                }
3159
0
                std::string vir_cid_to_idx_in_block_msg =
3160
0
                        fmt::format("_vir_cid_to_idx_in_block:[{}]", fmt::join(vcid_to_idx, ","));
3161
0
                return Status::InternalError(
3162
0
                        "Column in idx {} is nothing, block columns {}, normal_columns {}, "
3163
0
                        "vir_cid_to_idx_in_block_msg {}",
3164
0
                        idx, block->columns(), _schema->num_column_ids(),
3165
0
                        vir_cid_to_idx_in_block_msg);
3166
0
            }
3167
20.7k
        } else if (entry.column->size() != rows) {
3168
0
            return Status::InternalError(
3169
0
                    "Unmatched size {}, expected {}, column: {}, type: {}, idx_in_block: {}, "
3170
0
                    "block: {}",
3171
0
                    entry.column->size(), rows, entry.column->get_name(), entry.type->get_name(),
3172
0
                    idx, block->dump_structure());
3173
0
        }
3174
20.7k
        idx++;
3175
20.7k
    }
3176
10.1k
#endif
3177
10.1k
    return Status::OK();
3178
10.1k
}
3179
3180
0
Status SegmentIterator::_process_column_predicate() {
3181
0
    return Status::OK();
3182
0
}
3183
3184
2.81k
Status SegmentIterator::_process_eof(Block* block) {
3185
    // Convert all columns in _current_return_columns to schema column
3186
2.81k
    RETURN_IF_ERROR(_convert_to_expected_type(_schema->column_ids()));
3187
9.66k
    for (int i = 0; i < block->columns(); i++) {
3188
6.84k
        auto cid = _schema->column_id(i);
3189
6.84k
        if (!_is_pred_column[cid]) {
3190
6.56k
            block->replace_by_position(i, std::move(_current_return_columns[cid]));
3191
6.56k
        }
3192
6.84k
    }
3193
2.81k
    block->clear_column_data();
3194
    // clear and release iterators memory footprint in advance
3195
2.81k
    _column_iterators.clear();
3196
2.81k
    _index_iterators.clear();
3197
2.81k
    return Status::EndOfFile("no more data in segment");
3198
2.81k
}
3199
3200
Status SegmentIterator::_process_common_expr(uint16_t* sel_rowid_idx, uint16_t& selected_size,
3201
0
                                             Block* block) {
3202
    // Here we just use col0 as row_number indicator. when reach here, we will calculate the predicates first.
3203
    //  then use the result to reduce our data read(that is, expr push down). there's now row in block means the first
3204
    //  column is not in common expr. so it's safe to replace it temporarily to provide correct `selected_size`.
3205
0
    VLOG_DEBUG << fmt::format("Execute common expr. block rows {}, selected size {}", block->rows(),
3206
0
                              _selected_size);
3207
3208
0
    bool need_mock_col = block->rows() != selected_size;
3209
0
    MutableColumnPtr col0;
3210
0
    if (need_mock_col) {
3211
0
        col0 = std::move(*block->get_by_position(0).column).mutate();
3212
0
        block->replace_by_position(
3213
0
                0, block->get_by_position(0).type->create_column_const_with_default_value(
3214
0
                           _selected_size));
3215
0
    }
3216
3217
0
    std::vector<VExprContext*> common_ctxs;
3218
0
    common_ctxs.reserve(_common_expr_ctxs_push_down.size());
3219
0
    for (auto& ctx : _common_expr_ctxs_push_down) {
3220
0
        common_ctxs.push_back(ctx.get());
3221
0
    }
3222
0
    _output_index_result_column(common_ctxs, _sel_rowid_idx.data(), _selected_size, block);
3223
0
    block->shrink_char_type_column_suffix_zero(_char_type_idx);
3224
0
    RETURN_IF_ERROR(_execute_common_expr(_sel_rowid_idx.data(), _selected_size, block));
3225
3226
0
    if (need_mock_col) {
3227
0
        block->replace_by_position(0, std::move(col0));
3228
0
    }
3229
3230
0
    VLOG_DEBUG << fmt::format("Execute common expr end. block rows {}, selected size {}",
3231
0
                              block->rows(), _selected_size);
3232
0
    return Status::OK();
3233
0
}
3234
3235
Status SegmentIterator::_execute_common_expr(uint16_t* sel_rowid_idx, uint16_t& selected_size,
3236
0
                                             Block* block) {
3237
0
    SCOPED_RAW_TIMER(&_opts.stats->expr_filter_ns);
3238
0
    DCHECK(!_remaining_conjunct_roots.empty());
3239
0
    DCHECK(block->rows() != 0);
3240
0
    int prev_columns = block->columns();
3241
0
    uint16_t original_size = selected_size;
3242
0
    _opts.stats->expr_cond_input_rows += original_size;
3243
3244
0
    IColumn::Filter filter;
3245
0
    RETURN_IF_ERROR(VExprContext::execute_conjuncts_and_filter_block(
3246
0
            _common_expr_ctxs_push_down, block, _columns_to_filter, prev_columns, filter));
3247
3248
0
    selected_size = _evaluate_common_expr_filter(sel_rowid_idx, selected_size, filter);
3249
0
    _opts.stats->rows_expr_cond_filtered += original_size - selected_size;
3250
0
    return Status::OK();
3251
0
}
3252
3253
uint16_t SegmentIterator::_evaluate_common_expr_filter(uint16_t* sel_rowid_idx,
3254
                                                       uint16_t selected_size,
3255
0
                                                       const IColumn::Filter& filter) {
3256
0
    size_t count = filter.size() - simd::count_zero_num((int8_t*)filter.data(), filter.size());
3257
0
    if (count == 0) {
3258
0
        return 0;
3259
0
    } else {
3260
0
        const UInt8* filt_pos = filter.data();
3261
3262
0
        uint16_t new_size = 0;
3263
0
        uint32_t sel_pos = 0;
3264
0
        const uint32_t sel_end = selected_size;
3265
0
        static constexpr size_t SIMD_BYTES = simd::bits_mask_length();
3266
0
        const uint32_t sel_end_simd = sel_pos + selected_size / SIMD_BYTES * SIMD_BYTES;
3267
3268
0
        while (sel_pos < sel_end_simd) {
3269
0
            auto mask = simd::bytes_mask_to_bits_mask(filt_pos + sel_pos);
3270
0
            if (0 == mask) {
3271
                //pass
3272
0
            } else if (simd::bits_mask_all() == mask) {
3273
0
                for (uint32_t i = 0; i < SIMD_BYTES; i++) {
3274
0
                    sel_rowid_idx[new_size++] = sel_rowid_idx[sel_pos + i];
3275
0
                }
3276
0
            } else {
3277
0
                simd::iterate_through_bits_mask(
3278
0
                        [&](const size_t bit_pos) {
3279
0
                            sel_rowid_idx[new_size++] = sel_rowid_idx[sel_pos + bit_pos];
3280
0
                        },
3281
0
                        mask);
3282
0
            }
3283
0
            sel_pos += SIMD_BYTES;
3284
0
        }
3285
3286
0
        for (; sel_pos < sel_end; sel_pos++) {
3287
0
            if (filt_pos[sel_pos]) {
3288
0
                sel_rowid_idx[new_size++] = sel_rowid_idx[sel_pos];
3289
0
            }
3290
0
        }
3291
0
        return new_size;
3292
0
    }
3293
0
}
3294
3295
void SegmentIterator::_output_index_result_column(const std::vector<VExprContext*>& expr_ctxs,
3296
                                                  uint16_t* sel_rowid_idx, uint16_t select_size,
3297
0
                                                  Block* block) {
3298
0
    SCOPED_RAW_TIMER(&_opts.stats->output_index_result_column_timer);
3299
0
    if (block->rows() == 0) {
3300
0
        return;
3301
0
    }
3302
0
    for (auto* expr_ctx_ptr : expr_ctxs) {
3303
0
        auto index_ctx = expr_ctx_ptr->get_index_context();
3304
0
        if (index_ctx == nullptr) {
3305
0
            continue;
3306
0
        }
3307
0
        for (auto& inverted_index_result_bitmap_for_expr : index_ctx->get_index_result_bitmap()) {
3308
0
            const auto* expr = inverted_index_result_bitmap_for_expr.first;
3309
0
            const auto& result_bitmap = inverted_index_result_bitmap_for_expr.second;
3310
0
            const auto& index_result_bitmap = result_bitmap.get_data_bitmap();
3311
0
            auto index_result_column = ColumnUInt8::create();
3312
0
            ColumnUInt8::Container& vec_match_pred = index_result_column->get_data();
3313
0
            vec_match_pred.resize(block->rows());
3314
0
            std::fill(vec_match_pred.begin(), vec_match_pred.end(), 0);
3315
3316
0
            const auto& null_bitmap = result_bitmap.get_null_bitmap();
3317
0
            bool has_null_bitmap = null_bitmap != nullptr && !null_bitmap->isEmpty();
3318
0
            bool expr_returns_nullable = expr->data_type()->is_nullable();
3319
3320
0
            ColumnUInt8::MutablePtr null_map_column = nullptr;
3321
0
            ColumnUInt8::Container* null_map_data = nullptr;
3322
0
            if (has_null_bitmap && expr_returns_nullable) {
3323
0
                null_map_column = ColumnUInt8::create();
3324
0
                auto& null_map_vec = null_map_column->get_data();
3325
0
                null_map_vec.resize(block->rows());
3326
0
                std::fill(null_map_vec.begin(), null_map_vec.end(), 0);
3327
0
                null_map_data = &null_map_column->get_data();
3328
0
            }
3329
3330
0
            roaring::BulkContext bulk_context;
3331
0
            for (uint32_t i = 0; i < select_size; i++) {
3332
0
                auto rowid = sel_rowid_idx ? _block_rowids[sel_rowid_idx[i]] : _block_rowids[i];
3333
0
                if (index_result_bitmap) {
3334
0
                    vec_match_pred[i] = index_result_bitmap->containsBulk(bulk_context, rowid);
3335
0
                }
3336
0
                if (null_map_data != nullptr && null_bitmap->contains(rowid)) {
3337
0
                    (*null_map_data)[i] = 1;
3338
0
                    vec_match_pred[i] = 0;
3339
0
                }
3340
0
            }
3341
3342
0
            DCHECK(block->rows() == vec_match_pred.size());
3343
3344
0
            if (null_map_column) {
3345
0
                index_ctx->set_index_result_column_for_expr(
3346
0
                        expr, ColumnNullable::create(std::move(index_result_column),
3347
0
                                                     std::move(null_map_column)));
3348
0
            } else {
3349
0
                index_ctx->set_index_result_column_for_expr(expr, std::move(index_result_column));
3350
0
            }
3351
0
        }
3352
0
    }
3353
0
}
3354
3355
1.69k
void SegmentIterator::_convert_dict_code_for_predicate_if_necessary() {
3356
1.69k
    for (auto predicate : _short_cir_eval_predicate) {
3357
0
        _convert_dict_code_for_predicate_if_necessary_impl(predicate);
3358
0
    }
3359
3360
1.69k
    for (auto predicate : _pre_eval_block_predicate) {
3361
0
        _convert_dict_code_for_predicate_if_necessary_impl(predicate);
3362
0
    }
3363
3364
1.69k
    for (auto column_id : _delete_range_column_ids) {
3365
1.55k
        _current_return_columns[column_id].get()->convert_dict_codes_if_necessary();
3366
1.55k
    }
3367
3368
1.69k
    for (auto column_id : _delete_bloom_filter_column_ids) {
3369
0
        _current_return_columns[column_id].get()->initialize_hash_values_for_runtime_filter();
3370
0
    }
3371
1.69k
}
3372
3373
void SegmentIterator::_convert_dict_code_for_predicate_if_necessary_impl(
3374
0
        std::shared_ptr<ColumnPredicate> predicate) {
3375
0
    auto& column = _current_return_columns[predicate->column_id()];
3376
0
    auto* col_ptr = column.get();
3377
3378
0
    if (PredicateTypeTraits::is_range(predicate->type())) {
3379
0
        col_ptr->convert_dict_codes_if_necessary();
3380
0
    } else if (PredicateTypeTraits::is_bloom_filter(predicate->type())) {
3381
0
        col_ptr->initialize_hash_values_for_runtime_filter();
3382
0
    }
3383
0
}
3384
3385
2.93k
Status SegmentIterator::current_block_row_locations(std::vector<RowLocation>* block_row_locations) {
3386
2.93k
    DCHECK(_opts.record_rowids);
3387
2.93k
    DCHECK_GE(_block_rowids.size(), _selected_size);
3388
2.93k
    block_row_locations->resize(_selected_size);
3389
2.93k
    uint32_t sid = segment_id();
3390
2.93k
    if (!_is_need_vec_eval && !_is_need_short_eval && !_is_need_expr_eval) {
3391
4.24M
        for (auto i = 0; i < _selected_size; i++) {
3392
4.23M
            (*block_row_locations)[i] = RowLocation(sid, _block_rowids[i]);
3393
4.23M
        }
3394
1.76k
    } else {
3395
2.46M
        for (auto i = 0; i < _selected_size; i++) {
3396
2.46M
            (*block_row_locations)[i] = RowLocation(sid, _block_rowids[_sel_rowid_idx[i]]);
3397
2.46M
        }
3398
1.16k
    }
3399
2.93k
    return Status::OK();
3400
2.93k
}
3401
3402
2.82k
Status SegmentIterator::_construct_compound_expr_context() {
3403
2.82k
    ColumnIteratorOptions iter_opts {
3404
2.82k
            .use_page_cache = _opts.use_page_cache,
3405
2.82k
            .file_reader = _file_reader.get(),
3406
2.82k
            .stats = _opts.stats,
3407
2.82k
            .io_ctx = _opts.io_ctx,
3408
2.82k
    };
3409
2.82k
    auto inverted_index_context = std::make_shared<IndexExecContext>(
3410
2.82k
            _schema->column_ids(), _index_iterators, _storage_name_and_type,
3411
2.82k
            _common_expr_index_exec_status, _score_runtime, _segment.get(), iter_opts);
3412
2.82k
    inverted_index_context->set_index_query_context(_index_query_context);
3413
2.82k
    for (const auto& expr_ctx : _opts.common_expr_ctxs_push_down) {
3414
0
        VExprContextSPtr context;
3415
        // _ann_range_search_runtime will do deep copy.
3416
0
        RETURN_IF_ERROR(expr_ctx->clone(_opts.runtime_state, context));
3417
0
        context->set_index_context(inverted_index_context);
3418
0
        _common_expr_ctxs_push_down.emplace_back(context);
3419
0
    }
3420
    // Clone virtual column exprs before setting IndexExecContext, because
3421
    // IndexExecContext holds segment-specific index iterator references.
3422
    // Without cloning, shared VExprContext would be overwritten per-segment
3423
    // and could point to the wrong segment's context.
3424
2.82k
    for (auto& [cid, expr_ctx] : _virtual_column_exprs) {
3425
0
        VExprContextSPtr context;
3426
0
        RETURN_IF_ERROR(expr_ctx->clone(_opts.runtime_state, context));
3427
0
        context->set_index_context(inverted_index_context);
3428
0
        expr_ctx = context;
3429
0
    }
3430
2.82k
    RETURN_IF_ERROR(rebind_storage_exprs_to_reader_schema(
3431
2.82k
            _opts, *_schema, _common_expr_ctxs_push_down, _virtual_column_exprs));
3432
2.82k
    return Status::OK();
3433
2.82k
}
3434
3435
2.82k
void SegmentIterator::_calculate_expr_in_remaining_conjunct_root() {
3436
2.82k
    for (const auto& root_expr_ctx : _common_expr_ctxs_push_down) {
3437
0
        const auto& root_expr = root_expr_ctx->root();
3438
0
        if (root_expr == nullptr) {
3439
0
            continue;
3440
0
        }
3441
0
        _common_expr_to_slotref_map[root_expr_ctx.get()] = std::unordered_map<ColumnId, VExpr*>();
3442
3443
0
        std::stack<VExprSPtr> stack;
3444
0
        stack.emplace(root_expr);
3445
3446
0
        while (!stack.empty()) {
3447
0
            const auto& expr = stack.top();
3448
0
            stack.pop();
3449
3450
0
            for (const auto& child : expr->children()) {
3451
0
                if (child->is_virtual_slot_ref()) {
3452
                    // Expand virtual slot ref to its underlying expression tree and
3453
                    // collect real slot refs used inside. We still associate those
3454
                    // slot refs with the current parent expr node for inverted index
3455
                    // tracking, just like normal slot refs.
3456
0
                    auto* vir_slot_ref = assert_cast<VirtualSlotRef*>(child.get());
3457
0
                    auto vir_expr = vir_slot_ref->get_virtual_column_expr();
3458
0
                    if (vir_expr) {
3459
0
                        std::stack<VExprSPtr> vir_stack;
3460
0
                        vir_stack.emplace(vir_expr);
3461
3462
0
                        while (!vir_stack.empty()) {
3463
0
                            const auto& vir_node = vir_stack.top();
3464
0
                            vir_stack.pop();
3465
3466
0
                            for (const auto& vir_child : vir_node->children()) {
3467
0
                                if (vir_child->is_slot_ref()) {
3468
0
                                    auto* inner_slot_ref = assert_cast<VSlotRef*>(vir_child.get());
3469
0
                                    _common_expr_index_exec_status[_schema->column_id(
3470
0
                                            inner_slot_ref->column_id())][expr.get()] = false;
3471
0
                                    _common_expr_to_slotref_map[root_expr_ctx.get()]
3472
0
                                                               [inner_slot_ref->column_id()] =
3473
0
                                                                       expr.get();
3474
0
                                }
3475
3476
0
                                if (!vir_child->children().empty()) {
3477
0
                                    vir_stack.emplace(vir_child);
3478
0
                                }
3479
0
                            }
3480
0
                        }
3481
0
                    }
3482
0
                }
3483
                // Example: CAST(v['a'] AS VARCHAR) MATCH 'hello', do not add CAST expr to index tracking.
3484
0
                auto expr_without_cast = VExpr::expr_without_cast(child);
3485
0
                if (expr_without_cast->is_slot_ref() && expr->op() != TExprOpcode::CAST) {
3486
0
                    auto* column_slot_ref = assert_cast<VSlotRef*>(expr_without_cast.get());
3487
0
                    _common_expr_index_exec_status[_schema->column_id(column_slot_ref->column_id())]
3488
0
                                                  [expr.get()] = false;
3489
0
                    _common_expr_to_slotref_map[root_expr_ctx.get()][column_slot_ref->column_id()] =
3490
0
                            expr.get();
3491
0
                }
3492
0
            }
3493
3494
0
            const auto& children = expr->children();
3495
0
            for (int i = cast_set<int>(children.size()) - 1; i >= 0; --i) {
3496
0
                if (!children[i]->children().empty()) {
3497
0
                    stack.emplace(children[i]);
3498
0
                }
3499
0
            }
3500
0
        }
3501
0
    }
3502
2.82k
}
3503
3504
bool SegmentIterator::_no_need_read_key_data(ColumnId cid, MutableColumnPtr& column,
3505
27.1k
                                             size_t nrows_read) {
3506
27.1k
    if (_opts.runtime_state && !_opts.runtime_state->query_options().enable_no_need_read_data_opt) {
3507
0
        return false;
3508
0
    }
3509
3510
27.1k
    if (!((_opts.tablet_schema->keys_type() == KeysType::DUP_KEYS ||
3511
27.1k
           (_opts.tablet_schema->keys_type() == KeysType::UNIQUE_KEYS &&
3512
11.1k
            _opts.enable_unique_key_merge_on_write)))) {
3513
7.58k
        return false;
3514
7.58k
    }
3515
3516
19.5k
    if (_opts.push_down_agg_type_opt != TPushAggOp::COUNT_ON_INDEX) {
3517
19.5k
        return false;
3518
19.5k
    }
3519
3520
0
    if (!_opts.tablet_schema->column(cid).is_key()) {
3521
0
        return false;
3522
0
    }
3523
3524
0
    if (_has_delete_predicate(cid)) {
3525
0
        return false;
3526
0
    }
3527
3528
0
    if (!_check_all_conditions_passed_inverted_index_for_column(cid)) {
3529
0
        return false;
3530
0
    }
3531
3532
0
    if (column->is_nullable()) {
3533
0
        auto* nullable_col_ptr = reinterpret_cast<ColumnNullable*>(column.get());
3534
0
        nullable_col_ptr->get_null_map_column().insert_many_defaults(nrows_read);
3535
0
        nullable_col_ptr->get_nested_column_ptr()->insert_many_defaults(nrows_read);
3536
0
    } else {
3537
0
        column->insert_many_defaults(nrows_read);
3538
0
    }
3539
3540
0
    return true;
3541
0
}
3542
3543
19.5k
bool SegmentIterator::_has_delete_predicate(ColumnId cid) {
3544
19.5k
    std::set<uint32_t> delete_columns_set;
3545
19.5k
    _opts.delete_condition_predicates->get_all_column_ids(delete_columns_set);
3546
19.5k
    return delete_columns_set.contains(cid);
3547
19.5k
}
3548
3549
12.9k
bool SegmentIterator::_can_opt_limit_reads() {
3550
12.9k
    if (_opts.read_limit == 0) {
3551
12.9k
        return false;
3552
12.9k
    }
3553
3554
    // If SegmentIterator still needs to evaluate predicates/common exprs, LIMIT must be applied to
3555
    // post-filter rows by _apply_read_limit_to_selected_rows(); capping the raw read here could
3556
    // return fewer rows than the query LIMIT.
3557
7
    if (_is_need_vec_eval || _is_need_short_eval || _is_need_expr_eval) {
3558
3
        return false;
3559
3
    }
3560
3561
4
    if (_opts.delete_condition_predicates->num_of_column_predicate() > 0) {
3562
1
        return false;
3563
1
    }
3564
3565
3
    bool all_true = std::ranges::all_of(_schema->column_ids(), [this](auto cid) {
3566
3
        if (cid == _opts.tablet_schema->delete_sign_idx()) {
3567
0
            return true;
3568
0
        }
3569
3
        if (_check_all_conditions_passed_inverted_index_for_column(cid, true)) {
3570
2
            return true;
3571
2
        }
3572
1
        return false;
3573
3
    });
3574
3575
3
    DBUG_EXECUTE_IF("segment_iterator.topn_opt_1", {
3576
3
        LOG(INFO) << "col_predicates: " << _col_predicates.size() << ", all_true: " << all_true;
3577
3
    })
3578
3579
3
    DBUG_EXECUTE_IF("segment_iterator.topn_opt_2", {
3580
3
        if (all_true) {
3581
3
            return Status::Error<ErrorCode::INTERNAL_ERROR>("topn opt 2 execute failed");
3582
3
        }
3583
3
    })
3584
3585
3
    return all_true;
3586
3
}
3587
3588
// Before get next batch. make sure all virtual columns in block has type ColumnNothing.
3589
12.9k
void SegmentIterator::_init_virtual_columns(Block* block) {
3590
12.9k
    for (const auto& pair : _vir_cid_to_idx_in_block) {
3591
0
        auto& col_with_type_and_name = block->get_by_position(pair.second);
3592
0
        col_with_type_and_name.column = ColumnNothing::create(0);
3593
0
        col_with_type_and_name.type = _opts.vir_col_idx_to_type[pair.second];
3594
0
    }
3595
12.9k
}
3596
3597
10.1k
Status SegmentIterator::_materialization_of_virtual_column(Block* block) {
3598
    // Some expr can not process empty block, such as function `element_at`.
3599
    // So materialize virtual column in advance to avoid errors.
3600
10.1k
    if (block->rows() == 0) {
3601
30
        for (const auto& pair : _vir_cid_to_idx_in_block) {
3602
0
            auto& col_with_type_and_name = block->get_by_position(pair.second);
3603
0
            col_with_type_and_name.column = _opts.vir_col_idx_to_type[pair.second]->create_column();
3604
0
            col_with_type_and_name.type = _opts.vir_col_idx_to_type[pair.second];
3605
0
        }
3606
30
        return Status::OK();
3607
30
    }
3608
3609
10.0k
    for (const auto& cid_and_expr : _virtual_column_exprs) {
3610
0
        auto cid = cid_and_expr.first;
3611
0
        auto column_expr = cid_and_expr.second;
3612
0
        size_t idx_in_block = _vir_cid_to_idx_in_block[cid];
3613
0
        if (block->columns() <= idx_in_block) {
3614
0
            return Status::InternalError(
3615
0
                    "Virtual column index {} is out of range, block columns {}, "
3616
0
                    "virtual columns size {}, virtual column expr {}",
3617
0
                    idx_in_block, block->columns(), _vir_cid_to_idx_in_block.size(),
3618
0
                    column_expr->root()->debug_string());
3619
0
        } else if (block->get_by_position(idx_in_block).column.get() == nullptr) {
3620
0
            return Status::InternalError(
3621
0
                    "Virtual column index {} is null, block columns {}, virtual columns size {}, "
3622
0
                    "virtual column expr {}",
3623
0
                    idx_in_block, block->columns(), _vir_cid_to_idx_in_block.size(),
3624
0
                    column_expr->root()->debug_string());
3625
0
        }
3626
0
        block->shrink_char_type_column_suffix_zero(_char_type_idx);
3627
0
        if (check_and_get_column<const ColumnNothing>(
3628
0
                    block->get_by_position(idx_in_block).column.get())) {
3629
0
            VLOG_DEBUG << fmt::format("Virtual column is doing materialization, cid {}, col idx {}",
3630
0
                                      cid, idx_in_block);
3631
0
            ColumnPtr result_column;
3632
0
            RETURN_IF_ERROR(column_expr->execute(block, result_column));
3633
3634
0
            block->replace_by_position(idx_in_block, std::move(result_column));
3635
0
            if (block->get_by_position(idx_in_block).column->size() == 0) {
3636
0
                LOG_WARNING("Result of expr column {} is empty. cid {}, idx_in_block {}",
3637
0
                            column_expr->root()->debug_string(), cid, idx_in_block);
3638
0
            }
3639
0
        }
3640
0
    }
3641
10.0k
    return Status::OK();
3642
10.0k
}
3643
3644
2.82k
void SegmentIterator::_prepare_score_column_materialization() {
3645
2.82k
    if (_score_runtime == nullptr) {
3646
2.82k
        return;
3647
2.82k
    }
3648
3649
0
    ScoreRangeFilterPtr filter;
3650
0
    if (_score_runtime->has_score_range_filter()) {
3651
0
        const auto& range_info = _score_runtime->get_score_range_info();
3652
0
        filter = std::make_shared<ScoreRangeFilter>(range_info->op, range_info->threshold);
3653
0
    }
3654
3655
0
    IColumn::MutablePtr result_column;
3656
0
    auto result_row_ids = std::make_unique<std::vector<uint64_t>>();
3657
0
    if (_score_runtime->get_limit() > 0 && _col_predicates.empty() &&
3658
0
        _common_expr_ctxs_push_down.empty()) {
3659
0
        OrderType order_type = _score_runtime->is_asc() ? OrderType::ASC : OrderType::DESC;
3660
0
        _index_query_context->collection_similarity->get_topn_bm25_scores(
3661
0
                &_row_bitmap, result_column, result_row_ids, order_type,
3662
0
                _score_runtime->get_limit(), filter);
3663
0
    } else {
3664
0
        _index_query_context->collection_similarity->get_bm25_scores(&_row_bitmap, result_column,
3665
0
                                                                     result_row_ids, filter);
3666
0
    }
3667
0
    const size_t dst_col_idx = _score_runtime->get_dest_column_idx();
3668
0
    auto* column_iter = _column_iterators[_schema->column_id(dst_col_idx)].get();
3669
0
    auto* virtual_column_iter = dynamic_cast<VirtualColumnIterator*>(column_iter);
3670
0
    virtual_column_iter->prepare_materialization(
3671
0
            std::move(result_column),
3672
0
            std::shared_ptr<std::vector<uint64_t>>(std::move(result_row_ids)));
3673
0
}
3674
3675
} // namespace segment_v2
3676
} // namespace doris