Coverage Report

Created: 2026-07-13 20:12

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/format/table/paimon_reader.cpp
Line
Count
Source
1
// Licensed to the Apache Software Foundation (ASF) under one
2
// or more contributor license agreements.  See the NOTICE file
3
// distributed with this work for additional information
4
// regarding copyright ownership.  The ASF licenses this file
5
// to you under the Apache License, Version 2.0 (the
6
// "License"); you may not use this file except in compliance
7
// with the License.  You may obtain a copy of the License at
8
//
9
//   http://www.apache.org/licenses/LICENSE-2.0
10
//
11
// Unless required by applicable law or agreed to in writing,
12
// software distributed under the License is distributed on an
13
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
// KIND, either express or implied.  See the License for the
15
// specific language governing permissions and limitations
16
// under the License.
17
18
#include "format/table/paimon_reader.h"
19
20
#include <fmt/format.h>
21
22
#include <cstring>
23
#include <memory>
24
#include <utility>
25
#include <vector>
26
27
#include "common/status.h"
28
#include "exec/common/endian.h"
29
#include "format/table/deletion_vector_reader.h"
30
#include "runtime/runtime_state.h"
31
32
namespace doris {
33
34
namespace {
35
36
constexpr static char PAIMON_BITMAP_MAGIC[] = {'\x5E', '\x43', '\xF2', '\xD0'};
37
38
} // namespace
39
40
21.6k
std::string build_paimon_deletion_vector_cache_key(const TPaimonDeletionFileDesc& deletion_file) {
41
21.6k
    return fmt::format("paimon_dv_{}#{}#{}", deletion_file.path, deletion_file.offset,
42
21.6k
                       deletion_file.length);
43
21.6k
}
44
45
Status decode_paimon_deletion_vector_buffer(const char* buf, size_t buffer_size,
46
239
                                            DeletionVector* deletion_vector) {
47
239
    if (deletion_vector == nullptr) {
48
0
        return Status::InvalidArgument("deletion_vector must not be null");
49
0
    }
50
239
    if (buffer_size < 8) [[unlikely]] {
51
2
        return Status::DataQualityError("Deletion vector file size too small: {}", buffer_size);
52
2
    }
53
54
237
    const uint32_t actual_length = BigEndian::Load32(buf);
55
237
    if (actual_length + 4 != buffer_size) [[unlikely]] {
56
2
        return Status::RuntimeError(
57
2
                "DeletionVector deserialize error: length not match, "
58
2
                "actual length: {}, expect length: {}",
59
2
                actual_length, buffer_size - 4);
60
2
    }
61
62
235
    if (memcmp(buf + sizeof(actual_length), PAIMON_BITMAP_MAGIC, 4) != 0) [[unlikely]] {
63
2
        return Status::RuntimeError("DeletionVector deserialize error: invalid magic number {}",
64
2
                                    BigEndian::Load32(buf + sizeof(actual_length)));
65
2
    }
66
67
233
    roaring::Roaring roaring_bitmap;
68
233
    try {
69
233
        roaring_bitmap = roaring::Roaring::readSafe(buf + 8, buffer_size - 8);
70
233
    } catch (const std::runtime_error& e) {
71
2
        return Status::RuntimeError(
72
2
                "DeletionVector deserialize error: failed to deserialize roaring bitmap, {}",
73
2
                e.what());
74
2
    }
75
76
231
    *deletion_vector |= DeletionVector(std::move(roaring_bitmap));
77
231
    return Status::OK();
78
233
}
79
80
namespace {
81
82
template <typename Profile>
83
void init_deletion_vector_cache_profile(RuntimeProfile* profile, const char* parent,
84
57.6k
                                        Profile* counters) {
85
57.6k
    counters->decoded_cache_hit_count =
86
57.6k
            ADD_CHILD_COUNTER(profile, "DeletionVectorDecodedCacheHitCount", TUnit::UNIT, parent);
87
57.6k
    counters->decoded_cache_miss_count =
88
57.6k
            ADD_CHILD_COUNTER(profile, "DeletionVectorDecodedCacheMissCount", TUnit::UNIT, parent);
89
57.6k
    counters->file_cache_hit_count =
90
57.6k
            ADD_CHILD_COUNTER(profile, "DeletionVectorFileCacheHitCount", TUnit::UNIT, parent);
91
57.6k
    counters->file_cache_miss_count =
92
57.6k
            ADD_CHILD_COUNTER(profile, "DeletionVectorFileCacheMissCount", TUnit::UNIT, parent);
93
57.6k
    counters->file_cache_peer_read_count =
94
57.6k
            ADD_CHILD_COUNTER(profile, "DeletionVectorFileCachePeerReadCount", TUnit::UNIT, parent);
95
57.6k
}
paimon_reader.cpp:_ZN5doris12_GLOBAL__N_134init_deletion_vector_cache_profileINS_15PaimonOrcReader13PaimonProfileEEEvPNS_14RuntimeProfileEPKcPT_
Line
Count
Source
84
7.69k
                                        Profile* counters) {
85
7.69k
    counters->decoded_cache_hit_count =
86
7.69k
            ADD_CHILD_COUNTER(profile, "DeletionVectorDecodedCacheHitCount", TUnit::UNIT, parent);
87
7.69k
    counters->decoded_cache_miss_count =
88
7.69k
            ADD_CHILD_COUNTER(profile, "DeletionVectorDecodedCacheMissCount", TUnit::UNIT, parent);
89
7.69k
    counters->file_cache_hit_count =
90
7.69k
            ADD_CHILD_COUNTER(profile, "DeletionVectorFileCacheHitCount", TUnit::UNIT, parent);
91
7.69k
    counters->file_cache_miss_count =
92
7.69k
            ADD_CHILD_COUNTER(profile, "DeletionVectorFileCacheMissCount", TUnit::UNIT, parent);
93
7.69k
    counters->file_cache_peer_read_count =
94
7.69k
            ADD_CHILD_COUNTER(profile, "DeletionVectorFileCachePeerReadCount", TUnit::UNIT, parent);
95
7.69k
}
paimon_reader.cpp:_ZN5doris12_GLOBAL__N_134init_deletion_vector_cache_profileINS_19PaimonParquetReader13PaimonProfileEEEvPNS_14RuntimeProfileEPKcPT_
Line
Count
Source
84
49.9k
                                        Profile* counters) {
85
49.9k
    counters->decoded_cache_hit_count =
86
49.9k
            ADD_CHILD_COUNTER(profile, "DeletionVectorDecodedCacheHitCount", TUnit::UNIT, parent);
87
49.9k
    counters->decoded_cache_miss_count =
88
49.9k
            ADD_CHILD_COUNTER(profile, "DeletionVectorDecodedCacheMissCount", TUnit::UNIT, parent);
89
49.9k
    counters->file_cache_hit_count =
90
49.9k
            ADD_CHILD_COUNTER(profile, "DeletionVectorFileCacheHitCount", TUnit::UNIT, parent);
91
49.9k
    counters->file_cache_miss_count =
92
49.9k
            ADD_CHILD_COUNTER(profile, "DeletionVectorFileCacheMissCount", TUnit::UNIT, parent);
93
49.9k
    counters->file_cache_peer_read_count =
94
49.9k
            ADD_CHILD_COUNTER(profile, "DeletionVectorFileCachePeerReadCount", TUnit::UNIT, parent);
95
49.9k
}
96
97
template <typename Profile>
98
void update_deletion_vector_file_cache_profile(const DeletionVectorReader& reader,
99
228
                                               Profile* counters) {
100
228
    const auto& stats = reader.file_cache_statistics();
101
228
    COUNTER_UPDATE(counters->file_cache_hit_count, stats.num_local_io_total);
102
228
    COUNTER_UPDATE(counters->file_cache_miss_count, stats.num_remote_io_total);
103
228
    COUNTER_UPDATE(counters->file_cache_peer_read_count, stats.num_peer_io_total);
104
228
}
paimon_reader.cpp:_ZN5doris12_GLOBAL__N_141update_deletion_vector_file_cache_profileINS_15PaimonOrcReader13PaimonProfileEEEvRKNS_20DeletionVectorReaderEPT_
Line
Count
Source
99
96
                                               Profile* counters) {
100
96
    const auto& stats = reader.file_cache_statistics();
101
96
    COUNTER_UPDATE(counters->file_cache_hit_count, stats.num_local_io_total);
102
96
    COUNTER_UPDATE(counters->file_cache_miss_count, stats.num_remote_io_total);
103
96
    COUNTER_UPDATE(counters->file_cache_peer_read_count, stats.num_peer_io_total);
104
96
}
paimon_reader.cpp:_ZN5doris12_GLOBAL__N_141update_deletion_vector_file_cache_profileINS_19PaimonParquetReader13PaimonProfileEEEvRKNS_20DeletionVectorReaderEPT_
Line
Count
Source
99
132
                                               Profile* counters) {
100
132
    const auto& stats = reader.file_cache_statistics();
101
132
    COUNTER_UPDATE(counters->file_cache_hit_count, stats.num_local_io_total);
102
132
    COUNTER_UPDATE(counters->file_cache_miss_count, stats.num_remote_io_total);
103
132
    COUNTER_UPDATE(counters->file_cache_peer_read_count, stats.num_peer_io_total);
104
132
}
105
106
} // namespace
107
108
// ============================================================================
109
// PaimonOrcReader
110
// ============================================================================
111
7.67k
void PaimonOrcReader::_init_paimon_profile() {
112
7.67k
    static const char* paimon_profile = "PaimonProfile";
113
7.67k
    ADD_TIMER(get_profile(), paimon_profile);
114
7.67k
    _paimon_profile.num_delete_rows =
115
7.67k
            ADD_CHILD_COUNTER(get_profile(), "NumDeleteRows", TUnit::UNIT, paimon_profile);
116
7.67k
    _paimon_profile.delete_files_read_time =
117
7.67k
            ADD_CHILD_TIMER(get_profile(), "DeleteFileReadTime", paimon_profile);
118
7.67k
    _paimon_profile.parse_deletion_vector_time =
119
7.67k
            ADD_CHILD_TIMER(get_profile(), "ParseDeletionVectorTime", paimon_profile);
120
7.67k
    init_deletion_vector_cache_profile(get_profile(), paimon_profile, &_paimon_profile);
121
7.67k
}
122
123
7.68k
Status PaimonOrcReader::on_before_init_reader(ReaderInitContext* ctx) {
124
7.68k
    _column_descs = ctx->column_descs;
125
7.68k
    _fill_col_name_to_block_idx = ctx->col_name_to_block_idx;
126
7.68k
    RETURN_IF_ERROR(_extract_partition_values(*ctx->range, ctx->tuple_descriptor,
127
7.68k
                                              _fill_partition_values,
128
7.68k
                                              &_fill_partition_value_is_null));
129
7.68k
    const orc::Type* orc_type_ptr = nullptr;
130
7.68k
    RETURN_IF_ERROR(get_file_type(&orc_type_ptr));
131
132
7.68k
    RETURN_IF_ERROR(gen_table_info_node_by_field_id(
133
7.68k
            get_scan_params(), get_scan_range().table_format_params.paimon_params.schema_id,
134
7.68k
            get_tuple_descriptor(), orc_type_ptr));
135
7.68k
    ctx->table_info_node = table_info_node_ptr;
136
137
20.2k
    for (const auto& desc : *ctx->column_descs) {
138
20.2k
        if (desc.category == ColumnCategory::REGULAR ||
139
20.2k
            desc.category == ColumnCategory::GENERATED) {
140
20.0k
            ctx->column_names.push_back(desc.name);
141
20.0k
        }
142
20.2k
    }
143
7.68k
    return Status::OK();
144
7.68k
}
145
146
7.69k
Status PaimonOrcReader::on_after_init_reader(ReaderInitContext* /*ctx*/) {
147
7.69k
    return _init_deletion_vector();
148
7.69k
}
149
150
7.69k
Status PaimonOrcReader::_init_deletion_vector() {
151
7.69k
    const auto& table_desc = get_scan_range().table_format_params.paimon_params;
152
7.69k
    if (!table_desc.__isset.deletion_file) {
153
2.09k
        return Status::OK();
154
2.09k
    }
155
156
    // Cannot do count push down if there are delete files
157
5.60k
    if (!get_scan_range().table_format_params.paimon_params.__isset.row_count) {
158
5.60k
        set_push_down_agg_type(TPushAggOp::NONE);
159
5.60k
    }
160
5.60k
    const auto& deletion_file = table_desc.deletion_file;
161
162
5.60k
    Status create_status = Status::OK();
163
164
5.60k
    SCOPED_TIMER(_paimon_profile.delete_files_read_time);
165
5.60k
    bool decoded_cache_hit = false;
166
5.60k
    _deletion_vector = _kv_cache->get<DeletionVector>(
167
5.60k
            build_paimon_deletion_vector_cache_key(deletion_file),
168
5.60k
            [&]() -> DeletionVector* {
169
97
                auto deletion_vector = std::make_unique<DeletionVector>();
170
171
97
                TFileRangeDesc delete_range;
172
97
                delete_range.__set_fs_name(get_scan_range().fs_name);
173
97
                delete_range.path = deletion_file.path;
174
97
                delete_range.start_offset = deletion_file.offset;
175
97
                delete_range.size = deletion_file.length + 4;
176
97
                delete_range.file_size = -1;
177
178
97
                DeletionVectorReader dv_reader(get_state(), get_profile(), get_scan_params(),
179
97
                                               delete_range, get_io_ctx());
180
97
                create_status = dv_reader.open();
181
97
                if (!create_status.ok()) [[unlikely]] {
182
1
                    return nullptr;
183
1
                }
184
185
96
                size_t bytes_read = deletion_file.length + 4;
186
96
                std::vector<char> buffer(bytes_read);
187
96
                create_status =
188
96
                        dv_reader.read_at(deletion_file.offset, {buffer.data(), bytes_read});
189
96
                update_deletion_vector_file_cache_profile(dv_reader, &_paimon_profile);
190
96
                if (!create_status.ok()) [[unlikely]] {
191
0
                    return nullptr;
192
0
                }
193
194
96
                SCOPED_TIMER(_paimon_profile.parse_deletion_vector_time);
195
96
                create_status = decode_paimon_deletion_vector_buffer(buffer.data(), bytes_read,
196
96
                                                                     deletion_vector.get());
197
96
                if (!create_status.ok()) [[unlikely]] {
198
0
                    return nullptr;
199
0
                }
200
96
                COUNTER_UPDATE(_paimon_profile.num_delete_rows, deletion_vector->cardinality());
201
96
                return deletion_vector.release();
202
96
            },
203
5.60k
            &decoded_cache_hit);
204
5.60k
    RETURN_IF_ERROR(create_status);
205
5.60k
    COUNTER_UPDATE(decoded_cache_hit ? _paimon_profile.decoded_cache_hit_count
206
5.60k
                                     : _paimon_profile.decoded_cache_miss_count,
207
5.60k
                   1);
208
5.60k
    if (!_deletion_vector->isEmpty()) [[likely]] {
209
5.60k
        set_deletion_vector(_deletion_vector);
210
5.60k
    }
211
5.60k
    return Status::OK();
212
5.60k
}
213
214
// ============================================================================
215
// PaimonParquetReader
216
// ============================================================================
217
50.0k
void PaimonParquetReader::_init_paimon_profile() {
218
50.0k
    static const char* paimon_profile = "PaimonProfile";
219
50.0k
    ADD_TIMER(get_profile(), paimon_profile);
220
50.0k
    _paimon_profile.num_delete_rows =
221
50.0k
            ADD_CHILD_COUNTER(get_profile(), "NumDeleteRows", TUnit::UNIT, paimon_profile);
222
50.0k
    _paimon_profile.delete_files_read_time =
223
50.0k
            ADD_CHILD_TIMER(get_profile(), "DeleteFileReadTime", paimon_profile);
224
50.0k
    _paimon_profile.parse_deletion_vector_time =
225
50.0k
            ADD_CHILD_TIMER(get_profile(), "ParseDeletionVectorTime", paimon_profile);
226
50.0k
    init_deletion_vector_cache_profile(get_profile(), paimon_profile, &_paimon_profile);
227
50.0k
}
228
229
50.1k
Status PaimonParquetReader::on_before_init_reader(ReaderInitContext* ctx) {
230
50.1k
    _column_descs = ctx->column_descs;
231
50.1k
    _fill_col_name_to_block_idx = ctx->col_name_to_block_idx;
232
50.1k
    RETURN_IF_ERROR(_extract_partition_values(*ctx->range, ctx->tuple_descriptor,
233
50.1k
                                              _fill_partition_values,
234
50.1k
                                              &_fill_partition_value_is_null));
235
50.1k
    const FieldDescriptor* field_desc = nullptr;
236
50.1k
    RETURN_IF_ERROR(get_file_metadata_schema(&field_desc));
237
50.1k
    DCHECK(field_desc != nullptr);
238
239
50.1k
    RETURN_IF_ERROR(gen_table_info_node_by_field_id(
240
50.1k
            get_scan_params(), get_scan_range().table_format_params.paimon_params.schema_id,
241
50.1k
            get_tuple_descriptor(), *field_desc));
242
50.1k
    ctx->table_info_node = table_info_node_ptr;
243
244
98.9k
    for (const auto& desc : *ctx->column_descs) {
245
98.9k
        if (desc.category == ColumnCategory::REGULAR ||
246
98.9k
            desc.category == ColumnCategory::GENERATED) {
247
97.6k
            ctx->column_names.push_back(desc.name);
248
97.6k
        }
249
98.9k
    }
250
50.1k
    return Status::OK();
251
50.1k
}
252
253
50.0k
Status PaimonParquetReader::on_after_init_reader(ReaderInitContext* /*ctx*/) {
254
50.0k
    return _init_deletion_vector();
255
50.0k
}
256
257
49.9k
Status PaimonParquetReader::_init_deletion_vector() {
258
49.9k
    const auto& table_desc = get_scan_range().table_format_params.paimon_params;
259
49.9k
    if (!table_desc.__isset.deletion_file) {
260
34.0k
        return Status::OK();
261
34.0k
    }
262
263
16.0k
    if (!get_scan_range().table_format_params.paimon_params.__isset.row_count) {
264
16.0k
        set_push_down_agg_type(TPushAggOp::NONE);
265
16.0k
    }
266
15.9k
    const auto& deletion_file = table_desc.deletion_file;
267
268
15.9k
    Status create_status = Status::OK();
269
270
15.9k
    SCOPED_TIMER(_paimon_profile.delete_files_read_time);
271
15.9k
    bool decoded_cache_hit = false;
272
15.9k
    _deletion_vector = _kv_cache->get<DeletionVector>(
273
15.9k
            build_paimon_deletion_vector_cache_key(deletion_file),
274
15.9k
            [&]() -> DeletionVector* {
275
133
                auto deletion_vector = std::make_unique<DeletionVector>();
276
277
133
                TFileRangeDesc delete_range;
278
133
                delete_range.__set_fs_name(get_scan_range().fs_name);
279
133
                delete_range.path = deletion_file.path;
280
133
                delete_range.start_offset = deletion_file.offset;
281
133
                delete_range.size = deletion_file.length + 4;
282
133
                delete_range.file_size = -1;
283
284
133
                DeletionVectorReader dv_reader(get_state(), get_profile(), get_scan_params(),
285
133
                                               delete_range, get_io_ctx());
286
133
                create_status = dv_reader.open();
287
133
                if (!create_status.ok()) [[unlikely]] {
288
1
                    return nullptr;
289
1
                }
290
291
132
                size_t bytes_read = deletion_file.length + 4;
292
132
                std::vector<char> buffer(bytes_read);
293
132
                create_status =
294
132
                        dv_reader.read_at(deletion_file.offset, {buffer.data(), bytes_read});
295
132
                update_deletion_vector_file_cache_profile(dv_reader, &_paimon_profile);
296
132
                if (!create_status.ok()) [[unlikely]] {
297
0
                    return nullptr;
298
0
                }
299
300
132
                SCOPED_TIMER(_paimon_profile.parse_deletion_vector_time);
301
132
                create_status = decode_paimon_deletion_vector_buffer(buffer.data(), bytes_read,
302
132
                                                                     deletion_vector.get());
303
132
                if (!create_status.ok()) [[unlikely]] {
304
0
                    return nullptr;
305
0
                }
306
132
                COUNTER_UPDATE(_paimon_profile.num_delete_rows, deletion_vector->cardinality());
307
132
                return deletion_vector.release();
308
132
            },
309
15.9k
            &decoded_cache_hit);
310
15.9k
    RETURN_IF_ERROR(create_status);
311
15.9k
    COUNTER_UPDATE(decoded_cache_hit ? _paimon_profile.decoded_cache_hit_count
312
15.9k
                                     : _paimon_profile.decoded_cache_miss_count,
313
15.9k
                   1);
314
16.0k
    if (!_deletion_vector->isEmpty()) [[likely]] {
315
16.0k
        ParquetReader::set_deletion_vector(_deletion_vector);
316
16.0k
    }
317
15.9k
    return Status::OK();
318
15.9k
}
319
320
} // namespace doris