Skip to content

Commit 9c00bfa

Browse files
authored
fix: correct errors in update properties implementation (#429)
1 parent 7b95952 commit 9c00bfa

File tree

3 files changed

+56
-37
lines changed

3 files changed

+56
-37
lines changed

src/iceberg/test/update_properties_test.cc

Lines changed: 25 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919

2020
#include "iceberg/update/update_properties.h"
2121

22-
#include "iceberg/table.h"
2322
#include "iceberg/table_update.h"
2423
#include "iceberg/test/matchers.h"
2524
#include "iceberg/test/update_test_base.h"
@@ -28,11 +27,6 @@ namespace iceberg {
2827

2928
class UpdatePropertiesTest : public UpdateTestBase {};
3029

31-
TEST_F(UpdatePropertiesTest, EmptyUpdates) {
32-
ICEBERG_UNWRAP_OR_FAIL(auto update, table_->NewUpdateProperties());
33-
EXPECT_THAT(update->Commit(), IsOk());
34-
}
35-
3630
TEST_F(UpdatePropertiesTest, SetProperty) {
3731
ICEBERG_UNWRAP_OR_FAIL(auto update, table_->NewUpdateProperties());
3832
update->Set("key1", "value1").Set("key2", "value2");
@@ -43,7 +37,14 @@ TEST_F(UpdatePropertiesTest, SetProperty) {
4337
}
4438

4539
TEST_F(UpdatePropertiesTest, RemoveProperty) {
46-
ICEBERG_UNWRAP_OR_FAIL(auto update, table_->NewUpdateProperties());
40+
// First, add properties to remove
41+
ICEBERG_UNWRAP_OR_FAIL(auto setup_update, table_->NewUpdateProperties());
42+
setup_update->Set("key1", "value1").Set("key2", "value2");
43+
EXPECT_THAT(setup_update->Commit(), IsOk());
44+
45+
// Reload and remove the properties
46+
ICEBERG_UNWRAP_OR_FAIL(auto reloaded, catalog_->LoadTable(table_ident_));
47+
ICEBERG_UNWRAP_OR_FAIL(auto update, reloaded->NewUpdateProperties());
4748
update->Remove("key1").Remove("key2");
4849

4950
ICEBERG_UNWRAP_OR_FAIL(auto result, update->Apply());
@@ -69,6 +70,17 @@ TEST_F(UpdatePropertiesTest, RemoveThenSetSameKey) {
6970
EXPECT_THAT(result, HasErrorMessage("already marked for removal"));
7071
}
7172

73+
TEST_F(UpdatePropertiesTest, SetAndRemoveDifferentKeys) {
74+
ICEBERG_UNWRAP_OR_FAIL(auto update, table_->NewUpdateProperties());
75+
update->Set("key1", "value1").Remove("key2");
76+
EXPECT_THAT(update->Commit(), IsOk());
77+
78+
ICEBERG_UNWRAP_OR_FAIL(auto reloaded, catalog_->LoadTable(table_ident_));
79+
const auto& props = reloaded->properties().configs();
80+
EXPECT_EQ(props.at("key1"), "value1");
81+
EXPECT_FALSE(props.contains("key2"));
82+
}
83+
7284
TEST_F(UpdatePropertiesTest, UpgradeFormatVersionValid) {
7385
ICEBERG_UNWRAP_OR_FAIL(auto update, table_->NewUpdateProperties());
7486
update->Set("format-version", "2");
@@ -108,33 +120,20 @@ TEST_F(UpdatePropertiesTest, UpgradeFormatVersionUnsupported) {
108120
}
109121

110122
TEST_F(UpdatePropertiesTest, CommitSuccess) {
123+
ICEBERG_UNWRAP_OR_FAIL(auto empty_update, table_->NewUpdateProperties());
124+
EXPECT_THAT(empty_update->Commit(), IsOk());
125+
111126
ICEBERG_UNWRAP_OR_FAIL(auto update, table_->NewUpdateProperties());
112127
update->Set("new.property", "new.value");
128+
update->Set("format-version", "3");
113129

114130
EXPECT_THAT(update->Commit(), IsOk());
115131

116132
ICEBERG_UNWRAP_OR_FAIL(auto reloaded, catalog_->LoadTable(table_ident_));
117133
const auto& props = reloaded->properties().configs();
118134
EXPECT_EQ(props.at("new.property"), "new.value");
119-
}
120-
121-
TEST_F(UpdatePropertiesTest, FluentInterface) {
122-
ICEBERG_UNWRAP_OR_FAIL(auto update, table_->NewUpdateProperties());
123-
auto& ref = update->Set("key1", "value1").Remove("key2");
124-
125-
EXPECT_EQ(&ref, update.get());
126-
EXPECT_THAT(update->Apply(), IsOk());
127-
}
128-
129-
TEST_F(UpdatePropertiesTest, SetAndRemoveDifferentKeys) {
130-
ICEBERG_UNWRAP_OR_FAIL(auto update, table_->NewUpdateProperties());
131-
update->Set("key1", "value1").Remove("key2");
132-
EXPECT_THAT(update->Commit(), IsOk());
133-
134-
ICEBERG_UNWRAP_OR_FAIL(auto reloaded, catalog_->LoadTable(table_ident_));
135-
const auto& props = reloaded->properties().configs();
136-
EXPECT_EQ(props.at("key1"), "value1");
137-
EXPECT_FALSE(props.contains("key2"));
135+
const auto& format_version = reloaded->metadata()->format_version;
136+
EXPECT_EQ(format_version, 3);
138137
}
139138

140139
} // namespace iceberg

src/iceberg/update/update_properties.cc

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ UpdateProperties& UpdateProperties::Set(const std::string& key,
5656

5757
if (!TableProperties::reserved_properties().contains(key) ||
5858
key == TableProperties::kFormatVersion.key()) {
59-
updates_.emplace(key, value);
59+
updates_.insert_or_assign(key, value);
6060
}
6161

6262
return *this;
@@ -72,9 +72,21 @@ UpdateProperties& UpdateProperties::Remove(const std::string& key) {
7272

7373
Result<PendingUpdate::ApplyResult> UpdateProperties::Apply() {
7474
ICEBERG_RETURN_UNEXPECTED(CheckErrors());
75+
const auto& current_props = transaction_->current().properties.configs();
76+
std::unordered_map<std::string, std::string> new_properties;
77+
std::vector<std::string> removals;
78+
for (const auto& [key, value] : current_props) {
79+
if (!removals_.contains(key)) {
80+
new_properties[key] = value;
81+
}
82+
}
83+
84+
for (const auto& [key, value] : updates_) {
85+
new_properties[key] = value;
86+
}
7587

76-
auto iter = updates_.find(TableProperties::kFormatVersion.key());
77-
if (iter != updates_.end()) {
88+
auto iter = new_properties.find(TableProperties::kFormatVersion.key());
89+
if (iter != new_properties.end()) {
7890
int parsed_version = 0;
7991
const auto& val = iter->second;
8092
auto [ptr, ec] = std::from_chars(val.data(), val.data() + val.size(), parsed_version);
@@ -92,21 +104,27 @@ Result<PendingUpdate::ApplyResult> UpdateProperties::Apply() {
92104
}
93105
format_version_ = static_cast<int8_t>(parsed_version);
94106

95-
updates_.erase(iter);
107+
updates_.erase(TableProperties::kFormatVersion.key());
96108
}
97109

98110
if (auto schema = transaction_->current().Schema(); schema.has_value()) {
99111
ICEBERG_RETURN_UNEXPECTED(
100-
MetricsConfig::VerifyReferencedColumns(updates_, *schema.value()));
112+
MetricsConfig::VerifyReferencedColumns(new_properties, *schema.value()));
101113
}
102114

103115
ApplyResult result;
104116
if (!updates_.empty()) {
105117
result.updates.emplace_back(std::make_unique<table::SetProperties>(updates_));
106118
}
107119
if (!removals_.empty()) {
108-
result.updates.emplace_back(std::make_unique<table::RemoveProperties>(
109-
std::vector<std::string>{removals_.begin(), removals_.end()}));
120+
for (const auto& key : removals_) {
121+
if (current_props.contains(key)) {
122+
removals.push_back(key);
123+
}
124+
}
125+
if (!removals.empty()) {
126+
result.updates.emplace_back(std::make_unique<table::RemoveProperties>(removals));
127+
}
110128
}
111129
if (format_version_.has_value()) {
112130
result.updates.emplace_back(

src/iceberg/update/update_properties.h

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,14 +41,16 @@ class ICEBERG_EXPORT UpdateProperties : public PendingUpdate {
4141

4242
/// \brief Sets a property key to a specified value.
4343
///
44-
/// The key can not be marked for previous removal and reserved property keys will be
45-
/// ignored.
44+
/// The key must not have been previously marked for removal and reserved property keys
45+
/// will be ignored.
46+
///
47+
/// \param key The property key to set
48+
/// \param value The property value to set
49+
/// \return Reference to this UpdateProperties for chaining
4650
UpdateProperties& Set(const std::string& key, const std::string& value);
4751

4852
/// \brief Marks a property for removal.
4953
///
50-
/// The key can not be already marked for removal.
51-
///
5254
/// \param key The property key to remove
5355
/// \return Reference to this UpdateProperties for chaining
5456
UpdateProperties& Remove(const std::string& key);

0 commit comments

Comments
 (0)