@@ -204,13 +204,67 @@ impl Clone for FFI_ExecutionPlan {
204204 }
205205}
206206
207+ /// Helper function to recursively identify any children that do not
208+ /// have a runtime set but should because they are local to this same
209+ /// library. This does imply a restriction that all execution plans
210+ /// in this chain that are within the same library use the same runtime.
211+ fn pass_runtime_to_children (
212+ plan : & Arc < dyn ExecutionPlan > ,
213+ runtime : & Handle ,
214+ ) -> Result < Option < Arc < dyn ExecutionPlan > > > {
215+ println ! ( "checking plan {:?}" , plan. name( ) ) ;
216+ let mut updated_children = false ;
217+ let plan_is_foreign = plan. as_any ( ) . is :: < ForeignExecutionPlan > ( ) ;
218+
219+ let children = plan
220+ . children ( )
221+ . into_iter ( )
222+ . map ( |child| {
223+ let child = match pass_runtime_to_children ( child, runtime) ? {
224+ Some ( child) => {
225+ updated_children = true ;
226+ child
227+ }
228+ None => Arc :: clone ( child) ,
229+ } ;
230+
231+ // If the parent is foreign and the child is local to this library, then when
232+ // we called `children()` above we will get something other than a
233+ // `ForeignExecutionPlan`. In this case wrap the plan in a `ForeignExecutionPlan`
234+ // because when we call `with_new_children` below it will extract the
235+ // FFI plan that does contain the runtime.
236+ if plan_is_foreign && !child. as_any ( ) . is :: < ForeignExecutionPlan > ( ) {
237+ updated_children = true ;
238+ let ffi_child = FFI_ExecutionPlan :: new ( child, Some ( runtime. clone ( ) ) ) ;
239+ let foreign_child = ForeignExecutionPlan :: try_from ( ffi_child) ;
240+ foreign_child. map ( |c| Arc :: new ( c) as Arc < dyn ExecutionPlan > )
241+ } else {
242+ Ok ( child)
243+ }
244+ } )
245+ . collect :: < Result < Vec < _ > > > ( ) ?;
246+ if updated_children {
247+ Arc :: clone ( plan) . with_new_children ( children) . map ( Some )
248+ } else {
249+ Ok ( None )
250+ }
251+ }
252+
207253impl FFI_ExecutionPlan {
208254 /// This function is called on the provider's side.
209- pub fn new ( plan : Arc < dyn ExecutionPlan > , runtime : Option < Handle > ) -> Self {
255+ pub fn new ( mut plan : Arc < dyn ExecutionPlan > , runtime : Option < Handle > ) -> Self {
256+ // Note to developers: `pass_runtime_to_children` relies on the logic here to
257+ // get the underlying FFI plan during calls to `new_with_children`.
210258 if let Some ( plan) = plan. as_any ( ) . downcast_ref :: < ForeignExecutionPlan > ( ) {
211259 return plan. plan . clone ( ) ;
212260 }
213261
262+ if let Some ( rt) = & runtime
263+ && let Ok ( Some ( p) ) = pass_runtime_to_children ( & plan, rt)
264+ {
265+ plan = p;
266+ }
267+
214268 let private_data = Box :: new ( ExecutionPlanPrivateData { plan, runtime } ) ;
215269 Self {
216270 properties : properties_fn_wrapper,
@@ -278,28 +332,34 @@ impl TryFrom<&FFI_ExecutionPlan> for Arc<dyn ExecutionPlan> {
278332
279333 fn try_from ( plan : & FFI_ExecutionPlan ) -> Result < Self , Self :: Error > {
280334 if ( plan. library_marker_id ) ( ) == crate :: get_library_marker_id ( ) {
281- return Ok ( Arc :: clone ( plan. inner ( ) ) ) ;
335+ Ok ( Arc :: clone ( plan. inner ( ) ) )
336+ } else {
337+ let plan = ForeignExecutionPlan :: try_from ( plan. clone ( ) ) ?;
338+ Ok ( Arc :: new ( plan) )
282339 }
340+ }
341+ }
283342
343+ impl TryFrom < FFI_ExecutionPlan > for ForeignExecutionPlan {
344+ type Error = DataFusionError ;
345+ fn try_from ( plan : FFI_ExecutionPlan ) -> Result < Self , Self :: Error > {
284346 unsafe {
285- let name = ( plan. name ) ( plan) . into ( ) ;
347+ let name = ( plan. name ) ( & plan) . into ( ) ;
286348
287- let properties: PlanProperties = ( plan. properties ) ( plan) . try_into ( ) ?;
349+ let properties: PlanProperties = ( plan. properties ) ( & plan) . try_into ( ) ?;
288350
289- let children_rvec = ( plan. children ) ( plan) ;
351+ let children_rvec = ( plan. children ) ( & plan) ;
290352 let children = children_rvec
291353 . iter ( )
292354 . map ( <Arc < dyn ExecutionPlan > >:: try_from)
293355 . collect :: < Result < Vec < _ > > > ( ) ?;
294356
295- let plan = ForeignExecutionPlan {
357+ Ok ( ForeignExecutionPlan {
296358 name,
297- plan : plan . clone ( ) ,
359+ plan,
298360 properties,
299361 children,
300- } ;
301-
302- Ok ( Arc :: new ( plan) )
362+ } )
303363 }
304364 }
305365}
@@ -366,11 +426,10 @@ impl ExecutionPlan for ForeignExecutionPlan {
366426 }
367427}
368428
369- #[ cfg( test) ]
370- pub ( crate ) mod tests {
371- use arrow:: datatypes:: { DataType , Field , Schema } ;
372- use datafusion:: physical_plan:: Partitioning ;
373- use datafusion:: physical_plan:: execution_plan:: { Boundedness , EmissionType } ;
429+ #[ cfg( any( test, feature = "integration-tests" ) ) ]
430+ pub mod tests {
431+ use datafusion_physical_plan:: Partitioning ;
432+ use datafusion_physical_plan:: execution_plan:: { Boundedness , EmissionType } ;
374433
375434 use super :: * ;
376435
@@ -384,7 +443,7 @@ pub(crate) mod tests {
384443 pub fn new ( schema : arrow:: datatypes:: SchemaRef ) -> Self {
385444 Self {
386445 props : PlanProperties :: new (
387- datafusion :: physical_expr :: EquivalenceProperties :: new ( schema) ,
446+ datafusion_physical_expr :: EquivalenceProperties :: new ( schema) ,
388447 Partitioning :: UnknownPartitioning ( 3 ) ,
389448 EmissionType :: Incremental ,
390449 Boundedness :: Bounded ,
@@ -442,8 +501,9 @@ pub(crate) mod tests {
442501
443502 #[ test]
444503 fn test_round_trip_ffi_execution_plan ( ) -> Result < ( ) > {
445- let schema =
446- Arc :: new ( Schema :: new ( vec ! [ Field :: new( "a" , DataType :: Float32 , false ) ] ) ) ;
504+ let schema = Arc :: new ( arrow:: datatypes:: Schema :: new ( vec ! [
505+ arrow:: datatypes:: Field :: new( "a" , arrow:: datatypes:: DataType :: Float32 , false ) ,
506+ ] ) ) ;
447507
448508 let original_plan = Arc :: new ( EmptyExec :: new ( schema) ) ;
449509 let original_name = original_plan. name ( ) . to_string ( ) ;
@@ -455,7 +515,7 @@ pub(crate) mod tests {
455515
456516 assert_eq ! ( original_name, foreign_plan. name( ) ) ;
457517
458- let display = datafusion :: physical_plan :: display:: DisplayableExecutionPlan :: new (
518+ let display = datafusion_physical_plan :: display:: DisplayableExecutionPlan :: new (
459519 foreign_plan. as_ref ( ) ,
460520 ) ;
461521
@@ -470,8 +530,9 @@ pub(crate) mod tests {
470530
471531 #[ test]
472532 fn test_ffi_execution_plan_children ( ) -> Result < ( ) > {
473- let schema =
474- Arc :: new ( Schema :: new ( vec ! [ Field :: new( "a" , DataType :: Float32 , false ) ] ) ) ;
533+ let schema = Arc :: new ( arrow:: datatypes:: Schema :: new ( vec ! [
534+ arrow:: datatypes:: Field :: new( "a" , arrow:: datatypes:: DataType :: Float32 , false ) ,
535+ ] ) ) ;
475536
476537 // Version 1: Adding child to the foreign plan
477538 let child_plan = Arc :: new ( EmptyExec :: new ( Arc :: clone ( & schema) ) ) ;
@@ -509,8 +570,9 @@ pub(crate) mod tests {
509570
510571 #[ test]
511572 fn test_ffi_execution_plan_local_bypass ( ) {
512- let schema =
513- Arc :: new ( Schema :: new ( vec ! [ Field :: new( "a" , DataType :: Float32 , false ) ] ) ) ;
573+ let schema = Arc :: new ( arrow:: datatypes:: Schema :: new ( vec ! [
574+ arrow:: datatypes:: Field :: new( "a" , arrow:: datatypes:: DataType :: Float32 , false ) ,
575+ ] ) ) ;
514576
515577 let plan = Arc :: new ( EmptyExec :: new ( schema) ) ;
516578
0 commit comments