1use std::ffi::c_void;
57use std::sync::Arc;
58
59use async_ffi::{FfiFuture, FutureExt};
60use async_trait::async_trait;
61use datafusion_common::error::{DataFusionError, Result};
62use datafusion_expr::LogicalPlan;
63use datafusion_physical_plan::ExecutionPlan;
64use datafusion_proto::bytes::{
65 logical_plan_from_bytes_with_extension_codec,
66 logical_plan_to_bytes_with_extension_codec,
67 physical_plan_from_bytes_with_extension_codec,
68 physical_plan_to_bytes_with_extension_codec,
69};
70use datafusion_proto::logical_plan::LogicalExtensionCodec;
71use datafusion_proto::physical_plan::PhysicalExtensionCodec;
72use datafusion_session::{QueryPlanner, Session};
73use stabby::vec::Vec as SVec;
74use tokio::runtime::Handle;
75
76use crate::execution::FFI_TaskContextProvider;
77use crate::proto::logical_extension_codec::FFI_LogicalExtensionCodec;
78use crate::proto::physical_extension_codec::FFI_PhysicalExtensionCodec;
79use crate::session::{FFI_SessionRef, ForeignSession};
80use crate::util::FFI_Result;
81use crate::{df_result, sresult_return};
82
83#[repr(C)]
89#[derive(Debug)]
90pub struct FFI_QueryPlanner {
91 create_physical_plan: unsafe extern "C" fn(
92 &Self,
93 logical_plan_serialized: SVec<u8>,
94 session: FFI_SessionRef,
95 ) -> FfiFuture<FFI_Result<SVec<u8>>>,
96
97 logical_codec: FFI_LogicalExtensionCodec,
99
100 physical_codec: FFI_PhysicalExtensionCodec,
102
103 clone: unsafe extern "C" fn(planner: &Self) -> Self,
105
106 release: unsafe extern "C" fn(arg: &mut Self),
108
109 pub version: unsafe extern "C" fn() -> u64,
111
112 private_data: *mut c_void,
115
116 pub library_marker_id: extern "C" fn() -> usize,
119}
120
121unsafe impl Send for FFI_QueryPlanner {}
122unsafe impl Sync for FFI_QueryPlanner {}
123
124struct QueryPlannerPrivateData {
125 planner: Arc<dyn QueryPlanner + Send + Sync>,
126}
127
128impl FFI_QueryPlanner {
129 fn inner(&self) -> &Arc<dyn QueryPlanner + Send + Sync> {
130 let private_data = self.private_data as *const QueryPlannerPrivateData;
131 unsafe { &(*private_data).planner }
132 }
133}
134
135unsafe extern "C" fn create_physical_plan_fn_wrapper(
136 planner: &FFI_QueryPlanner,
137 logical_plan_serialized: SVec<u8>,
138 session: FFI_SessionRef,
139) -> FfiFuture<FFI_Result<SVec<u8>>> {
140 let internal_planner = Arc::clone(planner.inner());
141 let logical_codec: Arc<dyn LogicalExtensionCodec> = (&planner.logical_codec).into();
142 let physical_codec: Arc<dyn PhysicalExtensionCodec> =
143 (&planner.physical_codec).into();
144
145 async move {
146 let mut foreign_session = None;
147 let session = sresult_return!(
148 session
149 .as_local()
150 .map(Ok::<&dyn Session, DataFusionError>)
151 .unwrap_or_else(|| {
152 foreign_session = Some(ForeignSession::try_from(&session)?);
153 Ok(foreign_session.as_ref().unwrap())
154 })
155 );
156
157 let logical_plan = sresult_return!(logical_plan_from_bytes_with_extension_codec(
158 logical_plan_serialized.as_slice(),
159 session.task_ctx().as_ref(),
160 logical_codec.as_ref(),
161 ));
162
163 let physical_plan = sresult_return!(
164 internal_planner
165 .create_physical_plan(&logical_plan, session)
166 .await
167 );
168 let physical_plan = sresult_return!(physical_plan_to_bytes_with_extension_codec(
169 physical_plan,
170 physical_codec.as_ref(),
171 ));
172
173 FFI_Result::Ok(SVec::from(physical_plan.as_ref()))
174 }
175 .into_ffi()
176}
177
178unsafe extern "C" fn release_fn_wrapper(planner: &mut FFI_QueryPlanner) {
179 unsafe {
180 debug_assert!(!planner.private_data.is_null());
181 let private_data =
182 Box::from_raw(planner.private_data as *mut QueryPlannerPrivateData);
183 drop(private_data);
184 planner.private_data = std::ptr::null_mut();
185 }
186}
187
188unsafe extern "C" fn clone_fn_wrapper(planner: &FFI_QueryPlanner) -> FFI_QueryPlanner {
189 let old_planner = Arc::clone(planner.inner());
190
191 let private_data = Box::into_raw(Box::new(QueryPlannerPrivateData {
192 planner: old_planner,
193 })) as *mut c_void;
194
195 FFI_QueryPlanner {
196 create_physical_plan: create_physical_plan_fn_wrapper,
197 logical_codec: planner.logical_codec.clone(),
198 physical_codec: planner.physical_codec.clone(),
199 clone: clone_fn_wrapper,
200 release: release_fn_wrapper,
201 version: super::version,
202 private_data,
203 library_marker_id: crate::get_library_marker_id,
204 }
205}
206
207impl Drop for FFI_QueryPlanner {
208 fn drop(&mut self) {
209 unsafe { (self.release)(self) }
210 }
211}
212
213impl Clone for FFI_QueryPlanner {
214 fn clone(&self) -> Self {
215 unsafe { (self.clone)(self) }
216 }
217}
218
219impl FFI_QueryPlanner {
220 pub fn new(
230 planner: Arc<dyn QueryPlanner + Send + Sync>,
231 runtime: Option<Handle>,
232 task_ctx_provider: impl Into<FFI_TaskContextProvider>,
233 logical_codec: Arc<dyn LogicalExtensionCodec>,
234 physical_codec: Arc<dyn PhysicalExtensionCodec>,
235 ) -> Self {
236 let task_ctx_provider = task_ctx_provider.into();
237 let logical_codec = FFI_LogicalExtensionCodec::new(
238 logical_codec,
239 runtime.clone(),
240 task_ctx_provider.clone(),
241 );
242 let physical_codec =
243 FFI_PhysicalExtensionCodec::new(physical_codec, runtime, task_ctx_provider);
244 Self::new_with_ffi_codecs(planner, logical_codec, physical_codec)
245 }
246
247 pub fn new_with_ffi_codecs(
253 planner: Arc<dyn QueryPlanner + Send + Sync>,
254 logical_codec: FFI_LogicalExtensionCodec,
255 physical_codec: FFI_PhysicalExtensionCodec,
256 ) -> Self {
257 let any_ref: &dyn std::any::Any = planner.as_ref();
258 if let Some(planner) = any_ref.downcast_ref::<ForeignQueryPlanner>() {
259 let mut planner = planner.0.clone();
260 planner.logical_codec = logical_codec;
261 planner.physical_codec = physical_codec;
262 return planner;
263 }
264
265 let private_data = Box::new(QueryPlannerPrivateData { planner });
266
267 Self {
268 create_physical_plan: create_physical_plan_fn_wrapper,
269 logical_codec,
270 physical_codec,
271 clone: clone_fn_wrapper,
272 release: release_fn_wrapper,
273 version: super::version,
274 private_data: Box::into_raw(private_data) as *mut c_void,
275 library_marker_id: crate::get_library_marker_id,
276 }
277 }
278
279 pub async fn create_physical_plan_with_session_runtime(
291 &self,
292 logical_plan: &LogicalPlan,
293 session: &dyn Session,
294 session_runtime: Option<Handle>,
295 ) -> Result<Arc<dyn ExecutionPlan>> {
296 let codec: Arc<dyn LogicalExtensionCodec> = (&self.logical_codec).into();
297 let logical_plan =
298 logical_plan_to_bytes_with_extension_codec(logical_plan, codec.as_ref())?;
299 let logical_plan = SVec::from(logical_plan.as_ref());
300 let task_ctx = session.task_ctx();
301 let session = FFI_SessionRef::new_with_ffi_codecs(
302 session,
303 session_runtime,
304 self.logical_codec.clone(),
305 self.physical_codec.clone(),
306 );
307
308 let physical_plan = unsafe {
309 df_result!((self.create_physical_plan)(self, logical_plan, session).await)?
310 };
311 let physical_codec: Arc<dyn PhysicalExtensionCodec> =
312 (&self.physical_codec).into();
313
314 physical_plan_from_bytes_with_extension_codec(
315 physical_plan.as_slice(),
316 task_ctx.as_ref(),
317 physical_codec.as_ref(),
318 )
319 }
320}
321
322#[derive(Debug)]
327pub struct ForeignQueryPlanner(pub FFI_QueryPlanner);
328
329unsafe impl Send for ForeignQueryPlanner {}
330unsafe impl Sync for ForeignQueryPlanner {}
331
332impl From<&FFI_QueryPlanner> for Arc<dyn QueryPlanner + Send + Sync> {
333 fn from(planner: &FFI_QueryPlanner) -> Self {
334 if (planner.library_marker_id)() == crate::get_library_marker_id() {
335 Arc::clone(planner.inner())
336 } else {
337 Arc::new(ForeignQueryPlanner(planner.clone()))
338 }
339 }
340}
341
342#[async_trait]
343impl QueryPlanner for ForeignQueryPlanner {
344 async fn create_physical_plan(
345 &self,
346 logical_plan: &LogicalPlan,
347 session: &dyn Session,
348 ) -> Result<Arc<dyn ExecutionPlan>> {
349 self.0
350 .create_physical_plan_with_session_runtime(logical_plan, session, None)
351 .await
352 }
353}
354
355#[cfg(test)]
356mod tests {
357 use std::sync::Arc;
358
359 use arrow::datatypes::{DataType, Field, Schema};
360 use datafusion::prelude::SessionContext;
361 use datafusion_common::Result;
362 use datafusion_execution::TaskContextProvider;
363 use datafusion_expr::LogicalPlanBuilder;
364 use datafusion_physical_plan::empty::EmptyExec;
365 use datafusion_proto::logical_plan::DefaultLogicalExtensionCodec;
366 use datafusion_proto::physical_plan::DefaultPhysicalExtensionCodec;
367
368 use super::*;
369
370 #[derive(Debug)]
371 struct EmptyQueryPlanner;
372
373 #[async_trait]
374 impl QueryPlanner for EmptyQueryPlanner {
375 async fn create_physical_plan(
376 &self,
377 _logical_plan: &LogicalPlan,
378 _session: &dyn Session,
379 ) -> Result<Arc<dyn ExecutionPlan>> {
380 let schema =
381 Arc::new(Schema::new(vec![Field::new("a", DataType::Int32, true)]));
382 Ok(Arc::new(EmptyExec::new(schema)))
383 }
384 }
385
386 fn create_ffi_query_planner(ctx: Arc<SessionContext>) -> FFI_QueryPlanner {
387 let task_ctx_provider = Arc::clone(&ctx) as Arc<dyn TaskContextProvider>;
388 FFI_QueryPlanner::new(
389 Arc::new(EmptyQueryPlanner),
390 None,
391 &task_ctx_provider,
392 Arc::new(DefaultLogicalExtensionCodec {}),
393 Arc::new(DefaultPhysicalExtensionCodec {}),
394 )
395 }
396
397 #[test]
398 fn test_ffi_query_planner_local_bypass() {
399 let ctx = Arc::new(SessionContext::new());
400 let ffi_planner = create_ffi_query_planner(ctx);
401 let planner: Arc<dyn QueryPlanner + Send + Sync> = (&ffi_planner).into();
402 let any_ref: &dyn std::any::Any = planner.as_ref();
403 assert!(any_ref.downcast_ref::<EmptyQueryPlanner>().is_some());
404 }
405
406 #[tokio::test]
407 async fn test_round_trip_ffi_query_planner_create_physical_plan() -> Result<()> {
408 let ctx = Arc::new(SessionContext::new());
409 let mut ffi_planner = create_ffi_query_planner(Arc::clone(&ctx));
410 ffi_planner.library_marker_id = crate::mock_foreign_marker_id;
411
412 let planner: Arc<dyn QueryPlanner + Send + Sync> = (&ffi_planner).into();
413 let any_ref: &dyn std::any::Any = planner.as_ref();
414 assert!(any_ref.downcast_ref::<ForeignQueryPlanner>().is_some());
415
416 let logical_plan = LogicalPlanBuilder::empty(false).build()?;
417 let state = ctx.state();
418 let physical_plan = planner.create_physical_plan(&logical_plan, &state).await?;
419 assert_eq!(physical_plan.name(), "EmptyExec");
420 assert!(physical_plan.is::<EmptyExec>());
421
422 Ok(())
423 }
424
425 #[tokio::test]
426 async fn test_create_physical_plan_with_session_runtime() -> Result<()> {
427 let ctx = Arc::new(SessionContext::new());
428 let ffi_planner = create_ffi_query_planner(Arc::clone(&ctx));
429 let logical_plan = LogicalPlanBuilder::empty(false).build()?;
430 let state = ctx.state();
431
432 let physical_plan = ffi_planner
433 .create_physical_plan_with_session_runtime(
434 &logical_plan,
435 &state,
436 Some(Handle::current()),
437 )
438 .await?;
439
440 assert_eq!(physical_plan.name(), "EmptyExec");
441 assert!(physical_plan.is::<EmptyExec>());
442
443 Ok(())
444 }
445}