Coverage Report

Created: 2026-10-21 21:40

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/storage/schema_change/schema_change.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 <butil/macros.h>
21
#include <fmt/format.h>
22
#include <gen_cpp/Descriptors_types.h>
23
#include <glog/logging.h>
24
#include <stddef.h>
25
#include <stdint.h>
26
27
#include <memory>
28
#include <ostream>
29
#include <set>
30
#include <shared_mutex>
31
#include <string>
32
#include <unordered_map>
33
#include <unordered_set>
34
#include <utility>
35
#include <vector>
36
37
#include "common/config.h"
38
#include "common/object_pool.h"
39
#include "common/status.h"
40
#include "core/data_type/data_type.h"
41
#include "runtime/descriptors.h"
42
#include "runtime/memory/mem_tracker.h"
43
#include "runtime/runtime_state.h"
44
#include "storage/olap_common.h"
45
#include "storage/rowset/pending_rowset_helper.h"
46
#include "storage/rowset/rowset.h"
47
#include "storage/rowset/rowset_reader.h"
48
#include "storage/rowset/rowset_writer.h"
49
#include "storage/schema_change/column_mapping.h"
50
#include "storage/storage_engine.h"
51
#include "storage/tablet/tablet.h"
52
#include "storage/tablet/tablet_fwd.h"
53
#include "storage/tablet/tablet_schema.h"
54
55
namespace doris {
56
class DeleteHandler;
57
class TAlterInvertedIndexReq;
58
class TAlterTabletReqV2;
59
class TExpr;
60
enum AlterTabletType : int;
61
enum RowsetTypePB : int;
62
enum SegmentsOverlapPB : int;
63
64
class Block;
65
66
class BlockChanger {
67
public:
68
    BlockChanger(TabletSchemaSPtr tablet_schema, DescriptorTbl desc_tbl,
69
                 std::shared_ptr<RuntimeState> state);
70
71
    ~BlockChanger();
72
73
    ColumnMapping* get_mutable_column_mapping(size_t column_index);
74
75
    Status change_block(Block* ref_block, Block* new_block) const;
76
77
0
    void set_where_expr(const std::shared_ptr<TExpr>& where_expr) { _where_expr = where_expr; }
78
79
1
    void set_type(AlterTabletType type) { _type = type; }
80
81
1
    void set_compatible_version(int32_t version) noexcept { _fe_compatible_version = version; }
82
83
0
    bool has_where() const { return _where_expr != nullptr; }
84
85
    // Returns DataQualityError if any non-null value in `column` is longer than the declared
86
    // length in bytes of `new_column` when it is CHAR/VARCHAR.
87
    static Status check_string_length_within_schema(const ColumnPtr& column,
88
                                                    const TabletColumn& new_column,
89
                                                    const std::string& column_name);
90
91
private:
92
    static Status _check_cast_valid(ColumnPtr ref_column, ColumnPtr new_column);
93
94
    // @brief column-mapping specification of new schema
95
    SchemaMapping _schema_mapping;
96
97
    DescriptorTbl _desc_tbl;
98
99
    std::shared_ptr<TExpr> _where_expr;
100
101
    AlterTabletType _type;
102
103
    int32_t _fe_compatible_version = -1;
104
105
    std::shared_ptr<RuntimeState> _state;
106
};
107
108
class SchemaChange {
109
public:
110
1
    SchemaChange() = default;
111
1
    virtual ~SchemaChange() = default;
112
113
    virtual Status process(RowsetReaderSharedPtr rowset_reader, RowsetWriter* rowset_writer,
114
                           BaseTabletSPtr new_tablet, BaseTabletSPtr base_tablet,
115
                           TabletSchemaSPtr base_tablet_schema,
116
1
                           TabletSchemaSPtr new_tablet_schema) {
117
1
        if (rowset_reader->rowset()->empty() || rowset_reader->rowset()->num_rows() == 0) {
118
1
            RETURN_IF_ERROR(rowset_writer->flush());
119
1
            return Status::OK();
120
1
        }
121
122
0
        _filtered_rows = 0;
123
0
        _merged_rows = 0;
124
0
        RETURN_IF_ERROR_OR_CATCH_EXCEPTION(_inner_process(rowset_reader, rowset_writer, new_tablet,
125
0
                                                          base_tablet_schema, new_tablet_schema));
126
127
        // Check row num changes
128
0
        if (!_check_row_nums(rowset_reader, *rowset_writer)) {
129
0
            return Status::Error<ErrorCode::ALTER_STATUS_ERR>("SchemaChange check row nums failed");
130
0
        }
131
132
0
        LOG(INFO) << "all row nums. source_rows=" << rowset_reader->rowset()->num_rows()
133
0
                  << ", source_filtered_rows=" << rowset_reader->filtered_rows()
134
0
                  << ", source_merged_rows=" << rowset_reader->merged_rows()
135
0
                  << ", merged_rows=" << merged_rows() << ", filtered_rows=" << filtered_rows()
136
0
                  << ", new_index_rows=" << rowset_writer->num_rows()
137
0
                  << ", writer_filtered_rows=" << rowset_writer->num_rows_filtered();
138
0
        return Status::OK();
139
0
    }
140
141
0
    uint64_t filtered_rows() const { return _filtered_rows; }
142
143
0
    uint64_t merged_rows() const { return _merged_rows; }
144
145
protected:
146
0
    void _add_filtered_rows(uint64_t filtered_rows) { _filtered_rows += filtered_rows; }
147
148
0
    void _add_merged_rows(uint64_t merged_rows) { _merged_rows += merged_rows; }
149
150
    virtual Status _inner_process(RowsetReaderSharedPtr rowset_reader, RowsetWriter* rowset_writer,
151
                                  BaseTabletSPtr new_tablet, TabletSchemaSPtr base_tablet_schema,
152
0
                                  TabletSchemaSPtr new_tablet_schema) {
153
0
        return Status::NotSupported("inner process unsupported.");
154
0
    }
155
156
0
    virtual bool _check_row_nums(RowsetReaderSharedPtr reader, const RowsetWriter& writer) const {
157
0
        if (reader->rowset()->num_rows() - reader->filtered_rows() - reader->merged_rows() !=
158
0
            writer.num_rows() + writer.num_rows_filtered() + _merged_rows + _filtered_rows) {
159
0
            LOG(WARNING) << "fail to check row num! "
160
0
                         << "source_rows=" << reader->rowset()->num_rows()
161
0
                         << ", source_filtered_rows=" << reader->filtered_rows()
162
0
                         << ", source_merged_rows=" << reader->merged_rows()
163
0
                         << ", written_rows=" << writer.num_rows()
164
0
                         << ", writer_filtered_rows=" << writer.num_rows_filtered()
165
0
                         << ", merged_rows=" << merged_rows()
166
0
                         << ", filtered_rows=" << filtered_rows();
167
0
            if (!config::ignore_schema_change_check) {
168
0
                return false;
169
0
            }
170
0
        }
171
0
        return true;
172
0
    }
173
174
private:
175
    uint64_t _filtered_rows {};
176
    uint64_t _merged_rows {};
177
178
protected:
179
    std::vector<bool> _row_same_bit;
180
};
181
182
class LinkedSchemaChange : public SchemaChange {
183
public:
184
0
    LinkedSchemaChange() = default;
185
0
    ~LinkedSchemaChange() override = default;
186
187
    Status process(RowsetReaderSharedPtr rowset_reader, RowsetWriter* rowset_writer,
188
                   BaseTabletSPtr new_tablet, BaseTabletSPtr base_tablet,
189
                   TabletSchemaSPtr base_tablet_schema,
190
                   TabletSchemaSPtr new_tablet_schema) override;
191
192
private:
193
    DISALLOW_COPY_AND_ASSIGN(LinkedSchemaChange);
194
};
195
196
class VSchemaChangeDirectly : public SchemaChange {
197
public:
198
1
    VSchemaChangeDirectly(const BlockChanger& changer) : _changer(changer) {}
199
200
private:
201
    Status _inner_process(RowsetReaderSharedPtr rowset_reader, RowsetWriter* rowset_writer,
202
                          BaseTabletSPtr new_tablet, TabletSchemaSPtr base_tablet_schema,
203
                          TabletSchemaSPtr new_tablet_schema) override;
204
205
0
    bool _check_row_nums(RowsetReaderSharedPtr reader, const RowsetWriter& writer) const override {
206
0
        return _changer.has_where() || SchemaChange::_check_row_nums(reader, writer);
207
0
    }
208
209
    const BlockChanger& _changer;
210
};
211
212
class VBaseSchemaChangeWithSorting : public SchemaChange {
213
public:
214
    VBaseSchemaChangeWithSorting(const BlockChanger& changer, size_t memory_limitation);
215
0
    ~VBaseSchemaChangeWithSorting() override = default;
216
217
    Status _inner_process(RowsetReaderSharedPtr rowset_reader, RowsetWriter* rowset_writer,
218
                          BaseTabletSPtr new_tablet, TabletSchemaSPtr base_tablet_schema,
219
                          TabletSchemaSPtr new_tablet_schema) override;
220
221
    virtual Result<RowsetSharedPtr> _internal_sorting(
222
            const std::vector<std::unique_ptr<Block>>& blocks, const Version& temp_delta_versions,
223
            int64_t newest_write_timestamp, BaseTabletSPtr new_tablet, RowsetTypePB new_rowset_type,
224
            SegmentsOverlapPB segments_overlap, TabletSchemaSPtr new_tablet_schema);
225
226
    Status _external_sorting(std::vector<RowsetSharedPtr>& src_rowsets, RowsetWriter* rowset_writer,
227
                             BaseTabletSPtr new_tablet, TabletSchemaSPtr new_tablet_schema);
228
229
protected:
230
    // for external sorting
231
    // src_rowsets to store the rowset generated by internal sorting
232
    std::vector<RowsetSharedPtr> _src_rowsets;
233
234
private:
235
0
    bool _check_row_nums(RowsetReaderSharedPtr reader, const RowsetWriter& writer) const override {
236
0
        return _changer.has_where() || SchemaChange::_check_row_nums(reader, writer);
237
0
    }
238
239
    const BlockChanger& _changer;
240
    size_t _memory_limitation;
241
    Version _temp_delta_versions;
242
    std::unique_ptr<MemTracker> _mem_tracker;
243
};
244
245
// @breif schema change with sorting
246
// Mixin for local StorageEngine
247
class VLocalSchemaChangeWithSorting final : public VBaseSchemaChangeWithSorting {
248
public:
249
    VLocalSchemaChangeWithSorting(const BlockChanger& changer, size_t memory_limitation,
250
                                  StorageEngine& local_storage_engine)
251
0
            : VBaseSchemaChangeWithSorting(changer, memory_limitation),
252
0
              _local_storage_engine(local_storage_engine) {}
253
0
    ~VLocalSchemaChangeWithSorting() override = default;
254
255
    Status _inner_process(RowsetReaderSharedPtr rowset_reader, RowsetWriter* rowset_writer,
256
                          BaseTabletSPtr new_tablet, TabletSchemaSPtr base_tablet_schema,
257
                          TabletSchemaSPtr new_tablet_schema) override;
258
259
    Result<RowsetSharedPtr> _internal_sorting(
260
            const std::vector<std::unique_ptr<Block>>& blocks, const Version& temp_delta_versions,
261
            int64_t newest_write_timestamp, BaseTabletSPtr new_tablet, RowsetTypePB new_rowset_type,
262
            SegmentsOverlapPB segments_overlap, TabletSchemaSPtr new_tablet_schema) override;
263
264
private:
265
    StorageEngine& _local_storage_engine;
266
    std::vector<PendingRowsetGuard> _pending_rs_guards;
267
};
268
269
struct AlterMaterializedViewParam {
270
    std::string column_name;
271
    std::string origin_column_name;
272
    std::shared_ptr<TExpr> expr;
273
};
274
275
struct SchemaChangeParams {
276
    AlterTabletType alter_tablet_type;
277
    bool enable_unique_key_merge_on_write = false;
278
    std::vector<RowsetReaderSharedPtr> ref_rowset_readers;
279
    DeleteHandler* delete_handler = nullptr;
280
    std::unordered_map<std::string, AlterMaterializedViewParam> materialized_params_map;
281
    DescriptorTbl* desc_tbl = nullptr;
282
    ObjectPool pool;
283
    int32_t be_exec_version;
284
    std::string vault_id;
285
    bool output_to_file_cache;
286
    std::shared_ptr<RuntimeState> runtime_state;
287
};
288
289
class SchemaChangeJob {
290
public:
291
    SchemaChangeJob(StorageEngine& local_storage_engine, const TAlterTabletReqV2& request,
292
                    const std::string& job_id);
293
    Status process_alter_tablet(const TAlterTabletReqV2& request);
294
295
    bool tablet_in_converting(int64_t tablet_id);
296
297
    static Status parse_request(const SchemaChangeParams& sc_params,
298
                                TabletSchema* base_tablet_schema, TabletSchema* new_tablet_schema,
299
                                BlockChanger* changer, bool* sc_sorting, bool* sc_directly);
300
301
private:
302
    std::unique_ptr<SchemaChange> _get_sc_procedure(const BlockChanger& changer, bool sc_sorting,
303
0
                                                    bool sc_directly, int64_t mem_limit) {
304
0
        if (sc_sorting) {
305
0
            return std::make_unique<VLocalSchemaChangeWithSorting>(changer, mem_limit,
306
0
                                                                   _local_storage_engine);
307
0
        }
308
309
0
        if (sc_directly) {
310
0
            return std::make_unique<VSchemaChangeDirectly>(changer);
311
0
        }
312
313
0
        return std::make_unique<LinkedSchemaChange>();
314
0
    }
315
316
    Status _get_versions_to_be_changed(std::vector<Version>* versions_to_be_changed,
317
                                       RowsetSharedPtr* max_rowset);
318
319
    Status _do_process_alter_tablet(const TAlterTabletReqV2& request);
320
321
    Status _validate_alter_result(const TAlterTabletReqV2& request);
322
323
    Status _convert_historical_rowsets(const SchemaChangeParams& sc_params,
324
                                       int64_t* real_alter_version);
325
326
    // Initialization Settings for creating a default value
327
    static Status _init_column_mapping(ColumnMapping* column_mapping,
328
                                       const TabletColumn& column_schema, const std::string& value);
329
330
    Status _calc_delete_bitmap_for_mow_table(int64_t alter_version);
331
332
    StorageEngine& _local_storage_engine;
333
    TabletSharedPtr _base_tablet;
334
    TabletSharedPtr _new_tablet;
335
    TabletSchemaSPtr _base_tablet_schema;
336
    TabletSchemaSPtr _new_tablet_schema;
337
    std::shared_mutex _mutex;
338
    std::unordered_set<int64_t> _tablet_ids_in_converting;
339
    std::set<std::string> _supported_functions;
340
    std::string _job_id;
341
};
342
} // namespace doris