@@ -28,8 +28,8 @@ use datafusion::{
28
28
catalog:: { Session , TableProvider } ,
29
29
datasource:: TableType ,
30
30
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 } ,
33
33
physical_plan:: ExecutionPlan ,
34
34
prelude:: { Expr , SessionContext } ,
35
35
} ;
@@ -40,7 +40,7 @@ use datafusion_proto::{
40
40
protobuf:: LogicalExprList ,
41
41
} ;
42
42
use prost:: Message ;
43
- use tokio:: runtime:: Runtime ;
43
+ use tokio:: runtime:: Handle ;
44
44
45
45
use crate :: {
46
46
arrow_wrappers:: WrappedSchema ,
@@ -50,6 +50,7 @@ use crate::{
50
50
51
51
use super :: {
52
52
execution_plan:: { FFI_ExecutionPlan , ForeignExecutionPlan } ,
53
+ insert_op:: FFI_InsertOp ,
53
54
session_config:: FFI_SessionConfig ,
54
55
} ;
55
56
use datafusion:: error:: Result ;
@@ -133,6 +134,14 @@ pub struct FFI_TableProvider {
133
134
-> RResult < RVec < FFI_TableProviderFilterPushDown > , RString > ,
134
135
> ,
135
136
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
+
136
145
/// Used to create a clone on the provider of the execution plan. This should
137
146
/// only need to be called by the receiver of the plan.
138
147
pub clone : unsafe extern "C" fn ( plan : & Self ) -> Self ,
@@ -153,7 +162,7 @@ unsafe impl Sync for FFI_TableProvider {}
153
162
154
163
struct ProviderPrivateData {
155
164
provider : Arc < dyn TableProvider + Send > ,
156
- runtime : Option < Arc < Runtime > > ,
165
+ runtime : Option < Handle > ,
157
166
}
158
167
159
168
unsafe extern "C" fn schema_fn_wrapper ( provider : & FFI_TableProvider ) -> WrappedSchema {
@@ -276,6 +285,53 @@ unsafe extern "C" fn scan_fn_wrapper(
276
285
. into_ffi ( )
277
286
}
278
287
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
+
279
335
unsafe extern "C" fn release_fn_wrapper ( provider : & mut FFI_TableProvider ) {
280
336
let private_data = Box :: from_raw ( provider. private_data as * mut ProviderPrivateData ) ;
281
337
drop ( private_data) ;
@@ -295,6 +351,7 @@ unsafe extern "C" fn clone_fn_wrapper(provider: &FFI_TableProvider) -> FFI_Table
295
351
scan : scan_fn_wrapper,
296
352
table_type : table_type_fn_wrapper,
297
353
supports_filters_pushdown : provider. supports_filters_pushdown ,
354
+ insert_into : provider. insert_into ,
298
355
clone : clone_fn_wrapper,
299
356
release : release_fn_wrapper,
300
357
version : super :: version,
@@ -313,7 +370,7 @@ impl FFI_TableProvider {
313
370
pub fn new (
314
371
provider : Arc < dyn TableProvider + Send > ,
315
372
can_support_pushdown_filters : bool ,
316
- runtime : Option < Arc < Runtime > > ,
373
+ runtime : Option < Handle > ,
317
374
) -> Self {
318
375
let private_data = Box :: new ( ProviderPrivateData { provider, runtime } ) ;
319
376
@@ -325,6 +382,7 @@ impl FFI_TableProvider {
325
382
true => Some ( supports_filters_pushdown_fn_wrapper) ,
326
383
false => None ,
327
384
} ,
385
+ insert_into : insert_into_fn_wrapper,
328
386
clone : clone_fn_wrapper,
329
387
release : release_fn_wrapper,
330
388
version : super :: version,
@@ -443,6 +501,37 @@ impl TableProvider for ForeignTableProvider {
443
501
}
444
502
}
445
503
}
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
+ }
446
535
}
447
536
448
537
#[ cfg( test) ]
@@ -453,7 +542,7 @@ mod tests {
453
542
use super :: * ;
454
543
455
544
#[ tokio:: test]
456
- async fn test_round_trip_ffi_table_provider ( ) -> Result < ( ) > {
545
+ async fn test_round_trip_ffi_table_provider_scan ( ) -> Result < ( ) > {
457
546
use arrow:: datatypes:: Field ;
458
547
use datafusion:: arrow:: {
459
548
array:: Float32Array , datatypes:: DataType , record_batch:: RecordBatch ,
@@ -493,4 +582,54 @@ mod tests {
493
582
494
583
Ok ( ( ) )
495
584
}
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
+ }
496
635
}
0 commit comments