Coverage Report

Created: 2026-03-13 05:45

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/exec/sink/vrow_distribution.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 "exec/sink/vrow_distribution.h"
19
20
#include <gen_cpp/FrontendService.h>
21
#include <gen_cpp/FrontendService_types.h>
22
#include <glog/logging.h>
23
24
#include <cstdint>
25
#include <memory>
26
#include <string>
27
28
#include "common/cast_set.h"
29
#include "common/logging.h"
30
#include "common/metrics/doris_metrics.h"
31
#include "common/status.h"
32
#include "core/assert_cast.h"
33
#include "core/column/column.h"
34
#include "core/column/column_const.h"
35
#include "core/column/column_nullable.h"
36
#include "core/column/column_vector.h"
37
#include "core/data_type/data_type.h"
38
#include "exec/sink/writer/vtablet_writer.h"
39
#include "runtime/exec_env.h"
40
#include "runtime/query_context.h"
41
#include "runtime/runtime_state.h"
42
#include "service/backend_options.h"
43
#include "util/client_cache.h"
44
#include "util/debug_points.h"
45
#include "util/thrift_rpc_helper.h"
46
47
namespace doris {
48
#include "common/compile_check_begin.h"
49
50
38.2k
std::pair<VExprContextSPtrs, VExprSPtrs> VRowDistribution::_get_partition_function() {
51
38.2k
    return {_vpartition->get_part_func_ctx(), _vpartition->get_partition_function()};
52
38.2k
}
53
54
Status VRowDistribution::_save_missing_values(
55
        const Block& input_block,
56
        std::vector<std::vector<std::string>>& col_strs, // non-const ref for move
57
        int col_size, Block* block, const std::vector<uint32_t>& filter,
58
182
        const std::vector<const NullMap*>& col_null_maps) {
59
    // de-duplication for new partitions but save all rows.
60
182
    RETURN_IF_ERROR(
61
182
            _batching_block->add_rows(&input_block, filter.data(), filter.data() + filter.size()));
62
182
    std::vector<TNullableStringLiteral> cur_row_values;
63
20.6k
    for (int row = 0; row < col_strs[0].size(); ++row) {
64
20.4k
        cur_row_values.clear();
65
40.9k
        for (int col = 0; col < col_size; ++col) {
66
20.4k
            TNullableStringLiteral node;
67
20.4k
            const auto* null_map = col_null_maps[col]; // null map for this col
68
20.4k
            node.__set_is_null((null_map && (*null_map)[filter[row]])
69
20.4k
                                       ? true
70
20.4k
                                       : node.is_null); // if not, dont change(default false)
71
20.4k
            if (!node.is_null) {
72
20.4k
                node.__set_value(col_strs[col][row]);
73
20.4k
            }
74
20.4k
            cur_row_values.push_back(node);
75
20.4k
        }
76
20.4k
        if (!_deduper.contains(cur_row_values)) {
77
342
            _deduper.insert(cur_row_values);
78
342
            _partitions_need_create.emplace_back(cur_row_values);
79
342
        }
80
20.4k
    }
81
82
    // to avoid too large mem use
83
182
    if (_batching_block->rows() > _batch_size) {
84
2
        _deal_batched = true;
85
2
    }
86
182
    _batching_rows = _batching_block->rows();
87
182
    VLOG_NOTICE << "pushed some batching lines, now numbers = " << _batching_rows;
88
89
182
    return Status::OK();
90
182
}
91
92
172
void VRowDistribution::clear_batching_stats() {
93
172
    _partitions_need_create.clear();
94
172
    _batching_rows = 0;
95
172
    _batching_bytes = 0;
96
172
}
97
98
172
Status VRowDistribution::automatic_create_partition() {
99
172
    MonotonicStopWatch timer;
100
172
    if (_state->enable_profile() && _state->profile_level() >= 2) {
101
0
        timer.start();
102
0
    }
103
104
172
    SCOPED_TIMER(_add_partition_request_timer);
105
172
    TCreatePartitionRequest request;
106
172
    TCreatePartitionResult result;
107
172
    bool injected = false;
108
172
    std::string be_endpoint = BackendOptions::get_be_endpoint();
109
172
    request.__set_txn_id(_txn_id);
110
172
    request.__set_db_id(_vpartition->db_id());
111
172
    request.__set_table_id(_vpartition->table_id());
112
172
    request.__set_partitionValues(_partitions_need_create);
113
172
    request.__set_be_endpoint(be_endpoint);
114
172
    request.__set_write_single_replica(_write_single_replica);
115
172
    if (_state && _state->get_query_ctx()) {
116
        // Pass query_id to FE so it can determine if this is a multi-instance load by checking Coordinator
117
172
        request.__set_query_id(_state->get_query_ctx()->query_id());
118
172
    }
119
120
172
    DBUG_EXECUTE_IF("VRowDistribution.automatic_create_partition.inject_result", {
121
172
        DBUG_RUN_CALLBACK(&request, &result);
122
172
        injected = true;
123
172
    });
124
125
172
    VLOG_NOTICE << "automatic partition rpc begin request " << request;
126
172
    if (!injected) {
127
170
        std::shared_ptr<TNetworkAddress> master_addr;
128
170
        if (_vpartition->get_master_address() == nullptr) {
129
170
            auto* cluster_info = ExecEnv::GetInstance()->cluster_info();
130
170
            if (cluster_info == nullptr) {
131
0
                return Status::InternalError("cluster_info is null");
132
0
            }
133
170
            master_addr = std::make_shared<TNetworkAddress>(cluster_info->master_fe_addr);
134
170
        } else {
135
0
            master_addr = _vpartition->get_master_address();
136
0
        }
137
170
        int time_out = _state->execution_timeout() * 1000;
138
170
        RETURN_IF_ERROR(ThriftRpcHelper::rpc<FrontendServiceClient>(
139
170
                master_addr->hostname, master_addr->port,
140
170
                [&request, &result](FrontendServiceConnection& client) {
141
170
                    client->createPartition(result, request);
142
170
                },
143
170
                time_out));
144
170
    }
145
146
172
    Status status(Status::create(result.status));
147
172
    VLOG_NOTICE << "automatic partition rpc end response " << result;
148
172
    if (result.status.status_code == TStatusCode::OK) {
149
        // add new created partitions
150
172
        RETURN_IF_ERROR(_vpartition->add_partitions(result.partitions));
151
342
        for (const auto& part : result.partitions) {
152
342
            _new_partition_ids.insert(part.id);
153
342
            VLOG_TRACE << "record new id: " << part.id;
154
342
        }
155
172
        RETURN_IF_ERROR(_create_partition_callback(_caller, &result));
156
172
    }
157
158
    // Record this request's elapsed time
159
172
    if (_state->enable_profile() && _state->profile_level() >= 2) {
160
0
        int64_t elapsed_ns = timer.elapsed_time();
161
0
        _add_partition_request_times.push_back(elapsed_ns);
162
0
    }
163
172
    return status;
164
172
}
165
166
// for reuse the same create callback of create-partition
167
20
static TCreatePartitionResult cast_as_create_result(TReplacePartitionResult& arg) {
168
20
    TCreatePartitionResult result;
169
20
    result.status = arg.status;
170
20
    result.nodes = std::move(arg.nodes);
171
20
    result.partitions = std::move(arg.partitions);
172
20
    result.tablets = std::move(arg.tablets);
173
20
    result.slave_tablets = std::move(arg.slave_tablets);
174
20
    return result;
175
20
}
176
177
// use _partitions and replace them
178
41
Status VRowDistribution::_replace_overwriting_partition() {
179
41
    SCOPED_TIMER(_add_partition_request_timer); // also for replace_partition
180
41
    TReplacePartitionRequest request;
181
41
    TReplacePartitionResult result;
182
41
    bool injected = false;
183
41
    request.__set_overwrite_group_id(_vpartition->get_overwrite_group_id());
184
41
    request.__set_db_id(_vpartition->db_id());
185
41
    request.__set_table_id(_vpartition->table_id());
186
41
    request.__set_write_single_replica(_write_single_replica);
187
188
    // only request for partitions not recorded for replacement
189
41
    std::set<int64_t> id_deduper;
190
60.0k
    for (const auto* part : _partitions) {
191
60.0k
        if (part != nullptr) {
192
50.0k
            if (_new_partition_ids.contains(part->id)) {
193
                // this is a new partition. dont replace again.
194
40.0k
                VLOG_TRACE << "skip new partition: " << part->id;
195
40.0k
            } else {
196
                // request for replacement
197
10.0k
                id_deduper.insert(part->id);
198
10.0k
            }
199
50.0k
        } else if (_missing_map.empty()) {
200
            // no origin partition. and not allow to create.
201
6
            return Status::InvalidArgument(
202
6
                    "Cannot found origin partitions in auto detect overwriting, stop "
203
6
                    "processing");
204
6
        } // else: part is null and _missing_map is not empty. dealed outside using auto-partition way. nothing to do here.
205
60.0k
    }
206
35
    if (id_deduper.empty()) {
207
15
        return Status::OK(); // no need to request
208
15
    }
209
    // de-duplicate. there's no check in FE
210
20
    std::vector<int64_t> request_part_ids(id_deduper.begin(), id_deduper.end());
211
212
20
    request.__set_partition_ids(request_part_ids);
213
214
20
    std::string be_endpoint = BackendOptions::get_be_endpoint();
215
20
    request.__set_be_endpoint(be_endpoint);
216
20
    if (_state && _state->get_query_ctx()) {
217
        // Pass query_id to FE so it can determine if this is a multi-instance load by checking Coordinator
218
20
        request.__set_query_id(_state->get_query_ctx()->query_id());
219
20
    }
220
221
20
    DBUG_EXECUTE_IF("VRowDistribution.replace_overwriting_partition.inject_result", {
222
20
        DBUG_RUN_CALLBACK(&request, &result);
223
20
        injected = true;
224
20
    });
225
226
20
    VLOG_NOTICE << "auto detect replace partition request: " << request;
227
20
    if (!injected) {
228
19
        std::shared_ptr<TNetworkAddress> master_addr;
229
19
        if (_vpartition->get_master_address() == nullptr) {
230
19
            auto* cluster_info = ExecEnv::GetInstance()->cluster_info();
231
19
            if (cluster_info == nullptr) {
232
0
                return Status::InternalError("cluster_info is null");
233
0
            }
234
19
            master_addr = std::make_shared<TNetworkAddress>(cluster_info->master_fe_addr);
235
19
        } else {
236
0
            master_addr = _vpartition->get_master_address();
237
0
        }
238
19
        int time_out = _state->execution_timeout() * 1000;
239
19
        RETURN_IF_ERROR(ThriftRpcHelper::rpc<FrontendServiceClient>(
240
19
                master_addr->hostname, master_addr->port,
241
19
                [&request, &result](FrontendServiceConnection& client) {
242
19
                    client->replacePartition(result, request);
243
19
                },
244
19
                time_out));
245
19
    }
246
247
20
    Status status(Status::create(result.status));
248
20
    VLOG_NOTICE << "auto detect replace partition result: " << result;
249
20
    if (result.status.status_code == TStatusCode::OK) {
250
        // record new partitions
251
32
        for (const auto& part : result.partitions) {
252
32
            _new_partition_ids.insert(part.id);
253
32
            VLOG_TRACE << "record new id: " << part.id;
254
32
        }
255
        // replace data in _partitions
256
20
        RETURN_IF_ERROR(_vpartition->replace_partitions(request_part_ids, result.partitions));
257
        // reuse the function as the args' structure are same. it add nodes/locations and incremental_open
258
20
        auto result_as_create = cast_as_create_result(result);
259
20
        RETURN_IF_ERROR(_create_partition_callback(_caller, &result_as_create));
260
20
    }
261
262
20
    return status;
263
20
}
264
265
void VRowDistribution::_get_tablet_ids(Block* block, int32_t index_idx,
266
38.7k
                                       std::vector<int64_t>& tablet_ids) {
267
38.7k
    tablet_ids.resize(block->rows());
268
37.9M
    for (int row_idx = 0; row_idx < block->rows(); row_idx++) {
269
37.9M
        if (_skip[row_idx]) {
270
21.1k
            continue;
271
21.1k
        }
272
37.9M
        auto& partition = _partitions[row_idx];
273
37.9M
        auto& tablet_index = _tablet_indexes[row_idx];
274
37.9M
        auto& index = partition->indexes[index_idx];
275
276
37.9M
        auto tablet_id = index.tablets[tablet_index];
277
37.9M
        tablet_ids[row_idx] = tablet_id;
278
37.9M
    }
279
38.7k
}
280
281
38.6k
void VRowDistribution::_filter_block_by_skip(Block* block, RowPartTabletIds& row_part_tablet_id) {
282
38.6k
    auto& row_ids = row_part_tablet_id.row_ids;
283
38.6k
    auto& partition_ids = row_part_tablet_id.partition_ids;
284
38.6k
    auto& tablet_ids = row_part_tablet_id.tablet_ids;
285
286
38.6k
    auto rows = block->rows();
287
    // row count of a block should not exceed UINT32_MAX
288
38.6k
    auto rows_uint32 = cast_set<uint32_t>(rows);
289
38.3M
    for (uint32_t i = 0; i < rows_uint32; i++) {
290
38.3M
        if (!_skip[i]) {
291
38.3M
            row_ids.emplace_back(i);
292
38.3M
            partition_ids.emplace_back(_partitions[i]->id);
293
38.3M
            tablet_ids.emplace_back(_tablet_ids[i]);
294
38.3M
        }
295
38.3M
    }
296
38.6k
}
297
298
Status VRowDistribution::_filter_block_by_skip_and_where_clause(
299
35
        Block* block, const VExprContextSPtr& where_clause, RowPartTabletIds& row_part_tablet_id) {
300
    // TODO
301
    //SCOPED_RAW_TIMER(&_stat.where_clause_ns);
302
35
    int result_index = -1;
303
35
    size_t column_number = block->columns();
304
35
    RETURN_IF_ERROR(where_clause->execute(block, &result_index));
305
306
35
    auto filter_column = block->get_by_position(result_index).column;
307
308
35
    auto& row_ids = row_part_tablet_id.row_ids;
309
35
    auto& partition_ids = row_part_tablet_id.partition_ids;
310
35
    auto& tablet_ids = row_part_tablet_id.tablet_ids;
311
35
    if (const auto* nullable_column = check_and_get_column<ColumnNullable>(*filter_column)) {
312
29
        auto rows = block->rows();
313
        // row count of a block should not exceed UINT32_MAX
314
29
        auto rows_uint32 = cast_set<uint32_t>(rows);
315
58
        for (uint32_t i = 0; i < rows_uint32; i++) {
316
29
            if (nullable_column->get_bool_inline(i) && !_skip[i]) {
317
12
                row_ids.emplace_back(i);
318
12
                partition_ids.emplace_back(_partitions[i]->id);
319
12
                tablet_ids.emplace_back(_tablet_ids[i]);
320
12
            }
321
29
        }
322
29
    } else if (const auto* const_column = check_and_get_column<ColumnConst>(*filter_column)) {
323
1
        bool ret = const_column->get_bool(0);
324
1
        if (!ret) {
325
1
            return Status::OK();
326
1
        }
327
        // should we optimize?
328
0
        _filter_block_by_skip(block, row_part_tablet_id);
329
5
    } else {
330
5
        const auto& filter = assert_cast<const ColumnUInt8&>(*filter_column).get_data();
331
5
        auto rows = block->rows();
332
        // row count of a block should not exceed UINT32_MAX
333
5
        auto rows_uint32 = cast_set<uint32_t>(rows);
334
15
        for (uint32_t i = 0; i < rows_uint32; i++) {
335
10
            if (filter[i] != 0 && !_skip[i]) {
336
6
                row_ids.emplace_back(i);
337
6
                partition_ids.emplace_back(_partitions[i]->id);
338
6
                tablet_ids.emplace_back(_tablet_ids[i]);
339
6
            }
340
10
        }
341
5
    }
342
343
67
    for (size_t i = block->columns() - 1; i >= column_number; i--) {
344
33
        block->erase(i);
345
33
    }
346
34
    return Status::OK();
347
35
}
348
349
Status VRowDistribution::_filter_block(Block* block,
350
37.7k
                                       std::vector<RowPartTabletIds>& row_part_tablet_ids) {
351
76.4k
    for (int i = 0; i < _schema->indexes().size(); i++) {
352
38.7k
        _get_tablet_ids(block, i, _tablet_ids);
353
38.7k
        auto& where_clause = _schema->indexes()[i]->where_clause;
354
38.7k
        if (where_clause != nullptr) {
355
35
            RETURN_IF_ERROR(_filter_block_by_skip_and_where_clause(block, where_clause,
356
35
                                                                   row_part_tablet_ids[i]));
357
38.6k
        } else {
358
38.6k
            _filter_block_by_skip(block, row_part_tablet_ids[i]);
359
38.6k
        }
360
38.7k
    }
361
37.7k
    return Status::OK();
362
37.7k
}
363
364
Status VRowDistribution::_generate_rows_distribution_for_non_auto_partition(
365
37.3k
        Block* block, bool has_filtered_rows, std::vector<RowPartTabletIds>& row_part_tablet_ids) {
366
37.3k
    int num_rows = cast_set<int>(block->rows());
367
368
37.3k
    RETURN_IF_ERROR(_tablet_finder->find_tablets(_state, block, num_rows, _partitions,
369
37.3k
                                                 _tablet_indexes, _skip));
370
37.3k
    if (has_filtered_rows) {
371
479
        for (int i = 0; i < num_rows; i++) {
372
426
            _skip[i] = _skip[i] || _block_convertor->filter_map()[i];
373
426
        }
374
53
    }
375
37.3k
    RETURN_IF_ERROR(_filter_block(block, row_part_tablet_ids));
376
37.3k
    return Status::OK();
377
37.3k
}
378
379
Status VRowDistribution::_deal_missing_map(const Block& input_block, Block* block,
380
                                           const std::vector<uint16_t>& partition_cols_idx,
381
182
                                           int64_t& rows_stat_val) {
382
    // for missing partition keys, calc the missing partition and save in _partitions_need_create
383
182
    auto [part_ctxs, part_exprs] = _get_partition_function();
384
182
    int part_col_num = cast_set<int>(part_exprs.size());
385
    // the two vectors are in column-first-order
386
182
    std::vector<std::vector<std::string>> col_strs;
387
182
    std::vector<const NullMap*> col_null_maps;
388
182
    col_strs.resize(part_col_num);
389
182
    col_null_maps.reserve(part_col_num);
390
391
182
    auto format_options = DataTypeSerDe::get_default_format_options();
392
182
    format_options.timezone = &_state->timezone_obj();
393
394
372
    for (int i = 0; i < part_col_num; ++i) {
395
190
        auto return_type = part_exprs[i]->data_type();
396
        // expose the data column. the return type would be nullable
397
190
        const auto& [range_left_col, col_const] =
398
190
                unpack_if_const(block->get_by_position(partition_cols_idx[i]).column);
399
190
        if (range_left_col->is_nullable()) {
400
43
            col_null_maps.push_back(&(
401
43
                    assert_cast<const ColumnNullable*>(range_left_col.get())->get_null_map_data()));
402
147
        } else {
403
147
            col_null_maps.push_back(nullptr);
404
147
        }
405
20.4k
        for (auto row : _missing_map) {
406
20.4k
            col_strs[i].push_back(return_type->to_string(
407
20.4k
                    *range_left_col, index_check_const(row, col_const), format_options));
408
20.4k
        }
409
190
    }
410
411
    // calc the end value and save them. in the end of sending, we will create partitions for them and deal them.
412
    // NOTE: must save old batching stats before calling _save_missing_values(),
413
    // because _save_missing_values() will update _batching_rows internally.
414
182
    size_t old_bt_rows = _batching_rows;
415
182
    size_t old_bt_bytes = _batching_bytes;
416
417
182
    RETURN_IF_ERROR(_save_missing_values(input_block, col_strs, part_col_num, block, _missing_map,
418
182
                                         col_null_maps));
419
420
182
    size_t new_bt_rows = _batching_block->rows();
421
182
    size_t new_bt_bytes = _batching_block->bytes();
422
182
    rows_stat_val -= new_bt_rows - old_bt_rows;
423
182
    _state->update_num_rows_load_total(old_bt_rows - new_bt_rows);
424
182
    _state->update_num_bytes_load_total(old_bt_bytes - new_bt_bytes);
425
182
    DorisMetrics::instance()->load_rows->increment(old_bt_rows - new_bt_rows);
426
182
    DorisMetrics::instance()->load_bytes->increment(old_bt_bytes - new_bt_bytes);
427
428
182
    return Status::OK();
429
182
}
430
431
Status VRowDistribution::_generate_rows_distribution_for_auto_partition(
432
        const Block& input_block, Block* block, const std::vector<uint16_t>& partition_cols_idx,
433
        bool has_filtered_rows, std::vector<RowPartTabletIds>& row_part_tablet_ids,
434
347
        int64_t& rows_stat_val) {
435
347
    int num_rows = cast_set<int>(block->rows());
436
347
    std::vector<uint16_t> partition_keys = _vpartition->get_partition_keys();
437
438
347
    auto& partition_col = block->get_by_position(partition_keys[0]);
439
347
    _missing_map.clear();
440
347
    _missing_map.reserve(partition_col.column->size());
441
442
347
    RETURN_IF_ERROR(_tablet_finder->find_tablets(_state, block, num_rows, _partitions,
443
347
                                                 _tablet_indexes, _skip, &_missing_map));
444
445
    // the missing vals for auto partition are also skipped.
446
347
    if (has_filtered_rows) {
447
4
        for (int i = 0; i < num_rows; i++) {
448
2
            _skip[i] = _skip[i] || _block_convertor->filter_map()[i];
449
2
        }
450
2
    }
451
347
    RETURN_IF_ERROR(_filter_block(block, row_part_tablet_ids));
452
453
347
    if (!_missing_map.empty()) {
454
177
        RETURN_IF_ERROR(_deal_missing_map(input_block, block, partition_cols_idx,
455
177
                                          rows_stat_val)); // send input block to save
456
177
    }
457
347
    return Status::OK();
458
347
}
459
460
Status VRowDistribution::_generate_rows_distribution_for_auto_overwrite(
461
        const Block& input_block, Block* block, const std::vector<uint16_t>& partition_cols_idx,
462
        bool has_filtered_rows, std::vector<RowPartTabletIds>& row_part_tablet_ids,
463
41
        int64_t& rows_stat_val) {
464
41
    int num_rows = cast_set<int>(block->rows());
465
466
    // for non-auto-partition situation, goes into two 'else' branch. just find the origin partitions, replace them by rpc,
467
    //  and find the new partitions to use.
468
    // for auto-partition's, find and save origins in _partitions and replace them. at meanwhile save the missing values for auto
469
    //  partition. then we find partition again to get replaced partitions in _partitions. this time _missing_map is ignored cuz
470
    //  we already saved missing values.
471
41
    if (_vpartition->is_auto_partition() &&
472
41
        _state->query_options().enable_auto_create_when_overwrite) {
473
        // allow auto create partition for missing rows.
474
17
        std::vector<uint16_t> partition_keys = _vpartition->get_partition_keys();
475
17
        auto partition_col = block->get_by_position(partition_keys[0]);
476
17
        _missing_map.clear();
477
17
        _missing_map.reserve(partition_col.column->size());
478
479
17
        RETURN_IF_ERROR(_tablet_finder->find_tablets(_state, block, num_rows, _partitions,
480
17
                                                     _tablet_indexes, _skip, &_missing_map));
481
482
        // allow and really need to create during auto-detect-overwriting.
483
17
        if (!_missing_map.empty()) {
484
5
            RETURN_IF_ERROR(
485
5
                    _deal_missing_map(input_block, block, partition_cols_idx, rows_stat_val));
486
5
        }
487
24
    } else {
488
24
        RETURN_IF_ERROR(_tablet_finder->find_tablets(_state, block, num_rows, _partitions,
489
24
                                                     _tablet_indexes, _skip));
490
24
    }
491
41
    RETURN_IF_ERROR(_replace_overwriting_partition());
492
493
    // regenerate locations for new partitions & tablets
494
35
    _reset_find_tablets(num_rows);
495
35
    if (_vpartition->is_auto_partition() &&
496
35
        _state->query_options().enable_auto_create_when_overwrite) {
497
        // here _missing_map is just a placeholder
498
17
        RETURN_IF_ERROR(_tablet_finder->find_tablets(_state, block, num_rows, _partitions,
499
17
                                                     _tablet_indexes, _skip, &_missing_map));
500
17
        if (VLOG_TRACE_IS_ON) {
501
0
            std::string tmp;
502
0
            for (auto v : _missing_map) {
503
0
                tmp += std::to_string(v).append(", ");
504
0
            }
505
0
            VLOG_TRACE << "Trace missing map of " << this << ':' << tmp;
506
0
        }
507
18
    } else {
508
18
        RETURN_IF_ERROR(_tablet_finder->find_tablets(_state, block, num_rows, _partitions,
509
18
                                                     _tablet_indexes, _skip));
510
18
    }
511
35
    if (has_filtered_rows) {
512
0
        for (int i = 0; i < num_rows; i++) {
513
0
            _skip[i] = _skip[i] || _block_convertor->filter_map()[i];
514
0
        }
515
0
    }
516
35
    RETURN_IF_ERROR(_filter_block(block, row_part_tablet_ids));
517
35
    return Status::OK();
518
35
}
519
520
void VRowDistribution::_reset_row_part_tablet_ids(
521
37.7k
        std::vector<RowPartTabletIds>& row_part_tablet_ids, int64_t rows) {
522
37.7k
    row_part_tablet_ids.resize(_schema->indexes().size());
523
38.7k
    for (auto& row_part_tablet_id : row_part_tablet_ids) {
524
38.7k
        auto& row_ids = row_part_tablet_id.row_ids;
525
38.7k
        auto& partition_ids = row_part_tablet_id.partition_ids;
526
38.7k
        auto& tablet_ids = row_part_tablet_id.tablet_ids;
527
528
38.7k
        row_ids.clear();
529
38.7k
        partition_ids.clear();
530
38.7k
        tablet_ids.clear();
531
        // This is important for performance.
532
38.7k
        row_ids.reserve(rows);
533
38.7k
        partition_ids.reserve(rows);
534
38.7k
        tablet_ids.reserve(rows);
535
38.7k
    }
536
37.7k
}
537
538
Status VRowDistribution::generate_rows_distribution(
539
        Block& input_block, std::shared_ptr<Block>& block,
540
37.7k
        std::vector<RowPartTabletIds>& row_part_tablet_ids, int64_t& rows_stat_val) {
541
37.7k
    auto input_rows = input_block.rows();
542
37.7k
    _reset_row_part_tablet_ids(row_part_tablet_ids, input_rows);
543
544
    // we store the batching block with value of `input_block`. so just do all of these again.
545
37.7k
    bool has_filtered_rows = false;
546
37.7k
    RETURN_IF_ERROR(_block_convertor->validate_and_convert_block(
547
37.7k
            _state, &input_block, block, *_vec_output_expr_ctxs, input_rows, has_filtered_rows));
548
549
    // batching block rows which need new partitions. deal together at finish.
550
37.7k
    if (!_batching_block) [[unlikely]] {
551
30.1k
        std::unique_ptr<Block> tmp_block = input_block.create_same_struct_block(0);
552
30.1k
        _batching_block = MutableBlock::create_unique(std::move(*tmp_block));
553
30.1k
    }
554
555
37.7k
    auto num_rows = block->rows();
556
37.7k
    _reset_find_tablets(num_rows);
557
558
    // if there's projection of partition calc, we need to calc it first.
559
37.7k
    auto [part_ctxs, part_funcs] = _get_partition_function();
560
37.7k
    std::vector<uint16_t> partition_cols_idx;
561
37.7k
    if (_vpartition->is_projection_partition()) {
562
        // calc the start value of missing partition ranges.
563
534
        auto func_size = part_funcs.size();
564
1.18k
        for (int i = 0; i < func_size; ++i) {
565
652
            int result_idx = -1;
566
            // we just calc left range here. leave right to FE to avoid dup calc.
567
652
            RETURN_IF_ERROR(part_funcs[i]->execute(part_ctxs[i].get(), block.get(), &result_idx));
568
569
652
            VLOG_DEBUG << "Partition-calculated block:\n" << block->dump_data(0, 1);
570
652
            DCHECK(result_idx != -1);
571
572
652
            partition_cols_idx.push_back(cast_set<uint16_t>(result_idx));
573
652
        }
574
575
        // change the column to compare to transformed.
576
534
        _vpartition->set_transformed_slots(partition_cols_idx);
577
534
    }
578
579
37.7k
    Status st = Status::OK();
580
37.7k
    if (_vpartition->is_auto_detect_overwrite() && !_deal_batched) {
581
        // when overwrite, no auto create partition allowed.
582
41
        st = _generate_rows_distribution_for_auto_overwrite(input_block, block.get(),
583
41
                                                            partition_cols_idx, has_filtered_rows,
584
41
                                                            row_part_tablet_ids, rows_stat_val);
585
37.6k
    } else if (_vpartition->is_auto_partition() && !_deal_batched) {
586
347
        st = _generate_rows_distribution_for_auto_partition(input_block, block.get(),
587
347
                                                            partition_cols_idx, has_filtered_rows,
588
347
                                                            row_part_tablet_ids, rows_stat_val);
589
37.3k
    } else { // not auto partition
590
37.3k
        st = _generate_rows_distribution_for_non_auto_partition(block.get(), has_filtered_rows,
591
37.3k
                                                                row_part_tablet_ids);
592
37.3k
    }
593
594
37.7k
    return st;
595
37.7k
}
596
597
// reuse vars for find_tablets
598
37.7k
void VRowDistribution::_reset_find_tablets(int64_t rows) {
599
37.7k
    _tablet_finder->filter_bitmap().Reset(rows);
600
37.7k
    _partitions.assign(rows, nullptr);
601
37.7k
    _skip.assign(rows, false);
602
37.7k
    _tablet_indexes.assign(rows, 0);
603
37.7k
}
604
605
} // namespace doris