Skip to content

Commit e9fd408

Browse files
committed
Implement FFI_ExtensionOptions
1 parent 33ac70d commit e9fd408

2 files changed

Lines changed: 212 additions & 0 deletions

File tree

Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
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+
18+
use std::{any::Any, ffi::c_void, ops::Deref};
19+
20+
use abi_stable::{std_types::{RHashMap, RResult, RStr, RString, RVec}, RTuple, StableAbi};
21+
use abi_stable::std_types::{ROption, Tuple3};
22+
use arrow::{array::ArrayRef, error::ArrowError};
23+
use datafusion::{
24+
error::{DataFusionError, Result},
25+
scalar::ScalarValue,
26+
};
27+
use datafusion_common::config::{ConfigEntry, ExtensionOptions};
28+
use prost::Message;
29+
30+
use crate::{arrow_wrappers::WrappedArray, df_result, rresult, rresult_return};
31+
32+
/// A stable struct for sharing [`ExtensionOptions`] across FFI boundaries.
33+
/// For an explanation of each field, see the corresponding function
34+
/// defined in [`ExtensionOptions`].
35+
#[repr(C)]
36+
#[derive(Debug, StableAbi)]
37+
#[allow(non_camel_case_types)]
38+
pub struct FFI_ExtensionOptions {
39+
pub cloned: unsafe extern "C" fn(&Self) -> FFI_ExtensionOptions,
40+
41+
pub set: unsafe extern "C" fn(&mut Self, key: RStr, value: RStr) -> RResult<(), RString>,
42+
43+
pub entries:
44+
unsafe extern "C" fn(&Self) -> RVec<Tuple3<RString, ROption<RString>, RStr>>,
45+
46+
/// Release the memory of the private data when it is no longer being used.
47+
pub release: unsafe extern "C" fn(&mut Self),
48+
49+
/// Internal data. This is only to be accessed by the provider of the options.
50+
/// A [`ForeignExtensionOptions`] should never attempt to access this data.
51+
pub private_data: *mut c_void,
52+
}
53+
54+
unsafe impl Send for FFI_ExtensionOptions {}
55+
unsafe impl Sync for FFI_ExtensionOptions {}
56+
57+
pub struct ExtensionOptionsPrivateData {
58+
pub options: Box<dyn ExtensionOptions>,
59+
}
60+
61+
impl FFI_ExtensionOptions {
62+
#[inline]
63+
unsafe fn inner_mut(&mut self) -> &mut Box<dyn ExtensionOptions> {
64+
let private_data = self.private_data as *mut ExtensionOptionsPrivateData;
65+
&mut (*private_data).options
66+
}
67+
68+
#[inline]
69+
unsafe fn inner(&self) -> &dyn ExtensionOptions {
70+
let private_data = self.private_data as *const ExtensionOptionsPrivateData;
71+
(*private_data).options.deref()
72+
}
73+
}
74+
75+
unsafe extern "C" fn cloned_fn_wrapper(options: &FFI_ExtensionOptions) -> FFI_ExtensionOptions {
76+
options.inner().cloned().into()
77+
}
78+
79+
unsafe extern "C" fn set_fn_wrapper(
80+
options: &mut FFI_ExtensionOptions,
81+
key: RStr,
82+
value: RStr,
83+
) -> RResult<(), RString> {
84+
let key = key.as_str();
85+
let value = value.as_str();
86+
87+
rresult!(options.inner_mut().set(key, value))
88+
}
89+
90+
unsafe extern "C" fn entries_fn_wrapper(
91+
options: &FFI_ExtensionOptions,
92+
) -> RVec<Tuple3<RString, ROption<RString>, RStr>> {
93+
options.inner()
94+
.entries()
95+
.into_iter()
96+
.map(|entry| (entry.key.into(), entry.value.map(Into::into).into(), entry.description.into()).into())
97+
.collect()
98+
}
99+
100+
unsafe extern "C" fn release_fn_wrapper(options: &mut FFI_ExtensionOptions) {
101+
let private_data =
102+
Box::from_raw(options.private_data as *mut ExtensionOptionsPrivateData);
103+
drop(private_data);
104+
}
105+
106+
impl From<Box<dyn ExtensionOptions>> for FFI_ExtensionOptions {
107+
fn from(options: Box<dyn ExtensionOptions>) -> Self {
108+
let private_data = ExtensionOptionsPrivateData { options };
109+
110+
Self {
111+
cloned: cloned_fn_wrapper,
112+
set: set_fn_wrapper,
113+
entries: entries_fn_wrapper,
114+
release: release_fn_wrapper,
115+
private_data: Box::into_raw(Box::new(private_data)) as *mut c_void,
116+
}
117+
}
118+
}
119+
120+
impl Drop for FFI_ExtensionOptions {
121+
fn drop(&mut self) {
122+
unsafe { (self.release)(self) }
123+
}
124+
}
125+
126+
/// This struct is used to access an UDF provided by a foreign
127+
/// library across a FFI boundary.
128+
///
129+
/// The ForeignExtensionOptions is to be used by the caller of the UDF, so it has
130+
/// no knowledge or access to the private data. All interaction with the UDF
131+
/// must occur through the functions defined in FFI_ExtensionOptions.
132+
#[derive(Debug)]
133+
pub struct ForeignExtensionOptions(FFI_ExtensionOptions);
134+
135+
unsafe impl Send for ForeignExtensionOptions {}
136+
unsafe impl Sync for ForeignExtensionOptions {}
137+
138+
impl From<FFI_ExtensionOptions> for ForeignExtensionOptions {
139+
fn from(options: FFI_ExtensionOptions) -> Self {
140+
Self(options)
141+
}
142+
}
143+
144+
impl ExtensionOptions for ForeignExtensionOptions {
145+
fn as_any(&self) -> &dyn Any {
146+
self
147+
}
148+
149+
fn as_any_mut(&mut self) -> &mut dyn Any {
150+
self
151+
}
152+
153+
fn cloned(&self) -> Box<dyn ExtensionOptions> {
154+
(self.0.cloned)(&self.0).into()
155+
}
156+
157+
fn set(&mut self, key: &str, value: &str) -> Result<()> {
158+
df_result!({
159+
(self.0.set)(&mut self.0, key.into(), value.into())
160+
})
161+
}
162+
163+
fn entries(&self) -> Vec<ConfigEntry> {
164+
(self.0.entries)(&self.0).into_iter()
165+
.map(|entry_tuple| ConfigEntry {
166+
key: entry_tuple.0.into(),
167+
value: entry_tuple.1.into(),
168+
description: entry_tuple.2.into(),
169+
})
170+
.collect()
171+
}
172+
}
173+
174+
#[cfg(test)]
175+
mod tests {
176+
use datafusion_common::{
177+
config::ConfigExtension, config::ConfigOptions, extensions_options,
178+
};
179+
// Define a new configuration struct using the `extensions_options` macro
180+
extensions_options! {
181+
/// My own config options.
182+
pub struct MyConfig {
183+
/// Should "foo" be replaced by "bar"?
184+
pub foo_to_bar: bool, default = true
185+
186+
/// How many "baz" should be created?
187+
pub baz_count: usize, default = 1337
188+
}
189+
}
190+
191+
impl ConfigExtension for MyConfig {
192+
const PREFIX: &'static str = "my_config";
193+
}
194+
195+
#[test]
196+
fn round_trip_ffi_extension_options() {
197+
198+
// set up config struct and register extension
199+
let mut config = ConfigOptions::default();
200+
config.extensions.insert(MyConfig::default());
201+
202+
// overwrite config default
203+
config.set("my_config.baz_count", "42").unwrap();
204+
205+
// check config state
206+
let my_config = config.extensions.get::<MyConfig>().unwrap();
207+
assert!(my_config.foo_to_bar,);
208+
assert_eq!(my_config.baz_count, 42,);
209+
210+
}
211+
}

datafusion/ffi/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
pub mod arrow_wrappers;
2929
pub mod catalog_provider;
3030
pub mod catalog_provider_list;
31+
pub mod config_options;
3132
pub mod execution;
3233
pub mod execution_plan;
3334
pub mod expr;

0 commit comments

Comments
 (0)