feat: support geometry and geography types - #2933
Conversation
|
In case you are interested, PR #2019 (CLOSED as stale) also has code related to geo types. |
paleolimbot
left a comment
There was a problem hiding this comment.
There are a few details around the conversion of types, but this is looking good to me!
paleolimbot
left a comment
There was a problem hiding this comment.
I am not qualified to approve this from the icberg-rust end of things, but the geography type conversion seems solid (I suggested some optional improvements to errors inline). Thank you!
Happy to review any follow-ups on the statistics/pruning end of things if you are interested (or to attempt them if you aren't!).
I kept arrow-rs WkbType rather than introducing an Iceberg-specific copy because parquet 58.4 consumes its own WkbType; replacing only the Iceberg Arrow type would not fix the pre-59.1 Geography behavior and could make Parquet logical-type writing incompatible.
I think this is fine, although anybody who wants to write Geography will have to write invalid metadata into Iceberg (and will receive invalid metadata that will be rejected when reading using the arrow reader).
The workaround for this is to rewrite the metadata just for the Parquet reader, but I get how it's not worth adding that here since it will automatically resolve when arrow-rs is bumped. Our workaround for the write side is here:
Can this be documented somehow?
|
@paleolimbot Thanks for calling this out. I documented the arrow-rs 58.x Geography metadata limitation in 9a5d028 on the public Arrow schema conversion APIs. The docs now explain both directions: standards-compliant GeoArrow |
|
Great! Can this be unmarked as draft? (i.e., is it ready for review from an iceberg committer?) |
9a5d028 to
3ae7041
Compare
|
It seems like there's a merge conflict here. When that's fixed and CI is clean I'm happy to go fishing for iceberg-rust committers for review since this is a huge feature for us that will unblock a lot of things! |
3ae7041 to
bb2e601
Compare
@paleolimbot @wirybeaver I am have a very high level understanding of the Geo data type details, but this comment in paleolimbot's review confuses me. Does this mean that as this PR currently stands if a Java Iceberg reader attempts to read a Geo type data file created by this Rust library the Reader will break? Is the current implementation inter-mixable with Iceberg Reader & Writer implementations from the Java & Golang communities? |
The reader will not break; however, until We can inline that fix here or we can merge as is and get started on the rest of the geospatial support with a disclaimer. I don't mind which one (I'm happy to inline the fix as a PR into this one if that's helpful). |
|
DataFusion 55.0 is about to be released pretty soon (voting thread). I think we should just wait for datafusion 55.0 and arrow upgrade |
|
Just linking #2958, as that should have the requisite arrow-rs version. |
|
I'd like to see this PR get some more attention and ultimately have support for the Geo types in iceberg-rust. Thank you for working on this, and for everyone who have provided reviews so far! With that said, the PR looks very large at the moment - can we find good ways to break it down and incrementally merge? Variant so far has had success splitting into these streams: add ability to recognize the Variant type in tables and gracefully reject it / work around it (#2188), add the simpler unshredded read path for Variant (#2880), following up with shredded read and then writes. @EwoutH shared some information previously which might be useful for breaking this down. #1884 (comment) I'd like to take a look at this PR soon, but I think a good first PR to get merged in would be identifying the type and gracefully rejecting. In the meantime, after we cut 0.11.x it should allow #2958 to be merged soon after. |
Given that this requires some domain expertise to review (e.g., from the spatial side), I'd prefer to have this as a unit so that I can keep track of spatial correctness. |
|
#2598 get merged. I am working on the fix |
bb2e601 to
c1d5762
Compare
3f3a885 to
5951359
Compare
dannycjones
left a comment
There was a problem hiding this comment.
Thanks again work working on this, @wirybeaver! I've taken a look, although I apologize if any comments don't make sense - I'm new to geospatial types.
I don't see any read tests - is that left intentionally out of scope?
| assert!(data_file.lower_bounds().is_empty()); | ||
| assert!(data_file.upper_bounds().is_empty()); |
There was a problem hiding this comment.
Can you include an assertion message about why the bounds are empty? Is it simply because the field is optional, and this is left as a follow-up?
There was a problem hiding this comment.
I see its a follow-up, a code comment to confirm that would be good. #2933 (comment)
| fn edge_interpolation_algorithm_to_wkb_edges(algorithm: EdgeInterpolationAlgorithm) -> WkbEdges { | ||
| match algorithm { | ||
| EdgeInterpolationAlgorithm::Spherical => WkbEdges::Spherical, | ||
| EdgeInterpolationAlgorithm::Vincenty => WkbEdges::Vincenty, | ||
| EdgeInterpolationAlgorithm::Thomas => WkbEdges::Thomas, | ||
| EdgeInterpolationAlgorithm::Andoyer => WkbEdges::Andoyer, | ||
| EdgeInterpolationAlgorithm::Karney => WkbEdges::Karney, | ||
| } | ||
| } |
There was a problem hiding this comment.
Why not impl From<EdgeInterpolationAlgorithm> for WkbEdges? That way we get idiomatic/easy translation from the iceberg-rust type into the Parquet type (and the same for any future file format equivalent).
Same in reverse: impl From<WkbEdges> for EdgeInterpolationAlgorithm.
| PrimitiveType::Geometry(geometry) => match geometry.crs() { | ||
| Some(crs) => write!(f, "geometry({crs})"), | ||
| None => write!(f, "geometry"), | ||
| }, |
There was a problem hiding this comment.
I don't see a variant in the spec without CRS provided. Should we write the default?
There was a problem hiding this comment.
I know at least Spark rejects plain GEOMETRY but I don't know the relationship to iceberg.
| ( | ||
| PrimitiveType::Geometry(_) | PrimitiveType::Geography(_), | ||
| JsonValue::String(_), | ||
| ) => Err(Error::new( | ||
| ErrorKind::DataInvalid, | ||
| "Geometry and geography defaults must be null", | ||
| )), |
There was a problem hiding this comment.
This seems wrong. While we may not allow defaults, there is a valid deserialization from JSON in the spec.
We need to add the check elsewhere in the code to disallow defaults.
| wkb_edges_to_edge_interpolation_algorithm( | ||
| wkb_type.metadata().algorithm.unwrap_or_default(), | ||
| ), |
There was a problem hiding this comment.
Given this is the Parquet default, I'd rather we explicitly default for Iceberg.
| wkb_edges_to_edge_interpolation_algorithm( | |
| wkb_type.metadata().algorithm.unwrap_or_default(), | |
| ), | |
| wkb_edges_to_edge_interpolation_algorithm( | |
| wkb_type.metadata().algorithm.unwrap_or(WkbEdges::Spherical), | |
| ), |
| for i in 0..fields.len() { | ||
| let field = &fields[i]; | ||
| let field_type = &field_results[i]; | ||
| let field_type = self.apply_field_extension_type(field, &field_results[i])?; |
There was a problem hiding this comment.
(sorry, this comment isn't super actionable)
I need to better understand Variant's approach here. It appears to attach the field extension type earlier (I think because of field ID shenanigans), and I'm wondering if we should align or not. If we choose not to align, I think a comment is necessary on why they are separate.
| Type::Variant(_) => { | ||
| // A variant column's storage is a struct; tag the field with the canonical | ||
| // `arrow.parquet.variant` extension type so consumers read it as a Variant, not a struct. | ||
| arrow_field = arrow_field.with_extension_type(VariantExtensionType); | ||
| } |
There was a problem hiding this comment.
I think we should update this to be try_with_extension_type for consistency (or update geospatial types to use with_extension_type if justified).
paleolimbot
left a comment
There was a problem hiding this comment.
Just a look from the spatial end...I think the translations for None CRSes aren't quite correct (this was one of the errors that was fixed by the upstream parquet-geospatial change). Thank you!
Co-authored-by: Dewey Dunnington <10995762+paleolimbot@users.noreply.github.com>
|
Add @paleolimbot as the co-author for the substantial review. Will check other folks review later on. |
Summary
This draft PR adds Iceberg Geometry/Geography primitive type support by reusing arrow-rs/parquet-geospatial support instead of introducing a local geospatial model.
GeometryTypeandGeographyTypewith an Iceberg-ownedEdgeInterpolationAlgorithm, translating toparquet_geospatial::WkbEdgesat the Arrow boundary.Related issues
Related to #2411 and #1884.
Supersedes #2653, which was closed automatically by the stale bot. Existing review discussion remains available there.