Skip to main content

datafusion_ffi/session/
config.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;
19
20use crate::config::FFI_ConfigOptions;
21use datafusion_common::config::ConfigOptions;
22use datafusion_common::error::{DataFusionError, Result};
23use datafusion_execution::config::SessionConfig;
24
25/// A stable struct for sharing [`SessionConfig`] across FFI boundaries.
26/// Instead of attempting to expose the entire SessionConfig interface, we
27/// convert the config options into a map from a string to string and pass
28/// those values across the FFI boundary. On the receiver side, we
29/// reconstruct a SessionConfig from those values.
30///
31/// It is possible that using different versions of DataFusion across the
32/// FFI boundary could have differing expectations of the config options.
33/// This is a limitation of this approach, but exposing the entire
34/// SessionConfig via a FFI interface would be extensive and provide limited
35/// value over this version.
36#[repr(C)]
37#[derive(Debug)]
38pub struct FFI_SessionConfig {
39    /// FFI stable configuration options.
40    pub config_options: FFI_ConfigOptions,
41
42    /// Used to create a clone on the provider of the execution plan. This should
43    /// only need to be called by the receiver of the plan.
44    pub clone: unsafe extern "C" fn(plan: &Self) -> Self,
45
46    /// Release the memory of the private data when it is no longer being used.
47    pub release: unsafe extern "C" fn(arg: &mut Self),
48
49    /// Internal data. This is only to be accessed by the provider of the plan.
50    pub private_data: *mut c_void,
51
52    /// Utility to identify when FFI objects are accessed locally through
53    /// the foreign interface. See [`crate::get_library_marker_id`] and
54    /// the crate's `README.md` for more information.
55    pub library_marker_id: extern "C" fn() -> usize,
56}
57
58unsafe impl Send for FFI_SessionConfig {}
59unsafe impl Sync for FFI_SessionConfig {}
60
61impl FFI_SessionConfig {
62    fn inner(&self) -> &SessionConfig {
63        let private_data = self.private_data as *mut SessionConfigPrivateData;
64        unsafe { &(*private_data).config }
65    }
66}
67
68unsafe extern "C" fn release_fn_wrapper(config: &mut FFI_SessionConfig) {
69    unsafe {
70        debug_assert!(!config.private_data.is_null());
71        let private_data =
72            Box::from_raw(config.private_data as *mut SessionConfigPrivateData);
73        drop(private_data);
74        config.private_data = std::ptr::null_mut();
75    }
76}
77
78unsafe extern "C" fn clone_fn_wrapper(config: &FFI_SessionConfig) -> FFI_SessionConfig {
79    unsafe {
80        let old_private_data = config.private_data as *mut SessionConfigPrivateData;
81        let old_config = (*old_private_data).config.clone();
82
83        let private_data = Box::new(SessionConfigPrivateData { config: old_config });
84
85        FFI_SessionConfig {
86            config_options: config.config_options.clone(),
87            private_data: Box::into_raw(private_data) as *mut c_void,
88            clone: clone_fn_wrapper,
89            release: release_fn_wrapper,
90            library_marker_id: crate::get_library_marker_id,
91        }
92    }
93}
94
95struct SessionConfigPrivateData {
96    pub config: SessionConfig,
97}
98
99impl From<&SessionConfig> for FFI_SessionConfig {
100    fn from(session: &SessionConfig) -> Self {
101        let private_data = Box::new(SessionConfigPrivateData {
102            config: session.clone(),
103        });
104
105        let config_options = FFI_ConfigOptions::from(session.options().as_ref());
106
107        Self {
108            config_options,
109            private_data: Box::into_raw(private_data) as *mut c_void,
110            clone: clone_fn_wrapper,
111            release: release_fn_wrapper,
112            library_marker_id: crate::get_library_marker_id,
113        }
114    }
115}
116
117impl Clone for FFI_SessionConfig {
118    fn clone(&self) -> Self {
119        unsafe { (self.clone)(self) }
120    }
121}
122
123impl Drop for FFI_SessionConfig {
124    fn drop(&mut self) {
125        unsafe { (self.release)(self) };
126    }
127}
128
129impl TryFrom<&FFI_SessionConfig> for SessionConfig {
130    type Error = DataFusionError;
131
132    fn try_from(config: &FFI_SessionConfig) -> Result<Self, Self::Error> {
133        if (config.library_marker_id)() == crate::get_library_marker_id() {
134            return Ok(config.inner().clone());
135        }
136
137        let config_options = ConfigOptions::try_from(config.config_options.clone())?;
138
139        Ok(SessionConfig::from(config_options))
140    }
141}
142
143#[cfg(test)]
144mod tests {
145    use super::*;
146
147    #[test]
148    fn test_round_trip_ffi_session_config() -> Result<()> {
149        let session_config = SessionConfig::new();
150        let original_options = session_config.options().entries();
151
152        let mut ffi_config: FFI_SessionConfig = (&session_config).into();
153        let _ = ffi_config.clone();
154        ffi_config.library_marker_id = crate::mock_foreign_marker_id;
155
156        let foreign_config: SessionConfig = (&ffi_config).try_into()?;
157
158        let returned_options = foreign_config.options().entries();
159
160        assert_eq!(original_options.len(), returned_options.len());
161
162        Ok(())
163    }
164}