datafusion_ffi/udwf/
partition_evaluator.rs1use std::any::Any;
19use std::ffi::c_void;
20use std::ops::Range;
21
22use arrow::array::ArrayRef;
23use arrow::error::ArrowError;
24use datafusion_common::scalar::ScalarValue;
25use datafusion_common::{DataFusionError, Result};
26use datafusion_expr::PartitionEvaluator;
27use datafusion_expr::window_state::WindowAggState;
28use prost::Message;
29
30use stabby::vec::Vec as SVec;
31
32use super::range::FFI_Range;
33use crate::arrow_wrappers::WrappedArray;
34use crate::util::FFI_Result;
35use crate::{df_result, sresult, sresult_return};
36
37#[repr(C)]
41#[derive(Debug)]
42pub struct FFI_PartitionEvaluator {
43 pub evaluate_all: unsafe extern "C" fn(
44 evaluator: &mut Self,
45 values: SVec<WrappedArray>,
46 num_rows: usize,
47 ) -> FFI_Result<WrappedArray>,
48
49 pub evaluate: unsafe extern "C" fn(
50 evaluator: &mut Self,
51 values: SVec<WrappedArray>,
52 range: FFI_Range,
53 ) -> FFI_Result<SVec<u8>>,
54
55 pub evaluate_all_with_rank: unsafe extern "C" fn(
56 evaluator: &Self,
57 num_rows: usize,
58 ranks_in_partition: SVec<FFI_Range>,
59 ) -> FFI_Result<WrappedArray>,
60
61 pub get_range: unsafe extern "C" fn(
62 evaluator: &Self,
63 idx: usize,
64 n_rows: usize,
65 ) -> FFI_Result<FFI_Range>,
66
67 pub is_causal: bool,
68
69 pub supports_bounded_execution: bool,
70 pub uses_window_frame: bool,
71 pub include_rank: bool,
72
73 pub release: unsafe extern "C" fn(evaluator: &mut Self),
75
76 pub private_data: *mut c_void,
79
80 pub library_marker_id: extern "C" fn() -> usize,
84}
85
86unsafe impl Send for FFI_PartitionEvaluator {}
87unsafe impl Sync for FFI_PartitionEvaluator {}
88
89pub struct PartitionEvaluatorPrivateData {
90 pub evaluator: Box<dyn PartitionEvaluator>,
91}
92
93impl FFI_PartitionEvaluator {
94 unsafe fn inner_mut(&mut self) -> &mut Box<dyn PartitionEvaluator + 'static> {
95 unsafe {
96 let private_data = self.private_data as *mut PartitionEvaluatorPrivateData;
97 &mut (*private_data).evaluator
98 }
99 }
100
101 unsafe fn inner(&self) -> &(dyn PartitionEvaluator + 'static) {
102 unsafe {
103 let private_data = self.private_data as *mut PartitionEvaluatorPrivateData;
104 (*private_data).evaluator.as_ref()
105 }
106 }
107}
108
109unsafe extern "C" fn evaluate_all_fn_wrapper(
110 evaluator: &mut FFI_PartitionEvaluator,
111 values: SVec<WrappedArray>,
112 num_rows: usize,
113) -> FFI_Result<WrappedArray> {
114 unsafe {
115 let inner = evaluator.inner_mut();
116
117 let values_arrays = values
118 .into_iter()
119 .map(|v| v.try_into().map_err(DataFusionError::from))
120 .collect::<Result<Vec<ArrayRef>>>();
121 let values_arrays = sresult_return!(values_arrays);
122
123 let return_array =
124 inner
125 .evaluate_all(&values_arrays, num_rows)
126 .and_then(|array| {
127 WrappedArray::try_from(&array).map_err(DataFusionError::from)
128 });
129
130 sresult!(return_array)
131 }
132}
133
134unsafe extern "C" fn evaluate_fn_wrapper(
135 evaluator: &mut FFI_PartitionEvaluator,
136 values: SVec<WrappedArray>,
137 range: FFI_Range,
138) -> FFI_Result<SVec<u8>> {
139 unsafe {
140 let inner = evaluator.inner_mut();
141
142 let values_arrays = values
143 .into_iter()
144 .map(|v| v.try_into().map_err(DataFusionError::from))
145 .collect::<Result<Vec<ArrayRef>>>();
146 let values_arrays = sresult_return!(values_arrays);
147
148 let scalar_result =
151 sresult_return!(inner.evaluate(&values_arrays, &range.into()));
152 let proto_result: datafusion_proto::protobuf::ScalarValue =
153 sresult_return!((&scalar_result).try_into());
154
155 FFI_Result::Ok(proto_result.encode_to_vec().into_iter().collect())
156 }
157}
158
159unsafe extern "C" fn evaluate_all_with_rank_fn_wrapper(
160 evaluator: &FFI_PartitionEvaluator,
161 num_rows: usize,
162 ranks_in_partition: SVec<FFI_Range>,
163) -> FFI_Result<WrappedArray> {
164 unsafe {
165 let inner = evaluator.inner();
166
167 let ranks_in_partition = ranks_in_partition
168 .into_iter()
169 .map(Range::from)
170 .collect::<Vec<_>>();
171
172 let return_array = inner
173 .evaluate_all_with_rank(num_rows, &ranks_in_partition)
174 .and_then(|array| {
175 WrappedArray::try_from(&array).map_err(DataFusionError::from)
176 });
177
178 sresult!(return_array)
179 }
180}
181
182unsafe extern "C" fn get_range_fn_wrapper(
183 evaluator: &FFI_PartitionEvaluator,
184 idx: usize,
185 n_rows: usize,
186) -> FFI_Result<FFI_Range> {
187 unsafe {
188 let inner = evaluator.inner();
189 let range = inner.get_range(idx, n_rows).map(FFI_Range::from);
190
191 sresult!(range)
192 }
193}
194
195unsafe extern "C" fn release_fn_wrapper(evaluator: &mut FFI_PartitionEvaluator) {
196 unsafe {
197 if !evaluator.private_data.is_null() {
198 let private_data = Box::from_raw(
199 evaluator.private_data as *mut PartitionEvaluatorPrivateData,
200 );
201 drop(private_data);
202 evaluator.private_data = std::ptr::null_mut();
203 }
204 }
205}
206
207impl From<Box<dyn PartitionEvaluator>> for FFI_PartitionEvaluator {
208 fn from(evaluator: Box<dyn PartitionEvaluator>) -> Self {
209 if (evaluator.as_ref() as &dyn Any).is::<ForeignPartitionEvaluator>() {
210 let evaluator = (evaluator as Box<dyn Any>)
211 .downcast::<ForeignPartitionEvaluator>()
212 .expect("already checked type");
213 return evaluator.evaluator;
214 }
215
216 let is_causal = evaluator.is_causal();
217 let supports_bounded_execution = evaluator.supports_bounded_execution();
218 let include_rank = evaluator.include_rank();
219 let uses_window_frame = evaluator.uses_window_frame();
220
221 let private_data = PartitionEvaluatorPrivateData { evaluator };
222
223 Self {
224 evaluate: evaluate_fn_wrapper,
225 evaluate_all: evaluate_all_fn_wrapper,
226 evaluate_all_with_rank: evaluate_all_with_rank_fn_wrapper,
227 get_range: get_range_fn_wrapper,
228 is_causal,
229 supports_bounded_execution,
230 include_rank,
231 uses_window_frame,
232 release: release_fn_wrapper,
233 private_data: Box::into_raw(Box::new(private_data)) as *mut c_void,
234 library_marker_id: crate::get_library_marker_id,
235 }
236 }
237}
238
239impl Drop for FFI_PartitionEvaluator {
240 fn drop(&mut self) {
241 unsafe { (self.release)(self) }
242 }
243}
244
245#[derive(Debug)]
252pub struct ForeignPartitionEvaluator {
253 evaluator: FFI_PartitionEvaluator,
254}
255
256impl From<FFI_PartitionEvaluator> for Box<dyn PartitionEvaluator> {
257 fn from(mut evaluator: FFI_PartitionEvaluator) -> Self {
258 if (evaluator.library_marker_id)() == crate::get_library_marker_id() {
259 unsafe {
260 let private_data = Box::from_raw(
261 evaluator.private_data as *mut PartitionEvaluatorPrivateData,
262 );
263 evaluator.private_data = std::ptr::null_mut();
265 private_data.evaluator
266 }
267 } else {
268 Box::new(ForeignPartitionEvaluator { evaluator })
269 }
270 }
271}
272
273impl PartitionEvaluator for ForeignPartitionEvaluator {
274 fn memoize(&mut self, _state: &mut WindowAggState) -> Result<()> {
275 Ok(())
278 }
279
280 fn get_range(&self, idx: usize, n_rows: usize) -> Result<Range<usize>> {
281 let range = unsafe { (self.evaluator.get_range)(&self.evaluator, idx, n_rows) };
282 df_result!(range).map(Range::from)
283 }
284
285 fn is_causal(&self) -> bool {
287 self.evaluator.is_causal
288 }
289
290 fn evaluate_all(&mut self, values: &[ArrayRef], num_rows: usize) -> Result<ArrayRef> {
291 let result = unsafe {
292 let values = values
293 .iter()
294 .map(WrappedArray::try_from)
295 .collect::<std::result::Result<SVec<_>, ArrowError>>()?;
296 (self.evaluator.evaluate_all)(&mut self.evaluator, values, num_rows)
297 };
298
299 let array = df_result!(result)?;
300
301 Ok(array.try_into()?)
302 }
303
304 fn evaluate(
305 &mut self,
306 values: &[ArrayRef],
307 range: &Range<usize>,
308 ) -> Result<ScalarValue> {
309 unsafe {
310 let values = values
311 .iter()
312 .map(WrappedArray::try_from)
313 .collect::<std::result::Result<SVec<_>, ArrowError>>()?;
314
315 let scalar_bytes = df_result!((self.evaluator.evaluate)(
316 &mut self.evaluator,
317 values,
318 range.to_owned().into()
319 ))?;
320
321 let proto_scalar =
322 datafusion_proto::protobuf::ScalarValue::decode(scalar_bytes.as_ref())
323 .map_err(|e| DataFusionError::External(Box::new(e)))?;
324
325 ScalarValue::try_from(&proto_scalar).map_err(DataFusionError::from)
326 }
327 }
328
329 fn evaluate_all_with_rank(
330 &self,
331 num_rows: usize,
332 ranks_in_partition: &[Range<usize>],
333 ) -> Result<ArrayRef> {
334 let result = unsafe {
335 let ranks_in_partition = ranks_in_partition
336 .iter()
337 .map(|rank| FFI_Range::from(rank.to_owned()))
338 .collect();
339 (self.evaluator.evaluate_all_with_rank)(
340 &self.evaluator,
341 num_rows,
342 ranks_in_partition,
343 )
344 };
345
346 let array = df_result!(result)?;
347
348 Ok(array.try_into()?)
349 }
350
351 fn supports_bounded_execution(&self) -> bool {
352 self.evaluator.supports_bounded_execution
353 }
354
355 fn uses_window_frame(&self) -> bool {
356 self.evaluator.uses_window_frame
357 }
358
359 fn include_rank(&self) -> bool {
360 self.evaluator.include_rank
361 }
362}
363
364#[cfg(test)]
365mod tests {
366 use arrow::array::ArrayRef;
367 use datafusion::logical_expr::PartitionEvaluator;
368
369 use crate::udwf::partition_evaluator::{
370 FFI_PartitionEvaluator, ForeignPartitionEvaluator,
371 };
372
373 #[derive(Debug)]
374 struct TestPartitionEvaluator {}
375
376 impl PartitionEvaluator for TestPartitionEvaluator {
377 fn evaluate_all(
378 &mut self,
379 values: &[ArrayRef],
380 _num_rows: usize,
381 ) -> datafusion_common::Result<ArrayRef> {
382 Ok(values[0].to_owned())
383 }
384 }
385
386 #[test]
387 fn test_ffi_partition_evaluator_local_bypass_inner() -> datafusion_common::Result<()>
388 {
389 let original_accum = TestPartitionEvaluator {};
390 let boxed_accum: Box<dyn PartitionEvaluator> = Box::new(original_accum);
391
392 let ffi_accum: FFI_PartitionEvaluator = boxed_accum.into();
393
394 let foreign_accum: Box<dyn PartitionEvaluator> = ffi_accum.into();
396 unsafe {
397 let concrete = &*(foreign_accum.as_ref() as *const dyn PartitionEvaluator
398 as *const TestPartitionEvaluator);
399 assert!(!concrete.uses_window_frame());
400 }
401
402 let original_accum = TestPartitionEvaluator {};
404 let boxed_accum: Box<dyn PartitionEvaluator> = Box::new(original_accum);
405 let mut ffi_accum: FFI_PartitionEvaluator = boxed_accum.into();
406 ffi_accum.library_marker_id = crate::mock_foreign_marker_id;
407 let foreign_accum: Box<dyn PartitionEvaluator> = ffi_accum.into();
408 unsafe {
409 let concrete = &*(foreign_accum.as_ref() as *const dyn PartitionEvaluator
410 as *const ForeignPartitionEvaluator);
411 assert!(!concrete.uses_window_frame());
412 }
413
414 Ok(())
415 }
416}