This is an automated email from the ASF dual-hosted git repository.

gangwu pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/iceberg-cpp.git


The following commit(s) were added to refs/heads/main by this push:
     new 4ac1fa10 feat: impl PartitionSpec::PartitionPath (#500)
4ac1fa10 is described below

commit 4ac1fa10ac4c4f10fe0980e91a18ad12f5468d43
Author: wzhuo <[email protected]>
AuthorDate: Thu Jan 8 21:55:18 2026 +0800

    feat: impl PartitionSpec::PartitionPath (#500)
---
 src/iceberg/partition_spec.cc           | 22 +++++++++++++++
 src/iceberg/partition_spec.h            |  3 ++
 src/iceberg/test/partition_spec_test.cc | 49 +++++++++++++++++++++++++++++++++
 3 files changed, 74 insertions(+)

diff --git a/src/iceberg/partition_spec.cc b/src/iceberg/partition_spec.cc
index 3bfd0ffb..9c38d0c5 100644
--- a/src/iceberg/partition_spec.cc
+++ b/src/iceberg/partition_spec.cc
@@ -26,16 +26,19 @@
 #include <map>
 #include <memory>
 #include <ranges>
+#include <sstream>
 #include <unordered_map>
 #include <utility>
 
 #include "iceberg/result.h"
+#include "iceberg/row/partition_values.h"
 #include "iceberg/schema.h"
 #include "iceberg/schema_field.h"
 #include "iceberg/transform.h"
 #include "iceberg/util/formatter.h"  // IWYU pragma: keep
 #include "iceberg/util/macros.h"
 #include "iceberg/util/type_util.h"
+#include "iceberg/util/url_encoder.h"
 
 namespace iceberg {
 
@@ -98,6 +101,25 @@ Result<std::unique_ptr<StructType>> 
PartitionSpec::PartitionType(
   return std::make_unique<StructType>(std::move(partition_fields));
 }
 
+Result<std::string> PartitionSpec::PartitionPath(const PartitionValues& data) 
const {
+  ICEBERG_PRECHECK(fields_.size() == data.num_fields(),
+                   "Partition spec and data mismatch, expected field num {}, 
got {}",
+                   fields_.size(), data.num_fields());
+  std::stringstream ss;
+  for (int32_t i = 0; i < fields_.size(); ++i) {
+    ICEBERG_ASSIGN_OR_RAISE(auto value, data.ValueAt(i));
+    if (i > 0) {
+      ss << "/";
+    }
+    // TODO(zhuo.wang): transform for partition value, will be fixed after 
transform util
+    // is ready
+    std::string partition_value = value.get().ToString();
+    ss << UrlEncoder::Encode(fields_[i].name()) << "="
+       << UrlEncoder::Encode(partition_value);
+  }
+  return ss.str();
+}
+
 bool PartitionSpec::CompatibleWith(const PartitionSpec& other) const {
   if (Equals(other)) {
     return true;
diff --git a/src/iceberg/partition_spec.h b/src/iceberg/partition_spec.h
index ae10dfcc..0fb8814b 100644
--- a/src/iceberg/partition_spec.h
+++ b/src/iceberg/partition_spec.h
@@ -64,6 +64,9 @@ class ICEBERG_EXPORT PartitionSpec : public util::Formattable 
{
   /// \brief Get the partition type binding to the input schema.
   Result<std::unique_ptr<StructType>> PartitionType(const Schema& schema) 
const;
 
+  /// \brief Get the partition path for the given partition data.
+  Result<std::string> PartitionPath(const PartitionValues& data) const;
+
   /// \brief Returns true if this spec is equivalent to the other, with 
partition field
   /// ids ignored. That is, if both specs have the same number of fields, 
field order,
   /// field name, source columns, and transforms.
diff --git a/src/iceberg/test/partition_spec_test.cc 
b/src/iceberg/test/partition_spec_test.cc
index e2024504..ea3ea6e1 100644
--- a/src/iceberg/test/partition_spec_test.cc
+++ b/src/iceberg/test/partition_spec_test.cc
@@ -28,6 +28,7 @@
 
 #include "iceberg/json_internal.h"
 #include "iceberg/partition_field.h"
+#include "iceberg/row/partition_values.h"
 #include "iceberg/schema.h"
 #include "iceberg/schema_field.h"
 #include "iceberg/test/matchers.h"
@@ -425,4 +426,52 @@ TEST(PartitionSpecTest, 
ValidateRedundantPartitionsIdentityTransforms) {
   }
 }
 
+TEST(PartitionSpecTest, PartitionPath) {
+  // Create a schema with different field types
+  auto id_field = SchemaField::MakeRequired(1, "id", int64());
+  auto name_field = SchemaField::MakeRequired(2, "name", string());
+  auto ts_field = SchemaField::MakeRequired(3, "ts", timestamp());
+  Schema schema({id_field, name_field, ts_field}, Schema::kInitialSchemaId);
+
+  // Create partition fields
+  PartitionField id_field_partition(1, 1000, "id_partition", 
Transform::Identity());
+  PartitionField name_field_partition(2, 1001, "name_partition", 
Transform::Identity());
+  PartitionField ts_field_partition(3, 1002, "ts_partition", Transform::Day());
+
+  // Create partition spec
+  ICEBERG_UNWRAP_OR_FAIL(
+      auto spec,
+      PartitionSpec::Make(schema, 1,
+                          {id_field_partition, name_field_partition, 
ts_field_partition},
+                          false));
+
+  {
+    // Invalid partition values
+    PartitionValues part_data({Literal::Int(123)});
+    auto result = spec->PartitionPath(part_data);
+    EXPECT_THAT(result, IsError(ErrorKind::kInvalidArgument));
+    EXPECT_THAT(result, HasErrorMessage("Partition spec and data mismatch"));
+  }
+
+  {
+    // Normal partition values
+    PartitionValues part_data(
+        {Literal::Int(123), Literal::String("val2"), Literal::Date(19489)});
+    ICEBERG_UNWRAP_OR_FAIL(auto path, spec->PartitionPath(part_data));
+    std::string expected =
+        "id_partition=123/name_partition=%22val2%22/ts_partition=19489";
+    EXPECT_EQ(expected, path);
+  }
+
+  {
+    // Partition values with special characters
+    PartitionValues part_data(
+        {Literal::Int(123), Literal::String("val#2"), Literal::Date(19489)});
+    ICEBERG_UNWRAP_OR_FAIL(auto path, spec->PartitionPath(part_data));
+    std::string expected =
+        "id_partition=123/name_partition=%22val%232%22/ts_partition=19489";
+    EXPECT_EQ(expected, path);
+  }
+}
+
 }  // namespace iceberg

Reply via email to