| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| #pragma once |
| |
| /// \file iceberg/update/overwrite_files.h |
| |
| #include <cstdint> |
| #include <memory> |
| #include <optional> |
| #include <string> |
| |
| #include "iceberg/iceberg_export.h" |
| #include "iceberg/result.h" |
| #include "iceberg/type_fwd.h" |
| #include "iceberg/update/merging_snapshot_update.h" |
| #include "iceberg/util/data_file_set.h" |
| |
| namespace iceberg { |
| |
| /// \brief API for overwriting files in a table. |
| /// |
| /// This API accumulates file additions and produces a new Snapshot of the table by |
| /// replacing all deleted files with the set of additions. This operation is used |
| /// to implement idempotent writes that always replace a section of a table with |
| /// new data or update/delete operations that eagerly overwrite files. |
| /// |
| /// Overwrites can be validated. The default validation mode is idempotent, |
| /// meaning the overwrite is correct and should be committed regardless of other |
| /// concurrent changes to the table. For example, this can be used for replacing |
| /// all data for day D with query results. Alternatively, this API can be |
| /// configured for overwriting certain files with their filtered versions while |
| /// ensuring no new data that would need to be filtered has been added. |
| /// |
| /// When committing, these changes are applied to the latest table snapshot. |
| /// Commit conflicts are resolved by applying the changes to the new latest |
| /// snapshot and reattempting the commit. |
| class ICEBERG_EXPORT OverwriteFiles : public MergingSnapshotUpdate { |
| public: |
| /// \brief Create a new OverwriteFiles instance. |
| /// |
| /// \param table_name The name of the table |
| /// \param ctx The transaction context to use for this update |
| /// \return A Result containing the OverwriteFiles instance or an error |
| static Result<std::shared_ptr<OverwriteFiles>> Make( |
| std::string table_name, std::shared_ptr<TransactionContext> ctx); |
| |
| ~OverwriteFiles() override; |
| |
| /// \brief Add a DataFile to the table. |
| /// |
| /// \param file A data file. |
| /// \return This OverwriteFiles for method chaining. |
| OverwriteFiles& AddFile(const std::shared_ptr<DataFile>& file); |
| |
| /// \brief Delete a DataFile from the table. |
| /// |
| /// \param file A data file. |
| /// \return This OverwriteFiles for method chaining. |
| OverwriteFiles& DeleteFile(const std::shared_ptr<DataFile>& file); |
| |
| /// \brief Delete a set of data files from the table with their respective |
| /// delete files. |
| /// |
| /// \param data_files_to_delete The data files to be deleted from the table. |
| /// \param delete_files_to_delete The delete files corresponding to the data |
| /// files to be deleted from the table. |
| /// \return This OverwriteFiles for method chaining. |
| OverwriteFiles& DeleteFiles(const DataFileSet& data_files_to_delete, |
| const DeleteFileSet& delete_files_to_delete); |
| |
| /// \brief Delete files that match an expression on data rows from the table. |
| /// |
| /// A file is selected to be deleted by the expression if it could contain any |
| /// rows that match the expression. Candidate files are selected using an |
| /// inclusive partition projection. These candidate files are deleted if all of |
| /// the rows in the file must match the expression, determined by the |
| /// expression's strict partition projection. This guarantees that files are |
| /// deleted if and only if all rows in the file must match the expression. |
| /// |
| /// Files that may contain some rows that match the expression and some rows |
| /// that do not will result in a validation error. |
| /// |
| /// \param expr An expression on rows in the table. |
| /// \return This OverwriteFiles for method chaining. |
| OverwriteFiles& OverwriteByRowFilter(std::shared_ptr<Expression> expr); |
| |
| /// \brief Set the snapshot ID used in any reads for this operation. |
| /// |
| /// Validations check changes after this snapshot ID. If the from snapshot is |
| /// not set, all ancestor snapshots through the table's initial snapshot are |
| /// validated. |
| /// |
| /// \param snapshot_id A snapshot ID. |
| /// \return This OverwriteFiles for method chaining. |
| OverwriteFiles& ValidateFromSnapshot(int64_t snapshot_id); |
| |
| /// \brief Set a conflict detection filter used to validate concurrently added |
| /// data and delete files. |
| /// |
| /// \param expr An expression on rows in the table. |
| /// \return This OverwriteFiles for method chaining. |
| OverwriteFiles& ConflictDetectionFilter(std::shared_ptr<Expression> expr); |
| |
| /// \brief Enable validation that data added concurrently does not conflict |
| /// with this commit's operation. |
| /// |
| /// This method should be called while committing non-idempotent overwrite |
| /// operations. If a concurrent operation commits a new file after the data was |
| /// read and that file might contain rows matching the specified conflict |
| /// detection filter, the overwrite operation detects this and fails. |
| /// |
| /// Calling this method with a correct conflict detection filter is required to |
| /// maintain isolation for non-idempotent overwrite operations. |
| /// |
| /// Validation uses the conflict detection filter passed to |
| /// ConflictDetectionFilter() and applies to operations that happened after the |
| /// snapshot passed to ValidateFromSnapshot(). If the conflict detection filter |
| /// is not set, any new data added concurrently will fail this overwrite |
| /// operation. |
| /// |
| /// \return This OverwriteFiles for method chaining. |
| OverwriteFiles& ValidateNoConflictingData(); |
| |
| /// \brief Enable validation that deletes that happened concurrently do not |
| /// conflict with this commit's operation. |
| /// |
| /// Validating concurrent deletes is required during non-idempotent overwrite |
| /// operations. If a concurrent operation deletes data in one of the files being |
| /// overwritten, the overwrite operation must be aborted as it may undelete rows |
| /// that were removed concurrently. |
| /// |
| /// Calling this method with a correct conflict detection filter is required to |
| /// maintain isolation for non-idempotent overwrite operations. |
| /// |
| /// Validation uses the conflict detection filter passed to |
| /// ConflictDetectionFilter() and applies to operations that happened after the |
| /// snapshot passed to ValidateFromSnapshot(). If the conflict detection filter |
| /// is not set, this operation will use the row filter provided in |
| /// OverwriteByRowFilter() to check for new delete files and will ensure there |
| /// are no conflicting deletes for data files removed via DeleteFile(). |
| /// |
| /// \return This OverwriteFiles for method chaining. |
| OverwriteFiles& ValidateNoConflictingDeletes(); |
| |
| /// \brief Signal that each file added to the table must match the overwrite |
| /// expression. |
| /// |
| /// If this method is called, each added file is validated on commit to ensure |
| /// that it matches the overwrite row filter. This is used to ensure that |
| /// writes are idempotent: files cannot be added during a commit that would not |
| /// be removed if the operation were run a second time. |
| /// |
| /// \return This OverwriteFiles for method chaining. |
| OverwriteFiles& ValidateAddedFilesMatchOverwriteFilter(); |
| |
| /// \brief Enable or disable case-sensitive expression binding for validations |
| /// that accept expressions. |
| /// |
| /// \param case_sensitive Whether expression binding should be case sensitive. |
| /// \return This OverwriteFiles for method chaining. |
| OverwriteFiles& CaseSensitive(bool case_sensitive); |
| |
| std::string operation() override; |
| |
| protected: |
| Status Validate(const TableMetadata& current_metadata, |
| const std::shared_ptr<Snapshot>& snapshot) override; |
| |
| private: |
| explicit OverwriteFiles(std::string table_name, |
| std::shared_ptr<TransactionContext> ctx); |
| |
| /// \brief Select the conflict-detection filter from the configured state. |
| std::shared_ptr<Expression> DataConflictDetectionFilter() const; |
| |
| DataFileSet deleted_data_files_; |
| bool validate_added_files_match_overwrite_filter_ = false; |
| std::optional<int64_t> starting_snapshot_id_; |
| std::shared_ptr<Expression> conflict_detection_filter_; |
| bool validate_new_data_files_ = false; |
| bool validate_new_deletes_ = false; |
| }; |
| |
| } // namespace iceberg |