From e31bf410ea4e9689d890faa81290aa1ac334a23a Mon Sep 17 00:00:00 2001 From: Javier de la Torre Date: Sat, 4 Jul 2026 13:29:08 +0200 Subject: [PATCH] fix(go): geo type auto-detect should not require edges:spherical for GEOGRAPHY resolveGeoType's documented contract (and the original #117 semantics) says 'missing CRS, EPSG:4326, or unparsable CRS stays GEOGRAPHY', but the code required edges == "spherical" as well. Most GeoArrow producers (DuckDB, GeoPandas) emit a crs with no edges field, so all WGS84 data silently demoted to GEOMETRY on ingest. Auto-detect now resolves: SRID 4326 or no CRS -> GEOGRAPHY; any other SRID -> GEOMETRY (SRID survives the round trip); edges:spherical with a non-4326 SRID remains an error. Explicit ingest_geo_type is unchanged. Claude-Session: https://claude.ai/code/session_01Y3xqZXWrx9qXdPNXBzNxa2 --- go/docs/snowflake.md | 2 +- go/statement.go | 12 +++++++++--- go/statement_test.go | 12 +++++++++--- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/go/docs/snowflake.md b/go/docs/snowflake.md index 7425398..1f99f8a 100644 --- a/go/docs/snowflake.md +++ b/go/docs/snowflake.md @@ -366,7 +366,7 @@ Examples: `adbc.snowflake.statement.ingest_geo_type` : **Values:** `geography`, `geometry`, or empty string. **Default:** empty string - Which Snowflake data type to use when ingesting columns of GeoArrow extension types (`geoarrow.wkb`, `geoarrow.wkt`). If empty, then it will be detected: if the SRID is 4326 and `edges:spherical` is set, then it will be ingested as GEOGRAPHY, else GEOMETRY. If explicitly set, the driver will use the specified type. + Which Snowflake data type to use when ingesting columns of GeoArrow extension types (`geoarrow.wkb`, `geoarrow.wkt`). If empty, the type is detected per column from the CRS metadata: columns with SRID 4326 (or no CRS at all) are ingested as GEOGRAPHY, columns with any other SRID as GEOMETRY (so the SRID survives the round trip), and `edges:spherical` combined with a non-4326 SRID is an error. Most GeoArrow producers (e.g. DuckDB, GeoPandas) emit a `crs` but no `edges` field, so detection does not require `edges:spherical` for WGS84 data. If explicitly set, the driver uses the specified type for all GeoArrow columns. Can be set on the statement. diff --git a/go/statement.go b/go/statement.go index b78d5d3..796ebb4 100644 --- a/go/statement.go +++ b/go/statement.go @@ -740,9 +740,7 @@ func (opts *ingestOptions) resolveGeoType(fieldIndex int, fieldName string, extM return opts.geoType, nil } srid, edges := extractSRIDFromMeta(extMeta) - if srid == 4326 && edges == "spherical" { - return "geography", nil - } else if edges == "spherical" { + if edges == "spherical" && srid != 0 && srid != 4326 { // Snowflake GEOGRAPHY is always SRID 4326, so if the user // specified spherical edges but a different SRID, we should // error/ask them to explicitly set the geo type @@ -751,6 +749,14 @@ func (opts *ingestOptions) resolveGeoType(fieldIndex int, fieldName string, extM Code: adbc.StatusInvalidData, } } + // Missing CRS, EPSG:4326, or an unparsable CRS defaults to GEOGRAPHY, as + // documented above — most GeoArrow producers (e.g. DuckDB) emit a crs but + // no "edges" field, and requiring edges == "spherical" would silently + // demote WGS84 data to GEOMETRY. Only a concrete non-4326 SRID promotes + // the column so the SRID survives the round trip. + if srid == 0 || srid == 4326 { + return "geography", nil + } return "geometry", nil } diff --git a/go/statement_test.go b/go/statement_test.go index 4b5bc30..7a654cd 100644 --- a/go/statement_test.go +++ b/go/statement_test.go @@ -222,9 +222,13 @@ func TestExtractSRIDFromMeta(t *testing.T) { func TestResolveGeoType(t *testing.T) { opts := &ingestOptions{} + // EPSG:4326 without an "edges" field stays GEOGRAPHY: most GeoArrow + // producers (DuckDB, GeoPandas) emit a crs but no edges, and WGS84 data + // should not silently demote to GEOMETRY (matches the documented + // contract of resolveGeoType). ty, err := opts.resolveGeoType(0, "geom", `{"crs":"EPSG:4326"}`) assert.NoError(t, err) - assert.Equal(t, "geometry", ty) + assert.Equal(t, "geography", ty) ty, err = opts.resolveGeoType(0, "geom", `{"crs":"EPSG:3857"}`) assert.NoError(t, err) @@ -237,13 +241,15 @@ func TestResolveGeoType(t *testing.T) { _, err = opts.resolveGeoType(0, "geom", `{"crs":"EPSG:3857", "edges":"spherical"}`) assert.ErrorContains(t, err, `field #1 ("geom") is a GeoArrow array with spherical edges`) + // Missing or empty CRS metadata also stays GEOGRAPHY per the documented + // default. ty, err = opts.resolveGeoType(0, "geom", "") assert.NoError(t, err) - assert.Equal(t, "geometry", ty) + assert.Equal(t, "geography", ty) ty, err = opts.resolveGeoType(0, "geom", "{}") assert.NoError(t, err) - assert.Equal(t, "geometry", ty) + assert.Equal(t, "geography", ty) // explicit geoType should override CRS-derived default opts.geoType = "geometry"