Skip to main content

datafusion_physical_plan/test/
exec.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
18//! Simple iterator over batches for use in testing
19
20use crate::{ChildrenPropertiesMode, ReplaceChildrenOptions};
21use crate::{
22    DisplayAs, DisplayFormatType, ExecutionPlan, Partitioning, PlanProperties,
23    RecordBatchStream, SendableRecordBatchStream, Statistics, common,
24    execution_plan::Boundedness, statistics::StatisticsArgs,
25};
26use crate::{
27    execution_plan::EmissionType,
28    stream::{RecordBatchReceiverStream, RecordBatchStreamAdapter},
29};
30use std::sync::atomic::{AtomicUsize, Ordering};
31use std::{
32    pin::Pin,
33    sync::{Arc, Weak},
34    task::{Context, Poll},
35};
36
37use arrow::datatypes::{DataType, Field, Schema, SchemaRef};
38use arrow::record_batch::RecordBatch;
39use datafusion_common::tree_node::TreeNodeRecursion;
40use datafusion_common::{DataFusionError, Result, internal_err};
41use datafusion_execution::TaskContext;
42use datafusion_physical_expr::{EquivalenceProperties, PhysicalExpr};
43
44use futures::Stream;
45use tokio::sync::Barrier;
46
47/// Index into the data that has been returned so far
48#[derive(Debug, Default, Clone)]
49pub struct BatchIndex {
50    inner: Arc<std::sync::Mutex<usize>>,
51}
52
53impl BatchIndex {
54    /// Return the current index
55    pub fn value(&self) -> usize {
56        let inner = self.inner.lock().unwrap();
57        *inner
58    }
59
60    // increment the current index by one
61    pub fn incr(&self) {
62        let mut inner = self.inner.lock().unwrap();
63        *inner += 1;
64    }
65}
66
67/// Iterator over batches
68#[derive(Debug, Default)]
69pub struct TestStream {
70    /// Vector of record batches
71    data: Vec<RecordBatch>,
72    /// Index into the data that has been returned so far
73    index: BatchIndex,
74}
75
76impl TestStream {
77    /// Create an iterator for a vector of record batches. Assumes at
78    /// least one entry in data (for the schema)
79    pub fn new(data: Vec<RecordBatch>) -> Self {
80        Self {
81            data,
82            ..Default::default()
83        }
84    }
85
86    /// Return a handle to the index counter for this stream
87    pub fn index(&self) -> BatchIndex {
88        self.index.clone()
89    }
90}
91
92impl Stream for TestStream {
93    type Item = Result<RecordBatch>;
94
95    fn poll_next(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Option<Self::Item>> {
96        let next_batch = self.index.value();
97
98        Poll::Ready(if next_batch < self.data.len() {
99            let next_batch = self.index.value();
100            self.index.incr();
101            Some(Ok(self.data[next_batch].clone()))
102        } else {
103            None
104        })
105    }
106
107    fn size_hint(&self) -> (usize, Option<usize>) {
108        (self.data.len(), Some(self.data.len()))
109    }
110}
111
112impl RecordBatchStream for TestStream {
113    /// Get the schema
114    fn schema(&self) -> SchemaRef {
115        self.data[0].schema()
116    }
117}
118
119/// A Mock ExecutionPlan that can be used for writing tests of other
120/// ExecutionPlans
121#[derive(Debug)]
122pub struct MockExec {
123    /// the results to send back
124    data: Vec<Result<RecordBatch>>,
125    schema: SchemaRef,
126    /// if true (the default), sends data using a separate task to ensure the
127    /// batches are not available without this stream yielding first
128    use_task: bool,
129    /// if true, report unknown statistics instead of deriving them from
130    /// `data` (which propagates any planted errors at planning time)
131    unknown_statistics: bool,
132    cache: Arc<PlanProperties>,
133}
134
135impl MockExec {
136    /// Create a new `MockExec` with a single partition that returns
137    /// the specified `Results`s.
138    ///
139    /// By default, the batches are not produced immediately (the
140    /// caller has to actually yield and another task must run) to
141    /// ensure any poll loops are correct. This behavior can be
142    /// changed with `with_use_task`
143    pub fn new(data: Vec<Result<RecordBatch>>, schema: SchemaRef) -> Self {
144        let cache = Self::compute_properties(Arc::clone(&schema));
145        Self {
146            data,
147            schema,
148            use_task: true,
149            unknown_statistics: false,
150            cache: Arc::new(cache),
151        }
152    }
153
154    /// If `use_task` is true (the default) then the batches are sent
155    /// back using a separate task to ensure the underlying stream is
156    /// not immediately ready
157    pub fn with_use_task(mut self, use_task: bool) -> Self {
158        self.use_task = use_task;
159        self
160    }
161
162    /// Report unknown statistics rather than computing them from `data`.
163    ///
164    /// By default statistics are derived from `data`, which propagates any
165    /// planted errors when statistics are requested during planning (for
166    /// example when a parent node computes its properties). Use this when a
167    /// planted error should only surface at execution time.
168    pub fn with_unknown_statistics(mut self) -> Self {
169        self.unknown_statistics = true;
170        self
171    }
172
173    /// This function creates the cache object that stores the plan properties such as schema, equivalence properties, ordering, partitioning, etc.
174    fn compute_properties(schema: SchemaRef) -> PlanProperties {
175        PlanProperties::new(
176            EquivalenceProperties::new(schema),
177            Partitioning::UnknownPartitioning(1),
178            EmissionType::Incremental,
179            Boundedness::Bounded,
180        )
181    }
182}
183
184impl DisplayAs for MockExec {
185    fn fmt_as(
186        &self,
187        t: DisplayFormatType,
188        f: &mut std::fmt::Formatter,
189    ) -> std::fmt::Result {
190        match t {
191            DisplayFormatType::Default | DisplayFormatType::Verbose => {
192                write!(f, "MockExec")
193            }
194            DisplayFormatType::TreeRender => {
195                // TODO: collect info
196                write!(f, "")
197            }
198        }
199    }
200}
201
202impl ExecutionPlan for MockExec {
203    fn name(&self) -> &'static str {
204        Self::static_name()
205    }
206
207    fn properties(&self) -> &Arc<PlanProperties> {
208        &self.cache
209    }
210
211    fn children(&self) -> Vec<&Arc<dyn ExecutionPlan>> {
212        vec![]
213    }
214
215    fn apply_expressions(
216        &self,
217        _f: &mut dyn FnMut(&Arc<dyn PhysicalExpr>) -> Result<TreeNodeRecursion>,
218    ) -> Result<TreeNodeRecursion> {
219        Ok(TreeNodeRecursion::Continue)
220    }
221
222    fn replace_children(
223        self: Arc<Self>,
224        _: Vec<Arc<dyn ExecutionPlan>>,
225        _: ReplaceChildrenOptions,
226    ) -> Result<Arc<dyn ExecutionPlan>> {
227        unimplemented!()
228    }
229
230    fn with_new_children(
231        self: Arc<Self>,
232        children: Vec<Arc<dyn ExecutionPlan>>,
233    ) -> Result<Arc<dyn ExecutionPlan>> {
234        self.replace_children(
235            children,
236            ReplaceChildrenOptions::new(ChildrenPropertiesMode::Recompute),
237        )
238    }
239
240    /// Returns a stream which yields data
241    fn execute(
242        &self,
243        partition: usize,
244        _context: Arc<TaskContext>,
245    ) -> Result<SendableRecordBatchStream> {
246        assert_eq!(partition, 0);
247
248        // Result doesn't implement clone, so do it ourself
249        let data: Vec<_> = self
250            .data
251            .iter()
252            .map(|r| match r {
253                Ok(batch) => Ok(batch.clone()),
254                Err(e) => Err(clone_error(e)),
255            })
256            .collect();
257
258        if self.use_task {
259            let mut builder = RecordBatchReceiverStream::builder(self.schema(), 2);
260            // send data in order but in a separate task (to ensure
261            // the batches are not available without the stream
262            // yielding).
263            let tx = builder.tx();
264            builder.spawn(async move {
265                for batch in data {
266                    println!("Sending batch via delayed stream");
267                    if let Err(e) = tx.send(batch).await {
268                        println!("ERROR batch via delayed stream: {e}");
269                    }
270                }
271
272                Ok(())
273            });
274            // returned stream simply reads off the rx stream
275            Ok(builder.build())
276        } else {
277            // make an input that will error
278            let stream = futures::stream::iter(data);
279            Ok(Box::pin(RecordBatchStreamAdapter::new(
280                self.schema(),
281                stream,
282            )))
283        }
284    }
285
286    // Errors if one of the batches is an error, unless
287    // `with_unknown_statistics` was used
288    fn statistics_from_inputs(
289        &self,
290        _input_stats: &[Arc<Statistics>],
291        args: &StatisticsArgs,
292    ) -> Result<Arc<Statistics>> {
293        if self.unknown_statistics || args.partition().is_some() {
294            return Ok(Arc::new(Statistics::new_unknown(&self.schema)));
295        }
296        let data: Result<Vec<_>> = self
297            .data
298            .iter()
299            .map(|r| match r {
300                Ok(batch) => Ok(batch.clone()),
301                Err(e) => Err(clone_error(e)),
302            })
303            .collect();
304
305        let data = data?;
306
307        Ok(Arc::new(common::compute_record_batch_statistics(
308            &[data],
309            &self.schema,
310            None,
311        )))
312    }
313}
314
315fn clone_error(e: &DataFusionError) -> DataFusionError {
316    use DataFusionError::*;
317    match e {
318        Execution(msg) => Execution(msg.to_string()),
319        _ => unimplemented!(),
320    }
321}
322
323/// A Mock ExecutionPlan that does not start producing input until a
324/// barrier is called
325#[derive(Debug)]
326pub struct BarrierExec {
327    /// partitions to send back
328    data: Vec<Vec<RecordBatch>>,
329    schema: SchemaRef,
330
331    /// all streams wait on this barrier to produce
332    start_data_barrier: Option<Arc<Barrier>>,
333
334    /// the stream wait for this to return Poll::Ready(None)
335    finish_barrier: Option<Arc<(Barrier, AtomicUsize)>>,
336
337    cache: Arc<PlanProperties>,
338
339    log: bool,
340}
341
342impl BarrierExec {
343    /// Create a new exec with some number of partitions.
344    pub fn new(data: Vec<Vec<RecordBatch>>, schema: SchemaRef) -> Self {
345        // wait for all streams and the input
346        let barrier = Some(Arc::new(Barrier::new(data.len() + 1)));
347        let cache = Self::compute_properties(Arc::clone(&schema), &data);
348        Self {
349            data,
350            schema,
351            start_data_barrier: barrier,
352            cache: Arc::new(cache),
353            finish_barrier: None,
354            log: true,
355        }
356    }
357
358    pub fn with_log(mut self, log: bool) -> Self {
359        self.log = log;
360        self
361    }
362
363    pub fn without_start_barrier(mut self) -> Self {
364        self.start_data_barrier = None;
365        self
366    }
367
368    pub fn with_finish_barrier(mut self) -> Self {
369        let barrier = Arc::new((
370            // wait for all streams and the input
371            Barrier::new(self.data.len() + 1),
372            AtomicUsize::new(0),
373        ));
374
375        self.finish_barrier = Some(barrier);
376        self
377    }
378
379    /// wait until all the input streams and this function is ready
380    pub async fn wait(&self) {
381        let barrier = &self
382            .start_data_barrier
383            .as_ref()
384            .expect("Must only be called when having a start barrier");
385        if self.log {
386            println!("BarrierExec::wait waiting on barrier");
387        }
388        barrier.wait().await;
389        if self.log {
390            println!("BarrierExec::wait done waiting");
391        }
392    }
393
394    pub async fn wait_finish(&self) {
395        let (barrier, _) = &self
396            .finish_barrier
397            .as_deref()
398            .expect("Must only be called when having a finish barrier");
399
400        if self.log {
401            println!("BarrierExec::wait_finish waiting on barrier");
402        }
403        barrier.wait().await;
404        if self.log {
405            println!("BarrierExec::wait_finish done waiting");
406        }
407    }
408
409    /// Return true if the finish barrier has been reached in all partitions
410    pub fn is_finish_barrier_reached(&self) -> bool {
411        let (_, reached_finish) = self
412            .finish_barrier
413            .as_deref()
414            .expect("Must only be called when having finish barrier");
415
416        reached_finish.load(Ordering::Relaxed) == self.data.len()
417    }
418
419    /// This function creates the cache object that stores the plan properties such as schema, equivalence properties, ordering, partitioning, etc.
420    fn compute_properties(
421        schema: SchemaRef,
422        data: &[Vec<RecordBatch>],
423    ) -> PlanProperties {
424        PlanProperties::new(
425            EquivalenceProperties::new(schema),
426            Partitioning::UnknownPartitioning(data.len()),
427            EmissionType::Incremental,
428            Boundedness::Bounded,
429        )
430    }
431}
432
433impl DisplayAs for BarrierExec {
434    fn fmt_as(
435        &self,
436        t: DisplayFormatType,
437        f: &mut std::fmt::Formatter,
438    ) -> std::fmt::Result {
439        match t {
440            DisplayFormatType::Default | DisplayFormatType::Verbose => {
441                write!(f, "BarrierExec")
442            }
443            DisplayFormatType::TreeRender => {
444                // TODO: collect info
445                write!(f, "")
446            }
447        }
448    }
449}
450
451impl ExecutionPlan for BarrierExec {
452    fn name(&self) -> &'static str {
453        Self::static_name()
454    }
455
456    fn properties(&self) -> &Arc<PlanProperties> {
457        &self.cache
458    }
459
460    fn children(&self) -> Vec<&Arc<dyn ExecutionPlan>> {
461        unimplemented!()
462    }
463
464    fn replace_children(
465        self: Arc<Self>,
466        _: Vec<Arc<dyn ExecutionPlan>>,
467        _: ReplaceChildrenOptions,
468    ) -> Result<Arc<dyn ExecutionPlan>> {
469        unimplemented!()
470    }
471
472    fn apply_expressions(
473        &self,
474        _f: &mut dyn FnMut(&Arc<dyn PhysicalExpr>) -> Result<TreeNodeRecursion>,
475    ) -> Result<TreeNodeRecursion> {
476        Ok(TreeNodeRecursion::Continue)
477    }
478
479    fn with_new_children(
480        self: Arc<Self>,
481        children: Vec<Arc<dyn ExecutionPlan>>,
482    ) -> Result<Arc<dyn ExecutionPlan>> {
483        self.replace_children(
484            children,
485            ReplaceChildrenOptions::new(ChildrenPropertiesMode::Recompute),
486        )
487    }
488
489    /// Returns a stream which yields data
490    fn execute(
491        &self,
492        partition: usize,
493        _context: Arc<TaskContext>,
494    ) -> Result<SendableRecordBatchStream> {
495        assert!(partition < self.data.len());
496
497        let mut builder = RecordBatchReceiverStream::builder(self.schema(), 2);
498
499        // task simply sends data in order after barrier is reached
500        let data = self.data[partition].clone();
501        let start_barrier = self.start_data_barrier.as_ref().map(Arc::clone);
502        let finish_barrier = self.finish_barrier.as_ref().map(Arc::clone);
503        let log = self.log;
504        let tx = builder.tx();
505        builder.spawn(async move {
506            if let Some(barrier) = start_barrier {
507                if log {
508                    println!("Partition {partition} waiting on barrier");
509                }
510                barrier.wait().await;
511            }
512            for batch in data {
513                if log {
514                    println!("Partition {partition} sending batch");
515                }
516                if let Err(e) = tx.send(Ok(batch)).await {
517                    println!("ERROR batch via barrier stream stream: {e}");
518                }
519            }
520            if let Some((barrier, reached_finish)) = finish_barrier.as_deref() {
521                if log {
522                    println!("Partition {partition} waiting on finish barrier");
523                }
524                reached_finish.fetch_add(1, Ordering::Relaxed);
525                barrier.wait().await;
526            }
527
528            Ok(())
529        });
530
531        // returned stream simply reads off the rx stream
532        Ok(builder.build())
533    }
534
535    fn statistics_from_inputs(
536        &self,
537        _input_stats: &[Arc<Statistics>],
538        args: &StatisticsArgs,
539    ) -> Result<Arc<Statistics>> {
540        if args.partition().is_some() {
541            return Ok(Arc::new(Statistics::new_unknown(&self.schema)));
542        }
543        Ok(Arc::new(common::compute_record_batch_statistics(
544            &self.data,
545            &self.schema,
546            None,
547        )))
548    }
549}
550
551/// A mock execution plan that errors on a call to execute
552#[derive(Debug)]
553pub struct ErrorExec {
554    cache: Arc<PlanProperties>,
555}
556
557impl Default for ErrorExec {
558    fn default() -> Self {
559        Self::new()
560    }
561}
562
563impl ErrorExec {
564    pub fn new() -> Self {
565        let schema = Arc::new(Schema::new(vec![Field::new(
566            "dummy",
567            DataType::Int64,
568            true,
569        )]));
570        let cache = Self::compute_properties(schema);
571        Self {
572            cache: Arc::new(cache),
573        }
574    }
575
576    /// This function creates the cache object that stores the plan properties such as schema, equivalence properties, ordering, partitioning, etc.
577    fn compute_properties(schema: SchemaRef) -> PlanProperties {
578        PlanProperties::new(
579            EquivalenceProperties::new(schema),
580            Partitioning::UnknownPartitioning(1),
581            EmissionType::Incremental,
582            Boundedness::Bounded,
583        )
584    }
585}
586
587impl DisplayAs for ErrorExec {
588    fn fmt_as(
589        &self,
590        t: DisplayFormatType,
591        f: &mut std::fmt::Formatter,
592    ) -> std::fmt::Result {
593        match t {
594            DisplayFormatType::Default | DisplayFormatType::Verbose => {
595                write!(f, "ErrorExec")
596            }
597            DisplayFormatType::TreeRender => {
598                // TODO: collect info
599                write!(f, "")
600            }
601        }
602    }
603}
604
605impl ExecutionPlan for ErrorExec {
606    fn name(&self) -> &'static str {
607        Self::static_name()
608    }
609
610    fn properties(&self) -> &Arc<PlanProperties> {
611        &self.cache
612    }
613
614    fn children(&self) -> Vec<&Arc<dyn ExecutionPlan>> {
615        unimplemented!()
616    }
617
618    fn replace_children(
619        self: Arc<Self>,
620        _: Vec<Arc<dyn ExecutionPlan>>,
621        _: ReplaceChildrenOptions,
622    ) -> Result<Arc<dyn ExecutionPlan>> {
623        unimplemented!()
624    }
625
626    fn apply_expressions(
627        &self,
628        _f: &mut dyn FnMut(&Arc<dyn PhysicalExpr>) -> Result<TreeNodeRecursion>,
629    ) -> Result<TreeNodeRecursion> {
630        Ok(TreeNodeRecursion::Continue)
631    }
632
633    fn with_new_children(
634        self: Arc<Self>,
635        children: Vec<Arc<dyn ExecutionPlan>>,
636    ) -> Result<Arc<dyn ExecutionPlan>> {
637        self.replace_children(
638            children,
639            ReplaceChildrenOptions::new(ChildrenPropertiesMode::Recompute),
640        )
641    }
642
643    /// Returns a stream which yields data
644    fn execute(
645        &self,
646        partition: usize,
647        _context: Arc<TaskContext>,
648    ) -> Result<SendableRecordBatchStream> {
649        internal_err!("ErrorExec, unsurprisingly, errored in partition {partition}")
650    }
651}
652
653/// A mock execution plan that simply returns the provided statistics
654#[derive(Debug, Clone)]
655pub struct StatisticsExec {
656    stats: Statistics,
657    schema: Arc<Schema>,
658    cache: Arc<PlanProperties>,
659}
660impl StatisticsExec {
661    pub fn new(stats: Statistics, schema: Schema) -> Self {
662        assert_eq!(
663            stats.column_statistics.len(),
664            schema.fields().len(),
665            "if defined, the column statistics vector length should be the number of fields"
666        );
667        let cache = Self::compute_properties(Arc::new(schema.clone()));
668        Self {
669            stats,
670            schema: Arc::new(schema),
671            cache: Arc::new(cache),
672        }
673    }
674
675    /// This function creates the cache object that stores the plan properties such as schema, equivalence properties, ordering, partitioning, etc.
676    fn compute_properties(schema: SchemaRef) -> PlanProperties {
677        PlanProperties::new(
678            EquivalenceProperties::new(schema),
679            Partitioning::UnknownPartitioning(2),
680            EmissionType::Incremental,
681            Boundedness::Bounded,
682        )
683    }
684}
685
686impl DisplayAs for StatisticsExec {
687    fn fmt_as(
688        &self,
689        t: DisplayFormatType,
690        f: &mut std::fmt::Formatter,
691    ) -> std::fmt::Result {
692        match t {
693            DisplayFormatType::Default | DisplayFormatType::Verbose => {
694                write!(
695                    f,
696                    "StatisticsExec: col_count={}, row_count={:?}",
697                    self.schema.fields().len(),
698                    self.stats.num_rows,
699                )
700            }
701            DisplayFormatType::TreeRender => {
702                // TODO: collect info
703                write!(f, "")
704            }
705        }
706    }
707}
708
709impl ExecutionPlan for StatisticsExec {
710    fn name(&self) -> &'static str {
711        Self::static_name()
712    }
713
714    fn properties(&self) -> &Arc<PlanProperties> {
715        &self.cache
716    }
717
718    fn children(&self) -> Vec<&Arc<dyn ExecutionPlan>> {
719        vec![]
720    }
721
722    fn apply_expressions(
723        &self,
724        _f: &mut dyn FnMut(&Arc<dyn PhysicalExpr>) -> Result<TreeNodeRecursion>,
725    ) -> Result<TreeNodeRecursion> {
726        Ok(TreeNodeRecursion::Continue)
727    }
728
729    fn replace_children(
730        self: Arc<Self>,
731        _: Vec<Arc<dyn ExecutionPlan>>,
732        _: ReplaceChildrenOptions,
733    ) -> Result<Arc<dyn ExecutionPlan>> {
734        Ok(self)
735    }
736
737    fn with_new_children(
738        self: Arc<Self>,
739        children: Vec<Arc<dyn ExecutionPlan>>,
740    ) -> Result<Arc<dyn ExecutionPlan>> {
741        self.replace_children(
742            children,
743            ReplaceChildrenOptions::new(ChildrenPropertiesMode::Recompute),
744        )
745    }
746
747    fn execute(
748        &self,
749        _partition: usize,
750        _context: Arc<TaskContext>,
751    ) -> Result<SendableRecordBatchStream> {
752        unimplemented!("This plan only serves for testing statistics")
753    }
754
755    fn statistics_from_inputs(
756        &self,
757        _input_stats: &[Arc<Statistics>],
758        args: &StatisticsArgs,
759    ) -> Result<Arc<Statistics>> {
760        Ok(Arc::new(if args.partition().is_some() {
761            Statistics::new_unknown(&self.schema)
762        } else {
763            self.stats.clone()
764        }))
765    }
766}
767
768/// Execution plan that emits streams that block forever.
769///
770/// This is useful to test shutdown / cancellation behavior of certain execution plans.
771#[derive(Debug)]
772pub struct BlockingExec {
773    /// Schema that is mocked by this plan.
774    schema: SchemaRef,
775
776    /// Ref-counting helper to check if the plan and the produced stream are still in memory.
777    refs: Arc<()>,
778    cache: Arc<PlanProperties>,
779}
780
781impl BlockingExec {
782    /// Create new [`BlockingExec`] with a give schema and number of partitions.
783    pub fn new(schema: SchemaRef, n_partitions: usize) -> Self {
784        let cache = Self::compute_properties(Arc::clone(&schema), n_partitions);
785        Self {
786            schema,
787            refs: Default::default(),
788            cache: Arc::new(cache),
789        }
790    }
791
792    /// Weak pointer that can be used for ref-counting this execution plan and its streams.
793    ///
794    /// Use [`Weak::strong_count`] to determine if the plan itself and its streams are dropped (should be 0 in that
795    /// case). Note that tokio might take some time to cancel spawned tasks, so you need to wrap this check into a retry
796    /// loop. Use [`assert_strong_count_converges_to_zero`] to archive this.
797    pub fn refs(&self) -> Weak<()> {
798        Arc::downgrade(&self.refs)
799    }
800
801    /// This function creates the cache object that stores the plan properties such as schema, equivalence properties, ordering, partitioning, etc.
802    fn compute_properties(schema: SchemaRef, n_partitions: usize) -> PlanProperties {
803        PlanProperties::new(
804            EquivalenceProperties::new(schema),
805            Partitioning::UnknownPartitioning(n_partitions),
806            EmissionType::Incremental,
807            Boundedness::Bounded,
808        )
809    }
810}
811
812impl DisplayAs for BlockingExec {
813    fn fmt_as(
814        &self,
815        t: DisplayFormatType,
816        f: &mut std::fmt::Formatter,
817    ) -> std::fmt::Result {
818        match t {
819            DisplayFormatType::Default | DisplayFormatType::Verbose => {
820                write!(f, "BlockingExec",)
821            }
822            DisplayFormatType::TreeRender => {
823                // TODO: collect info
824                write!(f, "")
825            }
826        }
827    }
828}
829
830impl ExecutionPlan for BlockingExec {
831    fn name(&self) -> &'static str {
832        Self::static_name()
833    }
834
835    fn properties(&self) -> &Arc<PlanProperties> {
836        &self.cache
837    }
838
839    fn children(&self) -> Vec<&Arc<dyn ExecutionPlan>> {
840        // this is a leaf node and has no children
841        vec![]
842    }
843
844    fn replace_children(
845        self: Arc<Self>,
846        _: Vec<Arc<dyn ExecutionPlan>>,
847        _: ReplaceChildrenOptions,
848    ) -> Result<Arc<dyn ExecutionPlan>> {
849        internal_err!("Children cannot be replaced in {self:?}")
850    }
851
852    fn apply_expressions(
853        &self,
854        _f: &mut dyn FnMut(&Arc<dyn PhysicalExpr>) -> Result<TreeNodeRecursion>,
855    ) -> Result<TreeNodeRecursion> {
856        Ok(TreeNodeRecursion::Continue)
857    }
858
859    fn with_new_children(
860        self: Arc<Self>,
861        children: Vec<Arc<dyn ExecutionPlan>>,
862    ) -> Result<Arc<dyn ExecutionPlan>> {
863        self.replace_children(
864            children,
865            ReplaceChildrenOptions::new(ChildrenPropertiesMode::Recompute),
866        )
867    }
868
869    fn execute(
870        &self,
871        _partition: usize,
872        _context: Arc<TaskContext>,
873    ) -> Result<SendableRecordBatchStream> {
874        Ok(Box::pin(BlockingStream {
875            schema: Arc::clone(&self.schema),
876            _refs: Arc::clone(&self.refs),
877        }))
878    }
879}
880
881/// A [`RecordBatchStream`] that is pending forever.
882#[derive(Debug)]
883pub struct BlockingStream {
884    /// Schema mocked by this stream.
885    schema: SchemaRef,
886
887    /// Ref-counting helper to check if the stream are still in memory.
888    _refs: Arc<()>,
889}
890
891impl Stream for BlockingStream {
892    type Item = Result<RecordBatch>;
893
894    fn poll_next(
895        self: Pin<&mut Self>,
896        _cx: &mut Context<'_>,
897    ) -> Poll<Option<Self::Item>> {
898        Poll::Pending
899    }
900}
901
902impl RecordBatchStream for BlockingStream {
903    fn schema(&self) -> SchemaRef {
904        Arc::clone(&self.schema)
905    }
906}
907
908/// Asserts that the strong count of the given [`Weak`] pointer converges to zero.
909///
910/// This might take a while but has a timeout.
911pub async fn assert_strong_count_converges_to_zero<T>(refs: Weak<T>) {
912    tokio::time::timeout(std::time::Duration::from_secs(10), async {
913        loop {
914            if dbg!(Weak::strong_count(&refs)) == 0 {
915                break;
916            }
917            tokio::time::sleep(std::time::Duration::from_millis(10)).await;
918        }
919    })
920    .await
921    .unwrap();
922}
923
924/// Execution plan that emits streams that panics.
925///
926/// This is useful to test panic handling of certain execution plans.
927#[derive(Debug)]
928pub struct PanicExec {
929    /// Schema that is mocked by this plan.
930    schema: SchemaRef,
931
932    /// Number of output partitions. Each partition will produce this
933    /// many empty output record batches prior to panicking
934    batches_until_panics: Vec<usize>,
935    cache: Arc<PlanProperties>,
936}
937
938impl PanicExec {
939    /// Create new [`PanicExec`] with a give schema and number of
940    /// partitions, which will each panic immediately.
941    pub fn new(schema: SchemaRef, n_partitions: usize) -> Self {
942        let batches_until_panics = vec![0; n_partitions];
943        let cache = Self::compute_properties(Arc::clone(&schema), &batches_until_panics);
944        Self {
945            schema,
946            batches_until_panics,
947            cache: Arc::new(cache),
948        }
949    }
950
951    /// Set the number of batches prior to panic for a partition
952    pub fn with_partition_panic(mut self, partition: usize, count: usize) -> Self {
953        self.batches_until_panics[partition] = count;
954        self
955    }
956
957    /// This function creates the cache object that stores the plan properties such as schema, equivalence properties, ordering, partitioning, etc.
958    fn compute_properties(
959        schema: SchemaRef,
960        batches_until_panics: &[usize],
961    ) -> PlanProperties {
962        let num_partitions = batches_until_panics.len();
963        PlanProperties::new(
964            EquivalenceProperties::new(schema),
965            Partitioning::UnknownPartitioning(num_partitions),
966            EmissionType::Incremental,
967            Boundedness::Bounded,
968        )
969    }
970}
971
972impl DisplayAs for PanicExec {
973    fn fmt_as(
974        &self,
975        t: DisplayFormatType,
976        f: &mut std::fmt::Formatter,
977    ) -> std::fmt::Result {
978        match t {
979            DisplayFormatType::Default | DisplayFormatType::Verbose => {
980                write!(f, "PanicExec",)
981            }
982            DisplayFormatType::TreeRender => {
983                // TODO: collect info
984                write!(f, "")
985            }
986        }
987    }
988}
989
990impl ExecutionPlan for PanicExec {
991    fn name(&self) -> &'static str {
992        Self::static_name()
993    }
994
995    fn properties(&self) -> &Arc<PlanProperties> {
996        &self.cache
997    }
998
999    fn children(&self) -> Vec<&Arc<dyn ExecutionPlan>> {
1000        // this is a leaf node and has no children
1001        vec![]
1002    }
1003
1004    fn apply_expressions(
1005        &self,
1006        _f: &mut dyn FnMut(&Arc<dyn PhysicalExpr>) -> Result<TreeNodeRecursion>,
1007    ) -> Result<TreeNodeRecursion> {
1008        Ok(TreeNodeRecursion::Continue)
1009    }
1010
1011    fn replace_children(
1012        self: Arc<Self>,
1013        _: Vec<Arc<dyn ExecutionPlan>>,
1014        _: ReplaceChildrenOptions,
1015    ) -> Result<Arc<dyn ExecutionPlan>> {
1016        internal_err!("Children cannot be replaced in {:?}", self)
1017    }
1018
1019    fn with_new_children(
1020        self: Arc<Self>,
1021        children: Vec<Arc<dyn ExecutionPlan>>,
1022    ) -> Result<Arc<dyn ExecutionPlan>> {
1023        self.replace_children(
1024            children,
1025            ReplaceChildrenOptions::new(ChildrenPropertiesMode::Recompute),
1026        )
1027    }
1028
1029    fn execute(
1030        &self,
1031        partition: usize,
1032        _context: Arc<TaskContext>,
1033    ) -> Result<SendableRecordBatchStream> {
1034        Ok(Box::pin(PanicStream {
1035            partition,
1036            batches_until_panic: self.batches_until_panics[partition],
1037            schema: Arc::clone(&self.schema),
1038            ready: false,
1039        }))
1040    }
1041}
1042
1043/// A [`RecordBatchStream`] that yields every other batch and panics
1044/// after `batches_until_panic` batches have been produced.
1045///
1046/// Useful for testing the behavior of streams on panic
1047#[derive(Debug)]
1048struct PanicStream {
1049    /// Which partition was this
1050    partition: usize,
1051    /// How may batches will be produced until panic
1052    batches_until_panic: usize,
1053    /// Schema mocked by this stream.
1054    schema: SchemaRef,
1055    /// Should we return ready ?
1056    ready: bool,
1057}
1058
1059impl Stream for PanicStream {
1060    type Item = Result<RecordBatch>;
1061
1062    fn poll_next(
1063        mut self: Pin<&mut Self>,
1064        cx: &mut Context<'_>,
1065    ) -> Poll<Option<Self::Item>> {
1066        if self.batches_until_panic > 0 {
1067            if self.ready {
1068                self.batches_until_panic -= 1;
1069                self.ready = false;
1070                let batch = RecordBatch::new_empty(Arc::clone(&self.schema));
1071                return Poll::Ready(Some(Ok(batch)));
1072            } else {
1073                self.ready = true;
1074                // get called again
1075                cx.waker().wake_by_ref();
1076                return Poll::Pending;
1077            }
1078        }
1079        panic!("PanickingStream did panic: {}", self.partition)
1080    }
1081}
1082
1083impl RecordBatchStream for PanicStream {
1084    fn schema(&self) -> SchemaRef {
1085        Arc::clone(&self.schema)
1086    }
1087}