@@ -22,20 +22,18 @@ use arrow::datatypes::SchemaRef;
2222use async_ffi:: { FfiFuture , FutureExt } ;
2323use async_trait:: async_trait;
2424use datafusion_catalog:: { Session , TableProvider } ;
25+ use datafusion_common:: Statistics ;
2526use datafusion_common:: error:: { DataFusionError , Result } ;
26- use datafusion_common:: { DFSchemaRef , Statistics } ;
2727use datafusion_execution:: TaskContext ;
28- use datafusion_expr:: dml:: { InsertOp , MergeIntoClause , MergeIntoOp } ;
28+ use datafusion_expr:: dml:: InsertOp ;
2929use datafusion_expr:: { Expr , TableProviderFilterPushDown , TableType } ;
3030use datafusion_physical_plan:: ExecutionPlan ;
31- use datafusion_proto:: logical_plan:: from_proto:: { parse_exprs, parse_merge_into_op} ;
32- use datafusion_proto:: logical_plan:: to_proto:: {
33- serialize_exprs, serialize_merge_into_op,
34- } ;
31+ use datafusion_proto:: logical_plan:: from_proto:: parse_exprs;
32+ use datafusion_proto:: logical_plan:: to_proto:: serialize_exprs;
3533use datafusion_proto:: logical_plan:: {
3634 DefaultLogicalExtensionCodec , LogicalExtensionCodec ,
3735} ;
38- use datafusion_proto:: protobuf:: { DfSchema , LogicalExprList , MergeIntoOpNode } ;
36+ use datafusion_proto:: protobuf:: LogicalExprList ;
3937use prost:: Message ;
4038
4139use stabby:: vec:: Vec as SVec ;
@@ -137,17 +135,6 @@ pub struct FFI_TableProvider {
137135 insert_op : FFI_InsertOp ,
138136 ) -> FfiFuture < FFI_Result < FFI_ExecutionPlan > > ,
139137
140- /// Merge source rows into the table. The schema and MERGE operation are
141- /// prost-encoded to preserve logical qualifiers and expressions across the
142- /// FFI boundary.
143- merge_into : unsafe extern "C" fn (
144- provider : & Self ,
145- session : FFI_SessionRef ,
146- source : & FFI_ExecutionPlan ,
147- merge_schema_serialized : SVec < u8 > ,
148- merge_op_serialized : SVec < u8 > ,
149- ) -> FfiFuture < FFI_Result < FFI_ExecutionPlan > > ,
150-
151138 /// Snapshot the provider's table-level statistics. [`FFI_Option::None`]
152139 /// corresponds to [`TableProvider::statistics`] returning `None`;
153140 /// `Some(bytes)` is a prost-encoded `datafusion_proto_common::Statistics`.
@@ -349,60 +336,6 @@ unsafe extern "C" fn insert_into_fn_wrapper(
349336 . into_ffi ( )
350337}
351338
352- unsafe extern "C" fn merge_into_fn_wrapper (
353- provider : & FFI_TableProvider ,
354- session : FFI_SessionRef ,
355- source : & FFI_ExecutionPlan ,
356- merge_schema_serialized : SVec < u8 > ,
357- merge_op_serialized : SVec < u8 > ,
358- ) -> FfiFuture < FFI_Result < FFI_ExecutionPlan > > {
359- let task_ctx: Result < Arc < TaskContext > , DataFusionError > =
360- ( & provider. logical_codec . task_ctx_provider ) . try_into ( ) ;
361- let runtime = provider. runtime ( ) . clone ( ) ;
362- let logical_codec: Arc < dyn LogicalExtensionCodec > = ( & provider. logical_codec ) . into ( ) ;
363- let internal_provider = Arc :: clone ( provider. inner ( ) ) ;
364- let source = source. clone ( ) ;
365-
366- async move {
367- let mut foreign_session = None ;
368- let session = sresult_return ! (
369- session
370- . as_local( )
371- . map( Ok :: <& ( dyn Session + Send + Sync ) , DataFusionError >)
372- . unwrap_or_else( || {
373- foreign_session = Some ( ForeignSession :: try_from( & session) ?) ;
374- Ok ( foreign_session. as_ref( ) . unwrap( ) )
375- } )
376- ) ;
377-
378- let source = sresult_return ! ( <Arc <dyn ExecutionPlan >>:: try_from( & source) ) ;
379- let task_ctx = sresult_return ! ( task_ctx) ;
380-
381- let merge_schema =
382- sresult_return ! ( DfSchema :: decode( merge_schema_serialized. as_ref( ) ) ) ;
383- let merge_schema: DFSchemaRef = sresult_return ! (
384- DFSchemaRef :: try_from( merge_schema)
385- . map_err( |e| DataFusionError :: Plan ( e. to_string( ) ) )
386- ) ;
387-
388- let merge_op =
389- sresult_return ! ( MergeIntoOpNode :: decode( merge_op_serialized. as_ref( ) ) ) ;
390- let MergeIntoOp { on, clauses } = sresult_return ! (
391- parse_merge_into_op( & merge_op, task_ctx. as_ref( ) , logical_codec. as_ref( ) )
392- . map_err( |e| DataFusionError :: Plan ( e. to_string( ) ) )
393- ) ;
394-
395- let plan = sresult_return ! (
396- internal_provider
397- . merge_into( session, source, merge_schema, on, clauses)
398- . await
399- ) ;
400-
401- FFI_Result :: Ok ( FFI_ExecutionPlan :: new ( plan, runtime. clone ( ) ) )
402- }
403- . into_ffi ( )
404- }
405-
406339unsafe extern "C" fn release_fn_wrapper ( provider : & mut FFI_TableProvider ) {
407340 unsafe {
408341 debug_assert ! ( !provider. private_data. is_null( ) ) ;
@@ -428,7 +361,6 @@ unsafe extern "C" fn clone_fn_wrapper(provider: &FFI_TableProvider) -> FFI_Table
428361 table_type : table_type_fn_wrapper,
429362 supports_filters_pushdown : provider. supports_filters_pushdown ,
430363 insert_into : provider. insert_into ,
431- merge_into : provider. merge_into ,
432364 statistics : statistics_fn_wrapper,
433365 logical_codec : provider. logical_codec . clone ( ) ,
434366 clone : clone_fn_wrapper,
@@ -490,7 +422,6 @@ impl FFI_TableProvider {
490422 false => None ,
491423 } ,
492424 insert_into : insert_into_fn_wrapper,
493- merge_into : merge_into_fn_wrapper,
494425 statistics : statistics_fn_wrapper,
495426 logical_codec,
496427 clone : clone_fn_wrapper,
@@ -646,63 +577,16 @@ impl TableProvider for ForeignTableProvider {
646577
647578 Ok ( plan)
648579 }
649-
650- async fn merge_into (
651- & self ,
652- session : & dyn Session ,
653- source : Arc < dyn ExecutionPlan > ,
654- merge_schema : DFSchemaRef ,
655- on : Expr ,
656- clauses : Vec < MergeIntoClause > ,
657- ) -> Result < Arc < dyn ExecutionPlan > > {
658- let session = FFI_SessionRef :: new ( session, None , self . 0 . logical_codec . clone ( ) ) ;
659-
660- let rc = Handle :: try_current ( ) . ok ( ) ;
661- let source = FFI_ExecutionPlan :: new ( source, rc) ;
662-
663- let merge_schema = DfSchema :: try_from ( & merge_schema)
664- . map_err ( |e| DataFusionError :: Plan ( e. to_string ( ) ) ) ?;
665- let merge_schema_serialized: SVec < u8 > =
666- merge_schema. encode_to_vec ( ) . into_iter ( ) . collect ( ) ;
667-
668- let codec: Arc < dyn LogicalExtensionCodec > = ( & self . 0 . logical_codec ) . into ( ) ;
669- let merge_op =
670- serialize_merge_into_op ( & MergeIntoOp { on, clauses } , codec. as_ref ( ) )
671- . map_err ( |e| DataFusionError :: Plan ( e. to_string ( ) ) ) ?;
672- let merge_op_serialized = merge_op. encode_to_vec ( ) . into_iter ( ) . collect ( ) ;
673-
674- let plan = unsafe {
675- let maybe_plan = ( self . 0 . merge_into ) (
676- & self . 0 ,
677- session,
678- & source,
679- merge_schema_serialized,
680- merge_op_serialized,
681- )
682- . await ;
683-
684- <Arc < dyn ExecutionPlan > >:: try_from ( & df_result ! ( maybe_plan) ?) ?
685- } ;
686-
687- Ok ( plan)
688- }
689580}
690581
691582#[ cfg( test) ]
692583mod tests {
693- use std:: sync:: Mutex ;
694-
695- use arrow:: datatypes:: { DataType , Field , Schema } ;
696- use datafusion:: common:: { Column , DFSchema } ;
697- use datafusion:: logical_expr:: dml:: { MergeIntoAction , MergeIntoClauseKind } ;
698- use datafusion:: physical_plan:: empty:: EmptyExec ;
584+ use arrow:: datatypes:: Schema ;
699585 use datafusion:: prelude:: { SessionContext , col, lit} ;
700586 use datafusion_execution:: TaskContextProvider ;
701587
702588 use super :: * ;
703589
704- type CapturedMerge = ( usize , usize , String , Vec < String > ) ;
705-
706590 fn create_test_table_provider ( ) -> Result < Arc < dyn TableProvider > > {
707591 use arrow:: datatypes:: Field ;
708592 use datafusion:: arrow:: array:: Float32Array ;
@@ -729,122 +613,6 @@ mod tests {
729613 ) ?) )
730614 }
731615
732- #[ derive( Debug ) ]
733- struct CaptureMergeProvider {
734- schema : SchemaRef ,
735- captured : Mutex < Option < CapturedMerge > > ,
736- }
737-
738- #[ async_trait]
739- impl TableProvider for CaptureMergeProvider {
740- fn schema ( & self ) -> SchemaRef {
741- Arc :: clone ( & self . schema )
742- }
743-
744- fn table_type ( & self ) -> TableType {
745- TableType :: Base
746- }
747-
748- async fn scan (
749- & self ,
750- _state : & dyn Session ,
751- _projection : Option < & Vec < usize > > ,
752- _filters : & [ Expr ] ,
753- _limit : Option < usize > ,
754- ) -> Result < Arc < dyn ExecutionPlan > > {
755- Ok ( Arc :: new ( EmptyExec :: new ( Arc :: clone ( & self . schema ) ) ) )
756- }
757-
758- async fn merge_into (
759- & self ,
760- state : & dyn Session ,
761- source : Arc < dyn ExecutionPlan > ,
762- merge_schema : DFSchemaRef ,
763- on : Expr ,
764- clauses : Vec < MergeIntoClause > ,
765- ) -> Result < Arc < dyn ExecutionPlan > > {
766- let target_id =
767- merge_schema. index_of_column ( & Column :: new ( Some ( "target" ) , "id" ) ) ?;
768- let source_id =
769- merge_schema. index_of_column ( & Column :: new ( Some ( "source" ) , "id" ) ) ?;
770- let physical_on = state. create_physical_expr ( on, & merge_schema) ?;
771- let clauses = clauses. iter ( ) . map ( |clause| format ! ( "{clause:?}" ) ) . collect ( ) ;
772- * self . captured . lock ( ) . unwrap ( ) =
773- Some ( ( target_id, source_id, format ! ( "{physical_on:?}" ) , clauses) ) ;
774- Ok ( source)
775- }
776- }
777-
778- #[ tokio:: test]
779- async fn test_round_trip_ffi_table_provider_merge_into ( ) -> Result < ( ) > {
780- let schema = Arc :: new ( Schema :: new ( vec ! [
781- Field :: new( "id" , DataType :: Int32 , false ) ,
782- Field :: new( "val" , DataType :: Int64 , true ) ,
783- ] ) ) ;
784- let provider = Arc :: new ( CaptureMergeProvider {
785- schema : Arc :: clone ( & schema) ,
786- captured : Mutex :: new ( None ) ,
787- } ) ;
788- let ctx = Arc :: new ( SessionContext :: new ( ) ) ;
789- let task_ctx_provider = Arc :: clone ( & ctx) as Arc < dyn TaskContextProvider > ;
790- let task_ctx_provider = FFI_TaskContextProvider :: from ( & task_ctx_provider) ;
791-
792- let mut ffi_provider = FFI_TableProvider :: new (
793- Arc :: clone ( & provider) as Arc < dyn TableProvider > ,
794- true ,
795- None ,
796- task_ctx_provider,
797- None ,
798- ) ;
799- ffi_provider. library_marker_id = crate :: mock_foreign_marker_id;
800- let foreign: Arc < dyn TableProvider > = ( & ffi_provider) . into ( ) ;
801-
802- let target_schema = DFSchema :: try_from_qualified_schema ( "target" , & schema) ?;
803- let source_schema = DFSchema :: try_from_qualified_schema ( "source" , & schema) ?;
804- let merge_schema = Arc :: new ( target_schema. join ( & source_schema) ?) ;
805- let source: Arc < dyn ExecutionPlan > =
806- Arc :: new ( EmptyExec :: new ( Arc :: clone ( & schema) ) ) ;
807- let clauses = vec ! [
808- MergeIntoClause {
809- kind: MergeIntoClauseKind :: Matched ,
810- predicate: Some ( col( "target.val" ) . is_null( ) ) ,
811- action: MergeIntoAction :: Update ( vec![ (
812- "val" . to_string( ) ,
813- col( "source.val" ) ,
814- ) ] ) ,
815- } ,
816- MergeIntoClause {
817- kind: MergeIntoClauseKind :: NotMatched ,
818- predicate: None ,
819- action: MergeIntoAction :: Insert {
820- columns: vec![ "id" . to_string( ) , "val" . to_string( ) ] ,
821- values: vec![ col( "source.id" ) , col( "source.val" ) ] ,
822- } ,
823- } ,
824- ] ;
825-
826- foreign
827- . merge_into (
828- & ctx. state ( ) ,
829- source,
830- merge_schema,
831- col ( "target.id" ) . eq ( col ( "source.id" ) ) ,
832- clauses,
833- )
834- . await ?;
835-
836- let captured = provider. captured . lock ( ) . unwrap ( ) ;
837- let ( target_id, source_id, physical_on, clauses) =
838- captured. as_ref ( ) . expect ( "merge_into should be called" ) ;
839- assert_eq ! ( ( * target_id, * source_id) , ( 0 , 2 ) ) ;
840- assert ! ( physical_on. contains( "index: 0" ) ) ;
841- assert ! ( physical_on. contains( "index: 2" ) ) ;
842- assert_eq ! ( clauses. len( ) , 2 ) ;
843- assert ! ( clauses[ 0 ] . contains( "source" ) ) ;
844- assert ! ( clauses[ 1 ] . contains( "Insert" ) ) ;
845- Ok ( ( ) )
846- }
847-
848616 #[ tokio:: test]
849617 async fn test_round_trip_ffi_table_provider_scan ( ) -> Result < ( ) > {
850618 let provider = create_test_table_provider ( ) ?;
0 commit comments