Coverage Report

Created: 2026-08-24 21:00

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
using ReadSchemaSPtr = std::shared_ptr<ReadSchema>;
46
47
// Select columns by their ordinal in `columns`, preserving the requested order and duplicates.
48
// Keeping this source-layout operation outside ReadSchema prevents those ordinals from being
49
// confused with the dense ordinals of the resulting ReadSchema.
50
std::vector<TabletColumnPtr> project_columns_by_ordinal(
51
        const std::vector<TabletColumnPtr>& columns,
52
        const std::vector<ColumnId>& source_column_ordinals);
53
54
// The dense, ordered column layout consumed by one storage reader. For example, if the caller's
55
// Block is [k1, v1] and a historical delete predicate needs dropped_v2, the layout is
56
// [k1, v1, dropped_v2]: num_block_columns() is 2, num_read_columns() is 3, and every reader-side
57
// ColumnId is an ordinal in this list. TabletColumn keeps physical storage metadata, while
58
// data_type() records the expected materialized type, such as a pruned STRUCT type.
59
class ReadSchema {
60
public:
61
    using SequenceMap = std::unordered_map<ColumnId, std::vector<ColumnId>>;
62
    using RowBinlogValueColumnPairs = std::vector<std::pair<ColumnId, ColumnId>>;
63
64
    explicit ReadSchema(std::vector<TabletColumnPtr> columns);
65
66
    // Every column is initially caller-visible. Historical delete-predicate columns may be
67
    // appended later without changing num_block_columns().
68
    explicit ReadSchema(std::vector<TabletColumnPtr> columns, std::vector<DataTypePtr> read_types);
69
70
    static IColumn::MutablePtr get_predicate_column_ptr(const DataTypePtr& data_type,
71
                                                        const ReaderType reader_type);
72
73
10.6k
    const std::vector<TabletColumnPtr>& columns() const { return _read_columns; }
74
75
85.8k
    DataTypePtr data_type(size_t ordinal) const {
76
85.8k
        DCHECK_LT(ordinal, _read_types.size());
77
85.8k
        return _read_types[ordinal];
78
85.8k
    }
79
80
    // Append private historical delete-predicate columns without extending the Block prefix.
81
    void append_dropped_columns(std::vector<TabletColumn> columns);
82
83
    // Create caller-visible Blocks from the FE-slot prefix.
84
    Block create_read_block() const;
85
86
    std::string read_columns_to_string() const;
87
88
    Status init_sequence_map(const TabletSchema& tablet_schema);
89
90
0
    const SequenceMap& sequence_map() const { return _sequence_map; }
91
92
    // Initialize all row-binlog column relationships from the physical tablet schema and map
93
    // them to this ReadSchema's dense ordinals. Physical pairing avoids ambiguous column-name
94
    // lookup, while schemas without a complete physical layout retain the name-based BEFORE
95
    // mapping initialized by the constructor.
96
    void init_row_binlog_column_mappings(const TabletSchema& tablet_schema);
97
98
    // Return the matching before-image ordinal for a Row Binlog value column. For example, in
99
    // [v1, v2, __BEFORE__v1__, __BEFORE__v2__], 0 maps to 2 and 1 maps to 3. Columns without a
100
    // before image, including TSO/LSN/OP, map to themselves.
101
96
    ColumnId before_column_ordinal(ColumnId ordinal) const {
102
96
        DCHECK_LT(ordinal, _before_column_ordinals.size());
103
96
        return _before_column_ordinals[ordinal];
104
96
    }
105
106
19
    const RowBinlogValueColumnPairs& row_binlog_value_column_pairs() const {
107
19
        return _row_binlog_value_column_pairs;
108
19
    }
109
110
19
    bool row_binlog_value_pairs_complete() const { return _row_binlog_value_pairs_complete; }
111
112
1.23M
    const TabletColumn* column(size_t ordinal) const { return _read_columns[ordinal].get(); }
113
114
    // Total columns used inside storage, including appended storage-only columns.
115
    // Use this for per-column state and iteration over the complete ReadSchema.
116
161k
    size_t num_read_columns() const { return _read_columns.size(); }
117
118
    // Columns materialized in caller Blocks. They are the ReadSchema prefix before
119
    // appended storage-only columns; use this for Block layout and position bounds.
120
1.04M
    size_t num_block_columns() const { return _num_block_columns; }
121
122
    // Number of key columns present in the caller-visible ReadSchema. This is deliberately not the
123
    // full TabletSchema key count. Merge readers additionally require these columns to be the full,
124
    // leading storage-key prefix; direct/projected readers may contain fewer key columns.
125
1.06M
    size_t num_key_columns() const { return _num_key_columns; }
126
127
    // All special-column ordinals below address the caller-visible Block prefix and are -1 when
128
    // absent. A Row Binlog layout may be [k1, v1, __BEFORE__v1__, TSO, LSN, OP]; a snapshot layout
129
    // may instead contain COMMIT_TSO.
130
    // Logical-delete marker used by unique-key reads.
131
2.96k
    int32_t delete_sign_ordinal() const { return _delete_sign_ordinal; }
132
    // Sequence column used to choose the winning row during merge.
133
461
    int32_t sequence_ordinal() const { return _sequence_ordinal; }
134
    // Synthetic row identifier returned by rowid-producing scans.
135
7
    int32_t rowid_ordinal() const { return _rowid_ordinal; }
136
    // Rowset version synthesized for single-version reads.
137
12.9k
    int32_t version_ordinal() const { return _version_ordinal; }
138
    // Row Binlog transaction timestamp.
139
1.62k
    int32_t tso_ordinal() const { return _tso_ordinal; }
140
    // Row Binlog log-sequence number.
141
25
    int32_t lsn_ordinal() const { return _lsn_ordinal; }
142
    // Row Binlog operation kind, such as INSERT, UPDATE, or DELETE.
143
69
    int32_t op_ordinal() const { return _op_ordinal; }
144
    // Snapshot commit timestamp.
145
69
    int32_t commit_tso_ordinal() const { return _commit_tso_ordinal; }
146
147
    // -1 if no column with this unique id is present.
148
352
    int32_t ordinal_by_uid(int32_t unique_id) const {
149
352
        auto it = _uid_to_ordinal.find(unique_id);
150
352
        return it == _uid_to_ordinal.end() ? -1 : it->second;
151
352
    }
152
153
private:
154
    void _init_read_types();
155
    void _init_before_column_ordinals();
156
157
963
    void _init_descriptors() {
158
963
        DORIS_CHECK_LE(_num_block_columns, _read_columns.size());
159
963
        DORIS_CHECK_EQ(_read_columns.size(), _read_types.size());
160
963
        _num_key_columns = 0;
161
963
        _delete_sign_ordinal = -1;
162
963
        _sequence_ordinal = -1;
163
963
        _rowid_ordinal = -1;
164
963
        _version_ordinal = -1;
165
963
        _tso_ordinal = -1;
166
963
        _lsn_ordinal = -1;
167
963
        _op_ordinal = -1;
168
963
        _commit_tso_ordinal = -1;
169
963
        _before_column_ordinals.clear();
170
963
        _uid_to_ordinal.clear();
171
6.76k
        for (uint32_t i = 0; i < _read_columns.size(); ++i) {
172
5.80k
            const auto& col = *_read_columns[i];
173
5.80k
            if (col.unique_id() >= 0) {
174
5.60k
                _uid_to_ordinal.emplace(col.unique_id(), i);
175
5.60k
            }
176
5.80k
        }
177
6.76k
        for (uint32_t i = 0; i < _num_block_columns; ++i) {
178
5.80k
            const auto& col = *_read_columns[i];
179
5.80k
            if (col.is_key()) {
180
1.68k
                ++_num_key_columns;
181
1.68k
            }
182
5.80k
            if (col.name() == DELETE_SIGN) {
183
129
                _delete_sign_ordinal = i;
184
129
            }
185
5.80k
            if (col.name() == SEQUENCE_COL) {
186
64
                _sequence_ordinal = i;
187
64
            }
188
5.80k
            if (col.name().starts_with(BeConsts::GLOBAL_ROWID_COL)) {
189
0
                _rowid_ordinal = i;
190
0
            }
191
5.80k
            if (col.name() == VERSION_COL) {
192
40
                _version_ordinal = i;
193
40
            }
194
5.80k
            if (col.name() == BINLOG_TSO_COL) {
195
51
                _tso_ordinal = i;
196
51
            }
197
5.80k
            if (col.name() == BINLOG_LSN_COL) {
198
49
                _lsn_ordinal = i;
199
49
            }
200
5.80k
            if (col.name() == BINLOG_OP_COL) {
201
51
                _op_ordinal = i;
202
51
            }
203
5.80k
            if (col.name() == COMMIT_TSO_COL) {
204
4
                _commit_tso_ordinal = i;
205
4
            }
206
5.80k
        }
207
963
        if (_op_ordinal >= 0) {
208
51
            _init_before_column_ordinals();
209
51
        }
210
963
    }
211
212
    // Example: storage has k(uid=1, INT) and
213
    // s(uid=2, STRUCT<a:INT,b:STRING,c:BIGINT>). The scan needs k and only s.a/s.c, while
214
    // a historical delete predicate `old_v = 0` needs the dropped column old_v(uid=3, INT):
215
    //   [0] _read_columns: k:INT (uid=1)
216
    //       _read_types:   INT
217
    //   [1] _read_columns: s:STRUCT<a:INT,b:STRING,c:BIGINT> (uid=2)
218
    //       _read_types:   STRUCT<a:INT,c:BIGINT>
219
    //   [2] _read_columns: old_v:INT (uid=3, dropped)
220
    //       _read_types:   INT
221
    // `_num_block_columns` is 2, so create_read_block() materializes ordinals [0, 2). Ordinal 2
222
    // is read only for delete filtering and never appears in the caller Block.
223
    std::vector<TabletColumnPtr> _read_columns;
224
    // Types aligned by ordinal with `_read_columns`.
225
    std::vector<DataTypePtr> _read_types;
226
    // Boundary between the columns materialized in Blocks and the appended dropped columns.
227
    size_t _num_block_columns = 0;
228
229
    size_t _num_key_columns = 0;
230
    int32_t _delete_sign_ordinal = -1;
231
    int32_t _sequence_ordinal = -1;
232
    int32_t _rowid_ordinal = -1;
233
    int32_t _version_ordinal = -1;
234
    int32_t _tso_ordinal = -1;
235
    int32_t _lsn_ordinal = -1;
236
    int32_t _op_ordinal = -1;
237
    int32_t _commit_tso_ordinal = -1;
238
    std::unordered_map<int32_t, int32_t> _uid_to_ordinal;
239
    SequenceMap _sequence_map;
240
    std::vector<ColumnId> _before_column_ordinals;
241
    RowBinlogValueColumnPairs _row_binlog_value_column_pairs;
242
    bool _row_binlog_value_pairs_complete = false;
243
};
244
245
} // namespace doris