Coverage Report

Created: 2026-10-14 12:18

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 RowBinlogValueColumnPairs = 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
20.6M
    const std::vector<TabletColumnPtr>& columns() const { return _read_columns; }
79
80
76.0M
    DataTypePtr data_type(size_t ordinal) const {
81
76.0M
        DCHECK_LT(ordinal, _read_types.size());
82
76.0M
        return _read_types[ordinal];
83
76.0M
    }
84
85
    // Append private historical delete-predicate columns without extending the Block prefix.
86
    void append_dropped_columns(std::vector<TabletColumn> columns);
87
88
    // Create caller-visible Blocks from the FE-slot prefix.
89
    Block create_read_block() const;
90
91
    std::string read_columns_to_string() const;
92
93
    // Always sets the two facts below; the flags add the sequence mapping and the row-binlog
94
    // before-image mapping.
95
    Status init_from_tablet_schema(const TabletSchema& tablet_schema,
96
                                   bool merge_by_sequence_mapping, bool map_row_binlog_columns);
97
98
    // Whether the tablet schema defines a sequence mapping -- not whether this read merges by
99
    // one. A MoW query does not merge, yet a value predicate still must not be pushed down.
100
1.96M
    bool tablet_has_sequence_map() const { return _tablet_has_sequence_map; }
101
102
    // Whether the tablet schema materializes variant subcolumns as extracted columns. A
103
    // compaction read then takes them as flat leaves.
104
2.98M
    bool tablet_has_extracted_variant_columns() const {
105
2.98M
        return _tablet_has_extracted_variant_columns;
106
2.98M
    }
107
108
86
    const SequenceMap& sequence_map() const { return _sequence_map; }
109
110
    // Return the matching before-image ordinal for a Row Binlog value column. For example, in
111
    // [v1, v2, __BEFORE__v1__, __BEFORE__v2__], 0 maps to 2 and 1 maps to 3. Columns without a
112
    // before image, including TSO/LSN/OP, map to themselves.
113
1.16k
    ColumnId before_column_ordinal(ColumnId ordinal) const {
114
1.16k
        DCHECK_LT(ordinal, _before_column_ordinals.size());
115
1.16k
        return _before_column_ordinals[ordinal];
116
1.16k
    }
117
118
102
    const RowBinlogValueColumnPairs& row_binlog_value_column_pairs() const {
119
102
        return _row_binlog_value_column_pairs;
120
102
    }
121
122
102
    bool row_binlog_value_pairs_complete() const { return _row_binlog_value_pairs_complete; }
123
124
196M
    const TabletColumn* column(size_t ordinal) const { return _read_columns[ordinal].get(); }
125
126
    // Total columns used inside storage, including appended storage-only columns.
127
    // Use this for per-column state and iteration over the complete ReadSchema.
128
168M
    size_t num_read_columns() const { return _read_columns.size(); }
129
130
    // Columns materialized in caller Blocks. They are the ReadSchema prefix before
131
    // appended storage-only columns; use this for Block layout and position bounds.
132
104M
    size_t num_block_columns() const { return _num_block_columns; }
133
134
    // Number of key columns present in the caller-visible ReadSchema. This is deliberately not the
135
    // full TabletSchema key count. Merge readers additionally require these columns to be the full,
136
    // leading storage-key prefix; direct/projected readers may contain fewer key columns.
137
17.5M
    size_t num_key_columns() const { return _num_key_columns; }
138
139
    // All special-column ordinals below address the caller-visible Block prefix and are -1 when
140
    // absent. A Row Binlog layout may be [k1, v1, __BEFORE__v1__, TSO, LSN, OP]; a snapshot layout
141
    // may instead contain COMMIT_TSO.
142
    // Logical-delete marker used by unique-key reads.
143
973k
    int32_t delete_sign_ordinal() const { return _delete_sign_ordinal; }
144
    // Sequence column used to choose the winning row during merge.
145
702k
    int32_t sequence_ordinal() const { return _sequence_ordinal; }
146
    // Synthetic row identifier returned by rowid-producing scans.
147
7
    int32_t rowid_ordinal() const { return _rowid_ordinal; }
148
    // Rowset version synthesized for single-version reads.
149
0
    int32_t version_ordinal() const { return _version_ordinal; }
150
    // Row Binlog transaction timestamp.
151
2.99M
    int32_t tso_ordinal() const { return _tso_ordinal; }
152
    // Row Binlog log-sequence number.
153
568
    int32_t lsn_ordinal() const { return _lsn_ordinal; }
154
    // Row Binlog operation kind, such as INSERT, UPDATE, or DELETE.
155
616
    int32_t op_ordinal() const { return _op_ordinal; }
156
    // Snapshot commit timestamp.
157
107
    int32_t commit_tso_ordinal() const { return _commit_tso_ordinal; }
158
159
    // -1 if no column with this unique id is present.
160
5.42M
    int32_t ordinal_by_uid(int32_t unique_id) const {
161
5.42M
        auto it = _uid_to_ordinal.find(unique_id);
162
5.42M
        return it == _uid_to_ordinal.end() ? -1 : it->second;
163
5.42M
    }
164
165
private:
166
    void _init_read_types();
167
    void _init_before_column_ordinals();
168
    Status _init_sequence_map(const TabletSchema& tablet_schema);
169
    void _init_row_binlog_column_mappings(const TabletSchema& tablet_schema);
170
171
3.84M
    void _init_descriptors() {
172
3.84M
        DORIS_CHECK_LE(_num_block_columns, _read_columns.size());
173
3.84M
        DORIS_CHECK_EQ(_read_columns.size(), _read_types.size());
174
3.84M
        _num_key_columns = 0;
175
3.84M
        _delete_sign_ordinal = -1;
176
3.84M
        _sequence_ordinal = -1;
177
3.84M
        _rowid_ordinal = -1;
178
3.84M
        _version_ordinal = -1;
179
3.84M
        _tso_ordinal = -1;
180
3.84M
        _lsn_ordinal = -1;
181
3.84M
        _op_ordinal = -1;
182
3.84M
        _commit_tso_ordinal = -1;
183
3.84M
        _before_column_ordinals.clear();
184
3.84M
        _uid_to_ordinal.clear();
185
18.1M
        for (uint32_t i = 0; i < _read_columns.size(); ++i) {
186
14.3M
            const auto& col = *_read_columns[i];
187
14.3M
            if (col.unique_id() >= 0) {
188
14.2M
                _uid_to_ordinal.emplace(col.unique_id(), i);
189
14.2M
            }
190
14.3M
        }
191
18.1M
        for (uint32_t i = 0; i < _num_block_columns; ++i) {
192
14.2M
            const auto& col = *_read_columns[i];
193
14.2M
            if (col.is_key()) {
194
9.33M
                ++_num_key_columns;
195
9.33M
            }
196
14.2M
            if (col.name() == DELETE_SIGN) {
197
501k
                _delete_sign_ordinal = i;
198
501k
            }
199
14.2M
            if (col.name() == SEQUENCE_COL) {
200
554
                _sequence_ordinal = i;
201
554
            }
202
14.2M
            if (col.name().starts_with(BeConsts::GLOBAL_ROWID_COL)) {
203
7.34k
                _rowid_ordinal = i;
204
7.34k
            }
205
14.2M
            if (col.name() == VERSION_COL) {
206
5.63k
                _version_ordinal = i;
207
5.63k
            }
208
14.2M
            if (col.name() == BINLOG_TSO_COL) {
209
995
                _tso_ordinal = i;
210
995
            }
211
14.2M
            if (col.name() == BINLOG_LSN_COL) {
212
369
                _lsn_ordinal = i;
213
369
            }
214
14.2M
            if (col.name() == BINLOG_OP_COL) {
215
991
                _op_ordinal = i;
216
991
            }
217
14.2M
            if (col.name() == COMMIT_TSO_COL) {
218
1.20k
                _commit_tso_ordinal = i;
219
1.20k
            }
220
14.2M
        }
221
3.84M
        if (_op_ordinal >= 0) {
222
989
            _init_before_column_ordinals();
223
989
        }
224
3.84M
    }
225
226
    // Example: storage has k(uid=1, INT) and
227
    // s(uid=2, STRUCT<a:INT,b:STRING,c:BIGINT>). The scan needs k and only s.a/s.c, while
228
    // a historical delete predicate `old_v = 0` needs the dropped column old_v(uid=3, INT):
229
    //   [0] _read_columns: k:INT (uid=1)
230
    //       _read_types:   INT
231
    //   [1] _read_columns: s:STRUCT<a:INT,b:STRING,c:BIGINT> (uid=2)
232
    //       _read_types:   STRUCT<a:INT,c:BIGINT>
233
    //   [2] _read_columns: old_v:INT (uid=3, dropped)
234
    //       _read_types:   INT
235
    // `_num_block_columns` is 2, so create_read_block() materializes ordinals [0, 2). Ordinal 2
236
    // is read only for delete filtering and never appears in the caller Block.
237
    std::vector<TabletColumnPtr> _read_columns;
238
    // Types aligned by ordinal with `_read_columns`.
239
    std::vector<DataTypePtr> _read_types;
240
    // Boundary between the columns materialized in Blocks and the appended dropped columns.
241
    size_t _num_block_columns = 0;
242
243
    size_t _num_key_columns = 0;
244
    int32_t _delete_sign_ordinal = -1;
245
    int32_t _sequence_ordinal = -1;
246
    int32_t _rowid_ordinal = -1;
247
    int32_t _version_ordinal = -1;
248
    int32_t _tso_ordinal = -1;
249
    int32_t _lsn_ordinal = -1;
250
    int32_t _op_ordinal = -1;
251
    int32_t _commit_tso_ordinal = -1;
252
    std::unordered_map<int32_t, int32_t> _uid_to_ordinal;
253
    SequenceMap _sequence_map;
254
    std::vector<ColumnId> _before_column_ordinals;
255
    RowBinlogValueColumnPairs _row_binlog_value_column_pairs;
256
    bool _row_binlog_value_pairs_complete = false;
257
    bool _tablet_has_sequence_map = false;
258
    bool _tablet_has_extracted_variant_columns = false;
259
};
260
261
} // namespace doris