wgtmac commented on code in PR #127:
URL: https://github.com/apache/iceberg-cpp/pull/127#discussion_r2189060171


##########
src/iceberg/avro/avro_schema_util_internal.h:
##########
@@ -144,4 +145,39 @@ std::string ToString(const ::avro::LogicalType::Type& 
logical_type);
 /// \return True if the node has a map logical type, false otherwise.
 bool HasMapLogicalType(const ::avro::NodePtr& node);
 
+/// \brief Create a new Avro node with field IDs from name mapping.
+/// \param original_node The original Avro node to copy.
+/// \param name_mapping The name mapping to apply field IDs from.
+/// \return A new Avro node with field IDs applied, or an error.
+Result<::avro::NodePtr> CreateAvroNodeWithFieldIds(const ::avro::NodePtr& 
original_node,

Review Comment:
   Can we add only the following function to this header file?
   
   ```
   Result<::avro::NodePtr> ApplyNameMapping(const ::avro::NodePtr& 
original_node,
                                            const NameMapping& name_mapping);
   ```
   
   Other functions are only internal and do not have to be in the header files.



##########
src/iceberg/avro/avro_schema_util.cc:
##########
@@ -783,4 +785,201 @@ Result<SchemaProjection> Project(const Schema& 
expected_schema,
   return SchemaProjection{std::move(field_projection.children)};
 }
 
+// Helper function to create a new Avro node with field IDs from name mapping
+Result<::avro::NodePtr> CreateAvroNodeWithFieldIds(const ::avro::NodePtr& 
original_node,
+                                                   const NameMapping& 
name_mapping) {
+  switch (original_node->type()) {
+    case ::avro::AVRO_RECORD:
+      return CreateRecordNodeWithFieldIds(original_node, name_mapping);
+    case ::avro::AVRO_ARRAY:
+      return CreateArrayNodeWithFieldIds(original_node, name_mapping);
+    case ::avro::AVRO_MAP:
+      return CreateMapNodeWithFieldIds(original_node, name_mapping);
+    case ::avro::AVRO_UNION:
+      return CreateUnionNodeWithFieldIds(original_node, name_mapping);
+    case ::avro::AVRO_BOOL:
+    case ::avro::AVRO_INT:
+    case ::avro::AVRO_LONG:
+    case ::avro::AVRO_FLOAT:
+    case ::avro::AVRO_DOUBLE:
+    case ::avro::AVRO_STRING:
+    case ::avro::AVRO_BYTES:
+    case ::avro::AVRO_FIXED:
+      // For primitive types, just return a copy
+      return original_node;
+    case ::avro::AVRO_NULL:
+    case ::avro::AVRO_ENUM:
+    default:
+      return InvalidSchema("Unsupported Avro type for field ID application: 
{}",
+                           ToString(original_node));
+  }
+}
+
+Result<::avro::NodePtr> CreateRecordNodeWithFieldIds(const ::avro::NodePtr& 
original_node,
+                                                     const NameMapping& 
name_mapping) {
+  auto new_record_node = std::make_shared<::avro::NodeRecord>();
+  new_record_node->setName(original_node->name());
+
+  for (size_t i = 0; i < original_node->leaves(); ++i) {
+    const std::string& field_name = original_node->nameAt(i);
+    ::avro::NodePtr field_node = original_node->leafAt(i);
+
+    // Try to find field ID by name in the name mapping
+    if (auto field_ref = name_mapping.Find(field_name)) {

Review Comment:
   How do we support case sensitivity? The NameMapping class does not yet 
support it. We can leave a TODO comment and support this as a followup.



##########
src/iceberg/avro/avro_schema_util.cc:
##########
@@ -783,4 +785,201 @@ Result<SchemaProjection> Project(const Schema& 
expected_schema,
   return SchemaProjection{std::move(field_projection.children)};
 }
 
+// Helper function to create a new Avro node with field IDs from name mapping
+Result<::avro::NodePtr> CreateAvroNodeWithFieldIds(const ::avro::NodePtr& 
original_node,
+                                                   const NameMapping& 
name_mapping) {
+  switch (original_node->type()) {
+    case ::avro::AVRO_RECORD:
+      return CreateRecordNodeWithFieldIds(original_node, name_mapping);
+    case ::avro::AVRO_ARRAY:
+      return CreateArrayNodeWithFieldIds(original_node, name_mapping);
+    case ::avro::AVRO_MAP:
+      return CreateMapNodeWithFieldIds(original_node, name_mapping);
+    case ::avro::AVRO_UNION:
+      return CreateUnionNodeWithFieldIds(original_node, name_mapping);
+    case ::avro::AVRO_BOOL:
+    case ::avro::AVRO_INT:
+    case ::avro::AVRO_LONG:
+    case ::avro::AVRO_FLOAT:
+    case ::avro::AVRO_DOUBLE:
+    case ::avro::AVRO_STRING:
+    case ::avro::AVRO_BYTES:
+    case ::avro::AVRO_FIXED:
+      // For primitive types, just return a copy
+      return original_node;
+    case ::avro::AVRO_NULL:
+    case ::avro::AVRO_ENUM:
+    default:
+      return InvalidSchema("Unsupported Avro type for field ID application: 
{}",
+                           ToString(original_node));
+  }
+}
+
+Result<::avro::NodePtr> CreateRecordNodeWithFieldIds(const ::avro::NodePtr& 
original_node,
+                                                     const NameMapping& 
name_mapping) {
+  auto new_record_node = std::make_shared<::avro::NodeRecord>();
+  new_record_node->setName(original_node->name());
+
+  for (size_t i = 0; i < original_node->leaves(); ++i) {
+    const std::string& field_name = original_node->nameAt(i);
+    ::avro::NodePtr field_node = original_node->leafAt(i);
+
+    // Try to find field ID by name in the name mapping
+    if (auto field_ref = name_mapping.Find(field_name)) {
+      if (field_ref->get().field_id.has_value()) {
+        // Add field ID attribute to the new node
+        ::avro::CustomAttributes attributes;
+        attributes.addAttribute(std::string(kFieldIdProp),
+                                
std::to_string(field_ref->get().field_id.value()), false);
+        new_record_node->addCustomAttributesForField(attributes);
+      }
+
+      // Recursively apply field IDs to nested fields if they exist
+      if (field_ref->get().nested_mapping && field_node->type() == 
::avro::AVRO_RECORD) {
+        const auto& nested_mapping = field_ref->get().nested_mapping;
+        auto fields_span = nested_mapping->fields();
+        std::vector<MappedField> fields_vector(fields_span.begin(), 
fields_span.end());
+        auto nested_name_mapping = NameMapping::Make(std::move(fields_vector));
+
+        ICEBERG_ASSIGN_OR_RAISE(
+            auto new_nested_node,
+            CreateAvroNodeWithFieldIds(field_node, *nested_name_mapping));
+        new_record_node->addName(field_name);
+        new_record_node->addLeaf(new_nested_node);
+      } else {
+        // Recursively apply field IDs to child nodes
+        ICEBERG_ASSIGN_OR_RAISE(auto new_field_node,
+                                CreateAvroNodeWithFieldIds(field_node, 
name_mapping));
+        new_record_node->addName(field_name);
+        new_record_node->addLeaf(new_field_node);
+      }
+    } else {
+      // Recursively apply field IDs to child nodes even if no mapping found
+      ICEBERG_ASSIGN_OR_RAISE(auto new_field_node,

Review Comment:
   Should we return error status if field_id is missing? Then we don't need to 
validate this again in the Avro reader after applying name mapping.



##########
test/avro_schema_test.cc:
##########
@@ -1070,3 +1075,135 @@ TEST(AvroSchemaProjectionTest, 
ProjectDecimalIncompatible) {
 }
 
 }  // namespace iceberg::avro
+
+// NameMapping tests for Avro schema context
+namespace iceberg::avro {
+
+std::unique_ptr<NameMapping> CreateTestNameMapping() {

Review Comment:
   Could you please revert changes in this file? Or we can move them to 
`name_mapping_test.cc`.



##########
src/iceberg/avro/avro_schema_util.cc:
##########
@@ -783,4 +785,201 @@ Result<SchemaProjection> Project(const Schema& 
expected_schema,
   return SchemaProjection{std::move(field_projection.children)};
 }
 
+// Helper function to create a new Avro node with field IDs from name mapping
+Result<::avro::NodePtr> CreateAvroNodeWithFieldIds(const ::avro::NodePtr& 
original_node,
+                                                   const NameMapping& 
name_mapping) {
+  switch (original_node->type()) {
+    case ::avro::AVRO_RECORD:
+      return CreateRecordNodeWithFieldIds(original_node, name_mapping);
+    case ::avro::AVRO_ARRAY:
+      return CreateArrayNodeWithFieldIds(original_node, name_mapping);
+    case ::avro::AVRO_MAP:
+      return CreateMapNodeWithFieldIds(original_node, name_mapping);
+    case ::avro::AVRO_UNION:
+      return CreateUnionNodeWithFieldIds(original_node, name_mapping);
+    case ::avro::AVRO_BOOL:
+    case ::avro::AVRO_INT:
+    case ::avro::AVRO_LONG:
+    case ::avro::AVRO_FLOAT:
+    case ::avro::AVRO_DOUBLE:
+    case ::avro::AVRO_STRING:
+    case ::avro::AVRO_BYTES:
+    case ::avro::AVRO_FIXED:
+      // For primitive types, just return a copy
+      return original_node;
+    case ::avro::AVRO_NULL:
+    case ::avro::AVRO_ENUM:
+    default:
+      return InvalidSchema("Unsupported Avro type for field ID application: 
{}",
+                           ToString(original_node));
+  }
+}
+
+Result<::avro::NodePtr> CreateRecordNodeWithFieldIds(const ::avro::NodePtr& 
original_node,
+                                                     const NameMapping& 
name_mapping) {
+  auto new_record_node = std::make_shared<::avro::NodeRecord>();
+  new_record_node->setName(original_node->name());
+
+  for (size_t i = 0; i < original_node->leaves(); ++i) {
+    const std::string& field_name = original_node->nameAt(i);

Review Comment:
   We need to check `i < original_node->names()` and return error if fails 
before calling `original_node->nameAt(i)` just in case of a malformed node. 
Same for other `xxxAt(i)` variants.



##########
src/iceberg/avro/avro_schema_util.cc:
##########
@@ -783,4 +785,201 @@ Result<SchemaProjection> Project(const Schema& 
expected_schema,
   return SchemaProjection{std::move(field_projection.children)};
 }
 
+// Helper function to create a new Avro node with field IDs from name mapping
+Result<::avro::NodePtr> CreateAvroNodeWithFieldIds(const ::avro::NodePtr& 
original_node,
+                                                   const NameMapping& 
name_mapping) {
+  switch (original_node->type()) {
+    case ::avro::AVRO_RECORD:
+      return CreateRecordNodeWithFieldIds(original_node, name_mapping);
+    case ::avro::AVRO_ARRAY:
+      return CreateArrayNodeWithFieldIds(original_node, name_mapping);
+    case ::avro::AVRO_MAP:
+      return CreateMapNodeWithFieldIds(original_node, name_mapping);
+    case ::avro::AVRO_UNION:
+      return CreateUnionNodeWithFieldIds(original_node, name_mapping);
+    case ::avro::AVRO_BOOL:
+    case ::avro::AVRO_INT:
+    case ::avro::AVRO_LONG:
+    case ::avro::AVRO_FLOAT:
+    case ::avro::AVRO_DOUBLE:
+    case ::avro::AVRO_STRING:
+    case ::avro::AVRO_BYTES:
+    case ::avro::AVRO_FIXED:
+      // For primitive types, just return a copy
+      return original_node;
+    case ::avro::AVRO_NULL:
+    case ::avro::AVRO_ENUM:
+    default:
+      return InvalidSchema("Unsupported Avro type for field ID application: 
{}",
+                           ToString(original_node));
+  }
+}
+
+Result<::avro::NodePtr> CreateRecordNodeWithFieldIds(const ::avro::NodePtr& 
original_node,
+                                                     const NameMapping& 
name_mapping) {
+  auto new_record_node = std::make_shared<::avro::NodeRecord>();
+  new_record_node->setName(original_node->name());
+
+  for (size_t i = 0; i < original_node->leaves(); ++i) {
+    const std::string& field_name = original_node->nameAt(i);
+    ::avro::NodePtr field_node = original_node->leafAt(i);
+
+    // Try to find field ID by name in the name mapping
+    if (auto field_ref = name_mapping.Find(field_name)) {
+      if (field_ref->get().field_id.has_value()) {
+        // Add field ID attribute to the new node
+        ::avro::CustomAttributes attributes;
+        attributes.addAttribute(std::string(kFieldIdProp),
+                                
std::to_string(field_ref->get().field_id.value()), false);
+        new_record_node->addCustomAttributesForField(attributes);
+      }
+
+      // Recursively apply field IDs to nested fields if they exist
+      if (field_ref->get().nested_mapping && field_node->type() == 
::avro::AVRO_RECORD) {
+        const auto& nested_mapping = field_ref->get().nested_mapping;
+        auto fields_span = nested_mapping->fields();
+        std::vector<MappedField> fields_vector(fields_span.begin(), 
fields_span.end());
+        auto nested_name_mapping = NameMapping::Make(std::move(fields_vector));
+
+        ICEBERG_ASSIGN_OR_RAISE(
+            auto new_nested_node,
+            CreateAvroNodeWithFieldIds(field_node, *nested_name_mapping));
+        new_record_node->addName(field_name);
+        new_record_node->addLeaf(new_nested_node);
+      } else {
+        // Recursively apply field IDs to child nodes
+        ICEBERG_ASSIGN_OR_RAISE(auto new_field_node,
+                                CreateAvroNodeWithFieldIds(field_node, 
name_mapping));
+        new_record_node->addName(field_name);
+        new_record_node->addLeaf(new_field_node);
+      }
+    } else {
+      // Recursively apply field IDs to child nodes even if no mapping found
+      ICEBERG_ASSIGN_OR_RAISE(auto new_field_node,
+                              CreateAvroNodeWithFieldIds(field_node, 
name_mapping));
+      new_record_node->addName(field_name);
+      new_record_node->addLeaf(new_field_node);
+    }
+  }
+
+  return new_record_node;
+}
+
+Result<::avro::NodePtr> CreateArrayNodeWithFieldIds(const ::avro::NodePtr& 
original_node,
+                                                    const NameMapping& 
name_mapping) {
+  if (original_node->leaves() != 1) {
+    return InvalidSchema("Array type must have exactly one leaf");
+  }
+
+  auto new_array_node = std::make_shared<::avro::NodeArray>();
+  new_array_node->setName(original_node->name());
+  new_array_node->setLogicalType(original_node->logicalType());
+
+  // Check if this is a map represented as array
+  if (original_node->logicalType().type() == ::avro::LogicalType::CUSTOM &&

Review Comment:
   Please reuse `bool HasMapLogicalType(const ::avro::NodePtr& node)` in the 
`avro_schema_util_internal.h`



##########
src/iceberg/avro/avro_schema_util.cc:
##########
@@ -783,4 +785,201 @@ Result<SchemaProjection> Project(const Schema& 
expected_schema,
   return SchemaProjection{std::move(field_projection.children)};
 }
 
+// Helper function to create a new Avro node with field IDs from name mapping
+Result<::avro::NodePtr> CreateAvroNodeWithFieldIds(const ::avro::NodePtr& 
original_node,
+                                                   const NameMapping& 
name_mapping) {

Review Comment:
   ```suggestion
                                                      const MappedField& 
mapped_field) {
   ```
   
   Two suggestions here:
   1. These utility functions can be wrapped in the anonymous namespace.
   2. They can accept `MappedField` instead of `NameMapping` to avoid creating 
NameMapping over and over again.



##########
src/iceberg/avro/avro_schema_util_internal.h:
##########
@@ -144,4 +145,39 @@ std::string ToString(const ::avro::LogicalType::Type& 
logical_type);
 /// \return True if the node has a map logical type, false otherwise.
 bool HasMapLogicalType(const ::avro::NodePtr& node);
 
+/// \brief Create a new Avro node with field IDs from name mapping.
+/// \param original_node The original Avro node to copy.
+/// \param name_mapping The name mapping to apply field IDs from.
+/// \return A new Avro node with field IDs applied, or an error.
+Result<::avro::NodePtr> CreateAvroNodeWithFieldIds(const ::avro::NodePtr& 
original_node,

Review Comment:
   Please add test cases for applying name mapping to different Avro schemas 
(record/map/array/union). I think the current implementation has some obvious 
bugs for nested types. It would be helpful if we have them.



##########
src/iceberg/avro/avro_schema_util.cc:
##########
@@ -783,4 +785,201 @@ Result<SchemaProjection> Project(const Schema& 
expected_schema,
   return SchemaProjection{std::move(field_projection.children)};
 }
 
+// Helper function to create a new Avro node with field IDs from name mapping
+Result<::avro::NodePtr> CreateAvroNodeWithFieldIds(const ::avro::NodePtr& 
original_node,
+                                                   const NameMapping& 
name_mapping) {
+  switch (original_node->type()) {
+    case ::avro::AVRO_RECORD:
+      return CreateRecordNodeWithFieldIds(original_node, name_mapping);
+    case ::avro::AVRO_ARRAY:
+      return CreateArrayNodeWithFieldIds(original_node, name_mapping);
+    case ::avro::AVRO_MAP:
+      return CreateMapNodeWithFieldIds(original_node, name_mapping);
+    case ::avro::AVRO_UNION:
+      return CreateUnionNodeWithFieldIds(original_node, name_mapping);
+    case ::avro::AVRO_BOOL:
+    case ::avro::AVRO_INT:
+    case ::avro::AVRO_LONG:
+    case ::avro::AVRO_FLOAT:
+    case ::avro::AVRO_DOUBLE:
+    case ::avro::AVRO_STRING:
+    case ::avro::AVRO_BYTES:
+    case ::avro::AVRO_FIXED:
+      // For primitive types, just return a copy
+      return original_node;
+    case ::avro::AVRO_NULL:
+    case ::avro::AVRO_ENUM:
+    default:
+      return InvalidSchema("Unsupported Avro type for field ID application: 
{}",
+                           ToString(original_node));
+  }
+}
+
+Result<::avro::NodePtr> CreateRecordNodeWithFieldIds(const ::avro::NodePtr& 
original_node,
+                                                     const NameMapping& 
name_mapping) {
+  auto new_record_node = std::make_shared<::avro::NodeRecord>();
+  new_record_node->setName(original_node->name());
+
+  for (size_t i = 0; i < original_node->leaves(); ++i) {
+    const std::string& field_name = original_node->nameAt(i);
+    ::avro::NodePtr field_node = original_node->leafAt(i);
+
+    // Try to find field ID by name in the name mapping
+    if (auto field_ref = name_mapping.Find(field_name)) {
+      if (field_ref->get().field_id.has_value()) {
+        // Add field ID attribute to the new node
+        ::avro::CustomAttributes attributes;
+        attributes.addAttribute(std::string(kFieldIdProp),
+                                
std::to_string(field_ref->get().field_id.value()), false);
+        new_record_node->addCustomAttributesForField(attributes);

Review Comment:
   We need to preserve existing attributes of original leaves.



##########
src/iceberg/avro/avro_schema_util.cc:
##########
@@ -783,4 +785,201 @@ Result<SchemaProjection> Project(const Schema& 
expected_schema,
   return SchemaProjection{std::move(field_projection.children)};
 }
 
+// Helper function to create a new Avro node with field IDs from name mapping
+Result<::avro::NodePtr> CreateAvroNodeWithFieldIds(const ::avro::NodePtr& 
original_node,
+                                                   const NameMapping& 
name_mapping) {
+  switch (original_node->type()) {
+    case ::avro::AVRO_RECORD:
+      return CreateRecordNodeWithFieldIds(original_node, name_mapping);
+    case ::avro::AVRO_ARRAY:
+      return CreateArrayNodeWithFieldIds(original_node, name_mapping);
+    case ::avro::AVRO_MAP:
+      return CreateMapNodeWithFieldIds(original_node, name_mapping);
+    case ::avro::AVRO_UNION:
+      return CreateUnionNodeWithFieldIds(original_node, name_mapping);
+    case ::avro::AVRO_BOOL:
+    case ::avro::AVRO_INT:
+    case ::avro::AVRO_LONG:
+    case ::avro::AVRO_FLOAT:
+    case ::avro::AVRO_DOUBLE:
+    case ::avro::AVRO_STRING:
+    case ::avro::AVRO_BYTES:
+    case ::avro::AVRO_FIXED:
+      // For primitive types, just return a copy
+      return original_node;
+    case ::avro::AVRO_NULL:
+    case ::avro::AVRO_ENUM:
+    default:
+      return InvalidSchema("Unsupported Avro type for field ID application: 
{}",
+                           ToString(original_node));
+  }
+}
+
+Result<::avro::NodePtr> CreateRecordNodeWithFieldIds(const ::avro::NodePtr& 
original_node,
+                                                     const NameMapping& 
name_mapping) {
+  auto new_record_node = std::make_shared<::avro::NodeRecord>();
+  new_record_node->setName(original_node->name());
+
+  for (size_t i = 0; i < original_node->leaves(); ++i) {
+    const std::string& field_name = original_node->nameAt(i);
+    ::avro::NodePtr field_node = original_node->leafAt(i);
+
+    // Try to find field ID by name in the name mapping
+    if (auto field_ref = name_mapping.Find(field_name)) {
+      if (field_ref->get().field_id.has_value()) {
+        // Add field ID attribute to the new node
+        ::avro::CustomAttributes attributes;
+        attributes.addAttribute(std::string(kFieldIdProp),
+                                
std::to_string(field_ref->get().field_id.value()), false);
+        new_record_node->addCustomAttributesForField(attributes);
+      }
+
+      // Recursively apply field IDs to nested fields if they exist
+      if (field_ref->get().nested_mapping && field_node->type() == 
::avro::AVRO_RECORD) {

Review Comment:
   This if/else block looks incorrect. We should simply pass `const 
MappedField&` to `CreateAvroNodeWithFieldIds` and let it decide how to dispatch 
the call based on the actual node type.



##########
src/iceberg/avro/avro_schema_util.cc:
##########
@@ -783,4 +785,201 @@ Result<SchemaProjection> Project(const Schema& 
expected_schema,
   return SchemaProjection{std::move(field_projection.children)};
 }
 
+// Helper function to create a new Avro node with field IDs from name mapping
+Result<::avro::NodePtr> CreateAvroNodeWithFieldIds(const ::avro::NodePtr& 
original_node,
+                                                   const NameMapping& 
name_mapping) {
+  switch (original_node->type()) {
+    case ::avro::AVRO_RECORD:
+      return CreateRecordNodeWithFieldIds(original_node, name_mapping);
+    case ::avro::AVRO_ARRAY:
+      return CreateArrayNodeWithFieldIds(original_node, name_mapping);
+    case ::avro::AVRO_MAP:
+      return CreateMapNodeWithFieldIds(original_node, name_mapping);
+    case ::avro::AVRO_UNION:
+      return CreateUnionNodeWithFieldIds(original_node, name_mapping);
+    case ::avro::AVRO_BOOL:
+    case ::avro::AVRO_INT:
+    case ::avro::AVRO_LONG:
+    case ::avro::AVRO_FLOAT:
+    case ::avro::AVRO_DOUBLE:
+    case ::avro::AVRO_STRING:
+    case ::avro::AVRO_BYTES:
+    case ::avro::AVRO_FIXED:
+      // For primitive types, just return a copy
+      return original_node;
+    case ::avro::AVRO_NULL:
+    case ::avro::AVRO_ENUM:
+    default:
+      return InvalidSchema("Unsupported Avro type for field ID application: 
{}",
+                           ToString(original_node));
+  }
+}
+
+Result<::avro::NodePtr> CreateRecordNodeWithFieldIds(const ::avro::NodePtr& 
original_node,
+                                                     const NameMapping& 
name_mapping) {
+  auto new_record_node = std::make_shared<::avro::NodeRecord>();
+  new_record_node->setName(original_node->name());
+
+  for (size_t i = 0; i < original_node->leaves(); ++i) {
+    const std::string& field_name = original_node->nameAt(i);
+    ::avro::NodePtr field_node = original_node->leafAt(i);
+
+    // Try to find field ID by name in the name mapping
+    if (auto field_ref = name_mapping.Find(field_name)) {
+      if (field_ref->get().field_id.has_value()) {
+        // Add field ID attribute to the new node
+        ::avro::CustomAttributes attributes;
+        attributes.addAttribute(std::string(kFieldIdProp),
+                                
std::to_string(field_ref->get().field_id.value()), false);
+        new_record_node->addCustomAttributesForField(attributes);

Review Comment:
   Similarly, we may need to preserve default value of each field.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscr...@iceberg.apache.org

For queries about this service, please contact Infrastructure at:
us...@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscr...@iceberg.apache.org
For additional commands, e-mail: issues-h...@iceberg.apache.org

Reply via email to