@@ -28,8 +28,8 @@ use datafusion::{
2828 catalog:: { Session , TableProvider } ,
2929 datasource:: TableType ,
3030 error:: DataFusionError ,
31- execution:: session_state:: SessionStateBuilder ,
32- logical_expr:: TableProviderFilterPushDown ,
31+ execution:: { session_state:: SessionStateBuilder , TaskContext } ,
32+ logical_expr:: { logical_plan :: dml :: InsertOp , TableProviderFilterPushDown } ,
3333 physical_plan:: ExecutionPlan ,
3434 prelude:: { Expr , SessionContext } ,
3535} ;
@@ -40,7 +40,7 @@ use datafusion_proto::{
4040 protobuf:: LogicalExprList ,
4141} ;
4242use prost:: Message ;
43- use tokio:: runtime:: Runtime ;
43+ use tokio:: runtime:: Handle ;
4444
4545use crate :: {
4646 arrow_wrappers:: WrappedSchema ,
@@ -50,6 +50,7 @@ use crate::{
5050
5151use super :: {
5252 execution_plan:: { FFI_ExecutionPlan , ForeignExecutionPlan } ,
53+ insert_op:: FFI_InsertOp ,
5354 session_config:: FFI_SessionConfig ,
5455} ;
5556use datafusion:: error:: Result ;
@@ -133,6 +134,14 @@ pub struct FFI_TableProvider {
133134 -> RResult < RVec < FFI_TableProviderFilterPushDown > , RString > ,
134135 > ,
135136
137+ pub insert_into :
138+ unsafe extern "C" fn (
139+ provider : & Self ,
140+ session_config : & FFI_SessionConfig ,
141+ input : & FFI_ExecutionPlan ,
142+ insert_op : FFI_InsertOp ,
143+ ) -> FfiFuture < RResult < FFI_ExecutionPlan , RString > > ,
144+
136145 /// Used to create a clone on the provider of the execution plan. This should
137146 /// only need to be called by the receiver of the plan.
138147 pub clone : unsafe extern "C" fn ( plan : & Self ) -> Self ,
@@ -153,7 +162,7 @@ unsafe impl Sync for FFI_TableProvider {}
153162
154163struct ProviderPrivateData {
155164 provider : Arc < dyn TableProvider + Send > ,
156- runtime : Option < Arc < Runtime > > ,
165+ runtime : Option < Handle > ,
157166}
158167
159168unsafe extern "C" fn schema_fn_wrapper ( provider : & FFI_TableProvider ) -> WrappedSchema {
@@ -276,6 +285,53 @@ unsafe extern "C" fn scan_fn_wrapper(
276285 . into_ffi ( )
277286}
278287
288+ unsafe extern "C" fn insert_into_fn_wrapper (
289+ provider : & FFI_TableProvider ,
290+ session_config : & FFI_SessionConfig ,
291+ input : & FFI_ExecutionPlan ,
292+ insert_op : FFI_InsertOp ,
293+ ) -> FfiFuture < RResult < FFI_ExecutionPlan , RString > > {
294+ let private_data = provider. private_data as * mut ProviderPrivateData ;
295+ let internal_provider = & ( * private_data) . provider ;
296+ let session_config = session_config. clone ( ) ;
297+ let input = input. clone ( ) ;
298+ let runtime = & ( * private_data) . runtime ;
299+
300+ async move {
301+ let config = match ForeignSessionConfig :: try_from ( & session_config) {
302+ Ok ( c) => c,
303+ Err ( e) => return RResult :: RErr ( e. to_string ( ) . into ( ) ) ,
304+ } ;
305+ let session = SessionStateBuilder :: new ( )
306+ . with_default_features ( )
307+ . with_config ( config. 0 )
308+ . build ( ) ;
309+ let ctx = SessionContext :: new_with_state ( session) ;
310+
311+ let input = match ForeignExecutionPlan :: try_from ( & input) {
312+ Ok ( input) => Arc :: new ( input) ,
313+ Err ( e) => return RResult :: RErr ( e. to_string ( ) . into ( ) ) ,
314+ } ;
315+
316+ let insert_op = InsertOp :: from ( insert_op) ;
317+
318+ let plan = match internal_provider
319+ . insert_into ( & ctx. state ( ) , input, insert_op)
320+ . await
321+ {
322+ Ok ( p) => p,
323+ Err ( e) => return RResult :: RErr ( e. to_string ( ) . into ( ) ) ,
324+ } ;
325+
326+ RResult :: ROk ( FFI_ExecutionPlan :: new (
327+ plan,
328+ ctx. task_ctx ( ) ,
329+ runtime. clone ( ) ,
330+ ) )
331+ }
332+ . into_ffi ( )
333+ }
334+
279335unsafe extern "C" fn release_fn_wrapper ( provider : & mut FFI_TableProvider ) {
280336 let private_data = Box :: from_raw ( provider. private_data as * mut ProviderPrivateData ) ;
281337 drop ( private_data) ;
@@ -295,6 +351,7 @@ unsafe extern "C" fn clone_fn_wrapper(provider: &FFI_TableProvider) -> FFI_Table
295351 scan : scan_fn_wrapper,
296352 table_type : table_type_fn_wrapper,
297353 supports_filters_pushdown : provider. supports_filters_pushdown ,
354+ insert_into : provider. insert_into ,
298355 clone : clone_fn_wrapper,
299356 release : release_fn_wrapper,
300357 version : super :: version,
@@ -313,7 +370,7 @@ impl FFI_TableProvider {
313370 pub fn new (
314371 provider : Arc < dyn TableProvider + Send > ,
315372 can_support_pushdown_filters : bool ,
316- runtime : Option < Arc < Runtime > > ,
373+ runtime : Option < Handle > ,
317374 ) -> Self {
318375 let private_data = Box :: new ( ProviderPrivateData { provider, runtime } ) ;
319376
@@ -325,6 +382,7 @@ impl FFI_TableProvider {
325382 true => Some ( supports_filters_pushdown_fn_wrapper) ,
326383 false => None ,
327384 } ,
385+ insert_into : insert_into_fn_wrapper,
328386 clone : clone_fn_wrapper,
329387 release : release_fn_wrapper,
330388 version : super :: version,
@@ -443,6 +501,37 @@ impl TableProvider for ForeignTableProvider {
443501 }
444502 }
445503 }
504+
505+ async fn insert_into (
506+ & self ,
507+ session : & dyn Session ,
508+ input : Arc < dyn ExecutionPlan > ,
509+ insert_op : InsertOp ,
510+ ) -> Result < Arc < dyn ExecutionPlan > > {
511+ let session_config: FFI_SessionConfig = session. config ( ) . into ( ) ;
512+
513+ let rc = Handle :: try_current ( ) . ok ( ) ;
514+ let input =
515+ FFI_ExecutionPlan :: new ( input, Arc :: new ( TaskContext :: from ( session) ) , rc) ;
516+ let insert_op: FFI_InsertOp = insert_op. into ( ) ;
517+
518+ let plan = unsafe {
519+ let maybe_plan =
520+ ( self . 0 . insert_into ) ( & self . 0 , & session_config, & input, insert_op) . await ;
521+
522+ match maybe_plan {
523+ RResult :: ROk ( p) => ForeignExecutionPlan :: try_from ( & p) ?,
524+ RResult :: RErr ( e) => {
525+ return Err ( DataFusionError :: Internal ( format ! (
526+ "Unable to perform insert_into via FFI: {}" ,
527+ e
528+ ) ) )
529+ }
530+ }
531+ } ;
532+
533+ Ok ( Arc :: new ( plan) )
534+ }
446535}
447536
448537#[ cfg( test) ]
@@ -453,7 +542,7 @@ mod tests {
453542 use super :: * ;
454543
455544 #[ tokio:: test]
456- async fn test_round_trip_ffi_table_provider ( ) -> Result < ( ) > {
545+ async fn test_round_trip_ffi_table_provider_scan ( ) -> Result < ( ) > {
457546 use arrow:: datatypes:: Field ;
458547 use datafusion:: arrow:: {
459548 array:: Float32Array , datatypes:: DataType , record_batch:: RecordBatch ,
@@ -493,4 +582,54 @@ mod tests {
493582
494583 Ok ( ( ) )
495584 }
585+
586+ #[ tokio:: test]
587+ async fn test_round_trip_ffi_table_provider_insert_into ( ) -> Result < ( ) > {
588+ use arrow:: datatypes:: Field ;
589+ use datafusion:: arrow:: {
590+ array:: Float32Array , datatypes:: DataType , record_batch:: RecordBatch ,
591+ } ;
592+ use datafusion:: datasource:: MemTable ;
593+
594+ let schema =
595+ Arc :: new ( Schema :: new ( vec ! [ Field :: new( "a" , DataType :: Float32 , false ) ] ) ) ;
596+
597+ // define data in two partitions
598+ let batch1 = RecordBatch :: try_new (
599+ Arc :: clone ( & schema) ,
600+ vec ! [ Arc :: new( Float32Array :: from( vec![ 2.0 , 4.0 , 8.0 ] ) ) ] ,
601+ ) ?;
602+ let batch2 = RecordBatch :: try_new (
603+ Arc :: clone ( & schema) ,
604+ vec ! [ Arc :: new( Float32Array :: from( vec![ 64.0 ] ) ) ] ,
605+ ) ?;
606+
607+ let ctx = SessionContext :: new ( ) ;
608+
609+ let provider =
610+ Arc :: new ( MemTable :: try_new ( schema, vec ! [ vec![ batch1] , vec![ batch2] ] ) ?) ;
611+
612+ let ffi_provider = FFI_TableProvider :: new ( provider, true , None ) ;
613+
614+ let foreign_table_provider: ForeignTableProvider = ( & ffi_provider) . into ( ) ;
615+
616+ ctx. register_table ( "t" , Arc :: new ( foreign_table_provider) ) ?;
617+
618+ let result = ctx
619+ . sql ( "INSERT INTO t VALUES (128.0);" )
620+ . await ?
621+ . collect ( )
622+ . await ?;
623+
624+ assert ! ( result. len( ) == 1 && result[ 0 ] . num_rows( ) == 1 ) ;
625+
626+ ctx. table ( "t" )
627+ . await ?
628+ . select ( vec ! [ col( "a" ) ] ) ?
629+ . filter ( col ( "a" ) . gt ( lit ( 3.0 ) ) ) ?
630+ . show ( )
631+ . await ?;
632+
633+ Ok ( ( ) )
634+ }
496635}
0 commit comments