Coverage Report

Created: 2026-10-18 18:43

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