1use accumulator::FFI_Accumulator;
19use accumulator_args::{FFI_AccumulatorArgs, ForeignAccumulatorArgs};
20use arrow::datatypes::{DataType, Field};
21use arrow::ffi::FFI_ArrowSchema;
22use arrow_schema::FieldRef;
23use datafusion_common::{DataFusionError, Result, ffi_datafusion_err};
24use datafusion_expr::function::AggregateFunctionSimplification;
25use datafusion_expr::type_coercion::functions::fields_with_udf;
26use datafusion_expr::{
27 Accumulator, AggregateUDF, AggregateUDFImpl, GroupsAccumulator, Signature,
28};
29use datafusion_functions_aggregate_common::accumulator::{
30 AccumulatorArgs, StateFieldsArgs,
31};
32use datafusion_functions_aggregate_common::order::AggregateOrderSensitivity;
33use datafusion_proto_common::from_proto::parse_proto_fields_to_fields;
34use groups_accumulator::FFI_GroupsAccumulator;
35use prost::{DecodeError, Message};
36
37use stabby::str::Str as SStr;
38use stabby::string::String as SString;
39use stabby::vec::Vec as SVec;
40use std::ffi::c_void;
41use std::hash::{Hash, Hasher};
42use std::sync::Arc;
43
44use crate::arrow_wrappers::WrappedSchema;
45use crate::util::{
46 FFI_Option, FFI_Result, rvec_wrapped_to_vec_datatype, rvec_wrapped_to_vec_fieldref,
47 vec_datatype_to_rvec_wrapped, vec_fieldref_to_rvec_wrapped,
48};
49use crate::volatility::FFI_Volatility;
50use crate::{df_result, sresult, sresult_return};
51
52mod accumulator;
53mod accumulator_args;
54mod groups_accumulator;
55
56#[repr(C)]
58#[derive(Debug)]
59pub struct FFI_AggregateUDF {
60 pub name: SString,
62
63 pub aliases: SVec<SString>,
65
66 pub volatility: FFI_Volatility,
68
69 pub return_field: unsafe extern "C" fn(
72 udaf: &Self,
73 arg_fields: SVec<WrappedSchema>,
74 ) -> FFI_Result<WrappedSchema>,
75
76 pub is_nullable: bool,
78
79 pub groups_accumulator_supported:
81 unsafe extern "C" fn(udaf: &FFI_AggregateUDF, args: FFI_AccumulatorArgs) -> bool,
82
83 pub accumulator: unsafe extern "C" fn(
85 udaf: &FFI_AggregateUDF,
86 args: FFI_AccumulatorArgs,
87 ) -> FFI_Result<FFI_Accumulator>,
88
89 pub create_sliding_accumulator: unsafe extern "C" fn(
91 udaf: &FFI_AggregateUDF,
92 args: FFI_AccumulatorArgs,
93 )
94 -> FFI_Result<FFI_Accumulator>,
95
96 pub state_fields: unsafe extern "C" fn(
98 udaf: &FFI_AggregateUDF,
99 name: &SStr,
100 input_fields: SVec<WrappedSchema>,
101 return_field: WrappedSchema,
102 ordering_fields: SVec<SVec<u8>>,
103 is_distinct: bool,
104 ) -> FFI_Result<SVec<SVec<u8>>>,
105
106 pub create_groups_accumulator:
108 unsafe extern "C" fn(
109 udaf: &FFI_AggregateUDF,
110 args: FFI_AccumulatorArgs,
111 ) -> FFI_Result<FFI_GroupsAccumulator>,
112
113 pub with_beneficial_ordering:
115 unsafe extern "C" fn(
116 udaf: &FFI_AggregateUDF,
117 beneficial_ordering: bool,
118 ) -> FFI_Result<FFI_Option<FFI_AggregateUDF>>,
119
120 pub order_sensitivity:
122 unsafe extern "C" fn(udaf: &FFI_AggregateUDF) -> FFI_AggregateOrderSensitivity,
123
124 pub coerce_types: unsafe extern "C" fn(
129 udf: &Self,
130 arg_types: SVec<WrappedSchema>,
131 ) -> FFI_Result<SVec<WrappedSchema>>,
132
133 pub clone: unsafe extern "C" fn(udaf: &Self) -> Self,
136
137 pub release: unsafe extern "C" fn(udaf: &mut Self),
139
140 pub private_data: *mut c_void,
143
144 pub library_marker_id: extern "C" fn() -> usize,
148
149 pub supports_null_handling_clause:
151 unsafe extern "C" fn(udaf: &FFI_AggregateUDF) -> bool,
152}
153
154unsafe impl Send for FFI_AggregateUDF {}
155unsafe impl Sync for FFI_AggregateUDF {}
156
157pub struct AggregateUDFPrivateData {
158 pub udaf: Arc<AggregateUDF>,
159}
160
161impl FFI_AggregateUDF {
162 unsafe fn inner(&self) -> &Arc<AggregateUDF> {
163 unsafe {
164 let private_data = self.private_data as *const AggregateUDFPrivateData;
165 &(*private_data).udaf
166 }
167 }
168}
169
170unsafe extern "C" fn return_field_fn_wrapper(
171 udaf: &FFI_AggregateUDF,
172 arg_fields: SVec<WrappedSchema>,
173) -> FFI_Result<WrappedSchema> {
174 unsafe {
175 let udaf = udaf.inner();
176
177 let arg_fields = sresult_return!(rvec_wrapped_to_vec_fieldref(&arg_fields));
178
179 let return_field = udaf
180 .return_field(&arg_fields)
181 .and_then(|v| {
182 FFI_ArrowSchema::try_from(v.as_ref()).map_err(DataFusionError::from)
183 })
184 .map(WrappedSchema);
185
186 sresult!(return_field)
187 }
188}
189
190unsafe extern "C" fn accumulator_fn_wrapper(
191 udaf: &FFI_AggregateUDF,
192 args: FFI_AccumulatorArgs,
193) -> FFI_Result<FFI_Accumulator> {
194 unsafe {
195 let udaf = udaf.inner();
196
197 let accumulator_args = &sresult_return!(ForeignAccumulatorArgs::try_from(args));
198
199 sresult!(
200 udaf.accumulator(accumulator_args.into())
201 .map(FFI_Accumulator::from)
202 )
203 }
204}
205
206unsafe extern "C" fn create_sliding_accumulator_fn_wrapper(
207 udaf: &FFI_AggregateUDF,
208 args: FFI_AccumulatorArgs,
209) -> FFI_Result<FFI_Accumulator> {
210 unsafe {
211 let udaf = udaf.inner();
212
213 let accumulator_args = &sresult_return!(ForeignAccumulatorArgs::try_from(args));
214
215 sresult!(
216 udaf.create_sliding_accumulator(accumulator_args.into())
217 .map(FFI_Accumulator::from)
218 )
219 }
220}
221
222unsafe extern "C" fn create_groups_accumulator_fn_wrapper(
223 udaf: &FFI_AggregateUDF,
224 args: FFI_AccumulatorArgs,
225) -> FFI_Result<FFI_GroupsAccumulator> {
226 unsafe {
227 let udaf = udaf.inner();
228
229 let accumulator_args = &sresult_return!(ForeignAccumulatorArgs::try_from(args));
230
231 sresult!(
232 udaf.create_groups_accumulator(accumulator_args.into())
233 .map(FFI_GroupsAccumulator::from)
234 )
235 }
236}
237
238unsafe extern "C" fn groups_accumulator_supported_fn_wrapper(
239 udaf: &FFI_AggregateUDF,
240 args: FFI_AccumulatorArgs,
241) -> bool {
242 unsafe {
243 let udaf = udaf.inner();
244
245 ForeignAccumulatorArgs::try_from(args)
246 .map(|a| udaf.groups_accumulator_supported((&a).into()))
247 .unwrap_or_else(|e| {
248 log::warn!("Unable to parse accumulator args. {e}");
249 false
250 })
251 }
252}
253
254unsafe extern "C" fn with_beneficial_ordering_fn_wrapper(
255 udaf: &FFI_AggregateUDF,
256 beneficial_ordering: bool,
257) -> FFI_Result<FFI_Option<FFI_AggregateUDF>> {
258 unsafe {
259 let udaf = udaf.inner().as_ref().clone();
260
261 let result = sresult_return!(udaf.with_beneficial_ordering(beneficial_ordering));
262 let result = sresult_return!(
263 result
264 .map(|func| func.with_beneficial_ordering(beneficial_ordering))
265 .transpose()
266 )
267 .flatten()
268 .map(|func| FFI_AggregateUDF::from(Arc::new(func)));
269
270 FFI_Result::Ok(result.into())
271 }
272}
273
274unsafe extern "C" fn state_fields_fn_wrapper(
275 udaf: &FFI_AggregateUDF,
276 name: &SStr,
277 input_fields: SVec<WrappedSchema>,
278 return_field: WrappedSchema,
279 ordering_fields: SVec<SVec<u8>>,
280 is_distinct: bool,
281) -> FFI_Result<SVec<SVec<u8>>> {
282 unsafe {
283 let udaf = udaf.inner();
284
285 let input_fields = &sresult_return!(rvec_wrapped_to_vec_fieldref(&input_fields));
286 let return_field = sresult_return!(Field::try_from(&return_field.0)).into();
287
288 let ordering_fields = &sresult_return!(
289 ordering_fields
290 .into_iter()
291 .map(|field_bytes| datafusion_proto_common::Field::decode(
292 field_bytes.as_ref()
293 ))
294 .collect::<std::result::Result<Vec<_>, DecodeError>>()
295 );
296
297 let ordering_fields =
298 &sresult_return!(parse_proto_fields_to_fields(ordering_fields))
299 .into_iter()
300 .map(Arc::new)
301 .collect::<Vec<_>>();
302
303 let args = StateFieldsArgs {
304 name: name.as_str(),
305 input_fields,
306 return_field,
307 ordering_fields,
308 is_distinct,
309 };
310
311 let state_fields = sresult_return!(udaf.state_fields(args));
312 let state_fields = sresult_return!(
313 state_fields
314 .iter()
315 .map(|f| f.as_ref())
316 .map(datafusion_proto::protobuf::Field::try_from)
317 .map(|v| v.map_err(DataFusionError::from))
318 .collect::<Result<Vec<_>>>()
319 )
320 .into_iter()
321 .map(|field| field.encode_to_vec().into_iter().collect())
322 .collect();
323
324 FFI_Result::Ok(state_fields)
325 }
326}
327
328unsafe extern "C" fn order_sensitivity_fn_wrapper(
329 udaf: &FFI_AggregateUDF,
330) -> FFI_AggregateOrderSensitivity {
331 unsafe { udaf.inner().order_sensitivity().into() }
332}
333
334unsafe extern "C" fn supports_null_handling_clause_fn_wrapper(
335 udaf: &FFI_AggregateUDF,
336) -> bool {
337 unsafe { udaf.inner().supports_null_handling_clause() }
338}
339
340unsafe extern "C" fn coerce_types_fn_wrapper(
341 udaf: &FFI_AggregateUDF,
342 arg_types: SVec<WrappedSchema>,
343) -> FFI_Result<SVec<WrappedSchema>> {
344 unsafe {
345 let udaf = udaf.inner();
346
347 let arg_types = sresult_return!(rvec_wrapped_to_vec_datatype(&arg_types));
348
349 let arg_fields = arg_types
350 .iter()
351 .map(|dt| Field::new("f", dt.clone(), true))
352 .map(Arc::new)
353 .collect::<Vec<_>>();
354 let return_types = sresult_return!(fields_with_udf(&arg_fields, udaf.as_ref()))
355 .into_iter()
356 .map(|f| f.data_type().to_owned())
357 .collect::<Vec<_>>();
358
359 sresult!(vec_datatype_to_rvec_wrapped(&return_types))
360 }
361}
362
363unsafe extern "C" fn release_fn_wrapper(udaf: &mut FFI_AggregateUDF) {
364 unsafe {
365 debug_assert!(!udaf.private_data.is_null());
366 let private_data =
367 Box::from_raw(udaf.private_data as *mut AggregateUDFPrivateData);
368 drop(private_data);
369 udaf.private_data = std::ptr::null_mut();
370 }
371}
372
373unsafe extern "C" fn clone_fn_wrapper(udaf: &FFI_AggregateUDF) -> FFI_AggregateUDF {
374 unsafe { Arc::clone(udaf.inner()).into() }
375}
376
377impl Clone for FFI_AggregateUDF {
378 fn clone(&self) -> Self {
379 unsafe { (self.clone)(self) }
380 }
381}
382
383impl From<Arc<AggregateUDF>> for FFI_AggregateUDF {
384 fn from(udaf: Arc<AggregateUDF>) -> Self {
385 if let Some(udaf) = udaf.inner().downcast_ref::<ForeignAggregateUDF>() {
386 return udaf.udaf.clone();
387 }
388
389 let name = udaf.name().into();
390 let aliases = udaf.aliases().iter().map(|a| a.to_owned().into()).collect();
391 let is_nullable = udaf.is_nullable();
392 let volatility = udaf.signature().volatility.into();
393
394 let private_data = Box::new(AggregateUDFPrivateData { udaf });
395
396 Self {
397 name,
398 is_nullable,
399 volatility,
400 aliases,
401 return_field: return_field_fn_wrapper,
402 accumulator: accumulator_fn_wrapper,
403 create_sliding_accumulator: create_sliding_accumulator_fn_wrapper,
404 create_groups_accumulator: create_groups_accumulator_fn_wrapper,
405 groups_accumulator_supported: groups_accumulator_supported_fn_wrapper,
406 with_beneficial_ordering: with_beneficial_ordering_fn_wrapper,
407 state_fields: state_fields_fn_wrapper,
408 order_sensitivity: order_sensitivity_fn_wrapper,
409 coerce_types: coerce_types_fn_wrapper,
410 clone: clone_fn_wrapper,
411 release: release_fn_wrapper,
412 private_data: Box::into_raw(private_data) as *mut c_void,
413 library_marker_id: crate::get_library_marker_id,
414 supports_null_handling_clause: supports_null_handling_clause_fn_wrapper,
415 }
416 }
417}
418
419impl Drop for FFI_AggregateUDF {
420 fn drop(&mut self) {
421 unsafe { (self.release)(self) }
422 }
423}
424
425#[derive(Debug)]
432pub struct ForeignAggregateUDF {
433 signature: Signature,
434 aliases: Vec<String>,
435 udaf: FFI_AggregateUDF,
436}
437
438unsafe impl Send for ForeignAggregateUDF {}
439unsafe impl Sync for ForeignAggregateUDF {}
440
441impl PartialEq for ForeignAggregateUDF {
442 fn eq(&self, other: &Self) -> bool {
443 std::ptr::eq(self, other)
445 }
446}
447impl Eq for ForeignAggregateUDF {}
448impl Hash for ForeignAggregateUDF {
449 fn hash<H: Hasher>(&self, state: &mut H) {
450 std::ptr::hash(self, state)
451 }
452}
453
454impl From<&FFI_AggregateUDF> for Arc<dyn AggregateUDFImpl> {
455 fn from(udaf: &FFI_AggregateUDF) -> Self {
456 if (udaf.library_marker_id)() == crate::get_library_marker_id() {
457 return Arc::clone(unsafe { udaf.inner().inner() });
458 }
459
460 let signature = Signature::user_defined((&udaf.volatility).into());
461 let aliases = udaf.aliases.iter().map(|s| s.to_string()).collect();
462
463 Arc::new(ForeignAggregateUDF {
464 udaf: udaf.clone(),
465 signature,
466 aliases,
467 })
468 }
469}
470
471impl AggregateUDFImpl for ForeignAggregateUDF {
472 fn name(&self) -> &str {
473 self.udaf.name.as_str()
474 }
475
476 fn signature(&self) -> &Signature {
477 &self.signature
478 }
479
480 fn return_type(&self, _arg_types: &[DataType]) -> Result<DataType> {
481 unimplemented!()
482 }
483
484 fn return_field(&self, arg_fields: &[FieldRef]) -> Result<FieldRef> {
485 let arg_fields = vec_fieldref_to_rvec_wrapped(arg_fields)?;
486
487 let result = unsafe { (self.udaf.return_field)(&self.udaf, arg_fields) };
488
489 let result = df_result!(result);
490
491 result.and_then(|r| {
492 Field::try_from(&r.0)
493 .map(Arc::new)
494 .map_err(DataFusionError::from)
495 })
496 }
497
498 fn is_nullable(&self) -> bool {
499 self.udaf.is_nullable
500 }
501
502 fn accumulator(&self, acc_args: AccumulatorArgs) -> Result<Box<dyn Accumulator>> {
503 let args = acc_args.try_into()?;
504 unsafe {
505 df_result!((self.udaf.accumulator)(&self.udaf, args))
506 .map(<Box<dyn Accumulator>>::from)
507 }
508 }
509
510 fn state_fields(&self, args: StateFieldsArgs) -> Result<Vec<FieldRef>> {
511 unsafe {
512 let name = SStr::from(args.name);
513 let input_fields = vec_fieldref_to_rvec_wrapped(args.input_fields)?;
514 let return_field =
515 WrappedSchema(FFI_ArrowSchema::try_from(args.return_field.as_ref())?);
516 let ordering_fields = args
517 .ordering_fields
518 .iter()
519 .map(|f| f.as_ref())
520 .map(datafusion_proto::protobuf::Field::try_from)
521 .map(|v| v.map_err(DataFusionError::from))
522 .collect::<Result<Vec<_>>>()?
523 .into_iter()
524 .map(|proto_field| proto_field.encode_to_vec().into_iter().collect())
525 .collect();
526
527 let fields = df_result!((self.udaf.state_fields)(
528 &self.udaf,
529 &name,
530 input_fields,
531 return_field,
532 ordering_fields,
533 args.is_distinct
534 ))?;
535 let fields = fields
536 .into_iter()
537 .map(|field_bytes| {
538 datafusion_proto_common::Field::decode(field_bytes.as_ref())
539 .map_err(|e| ffi_datafusion_err!("{e}"))
540 })
541 .collect::<Result<Vec<_>>>()?;
542
543 parse_proto_fields_to_fields(fields.iter())
544 .map(|fields| fields.into_iter().map(Arc::new).collect())
545 .map_err(|e| ffi_datafusion_err!("{e}"))
546 }
547 }
548
549 fn groups_accumulator_supported(&self, args: AccumulatorArgs) -> bool {
550 let args = match FFI_AccumulatorArgs::try_from(args) {
551 Ok(v) => v,
552 Err(e) => {
553 log::warn!("Attempting to convert accumulator arguments: {e}");
554 return false;
555 }
556 };
557
558 unsafe { (self.udaf.groups_accumulator_supported)(&self.udaf, args) }
559 }
560
561 fn create_groups_accumulator(
562 &self,
563 args: AccumulatorArgs,
564 ) -> Result<Box<dyn GroupsAccumulator>> {
565 let args = FFI_AccumulatorArgs::try_from(args)?;
566
567 unsafe {
568 df_result!((self.udaf.create_groups_accumulator)(&self.udaf, args))
569 .map(<Box<dyn GroupsAccumulator>>::from)
570 }
571 }
572
573 fn aliases(&self) -> &[String] {
574 &self.aliases
575 }
576
577 fn create_sliding_accumulator(
578 &self,
579 args: AccumulatorArgs,
580 ) -> Result<Box<dyn Accumulator>> {
581 let args = args.try_into()?;
582 unsafe {
583 df_result!((self.udaf.create_sliding_accumulator)(&self.udaf, args))
584 .map(<Box<dyn Accumulator>>::from)
585 }
586 }
587
588 fn with_beneficial_ordering(
589 self: Arc<Self>,
590 beneficial_ordering: bool,
591 ) -> Result<Option<Arc<dyn AggregateUDFImpl>>> {
592 unsafe {
593 let result = df_result!((self.udaf.with_beneficial_ordering)(
594 &self.udaf,
595 beneficial_ordering
596 ))?
597 .into_option();
598
599 let result = result.map(|func| <Arc<dyn AggregateUDFImpl>>::from(&func));
600
601 Ok(result)
602 }
603 }
604
605 fn order_sensitivity(&self) -> AggregateOrderSensitivity {
606 unsafe { (self.udaf.order_sensitivity)(&self.udaf).into() }
607 }
608
609 fn supports_null_handling_clause(&self) -> bool {
610 unsafe { (self.udaf.supports_null_handling_clause)(&self.udaf) }
611 }
612
613 fn simplify(&self) -> Option<AggregateFunctionSimplification> {
614 None
615 }
616
617 fn coerce_types(&self, arg_types: &[DataType]) -> Result<Vec<DataType>> {
618 unsafe {
619 let arg_types = vec_datatype_to_rvec_wrapped(arg_types)?;
620 let result_types =
621 df_result!((self.udaf.coerce_types)(&self.udaf, arg_types))?;
622 Ok(rvec_wrapped_to_vec_datatype(&result_types)?)
623 }
624 }
625}
626
627#[repr(C)]
628#[derive(Debug)]
629pub enum FFI_AggregateOrderSensitivity {
630 Insensitive,
631 HardRequirement,
632 SoftRequirement,
633 Beneficial,
634}
635
636impl From<FFI_AggregateOrderSensitivity> for AggregateOrderSensitivity {
637 fn from(value: FFI_AggregateOrderSensitivity) -> Self {
638 match value {
639 FFI_AggregateOrderSensitivity::Insensitive => Self::Insensitive,
640 FFI_AggregateOrderSensitivity::HardRequirement => Self::HardRequirement,
641 FFI_AggregateOrderSensitivity::SoftRequirement => Self::SoftRequirement,
642 FFI_AggregateOrderSensitivity::Beneficial => Self::Beneficial,
643 }
644 }
645}
646
647impl From<AggregateOrderSensitivity> for FFI_AggregateOrderSensitivity {
648 fn from(value: AggregateOrderSensitivity) -> Self {
649 match value {
650 AggregateOrderSensitivity::Insensitive => Self::Insensitive,
651 AggregateOrderSensitivity::HardRequirement => Self::HardRequirement,
652 AggregateOrderSensitivity::SoftRequirement => Self::SoftRequirement,
653 AggregateOrderSensitivity::Beneficial => Self::Beneficial,
654 }
655 }
656}
657
658#[cfg(test)]
659mod tests {
660 use std::collections::HashMap;
661
662 use arrow::datatypes::Schema;
663 use datafusion::common::create_array;
664 use datafusion::functions_aggregate::sum::Sum;
665 use datafusion::physical_expr::PhysicalSortExpr;
666 use datafusion::physical_plan::expressions::col;
667 use datafusion::scalar::ScalarValue;
668
669 use super::*;
670
671 #[derive(Default, Debug, Hash, Eq, PartialEq)]
672 struct SumWithCopiedMetadata {
673 inner: Sum,
674 }
675
676 impl AggregateUDFImpl for SumWithCopiedMetadata {
677 fn name(&self) -> &str {
678 self.inner.name()
679 }
680
681 fn signature(&self) -> &Signature {
682 self.inner.signature()
683 }
684
685 fn return_type(&self, _arg_types: &[DataType]) -> Result<DataType> {
686 unimplemented!()
687 }
688
689 fn return_field(&self, arg_fields: &[FieldRef]) -> Result<FieldRef> {
690 Ok(Arc::clone(&arg_fields[0]))
692 }
693
694 fn accumulator(&self, acc_args: AccumulatorArgs) -> Result<Box<dyn Accumulator>> {
695 self.inner.accumulator(acc_args)
696 }
697 }
698
699 fn create_test_foreign_udaf(
700 original_udaf: impl AggregateUDFImpl + 'static,
701 ) -> Result<AggregateUDF> {
702 let original_udaf = Arc::new(AggregateUDF::from(original_udaf));
703
704 let mut local_udaf: FFI_AggregateUDF = Arc::clone(&original_udaf).into();
705 local_udaf.library_marker_id = crate::mock_foreign_marker_id;
706
707 let foreign_udaf: Arc<dyn AggregateUDFImpl> = (&local_udaf).into();
708 Ok(AggregateUDF::new_from_shared_impl(foreign_udaf))
709 }
710
711 #[test]
712 fn test_round_trip_udaf() -> Result<()> {
713 let original_udaf = Sum::new();
714 let original_name = original_udaf.name().to_owned();
715 let original_udaf = Arc::new(AggregateUDF::from(original_udaf));
716
717 let mut local_udaf: FFI_AggregateUDF = Arc::clone(&original_udaf).into();
719 local_udaf.library_marker_id = crate::mock_foreign_marker_id;
720
721 let foreign_udaf: Arc<dyn AggregateUDFImpl> = (&local_udaf).into();
723 let foreign_udaf = AggregateUDF::new_from_shared_impl(foreign_udaf);
724
725 assert_eq!(original_name, foreign_udaf.name());
726 Ok(())
727 }
728
729 #[test]
730 fn test_foreign_udaf_aliases() -> Result<()> {
731 let foreign_udaf =
732 create_test_foreign_udaf(Sum::new())?.with_aliases(["my_function"]);
733
734 let return_field =
735 foreign_udaf
736 .return_field(&[Field::new("a", DataType::Float64, true).into()])?;
737 let return_type = return_field.data_type();
738 assert_eq!(return_type, &DataType::Float64);
739 Ok(())
740 }
741
742 #[test]
743 fn test_foreign_udaf_accumulator() -> Result<()> {
744 let foreign_udaf = create_test_foreign_udaf(Sum::new())?;
745
746 let schema = Schema::new(vec![Field::new("a", DataType::Float64, true)]);
747 let acc_args = AccumulatorArgs {
748 return_field: Field::new("f", DataType::Float64, true).into(),
749 schema: &schema,
750 expr_fields: &[Field::new("a", DataType::Float64, true).into()],
751 ignore_nulls: true,
752 order_bys: &[PhysicalSortExpr::new_default(col("a", &schema)?)],
753 is_reversed: false,
754 name: "round_trip",
755 is_distinct: true,
756 exprs: &[col("a", &schema)?],
757 };
758 let mut accumulator = foreign_udaf.accumulator(acc_args)?;
759 let values = create_array!(Float64, vec![10., 20., 30., 40., 50.]);
760 accumulator.update_batch(&[values])?;
761 let resultant_value = accumulator.evaluate()?;
762 assert_eq!(resultant_value, ScalarValue::Float64(Some(150.)));
763
764 Ok(())
765 }
766
767 #[test]
768 fn test_round_trip_udaf_metadata() -> Result<()> {
769 let original_udaf = SumWithCopiedMetadata::default();
770 let original_udaf = Arc::new(AggregateUDF::from(original_udaf));
771
772 let local_udaf: FFI_AggregateUDF = Arc::clone(&original_udaf).into();
774
775 let foreign_udaf: Arc<dyn AggregateUDFImpl> = (&local_udaf).into();
777 let foreign_udaf = AggregateUDF::new_from_shared_impl(foreign_udaf);
778
779 let metadata: HashMap<String, String> =
780 [("a_key".to_string(), "a_value".to_string())]
781 .into_iter()
782 .collect();
783 let input_field = Arc::new(
784 Field::new("a", DataType::Float64, false).with_metadata(metadata.clone()),
785 );
786 let return_field = foreign_udaf.return_field(&[input_field])?;
787
788 assert_eq!(&metadata, return_field.metadata());
789 Ok(())
790 }
791
792 #[test]
793 fn test_supports_null_handling_clause() -> Result<()> {
794 let first_value = create_test_foreign_udaf(
795 datafusion::functions_aggregate::first_last::FirstValue::new(),
796 )?;
797 assert!(first_value.supports_null_handling_clause());
798
799 let sum = create_test_foreign_udaf(Sum::new())?;
800 assert!(!sum.supports_null_handling_clause());
801
802 Ok(())
803 }
804
805 #[test]
806 fn test_beneficial_ordering() -> Result<()> {
807 let foreign_udaf = create_test_foreign_udaf(
808 datafusion::functions_aggregate::first_last::FirstValue::new(),
809 )?;
810
811 let foreign_udaf = foreign_udaf.with_beneficial_ordering(true)?.unwrap();
812
813 assert_eq!(
814 foreign_udaf.order_sensitivity(),
815 AggregateOrderSensitivity::Beneficial
816 );
817
818 let a_field = Arc::new(Field::new("a", DataType::Float64, true));
819 let state_fields = foreign_udaf.state_fields(StateFieldsArgs {
820 name: "a",
821 input_fields: &[Field::new("f", DataType::Float64, true).into()],
822 return_field: Field::new("f", DataType::Float64, true).into(),
823 ordering_fields: &[Arc::clone(&a_field)],
824 is_distinct: false,
825 })?;
826
827 assert_eq!(state_fields.len(), 3);
828 assert_eq!(state_fields[1], a_field);
829 Ok(())
830 }
831
832 #[test]
833 fn test_sliding_accumulator() -> Result<()> {
834 let foreign_udaf = create_test_foreign_udaf(Sum::new())?;
835
836 let schema = Schema::new(vec![Field::new("a", DataType::Float64, true)]);
837 let acc_args = AccumulatorArgs {
839 return_field: Field::new("f", DataType::Float64, true).into(),
840 schema: &schema,
841 expr_fields: &[Field::new("a", DataType::Float64, true).into()],
842 ignore_nulls: true,
843 order_bys: &[PhysicalSortExpr::new_default(col("a", &schema)?)],
844 is_reversed: false,
845 name: "round_trip",
846 is_distinct: false,
847 exprs: &[col("a", &schema)?],
848 };
849
850 let mut accumulator = foreign_udaf.create_sliding_accumulator(acc_args)?;
851 let values = create_array!(Float64, vec![10., 20., 30., 40., 50.]);
852 accumulator.update_batch(&[values])?;
853 let resultant_value = accumulator.evaluate()?;
854 assert_eq!(resultant_value, ScalarValue::Float64(Some(150.)));
855
856 Ok(())
857 }
858
859 fn test_round_trip_order_sensitivity(sensitivity: AggregateOrderSensitivity) {
860 let ffi_sensitivity: FFI_AggregateOrderSensitivity = sensitivity.into();
861 let round_trip_sensitivity: AggregateOrderSensitivity = ffi_sensitivity.into();
862
863 assert_eq!(sensitivity, round_trip_sensitivity);
864 }
865
866 #[test]
867 fn test_round_trip_all_order_sensitivities() {
868 test_round_trip_order_sensitivity(AggregateOrderSensitivity::Insensitive);
869 test_round_trip_order_sensitivity(AggregateOrderSensitivity::HardRequirement);
870 test_round_trip_order_sensitivity(AggregateOrderSensitivity::SoftRequirement);
871 test_round_trip_order_sensitivity(AggregateOrderSensitivity::Beneficial);
872 }
873
874 #[test]
875 fn test_ffi_udaf_local_bypass() -> Result<()> {
876 let original_udaf = Sum::new();
877 let original_udaf = Arc::new(AggregateUDF::from(original_udaf));
878
879 let mut ffi_udaf = FFI_AggregateUDF::from(original_udaf);
880
881 let foreign_udaf: Arc<dyn AggregateUDFImpl> = (&ffi_udaf).into();
883 assert!(foreign_udaf.is::<Sum>());
884
885 ffi_udaf.library_marker_id = crate::mock_foreign_marker_id;
887 let foreign_udaf: Arc<dyn AggregateUDFImpl> = (&ffi_udaf).into();
888 assert!(foreign_udaf.is::<ForeignAggregateUDF>());
889
890 Ok(())
891 }
892}