Coverage Report

Created: 2026-07-24 11:49

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 validate_paimon_deletion_vector_descriptor(const TPaimonDeletionFileDesc& deletion_file,
46
21.4k
                                                  size_t& bytes_read) {
47
21.4k
    if (!deletion_file.__isset.path || !deletion_file.__isset.offset ||
48
21.4k
        !deletion_file.__isset.length) {
49
1
        return Status::DataQualityError(
50
1
                "Paimon deletion file descriptor misses path/offset/length");
51
1
    }
52
21.4k
    return validate_paimon_deletion_vector_read_range(deletion_file.offset, deletion_file.length,
53
21.4k
                                                      bytes_read);
54
21.4k
}
55
56
Status decode_paimon_deletion_vector_buffer(const char* buf, size_t buffer_size,
57
244
                                            DeletionVector* deletion_vector) {
58
244
    if (deletion_vector == nullptr) {
59
0
        return Status::InvalidArgument("deletion vector output must not be null");
60
0
    }
61
244
    if (buf == nullptr) {
62
1
        return Status::DataQualityError("Paimon deletion vector blob is null");
63
1
    }
64
243
    if (buffer_size < 8) [[unlikely]] {
65
2
        return Status::DataQualityError("Deletion vector file size too small: {}", buffer_size);
66
2
    }
67
68
241
    const uint32_t actual_length = BigEndian::Load32(buf);
69
241
    if (static_cast<uint64_t>(actual_length) + 4 != buffer_size) [[unlikely]] {
70
2
        return Status::DataQualityError(
71
2
                "Paimon deletion vector length mismatch, expected: {}, actual: {}",
72
2
                static_cast<uint64_t>(actual_length) + 4, buffer_size);
73
2
    }
74
75
239
    if (memcmp(buf + sizeof(actual_length), PAIMON_BITMAP_MAGIC, 4) != 0) [[unlikely]] {
76
2
        return Status::DataQualityError(
77
2
                "Paimon deletion vector magic number mismatch, expected: {}, actual: {}",
78
2
                BigEndian::Load32(PAIMON_BITMAP_MAGIC),
79
2
                BigEndian::Load32(buf + sizeof(actual_length)));
80
2
    }
81
82
237
    roaring::Roaring roaring_bitmap;
83
237
    try {
84
237
        roaring_bitmap = roaring::Roaring::readSafe(buf + 8, buffer_size - 8);
85
237
    } catch (const std::runtime_error& e) {
86
2
        return Status::RuntimeError(
87
2
                "DeletionVector deserialize error: failed to deserialize roaring bitmap, {}",
88
2
                e.what());
89
2
    }
90
91
233
    *deletion_vector |= DeletionVector(std::move(roaring_bitmap));
92
233
    return Status::OK();
93
237
}
94
95
namespace {
96
97
template <typename Profile>
98
void init_deletion_vector_cache_profile(RuntimeProfile* profile, const char* parent,
99
55.1k
                                        Profile* counters) {
100
55.1k
    counters->decoded_cache_hit_count =
101
55.1k
            ADD_CHILD_COUNTER(profile, "DeletionVectorDecodedCacheHitCount", TUnit::UNIT, parent);
102
55.1k
    counters->decoded_cache_miss_count =
103
55.1k
            ADD_CHILD_COUNTER(profile, "DeletionVectorDecodedCacheMissCount", TUnit::UNIT, parent);
104
55.1k
    counters->file_cache_hit_count =
105
55.1k
            ADD_CHILD_COUNTER(profile, "DeletionVectorFileCacheHitCount", TUnit::UNIT, parent);
106
55.1k
    counters->file_cache_miss_count =
107
55.1k
            ADD_CHILD_COUNTER(profile, "DeletionVectorFileCacheMissCount", TUnit::UNIT, parent);
108
55.1k
    counters->file_cache_peer_read_count =
109
55.1k
            ADD_CHILD_COUNTER(profile, "DeletionVectorFileCachePeerReadCount", TUnit::UNIT, parent);
110
55.1k
}
paimon_reader.cpp:_ZN5doris12_GLOBAL__N_134init_deletion_vector_cache_profileINS_15PaimonOrcReader13PaimonProfileEEEvPNS_14RuntimeProfileEPKcPT_
Line
Count
Source
99
7.67k
                                        Profile* counters) {
100
7.67k
    counters->decoded_cache_hit_count =
101
7.67k
            ADD_CHILD_COUNTER(profile, "DeletionVectorDecodedCacheHitCount", TUnit::UNIT, parent);
102
7.67k
    counters->decoded_cache_miss_count =
103
7.67k
            ADD_CHILD_COUNTER(profile, "DeletionVectorDecodedCacheMissCount", TUnit::UNIT, parent);
104
7.67k
    counters->file_cache_hit_count =
105
7.67k
            ADD_CHILD_COUNTER(profile, "DeletionVectorFileCacheHitCount", TUnit::UNIT, parent);
106
7.67k
    counters->file_cache_miss_count =
107
7.67k
            ADD_CHILD_COUNTER(profile, "DeletionVectorFileCacheMissCount", TUnit::UNIT, parent);
108
7.67k
    counters->file_cache_peer_read_count =
109
7.67k
            ADD_CHILD_COUNTER(profile, "DeletionVectorFileCachePeerReadCount", TUnit::UNIT, parent);
110
7.67k
}
paimon_reader.cpp:_ZN5doris12_GLOBAL__N_134init_deletion_vector_cache_profileINS_19PaimonParquetReader13PaimonProfileEEEvPNS_14RuntimeProfileEPKcPT_
Line
Count
Source
99
47.4k
                                        Profile* counters) {
100
47.4k
    counters->decoded_cache_hit_count =
101
47.4k
            ADD_CHILD_COUNTER(profile, "DeletionVectorDecodedCacheHitCount", TUnit::UNIT, parent);
102
47.4k
    counters->decoded_cache_miss_count =
103
47.4k
            ADD_CHILD_COUNTER(profile, "DeletionVectorDecodedCacheMissCount", TUnit::UNIT, parent);
104
47.4k
    counters->file_cache_hit_count =
105
47.4k
            ADD_CHILD_COUNTER(profile, "DeletionVectorFileCacheHitCount", TUnit::UNIT, parent);
106
47.4k
    counters->file_cache_miss_count =
107
47.4k
            ADD_CHILD_COUNTER(profile, "DeletionVectorFileCacheMissCount", TUnit::UNIT, parent);
108
47.4k
    counters->file_cache_peer_read_count =
109
47.4k
            ADD_CHILD_COUNTER(profile, "DeletionVectorFileCachePeerReadCount", TUnit::UNIT, parent);
110
47.4k
}
111
112
template <typename Profile>
113
void update_deletion_vector_file_cache_profile(const DeletionVectorReader& reader,
114
232
                                               Profile* counters) {
115
232
    const auto& stats = reader.file_cache_statistics();
116
232
    COUNTER_UPDATE(counters->file_cache_hit_count, stats.num_local_io_total);
117
232
    COUNTER_UPDATE(counters->file_cache_miss_count, stats.num_remote_io_total);
118
232
    COUNTER_UPDATE(counters->file_cache_peer_read_count, stats.num_peer_io_total);
119
232
}
paimon_reader.cpp:_ZN5doris12_GLOBAL__N_141update_deletion_vector_file_cache_profileINS_15PaimonOrcReader13PaimonProfileEEEvRKNS_20DeletionVectorReaderEPT_
Line
Count
Source
114
96
                                               Profile* counters) {
115
96
    const auto& stats = reader.file_cache_statistics();
116
96
    COUNTER_UPDATE(counters->file_cache_hit_count, stats.num_local_io_total);
117
96
    COUNTER_UPDATE(counters->file_cache_miss_count, stats.num_remote_io_total);
118
96
    COUNTER_UPDATE(counters->file_cache_peer_read_count, stats.num_peer_io_total);
119
96
}
paimon_reader.cpp:_ZN5doris12_GLOBAL__N_141update_deletion_vector_file_cache_profileINS_19PaimonParquetReader13PaimonProfileEEEvRKNS_20DeletionVectorReaderEPT_
Line
Count
Source
114
136
                                               Profile* counters) {
115
136
    const auto& stats = reader.file_cache_statistics();
116
136
    COUNTER_UPDATE(counters->file_cache_hit_count, stats.num_local_io_total);
117
136
    COUNTER_UPDATE(counters->file_cache_miss_count, stats.num_remote_io_total);
118
136
    COUNTER_UPDATE(counters->file_cache_peer_read_count, stats.num_peer_io_total);
119
136
}
120
121
} // namespace
122
123
// ============================================================================
124
// PaimonOrcReader
125
// ============================================================================
126
7.65k
void PaimonOrcReader::_init_paimon_profile() {
127
7.65k
    static const char* paimon_profile = "PaimonProfile";
128
7.65k
    ADD_TIMER(get_profile(), paimon_profile);
129
7.65k
    _paimon_profile.num_delete_rows =
130
7.65k
            ADD_CHILD_COUNTER(get_profile(), "NumDeleteRows", TUnit::UNIT, paimon_profile);
131
7.65k
    _paimon_profile.delete_files_read_time =
132
7.65k
            ADD_CHILD_TIMER(get_profile(), "DeleteFileReadTime", paimon_profile);
133
7.65k
    _paimon_profile.parse_deletion_vector_time =
134
7.65k
            ADD_CHILD_TIMER(get_profile(), "ParseDeletionVectorTime", paimon_profile);
135
7.65k
    init_deletion_vector_cache_profile(get_profile(), paimon_profile, &_paimon_profile);
136
7.65k
}
137
138
7.63k
Status PaimonOrcReader::on_before_init_reader(ReaderInitContext* ctx) {
139
7.63k
    _column_descs = ctx->column_descs;
140
7.63k
    _fill_col_name_to_block_idx = ctx->col_name_to_block_idx;
141
7.63k
    RETURN_IF_ERROR(_extract_partition_values(*ctx->range, ctx->tuple_descriptor,
142
7.63k
                                              _fill_partition_values,
143
7.63k
                                              &_fill_partition_value_is_null));
144
7.63k
    const orc::Type* orc_type_ptr = nullptr;
145
7.63k
    RETURN_IF_ERROR(get_file_type(&orc_type_ptr));
146
147
7.63k
    RETURN_IF_ERROR(gen_table_info_node_by_field_id(
148
7.63k
            get_scan_params(), get_scan_range().table_format_params.paimon_params.schema_id,
149
7.63k
            get_tuple_descriptor(), orc_type_ptr));
150
7.63k
    ctx->table_info_node = table_info_node_ptr;
151
152
20.0k
    for (const auto& desc : *ctx->column_descs) {
153
20.0k
        if (desc.category == ColumnCategory::REGULAR ||
154
20.0k
            desc.category == ColumnCategory::GENERATED) {
155
19.8k
            ctx->column_names.push_back(desc.name);
156
19.8k
        }
157
20.0k
    }
158
7.63k
    return Status::OK();
159
7.63k
}
160
161
7.73k
Status PaimonOrcReader::on_after_init_reader(ReaderInitContext* /*ctx*/) {
162
7.73k
    return _init_deletion_vector();
163
7.73k
}
164
165
7.73k
Status PaimonOrcReader::_init_deletion_vector() {
166
7.73k
    const auto& table_desc = get_scan_range().table_format_params.paimon_params;
167
7.73k
    if (!table_desc.__isset.deletion_file) {
168
2.12k
        return Status::OK();
169
2.12k
    }
170
171
    // Cannot do count push down if there are delete files
172
5.60k
    if (!get_scan_range().table_format_params.paimon_params.__isset.row_count) {
173
5.60k
        set_push_down_agg_type(TPushAggOp::NONE);
174
5.60k
    }
175
5.60k
    const auto& deletion_file = table_desc.deletion_file;
176
5.60k
    size_t bytes_read = 0;
177
5.60k
    RETURN_IF_ERROR(validate_paimon_deletion_vector_descriptor(deletion_file, bytes_read));
178
179
5.60k
    Status create_status = Status::OK();
180
181
5.60k
    SCOPED_TIMER(_paimon_profile.delete_files_read_time);
182
5.60k
    bool decoded_cache_hit = false;
183
5.60k
    _deletion_vector = _kv_cache->get<DeletionVector>(
184
5.60k
            build_paimon_deletion_vector_cache_key(deletion_file),
185
5.60k
            [&]() -> DeletionVector* {
186
97
                auto deletion_vector = std::make_unique<DeletionVector>();
187
188
97
                TFileRangeDesc delete_range;
189
97
                delete_range.__set_fs_name(get_scan_range().fs_name);
190
97
                delete_range.path = deletion_file.path;
191
97
                delete_range.start_offset = deletion_file.offset;
192
97
                delete_range.size = static_cast<int64_t>(bytes_read);
193
97
                delete_range.file_size = -1;
194
195
97
                DeletionVectorReader dv_reader(get_state(), get_profile(), get_scan_params(),
196
97
                                               delete_range, get_io_ctx());
197
97
                create_status = dv_reader.open();
198
97
                if (!create_status.ok()) [[unlikely]] {
199
1
                    return nullptr;
200
1
                }
201
202
96
                std::vector<char> buffer(bytes_read);
203
96
                create_status =
204
96
                        dv_reader.read_at(deletion_file.offset, {buffer.data(), bytes_read});
205
96
                update_deletion_vector_file_cache_profile(dv_reader, &_paimon_profile);
206
96
                if (!create_status.ok()) [[unlikely]] {
207
0
                    return nullptr;
208
0
                }
209
210
96
                SCOPED_TIMER(_paimon_profile.parse_deletion_vector_time);
211
96
                create_status = decode_paimon_deletion_vector_buffer(buffer.data(), bytes_read,
212
96
                                                                     deletion_vector.get());
213
96
                if (!create_status.ok()) [[unlikely]] {
214
0
                    return nullptr;
215
0
                }
216
96
                COUNTER_UPDATE(_paimon_profile.num_delete_rows, deletion_vector->cardinality());
217
96
                return deletion_vector.release();
218
96
            },
219
5.60k
            &decoded_cache_hit);
220
5.60k
    RETURN_IF_ERROR(create_status);
221
5.60k
    COUNTER_UPDATE(decoded_cache_hit ? _paimon_profile.decoded_cache_hit_count
222
5.60k
                                     : _paimon_profile.decoded_cache_miss_count,
223
5.60k
                   1);
224
5.60k
    if (!_deletion_vector->isEmpty()) [[likely]] {
225
5.60k
        set_deletion_vector(_deletion_vector);
226
5.60k
    }
227
5.60k
    return Status::OK();
228
5.60k
}
229
230
// ============================================================================
231
// PaimonParquetReader
232
// ============================================================================
233
48.2k
void PaimonParquetReader::_init_paimon_profile() {
234
48.2k
    static const char* paimon_profile = "PaimonProfile";
235
48.2k
    ADD_TIMER(get_profile(), paimon_profile);
236
48.2k
    _paimon_profile.num_delete_rows =
237
48.2k
            ADD_CHILD_COUNTER(get_profile(), "NumDeleteRows", TUnit::UNIT, paimon_profile);
238
48.2k
    _paimon_profile.delete_files_read_time =
239
48.2k
            ADD_CHILD_TIMER(get_profile(), "DeleteFileReadTime", paimon_profile);
240
48.2k
    _paimon_profile.parse_deletion_vector_time =
241
48.2k
            ADD_CHILD_TIMER(get_profile(), "ParseDeletionVectorTime", paimon_profile);
242
48.2k
    init_deletion_vector_cache_profile(get_profile(), paimon_profile, &_paimon_profile);
243
48.2k
}
244
245
50.2k
Status PaimonParquetReader::on_before_init_reader(ReaderInitContext* ctx) {
246
50.2k
    _column_descs = ctx->column_descs;
247
50.2k
    _fill_col_name_to_block_idx = ctx->col_name_to_block_idx;
248
50.2k
    RETURN_IF_ERROR(_extract_partition_values(*ctx->range, ctx->tuple_descriptor,
249
50.2k
                                              _fill_partition_values,
250
50.2k
                                              &_fill_partition_value_is_null));
251
50.2k
    const FieldDescriptor* field_desc = nullptr;
252
50.2k
    RETURN_IF_ERROR(get_file_metadata_schema(&field_desc));
253
50.2k
    DCHECK(field_desc != nullptr);
254
255
50.2k
    RETURN_IF_ERROR(gen_table_info_node_by_field_id(
256
50.2k
            get_scan_params(), get_scan_range().table_format_params.paimon_params.schema_id,
257
50.2k
            get_tuple_descriptor(), *field_desc));
258
50.2k
    ctx->table_info_node = table_info_node_ptr;
259
260
99.3k
    for (const auto& desc : *ctx->column_descs) {
261
99.3k
        if (desc.category == ColumnCategory::REGULAR ||
262
99.3k
            desc.category == ColumnCategory::GENERATED) {
263
97.9k
            ctx->column_names.push_back(desc.name);
264
97.9k
        }
265
99.3k
    }
266
50.2k
    return Status::OK();
267
50.2k
}
268
269
48.6k
Status PaimonParquetReader::on_after_init_reader(ReaderInitContext* /*ctx*/) {
270
48.6k
    return _init_deletion_vector();
271
48.6k
}
272
273
48.0k
Status PaimonParquetReader::_init_deletion_vector() {
274
48.0k
    const auto& table_desc = get_scan_range().table_format_params.paimon_params;
275
48.0k
    if (!table_desc.__isset.deletion_file) {
276
33.2k
        return Status::OK();
277
33.2k
    }
278
279
15.8k
    if (!get_scan_range().table_format_params.paimon_params.__isset.row_count) {
280
15.8k
        set_push_down_agg_type(TPushAggOp::NONE);
281
15.8k
    }
282
14.7k
    const auto& deletion_file = table_desc.deletion_file;
283
14.7k
    size_t bytes_read = 0;
284
14.7k
    RETURN_IF_ERROR(validate_paimon_deletion_vector_descriptor(deletion_file, bytes_read));
285
286
14.7k
    Status create_status = Status::OK();
287
288
14.7k
    SCOPED_TIMER(_paimon_profile.delete_files_read_time);
289
14.7k
    bool decoded_cache_hit = false;
290
14.7k
    _deletion_vector = _kv_cache->get<DeletionVector>(
291
14.7k
            build_paimon_deletion_vector_cache_key(deletion_file),
292
14.7k
            [&]() -> DeletionVector* {
293
137
                auto deletion_vector = std::make_unique<DeletionVector>();
294
295
137
                TFileRangeDesc delete_range;
296
137
                delete_range.__set_fs_name(get_scan_range().fs_name);
297
137
                delete_range.path = deletion_file.path;
298
137
                delete_range.start_offset = deletion_file.offset;
299
137
                delete_range.size = static_cast<int64_t>(bytes_read);
300
137
                delete_range.file_size = -1;
301
302
137
                DeletionVectorReader dv_reader(get_state(), get_profile(), get_scan_params(),
303
137
                                               delete_range, get_io_ctx());
304
137
                create_status = dv_reader.open();
305
137
                if (!create_status.ok()) [[unlikely]] {
306
1
                    return nullptr;
307
1
                }
308
309
136
                std::vector<char> buffer(bytes_read);
310
136
                create_status =
311
136
                        dv_reader.read_at(deletion_file.offset, {buffer.data(), bytes_read});
312
136
                update_deletion_vector_file_cache_profile(dv_reader, &_paimon_profile);
313
136
                if (!create_status.ok()) [[unlikely]] {
314
0
                    return nullptr;
315
0
                }
316
317
136
                SCOPED_TIMER(_paimon_profile.parse_deletion_vector_time);
318
136
                create_status = decode_paimon_deletion_vector_buffer(buffer.data(), bytes_read,
319
136
                                                                     deletion_vector.get());
320
136
                if (!create_status.ok()) [[unlikely]] {
321
0
                    return nullptr;
322
0
                }
323
136
                COUNTER_UPDATE(_paimon_profile.num_delete_rows, deletion_vector->cardinality());
324
136
                return deletion_vector.release();
325
136
            },
326
14.7k
            &decoded_cache_hit);
327
14.7k
    RETURN_IF_ERROR(create_status);
328
14.7k
    COUNTER_UPDATE(decoded_cache_hit ? _paimon_profile.decoded_cache_hit_count
329
14.7k
                                     : _paimon_profile.decoded_cache_miss_count,
330
14.7k
                   1);
331
16.0k
    if (!_deletion_vector->isEmpty()) [[likely]] {
332
16.0k
        ParquetReader::set_deletion_vector(_deletion_vector);
333
16.0k
    }
334
14.7k
    return Status::OK();
335
14.7k
}
336
337
} // namespace doris