@@ -32,7 +32,6 @@ use std::mem::{size_of, size_of_val};
3232use std:: str:: FromStr ;
3333use std:: sync:: Arc ;
3434
35- use crate :: arrow_datafusion_err;
3635use crate :: cast:: {
3736 as_decimal128_array, as_decimal256_array, as_dictionary_array,
3837 as_fixed_size_binary_array, as_fixed_size_list_array,
@@ -41,6 +40,7 @@ use crate::error::{DataFusionError, Result, _exec_err, _internal_err, _not_impl_
4140use crate :: format:: DEFAULT_CAST_OPTIONS ;
4241use crate :: hash_utils:: create_hashes;
4342use crate :: utils:: SingleRowListArrayBuilder ;
43+ use crate :: { _internal_datafusion_err, arrow_datafusion_err} ;
4444use arrow:: array:: {
4545 types:: { IntervalDayTime , IntervalMonthDayNano } ,
4646 * ,
@@ -1849,10 +1849,6 @@ impl ScalarValue {
18491849 /// Returns an error if the iterator is empty or if the
18501850 /// [`ScalarValue`]s are not all the same type
18511851 ///
1852- /// # Panics
1853- ///
1854- /// Panics if `self` is a dictionary with invalid key type
1855- ///
18561852 /// # Example
18571853 /// ```
18581854 /// use datafusion_common::ScalarValue;
@@ -3343,6 +3339,16 @@ impl ScalarValue {
33433339 arr1 == & right
33443340 }
33453341
3342+ /// Compare `self` with `other` and return an `Ordering`.
3343+ ///
3344+ /// This is the same as [`PartialOrd`] except that it returns
3345+ /// `Err` if the values cannot be compared, e.g., they have incompatible data types.
3346+ pub fn try_cmp ( & self , other : & Self ) -> Result < Ordering > {
3347+ self . partial_cmp ( other) . ok_or_else ( || {
3348+ _internal_datafusion_err ! ( "Uncomparable values: {self:?}, {other:?}" )
3349+ } )
3350+ }
3351+
33463352 /// Estimate size if bytes including `Self`. For values with internal containers such as `String`
33473353 /// includes the allocated size (`capacity`) rather than the current length (`len`)
33483354 pub fn size ( & self ) -> usize {
@@ -4761,6 +4767,32 @@ mod tests {
47614767 Ok ( ( ) )
47624768 }
47634769
4770+ #[ test]
4771+ fn test_try_cmp ( ) {
4772+ assert_eq ! (
4773+ ScalarValue :: try_cmp(
4774+ & ScalarValue :: Int32 ( Some ( 1 ) ) ,
4775+ & ScalarValue :: Int32 ( Some ( 2 ) )
4776+ )
4777+ . unwrap( ) ,
4778+ Ordering :: Less
4779+ ) ;
4780+ assert_eq ! (
4781+ ScalarValue :: try_cmp( & ScalarValue :: Int32 ( None ) , & ScalarValue :: Int32 ( Some ( 2 ) ) )
4782+ . unwrap( ) ,
4783+ Ordering :: Less
4784+ ) ;
4785+ assert_starts_with (
4786+ ScalarValue :: try_cmp (
4787+ & ScalarValue :: Int32 ( Some ( 1 ) ) ,
4788+ & ScalarValue :: Int64 ( Some ( 2 ) ) ,
4789+ )
4790+ . unwrap_err ( )
4791+ . message ( ) ,
4792+ "Uncomparable values: Int32(1), Int64(2)" ,
4793+ ) ;
4794+ }
4795+
47644796 #[ test]
47654797 fn scalar_decimal_test ( ) -> Result < ( ) > {
47664798 let decimal_value = ScalarValue :: Decimal128 ( Some ( 123 ) , 10 , 1 ) ;
@@ -7669,4 +7701,15 @@ mod tests {
76697701 ] ;
76707702 assert ! ( scalars. iter( ) . all( |s| s. is_null( ) ) ) ;
76717703 }
7704+
7705+ // `err.to_string()` depends on backtrace being present (may have backtrace appended)
7706+ // `err.strip_backtrace()` also depends on backtrace being present (may have "This was likely caused by ..." stripped)
7707+ fn assert_starts_with ( actual : impl AsRef < str > , expected_prefix : impl AsRef < str > ) {
7708+ let actual = actual. as_ref ( ) ;
7709+ let expected_prefix = expected_prefix. as_ref ( ) ;
7710+ assert ! (
7711+ actual. starts_with( expected_prefix) ,
7712+ "Expected '{actual}' to start with '{expected_prefix}'"
7713+ ) ;
7714+ }
76727715}
0 commit comments