Coverage Report

Created: 2026-08-01 21:36

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/storage/tablet_info.cpp
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
#include "storage/tablet_info.h"
19
20
#include <butil/logging.h>
21
#include <gen_cpp/Descriptors_types.h>
22
#include <gen_cpp/Exprs_types.h>
23
#include <gen_cpp/Partitions_types.h>
24
#include <gen_cpp/Types_types.h>
25
#include <gen_cpp/descriptors.pb.h>
26
#include <gen_cpp/olap_file.pb.h>
27
#include <glog/logging.h>
28
29
#include <algorithm>
30
#include <cstddef>
31
#include <cstdint>
32
#include <memory>
33
#include <ostream>
34
#include <string>
35
#include <tuple>
36
37
#include "common/exception.h"
38
#include "common/logging.h"
39
#include "common/status.h"
40
#include "core/column/column.h"
41
#include "core/data_type/data_type.h"
42
#include "core/data_type/define_primitive_type.h"
43
#include "core/data_type/primitive_type.h"
44
#include "core/data_type/storage_field_type.h"
45
#include "core/data_type_serde/data_type_datetimev2_nano_serde.h"
46
#include "core/value/large_int_value.h"
47
#include "runtime/descriptors.h"
48
#include "runtime/memory/mem_tracker.h"
49
#include "storage/tablet/tablet_schema.h"
50
#include "util/raw_value.h"
51
#include "util/string_parser.hpp"
52
#include "util/string_util.h"
53
// NOLINTNEXTLINE(unused-includes)
54
#include "core/value/vdatetime_value.h"
55
#include "exprs/function/cast/cast_to_date_or_datetime_impl.hpp"
56
#include "exprs/function/cast/cast_to_datetimev2_impl.hpp"
57
#include "exprs/function/cast/cast_to_datev2_impl.hpp"
58
#include "exprs/function/cast/cast_to_timestamptz.h"
59
#include "exprs/vexpr_context.h" // IWYU pragma: keep
60
#include "exprs/vliteral.h"
61
62
namespace doris {
63
64
65.0k
void OlapTableIndexSchema::to_protobuf(POlapTableIndexSchema* pindex) const {
65
65.0k
    pindex->set_id(index_id);
66
65.0k
    pindex->set_schema_hash(schema_hash);
67
65.0k
    if (row_binlog_id > 0) {
68
1
        pindex->set_row_binlog_id(row_binlog_id);
69
1
    }
70
429k
    for (auto* slot : slots) {
71
429k
        pindex->add_columns(slot->col_name());
72
429k
    }
73
437k
    for (auto* column : columns) {
74
437k
        column->to_schema_pb(pindex->add_columns_desc());
75
437k
    }
76
65.0k
    for (auto* index : indexes) {
77
0
        index->to_schema_pb(pindex->add_indexes_desc());
78
0
    }
79
65.0k
}
80
81
bool VOlapTablePartKeyComparator::operator()(const BlockRowWithIndicator& lhs,
82
533k
                                             const BlockRowWithIndicator& rhs) const {
83
533k
    Block* l_block = std::get<0>(lhs);
84
533k
    Block* r_block = std::get<0>(rhs);
85
533k
    int32_t l_row = std::get<1>(lhs);
86
533k
    int32_t r_row = std::get<1>(rhs);
87
533k
    bool l_use_new = std::get<2>(lhs);
88
533k
    bool r_use_new = std::get<2>(rhs);
89
90
533k
    VLOG_TRACE << '\n' << l_block->dump_data() << '\n' << r_block->dump_data();
91
92
533k
    if (l_row == -1) {
93
6
        return false;
94
533k
    } else if (r_row == -1) {
95
71.5k
        return true;
96
71.5k
    }
97
98
461k
    if (_param_locs.empty()) { // no transform, use origin column
99
461k
        for (auto slot_loc : _slot_locs) {
100
461k
            auto res = l_block->get_by_position(slot_loc).column->compare_at(
101
461k
                    l_row, r_row, *r_block->get_by_position(slot_loc).column, -1);
102
461k
            if (res != 0) {
103
459k
                return res < 0;
104
459k
            }
105
461k
        }
106
461k
    } else { // use transformed column to compare
107
40
        DCHECK(_slot_locs.size() == _param_locs.size())
108
0
                << _slot_locs.size() << ' ' << _param_locs.size();
109
110
40
        const std::vector<uint16_t>* l_index = l_use_new ? &_param_locs : &_slot_locs;
111
40
        const std::vector<uint16_t>* r_index = r_use_new ? &_param_locs : &_slot_locs;
112
113
52
        for (int i = 0; i < _slot_locs.size(); i++) {
114
40
            ColumnPtr l_col = l_block->get_by_position((*l_index)[i]).column;
115
40
            ColumnPtr r_col = r_block->get_by_position((*r_index)[i]).column;
116
117
40
            auto res = l_col->compare_at(l_row, r_row, *r_col, -1);
118
40
            if (res != 0) {
119
28
                return res < 0;
120
28
            }
121
40
        }
122
40
    }
123
124
    // equal, return false
125
2.19k
    return false;
126
461k
}
127
128
6.21k
Status OlapTableSchemaParam::init(const POlapTableSchemaParam& pschema) {
129
6.21k
    _db_id = pschema.db_id();
130
6.21k
    _table_id = pschema.table_id();
131
6.21k
    _version = pschema.version();
132
6.21k
    if (pschema.has_unique_key_update_mode()) {
133
6.21k
        _unique_key_update_mode = pschema.unique_key_update_mode();
134
6.21k
        if (pschema.has_sequence_map_col_unique_id()) {
135
6.21k
            _sequence_map_col_uid = pschema.sequence_map_col_unique_id();
136
6.21k
        }
137
6.21k
    } else {
138
        // for backward compatibility
139
3
        if (pschema.has_partial_update() && pschema.partial_update()) {
140
0
            _unique_key_update_mode = UniqueKeyUpdateModePB::UPDATE_FIXED_COLUMNS;
141
3
        } else {
142
3
            _unique_key_update_mode = UniqueKeyUpdateModePB::UPSERT;
143
3
        }
144
3
    }
145
6.21k
    _is_strict_mode = pschema.is_strict_mode();
146
6.21k
    if (_unique_key_update_mode == UniqueKeyUpdateModePB::UPDATE_FIXED_COLUMNS) {
147
312
        _auto_increment_column = pschema.auto_increment_column();
148
312
        if (!_auto_increment_column.empty() && pschema.auto_increment_column_unique_id() == -1) {
149
0
            return Status::InternalError(
150
0
                    "Auto increment column id is not set in FE. Maybe FE is an older version "
151
0
                    "different from BE.");
152
0
        }
153
312
        _auto_increment_column_unique_id = pschema.auto_increment_column_unique_id();
154
312
    }
155
6.21k
    if (_unique_key_update_mode != UniqueKeyUpdateModePB::UPSERT) {
156
312
        if (pschema.has_partial_update_new_key_policy()) {
157
312
            _partial_update_new_row_policy = pschema.partial_update_new_key_policy();
158
312
        }
159
312
    }
160
6.21k
    _timestamp_ms = pschema.timestamp_ms();
161
6.21k
    if (pschema.has_nano_seconds()) {
162
6.21k
        _nano_seconds = pschema.nano_seconds();
163
6.21k
    }
164
6.21k
    _timezone = pschema.timezone();
165
166
6.21k
    for (const auto& col : pschema.partial_update_input_columns()) {
167
1.45k
        _partial_update_input_columns.insert(col);
168
1.45k
    }
169
6.21k
    std::unordered_map<std::string, SlotDescriptor*> slots_map;
170
171
6.21k
    _tuple_desc = _obj_pool.add(new TupleDescriptor(pschema.tuple_desc()));
172
173
45.6k
    for (const auto& p_slot_desc : pschema.slot_descs()) {
174
45.6k
        auto* slot_desc = _obj_pool.add(new SlotDescriptor(p_slot_desc));
175
45.6k
        _tuple_desc->add_slot(slot_desc);
176
45.6k
        std::string is_null_str = slot_desc->is_nullable() ? "true" : "false";
177
45.6k
        std::string data_type_str = std::to_string(
178
45.6k
                int64_t(primitive_type_to_storage_field_type(slot_desc->col_type())));
179
45.6k
        slots_map.emplace(to_lower(slot_desc->col_name()) + "+" + data_type_str + is_null_str,
180
45.6k
                          slot_desc);
181
45.6k
    }
182
183
6.23k
    for (const auto& p_index : pschema.indexes()) {
184
6.23k
        auto* index = _obj_pool.add(new OlapTableIndexSchema());
185
6.23k
        index->index_id = p_index.id();
186
6.23k
        index->schema_hash = p_index.schema_hash();
187
6.23k
        if (p_index.has_row_binlog_id()) {
188
0
            index->row_binlog_id = p_index.row_binlog_id();
189
0
        }
190
47.0k
        for (const auto& pcolumn_desc : p_index.columns_desc()) {
191
47.0k
            TabletColumn* tc = _obj_pool.add(new TabletColumn());
192
47.0k
            tc->init_from_pb(pcolumn_desc);
193
47.0k
            if (_unique_key_update_mode != UniqueKeyUpdateModePB::UPDATE_FIXED_COLUMNS ||
194
47.0k
                _partial_update_input_columns.contains(pcolumn_desc.name())) {
195
45.5k
                std::string is_null_str = pcolumn_desc.is_nullable() ? "true" : "false";
196
45.5k
                std::string data_type_str = std::to_string(int64_t(tc->type()));
197
45.5k
                auto it = slots_map.find(to_lower(pcolumn_desc.name()) + "+" + data_type_str +
198
45.5k
                                         is_null_str);
199
45.5k
                if (it == std::end(slots_map)) {
200
0
                    std::string keys {};
201
0
                    for (const auto& [key, _] : slots_map) {
202
0
                        keys += fmt::format("{},", key);
203
0
                    }
204
0
                    LOG_EVERY_SECOND(WARNING) << fmt::format(
205
0
                            "[OlapTableSchemaParam::init(const POlapTableSchemaParam& pschema)]: "
206
0
                            "unknown index column, column={}, type={}, data_type_str={}, "
207
0
                            "is_null_str={}, slots_map.keys()=[{}], {}\npschema={}",
208
0
                            pcolumn_desc.name(), pcolumn_desc.type(), data_type_str, is_null_str,
209
0
                            keys, debug_string(), pschema.ShortDebugString());
210
211
0
                    return Status::InternalError("unknown index column, column={}, type={}",
212
0
                                                 pcolumn_desc.name(), pcolumn_desc.type());
213
0
                }
214
45.5k
                index->slots.emplace_back(it->second);
215
45.5k
            }
216
47.0k
            index->columns.emplace_back(tc);
217
47.0k
        }
218
6.23k
        for (const auto& pindex_desc : p_index.indexes_desc()) {
219
0
            TabletIndex* ti = _obj_pool.add(new TabletIndex());
220
0
            ti->init_from_pb(pindex_desc);
221
0
            index->indexes.emplace_back(ti);
222
0
        }
223
6.23k
        _indexes.emplace_back(index);
224
6.23k
    }
225
226
6.21k
    if (pschema.has_row_binlog_index_schema()) {
227
0
        const auto& p_index = pschema.row_binlog_index_schema();
228
0
        auto* index = _obj_pool.add(new OlapTableIndexSchema());
229
0
        index->index_id = p_index.id();
230
0
        index->schema_hash = p_index.schema_hash();
231
0
        if (p_index.has_row_binlog_id()) {
232
0
            index->row_binlog_id = p_index.row_binlog_id();
233
0
        }
234
0
        for (const auto& pcolumn_desc : p_index.columns_desc()) {
235
0
            TabletColumn* tc = _obj_pool.add(new TabletColumn());
236
0
            tc->init_from_pb(pcolumn_desc);
237
0
            index->columns.emplace_back(tc);
238
0
        }
239
0
        for (const auto& pindex_desc : p_index.indexes_desc()) {
240
0
            TabletIndex* ti = _obj_pool.add(new TabletIndex());
241
0
            ti->init_from_pb(pindex_desc);
242
0
            index->indexes.emplace_back(ti);
243
0
        }
244
0
        _row_binlog_index_schema = index;
245
0
    }
246
247
6.21k
    std::sort(_indexes.begin(), _indexes.end(),
248
6.21k
              [](const OlapTableIndexSchema* lhs, const OlapTableIndexSchema* rhs) {
249
28
                  return lhs->index_id < rhs->index_id;
250
28
              });
251
6.21k
    return Status::OK();
252
6.21k
}
253
254
22.3k
Status OlapTableSchemaParam::init_unique_key_update_mode(const TOlapTableSchemaParam& tschema) {
255
22.3k
    if (tschema.__isset.unique_key_update_mode) {
256
22.3k
        switch (tschema.unique_key_update_mode) {
257
18.7k
        case doris::TUniqueKeyUpdateMode::UPSERT: {
258
18.7k
            _unique_key_update_mode = UniqueKeyUpdateModePB::UPSERT;
259
18.7k
            break;
260
0
        }
261
3.66k
        case doris::TUniqueKeyUpdateMode::UPDATE_FIXED_COLUMNS: {
262
3.66k
            _unique_key_update_mode = UniqueKeyUpdateModePB::UPDATE_FIXED_COLUMNS;
263
3.66k
            break;
264
0
        }
265
0
        case doris::TUniqueKeyUpdateMode::UPDATE_FLEXIBLE_COLUMNS: {
266
0
            _unique_key_update_mode = UniqueKeyUpdateModePB::UPDATE_FLEXIBLE_COLUMNS;
267
0
            break;
268
0
        }
269
0
        default: {
270
0
            return Status::InternalError(
271
0
                    "Unknown unique_key_update_mode: {}, should be one of "
272
0
                    "UPSERT/UPDATE_FIXED_COLUMNS/UPDATE_FLEXIBLE_COLUMNS",
273
0
                    tschema.unique_key_update_mode);
274
0
        }
275
22.3k
        }
276
22.3k
        if (tschema.__isset.sequence_map_col_unique_id) {
277
22.3k
            _sequence_map_col_uid = tschema.sequence_map_col_unique_id;
278
22.3k
        }
279
18.4E
    } else {
280
        // for backward compatibility
281
18.4E
        if (tschema.__isset.is_partial_update && tschema.is_partial_update) {
282
0
            _unique_key_update_mode = UniqueKeyUpdateModePB::UPDATE_FIXED_COLUMNS;
283
18.4E
        } else {
284
18.4E
            _unique_key_update_mode = UniqueKeyUpdateModePB::UPSERT;
285
18.4E
        }
286
18.4E
    }
287
22.3k
    return Status::OK();
288
22.3k
}
289
290
22.3k
Status OlapTableSchemaParam::init(const TOlapTableSchemaParam& tschema) {
291
22.3k
    _db_id = tschema.db_id;
292
22.3k
    _table_id = tschema.table_id;
293
22.3k
    _version = tschema.version;
294
22.3k
    RETURN_IF_ERROR(init_unique_key_update_mode(tschema));
295
22.3k
    if (tschema.__isset.is_strict_mode) {
296
22.3k
        _is_strict_mode = tschema.is_strict_mode;
297
22.3k
    }
298
22.3k
    if (_unique_key_update_mode == UniqueKeyUpdateModePB::UPDATE_FIXED_COLUMNS) {
299
3.63k
        _auto_increment_column = tschema.auto_increment_column;
300
3.63k
        if (!_auto_increment_column.empty() && tschema.auto_increment_column_unique_id == -1) {
301
0
            return Status::InternalError(
302
0
                    "Auto increment column id is not set in FE. Maybe FE is an older version "
303
0
                    "different from BE.");
304
0
        }
305
3.63k
        _auto_increment_column_unique_id = tschema.auto_increment_column_unique_id;
306
3.63k
    }
307
308
22.3k
    if (_unique_key_update_mode != UniqueKeyUpdateModePB::UPSERT) {
309
3.64k
        if (tschema.__isset.partial_update_new_key_policy) {
310
3.64k
            switch (tschema.partial_update_new_key_policy) {
311
3.64k
            case doris::TPartialUpdateNewRowPolicy::APPEND: {
312
3.64k
                _partial_update_new_row_policy = PartialUpdateNewRowPolicyPB::APPEND;
313
3.64k
                break;
314
0
            }
315
0
            case doris::TPartialUpdateNewRowPolicy::ERROR: {
316
0
                _partial_update_new_row_policy = PartialUpdateNewRowPolicyPB::ERROR;
317
0
                break;
318
0
            }
319
0
            default: {
320
0
                return Status::InvalidArgument(
321
0
                        "Unknown partial_update_new_key_behavior: {}, should be one of "
322
0
                        "'APPEND' or 'ERROR'",
323
0
                        tschema.partial_update_new_key_policy);
324
0
            }
325
3.64k
            }
326
3.64k
        }
327
3.64k
    }
328
329
22.3k
    for (const auto& tcolumn : tschema.partial_update_input_columns) {
330
11.9k
        _partial_update_input_columns.insert(tcolumn);
331
11.9k
    }
332
22.3k
    std::unordered_map<std::string, SlotDescriptor*> slots_map;
333
22.3k
    _tuple_desc = _obj_pool.add(new TupleDescriptor(tschema.tuple_desc));
334
130k
    for (const auto& t_slot_desc : tschema.slot_descs) {
335
130k
        auto* slot_desc = _obj_pool.add(new SlotDescriptor(t_slot_desc));
336
130k
        _tuple_desc->add_slot(slot_desc);
337
130k
        std::string is_null_str = slot_desc->is_nullable() ? "true" : "false";
338
130k
        std::string data_type_str = std::to_string(int64_t(slot_desc->col_type()));
339
130k
        slots_map.emplace(to_lower(slot_desc->col_name()) + "+" + data_type_str + is_null_str,
340
130k
                          slot_desc);
341
130k
    }
342
343
22.3k
    for (const auto& t_index : tschema.indexes) {
344
22.3k
        std::unordered_map<std::string, int32_t> index_slots_map;
345
22.3k
        auto* index = _obj_pool.add(new OlapTableIndexSchema());
346
22.3k
        index->index_id = t_index.id;
347
22.3k
        index->schema_hash = t_index.schema_hash;
348
22.3k
        if (t_index.__isset.row_binlog_id) {
349
22.3k
            index->row_binlog_id = t_index.row_binlog_id;
350
22.3k
        }
351
142k
        for (const auto& tcolumn_desc : t_index.columns_desc) {
352
142k
            TabletColumn* tc = _obj_pool.add(new TabletColumn());
353
142k
            tc->init_from_thrift(tcolumn_desc);
354
142k
            if (_unique_key_update_mode != UniqueKeyUpdateModePB::UPDATE_FIXED_COLUMNS ||
355
142k
                _partial_update_input_columns.contains(tcolumn_desc.column_name)) {
356
130k
                std::string is_null_str = tcolumn_desc.is_allow_null ? "true" : "false";
357
130k
                std::string data_type_str =
358
130k
                        std::to_string(int64_t(storage_field_type_to_primitive_type(tc->type())));
359
130k
                auto it = slots_map.find(to_lower(tcolumn_desc.column_name) + "+" + data_type_str +
360
130k
                                         is_null_str);
361
130k
                if (it == slots_map.end()) {
362
0
                    std::stringstream ss;
363
0
                    ss << tschema;
364
0
                    std::string keys {};
365
0
                    for (const auto& [key, _] : slots_map) {
366
0
                        keys += fmt::format("{},", key);
367
0
                    }
368
0
                    LOG_EVERY_SECOND(WARNING) << fmt::format(
369
0
                            "[OlapTableSchemaParam::init(const TOlapTableSchemaParam& tschema)]: "
370
0
                            "unknown index column, column={}, type={}, data_type_str={}, "
371
0
                            "is_null_str={}, slots_map.keys()=[{}], {}\ntschema={}",
372
0
                            tcolumn_desc.column_name, tcolumn_desc.column_type.type, data_type_str,
373
0
                            is_null_str, keys, debug_string(), ss.str());
374
0
                    return Status::InternalError("unknown index column, column={}, type={}",
375
0
                                                 tcolumn_desc.column_name,
376
0
                                                 tcolumn_desc.column_type.type);
377
0
                }
378
130k
                index->slots.emplace_back(it->second);
379
130k
            }
380
142k
            index_slots_map.emplace(to_lower(tcolumn_desc.column_name), tcolumn_desc.col_unique_id);
381
142k
            index->columns.emplace_back(tc);
382
142k
        }
383
22.3k
        if (t_index.__isset.indexes_desc) {
384
22.2k
            for (const auto& tindex_desc : t_index.indexes_desc) {
385
0
                std::vector<int32_t> column_unique_ids(tindex_desc.columns.size());
386
0
                for (size_t i = 0; i < tindex_desc.columns.size(); i++) {
387
0
                    auto it = index_slots_map.find(to_lower(tindex_desc.columns[i]));
388
0
                    if (it != index_slots_map.end()) {
389
0
                        column_unique_ids[i] = it->second;
390
0
                    }
391
0
                }
392
0
                TabletIndex* ti = _obj_pool.add(new TabletIndex());
393
0
                ti->init_from_thrift(tindex_desc, column_unique_ids);
394
0
                index->indexes.emplace_back(ti);
395
0
            }
396
22.2k
        }
397
22.3k
        if (t_index.__isset.where_clause) {
398
0
            RETURN_IF_ERROR(VExpr::create_expr_tree(t_index.where_clause, index->where_clause));
399
0
        }
400
22.3k
        _indexes.emplace_back(index);
401
22.3k
    }
402
403
22.3k
    if (tschema.__isset.row_binlog_index_schema) {
404
4
        const auto& t_index = tschema.row_binlog_index_schema;
405
4
        auto* index = _obj_pool.add(new OlapTableIndexSchema());
406
4
        index->index_id = t_index.id;
407
4
        index->schema_hash = t_index.schema_hash;
408
4
        if (t_index.__isset.row_binlog_id) {
409
0
            index->row_binlog_id = t_index.row_binlog_id;
410
0
        }
411
14
        for (const auto& tcolumn_desc : t_index.columns_desc) {
412
14
            TabletColumn* tc = _obj_pool.add(new TabletColumn());
413
14
            tc->init_from_thrift(tcolumn_desc);
414
14
            index->columns.emplace_back(tc);
415
14
        }
416
4
        _row_binlog_index_schema = index;
417
4
    }
418
419
22.3k
    std::sort(_indexes.begin(), _indexes.end(),
420
22.3k
              [](const OlapTableIndexSchema* lhs, const OlapTableIndexSchema* rhs) {
421
32
                  return lhs->index_id < rhs->index_id;
422
32
              });
423
22.3k
    return Status::OK();
424
22.3k
}
425
426
10.5k
void OlapTableSchemaParam::to_protobuf(POlapTableSchemaParam* pschema) const {
427
10.5k
    pschema->set_db_id(_db_id);
428
10.5k
    pschema->set_table_id(_table_id);
429
10.5k
    pschema->set_version(_version);
430
10.5k
    pschema->set_unique_key_update_mode(_unique_key_update_mode);
431
10.5k
    if (_unique_key_update_mode == UniqueKeyUpdateModePB::UPDATE_FIXED_COLUMNS) {
432
        // for backward compatibility
433
1.71k
        pschema->set_partial_update(true);
434
1.71k
    }
435
10.5k
    pschema->set_partial_update_new_key_policy(_partial_update_new_row_policy);
436
10.5k
    pschema->set_is_strict_mode(_is_strict_mode);
437
10.5k
    pschema->set_auto_increment_column(_auto_increment_column);
438
10.5k
    pschema->set_auto_increment_column_unique_id(_auto_increment_column_unique_id);
439
10.5k
    pschema->set_timestamp_ms(_timestamp_ms);
440
10.5k
    pschema->set_timezone(_timezone);
441
10.5k
    pschema->set_nano_seconds(_nano_seconds);
442
10.5k
    pschema->set_sequence_map_col_unique_id(_sequence_map_col_uid);
443
10.5k
    for (auto col : _partial_update_input_columns) {
444
5.69k
        *pschema->add_partial_update_input_columns() = col;
445
5.69k
    }
446
10.5k
    _tuple_desc->to_protobuf(pschema->mutable_tuple_desc());
447
73.3k
    for (auto* slot : _tuple_desc->slots()) {
448
73.3k
        slot->to_protobuf(pschema->add_slot_descs());
449
73.3k
    }
450
10.5k
    for (auto* index : _indexes) {
451
10.5k
        index->to_protobuf(pschema->add_indexes());
452
10.5k
    }
453
10.5k
    if (_row_binlog_index_schema != nullptr) {
454
0
        _row_binlog_index_schema->to_protobuf(pschema->mutable_row_binlog_index_schema());
455
0
    }
456
10.5k
}
457
458
0
std::string OlapTableSchemaParam::debug_string() const {
459
0
    std::stringstream ss;
460
0
    ss << "tuple_desc=" << _tuple_desc->debug_string();
461
0
    return ss.str();
462
0
}
463
464
VOlapTablePartitionParam::VOlapTablePartitionParam(std::shared_ptr<OlapTableSchemaParam>& schema,
465
                                                   const TOlapTablePartitionParam& t_param)
466
15.9k
        : _schema(schema),
467
15.9k
          _t_param(t_param),
468
15.9k
          _slots(_schema->tuple_desc()->slots()),
469
15.9k
          _mem_tracker(std::make_unique<MemTracker>("OlapTablePartitionParam")),
470
15.9k
          _part_type(t_param.partition_type) {
471
15.9k
    if (t_param.__isset.enable_automatic_partition && t_param.enable_automatic_partition) {
472
48
        _is_auto_partition = true;
473
48
        auto size = t_param.partition_function_exprs.size();
474
48
        _part_func_ctx.resize(size);
475
48
        _partition_function.resize(size);
476
48
        DCHECK((t_param.partition_type == TPartitionType::RANGE_PARTITIONED && size == 1) ||
477
0
               (t_param.partition_type == TPartitionType::LIST_PARTITIONED && size >= 1))
478
0
                << "now support only 1 partition column for auto range partitions. "
479
0
                << t_param.partition_type << " " << size;
480
96
        for (int i = 0; i < size; ++i) {
481
48
            Status st =
482
48
                    VExpr::create_expr_tree(t_param.partition_function_exprs[i], _part_func_ctx[i]);
483
48
            if (!st.ok()) {
484
0
                throw Exception(Status::InternalError("Partition function expr is not valid"),
485
0
                                "Partition function expr is not valid");
486
0
            }
487
48
            _partition_function[i] = _part_func_ctx[i]->root();
488
48
        }
489
48
    }
490
491
15.9k
    if (t_param.__isset.enable_auto_detect_overwrite && t_param.enable_auto_detect_overwrite) {
492
1
        _is_auto_detect_overwrite = true;
493
1
        DCHECK(t_param.__isset.overwrite_group_id);
494
1
        _overwrite_group_id = t_param.overwrite_group_id;
495
1
    }
496
497
15.9k
    if (t_param.__isset.master_address) {
498
40
        _master_address = std::make_shared<TNetworkAddress>(t_param.master_address);
499
40
    }
500
501
15.9k
    if (_is_auto_partition) {
502
        // the nullable mode depends on partition_exprs. not column slots. so use them.
503
18.4E
        DCHECK(_partition_function.size() <= _slots.size())
504
18.4E
                << _partition_function.size() << ", " << _slots.size();
505
506
        // suppose (k0, [k1], [k2]), so get [k1, 0], [k2, 1]
507
48
        std::map<std::string, int> partition_slots_map; // name to idx in part_exprs
508
96
        for (size_t i = 0; i < t_param.partition_columns.size(); i++) {
509
48
            partition_slots_map.emplace(t_param.partition_columns[i], i);
510
48
        }
511
512
        // here we rely on the same order and number of the _part_funcs and _slots in the prefix
513
        // _part_block contains all slots of table.
514
190
        for (auto* slot : _slots) {
515
            // try to replace with partition expr.
516
190
            if (auto it = partition_slots_map.find(slot->col_name());
517
190
                it != partition_slots_map.end()) { // it's a partition column slot
518
50
                auto& expr_type = _partition_function[it->second]->data_type();
519
50
                _partition_block.insert({expr_type->create_column(), expr_type, slot->col_name()});
520
140
            } else {
521
140
                _partition_block.insert({slot->get_empty_mutable_column(),
522
140
                                         slot->get_data_type_ptr(), slot->col_name()});
523
140
            }
524
190
        }
525
18.4E
        VLOG_TRACE << _partition_block.dump_structure();
526
15.8k
    } else {
527
        // we insert all. but not all will be used. it will controlled by _partition_slot_locs
528
80.2k
        for (auto* slot : _slots) {
529
80.2k
            _partition_block.insert({slot->get_empty_mutable_column(), slot->get_data_type_ptr(),
530
80.2k
                                     slot->col_name()});
531
80.2k
        }
532
15.8k
    }
533
15.9k
}
534
535
16.0k
VOlapTablePartitionParam::~VOlapTablePartitionParam() {
536
16.0k
    _mem_tracker->release(_mem_usage);
537
16.0k
}
538
539
15.5k
Status VOlapTablePartitionParam::init() {
540
15.5k
    std::vector<std::string> slot_column_names;
541
80.8k
    for (auto* slot_desc : _schema->tuple_desc()->slots()) {
542
80.8k
        slot_column_names.emplace_back(slot_desc->col_name());
543
80.8k
    }
544
545
15.5k
    auto find_slot_locs = [&slot_column_names](const std::string& slot_name,
546
15.5k
                                               std::vector<uint16_t>& locs,
547
21.3k
                                               const std::string& column_type) {
548
21.3k
        auto it = std::find(slot_column_names.begin(), slot_column_names.end(), slot_name);
549
21.3k
        if (it == slot_column_names.end()) {
550
0
            return Status::InternalError("{} column not found, column ={}", column_type, slot_name);
551
0
        }
552
21.3k
        locs.emplace_back(it - slot_column_names.begin());
553
21.3k
        return Status::OK();
554
21.3k
    };
555
556
    // here we find the partition columns. others maybe non-partition columns/special columns.
557
15.5k
    if (_t_param.__isset.partition_columns) {
558
2.56k
        for (auto& part_col : _t_param.partition_columns) {
559
2.56k
            RETURN_IF_ERROR(find_slot_locs(part_col, _partition_slot_locs, "partition"));
560
2.56k
        }
561
2.53k
    }
562
563
15.5k
    _partitions_map = std::make_unique<
564
15.5k
            std::map<BlockRowWithIndicator, VOlapTablePartition*, VOlapTablePartKeyComparator>>(
565
15.5k
            VOlapTablePartKeyComparator(_partition_slot_locs, _transformed_slot_locs));
566
15.7k
    if (_t_param.__isset.distributed_columns) {
567
18.8k
        for (auto& col : _t_param.distributed_columns) {
568
18.8k
            RETURN_IF_ERROR(find_slot_locs(col, _distributed_slot_locs, "distributed"));
569
18.8k
        }
570
15.7k
    }
571
572
    // for both auto/non-auto partition table.
573
15.5k
    _is_in_partition = _part_type == TPartitionType::type::LIST_PARTITIONED;
574
575
    // initial partitions. if meet dummy partitions only for open BE nodes, not generate key of them for finding
576
16.2k
    for (const auto& t_part : _t_param.partitions) {
577
16.2k
        VOlapTablePartition* part = nullptr;
578
16.2k
        RETURN_IF_ERROR(generate_partition_from(t_part, part));
579
16.2k
        _partitions.emplace_back(part);
580
581
16.3k
        if (!_t_param.partitions_is_fake) {
582
16.3k
            if (_is_in_partition) {
583
1.84k
                for (auto& in_key : part->in_keys) {
584
1.84k
                    _partitions_map->emplace(std::tuple {in_key.first, in_key.second, false}, part);
585
1.84k
                }
586
14.4k
            } else {
587
14.4k
                _partitions_map->emplace(
588
14.4k
                        std::tuple {part->end_key.first, part->end_key.second, false}, part);
589
14.4k
            }
590
16.3k
        }
591
16.2k
    }
592
593
15.5k
    _mem_usage = _partition_block.allocated_bytes();
594
15.5k
    _mem_tracker->consume(_mem_usage);
595
15.5k
    return Status::OK();
596
15.5k
}
597
598
bool VOlapTablePartitionParam::_part_contains(VOlapTablePartition* part,
599
223k
                                              BlockRowWithIndicator key) const {
600
223k
    VOlapTablePartKeyComparator comparator(_partition_slot_locs, _transformed_slot_locs);
601
    // we have used upper_bound to find to ensure key < part.right and this part is closest(right - key is min)
602
    // now we only have to check (key >= part.left). the comparator(a,b) means a < b, so we use anti
603
223k
    return part->start_key.second == -1 /* spj: start_key.second == -1 means only single partition*/
604
223k
           || !comparator(key, std::tuple {part->start_key.first, part->start_key.second, false});
605
223k
}
606
607
// insert value into _partition_block's column
608
// NOLINTBEGIN(readability-function-size)
609
4.45k
static Status _create_partition_key(const TExprNode& t_expr, BlockRow* part_key, uint16_t pos) {
610
4.45k
    auto column = std::move(*part_key->first->get_by_position(pos).column).mutate();
611
4.45k
    switch (t_expr.node_type) {
612
2.69k
    case TExprNodeType::DATE_LITERAL: {
613
2.69k
        const auto& partition_column_type = part_key->first->get_by_position(pos).type;
614
2.69k
        const auto primitive_type = partition_column_type->get_primitive_type();
615
2.69k
        if (primitive_type == TYPE_DATEV2) {
616
900
            DateV2Value<DateV2ValueType> dt;
617
900
            CastParameters params;
618
900
            if (!CastToDateV2::from_string_strict_mode<DatelikeParseMode::STRICT>(
619
900
                        {t_expr.date_literal.value.c_str(), t_expr.date_literal.value.size()}, dt,
620
900
                        nullptr, params)) {
621
0
                std::stringstream ss;
622
0
                ss << "invalid date literal in partition column, date=" << t_expr.date_literal;
623
0
                return Status::InternalError(ss.str());
624
0
            }
625
900
            column->insert_data(reinterpret_cast<const char*>(&dt), 0);
626
1.79k
        } else if (primitive_type == TYPE_DATETIMEV2) {
627
1.79k
            DateV2Value<DateTimeV2ValueType> dt;
628
1.79k
            const auto scale = static_cast<int32_t>(partition_column_type->get_scale());
629
1.79k
            CastParameters params;
630
1.79k
            if (!CastToDatetimeV2::from_string_strict_mode<DatelikeParseMode::STRICT>(
631
1.79k
                        {t_expr.date_literal.value.c_str(), t_expr.date_literal.value.size()}, dt,
632
1.79k
                        nullptr, scale, params)) {
633
0
                std::stringstream ss;
634
0
                ss << "invalid date literal in partition column, date=" << t_expr.date_literal;
635
0
                return Status::InternalError(ss.str());
636
0
            }
637
1.79k
            column->insert_data(reinterpret_cast<const char*>(&dt), 0);
638
1.79k
        } else if (primitive_type == TYPE_DATETIMEV2_NANO) {
639
0
            const auto scale = static_cast<int32_t>(partition_column_type->get_scale());
640
0
            int64_t epoch_nanos = 0;
641
0
            RETURN_IF_ERROR(parse_datetimev2_nano(
642
0
                    {t_expr.date_literal.value.data(), t_expr.date_literal.value.size()}, scale,
643
0
                    &epoch_nanos));
644
0
            const DateTimeV2NanoValue dt(epoch_nanos);
645
0
            column->insert_data(reinterpret_cast<const char*>(&dt), 0);
646
2
        } else if (primitive_type == TYPE_TIMESTAMPTZ) {
647
0
            TimestampTzValue res;
648
0
            CastParameters params {.status = Status::OK(), .is_strict = true};
649
0
            const auto scale = static_cast<int32_t>(partition_column_type->get_scale());
650
0
            if (!CastToTimestampTz::from_string(
651
0
                        {t_expr.date_literal.value.c_str(), t_expr.date_literal.value.size()}, res,
652
0
                        params, nullptr, scale)) [[unlikely]] {
653
0
                std::stringstream ss;
654
0
                ss << "invalid timestamptz literal in partition column, value="
655
0
                   << t_expr.date_literal;
656
0
                return Status::InternalError(ss.str());
657
0
            }
658
0
            column->insert_data(reinterpret_cast<const char*>(&res), 0);
659
2
        } else {
660
2
            VecDateTimeValue dt;
661
2
            CastParameters params;
662
2
            if (!CastToDateOrDatetime::from_string_strict_mode<DatelikeParseMode::STRICT,
663
2
                                                               DatelikeTargetType::DATE_TIME>(
664
2
                        {t_expr.date_literal.value.c_str(), t_expr.date_literal.value.size()}, dt,
665
2
                        nullptr, params)) {
666
0
                std::stringstream ss;
667
0
                ss << "invalid date literal in partition column, date=" << t_expr.date_literal;
668
0
                return Status::InternalError(ss.str());
669
0
            }
670
2
            if (primitive_type == TYPE_DATE) {
671
0
                dt.cast_to_date();
672
0
            }
673
2
            column->insert_data(reinterpret_cast<const char*>(&dt), 0);
674
2
        }
675
2.69k
        break;
676
2.69k
    }
677
2.69k
    case TExprNodeType::INT_LITERAL: {
678
720
        switch (t_expr.type.types[0].scalar_type.type) {
679
48
        case TPrimitiveType::TINYINT: {
680
48
            auto value = cast_set<int8_t>(t_expr.int_literal.value);
681
48
            column->insert_data(reinterpret_cast<const char*>(&value), 0);
682
48
            break;
683
0
        }
684
0
        case TPrimitiveType::SMALLINT: {
685
0
            auto value = cast_set<int16_t>(t_expr.int_literal.value);
686
0
            column->insert_data(reinterpret_cast<const char*>(&value), 0);
687
0
            break;
688
0
        }
689
674
        case TPrimitiveType::INT: {
690
674
            auto value = cast_set<int32_t>(t_expr.int_literal.value);
691
674
            column->insert_data(reinterpret_cast<const char*>(&value), 0);
692
674
            break;
693
0
        }
694
0
        default:
695
0
            int64_t value = t_expr.int_literal.value;
696
0
            column->insert_data(reinterpret_cast<const char*>(&value), 0);
697
720
        }
698
754
        break;
699
720
    }
700
754
    case TExprNodeType::LARGE_INT_LITERAL: {
701
0
        StringParser::ParseResult parse_result = StringParser::PARSE_SUCCESS;
702
0
        auto value = StringParser::string_to_int<__int128>(t_expr.large_int_literal.value.c_str(),
703
0
                                                           t_expr.large_int_literal.value.size(),
704
0
                                                           &parse_result);
705
0
        if (parse_result != StringParser::PARSE_SUCCESS) {
706
0
            value = MAX_INT128;
707
0
        }
708
0
        column->insert_data(reinterpret_cast<const char*>(&value), 0);
709
0
        break;
710
720
    }
711
908
    case TExprNodeType::STRING_LITERAL: {
712
908
        size_t len = t_expr.string_literal.value.size();
713
908
        const char* str_val = t_expr.string_literal.value.c_str();
714
908
        column->insert_data(str_val, len);
715
908
        break;
716
720
    }
717
0
    case TExprNodeType::BOOL_LITERAL: {
718
0
        column->insert_data(reinterpret_cast<const char*>(&t_expr.bool_literal.value), 0);
719
0
        break;
720
720
    }
721
82
    case TExprNodeType::NULL_LITERAL: {
722
        // insert a null literal
723
82
        if (!column->is_nullable()) {
724
            // https://github.com/apache/doris/pull/39449 have forbid this cause. always add this check as protective measures
725
0
            return Status::InternalError("The column {} is not null, can't insert into NULL value.",
726
0
                                         part_key->first->get_by_position(pos).name);
727
0
        }
728
82
        column->insert_data(nullptr, 0);
729
82
        break;
730
82
    }
731
0
    default: {
732
0
        return Status::InternalError("unsupported partition column node type, type={}",
733
0
                                     t_expr.node_type);
734
82
    }
735
4.45k
    }
736
4.51k
    part_key->second = cast_set<int32_t>(column->size() - 1);
737
4.51k
    return Status::OK();
738
4.45k
}
739
// NOLINTEND(readability-function-size)
740
741
Status VOlapTablePartitionParam::_create_partition_keys(const std::vector<TExprNode>& t_exprs,
742
4.40k
                                                        BlockRow* part_key) {
743
8.88k
    for (int i = 0; i < t_exprs.size(); i++) {
744
4.48k
        RETURN_IF_ERROR(_create_partition_key(t_exprs[i], part_key, _partition_slot_locs[i]));
745
4.48k
    }
746
4.40k
    return Status::OK();
747
4.40k
}
748
749
Status VOlapTablePartitionParam::generate_partition_from(const TOlapTablePartition& t_part,
750
16.1k
                                                         VOlapTablePartition*& part_result) {
751
16.1k
    DCHECK(part_result == nullptr);
752
    // here we set the default value of partition bounds first! if it doesn't have some key, it will be -1.
753
16.1k
    part_result = _obj_pool.add(new VOlapTablePartition(&_partition_block));
754
16.1k
    part_result->id = t_part.id;
755
16.1k
    part_result->is_mutable = t_part.is_mutable;
756
16.1k
    if (t_part.__isset.load_tablet_idx) {
757
2.43k
        part_result->load_tablet_idx = t_part.load_tablet_idx;
758
2.43k
    }
759
16.1k
    if (t_part.__isset.bucket_be_id) {
760
0
        part_result->bucket_be_id = t_part.bucket_be_id;
761
0
    }
762
16.1k
    if (t_part.__isset.local_bucket_seqs) {
763
0
        part_result->local_bucket_seqs = t_part.local_bucket_seqs;
764
0
    }
765
766
16.1k
    if (_is_in_partition) {
767
1.83k
        for (const auto& keys : t_part.in_keys) {
768
1.81k
            RETURN_IF_ERROR(_create_partition_keys(
769
1.81k
                    keys, &part_result->in_keys.emplace_back(&_partition_block, -1)));
770
1.81k
        }
771
1.83k
        if (t_part.__isset.is_default_partition && t_part.is_default_partition &&
772
1.83k
            _default_partition == nullptr) {
773
0
            _default_partition = part_result;
774
0
        }
775
14.3k
    } else { // range
776
14.3k
        if (t_part.__isset.start_keys) {
777
1.27k
            RETURN_IF_ERROR(_create_partition_keys(t_part.start_keys, &part_result->start_key));
778
1.27k
        }
779
        // we generate the right bound but not insert into partition map
780
14.3k
        if (t_part.__isset.end_keys) {
781
1.30k
            RETURN_IF_ERROR(_create_partition_keys(t_part.end_keys, &part_result->end_key));
782
1.30k
        }
783
14.3k
    }
784
785
16.1k
    part_result->num_buckets = t_part.num_buckets;
786
16.1k
    auto num_indexes = _schema->indexes().size();
787
16.1k
    if (t_part.indexes.size() != num_indexes) {
788
0
        return Status::InternalError(
789
0
                "number of partition's index is not equal with schema's"
790
0
                ", num_part_indexes={}, num_schema_indexes={}",
791
0
                t_part.indexes.size(), num_indexes);
792
0
    }
793
16.1k
    part_result->indexes = t_part.indexes;
794
16.1k
    std::sort(part_result->indexes.begin(), part_result->indexes.end(),
795
16.1k
              [](const OlapTableIndexTablets& lhs, const OlapTableIndexTablets& rhs) {
796
0
                  return lhs.index_id < rhs.index_id;
797
0
              });
798
    // check index
799
32.5k
    for (int j = 0; j < num_indexes; ++j) {
800
16.3k
        if (part_result->indexes[j].index_id != _schema->indexes()[j]->index_id) {
801
0
            return Status::InternalError(
802
0
                    "partition's index is not equal with schema's"
803
0
                    ", part_index={}, schema_index={}",
804
0
                    part_result->indexes[j].index_id, _schema->indexes()[j]->index_id);
805
0
        }
806
16.3k
    }
807
16.4k
    if (t_part.__isset.total_replica_num) {
808
16.4k
        part_result->total_replica_num = t_part.total_replica_num;
809
16.4k
    }
810
16.5k
    if (t_part.__isset.load_required_replica_num) {
811
16.5k
        part_result->load_required_replica_num = t_part.load_required_replica_num;
812
16.5k
    }
813
16.1k
    if (t_part.__isset.tablet_version_gap_backends) {
814
0
        for (const auto& [tablet_id, backend_ids] : t_part.tablet_version_gap_backends) {
815
0
            auto& gap_set = part_result->tablet_version_gap_backends[tablet_id];
816
0
            for (auto backend_id : backend_ids) {
817
0
                gap_set.insert(backend_id);
818
0
            }
819
0
        }
820
0
    }
821
16.1k
    return Status::OK();
822
16.1k
}
823
824
Status VOlapTablePartitionParam::add_partitions(
825
8
        const std::vector<TOlapTablePartition>& partitions) {
826
8
    for (const auto& t_part : partitions) {
827
8
        auto* part = _obj_pool.add(new VOlapTablePartition(&_partition_block));
828
8
        part->id = t_part.id;
829
8
        part->is_mutable = t_part.is_mutable;
830
831
        // we dont pass right keys when it's MAX_VALUE. so there's possibility we only have start_key but not end_key
832
        // range partition
833
8
        if (t_part.__isset.start_keys) {
834
2
            RETURN_IF_ERROR(_create_partition_keys(t_part.start_keys, &part->start_key));
835
2
        }
836
8
        if (t_part.__isset.end_keys) {
837
2
            RETURN_IF_ERROR(_create_partition_keys(t_part.end_keys, &part->end_key));
838
2
        }
839
        // list partition - we only set 1 value in 1 partition for new created ones
840
8
        if (t_part.__isset.in_keys) {
841
6
            for (const auto& keys : t_part.in_keys) {
842
6
                RETURN_IF_ERROR(_create_partition_keys(
843
6
                        keys, &part->in_keys.emplace_back(&_partition_block, -1)));
844
6
            }
845
6
            if (t_part.__isset.is_default_partition && t_part.is_default_partition) {
846
0
                _default_partition = part;
847
0
            }
848
6
        }
849
850
8
        part->num_buckets = t_part.num_buckets;
851
8
        if (t_part.__isset.load_tablet_idx) {
852
0
            part->load_tablet_idx = t_part.load_tablet_idx;
853
0
        }
854
8
        if (t_part.__isset.bucket_be_id) {
855
0
            part->bucket_be_id = t_part.bucket_be_id;
856
0
        }
857
8
        if (t_part.__isset.local_bucket_seqs) {
858
0
            part->local_bucket_seqs = t_part.local_bucket_seqs;
859
0
        }
860
8
        auto num_indexes = _schema->indexes().size();
861
8
        if (t_part.indexes.size() != num_indexes) {
862
0
            return Status::InternalError(
863
0
                    "number of partition's index is not equal with schema's"
864
0
                    ", num_part_indexes={}, num_schema_indexes={}",
865
0
                    t_part.indexes.size(), num_indexes);
866
0
        }
867
8
        part->indexes = t_part.indexes;
868
8
        std::sort(part->indexes.begin(), part->indexes.end(),
869
8
                  [](const OlapTableIndexTablets& lhs, const OlapTableIndexTablets& rhs) {
870
0
                      return lhs.index_id < rhs.index_id;
871
0
                  });
872
        // check index
873
16
        for (int j = 0; j < num_indexes; ++j) {
874
8
            if (part->indexes[j].index_id != _schema->indexes()[j]->index_id) {
875
0
                return Status::InternalError(
876
0
                        "partition's index is not equal with schema's"
877
0
                        ", part_index={}, schema_index={}",
878
0
                        part->indexes[j].index_id, _schema->indexes()[j]->index_id);
879
0
            }
880
8
        }
881
8
        _partitions.emplace_back(part);
882
        // after _creating_partiton_keys
883
8
        if (_is_in_partition) {
884
6
            for (auto& in_key : part->in_keys) {
885
6
                _partitions_map->emplace(std::tuple {in_key.first, in_key.second, false}, part);
886
6
            }
887
6
        } else {
888
2
            _partitions_map->emplace(std::tuple {part->end_key.first, part->end_key.second, false},
889
2
                                     part);
890
2
        }
891
8
    }
892
893
8
    return Status::OK();
894
8
}
895
896
Status VOlapTablePartitionParam::replace_partitions(
897
        std::vector<int64_t>& old_partition_ids,
898
1
        const std::vector<TOlapTablePartition>& new_partitions) {
899
    // remove old replaced partitions
900
1
    DCHECK(old_partition_ids.size() == new_partitions.size());
901
902
    // init and add new partitions. insert into _partitions
903
3
    for (int i = 0; i < new_partitions.size(); i++) {
904
2
        const auto& t_part = new_partitions[i];
905
        // pair old_partition_ids and new_partitions one by one. TODO: sort to opt performance
906
2
        VOlapTablePartition* old_part = nullptr;
907
2
        auto old_part_id = old_partition_ids[i];
908
2
        if (auto it = std::find_if(
909
2
                    _partitions.begin(), _partitions.end(),
910
3
                    [=](const VOlapTablePartition* lhs) { return lhs->id == old_part_id; });
911
2
            it != _partitions.end()) {
912
2
            old_part = *it;
913
2
        } else {
914
0
            return Status::InternalError("Cannot find old tablet {} in replacing", old_part_id);
915
0
        }
916
917
2
        auto* part = _obj_pool.add(new VOlapTablePartition(&_partition_block));
918
2
        part->id = t_part.id;
919
2
        part->is_mutable = t_part.is_mutable;
920
921
        /// just substitute directly. no need to remove and reinsert keys.
922
        // range partition
923
2
        part->start_key = std::move(old_part->start_key);
924
2
        part->end_key = std::move(old_part->end_key);
925
        // list partition
926
2
        part->in_keys = std::move(old_part->in_keys);
927
2
        if (t_part.__isset.is_default_partition && t_part.is_default_partition) {
928
0
            _default_partition = part;
929
0
        }
930
931
2
        part->num_buckets = t_part.num_buckets;
932
2
        if (t_part.__isset.load_tablet_idx) {
933
0
            part->load_tablet_idx = t_part.load_tablet_idx;
934
0
        }
935
2
        if (t_part.__isset.bucket_be_id) {
936
0
            part->bucket_be_id = t_part.bucket_be_id;
937
0
        }
938
2
        if (t_part.__isset.local_bucket_seqs) {
939
0
            part->local_bucket_seqs = t_part.local_bucket_seqs;
940
0
        }
941
2
        auto num_indexes = _schema->indexes().size();
942
2
        if (t_part.indexes.size() != num_indexes) {
943
0
            return Status::InternalError(
944
0
                    "number of partition's index is not equal with schema's"
945
0
                    ", num_part_indexes={}, num_schema_indexes={}",
946
0
                    t_part.indexes.size(), num_indexes);
947
0
        }
948
2
        part->indexes = t_part.indexes;
949
2
        std::sort(part->indexes.begin(), part->indexes.end(),
950
2
                  [](const OlapTableIndexTablets& lhs, const OlapTableIndexTablets& rhs) {
951
0
                      return lhs.index_id < rhs.index_id;
952
0
                  });
953
        // check index
954
4
        for (int j = 0; j < num_indexes; ++j) {
955
2
            if (part->indexes[j].index_id != _schema->indexes()[j]->index_id) {
956
0
                return Status::InternalError(
957
0
                        "partition's index is not equal with schema's"
958
0
                        ", part_index={}, schema_index={}",
959
0
                        part->indexes[j].index_id, _schema->indexes()[j]->index_id);
960
0
            }
961
2
        }
962
963
        // add new partitions with new id.
964
2
        _partitions.emplace_back(part);
965
2
        VLOG_NOTICE << "params add new partition " << part->id;
966
967
        // replace items in _partition_maps
968
2
        if (_is_in_partition) {
969
0
            for (auto& in_key : part->in_keys) {
970
0
                (*_partitions_map)[std::tuple {in_key.first, in_key.second, false}] = part;
971
0
            }
972
2
        } else {
973
2
            (*_partitions_map)[std::tuple {part->end_key.first, part->end_key.second, false}] =
974
2
                    part;
975
2
        }
976
2
    }
977
    // remove old partitions by id
978
1
    std::ranges::sort(old_partition_ids);
979
5
    for (auto it = _partitions.begin(); it != _partitions.end();) {
980
4
        if (std::ranges::binary_search(old_partition_ids, (*it)->id)) {
981
2
            it = _partitions.erase(it);
982
2
        } else {
983
2
            it++;
984
2
        }
985
4
    }
986
987
1
    return Status::OK();
988
1
}
989
990
} // namespace doris