Coverage Report

Created: 2026-07-30 07:32

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/storage/segment/column_reader.h
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
#pragma once
19
20
#include <gen_cpp/Descriptors_types.h>
21
#include <gen_cpp/segment_v2.pb.h>
22
#include <glog/logging.h>
23
#include <sys/types.h>
24
25
#include <cstddef> // for size_t
26
#include <cstdint> // for uint32_t
27
#include <functional>
28
#include <map>
29
#include <memory> // for unique_ptr
30
#include <optional>
31
#include <string>
32
#include <utility>
33
#include <vector>
34
35
#include "common/compiler_util.h"
36
#include "common/config.h"
37
#include "common/logging.h"
38
#include "common/status.h"            // for Status
39
#include "core/column/column_array.h" // ColumnArray
40
#include "core/data_type/data_type.h"
41
#include "core/data_type/storage_field_type.h"
42
#include "io/cache/cached_remote_file_reader.h"
43
#include "io/fs/file_reader_writer_fwd.h"
44
#include "io/io_common.h"
45
#include "storage/index/index_reader.h"
46
#include "storage/index/ordinal_page_index.h" // for OrdinalPageIndexIterator
47
#include "storage/index/zone_map/zone_map_index.h"
48
#include "storage/olap_common.h"
49
#include "storage/predicate/column_predicate.h"
50
#include "storage/segment/common.h"
51
#include "storage/segment/page_handle.h" // for PageHandle
52
#include "storage/segment/page_pointer.h"
53
#include "storage/segment/parsed_page.h" // for ParsedPage
54
#include "storage/segment/row_ranges.h"
55
#include "storage/segment/segment_prefetcher.h"
56
#include "storage/segment/stream_reader.h"
57
#include "storage/tablet/tablet_schema.h"
58
#include "storage/types.h"
59
#include "storage/utils.h"
60
#include "util/once.h"
61
62
namespace doris {
63
64
class BlockCompressionCodec;
65
class AndBlockColumnPredicate;
66
class ColumnPredicate;
67
class TabletIndex;
68
class StorageReadOptions;
69
70
namespace io {
71
class FileReader;
72
} // namespace io
73
struct Slice;
74
struct StringRef;
75
76
using TColumnAccessPaths = std::vector<TColumnAccessPath>;
77
78
namespace segment_v2 {
79
class EncodingInfo;
80
class ColumnIterator;
81
class BloomFilterIndexReader;
82
class InvertedIndexIterator;
83
class InvertedIndexReader;
84
class IndexFileReader;
85
class PageDecoder;
86
class RowRanges;
87
class ZoneMapIndexReader;
88
class IndexIterator;
89
class ColumnMetaAccessor;
90
91
struct ColumnReaderOptions {
92
    // whether verify checksum when read page
93
    bool verify_checksum = true;
94
    // for in memory olap table, use DURABLE CachePriority in page cache
95
    bool kept_in_memory = false;
96
97
    int be_exec_version = -1;
98
99
    TabletSchemaSPtr tablet_schema = nullptr;
100
101
    // When set, ColumnReader::create returns a ConstantColumnReader carrying this value instead
102
    // of reading on-disk data. Used for read-time-filled constant columns (e.g.
103
    // __DORIS_COMMIT_TSO_COL__) on a single-version segment, whose on-disk value is only a
104
    // placeholder. The value is constant within a segment, so the resulting reader is cacheable.
105
    std::optional<Field> const_value = std::nullopt;
106
};
107
108
struct ColumnIteratorOptions {
109
    bool use_page_cache = false;
110
    bool is_predicate_column = false;
111
    // for page cache allocation
112
    // page types are divided into DATA_PAGE & INDEX_PAGE
113
    // INDEX_PAGE including index_page, dict_page and short_key_page
114
    PageTypePB type = PageTypePB::UNKNOWN_PAGE_TYPE;
115
    io::FileReader* file_reader = nullptr; // Ref
116
    // reader statistics
117
    OlapReaderStatistics* stats = nullptr; // Ref
118
    io::IOContext io_ctx;
119
    bool only_read_offsets = false;
120
121
2.09M
    void sanity_check() const {
122
2.09M
        CHECK_NOTNULL(file_reader);
123
2.09M
        CHECK_NOTNULL(stats);
124
2.09M
    }
125
};
126
127
class ColumnIterator;
128
class OffsetFileColumnIterator;
129
class FileColumnIterator;
130
131
using ColumnIteratorUPtr = std::unique_ptr<ColumnIterator>;
132
using OffsetFileColumnIteratorUPtr = std::unique_ptr<OffsetFileColumnIterator>;
133
using FileColumnIteratorUPtr = std::unique_ptr<FileColumnIterator>;
134
using ColumnIteratorSPtr = std::shared_ptr<ColumnIterator>;
135
136
// There will be concurrent users to read the same column. So
137
// we should do our best to reduce resource usage through share
138
// same information, such as OrdinalPageIndex and Page data.
139
// This will cache data shared by all reader
140
class ColumnReader : public MetadataAdder<ColumnReader>,
141
                     public std::enable_shared_from_this<ColumnReader> {
142
public:
143
    ColumnReader();
144
    // Create an initialized ColumnReader in *reader.
145
    // This should be a lightweight operation without I/O.
146
    static Status create(const ColumnReaderOptions& opts, const ColumnMetaPB& meta,
147
                         uint64_t num_rows, const io::FileReaderSPtr& file_reader,
148
                         std::shared_ptr<ColumnReader>* reader);
149
150
    static Status create_array(const ColumnReaderOptions& opts, const ColumnMetaPB& meta,
151
                               const io::FileReaderSPtr& file_reader,
152
                               std::shared_ptr<ColumnReader>* reader);
153
    static Status create_map(const ColumnReaderOptions& opts, const ColumnMetaPB& meta,
154
                             const io::FileReaderSPtr& file_reader,
155
                             std::shared_ptr<ColumnReader>* reader);
156
    static Status create_struct(const ColumnReaderOptions& opts, const ColumnMetaPB& meta,
157
                                uint64_t num_rows, const io::FileReaderSPtr& file_reader,
158
                                std::shared_ptr<ColumnReader>* reader);
159
    static Status create_agg_state(const ColumnReaderOptions& opts, const ColumnMetaPB& meta,
160
                                   uint64_t num_rows, const io::FileReaderSPtr& file_reader,
161
                                   std::shared_ptr<ColumnReader>* reader);
162
163
    enum DictEncodingType { UNKNOWN_DICT_ENCODING, PARTIAL_DICT_ENCODING, ALL_DICT_ENCODING };
164
165
    static bool is_compaction_reader_type(ReaderType type);
166
167
    ~ColumnReader() override;
168
169
    // create a new column iterator. Client should delete returned iterator
170
    virtual Status new_iterator(ColumnIteratorUPtr* iterator, const TabletColumn* col,
171
                                const StorageReadOptions*);
172
    Status new_iterator(ColumnIteratorUPtr* iterator, const TabletColumn* tablet_column);
173
    Status new_array_iterator(ColumnIteratorUPtr* iterator, const TabletColumn* tablet_column);
174
    Status new_struct_iterator(ColumnIteratorUPtr* iterator, const TabletColumn* tablet_column);
175
    Status new_map_iterator(ColumnIteratorUPtr* iterator, const TabletColumn* tablet_column);
176
    Status new_agg_state_iterator(ColumnIteratorUPtr* iterator);
177
178
    Status new_index_iterator(const std::shared_ptr<IndexFileReader>& index_file_reader,
179
                              const TabletIndex* index_meta, const std::string& rowset_id,
180
                              uint32_t segment_id, size_t rows_of_segment,
181
                              std::unique_ptr<IndexIterator>* iterator);
182
183
    Status seek_at_or_before(ordinal_t ordinal, OrdinalPageIndexIterator* iter,
184
                             const ColumnIteratorOptions& iter_opts);
185
    Status get_ordinal_index_reader(OrdinalIndexReader*& reader,
186
                                    OlapReaderStatistics* index_load_stats);
187
188
    // read a page from file into a page handle
189
    Status read_page(const ColumnIteratorOptions& iter_opts, const PagePointer& pp,
190
                     PageHandle* handle, Slice* page_body, PageFooterPB* footer,
191
                     BlockCompressionCodec* codec) const;
192
193
852k
    bool is_nullable() const { return _meta_is_nullable; }
194
195
31.8M
    const EncodingInfo* encoding_info() const { return _encoding_info; }
196
197
2.10M
    virtual bool has_zone_map() const { return _zone_map_index != nullptr; }
198
    bool has_bloom_filter_index(bool ngram) const;
199
    // Check if this column could match `cond' using segment zone map.
200
    // Since segment zone map is stored in metadata, this function is fast without I/O.
201
    // set matched to true if segment zone map is absent or `cond' could be satisfied, false otherwise.
202
    virtual Status match_condition(const AndBlockColumnPredicate* col_predicates,
203
                                   bool* matched) const;
204
205
    Status next_batch_of_zone_map(size_t* n, MutableColumnPtr& dst) const;
206
207
    // get row ranges with zone map
208
    // - cond_column is user's query predicate
209
    // - delete_condition is a delete predicate of one version
210
    Status get_row_ranges_by_zone_map(
211
            const AndBlockColumnPredicate* col_predicates,
212
            const std::vector<std::shared_ptr<const ColumnPredicate>>* delete_predicates,
213
            RowRanges* row_ranges, const ColumnIteratorOptions& iter_opts);
214
215
    // get row ranges with bloom filter index
216
    Status get_row_ranges_by_bloom_filter(const AndBlockColumnPredicate* col_predicates,
217
                                          RowRanges* row_ranges,
218
                                          const ColumnIteratorOptions& iter_opts);
219
220
413k
    PagePointer get_dict_page_pointer() const { return _meta_dict_page; }
221
222
28.7M
    bool is_empty() const { return _num_rows == 0; }
223
224
    Status prune_predicates_by_zone_map(std::vector<std::shared_ptr<ColumnPredicate>>& predicates,
225
                                        const int column_id, bool* pruned) const;
226
227
    virtual Status get_segment_zone_map(segment_v2::ZoneMap* zone_map) const;
228
    Status get_page_zone_maps(const ColumnIteratorOptions& iter_opts,
229
                              const std::vector<ZoneMapPB>** zone_maps);
230
    Status get_row_range_for_page(uint32_t page_index, const ColumnIteratorOptions& iter_opts,
231
                                  RowRange* row_range);
232
233
28.6M
    CompressionTypePB get_compression() const { return _meta_compression; }
234
235
770k
    uint64_t num_rows() const { return _num_rows; }
236
237
8.47k
    void set_dict_encoding_type(DictEncodingType type) {
238
8.47k
        static_cast<void>(_set_dict_encoding_type_once.call([&] {
239
8.45k
            _dict_encoding_type = type;
240
8.45k
            return Status::OK();
241
8.45k
        }));
242
8.47k
    }
243
244
17.0M
    DictEncodingType get_dict_encoding_type() { return _dict_encoding_type; }
245
246
27.5M
    void disable_index_meta_cache() { _use_index_page_cache = false; }
247
248
19.7k
    DataTypePtr get_vec_data_type() { return _data_type; }
249
250
57.1M
    virtual FieldType get_meta_type() { return _meta_type; }
251
252
    int64_t get_metadata_size() const override;
253
254
#ifdef BE_TEST
255
    void check_data_by_zone_map_for_test(const MutableColumnPtr& dst) const;
256
#endif
257
258
private:
259
    friend class VariantColumnReader;
260
    friend class FileColumnIterator;
261
    friend class SegmentPrefetcher;
262
263
    ColumnReader(const ColumnReaderOptions& opts, const ColumnMetaPB& meta, uint64_t num_rows,
264
                 io::FileReaderSPtr file_reader);
265
    Status init(const ColumnMetaPB* meta);
266
267
    [[nodiscard]] Status _load_zone_map_index(bool use_page_cache, bool kept_in_memory,
268
                                              const ColumnIteratorOptions& iter_opts);
269
    [[nodiscard]] Status _load_ordinal_index(bool use_page_cache, bool kept_in_memory,
270
                                             const ColumnIteratorOptions& iter_opts);
271
272
    [[nodiscard]] Status _load_index(const std::shared_ptr<IndexFileReader>& index_file_reader,
273
                                     const TabletIndex* index_meta, const std::string& rowset_id,
274
                                     uint32_t segment_id, size_t rows_of_segment);
275
    [[nodiscard]] Status _load_bloom_filter_index(bool use_page_cache, bool kept_in_memory,
276
                                                  const ColumnIteratorOptions& iter_opts);
277
278
    bool _zone_map_match_condition(const segment_v2::ZoneMap& zone_map,
279
                                   const AndBlockColumnPredicate* col_predicates) const;
280
281
    Status _get_filtered_pages(
282
            const AndBlockColumnPredicate* col_predicates,
283
            const std::vector<std::shared_ptr<const ColumnPredicate>>* delete_predicates,
284
            std::vector<uint32_t>* page_indexes, const ColumnIteratorOptions& iter_opts);
285
286
    Status _calculate_row_ranges(const std::vector<uint32_t>& page_indexes, RowRanges* row_ranges,
287
                                 const ColumnIteratorOptions& iter_opts);
288
289
    int64_t _meta_length;
290
    FieldType _meta_type;
291
    FieldType _meta_children_column_type;
292
    bool _meta_is_nullable;
293
    bool _use_index_page_cache;
294
    int _be_exec_version = -1;
295
296
    PagePointer _meta_dict_page;
297
    CompressionTypePB _meta_compression;
298
299
    ColumnReaderOptions _opts;
300
    uint64_t _num_rows;
301
302
    io::FileReaderSPtr _file_reader;
303
304
    DictEncodingType _dict_encoding_type;
305
306
    DataTypePtr _data_type;
307
308
    FieldType _type =
309
            FieldType::OLAP_FIELD_TYPE_NONE; // initialized in init(), may changed by subclasses.
310
    const EncodingInfo* _encoding_info =
311
            nullptr; // initialized in init(), used for create PageDecoder
312
313
    // meta for various column indexes (null if the index is absent)
314
    std::unique_ptr<ZoneMapPB> _segment_zone_map;
315
316
    mutable std::shared_mutex _load_index_lock;
317
    std::unique_ptr<ZoneMapIndexReader> _zone_map_index;
318
    std::unique_ptr<OrdinalIndexReader> _ordinal_index;
319
    std::shared_ptr<BloomFilterIndexReader> _bloom_filter_index;
320
321
    std::unordered_map<int64_t, IndexReaderPtr> _index_readers;
322
323
    std::vector<std::shared_ptr<ColumnReader>> _sub_readers;
324
325
    DorisCallOnce<Status> _set_dict_encoding_type_once;
326
};
327
328
// Base iterator to read one column data
329
class ColumnIterator {
330
public:
331
28.9M
    ColumnIterator() = default;
332
29.0M
    virtual ~ColumnIterator() = default;
333
334
36.8k
    virtual Status init(const ColumnIteratorOptions& opts) {
335
36.8k
        _opts = opts;
336
36.8k
        return Status::OK();
337
36.8k
    }
338
339
    // Seek to the given ordinal entry in the column.
340
    // Entry 0 is the first entry written to the column.
341
    // If provided seek point is past the end of the file,
342
    // then returns false.
343
    virtual Status seek_to_ordinal(ordinal_t ord) = 0;
344
345
2.37M
    Status next_batch(size_t* n, MutableColumnPtr& dst) {
346
2.37M
        bool has_null;
347
2.37M
        return next_batch(n, dst, &has_null);
348
2.37M
    }
349
350
0
    virtual Status next_batch(size_t* n, MutableColumnPtr& dst, bool* has_null) {
351
0
        return Status::NotSupported("next_batch not implement");
352
0
    }
353
354
0
    virtual Status next_batch_of_zone_map(size_t* n, MutableColumnPtr& dst) {
355
0
        return Status::NotSupported("next_batch_of_zone_map not implement");
356
0
    }
357
358
    virtual Status read_by_rowids(const rowid_t* rowids, const size_t count,
359
0
                                  MutableColumnPtr& dst) {
360
0
        return Status::NotSupported("read_by_rowids not implement");
361
0
    }
362
363
    virtual ordinal_t get_current_ordinal() const = 0;
364
365
    virtual Status get_row_ranges_by_zone_map(
366
            const AndBlockColumnPredicate* col_predicates,
367
            const std::vector<std::shared_ptr<const ColumnPredicate>>* delete_predicates,
368
68
            RowRanges* row_ranges) {
369
68
        return Status::OK();
370
68
    }
371
372
    virtual Status get_row_ranges_by_bloom_filter(const AndBlockColumnPredicate* col_predicates,
373
68
                                                  RowRanges* row_ranges) {
374
68
        return Status::OK();
375
68
    }
376
377
    virtual Status get_row_ranges_by_dict(const AndBlockColumnPredicate* col_predicates,
378
66
                                          RowRanges* row_ranges) {
379
66
        return Status::OK();
380
66
    }
381
382
2
    virtual bool is_all_dict_encoding() const { return false; }
383
384
    virtual Status set_access_paths(const TColumnAccessPaths& all_access_paths,
385
1.21k
                                    const TColumnAccessPaths& predicate_access_paths) {
386
1.21k
        if (!predicate_access_paths.empty()) {
387
53
            set_read_requirement_self(ReadRequirement::PREDICATE);
388
53
        }
389
1.21k
        return Status::OK();
390
1.21k
    }
391
392
28.9M
    void set_column_name(const std::string& column_name) { _column_name = column_name; }
393
394
73.3k
    const std::string& column_name() const { return _column_name; }
395
396
    // Per-iterator read requirement derived from nested access paths.
397
    //
398
    // The ordering is intentional and used by set_read_requirement_self(): requirements are
399
    // monotonic and a weaker requirement must not downgrade a stronger one.
400
    // - NORMAL: no pruning decision has been made yet.
401
    // - SKIP: this iterator should not be read.
402
    // - LAZY_OUTPUT: materialize this iterator in the lazy phase after predicate filtering.
403
    // - PREDICATE: read this iterator in the predicate phase. This must stay stronger than
404
    //   LAZY_OUTPUT because parents may mark children as LAZY_OUTPUT after child set_access_paths()
405
    //   has already promoted predicate-only children to PREDICATE.
406
    enum class ReadRequirement : int { NORMAL, SKIP, LAZY_OUTPUT, PREDICATE };
407
408
    // Set the read requirement on this iterator and all nested child iterators.
409
95.0k
    virtual void set_read_requirement(ReadRequirement requirement) {
410
95.0k
        set_read_requirement_self(requirement);
411
95.0k
    }
412
413
33.9k
    ReadRequirement read_requirement() const { return _read_requirement; }
414
415
85.4k
    virtual void set_lazy_output_requirement() {
416
85.4k
        set_read_requirement(ReadRequirement::LAZY_OUTPUT);
417
85.4k
    }
418
419
90.2k
    virtual void remove_pruned_sub_iterators() {};
420
421
0
    virtual Status init_prefetcher(const SegmentPrefetchParams& params) { return Status::OK(); }
422
423
    virtual void collect_prefetchers(
424
            std::map<PrefetcherInitMethod, std::vector<SegmentPrefetcher*>>& prefetchers,
425
0
            PrefetcherInitMethod init_method) {}
426
427
    static constexpr const char* ACCESS_OFFSET = "OFFSET";
428
    static constexpr const char* ACCESS_ALL = "*";
429
    static constexpr const char* ACCESS_MAP_KEYS = "KEYS";
430
    static constexpr const char* ACCESS_MAP_VALUES = "VALUES";
431
    static constexpr const char* ACCESS_NULL = "NULL";
432
433
    // Meta-only read modes:
434
    // - OFFSET_ONLY: read offsets while skipping actual child/string data. For nullable
435
    //   complex columns, the parent null map is still materialized when needed.
436
    // - NULL_MAP_ONLY: only read null map (e.g., for IS NULL / IS NOT NULL predicates)
437
    // When these modes are enabled, actual content data is skipped.
438
    enum class MetaReadMode : int { DEFAULT, OFFSET_ONLY, NULL_MAP_ONLY };
439
440
17.5M
    bool read_offset_only() const { return _meta_read_mode == MetaReadMode::OFFSET_ONLY; }
441
3.95M
    bool read_null_map_only() const { return _meta_read_mode == MetaReadMode::NULL_MAP_ONLY; }
442
443
    // The current scanner phase. This is intentionally separate from ReadRequirement
444
    // (why this iterator is needed) and MetaReadMode (what physical metadata to read).
445
    enum class ReadPhase : int {
446
        NORMAL,    // default full materialization without lazy read split
447
        PREDICATE, // predicate evaluation before row filtering
448
        LAZY       // post-filter lazy materialization
449
    };
450
451
9.29M
    virtual void set_read_phase(ReadPhase mode) {
452
9.29M
        _read_phase = mode;
453
9.29M
        if (mode == ReadPhase::PREDICATE) {
454
8.43k
            _has_place_holder_column = false;
455
8.43k
        }
456
9.29M
    }
457
458
7.39M
    virtual bool need_to_read() const {
459
7.39M
        switch (_read_phase) {
460
7.35M
        case ReadPhase::NORMAL:
461
7.35M
            return _read_requirement != ReadRequirement::SKIP;
462
9.77k
        case ReadPhase::PREDICATE:
463
9.77k
            return _read_requirement == ReadRequirement::PREDICATE;
464
31.9k
        case ReadPhase::LAZY:
465
31.9k
            return _read_requirement == ReadRequirement::LAZY_OUTPUT;
466
0
        default:
467
0
            return false;
468
7.39M
        }
469
7.39M
    }
470
471
    // Whether the current iterator itself should materialize meta columns, such as
472
    // the null-map column or the offset column, into the destination column.
473
    //
474
    // Do not use the virtual need_to_read() here. Complex iterators override
475
    // need_to_read() in LAZY mode to keep the parent iterator active when only a
476
    // nested child still has data to materialize. That parent-level control-flow
477
    // decision is different from materializing the parent's own offsets/null-map:
478
    // if the parent was already read for predicate evaluation, LAZY mode should
479
    // only fill the missing children and must not append parent meta again.
480
164k
    bool need_to_read_meta_columns() const { return ColumnIterator::need_to_read(); }
481
482
3.60k
    virtual void finalize_lazy_phase(MutableColumnPtr& dst) {
483
3.60k
        _recovery_from_place_holder_column(dst);
484
3.60k
    }
485
486
    // Set only this iterator's requirement without modifying requirements of any nested child
487
    // iterators. Use this when the parent/wrapper state must be updated while child requirements
488
    // are decided independently.
489
213k
    virtual void set_read_requirement_self(ReadRequirement requirement) {
490
213k
        if (static_cast<int>(requirement) > static_cast<int>(_read_requirement)) {
491
155k
            _read_requirement = requirement;
492
155k
        }
493
213k
    }
494
495
    // Whether this iterator or any nested iterator has data that must be materialized
496
    // in lazy mode. Predicate-only branches are read before filtering and must not be
497
    // re-read in the lazy phase. Meta-only access paths still become lazy targets when
498
    // they appear only in all_access_paths, because OFFSET/NULL is the requested output.
499
24.3k
    virtual bool has_lazy_read_target() const {
500
24.3k
        return _read_requirement == ReadRequirement::LAZY_OUTPUT;
501
24.3k
    }
502
503
protected:
504
    struct AccessPathSplit {
505
        TColumnAccessPaths descendant_paths;
506
        bool reads_current_data = false;
507
        MetaReadMode current_meta_mode = MetaReadMode::DEFAULT;
508
509
174k
        bool has_descendant_paths() const { return !descendant_paths.empty(); }
510
    };
511
512
    // Nested columns share the same current-level access-path planning, while their data-child
513
    // topology and descendant routing remain container-specific.
514
    struct NestedAccessPathPlan {
515
        AccessPathSplit all;
516
        AccessPathSplit predicate;
517
        bool skip_data_descendants = false;
518
    };
519
520
    // At their current level, Struct supports null-map metadata. Map and Array additionally
521
    // support offsets.
522
    enum class NestedMetaSupport { NULL_MAP, NULL_MAP_AND_OFFSET };
523
524
    void _convert_to_place_holder_column(MutableColumnPtr& dst, size_t count);
525
526
    void _recovery_from_place_holder_column(MutableColumnPtr& dst);
527
528
    // Derive current-level meta-only read mode from an explicit access-path split. Meta-only is
529
    // valid only when this iterator had no data-read requirement before applying the current paths,
530
    // no current DATA path exists, and no path must be routed to a descendant iterator.
531
    Status _check_and_set_meta_read_mode(ReadRequirement requirement_before_access_path,
532
                                         const AccessPathSplit& all_access_paths);
533
534
    // Apply the common current-level access-path state transitions and select a supported
535
    // parent-owned meta-only mode. When that mode skips data descendants, synchronously invoke the
536
    // callback once with SKIP before returning the routing plan. The callback is never retained.
537
    Result<NestedAccessPathPlan> _prepare_nested_access_paths(
538
            const TColumnAccessPaths& all_access_paths,
539
            const TColumnAccessPaths& predicate_access_paths, NestedMetaSupport meta_support,
540
            const std::function<void(ReadRequirement)>& set_all_data_descendants_read_requirement);
541
542
    // Normalize the wire encoding, strip this iterator's column name, and explicitly partition
543
    // paths consumed by this iterator from paths that must be routed to descendants. This helper is
544
    // intentionally side-effect free; callers apply DATA/predicate read requirements explicitly.
545
    Result<AccessPathSplit> _split_access_paths(TColumnAccessPaths access_paths) const;
546
    ColumnIteratorOptions _opts;
547
548
    ReadRequirement _read_requirement {ReadRequirement::NORMAL};
549
    MetaReadMode _meta_read_mode = MetaReadMode::DEFAULT;
550
    ReadPhase _read_phase {ReadPhase::NORMAL};
551
    std::string _column_name;
552
553
    bool _has_place_holder_column {false};
554
};
555
556
// This iterator is used to read column data from file
557
// for scalar type
558
class FileColumnIterator : public ColumnIterator {
559
public:
560
    explicit FileColumnIterator(std::shared_ptr<ColumnReader> reader);
561
    ~FileColumnIterator() override;
562
563
    Status init(const ColumnIteratorOptions& opts) override;
564
565
    Status seek_to_ordinal(ordinal_t ord) override;
566
567
    Status seek_to_page_start();
568
569
    Status next_batch(size_t* n, MutableColumnPtr& dst, bool* has_null) override;
570
571
    Status next_batch_of_zone_map(size_t* n, MutableColumnPtr& dst) override;
572
573
    Status read_by_rowids(const rowid_t* rowids, const size_t count,
574
                          MutableColumnPtr& dst) override;
575
576
    Status set_access_paths(const TColumnAccessPaths& all_access_paths,
577
                            const TColumnAccessPaths& predicate_access_paths) override;
578
579
2
    ordinal_t get_current_ordinal() const override { return _current_ordinal; }
580
581
    // get row ranges by zone map
582
    // - cond_column is user's query predicate
583
    // - delete_condition is delete predicate of one version
584
    Status get_row_ranges_by_zone_map(
585
            const AndBlockColumnPredicate* col_predicates,
586
            const std::vector<std::shared_ptr<const ColumnPredicate>>* delete_predicates,
587
            RowRanges* row_ranges) override;
588
589
    Status get_row_ranges_by_bloom_filter(const AndBlockColumnPredicate* col_predicates,
590
                                          RowRanges* row_ranges) override;
591
592
    Status get_row_ranges_by_dict(const AndBlockColumnPredicate* col_predicates,
593
                                  RowRanges* row_ranges) override;
594
595
537k
    ParsedPage* get_current_page() { return &_page; }
596
597
0
    bool is_nullable() { return _reader->is_nullable(); }
598
599
10.4k
    bool is_all_dict_encoding() const override { return _is_all_dict_encoding; }
600
601
    Status init_prefetcher(const SegmentPrefetchParams& params) override;
602
    void collect_prefetchers(
603
            std::map<PrefetcherInitMethod, std::vector<SegmentPrefetcher*>>& prefetchers,
604
            PrefetcherInitMethod init_method) override;
605
606
protected:
607
    // Exposed to derived iterators (e.g. StringFileColumnIterator) so they can
608
    // query column metadata such as the storage field type.
609
509
    const std::shared_ptr<ColumnReader>& get_reader() const { return _reader; }
610
611
private:
612
    Status _seek_to_pos_in_page(ParsedPage* page, ordinal_t offset_in_page) const;
613
    Status _load_next_page(bool* eos);
614
    Status _read_data_page(const OrdinalPageIndexIterator& iter);
615
    Status _read_dict_data();
616
    void _trigger_prefetch_if_eligible(ordinal_t ord);
617
618
    std::shared_ptr<ColumnReader> _reader = nullptr;
619
620
    BlockCompressionCodec* _compress_codec = nullptr;
621
622
    // 1. The _page represents current page.
623
    // 2. We define an operation is one seek and following read,
624
    //    If new seek is issued, the _page will be reset.
625
    ParsedPage _page;
626
627
    // keep dict page decoder
628
    std::unique_ptr<PageDecoder> _dict_decoder;
629
630
    // keep dict page handle to avoid released
631
    PageHandle _dict_page_handle;
632
633
    // page iterator used to get next page when current page is finished.
634
    // This value will be reset when a new seek is issued
635
    OrdinalPageIndexIterator _page_iter;
636
637
    // current value ordinal
638
    ordinal_t _current_ordinal = 0;
639
640
    bool _is_all_dict_encoding = false;
641
642
    std::unique_ptr<StringRef[]> _dict_word_info;
643
644
    bool _enable_prefetch {false};
645
    std::unique_ptr<SegmentPrefetcher> _prefetcher;
646
    std::shared_ptr<io::CachedRemoteFileReader> _cached_remote_file_reader {nullptr};
647
};
648
649
class EmptyFileColumnIterator final : public ColumnIterator {
650
public:
651
19.9k
    Status seek_to_ordinal(ordinal_t ord) override { return Status::OK(); }
652
0
    ordinal_t get_current_ordinal() const override { return 0; }
653
};
654
655
// StringFileColumnIterator extends FileColumnIterator's NULL metadata support with OFFSET-only
656
// reading for string/binary column types. When the OFFSET path is detected in set_access_paths, it
657
// sets only_read_offsets on the ColumnIteratorOptions so that the BinaryPlainPageDecoder skips
658
// chars memcpy and only fills offsets.
659
class StringFileColumnIterator final : public FileColumnIterator {
660
public:
661
    explicit StringFileColumnIterator(std::shared_ptr<ColumnReader> reader);
662
    ~StringFileColumnIterator() override = default;
663
664
    Status init(const ColumnIteratorOptions& opts) override;
665
666
    Status set_access_paths(const TColumnAccessPaths& all_access_paths,
667
                            const TColumnAccessPaths& predicate_access_paths) override;
668
};
669
670
// This iterator make offset operation write once for
671
class OffsetFileColumnIterator final : public ColumnIterator {
672
public:
673
97.6k
    explicit OffsetFileColumnIterator(FileColumnIteratorUPtr offset_reader) {
674
97.6k
        _offset_iterator = std::move(offset_reader);
675
97.6k
    }
676
677
98.2k
    ~OffsetFileColumnIterator() override = default;
678
679
    Status init(const ColumnIteratorOptions& opts) override;
680
681
    Status next_batch(size_t* n, MutableColumnPtr& dst, bool* has_null) override;
682
683
0
    Status next_batch(size_t* n, MutableColumnPtr& dst) {
684
0
        bool has_null;
685
0
        return next_batch(n, dst, &has_null);
686
0
    }
687
688
0
    ordinal_t get_current_ordinal() const override {
689
0
        return _offset_iterator->get_current_ordinal();
690
0
    }
691
140k
    Status seek_to_ordinal(ordinal_t ord) override {
692
140k
        RETURN_IF_ERROR(_offset_iterator->seek_to_ordinal(ord));
693
140k
        return Status::OK();
694
140k
    }
695
696
    Status _peek_one_offset(ordinal_t* offset);
697
698
    Status _calculate_offsets(ssize_t start, ColumnArray::ColumnOffsets& column_offsets);
699
700
    Status read_by_rowids(const rowid_t* rowids, const size_t count,
701
25.0k
                          MutableColumnPtr& dst) override {
702
25.0k
        return _offset_iterator->read_by_rowids(rowids, count, dst);
703
25.0k
    }
704
705
0
    void set_read_requirement(ReadRequirement requirement) override {
706
0
        set_read_requirement_self(requirement);
707
0
        _offset_iterator->set_read_requirement(requirement);
708
0
    }
709
710
    Status init_prefetcher(const SegmentPrefetchParams& params) override;
711
    void collect_prefetchers(
712
            std::map<PrefetcherInitMethod, std::vector<SegmentPrefetcher*>>& prefetchers,
713
            PrefetcherInitMethod init_method) override;
714
715
private:
716
    std::unique_ptr<FileColumnIterator> _offset_iterator;
717
    // reuse a tiny column for peek to avoid frequent allocations
718
    MutableColumnPtr _peek_tmp_col;
719
};
720
721
// This iterator is used to read map value column
722
class MapFileColumnIterator final : public ColumnIterator {
723
public:
724
    explicit MapFileColumnIterator(std::shared_ptr<ColumnReader> reader,
725
                                   ColumnIteratorUPtr null_iterator,
726
                                   OffsetFileColumnIteratorUPtr offsets_iterator,
727
                                   ColumnIteratorUPtr key_iterator,
728
                                   ColumnIteratorUPtr val_iterator);
729
730
32.9k
    ~MapFileColumnIterator() override = default;
731
732
    Status init(const ColumnIteratorOptions& opts) override;
733
734
    Status next_batch(size_t* n, MutableColumnPtr& dst, bool* has_null) override;
735
736
    Status read_by_rowids(const rowid_t* rowids, const size_t count,
737
                          MutableColumnPtr& dst) override;
738
739
    Status seek_to_ordinal(ordinal_t ord) override;
740
741
0
    ordinal_t get_current_ordinal() const override {
742
0
        if (read_null_map_only() && _null_iterator) {
743
0
            return _null_iterator->get_current_ordinal();
744
0
        }
745
0
        return _offsets_iterator->get_current_ordinal();
746
0
    }
747
    Status init_prefetcher(const SegmentPrefetchParams& params) override;
748
    void collect_prefetchers(
749
            std::map<PrefetcherInitMethod, std::vector<SegmentPrefetcher*>>& prefetchers,
750
            PrefetcherInitMethod init_method) override;
751
752
    Status set_access_paths(const TColumnAccessPaths& all_access_paths,
753
                            const TColumnAccessPaths& predicate_access_paths) override;
754
755
    void set_lazy_output_requirement() override;
756
757
    void remove_pruned_sub_iterators() override;
758
759
    void set_read_phase(ReadPhase mode) override;
760
761
55.4k
    bool need_to_read() const override {
762
55.4k
        switch (_read_phase) {
763
49.8k
        case ReadPhase::NORMAL:
764
49.8k
            return _read_requirement != ReadRequirement::SKIP;
765
1.19k
        case ReadPhase::PREDICATE:
766
1.19k
            return _read_requirement == ReadRequirement::PREDICATE;
767
4.31k
        case ReadPhase::LAZY:
768
            // In lazy mode, read this map only when at least one key/value branch still
769
            // has non-predicate data to materialize.
770
4.31k
            return has_lazy_read_target();
771
0
        default:
772
0
            return false;
773
55.4k
        }
774
55.4k
    }
775
776
    void finalize_lazy_phase(MutableColumnPtr& dst) override;
777
778
    void set_read_requirement(ReadRequirement requirement) override;
779
780
    bool has_lazy_read_target() const override;
781
782
private:
783
    std::shared_ptr<ColumnReader> _map_reader = nullptr;
784
    ColumnIteratorUPtr _null_iterator;
785
    OffsetFileColumnIteratorUPtr _offsets_iterator; //OffsetFileIterator
786
    ColumnIteratorUPtr _key_iterator;
787
    ColumnIteratorUPtr _val_iterator;
788
};
789
790
class StructFileColumnIterator final : public ColumnIterator {
791
public:
792
    explicit StructFileColumnIterator(std::shared_ptr<ColumnReader> reader,
793
                                      ColumnIteratorUPtr null_iterator,
794
                                      std::vector<ColumnIteratorUPtr>&& sub_column_iterators);
795
796
8.28k
    ~StructFileColumnIterator() override = default;
797
798
    Status init(const ColumnIteratorOptions& opts) override;
799
800
    Status next_batch(size_t* n, MutableColumnPtr& dst, bool* has_null) override;
801
802
5.93k
    Status next_batch(size_t* n, MutableColumnPtr& dst) {
803
5.93k
        bool has_null;
804
5.93k
        return next_batch(n, dst, &has_null);
805
5.93k
    }
806
807
    Status read_by_rowids(const rowid_t* rowids, const size_t count,
808
                          MutableColumnPtr& dst) override;
809
810
    Status seek_to_ordinal(ordinal_t ord) override;
811
812
0
    ordinal_t get_current_ordinal() const override {
813
0
        if (read_null_map_only() && _null_iterator) {
814
0
            return _null_iterator->get_current_ordinal();
815
0
        }
816
0
        return _sub_column_iterators[0]->get_current_ordinal();
817
0
    }
818
819
    Status set_access_paths(const TColumnAccessPaths& all_access_paths,
820
                            const TColumnAccessPaths& predicate_access_paths) override;
821
822
    void set_lazy_output_requirement() override;
823
824
    void remove_pruned_sub_iterators() override;
825
826
    Status init_prefetcher(const SegmentPrefetchParams& params) override;
827
    void collect_prefetchers(
828
            std::map<PrefetcherInitMethod, std::vector<SegmentPrefetcher*>>& prefetchers,
829
            PrefetcherInitMethod init_method) override;
830
831
    void set_read_phase(ReadPhase mode) override;
832
833
27.0k
    bool need_to_read() const override {
834
27.0k
        switch (_read_phase) {
835
12.6k
        case ReadPhase::NORMAL:
836
12.6k
            return _read_requirement != ReadRequirement::SKIP;
837
3.10k
        case ReadPhase::PREDICATE:
838
3.10k
            return _read_requirement == ReadRequirement::PREDICATE;
839
11.3k
        case ReadPhase::LAZY:
840
            // In lazy mode, read this struct only when at least one nested branch still
841
            // has non-predicate data to materialize.
842
11.3k
            return has_lazy_read_target();
843
0
        default:
844
0
            return false;
845
27.0k
        }
846
27.0k
    }
847
848
    void finalize_lazy_phase(MutableColumnPtr& dst) override;
849
    void set_read_requirement(ReadRequirement requirement) override;
850
    bool has_lazy_read_target() const override;
851
852
private:
853
    std::shared_ptr<ColumnReader> _struct_reader = nullptr;
854
    ColumnIteratorUPtr _null_iterator;
855
    std::vector<ColumnIteratorUPtr> _sub_column_iterators;
856
};
857
858
class ArrayFileColumnIterator final : public ColumnIterator {
859
public:
860
    explicit ArrayFileColumnIterator(std::shared_ptr<ColumnReader> reader,
861
                                     OffsetFileColumnIteratorUPtr offset_reader,
862
                                     ColumnIteratorUPtr item_iterator,
863
                                     ColumnIteratorUPtr null_iterator);
864
865
65.3k
    ~ArrayFileColumnIterator() override = default;
866
867
    Status init(const ColumnIteratorOptions& opts) override;
868
869
    Status next_batch(size_t* n, MutableColumnPtr& dst, bool* has_null) override;
870
871
78.7k
    Status next_batch(size_t* n, MutableColumnPtr& dst) {
872
78.7k
        bool has_null;
873
78.7k
        return next_batch(n, dst, &has_null);
874
78.7k
    }
875
876
    Status read_by_rowids(const rowid_t* rowids, const size_t count,
877
                          MutableColumnPtr& dst) override;
878
879
    Status seek_to_ordinal(ordinal_t ord) override;
880
881
0
    ordinal_t get_current_ordinal() const override {
882
0
        if (read_null_map_only() && _null_iterator) {
883
0
            return _null_iterator->get_current_ordinal();
884
0
        }
885
0
        return _offset_iterator->get_current_ordinal();
886
0
    }
887
888
    Status set_access_paths(const TColumnAccessPaths& all_access_paths,
889
                            const TColumnAccessPaths& predicate_access_paths) override;
890
    void set_lazy_output_requirement() override;
891
892
    void remove_pruned_sub_iterators() override;
893
894
    Status init_prefetcher(const SegmentPrefetchParams& params) override;
895
    void collect_prefetchers(
896
            std::map<PrefetcherInitMethod, std::vector<SegmentPrefetcher*>>& prefetchers,
897
            PrefetcherInitMethod init_method) override;
898
899
    void set_read_phase(ReadPhase mode) override;
900
901
251k
    bool need_to_read() const override {
902
251k
        switch (_read_phase) {
903
243k
        case ReadPhase::NORMAL:
904
243k
            return _read_requirement != ReadRequirement::SKIP;
905
2.12k
        case ReadPhase::PREDICATE:
906
2.12k
            return _read_requirement == ReadRequirement::PREDICATE;
907
6.26k
        case ReadPhase::LAZY:
908
            // In lazy mode, read this array only when its item branch still has
909
            // non-predicate data to materialize.
910
6.26k
            return has_lazy_read_target();
911
0
        default:
912
0
            return false;
913
251k
        }
914
251k
    }
915
916
    void finalize_lazy_phase(MutableColumnPtr& dst) override;
917
918
    void set_read_requirement(ReadRequirement requirement) override;
919
920
    bool has_lazy_read_target() const override;
921
922
private:
923
    std::shared_ptr<ColumnReader> _array_reader = nullptr;
924
    std::unique_ptr<OffsetFileColumnIterator> _offset_iterator;
925
    std::unique_ptr<ColumnIterator> _null_iterator;
926
    std::unique_ptr<ColumnIterator> _item_iterator;
927
928
    Status _seek_by_offsets(ordinal_t ord);
929
};
930
931
class RowIdColumnIterator : public ColumnIterator {
932
public:
933
    RowIdColumnIterator() = delete;
934
    RowIdColumnIterator(int64_t tid, RowsetId rid, int32_t segid)
935
0
            : _tablet_id(tid), _rowset_id(rid), _segment_id(segid) {}
936
937
0
    Status seek_to_ordinal(ordinal_t ord_idx) override {
938
0
        _current_rowid = cast_set<uint32_t>(ord_idx);
939
0
        return Status::OK();
940
0
    }
941
942
0
    Status next_batch(size_t* n, MutableColumnPtr& dst) {
943
0
        bool has_null;
944
0
        return next_batch(n, dst, &has_null);
945
0
    }
946
947
0
    Status next_batch(size_t* n, MutableColumnPtr& dst, bool* has_null) override {
948
0
        for (size_t i = 0; i < *n; ++i) {
949
0
            const auto row_id = cast_set<uint32_t>(_current_rowid + i);
950
0
            GlobalRowLoacation location(_tablet_id, _rowset_id, _segment_id, row_id);
951
0
            dst->insert_data(reinterpret_cast<const char*>(&location), sizeof(GlobalRowLoacation));
952
0
        }
953
0
        _current_rowid += *n;
954
0
        return Status::OK();
955
0
    }
956
957
    Status read_by_rowids(const rowid_t* rowids, const size_t count,
958
0
                          MutableColumnPtr& dst) override {
959
0
        for (size_t i = 0; i < count; ++i) {
960
0
            rowid_t row_id = rowids[i];
961
0
            GlobalRowLoacation location(_tablet_id, _rowset_id, _segment_id, row_id);
962
0
            dst->insert_data(reinterpret_cast<const char*>(&location), sizeof(GlobalRowLoacation));
963
0
        }
964
0
        return Status::OK();
965
0
    }
966
967
0
    ordinal_t get_current_ordinal() const override { return _current_rowid; }
968
969
private:
970
    rowid_t _current_rowid = 0;
971
    int64_t _tablet_id = 0;
972
    RowsetId _rowset_id;
973
    int32_t _segment_id = 0;
974
};
975
976
// Add new RowIdColumnIteratorV2
977
class RowIdColumnIteratorV2 : public ColumnIterator {
978
public:
979
    RowIdColumnIteratorV2(uint8_t version, int64_t backend_id, uint32_t file_id)
980
9.46k
            : _version(version), _backend_id(backend_id), _file_id(file_id) {}
981
982
5.55k
    Status seek_to_ordinal(ordinal_t ord_idx) override {
983
5.55k
        _current_rowid = cast_set<uint32_t>(ord_idx);
984
5.55k
        return Status::OK();
985
5.55k
    }
986
987
0
    Status next_batch(size_t* n, MutableColumnPtr& dst) {
988
0
        bool has_null;
989
0
        return next_batch(n, dst, &has_null);
990
0
    }
991
992
    Status next_batch(size_t* n, MutableColumnPtr& dst, bool* has_null) override;
993
994
    Status read_by_rowids(const rowid_t* rowids, const size_t count,
995
                          MutableColumnPtr& dst) override;
996
997
0
    ordinal_t get_current_ordinal() const override { return _current_rowid; }
998
999
private:
1000
    uint32_t _current_rowid = 0;
1001
    uint8_t _version;
1002
    int64_t _backend_id;
1003
    uint32_t _file_id;
1004
};
1005
1006
// This iterator is used to read default value column
1007
class DefaultValueColumnIterator : public ColumnIterator {
1008
public:
1009
    DefaultValueColumnIterator(bool has_default_value, std::string default_value, bool is_nullable,
1010
                               FieldType type, int precision, int scale, int len)
1011
9.51k
            : _has_default_value(has_default_value),
1012
9.51k
              _default_value(std::move(default_value)),
1013
9.51k
              _is_nullable(is_nullable),
1014
9.51k
              _type(type),
1015
9.51k
              _precision(precision),
1016
9.51k
              _scale(scale),
1017
9.51k
              _len(len) {}
1018
1019
    Status init(const ColumnIteratorOptions& opts) override;
1020
1021
2.33k
    Status seek_to_ordinal(ordinal_t ord_idx) override {
1022
2.33k
        _current_rowid = ord_idx;
1023
2.33k
        return Status::OK();
1024
2.33k
    }
1025
1026
104
    Status next_batch(size_t* n, MutableColumnPtr& dst) {
1027
104
        bool has_null;
1028
104
        return next_batch(n, dst, &has_null);
1029
104
    }
1030
1031
    Status next_batch(size_t* n, MutableColumnPtr& dst, bool* has_null) override;
1032
1033
104
    Status next_batch_of_zone_map(size_t* n, MutableColumnPtr& dst) override {
1034
104
        return next_batch(n, dst);
1035
104
    }
1036
1037
    Status read_by_rowids(const rowid_t* rowids, const size_t count,
1038
                          MutableColumnPtr& dst) override;
1039
1040
0
    ordinal_t get_current_ordinal() const override { return _current_rowid; }
1041
1042
private:
1043
    void _insert_many_default(MutableColumnPtr& dst, size_t n);
1044
1045
    bool _has_default_value;
1046
    std::string _default_value;
1047
    bool _is_nullable;
1048
    FieldType _type;
1049
    int _precision;
1050
    int _scale;
1051
    const int _len;
1052
    Field _default_value_field;
1053
1054
    // current rowid
1055
    ordinal_t _current_rowid = 0;
1056
};
1057
1058
// Produces a column whose every row is the same constant Field value.
1059
// Used for read-time-filled constant hidden columns (e.g. __DORIS_COMMIT_TSO_COL__),
1060
// where the on-disk value is only a placeholder and the real value comes from the read
1061
// context (StorageReadOptions).
1062
class ConstantColumnIterator : public ColumnIterator {
1063
public:
1064
    ConstantColumnIterator() = delete;
1065
7
    explicit ConstantColumnIterator(Field value) : _value(std::move(value)) {}
1066
1067
1
    Status seek_to_ordinal(ordinal_t ord_idx) override {
1068
1
        _current_rowid = ord_idx;
1069
1
        return Status::OK();
1070
1
    }
1071
1072
1
    Status next_batch(size_t* n, MutableColumnPtr& dst) {
1073
1
        bool has_null;
1074
1
        return next_batch(n, dst, &has_null);
1075
1
    }
1076
1077
6
    Status next_batch(size_t* n, MutableColumnPtr& dst, bool* has_null) override {
1078
6
        *has_null = _value.is_null();
1079
6
        Status st = _insert_many(dst, *n);
1080
6
        if (!st.ok()) {
1081
0
            return st;
1082
0
        }
1083
6
        _current_rowid += *n;
1084
6
        return st;
1085
6
    }
1086
1087
1
    Status next_batch_of_zone_map(size_t* n, MutableColumnPtr& dst) override {
1088
1
        return next_batch(n, dst);
1089
1
    }
1090
1091
    Status read_by_rowids(const rowid_t* rowids, const size_t count,
1092
1
                          MutableColumnPtr& dst) override {
1093
1
        return _insert_many(dst, count);
1094
1
    }
1095
1096
3
    ordinal_t get_current_ordinal() const override { return _current_rowid; }
1097
1098
private:
1099
7
    Status _insert_many(MutableColumnPtr& dst, size_t n) {
1100
7
        if (_value.is_null()) {
1101
1
            if (UNLIKELY(!dst->is_nullable())) {
1102
0
                return Status::InternalError(
1103
0
                        "try to apply constant null value to not nullable target column");
1104
0
            }
1105
1
            dst->insert_many_defaults(n);
1106
1
            return Status::OK();
1107
1
        }
1108
6
        dst->insert_duplicate_fields(_value, n);
1109
6
        return Status::OK();
1110
7
    }
1111
1112
    Field _value;
1113
    ordinal_t _current_rowid = 0;
1114
};
1115
1116
// A ColumnReader that represents a single constant value for the whole segment instead of reading
1117
// on-disk data. Used for read-time-filled constant columns (e.g. __DORIS_COMMIT_TSO_COL__) on a
1118
// single-version segment, whose on-disk zonemap only reflects the placeholder. It advertises a
1119
// single-value [v, v] zonemap so segment-level pruning matches against the real value, and produces
1120
// a ConstantColumnIterator for data reads.
1121
class ConstantColumnReader : public ColumnReader {
1122
public:
1123
4
    explicit ConstantColumnReader(Field value) : _value(std::move(value)) {}
1124
1125
2
    bool has_zone_map() const override { return true; }
1126
1127
    // The base ColumnReader default-constructs without initializing its _meta_type. The data-read
1128
    // path (Segment::new_column_iterator) verifies tablet_column.type() == reader->get_meta_type()
1129
    // when config::enable_column_type_check is on (default true), so derive the real OLAP type from
1130
    // the constant value to avoid a spurious "different type between schema and column reader" error.
1131
3
    FieldType get_meta_type() override {
1132
3
        return primitive_type_to_storage_field_type(_value.get_type());
1133
3
    }
1134
1135
    Status match_condition(const AndBlockColumnPredicate* col_predicates,
1136
                           bool* matched) const override;
1137
1138
    Status new_iterator(ColumnIteratorUPtr* iterator, const TabletColumn* /*col*/,
1139
2
                        const StorageReadOptions* /*opt*/) override {
1140
2
        *iterator = std::make_unique<ConstantColumnIterator>(_value);
1141
2
        return Status::OK();
1142
2
    }
1143
1144
    Status get_segment_zone_map(segment_v2::ZoneMap* zone_map) const override;
1145
1146
private:
1147
    Field _value;
1148
};
1149
1150
} // namespace segment_v2
1151
} // namespace doris