be/src/core/block/block.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 | | // This file is copied from |
18 | | // https://github.com/ClickHouse/ClickHouse/blob/master/src/Core/Block.cpp |
19 | | // and modified by Doris |
20 | | |
21 | | #include "core/block/block.h" |
22 | | |
23 | | #include <concurrentqueue.h> |
24 | | #include <fmt/format.h> |
25 | | #include <gen_cpp/data.pb.h> |
26 | | #include <glog/logging.h> |
27 | | #include <snappy.h> |
28 | | #include <streamvbyte.h> |
29 | | |
30 | | #include <algorithm> |
31 | | #include <cassert> |
32 | | #include <iomanip> |
33 | | #include <limits> |
34 | | #include <ranges> |
35 | | |
36 | | #include "agent/be_exec_version_manager.h" |
37 | | #include "common/compiler_util.h" // IWYU pragma: keep |
38 | | #include "common/logging.h" |
39 | | #include "common/status.h" |
40 | | #include "core/assert_cast.h" |
41 | | #include "core/column/column.h" |
42 | | #include "core/column/column_const.h" |
43 | | #include "core/column/column_nothing.h" |
44 | | #include "core/column/column_nullable.h" |
45 | | #include "core/column/column_vector.h" |
46 | | #include "core/data_type/data_type_factory.hpp" |
47 | | #include "core/data_type/data_type_nullable.h" |
48 | | #include "core/data_type_serde/data_type_serde.h" |
49 | | #include "runtime/descriptors.h" |
50 | | #include "runtime/runtime_profile.h" |
51 | | #include "runtime/thread_context.h" |
52 | | #include "util/block_compression.h" |
53 | | #include "util/faststring.h" |
54 | | #include "util/simd/bits.h" |
55 | | #include "util/slice.h" |
56 | | |
57 | | class SipHash; |
58 | | |
59 | | namespace doris::segment_v2 { |
60 | | enum CompressionTypePB : int; |
61 | | } // namespace doris::segment_v2 |
62 | | namespace doris { |
63 | | template <typename T> |
64 | | void clear_blocks(moodycamel::ConcurrentQueue<T>& blocks, |
65 | 2 | RuntimeProfile::Counter* memory_used_counter = nullptr) { |
66 | 2 | T block; |
67 | 6 | while (blocks.try_dequeue(block)) { |
68 | 4 | if (memory_used_counter) { |
69 | 4 | if constexpr (std::is_same_v<T, Block>) { |
70 | 2 | memory_used_counter->update(-block.allocated_bytes()); |
71 | 2 | } else { |
72 | 2 | memory_used_counter->update(-block->allocated_bytes()); |
73 | 2 | } |
74 | 4 | } |
75 | 4 | } |
76 | 2 | } _ZN5doris12clear_blocksINS_5BlockEEEvRN10moodycamel15ConcurrentQueueIT_NS2_28ConcurrentQueueDefaultTraitsEEEPNS_14RuntimeProfile7CounterE Line | Count | Source | 65 | 1 | RuntimeProfile::Counter* memory_used_counter = nullptr) { | 66 | 1 | T block; | 67 | 3 | while (blocks.try_dequeue(block)) { | 68 | 2 | if (memory_used_counter) { | 69 | 2 | if constexpr (std::is_same_v<T, Block>) { | 70 | 2 | memory_used_counter->update(-block.allocated_bytes()); | 71 | | } else { | 72 | | memory_used_counter->update(-block->allocated_bytes()); | 73 | | } | 74 | 2 | } | 75 | 2 | } | 76 | 1 | } |
_ZN5doris12clear_blocksISt10unique_ptrINS_5BlockESt14default_deleteIS2_EEEEvRN10moodycamel15ConcurrentQueueIT_NS6_28ConcurrentQueueDefaultTraitsEEEPNS_14RuntimeProfile7CounterE Line | Count | Source | 65 | 1 | RuntimeProfile::Counter* memory_used_counter = nullptr) { | 66 | 1 | T block; | 67 | 3 | while (blocks.try_dequeue(block)) { | 68 | 2 | if (memory_used_counter) { | 69 | | if constexpr (std::is_same_v<T, Block>) { | 70 | | memory_used_counter->update(-block.allocated_bytes()); | 71 | 2 | } else { | 72 | 2 | memory_used_counter->update(-block->allocated_bytes()); | 73 | 2 | } | 74 | 2 | } | 75 | 2 | } | 76 | 1 | } |
|
77 | | |
78 | | template void clear_blocks<Block>(moodycamel::ConcurrentQueue<Block>&, |
79 | | RuntimeProfile::Counter* memory_used_counter); |
80 | | template void clear_blocks<BlockUPtr>(moodycamel::ConcurrentQueue<BlockUPtr>&, |
81 | | RuntimeProfile::Counter* memory_used_counter); |
82 | | |
83 | | namespace { |
84 | | |
85 | | // The no-clone fast path is only safe when the whole column tree is uniquely |
86 | | // owned. A composite column with shared children still needs COW detachment. |
87 | 42.5k | bool is_recursively_exclusive(const IColumn& column) { |
88 | 42.5k | if (!column.is_exclusive()) { |
89 | 718 | return false; |
90 | 718 | } |
91 | | |
92 | 41.7k | bool exclusive = true; |
93 | 41.7k | IColumn::ColumnCallback callback = [&](const IColumn& subcolumn) { |
94 | 29.4k | if (!exclusive) { |
95 | 0 | return; |
96 | 0 | } |
97 | 29.4k | exclusive = is_recursively_exclusive(subcolumn); |
98 | 29.4k | }; |
99 | 41.7k | column.for_each_subcolumn(callback); |
100 | 41.7k | return exclusive; |
101 | 42.5k | } |
102 | | |
103 | | // Acquire one live Block slot transactionally. Shared columns are detached while |
104 | | // the original slot is still intact, so a clone failure cannot leave Block with |
105 | | // a moved-from/null column. Exclusive column trees keep the stealing fast path. |
106 | 13.0k | MutableColumnPtr scoped_mutate_column(ColumnPtr& column, const DataTypePtr& type) { |
107 | 13.0k | DCHECK(type); |
108 | 13.0k | if (!column) { |
109 | 1 | return type->create_column(); |
110 | 1 | } |
111 | | |
112 | 13.0k | MutableColumnPtr mutable_column; |
113 | 13.0k | if (is_recursively_exclusive(*column)) { |
114 | 12.2k | mutable_column = std::move(*column).mutate(); |
115 | 12.2k | } else { |
116 | 718 | mutable_column = IColumn::mutate(column); |
117 | 718 | } |
118 | 13.0k | column = nullptr; |
119 | 13.0k | return mutable_column; |
120 | 13.0k | } |
121 | | |
122 | | } // namespace |
123 | | |
124 | 4.53k | Block::Block(std::initializer_list<ColumnWithTypeAndName> il) : data {il} {} |
125 | | |
126 | 258k | Block::Block(ColumnsWithTypeAndName data_) : data {std::move(data_)} {} |
127 | | |
128 | 56.0k | Block::Block(const std::vector<SlotDescriptor*>& slots, size_t block_size) { |
129 | 271k | for (auto* const slot_desc : slots) { |
130 | 271k | auto column_ptr = slot_desc->get_empty_mutable_column(); |
131 | 271k | column_ptr->reserve(block_size); |
132 | 271k | insert(ColumnWithTypeAndName(std::move(column_ptr), slot_desc->get_data_type_ptr(), |
133 | 271k | slot_desc->col_name())); |
134 | 271k | } |
135 | 56.0k | } |
136 | | |
137 | 1 | Block::Block(const std::vector<SlotDescriptor>& slots, size_t block_size) { |
138 | 1 | std::vector<SlotDescriptor*> slot_ptrs(slots.size()); |
139 | 3 | for (size_t i = 0; i < slots.size(); ++i) { |
140 | | // Slots remain unmodified and are used to read column information; const_cast can be employed. |
141 | | // used in src/exec/rowid_fetcher.cpp |
142 | 2 | slot_ptrs[i] = const_cast<SlotDescriptor*>(&slots[i]); |
143 | 2 | } |
144 | 1 | *this = Block(slot_ptrs, block_size); |
145 | 1 | } |
146 | | |
147 | | Status Block::deserialize(const PBlock& pblock, size_t* uncompressed_bytes, |
148 | 996 | int64_t* decompress_time) { |
149 | 996 | swap(Block()); |
150 | 996 | int be_exec_version = pblock.has_be_exec_version() ? pblock.be_exec_version() : 0; |
151 | 996 | RETURN_IF_ERROR(BeExecVersionManager::check_be_exec_version(be_exec_version)); |
152 | | |
153 | 996 | const char* buf = nullptr; |
154 | 996 | std::string compression_scratch; |
155 | 996 | if (pblock.compressed()) { |
156 | | // Decompress |
157 | 487 | SCOPED_RAW_TIMER(decompress_time); |
158 | 487 | const char* compressed_data = pblock.column_values().c_str(); |
159 | 487 | size_t compressed_size = pblock.column_values().size(); |
160 | 487 | size_t uncompressed_size = 0; |
161 | 487 | if (pblock.has_compression_type() && pblock.has_uncompressed_size()) { |
162 | 487 | BlockCompressionCodec* codec; |
163 | 487 | RETURN_IF_ERROR(get_block_compression_codec(pblock.compression_type(), &codec)); |
164 | 487 | uncompressed_size = pblock.uncompressed_size(); |
165 | | // Should also use allocator to allocate memory here. |
166 | 487 | compression_scratch.resize(uncompressed_size); |
167 | 487 | Slice decompressed_slice(compression_scratch); |
168 | 487 | RETURN_IF_ERROR(codec->decompress(Slice(compressed_data, compressed_size), |
169 | 487 | &decompressed_slice)); |
170 | 487 | DCHECK(uncompressed_size == decompressed_slice.size); |
171 | 487 | } else { |
172 | 0 | bool success = snappy::GetUncompressedLength(compressed_data, compressed_size, |
173 | 0 | &uncompressed_size); |
174 | 0 | DCHECK(success) << "snappy::GetUncompressedLength failed"; |
175 | 0 | compression_scratch.resize(uncompressed_size); |
176 | 0 | success = snappy::RawUncompress(compressed_data, compressed_size, |
177 | 0 | compression_scratch.data()); |
178 | 0 | DCHECK(success) << "snappy::RawUncompress failed"; |
179 | 0 | } |
180 | 487 | *uncompressed_bytes = uncompressed_size; |
181 | 487 | buf = compression_scratch.data(); |
182 | 509 | } else { |
183 | 509 | buf = pblock.column_values().data(); |
184 | 509 | } |
185 | | |
186 | 1.58k | for (const auto& pcol_meta : pblock.column_metas()) { |
187 | 1.58k | DataTypePtr type = DataTypeFactory::instance().create_data_type(pcol_meta); |
188 | 1.58k | MutableColumnPtr data_column = type->create_column(); |
189 | | // Here will try to allocate large memory, should return error if failed. |
190 | 1.58k | RETURN_IF_CATCH_EXCEPTION( |
191 | 1.58k | buf = type->deserialize(buf, &data_column, pblock.be_exec_version())); |
192 | 1.58k | data.emplace_back(data_column->get_ptr(), type, pcol_meta.name()); |
193 | 1.58k | } |
194 | | |
195 | 996 | return Status::OK(); |
196 | 996 | } |
197 | | |
198 | 13.7k | void Block::reserve(size_t count) { |
199 | 13.7k | data.reserve(count); |
200 | 13.7k | } |
201 | | |
202 | 4 | void Block::insert(size_t position, const ColumnWithTypeAndName& elem) { |
203 | 4 | if (position > data.size()) { |
204 | 1 | throw Exception(ErrorCode::INTERNAL_ERROR, |
205 | 1 | "invalid input position, position={}, data.size={}, names={}", position, |
206 | 1 | data.size(), dump_names()); |
207 | 1 | } |
208 | | |
209 | 3 | data.emplace(data.begin() + position, elem); |
210 | 3 | } |
211 | | |
212 | 3 | void Block::insert(size_t position, ColumnWithTypeAndName&& elem) { |
213 | 3 | if (position > data.size()) { |
214 | 1 | throw Exception(ErrorCode::INTERNAL_ERROR, |
215 | 1 | "invalid input position, position={}, data.size={}, names={}", position, |
216 | 1 | data.size(), dump_names()); |
217 | 1 | } |
218 | | |
219 | 2 | data.emplace(data.begin() + position, std::move(elem)); |
220 | 2 | } |
221 | | |
222 | 8.09k | void Block::clear_names() { |
223 | 178k | for (auto& entry : data) { |
224 | 178k | entry.name.clear(); |
225 | 178k | } |
226 | 8.09k | } |
227 | | |
228 | 16.7k | void Block::insert(const ColumnWithTypeAndName& elem) { |
229 | 16.7k | data.emplace_back(elem); |
230 | 16.7k | } |
231 | | |
232 | 1.01M | void Block::insert(ColumnWithTypeAndName&& elem) { |
233 | 1.01M | data.emplace_back(std::move(elem)); |
234 | 1.01M | } |
235 | | |
236 | 35 | void Block::erase(const std::set<size_t>& positions) { |
237 | 35 | for (unsigned long position : std::ranges::reverse_view(positions)) { |
238 | 31 | erase(position); |
239 | 31 | } |
240 | 35 | } |
241 | | |
242 | 2.83k | void Block::erase_tail(size_t start) { |
243 | 2.83k | DCHECK(start <= data.size()) << fmt::format( |
244 | 0 | "Position out of bound in Block::erase(), max position = {}", data.size()); |
245 | 2.83k | data.erase(data.begin() + start, data.end()); |
246 | 2.83k | } |
247 | | |
248 | 124k | void Block::erase(size_t position) { |
249 | 124k | DCHECK(!data.empty()) << "Block is empty"; |
250 | 124k | DCHECK_LT(position, data.size()) << fmt::format( |
251 | 0 | "Position out of bound in Block::erase(), max position = {}", data.size() - 1); |
252 | | |
253 | 124k | erase_impl(position); |
254 | 124k | } |
255 | | |
256 | 124k | void Block::erase_impl(size_t position) { |
257 | 124k | data.erase(data.begin() + position); |
258 | 124k | } |
259 | | |
260 | 76.1k | ColumnWithTypeAndName& Block::safe_get_by_position(size_t position) { |
261 | 76.1k | if (position >= data.size()) { |
262 | 0 | throw Exception(ErrorCode::INTERNAL_ERROR, |
263 | 0 | "invalid input position, position={}, data.size={}, names={}", position, |
264 | 0 | data.size(), dump_names()); |
265 | 0 | } |
266 | 76.1k | return data[position]; |
267 | 76.1k | } |
268 | | |
269 | 109 | const ColumnWithTypeAndName& Block::safe_get_by_position(size_t position) const { |
270 | 109 | if (position >= data.size()) { |
271 | 0 | throw Exception(ErrorCode::INTERNAL_ERROR, |
272 | 0 | "invalid input position, position={}, data.size={}, names={}", position, |
273 | 0 | data.size(), dump_names()); |
274 | 0 | } |
275 | 109 | return data[position]; |
276 | 109 | } |
277 | | |
278 | 416 | int Block::get_position_by_name(const std::string& name) const { |
279 | 2.93k | for (int i = 0; i < data.size(); i++) { |
280 | 2.89k | if (data[i].name == name) { |
281 | 376 | return i; |
282 | 376 | } |
283 | 2.89k | } |
284 | 40 | return -1; |
285 | 416 | } |
286 | | |
287 | 5 | void Block::check_number_of_rows(bool allow_null_columns) const { |
288 | 5 | ssize_t rows = -1; |
289 | 9 | for (const auto& elem : data) { |
290 | 9 | if (!elem.column && allow_null_columns) { |
291 | 2 | continue; |
292 | 2 | } |
293 | | |
294 | 7 | if (!elem.column) { |
295 | 1 | throw Exception(ErrorCode::INTERNAL_ERROR, |
296 | 1 | "Column {} in block is nullptr, in method check_number_of_rows.", |
297 | 1 | elem.name); |
298 | 1 | } |
299 | | |
300 | 6 | ssize_t size = elem.column->size(); |
301 | | |
302 | 6 | if (rows == -1) { |
303 | 5 | rows = size; |
304 | 5 | } else if (rows != size) { |
305 | 1 | throw Exception(ErrorCode::INTERNAL_ERROR, "Sizes of columns doesn't match, block={}", |
306 | 1 | dump_structure()); |
307 | 1 | } |
308 | 6 | } |
309 | 5 | } |
310 | | |
311 | 3.06M | Status Block::check_type_and_column() const { |
312 | 3.06M | #ifndef NDEBUG |
313 | 3.06M | for (const auto& elem : data) { |
314 | 219k | if (!elem.column) { |
315 | 0 | continue; |
316 | 0 | } |
317 | 219k | if (!elem.type) { |
318 | 0 | continue; |
319 | 0 | } |
320 | | |
321 | | // ColumnNothing is a special column type, it is used to represent a column that |
322 | | // is not materialized, so we don't need to check it. |
323 | 219k | if (check_and_get_column<ColumnNothing>(elem.column.get())) { |
324 | 0 | continue; |
325 | 0 | } |
326 | | |
327 | 219k | const auto& type = elem.type; |
328 | 219k | const auto& column = elem.column; |
329 | | |
330 | 219k | RETURN_IF_ERROR(column->column_self_check()); |
331 | 219k | auto st = type->check_column(*column); |
332 | 219k | if (!st.ok()) { |
333 | 1 | return Status::InternalError( |
334 | 1 | "Column {} in block is not compatible with its column type :{}, data type :{}, " |
335 | 1 | "error: {}", |
336 | 1 | elem.name, column->get_name(), type->get_name(), st.msg()); |
337 | 1 | } |
338 | 219k | } |
339 | 3.06M | #endif |
340 | 3.06M | return Status::OK(); |
341 | 3.06M | } |
342 | | |
343 | 1.57M | Status Block::check_column_and_type_not_null() const { |
344 | 1.76M | for (size_t i = 0; i != data.size(); ++i) { |
345 | 190k | const auto& elem = data[i]; |
346 | 190k | if (!elem.column) { |
347 | 1 | return Status::InternalError("Column in block is nullptr, column index: {}, name: {}", |
348 | 1 | i, elem.name); |
349 | 1 | } |
350 | 190k | if (!elem.type) { |
351 | 1 | return Status::InternalError("Type in block is nullptr, column index: {}, name: {}", i, |
352 | 1 | elem.name); |
353 | 1 | } |
354 | 190k | } |
355 | 1.57M | return Status::OK(); |
356 | 1.57M | } |
357 | | |
358 | 1.57M | Status Block::check_no_column_string64() const { |
359 | 1.76M | for (size_t i = 0; i != data.size(); ++i) { |
360 | 190k | const auto& elem = data[i]; |
361 | 190k | DCHECK(elem.column); |
362 | 190k | if (elem.column->contains_column_string64()) { |
363 | 2 | return Status::InternalError( |
364 | 2 | "ColumnString64 is not allowed at operator boundaries, column index: {}, " |
365 | 2 | "name: {}, structure: {}", |
366 | 2 | i, elem.name, elem.column->dump_structure()); |
367 | 2 | } |
368 | 190k | } |
369 | 1.57M | return Status::OK(); |
370 | 1.57M | } |
371 | | |
372 | 52.0M | size_t Block::rows() const { |
373 | 52.0M | for (const auto& elem : data) { |
374 | 47.6M | if (elem.column) { |
375 | 47.6M | return elem.column->size(); |
376 | 47.6M | } |
377 | 47.6M | } |
378 | | |
379 | 4.43M | return 0; |
380 | 52.0M | } |
381 | | |
382 | 6 | void Block::set_num_rows(size_t length) { |
383 | 6 | if (rows() > length) { |
384 | 4 | for (auto& elem : data) { |
385 | 4 | if (elem.column) { |
386 | 4 | elem.column = elem.column->shrink(length); |
387 | 4 | } |
388 | 4 | } |
389 | 4 | } |
390 | 6 | } |
391 | | |
392 | 1 | void Block::skip_num_rows(int64_t& length) { |
393 | 1 | auto origin_rows = rows(); |
394 | 1 | if (origin_rows <= length) { |
395 | 0 | clear(); |
396 | 0 | length -= origin_rows; |
397 | 1 | } else { |
398 | 1 | for (auto& elem : data) { |
399 | 1 | if (elem.column) { |
400 | 1 | elem.column = elem.column->cut(length, origin_rows - length); |
401 | 1 | } |
402 | 1 | } |
403 | 1 | } |
404 | 1 | } |
405 | | |
406 | 18.3k | size_t Block::bytes() const { |
407 | 18.3k | size_t res = 0; |
408 | 41.3k | for (const auto& elem : data) { |
409 | 41.3k | if (!elem.column) { |
410 | 0 | std::stringstream ss; |
411 | 0 | for (const auto& e : data) { |
412 | 0 | ss << e.name + " "; |
413 | 0 | } |
414 | 0 | throw Exception(ErrorCode::INTERNAL_ERROR, |
415 | 0 | "Column {} in block is nullptr, in method bytes. All Columns are {}", |
416 | 0 | elem.name, ss.str()); |
417 | 0 | } |
418 | 41.3k | res += elem.column->byte_size(); |
419 | 41.3k | } |
420 | | |
421 | 18.3k | return res; |
422 | 18.3k | } |
423 | | |
424 | 195k | size_t Block::allocated_bytes() const { |
425 | 195k | size_t res = 0; |
426 | 381k | for (const auto& elem : data) { |
427 | 381k | if (!elem.column) { |
428 | | // Sometimes if expr failed, then there will be a nullptr |
429 | | // column left in the block. |
430 | 1 | continue; |
431 | 1 | } |
432 | 381k | res += elem.column->allocated_bytes(); |
433 | 381k | } |
434 | | |
435 | 195k | return res; |
436 | 195k | } |
437 | | |
438 | 9 | std::string Block::dump_names() const { |
439 | 9 | std::string out; |
440 | 26 | for (auto it = data.begin(); it != data.end(); ++it) { |
441 | 17 | if (it != data.begin()) { |
442 | 8 | out += ", "; |
443 | 8 | } |
444 | 17 | out += it->name; |
445 | 17 | } |
446 | 9 | return out; |
447 | 9 | } |
448 | | |
449 | 8 | std::string Block::dump_types() const { |
450 | 8 | std::string out; |
451 | 24 | for (auto it = data.begin(); it != data.end(); ++it) { |
452 | 16 | if (it != data.begin()) { |
453 | 8 | out += ", "; |
454 | 8 | } |
455 | 16 | out += it->type->get_name(); |
456 | 16 | } |
457 | 8 | return out; |
458 | 8 | } |
459 | | |
460 | 31 | std::string Block::dump_data_json(size_t begin, size_t row_limit, bool allow_null_mismatch) const { |
461 | 31 | std::stringstream ss; |
462 | | |
463 | 31 | std::vector<std::string> headers; |
464 | 31 | headers.reserve(columns()); |
465 | 46 | for (const auto& it : data) { |
466 | | // fmt::format is from the {fmt} library, you might be using std::format in C++20 |
467 | | // If not, you can build the string with a stringstream as a fallback. |
468 | 46 | headers.push_back(fmt::format("{}({})", it.name, it.type->get_name())); |
469 | 46 | } |
470 | | |
471 | 31 | size_t start_row = std::min(begin, rows()); |
472 | 31 | size_t end_row = std::min(rows(), begin + row_limit); |
473 | | |
474 | 31 | auto format_options = DataTypeSerDe::get_default_format_options(); |
475 | 31 | auto time_zone = cctz::utc_time_zone(); |
476 | 31 | format_options.timezone = &time_zone; |
477 | | |
478 | 31 | ss << "["; |
479 | 3.59k | for (size_t row_num = start_row; row_num < end_row; ++row_num) { |
480 | 3.56k | if (row_num > start_row) { |
481 | 3.53k | ss << ","; |
482 | 3.53k | } |
483 | 3.56k | ss << "{"; |
484 | 8.33k | for (size_t i = 0; i < columns(); ++i) { |
485 | 4.77k | if (i > 0) { |
486 | 1.21k | ss << ","; |
487 | 1.21k | } |
488 | 4.77k | ss << "\"" << headers[i] << "\":"; |
489 | 4.77k | std::string s; |
490 | | |
491 | | // This value-extraction logic is preserved from your original function |
492 | | // to maintain consistency, especially for handling nullability mismatches. |
493 | 4.77k | if (data[i].column && data[i].type->is_nullable() && !data[i].column->is_nullable()) { |
494 | | // This branch handles a specific internal representation of nullable columns. |
495 | | // The original code would assert here if allow_null_mismatch is false. |
496 | 0 | assert(allow_null_mismatch); |
497 | 0 | s = assert_cast<const DataTypeNullable*>(data[i].type.get()) |
498 | 0 | ->get_nested_type() |
499 | 0 | ->to_string(*data[i].column, row_num, format_options); |
500 | 4.77k | } else { |
501 | | // This is the standard path. The to_string method is expected to correctly |
502 | | // handle all cases, including when the column is null (e.g., by returning "NULL"). |
503 | 4.77k | s = data[i].to_string(row_num, format_options); |
504 | 4.77k | } |
505 | 4.77k | ss << "\"" << s << "\""; |
506 | 4.77k | } |
507 | 3.56k | ss << "}"; |
508 | 3.56k | } |
509 | 31 | ss << "]"; |
510 | 31 | return ss.str(); |
511 | 31 | } |
512 | | |
513 | 858 | std::string Block::dump_data(size_t begin, size_t row_limit, bool allow_null_mismatch) const { |
514 | 858 | std::vector<std::string> headers; |
515 | 858 | std::vector<int> headers_size; |
516 | 2.10k | for (const auto& it : data) { |
517 | 2.10k | std::string s = fmt::format("{}({})", it.name, it.type->get_name()); |
518 | 2.10k | headers_size.push_back(s.size() > 15 ? (int)s.size() : 15); |
519 | 2.10k | headers.emplace_back(s); |
520 | 2.10k | } |
521 | | |
522 | 858 | std::stringstream out; |
523 | | // header upper line |
524 | 2.16k | auto line = [&]() { |
525 | 8.07k | for (size_t i = 0; i < columns(); ++i) { |
526 | 5.91k | out << std::setfill('-') << std::setw(1) << "+" << std::setw(headers_size[i]) << "-"; |
527 | 5.91k | } |
528 | 2.16k | out << std::setw(1) << "+" << std::endl; |
529 | 2.16k | }; |
530 | 858 | line(); |
531 | | // header text |
532 | 2.96k | for (size_t i = 0; i < columns(); ++i) { |
533 | 2.10k | out << std::setfill(' ') << std::setw(1) << "|" << std::left << std::setw(headers_size[i]) |
534 | 2.10k | << headers[i]; |
535 | 2.10k | } |
536 | 858 | out << std::setw(1) << "|" << std::endl; |
537 | | // header bottom line |
538 | 858 | line(); |
539 | 858 | if (rows() == 0) { |
540 | 414 | return out.str(); |
541 | 414 | } |
542 | | |
543 | 444 | auto format_options = DataTypeSerDe::get_default_format_options(); |
544 | 444 | auto time_zone = cctz::utc_time_zone(); |
545 | 444 | format_options.timezone = &time_zone; |
546 | | |
547 | | // content |
548 | 12.2k | for (size_t row_num = begin; row_num < rows() && row_num < row_limit + begin; ++row_num) { |
549 | 32.4k | for (size_t i = 0; i < columns(); ++i) { |
550 | 20.6k | if (!data[i].column || data[i].column->empty()) { |
551 | 0 | out << std::setfill(' ') << std::setw(1) << "|" << std::setw(headers_size[i]) |
552 | 0 | << std::right; |
553 | 0 | continue; |
554 | 0 | } |
555 | 20.6k | std::string s; |
556 | 20.6k | if (data[i].column) { // column may be const |
557 | | // for code inside `default_implementation_for_nulls`, there's could have: type = null, col != null |
558 | 20.6k | if (data[i].type->is_nullable() && !data[i].column->is_nullable()) { |
559 | 0 | assert(allow_null_mismatch); |
560 | 0 | s = assert_cast<const DataTypeNullable*>(data[i].type.get()) |
561 | 0 | ->get_nested_type() |
562 | 0 | ->to_string(*data[i].column, row_num, format_options); |
563 | 20.6k | } else { |
564 | 20.6k | s = data[i].to_string(row_num, format_options); |
565 | 20.6k | } |
566 | 20.6k | } |
567 | 20.6k | if (s.length() > headers_size[i]) { |
568 | 2.12k | s = s.substr(0, headers_size[i] - 3) + "..."; |
569 | 2.12k | } |
570 | 20.6k | out << std::setfill(' ') << std::setw(1) << "|" << std::setw(headers_size[i]) |
571 | 20.6k | << std::right << s; |
572 | 20.6k | } |
573 | 11.7k | out << std::setw(1) << "|" << std::endl; |
574 | 11.7k | } |
575 | | // bottom line |
576 | 444 | line(); |
577 | 444 | if (row_limit < rows()) { |
578 | 112 | out << rows() << " rows in block, only show first " << row_limit << " rows." << std::endl; |
579 | 112 | } |
580 | 444 | return out.str(); |
581 | 444 | } |
582 | | |
583 | 1 | std::string Block::dump_one_line(size_t row, int column_end) const { |
584 | 1 | assert(column_end <= columns()); |
585 | 1 | fmt::memory_buffer line; |
586 | | |
587 | 1 | auto format_options = DataTypeSerDe::get_default_format_options(); |
588 | 1 | auto time_zone = cctz::utc_time_zone(); |
589 | 1 | format_options.timezone = &time_zone; |
590 | | |
591 | 3 | for (int i = 0; i < column_end; ++i) { |
592 | 2 | if (LIKELY(i != 0)) { |
593 | | // TODO: need more effective function of to string. now the impl is slow |
594 | 1 | fmt::format_to(line, " {}", data[i].to_string(row, format_options)); |
595 | 1 | } else { |
596 | 1 | fmt::format_to(line, "{}", data[i].to_string(row, format_options)); |
597 | 1 | } |
598 | 2 | } |
599 | 1 | return fmt::to_string(line); |
600 | 1 | } |
601 | | |
602 | 49 | std::string Block::dump_structure() const { |
603 | 49 | std::string out; |
604 | 390 | for (auto it = data.begin(); it != data.end(); ++it) { |
605 | 341 | if (it != data.begin()) { |
606 | 292 | out += ", \n"; |
607 | 292 | } |
608 | 341 | out += it->dump_structure(); |
609 | 341 | } |
610 | 49 | return out; |
611 | 49 | } |
612 | | |
613 | 49.1k | Block Block::clone_empty() const { |
614 | 49.1k | Block res; |
615 | 96.1k | for (const auto& elem : data) { |
616 | 96.1k | res.insert(elem.clone_empty()); |
617 | 96.1k | } |
618 | 49.1k | return res; |
619 | 49.1k | } |
620 | | |
621 | 49 | MutableColumns Block::clone_empty_columns() const { |
622 | 49 | size_t num_columns = data.size(); |
623 | 49 | MutableColumns columns(num_columns); |
624 | 266 | for (size_t i = 0; i < num_columns; ++i) { |
625 | 217 | columns[i] = data[i].column ? data[i].column->clone_empty() : data[i].type->create_column(); |
626 | 217 | } |
627 | 49 | return columns; |
628 | 49 | } |
629 | | |
630 | 26.5k | Columns Block::get_columns() const { |
631 | 26.5k | size_t num_columns = data.size(); |
632 | 26.5k | Columns columns(num_columns); |
633 | 118k | for (size_t i = 0; i < num_columns; ++i) { |
634 | 92.1k | columns[i] = data[i].column->convert_to_full_column_if_const(); |
635 | 92.1k | } |
636 | 26.5k | return columns; |
637 | 26.5k | } |
638 | | |
639 | 570 | Columns Block::get_columns_and_convert() { |
640 | 570 | size_t num_columns = data.size(); |
641 | 570 | Columns columns(num_columns); |
642 | 1.20k | for (size_t i = 0; i < num_columns; ++i) { |
643 | 636 | data[i].column = data[i].column->convert_to_full_column_if_const(); |
644 | 636 | columns[i] = data[i].column; |
645 | 636 | } |
646 | 570 | return columns; |
647 | 570 | } |
648 | | |
649 | 7.00k | Block::ScopedMutableColumns::ScopedMutableColumns(Block& block) : _block(&block) { |
650 | 7.00k | const size_t num_columns = block.data.size(); |
651 | 7.00k | _columns.resize(num_columns); |
652 | 7.00k | size_t acquired_columns = 0; |
653 | 7.00k | try { |
654 | 19.9k | for (; acquired_columns < num_columns; ++acquired_columns) { |
655 | 12.9k | auto& column_with_type_and_name = block.data[acquired_columns]; |
656 | 12.9k | _columns[acquired_columns] = scoped_mutate_column(column_with_type_and_name.column, |
657 | 12.9k | column_with_type_and_name.type); |
658 | 12.9k | } |
659 | 7.00k | } catch (...) { |
660 | 4 | for (size_t i = 0; i < acquired_columns; ++i) { |
661 | 2 | block.data[i].column = std::move(_columns[i]); |
662 | 2 | } |
663 | 2 | _block = nullptr; |
664 | 2 | throw; |
665 | 2 | } |
666 | 7.00k | } |
667 | | |
668 | 7.00k | Block::ScopedMutableColumns::~ScopedMutableColumns() { |
669 | 7.00k | restore(); |
670 | 7.00k | } |
671 | | |
672 | | Block::ScopedMutableColumns::ScopedMutableColumns(ScopedMutableColumns&& other) noexcept |
673 | 0 | : _block(std::exchange(other._block, nullptr)), _columns(std::move(other._columns)) {} |
674 | | |
675 | | Block::ScopedMutableColumns& Block::ScopedMutableColumns::operator=( |
676 | 0 | ScopedMutableColumns&& other) noexcept { |
677 | 0 | if (this != &other) { |
678 | 0 | restore(); |
679 | 0 | _block = std::exchange(other._block, nullptr); |
680 | 0 | _columns = std::move(other._columns); |
681 | 0 | } |
682 | 0 | return *this; |
683 | 0 | } |
684 | | |
685 | 187 | const DataTypePtr& Block::ScopedMutableColumns::get_datatype_by_position(size_t position) const { |
686 | 187 | DCHECK(_block != nullptr); |
687 | 187 | return _block->get_by_position(position).type; |
688 | 187 | } |
689 | | |
690 | 2 | const std::string& Block::ScopedMutableColumns::get_name_by_position(size_t position) const { |
691 | 2 | DCHECK(_block != nullptr); |
692 | 2 | return _block->get_by_position(position).name; |
693 | 2 | } |
694 | | |
695 | 909 | MutableColumns Block::ScopedMutableColumns::release() { |
696 | 909 | DCHECK(_block != nullptr); |
697 | 909 | _block = nullptr; |
698 | 909 | return std::move(_columns); |
699 | 909 | } |
700 | | |
701 | 9.53k | void Block::ScopedMutableColumns::restore() { |
702 | 9.53k | if (_block != nullptr) { |
703 | 6.09k | _block->set_columns(std::move(_columns)); |
704 | 6.09k | _block = nullptr; |
705 | 6.09k | } |
706 | 9.53k | } |
707 | | |
708 | | Block::ScopedMutableColumn::ScopedMutableColumn(Block& block, size_t position) |
709 | 92 | : _block(&block), _position(position) { |
710 | 92 | DCHECK_LT(_position, _block->data.size()); |
711 | 92 | auto& column_with_type_and_name = _block->data[_position]; |
712 | 92 | DCHECK(column_with_type_and_name.type); |
713 | 92 | _column = |
714 | 92 | scoped_mutate_column(column_with_type_and_name.column, column_with_type_and_name.type); |
715 | 92 | } |
716 | | |
717 | 91 | Block::ScopedMutableColumn::~ScopedMutableColumn() { |
718 | 91 | restore(); |
719 | 91 | } |
720 | | |
721 | | Block::ScopedMutableColumn::ScopedMutableColumn(ScopedMutableColumn&& other) noexcept |
722 | 0 | : _block(std::exchange(other._block, nullptr)), |
723 | 0 | _position(other._position), |
724 | 0 | _column(std::move(other._column)) {} |
725 | | |
726 | | Block::ScopedMutableColumn& Block::ScopedMutableColumn::operator=( |
727 | 0 | ScopedMutableColumn&& other) noexcept { |
728 | 0 | if (this != &other) { |
729 | 0 | restore(); |
730 | 0 | _block = std::exchange(other._block, nullptr); |
731 | 0 | _position = other._position; |
732 | 0 | _column = std::move(other._column); |
733 | 0 | } |
734 | 0 | return *this; |
735 | 0 | } |
736 | | |
737 | 91 | void Block::ScopedMutableColumn::restore() { |
738 | 91 | if (_block != nullptr) { |
739 | 91 | DCHECK_LT(_position, _block->data.size()); |
740 | 91 | _block->data[_position].column = std::move(_column); |
741 | 91 | _block = nullptr; |
742 | 91 | } |
743 | 91 | } |
744 | | |
745 | 6.96k | Block::ScopedMutableColumns Block::mutate_columns_scoped() & { |
746 | 6.96k | return ScopedMutableColumns(*this); |
747 | 6.96k | } |
748 | | |
749 | 92 | Block::ScopedMutableColumn Block::mutate_column_scoped(size_t position) & { |
750 | 92 | return ScopedMutableColumn(*this, position); |
751 | 92 | } |
752 | | |
753 | 910 | ScopedMutableBlock::ScopedMutableBlock(Block* block) { |
754 | 910 | DCHECK(block != nullptr); |
755 | 910 | DataTypes data_types = block->get_data_types(); |
756 | 910 | std::vector<std::string> names = block->get_names(); |
757 | 910 | auto columns_guard = block->mutate_columns_scoped(); |
758 | 910 | _mutable_block.data_types() = std::move(data_types); |
759 | 910 | _mutable_block.get_names() = std::move(names); |
760 | 910 | _mutable_block.set_mutable_columns(columns_guard.release()); |
761 | 910 | _block = block; |
762 | 910 | } |
763 | | |
764 | 147k | MutableColumns Block::mutate_columns() && { |
765 | 147k | size_t num_columns = data.size(); |
766 | 147k | MutableColumns columns(num_columns); |
767 | 439k | for (size_t i = 0; i < num_columns; ++i) { |
768 | 292k | DCHECK(data[i].type); |
769 | 292k | columns[i] = data[i].column ? IColumn::mutate(std::move(data[i].column)) |
770 | 292k | : data[i].type->create_column(); |
771 | 292k | } |
772 | 147k | return columns; |
773 | 147k | } |
774 | | |
775 | 8.91k | void Block::set_columns(MutableColumns&& columns) { |
776 | 8.91k | DCHECK_GE(columns.size(), data.size()) |
777 | 0 | << fmt::format("Invalid size of columns, columns size: {}, data size: {}", |
778 | 0 | columns.size(), data.size()); |
779 | 8.91k | size_t num_columns = data.size(); |
780 | 31.5k | for (size_t i = 0; i < num_columns; ++i) { |
781 | 22.5k | data[i].column = std::move(columns[i]); |
782 | 22.5k | } |
783 | 8.91k | } |
784 | | |
785 | 51 | Block Block::clone_without_columns(const std::vector<int>* column_offset) const { |
786 | 51 | Block res; |
787 | | |
788 | 51 | if (column_offset != nullptr) { |
789 | 32 | size_t num_columns = column_offset->size(); |
790 | 174 | for (size_t i = 0; i < num_columns; ++i) { |
791 | 142 | res.insert({nullptr, data[(*column_offset)[i]].type, data[(*column_offset)[i]].name}); |
792 | 142 | } |
793 | 32 | } else { |
794 | 19 | size_t num_columns = data.size(); |
795 | 53 | for (size_t i = 0; i < num_columns; ++i) { |
796 | 34 | res.insert({nullptr, data[i].type, data[i].name}); |
797 | 34 | } |
798 | 19 | } |
799 | 51 | return res; |
800 | 51 | } |
801 | | |
802 | 56.1k | const ColumnsWithTypeAndName& Block::get_columns_with_type_and_name() const { |
803 | 56.1k | return data; |
804 | 56.1k | } |
805 | | |
806 | 145k | std::vector<std::string> Block::get_names() const { |
807 | 145k | std::vector<std::string> res; |
808 | 145k | res.reserve(columns()); |
809 | | |
810 | 286k | for (const auto& elem : data) { |
811 | 286k | res.push_back(elem.name); |
812 | 286k | } |
813 | | |
814 | 145k | return res; |
815 | 145k | } |
816 | | |
817 | 145k | DataTypes Block::get_data_types() const { |
818 | 145k | DataTypes res; |
819 | 145k | res.reserve(columns()); |
820 | | |
821 | 286k | for (const auto& elem : data) { |
822 | 286k | res.push_back(elem.type); |
823 | 286k | } |
824 | | |
825 | 145k | return res; |
826 | 145k | } |
827 | | |
828 | 53.0k | void Block::clear() { |
829 | 53.0k | data.clear(); |
830 | 53.0k | } |
831 | | |
832 | 1.51M | void Block::clear_column_data(int64_t column_size) { |
833 | 1.51M | SCOPED_SKIP_MEMORY_CHECK(); |
834 | | // data.size() greater than column_size, means here have some |
835 | | // function exec result in block, need erase it here |
836 | 1.51M | if (column_size != -1 and data.size() > column_size) { |
837 | 2.21k | for (int64_t i = data.size() - 1; i >= column_size; --i) { |
838 | 1.10k | erase(i); |
839 | 1.10k | } |
840 | 1.10k | } |
841 | 1.51M | for (auto& d : data) { |
842 | 75.7k | if (d.column) { |
843 | 75.7k | if (d.column->is_exclusive()) { |
844 | 75.4k | d.column->assert_mutable()->clear(); |
845 | 75.4k | } else { |
846 | 318 | d.column = d.column->clone_empty(); |
847 | 318 | } |
848 | 75.7k | } |
849 | 75.7k | } |
850 | 1.51M | } |
851 | | |
852 | 35 | void Block::clear_column_data(const std::vector<uint32_t>& columns_to_clear) { |
853 | 35 | SCOPED_SKIP_MEMORY_CHECK(); |
854 | 46 | for (auto col : columns_to_clear) { |
855 | 46 | DCHECK_LT(col, data.size()); |
856 | 46 | auto& column = data[col].column; |
857 | 46 | if (column) { |
858 | 46 | if (column->is_exclusive()) { |
859 | 44 | column->assert_mutable()->clear(); |
860 | 44 | } else { |
861 | 2 | column = column->clone_empty(); |
862 | 2 | } |
863 | 46 | } |
864 | 46 | } |
865 | 35 | } |
866 | | |
867 | | void Block::clear_column_mem_not_keep(const std::vector<bool>& column_keep_flags, |
868 | 48.0k | bool need_keep_first) { |
869 | 48.0k | if (data.size() >= column_keep_flags.size()) { |
870 | 48.0k | auto origin_rows = rows(); |
871 | 142k | for (size_t i = 0; i < column_keep_flags.size(); ++i) { |
872 | 94.1k | if (!column_keep_flags[i]) { |
873 | 36.8k | data[i].column = data[i].column->clone_empty(); |
874 | 36.8k | } |
875 | 94.1k | } |
876 | | |
877 | 48.0k | if (need_keep_first && !column_keep_flags[0]) { |
878 | 1 | auto first_column = data[0].column->clone_empty(); |
879 | 1 | first_column->resize(origin_rows); |
880 | 1 | data[0].column = std::move(first_column); |
881 | 1 | } |
882 | 48.0k | } |
883 | 48.0k | } |
884 | | |
885 | 1.33k | void Block::swap(Block& other) noexcept { |
886 | 1.33k | SCOPED_SKIP_MEMORY_CHECK(); |
887 | 1.33k | data.swap(other.data); |
888 | 1.33k | } |
889 | | |
890 | 1.77k | void Block::swap(Block&& other) noexcept { |
891 | 1.77k | SCOPED_SKIP_MEMORY_CHECK(); |
892 | 1.77k | data = std::move(other.data); |
893 | 1.77k | } |
894 | | |
895 | 3 | void Block::shuffle_columns(const std::vector<int>& result_column_ids) { |
896 | 3 | Container tmp_data; |
897 | 3 | tmp_data.reserve(result_column_ids.size()); |
898 | 5 | for (const int result_column_id : result_column_ids) { |
899 | 5 | tmp_data.push_back(data[result_column_id]); |
900 | 5 | } |
901 | 3 | data = std::move(tmp_data); |
902 | 3 | } |
903 | | |
904 | 2 | void Block::update_hash(SipHash& hash) const { |
905 | 8 | for (size_t row_no = 0, num_rows = rows(); row_no < num_rows; ++row_no) { |
906 | 12 | for (const auto& col : data) { |
907 | 12 | col.column->update_hash_with_value(row_no, hash); |
908 | 12 | } |
909 | 6 | } |
910 | 2 | } |
911 | | |
912 | | void Block::filter_block_internal(Block* block, const std::vector<uint32_t>& columns_to_filter, |
913 | 2.61k | const IColumn::Filter& filter) { |
914 | 2.61k | size_t count = filter.size() - simd::count_zero_num((int8_t*)filter.data(), filter.size()); |
915 | 5.73k | for (const auto& col : columns_to_filter) { |
916 | 5.73k | auto& column = block->get_by_position(col).column; |
917 | 5.73k | if (column->size() == count) { |
918 | 5.39k | continue; |
919 | 5.39k | } |
920 | 337 | if (count == 0) { |
921 | 21 | if (column->is_exclusive()) { |
922 | 19 | column->assert_mutable()->clear(); |
923 | 19 | } else { |
924 | 2 | column = column->clone_empty(); |
925 | 2 | } |
926 | 21 | continue; |
927 | 21 | } |
928 | 316 | if (column->is_exclusive()) { |
929 | | // COW: safe to mutate in-place since we have exclusive ownership |
930 | 220 | const auto result_size = column->assert_mutable()->filter(filter); |
931 | 220 | if (result_size != count) [[unlikely]] { |
932 | 0 | throw Exception(ErrorCode::INTERNAL_ERROR, |
933 | 0 | "result_size not equal with filter_size, result_size={}, " |
934 | 0 | "filter_size={}", |
935 | 0 | result_size, count); |
936 | 0 | } |
937 | 220 | } else { |
938 | | // COW: must create a copy since column is shared |
939 | 96 | column = column->filter(filter, count); |
940 | 96 | } |
941 | 316 | } |
942 | 2.61k | } |
943 | | |
944 | | void Block::filter_block_internal(Block* block, const IColumn::Filter& filter, |
945 | 28 | uint32_t column_to_keep) { |
946 | 28 | std::vector<uint32_t> columns_to_filter; |
947 | 28 | columns_to_filter.resize(column_to_keep); |
948 | 88 | for (uint32_t i = 0; i < column_to_keep; ++i) { |
949 | 60 | columns_to_filter[i] = i; |
950 | 60 | } |
951 | 28 | filter_block_internal(block, columns_to_filter, filter); |
952 | 28 | } |
953 | | |
954 | 9 | void Block::filter_block_internal(Block* block, const IColumn::Filter& filter) { |
955 | 9 | const size_t count = |
956 | 9 | filter.size() - simd::count_zero_num((int8_t*)filter.data(), filter.size()); |
957 | 27 | for (int i = 0; i < block->columns(); ++i) { |
958 | 18 | auto& column = block->get_by_position(i).column; |
959 | 18 | if (column->is_exclusive()) { |
960 | 18 | column->assert_mutable()->filter(filter); |
961 | 18 | } else { |
962 | 0 | column = column->filter(filter, count); |
963 | 0 | } |
964 | 18 | } |
965 | 9 | } |
966 | | |
967 | | Status Block::append_to_block_by_selector(MutableBlock* dst, |
968 | 1 | const IColumn::Selector& selector) const { |
969 | 1 | RETURN_IF_CATCH_EXCEPTION({ |
970 | 1 | DCHECK_EQ(data.size(), dst->mutable_columns().size()); |
971 | 1 | for (size_t i = 0; i < data.size(); i++) { |
972 | | // FIXME: this is a quickfix. we assume that only partition functions make there some |
973 | 1 | if (!is_column_const(*data[i].column)) { |
974 | 1 | data[i].column->append_data_by_selector(dst->mutable_columns()[i], selector); |
975 | 1 | } |
976 | 1 | } |
977 | 1 | }); |
978 | 1 | return Status::OK(); |
979 | 1 | } |
980 | | |
981 | | Status Block::filter_block(Block* block, const std::vector<uint32_t>& columns_to_filter, |
982 | 2.55k | size_t filter_column_id, size_t column_to_keep) { |
983 | 2.55k | const auto& filter_column = block->get_by_position(filter_column_id).column; |
984 | 2.55k | if (const auto* nullable_column = check_and_get_column<ColumnNullable>(*filter_column)) { |
985 | 1 | const auto& nested_column = nullable_column->get_nested_column_ptr(); |
986 | | |
987 | 1 | MutableColumnPtr mutable_holder = |
988 | 1 | nested_column->use_count() == 1 |
989 | 1 | ? nested_column->assert_mutable() |
990 | 1 | : nested_column->clone_resized(nested_column->size()); |
991 | | |
992 | 1 | auto* concrete_column = assert_cast<ColumnUInt8*>(mutable_holder.get()); |
993 | 1 | const auto* __restrict null_map = nullable_column->get_null_map_data().data(); |
994 | 1 | IColumn::Filter& filter = concrete_column->get_data(); |
995 | 1 | auto* __restrict filter_data = filter.data(); |
996 | | |
997 | 1 | const size_t size = filter.size(); |
998 | 4 | for (size_t i = 0; i < size; ++i) { |
999 | 3 | filter_data[i] &= !null_map[i]; |
1000 | 3 | } |
1001 | 1 | RETURN_IF_CATCH_EXCEPTION(filter_block_internal(block, columns_to_filter, filter)); |
1002 | 2.55k | } else if (const auto* const_column = check_and_get_column<ColumnConst>(*filter_column)) { |
1003 | 2 | bool ret = const_column->get_bool(0); |
1004 | 2 | if (!ret) { |
1005 | 2 | for (const auto& col : columns_to_filter) { |
1006 | 2 | auto& column = block->get_by_position(col).column; |
1007 | 2 | if (column->is_exclusive()) { |
1008 | 2 | column->assert_mutable()->clear(); |
1009 | 2 | } else { |
1010 | 0 | column = column->clone_empty(); |
1011 | 0 | } |
1012 | 2 | } |
1013 | 1 | } |
1014 | 2.54k | } else { |
1015 | 2.54k | const IColumn::Filter& filter = |
1016 | 2.54k | assert_cast<const doris::ColumnUInt8&>(*filter_column).get_data(); |
1017 | 2.54k | RETURN_IF_CATCH_EXCEPTION(filter_block_internal(block, columns_to_filter, filter)); |
1018 | 2.54k | } |
1019 | | |
1020 | 2.55k | erase_useless_column(block, column_to_keep); |
1021 | 2.55k | return Status::OK(); |
1022 | 2.55k | } |
1023 | | |
1024 | 2.54k | Status Block::filter_block(Block* block, size_t filter_column_id, size_t column_to_keep) { |
1025 | 2.54k | std::vector<uint32_t> columns_to_filter; |
1026 | 2.54k | columns_to_filter.resize(column_to_keep); |
1027 | 8.13k | for (uint32_t i = 0; i < column_to_keep; ++i) { |
1028 | 5.58k | columns_to_filter[i] = i; |
1029 | 5.58k | } |
1030 | 2.54k | return filter_block(block, columns_to_filter, filter_column_id, column_to_keep); |
1031 | 2.54k | } |
1032 | | |
1033 | | Status Block::serialize(int be_exec_version, PBlock* pblock, |
1034 | | /*std::string* compressed_buffer,*/ size_t* uncompressed_bytes, |
1035 | | size_t* compressed_bytes, int64_t* compress_time, |
1036 | | segment_v2::CompressionTypePB compression_type, |
1037 | 2.74k | bool allow_transfer_large_data) const { |
1038 | 2.74k | RETURN_IF_ERROR(BeExecVersionManager::check_be_exec_version(be_exec_version)); |
1039 | 2.74k | pblock->set_be_exec_version(be_exec_version); |
1040 | | |
1041 | | // calc uncompressed size for allocation |
1042 | 2.74k | size_t content_uncompressed_size = 0; |
1043 | 2.74k | RETURN_IF_CATCH_EXCEPTION({ |
1044 | 2.74k | for (const auto& c : *this) { |
1045 | 2.74k | PColumnMeta* pcm = pblock->add_column_metas(); |
1046 | 2.74k | c.to_pb_column_meta(pcm); |
1047 | 2.74k | DCHECK(pcm->type() != PGenericType::UNKNOWN) << " forget to set pb type"; |
1048 | | // get serialized size |
1049 | 2.74k | content_uncompressed_size += c.type->get_uncompressed_serialized_bytes( |
1050 | 2.74k | *(c.column), pblock->be_exec_version()); |
1051 | 2.74k | } |
1052 | 2.74k | }); |
1053 | | |
1054 | | // serialize data values |
1055 | | // when data type is HLL, content_uncompressed_size maybe larger than real size. |
1056 | 2.73k | std::string column_values; |
1057 | 2.73k | try { |
1058 | | // TODO: After support c++23, we should use resize_and_overwrite to replace resize |
1059 | 2.73k | column_values.resize(content_uncompressed_size); |
1060 | 2.73k | } catch (...) { |
1061 | 0 | std::string msg = fmt::format("Try to alloc {} bytes for pblock column values failed.", |
1062 | 0 | content_uncompressed_size); |
1063 | 0 | LOG(WARNING) << msg; |
1064 | 0 | return Status::BufferAllocFailed(msg); |
1065 | 0 | } |
1066 | 2.74k | char* buf = column_values.data(); |
1067 | | |
1068 | 2.74k | RETURN_IF_CATCH_EXCEPTION({ |
1069 | 2.74k | for (const auto& c : *this) { |
1070 | 2.74k | buf = c.type->serialize(*(c.column), buf, pblock->be_exec_version()); |
1071 | 2.74k | } |
1072 | 2.74k | }); |
1073 | 2.73k | *uncompressed_bytes = content_uncompressed_size; |
1074 | 2.73k | const size_t serialize_bytes = buf - column_values.data() + STREAMVBYTE_PADDING; |
1075 | 2.73k | *compressed_bytes = serialize_bytes; |
1076 | 2.73k | column_values.resize(serialize_bytes); |
1077 | | |
1078 | | // compress |
1079 | 2.73k | if (compression_type != segment_v2::NO_COMPRESSION && content_uncompressed_size > 0) { |
1080 | 641 | SCOPED_RAW_TIMER(compress_time); |
1081 | 641 | pblock->set_compression_type(compression_type); |
1082 | 641 | pblock->set_uncompressed_size(serialize_bytes); |
1083 | | |
1084 | 641 | BlockCompressionCodec* codec; |
1085 | 641 | RETURN_IF_ERROR(get_block_compression_codec(compression_type, &codec)); |
1086 | | |
1087 | 641 | faststring buf_compressed; |
1088 | 641 | RETURN_IF_ERROR_OR_CATCH_EXCEPTION( |
1089 | 641 | codec->compress(Slice(column_values.data(), serialize_bytes), &buf_compressed)); |
1090 | 641 | size_t compressed_size = buf_compressed.size(); |
1091 | 641 | if (LIKELY(compressed_size < serialize_bytes)) { |
1092 | | // TODO: rethink the logic here may copy again ? |
1093 | 641 | pblock->set_column_values(buf_compressed.data(), buf_compressed.size()); |
1094 | 641 | pblock->set_compressed(true); |
1095 | 641 | *compressed_bytes = compressed_size; |
1096 | 641 | } else { |
1097 | 0 | pblock->set_column_values(std::move(column_values)); |
1098 | 0 | } |
1099 | | |
1100 | 641 | VLOG_ROW << "uncompressed size: " << content_uncompressed_size |
1101 | 0 | << ", compressed size: " << compressed_size; |
1102 | 2.09k | } else { |
1103 | 2.09k | pblock->set_column_values(std::move(column_values)); |
1104 | 2.09k | } |
1105 | 2.74k | if (!allow_transfer_large_data && *compressed_bytes >= std::numeric_limits<int32_t>::max()) { |
1106 | 0 | return Status::InternalError("The block is large than 2GB({}), can not send by Protobuf.", |
1107 | 0 | *compressed_bytes); |
1108 | 0 | } |
1109 | 2.73k | return Status::OK(); |
1110 | 2.73k | } |
1111 | | |
1112 | 240k | size_t MutableBlock::rows() const { |
1113 | 240k | for (const auto& column : _columns) { |
1114 | 144k | if (column) { |
1115 | 144k | return column->size(); |
1116 | 144k | } |
1117 | 144k | } |
1118 | | |
1119 | 96.0k | return 0; |
1120 | 240k | } |
1121 | | |
1122 | 0 | void MutableBlock::swap(MutableBlock& another) noexcept { |
1123 | 0 | SCOPED_SKIP_MEMORY_CHECK(); |
1124 | 0 | _columns.swap(another._columns); |
1125 | 0 | _data_types.swap(another._data_types); |
1126 | 0 | _names.swap(another._names); |
1127 | 0 | } |
1128 | | |
1129 | 16 | void MutableBlock::add_row(const Block* block, int row) { |
1130 | 16 | const auto& block_data = block->get_columns_with_type_and_name(); |
1131 | 448 | for (size_t i = 0; i < _columns.size(); ++i) { |
1132 | 432 | _columns[i]->insert_from(*block_data[i].column.get(), row); |
1133 | 432 | } |
1134 | 16 | } |
1135 | | |
1136 | | Status MutableBlock::add_rows(const Block* block, const uint32_t* row_begin, |
1137 | 165 | const uint32_t* row_end, const std::vector<int>* column_offset) { |
1138 | 165 | RETURN_IF_CATCH_EXCEPTION({ |
1139 | 165 | DCHECK_LE(columns(), block->columns()); |
1140 | 165 | if (column_offset != nullptr) { |
1141 | 165 | DCHECK_EQ(columns(), column_offset->size()); |
1142 | 165 | } |
1143 | 165 | const auto& block_data = block->get_columns_with_type_and_name(); |
1144 | 165 | for (size_t i = 0; i < _columns.size(); ++i) { |
1145 | 165 | const auto& src_col = column_offset ? block_data[(*column_offset)[i]] : block_data[i]; |
1146 | 165 | DCHECK_EQ(_data_types[i]->get_name(), src_col.type->get_name()); |
1147 | 165 | auto& dst = _columns[i]; |
1148 | 165 | const auto& src = *src_col.column.get(); |
1149 | 165 | DCHECK_GE(src.size(), row_end - row_begin); |
1150 | 165 | dst->insert_indices_from(src, row_begin, row_end); |
1151 | 165 | } |
1152 | 165 | }); |
1153 | 164 | return Status::OK(); |
1154 | 165 | } |
1155 | | |
1156 | 172 | Status MutableBlock::add_rows(const Block* block, size_t row_begin, size_t length) { |
1157 | 172 | RETURN_IF_CATCH_EXCEPTION({ |
1158 | 172 | DCHECK_LE(columns(), block->columns()); |
1159 | 172 | const auto& block_data = block->get_columns_with_type_and_name(); |
1160 | 172 | for (size_t i = 0; i < _columns.size(); ++i) { |
1161 | 172 | DCHECK_EQ(_data_types[i]->get_name(), block_data[i].type->get_name()); |
1162 | 172 | auto& dst = _columns[i]; |
1163 | 172 | const auto& src = *block_data[i].column.get(); |
1164 | 172 | dst->insert_range_from(src, row_begin, length); |
1165 | 172 | } |
1166 | 172 | }); |
1167 | 172 | return Status::OK(); |
1168 | 172 | } |
1169 | | |
1170 | 144k | Block MutableBlock::to_block(int start_column) { |
1171 | 144k | return to_block(start_column, (int)_columns.size()); |
1172 | 144k | } |
1173 | | |
1174 | 144k | Block MutableBlock::to_block(int start_column, int end_column) { |
1175 | 144k | ColumnsWithTypeAndName columns_with_schema; |
1176 | 144k | columns_with_schema.reserve(end_column - start_column); |
1177 | 429k | for (size_t i = start_column; i < end_column; ++i) { |
1178 | 284k | columns_with_schema.emplace_back(std::move(_columns[i]), _data_types[i], _names[i]); |
1179 | 284k | } |
1180 | 144k | return {columns_with_schema}; |
1181 | 144k | } |
1182 | | |
1183 | 1 | std::string MutableBlock::dump_data_json(size_t row_limit) const { |
1184 | 1 | std::stringstream ss; |
1185 | 1 | std::vector<std::string> headers; |
1186 | | |
1187 | 1 | headers.reserve(columns()); |
1188 | 2 | for (size_t i = 0; i < columns(); ++i) { |
1189 | 1 | headers.push_back(_data_types[i]->get_name()); |
1190 | 1 | } |
1191 | 1 | size_t num_rows_to_dump = std::min(rows(), row_limit); |
1192 | 1 | ss << "["; |
1193 | | |
1194 | 1 | auto format_options = DataTypeSerDe::get_default_format_options(); |
1195 | 1 | auto time_zone = cctz::utc_time_zone(); |
1196 | 1 | format_options.timezone = &time_zone; |
1197 | | |
1198 | 4 | for (size_t row_num = 0; row_num < num_rows_to_dump; ++row_num) { |
1199 | 3 | if (row_num > 0) { |
1200 | 2 | ss << ","; |
1201 | 2 | } |
1202 | 3 | ss << "{"; |
1203 | 6 | for (size_t i = 0; i < columns(); ++i) { |
1204 | 3 | if (i > 0) { |
1205 | 0 | ss << ","; |
1206 | 0 | } |
1207 | 3 | ss << "\"" << headers[i] << "\":"; |
1208 | 3 | std::string s = _data_types[i]->to_string(*_columns[i].get(), row_num, format_options); |
1209 | 3 | ss << "\"" << s << "\""; |
1210 | 3 | } |
1211 | 3 | ss << "}"; |
1212 | 3 | } |
1213 | 1 | ss << "]"; |
1214 | 1 | return ss.str(); |
1215 | 1 | } |
1216 | | |
1217 | 1 | std::string MutableBlock::dump_data(size_t row_limit) const { |
1218 | 1 | std::vector<std::string> headers; |
1219 | 1 | std::vector<int> headers_size; |
1220 | 2 | for (size_t i = 0; i < columns(); ++i) { |
1221 | 1 | std::string s = _data_types[i]->get_name(); |
1222 | 1 | headers_size.push_back(s.size() > 15 ? (int)s.size() : 15); |
1223 | 1 | headers.emplace_back(s); |
1224 | 1 | } |
1225 | | |
1226 | 1 | std::stringstream out; |
1227 | | // header upper line |
1228 | 3 | auto line = [&]() { |
1229 | 6 | for (size_t i = 0; i < columns(); ++i) { |
1230 | 3 | out << std::setfill('-') << std::setw(1) << "+" << std::setw(headers_size[i]) << "-"; |
1231 | 3 | } |
1232 | 3 | out << std::setw(1) << "+" << std::endl; |
1233 | 3 | }; |
1234 | 1 | line(); |
1235 | | // header text |
1236 | 2 | for (size_t i = 0; i < columns(); ++i) { |
1237 | 1 | out << std::setfill(' ') << std::setw(1) << "|" << std::left << std::setw(headers_size[i]) |
1238 | 1 | << headers[i]; |
1239 | 1 | } |
1240 | 1 | out << std::setw(1) << "|" << std::endl; |
1241 | | // header bottom line |
1242 | 1 | line(); |
1243 | 1 | if (rows() == 0) { |
1244 | 0 | return out.str(); |
1245 | 0 | } |
1246 | | |
1247 | 1 | auto format_options = DataTypeSerDe::get_default_format_options(); |
1248 | 1 | auto time_zone = cctz::utc_time_zone(); |
1249 | 1 | format_options.timezone = &time_zone; |
1250 | | |
1251 | | // content |
1252 | 4 | for (size_t row_num = 0; row_num < rows() && row_num < row_limit; ++row_num) { |
1253 | 6 | for (size_t i = 0; i < columns(); ++i) { |
1254 | 3 | if (_columns[i].get()->empty()) { |
1255 | 0 | out << std::setfill(' ') << std::setw(1) << "|" << std::setw(headers_size[i]) |
1256 | 0 | << std::right; |
1257 | 0 | continue; |
1258 | 0 | } |
1259 | 3 | std::string s = _data_types[i]->to_string(*_columns[i].get(), row_num, format_options); |
1260 | 3 | if (s.length() > headers_size[i]) { |
1261 | 0 | s = s.substr(0, headers_size[i] - 3) + "..."; |
1262 | 0 | } |
1263 | 3 | out << std::setfill(' ') << std::setw(1) << "|" << std::setw(headers_size[i]) |
1264 | 3 | << std::right << s; |
1265 | 3 | } |
1266 | 3 | out << std::setw(1) << "|" << std::endl; |
1267 | 3 | } |
1268 | | // bottom line |
1269 | 1 | line(); |
1270 | 1 | if (row_limit < rows()) { |
1271 | 0 | out << rows() << " rows in block, only show first " << row_limit << " rows." << std::endl; |
1272 | 0 | } |
1273 | 1 | return out.str(); |
1274 | 1 | } |
1275 | | |
1276 | 48.0k | std::unique_ptr<Block> Block::create_same_struct_block(size_t size, bool is_reserve) const { |
1277 | 48.0k | auto temp_block = Block::create_unique(); |
1278 | 94.1k | for (const auto& d : data) { |
1279 | 94.1k | auto column = d.type->create_column(); |
1280 | 94.1k | if (is_reserve) { |
1281 | 2 | column->reserve(size); |
1282 | 94.1k | } else { |
1283 | 94.1k | column->insert_many_defaults(size); |
1284 | 94.1k | } |
1285 | 94.1k | temp_block->insert({std::move(column), d.type, d.name}); |
1286 | 94.1k | } |
1287 | 48.0k | return temp_block; |
1288 | 48.0k | } |
1289 | | |
1290 | 96.1k | size_t MutableBlock::allocated_bytes() const { |
1291 | 96.1k | size_t res = 0; |
1292 | 188k | for (const auto& col : _columns) { |
1293 | 188k | if (col) { |
1294 | 188k | res += col->allocated_bytes(); |
1295 | 188k | } |
1296 | 188k | } |
1297 | | |
1298 | 96.1k | return res; |
1299 | 96.1k | } |
1300 | | |
1301 | 1 | void MutableBlock::clear_column_data() noexcept { |
1302 | 1 | SCOPED_SKIP_MEMORY_CHECK(); |
1303 | 1 | for (auto& col : _columns) { |
1304 | 1 | if (col) { |
1305 | 1 | col->clear(); |
1306 | 1 | } |
1307 | 1 | } |
1308 | 1 | } |
1309 | | |
1310 | 6 | std::string MutableBlock::dump_names() const { |
1311 | 6 | std::string out; |
1312 | 18 | for (auto it = _names.begin(); it != _names.end(); ++it) { |
1313 | 12 | if (it != _names.begin()) { |
1314 | 6 | out += ", "; |
1315 | 6 | } |
1316 | 12 | out += *it; |
1317 | 12 | } |
1318 | 6 | return out; |
1319 | 6 | } |
1320 | | } // namespace doris |