Skip to main content

datafusion_ffi/udf/
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
18use std::ffi::c_void;
19use std::hash::{Hash, Hasher};
20use std::sync::Arc;
21
22use arrow::array::Array;
23use arrow::datatypes::{DataType, Field};
24use arrow::error::ArrowError;
25use arrow::ffi::{FFI_ArrowSchema, from_ffi, to_ffi};
26use arrow_schema::FieldRef;
27use datafusion_common::config::ConfigOptions;
28use datafusion_common::{DataFusionError, Result, internal_err};
29use datafusion_expr::type_coercion::functions::fields_with_udf;
30use datafusion_expr::{
31    ColumnarValue, ReturnFieldArgs, ScalarFunctionArgs, ScalarUDF, ScalarUDFImpl,
32    Signature,
33};
34use return_type_args::{
35    FFI_ReturnFieldArgs, ForeignReturnFieldArgs, ForeignReturnFieldArgsOwned,
36};
37
38use stabby::string::String as SString;
39use stabby::vec::Vec as SVec;
40
41use crate::arrow_wrappers::{WrappedArray, WrappedSchema};
42use crate::config::FFI_ConfigOptions;
43use crate::expr::columnar_value::FFI_ColumnarValue;
44use crate::util::{
45    FFI_Result, rvec_wrapped_to_vec_datatype, vec_datatype_to_rvec_wrapped,
46};
47use crate::volatility::FFI_Volatility;
48use crate::{df_result, sresult, sresult_return};
49
50pub mod return_type_args;
51
52/// A stable struct for sharing a [`ScalarUDF`] across FFI boundaries.
53#[repr(C)]
54#[derive(Debug)]
55pub struct FFI_ScalarUDF {
56    /// FFI equivalent to the `name` of a [`ScalarUDF`]
57    pub name: SString,
58
59    /// FFI equivalent to the `aliases` of a [`ScalarUDF`]
60    pub aliases: SVec<SString>,
61
62    /// FFI equivalent to the `volatility` of a [`ScalarUDF`]
63    pub volatility: FFI_Volatility,
64
65    /// Determines the return info of the underlying [`ScalarUDF`].
66    pub return_field_from_args: unsafe extern "C" fn(
67        udf: &Self,
68        args: FFI_ReturnFieldArgs,
69    ) -> FFI_Result<WrappedSchema>,
70
71    /// Execute the underlying [`ScalarUDF`] and return the result as a `FFI_ArrowArray`
72    /// within an AbiStable wrapper.
73    pub invoke_with_args: unsafe extern "C" fn(
74        udf: &Self,
75        args: SVec<WrappedArray>,
76        arg_fields: SVec<WrappedSchema>,
77        num_rows: usize,
78        return_field: WrappedSchema,
79        config_options: FFI_ConfigOptions,
80    ) -> FFI_Result<FFI_ColumnarValue>,
81
82    /// See [`ScalarUDFImpl`] for details on short_circuits
83    pub short_circuits: bool,
84
85    /// Performs type coercion. To simply this interface, all UDFs are treated as having
86    /// user defined signatures, which will in turn call coerce_types to be called. This
87    /// call should be transparent to most users as the internal function performs the
88    /// appropriate calls on the underlying [`ScalarUDF`]
89    pub coerce_types: unsafe extern "C" fn(
90        udf: &Self,
91        arg_types: SVec<WrappedSchema>,
92    ) -> FFI_Result<SVec<WrappedSchema>>,
93
94    /// Used to create a clone on the provider of the udf. This should
95    /// only need to be called by the receiver of the udf.
96    pub clone: unsafe extern "C" fn(udf: &Self) -> Self,
97
98    /// Release the memory of the private data when it is no longer being used.
99    pub release: unsafe extern "C" fn(udf: &mut Self),
100
101    /// Internal data. This is only to be accessed by the provider of the udf.
102    /// A [`ForeignScalarUDF`] should never attempt to access this data.
103    pub private_data: *mut c_void,
104
105    /// Utility to identify when FFI objects are accessed locally through
106    /// the foreign interface. See [`crate::get_library_marker_id`] and
107    /// the crate's `README.md` for more information.
108    pub library_marker_id: extern "C" fn() -> usize,
109}
110
111unsafe impl Send for FFI_ScalarUDF {}
112unsafe impl Sync for FFI_ScalarUDF {}
113
114pub struct ScalarUDFPrivateData {
115    pub udf: Arc<ScalarUDF>,
116}
117
118impl FFI_ScalarUDF {
119    fn inner(&self) -> &Arc<ScalarUDF> {
120        let private_data = self.private_data as *const ScalarUDFPrivateData;
121        unsafe { &(*private_data).udf }
122    }
123}
124
125unsafe extern "C" fn return_field_from_args_fn_wrapper(
126    udf: &FFI_ScalarUDF,
127    args: FFI_ReturnFieldArgs,
128) -> FFI_Result<WrappedSchema> {
129    let args: ForeignReturnFieldArgsOwned = sresult_return!((&args).try_into());
130    let args_ref: ForeignReturnFieldArgs = (&args).into();
131
132    let return_type = udf
133        .inner()
134        .return_field_from_args((&args_ref).into())
135        .and_then(|f| FFI_ArrowSchema::try_from(&f).map_err(DataFusionError::from))
136        .map(WrappedSchema);
137
138    sresult!(return_type)
139}
140
141unsafe extern "C" fn coerce_types_fn_wrapper(
142    udf: &FFI_ScalarUDF,
143    arg_types: SVec<WrappedSchema>,
144) -> FFI_Result<SVec<WrappedSchema>> {
145    let arg_types = sresult_return!(rvec_wrapped_to_vec_datatype(&arg_types));
146
147    let arg_fields = arg_types
148        .iter()
149        .map(|dt| Arc::new(Field::new("f", dt.clone(), true)))
150        .collect::<Vec<_>>();
151    let return_types =
152        sresult_return!(fields_with_udf(&arg_fields, udf.inner().as_ref()))
153            .into_iter()
154            .map(|f| f.data_type().to_owned())
155            .collect::<Vec<_>>();
156
157    sresult!(vec_datatype_to_rvec_wrapped(&return_types))
158}
159
160unsafe extern "C" fn invoke_with_args_fn_wrapper(
161    udf: &FFI_ScalarUDF,
162    args: SVec<WrappedArray>,
163    arg_fields: SVec<WrappedSchema>,
164    number_rows: usize,
165    return_field: WrappedSchema,
166    config_options: FFI_ConfigOptions,
167) -> FFI_Result<FFI_ColumnarValue> {
168    unsafe {
169        let args = args
170            .into_iter()
171            .map(|arr| {
172                from_ffi(arr.array, &arr.schema.0)
173                    .map(|v| ColumnarValue::Array(arrow::array::make_array(v)))
174            })
175            .collect::<std::result::Result<_, _>>();
176
177        let args = sresult_return!(args);
178        let return_field = sresult_return!(Field::try_from(&return_field.0)).into();
179
180        let arg_fields = arg_fields
181            .into_iter()
182            .map(|wrapped_field| {
183                Field::try_from(&wrapped_field.0)
184                    .map(Arc::new)
185                    .map_err(DataFusionError::from)
186            })
187            .collect::<Result<Vec<FieldRef>>>();
188        let arg_fields = sresult_return!(arg_fields);
189        let config_options = sresult_return!(ConfigOptions::try_from(config_options));
190        let config_options = Arc::new(config_options);
191
192        let args = ScalarFunctionArgs {
193            args,
194            arg_fields,
195            number_rows,
196            return_field,
197            config_options,
198        };
199
200        sresult!(
201            udf.inner()
202                .invoke_with_args(args)
203                .and_then(FFI_ColumnarValue::try_from)
204        )
205    }
206}
207
208unsafe extern "C" fn release_fn_wrapper(udf: &mut FFI_ScalarUDF) {
209    unsafe {
210        debug_assert!(!udf.private_data.is_null());
211        let private_data = Box::from_raw(udf.private_data as *mut ScalarUDFPrivateData);
212        drop(private_data);
213        udf.private_data = std::ptr::null_mut();
214    }
215}
216
217unsafe extern "C" fn clone_fn_wrapper(udf: &FFI_ScalarUDF) -> FFI_ScalarUDF {
218    unsafe {
219        let private_data = udf.private_data as *const ScalarUDFPrivateData;
220        let udf_data = &(*private_data);
221
222        Arc::clone(&udf_data.udf).into()
223    }
224}
225
226impl Clone for FFI_ScalarUDF {
227    fn clone(&self) -> Self {
228        unsafe { (self.clone)(self) }
229    }
230}
231
232impl From<Arc<ScalarUDF>> for FFI_ScalarUDF {
233    fn from(udf: Arc<ScalarUDF>) -> Self {
234        if let Some(udf) = udf.inner().downcast_ref::<ForeignScalarUDF>() {
235            return udf.udf.clone();
236        }
237
238        let name = udf.name().into();
239        let aliases = udf.aliases().iter().map(|a| a.to_owned().into()).collect();
240        let volatility = udf.signature().volatility.into();
241        let short_circuits = udf.short_circuits();
242
243        let private_data = Box::new(ScalarUDFPrivateData { udf });
244
245        Self {
246            name,
247            aliases,
248            volatility,
249            short_circuits,
250            invoke_with_args: invoke_with_args_fn_wrapper,
251            return_field_from_args: return_field_from_args_fn_wrapper,
252            coerce_types: coerce_types_fn_wrapper,
253            clone: clone_fn_wrapper,
254            release: release_fn_wrapper,
255            private_data: Box::into_raw(private_data) as *mut c_void,
256            library_marker_id: crate::get_library_marker_id,
257        }
258    }
259}
260
261impl Drop for FFI_ScalarUDF {
262    fn drop(&mut self) {
263        unsafe { (self.release)(self) }
264    }
265}
266
267/// This struct is used to access an UDF provided by a foreign
268/// library across a FFI boundary.
269///
270/// The ForeignScalarUDF is to be used by the caller of the UDF, so it has
271/// no knowledge or access to the private data. All interaction with the UDF
272/// must occur through the functions defined in FFI_ScalarUDF.
273#[derive(Debug)]
274pub struct ForeignScalarUDF {
275    name: String,
276    aliases: Vec<String>,
277    udf: FFI_ScalarUDF,
278    signature: Signature,
279}
280
281unsafe impl Send for ForeignScalarUDF {}
282unsafe impl Sync for ForeignScalarUDF {}
283
284impl PartialEq for ForeignScalarUDF {
285    fn eq(&self, other: &Self) -> bool {
286        let Self {
287            name,
288            aliases,
289            udf,
290            signature,
291        } = self;
292        name == &other.name
293            && aliases == &other.aliases
294            && std::ptr::eq(udf, &other.udf)
295            && signature == &other.signature
296    }
297}
298impl Eq for ForeignScalarUDF {}
299
300impl Hash for ForeignScalarUDF {
301    fn hash<H: Hasher>(&self, state: &mut H) {
302        let Self {
303            name,
304            aliases,
305            udf,
306            signature,
307        } = self;
308        name.hash(state);
309        aliases.hash(state);
310        std::ptr::hash(udf, state);
311        signature.hash(state);
312    }
313}
314
315impl From<&FFI_ScalarUDF> for Arc<dyn ScalarUDFImpl> {
316    fn from(udf: &FFI_ScalarUDF) -> Self {
317        if (udf.library_marker_id)() == crate::get_library_marker_id() {
318            Arc::clone(udf.inner().inner())
319        } else {
320            let name = udf.name.to_string();
321            let signature = Signature::user_defined((&udf.volatility).into());
322
323            let aliases = udf.aliases.iter().map(|s| s.to_string()).collect();
324
325            Arc::new(ForeignScalarUDF {
326                name,
327                udf: udf.clone(),
328                aliases,
329                signature,
330            })
331        }
332    }
333}
334
335impl ScalarUDFImpl for ForeignScalarUDF {
336    fn name(&self) -> &str {
337        &self.name
338    }
339
340    fn signature(&self) -> &Signature {
341        &self.signature
342    }
343
344    fn return_type(&self, _arg_types: &[DataType]) -> Result<DataType> {
345        internal_err!("ForeignScalarUDF implements return_field_from_args instead.")
346    }
347
348    fn return_field_from_args(&self, args: ReturnFieldArgs) -> Result<FieldRef> {
349        let args: FFI_ReturnFieldArgs = args.try_into()?;
350
351        let result = unsafe { (self.udf.return_field_from_args)(&self.udf, args) };
352
353        let result = df_result!(result);
354
355        result.and_then(|r| {
356            Field::try_from(&r.0)
357                .map(Arc::new)
358                .map_err(DataFusionError::from)
359        })
360    }
361
362    fn invoke_with_args(&self, invoke_args: ScalarFunctionArgs) -> Result<ColumnarValue> {
363        let ScalarFunctionArgs {
364            args,
365            arg_fields,
366            number_rows,
367            return_field,
368            config_options,
369        } = invoke_args;
370
371        let args = args
372            .into_iter()
373            .map(|v| v.to_array(number_rows))
374            .collect::<Result<Vec<_>>>()?
375            .into_iter()
376            .map(|v| {
377                to_ffi(&v.to_data()).map(|(ffi_array, ffi_schema)| WrappedArray {
378                    array: ffi_array,
379                    schema: WrappedSchema(ffi_schema),
380                })
381            })
382            .collect::<std::result::Result<Vec<_>, ArrowError>>()?
383            .into_iter()
384            .collect();
385
386        let arg_fields_wrapped = arg_fields
387            .iter()
388            .map(FFI_ArrowSchema::try_from)
389            .collect::<std::result::Result<Vec<_>, ArrowError>>()?;
390
391        let arg_fields = arg_fields_wrapped
392            .into_iter()
393            .map(WrappedSchema)
394            .collect::<SVec<_>>();
395
396        let return_field = Arc::unwrap_or_clone(return_field);
397        let return_field = WrappedSchema(FFI_ArrowSchema::try_from(return_field)?);
398        let config_options = config_options.as_ref().into();
399
400        let result = unsafe {
401            (self.udf.invoke_with_args)(
402                &self.udf,
403                args,
404                arg_fields,
405                number_rows,
406                return_field,
407                config_options,
408            )
409        };
410
411        let result = df_result!(result)?;
412        result.try_into()
413    }
414
415    fn aliases(&self) -> &[String] {
416        &self.aliases
417    }
418
419    fn short_circuits(&self) -> bool {
420        self.udf.short_circuits
421    }
422
423    fn coerce_types(&self, arg_types: &[DataType]) -> Result<Vec<DataType>> {
424        unsafe {
425            let arg_types = vec_datatype_to_rvec_wrapped(arg_types)?;
426            let result_types = df_result!((self.udf.coerce_types)(&self.udf, arg_types))?;
427            Ok(rvec_wrapped_to_vec_datatype(&result_types)?)
428        }
429    }
430}
431
432#[cfg(test)]
433mod tests {
434    use super::*;
435
436    #[test]
437    fn test_round_trip_scalar_udf() -> Result<()> {
438        let original_udf = datafusion::functions::math::abs::AbsFunc::new();
439        let original_udf = Arc::new(ScalarUDF::from(original_udf));
440
441        let mut local_udf: FFI_ScalarUDF = Arc::clone(&original_udf).into();
442        local_udf.library_marker_id = crate::mock_foreign_marker_id;
443
444        let foreign_udf: Arc<dyn ScalarUDFImpl> = (&local_udf).into();
445
446        assert_eq!(original_udf.name(), foreign_udf.name());
447
448        Ok(())
449    }
450
451    #[test]
452    fn test_ffi_udf_local_bypass() -> Result<()> {
453        use datafusion::functions::math::abs::AbsFunc;
454        let original_udf = AbsFunc::new();
455        let original_udf = Arc::new(ScalarUDF::from(original_udf));
456
457        let mut ffi_udf = FFI_ScalarUDF::from(original_udf);
458
459        // Verify local libraries can be downcast to their original
460        let foreign_udf: Arc<dyn ScalarUDFImpl> = (&ffi_udf).into();
461        assert!(foreign_udf.is::<AbsFunc>());
462
463        // Verify different library markers generate foreign providers
464        ffi_udf.library_marker_id = crate::mock_foreign_marker_id;
465        let foreign_udf: Arc<dyn ScalarUDFImpl> = (&ffi_udf).into();
466        assert!(foreign_udf.is::<ForeignScalarUDF>());
467
468        Ok(())
469    }
470}