feat: add PendingUpdate interface for table changes#334
Conversation
Add PendingUpdate<T> template class that provides a builder pattern API for constructing and committing table metadata changes. This interface follows the Java PendingUpdate design with C++ idioms: - Apply() returns Result<T> with uncommitted changes for validation - Commit() returns Status and atomically commits changes to the table - Template parameter T allows type-safe results (Snapshot, Schema, etc.) The interface is designed for future implementations like AppendFiles, UpdateSchema, UpdateProperties, and other table update operations.
| /// | ||
| /// \return Status::OK if the commit was successful, or an error status if | ||
| /// validation failed or the commit encountered conflicts | ||
| virtual Status Commit() = 0; |
There was a problem hiding this comment.
Define explicit Status codes for commit failure categories (validation failed, conflicts, commit-state-unknown) so callers can distinguish the same cases Java documents ?
There was a problem hiding this comment.
I think we have already added some. If not, we can extend it: https://github.com/apache/iceberg-cpp/blob/main/src/iceberg/result.h#L31. Is this what you expected?
There was a problem hiding this comment.
My meaning is that the status code returned by the Commit interface is in what scenario, and if the status code is clearly named, it can follow the definition in result.h.
| /// | ||
| /// \tparam T The type of result returned by Apply() | ||
| template <typename T> | ||
| class ICEBERG_EXPORT PendingUpdate { |
There was a problem hiding this comment.
Both PendingUpdateBase/PendingUpdateTyped<T> and PendingUpdate<T> have advantages and disadvantages for the caller. Does iceberg-java have scenarios where PendingUpdate is used directly?
There was a problem hiding this comment.
If we want to use the same approach in the Transaction, we need to make PendingUpdate non-templated.
There was a problem hiding this comment.
Yes, if we need to collect a list of PendingUpdate, we need PendingUpdate without template parameters. We can obtain the specific implementation class through type promotion.
There was a problem hiding this comment.
Yeah, make sense
bb42e85 to
b840e7a
Compare
Deleted member functions should be public for better error messages and to follow C++ best practices. Matches the pattern used in TableMetadataBuilder.
3c74603 to
9f8b0da
Compare
Jinchul81
left a comment
There was a problem hiding this comment.
Please don't forget to add unit tests for PendingUpdate in the follow up PRs.
| PendingUpdate() = default; | ||
|
|
||
| // Non-copyable, movable | ||
| PendingUpdate(const PendingUpdate&) = delete; |
|
|
||
| // Non-copyable, movable | ||
| PendingUpdate(const PendingUpdate&) = delete; | ||
| PendingUpdate& operator=(const PendingUpdate&) = delete; |
|
|
||
| namespace iceberg { | ||
|
|
||
| /// \brief Base class for table metadata changes using builder pattern |
There was a problem hiding this comment.
I don't prefer you to delete the context that you're using builder pattern. This is the reason why the ctors of this class are in the protected region, right?
Just added |
50d119f to
55044b3
Compare
| /// | ||
| /// This matches the Java Iceberg pattern where BaseTransaction stores a | ||
| /// List<PendingUpdate> without type parameters. | ||
| class ICEBERG_EXPORT PendingUpdateBase { |
There was a problem hiding this comment.
| class ICEBERG_EXPORT PendingUpdateBase { | |
| class ICEBERG_EXPORT PendingUpdate { |
Should we use PendingUpdate as the base name? std::vector<std::unique_ptr<PendingUpdate>> looks nicer than std::vector<std::unique_ptr<PendingUpdateBase>> IMHO.
| /// | ||
| /// \tparam T The type of result returned by Apply() | ||
| template <typename T> | ||
| class ICEBERG_EXPORT PendingUpdate : public PendingUpdateBase { |
There was a problem hiding this comment.
We can rename it to BasePendingUpdate.
There was a problem hiding this comment.
Rename it ti PendingUpdateTyped
e1de414 to
4852fcb
Compare
Added comprehensive unit tests for the PendingUpdate interface including: - Basic Apply() success and validation failure scenarios - Commit() success and failure scenarios - Base class polymorphism to verify PendingUpdateBase works correctly These tests verify the interface compiles correctly and can be properly implemented by subclasses.
The builder pattern context explains why constructors are protected - subclasses should be created through their builder methods, not directly instantiated.
Renamed: - PendingUpdateBase -> PendingUpdate (non-template base class) - PendingUpdate<T> -> PendingUpdateTyped<T> (template class) This makes the API more intuitive: std::vector<std::unique_ptr<PendingUpdate>> is cleaner than std::vector<std::unique_ptr<PendingUpdateBase>>
4852fcb to
4d560bd
Compare
wgtmac
left a comment
There was a problem hiding this comment.
Generally LGTM. I've left some minor comments. Thanks @shangxinli!
@gty404 @Jinchul81 Do you have more comments?
| kDecompressError, | ||
| kInvalid, // For general invalid errors | ||
| kInvalidArgument, | ||
| kValidationFailed, |
| DEFINE_ERROR_FUNCTION(DecompressError) | ||
| DEFINE_ERROR_FUNCTION(Invalid) | ||
| DEFINE_ERROR_FUNCTION(InvalidArgument) | ||
| DEFINE_ERROR_FUNCTION(ValidationFailed) |
| schema_json_test.cc | ||
| table_metadata_builder_test.cc) | ||
| table_metadata_builder_test.cc | ||
| pending_update_test.cc) |
There was a problem hiding this comment.
These files are unfortunately not sorted alphabetically. Could you help fix it as well?
HeartLinked
left a comment
There was a problem hiding this comment.
LGTM. I only have a minor question and comment.
| if (should_fail_commit_) { | ||
| return CommitFailed("Mock commit failed"); | ||
| } | ||
| apply_called_ = true; |
There was a problem hiding this comment.
Should we remove apply_called_ = true here? I don't think we should assume that Apply has already been called by the user when Commit is invoked.I haven't seen any content in the comments that requires users to guarantee this.
- Move kValidationFailed to the end of ErrorKind enum after kUnknownError - Reorder kInvalidSchema to come after kInvalidManifestList - Sort DEFINE_ERROR_FUNCTION calls to match enum order - Sort table_test source files alphabetically in CMakeLists.txt - Fix apply_called_ tracking to be set in Apply() method instead of Commit()

Add PendingUpdate template class that provides a builder pattern API for constructing and committing table metadata changes. This interface follows the Java PendingUpdate design with C++ idioms:
The interface is designed for future implementations like AppendFiles, UpdateSchema, UpdateProperties, and other table update operations.