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