From db5330cda176b03ba7bd5b44d10bf2e05fcfdc19 Mon Sep 17 00:00:00 2001 From: Mrinal Paliwal Date: Mon, 6 Oct 2025 19:07:52 +0530 Subject: [PATCH] feat: support more types in PartitionSpec::partition_to_path --- crates/iceberg/src/spec/partition.rs | 20 +++++++++++++++++--- crates/iceberg/src/spec/transform.rs | 24 +++++++++++------------- crates/iceberg/src/spec/values.rs | 2 +- 3 files changed, 29 insertions(+), 17 deletions(-) diff --git a/crates/iceberg/src/spec/partition.rs b/crates/iceberg/src/spec/partition.rs index 128db338a..b594895cc 100644 --- a/crates/iceberg/src/spec/partition.rs +++ b/crates/iceberg/src/spec/partition.rs @@ -155,7 +155,9 @@ impl PartitionSpec { true } - pub(crate) fn partition_to_path(&self, data: &Struct, schema: SchemaRef) -> String { + /// Returns partition path string containing partition type and partition + /// value as key-value pairs. + pub fn partition_to_path(&self, data: &Struct, schema: SchemaRef) -> String { let partition_type = self.partition_type(&schema).unwrap(); let field_types = partition_type.fields(); @@ -1813,6 +1815,9 @@ mod tests { .with_fields(vec![ NestedField::required(1, "id", Type::Primitive(PrimitiveType::Int)).into(), NestedField::required(2, "name", Type::Primitive(PrimitiveType::String)).into(), + NestedField::required(3, "timestamp", Type::Primitive(PrimitiveType::Timestamp)) + .into(), + NestedField::required(4, "empty", Type::Primitive(PrimitiveType::String)).into(), ]) .build() .unwrap(); @@ -1822,14 +1827,23 @@ mod tests { .unwrap() .add_partition_field("name", "name", Transform::Identity) .unwrap() + .add_partition_field("timestamp", "ts_hour", Transform::Hour) + .unwrap() + .add_partition_field("empty", "empty_void", Transform::Void) + .unwrap() .build() .unwrap(); - let data = Struct::from_iter([Some(Literal::int(42)), Some(Literal::string("alice"))]); + let data = Struct::from_iter([ + Some(Literal::int(42)), + Some(Literal::string("alice")), + Some(Literal::int(1000)), + Some(Literal::string("empty")), + ]); assert_eq!( spec.partition_to_path(&data, schema.into()), - "id=42/name=alice" + "id=42/name=alice/ts_hour=1000/empty_void=null" ); } } diff --git a/crates/iceberg/src/spec/transform.rs b/crates/iceberg/src/spec/transform.rs index 8b6d2225a..d69d15c28 100644 --- a/crates/iceberg/src/spec/transform.rs +++ b/crates/iceberg/src/spec/transform.rs @@ -137,19 +137,17 @@ pub enum Transform { impl Transform { /// Returns a human-readable String representation of a transformed value. pub fn to_human_string(&self, field_type: &Type, value: Option<&Literal>) -> String { - if let Some(value) = value { - if let Some(value) = value.as_primitive_literal() { - let field_type = field_type.as_primitive_type().unwrap(); - let datum = Datum::new(field_type.clone(), value); - match self { - Self::Identity => datum.to_human_string(), - Self::Void => "null".to_string(), - _ => { - todo!() - } - } - } else { - "null".to_string() + let Some(value) = value else { + return "null".to_string(); + }; + + if let Some(value) = value.as_primitive_literal() { + let field_type = field_type.as_primitive_type().unwrap(); + let datum = Datum::new(field_type.clone(), value); + + match self { + Self::Void => "null".to_string(), + _ => datum.to_human_string(), } } else { "null".to_string() diff --git a/crates/iceberg/src/spec/values.rs b/crates/iceberg/src/spec/values.rs index 7f214810e..8d56604b1 100644 --- a/crates/iceberg/src/spec/values.rs +++ b/crates/iceberg/src/spec/values.rs @@ -1271,7 +1271,7 @@ impl Datum { /// /// For string literals, this returns the raw string value without quotes. /// For all other literals, it falls back to [`to_string()`]. - pub(crate) fn to_human_string(&self) -> String { + pub fn to_human_string(&self) -> String { match self.literal() { PrimitiveLiteral::String(s) => s.to_string(), _ => self.to_string(),