You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@datafusion.apache.org by gi...@apache.org on 2026/08/04 09:28:56 UTC
(datafusion) branch main updated: fix(ffi): preserve aggregate null-handling support (#23908)
This is an automated email from the ASF dual-hosted git repository.
github-merge-queue[bot] pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/datafusion.git
The following commit(s) were added to refs/heads/main by this push:
new db0c31bfe1 fix(ffi): preserve aggregate null-handling support (#23908)
db0c31bfe1 is described below
commit db0c31bfe16628ed6118ea3fc5d4002c8f2365fb
Author: Amogh Ramesh <ra...@gmail.com>
AuthorDate: Tue Aug 4 14:34:14 2026 +0530
fix(ffi): preserve aggregate null-handling support (#23908)
## Which issue does this PR close?
Part of #22331.
## Rationale for this change
`ForeignAggregateUDF` inherits the default
`supports_null_handling_clause`, so producer overrides are lost across
the FFI boundary.
## What changes are included in this PR?
- Forward `supports_null_handling_clause` through `FFI_AggregateUDF`.
- Use `first_value` as the positive case and `sum` as the negative case
in forced-foreign and dynamic-library tests.
## Are these changes tested?
- `cargo test -p datafusion-ffi --features integration-tests`
- `cargo clippy --all-targets --all-features -- -D warnings`
## Are there any user-facing changes?
The FFI ABI changes. Foreign libraries must rebuild against the new
DataFusion version.
---
datafusion/ffi/src/tests/mod.rs | 9 +++++++--
datafusion/ffi/src/tests/udf_udaf_udwf.rs | 7 +++++++
datafusion/ffi/src/udaf/mod.rs | 28 ++++++++++++++++++++++++++++
datafusion/ffi/tests/ffi_udaf.rs | 16 ++++++++++++++++
4 files changed, 58 insertions(+), 2 deletions(-)
diff --git a/datafusion/ffi/src/tests/mod.rs b/datafusion/ffi/src/tests/mod.rs
index d372dcf917..59bcc861d0 100644
--- a/datafusion/ffi/src/tests/mod.rs
+++ b/datafusion/ffi/src/tests/mod.rs
@@ -31,8 +31,9 @@ use datafusion_expr::{Expr, TableType};
use datafusion_physical_plan::ExecutionPlan;
use sync_provider::create_sync_table_provider;
use udf_udaf_udwf::{
- create_ffi_abs_func, create_ffi_random_func, create_ffi_rank_func,
- create_ffi_stddev_func, create_ffi_sum_func, create_ffi_table_func,
+ create_ffi_abs_func, create_ffi_first_value_func, create_ffi_random_func,
+ create_ffi_rank_func, create_ffi_stddev_func, create_ffi_sum_func,
+ create_ffi_table_func,
};
use crate::catalog_provider::FFI_CatalogProvider;
@@ -117,6 +118,9 @@ pub struct ForeignLibraryModule {
pub create_context_aware_optimizer_rule: extern "C" fn() -> FFI_PhysicalOptimizerRule,
pub version: extern "C" fn() -> u64,
+
+ /// Create an aggregate UDAF using first_value
+ pub create_first_value_udaf: extern "C" fn() -> FFI_AggregateUDF,
}
pub fn create_test_schema() -> Arc<Schema> {
@@ -266,5 +270,6 @@ pub extern "C" fn datafusion_ffi_get_module() -> ForeignLibraryModule {
create_context_aware_optimizer_rule:
physical_optimizer::create_context_aware_optimizer_rule,
version: super::version,
+ create_first_value_udaf: create_ffi_first_value_func,
}
}
diff --git a/datafusion/ffi/src/tests/udf_udaf_udwf.rs b/datafusion/ffi/src/tests/udf_udaf_udwf.rs
index a84df52b8d..04d6fb26c1 100644
--- a/datafusion/ffi/src/tests/udf_udaf_udwf.rs
+++ b/datafusion/ffi/src/tests/udf_udaf_udwf.rs
@@ -27,6 +27,7 @@ use datafusion_expr::{
};
use datafusion_functions::math::abs::AbsFunc;
use datafusion_functions::math::random::RandomFunc;
+use datafusion_functions_aggregate::first_last::FirstValue;
use datafusion_functions_aggregate::stddev::Stddev;
use datafusion_functions_aggregate::sum::Sum;
use datafusion_functions_table::generate_series::RangeFunc;
@@ -184,6 +185,12 @@ pub(crate) extern "C" fn create_ffi_sum_func() -> FFI_AggregateUDF {
udaf.into()
}
+pub(crate) extern "C" fn create_ffi_first_value_func() -> FFI_AggregateUDF {
+ let udaf: Arc<AggregateUDF> = Arc::new(FirstValue::new().into());
+
+ udaf.into()
+}
+
pub(crate) extern "C" fn create_ffi_stddev_func() -> FFI_AggregateUDF {
let udaf: Arc<AggregateUDF> = Arc::new(Stddev::new().into());
diff --git a/datafusion/ffi/src/udaf/mod.rs b/datafusion/ffi/src/udaf/mod.rs
index c4f8fb1254..b3a087e5d0 100644
--- a/datafusion/ffi/src/udaf/mod.rs
+++ b/datafusion/ffi/src/udaf/mod.rs
@@ -145,6 +145,10 @@ pub struct FFI_AggregateUDF {
/// the foreign interface. See [`crate::get_library_marker_id`] and
/// the crate's `README.md` for more information.
pub library_marker_id: extern "C" fn() -> usize,
+
+ /// FFI equivalent to [`AggregateUDF::supports_null_handling_clause`]
+ pub supports_null_handling_clause:
+ unsafe extern "C" fn(udaf: &FFI_AggregateUDF) -> bool,
}
unsafe impl Send for FFI_AggregateUDF {}
@@ -327,6 +331,12 @@ unsafe extern "C" fn order_sensitivity_fn_wrapper(
unsafe { udaf.inner().order_sensitivity().into() }
}
+unsafe extern "C" fn supports_null_handling_clause_fn_wrapper(
+ udaf: &FFI_AggregateUDF,
+) -> bool {
+ unsafe { udaf.inner().supports_null_handling_clause() }
+}
+
unsafe extern "C" fn coerce_types_fn_wrapper(
udaf: &FFI_AggregateUDF,
arg_types: SVec<WrappedSchema>,
@@ -401,6 +411,7 @@ impl From<Arc<AggregateUDF>> for FFI_AggregateUDF {
release: release_fn_wrapper,
private_data: Box::into_raw(private_data) as *mut c_void,
library_marker_id: crate::get_library_marker_id,
+ supports_null_handling_clause: supports_null_handling_clause_fn_wrapper,
}
}
}
@@ -595,6 +606,10 @@ impl AggregateUDFImpl for ForeignAggregateUDF {
unsafe { (self.udaf.order_sensitivity)(&self.udaf).into() }
}
+ fn supports_null_handling_clause(&self) -> bool {
+ unsafe { (self.udaf.supports_null_handling_clause)(&self.udaf) }
+ }
+
fn simplify(&self) -> Option<AggregateFunctionSimplification> {
None
}
@@ -774,6 +789,19 @@ mod tests {
Ok(())
}
+ #[test]
+ fn test_supports_null_handling_clause() -> Result<()> {
+ let first_value = create_test_foreign_udaf(
+ datafusion::functions_aggregate::first_last::FirstValue::new(),
+ )?;
+ assert!(first_value.supports_null_handling_clause());
+
+ let sum = create_test_foreign_udaf(Sum::new())?;
+ assert!(!sum.supports_null_handling_clause());
+
+ Ok(())
+ }
+
#[test]
fn test_beneficial_ordering() -> Result<()> {
let foreign_udaf = create_test_foreign_udaf(
diff --git a/datafusion/ffi/tests/ffi_udaf.rs b/datafusion/ffi/tests/ffi_udaf.rs
index 3234f6533d..090151416e 100644
--- a/datafusion/ffi/tests/ffi_udaf.rs
+++ b/datafusion/ffi/tests/ffi_udaf.rs
@@ -66,6 +66,22 @@ mod tests {
Ok(())
}
+ #[test]
+ fn test_supports_null_handling_clause() -> Result<()> {
+ let module = get_module()?;
+
+ let ffi_first_value_func = (module.create_first_value_udaf)();
+ let foreign_first_value_func: Arc<dyn AggregateUDFImpl> =
+ (&ffi_first_value_func).into();
+ assert!(foreign_first_value_func.supports_null_handling_clause());
+
+ let ffi_sum_func = (module.create_sum_udaf)();
+ let foreign_sum_func: Arc<dyn AggregateUDFImpl> = (&ffi_sum_func).into();
+ assert!(!foreign_sum_func.supports_null_handling_clause());
+
+ Ok(())
+ }
+
#[tokio::test]
async fn test_ffi_grouping_udaf() -> Result<()> {
let module = get_module()?;
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@datafusion.apache.org
For additional commands, e-mail: commits-help@datafusion.apache.org