1515// specific language governing permissions and limitations
1616// under the License.
1717
18- use std:: collections:: HashMap ;
1918use std:: fmt;
20- use std:: sync:: atomic :: { AtomicU64 , AtomicUsize , Ordering } ;
21- use std:: sync:: { Arc , Mutex , OnceLock } ;
19+ use std:: sync:: Arc ;
20+ use std:: sync:: atomic :: { AtomicUsize , Ordering } ;
2221
23- use datafusion:: common:: { DataFusionError , Result } ;
24- use datafusion:: datasource:: source:: DataSourceExec ;
22+ use datafusion:: common:: Result ;
2523use datafusion:: execution:: TaskContext ;
2624use datafusion:: logical_expr:: ScalarUDF ;
2725use datafusion:: physical_plan:: ExecutionPlan ;
28- use datafusion_ffi:: execution_plan:: ForeignExecutionPlan ;
2926use datafusion_ffi:: proto:: physical_extension_codec:: FFI_PhysicalExtensionCodec ;
3027use datafusion_proto:: physical_plan:: {
3128 DefaultPhysicalExtensionCodec , PhysicalExtensionCodec , PhysicalProtoConverterExtension ,
@@ -34,26 +31,9 @@ use datafusion_python_util::{ffi_task_context_provider_from_pycapsule, get_tokio
3431use pyo3:: prelude:: * ;
3532use pyo3:: types:: PyCapsule ;
3633
34+ use crate :: foreign_plan_workaround;
3735use crate :: required_udf:: { TaskContextProbe , resolve_required_udf} ;
3836
39- const EXECUTION_PLAN_TOKEN : & [ u8 ] = b"DFPYEXEP" ;
40- static NEXT_EXECUTION_PLAN_ID : AtomicU64 = AtomicU64 :: new ( 1 ) ;
41- static EXECUTION_PLANS : OnceLock < Mutex < HashMap < u64 , Arc < dyn ExecutionPlan > > > > = OnceLock :: new ( ) ;
42-
43- /// Execution-plan counterpart of the logical codec's provider registry, with
44- /// the same lifecycle: encoding inserts, decoding removes, so a decode
45- /// consumes its token and an encode that is never decoded leaks. See
46- /// [`crate::logical_extension_codec`] for why that is acceptable here and not
47- /// in a real codec.
48- fn execution_plans ( ) -> & ' static Mutex < HashMap < u64 , Arc < dyn ExecutionPlan > > > {
49- EXECUTION_PLANS . get_or_init ( || Mutex :: new ( HashMap :: new ( ) ) )
50- }
51-
52- fn token_id ( buf : & [ u8 ] ) -> Option < u64 > {
53- let id: [ u8 ; 8 ] = buf. strip_prefix ( EXECUTION_PLAN_TOKEN ) ?. try_into ( ) . ok ( ) ?;
54- Some ( u64:: from_le_bytes ( id) )
55- }
56-
5737#[ derive( Debug , Default ) ]
5838pub ( crate ) struct PhysicalCallCounters {
5939 pub encode_udf : AtomicUsize ,
@@ -99,19 +79,11 @@ impl PhysicalExtensionCodec for CountingPhysicalExtensionCodec {
9979 proto_converter : & dyn PhysicalProtoConverterExtension ,
10080 ) -> Result < Arc < dyn ExecutionPlan > > {
10181 resolve_required_udf ( self . required_udf . as_deref ( ) , ctx, & self . counters . task_ctx ) ?;
102- if let Some ( id ) = token_id ( buf) {
82+ if let Some ( plan ) = foreign_plan_workaround :: take ( buf) ? {
10383 self . counters
10484 . decode_execution_plan
10585 . fetch_add ( 1 , Ordering :: SeqCst ) ;
106- return execution_plans ( )
107- . lock ( )
108- . map_err ( |err| DataFusionError :: Internal ( err. to_string ( ) ) ) ?
109- . remove ( & id)
110- . ok_or_else ( || {
111- DataFusionError :: Internal ( format ! (
112- "Unknown datafusion-ffi-example execution plan token {id}"
113- ) )
114- } ) ;
86+ return Ok ( plan) ;
11587 }
11688 self . inner . try_decode ( buf, inputs, ctx, proto_converter)
11789 }
@@ -122,20 +94,11 @@ impl PhysicalExtensionCodec for CountingPhysicalExtensionCodec {
12294 buf : & mut Vec < u8 > ,
12395 proto_converter : & dyn PhysicalProtoConverterExtension ,
12496 ) -> Result < ( ) > {
125- // The provider owns DataSourceExec. A ForeignExecutionPlan can wrap a
126- // host-added execution decorator around that scan; retaining the opaque
127- // wrapper preserves its original library identity without downcasting it.
128- if node. is :: < DataSourceExec > ( ) || node. is :: < ForeignExecutionPlan > ( ) {
97+ if foreign_plan_workaround:: claims ( & node) {
12998 self . counters
13099 . encode_execution_plan
131100 . fetch_add ( 1 , Ordering :: SeqCst ) ;
132- let id = NEXT_EXECUTION_PLAN_ID . fetch_add ( 1 , Ordering :: SeqCst ) ;
133- execution_plans ( )
134- . lock ( )
135- . map_err ( |err| DataFusionError :: Internal ( err. to_string ( ) ) ) ?
136- . insert ( id, node) ;
137- buf. extend_from_slice ( EXECUTION_PLAN_TOKEN ) ;
138- buf. extend_from_slice ( & id. to_le_bytes ( ) ) ;
101+ foreign_plan_workaround:: park ( node, buf) ?;
139102 return Ok ( ( ) ) ;
140103 }
141104 self . inner . try_encode ( node, buf, proto_converter)
0 commit comments