Coverage Report

Created: 2026-08-06 18:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
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
155k
bool is_recursively_exclusive(const IColumn& column) {
88
155k
    if (!column.is_exclusive()) {
89
1.04k
        return false;
90
1.04k
    }
91
92
154k
    bool exclusive = true;
93
154k
    IColumn::ColumnCallback callback = [&](const IColumn& subcolumn) {
94
66.2k
        if (!exclusive) {
95
4
            return;
96
4
        }
97
66.2k
        exclusive = is_recursively_exclusive(subcolumn);
98
66.2k
    };
99
154k
    column.for_each_subcolumn(callback);
100
154k
    return exclusive;
101
155k
}
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
997
    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
177k
    for (auto& entry : data) {
224
177k
        entry.name.clear();
225
177k
    }
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.07M
Status Block::check_type_and_column() const {
312
3.07M
#ifndef NDEBUG
313
3.07M
    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.07M
#endif
340
3.07M
    return Status::OK();
341
3.07M
}
342
343
1.58M
Status Block::check_column_and_type_not_null() const {
344
1.77M
    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.58M
    return Status::OK();
356
1.58M
}
357
358
1.58M
Status Block::check_no_column_string64() const {
359
1.77M
    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.58M
    return Status::OK();
370
1.58M
}
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.45M
    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
911
MutableColumns Block::ScopedMutableColumns::release() {
696
911
    DCHECK(_block != nullptr);
697
911
    _block = nullptr;
698
911
    return std::move(_columns);
699
911
}
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
912
ScopedMutableBlock::ScopedMutableBlock(Block* block) {
754
912
    DCHECK(block != nullptr);
755
912
    DataTypes data_types = block->get_data_types();
756
912
    std::vector<std::string> names = block->get_names();
757
912
    auto columns_guard = block->mutate_columns_scoped();
758
912
    _mutable_block.data_types() = std::move(data_types);
759
912
    _mutable_block.get_names() = std::move(names);
760
912
    _mutable_block.set_mutable_columns(columns_guard.release());
761
912
    _block = block;
762
912
}
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.0k
const ColumnsWithTypeAndName& Block::get_columns_with_type_and_name() const {
803
56.0k
    return data;
804
56.0k
}
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
// Both clear paths must preserve shared children even when a composite column is top-level exclusive.
833
1.52M
void Block::clear_column_data(int64_t column_size) {
834
1.52M
    SCOPED_SKIP_MEMORY_CHECK();
835
    // data.size() greater than column_size, means here have some
836
    // function exec result in block, need erase it here
837
1.52M
    if (column_size != -1 and data.size() > column_size) {
838
2.21k
        for (int64_t i = data.size() - 1; i >= column_size; --i) {
839
1.10k
            erase(i);
840
1.10k
        }
841
1.10k
    }
842
1.52M
    for (auto& d : data) {
843
75.7k
        if (d.column) {
844
75.7k
            if (is_recursively_exclusive(*d.column)) {
845
75.4k
                d.column->assert_mutable()->clear();
846
75.4k
            } else {
847
321
                d.column = d.column->clone_empty();
848
321
            }
849
75.7k
        }
850
75.7k
    }
851
1.52M
}
852
853
36
void Block::clear_column_data(const std::vector<uint32_t>& columns_to_clear) {
854
36
    SCOPED_SKIP_MEMORY_CHECK();
855
47
    for (auto col : columns_to_clear) {
856
47
        DCHECK_LT(col, data.size());
857
47
        auto& column = data[col].column;
858
47
        if (column) {
859
47
            if (is_recursively_exclusive(*column)) {
860
44
                column->assert_mutable()->clear();
861
44
            } else {
862
3
                column = column->clone_empty();
863
3
            }
864
47
        }
865
47
    }
866
36
}
867
868
void Block::clear_column_mem_not_keep(const std::vector<bool>& column_keep_flags,
869
48.0k
                                      bool need_keep_first) {
870
48.0k
    if (data.size() >= column_keep_flags.size()) {
871
48.0k
        auto origin_rows = rows();
872
142k
        for (size_t i = 0; i < column_keep_flags.size(); ++i) {
873
94.1k
            if (!column_keep_flags[i]) {
874
36.8k
                data[i].column = data[i].column->clone_empty();
875
36.8k
            }
876
94.1k
        }
877
878
48.0k
        if (need_keep_first && !column_keep_flags[0]) {
879
1
            auto first_column = data[0].column->clone_empty();
880
1
            first_column->resize(origin_rows);
881
1
            data[0].column = std::move(first_column);
882
1
        }
883
48.0k
    }
884
48.0k
}
885
886
1.33k
void Block::swap(Block& other) noexcept {
887
1.33k
    SCOPED_SKIP_MEMORY_CHECK();
888
1.33k
    data.swap(other.data);
889
1.33k
}
890
891
1.77k
void Block::swap(Block&& other) noexcept {
892
1.77k
    SCOPED_SKIP_MEMORY_CHECK();
893
1.77k
    data = std::move(other.data);
894
1.77k
}
895
896
3
void Block::shuffle_columns(const std::vector<int>& result_column_ids) {
897
3
    Container tmp_data;
898
3
    tmp_data.reserve(result_column_ids.size());
899
5
    for (const int result_column_id : result_column_ids) {
900
5
        tmp_data.push_back(data[result_column_id]);
901
5
    }
902
3
    data = std::move(tmp_data);
903
3
}
904
905
2
void Block::update_hash(SipHash& hash) const {
906
8
    for (size_t row_no = 0, num_rows = rows(); row_no < num_rows; ++row_no) {
907
12
        for (const auto& col : data) {
908
12
            col.column->update_hash_with_value(row_no, hash);
909
12
        }
910
6
    }
911
2
}
912
913
void Block::filter_block_internal(Block* block, const std::vector<uint32_t>& columns_to_filter,
914
2.61k
                                  const IColumn::Filter& filter) {
915
2.61k
    size_t count = filter.size() - simd::count_zero_num((int8_t*)filter.data(), filter.size());
916
5.73k
    for (const auto& col : columns_to_filter) {
917
5.73k
        auto& column = block->get_by_position(col).column;
918
5.73k
        if (column->size() == count) {
919
5.39k
            continue;
920
5.39k
        }
921
337
        if (count == 0) {
922
21
            if (column->is_exclusive()) {
923
19
                column->assert_mutable()->clear();
924
19
            } else {
925
2
                column = column->clone_empty();
926
2
            }
927
21
            continue;
928
21
        }
929
316
        if (column->is_exclusive()) {
930
            // COW: safe to mutate in-place since we have exclusive ownership
931
220
            const auto result_size = column->assert_mutable()->filter(filter);
932
220
            if (result_size != count) [[unlikely]] {
933
0
                throw Exception(ErrorCode::INTERNAL_ERROR,
934
0
                                "result_size not equal with filter_size, result_size={}, "
935
0
                                "filter_size={}",
936
0
                                result_size, count);
937
0
            }
938
220
        } else {
939
            // COW: must create a copy since column is shared
940
96
            column = column->filter(filter, count);
941
96
        }
942
316
    }
943
2.61k
}
944
945
void Block::filter_block_internal(Block* block, const IColumn::Filter& filter,
946
28
                                  uint32_t column_to_keep) {
947
28
    std::vector<uint32_t> columns_to_filter;
948
28
    columns_to_filter.resize(column_to_keep);
949
88
    for (uint32_t i = 0; i < column_to_keep; ++i) {
950
60
        columns_to_filter[i] = i;
951
60
    }
952
28
    filter_block_internal(block, columns_to_filter, filter);
953
28
}
954
955
9
void Block::filter_block_internal(Block* block, const IColumn::Filter& filter) {
956
9
    const size_t count =
957
9
            filter.size() - simd::count_zero_num((int8_t*)filter.data(), filter.size());
958
27
    for (int i = 0; i < block->columns(); ++i) {
959
18
        auto& column = block->get_by_position(i).column;
960
18
        if (column->is_exclusive()) {
961
18
            column->assert_mutable()->filter(filter);
962
18
        } else {
963
0
            column = column->filter(filter, count);
964
0
        }
965
18
    }
966
9
}
967
968
Status Block::append_to_block_by_selector(MutableBlock* dst,
969
1
                                          const IColumn::Selector& selector) const {
970
1
    RETURN_IF_CATCH_EXCEPTION({
971
1
        DCHECK_EQ(data.size(), dst->mutable_columns().size());
972
1
        for (size_t i = 0; i < data.size(); i++) {
973
            // FIXME: this is a quickfix. we assume that only partition functions make there some
974
1
            if (!is_column_const(*data[i].column)) {
975
1
                data[i].column->append_data_by_selector(dst->mutable_columns()[i], selector);
976
1
            }
977
1
        }
978
1
    });
979
1
    return Status::OK();
980
1
}
981
982
Status Block::filter_block(Block* block, const std::vector<uint32_t>& columns_to_filter,
983
2.55k
                           size_t filter_column_id, size_t column_to_keep) {
984
2.55k
    const auto& filter_column = block->get_by_position(filter_column_id).column;
985
2.55k
    if (const auto* nullable_column = check_and_get_column<ColumnNullable>(*filter_column)) {
986
1
        const auto& nested_column = nullable_column->get_nested_column_ptr();
987
988
1
        MutableColumnPtr mutable_holder =
989
1
                nested_column->use_count() == 1
990
1
                        ? nested_column->assert_mutable()
991
1
                        : nested_column->clone_resized(nested_column->size());
992
993
1
        auto* concrete_column = assert_cast<ColumnUInt8*>(mutable_holder.get());
994
1
        const auto* __restrict null_map = nullable_column->get_null_map_data().data();
995
1
        IColumn::Filter& filter = concrete_column->get_data();
996
1
        auto* __restrict filter_data = filter.data();
997
998
1
        const size_t size = filter.size();
999
4
        for (size_t i = 0; i < size; ++i) {
1000
3
            filter_data[i] &= !null_map[i];
1001
3
        }
1002
1
        RETURN_IF_CATCH_EXCEPTION(filter_block_internal(block, columns_to_filter, filter));
1003
2.55k
    } else if (const auto* const_column = check_and_get_column<ColumnConst>(*filter_column)) {
1004
2
        bool ret = const_column->get_bool(0);
1005
2
        if (!ret) {
1006
2
            for (const auto& col : columns_to_filter) {
1007
2
                auto& column = block->get_by_position(col).column;
1008
2
                if (column->is_exclusive()) {
1009
2
                    column->assert_mutable()->clear();
1010
2
                } else {
1011
0
                    column = column->clone_empty();
1012
0
                }
1013
2
            }
1014
1
        }
1015
2.54k
    } else {
1016
2.54k
        const IColumn::Filter& filter =
1017
2.54k
                assert_cast<const doris::ColumnUInt8&>(*filter_column).get_data();
1018
2.54k
        RETURN_IF_CATCH_EXCEPTION(filter_block_internal(block, columns_to_filter, filter));
1019
2.54k
    }
1020
1021
2.55k
    erase_useless_column(block, column_to_keep);
1022
2.55k
    return Status::OK();
1023
2.55k
}
1024
1025
2.54k
Status Block::filter_block(Block* block, size_t filter_column_id, size_t column_to_keep) {
1026
2.54k
    std::vector<uint32_t> columns_to_filter;
1027
2.54k
    columns_to_filter.resize(column_to_keep);
1028
8.13k
    for (uint32_t i = 0; i < column_to_keep; ++i) {
1029
5.58k
        columns_to_filter[i] = i;
1030
5.58k
    }
1031
2.54k
    return filter_block(block, columns_to_filter, filter_column_id, column_to_keep);
1032
2.54k
}
1033
1034
Status Block::serialize(int be_exec_version, PBlock* pblock,
1035
                        /*std::string* compressed_buffer,*/ size_t* uncompressed_bytes,
1036
                        size_t* compressed_bytes, int64_t* compress_time,
1037
                        segment_v2::CompressionTypePB compression_type,
1038
2.73k
                        bool allow_transfer_large_data) const {
1039
2.73k
    RETURN_IF_ERROR(BeExecVersionManager::check_be_exec_version(be_exec_version));
1040
2.73k
    pblock->set_be_exec_version(be_exec_version);
1041
1042
    // calc uncompressed size for allocation
1043
2.73k
    size_t content_uncompressed_size = 0;
1044
2.73k
    RETURN_IF_CATCH_EXCEPTION({
1045
2.73k
        for (const auto& c : *this) {
1046
2.73k
            PColumnMeta* pcm = pblock->add_column_metas();
1047
2.73k
            c.to_pb_column_meta(pcm);
1048
2.73k
            DCHECK(pcm->type() != PGenericType::UNKNOWN) << " forget to set pb type";
1049
            // get serialized size
1050
2.73k
            content_uncompressed_size += c.type->get_uncompressed_serialized_bytes(
1051
2.73k
                    *(c.column), pblock->be_exec_version());
1052
2.73k
        }
1053
2.73k
    });
1054
1055
    // serialize data values
1056
    // when data type is HLL, content_uncompressed_size maybe larger than real size.
1057
2.73k
    std::string column_values;
1058
2.73k
    try {
1059
        // TODO: After support c++23, we should use resize_and_overwrite to replace resize
1060
2.73k
        column_values.resize(content_uncompressed_size);
1061
2.73k
    } catch (...) {
1062
0
        std::string msg = fmt::format("Try to alloc {} bytes for pblock column values failed.",
1063
0
                                      content_uncompressed_size);
1064
0
        LOG(WARNING) << msg;
1065
0
        return Status::BufferAllocFailed(msg);
1066
0
    }
1067
2.74k
    char* buf = column_values.data();
1068
1069
2.74k
    RETURN_IF_CATCH_EXCEPTION({
1070
2.74k
        for (const auto& c : *this) {
1071
2.74k
            buf = c.type->serialize(*(c.column), buf, pblock->be_exec_version());
1072
2.74k
        }
1073
2.74k
    });
1074
2.73k
    *uncompressed_bytes = content_uncompressed_size;
1075
2.73k
    const size_t serialize_bytes = buf - column_values.data() + STREAMVBYTE_PADDING;
1076
2.73k
    *compressed_bytes = serialize_bytes;
1077
2.73k
    column_values.resize(serialize_bytes);
1078
1079
    // compress
1080
2.73k
    if (compression_type != segment_v2::NO_COMPRESSION && content_uncompressed_size > 0) {
1081
641
        SCOPED_RAW_TIMER(compress_time);
1082
641
        pblock->set_compression_type(compression_type);
1083
641
        pblock->set_uncompressed_size(serialize_bytes);
1084
1085
641
        BlockCompressionCodec* codec;
1086
641
        RETURN_IF_ERROR(get_block_compression_codec(compression_type, &codec));
1087
1088
641
        faststring buf_compressed;
1089
641
        RETURN_IF_ERROR_OR_CATCH_EXCEPTION(
1090
641
                codec->compress(Slice(column_values.data(), serialize_bytes), &buf_compressed));
1091
641
        size_t compressed_size = buf_compressed.size();
1092
641
        if (LIKELY(compressed_size < serialize_bytes)) {
1093
            // TODO: rethink the logic here may copy again ?
1094
641
            pblock->set_column_values(buf_compressed.data(), buf_compressed.size());
1095
641
            pblock->set_compressed(true);
1096
641
            *compressed_bytes = compressed_size;
1097
641
        } else {
1098
0
            pblock->set_column_values(std::move(column_values));
1099
0
        }
1100
1101
641
        VLOG_ROW << "uncompressed size: " << content_uncompressed_size
1102
0
                 << ", compressed size: " << compressed_size;
1103
2.09k
    } else {
1104
2.09k
        pblock->set_column_values(std::move(column_values));
1105
2.09k
    }
1106
2.74k
    if (!allow_transfer_large_data && *compressed_bytes >= std::numeric_limits<int32_t>::max()) {
1107
0
        return Status::InternalError("The block is large than 2GB({}), can not send by Protobuf.",
1108
0
                                     *compressed_bytes);
1109
0
    }
1110
2.73k
    return Status::OK();
1111
2.73k
}
1112
1113
240k
size_t MutableBlock::rows() const {
1114
240k
    for (const auto& column : _columns) {
1115
144k
        if (column) {
1116
144k
            return column->size();
1117
144k
        }
1118
144k
    }
1119
1120
96.0k
    return 0;
1121
240k
}
1122
1123
0
void MutableBlock::swap(MutableBlock& another) noexcept {
1124
0
    SCOPED_SKIP_MEMORY_CHECK();
1125
0
    _columns.swap(another._columns);
1126
0
    _data_types.swap(another._data_types);
1127
0
    _names.swap(another._names);
1128
0
}
1129
1130
16
void MutableBlock::add_row(const Block* block, int row) {
1131
16
    const auto& block_data = block->get_columns_with_type_and_name();
1132
448
    for (size_t i = 0; i < _columns.size(); ++i) {
1133
432
        _columns[i]->insert_from(*block_data[i].column.get(), row);
1134
432
    }
1135
16
}
1136
1137
Status MutableBlock::add_rows(const Block* block, const uint32_t* row_begin,
1138
165
                              const uint32_t* row_end, const std::vector<int>* column_offset) {
1139
165
    RETURN_IF_CATCH_EXCEPTION({
1140
165
        DCHECK_LE(columns(), block->columns());
1141
165
        if (column_offset != nullptr) {
1142
165
            DCHECK_EQ(columns(), column_offset->size());
1143
165
        }
1144
165
        const auto& block_data = block->get_columns_with_type_and_name();
1145
165
        for (size_t i = 0; i < _columns.size(); ++i) {
1146
165
            const auto& src_col = column_offset ? block_data[(*column_offset)[i]] : block_data[i];
1147
165
            DCHECK_EQ(_data_types[i]->get_name(), src_col.type->get_name());
1148
165
            auto& dst = _columns[i];
1149
165
            const auto& src = *src_col.column.get();
1150
165
            DCHECK_GE(src.size(), row_end - row_begin);
1151
165
            dst->insert_indices_from(src, row_begin, row_end);
1152
165
        }
1153
165
    });
1154
164
    return Status::OK();
1155
165
}
1156
1157
172
Status MutableBlock::add_rows(const Block* block, size_t row_begin, size_t length) {
1158
172
    RETURN_IF_CATCH_EXCEPTION({
1159
172
        DCHECK_LE(columns(), block->columns());
1160
172
        const auto& block_data = block->get_columns_with_type_and_name();
1161
172
        for (size_t i = 0; i < _columns.size(); ++i) {
1162
172
            DCHECK_EQ(_data_types[i]->get_name(), block_data[i].type->get_name());
1163
172
            auto& dst = _columns[i];
1164
172
            const auto& src = *block_data[i].column.get();
1165
172
            dst->insert_range_from(src, row_begin, length);
1166
172
        }
1167
172
    });
1168
172
    return Status::OK();
1169
172
}
1170
1171
144k
Block MutableBlock::to_block(int start_column) {
1172
144k
    return to_block(start_column, (int)_columns.size());
1173
144k
}
1174
1175
144k
Block MutableBlock::to_block(int start_column, int end_column) {
1176
144k
    ColumnsWithTypeAndName columns_with_schema;
1177
144k
    columns_with_schema.reserve(end_column - start_column);
1178
429k
    for (size_t i = start_column; i < end_column; ++i) {
1179
284k
        columns_with_schema.emplace_back(std::move(_columns[i]), _data_types[i], _names[i]);
1180
284k
    }
1181
144k
    return {columns_with_schema};
1182
144k
}
1183
1184
1
std::string MutableBlock::dump_data_json(size_t row_limit) const {
1185
1
    std::stringstream ss;
1186
1
    std::vector<std::string> headers;
1187
1188
1
    headers.reserve(columns());
1189
2
    for (size_t i = 0; i < columns(); ++i) {
1190
1
        headers.push_back(_data_types[i]->get_name());
1191
1
    }
1192
1
    size_t num_rows_to_dump = std::min(rows(), row_limit);
1193
1
    ss << "[";
1194
1195
1
    auto format_options = DataTypeSerDe::get_default_format_options();
1196
1
    auto time_zone = cctz::utc_time_zone();
1197
1
    format_options.timezone = &time_zone;
1198
1199
4
    for (size_t row_num = 0; row_num < num_rows_to_dump; ++row_num) {
1200
3
        if (row_num > 0) {
1201
2
            ss << ",";
1202
2
        }
1203
3
        ss << "{";
1204
6
        for (size_t i = 0; i < columns(); ++i) {
1205
3
            if (i > 0) {
1206
0
                ss << ",";
1207
0
            }
1208
3
            ss << "\"" << headers[i] << "\":";
1209
3
            std::string s = _data_types[i]->to_string(*_columns[i].get(), row_num, format_options);
1210
3
            ss << "\"" << s << "\"";
1211
3
        }
1212
3
        ss << "}";
1213
3
    }
1214
1
    ss << "]";
1215
1
    return ss.str();
1216
1
}
1217
1218
1
std::string MutableBlock::dump_data(size_t row_limit) const {
1219
1
    std::vector<std::string> headers;
1220
1
    std::vector<int> headers_size;
1221
2
    for (size_t i = 0; i < columns(); ++i) {
1222
1
        std::string s = _data_types[i]->get_name();
1223
1
        headers_size.push_back(s.size() > 15 ? (int)s.size() : 15);
1224
1
        headers.emplace_back(s);
1225
1
    }
1226
1227
1
    std::stringstream out;
1228
    // header upper line
1229
3
    auto line = [&]() {
1230
6
        for (size_t i = 0; i < columns(); ++i) {
1231
3
            out << std::setfill('-') << std::setw(1) << "+" << std::setw(headers_size[i]) << "-";
1232
3
        }
1233
3
        out << std::setw(1) << "+" << std::endl;
1234
3
    };
1235
1
    line();
1236
    // header text
1237
2
    for (size_t i = 0; i < columns(); ++i) {
1238
1
        out << std::setfill(' ') << std::setw(1) << "|" << std::left << std::setw(headers_size[i])
1239
1
            << headers[i];
1240
1
    }
1241
1
    out << std::setw(1) << "|" << std::endl;
1242
    // header bottom line
1243
1
    line();
1244
1
    if (rows() == 0) {
1245
0
        return out.str();
1246
0
    }
1247
1248
1
    auto format_options = DataTypeSerDe::get_default_format_options();
1249
1
    auto time_zone = cctz::utc_time_zone();
1250
1
    format_options.timezone = &time_zone;
1251
1252
    // content
1253
4
    for (size_t row_num = 0; row_num < rows() && row_num < row_limit; ++row_num) {
1254
6
        for (size_t i = 0; i < columns(); ++i) {
1255
3
            if (_columns[i].get()->empty()) {
1256
0
                out << std::setfill(' ') << std::setw(1) << "|" << std::setw(headers_size[i])
1257
0
                    << std::right;
1258
0
                continue;
1259
0
            }
1260
3
            std::string s = _data_types[i]->to_string(*_columns[i].get(), row_num, format_options);
1261
3
            if (s.length() > headers_size[i]) {
1262
0
                s = s.substr(0, headers_size[i] - 3) + "...";
1263
0
            }
1264
3
            out << std::setfill(' ') << std::setw(1) << "|" << std::setw(headers_size[i])
1265
3
                << std::right << s;
1266
3
        }
1267
3
        out << std::setw(1) << "|" << std::endl;
1268
3
    }
1269
    // bottom line
1270
1
    line();
1271
1
    if (row_limit < rows()) {
1272
0
        out << rows() << " rows in block, only show first " << row_limit << " rows." << std::endl;
1273
0
    }
1274
1
    return out.str();
1275
1
}
1276
1277
48.0k
std::unique_ptr<Block> Block::create_same_struct_block(size_t size, bool is_reserve) const {
1278
48.0k
    auto temp_block = Block::create_unique();
1279
94.1k
    for (const auto& d : data) {
1280
94.1k
        auto column = d.type->create_column();
1281
94.1k
        if (is_reserve) {
1282
0
            column->reserve(size);
1283
94.1k
        } else {
1284
94.1k
            column->insert_many_defaults(size);
1285
94.1k
        }
1286
94.1k
        temp_block->insert({std::move(column), d.type, d.name});
1287
94.1k
    }
1288
48.0k
    return temp_block;
1289
48.0k
}
1290
1291
96.1k
size_t MutableBlock::allocated_bytes() const {
1292
96.1k
    size_t res = 0;
1293
188k
    for (const auto& col : _columns) {
1294
188k
        if (col) {
1295
188k
            res += col->allocated_bytes();
1296
188k
        }
1297
188k
    }
1298
1299
96.1k
    return res;
1300
96.1k
}
1301
1302
1
void MutableBlock::clear_column_data() noexcept {
1303
1
    SCOPED_SKIP_MEMORY_CHECK();
1304
1
    for (auto& col : _columns) {
1305
1
        if (col) {
1306
1
            col->clear();
1307
1
        }
1308
1
    }
1309
1
}
1310
1311
6
std::string MutableBlock::dump_names() const {
1312
6
    std::string out;
1313
18
    for (auto it = _names.begin(); it != _names.end(); ++it) {
1314
12
        if (it != _names.begin()) {
1315
6
            out += ", ";
1316
6
        }
1317
12
        out += *it;
1318
12
    }
1319
6
    return out;
1320
6
}
1321
} // namespace doris