Skip to content

Commit 2b308fb

Browse files
committed
feat: update properties api
1 parent 4401ceb commit 2b308fb

20 files changed

+794
-41
lines changed

src/iceberg/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ set(ICEBERG_SOURCES
4343
manifest/v2_metadata.cc
4444
manifest/v3_metadata.cc
4545
metadata_columns.cc
46+
metrics_config.cc
4647
name_mapping.cc
4748
partition_field.cc
4849
partition_spec.cc
@@ -69,6 +70,7 @@ set(ICEBERG_SOURCES
6970
transform.cc
7071
transform_function.cc
7172
type.cc
73+
update/update_properties.cc
7274
util/bucket_util.cc
7375
util/conversions.cc
7476
util/decimal.cc
@@ -134,6 +136,7 @@ add_subdirectory(catalog)
134136
add_subdirectory(expression)
135137
add_subdirectory(manifest)
136138
add_subdirectory(row)
139+
add_subdirectory(update)
137140
add_subdirectory(util)
138141

139142
if(ICEBERG_BUILD_BUNDLE)

src/iceberg/meson.build

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ iceberg_sources = files(
6565
'manifest/v2_metadata.cc',
6666
'manifest/v3_metadata.cc',
6767
'metadata_columns.cc',
68+
'metrics_config.cc',
6869
'name_mapping.cc',
6970
'partition_field.cc',
7071
'partition_spec.cc',
@@ -91,6 +92,7 @@ iceberg_sources = files(
9192
'transform.cc',
9293
'transform_function.cc',
9394
'type.cc',
95+
'update/update_properties.cc',
9496
'util/bucket_util.cc',
9597
'util/conversions.cc',
9698
'util/decimal.cc',
@@ -164,9 +166,11 @@ install_headers(
164166
'location_provider.h',
165167
'metadata_columns.h',
166168
'metrics.h',
169+
'metrics_config.h',
167170
'name_mapping.h',
168171
'partition_field.h',
169172
'partition_spec.h',
173+
'pending_update.h',
170174
'result.h',
171175
'schema_field.h',
172176
'schema.h',
@@ -187,6 +191,7 @@ install_headers(
187191
'transform.h',
188192
'type_fwd.h',
189193
'type.h',
194+
'update/update_properties.h',
190195
],
191196
subdir: 'iceberg',
192197
)

src/iceberg/metrics_config.cc

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
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/metrics_config.h"
21+
22+
#include <string>
23+
#include <unordered_map>
24+
25+
#include "iceberg/result.h"
26+
#include "iceberg/schema.h"
27+
#include "iceberg/table_properties.h"
28+
29+
namespace iceberg {
30+
31+
Status MetricsConfig::VerifyReferencedColumns(
32+
const std::unordered_map<std::string, std::string>& updates, const Schema& schema) {
33+
for (const auto& [key, value] : updates) {
34+
if (!key.starts_with(TableProperties::kMetricModeColumnConfPrefix)) {
35+
continue;
36+
}
37+
auto field_name =
38+
std::string_view(key).substr(TableProperties::kMetricModeColumnConfPrefix.size());
39+
auto field = schema.FindFieldByName(field_name);
40+
if (!field.has_value() || !field.value().has_value()) {
41+
return ValidationFailed(
42+
"Invalid metrics config, could not find column {} from table prop {} in "
43+
"schema {}",
44+
field_name, key, schema.ToString());
45+
}
46+
}
47+
return {};
48+
}
49+
50+
} // namespace iceberg

src/iceberg/metrics_config.h

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
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/metrics_config.h
23+
/// \brief Metrics configuration for Iceberg tables
24+
25+
#include <string>
26+
#include <unordered_map>
27+
28+
#include "iceberg/iceberg_export.h"
29+
#include "iceberg/result.h"
30+
#include "iceberg/type_fwd.h"
31+
32+
namespace iceberg {
33+
34+
/// \brief Configuration utilities for table metrics
35+
class ICEBERG_EXPORT MetricsConfig {
36+
public:
37+
/// \brief Verify that all referenced columns are valid
38+
/// \param updates The updates to verify
39+
/// \param schema The schema to verify against
40+
/// \return OK if all referenced columns are valid
41+
static Status VerifyReferencedColumns(
42+
const std::unordered_map<std::string, std::string>& updates, const Schema& schema);
43+
};
44+
45+
} // namespace iceberg

src/iceberg/pending_update.h

Lines changed: 7 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,16 @@ namespace iceberg {
3737
///
3838
/// This matches the Java Iceberg pattern where BaseTransaction stores a
3939
/// List<PendingUpdate> without type parameters.
40-
class ICEBERG_EXPORT PendingUpdate {
40+
class ICEBERG_EXPORT PendingUpdate : public ErrorCollector {
4141
public:
4242
virtual ~PendingUpdate() = default;
4343

44+
/// \brief Verify that the changes are valid and apply them.
45+
/// \return Status::OK if the changes are valid, or an error:
46+
/// - ValidationFailed: if pending changes cannot be applied
47+
/// - InvalidArgument: if pending changes are conflicting
48+
virtual Status Apply() = 0;
49+
4450
/// \brief Apply and commit the pending changes to the table
4551
///
4652
/// Changes are committed by calling the underlying table's commit operation.
@@ -63,33 +69,4 @@ class ICEBERG_EXPORT PendingUpdate {
6369
PendingUpdate() = default;
6470
};
6571

66-
/// \brief Template class for type-safe table metadata changes using builder pattern
67-
///
68-
/// PendingUpdateTyped extends PendingUpdate with a type-safe Apply() method that
69-
/// returns the specific result type for each operation. Subclasses implement
70-
/// specific types of table updates such as schema changes, property updates, or
71-
/// snapshot-producing operations like appends and deletes.
72-
///
73-
/// Apply() can be used to validate and inspect the uncommitted changes before
74-
/// committing. Commit() applies the changes and commits them to the table.
75-
///
76-
/// \tparam T The type of result returned by Apply()
77-
template <typename T>
78-
class ICEBERG_EXPORT PendingUpdateTyped : public PendingUpdate, public ErrorCollector {
79-
public:
80-
~PendingUpdateTyped() override = default;
81-
82-
/// \brief Apply the pending changes and return the uncommitted result
83-
///
84-
/// This does not result in a permanent update.
85-
///
86-
/// \return the uncommitted changes that would be committed, or an error:
87-
/// - ValidationFailed: if pending changes cannot be applied
88-
/// - InvalidArgument: if pending changes are conflicting
89-
virtual Result<T> Apply() = 0;
90-
91-
protected:
92-
PendingUpdateTyped() = default;
93-
};
94-
9572
} // namespace iceberg

src/iceberg/table.cc

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,14 @@
1919

2020
#include "iceberg/table.h"
2121

22-
#include <algorithm>
23-
2422
#include "iceberg/catalog.h"
2523
#include "iceberg/partition_spec.h"
2624
#include "iceberg/schema.h"
2725
#include "iceberg/sort_order.h"
2826
#include "iceberg/table_metadata.h"
2927
#include "iceberg/table_properties.h"
3028
#include "iceberg/table_scan.h"
29+
#include "iceberg/update/update_properties.h"
3130
#include "iceberg/util/macros.h"
3231

3332
namespace iceberg {
@@ -110,6 +109,10 @@ const std::vector<SnapshotLogEntry>& Table::history() const {
110109
return metadata_->snapshot_log;
111110
}
112111

112+
std::unique_ptr<UpdateProperties> Table::UpdateProperties() const {
113+
return std::make_unique<iceberg::UpdateProperties>(identifier_, catalog_, metadata_);
114+
}
115+
113116
std::unique_ptr<Transaction> Table::NewTransaction() const {
114117
throw NotImplemented("Table::NewTransaction is not implemented");
115118
}

src/iceberg/table.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,12 @@ class ICEBERG_EXPORT Table {
104104
/// \return a vector of history entries
105105
const std::vector<SnapshotLogEntry>& history() const;
106106

107+
/// \brief Create a new UpdateProperties to update table properties and commit the
108+
/// changes
109+
///
110+
/// \return a new UpdateProperties instance
111+
virtual std::unique_ptr<iceberg::UpdateProperties> UpdateProperties() const;
112+
107113
/// \brief Create a new table scan builder for this table
108114
///
109115
/// Once a table scan builder is created, it can be refined to project columns and

src/iceberg/table_metadata.cc

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -364,7 +364,35 @@ TableMetadataBuilder& TableMetadataBuilder::AssignUUID(std::string_view uuid) {
364364

365365
TableMetadataBuilder& TableMetadataBuilder::UpgradeFormatVersion(
366366
int8_t new_format_version) {
367-
throw IcebergError(std::format("{} not implemented", __FUNCTION__));
367+
// Check that the new format version is supported
368+
if (new_format_version > TableMetadata::kSupportedTableFormatVersion) {
369+
return AddError(
370+
ErrorKind::kInvalidArgument,
371+
std::format(
372+
"Cannot upgrade table to unsupported format version: v{} (supported: v{})",
373+
new_format_version, TableMetadata::kSupportedTableFormatVersion));
374+
}
375+
376+
// Check that we're not downgrading
377+
if (new_format_version < impl_->metadata.format_version) {
378+
return AddError(ErrorKind::kInvalidArgument,
379+
std::format("Cannot downgrade v{} table to v{}",
380+
impl_->metadata.format_version, new_format_version));
381+
}
382+
383+
// No-op if the version is the same
384+
if (new_format_version == impl_->metadata.format_version) {
385+
return *this;
386+
}
387+
388+
// Update the format version
389+
impl_->metadata.format_version = new_format_version;
390+
391+
// Record the change
392+
impl_->changes.push_back(
393+
std::make_unique<table::UpgradeFormatVersion>(new_format_version));
394+
395+
return *this;
368396
}
369397

370398
TableMetadataBuilder& TableMetadataBuilder::SetCurrentSchema(
@@ -472,12 +500,38 @@ TableMetadataBuilder& TableMetadataBuilder::RemovePartitionStatistics(
472500

473501
TableMetadataBuilder& TableMetadataBuilder::SetProperties(
474502
const std::unordered_map<std::string, std::string>& updated) {
475-
throw IcebergError(std::format("{} not implemented", __FUNCTION__));
503+
// If updated is empty, return early (no-op)
504+
if (updated.empty()) {
505+
return *this;
506+
}
507+
508+
// Add all updated properties to the metadata properties
509+
for (const auto& [key, value] : updated) {
510+
impl_->metadata.properties[key] = value;
511+
}
512+
513+
// Record the change
514+
impl_->changes.push_back(std::make_unique<table::SetProperties>(updated));
515+
516+
return *this;
476517
}
477518

478519
TableMetadataBuilder& TableMetadataBuilder::RemoveProperties(
479520
const std::vector<std::string>& removed) {
480-
throw IcebergError(std::format("{} not implemented", __FUNCTION__));
521+
// If removed is empty, return early (no-op)
522+
if (removed.empty()) {
523+
return *this;
524+
}
525+
526+
// Remove each property from the metadata properties
527+
for (const auto& key : removed) {
528+
impl_->metadata.properties.erase(key);
529+
}
530+
531+
// Record the change
532+
impl_->changes.push_back(std::make_unique<table::RemoveProperties>(removed));
533+
534+
return *this;
481535
}
482536

483537
TableMetadataBuilder& TableMetadataBuilder::SetLocation(std::string_view location) {

src/iceberg/table_properties.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,8 @@ class ICEBERG_EXPORT TableProperties : public ConfigBase<TableProperties> {
231231
"write.metadata.delete-after-commit.enabled", false};
232232
inline static Entry<int32_t> kMetricsMaxInferredColumnDefaults{
233233
"write.metadata.metrics.max-inferred-column-defaults", 100};
234+
inline static constexpr std::string_view kMetricModeColumnConfPrefix =
235+
"write.metadata.metrics.column.";
234236
inline static Entry<std::string> kDefaultWriteMetricsMode{
235237
"write.metadata.metrics.default", "truncate(16)"};
236238

src/iceberg/table_requirements.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "iceberg/table_metadata.h"
2525
#include "iceberg/table_requirement.h"
2626
#include "iceberg/table_update.h"
27+
#include "iceberg/util/macros.h"
2728

2829
namespace iceberg {
2930

0 commit comments

Comments
 (0)