datafusion_ffi/session/
config.rs1use std::collections::HashMap;
19use std::ffi::c_void;
20
21use abi_stable::StableAbi;
22use abi_stable::std_types::{RHashMap, RString};
23use datafusion_common::error::{DataFusionError, Result};
24use datafusion_execution::config::SessionConfig;
25
26#[repr(C)]
38#[derive(Debug, StableAbi)]
39pub struct FFI_SessionConfig {
40 pub config_options: unsafe extern "C" fn(config: &Self) -> RHashMap<RString, RString>,
43
44 pub clone: unsafe extern "C" fn(plan: &Self) -> Self,
47
48 pub release: unsafe extern "C" fn(arg: &mut Self),
50
51 pub private_data: *mut c_void,
53
54 pub library_marker_id: extern "C" fn() -> usize,
58}
59
60unsafe impl Send for FFI_SessionConfig {}
61unsafe impl Sync for FFI_SessionConfig {}
62
63impl FFI_SessionConfig {
64 fn inner(&self) -> &SessionConfig {
65 let private_data = self.private_data as *mut SessionConfigPrivateData;
66 unsafe { &(*private_data).config }
67 }
68}
69
70unsafe extern "C" fn config_options_fn_wrapper(
71 config: &FFI_SessionConfig,
72) -> RHashMap<RString, RString> {
73 let config_options = config.inner().options();
74
75 let mut options = RHashMap::default();
76 for config_entry in config_options.entries() {
77 if let Some(value) = config_entry.value {
78 options.insert(config_entry.key.into(), value.into());
79 }
80 }
81
82 options
83}
84
85unsafe extern "C" fn release_fn_wrapper(config: &mut FFI_SessionConfig) {
86 unsafe {
87 debug_assert!(!config.private_data.is_null());
88 let private_data =
89 Box::from_raw(config.private_data as *mut SessionConfigPrivateData);
90 drop(private_data);
91 config.private_data = std::ptr::null_mut();
92 }
93}
94
95unsafe extern "C" fn clone_fn_wrapper(config: &FFI_SessionConfig) -> FFI_SessionConfig {
96 unsafe {
97 let old_private_data = config.private_data as *mut SessionConfigPrivateData;
98 let old_config = (*old_private_data).config.clone();
99
100 let private_data = Box::new(SessionConfigPrivateData { config: old_config });
101
102 FFI_SessionConfig {
103 config_options: config_options_fn_wrapper,
104 private_data: Box::into_raw(private_data) as *mut c_void,
105 clone: clone_fn_wrapper,
106 release: release_fn_wrapper,
107 library_marker_id: crate::get_library_marker_id,
108 }
109 }
110}
111
112struct SessionConfigPrivateData {
113 pub config: SessionConfig,
114}
115
116impl From<&SessionConfig> for FFI_SessionConfig {
117 fn from(session: &SessionConfig) -> Self {
118 let private_data = Box::new(SessionConfigPrivateData {
119 config: session.clone(),
120 });
121
122 Self {
123 config_options: config_options_fn_wrapper,
124 private_data: Box::into_raw(private_data) as *mut c_void,
125 clone: clone_fn_wrapper,
126 release: release_fn_wrapper,
127 library_marker_id: crate::get_library_marker_id,
128 }
129 }
130}
131
132impl Clone for FFI_SessionConfig {
133 fn clone(&self) -> Self {
134 unsafe { (self.clone)(self) }
135 }
136}
137
138impl Drop for FFI_SessionConfig {
139 fn drop(&mut self) {
140 unsafe { (self.release)(self) };
141 }
142}
143
144impl TryFrom<&FFI_SessionConfig> for SessionConfig {
145 type Error = DataFusionError;
146
147 fn try_from(config: &FFI_SessionConfig) -> Result<Self, Self::Error> {
148 if (config.library_marker_id)() == crate::get_library_marker_id() {
149 return Ok(config.inner().clone());
150 }
151
152 let config_options = unsafe { (config.config_options)(config) };
153
154 let mut options_map = HashMap::new();
155 config_options.iter().for_each(|kv_pair| {
156 options_map.insert(kv_pair.0.to_string(), kv_pair.1.to_string());
157 });
158
159 SessionConfig::from_string_hash_map(&options_map)
160 }
161}
162
163#[cfg(test)]
164mod tests {
165 use super::*;
166
167 #[test]
168 fn test_round_trip_ffi_session_config() -> Result<()> {
169 let session_config = SessionConfig::new();
170 let original_options = session_config.options().entries();
171
172 let mut ffi_config: FFI_SessionConfig = (&session_config).into();
173 let _ = ffi_config.clone();
174 ffi_config.library_marker_id = crate::mock_foreign_marker_id;
175
176 let foreign_config: SessionConfig = (&ffi_config).try_into()?;
177
178 let returned_options = foreign_config.options().entries();
179
180 assert_eq!(original_options.len(), returned_options.len());
181
182 Ok(())
183 }
184}