Coverage Report

Created: 2026-10-31 03:52

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