This is an automated email from the ASF dual-hosted git repository.

paleolimbot pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/sedona-db.git


The following commit(s) were added to refs/heads/main by this push:
     new b64611ec feat(rust/sedona-functions): Add item SRID support to 
geometry constructors (#574)
b64611ec is described below

commit b64611ecf7465d1c5a0564d08208dbe7483a3e60
Author: Dewey Dunnington <[email protected]>
AuthorDate: Mon Feb 9 16:42:34 2026 -0600

    feat(rust/sedona-functions): Add item SRID support to geometry constructors 
(#574)
    
    Co-authored-by: Copilot <[email protected]>
---
 rust/sedona-functions/src/st_geomfromwkb.rs | 79 +++++++++++++++++++++---
 rust/sedona-functions/src/st_geomfromwkt.rs | 63 ++++++++++++++++++-
 rust/sedona-functions/src/st_point.rs       | 59 ++++++++++++++++++
 rust/sedona-functions/src/st_setsrid.rs     | 95 +++++++++++++++++++++--------
 4 files changed, 262 insertions(+), 34 deletions(-)

diff --git a/rust/sedona-functions/src/st_geomfromwkb.rs 
b/rust/sedona-functions/src/st_geomfromwkb.rs
index b218747c..38c4239b 100644
--- a/rust/sedona-functions/src/st_geomfromwkb.rs
+++ b/rust/sedona-functions/src/st_geomfromwkb.rs
@@ -27,18 +27,20 @@ use sedona_schema::{
     matchers::ArgMatcher,
 };
 
-use crate::executor::WkbExecutor;
+use crate::{executor::WkbExecutor, st_setsrid::SRIDifiedKernel};
 
 /// ST_GeomFromWKB() scalar UDF implementation
 ///
 /// An implementation of WKB reading using GeoRust's wkb crate.
 pub fn st_geomfromwkb_udf() -> SedonaScalarUDF {
+    let kernel = Arc::new(STGeomFromWKB {
+        validate: true,
+        out_type: WKB_VIEW_GEOMETRY,
+    });
+    let sridified_kernel = Arc::new(SRIDifiedKernel::new(kernel.clone()));
     SedonaScalarUDF::new(
         "st_geomfromwkb",
-        vec![Arc::new(STGeomFromWKB {
-            validate: true,
-            out_type: WKB_VIEW_GEOMETRY,
-        })],
+        vec![sridified_kernel, kernel],
         Volatility::Immutable,
         Some(doc("ST_GeomFromWKB", "Geometry")),
     )
@@ -155,14 +157,13 @@ impl SedonaScalarKernel for STGeomFromWKB {
 
 #[cfg(test)]
 mod tests {
-    use arrow_array::{ArrayRef, BinaryArray, BinaryViewArray};
+    use arrow_array::{create_array, ArrayRef, BinaryArray, BinaryViewArray};
     use datafusion_common::scalar::ScalarValue;
     use datafusion_expr::ScalarUDF;
     use rstest::rstest;
     use sedona_testing::{
         compare::{assert_array_equal, assert_scalar_equal},
-        create::create_array,
-        create::create_scalar,
+        create::{create_array, create_array_item_crs, create_array_storage, 
create_scalar},
         testers::ScalarUdfTester,
     };
 
@@ -306,6 +307,68 @@ mod tests {
         }
     }
 
+    #[test]
+    fn udf_invoke_with_array_crs() {
+        let udf = st_geomfromwkb_udf();
+        let tester = ScalarUdfTester::new(
+            udf.into(),
+            vec![
+                SedonaType::Arrow(DataType::Binary),
+                SedonaType::Arrow(DataType::Utf8),
+            ],
+        );
+
+        let return_type = tester.return_type().unwrap();
+        assert_eq!(
+            return_type,
+            SedonaType::new_item_crs(&WKB_VIEW_GEOMETRY).unwrap()
+        );
+
+        let sedona_type = WKB_GEOMETRY;
+        let wkb_array = create_array_storage(
+            &[
+                Some("POINT (0 1)"),
+                Some("POINT (2 3)"),
+                Some("POINT (4 5)"),
+                Some("POINT (6 7)"),
+                None,
+            ],
+            &sedona_type,
+        );
+        let crs_array = create_array!(
+            Utf8,
+            [
+                Some("EPSG:4326"),
+                Some("EPSG:3857"),
+                Some("EPSG:3857"),
+                Some("0"),
+                None
+            ]
+        ) as ArrayRef;
+
+        let result = tester.invoke_arrays(vec![wkb_array, crs_array]).unwrap();
+        assert_eq!(
+            &result,
+            &create_array_item_crs(
+                &[
+                    Some("POINT (0 1)"),
+                    Some("POINT (2 3)"),
+                    Some("POINT (4 5)"),
+                    Some("POINT (6 7)"),
+                    None
+                ],
+                [
+                    Some("OGC:CRS84"),
+                    Some("EPSG:3857"),
+                    Some("EPSG:3857"),
+                    None,
+                    None
+                ],
+                &WKB_VIEW_GEOMETRY
+            )
+        );
+    }
+
     #[test]
     fn geog() {
         let udf = st_geogfromwkb_udf();
diff --git a/rust/sedona-functions/src/st_geomfromwkt.rs 
b/rust/sedona-functions/src/st_geomfromwkt.rs
index 01eef0fe..4bb5655f 100644
--- a/rust/sedona-functions/src/st_geomfromwkt.rs
+++ b/rust/sedona-functions/src/st_geomfromwkt.rs
@@ -276,7 +276,7 @@ fn invoke_scalar_with_srid(
 
 #[cfg(test)]
 mod tests {
-    use arrow_array::ArrayRef;
+    use arrow_array::{create_array, ArrayRef};
     use arrow_schema::DataType;
     use datafusion_common::scalar::ScalarValue;
     use datafusion_expr::{Literal, ScalarUDF};
@@ -406,6 +406,67 @@ mod tests {
         assert_array_equal(&tester.invoke_array(array_in).unwrap(), &expected);
     }
 
+    #[test]
+    fn udf_invoke_with_array_crs() {
+        let udf = st_geomfromwkt_udf();
+        let tester = ScalarUdfTester::new(
+            udf.into(),
+            vec![
+                SedonaType::Arrow(DataType::Utf8),
+                SedonaType::Arrow(DataType::Utf8),
+            ],
+        );
+
+        let return_type = tester.return_type().unwrap();
+        assert_eq!(
+            return_type,
+            SedonaType::new_item_crs(&WKB_GEOMETRY).unwrap()
+        );
+
+        let wkt_array: ArrayRef = create_array!(
+            Utf8,
+            [
+                Some("POINT (0 1)"),
+                Some("POINT (2 3)"),
+                Some("POINT (4 5)"),
+                Some("POINT (6 7)"),
+                None
+            ]
+        );
+        let crs_array = create_array!(
+            Utf8,
+            [
+                Some("EPSG:4326"),
+                Some("EPSG:3857"),
+                Some("EPSG:3857"),
+                Some("0"),
+                None
+            ]
+        ) as ArrayRef;
+
+        let result = tester.invoke_arrays(vec![wkt_array, crs_array]).unwrap();
+        assert_eq!(
+            &result,
+            &create_array_item_crs(
+                &[
+                    Some("POINT (0 1)"),
+                    Some("POINT (2 3)"),
+                    Some("POINT (4 5)"),
+                    Some("POINT (6 7)"),
+                    None
+                ],
+                [
+                    Some("OGC:CRS84"),
+                    Some("EPSG:3857"),
+                    Some("EPSG:3857"),
+                    None,
+                    None
+                ],
+                &WKB_GEOMETRY
+            )
+        );
+    }
+
     #[test]
     fn invalid_wkt() {
         let udf = st_geomfromwkt_udf();
diff --git a/rust/sedona-functions/src/st_point.rs 
b/rust/sedona-functions/src/st_point.rs
index aedf5e34..de3e279e 100644
--- a/rust/sedona-functions/src/st_point.rs
+++ b/rust/sedona-functions/src/st_point.rs
@@ -170,6 +170,7 @@ mod tests {
     use sedona_schema::crs::lnglat;
     use sedona_schema::datatypes::Edges;
     use sedona_testing::compare::assert_array_equal;
+    use sedona_testing::create::create_array_item_crs;
     use sedona_testing::{create::create_array, testers::ScalarUdfTester};
 
     use super::*;
@@ -284,6 +285,64 @@ mod tests {
         tester.assert_scalar_result_equals_with_return_type(result, "POINT (1 
2)", return_type);
     }
 
+    #[test]
+    fn udf_invoke_with_array_crs() {
+        let udf = st_point_udf();
+        let tester = ScalarUdfTester::new(
+            udf.into(),
+            vec![
+                SedonaType::Arrow(DataType::Float64),
+                SedonaType::Arrow(DataType::Float64),
+                SedonaType::Arrow(DataType::Utf8),
+            ],
+        );
+
+        let return_type = tester.return_type().unwrap();
+        assert_eq!(
+            return_type,
+            SedonaType::new_item_crs(&WKB_GEOMETRY).unwrap()
+        );
+
+        let x_array: ArrayRef =
+            create_array!(Float64, [Some(0.0), Some(2.0), Some(4.0), 
Some(6.0), None]);
+        let y_array: ArrayRef =
+            create_array!(Float64, [Some(1.0), Some(3.0), Some(5.0), 
Some(7.0), None]);
+        let crs_array = create_array!(
+            Utf8,
+            [
+                Some("EPSG:4326"),
+                Some("EPSG:3857"),
+                Some("EPSG:3857"),
+                Some("0"),
+                None
+            ]
+        ) as ArrayRef;
+
+        let result = tester
+            .invoke_arrays(vec![x_array, y_array, crs_array])
+            .unwrap();
+        assert_eq!(
+            &result,
+            &create_array_item_crs(
+                &[
+                    Some("POINT (0 1)"),
+                    Some("POINT (2 3)"),
+                    Some("POINT (4 5)"),
+                    Some("POINT (6 7)"),
+                    None
+                ],
+                [
+                    Some("OGC:CRS84"),
+                    Some("EPSG:3857"),
+                    Some("EPSG:3857"),
+                    None,
+                    None
+                ],
+                &WKB_GEOMETRY
+            )
+        );
+    }
+
     #[test]
     fn udf_invoke_with_invalid_srid() {
         let udf = st_point_udf();
diff --git a/rust/sedona-functions/src/st_setsrid.rs 
b/rust/sedona-functions/src/st_setsrid.rs
index 0d0e09fb..78fe9ae6 100644
--- a/rust/sedona-functions/src/st_setsrid.rs
+++ b/rust/sedona-functions/src/st_setsrid.rs
@@ -36,7 +36,10 @@ use datafusion_expr::{
 };
 use sedona_common::sedona_internal_err;
 use sedona_expr::{
-    item_crs::{make_item_crs, parse_item_crs_arg, 
parse_item_crs_arg_type_strip_crs},
+    item_crs::{
+        make_item_crs, parse_item_crs_arg, parse_item_crs_arg_type,
+        parse_item_crs_arg_type_strip_crs,
+    },
     scalar_udf::{ScalarKernelRef, SedonaScalarKernel, SedonaScalarUDF},
 };
 use sedona_geometry::transform::CrsEngine;
@@ -317,38 +320,61 @@ impl SedonaScalarKernel for SRIDifiedKernel {
             None => return Ok(None),
         };
 
-        let crs = match scalar_args[orig_args_len] {
-            Some(crs) => crs,
-            None => return Ok(None),
-        };
-        let new_crs = match crs.cast_to(&DataType::Utf8) {
-            Ok(ScalarValue::Utf8(Some(crs))) => {
-                if crs == "0" {
-                    None
-                } else {
-                    validate_crs(&crs, None)?;
-                    deserialize_crs(&crs)?
+        // If we have a scalar CRS, the output type has a type-level CRS
+        if let Some(scalar_crs) = scalar_args[orig_args_len] {
+            let new_crs = match scalar_crs.cast_to(&DataType::Utf8) {
+                Ok(ScalarValue::Utf8(Some(crs))) => {
+                    if crs == "0" {
+                        None
+                    } else {
+                        validate_crs(&crs, None)?;
+                        deserialize_crs(&crs)?
+                    }
+                }
+                Ok(ScalarValue::Utf8(None)) => None,
+                Ok(_) | Err(_) => {
+                    return sedona_internal_err!("Can't cast Crs {scalar_crs:?} 
to Utf8")
+                }
+            };
+
+            match &mut inner_result {
+                SedonaType::Wkb(_, crs) => *crs = new_crs,
+                SedonaType::WkbView(_, crs) => *crs = new_crs,
+                _ => {
+                    return sedona_internal_err!("Return type must be Wkb or 
WkbView");
                 }
             }
-            Ok(ScalarValue::Utf8(None)) => None,
-            Ok(_) | Err(_) => return sedona_internal_err!("Can't cast Crs 
{crs:?} to Utf8"),
-        };
 
-        match &mut inner_result {
-            SedonaType::Wkb(_, crs) => *crs = new_crs,
-            SedonaType::WkbView(_, crs) => *crs = new_crs,
-            _ => {
-                return sedona_internal_err!("Return type must be Wkb or 
WkbView");
+            Ok(Some(inner_result))
+        } else {
+            // If we have a column CRS, the output has an item level CRS
+
+            // Assert that the output type had a Crs of None. If the inner 
kernel returned
+            // a specific output CRS it is likely that the SRIDified kernel is 
not appropriate.
+            match &inner_result {
+                SedonaType::Wkb(_, crs) | SedonaType::WkbView(_, crs) => {
+                    if crs.is_some() {
+                        return sedona_internal_err!(
+                            "Return type of SRIDifiedKernel inner specified an 
explicit CRS"
+                        );
+                    }
+                }
+                _ => {
+                    return sedona_internal_err!("Return type must be Wkb or 
WkbView");
+                }
             }
-        }
 
-        Ok(Some(inner_result))
+            Ok(Some(SedonaType::new_item_crs(&inner_result)?))
+        }
     }
 
-    fn invoke_batch(
+    fn invoke_batch_from_args(
         &self,
         arg_types: &[SedonaType],
         args: &[ColumnarValue],
+        return_type: &SedonaType,
+        _num_rows: usize,
+        _config_options: Option<&ConfigOptions>,
     ) -> Result<ColumnarValue> {
         let orig_args_len = arg_types.len() - 1;
         let orig_arg_types = &arg_types[..orig_args_len];
@@ -358,8 +384,10 @@ impl SedonaScalarKernel for SRIDifiedKernel {
         // Note that, this behavior is different from PostGIS.
         let result = self.inner.invoke_batch(orig_arg_types, orig_args)?;
 
-        // If the specified SRID is NULL, the result is also NULL.
+        // If the CRS input is a scalar, we can return the inner result as-is 
except
+        // for the NULL case.
         if let ColumnarValue::Scalar(sc) = &args[orig_args_len] {
+            // If the specified SRID is NULL, the result is also NULL.
             if sc.is_null() {
                 // Create the same length of NULLs as the original result.
                 let len = match &result {
@@ -374,9 +402,26 @@ impl SedonaScalarKernel for SRIDifiedKernel {
                 let new_array = builder.finish();
                 return Ok(ColumnarValue::Array(Arc::new(new_array)));
             }
+
+            Ok(result)
+        } else {
+            let (item_type, _) = parse_item_crs_arg_type(return_type)?;
+            let normalized_crs_value = 
normalize_crs_array(&args[orig_args_len], None)?;
+            make_item_crs(
+                &item_type,
+                result,
+                &ColumnarValue::Array(normalized_crs_value),
+                crs_input_nulls(&args[orig_args_len]),
+            )
         }
+    }
 
-        Ok(result)
+    fn invoke_batch(
+        &self,
+        _arg_types: &[SedonaType],
+        _args: &[ColumnarValue],
+    ) -> Result<ColumnarValue> {
+        sedona_internal_err!("Should not be called because 
invoke_batch_from_args() is implemented")
     }
 
     fn return_type(&self, _args: &[SedonaType]) -> Result<Option<SedonaType>> {

Reply via email to