wirybeaver commented on code in PR #3342:
URL: https://github.com/apache/iceberg-rust/pull/3342#discussion_r4462066136
##########
crates/iceberg/src/writer/file_writer/parquet_writer.rs:
##########
@@ -401,8 +489,48 @@ impl MinMaxColAggregator {
Ok(())
}
+ fn update_geo(&mut self, field_id: i32, column: &ColumnChunkMetaData) {
+ // Null-only row groups do not contribute coordinates and need no
bounding box.
+ if column.num_values() == 0
+ || column
+ .statistics()
+ .and_then(Statistics::null_count_opt)
+ .is_some_and(|nulls| nulls == column.num_values() as u64)
+ {
+ return;
+ }
+ let bbox = column
+ .geo_statistics()
+ .and_then(|stats| stats.bounding_box());
+ self.geo_bounds
+ .entry(field_id)
+ .and_modify(|state| {
+ if let (Some(bounds), Some(bbox)) = (state.as_mut(), bbox) {
+ bounds.merge(bbox);
+ } else {
+ *state = None;
+ }
+ })
+ .or_insert_with(|| bbox.map(GeospatialBounds::from));
+ }
+
/// Returns lower and upper bounds
- fn produce(self) -> (HashMap<i32, Datum>, HashMap<i32, Datum>) {
+ fn produce(mut self) -> (HashMap<i32, Datum>, HashMap<i32, Datum>) {
+ for (field_id, bounds) in self.geo_bounds {
+ if let Some(bounds) = bounds {
+ let ty = self
+ .schema
+ .field_by_id(field_id)
+ .unwrap()
+ .field_type
+ .as_primitive_type()
+ .unwrap()
+ .clone();
+ let (lower, upper) = bounds.into_datums(ty);
Review Comment:
Addressed in 88ce4168. Each geometry bounds entry now carries its
PrimitiveType with the accumulated bounds. produce() uses that stored type
directly; it no longer looks the field up in the schema again.
##########
crates/iceberg/src/writer/file_writer/parquet_writer.rs:
##########
@@ -2570,11 +2703,252 @@ mod tests {
(
parquet::basic::Type::FIXED_LEN_BYTE_ARRAY,
16,
- Some(&parquet::basic::LogicalType::Uuid)
+ Some(&LogicalType::Uuid)
)
);
}
+ #[tokio::test]
+ async fn test_geospatial_write_roundtrip() -> Result<()> {
Review Comment:
Addressed in 88ce4168. Split the long lifecycle test into named Parquet
payload/metadata round-trip, manifest bounds round-trip, and scalar-metrics
safety tests, sharing one file fixture. Added focused XY, XYZ, and
wrapping-geometry serialization tests; the wrapping case checks
negative/positive infinity for X. Missing statistics and null-only row groups
have separate, small aggregation scenarios.
##########
crates/iceberg/src/writer/file_writer/parquet_writer.rs:
##########
@@ -364,13 +443,22 @@ impl MinMaxColAggregator {
// Ignore the field if it is not in schema.
return Ok(());
};
- let Type::Primitive(ty) = ty.clone() else {
+ let Type::Primitive(ty) = ty else {
return Err(Error::new(
ErrorKind::Unexpected,
format!("Composed type {ty} is not supported for min max
aggregation."),
));
};
+ if matches!(ty, PrimitiveType::Geometry(_) |
PrimitiveType::Geography(_)) {
Review Comment:
Agreed on keeping the scope to Geometry. In 88ce4168, only Geometry consumes
Parquet bounding boxes; Geography returns without producing lower/upper bounds,
even when its column metadata includes geo_statistics. Removed the geography
union/normalization path and added a regression test for that omission.
Geography payload and logical metadata writing remain covered by the Parquet
round-trip test.
--
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: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]