Coverage Report

Created: 2026-10-27 17:54

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/storage/schema.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 <stddef.h>
21
#include <stdint.h>
22
23
#include <algorithm>
24
#include <cstdint>
25
#include <memory>
26
#include <string>
27
#include <unordered_map>
28
#include <utility>
29
#include <vector>
30
31
#include "common/consts.h"
32
#include "common/status.h"
33
#include "core/column/column.h"
34
#include "exprs/aggregate/aggregate_function.h"
35
#include "io/io_common.h"
36
#include "runtime/thread_context.h"
37
#include "storage/olap_common.h"
38
#include "storage/tablet/tablet_schema.h"
39
#include "storage/utils.h"
40
41
namespace doris {
42
43
class ReadSchema;
44
class Block;
45
class SlotDescriptor;
46
using ReadSchemaSPtr = std::shared_ptr<ReadSchema>;
47
48
// Select columns by their ordinal in `columns`, preserving the requested order and duplicates.
49
// Keeping this source-layout operation outside ReadSchema prevents those ordinals from being
50
// confused with the dense ordinals of the resulting ReadSchema.
51
std::vector<TabletColumnPtr> project_columns_by_ordinal(
52
        const std::vector<TabletColumnPtr>& columns,
53
        const std::vector<ColumnId>& source_column_ordinals);
54
55
// The dense, ordered column layout consumed by one storage reader. For example, if the caller's
56
// Block is [k1, v1] and a historical delete predicate needs dropped_v2, the layout is
57
// [k1, v1, dropped_v2]: num_block_columns() is 2, num_read_columns() is 3, and every reader-side
58
// ColumnId is an ordinal in this list. TabletColumn keeps physical storage metadata, while
59
// data_type() records the expected materialized type, such as a pruned STRUCT type.
60
class ReadSchema {
61
public:
62
    using SequenceMap = std::unordered_map<ColumnId, std::vector<ColumnId>>;
63
    using ChangelogValueColumnPairs = std::vector<std::pair<ColumnId, ColumnId>>;
64
65
    explicit ReadSchema(std::vector<TabletColumnPtr> columns);
66
67
    // Every column is initially caller-visible. Historical delete-predicate columns may be
68
    // appended later without changing num_block_columns().
69
    explicit ReadSchema(std::vector<TabletColumnPtr> columns, std::vector<DataTypePtr> read_types);
70
71
    static IColumn::MutablePtr get_predicate_column_ptr(const DataTypePtr& data_type,
72
                                                        const ReaderType reader_type);
73
74
    // Builds the column `slot` reads from the slot's name, unique id, type and default value.
75
    // For a column the tablet schema does not have yet, e.g. one a light schema change just added.
76
    static TabletColumn tablet_column_from_slot(const SlotDescriptor& slot);
77
78
18.3M
    const std::vector<TabletColumnPtr>& columns() const { return _read_columns; }
79
80
52.2M
    DataTypePtr data_type(size_t ordinal) const {
81
52.2M
        DCHECK_LT(ordinal, _read_types.size());
82
52.2M
        return _read_types[ordinal];
83
52.2M
    }
84
85
    // Append private historical delete-predicate columns without extending the Block prefix.
86
    void append_dropped_columns(std::vector<TabletColumn> columns);
87
88
    // Append COMPACTION_ROWID_COL to the Block prefix before any historical delete columns.
89
    // It carries each source row's physical location through compaction filtering and merge.
90
    void append_compaction_rowid_column();
91
92
    // Create caller-visible Blocks from the FE-slot prefix.
93
    Block create_read_block() const;
94
95
    std::string read_columns_to_string() const;
96
97
    // Sets tablet facts and resolves special-column UIDs, without deriving value relationships.
98
    Status init_from_tablet_schema(const TabletSchema& tablet_schema,
99
                                   bool merge_by_sequence_mapping);
100
101
    // All positions address the fixed caller-visible Block prefix. -1 means a system column
102
    // is absent. Pairs are explicit relationships, independent of column names and ordering.
103
    Status init_changelog_column_mappings(const ChangelogValueColumnPairs& pairs,
104
                                          int32_t tso_ordinal, int32_t lsn_ordinal,
105
                                          int32_t op_ordinal);
106
    Status init_changelog_column_mappings(const TabletSchema& tablet_schema,
107
                                          const ChangelogValueColumnPairs& pairs);
108
109
    // Whether the tablet schema defines a sequence mapping -- not whether this read merges by
110
    // one. A MoW query does not merge, yet a value predicate still must not be pushed down.
111
1.83M
    bool tablet_has_sequence_map() const { return _tablet_has_sequence_map; }
112
113
    // Whether the tablet schema materializes variant subcolumns as extracted columns. A
114
    // compaction read then takes them as flat leaves.
115
2.81M
    bool tablet_has_extracted_variant_columns() const {
116
2.81M
        return _tablet_has_extracted_variant_columns;
117
2.81M
    }
118
119
82
    const SequenceMap& sequence_map() const { return _sequence_map; }
120
121
    // Return the matching before-image ordinal for a changelog value column. For example, in
122
    // [v1, v2, __DORIS_CHANGELOG_BEFORE_v1__, __DORIS_CHANGELOG_BEFORE_v2__], 0 maps to 2 and 1
123
    // maps to 3. Columns without a before image, including TSO/LSN/OP, map to themselves.
124
1.32k
    ColumnId before_column_ordinal(ColumnId ordinal) const {
125
1.32k
        DCHECK_LT(ordinal, _before_column_ordinals.size());
126
1.32k
        return _before_column_ordinals[ordinal];
127
1.32k
    }
128
129
122
    const ChangelogValueColumnPairs& changelog_value_column_pairs() const {
130
122
        return _changelog_value_column_pairs;
131
122
    }
132
133
120
    bool changelog_value_pairs_complete() const { return _changelog_value_pairs_complete; }
134
135
194M
    const TabletColumn* column(size_t ordinal) const { return _read_columns[ordinal].get(); }
136
137
    // Total columns used inside storage, including appended storage-only columns.
138
    // Use this for per-column state and iteration over the complete ReadSchema.
139
154M
    size_t num_read_columns() const { return _read_columns.size(); }
140
141
    // Columns materialized in caller Blocks. They are the ReadSchema prefix before
142
    // appended storage-only columns; use this for Block layout and position bounds.
143
94.8M
    size_t num_block_columns() const { return _num_block_columns; }
144
145
    // Number of key columns present in the caller-visible ReadSchema. This is deliberately not the
146
    // full TabletSchema key count. Merge readers additionally require these columns to be the full,
147
    // leading storage-key prefix; direct/projected readers may contain fewer key columns.
148
17.8M
    size_t num_key_columns() const { return _num_key_columns; }
149
150
    // All special-column ordinals below address the caller-visible Block prefix and are -1 when
151
    // absent. A changelog layout may be [k1, v1, __DORIS_CHANGELOG_BEFORE_v1__, TSO, LSN, OP]; a
152
    // snapshot layout may instead contain COMMIT_TSO.
153
    // Logical-delete marker used by unique-key reads.
154
953k
    int32_t delete_sign_ordinal() const { return _delete_sign_ordinal; }
155
    // Sequence column used to choose the winning row during merge.
156
737k
    int32_t sequence_ordinal() const { return _sequence_ordinal; }
157
    // Synthetic row identifier returned by queries or carried by compaction reads.
158
69.9k
    int32_t rowid_ordinal() const { return _rowid_ordinal; }
159
    // Rowset version synthesized for single-version reads.
160
0
    int32_t version_ordinal() const { return _version_ordinal; }
161
    // Changelog transaction timestamp.
162
2.84M
    int32_t tso_ordinal() const { return _tso_ordinal; }
163
    // Changelog log-sequence number.
164
615
    int32_t lsn_ordinal() const { return _lsn_ordinal; }
165
    // Changelog operation kind, such as INSERT, UPDATE, or DELETE.
166
1.36k
    int32_t op_ordinal() const { return _op_ordinal; }
167
    // Snapshot commit timestamp.
168
120
    int32_t commit_tso_ordinal() const { return _commit_tso_ordinal; }
169
170
    // -1 if no column with this unique id is present.
171
4.83M
    int32_t ordinal_by_uid(int32_t unique_id) const {
172
4.83M
        auto it = _uid_to_ordinal.find(unique_id);
173
4.83M
        return it == _uid_to_ordinal.end() ? -1 : it->second;
174
4.83M
    }
175
176
private:
177
    void _init_read_types();
178
    Status _init_sequence_map(const TabletSchema& tablet_schema);
179
180
3.78M
    void _init_descriptors() {
181
3.78M
        DORIS_CHECK_LE(_num_block_columns, _read_columns.size());
182
3.78M
        DORIS_CHECK_EQ(_read_columns.size(), _read_types.size());
183
3.78M
        _num_key_columns = 0;
184
3.78M
        _delete_sign_ordinal = -1;
185
3.78M
        _sequence_ordinal = -1;
186
3.78M
        _rowid_ordinal = -1;
187
3.78M
        _version_ordinal = -1;
188
3.78M
        _tso_ordinal = -1;
189
3.78M
        _lsn_ordinal = -1;
190
3.78M
        _op_ordinal = -1;
191
3.78M
        _commit_tso_ordinal = -1;
192
3.78M
        _before_column_ordinals.clear();
193
3.78M
        _uid_to_ordinal.clear();
194
18.0M
        for (uint32_t i = 0; i < _read_columns.size(); ++i) {
195
14.2M
            const auto& col = *_read_columns[i];
196
14.2M
            if (col.unique_id() >= 0) {
197
14.1M
                _uid_to_ordinal.emplace(col.unique_id(), i);
198
14.1M
            }
199
14.2M
        }
200
17.9M
        for (uint32_t i = 0; i < _num_block_columns; ++i) {
201
14.1M
            const auto& col = *_read_columns[i];
202
14.1M
            if (col.is_key()) {
203
9.19M
                ++_num_key_columns;
204
9.19M
            }
205
14.1M
            if (col.name() == DELETE_SIGN) {
206
503k
                _delete_sign_ordinal = i;
207
503k
            }
208
14.1M
            if (col.name() == SEQUENCE_COL) {
209
589
                _sequence_ordinal = i;
210
589
            }
211
14.1M
            if (col.name().starts_with(BeConsts::GLOBAL_ROWID_COL) ||
212
14.1M
                col.name() == BeConsts::COMPACTION_ROWID_COL) {
213
9.02k
                _rowid_ordinal = i;
214
9.02k
            }
215
14.1M
            if (col.name() == VERSION_COL) {
216
5.23k
                _version_ordinal = i;
217
5.23k
            }
218
14.1M
            if (col.name() == COMMIT_TSO_COL) {
219
1.26k
                _commit_tso_ordinal = i;
220
1.26k
            }
221
14.1M
        }
222
3.78M
        _before_column_ordinals.resize(_num_block_columns);
223
17.9M
        for (ColumnId ordinal = 0; ordinal < _num_block_columns; ++ordinal) {
224
14.1M
            _before_column_ordinals[ordinal] = ordinal;
225
14.1M
        }
226
3.78M
    }
227
228
    // Example: storage has k(uid=1, INT) and
229
    // s(uid=2, STRUCT<a:INT,b:STRING,c:BIGINT>). The scan needs k and only s.a/s.c, while
230
    // a historical delete predicate `old_v = 0` needs the dropped column old_v(uid=3, INT):
231
    //   [0] _read_columns: k:INT (uid=1)
232
    //       _read_types:   INT
233
    //   [1] _read_columns: s:STRUCT<a:INT,b:STRING,c:BIGINT> (uid=2)
234
    //       _read_types:   STRUCT<a:INT,c:BIGINT>
235
    //   [2] _read_columns: old_v:INT (uid=3, dropped)
236
    //       _read_types:   INT
237
    // `_num_block_columns` is 2, so create_read_block() materializes ordinals [0, 2). Ordinal 2
238
    // is read only for delete filtering and never appears in the caller Block.
239
    std::vector<TabletColumnPtr> _read_columns;
240
    // Types aligned by ordinal with `_read_columns`.
241
    std::vector<DataTypePtr> _read_types;
242
    // Boundary between the columns materialized in Blocks and the appended dropped columns.
243
    size_t _num_block_columns = 0;
244
245
    size_t _num_key_columns = 0;
246
    int32_t _delete_sign_ordinal = -1;
247
    int32_t _sequence_ordinal = -1;
248
    int32_t _rowid_ordinal = -1;
249
    int32_t _version_ordinal = -1;
250
    int32_t _tso_ordinal = -1;
251
    int32_t _lsn_ordinal = -1;
252
    int32_t _op_ordinal = -1;
253
    int32_t _commit_tso_ordinal = -1;
254
    std::unordered_map<int32_t, int32_t> _uid_to_ordinal;
255
    SequenceMap _sequence_map;
256
    std::vector<ColumnId> _before_column_ordinals;
257
    ChangelogValueColumnPairs _changelog_value_column_pairs;
258
    bool _changelog_value_pairs_complete = false;
259
    bool _tablet_has_sequence_map = false;
260
    bool _tablet_has_extracted_variant_columns = false;
261
};
262
263
} // namespace doris