Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 94 additions & 0 deletions src/iceberg/pending_update.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/*
* 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/pending_update.h
/// API for table changes using builder pattern

#include "iceberg/iceberg_export.h"
#include "iceberg/result.h"
#include "iceberg/type_fwd.h"

namespace iceberg {

/// \brief Base class for table metadata changes using builder pattern
///
/// This base class allows storing different types of PendingUpdate operations
/// in the same collection (e.g., in Transaction). It provides the common Commit()
/// interface that all updates share.
///
/// This matches the Java Iceberg pattern where BaseTransaction stores a
/// List<PendingUpdate> without type parameters.
class ICEBERG_EXPORT PendingUpdate {
public:
virtual ~PendingUpdate() = default;

/// \brief Apply and commit the pending changes to the table
///
/// Changes are committed by calling the underlying table's commit operation.
///
/// Once the commit is successful, the updated table will be refreshed.
///
/// \return Status::OK if the commit was successful, or an error:
/// - ValidationFailed: if update cannot be applied to current metadata
/// - CommitFailed: if update cannot be committed due to conflicts
/// - CommitStateUnknown: if commit success state is unknown
virtual Status Commit() = 0;

// Non-copyable, movable
PendingUpdate(const PendingUpdate&) = delete;
PendingUpdate& operator=(const PendingUpdate&) = delete;
PendingUpdate(PendingUpdate&&) noexcept = default;
PendingUpdate& operator=(PendingUpdate&&) noexcept = default;

protected:
PendingUpdate() = default;
};

/// \brief Template class for type-safe table metadata changes using builder pattern
///
/// PendingUpdateTyped extends PendingUpdate with a type-safe Apply() method that
/// returns the specific result type for each operation. Subclasses implement
/// specific types of table updates such as schema changes, property updates, or
/// snapshot-producing operations like appends and deletes.
///
/// Apply() can be used to validate and inspect the uncommitted changes before
/// committing. Commit() applies the changes and commits them to the table.
///
/// \tparam T The type of result returned by Apply()
template <typename T>
class ICEBERG_EXPORT PendingUpdateTyped : public PendingUpdate {
public:
~PendingUpdateTyped() override = default;

/// \brief Apply the pending changes and return the uncommitted result
///
/// This does not result in a permanent update.
///
/// \return the uncommitted changes that would be committed, or an error:
/// - ValidationFailed: if pending changes cannot be applied
/// - InvalidArgument: if pending changes are conflicting
virtual Result<T> Apply() = 0;

protected:
PendingUpdateTyped() = default;
};

} // namespace iceberg
6 changes: 4 additions & 2 deletions src/iceberg/result.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,9 @@ enum class ErrorKind {
kInvalidArgument,
kInvalidArrowData,
kInvalidExpression,
kInvalidSchema,
kInvalidManifest,
kInvalidManifestList,
kInvalidSchema,
kIOError,
kJsonParseError,
kNoSuchNamespace,
Expand All @@ -49,6 +49,7 @@ enum class ErrorKind {
kNotImplemented,
kNotSupported,
kUnknownError,
kValidationFailed,
};

/// \brief Error with a kind and a message.
Expand Down Expand Up @@ -86,9 +87,9 @@ DEFINE_ERROR_FUNCTION(Invalid)
DEFINE_ERROR_FUNCTION(InvalidArgument)
DEFINE_ERROR_FUNCTION(InvalidArrowData)
DEFINE_ERROR_FUNCTION(InvalidExpression)
DEFINE_ERROR_FUNCTION(InvalidSchema)
DEFINE_ERROR_FUNCTION(InvalidManifest)
DEFINE_ERROR_FUNCTION(InvalidManifestList)
DEFINE_ERROR_FUNCTION(InvalidSchema)
DEFINE_ERROR_FUNCTION(IOError)
DEFINE_ERROR_FUNCTION(JsonParseError)
DEFINE_ERROR_FUNCTION(NoSuchNamespace)
Expand All @@ -98,6 +99,7 @@ DEFINE_ERROR_FUNCTION(NotFound)
DEFINE_ERROR_FUNCTION(NotImplemented)
DEFINE_ERROR_FUNCTION(NotSupported)
DEFINE_ERROR_FUNCTION(UnknownError)
DEFINE_ERROR_FUNCTION(ValidationFailed)

#undef DEFINE_ERROR_FUNCTION

Expand Down
7 changes: 4 additions & 3 deletions src/iceberg/test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -79,11 +79,12 @@ add_iceberg_test(schema_test

add_iceberg_test(table_test
SOURCES
test_common.cc
json_internal_test.cc
table_test.cc
pending_update_test.cc
schema_json_test.cc
table_metadata_builder_test.cc)
table_metadata_builder_test.cc
table_test.cc
test_common.cc)

add_iceberg_test(expression_test
SOURCES
Expand Down
99 changes: 99 additions & 0 deletions src/iceberg/test/pending_update_test.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/*
* 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.
*/

#include "iceberg/pending_update.h"

#include <gtest/gtest.h>

#include "iceberg/result.h"
#include "iceberg/test/matchers.h"

namespace iceberg {

// Mock implementation for testing the interface
class MockSnapshot {};

class MockPendingUpdate : public PendingUpdateTyped<MockSnapshot> {
public:
MockPendingUpdate() = default;

Result<MockSnapshot> Apply() override {
if (should_fail_) {
return ValidationFailed("Mock validation failed");
}
apply_called_ = true;
return MockSnapshot{};
}

Status Commit() override {
if (should_fail_commit_) {
return CommitFailed("Mock commit failed");
}
commit_called_ = true;
return {};
}

void SetShouldFail(bool fail) { should_fail_ = fail; }
void SetShouldFailCommit(bool fail) { should_fail_commit_ = fail; }
bool ApplyCalled() const { return apply_called_; }
bool CommitCalled() const { return commit_called_; }

private:
bool should_fail_ = false;
bool should_fail_commit_ = false;
bool apply_called_ = false;
bool commit_called_ = false;
};

TEST(PendingUpdateTest, ApplySuccess) {
MockPendingUpdate update;
auto result = update.Apply();
EXPECT_THAT(result, IsOk());
}

TEST(PendingUpdateTest, ApplyValidationFailed) {
MockPendingUpdate update;
update.SetShouldFail(true);
auto result = update.Apply();
EXPECT_THAT(result, IsError(ErrorKind::kValidationFailed));
EXPECT_THAT(result, HasErrorMessage("Mock validation failed"));
}

TEST(PendingUpdateTest, CommitSuccess) {
MockPendingUpdate update;
auto status = update.Commit();
EXPECT_THAT(status, IsOk());
EXPECT_TRUE(update.CommitCalled());
}

TEST(PendingUpdateTest, CommitFailed) {
MockPendingUpdate update;
update.SetShouldFailCommit(true);
auto status = update.Commit();
EXPECT_THAT(status, IsError(ErrorKind::kCommitFailed));
EXPECT_THAT(status, HasErrorMessage("Mock commit failed"));
}

TEST(PendingUpdateTest, BaseClassPolymorphism) {
std::unique_ptr<PendingUpdate> base_ptr = std::make_unique<MockPendingUpdate>();
auto status = base_ptr->Commit();
EXPECT_THAT(status, IsOk());
}

} // namespace iceberg
4 changes: 4 additions & 0 deletions src/iceberg/type_fwd.h
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,10 @@ class TableRequirement;
class TableMetadataBuilder;
class TableUpdateContext;

class PendingUpdate;
template <typename T>
class PendingUpdateTyped;

/// ----------------------------------------------------------------------------
/// TODO: Forward declarations below are not added yet.
/// ----------------------------------------------------------------------------
Expand Down
Loading