2626#include " arrow/c/abi.h"
2727#include " arrow/c/bridge.h"
2828#include " paimon/common/data/columnar/columnar_row.h"
29+ #include " paimon/common/predicate/predicate_validator.h"
30+ #include " paimon/common/reader/late_materializing_file_batch_reader.h"
2931#include " paimon/common/utils/arrow/status_utils.h"
32+ #include " paimon/common/utils/checked_cast.h"
3033#include " paimon/core/io/rolling_file_writer.h"
3134#include " paimon/core/manifest/manifest_entry.h"
3235#include " paimon/core/manifest/manifest_entry_serializer.h"
3942#include " paimon/format/file_format.h"
4043#include " paimon/format/reader_builder.h"
4144#include " paimon/format/writer_builder.h"
45+ #include " paimon/predicate/predicate_builder.h"
4246#include " paimon/status.h"
4347
4448namespace arrow {
@@ -49,6 +53,12 @@ class Schema;
4953namespace paimon {
5054class MemoryPool ;
5155
56+ namespace {
57+ constexpr int32_t kVersionFieldIndex = 0 ;
58+ constexpr int32_t kBucketFieldIndex = 3 ;
59+ constexpr int32_t kTotalBucketsFieldIndex = 4 ;
60+ } // namespace
61+
5262ManifestFile::ManifestFile (const std::shared_ptr<FileSystem>& file_system,
5363 const std::shared_ptr<ReaderBuilder>& reader_builder,
5464 const std::shared_ptr<WriterBuilder>& writer_builder,
@@ -91,25 +101,87 @@ Result<std::unique_ptr<ManifestFile>> ManifestFile::Create(
91101}
92102
93103Status ManifestFile::ReadBucketEntries (const std::string& file_name, int32_t bucket,
104+ const std::optional<int32_t >& expected_total_buckets,
94105 std::optional<int64_t > file_size,
95106 std::vector<ManifestEntry>* entries) const {
107+ // Readers without precise bitmap selection still filter aligned Arrow columns
108+ // before constructing ManifestEntry and DataFileMeta objects.
96109 return ReadArrowBatches (
97- file_name,
98- [this , bucket, entries]( const std::shared_ptr<arrow::StructArray>& batch) -> Status {
99- const arrow::ArrayVector& fields = batch-> fields ();
100- ColumnarRow row (fields, pool_, /* row_id=*/ 0 );
101- for (int64_t i = 0 ; i < batch->length (); i++ ) {
110+ file_name, file_size,
111+ [this , bucket, expected_total_buckets,
112+ entries]( const std::shared_ptr< arrow::StructArray>& batch) -> Status {
113+ ColumnarRow row (batch-> fields () , pool_, /* row_id=*/ 0 );
114+ for (int64_t i = 0 ; i < batch->length (); ++i ) {
102115 row.SetRowId (i);
103- PAIMON_RETURN_NOT_OK (ManifestEntrySerializer::ValidateVersion (row.GetInt (0 )));
104- if (ManifestEntrySerializer::GetBucket (row) != bucket) {
116+ PAIMON_RETURN_NOT_OK (
117+ ManifestEntrySerializer::ValidateVersion (row.GetInt (kVersionFieldIndex )));
118+ // Different or unknown bucket counts must reach per-entry bucket filtering.
119+ const bool historical_layout =
120+ expected_total_buckets &&
121+ (row.IsNullAt (kBucketFieldIndex ) || row.IsNullAt (kTotalBucketsFieldIndex ) ||
122+ row.GetInt (kTotalBucketsFieldIndex ) != expected_total_buckets.value ());
123+ if (!historical_layout && ManifestEntrySerializer::GetBucket (row) != bucket) {
105124 continue ;
106125 }
107126 PAIMON_ASSIGN_OR_RAISE (ManifestEntry entry, serializer_->FromRow (row));
108127 entries->push_back (std::move (entry));
109128 }
110129 return Status::OK ();
111130 },
112- file_size);
131+ [this , bucket, expected_total_buckets](std::unique_ptr<FileBatchReader>* reader) {
132+ return PrepareBucketRead (bucket, expected_total_buckets, reader);
133+ });
134+ }
135+
136+ Status ManifestFile::PrepareBucketRead (int32_t bucket,
137+ const std::optional<int32_t >& expected_total_buckets,
138+ std::unique_ptr<FileBatchReader>* reader) const {
139+ if (!(*reader)->SupportPreciseBitmapSelection ()) {
140+ return Status::OK ();
141+ }
142+ PAIMON_ASSIGN_OR_RAISE (std::unique_ptr<ArrowSchema> c_schema, (*reader)->GetFileSchema ());
143+ PAIMON_ASSIGN_OR_RAISE_FROM_ARROW (std::shared_ptr<arrow::Schema> file_schema,
144+ arrow::ImportSchema (c_schema.get ()));
145+ const auto & target_type = serializer_->GetDataType ();
146+ const std::string& bucket_name = target_type->field (kBucketFieldIndex )->name ();
147+ std::shared_ptr<Predicate> selector =
148+ PredicateBuilder::Equal (kBucketFieldIndex , bucket_name, FieldType::INT , Literal (bucket));
149+ if (expected_total_buckets) {
150+ const std::string& total_name = target_type->field (kTotalBucketsFieldIndex )->name ();
151+ PAIMON_ASSIGN_OR_RAISE (
152+ selector,
153+ PredicateBuilder::Or (
154+ {selector,
155+ PredicateBuilder::IsNull (kTotalBucketsFieldIndex , total_name, FieldType::INT ),
156+ PredicateBuilder::NotEqual (kTotalBucketsFieldIndex , total_name, FieldType::INT ,
157+ Literal (expected_total_buckets.value ()))}));
158+ }
159+ // Retain unsupported versions regardless of bucket so the consumer validates every version
160+ // before bucket filtering, including when the probe would otherwise select no entries.
161+ const std::string& version_name = target_type->field (kVersionFieldIndex )->name ();
162+ PAIMON_ASSIGN_OR_RAISE (
163+ selector,
164+ PredicateBuilder::Or (
165+ {selector, PredicateBuilder::IsNull (kVersionFieldIndex , version_name, FieldType::INT ),
166+ PredicateBuilder::NotEqual (
167+ kVersionFieldIndex , version_name, FieldType::INT ,
168+ Literal (
169+ checked_cast<ManifestEntrySerializer*>(serializer_.get ())->GetVersion ()))}));
170+ if (!PredicateValidator::ValidatePredicateWithSchema (*file_schema, selector,
171+ /* validate_field_idx=*/ true )
172+ .ok ()) {
173+ // An incompatible projection must fall back to ordinary schema evolution.
174+ return Status::OK ();
175+ }
176+ PAIMON_ASSIGN_OR_RAISE (
177+ std::unique_ptr<LateMaterializingFileBatchReader> selective_reader,
178+ LateMaterializingFileBatchReader::Create (std::move (*reader), arrow_pool_));
179+ // Keep the on-disk schema; ManifestMetaReader still performs schema evolution afterwards.
180+ ArrowSchema full_c_schema;
181+ PAIMON_RETURN_NOT_OK_FROM_ARROW (arrow::ExportSchema (*file_schema, &full_c_schema));
182+ PAIMON_RETURN_NOT_OK (selective_reader->SetReadSchema (&full_c_schema, selector, std::nullopt ));
183+ *reader = std::move (selective_reader);
184+ return Status::OK ();
113185}
114186
115187Result<std::vector<ManifestFileMeta>> ManifestFile::Write (
0 commit comments