diff --git a/datafusion/expr/src/type_coercion/functions.rs b/datafusion/expr/src/type_coercion/functions.rs index ec3ab6f441827..663ae8c9f31f6 100644 --- a/datafusion/expr/src/type_coercion/functions.rs +++ b/datafusion/expr/src/type_coercion/functions.rs @@ -1158,6 +1158,19 @@ fn coerced_from<'a>( { Some(type_into.clone()) } + // coerced run-end encoded the same way as dictionary: callers of a + // signature built from `Exact`/`OneOf` only know the logical value + // type, not that the argument happens to be physically run-encoded. + (_, RunEndEncoded(_, value_type)) + if coerced_from(type_into, value_type.data_type()).is_some() => + { + Some(type_into.clone()) + } + (RunEndEncoded(_, value_type), _) + if coerced_from(value_type.data_type(), type_from).is_some() => + { + Some(type_into.clone()) + } // coerced into type_into (Int8, Null | Int8) => Some(type_into.clone()), (Int16, Null | Int8 | Int16 | UInt8) => Some(type_into.clone()), @@ -1617,6 +1630,38 @@ mod tests { ); } + #[test] + fn test_coerced_from_run_end_encoded() { + let run_end_encoded_of = |value_type: DataType| { + DataType::RunEndEncoded( + Field::new("run_ends", DataType::Int32, false).into(), + Field::new("values", value_type, true).into(), + ) + }; + + let type_into = run_end_encoded_of(DataType::UInt32); + let type_from = DataType::Int64; + assert_eq!(coerced_from(&type_into, &type_from), None); + + let type_from = run_end_encoded_of(DataType::UInt32); + let type_into = DataType::Int64; + assert_eq!( + coerced_from(&type_into, &type_from), + Some(type_into.clone()) + ); + + // Signature candidates for functions like `date_bin` are plain + // Timestamp, but a REE-encoded column (e.g. a segment written with + // REE-dict encoding for that field) should still coerce against + // them via the wrapped value type. + let type_from = run_end_encoded_of(DataType::Timestamp(TimeUnit::Nanosecond, None)); + let type_into = DataType::Timestamp(TimeUnit::Nanosecond, None); + assert_eq!( + coerced_from(&type_into, &type_from), + Some(type_into.clone()) + ); + } + #[test] fn test_get_valid_types_array_and_array() -> Result<()> { let function = "array_and_array"; diff --git a/datafusion/optimizer/src/analyzer/type_coercion.rs b/datafusion/optimizer/src/analyzer/type_coercion.rs index feadc0370bfd5..2887486179f6c 100644 --- a/datafusion/optimizer/src/analyzer/type_coercion.rs +++ b/datafusion/optimizer/src/analyzer/type_coercion.rs @@ -1083,6 +1083,8 @@ fn extract_window_frame_target_type(col_type: &DataType) -> Result { Ok(DataType::Interval(IntervalUnit::MonthDayNano)) } else if let DataType::Dictionary(_, value_type) = col_type { extract_window_frame_target_type(value_type) + } else if let DataType::RunEndEncoded(_, value_type) = col_type { + extract_window_frame_target_type(value_type.data_type()) } else { internal_err!("Cannot run range queries on datatype: {col_type}") }