Coverage Report

Created: 2026-08-25 15:02

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/storage/segment/segment.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 <butil/macros.h>
21
#include <gen_cpp/olap_file.pb.h>
22
#include <gen_cpp/segment_v2.pb.h>
23
#include <glog/logging.h>
24
25
#include <cstdint>
26
#include <map>
27
#include <memory> // for unique_ptr
28
#include <optional>
29
#include <string>
30
#include <unordered_map>
31
32
#include "agent/be_exec_version_manager.h"
33
#include "common/be_mock_util.h"
34
#include "common/status.h" // Status
35
#include "core/column/column.h"
36
#include "core/data_type/data_type.h"
37
#include "core/field.h"
38
#include "io/cache/file_cache_common.h" // io::UInt128Wrapper returned by value
39
#include "io/fs/file_reader.h"
40
#include "io/fs/file_reader_writer_fwd.h"
41
#include "io/fs/file_system.h"
42
#include "io/io_common.h"
43
#include "runtime/descriptors.h"
44
#include "storage/cache/page_cache.h"
45
#include "storage/olap_common.h"
46
#include "storage/schema.h"
47
#include "storage/segment/page_handle.h"
48
#include "storage/tablet/tablet_schema.h"
49
#include "util/once.h"
50
#include "util/slice.h"
51
namespace doris {
52
class IDataType;
53
54
class ShortKeyIndexDecoder;
55
class ReadSchema;
56
class StorageReadOptions;
57
class PrimaryKeyIndexReader;
58
class RowwiseIterator;
59
struct RowLocation;
60
61
namespace segment_v2 {
62
63
class Segment;
64
class InvertedIndexIterator;
65
class IndexFileReader;
66
class IndexIterator;
67
class ColumnReader;
68
class ColumnIterator;
69
class ColumnReaderCache;
70
class ColumnMetaAccessor;
71
72
using SegmentSharedPtr = std::shared_ptr<Segment>;
73
74
struct SparseColumnCache;
75
using SparseColumnCacheSPtr = std::shared_ptr<SparseColumnCache>;
76
77
// key is column path, value is the sparse column cache
78
// now column path is only SPARSE_COLUMN_PATH, in the future, we can add more sparse column paths
79
using PathToSparseColumnCache = std::unordered_map<std::string, SparseColumnCacheSPtr>;
80
using PathToSparseColumnCacheUPtr = std::unique_ptr<PathToSparseColumnCache>;
81
82
struct BinaryColumnCache;
83
using BinaryColumnCacheSPtr = std::shared_ptr<BinaryColumnCache>;
84
using PathToBinaryColumnCache = std::unordered_map<std::string, BinaryColumnCacheSPtr>;
85
using PathToBinaryColumnCacheUPtr = std::unique_ptr<PathToBinaryColumnCache>;
86
87
// A Segment is used to represent a segment in memory format. When segment is
88
// generated, it won't be modified, so this struct aimed to help read operation.
89
// It will prepare all ColumnReader to create ColumnIterator as needed.
90
// And user can create a RowwiseIterator through new_iterator function.
91
//
92
// NOTE: This segment is used to a specified TabletSchema, when TabletSchema
93
// is changed, this segment can not be used any more. For example, after a schema
94
// change finished, client should disable all cached Segment for old TabletSchema.
95
class Segment : public std::enable_shared_from_this<Segment>, public MetadataAdder<Segment> {
96
public:
97
    static Status open(io::FileSystemSPtr fs, const std::string& path, int64_t tablet_id,
98
                       uint32_t segment_id, RowsetId rowset_id, TabletSchemaSPtr tablet_schema,
99
                       const io::FileReaderOptions& reader_options,
100
                       std::shared_ptr<Segment>* output, InvertedIndexFileInfo idx_file_info = {},
101
                       OlapReaderStatistics* stats = nullptr,
102
                       const io::IOContext* io_ctx = nullptr);
103
104
    static io::UInt128Wrapper file_cache_key(std::string_view rowset_id, uint32_t seg_id);
105
0
    io::UInt128Wrapper file_cache_key() const {
106
0
        return file_cache_key(_rowset_id.to_string(), _segment_id);
107
0
    }
108
109
    ~Segment() override;
110
111
    int64_t get_metadata_size() const override;
112
    void update_metadata_size();
113
114
    Status new_iterator(ReadSchemaSPtr schema, const StorageReadOptions& read_options,
115
                        std::unique_ptr<RowwiseIterator>* iter);
116
117
    static Status new_default_iterator(const TabletColumn& tablet_column,
118
                                       std::unique_ptr<ColumnIterator>* iter);
119
120
10.3M
    uint32_t id() const { return _segment_id; }
121
122
1.17M
    RowsetId rowset_id() const { return _rowset_id; }
123
124
13.8M
    MOCK_FUNCTION uint32_t num_rows() const { return _num_rows; }
125
126
    // if variant_sparse_column_cache is nullptr, means the sparse column cache is not used
127
    Status new_column_iterator(const TabletColumn& tablet_column,
128
                               std::unique_ptr<ColumnIterator>* iter, const StorageReadOptions* opt,
129
                               const std::unordered_map<int32_t, PathToBinaryColumnCacheUPtr>*
130
                                       variant_sparse_column_cache = nullptr);
131
132
    Status new_index_iterator(const TabletColumn& tablet_column, const TabletIndex* index_meta,
133
                              const StorageReadOptions& read_options,
134
                              std::unique_ptr<IndexIterator>* iter);
135
136
766k
    const ShortKeyIndexDecoder* get_short_key_index() const {
137
766k
        DCHECK(_load_index_once.has_called() && _load_index_once.stored_result().ok());
138
766k
        return _sk_index_decoder.get();
139
766k
    }
140
141
4.11M
    const PrimaryKeyIndexReader* get_primary_key_index() const {
142
4.11M
        DCHECK(_load_index_once.has_called() && _load_index_once.stored_result().ok());
143
4.11M
        return _pk_index_reader.get();
144
4.11M
    }
145
146
    Status lookup_row_key(const Slice& key, const TabletSchema* latest_schema, bool with_seq_col,
147
                          bool with_rowid, RowLocation* row_location, OlapReaderStatistics* stats,
148
                          std::string* encoded_seq_value = nullptr,
149
                          const io::IOContext* io_ctx = nullptr);
150
151
    Status read_key_by_rowid(uint32_t row_id, std::string* key);
152
153
    // row_ids must be strictly increasing.
154
    Status seek_and_read_by_rowid(const TabletSchema& schema, SlotDescriptor* slot,
155
                                  const std::vector<uint32_t>& row_ids, MutableColumnPtr& result,
156
                                  StorageReadOptions& storage_read_options,
157
                                  std::unique_ptr<ColumnIterator>& iterator_hint);
158
159
    Status load_index(OlapReaderStatistics* stats, const io::IOContext* io_ctx = nullptr);
160
161
    Status load_pk_index_and_bf(OlapReaderStatistics* stats, const io::IOContext* io_ctx = nullptr);
162
163
24
    void update_healthy_status(Status new_status) { _healthy_status.update(new_status); }
164
    // The segment is loaded into SegmentCache and then will load indices, if there are something wrong
165
    // during loading indices, should remove it from SegmentCache. If not, it will always report error during
166
    // query. So we add a healthy status API, the caller should check the healhty status before using the segment.
167
    Status healthy_status();
168
169
2.04M
    std::string min_key() {
170
2.04M
        DCHECK(_tablet_schema->keys_type() == UNIQUE_KEYS && _pk_index_meta != nullptr);
171
2.04M
        return _pk_index_meta->min_key();
172
2.04M
    }
173
1.90M
    std::string max_key() {
174
1.90M
        DCHECK(_tablet_schema->keys_type() == UNIQUE_KEYS && _pk_index_meta != nullptr);
175
1.90M
        return _pk_index_meta->max_key();
176
1.90M
    }
177
178
21.8k
    io::FileReaderSPtr file_reader() { return _file_reader; }
179
180
    // Including the column reader memory.
181
    // another method `get_metadata_size` not include the column reader, only the segment object itself.
182
1.73M
    int64_t meta_mem_usage() const { return _meta_mem_usage; }
183
184
    // Variant paths use segment metadata; other columns use `read_type`.
185
    std::shared_ptr<const IDataType> get_data_type_of(const TabletColumn& read_column,
186
                                                      const DataTypePtr& read_type,
187
                                                      const StorageReadOptions& read_options);
188
189
    // If column in segment is the same type in schema, then it is safe to apply predicate.
190
    // `ordinal` is a read-schema ordinal (the coordinate ColumnPredicate::column_id() carries).
191
    bool can_apply_predicate_safely(
192
            int ordinal, const ReadSchema& schema,
193
            const std::map<std::string, DataTypePtr>& target_cast_type_for_variants,
194
2.66M
            const StorageReadOptions& read_options) {
195
2.66M
        const TabletColumn* col = schema.column(ordinal);
196
18.4E
        DCHECK(col != nullptr) << "Column not found in schema for ordinal=" << ordinal;
197
2.66M
        DataTypePtr storage_column_type =
198
2.66M
                get_data_type_of(*col, schema.data_type(ordinal), read_options);
199
2.68M
        if (storage_column_type == nullptr || col->type() != FieldType::OLAP_FIELD_TYPE_VARIANT ||
200
2.67M
            !target_cast_type_for_variants.contains(col->name())) {
201
            // Default column iterator or not variant column
202
2.67M
            return true;
203
2.67M
        }
204
18.4E
        if (storage_column_type->equals(*target_cast_type_for_variants.at(col->name()))) {
205
1.12k
            return true;
206
18.4E
        } else {
207
18.4E
            return false;
208
18.4E
        }
209
18.4E
    }
210
211
    // The tso column (__DORIS_BINLOG_TSO__) is a NULL placeholder on disk on a
212
    // single-version binlog segment, replaced with the real commit_tso at read time
213
    // (SegmentIterator::_update_tso_col_if_needed). Its zonemap reflects the placeholder, so
214
    // it must NOT drive zonemap pruning. Mirrors the guards of _update_tso_col_if_needed.
215
    // Returns false for range (compaction) segments whose on-disk value is real.
216
    bool is_tso_placeholder_col(int cid, const ReadSchema& schema,
217
                                const StorageReadOptions& read_options) const;
218
219
    // Return the logical value of a hidden column when it is constant for this read of the whole
220
    // segment even though the segment stores only a placeholder. This applies only when
221
    // read_options.version is a single version:
222
    //   * VERSION_COL: version.second; the stored value is 0.
223
    //   * COMMIT_TSO_COL: commit_tso.end_tso(), when it is assigned (!= -1); the stored value is 0.
224
    //   * BINLOG_TSO_COL: commit_tso.end_tso(), or 0 when it is unassigned, for READER_BINLOG and
225
    //     READER_BINLOG_COMPACTION only; the stored value is NULL.
226
    //
227
    // Expression ZoneMap pruning runs before row materialization applies these read-time values.
228
    // A physical segment/page ZoneMap therefore describes the placeholder rather than the value
229
    // seen by predicates; evaluating it may return kNoMatch and incorrectly discard valid rows.
230
    // Segment-level pruning must use a synthetic [value, value] ZoneMap. Page-level pruning must
231
    // skip the physical page ZoneMaps; row-level evaluation remains in SegmentIterator's pre-lazy
232
    // common-expression path or Scanner's residual conjuncts.
233
    //
234
    // Return nullopt when no read-time substitution applies; the on-disk value and ZoneMaps are
235
    // authoritative for that read.
236
    std::optional<Field> get_read_time_constant_value(int cid, const ReadSchema& schema,
237
                                                      const StorageReadOptions& read_options) const;
238
239
21
    const TabletSchemaSPtr& tablet_schema() const { return _tablet_schema; }
240
241
    // get the column reader by tablet column, return NOT_FOUND if not found reader in this segment
242
    Status get_column_reader(const TabletColumn& col, std::shared_ptr<ColumnReader>* column_reader,
243
                             OlapReaderStatistics* stats, const io::IOContext* io_ctx = nullptr,
244
                             std::optional<Field> const_value = std::nullopt);
245
246
    // get the column reader by column unique id, return NOT_FOUND if not found reader in this segment
247
    Status get_column_reader(int32_t col_uid, std::shared_ptr<ColumnReader>* column_reader,
248
                             OlapReaderStatistics* stats, const io::IOContext* io_ctx = nullptr,
249
                             std::optional<Field> const_value = std::nullopt);
250
251
    Status traverse_column_meta_pbs(const std::function<void(const ColumnMetaPB&)>& visitor);
252
253
    // Returns the cached raw_data_bytes for the given column unique id, or 0 if not found.
254
    // Data is populated during _create_column_meta (under call_once), so thread-safe after init.
255
18.0M
    uint64_t column_raw_data_bytes(int32_t column_uid) const {
256
18.0M
        auto it = _column_uid_to_raw_bytes.find(column_uid);
257
18.4E
        return it != _column_uid_to_raw_bytes.end() ? it->second : 0;
258
18.0M
    }
259
260
    static StoragePageCache::CacheKey get_segment_footer_cache_key(
261
            const io::FileReaderSPtr& file_reader);
262
263
private:
264
    DISALLOW_COPY_AND_ASSIGN(Segment);
265
    Segment(uint32_t segment_id, RowsetId rowset_id, TabletSchemaSPtr tablet_schema,
266
            InvertedIndexFileInfo idx_file_info = InvertedIndexFileInfo());
267
    static Status _open(io::FileSystemSPtr fs, const std::string& path, uint32_t segment_id,
268
                        RowsetId rowset_id, TabletSchemaSPtr tablet_schema,
269
                        const io::FileReaderOptions& reader_options,
270
                        std::shared_ptr<Segment>* output, InvertedIndexFileInfo idx_file_info,
271
                        OlapReaderStatistics* stats = nullptr,
272
                        const io::IOContext* io_ctx = nullptr);
273
    // open segment file and read the minimum amount of necessary information (footer)
274
    Status _open(OlapReaderStatistics* stats, const io::IOContext* io_ctx = nullptr);
275
    Status _parse_footer(std::shared_ptr<SegmentFooterPB>& footer,
276
                         OlapReaderStatistics* stats = nullptr,
277
                         const io::IOContext* io_ctx = nullptr);
278
    Status _create_column_meta(const SegmentFooterPB& footer, OlapReaderStatistics* stats = nullptr,
279
                               const io::IOContext* io_ctx = nullptr);
280
    Status _load_pk_bloom_filter(OlapReaderStatistics* stats,
281
                                 const io::IOContext* io_ctx = nullptr);
282
283
    Status _write_error_file(size_t file_size, size_t offset, size_t bytes_read, char* data,
284
                             io::IOContext& io_ctx);
285
286
    Status _open_index_file_reader();
287
288
    Status _create_column_meta_once(OlapReaderStatistics* stats,
289
                                    const io::IOContext* io_ctx = nullptr);
290
291
    virtual Status _get_segment_footer(std::shared_ptr<SegmentFooterPB>&,
292
                                       OlapReaderStatistics* stats,
293
                                       const io::IOContext* io_ctx = nullptr);
294
295
    StoragePageCache::CacheKey get_segment_footer_cache_key() const;
296
297
    friend class SegmentIterator;
298
    friend class ColumnReaderCache;
299
    friend class MockSegment;
300
301
    io::FileSystemSPtr _fs;
302
    io::FileReaderSPtr _file_reader;
303
    // Relative path passed to `open`, used to derive the inverted index path (see
304
    // _open_index_file_reader).
305
    std::string _seg_path;
306
    uint32_t _segment_id;
307
    uint32_t _num_rows;
308
    AtomicStatus _healthy_status;
309
310
    // 1. Tracking memory use by segment meta data such as footer or index page.
311
    // 2. Tracking memory use by segment column reader
312
    // The memory consumed by querying is tracked in segment iterator.
313
    int64_t _meta_mem_usage;
314
    int64_t _tracked_meta_mem_usage = 0;
315
316
    RowsetId _rowset_id;
317
    TabletSchemaSPtr _tablet_schema;
318
319
    std::unique_ptr<PrimaryKeyIndexMetaPB> _pk_index_meta;
320
    PagePointerPB _sk_index_page;
321
322
    // Limited cache for column readers
323
    std::unique_ptr<ColumnReaderCache> _column_reader_cache;
324
325
    // Centralized accessor for column metadata layout and uid->column_ordinal mapping.
326
    std::unique_ptr<ColumnMetaAccessor> _column_meta_accessor;
327
328
    // Init from ColumnMetaPB in SegmentFooterPB
329
    // map column unique id ---> it's inner data type
330
    std::map<int32_t, std::shared_ptr<const IDataType>> _file_column_types;
331
332
    // used to guarantee that short key index will be loaded at most once in a thread-safe way
333
    DorisCallOnce<Status> _load_index_once;
334
    // used to guarantee that primary key bloom filter will be loaded at most once in a thread-safe way
335
    DorisCallOnce<Status> _load_pk_bf_once;
336
337
    DorisCallOnce<Status> _create_column_meta_once_call;
338
339
    std::weak_ptr<SegmentFooterPB> _footer_pb;
340
341
    // Cached raw_data_bytes per column unique id, populated once in _create_column_meta().
342
    std::unordered_map<int32_t, uint64_t> _column_uid_to_raw_bytes;
343
344
    // used to hold short key index page in memory
345
    PageHandle _sk_index_handle;
346
    // short key index decoder
347
    // all content is in memory
348
    std::unique_ptr<ShortKeyIndexDecoder> _sk_index_decoder;
349
    // primary key index reader
350
    std::unique_ptr<PrimaryKeyIndexReader> _pk_index_reader;
351
    std::mutex _open_lock;
352
    // inverted index file reader
353
    std::shared_ptr<IndexFileReader> _index_file_reader;
354
    DorisCallOnce<Status> _index_file_reader_open;
355
356
    InvertedIndexFileInfo _idx_file_info;
357
    int64_t _tablet_id = -1;
358
359
    int _be_exec_version = BeExecVersionManager::get_newest_version();
360
};
361
362
} // namespace segment_v2
363
} // namespace doris