1pub(crate) mod metrics;
19pub(crate) mod partitioning;
20pub(crate) mod sort;
21
22use std::ffi::c_void;
23use std::fmt::{Display, Formatter};
24use std::hash::{DefaultHasher, Hash, Hasher};
25use std::sync::Arc;
26
27use arrow::array::{ArrayRef, BooleanArray, RecordBatch};
28use arrow::datatypes::SchemaRef;
29use arrow_schema::ffi::FFI_ArrowSchema;
30use arrow_schema::{DataType, Field, FieldRef, Schema};
31use datafusion_common::{Result, ffi_datafusion_err};
32use datafusion_expr::ColumnarValue;
33use datafusion_expr::interval_arithmetic::Interval;
34use datafusion_expr::sort_properties::ExprProperties;
35#[expect(deprecated)]
36use datafusion_expr::statistics::Distribution;
37use datafusion_physical_expr::PhysicalExpr;
38use datafusion_physical_expr_common::physical_expr::fmt_sql;
39
40use stabby::string::String as SString;
41use stabby::vec::Vec as SVec;
42
43use crate::arrow_wrappers::{WrappedArray, WrappedSchema};
44use crate::expr::columnar_value::FFI_ColumnarValue;
45use crate::expr::distribution::FFI_Distribution;
46use crate::expr::expr_properties::FFI_ExprProperties;
47use crate::expr::interval::FFI_Interval;
48use crate::record_batch_stream::{
49 record_batch_to_wrapped_array, wrapped_array_to_record_batch,
50};
51use crate::util::{FFI_Option, FFI_Result};
52use crate::{df_result, sresult, sresult_return};
53
54#[repr(C)]
55#[derive(Debug)]
56pub struct FFI_PhysicalExpr {
57 pub data_type: unsafe extern "C" fn(
58 &Self,
59 input_schema: WrappedSchema,
60 ) -> FFI_Result<WrappedSchema>,
61
62 pub nullable:
63 unsafe extern "C" fn(&Self, input_schema: WrappedSchema) -> FFI_Result<bool>,
64
65 pub evaluate:
66 unsafe extern "C" fn(&Self, batch: WrappedArray) -> FFI_Result<FFI_ColumnarValue>,
67
68 pub return_field: unsafe extern "C" fn(
69 &Self,
70 input_schema: WrappedSchema,
71 ) -> FFI_Result<WrappedSchema>,
72
73 pub evaluate_selection: unsafe extern "C" fn(
74 &Self,
75 batch: WrappedArray,
76 selection: WrappedArray,
77 ) -> FFI_Result<FFI_ColumnarValue>,
78
79 pub children: unsafe extern "C" fn(&Self) -> SVec<FFI_PhysicalExpr>,
80
81 pub new_with_children: unsafe extern "C" fn(
82 &Self,
83 children: &SVec<FFI_PhysicalExpr>,
84 ) -> FFI_Result<Self>,
85
86 pub evaluate_bounds: unsafe extern "C" fn(
87 &Self,
88 children: SVec<FFI_Interval>,
89 ) -> FFI_Result<FFI_Interval>,
90
91 pub propagate_constraints:
92 unsafe extern "C" fn(
93 &Self,
94 interval: FFI_Interval,
95 children: SVec<FFI_Interval>,
96 ) -> FFI_Result<FFI_Option<SVec<FFI_Interval>>>,
97
98 pub evaluate_statistics: unsafe extern "C" fn(
99 &Self,
100 children: SVec<FFI_Distribution>,
101 ) -> FFI_Result<FFI_Distribution>,
102
103 pub propagate_statistics:
104 unsafe extern "C" fn(
105 &Self,
106 parent: FFI_Distribution,
107 children: SVec<FFI_Distribution>,
108 ) -> FFI_Result<FFI_Option<SVec<FFI_Distribution>>>,
109
110 pub get_properties: unsafe extern "C" fn(
111 &Self,
112 children: SVec<FFI_ExprProperties>,
113 ) -> FFI_Result<FFI_ExprProperties>,
114
115 pub fmt_sql: unsafe extern "C" fn(&Self) -> FFI_Result<SString>,
116
117 pub snapshot: unsafe extern "C" fn(&Self) -> FFI_Result<FFI_Option<FFI_PhysicalExpr>>,
118
119 pub snapshot_generation: unsafe extern "C" fn(&Self) -> u64,
120
121 pub is_volatile_node: unsafe extern "C" fn(&Self) -> bool,
122
123 pub expression_id: unsafe extern "C" fn(&Self) -> FFI_Option<u64>,
124
125 pub display: unsafe extern "C" fn(&Self) -> SString,
127
128 pub hash: unsafe extern "C" fn(&Self) -> u64,
130
131 pub clone: unsafe extern "C" fn(plan: &Self) -> Self,
134
135 pub release: unsafe extern "C" fn(arg: &mut Self),
137
138 pub version: unsafe extern "C" fn() -> u64,
140
141 pub private_data: *mut c_void,
144
145 pub library_marker_id: extern "C" fn() -> usize,
148}
149
150unsafe impl Send for FFI_PhysicalExpr {}
151unsafe impl Sync for FFI_PhysicalExpr {}
152
153impl FFI_PhysicalExpr {
154 fn inner(&self) -> &Arc<dyn PhysicalExpr> {
155 unsafe {
156 let private_data = self.private_data as *const PhysicalExprPrivateData;
157 &(*private_data).expr
158 }
159 }
160}
161
162struct PhysicalExprPrivateData {
163 expr: Arc<dyn PhysicalExpr>,
164}
165
166unsafe extern "C" fn data_type_fn_wrapper(
167 expr: &FFI_PhysicalExpr,
168 input_schema: WrappedSchema,
169) -> FFI_Result<WrappedSchema> {
170 let expr = expr.inner();
171 let schema: SchemaRef = input_schema.into();
172 let data_type = expr
173 .data_type(&schema)
174 .and_then(|dt| FFI_ArrowSchema::try_from(dt).map_err(Into::into))
175 .map(WrappedSchema);
176 sresult!(data_type)
177}
178
179unsafe extern "C" fn nullable_fn_wrapper(
180 expr: &FFI_PhysicalExpr,
181 input_schema: WrappedSchema,
182) -> FFI_Result<bool> {
183 let expr = expr.inner();
184 let schema: SchemaRef = input_schema.into();
185 sresult!(expr.nullable(&schema))
186}
187
188unsafe extern "C" fn evaluate_fn_wrapper(
189 expr: &FFI_PhysicalExpr,
190 batch: WrappedArray,
191) -> FFI_Result<FFI_ColumnarValue> {
192 let batch = sresult_return!(wrapped_array_to_record_batch(batch));
193 sresult!(
194 expr.inner()
195 .evaluate(&batch)
196 .and_then(FFI_ColumnarValue::try_from)
197 )
198}
199
200unsafe extern "C" fn return_field_fn_wrapper(
201 expr: &FFI_PhysicalExpr,
202 input_schema: WrappedSchema,
203) -> FFI_Result<WrappedSchema> {
204 let expr = expr.inner();
205 let schema: SchemaRef = input_schema.into();
206 sresult!(
207 expr.return_field(&schema)
208 .and_then(|f| FFI_ArrowSchema::try_from(&f).map_err(Into::into))
209 .map(WrappedSchema)
210 )
211}
212
213unsafe extern "C" fn evaluate_selection_fn_wrapper(
214 expr: &FFI_PhysicalExpr,
215 batch: WrappedArray,
216 selection: WrappedArray,
217) -> FFI_Result<FFI_ColumnarValue> {
218 let batch = sresult_return!(wrapped_array_to_record_batch(batch));
219 let selection: ArrayRef = sresult_return!(selection.try_into());
220 let selection = sresult_return!(
221 selection
222 .as_any()
223 .downcast_ref::<BooleanArray>()
224 .ok_or(ffi_datafusion_err!("Unexpected selection array type"))
225 );
226 sresult!(
227 expr.inner()
228 .evaluate_selection(&batch, selection)
229 .and_then(FFI_ColumnarValue::try_from)
230 )
231}
232
233unsafe extern "C" fn children_fn_wrapper(
234 expr: &FFI_PhysicalExpr,
235) -> SVec<FFI_PhysicalExpr> {
236 let expr = expr.inner();
237 let children = expr.children();
238 children
239 .into_iter()
240 .map(|child| FFI_PhysicalExpr::from(Arc::clone(child)))
241 .collect()
242}
243
244unsafe extern "C" fn new_with_children_fn_wrapper(
245 expr: &FFI_PhysicalExpr,
246 children: &SVec<FFI_PhysicalExpr>,
247) -> FFI_Result<FFI_PhysicalExpr> {
248 let expr = Arc::clone(expr.inner());
249 let children = children.iter().map(Into::into).collect::<Vec<_>>();
250 sresult!(expr.with_new_children(children).map(FFI_PhysicalExpr::from))
251}
252
253unsafe extern "C" fn evaluate_bounds_fn_wrapper(
254 expr: &FFI_PhysicalExpr,
255 children: SVec<FFI_Interval>,
256) -> FFI_Result<FFI_Interval> {
257 let expr = expr.inner();
258 let children = sresult_return!(
259 children
260 .into_iter()
261 .map(Interval::try_from)
262 .collect::<Result<Vec<_>>>()
263 );
264 let children_borrowed = children.iter().collect::<Vec<_>>();
265
266 sresult!(
267 expr.evaluate_bounds(&children_borrowed)
268 .and_then(FFI_Interval::try_from)
269 )
270}
271
272unsafe extern "C" fn propagate_constraints_fn_wrapper(
273 expr: &FFI_PhysicalExpr,
274 interval: FFI_Interval,
275 children: SVec<FFI_Interval>,
276) -> FFI_Result<FFI_Option<SVec<FFI_Interval>>> {
277 let expr = expr.inner();
278 let interval = sresult_return!(Interval::try_from(interval));
279 let children = sresult_return!(
280 children
281 .into_iter()
282 .map(Interval::try_from)
283 .collect::<Result<Vec<_>>>()
284 );
285 let children_borrowed = children.iter().collect::<Vec<_>>();
286
287 let result =
288 sresult_return!(expr.propagate_constraints(&interval, &children_borrowed));
289
290 let result = sresult_return!(
291 result
292 .map(|intervals| intervals
293 .into_iter()
294 .map(FFI_Interval::try_from)
295 .collect::<Result<SVec<_>>>())
296 .transpose()
297 );
298
299 FFI_Result::Ok(result.into())
300}
301
302#[expect(deprecated)]
303unsafe extern "C" fn evaluate_statistics_fn_wrapper(
304 expr: &FFI_PhysicalExpr,
305 children: SVec<FFI_Distribution>,
306) -> FFI_Result<FFI_Distribution> {
307 let expr = expr.inner();
308 let children = sresult_return!(
309 children
310 .into_iter()
311 .map(Distribution::try_from)
312 .collect::<Result<Vec<_>>>()
313 );
314 let children_borrowed = children.iter().collect::<Vec<_>>();
315 sresult!(
316 expr.evaluate_statistics(&children_borrowed)
317 .and_then(|dist| FFI_Distribution::try_from(&dist))
318 )
319}
320
321#[expect(deprecated)]
322unsafe extern "C" fn propagate_statistics_fn_wrapper(
323 expr: &FFI_PhysicalExpr,
324 parent: FFI_Distribution,
325 children: SVec<FFI_Distribution>,
326) -> FFI_Result<FFI_Option<SVec<FFI_Distribution>>> {
327 let expr = expr.inner();
328 let parent = sresult_return!(Distribution::try_from(parent));
329 let children = sresult_return!(
330 children
331 .into_iter()
332 .map(Distribution::try_from)
333 .collect::<Result<Vec<_>>>()
334 );
335 let children_borrowed = children.iter().collect::<Vec<_>>();
336
337 let result = sresult_return!(expr.propagate_statistics(&parent, &children_borrowed));
338 let result = sresult_return!(
339 result
340 .map(|dists| dists
341 .iter()
342 .map(FFI_Distribution::try_from)
343 .collect::<Result<SVec<_>>>())
344 .transpose()
345 );
346
347 FFI_Result::Ok(result.into())
348}
349
350unsafe extern "C" fn get_properties_fn_wrapper(
351 expr: &FFI_PhysicalExpr,
352 children: SVec<FFI_ExprProperties>,
353) -> FFI_Result<FFI_ExprProperties> {
354 let expr = expr.inner();
355 let children = sresult_return!(
356 children
357 .into_iter()
358 .map(ExprProperties::try_from)
359 .collect::<Result<Vec<_>>>()
360 );
361 sresult!(
362 expr.get_properties(&children)
363 .and_then(|p| FFI_ExprProperties::try_from(&p))
364 )
365}
366
367unsafe extern "C" fn fmt_sql_fn_wrapper(expr: &FFI_PhysicalExpr) -> FFI_Result<SString> {
368 let expr = expr.inner();
369 let result = fmt_sql(expr.as_ref()).to_string();
370 FFI_Result::Ok(result.into())
371}
372
373unsafe extern "C" fn snapshot_fn_wrapper(
374 expr: &FFI_PhysicalExpr,
375) -> FFI_Result<FFI_Option<FFI_PhysicalExpr>> {
376 let expr = expr.inner();
377 sresult!(
378 expr.snapshot()
379 .map(|snapshot| snapshot.map(FFI_PhysicalExpr::from).into())
380 )
381}
382
383unsafe extern "C" fn snapshot_generation_fn_wrapper(expr: &FFI_PhysicalExpr) -> u64 {
384 let expr = expr.inner();
385 expr.snapshot_generation()
386}
387
388unsafe extern "C" fn is_volatile_node_fn_wrapper(expr: &FFI_PhysicalExpr) -> bool {
389 let expr = expr.inner();
390 expr.is_volatile_node()
391}
392
393unsafe extern "C" fn expression_id_fn_wrapper(
394 expr: &FFI_PhysicalExpr,
395) -> FFI_Option<u64> {
396 expr.inner().expression_id().into()
397}
398
399unsafe extern "C" fn display_fn_wrapper(expr: &FFI_PhysicalExpr) -> SString {
400 let expr = expr.inner();
401 format!("{expr}").into()
402}
403
404unsafe extern "C" fn hash_fn_wrapper(expr: &FFI_PhysicalExpr) -> u64 {
405 let expr = expr.inner();
406 let mut hasher = DefaultHasher::new();
407 expr.hash(&mut hasher);
408 hasher.finish()
409}
410
411unsafe extern "C" fn release_fn_wrapper(expr: &mut FFI_PhysicalExpr) {
412 unsafe {
413 debug_assert!(!expr.private_data.is_null());
414 let private_data =
415 Box::from_raw(expr.private_data as *mut PhysicalExprPrivateData);
416 drop(private_data);
417 expr.private_data = std::ptr::null_mut();
418 }
419}
420
421unsafe extern "C" fn clone_fn_wrapper(expr: &FFI_PhysicalExpr) -> FFI_PhysicalExpr {
422 unsafe {
423 let old_private_data = expr.private_data as *const PhysicalExprPrivateData;
424
425 let private_data = Box::into_raw(Box::new(PhysicalExprPrivateData {
426 expr: Arc::clone(&(*old_private_data).expr),
427 })) as *mut c_void;
428
429 FFI_PhysicalExpr {
430 data_type: data_type_fn_wrapper,
431 nullable: nullable_fn_wrapper,
432 evaluate: evaluate_fn_wrapper,
433 return_field: return_field_fn_wrapper,
434 evaluate_selection: evaluate_selection_fn_wrapper,
435 children: children_fn_wrapper,
436 new_with_children: new_with_children_fn_wrapper,
437 evaluate_bounds: evaluate_bounds_fn_wrapper,
438 propagate_constraints: propagate_constraints_fn_wrapper,
439 evaluate_statistics: evaluate_statistics_fn_wrapper,
440 propagate_statistics: propagate_statistics_fn_wrapper,
441 get_properties: get_properties_fn_wrapper,
442 fmt_sql: fmt_sql_fn_wrapper,
443 snapshot: snapshot_fn_wrapper,
444 snapshot_generation: snapshot_generation_fn_wrapper,
445 is_volatile_node: is_volatile_node_fn_wrapper,
446 expression_id: expression_id_fn_wrapper,
447 display: display_fn_wrapper,
448 hash: hash_fn_wrapper,
449 clone: clone_fn_wrapper,
450 release: release_fn_wrapper,
451 version: super::version,
452 private_data,
453 library_marker_id: crate::get_library_marker_id,
454 }
455 }
456}
457
458impl Drop for FFI_PhysicalExpr {
459 fn drop(&mut self) {
460 unsafe { (self.release)(self) }
461 }
462}
463
464impl From<Arc<dyn PhysicalExpr>> for FFI_PhysicalExpr {
465 fn from(expr: Arc<dyn PhysicalExpr>) -> Self {
467 if let Some(expr) = expr.downcast_ref::<ForeignPhysicalExpr>() {
468 return expr.expr.clone();
469 }
470
471 let private_data = Box::new(PhysicalExprPrivateData { expr });
472
473 Self {
474 data_type: data_type_fn_wrapper,
475 nullable: nullable_fn_wrapper,
476 evaluate: evaluate_fn_wrapper,
477 return_field: return_field_fn_wrapper,
478 evaluate_selection: evaluate_selection_fn_wrapper,
479 children: children_fn_wrapper,
480 new_with_children: new_with_children_fn_wrapper,
481 evaluate_bounds: evaluate_bounds_fn_wrapper,
482 propagate_constraints: propagate_constraints_fn_wrapper,
483 evaluate_statistics: evaluate_statistics_fn_wrapper,
484 propagate_statistics: propagate_statistics_fn_wrapper,
485 get_properties: get_properties_fn_wrapper,
486 fmt_sql: fmt_sql_fn_wrapper,
487 snapshot: snapshot_fn_wrapper,
488 snapshot_generation: snapshot_generation_fn_wrapper,
489 is_volatile_node: is_volatile_node_fn_wrapper,
490 expression_id: expression_id_fn_wrapper,
491 display: display_fn_wrapper,
492 hash: hash_fn_wrapper,
493 clone: clone_fn_wrapper,
494 release: release_fn_wrapper,
495 version: super::version,
496 private_data: Box::into_raw(private_data) as *mut c_void,
497 library_marker_id: crate::get_library_marker_id,
498 }
499 }
500}
501
502#[derive(Debug)]
507pub struct ForeignPhysicalExpr {
508 expr: FFI_PhysicalExpr,
509 children: Vec<Arc<dyn PhysicalExpr>>,
510}
511
512unsafe impl Send for ForeignPhysicalExpr {}
513unsafe impl Sync for ForeignPhysicalExpr {}
514
515impl From<&FFI_PhysicalExpr> for Arc<dyn PhysicalExpr> {
516 fn from(ffi_expr: &FFI_PhysicalExpr) -> Self {
517 if (ffi_expr.library_marker_id)() == crate::get_library_marker_id() {
518 Arc::clone(ffi_expr.inner())
519 } else {
520 let children = unsafe {
521 (ffi_expr.children)(ffi_expr)
522 .into_iter()
523 .map(|expr| <Arc<dyn PhysicalExpr>>::from(&expr))
524 .collect()
525 };
526
527 Arc::new(ForeignPhysicalExpr {
528 expr: ffi_expr.clone(),
529 children,
530 })
531 }
532 }
533}
534
535impl Clone for FFI_PhysicalExpr {
536 fn clone(&self) -> Self {
537 unsafe { (self.clone)(self) }
538 }
539}
540
541impl PhysicalExpr for ForeignPhysicalExpr {
542 fn data_type(&self, input_schema: &Schema) -> Result<DataType> {
543 unsafe {
544 let schema = WrappedSchema::from(Arc::new(input_schema.clone()));
545 df_result!((self.expr.data_type)(&self.expr, schema))
546 .and_then(|d| DataType::try_from(&d.0).map_err(Into::into))
547 }
548 }
549
550 fn nullable(&self, input_schema: &Schema) -> Result<bool> {
551 unsafe {
552 let schema = WrappedSchema::from(Arc::new(input_schema.clone()));
553 df_result!((self.expr.nullable)(&self.expr, schema))
554 }
555 }
556
557 fn evaluate(&self, batch: &RecordBatch) -> Result<ColumnarValue> {
558 unsafe {
559 let batch = df_result!(record_batch_to_wrapped_array(batch.clone()))?;
560 df_result!((self.expr.evaluate)(&self.expr, batch))
561 .and_then(ColumnarValue::try_from)
562 }
563 }
564
565 fn return_field(&self, input_schema: &Schema) -> Result<FieldRef> {
566 unsafe {
567 let schema = WrappedSchema::from(Arc::new(input_schema.clone()));
568 let result = df_result!((self.expr.return_field)(&self.expr, schema))?;
569 Field::try_from(&result.0).map(Arc::new).map_err(Into::into)
570 }
571 }
572
573 fn evaluate_selection(
574 &self,
575 batch: &RecordBatch,
576 selection: &BooleanArray,
577 ) -> Result<ColumnarValue> {
578 unsafe {
579 let batch = df_result!(record_batch_to_wrapped_array(batch.clone()))?;
580 let selection: ArrayRef = Arc::new(selection.clone());
584 let selection = WrappedArray::try_from(&selection)?;
585 df_result!((self.expr.evaluate_selection)(&self.expr, batch, selection))
586 .and_then(ColumnarValue::try_from)
587 }
588 }
589
590 fn children(&self) -> Vec<&Arc<dyn PhysicalExpr>> {
591 self.children.iter().collect()
592 }
593
594 fn with_new_children(
595 self: Arc<Self>,
596 children: Vec<Arc<dyn PhysicalExpr>>,
597 ) -> Result<Arc<dyn PhysicalExpr>> {
598 unsafe {
599 let children = children.into_iter().map(FFI_PhysicalExpr::from).collect();
600 df_result!(
601 (self.expr.new_with_children)(&self.expr, &children).map(|expr| <Arc<
602 dyn PhysicalExpr,
603 >>::from(
604 &expr
605 ))
606 )
607 }
608 }
609
610 fn evaluate_bounds(&self, children: &[&Interval]) -> Result<Interval> {
611 unsafe {
612 let children = children
613 .iter()
614 .map(|interval| FFI_Interval::try_from(*interval))
615 .collect::<Result<SVec<_>>>()?;
616 df_result!((self.expr.evaluate_bounds)(&self.expr, children))
617 .and_then(Interval::try_from)
618 }
619 }
620
621 fn propagate_constraints(
622 &self,
623 interval: &Interval,
624 children: &[&Interval],
625 ) -> Result<Option<Vec<Interval>>> {
626 unsafe {
627 let interval = interval.try_into()?;
628 let children = children
629 .iter()
630 .map(|interval| FFI_Interval::try_from(*interval))
631 .collect::<Result<SVec<_>>>()?;
632 let result = df_result!((self.expr.propagate_constraints)(
633 &self.expr, interval, children
634 ))?;
635
636 let result: Option<_> = result
637 .map(|intervals| {
638 intervals
639 .into_iter()
640 .map(Interval::try_from)
641 .collect::<Result<Vec<_>>>()
642 })
643 .into();
644 result.transpose()
645 }
646 }
647
648 #[expect(deprecated)]
649 fn evaluate_statistics(&self, children: &[&Distribution]) -> Result<Distribution> {
650 unsafe {
651 let children = children
652 .iter()
653 .map(|dist| FFI_Distribution::try_from(*dist))
654 .collect::<Result<SVec<_>>>()?;
655
656 let result =
657 df_result!((self.expr.evaluate_statistics)(&self.expr, children))?;
658 Distribution::try_from(result)
659 }
660 }
661
662 #[expect(deprecated)]
663 fn propagate_statistics(
664 &self,
665 parent: &Distribution,
666 children: &[&Distribution],
667 ) -> Result<Option<Vec<Distribution>>> {
668 unsafe {
669 let parent = FFI_Distribution::try_from(parent)?;
670 let children = children
671 .iter()
672 .map(|dist| FFI_Distribution::try_from(*dist))
673 .collect::<Result<SVec<_>>>()?;
674 let result = df_result!((self.expr.propagate_statistics)(
675 &self.expr, parent, children
676 ))?;
677
678 let result: Option<Result<Vec<Distribution>>> = result
679 .map(|dists| {
680 dists
681 .into_iter()
682 .map(Distribution::try_from)
683 .collect::<Result<Vec<_>>>()
684 })
685 .into();
686
687 result.transpose()
688 }
689 }
690
691 fn get_properties(&self, children: &[ExprProperties]) -> Result<ExprProperties> {
692 unsafe {
693 let children = children
694 .iter()
695 .map(FFI_ExprProperties::try_from)
696 .collect::<Result<SVec<_>>>()?;
697 df_result!((self.expr.get_properties)(&self.expr, children))
698 .and_then(ExprProperties::try_from)
699 }
700 }
701
702 fn fmt_sql(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
703 unsafe {
704 match (self.expr.fmt_sql)(&self.expr) {
705 FFI_Result::Ok(sql) => write!(f, "{sql}"),
706 FFI_Result::Err(_) => Err(std::fmt::Error),
707 }
708 }
709 }
710
711 fn snapshot(&self) -> Result<Option<Arc<dyn PhysicalExpr>>> {
712 unsafe {
713 let result = df_result!((self.expr.snapshot)(&self.expr))?;
714 Ok(result
715 .map(|expr| <Arc<dyn PhysicalExpr>>::from(&expr))
716 .into())
717 }
718 }
719
720 fn snapshot_generation(&self) -> u64 {
721 unsafe { (self.expr.snapshot_generation)(&self.expr) }
722 }
723
724 fn is_volatile_node(&self) -> bool {
725 unsafe { (self.expr.is_volatile_node)(&self.expr) }
726 }
727
728 fn expression_id(&self) -> Option<u64> {
729 unsafe { (self.expr.expression_id)(&self.expr) }.into()
730 }
731}
732
733impl Eq for ForeignPhysicalExpr {}
734impl PartialEq for ForeignPhysicalExpr {
735 fn eq(&self, other: &Self) -> bool {
736 std::ptr::eq(self, other)
738 }
739}
740impl Hash for ForeignPhysicalExpr {
741 fn hash<H: Hasher>(&self, state: &mut H) {
742 let value = unsafe { (self.expr.hash)(&self.expr) };
743 value.hash(state)
744 }
745}
746
747impl Display for ForeignPhysicalExpr {
748 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
749 let display = unsafe { (self.expr.display)(&self.expr) };
750 write!(f, "{display}")
751 }
752}
753
754#[cfg(test)]
755mod tests {
756 use std::hash::{DefaultHasher, Hash, Hasher};
757 use std::sync::Arc;
758
759 use arrow::array::{BooleanArray, RecordBatch, record_batch};
760 use datafusion_common::tree_node::DynTreeNode;
761 use datafusion_common::{DataFusionError, ScalarValue};
762 use datafusion_expr::interval_arithmetic::Interval;
763 #[expect(deprecated)]
764 use datafusion_expr::statistics::Distribution;
765 use datafusion_physical_expr::expressions::{
766 Column, DynamicFilterPhysicalExpr, NegativeExpr, NotExpr, lit,
767 };
768 use datafusion_physical_expr_common::physical_expr::{PhysicalExpr, fmt_sql};
769
770 use crate::physical_expr::FFI_PhysicalExpr;
771
772 fn create_test_expr() -> (Arc<dyn PhysicalExpr>, Arc<dyn PhysicalExpr>) {
773 let original = Arc::new(Column::new("a", 0)) as Arc<dyn PhysicalExpr>;
774 let mut ffi_expr = FFI_PhysicalExpr::from(Arc::clone(&original));
775 ffi_expr.library_marker_id = crate::mock_foreign_marker_id;
776
777 let foreign_expr: Arc<dyn PhysicalExpr> = (&ffi_expr).into();
778
779 (original, foreign_expr)
780 }
781
782 #[test]
783 fn ffi_physical_expr_expression_id() {
784 let dynamic_filter = Arc::new(DynamicFilterPhysicalExpr::new(vec![], lit(true)));
785 let expected_id = dynamic_filter
786 .expression_id()
787 .expect("dynamic filters always have an expression ID");
788 let expression: Arc<dyn PhysicalExpr> =
789 Arc::<DynamicFilterPhysicalExpr>::clone(&dynamic_filter);
790 let mut ffi_expr = FFI_PhysicalExpr::from(expression);
791 ffi_expr.library_marker_id = crate::mock_foreign_marker_id;
792
793 let foreign_expr: Arc<dyn PhysicalExpr> = (&ffi_expr).into();
794 assert_eq!(foreign_expr.expression_id(), Some(expected_id));
795 }
796
797 fn test_record_batch() -> RecordBatch {
798 record_batch!(("a", Int32, [1, 2, 3])).unwrap()
799 }
800
801 #[test]
802 fn ffi_physical_expr_fields() -> Result<(), DataFusionError> {
803 let (original, foreign_expr) = create_test_expr();
804 let schema = test_record_batch().schema();
805
806 assert_ne!(original.as_ref(), foreign_expr.as_ref());
808
809 assert_eq!(
810 original.return_field(&schema)?,
811 foreign_expr.return_field(&schema)?
812 );
813
814 assert_eq!(
815 original.data_type(&schema)?,
816 foreign_expr.data_type(&schema)?
817 );
818 assert_eq!(original.nullable(&schema)?, foreign_expr.nullable(&schema)?);
819
820 Ok(())
821 }
822 #[test]
823 fn ffi_physical_expr_evaluate() -> Result<(), DataFusionError> {
824 let (original, foreign_expr) = create_test_expr();
825 let rb = test_record_batch();
826
827 assert_eq!(
828 original.evaluate(&rb)?.to_array(3)?.as_ref(),
829 foreign_expr.evaluate(&rb)?.to_array(3)?.as_ref()
830 );
831
832 Ok(())
833 }
834 #[test]
835 fn ffi_physical_expr_selection() -> Result<(), DataFusionError> {
836 let (original, foreign_expr) = create_test_expr();
837 let rb = test_record_batch();
838
839 let selection = BooleanArray::from(vec![true, false, true]);
840
841 assert_eq!(
842 original
843 .evaluate_selection(&rb, &selection)?
844 .to_array(3)?
845 .as_ref(),
846 foreign_expr
847 .evaluate_selection(&rb, &selection)?
848 .to_array(3)?
849 .as_ref()
850 );
851 Ok(())
852 }
853
854 #[test]
855 fn ffi_physical_expr_with_children() -> Result<(), DataFusionError> {
856 let (original, _) = create_test_expr();
857 let not_expr =
858 Arc::new(NotExpr::new(Arc::clone(&original))) as Arc<dyn PhysicalExpr>;
859 let mut ffi_not = FFI_PhysicalExpr::from(not_expr);
860 ffi_not.library_marker_id = crate::mock_foreign_marker_id;
861 let foreign_not: Arc<dyn PhysicalExpr> = (&ffi_not).into();
862
863 let replacement = Arc::new(Column::new("b", 1)) as Arc<dyn PhysicalExpr>;
864 let updated =
865 Arc::clone(&foreign_not).with_new_children(vec![Arc::clone(&replacement)])?;
866 assert_eq!(
867 format!("{updated:?}").as_str(),
868 "NotExpr { arg: Column { name: \"b\", index: 1 } }"
869 );
870
871 let updated = foreign_not
872 .with_new_arc_children(Arc::clone(&foreign_not), vec![replacement])?;
873 assert_eq!(format!("{updated}").as_str(), "NOT b@1");
874
875 Ok(())
876 }
877
878 fn create_test_negative_expr() -> (Arc<dyn PhysicalExpr>, Arc<dyn PhysicalExpr>) {
879 let (original, _) = create_test_expr();
880
881 let negative_expr =
882 Arc::new(NegativeExpr::new(Arc::clone(&original))) as Arc<dyn PhysicalExpr>;
883 let mut ffi_neg = FFI_PhysicalExpr::from(Arc::clone(&negative_expr));
884 ffi_neg.library_marker_id = crate::mock_foreign_marker_id;
885 let foreign_neg: Arc<dyn PhysicalExpr> = (&ffi_neg).into();
886
887 (negative_expr, foreign_neg)
888 }
889
890 #[test]
891 fn ffi_physical_expr_bounds() -> Result<(), DataFusionError> {
892 let (negative_expr, foreign_neg) = create_test_negative_expr();
893
894 let interval =
895 Interval::try_new(ScalarValue::Int32(Some(0)), ScalarValue::Int32(Some(10)))?;
896 let left = negative_expr.evaluate_bounds(&[&interval])?;
897 let right = foreign_neg.evaluate_bounds(&[&interval])?;
898
899 assert_eq!(left, right);
900
901 Ok(())
902 }
903
904 #[test]
905 fn ffi_physical_expr_constraints() -> Result<(), DataFusionError> {
906 let (negative_expr, foreign_neg) = create_test_negative_expr();
907
908 let interval =
909 Interval::try_new(ScalarValue::Int32(Some(0)), ScalarValue::Int32(Some(10)))?;
910
911 let child =
912 Interval::try_new(ScalarValue::Int32(Some(0)), ScalarValue::Int32(Some(10)))?;
913 let left = negative_expr.propagate_constraints(&interval, &[&child])?;
914 let right = foreign_neg.propagate_constraints(&interval, &[&child])?;
915
916 assert_eq!(left, right);
917 Ok(())
918 }
919
920 #[test]
921 #[expect(deprecated)]
922 fn ffi_physical_expr_statistics() -> Result<(), DataFusionError> {
923 let (negative_expr, foreign_neg) = create_test_negative_expr();
924 let interval =
925 Interval::try_new(ScalarValue::Int32(Some(0)), ScalarValue::Int32(Some(10)))?;
926
927 for distribution in [
928 Distribution::new_uniform(interval.clone())?,
929 Distribution::new_exponential(
930 ScalarValue::Int32(Some(10)),
931 ScalarValue::Int32(Some(10)),
932 true,
933 )?,
934 Distribution::new_gaussian(
935 ScalarValue::Int32(Some(10)),
936 ScalarValue::Int32(Some(10)),
937 )?,
938 Distribution::new_generic(
939 ScalarValue::Int32(Some(10)),
940 ScalarValue::Int32(Some(10)),
941 ScalarValue::Int32(Some(10)),
942 interval,
943 )?,
944 ] {
945 let left = negative_expr.evaluate_statistics(&[&distribution])?;
946 let right = foreign_neg.evaluate_statistics(&[&distribution])?;
947
948 assert_eq!(left, right);
949
950 let left =
951 negative_expr.propagate_statistics(&distribution, &[&distribution])?;
952 let right =
953 foreign_neg.propagate_statistics(&distribution, &[&distribution])?;
954
955 assert_eq!(left, right);
956 }
957 Ok(())
958 }
959
960 #[test]
961 fn ffi_physical_expr_properties() -> Result<(), DataFusionError> {
962 let (original, foreign_expr) = create_test_expr();
963
964 let left = original.get_properties(&[])?;
965 let right = foreign_expr.get_properties(&[])?;
966
967 assert_eq!(left.sort_properties, right.sort_properties);
968 assert_eq!(left.range, right.range);
969
970 Ok(())
971 }
972
973 #[test]
974 fn ffi_physical_formatting() {
975 let (original, foreign_expr) = create_test_expr();
976
977 let left = format!("{}", fmt_sql(original.as_ref()));
978 let right = format!("{}", fmt_sql(foreign_expr.as_ref()));
979 assert_eq!(left, right);
980 }
981
982 #[test]
983 fn ffi_physical_expr_snapshots() -> Result<(), DataFusionError> {
984 let (original, foreign_expr) = create_test_expr();
985
986 let left = original.snapshot()?;
987 let right = foreign_expr.snapshot()?;
988 assert_eq!(left, right);
989
990 assert_eq!(
991 original.snapshot_generation(),
992 foreign_expr.snapshot_generation()
993 );
994
995 Ok(())
996 }
997
998 #[test]
999 fn ffi_physical_expr_volatility() {
1000 let (original, foreign_expr) = create_test_expr();
1001 assert_eq!(original.is_volatile_node(), foreign_expr.is_volatile_node());
1002 }
1003
1004 #[test]
1005 fn ffi_physical_expr_hash() {
1006 let (_, foreign_1) = create_test_expr();
1007 let (_, foreign_2) = create_test_expr();
1008
1009 assert_ne!(&foreign_1, &foreign_2);
1010
1011 let mut hasher = DefaultHasher::new();
1012 foreign_1.as_ref().hash(&mut hasher);
1013 let hash_1 = hasher.finish();
1014
1015 let mut hasher = DefaultHasher::new();
1016 foreign_2.as_ref().hash(&mut hasher);
1017 let hash_2 = hasher.finish();
1018
1019 assert_eq!(hash_1, hash_2);
1023 }
1024
1025 #[test]
1026 fn ffi_physical_expr_display() {
1027 let (original, foreign_expr) = create_test_expr();
1028 assert_eq!(format!("{original}"), format!("{foreign_expr}"));
1029 }
1030}