iceberg-cpp
Loading...
Searching...
No Matches
delete_file_index.h
Go to the documentation of this file.
1/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19
20#pragma once
21
24
25#include <functional>
26#include <map>
27#include <memory>
28#include <optional>
29#include <unordered_map>
30#include <vector>
31
32#include "iceberg/expression/literal.h"
33#include "iceberg/iceberg_export.h"
35#include "iceberg/result.h"
36#include "iceberg/type_fwd.h"
38#include "iceberg/util/executor.h"
39#include "iceberg/util/partition_value_util.h"
40
41namespace iceberg {
42
43namespace internal {
44
46struct ICEBERG_EXPORT EqualityDeleteFile {
47 const Schema* schema;
48 ManifestEntry wrapped;
49 int64_t apply_sequence_number; // = data_sequence_number - 1
50
51 // Lazily converted bounds for pruning
52 mutable std::unordered_map<int32_t, Literal> lower_bounds;
53 mutable std::unordered_map<int32_t, Literal> upper_bounds;
54 mutable bool bounds_converted = false;
55
56 EqualityDeleteFile(const Schema* schema, ManifestEntry&& entry)
57 : schema(schema),
58 wrapped(std::move(entry)),
59 apply_sequence_number(wrapped.sequence_number.value() - 1) {}
60
63 return !wrapped.data_file->lower_bounds.empty() &&
64 !wrapped.data_file->upper_bounds.empty();
65 }
66
68 Result<std::optional<std::reference_wrapper<const Literal>>> LowerBound(
69 int32_t id) const {
70 ICEBERG_RETURN_UNEXPECTED(ConvertBoundsIfNeeded());
71 auto it = lower_bounds.find(id);
72 return it != lower_bounds.cend() ? std::make_optional(std::cref(it->second))
73 : std::nullopt;
74 }
75
77 Result<std::optional<std::reference_wrapper<const Literal>>> UpperBound(
78 int32_t id) const {
79 ICEBERG_RETURN_UNEXPECTED(ConvertBoundsIfNeeded());
80 auto it = upper_bounds.find(id);
81 return it != upper_bounds.cend() ? std::make_optional(std::cref(it->second))
82 : std::nullopt;
83 }
84
85 private:
87 Status ConvertBoundsIfNeeded() const;
88};
89
91inline bool RangesOverlap(const Literal& data_lower, const Literal& data_upper,
92 const Literal& delete_lower, const Literal& delete_upper) {
93 if (data_lower > delete_upper) {
94 return false;
95 }
96 if (delete_lower > data_upper) {
97 return false;
98 }
99 return true;
100}
101
103inline bool AllNull(const std::map<int32_t, int64_t>& null_counts,
104 const std::map<int32_t, int64_t>& value_counts, int32_t field_id,
105 bool is_required) {
106 if (is_required) {
107 return false;
108 }
109
110 auto null_it = null_counts.find(field_id);
111 auto value_it = value_counts.find(field_id);
112 if (null_it == null_counts.cend() || value_it == value_counts.cend()) {
113 return false;
114 }
115
116 return null_it->second == value_it->second;
117}
118
120inline bool AllNonNull(const std::map<int32_t, int64_t>& null_counts, int32_t field_id,
121 bool is_required) {
122 if (is_required) {
123 return true;
124 }
125
126 auto it = null_counts.find(field_id);
127 if (it == null_counts.cend()) {
128 return false;
129 }
130
131 return it->second <= 0;
132}
133
135inline bool ContainsNull(const std::map<int32_t, int64_t>& null_counts, int32_t field_id,
136 bool is_required) {
137 if (is_required) {
138 return false;
139 }
140
141 auto it = null_counts.find(field_id);
142 if (it == null_counts.cend()) {
143 return true; // Unknown, assume may contain null
144 }
145
146 return it->second > 0;
147}
148
150ICEBERG_EXPORT Result<bool> CanContainEqDeletesForFile(
151 const DataFile& data_file, const EqualityDeleteFile& delete_file);
152
157class ICEBERG_EXPORT PositionDeletes {
158 public:
159 PositionDeletes() = default;
160
162 [[nodiscard]] Status Add(ManifestEntry&& entry);
163
166 std::vector<std::shared_ptr<DataFile>> Filter(int64_t seq);
167
169 std::vector<std::shared_ptr<DataFile>> ReferencedDeleteFiles();
170
172 bool empty() const { return files_.empty(); }
173
174 private:
175 void IndexIfNeeded();
176
177 std::vector<ManifestEntry> files_;
178 std::vector<int64_t> seqs_;
179 bool indexed_ = false;
180};
181
186class ICEBERG_EXPORT EqualityDeletes {
187 public:
188 explicit EqualityDeletes(const Schema& schema) : schema_(schema) {}
189
191 [[nodiscard]] Status Add(ManifestEntry&& entry);
192
198 Result<std::vector<std::shared_ptr<DataFile>>> Filter(int64_t seq,
199 const DataFile& data_file);
200
202 std::vector<std::shared_ptr<DataFile>> ReferencedDeleteFiles();
203
205 bool empty() const { return files_.empty(); }
206
207 private:
208 void IndexIfNeeded();
209
210 const Schema& schema_;
211 std::vector<EqualityDeleteFile> files_;
212 std::vector<int64_t> seqs_;
213 bool indexed_ = false;
214};
215
216} // namespace internal
217
229class ICEBERG_EXPORT DeleteFileIndex {
230 public:
231 class Builder;
232
234
236 DeleteFileIndex& operator=(DeleteFileIndex&&) noexcept;
237 DeleteFileIndex(const DeleteFileIndex&) = delete;
238 DeleteFileIndex& operator=(const DeleteFileIndex&) = delete;
239
241 bool empty() const;
242
244 bool has_equality_deletes() const;
245
247 bool has_position_deletes() const;
248
251 std::vector<std::shared_ptr<DataFile>> ReferencedDeleteFiles() const;
252
257 Result<std::vector<std::shared_ptr<DataFile>>> ForEntry(
258 const ManifestEntry& entry) const;
259
266 Result<std::vector<std::shared_ptr<DataFile>>> ForDataFile(int64_t sequence_number,
267 const DataFile& file) const;
268
276 static Result<Builder> BuilderFor(
277 std::shared_ptr<FileIO> io, std::shared_ptr<Schema> schema,
278 std::unordered_map<int32_t, std::shared_ptr<PartitionSpec>> specs_by_id,
279 std::vector<ManifestFile> delete_manifests);
280
281 private:
282 friend class Builder;
283
284 // Private constructor used by Builder
286 std::unique_ptr<internal::EqualityDeletes> global_deletes,
287 std::unique_ptr<PartitionMap<std::unique_ptr<internal::EqualityDeletes>>>
288 eq_deletes_by_partition,
289 std::unique_ptr<PartitionMap<std::unique_ptr<internal::PositionDeletes>>>
290 pos_deletes_by_partition,
291 std::unique_ptr<
292 std::unordered_map<std::string, std::unique_ptr<internal::PositionDeletes>>>
293 pos_deletes_by_path,
294 std::unique_ptr<std::unordered_map<std::string, ManifestEntry>> dv_by_path);
295
296 // Helper methods for finding delete files
297 Result<std::vector<std::shared_ptr<DataFile>>> FindGlobalDeletes(
298 int64_t seq, const DataFile& data_file) const;
299 Result<std::vector<std::shared_ptr<DataFile>>> FindEqPartitionDeletes(
300 int64_t seq, const DataFile& data_file) const;
301 Result<std::vector<std::shared_ptr<DataFile>>> FindPosPartitionDeletes(
302 int64_t seq, const DataFile& data_file) const;
303 Result<std::vector<std::shared_ptr<DataFile>>> FindPathDeletes(
304 int64_t seq, const DataFile& data_file) const;
305 Result<std::shared_ptr<DataFile>> FindDV(int64_t seq, const DataFile& data_file) const;
306
307 // Index data structures
308 std::unique_ptr<internal::EqualityDeletes> global_deletes_;
309 std::unique_ptr<PartitionMap<std::unique_ptr<internal::EqualityDeletes>>>
310 eq_deletes_by_partition_;
311 std::unique_ptr<PartitionMap<std::unique_ptr<internal::PositionDeletes>>>
312 pos_deletes_by_partition_;
313 std::unique_ptr<
314 std::unordered_map<std::string, std::unique_ptr<internal::PositionDeletes>>>
315 pos_deletes_by_path_;
316 std::unique_ptr<std::unordered_map<std::string, ManifestEntry>> dv_by_path_;
317
318 bool has_eq_deletes_ = false;
319 bool has_pos_deletes_ = false;
320 bool is_empty_ = true;
321};
322
323class ICEBERG_EXPORT DeleteFileIndex::Builder : public ErrorCollector {
324 public:
326 Builder(std::shared_ptr<FileIO> io, std::shared_ptr<Schema> schema,
327 std::unordered_map<int32_t, std::shared_ptr<PartitionSpec>> specs_by_id,
328 std::vector<ManifestFile> delete_manifests);
329
330 ~Builder() override;
331
332 Builder(Builder&&) noexcept;
333 Builder& operator=(Builder&&) noexcept;
334 Builder(const Builder&) = delete;
335 Builder& operator=(const Builder&) = delete;
336
340 Builder& AfterSequenceNumber(int64_t seq);
341
346 Builder& DataFilter(std::shared_ptr<Expression> filter);
347
349 Builder& PartitionFilter(std::shared_ptr<Expression> filter);
350
352 Builder& FilterPartitions(std::shared_ptr<PartitionSet> partition_set);
353
355 Builder& CaseSensitive(bool case_sensitive);
356
358 Builder& IgnoreResiduals();
359
364 Builder& PlanWith(OptionalExecutor executor);
365
367 Result<std::unique_ptr<DeleteFileIndex>> Build();
368
369 private:
370 // Load delete files from manifests
371 Result<std::vector<ManifestEntry>> LoadDeleteFiles();
372
373 // Add a DV to the index
374 Status AddDV(std::unordered_map<std::string, ManifestEntry>& dv_by_path,
375 ManifestEntry&& entry);
376
377 // Add a position delete file to the index
378 Status AddPositionDelete(
379 std::unordered_map<std::string, std::unique_ptr<internal::PositionDeletes>>&
380 deletes_by_path,
381 PartitionMap<std::unique_ptr<internal::PositionDeletes>>& deletes_by_partition,
382 ManifestEntry&& entry);
383
384 // Add an equality delete file to the index
385 Status AddEqualityDelete(
386 internal::EqualityDeletes& global_deletes,
387 PartitionMap<std::unique_ptr<internal::EqualityDeletes>>& deletes_by_partition,
388 ManifestEntry&& entry);
389
390 std::shared_ptr<FileIO> io_;
391 std::shared_ptr<Schema> schema_;
392 std::unordered_map<int32_t, std::shared_ptr<PartitionSpec>> specs_by_id_;
393 std::vector<ManifestFile> delete_manifests_;
394 int64_t min_sequence_number_ = 0;
395 std::shared_ptr<Expression> data_filter_;
396 std::shared_ptr<Expression> partition_filter_;
397 std::shared_ptr<PartitionSet> partition_set_;
398 OptionalExecutor executor_;
399 bool case_sensitive_ = true;
400 bool ignore_residuals_ = false;
401};
402
403} // namespace iceberg
An index of delete files by sequence number.
Definition delete_file_index.h:229
Builder(std::shared_ptr< FileIO > io, std::shared_ptr< Schema > schema, std::unordered_map< int32_t, std::shared_ptr< PartitionSpec > > specs_by_id, std::vector< ManifestFile > delete_manifests)
Construct a builder from manifest files.
Base class for collecting errors in the builder pattern.
Definition error_collector.h:93
Represents a boolean expression tree.
Definition expression.h:37
Pluggable module for reading, writing, and deleting files.
Definition file_io.h:125
Literal is a literal value that is associated with a primitive type.
Definition literal.h:39
A map that uses a pair of spec ID and partition tuple as keys.
Definition partition_value_util.h:115
A set that uses a pair of spec ID and partition tuple as elements.
Definition partition_value_util.h:204
A partition spec for a Table.
Definition partition_spec.h:47
A schema for a Table.
Definition schema.h:51
A group of equality delete files sorted by apply sequence number.
Definition delete_file_index.h:186
bool empty() const
Check if this group is empty.
Definition delete_file_index.h:205
A group of position delete files sorted by the sequence number they apply to.
Definition delete_file_index.h:157
bool empty() const
Check if this group is empty.
Definition delete_file_index.h:172
bool AllNull(const std::map< int32_t, int64_t > &null_counts, const std::map< int32_t, int64_t > &value_counts, int32_t field_id, bool is_required)
Check if a value count map indicates all values are null.
Definition delete_file_index.h:103
bool RangesOverlap(const Literal &data_lower, const Literal &data_upper, const Literal &delete_lower, const Literal &delete_upper)
Check if two ranges overlap.
Definition delete_file_index.h:91
bool ContainsNull(const std::map< int32_t, int64_t > &null_counts, int32_t field_id, bool is_required)
Check if the column contains any null values.
Definition delete_file_index.h:135
bool AllNonNull(const std::map< int32_t, int64_t > &null_counts, int32_t field_id, bool is_required)
Check if all values are non-null.
Definition delete_file_index.h:120
STL namespace.
DataFile carries data file path, partition tuple, metrics, ...
Definition manifest_entry.h:62
A manifest is an immutable Avro file that lists data files or delete files, along with each file's pa...
Definition manifest_entry.h:311
std::optional< int64_t > sequence_number
Definition manifest_entry.h:332
std::shared_ptr< DataFile > data_file
Definition manifest_entry.h:348
Entry in a manifest list.
Definition manifest_list.h:85
Wrapper for equality delete files that caches converted bounds.
Definition delete_file_index.h:46
Result< std::optional< std::reference_wrapper< const Literal > > > UpperBound(int32_t id) const
Get the upper bound for a field ID.
Definition delete_file_index.h:77
Result< std::optional< std::reference_wrapper< const Literal > > > LowerBound(int32_t id) const
Get the lower bound for a field ID.
Definition delete_file_index.h:68
bool HasLowerAndUpperBounds() const
Check if this delete file has both lower and upper bounds.
Definition delete_file_index.h:62