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

Jefffrey pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow-rs.git


The following commit(s) were added to refs/heads/main by this push:
     new c9e83cc864 fix(arrow-ord): return an error for nested dictionary 
comparisons (#11104)
c9e83cc864 is described below

commit c9e83cc86427fdbb33b058939adf334fae0f08d5
Author: Jaideep Pyne <[email protected]>
AuthorDate: Thu Sep 17 05:56:15 2026 +0530

    fix(arrow-ord): return an error for nested dictionary comparisons (#11104)
    
    Fixes #11094.
    
    ## Summary
    
    - After unwrapping one dictionary/REE layer, `compare_op` now errors if
    the leftover type is still `Dictionary` or `RunEndEncoded`.
    - Add a regression: `eq` on `Dictionary<Int32, Dictionary<Int32, Utf8>>`
    returns `InvalidArgumentError` instead of panicking.
    
    ## Why
    
    `DataType::is_nested` is false for `Dictionary` and `RunEndEncoded`.
    Nested dictionaries therefore passed the existing nested/type-mismatch
    checks and fell into `downcast_primitive_array!`'s `unreachable!()` arm,
    taking down the caller thread. Other unsupported inputs already return
    `Err`.
    
    The check is on the leftover type after the one-level unwrap so
    primitive REE comparisons keep working.
    
    ## Validation
    
    - `cargo test -p arrow-ord --lib cmp::`
---
 arrow-ord/src/cmp.rs | 23 +++++++++++++++++++++++
 1 file changed, 23 insertions(+)

diff --git a/arrow-ord/src/cmp.rs b/arrow-ord/src/cmp.rs
index 6c9d6115c6..e651c5f0b8 100644
--- a/arrow-ord/src/cmp.rs
+++ b/arrow-ord/src/cmp.rs
@@ -257,6 +257,15 @@ fn compare_op(op: Op, lhs: &dyn Datum, rhs: &dyn Datum) -> 
Result<BooleanArray,
         return Err(ArrowError::InvalidArgumentError(format!(
             "Nested comparison: {l_t} {op} {r_t} (hint: use make_comparator 
instead)"
         )));
+    } else if matches!(l_t, Dictionary(_, _) | RunEndEncoded(_, _))
+        || matches!(r_t, Dictionary(_, _) | RunEndEncoded(_, _))
+    {
+        // One dictionary/REE layer is unwrapped above. A leftover dictionary
+        // (nested dict) or REE is not nested according to 
`DataType::is_nested`,
+        // so without this check `downcast_primitive_array!` panics.
+        return Err(ArrowError::InvalidArgumentError(format!(
+            "Invalid comparison operation: {l_t} {op} {r_t}"
+        )));
     } else if l_t != r_t {
         return Err(ArrowError::InvalidArgumentError(format!(
             "Invalid comparison operation: {l_t} {op} {r_t}"
@@ -1180,6 +1189,20 @@ mod tests {
         neq(&col.slice(0, col.len() - 1), &col.slice(1, col.len() - 
1)).unwrap();
     }
 
+    #[test]
+    fn test_nested_dictionary_eq_returns_error() {
+        let inner = DictionaryArray::new(
+            Int32Array::from(vec![0, 1]),
+            Arc::new(StringArray::from(vec!["a", "b"])),
+        );
+        let outer = DictionaryArray::new(Int32Array::from(vec![0, 1]), 
Arc::new(inner));
+        let err = eq(&outer, &outer).expect_err("nested dictionary must not 
panic");
+        assert!(
+            matches!(err, ArrowError::InvalidArgumentError(_)),
+            "unexpected error: {err}"
+        );
+    }
+
     #[test]
     fn test_string_view_mixed_lt() {
         let a = arrow_array::StringViewArray::from(vec![

Reply via email to