Skip to main content

datafusion_ffi/physical_expr/
mod.rs

1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements.  See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership.  The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License.  You may obtain a copy of the License at
8//
9//   http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied.  See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18pub(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    // Display trait
126    pub display: unsafe extern "C" fn(&Self) -> SString,
127
128    // Hash trait
129    pub hash: unsafe extern "C" fn(&Self) -> u64,
130
131    /// Used to create a clone on the provider of the execution plan. This should
132    /// only need to be called by the receiver of the plan.
133    pub clone: unsafe extern "C" fn(plan: &Self) -> Self,
134
135    /// Release the memory of the private data when it is no longer being used.
136    pub release: unsafe extern "C" fn(arg: &mut Self),
137
138    /// Return the major DataFusion version number of this provider.
139    pub version: unsafe extern "C" fn() -> u64,
140
141    /// Internal data. This is only to be accessed by the provider of the plan.
142    /// A [`ForeignPhysicalExpr`] should never attempt to access this data.
143    pub private_data: *mut c_void,
144
145    /// Utility to identify when FFI objects are accessed locally through
146    /// the foreign interface.
147    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    /// Creates a new [`FFI_PhysicalExpr`].
466    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/// This wrapper struct exists on the receiver side of the FFI interface, so it has
503/// no guarantees about being able to access the data in `private_data`. Any functions
504/// defined on this struct must only use the stable functions provided in
505/// FFI_PhysicalExpr to interact with the expression.
506#[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            // This is not ideal - we are cloning the selection array
581            // This is not terrible since it will be a small array.
582            // The other alternative is to modify the trait signature.
583            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        // FFI_PhysicalExpr cannot be compared, so identity equality is the best we can do.
737        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        // Verify the mock marker worked, otherwise tests to follow are not useful
807        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        // We cannot compare a local object and a foreign object
1020        // so create two foreign objects that *should* be identical
1021        // even though they were created differently.
1022        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}