Coverage Report

Created: 2026-07-03 13:28

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