Coverage Report

Created: 2026-03-30 13:00

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