Coverage Report

Created: 2026-04-14 17:06

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/storage/index/index_page.cpp
Line
Count
Source
1
// Licensed to the Apache Software Foundation (ASF) under one
2
// or more contributor license agreements.  See the NOTICE file
3
// distributed with this work for additional information
4
// regarding copyright ownership.  The ASF licenses this file
5
// to you under the Apache License, Version 2.0 (the
6
// "License"); you may not use this file except in compliance
7
// with the License.  You may obtain a copy of the License at
8
//
9
//   http://www.apache.org/licenses/LICENSE-2.0
10
//
11
// Unless required by applicable law or agreed to in writing,
12
// software distributed under the License is distributed on an
13
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
// KIND, either express or implied.  See the License for the
15
// specific language governing permissions and limitations
16
// under the License.
17
18
#include "storage/index/index_page.h"
19
20
#include <gen_cpp/segment_v2.pb.h>
21
22
#include <algorithm>
23
#include <ostream>
24
25
#include "util/coding.h"
26
27
namespace doris {
28
namespace segment_v2 {
29
30
54.8k
void IndexPageBuilder::add(const Slice& key, const PagePointer& ptr) {
31
54.8k
    DCHECK(!_finished) << "must reset() after finish() to add new entry";
32
54.8k
    put_length_prefixed_slice(&_buffer, key);
33
54.8k
    ptr.encode_to(&_buffer);
34
54.8k
    _count++;
35
54.8k
}
36
37
0
bool IndexPageBuilder::is_full() const {
38
    // estimate size of IndexPageFooterPB to be 16
39
0
    return _buffer.size() + 16 > _index_page_size;
40
0
}
41
42
1.67k
void IndexPageBuilder::finish(OwnedSlice* body, PageFooterPB* footer) {
43
1.67k
    DCHECK(!_finished) << "already called finish()";
44
1.67k
    *body = _buffer.build();
45
46
1.67k
    footer->set_type(INDEX_PAGE);
47
1.67k
    footer->set_uncompressed_size(cast_set<uint32_t>(body->slice().get_size()));
48
1.67k
    footer->mutable_index_page_footer()->set_num_entries(_count);
49
1.67k
    footer->mutable_index_page_footer()->set_type(_is_leaf ? IndexPageFooterPB::LEAF
50
1.67k
                                                           : IndexPageFooterPB::INTERNAL);
51
1.67k
}
52
53
0
Status IndexPageBuilder::get_first_key(Slice* key) const {
54
0
    if (_count == 0) {
55
0
        return Status::Error<ErrorCode::ENTRY_NOT_FOUND>("index page is empty");
56
0
    }
57
0
    Slice input(_buffer);
58
0
    if (get_length_prefixed_slice(&input, key)) {
59
0
        return Status::OK();
60
0
    } else {
61
0
        return Status::Corruption("can't decode first key");
62
0
    }
63
0
}
64
65
///////////////////////////////////////////////////////////////////////////////
66
67
1.74k
int64_t IndexPageReader::get_metadata_size() const {
68
1.74k
    return sizeof(IndexPageReader) + _footer.ByteSizeLong();
69
1.74k
}
70
71
1.74k
Status IndexPageReader::parse(const Slice& body, const IndexPageFooterPB& footer) {
72
1.74k
    _footer = footer;
73
1.74k
    size_t num_entries = _footer.num_entries();
74
75
1.74k
    Slice input(body);
76
22.2k
    for (int i = 0; i < num_entries; ++i) {
77
20.4k
        Slice key;
78
20.4k
        PagePointer value;
79
20.4k
        if (!get_length_prefixed_slice(&input, &key)) {
80
0
            return Status::InternalError("Data corruption");
81
0
        }
82
20.4k
        if (!value.decode_from(&input)) {
83
0
            return Status::InternalError("Data corruption");
84
0
        }
85
20.4k
        _keys.push_back(key);
86
20.4k
        _values.push_back(value);
87
20.4k
    }
88
89
1.74k
    update_metadata_size();
90
1.74k
    _parsed = true;
91
1.74k
    return Status::OK();
92
1.74k
}
93
///////////////////////////////////////////////////////////////////////////////
94
95
140
Status IndexPageIterator::seek_at_or_before(const Slice& search_key) {
96
140
    int32_t left = 0;
97
140
    auto right = cast_set<int32_t>(_reader->count() - 1);
98
388
    while (left <= right) {
99
276
        int32_t mid = left + (right - left) / 2;
100
276
        int cmp = search_key.compare(_reader->get_key(mid));
101
276
        if (cmp < 0) {
102
82
            right = mid - 1;
103
194
        } else if (cmp > 0) {
104
166
            left = mid + 1;
105
166
        } else {
106
28
            _pos = mid;
107
28
            return Status::OK();
108
28
        }
109
276
    }
110
    // no exact match, the insertion point is `left`
111
112
    if (left == 0) {
112
        // search key is smaller than all keys
113
0
        return Status::Error<ErrorCode::ENTRY_NOT_FOUND>(
114
0
                "given key is smaller than all keys in page");
115
0
    }
116
    // index entry records the first key of the indexed page,
117
    // therefore the first page with keys >= searched key is the one before the insertion point
118
112
    _pos = left - 1;
119
112
    return Status::OK();
120
112
}
121
122
} // namespace segment_v2
123
} // namespace doris