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