Skip to content

Commit 2adb461

Browse files
authored
feat: add PendingUpdate interface for table changes (#334)
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.
1 parent c142ef9 commit 2adb461

File tree

5 files changed

+205
-5
lines changed

5 files changed

+205
-5
lines changed

src/iceberg/pending_update.h

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
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+
22+
/// \file iceberg/pending_update.h
23+
/// API for table changes using builder pattern
24+
25+
#include "iceberg/iceberg_export.h"
26+
#include "iceberg/result.h"
27+
#include "iceberg/type_fwd.h"
28+
29+
namespace iceberg {
30+
31+
/// \brief Base class for table metadata changes using builder pattern
32+
///
33+
/// This base class allows storing different types of PendingUpdate operations
34+
/// in the same collection (e.g., in Transaction). It provides the common Commit()
35+
/// interface that all updates share.
36+
///
37+
/// This matches the Java Iceberg pattern where BaseTransaction stores a
38+
/// List<PendingUpdate> without type parameters.
39+
class ICEBERG_EXPORT PendingUpdate {
40+
public:
41+
virtual ~PendingUpdate() = default;
42+
43+
/// \brief Apply and commit the pending changes to the table
44+
///
45+
/// Changes are committed by calling the underlying table's commit operation.
46+
///
47+
/// Once the commit is successful, the updated table will be refreshed.
48+
///
49+
/// \return Status::OK if the commit was successful, or an error:
50+
/// - ValidationFailed: if update cannot be applied to current metadata
51+
/// - CommitFailed: if update cannot be committed due to conflicts
52+
/// - CommitStateUnknown: if commit success state is unknown
53+
virtual Status Commit() = 0;
54+
55+
// Non-copyable, movable
56+
PendingUpdate(const PendingUpdate&) = delete;
57+
PendingUpdate& operator=(const PendingUpdate&) = delete;
58+
PendingUpdate(PendingUpdate&&) noexcept = default;
59+
PendingUpdate& operator=(PendingUpdate&&) noexcept = default;
60+
61+
protected:
62+
PendingUpdate() = default;
63+
};
64+
65+
/// \brief Template class for type-safe table metadata changes using builder pattern
66+
///
67+
/// PendingUpdateTyped extends PendingUpdate with a type-safe Apply() method that
68+
/// returns the specific result type for each operation. Subclasses implement
69+
/// specific types of table updates such as schema changes, property updates, or
70+
/// snapshot-producing operations like appends and deletes.
71+
///
72+
/// Apply() can be used to validate and inspect the uncommitted changes before
73+
/// committing. Commit() applies the changes and commits them to the table.
74+
///
75+
/// \tparam T The type of result returned by Apply()
76+
template <typename T>
77+
class ICEBERG_EXPORT PendingUpdateTyped : public PendingUpdate {
78+
public:
79+
~PendingUpdateTyped() override = default;
80+
81+
/// \brief Apply the pending changes and return the uncommitted result
82+
///
83+
/// This does not result in a permanent update.
84+
///
85+
/// \return the uncommitted changes that would be committed, or an error:
86+
/// - ValidationFailed: if pending changes cannot be applied
87+
/// - InvalidArgument: if pending changes are conflicting
88+
virtual Result<T> Apply() = 0;
89+
90+
protected:
91+
PendingUpdateTyped() = default;
92+
};
93+
94+
} // namespace iceberg

src/iceberg/result.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,9 @@ enum class ErrorKind {
3737
kInvalidArgument,
3838
kInvalidArrowData,
3939
kInvalidExpression,
40-
kInvalidSchema,
4140
kInvalidManifest,
4241
kInvalidManifestList,
42+
kInvalidSchema,
4343
kIOError,
4444
kJsonParseError,
4545
kNoSuchNamespace,
@@ -49,6 +49,7 @@ enum class ErrorKind {
4949
kNotImplemented,
5050
kNotSupported,
5151
kUnknownError,
52+
kValidationFailed,
5253
};
5354

5455
/// \brief Error with a kind and a message.
@@ -86,9 +87,9 @@ DEFINE_ERROR_FUNCTION(Invalid)
8687
DEFINE_ERROR_FUNCTION(InvalidArgument)
8788
DEFINE_ERROR_FUNCTION(InvalidArrowData)
8889
DEFINE_ERROR_FUNCTION(InvalidExpression)
89-
DEFINE_ERROR_FUNCTION(InvalidSchema)
9090
DEFINE_ERROR_FUNCTION(InvalidManifest)
9191
DEFINE_ERROR_FUNCTION(InvalidManifestList)
92+
DEFINE_ERROR_FUNCTION(InvalidSchema)
9293
DEFINE_ERROR_FUNCTION(IOError)
9394
DEFINE_ERROR_FUNCTION(JsonParseError)
9495
DEFINE_ERROR_FUNCTION(NoSuchNamespace)
@@ -98,6 +99,7 @@ DEFINE_ERROR_FUNCTION(NotFound)
9899
DEFINE_ERROR_FUNCTION(NotImplemented)
99100
DEFINE_ERROR_FUNCTION(NotSupported)
100101
DEFINE_ERROR_FUNCTION(UnknownError)
102+
DEFINE_ERROR_FUNCTION(ValidationFailed)
101103

102104
#undef DEFINE_ERROR_FUNCTION
103105

src/iceberg/test/CMakeLists.txt

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,11 +79,12 @@ add_iceberg_test(schema_test
7979

8080
add_iceberg_test(table_test
8181
SOURCES
82-
test_common.cc
8382
json_internal_test.cc
84-
table_test.cc
83+
pending_update_test.cc
8584
schema_json_test.cc
86-
table_metadata_builder_test.cc)
85+
table_metadata_builder_test.cc
86+
table_test.cc
87+
test_common.cc)
8788

8889
add_iceberg_test(expression_test
8990
SOURCES
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
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+
#include "iceberg/pending_update.h"
21+
22+
#include <gtest/gtest.h>
23+
24+
#include "iceberg/result.h"
25+
#include "iceberg/test/matchers.h"
26+
27+
namespace iceberg {
28+
29+
// Mock implementation for testing the interface
30+
class MockSnapshot {};
31+
32+
class MockPendingUpdate : public PendingUpdateTyped<MockSnapshot> {
33+
public:
34+
MockPendingUpdate() = default;
35+
36+
Result<MockSnapshot> Apply() override {
37+
if (should_fail_) {
38+
return ValidationFailed("Mock validation failed");
39+
}
40+
apply_called_ = true;
41+
return MockSnapshot{};
42+
}
43+
44+
Status Commit() override {
45+
if (should_fail_commit_) {
46+
return CommitFailed("Mock commit failed");
47+
}
48+
commit_called_ = true;
49+
return {};
50+
}
51+
52+
void SetShouldFail(bool fail) { should_fail_ = fail; }
53+
void SetShouldFailCommit(bool fail) { should_fail_commit_ = fail; }
54+
bool ApplyCalled() const { return apply_called_; }
55+
bool CommitCalled() const { return commit_called_; }
56+
57+
private:
58+
bool should_fail_ = false;
59+
bool should_fail_commit_ = false;
60+
bool apply_called_ = false;
61+
bool commit_called_ = false;
62+
};
63+
64+
TEST(PendingUpdateTest, ApplySuccess) {
65+
MockPendingUpdate update;
66+
auto result = update.Apply();
67+
EXPECT_THAT(result, IsOk());
68+
}
69+
70+
TEST(PendingUpdateTest, ApplyValidationFailed) {
71+
MockPendingUpdate update;
72+
update.SetShouldFail(true);
73+
auto result = update.Apply();
74+
EXPECT_THAT(result, IsError(ErrorKind::kValidationFailed));
75+
EXPECT_THAT(result, HasErrorMessage("Mock validation failed"));
76+
}
77+
78+
TEST(PendingUpdateTest, CommitSuccess) {
79+
MockPendingUpdate update;
80+
auto status = update.Commit();
81+
EXPECT_THAT(status, IsOk());
82+
EXPECT_TRUE(update.CommitCalled());
83+
}
84+
85+
TEST(PendingUpdateTest, CommitFailed) {
86+
MockPendingUpdate update;
87+
update.SetShouldFailCommit(true);
88+
auto status = update.Commit();
89+
EXPECT_THAT(status, IsError(ErrorKind::kCommitFailed));
90+
EXPECT_THAT(status, HasErrorMessage("Mock commit failed"));
91+
}
92+
93+
TEST(PendingUpdateTest, BaseClassPolymorphism) {
94+
std::unique_ptr<PendingUpdate> base_ptr = std::make_unique<MockPendingUpdate>();
95+
auto status = base_ptr->Commit();
96+
EXPECT_THAT(status, IsOk());
97+
}
98+
99+
} // namespace iceberg

src/iceberg/type_fwd.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,10 @@ class TableRequirement;
156156
class TableMetadataBuilder;
157157
class TableUpdateContext;
158158

159+
class PendingUpdate;
160+
template <typename T>
161+
class PendingUpdateTyped;
162+
159163
/// ----------------------------------------------------------------------------
160164
/// TODO: Forward declarations below are not added yet.
161165
/// ----------------------------------------------------------------------------

0 commit comments

Comments
 (0)