1use std::any::Any;
19use std::ffi::c_void;
20use std::ops::Deref;
21use std::ptr::null_mut;
22use std::sync::Arc;
23
24use arrow::array::{Array, ArrayRef, BooleanArray};
25use arrow::error::ArrowError;
26use arrow::ffi::to_ffi;
27use datafusion_common::error::{DataFusionError, Result};
28use datafusion_expr::{EmitTo, GroupsAccumulator};
29
30use stabby::vec::Vec as SVec;
31
32use crate::arrow_wrappers::{WrappedArray, WrappedSchema};
33use crate::util::{FFI_Option, FFI_Result};
34use crate::{df_result, sresult, sresult_return};
35
36#[repr(C)]
40#[derive(Debug)]
41pub struct FFI_GroupsAccumulator {
42 pub update_batch: unsafe extern "C" fn(
43 accumulator: &mut Self,
44 values: SVec<WrappedArray>,
45 group_indices: SVec<usize>,
46 opt_filter: FFI_Option<WrappedArray>,
47 total_num_groups: usize,
48 ) -> FFI_Result<()>,
49
50 pub evaluate: unsafe extern "C" fn(
52 accumulator: &mut Self,
53 emit_to: FFI_EmitTo,
54 ) -> FFI_Result<WrappedArray>,
55
56 pub size: unsafe extern "C" fn(accumulator: &Self) -> usize,
57
58 pub state: unsafe extern "C" fn(
59 accumulator: &mut Self,
60 emit_to: FFI_EmitTo,
61 ) -> FFI_Result<SVec<WrappedArray>>,
62
63 pub merge_batch: unsafe extern "C" fn(
64 accumulator: &mut Self,
65 values: SVec<WrappedArray>,
66 group_indices: SVec<usize>,
67 opt_filter: FFI_Option<WrappedArray>,
68 total_num_groups: usize,
69 ) -> FFI_Result<()>,
70
71 pub convert_to_state: unsafe extern "C" fn(
72 accumulator: &Self,
73 values: SVec<WrappedArray>,
74 opt_filter: FFI_Option<WrappedArray>,
75 ) -> FFI_Result<SVec<WrappedArray>>,
76
77 pub supports_convert_to_state: bool,
78
79 pub release: unsafe extern "C" fn(accumulator: &mut Self),
81
82 pub private_data: *mut c_void,
85
86 pub library_marker_id: extern "C" fn() -> usize,
90}
91
92pub struct GroupsAccumulatorPrivateData {
93 pub accumulator: Box<dyn GroupsAccumulator>,
94}
95
96impl FFI_GroupsAccumulator {
97 #[inline]
98 unsafe fn inner_mut(&mut self) -> &mut Box<dyn GroupsAccumulator> {
99 unsafe {
100 let private_data = self.private_data as *mut GroupsAccumulatorPrivateData;
101 &mut (*private_data).accumulator
102 }
103 }
104
105 #[inline]
106 unsafe fn inner(&self) -> &dyn GroupsAccumulator {
107 unsafe {
108 let private_data = self.private_data as *const GroupsAccumulatorPrivateData;
109 (*private_data).accumulator.deref()
110 }
111 }
112}
113
114fn process_values(values: SVec<WrappedArray>) -> Result<Vec<Arc<dyn Array>>> {
115 values
116 .into_iter()
117 .map(|v| v.try_into().map_err(DataFusionError::from))
118 .collect::<Result<Vec<ArrayRef>>>()
119}
120
121fn process_opt_filter(
123 opt_filter: FFI_Option<WrappedArray>,
124) -> Result<Option<BooleanArray>> {
125 opt_filter
126 .into_option()
127 .map(|filter| {
128 ArrayRef::try_from(filter)
129 .map_err(DataFusionError::from)
130 .map(|arr| BooleanArray::from(arr.into_data()))
131 })
132 .transpose()
133}
134
135unsafe extern "C" fn update_batch_fn_wrapper(
136 accumulator: &mut FFI_GroupsAccumulator,
137 values: SVec<WrappedArray>,
138 group_indices: SVec<usize>,
139 opt_filter: FFI_Option<WrappedArray>,
140 total_num_groups: usize,
141) -> FFI_Result<()> {
142 unsafe {
143 let accumulator = accumulator.inner_mut();
144 let values = sresult_return!(process_values(values));
145 let group_indices: Vec<usize> = group_indices.into_iter().collect();
146 let opt_filter = sresult_return!(process_opt_filter(opt_filter));
147
148 sresult!(accumulator.update_batch(
149 &values,
150 &group_indices,
151 opt_filter.as_ref(),
152 total_num_groups
153 ))
154 }
155}
156
157unsafe extern "C" fn evaluate_fn_wrapper(
158 accumulator: &mut FFI_GroupsAccumulator,
159 emit_to: FFI_EmitTo,
160) -> FFI_Result<WrappedArray> {
161 unsafe {
162 let accumulator = accumulator.inner_mut();
163
164 let result = sresult_return!(accumulator.evaluate(emit_to.into()));
165
166 sresult!(WrappedArray::try_from(&result))
167 }
168}
169
170unsafe extern "C" fn size_fn_wrapper(accumulator: &FFI_GroupsAccumulator) -> usize {
171 unsafe {
172 let accumulator = accumulator.inner();
173 accumulator.size()
174 }
175}
176
177unsafe extern "C" fn state_fn_wrapper(
178 accumulator: &mut FFI_GroupsAccumulator,
179 emit_to: FFI_EmitTo,
180) -> FFI_Result<SVec<WrappedArray>> {
181 unsafe {
182 let accumulator = accumulator.inner_mut();
183
184 let state = sresult_return!(accumulator.state(emit_to.into()));
185 sresult!(
186 state
187 .into_iter()
188 .map(|arr| WrappedArray::try_from(&arr).map_err(DataFusionError::from))
189 .collect::<Result<SVec<_>>>()
190 )
191 }
192}
193
194unsafe extern "C" fn merge_batch_fn_wrapper(
195 accumulator: &mut FFI_GroupsAccumulator,
196 values: SVec<WrappedArray>,
197 group_indices: SVec<usize>,
198 opt_filter: FFI_Option<WrappedArray>,
199 total_num_groups: usize,
200) -> FFI_Result<()> {
201 unsafe {
202 let accumulator = accumulator.inner_mut();
203 let values = sresult_return!(process_values(values));
204 let group_indices: Vec<usize> = group_indices.into_iter().collect();
205 let opt_filter = sresult_return!(process_opt_filter(opt_filter));
206
207 sresult!(accumulator.merge_batch(
208 &values,
209 &group_indices,
210 opt_filter.as_ref(),
211 total_num_groups
212 ))
213 }
214}
215
216unsafe extern "C" fn convert_to_state_fn_wrapper(
217 accumulator: &FFI_GroupsAccumulator,
218 values: SVec<WrappedArray>,
219 opt_filter: FFI_Option<WrappedArray>,
220) -> FFI_Result<SVec<WrappedArray>> {
221 unsafe {
222 let accumulator = accumulator.inner();
223 let values = sresult_return!(process_values(values));
224 let opt_filter = sresult_return!(process_opt_filter(opt_filter));
225 let state =
226 sresult_return!(accumulator.convert_to_state(&values, opt_filter.as_ref()));
227
228 sresult!(
229 state
230 .iter()
231 .map(|arr| WrappedArray::try_from(arr).map_err(DataFusionError::from))
232 .collect::<Result<SVec<_>>>()
233 )
234 }
235}
236
237unsafe extern "C" fn release_fn_wrapper(accumulator: &mut FFI_GroupsAccumulator) {
238 unsafe {
239 if !accumulator.private_data.is_null() {
240 let private_data = Box::from_raw(
241 accumulator.private_data as *mut GroupsAccumulatorPrivateData,
242 );
243 drop(private_data);
244 accumulator.private_data = null_mut();
245 }
246 }
247}
248
249impl From<Box<dyn GroupsAccumulator>> for FFI_GroupsAccumulator {
250 fn from(accumulator: Box<dyn GroupsAccumulator>) -> Self {
251 if (accumulator.as_ref() as &dyn Any).is::<ForeignGroupsAccumulator>() {
252 let accumulator = (accumulator as Box<dyn Any>)
253 .downcast::<ForeignGroupsAccumulator>()
254 .expect("already checked type");
255 return accumulator.accumulator;
256 }
257
258 let supports_convert_to_state = accumulator.supports_convert_to_state();
259 let private_data = GroupsAccumulatorPrivateData { accumulator };
260
261 Self {
262 update_batch: update_batch_fn_wrapper,
263 evaluate: evaluate_fn_wrapper,
264 size: size_fn_wrapper,
265 state: state_fn_wrapper,
266 merge_batch: merge_batch_fn_wrapper,
267 convert_to_state: convert_to_state_fn_wrapper,
268 supports_convert_to_state,
269
270 release: release_fn_wrapper,
271 private_data: Box::into_raw(Box::new(private_data)) as *mut c_void,
272 library_marker_id: crate::get_library_marker_id,
273 }
274 }
275}
276
277impl Drop for FFI_GroupsAccumulator {
278 fn drop(&mut self) {
279 unsafe { (self.release)(self) }
280 }
281}
282
283#[derive(Debug)]
290pub struct ForeignGroupsAccumulator {
291 accumulator: FFI_GroupsAccumulator,
292}
293
294unsafe impl Send for ForeignGroupsAccumulator {}
295unsafe impl Sync for ForeignGroupsAccumulator {}
296
297impl From<FFI_GroupsAccumulator> for Box<dyn GroupsAccumulator> {
298 fn from(mut accumulator: FFI_GroupsAccumulator) -> Self {
299 if (accumulator.library_marker_id)() == crate::get_library_marker_id() {
300 unsafe {
301 let private_data = Box::from_raw(
302 accumulator.private_data as *mut GroupsAccumulatorPrivateData,
303 );
304 accumulator.private_data = null_mut();
306 private_data.accumulator
307 }
308 } else {
309 Box::new(ForeignGroupsAccumulator { accumulator })
310 }
311 }
312}
313
314impl GroupsAccumulator for ForeignGroupsAccumulator {
315 fn update_batch(
316 &mut self,
317 values: &[ArrayRef],
318 group_indices: &[usize],
319 opt_filter: Option<&BooleanArray>,
320 total_num_groups: usize,
321 ) -> Result<()> {
322 unsafe {
323 let values = values
324 .iter()
325 .map(WrappedArray::try_from)
326 .collect::<std::result::Result<Vec<_>, ArrowError>>()?;
327 let group_indices = group_indices.iter().cloned().collect();
328 let opt_filter = opt_filter
329 .map(|bool_array| to_ffi(&bool_array.to_data()))
330 .transpose()?
331 .map(|(array, schema)| WrappedArray {
332 array,
333 schema: WrappedSchema(schema),
334 })
335 .into();
336
337 df_result!((self.accumulator.update_batch)(
338 &mut self.accumulator,
339 values.into_iter().collect(),
340 group_indices,
341 opt_filter,
342 total_num_groups
343 ))
344 }
345 }
346
347 fn size(&self) -> usize {
348 unsafe { (self.accumulator.size)(&self.accumulator) }
349 }
350
351 fn evaluate(&mut self, emit_to: EmitTo) -> Result<ArrayRef> {
352 unsafe {
353 let return_array = df_result!((self.accumulator.evaluate)(
354 &mut self.accumulator,
355 emit_to.into()
356 ))?;
357
358 return_array.try_into().map_err(DataFusionError::from)
359 }
360 }
361
362 fn state(&mut self, emit_to: EmitTo) -> Result<Vec<ArrayRef>> {
363 unsafe {
364 let returned_arrays = df_result!((self.accumulator.state)(
365 &mut self.accumulator,
366 emit_to.into()
367 ))?;
368
369 returned_arrays
370 .into_iter()
371 .map(|wrapped_array| {
372 wrapped_array.try_into().map_err(DataFusionError::from)
373 })
374 .collect::<Result<Vec<_>>>()
375 }
376 }
377
378 fn merge_batch(
379 &mut self,
380 values: &[ArrayRef],
381 group_indices: &[usize],
382 opt_filter: Option<&BooleanArray>,
383 total_num_groups: usize,
384 ) -> Result<()> {
385 unsafe {
386 let values = values
387 .iter()
388 .map(WrappedArray::try_from)
389 .collect::<std::result::Result<Vec<_>, ArrowError>>()?;
390 let group_indices = group_indices.iter().cloned().collect();
391 let opt_filter = opt_filter
392 .map(|bool_array| to_ffi(&bool_array.to_data()))
393 .transpose()?
394 .map(|(array, schema)| WrappedArray {
395 array,
396 schema: WrappedSchema(schema),
397 })
398 .into();
399
400 df_result!((self.accumulator.merge_batch)(
401 &mut self.accumulator,
402 values.into_iter().collect(),
403 group_indices,
404 opt_filter,
405 total_num_groups
406 ))
407 }
408 }
409
410 fn convert_to_state(
411 &self,
412 values: &[ArrayRef],
413 opt_filter: Option<&BooleanArray>,
414 ) -> Result<Vec<ArrayRef>> {
415 unsafe {
416 let values = values
417 .iter()
418 .map(WrappedArray::try_from)
419 .collect::<std::result::Result<SVec<_>, ArrowError>>()?;
420
421 let opt_filter = opt_filter
422 .map(|bool_array| to_ffi(&bool_array.to_data()))
423 .transpose()?
424 .map(|(array, schema)| WrappedArray {
425 array,
426 schema: WrappedSchema(schema),
427 })
428 .into();
429
430 let returned_array = df_result!((self.accumulator.convert_to_state)(
431 &self.accumulator,
432 values,
433 opt_filter
434 ))?;
435
436 returned_array
437 .into_iter()
438 .map(|arr| arr.try_into().map_err(DataFusionError::from))
439 .collect()
440 }
441 }
442
443 fn supports_convert_to_state(&self) -> bool {
444 self.accumulator.supports_convert_to_state
445 }
446}
447
448#[repr(C)]
449#[derive(Debug)]
450pub enum FFI_EmitTo {
451 All,
452 First(usize),
453}
454
455impl From<EmitTo> for FFI_EmitTo {
456 fn from(value: EmitTo) -> Self {
457 match value {
458 EmitTo::All => Self::All,
459 EmitTo::First(v) => Self::First(v),
460 }
461 }
462}
463
464impl From<FFI_EmitTo> for EmitTo {
465 fn from(value: FFI_EmitTo) -> Self {
466 match value {
467 FFI_EmitTo::All => Self::All,
468 FFI_EmitTo::First(v) => Self::First(v),
469 }
470 }
471}
472
473#[cfg(test)]
474mod tests {
475 use arrow::array::{Array, BooleanArray, make_array};
476 use datafusion::common::create_array;
477 use datafusion::error::Result;
478 use datafusion::functions_aggregate::stddev::StddevGroupsAccumulator;
479 use datafusion::logical_expr::{EmitTo, GroupsAccumulator};
480 use datafusion_functions_aggregate_common::aggregate::groups_accumulator::bool_op::BooleanGroupsAccumulator;
481 use datafusion_functions_aggregate_common::stats::StatsType;
482
483 use super::{FFI_EmitTo, FFI_GroupsAccumulator, ForeignGroupsAccumulator};
484
485 #[test]
486 fn test_foreign_avg_accumulator() -> Result<()> {
487 let boxed_accum: Box<dyn GroupsAccumulator> =
488 Box::new(BooleanGroupsAccumulator::new(|a, b| a && b, true));
489 let mut ffi_accum: FFI_GroupsAccumulator = boxed_accum.into();
490 ffi_accum.library_marker_id = crate::mock_foreign_marker_id;
491 let mut foreign_accum: Box<dyn GroupsAccumulator> = ffi_accum.into();
492
493 let values = create_array!(Boolean, vec![true, true, true, false, true, true]);
495 let opt_filter =
496 create_array!(Boolean, vec![true, true, true, true, false, false]);
497 foreign_accum.update_batch(
498 &[values],
499 &[0, 0, 1, 1, 2, 2],
500 Some(opt_filter.as_ref()),
501 3,
502 )?;
503
504 let groups_bool = foreign_accum.evaluate(EmitTo::All)?;
505 let groups_bool = groups_bool.as_any().downcast_ref::<BooleanArray>().unwrap();
506
507 assert_eq!(
508 groups_bool,
509 create_array!(Boolean, vec![Some(true), Some(false), None]).as_ref()
510 );
511
512 let state = foreign_accum.state(EmitTo::All)?;
513 assert_eq!(state.len(), 1);
514
515 let second_states =
518 vec![make_array(create_array!(Boolean, vec![false]).to_data())];
519
520 let opt_filter = create_array!(Boolean, vec![true]);
521 foreign_accum.merge_batch(&second_states, &[0], Some(opt_filter.as_ref()), 1)?;
522 let groups_bool = foreign_accum.evaluate(EmitTo::All)?;
523 assert_eq!(groups_bool.len(), 1);
524 assert_eq!(
525 groups_bool.as_ref(),
526 make_array(create_array!(Boolean, vec![false]).to_data()).as_ref()
527 );
528
529 let values = create_array!(Boolean, vec![false]);
530 let opt_filter = create_array!(Boolean, vec![true]);
531 let groups_bool =
532 foreign_accum.convert_to_state(&[values], Some(opt_filter.as_ref()))?;
533
534 assert_eq!(
535 groups_bool[0].as_ref(),
536 make_array(create_array!(Boolean, vec![false]).to_data()).as_ref()
537 );
538
539 Ok(())
540 }
541
542 fn test_emit_to_round_trip(value: EmitTo) -> Result<()> {
543 let ffi_value: FFI_EmitTo = value.into();
544 let round_trip_value: EmitTo = ffi_value.into();
545
546 assert_eq!(value, round_trip_value);
547 Ok(())
548 }
549
550 #[test]
552 fn test_all_emit_to_round_trip() -> Result<()> {
553 test_emit_to_round_trip(EmitTo::All)?;
554 test_emit_to_round_trip(EmitTo::First(10))?;
555
556 Ok(())
557 }
558
559 #[test]
560 fn test_ffi_groups_accumulator_local_bypass_inner() -> Result<()> {
561 let original_accum = StddevGroupsAccumulator::new(StatsType::Population);
562 let boxed_accum: Box<dyn GroupsAccumulator> = Box::new(original_accum);
563 let original_size = boxed_accum.size();
564
565 let ffi_accum: FFI_GroupsAccumulator = boxed_accum.into();
566
567 let foreign_accum: Box<dyn GroupsAccumulator> = ffi_accum.into();
569 unsafe {
570 let concrete = &*(foreign_accum.as_ref() as *const dyn GroupsAccumulator
571 as *const StddevGroupsAccumulator);
572 assert_eq!(original_size, concrete.size());
573 }
574
575 let original_accum = StddevGroupsAccumulator::new(StatsType::Population);
577 let boxed_accum: Box<dyn GroupsAccumulator> = Box::new(original_accum);
578 let mut ffi_accum: FFI_GroupsAccumulator = boxed_accum.into();
579 ffi_accum.library_marker_id = crate::mock_foreign_marker_id;
580 let foreign_accum: Box<dyn GroupsAccumulator> = ffi_accum.into();
581 unsafe {
582 let concrete = &*(foreign_accum.as_ref() as *const dyn GroupsAccumulator
583 as *const ForeignGroupsAccumulator);
584 assert_eq!(original_size, concrete.size());
585 }
586
587 Ok(())
588 }
589}