laskoviymishka commented on code in PR #1235:
URL: https://github.com/apache/iceberg-go/pull/1235#discussion_r3443416422


##########
table/arrow_utils.go:
##########
@@ -1944,6 +1944,9 @@ func geoArrowMetadataToIcebergType(meta 
geoarrow.Metadata) (iceberg.Type, error)
                return nil, err
        }
 
+       // Missing GeoArrow edges use the planar default, which reads back as

Review Comment:
   The arrow-rs half checks out (arrow-rs#10065 reads no-edges as Geometry), 
but the PyIceberg framing isn't quite right — PyIceberg has no Arrow→Iceberg 
geo path that inspects GeoArrow extension metadata; it goes off the Iceberg 
schema in file metadata. There's a sharper point hiding here too: PyIceberg 
writes Geography(vincenty/karney/thomas/andoyer) *without* an edges member, and 
this branch decodes those as Geometry — a silent downgrade. I'd cite arrow-rs 
for the planar default and name the PyIceberg case as a known interop gap 
rather than a shared default (and maybe file a follow-up for the downgrade 
itself).



##########
table/internal/parquet_files_test.go:
##########
@@ -658,26 +713,42 @@ func TestParquetGeoArrowExtensionMetadataRoundTrip(t 
*testing.T) {
 
        roundTripSchema := tbl.Schema()
 
-       origGeomType := arrowSchema.Field(1).Type.(*geoarrow.WKBType)
-       origGeogType := arrowSchema.Field(2).Type.(*geoarrow.WKBType)
-       rtGeomField := roundTripSchema.Field(1)
-       rtGeogField := roundTripSchema.Field(2)
-
-       rtGeomExt := rtGeomField.Type.(arrow.ExtensionType)
-       rtGeogExt := rtGeogField.Type.(arrow.ExtensionType)
-       rtGeomType := rtGeomExt.(*geoarrow.WKBType)
-       rtGeogType := rtGeogExt.(*geoarrow.WKBType)
-
-       assert.Equal(t, origGeomType.ExtensionName(), rtGeomExt.ExtensionName())
-       assert.Equal(t, origGeogType.ExtensionName(), rtGeogExt.ExtensionName())
-       assert.True(t, arrow.TypeEqual(origGeomType.StorageType(), 
rtGeomType.StorageType()))
-       assert.True(t, arrow.TypeEqual(origGeogType.StorageType(), 
rtGeogType.StorageType()))
-       assert.Equal(t, origGeomType.Metadata(), rtGeomType.Metadata())
-       assert.Equal(t, origGeogType.Metadata(), rtGeogType.Metadata())
-       assert.Equal(t, geoarrow.CRSTypeSRID, rtGeomType.Metadata().CRSType)
-       assert.Equal(t, geoarrow.EdgePlanar, rtGeomType.Metadata().Edges)
-       assert.Equal(t, geoarrow.CRSTypeSRID, rtGeogType.Metadata().CRSType)
-       assert.Equal(t, geoarrow.EdgeVincenty, rtGeogType.Metadata().Edges)
+       tests := []struct {
+               name     string
+               idx      int
+               wantMeta geoarrow.Metadata
+               wantType geoarrow.EdgeInterpolation
+       }{
+               {
+                       name: "explicit_srid_geometry", idx: 1,
+                       wantMeta: geoarrow.Metadata{CRS: geoArrowCRS("4326"), 
Edges: geoarrow.EdgePlanar},
+                       wantType: geoarrow.EdgePlanar,
+               },
+               {
+                       name: "explicit_srid_geography", idx: 2,
+                       wantMeta: geoarrow.Metadata{CRS: geoArrowCRS("4326"), 
Edges: geoarrow.EdgeVincenty},
+                       wantType: geoarrow.EdgeVincenty,
+               },
+               {
+                       name: "default_crs_geometry", idx: 3,
+                       wantMeta: geoarrow.Metadata{CRS: 
geoArrowCRS("OGC:CRS84"), Edges: geoarrow.EdgePlanar},
+                       wantType: geoarrow.EdgePlanar,
+               },
+               {
+                       name: "default_crs_geography", idx: 4,
+                       wantMeta: geoarrow.Metadata{CRS: 
geoArrowCRS("OGC:CRS84"), Edges: geoarrow.EdgeSpherical},
+                       wantType: geoarrow.EdgeSpherical,
+               },
+       }
+
+       for _, tt := range tests {
+               t.Run("metadata/"+tt.name, func(t *testing.T) {
+                       origType := assertGeoArrowWKBType(t, 
arrowSchema.Field(tt.idx).Type, arrow.BinaryTypes.Binary, tt.wantMeta)
+                       rtType := assertGeoArrowWKBType(t, 
roundTripSchema.Field(tt.idx).Type, arrow.BinaryTypes.Binary, tt.wantMeta)
+                       assert.Equal(t, origType.Metadata(), rtType.Metadata())
+                       assert.Equal(t, tt.wantType, rtType.Metadata().Edges)

Review Comment:
   `tt.wantType` is always equal to `tt.wantMeta.Edges`, and 
`assertGeoArrowWKBType` already asserts that value against 
`rtType.Metadata().Edges` — so this line is a duplicate assertion. It also 
implies the two can vary independently, which invites a future editor to update 
one and not the other. I'd drop the `wantType` field and this assert.



##########
table/internal/parquet_files_test.go:
##########
@@ -658,26 +713,42 @@ func TestParquetGeoArrowExtensionMetadataRoundTrip(t 
*testing.T) {
 
        roundTripSchema := tbl.Schema()
 
-       origGeomType := arrowSchema.Field(1).Type.(*geoarrow.WKBType)
-       origGeogType := arrowSchema.Field(2).Type.(*geoarrow.WKBType)
-       rtGeomField := roundTripSchema.Field(1)
-       rtGeogField := roundTripSchema.Field(2)
-
-       rtGeomExt := rtGeomField.Type.(arrow.ExtensionType)
-       rtGeogExt := rtGeogField.Type.(arrow.ExtensionType)
-       rtGeomType := rtGeomExt.(*geoarrow.WKBType)
-       rtGeogType := rtGeogExt.(*geoarrow.WKBType)
-
-       assert.Equal(t, origGeomType.ExtensionName(), rtGeomExt.ExtensionName())
-       assert.Equal(t, origGeogType.ExtensionName(), rtGeogExt.ExtensionName())
-       assert.True(t, arrow.TypeEqual(origGeomType.StorageType(), 
rtGeomType.StorageType()))
-       assert.True(t, arrow.TypeEqual(origGeogType.StorageType(), 
rtGeogType.StorageType()))
-       assert.Equal(t, origGeomType.Metadata(), rtGeomType.Metadata())
-       assert.Equal(t, origGeogType.Metadata(), rtGeogType.Metadata())
-       assert.Equal(t, geoarrow.CRSTypeSRID, rtGeomType.Metadata().CRSType)
-       assert.Equal(t, geoarrow.EdgePlanar, rtGeomType.Metadata().Edges)
-       assert.Equal(t, geoarrow.CRSTypeSRID, rtGeogType.Metadata().CRSType)
-       assert.Equal(t, geoarrow.EdgeVincenty, rtGeogType.Metadata().Edges)
+       tests := []struct {
+               name     string
+               idx      int
+               wantMeta geoarrow.Metadata
+               wantType geoarrow.EdgeInterpolation
+       }{
+               {
+                       name: "explicit_srid_geometry", idx: 1,
+                       wantMeta: geoarrow.Metadata{CRS: geoArrowCRS("4326"), 
Edges: geoarrow.EdgePlanar},
+                       wantType: geoarrow.EdgePlanar,
+               },
+               {
+                       name: "explicit_srid_geography", idx: 2,
+                       wantMeta: geoarrow.Metadata{CRS: geoArrowCRS("4326"), 
Edges: geoarrow.EdgeVincenty},
+                       wantType: geoarrow.EdgeVincenty,
+               },
+               {
+                       name: "default_crs_geometry", idx: 3,
+                       wantMeta: geoarrow.Metadata{CRS: 
geoArrowCRS("OGC:CRS84"), Edges: geoarrow.EdgePlanar},
+                       wantType: geoarrow.EdgePlanar,
+               },
+               {
+                       name: "default_crs_geography", idx: 4,
+                       wantMeta: geoarrow.Metadata{CRS: 
geoArrowCRS("OGC:CRS84"), Edges: geoarrow.EdgeSpherical},
+                       wantType: geoarrow.EdgeSpherical,
+               },
+       }
+
+       for _, tt := range tests {
+               t.Run("metadata/"+tt.name, func(t *testing.T) {
+                       origType := assertGeoArrowWKBType(t, 
arrowSchema.Field(tt.idx).Type, arrow.BinaryTypes.Binary, tt.wantMeta)
+                       rtType := assertGeoArrowWKBType(t, 
roundTripSchema.Field(tt.idx).Type, arrow.BinaryTypes.Binary, tt.wantMeta)
+                       assert.Equal(t, origType.Metadata(), rtType.Metadata())

Review Comment:
   `assertGeoArrowWKBType` only checks CRS and Edges, never CRSType, so the 
table doesn't pin it anywhere. The old code asserted `CRSTypeSRID` for the 
geom/geog SRID cases; that's gone, and the two new default-CRS columns expect 
`CRSTypeAuthorityCode` with nothing verifying it. This orig-vs-rt equality only 
compares the two computed sides, so a regression that corrupts CRSType on both 
sides slips through. I'd thread `want.CRSType` through the helper (assert when 
non-empty) and set it on all four `wantMeta` structs.



##########
table/internal/parquet_files_test.go:
##########
@@ -604,6 +605,42 @@ func TestDecimalPhysicalTypes(t *testing.T) {
        }
 }
 
+func geoArrowCRS(s string) json.RawMessage {
+       raw, _ := json.Marshal(s)
+
+       return raw
+}
+
+func assertGeoArrowWKBType(t *testing.T, dt arrow.DataType, storage 
arrow.DataType, want geoarrow.Metadata) *geoarrow.WKBType {
+       t.Helper()
+
+       wkbType, ok := dt.(*geoarrow.WKBType)
+       require.True(t, ok, "expected *geoarrow.WKBType, got %T", dt)
+       assert.Equal(t, "geoarrow.wkb", wkbType.ExtensionName())
+       assert.True(t, arrow.TypeEqual(storage, wkbType.StorageType()))
+       assert.Equal(t, want.CRS, wkbType.Metadata().CRS)
+       assert.Equal(t, want.Edges, wkbType.Metadata().Edges)

Review Comment:
   `EdgePlanar` is the zero value (`""`), so the geometry-side Edges assertions 
pass even if a refactor stopped setting Edges explicitly. Pre-existing property 
of the library, not something you introduced — but the new assertions inherit 
it. Asserting the inverse for the geometry cases (e.g. `meta.Edges != 
EdgeSpherical`) would make it actually catch a regression. Non-blocking.



##########
table/arrow_utils.go:
##########
@@ -683,8 +683,8 @@ func (c convertToArrow) VisitGeometry(g 
iceberg.GeometryType) arrow.Field {
 
 func (c convertToArrow) VisitGeography(g iceberg.GeographyType) arrow.Field {
        meta := icebergCRSToGeoArrowMetadata(g.CRS())
-       // Always add an edge to differentiate between Geography and Geometry 
arrow fields.
-       // Note that the edge convention is a best-effort hint and planar 
geography from other clients won't round-trip through Arrow alone.
+       // Always add an edge algorithm so Arrow metadata can distinguish

Review Comment:
   The new wording reads like the edge convention guarantees a correct round 
trip, but that only holds when iceberg-go is the writer. The old caveat was 
load-bearing — for files written by other clients that omit edges for 
non-spherical geography, the round trip through Arrow alone is lossy. I'd keep 
a trimmed version of that note rather than drop it.



-- 
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]

Reply via email to