Skip to main content

datafusion_ffi/proto/
physical_extension_codec.rs

1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements.  See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership.  The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License.  You may obtain a copy of the License at
8//
9//   http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied.  See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18use std::any::Any;
19use std::ffi::c_void;
20use std::sync::Arc;
21
22use datafusion_common::error::Result;
23use datafusion_execution::TaskContext;
24use datafusion_expr::{
25    AggregateUDF, AggregateUDFImpl, ScalarUDF, ScalarUDFImpl, WindowUDF, WindowUDFImpl,
26};
27use datafusion_physical_plan::ExecutionPlan;
28use datafusion_proto::physical_plan::{
29    DefaultPhysicalProtoConverter, PhysicalExtensionCodec,
30    PhysicalProtoConverterExtension,
31};
32
33use stabby::slice::Slice as SSlice;
34use stabby::str::Str as SStr;
35use stabby::vec::Vec as SVec;
36use tokio::runtime::Handle;
37
38use crate::execution::FFI_TaskContextProvider;
39use crate::execution_plan::FFI_ExecutionPlan;
40use crate::udaf::FFI_AggregateUDF;
41use crate::udf::FFI_ScalarUDF;
42use crate::udwf::FFI_WindowUDF;
43use crate::util::FFI_Result;
44use crate::{df_result, sresult_return};
45
46/// A stable struct for sharing [`PhysicalExtensionCodec`] across FFI boundaries.
47#[repr(C)]
48#[derive(Debug)]
49pub struct FFI_PhysicalExtensionCodec {
50    /// Decode bytes into an execution plan.
51    try_decode: unsafe extern "C" fn(
52        &Self,
53        buf: SSlice<u8>,
54        inputs: SVec<FFI_ExecutionPlan>,
55    ) -> FFI_Result<FFI_ExecutionPlan>,
56
57    /// Encode an execution plan into bytes.
58    try_encode:
59        unsafe extern "C" fn(&Self, node: FFI_ExecutionPlan) -> FFI_Result<SVec<u8>>,
60
61    /// Decode bytes into a user defined scalar function.
62    try_decode_udf: unsafe extern "C" fn(
63        &Self,
64        name: SStr,
65        buf: SSlice<u8>,
66    ) -> FFI_Result<FFI_ScalarUDF>,
67
68    /// Encode a user defined scalar function into bytes.
69    try_encode_udf:
70        unsafe extern "C" fn(&Self, node: FFI_ScalarUDF) -> FFI_Result<SVec<u8>>,
71
72    /// Decode bytes into a user defined aggregate function.
73    try_decode_udaf: unsafe extern "C" fn(
74        &Self,
75        name: SStr,
76        buf: SSlice<u8>,
77    ) -> FFI_Result<FFI_AggregateUDF>,
78
79    /// Encode a user defined aggregate function into bytes.
80    try_encode_udaf:
81        unsafe extern "C" fn(&Self, node: FFI_AggregateUDF) -> FFI_Result<SVec<u8>>,
82
83    /// Decode bytes into a user defined window function.
84    try_decode_udwf: unsafe extern "C" fn(
85        &Self,
86        name: SStr,
87        buf: SSlice<u8>,
88    ) -> FFI_Result<FFI_WindowUDF>,
89
90    /// Encode a user defined window function into bytes.
91    try_encode_udwf:
92        unsafe extern "C" fn(&Self, node: FFI_WindowUDF) -> FFI_Result<SVec<u8>>,
93
94    /// Access the current [`TaskContext`].
95    pub(crate) task_ctx_provider: FFI_TaskContextProvider,
96
97    /// Used to create a clone on the provider of the execution plan. This should
98    /// only need to be called by the receiver of the plan.
99    pub clone: unsafe extern "C" fn(plan: &Self) -> Self,
100
101    /// Release the memory of the private data when it is no longer being used.
102    pub release: unsafe extern "C" fn(arg: &mut Self),
103
104    /// Return the major DataFusion version number of this provider.
105    pub version: unsafe extern "C" fn() -> u64,
106
107    /// Internal data. This is only to be accessed by the provider of the plan.
108    /// A [`ForeignPhysicalExtensionCodec`] should never attempt to access this data.
109    pub private_data: *mut c_void,
110
111    /// Utility to identify when FFI objects are accessed locally through
112    /// the foreign interface.
113    pub library_marker_id: extern "C" fn() -> usize,
114}
115
116unsafe impl Send for FFI_PhysicalExtensionCodec {}
117unsafe impl Sync for FFI_PhysicalExtensionCodec {}
118
119struct PhysicalExtensionCodecPrivateData {
120    codec: Arc<dyn PhysicalExtensionCodec>,
121    runtime: Option<Handle>,
122}
123
124impl FFI_PhysicalExtensionCodec {
125    fn inner(&self) -> &Arc<dyn PhysicalExtensionCodec> {
126        let private_data = self.private_data as *const PhysicalExtensionCodecPrivateData;
127        unsafe { &(*private_data).codec }
128    }
129
130    fn runtime(&self) -> &Option<Handle> {
131        let private_data = self.private_data as *const PhysicalExtensionCodecPrivateData;
132        unsafe { &(*private_data).runtime }
133    }
134}
135
136unsafe extern "C" fn try_decode_fn_wrapper(
137    codec: &FFI_PhysicalExtensionCodec,
138    buf: SSlice<u8>,
139    inputs: SVec<FFI_ExecutionPlan>,
140) -> FFI_Result<FFI_ExecutionPlan> {
141    let runtime = codec.runtime().clone();
142    let task_ctx: Arc<TaskContext> =
143        sresult_return!((&codec.task_ctx_provider).try_into());
144    let codec = codec.inner();
145    let inputs = inputs
146        .into_iter()
147        .map(|plan| <Arc<dyn ExecutionPlan>>::try_from(&plan))
148        .collect::<Result<Vec<_>>>();
149    let inputs = sresult_return!(inputs);
150
151    let plan = sresult_return!(codec.try_decode(
152        buf.as_ref(),
153        &inputs,
154        task_ctx.as_ref(),
155        &DefaultPhysicalProtoConverter {},
156    ));
157
158    FFI_Result::Ok(FFI_ExecutionPlan::new(plan, runtime))
159}
160
161unsafe extern "C" fn try_encode_fn_wrapper(
162    codec: &FFI_PhysicalExtensionCodec,
163    node: FFI_ExecutionPlan,
164) -> FFI_Result<SVec<u8>> {
165    let codec = codec.inner();
166
167    let plan: Arc<dyn ExecutionPlan> = sresult_return!((&node).try_into());
168
169    let mut bytes = Vec::new();
170    sresult_return!(codec.try_encode(
171        plan,
172        &mut bytes,
173        &DefaultPhysicalProtoConverter {}
174    ));
175
176    FFI_Result::Ok(bytes.into_iter().collect())
177}
178
179unsafe extern "C" fn try_decode_udf_fn_wrapper(
180    codec: &FFI_PhysicalExtensionCodec,
181    name: SStr,
182    buf: SSlice<u8>,
183) -> FFI_Result<FFI_ScalarUDF> {
184    let codec = codec.inner();
185
186    let udf = sresult_return!(codec.try_decode_udf(name.as_str(), buf.as_ref()));
187    let udf = FFI_ScalarUDF::from(udf);
188
189    FFI_Result::Ok(udf)
190}
191
192unsafe extern "C" fn try_encode_udf_fn_wrapper(
193    codec: &FFI_PhysicalExtensionCodec,
194    node: FFI_ScalarUDF,
195) -> FFI_Result<SVec<u8>> {
196    let codec = codec.inner();
197    let node: Arc<dyn ScalarUDFImpl> = (&node).into();
198    let node = ScalarUDF::new_from_shared_impl(node);
199
200    let mut bytes = Vec::new();
201    sresult_return!(codec.try_encode_udf(&node, &mut bytes));
202
203    FFI_Result::Ok(bytes.into_iter().collect())
204}
205
206unsafe extern "C" fn try_decode_udaf_fn_wrapper(
207    codec: &FFI_PhysicalExtensionCodec,
208    name: SStr,
209    buf: SSlice<u8>,
210) -> FFI_Result<FFI_AggregateUDF> {
211    let codec_inner = codec.inner();
212    let udaf = sresult_return!(codec_inner.try_decode_udaf(name.into(), buf.as_ref()));
213    let udaf = FFI_AggregateUDF::from(udaf);
214
215    FFI_Result::Ok(udaf)
216}
217
218unsafe extern "C" fn try_encode_udaf_fn_wrapper(
219    codec: &FFI_PhysicalExtensionCodec,
220    node: FFI_AggregateUDF,
221) -> FFI_Result<SVec<u8>> {
222    let codec = codec.inner();
223    let udaf: Arc<dyn AggregateUDFImpl> = (&node).into();
224    let udaf = AggregateUDF::new_from_shared_impl(udaf);
225
226    let mut bytes = Vec::new();
227    sresult_return!(codec.try_encode_udaf(&udaf, &mut bytes));
228
229    FFI_Result::Ok(bytes.into_iter().collect())
230}
231
232unsafe extern "C" fn try_decode_udwf_fn_wrapper(
233    codec: &FFI_PhysicalExtensionCodec,
234    name: SStr,
235    buf: SSlice<u8>,
236) -> FFI_Result<FFI_WindowUDF> {
237    let codec = codec.inner();
238    let udwf = sresult_return!(codec.try_decode_udwf(name.into(), buf.as_ref()));
239    let udwf = FFI_WindowUDF::from(udwf);
240
241    FFI_Result::Ok(udwf)
242}
243
244unsafe extern "C" fn try_encode_udwf_fn_wrapper(
245    codec: &FFI_PhysicalExtensionCodec,
246    node: FFI_WindowUDF,
247) -> FFI_Result<SVec<u8>> {
248    let codec = codec.inner();
249    let udwf: Arc<dyn WindowUDFImpl> = (&node).into();
250    let udwf = WindowUDF::new_from_shared_impl(udwf);
251
252    let mut bytes = Vec::new();
253    sresult_return!(codec.try_encode_udwf(&udwf, &mut bytes));
254
255    FFI_Result::Ok(bytes.into_iter().collect())
256}
257
258unsafe extern "C" fn release_fn_wrapper(codec: &mut FFI_PhysicalExtensionCodec) {
259    unsafe {
260        let private_data =
261            Box::from_raw(codec.private_data as *mut PhysicalExtensionCodecPrivateData);
262        drop(private_data);
263    }
264}
265
266unsafe extern "C" fn clone_fn_wrapper(
267    codec: &FFI_PhysicalExtensionCodec,
268) -> FFI_PhysicalExtensionCodec {
269    let old_codec = Arc::clone(codec.inner());
270    let runtime = codec.runtime().clone();
271
272    FFI_PhysicalExtensionCodec::new(old_codec, runtime, codec.task_ctx_provider.clone())
273}
274
275impl Drop for FFI_PhysicalExtensionCodec {
276    fn drop(&mut self) {
277        unsafe { (self.release)(self) }
278    }
279}
280
281impl FFI_PhysicalExtensionCodec {
282    /// Creates a new [`FFI_PhysicalExtensionCodec`].
283    pub fn new(
284        codec: Arc<dyn PhysicalExtensionCodec>,
285        runtime: Option<Handle>,
286        task_ctx_provider: impl Into<FFI_TaskContextProvider>,
287    ) -> Self {
288        if let Some(codec) = (Arc::clone(&codec) as Arc<dyn Any>)
289            .downcast_ref::<ForeignPhysicalExtensionCodec>()
290        {
291            return codec.0.clone();
292        }
293
294        let task_ctx_provider = task_ctx_provider.into();
295        let private_data = Box::new(PhysicalExtensionCodecPrivateData { codec, runtime });
296
297        Self {
298            try_decode: try_decode_fn_wrapper,
299            try_encode: try_encode_fn_wrapper,
300            try_decode_udf: try_decode_udf_fn_wrapper,
301            try_encode_udf: try_encode_udf_fn_wrapper,
302            try_decode_udaf: try_decode_udaf_fn_wrapper,
303            try_encode_udaf: try_encode_udaf_fn_wrapper,
304            try_decode_udwf: try_decode_udwf_fn_wrapper,
305            try_encode_udwf: try_encode_udwf_fn_wrapper,
306            task_ctx_provider,
307
308            clone: clone_fn_wrapper,
309            release: release_fn_wrapper,
310            version: crate::version,
311            private_data: Box::into_raw(private_data) as *mut c_void,
312            library_marker_id: crate::get_library_marker_id,
313        }
314    }
315}
316
317/// This wrapper struct exists on the receiver side of the FFI interface, so it has
318/// no guarantees about being able to access the data in `private_data`. Any functions
319/// defined on this struct must only use the stable functions provided in
320/// FFI_PhysicalExtensionCodec to interact with the foreign table provider.
321#[derive(Debug)]
322pub struct ForeignPhysicalExtensionCodec(pub FFI_PhysicalExtensionCodec);
323
324unsafe impl Send for ForeignPhysicalExtensionCodec {}
325unsafe impl Sync for ForeignPhysicalExtensionCodec {}
326
327impl From<&FFI_PhysicalExtensionCodec> for Arc<dyn PhysicalExtensionCodec> {
328    fn from(codec: &FFI_PhysicalExtensionCodec) -> Self {
329        if (codec.library_marker_id)() == crate::get_library_marker_id() {
330            Arc::clone(codec.inner())
331        } else {
332            Arc::new(ForeignPhysicalExtensionCodec(codec.clone()))
333        }
334    }
335}
336
337impl Clone for FFI_PhysicalExtensionCodec {
338    fn clone(&self) -> Self {
339        unsafe { (self.clone)(self) }
340    }
341}
342
343impl PhysicalExtensionCodec for ForeignPhysicalExtensionCodec {
344    fn try_decode(
345        &self,
346        buf: &[u8],
347        inputs: &[Arc<dyn ExecutionPlan>],
348        _ctx: &TaskContext,
349        _proto_converter: &dyn PhysicalProtoConverterExtension,
350    ) -> Result<Arc<dyn ExecutionPlan>> {
351        let inputs = inputs
352            .iter()
353            .map(|plan| FFI_ExecutionPlan::new(Arc::clone(plan), None))
354            .collect();
355
356        let plan =
357            df_result!(unsafe { (self.0.try_decode)(&self.0, buf.into(), inputs) })?;
358        let plan: Arc<dyn ExecutionPlan> = (&plan).try_into()?;
359
360        Ok(plan)
361    }
362
363    fn try_encode(
364        &self,
365        node: Arc<dyn ExecutionPlan>,
366        buf: &mut Vec<u8>,
367        _proto_converter: &dyn PhysicalProtoConverterExtension,
368    ) -> Result<()> {
369        let plan = FFI_ExecutionPlan::new(node, None);
370        let bytes = df_result!(unsafe { (self.0.try_encode)(&self.0, plan) })?;
371
372        buf.extend(bytes);
373        Ok(())
374    }
375
376    fn try_decode_udf(&self, name: &str, buf: &[u8]) -> Result<Arc<ScalarUDF>> {
377        let udf = unsafe {
378            df_result!((self.0.try_decode_udf)(&self.0, name.into(), buf.into()))
379        }?;
380        let udf: Arc<dyn ScalarUDFImpl> = (&udf).into();
381
382        Ok(Arc::new(ScalarUDF::new_from_shared_impl(udf)))
383    }
384
385    fn try_encode_udf(&self, node: &ScalarUDF, buf: &mut Vec<u8>) -> Result<()> {
386        let node = FFI_ScalarUDF::from(Arc::new(node.clone()));
387        let bytes = df_result!(unsafe { (self.0.try_encode_udf)(&self.0, node) })?;
388
389        buf.extend(bytes);
390
391        Ok(())
392    }
393
394    fn try_decode_udaf(&self, name: &str, buf: &[u8]) -> Result<Arc<AggregateUDF>> {
395        let udaf = unsafe {
396            df_result!((self.0.try_decode_udaf)(&self.0, name.into(), buf.into()))
397        }?;
398        let udaf: Arc<dyn AggregateUDFImpl> = (&udaf).into();
399
400        Ok(Arc::new(AggregateUDF::new_from_shared_impl(udaf)))
401    }
402
403    fn try_encode_udaf(&self, node: &AggregateUDF, buf: &mut Vec<u8>) -> Result<()> {
404        let node = Arc::new(node.clone());
405        let node = FFI_AggregateUDF::from(node);
406        let bytes = df_result!(unsafe { (self.0.try_encode_udaf)(&self.0, node) })?;
407
408        buf.extend(bytes);
409
410        Ok(())
411    }
412
413    fn try_decode_udwf(&self, name: &str, buf: &[u8]) -> Result<Arc<WindowUDF>> {
414        let udwf = unsafe {
415            df_result!((self.0.try_decode_udwf)(&self.0, name.into(), buf.into()))
416        }?;
417        let udwf: Arc<dyn WindowUDFImpl> = (&udwf).into();
418
419        Ok(Arc::new(WindowUDF::new_from_shared_impl(udwf)))
420    }
421
422    fn try_encode_udwf(&self, node: &WindowUDF, buf: &mut Vec<u8>) -> Result<()> {
423        let node = Arc::new(node.clone());
424        let node = FFI_WindowUDF::from(node);
425        let bytes = df_result!(unsafe { (self.0.try_encode_udwf)(&self.0, node) })?;
426
427        buf.extend(bytes);
428
429        Ok(())
430    }
431}
432
433#[cfg(test)]
434pub(crate) mod tests {
435    use std::sync::Arc;
436
437    use arrow_schema::{DataType, Field, Schema};
438    use datafusion_common::{Result, exec_err};
439    use datafusion_execution::TaskContext;
440    use datafusion_expr::ptr_eq::arc_ptr_eq;
441    use datafusion_expr::{AggregateUDF, ScalarUDF, WindowUDF, WindowUDFImpl};
442    use datafusion_functions::math::abs::AbsFunc;
443    use datafusion_functions_aggregate::sum::Sum;
444    use datafusion_functions_window::rank::{Rank, RankType};
445    use datafusion_physical_plan::ExecutionPlan;
446    use datafusion_proto::physical_plan::{
447        DefaultPhysicalProtoConverter, PhysicalExtensionCodec,
448        PhysicalProtoConverterExtension,
449    };
450
451    use crate::execution_plan::tests::EmptyExec;
452    use crate::proto::physical_extension_codec::FFI_PhysicalExtensionCodec;
453
454    #[derive(Debug)]
455    pub(crate) struct TestExtensionCodec;
456
457    impl TestExtensionCodec {
458        pub(crate) const MAGIC_NUMBER: u8 = 127;
459        pub(crate) const EMPTY_EXEC_SERIALIZED: u8 = 1;
460        pub(crate) const ABS_FUNC_SERIALIZED: u8 = 2;
461        pub(crate) const SUM_UDAF_SERIALIZED: u8 = 3;
462        pub(crate) const RANK_UDWF_SERIALIZED: u8 = 4;
463        pub(crate) const MEMTABLE_SERIALIZED: u8 = 5;
464    }
465
466    impl PhysicalExtensionCodec for TestExtensionCodec {
467        fn try_decode(
468            &self,
469            buf: &[u8],
470            _inputs: &[Arc<dyn ExecutionPlan>],
471            _ctx: &TaskContext,
472            _proto_converter: &dyn PhysicalProtoConverterExtension,
473        ) -> Result<Arc<dyn ExecutionPlan>> {
474            if buf[0] != Self::MAGIC_NUMBER {
475                return exec_err!(
476                    "TestExtensionCodec input buffer does not start with magic number"
477                );
478            }
479
480            if buf.len() != 2 || buf[1] != Self::EMPTY_EXEC_SERIALIZED {
481                return exec_err!("TestExtensionCodec unable to decode execution plan");
482            }
483
484            Ok(create_test_exec())
485        }
486
487        fn try_encode(
488            &self,
489            node: Arc<dyn ExecutionPlan>,
490            buf: &mut Vec<u8>,
491            _proto_converter: &dyn PhysicalProtoConverterExtension,
492        ) -> Result<()> {
493            buf.push(Self::MAGIC_NUMBER);
494
495            let Some(_) = node.downcast_ref::<EmptyExec>() else {
496                return exec_err!("TestExtensionCodec only expects EmptyExec");
497            };
498
499            buf.push(Self::EMPTY_EXEC_SERIALIZED);
500
501            Ok(())
502        }
503
504        fn try_decode_udf(&self, _name: &str, buf: &[u8]) -> Result<Arc<ScalarUDF>> {
505            if buf[0] != Self::MAGIC_NUMBER {
506                return exec_err!(
507                    "TestExtensionCodec input buffer does not start with magic number"
508                );
509            }
510
511            if buf.len() != 2 || buf[1] != Self::ABS_FUNC_SERIALIZED {
512                return exec_err!("TestExtensionCodec unable to decode udf");
513            }
514
515            Ok(Arc::new(ScalarUDF::from(AbsFunc::new())))
516        }
517
518        fn try_encode_udf(&self, node: &ScalarUDF, buf: &mut Vec<u8>) -> Result<()> {
519            buf.push(Self::MAGIC_NUMBER);
520
521            let udf = node.inner();
522            if !udf.is::<AbsFunc>() {
523                return exec_err!("TestExtensionCodec only expects Abs UDF");
524            };
525
526            buf.push(Self::ABS_FUNC_SERIALIZED);
527
528            Ok(())
529        }
530
531        fn try_decode_udaf(&self, _name: &str, buf: &[u8]) -> Result<Arc<AggregateUDF>> {
532            if buf[0] != Self::MAGIC_NUMBER {
533                return exec_err!(
534                    "TestExtensionCodec input buffer does not start with magic number"
535                );
536            }
537
538            if buf.len() != 2 || buf[1] != Self::SUM_UDAF_SERIALIZED {
539                return exec_err!("TestExtensionCodec unable to decode udaf");
540            }
541
542            Ok(Arc::new(AggregateUDF::from(Sum::new())))
543        }
544
545        fn try_encode_udaf(&self, node: &AggregateUDF, buf: &mut Vec<u8>) -> Result<()> {
546            buf.push(Self::MAGIC_NUMBER);
547
548            let udf = node.inner();
549            let Some(_udf) = udf.downcast_ref::<Sum>() else {
550                return exec_err!("TestExtensionCodec only expects Sum UDAF");
551            };
552
553            buf.push(Self::SUM_UDAF_SERIALIZED);
554
555            Ok(())
556        }
557
558        fn try_decode_udwf(&self, _name: &str, buf: &[u8]) -> Result<Arc<WindowUDF>> {
559            if buf[0] != Self::MAGIC_NUMBER {
560                return exec_err!(
561                    "TestExtensionCodec input buffer does not start with magic number"
562                );
563            }
564
565            if buf.len() != 2 || buf[1] != Self::RANK_UDWF_SERIALIZED {
566                return exec_err!("TestExtensionCodec unable to decode udwf");
567            }
568
569            Ok(Arc::new(WindowUDF::from(Rank::new(
570                "my_rank".to_owned(),
571                RankType::Basic,
572            ))))
573        }
574
575        fn try_encode_udwf(&self, node: &WindowUDF, buf: &mut Vec<u8>) -> Result<()> {
576            buf.push(Self::MAGIC_NUMBER);
577
578            let udf = node.inner();
579            let Some(udf) = udf.downcast_ref::<Rank>() else {
580                return exec_err!("TestExtensionCodec only expects Rank UDWF");
581            };
582
583            if udf.name() != "my_rank" {
584                return exec_err!("TestExtensionCodec only expects my_rank UDWF name");
585            }
586
587            buf.push(Self::RANK_UDWF_SERIALIZED);
588
589            Ok(())
590        }
591    }
592
593    fn create_test_exec() -> Arc<dyn ExecutionPlan> {
594        let schema =
595            Arc::new(Schema::new(vec![Field::new("a", DataType::Float32, false)]));
596        Arc::new(EmptyExec::new(schema)) as Arc<dyn ExecutionPlan>
597    }
598
599    #[test]
600    fn roundtrip_ffi_physical_extension_codec_exec_plan() -> Result<()> {
601        let codec = Arc::new(TestExtensionCodec {});
602        let (ctx, task_ctx_provider) = crate::util::tests::test_session_and_ctx();
603
604        let mut ffi_codec =
605            FFI_PhysicalExtensionCodec::new(codec, None, task_ctx_provider);
606        ffi_codec.library_marker_id = crate::mock_foreign_marker_id;
607        let foreign_codec: Arc<dyn PhysicalExtensionCodec> = (&ffi_codec).into();
608
609        let exec = create_test_exec();
610        let input_execs = [create_test_exec()];
611        let mut bytes = Vec::new();
612        foreign_codec.try_encode(
613            Arc::clone(&exec),
614            &mut bytes,
615            &DefaultPhysicalProtoConverter {},
616        )?;
617
618        let returned_exec = foreign_codec.try_decode(
619            &bytes,
620            &input_execs,
621            ctx.task_ctx().as_ref(),
622            &DefaultPhysicalProtoConverter {},
623        )?;
624
625        assert!(returned_exec.is::<EmptyExec>());
626
627        Ok(())
628    }
629
630    #[test]
631    fn roundtrip_ffi_physical_extension_codec_udf() -> Result<()> {
632        let codec = Arc::new(TestExtensionCodec {});
633        let (_ctx, task_ctx_provider) = crate::util::tests::test_session_and_ctx();
634
635        let mut ffi_codec =
636            FFI_PhysicalExtensionCodec::new(codec, None, task_ctx_provider);
637        ffi_codec.library_marker_id = crate::mock_foreign_marker_id;
638        let foreign_codec: Arc<dyn PhysicalExtensionCodec> = (&ffi_codec).into();
639
640        let udf = Arc::new(ScalarUDF::from(AbsFunc::new()));
641        let mut bytes = Vec::new();
642        foreign_codec.try_encode_udf(udf.as_ref(), &mut bytes)?;
643
644        let returned_udf = foreign_codec.try_decode_udf(udf.name(), &bytes)?;
645
646        assert!(returned_udf.inner().is::<AbsFunc>());
647
648        Ok(())
649    }
650
651    #[test]
652    fn roundtrip_ffi_physical_extension_codec_udaf() -> Result<()> {
653        let codec = Arc::new(TestExtensionCodec {});
654        let (_ctx, task_ctx_provider) = crate::util::tests::test_session_and_ctx();
655
656        let mut ffi_codec =
657            FFI_PhysicalExtensionCodec::new(codec, None, task_ctx_provider);
658        ffi_codec.library_marker_id = crate::mock_foreign_marker_id;
659        let foreign_codec: Arc<dyn PhysicalExtensionCodec> = (&ffi_codec).into();
660
661        let udf = Arc::new(AggregateUDF::from(Sum::new()));
662        let mut bytes = Vec::new();
663        foreign_codec.try_encode_udaf(udf.as_ref(), &mut bytes)?;
664
665        let returned_udf = foreign_codec.try_decode_udaf(udf.name(), &bytes)?;
666
667        assert!(returned_udf.inner().is::<Sum>());
668
669        Ok(())
670    }
671
672    #[test]
673    fn roundtrip_ffi_physical_extension_codec_udwf() -> Result<()> {
674        let codec = Arc::new(TestExtensionCodec {});
675        let (_ctx, task_ctx_provider) = crate::util::tests::test_session_and_ctx();
676
677        let mut ffi_codec =
678            FFI_PhysicalExtensionCodec::new(codec, None, task_ctx_provider);
679        ffi_codec.library_marker_id = crate::mock_foreign_marker_id;
680        let foreign_codec: Arc<dyn PhysicalExtensionCodec> = (&ffi_codec).into();
681
682        let udf = Arc::new(WindowUDF::from(Rank::new(
683            "my_rank".to_owned(),
684            RankType::Basic,
685        )));
686        let mut bytes = Vec::new();
687        foreign_codec.try_encode_udwf(udf.as_ref(), &mut bytes)?;
688
689        let returned_udf = foreign_codec.try_decode_udwf(udf.name(), &bytes)?;
690
691        assert!(returned_udf.inner().is::<Rank>());
692
693        Ok(())
694    }
695
696    #[test]
697    fn ffi_physical_extension_codec_local_bypass() {
698        let codec = Arc::new(TestExtensionCodec {}) as Arc<dyn PhysicalExtensionCodec>;
699        let (_ctx, task_ctx_provider) = crate::util::tests::test_session_and_ctx();
700
701        let mut ffi_codec =
702            FFI_PhysicalExtensionCodec::new(Arc::clone(&codec), None, task_ctx_provider);
703
704        // Verify local libraries can be downcast to their original
705        let foreign_codec: Arc<dyn PhysicalExtensionCodec> = (&ffi_codec).into();
706        assert!(arc_ptr_eq(&foreign_codec, &codec));
707
708        // Verify different library markers generate foreign providers
709        ffi_codec.library_marker_id = crate::mock_foreign_marker_id;
710        let foreign_codec: Arc<dyn PhysicalExtensionCodec> = (&ffi_codec).into();
711        assert!(!arc_ptr_eq(&foreign_codec, &codec));
712    }
713}