Skip to content

Commit 3483316

Browse files
authored
feat(parquet): support pushing bitmaps down to page-level filtering (except for nested columns) (#375)
1 parent ffbda4f commit 3483316

13 files changed

Lines changed: 870 additions & 132 deletions

include/paimon/utils/roaring_bitmap32.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include <cstddef>
1919
#include <cstdint>
2020
#include <limits>
21+
#include <optional>
2122
#include <string>
2223
#include <vector>
2324

@@ -135,6 +136,10 @@ class PAIMON_EXPORT RoaringBitmap32 {
135136
Iterator End() const;
136137
/// @return the iterator moved to the value which is equal or larger than key
137138
Iterator EqualOrLarger(int32_t key) const;
139+
/// @return the first value which is equal or larger than x
140+
std::optional<int32_t> NextValue(int32_t x) const;
141+
/// @return the largest value which is smaller than x
142+
std::optional<int32_t> PreviousValue(int32_t x) const;
138143

139144
/// Computes the intersection between two bitmaps and returns new bitmap.
140145
/// The current bitmap and the provided bitmap are unchanged.

src/paimon/common/utils/roaring_bitmap32.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,4 +310,33 @@ RoaringBitmap32::Iterator RoaringBitmap32::EqualOrLarger(int32_t key) const {
310310
return iter;
311311
}
312312

313+
std::optional<int32_t> RoaringBitmap32::NextValue(int32_t x) const {
314+
auto iter = EqualOrLarger(x);
315+
if (iter == End()) {
316+
return std::nullopt;
317+
}
318+
return *iter;
319+
}
320+
321+
std::optional<int32_t> RoaringBitmap32::PreviousValue(int32_t x) const {
322+
if (IsEmpty()) {
323+
return std::nullopt;
324+
}
325+
326+
auto& bitmap = GetRoaringBitmap(roaring_bitmap_);
327+
if (x <= static_cast<int32_t>(bitmap.minimum())) {
328+
return std::nullopt;
329+
}
330+
331+
const uint64_t rank = bitmap.rank(static_cast<uint32_t>(x - 1));
332+
if (rank == 0) {
333+
return std::nullopt;
334+
}
335+
336+
uint32_t value = 0;
337+
[[maybe_unused]] bool found = bitmap.select(static_cast<uint32_t>(rank - 1), &value);
338+
assert(found);
339+
return static_cast<int32_t>(value);
340+
}
341+
313342
} // namespace paimon

src/paimon/common/utils/roaring_bitmap32_test.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,25 @@ TEST(RoaringBitmap32Test, TestIterator) {
254254
ASSERT_EQ(iter, roaring.End());
255255
}
256256

257+
TEST(RoaringBitmap32Test, TestNextAndPreviousValue) {
258+
RoaringBitmap32 roaring = RoaringBitmap32::From({10, 20, 30});
259+
260+
ASSERT_EQ(roaring.NextValue(5), std::optional<int32_t>(10));
261+
ASSERT_EQ(roaring.NextValue(10), std::optional<int32_t>(10));
262+
ASSERT_EQ(roaring.NextValue(25), std::optional<int32_t>(30));
263+
ASSERT_EQ(roaring.NextValue(31), std::nullopt);
264+
265+
ASSERT_EQ(roaring.PreviousValue(10), std::nullopt);
266+
ASSERT_EQ(roaring.PreviousValue(11), std::optional<int32_t>(10));
267+
ASSERT_EQ(roaring.PreviousValue(20), std::optional<int32_t>(10));
268+
ASSERT_EQ(roaring.PreviousValue(21), std::optional<int32_t>(20));
269+
ASSERT_EQ(roaring.PreviousValue(100), std::optional<int32_t>(30));
270+
271+
RoaringBitmap32 empty;
272+
ASSERT_EQ(empty.NextValue(0), std::nullopt);
273+
ASSERT_EQ(empty.PreviousValue(0), std::nullopt);
274+
}
275+
257276
TEST(RoaringBitmap32Test, TestIteratorAssignAndMove) {
258277
RoaringBitmap32 roaring = RoaringBitmap32::From({10, 100, 200});
259278

src/paimon/format/parquet/file_reader_wrapper.cpp

Lines changed: 23 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -164,14 +164,15 @@ void FileReaderWrapper::AdvanceToNextRowGroup() {
164164
current_row_group_idx_++;
165165
// Skip row groups excluded by read range.
166166
while (current_row_group_idx_ < target_row_groups_.size() &&
167-
target_row_groups_[current_row_group_idx_].excluded_by_read_range) {
167+
target_row_groups_[current_row_group_idx_].IsExcludedByReadRange()) {
168168
current_row_group_idx_++;
169169
}
170170
if (current_row_group_idx_ >= target_row_groups_.size()) {
171171
next_row_to_read_ = num_rows_;
172172
} else {
173173
next_row_to_read_ =
174-
all_row_group_ranges_[target_row_groups_[current_row_group_idx_].row_group_index].first;
174+
all_row_group_ranges_[target_row_groups_[current_row_group_idx_].GetRowGroupIndex()]
175+
.first;
175176
}
176177
}
177178

@@ -181,10 +182,10 @@ Status FileReaderWrapper::SeekToRow(uint64_t row_number) {
181182
filtered_global_offset_ = 0;
182183

183184
for (uint64_t i = 0; i < target_row_groups_.size(); i++) {
184-
if (target_row_groups_[i].excluded_by_read_range) {
185+
if (target_row_groups_[i].IsExcludedByReadRange()) {
185186
continue;
186187
}
187-
int32_t rg_id = target_row_groups_[i].row_group_index;
188+
int32_t rg_id = target_row_groups_[i].GetRowGroupIndex();
188189
uint64_t rg_start = all_row_group_ranges_[rg_id].first;
189190
uint64_t rg_end = all_row_group_ranges_[rg_id].second;
190191
if (row_number > rg_start && row_number < rg_end) {
@@ -200,9 +201,9 @@ Status FileReaderWrapper::SeekToRow(uint64_t row_number) {
200201
// Rebuild batch_reader_ for non-page-filtered RGs at/after seek position.
201202
std::vector<int32_t> fully_matched_indices;
202203
for (uint64_t j = i; j < target_row_groups_.size(); j++) {
203-
if (!target_row_groups_[j].excluded_by_read_range &&
204-
!target_row_groups_[j].is_partially_matched) {
205-
fully_matched_indices.push_back(target_row_groups_[j].row_group_index);
204+
if (!target_row_groups_[j].IsExcludedByReadRange() &&
205+
!target_row_groups_[j].IsPartiallyMatched()) {
206+
fully_matched_indices.push_back(target_row_groups_[j].GetRowGroupIndex());
206207
}
207208
}
208209
if (!fully_matched_indices.empty()) {
@@ -222,7 +223,7 @@ Status FileReaderWrapper::SeekToRow(uint64_t row_number) {
222223
}
223224

224225
Result<std::shared_ptr<arrow::RecordBatch>> FileReaderWrapper::NextPageFiltered() {
225-
int32_t rg_id = target_row_groups_[current_row_group_idx_].row_group_index;
226+
int32_t rg_id = target_row_groups_[current_row_group_idx_].GetRowGroupIndex();
226227

227228
// Construct the per-RG streaming reader on demand.
228229
if (!current_page_filtered_reader_) {
@@ -237,7 +238,7 @@ Result<std::shared_ptr<arrow::RecordBatch>> FileReaderWrapper::NextPageFiltered(
237238
file_reader_->parquet_reader(), target_rg, target_column_indices_,
238239
page_filtered_read_schema_, file_reader_->properties().cache_options(),
239240
pre_buffered, page_ranges, max_chunksize, pool_));
240-
current_filtered_row_ranges_ = target_rg.row_ranges;
241+
current_filtered_row_ranges_ = target_rg.GetRowRanges();
241242
current_filtered_rg_start_ = all_row_group_ranges_[rg_id].first;
242243
filtered_global_offset_ = 0;
243244
}
@@ -273,7 +274,7 @@ Result<std::shared_ptr<arrow::RecordBatch>> FileReaderWrapper::NextFullyMatched(
273274
return std::shared_ptr<arrow::RecordBatch>();
274275
}
275276

276-
int32_t rg_id = target_row_groups_[current_row_group_idx_].row_group_index;
277+
int32_t rg_id = target_row_groups_[current_row_group_idx_].GetRowGroupIndex();
277278
uint64_t rg_end = all_row_group_ranges_[rg_id].second;
278279
int64_t num_rows = record_batch->num_rows();
279280

@@ -298,7 +299,7 @@ Result<std::shared_ptr<arrow::RecordBatch>> FileReaderWrapper::Next() {
298299

299300
while (current_row_group_idx_ < target_row_groups_.size()) {
300301
bool is_partially_matched =
301-
target_row_groups_[current_row_group_idx_].is_partially_matched;
302+
target_row_groups_[current_row_group_idx_].IsPartiallyMatched();
302303
PAIMON_ASSIGN_OR_RAISE(std::shared_ptr<arrow::RecordBatch> batch,
303304
is_partially_matched ? NextPageFiltered() : NextFullyMatched());
304305
if (batch) {
@@ -368,17 +369,17 @@ std::vector<::arrow::io::ReadRange> FileReaderWrapper::CollectPreBufferRanges(
368369
auto file_metadata = file_reader_->parquet_reader()->metadata();
369370

370371
for (const auto& trg : target_row_groups_) {
371-
if (trg.excluded_by_read_range) continue;
372+
if (trg.IsExcludedByReadRange()) continue;
372373

373-
if (trg.is_partially_matched) {
374+
if (trg.IsPartiallyMatched()) {
374375
// Page-filtered RGs: only matching page byte ranges.
375376
auto page_ranges = PageFilteredRowGroupReader::ComputePageRanges(
376377
file_reader_->parquet_reader(), trg, column_indices);
377378
ranges.insert(ranges.end(), std::make_move_iterator(page_ranges.begin()),
378379
std::make_move_iterator(page_ranges.end()));
379380
} else {
380381
// Fully-matched RGs: entire column chunk ranges.
381-
auto rg_metadata = file_metadata->RowGroup(trg.row_group_index);
382+
auto rg_metadata = file_metadata->RowGroup(trg.GetRowGroupIndex());
382383
for (int32_t col_idx : column_indices) {
383384
auto col_chunk = rg_metadata->ColumnChunk(col_idx);
384385
int64_t offset = col_chunk->data_page_offset();
@@ -416,12 +417,12 @@ Status FileReaderWrapper::PrepareForReading(const std::vector<TargetRowGroup>& t
416417
std::vector<int32_t> fully_matched_row_groups;
417418
uint64_t active_count = 0;
418419
for (const auto& trg : target_row_groups_) {
419-
if (trg.excluded_by_read_range) {
420+
if (trg.IsExcludedByReadRange()) {
420421
continue;
421422
}
422423
active_count++;
423-
if (!trg.is_partially_matched) {
424-
fully_matched_row_groups.push_back(trg.row_group_index);
424+
if (!trg.IsPartiallyMatched()) {
425+
fully_matched_row_groups.push_back(trg.GetRowGroupIndex());
425426
}
426427
}
427428

@@ -455,14 +456,15 @@ Status FileReaderWrapper::PrepareForReading(const std::vector<TargetRowGroup>& t
455456
// Reset read state. Find the first non-excluded row group.
456457
uint64_t first_active_idx = 0;
457458
while (first_active_idx < target_row_groups_.size() &&
458-
target_row_groups_[first_active_idx].excluded_by_read_range) {
459+
target_row_groups_[first_active_idx].IsExcludedByReadRange()) {
459460
first_active_idx++;
460461
}
461462
if (first_active_idx >= target_row_groups_.size()) {
462463
next_row_to_read_ = num_rows_;
463464
} else {
464465
next_row_to_read_ =
465-
all_row_group_ranges_[target_row_groups_[first_active_idx].row_group_index].first;
466+
all_row_group_ranges_[target_row_groups_[first_active_idx].GetRowGroupIndex()]
467+
.first;
466468
}
467469
previous_first_row_ = std::numeric_limits<uint64_t>::max();
468470
current_row_group_idx_ = first_active_idx;
@@ -476,7 +478,7 @@ Status FileReaderWrapper::ApplyReadRanges(
476478
const std::vector<std::pair<uint64_t, uint64_t>>& read_ranges) {
477479
if (read_ranges.empty()) {
478480
for (auto& trg : target_row_groups_) {
479-
trg.excluded_by_read_range = true;
481+
trg.SetExcludedByReadRange(true);
480482
}
481483
reader_initialized_ = false;
482484
return Status::OK();
@@ -492,7 +494,7 @@ Status FileReaderWrapper::ApplyReadRanges(
492494
}
493495
// Mark each target row group as excluded or not based on the matching set.
494496
for (auto& trg : target_row_groups_) {
495-
trg.excluded_by_read_range = matching_rg_indices.count(trg.row_group_index) == 0;
497+
trg.SetExcludedByReadRange(matching_rg_indices.count(trg.GetRowGroupIndex()) == 0);
496498
}
497499
reader_initialized_ = false;
498500
return Status::OK();

src/paimon/format/parquet/file_reader_wrapper.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
#include "arrow/type_fwd.h"
3434
#include "paimon/common/utils/arrow/status_utils.h"
3535
#include "paimon/format/parquet/row_ranges.h"
36+
#include "paimon/format/parquet/target_row_group.h"
3637
#include "paimon/result.h"
3738
#include "paimon/status.h"
3839
#include "parquet/arrow/reader.h"

src/paimon/format/parquet/page_filtered_row_group_reader.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -234,8 +234,8 @@ Result<std::unique_ptr<arrow::RecordBatchReader>> PageFilteredRowGroupReader::Re
234234
const ::arrow::io::CacheOptions& cache_options, bool pre_buffered,
235235
const std::vector<::arrow::io::ReadRange>& page_ranges, int64_t max_chunksize,
236236
std::shared_ptr<::arrow::MemoryPool> pool) {
237-
const auto& row_ranges = target_row_group.row_ranges;
238-
int32_t row_group_index = target_row_group.row_group_index;
237+
const auto& row_ranges = target_row_group.GetRowRanges();
238+
int32_t row_group_index = target_row_group.GetRowGroupIndex();
239239

240240
if (row_ranges.IsEmpty()) {
241241
PAIMON_ASSIGN_OR_RAISE_FROM_ARROW(std::shared_ptr<arrow::Table> empty_table,
@@ -289,8 +289,8 @@ Result<std::unique_ptr<arrow::RecordBatchReader>> PageFilteredRowGroupReader::Re
289289
std::vector<::arrow::io::ReadRange> PageFilteredRowGroupReader::ComputePageRanges(
290290
::parquet::ParquetFileReader* parquet_reader, const TargetRowGroup& target_row_group,
291291
const std::vector<int32_t>& column_indices) {
292-
int32_t row_group_index = target_row_group.row_group_index;
293-
const auto& row_ranges = target_row_group.row_ranges;
292+
int32_t row_group_index = target_row_group.GetRowGroupIndex();
293+
const auto& row_ranges = target_row_group.GetRowRanges();
294294

295295
std::vector<::arrow::io::ReadRange> ranges;
296296
auto file_metadata = parquet_reader->metadata();

src/paimon/format/parquet/page_filtered_row_group_reader.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include "arrow/record_batch.h"
2828
#include "arrow/type.h"
2929
#include "paimon/format/parquet/row_ranges.h"
30+
#include "paimon/format/parquet/target_row_group.h"
3031
#include "paimon/result.h"
3132
#include "parquet/column_reader.h"
3233
#include "parquet/file_reader.h"

0 commit comments

Comments
 (0)