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-24104-6b6cc0939ef599235e0356402492b4ab2c738e55 in repository https://gitbox.apache.org/repos/asf/datafusion.git
commit cc61491adde99acc59f334c48398ae4ea0f8d3fd Author: Ma Zhengxuan <[email protected]> AuthorDate: Sun Aug 9 10:12:26 2026 +0800 fix: support untyped NULL input for median (#24104) ## Which issue does this PR close? - Closes #24077. ## Rationale for this change `median(NULL)` worked in DataFusion 53 but regressed in DataFusion 54. The untyped NULL argument remained `DataType::Null`, causing physical planning to create a median accumulator for an unsupported input type. ## What changes are included in this PR? - Keep the existing declarative signature for `median`. - Handle `DataType::Null` in physical execution using `NoopAccumulator`. - Use a Null state field for Null input. - Fall back to the generic groups accumulator adapter for Null input. - Add a SQL logic regression test for `median(NULL)`. ## Are these changes tested? Yes. - `cargo fmt --all -- --check` - `cargo test --profile=ci --test sqllogictests -- aggregate.slt` - `cargo test -p datafusion-functions-aggregate` - `cargo clippy -p datafusion-functions-aggregate --all-targets --all-features -- -D warnings` ## Are there any user-facing changes? Yes. `median(NULL)` now returns NULL with `DataType::Null` instead of failing during physical execution. --------- Co-authored-by: mazhengxuan <[email protected]> --- datafusion/functions-aggregate/src/median.rs | 18 +++++++++++++++++- datafusion/sqllogictest/test_files/aggregate.slt | 5 +++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/datafusion/functions-aggregate/src/median.rs b/datafusion/functions-aggregate/src/median.rs index 81a3c076df..fb74da87c7 100644 --- a/datafusion/functions-aggregate/src/median.rs +++ b/datafusion/functions-aggregate/src/median.rs @@ -53,6 +53,7 @@ use datafusion_expr::{ use datafusion_expr::{EmitTo, GroupsAccumulator}; use datafusion_functions_aggregate_common::aggregate::groups_accumulator::accumulate::accumulate; use datafusion_functions_aggregate_common::aggregate::groups_accumulator::nulls::filtered_null_mask; +use datafusion_functions_aggregate_common::noop_accumulator::NoopAccumulator; use datafusion_functions_aggregate_common::utils::{GenericDistinctBuffer, Hashable}; use datafusion_macros::user_doc; use std::collections::HashMap; @@ -138,6 +139,17 @@ impl AggregateUDFImpl for Median { } fn state_fields(&self, args: StateFieldsArgs) -> Result<Vec<FieldRef>> { + if args.input_fields[0].data_type().is_null() { + return Ok(vec![ + Field::new( + format_state_name(args.name, self.name()), + DataType::Null, + true, + ) + .into(), + ]); + } + //Intermediate state is a list of the elements we have collected so far let field = Field::new_list_field(args.input_fields[0].data_type().clone(), true); let state_name = if args.is_distinct { @@ -174,6 +186,10 @@ impl AggregateUDFImpl for Median { } let dt = acc_args.expr_fields[0].data_type().clone(); + if dt.is_null() { + return Ok(Box::new(NoopAccumulator::default())); + } + downcast_integer! { dt => (helper, dt), DataType::Float16 => helper!(Float16Type, dt), @@ -192,7 +208,7 @@ impl AggregateUDFImpl for Median { } fn groups_accumulator_supported(&self, args: AccumulatorArgs) -> bool { - !args.is_distinct + !args.is_distinct && !args.expr_fields[0].data_type().is_null() } fn create_groups_accumulator( diff --git a/datafusion/sqllogictest/test_files/aggregate.slt b/datafusion/sqllogictest/test_files/aggregate.slt index 26b8a78f39..1a3e3f5aa6 100644 --- a/datafusion/sqllogictest/test_files/aggregate.slt +++ b/datafusion/sqllogictest/test_files/aggregate.slt @@ -1200,6 +1200,11 @@ select approx_median(NULL), arrow_typeof(approx_median(NULL)) from median_table; ---- NULL Null +query ?T +select median(NULL), arrow_typeof(median(NULL)); +---- +NULL Null + # median decimal statement ok create table t(c decimal(10, 4)) as values (0.0001), (0.0002), (0.0003), (0.0004), (0.0005), (0.0006); --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
