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 |