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

github-merge-queue[bot] pushed a commit to branch 
gh-readonly-queue/main/pr-11630-a8306d68e57527a6b929a15cf046bd8550e995ea
in repository https://gitbox.apache.org/repos/asf/arrow-rs.git

commit 8a9f96070f405e5a5f85a63cdc4a3d9c32a20086
Author: Spicy Shark <[email protected]>
AuthorDate: Sun Oct 18 02:25:00 2026 +0000

    fix(arrow-schema): keep map keys-sorted flag in FFI field export (#11630)
    
    # Which issue does this PR close?
    
    - Closes #11603
    
    # Rationale for this change
    
    Exporting a sorted map as a `Field` drops the keys-sorted flag. With
    `DataType::Map(entries, true)`, `FFI_ArrowSchema::try_from(&DataType)`
    sets `ARROW_FLAG_MAP_KEYS_SORTED`, but
    `FFI_ArrowSchema::try_from(&Field)` replaces it with only the nullable
    and dictionary-ordered flags, so `Field::try_from(&c_schema)` returns
    `Map(.., false)`. Struct, list, union and `Schema` children are exported
    as fields, so a sorted map inside any of them is imported as unsorted as
    well. Exporting the bare `DataType` already round-trips.
    
    # What changes are included in this PR?
    
    `TryFrom<&Field> for FFI_ArrowSchema` now sets `MAP_KEYS_SORTED` when
    the field's data type is `DataType::Map(_, true)`, the same way it sets
    the nullable and dictionary-ordered flags. `FFI_ArrowSchema::with_flags`
    is unchanged.
    
    # Are these changes tested?
    
    Yes, two unit tests in `arrow-schema/src/ffi.rs`, using the existing
    round-trip helpers. Both fail on `main` and pass with this change:
    
    - `test_map_keys_sorted_field`: a map field with sorted and unsorted
    keys, nullable and not, keeps the keys-sorted and nullable flags and its
    data type through a round trip.
    - `test_map_keys_sorted_nested`: sorted and unsorted maps round-trip as
    children of a struct, a `List`, a dense union and a `Schema`.
    
    # Are there any significant user-facing changes?
    
    No.
---
 arrow-schema/src/ffi.rs | 54 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 54 insertions(+)

diff --git a/arrow-schema/src/ffi.rs b/arrow-schema/src/ffi.rs
index 85fb327285..823a42086a 100644
--- a/arrow-schema/src/ffi.rs
+++ b/arrow-schema/src/ffi.rs
@@ -889,6 +889,10 @@ impl TryFrom<&Field> for FFI_ArrowSchema {
             flags |= Flags::DICTIONARY_ORDERED;
         }
 
+        if let DataType::Map(_, true) = field.data_type() {
+            flags |= Flags::MAP_KEYS_SORTED;
+        }
+
         let schema = FFI_ArrowSchema::try_from(field.data_type())?
             .with_name(field.name())?
             .with_flags(flags)?;
@@ -1068,6 +1072,56 @@ mod tests {
         assert!(arrow_schema.map_keys_sorted());
     }
 
+    fn map_type(keys_sorted: bool) -> DataType {
+        let entries = Field::new(
+            Field::MAP_ENTRIES_FIELD_DEFAULT_NAME,
+            DataType::Struct(Fields::from(vec![
+                Field::new(Field::MAP_KEY_FIELD_DEFAULT_NAME, DataType::Utf8, 
false),
+                Field::new(Field::MAP_VALUE_FIELD_DEFAULT_NAME, 
DataType::Int32, true),
+            ])),
+            false,
+        );
+        DataType::Map(Arc::new(entries), keys_sorted)
+    }
+
+    #[test]
+    fn test_map_keys_sorted_field() {
+        for keys_sorted in [false, true] {
+            for nullable in [false, true] {
+                let field = Field::new("m", map_type(keys_sorted), nullable);
+                let c_schema = FFI_ArrowSchema::try_from(&field).unwrap();
+                assert_eq!(c_schema.map_keys_sorted(), keys_sorted);
+                assert_eq!(c_schema.nullable(), nullable);
+                round_trip_field(field);
+            }
+        }
+    }
+
+    #[test]
+    fn test_map_keys_sorted_nested() {
+        let map = |keys_sorted| Field::new("m", map_type(keys_sorted), true);
+
+        for keys_sorted in [false, true] {
+            round_trip_type(DataType::Struct(Fields::from(vec![
+                Field::new("a", DataType::Int32, true),
+                map(keys_sorted),
+            ])));
+            round_trip_type(DataType::List(Arc::new(map(keys_sorted))));
+            round_trip_type(DataType::Union(
+                UnionFields::try_new(
+                    vec![0, 1],
+                    vec![Field::new("i", DataType::Int32, true), 
map(keys_sorted)],
+                )
+                .unwrap(),
+                UnionMode::Dense,
+            ));
+            round_trip_schema(Schema::new(vec![
+                Field::new("id", DataType::Int64, false),
+                map(keys_sorted),
+            ]));
+        }
+    }
+
     #[test]
     fn test_dictionary_ordered() {
         #[expect(deprecated)]

Reply via email to