1use std::sync::Arc;
21
22use crate::common::spawn_buffered;
23use crate::limit::LimitStream;
24use crate::metrics::{BaselineMetrics, ExecutionPlanMetricsSet, MetricsSet};
25use crate::projection::{ProjectionExec, make_with_child, update_ordering};
26use crate::sorts::streaming_merge::StreamingMergeBuilder;
27use crate::statistics::{ChildStats, StatisticsArgs};
28use crate::{
29 ChildrenPropertiesMode, DisplayAs, DisplayFormatType, Distribution, ExecutionPlan,
30 ExecutionPlanProperties, Partitioning, PlanProperties, ReplaceChildrenOptions,
31 SendableRecordBatchStream, Statistics, validate_child_count,
32};
33
34use datafusion_common::tree_node::TreeNodeRecursion;
35use datafusion_common::{Result, assert_eq_or_internal_err, internal_err};
36use datafusion_execution::TaskContext;
37use datafusion_execution::memory_pool::MemoryConsumer;
38use datafusion_physical_expr::PhysicalExpr;
39use datafusion_physical_expr_common::sort_expr::{LexOrdering, OrderingRequirements};
40
41use crate::execution_plan::{
42 CardinalityEffect, EvaluationType, SchedulingType, replace_children_if_necessary,
43};
44use log::{debug, trace};
45
46#[derive(Debug, Clone)]
91pub struct SortPreservingMergeExec {
92 input: Arc<dyn ExecutionPlan>,
94 expr: LexOrdering,
96 metrics: ExecutionPlanMetricsSet,
98 fetch: Option<usize>,
100 cache: Arc<PlanProperties>,
102 enable_round_robin_repartition: bool,
106}
107
108impl SortPreservingMergeExec {
109 pub fn new(expr: LexOrdering, input: Arc<dyn ExecutionPlan>) -> Self {
111 let cache = Self::compute_properties(&input, expr.clone());
112 Self {
113 input,
114 expr,
115 metrics: ExecutionPlanMetricsSet::new(),
116 fetch: None,
117 cache: Arc::new(cache),
118 enable_round_robin_repartition: true,
119 }
120 }
121
122 pub fn with_fetch(mut self, fetch: Option<usize>) -> Self {
124 self.fetch = fetch;
125 self
126 }
127
128 pub fn with_round_robin_repartition(
138 mut self,
139 enable_round_robin_repartition: bool,
140 ) -> Self {
141 self.enable_round_robin_repartition = enable_round_robin_repartition;
142 self
143 }
144
145 pub fn input(&self) -> &Arc<dyn ExecutionPlan> {
147 &self.input
148 }
149
150 pub fn expr(&self) -> &LexOrdering {
152 &self.expr
153 }
154
155 pub fn fetch(&self) -> Option<usize> {
157 self.fetch
158 }
159
160 fn compute_properties(
163 input: &Arc<dyn ExecutionPlan>,
164 ordering: LexOrdering,
165 ) -> PlanProperties {
166 let input_partitions = input.output_partitioning().partition_count();
167 let (drive, scheduling) = if input_partitions > 1 {
168 (EvaluationType::Eager, SchedulingType::Cooperative)
169 } else {
170 (
171 input.properties().evaluation_type,
172 input.properties().scheduling_type,
173 )
174 };
175
176 let mut eq_properties = input.equivalence_properties().clone();
177 eq_properties.clear_per_partition_constants();
178 eq_properties.add_ordering(ordering);
179 PlanProperties::new(
180 eq_properties, Partitioning::UnknownPartitioning(1), input.pipeline_behavior(), input.boundedness(), )
185 .with_evaluation_type(drive)
186 .with_scheduling_type(scheduling)
187 }
188}
189
190impl DisplayAs for SortPreservingMergeExec {
191 fn fmt_as(
192 &self,
193 t: DisplayFormatType,
194 f: &mut std::fmt::Formatter,
195 ) -> std::fmt::Result {
196 match t {
197 DisplayFormatType::Default | DisplayFormatType::Verbose => {
198 write!(f, "SortPreservingMergeExec: [{}]", self.expr)?;
199 if let Some(fetch) = self.fetch {
200 write!(f, ", fetch={fetch}")?;
201 };
202
203 Ok(())
204 }
205 DisplayFormatType::TreeRender => {
206 if let Some(fetch) = self.fetch {
207 writeln!(f, "limit={fetch}")?;
208 };
209
210 for (i, e) in self.expr().iter().enumerate() {
211 e.fmt_sql(f)?;
212 if i != self.expr().len() - 1 {
213 write!(f, ", ")?;
214 }
215 }
216
217 Ok(())
218 }
219 }
220 }
221}
222
223impl ExecutionPlan for SortPreservingMergeExec {
224 fn name(&self) -> &'static str {
225 "SortPreservingMergeExec"
226 }
227
228 fn properties(&self) -> &Arc<PlanProperties> {
230 &self.cache
231 }
232
233 fn fetch(&self) -> Option<usize> {
234 self.fetch
235 }
236
237 fn with_fetch(&self, limit: Option<usize>) -> Option<Arc<dyn ExecutionPlan>> {
239 Some(Arc::new(Self {
240 input: Arc::clone(&self.input),
241 expr: self.expr.clone(),
242 metrics: self.metrics.clone(),
243 fetch: limit,
244 cache: Arc::clone(&self.cache),
245 enable_round_robin_repartition: self.enable_round_robin_repartition,
246 }))
247 }
248
249 fn with_preserve_order(
250 &self,
251 preserve_order: bool,
252 ) -> Option<Arc<dyn ExecutionPlan>> {
253 self.input
254 .with_preserve_order(preserve_order)
255 .and_then(|new_input| {
256 replace_children_if_necessary(Arc::new(self.clone()), vec![new_input])
257 .ok()
258 })
259 }
260
261 fn required_input_distribution(&self) -> Vec<Distribution> {
262 self.input_distribution_requirements().into_per_child()
263 }
264
265 fn input_distribution_requirements(&self) -> crate::InputDistributionRequirements {
266 crate::InputDistributionRequirements::new(vec![
267 Distribution::UnspecifiedDistribution,
268 ])
269 }
270
271 fn benefits_from_input_partitioning(&self) -> Vec<bool> {
272 vec![false]
273 }
274
275 fn required_input_ordering(&self) -> Vec<Option<OrderingRequirements>> {
276 vec![Some(OrderingRequirements::from(self.expr.clone()))]
277 }
278
279 fn maintains_input_order(&self) -> Vec<bool> {
280 vec![true]
281 }
282
283 fn children(&self) -> Vec<&Arc<dyn ExecutionPlan>> {
284 vec![&self.input]
285 }
286
287 fn apply_expressions(
288 &self,
289 f: &mut dyn FnMut(&Arc<dyn PhysicalExpr>) -> Result<TreeNodeRecursion>,
290 ) -> Result<TreeNodeRecursion> {
291 crate::apply_expression_roots(
292 self.expr.iter().map(|sort_expr| &sort_expr.expr),
293 f,
294 )
295 }
296
297 fn replace_children(
298 self: Arc<Self>,
299 mut children: Vec<Arc<dyn ExecutionPlan>>,
300 options: ReplaceChildrenOptions,
301 ) -> Result<Arc<dyn ExecutionPlan>> {
302 validate_child_count!(self, children);
303 match options.children_properties {
304 ChildrenPropertiesMode::Keep => Ok(Arc::new(Self {
305 input: children.swap_remove(0),
306 metrics: ExecutionPlanMetricsSet::new(),
307 ..Self::clone(&*self)
308 })),
309 ChildrenPropertiesMode::Recompute => Ok(Arc::new(
310 SortPreservingMergeExec::new(self.expr.clone(), children.swap_remove(0))
311 .with_fetch(self.fetch),
312 )),
313 }
314 }
315
316 fn with_new_children(
317 self: Arc<Self>,
318 children: Vec<Arc<dyn ExecutionPlan>>,
319 ) -> Result<Arc<dyn ExecutionPlan>> {
320 self.replace_children(
321 children,
322 ReplaceChildrenOptions::new(ChildrenPropertiesMode::Recompute),
323 )
324 }
325
326 fn with_new_children_and_same_properties(
327 self: Arc<Self>,
328 children: Vec<Arc<dyn ExecutionPlan>>,
329 ) -> Result<Arc<dyn ExecutionPlan>> {
330 self.replace_children(
331 children,
332 ReplaceChildrenOptions::new(ChildrenPropertiesMode::Keep),
333 )
334 }
335
336 fn execute(
337 &self,
338 partition: usize,
339 context: Arc<TaskContext>,
340 ) -> Result<SendableRecordBatchStream> {
341 trace!("Start SortPreservingMergeExec::execute for partition: {partition}");
342 assert_eq_or_internal_err!(
343 partition,
344 0,
345 "SortPreservingMergeExec invalid partition {partition}"
346 );
347
348 let input_partitions = self.input.output_partitioning().partition_count();
349 trace!(
350 "Number of input partitions of SortPreservingMergeExec::execute: {input_partitions}"
351 );
352 let schema = self.schema();
353
354 let reservation =
355 MemoryConsumer::new(format!("SortPreservingMergeExec[{partition}]"))
356 .register(&context.runtime_env().memory_pool);
357
358 match input_partitions {
359 0 => internal_err!(
360 "SortPreservingMergeExec requires at least one input partition"
361 ),
362 1 => match self.fetch {
363 Some(fetch) => {
364 let stream = self.input.execute(0, context)?;
365 debug!(
366 "Done getting stream for SortPreservingMergeExec::execute with 1 input with {fetch}"
367 );
368 Ok(Box::pin(LimitStream::new(
369 stream,
370 0,
371 Some(fetch),
372 BaselineMetrics::new(&self.metrics, partition),
373 )))
374 }
375 None => {
376 let stream = self.input.execute(0, context);
377 debug!(
378 "Done getting stream for SortPreservingMergeExec::execute with 1 input without fetch"
379 );
380 stream
381 }
382 },
383 _ => {
384 let receivers = (0..input_partitions)
385 .map(|partition| {
386 let stream =
387 self.input.execute(partition, Arc::clone(&context))?;
388 Ok(spawn_buffered(stream, 1))
389 })
390 .collect::<Result<_>>()?;
391
392 debug!(
393 "Done setting up sender-receiver for SortPreservingMergeExec::execute"
394 );
395
396 let result = StreamingMergeBuilder::new()
397 .with_streams(receivers)
398 .with_schema(schema)
399 .with_expressions(&self.expr)
400 .with_metrics(BaselineMetrics::new(&self.metrics, partition))
401 .with_batch_size(context.session_config().batch_size())
402 .with_fetch(self.fetch)
403 .with_reservation(reservation)
404 .with_round_robin_tie_breaker(self.enable_round_robin_repartition)
405 .build()?;
406
407 debug!(
408 "Got stream result from SortPreservingMergeStream::new_from_receivers"
409 );
410
411 Ok(result)
412 }
413 }
414 }
415
416 fn metrics(&self) -> Option<MetricsSet> {
417 Some(self.metrics.clone_inner())
418 }
419
420 fn child_stats_requests(&self, _partition: Option<usize>) -> Vec<ChildStats> {
421 vec![ChildStats::At(None)]
422 }
423
424 fn statistics_from_inputs(
425 &self,
426 input_stats: &[Arc<Statistics>],
427 _args: &StatisticsArgs,
428 ) -> Result<Arc<Statistics>> {
429 let stats = input_stats[0].as_ref().clone();
430 Ok(Arc::new(stats.with_fetch(self.fetch, 0, 1)?))
431 }
432
433 fn cardinality_effect(&self) -> CardinalityEffect {
434 if self.fetch.is_none() {
435 CardinalityEffect::Equal
436 } else {
437 CardinalityEffect::LowerEqual
438 }
439 }
440
441 fn supports_limit_pushdown(&self) -> bool {
442 true
443 }
444
445 fn try_swapping_with_projection(
449 &self,
450 projection: &ProjectionExec,
451 ) -> Result<Option<Arc<dyn ExecutionPlan>>> {
452 if projection.expr().len() >= projection.input().schema().fields().len() {
454 return Ok(None);
455 }
456
457 let Some(updated_exprs) = update_ordering(self.expr.clone(), projection.expr())?
458 else {
459 return Ok(None);
460 };
461
462 Ok(Some(Arc::new(
463 SortPreservingMergeExec::new(
464 updated_exprs,
465 make_with_child(projection, self.input())?,
466 )
467 .with_fetch(self.fetch()),
468 )))
469 }
470 #[cfg(feature = "proto")]
471 fn try_to_proto(
472 &self,
473 ctx: &crate::proto::ExecutionPlanEncodeCtx<'_>,
474 ) -> Result<Option<datafusion_proto_models::protobuf::PhysicalPlanNode>> {
475 use datafusion_proto_models::protobuf;
476 let input = ctx.encode_child(self.input())?;
477 let expr = self
478 .expr()
479 .iter()
480 .map(|e| {
481 Ok(protobuf::PhysicalExprNode {
482 expr_id: None,
483 expr_type: Some(protobuf::physical_expr_node::ExprType::Sort(
484 Box::new(protobuf::PhysicalSortExprNode {
485 expr: Some(Box::new(ctx.encode_expr(&e.expr)?)),
486 asc: !e.options.descending,
487 nulls_first: e.options.nulls_first,
488 }),
489 )),
490 })
491 })
492 .collect::<Result<Vec<_>>>()?;
493 Ok(Some(protobuf::PhysicalPlanNode {
494 physical_plan_type: Some(
495 protobuf::physical_plan_node::PhysicalPlanType::SortPreservingMerge(
496 Box::new(protobuf::SortPreservingMergeExecNode {
497 input: Some(Box::new(input)),
498 expr,
499 fetch: self.fetch().map(|f| f as i64).unwrap_or(-1),
500 }),
501 ),
502 ),
503 }))
504 }
505}
506
507#[cfg(feature = "proto")]
508impl SortPreservingMergeExec {
509 pub fn try_from_proto(
510 node: &datafusion_proto_models::protobuf::PhysicalPlanNode,
511 ctx: &crate::proto::ExecutionPlanDecodeCtx<'_>,
512 ) -> Result<Arc<dyn ExecutionPlan>> {
513 use arrow::compute::SortOptions;
514 use datafusion_physical_expr_common::sort_expr::PhysicalSortExpr;
515 use datafusion_proto_models::protobuf;
516 let spm = crate::expect_plan_variant!(
517 node,
518 protobuf::physical_plan_node::PhysicalPlanType::SortPreservingMerge,
519 "SortPreservingMergeExec",
520 );
521 let input = ctx.decode_required_child(
522 spm.input.as_deref(),
523 "SortPreservingMergeExec",
524 "input",
525 )?;
526 let input_schema = input.schema();
527 let exprs = spm
528 .expr
529 .iter()
530 .map(|e| {
531 let sort = match &e.expr_type {
532 Some(protobuf::physical_expr_node::ExprType::Sort(s)) => s,
533 _ => {
534 return internal_err!(
535 "SortPreservingMergeExec expression is not a sort expression"
536 );
537 }
538 };
539 let expr = ctx.decode_required_expr(
540 sort.expr.as_deref(),
541 input_schema.as_ref(),
542 "SortPreservingMergeExec",
543 "sort expression",
544 )?;
545 Ok(PhysicalSortExpr {
546 expr,
547 options: SortOptions {
548 descending: !sort.asc,
549 nulls_first: sort.nulls_first,
550 },
551 })
552 })
553 .collect::<Result<Vec<_>>>()?;
554 let Some(ordering) = LexOrdering::new(exprs) else {
555 return internal_err!("SortPreservingMergeExec requires an ordering");
556 };
557 let fetch = (spm.fetch >= 0).then_some(spm.fetch as usize);
558 Ok(Arc::new(
559 SortPreservingMergeExec::new(ordering, input).with_fetch(fetch),
560 ))
561 }
562}
563
564#[cfg(test)]
565mod tests {
566 use std::collections::HashSet;
567 use std::fmt::Formatter;
568 use std::pin::Pin;
569 use std::sync::Mutex;
570 use std::task::{Context, Poll, Waker, ready};
571 use std::time::Duration;
572
573 use super::*;
574 use crate::coalesce_partitions::CoalescePartitionsExec;
575 use crate::execution_plan::{Boundedness, EmissionType};
576 use crate::expressions::col;
577 use crate::metrics::{MetricValue, Timestamp};
578 use crate::repartition::RepartitionExec;
579 use crate::sorts::sort::SortExec;
580 use crate::statistics::StatisticsContext;
581 use crate::stream::RecordBatchReceiverStream;
582 use crate::test::TestMemoryExec;
583 use crate::test::exec::{
584 BlockingExec, StatisticsExec, assert_strong_count_converges_to_zero,
585 };
586 use crate::test::{self, assert_is_pending, make_partition};
587 use crate::{collect, common};
588
589 use arrow::array::{
590 ArrayRef, Int32Array, Int64Array, RecordBatch, StringArray,
591 TimestampNanosecondArray,
592 };
593 use arrow::compute::SortOptions;
594 use arrow::datatypes::{DataType, Field, Schema, SchemaRef};
595 use datafusion_common::stats::Precision;
596 use datafusion_common::test_util::batches_to_string;
597 use datafusion_common::{ColumnStatistics, assert_batches_eq, exec_err};
598 use datafusion_common_runtime::SpawnedTask;
599 use datafusion_execution::RecordBatchStream;
600 use datafusion_execution::config::SessionConfig;
601 use datafusion_execution::runtime_env::RuntimeEnvBuilder;
602 use datafusion_physical_expr::EquivalenceProperties;
603 use datafusion_physical_expr::expressions::Column;
604 use datafusion_physical_expr_common::physical_expr::PhysicalExpr;
605 use datafusion_physical_expr_common::sort_expr::PhysicalSortExpr;
606
607 use futures::{FutureExt, Stream, StreamExt};
608 use insta::assert_snapshot;
609 use tokio::time::timeout;
610
611 fn generate_task_ctx_for_round_robin_tie_breaker(
614 target_batch_size: usize,
615 ) -> Result<Arc<TaskContext>> {
616 let runtime = RuntimeEnvBuilder::new()
617 .with_memory_limit(20_000_000, 1.0)
618 .build_arc()?;
619 let mut config = SessionConfig::new();
620 config.options_mut().execution.batch_size =
621 datafusion_common::config::ConfigNonZeroUsize::try_new(target_batch_size)?;
622 let task_ctx = TaskContext::default()
623 .with_runtime(runtime)
624 .with_session_config(config);
625 Ok(Arc::new(task_ctx))
626 }
627
628 fn generate_spm_for_round_robin_tie_breaker(
631 enable_round_robin_repartition: bool,
632 ) -> Result<Arc<SortPreservingMergeExec>> {
633 let row_size = 12500;
634 let a: ArrayRef = Arc::new(Int32Array::from(vec![1; row_size]));
635 let b: ArrayRef = Arc::new(StringArray::from_iter(vec![Some("a"); row_size]));
636 let c: ArrayRef = Arc::new(Int64Array::from_iter(vec![0; row_size]));
637 let rb = RecordBatch::try_from_iter(vec![("a", a), ("b", b), ("c", c)])?;
638 let schema = rb.schema();
639
640 let rbs = std::iter::repeat_n(rb, 1024).collect::<Vec<_>>();
641 let sort = [
642 PhysicalSortExpr {
643 expr: col("b", &schema)?,
644 options: Default::default(),
645 },
646 PhysicalSortExpr {
647 expr: col("c", &schema)?,
648 options: Default::default(),
649 },
650 ]
651 .into();
652
653 let repartition_exec = RepartitionExec::try_new(
654 TestMemoryExec::try_new_exec(&[rbs], schema, None)?,
655 Partitioning::RoundRobinBatch(2),
656 )?;
657 let spm = SortPreservingMergeExec::new(sort, Arc::new(repartition_exec))
658 .with_round_robin_repartition(enable_round_robin_repartition);
659 Ok(Arc::new(spm))
660 }
661
662 #[test]
663 fn test_fetch_caps_statistics() -> Result<()> {
664 let schema = Schema::new(vec![Field::new("a", DataType::Int32, false)]);
665 let input = Arc::new(StatisticsExec::new(
666 Statistics {
667 num_rows: Precision::Exact(1_000),
668 total_byte_size: Precision::Exact(8_000),
669 column_statistics: vec![ColumnStatistics::new_unknown()],
670 },
671 schema.clone(),
672 ));
673 let sort = [PhysicalSortExpr::new_default(Arc::new(Column::new("a", 0)))].into();
674
675 let spm = SortPreservingMergeExec::new(sort, input).with_fetch(Some(1));
676 let statistics =
677 StatisticsContext::new().compute(&spm, &StatisticsArgs::new())?;
678
679 assert_eq!(statistics.num_rows, Precision::Exact(1));
680 assert_eq!(statistics.total_byte_size, Precision::Inexact(8));
681 assert!(matches!(
682 spm.cardinality_effect(),
683 CardinalityEffect::LowerEqual
684 ));
685 Ok(())
686 }
687
688 #[test]
689 fn test_no_fetch_preserves_statistics() -> Result<()> {
690 let schema = Schema::new(vec![Field::new("a", DataType::Int32, false)]);
691 let input_stats = Statistics {
692 num_rows: Precision::Absent,
693 total_byte_size: Precision::Exact(8_000),
694 column_statistics: vec![ColumnStatistics::new_unknown()],
695 };
696 let input = Arc::new(StatisticsExec::new(input_stats.clone(), schema.clone()));
697 let sort = [PhysicalSortExpr::new_default(Arc::new(Column::new("a", 0)))].into();
698
699 let spm = SortPreservingMergeExec::new(sort, input);
700 let statistics =
701 StatisticsContext::new().compute(&spm, &StatisticsArgs::new())?;
702
703 assert_eq!(*statistics, input_stats);
704 assert!(matches!(spm.cardinality_effect(), CardinalityEffect::Equal));
705 Ok(())
706 }
707
708 #[tokio::test(flavor = "multi_thread")]
714 async fn test_round_robin_tie_breaker_success() -> Result<()> {
715 let target_batch_size = 12500;
716 let task_ctx = generate_task_ctx_for_round_robin_tie_breaker(target_batch_size)?;
717 let spm = generate_spm_for_round_robin_tie_breaker(true)?;
718 let _collected = collect(spm, task_ctx).await?;
719 Ok(())
720 }
721
722 #[tokio::test(flavor = "multi_thread")]
728 async fn test_round_robin_tie_breaker_fail() -> Result<()> {
729 let task_ctx = generate_task_ctx_for_round_robin_tie_breaker(8192)?;
730 let spm = generate_spm_for_round_robin_tie_breaker(false)?;
731 let _err = collect(spm, task_ctx).await.unwrap_err();
732 Ok(())
733 }
734
735 #[tokio::test]
736 async fn test_merge_interleave() {
737 let task_ctx = Arc::new(TaskContext::default());
738 let a: ArrayRef = Arc::new(Int32Array::from(vec![1, 2, 7, 9, 3]));
739 let b: ArrayRef = Arc::new(StringArray::from_iter(vec![
740 Some("a"),
741 Some("c"),
742 Some("e"),
743 Some("g"),
744 Some("j"),
745 ]));
746 let c: ArrayRef = Arc::new(TimestampNanosecondArray::from(vec![8, 7, 6, 5, 8]));
747 let b1 = RecordBatch::try_from_iter(vec![("a", a), ("b", b), ("c", c)]).unwrap();
748
749 let a: ArrayRef = Arc::new(Int32Array::from(vec![10, 20, 70, 90, 30]));
750 let b: ArrayRef = Arc::new(StringArray::from_iter(vec![
751 Some("b"),
752 Some("d"),
753 Some("f"),
754 Some("h"),
755 Some("j"),
756 ]));
757 let c: ArrayRef = Arc::new(TimestampNanosecondArray::from(vec![4, 6, 2, 2, 6]));
758 let b2 = RecordBatch::try_from_iter(vec![("a", a), ("b", b), ("c", c)]).unwrap();
759
760 _test_merge(
761 &[vec![b1], vec![b2]],
762 &[
763 "+----+---+-------------------------------+",
764 "| a | b | c |",
765 "+----+---+-------------------------------+",
766 "| 1 | a | 1970-01-01T00:00:00.000000008 |",
767 "| 10 | b | 1970-01-01T00:00:00.000000004 |",
768 "| 2 | c | 1970-01-01T00:00:00.000000007 |",
769 "| 20 | d | 1970-01-01T00:00:00.000000006 |",
770 "| 7 | e | 1970-01-01T00:00:00.000000006 |",
771 "| 70 | f | 1970-01-01T00:00:00.000000002 |",
772 "| 9 | g | 1970-01-01T00:00:00.000000005 |",
773 "| 90 | h | 1970-01-01T00:00:00.000000002 |",
774 "| 30 | j | 1970-01-01T00:00:00.000000006 |", "| 3 | j | 1970-01-01T00:00:00.000000008 |",
776 "+----+---+-------------------------------+",
777 ],
778 task_ctx,
779 )
780 .await;
781 }
782
783 #[tokio::test]
784 async fn test_merge_some_overlap() {
785 let task_ctx = Arc::new(TaskContext::default());
786 let a: ArrayRef = Arc::new(Int32Array::from(vec![1, 2, 7, 9, 3]));
787 let b: ArrayRef = Arc::new(StringArray::from_iter(vec![
788 Some("a"),
789 Some("b"),
790 Some("c"),
791 Some("d"),
792 Some("e"),
793 ]));
794 let c: ArrayRef = Arc::new(TimestampNanosecondArray::from(vec![8, 7, 6, 5, 8]));
795 let b1 = RecordBatch::try_from_iter(vec![("a", a), ("b", b), ("c", c)]).unwrap();
796
797 let a: ArrayRef = Arc::new(Int32Array::from(vec![70, 90, 30, 100, 110]));
798 let b: ArrayRef = Arc::new(StringArray::from_iter(vec![
799 Some("c"),
800 Some("d"),
801 Some("e"),
802 Some("f"),
803 Some("g"),
804 ]));
805 let c: ArrayRef = Arc::new(TimestampNanosecondArray::from(vec![4, 6, 2, 2, 6]));
806 let b2 = RecordBatch::try_from_iter(vec![("a", a), ("b", b), ("c", c)]).unwrap();
807
808 _test_merge(
809 &[vec![b1], vec![b2]],
810 &[
811 "+-----+---+-------------------------------+",
812 "| a | b | c |",
813 "+-----+---+-------------------------------+",
814 "| 1 | a | 1970-01-01T00:00:00.000000008 |",
815 "| 2 | b | 1970-01-01T00:00:00.000000007 |",
816 "| 70 | c | 1970-01-01T00:00:00.000000004 |",
817 "| 7 | c | 1970-01-01T00:00:00.000000006 |",
818 "| 9 | d | 1970-01-01T00:00:00.000000005 |",
819 "| 90 | d | 1970-01-01T00:00:00.000000006 |",
820 "| 30 | e | 1970-01-01T00:00:00.000000002 |",
821 "| 3 | e | 1970-01-01T00:00:00.000000008 |",
822 "| 100 | f | 1970-01-01T00:00:00.000000002 |",
823 "| 110 | g | 1970-01-01T00:00:00.000000006 |",
824 "+-----+---+-------------------------------+",
825 ],
826 task_ctx,
827 )
828 .await;
829 }
830
831 #[tokio::test]
832 async fn test_merge_no_overlap() {
833 let task_ctx = Arc::new(TaskContext::default());
834 let a: ArrayRef = Arc::new(Int32Array::from(vec![1, 2, 7, 9, 3]));
835 let b: ArrayRef = Arc::new(StringArray::from_iter(vec![
836 Some("a"),
837 Some("b"),
838 Some("c"),
839 Some("d"),
840 Some("e"),
841 ]));
842 let c: ArrayRef = Arc::new(TimestampNanosecondArray::from(vec![8, 7, 6, 5, 8]));
843 let b1 = RecordBatch::try_from_iter(vec![("a", a), ("b", b), ("c", c)]).unwrap();
844
845 let a: ArrayRef = Arc::new(Int32Array::from(vec![10, 20, 70, 90, 30]));
846 let b: ArrayRef = Arc::new(StringArray::from_iter(vec![
847 Some("f"),
848 Some("g"),
849 Some("h"),
850 Some("i"),
851 Some("j"),
852 ]));
853 let c: ArrayRef = Arc::new(TimestampNanosecondArray::from(vec![4, 6, 2, 2, 6]));
854 let b2 = RecordBatch::try_from_iter(vec![("a", a), ("b", b), ("c", c)]).unwrap();
855
856 _test_merge(
857 &[vec![b1], vec![b2]],
858 &[
859 "+----+---+-------------------------------+",
860 "| a | b | c |",
861 "+----+---+-------------------------------+",
862 "| 1 | a | 1970-01-01T00:00:00.000000008 |",
863 "| 2 | b | 1970-01-01T00:00:00.000000007 |",
864 "| 7 | c | 1970-01-01T00:00:00.000000006 |",
865 "| 9 | d | 1970-01-01T00:00:00.000000005 |",
866 "| 3 | e | 1970-01-01T00:00:00.000000008 |",
867 "| 10 | f | 1970-01-01T00:00:00.000000004 |",
868 "| 20 | g | 1970-01-01T00:00:00.000000006 |",
869 "| 70 | h | 1970-01-01T00:00:00.000000002 |",
870 "| 90 | i | 1970-01-01T00:00:00.000000002 |",
871 "| 30 | j | 1970-01-01T00:00:00.000000006 |",
872 "+----+---+-------------------------------+",
873 ],
874 task_ctx,
875 )
876 .await;
877 }
878
879 #[tokio::test]
880 async fn test_merge_three_partitions() {
881 let task_ctx = Arc::new(TaskContext::default());
882 let a: ArrayRef = Arc::new(Int32Array::from(vec![1, 2, 7, 9, 3]));
883 let b: ArrayRef = Arc::new(StringArray::from_iter(vec![
884 Some("a"),
885 Some("b"),
886 Some("c"),
887 Some("d"),
888 Some("f"),
889 ]));
890 let c: ArrayRef = Arc::new(TimestampNanosecondArray::from(vec![8, 7, 6, 5, 8]));
891 let b1 = RecordBatch::try_from_iter(vec![("a", a), ("b", b), ("c", c)]).unwrap();
892
893 let a: ArrayRef = Arc::new(Int32Array::from(vec![10, 20, 70, 90, 30]));
894 let b: ArrayRef = Arc::new(StringArray::from_iter(vec![
895 Some("e"),
896 Some("g"),
897 Some("h"),
898 Some("i"),
899 Some("j"),
900 ]));
901 let c: ArrayRef =
902 Arc::new(TimestampNanosecondArray::from(vec![40, 60, 20, 20, 60]));
903 let b2 = RecordBatch::try_from_iter(vec![("a", a), ("b", b), ("c", c)]).unwrap();
904
905 let a: ArrayRef = Arc::new(Int32Array::from(vec![100, 200, 700, 900, 300]));
906 let b: ArrayRef = Arc::new(StringArray::from_iter(vec![
907 Some("f"),
908 Some("g"),
909 Some("h"),
910 Some("i"),
911 Some("j"),
912 ]));
913 let c: ArrayRef = Arc::new(TimestampNanosecondArray::from(vec![4, 6, 2, 2, 6]));
914 let b3 = RecordBatch::try_from_iter(vec![("a", a), ("b", b), ("c", c)]).unwrap();
915
916 _test_merge(
917 &[vec![b1], vec![b2], vec![b3]],
918 &[
919 "+-----+---+-------------------------------+",
920 "| a | b | c |",
921 "+-----+---+-------------------------------+",
922 "| 1 | a | 1970-01-01T00:00:00.000000008 |",
923 "| 2 | b | 1970-01-01T00:00:00.000000007 |",
924 "| 7 | c | 1970-01-01T00:00:00.000000006 |",
925 "| 9 | d | 1970-01-01T00:00:00.000000005 |",
926 "| 10 | e | 1970-01-01T00:00:00.000000040 |",
927 "| 100 | f | 1970-01-01T00:00:00.000000004 |",
928 "| 3 | f | 1970-01-01T00:00:00.000000008 |",
929 "| 200 | g | 1970-01-01T00:00:00.000000006 |",
930 "| 20 | g | 1970-01-01T00:00:00.000000060 |",
931 "| 700 | h | 1970-01-01T00:00:00.000000002 |",
932 "| 70 | h | 1970-01-01T00:00:00.000000020 |",
933 "| 900 | i | 1970-01-01T00:00:00.000000002 |",
934 "| 90 | i | 1970-01-01T00:00:00.000000020 |",
935 "| 300 | j | 1970-01-01T00:00:00.000000006 |",
936 "| 30 | j | 1970-01-01T00:00:00.000000060 |",
937 "+-----+---+-------------------------------+",
938 ],
939 task_ctx,
940 )
941 .await;
942 }
943
944 async fn _test_merge(
945 partitions: &[Vec<RecordBatch>],
946 exp: &[&str],
947 context: Arc<TaskContext>,
948 ) {
949 let schema = partitions[0][0].schema();
950 let sort = [
951 PhysicalSortExpr {
952 expr: col("b", &schema).unwrap(),
953 options: Default::default(),
954 },
955 PhysicalSortExpr {
956 expr: col("c", &schema).unwrap(),
957 options: Default::default(),
958 },
959 ]
960 .into();
961 let exec = TestMemoryExec::try_new_exec(partitions, schema, None).unwrap();
962 let merge = Arc::new(SortPreservingMergeExec::new(sort, exec));
963
964 let collected = collect(merge, context).await.unwrap();
965 assert_batches_eq!(exp, collected.as_slice());
966 }
967
968 async fn sorted_merge(
969 input: Arc<dyn ExecutionPlan>,
970 sort: LexOrdering,
971 context: Arc<TaskContext>,
972 ) -> RecordBatch {
973 let merge = Arc::new(SortPreservingMergeExec::new(sort, input));
974 let mut result = collect(merge, context).await.unwrap();
975 assert_eq!(result.len(), 1);
976 result.remove(0)
977 }
978
979 async fn partition_sort(
980 input: Arc<dyn ExecutionPlan>,
981 sort: LexOrdering,
982 context: Arc<TaskContext>,
983 ) -> RecordBatch {
984 let sort_exec =
985 Arc::new(SortExec::new(sort.clone(), input).with_preserve_partitioning(true));
986 sorted_merge(sort_exec, sort, context).await
987 }
988
989 async fn basic_sort(
990 src: Arc<dyn ExecutionPlan>,
991 sort: LexOrdering,
992 context: Arc<TaskContext>,
993 ) -> RecordBatch {
994 let merge = Arc::new(CoalescePartitionsExec::new(src));
995 let sort_exec = Arc::new(SortExec::new(sort, merge));
996 let mut result = collect(sort_exec, context).await.unwrap();
997 assert_eq!(result.len(), 1);
998 result.remove(0)
999 }
1000
1001 #[tokio::test]
1002 async fn test_partition_sort() -> Result<()> {
1003 let task_ctx = Arc::new(TaskContext::default());
1004 let partitions = 4;
1005 let csv = test::scan_partitioned(partitions);
1006 let schema = csv.schema();
1007
1008 let sort: LexOrdering = [PhysicalSortExpr {
1009 expr: col("i", &schema)?,
1010 options: SortOptions {
1011 descending: true,
1012 nulls_first: true,
1013 },
1014 }]
1015 .into();
1016
1017 let basic =
1018 basic_sort(Arc::clone(&csv), sort.clone(), Arc::clone(&task_ctx)).await;
1019 let partition = partition_sort(csv, sort, Arc::clone(&task_ctx)).await;
1020
1021 let basic = arrow::util::pretty::pretty_format_batches(&[basic])
1022 .unwrap()
1023 .to_string();
1024 let partition = arrow::util::pretty::pretty_format_batches(&[partition])
1025 .unwrap()
1026 .to_string();
1027
1028 assert_eq!(
1029 basic, partition,
1030 "basic:\n\n{basic}\n\npartition:\n\n{partition}\n\n"
1031 );
1032
1033 Ok(())
1034 }
1035
1036 fn split_batch(sorted: &RecordBatch, batch_size: usize) -> Vec<RecordBatch> {
1038 let batches = sorted.num_rows().div_ceil(batch_size);
1039
1040 (0..batches)
1042 .map(|batch_idx| {
1043 let columns = (0..sorted.num_columns())
1044 .map(|column_idx| {
1045 let length =
1046 batch_size.min(sorted.num_rows() - batch_idx * batch_size);
1047
1048 sorted
1049 .column(column_idx)
1050 .slice(batch_idx * batch_size, length)
1051 })
1052 .collect();
1053
1054 RecordBatch::try_new(sorted.schema(), columns).unwrap()
1055 })
1056 .collect()
1057 }
1058
1059 async fn sorted_partitioned_input(
1060 sort: LexOrdering,
1061 sizes: &[usize],
1062 context: Arc<TaskContext>,
1063 ) -> Result<Arc<dyn ExecutionPlan>> {
1064 let partitions = 4;
1065 let csv = test::scan_partitioned(partitions);
1066
1067 let sorted = basic_sort(csv, sort, context).await;
1068 let split: Vec<_> = sizes.iter().map(|x| split_batch(&sorted, *x)).collect();
1069
1070 TestMemoryExec::try_new_exec(&split, sorted.schema(), None).map(|e| e as _)
1071 }
1072
1073 #[tokio::test]
1074 async fn test_partition_sort_streaming_input() -> Result<()> {
1075 let task_ctx = Arc::new(TaskContext::default());
1076 let schema = make_partition(11).schema();
1077 let sort: LexOrdering = [PhysicalSortExpr {
1078 expr: col("i", &schema)?,
1079 options: Default::default(),
1080 }]
1081 .into();
1082
1083 let input =
1084 sorted_partitioned_input(sort.clone(), &[10, 3, 11], Arc::clone(&task_ctx))
1085 .await?;
1086 let basic =
1087 basic_sort(Arc::clone(&input), sort.clone(), Arc::clone(&task_ctx)).await;
1088 let partition = sorted_merge(input, sort, Arc::clone(&task_ctx)).await;
1089
1090 assert_eq!(basic.num_rows(), 1200);
1091 assert_eq!(partition.num_rows(), 1200);
1092
1093 let basic = arrow::util::pretty::pretty_format_batches(&[basic])?.to_string();
1094 let partition =
1095 arrow::util::pretty::pretty_format_batches(&[partition])?.to_string();
1096
1097 assert_eq!(basic, partition);
1098
1099 Ok(())
1100 }
1101
1102 #[tokio::test]
1103 async fn test_partition_sort_streaming_input_output() -> Result<()> {
1104 let schema = make_partition(11).schema();
1105 let sort: LexOrdering = [PhysicalSortExpr {
1106 expr: col("i", &schema)?,
1107 options: Default::default(),
1108 }]
1109 .into();
1110
1111 let task_ctx = Arc::new(TaskContext::default());
1113 let input =
1114 sorted_partitioned_input(sort.clone(), &[10, 5, 13], Arc::clone(&task_ctx))
1115 .await?;
1116 let basic = basic_sort(Arc::clone(&input), sort.clone(), task_ctx).await;
1117
1118 let task_ctx = TaskContext::default()
1120 .with_session_config(SessionConfig::new().with_batch_size(23));
1121 let task_ctx = Arc::new(task_ctx);
1122
1123 let merge = Arc::new(SortPreservingMergeExec::new(sort, input));
1124 let merged = collect(merge, task_ctx).await?;
1125
1126 assert_eq!(merged.len(), 53);
1127 assert_eq!(basic.num_rows(), 1200);
1128 assert_eq!(merged.iter().map(|x| x.num_rows()).sum::<usize>(), 1200);
1129
1130 let basic = arrow::util::pretty::pretty_format_batches(&[basic])?.to_string();
1131 let partition = arrow::util::pretty::pretty_format_batches(&merged)?.to_string();
1132
1133 assert_eq!(basic, partition);
1134
1135 Ok(())
1136 }
1137
1138 #[tokio::test]
1139 async fn test_nulls() {
1140 let task_ctx = Arc::new(TaskContext::default());
1141 let a: ArrayRef = Arc::new(Int32Array::from(vec![1, 2, 7, 9, 3]));
1142 let b: ArrayRef = Arc::new(StringArray::from_iter(vec![
1143 None,
1144 Some("a"),
1145 Some("b"),
1146 Some("d"),
1147 Some("e"),
1148 ]));
1149 let c: ArrayRef = Arc::new(TimestampNanosecondArray::from(vec![
1150 Some(8),
1151 None,
1152 Some(6),
1153 None,
1154 Some(4),
1155 ]));
1156 let b1 = RecordBatch::try_from_iter(vec![("a", a), ("b", b), ("c", c)]).unwrap();
1157
1158 let a: ArrayRef = Arc::new(Int32Array::from(vec![1, 2, 3, 4, 5]));
1159 let b: ArrayRef = Arc::new(StringArray::from_iter(vec![
1160 None,
1161 Some("b"),
1162 Some("g"),
1163 Some("h"),
1164 Some("i"),
1165 ]));
1166 let c: ArrayRef = Arc::new(TimestampNanosecondArray::from(vec![
1167 Some(8),
1168 None,
1169 Some(5),
1170 None,
1171 Some(4),
1172 ]));
1173 let b2 = RecordBatch::try_from_iter(vec![("a", a), ("b", b), ("c", c)]).unwrap();
1174 let schema = b1.schema();
1175
1176 let sort = [
1177 PhysicalSortExpr {
1178 expr: col("b", &schema).unwrap(),
1179 options: SortOptions {
1180 descending: false,
1181 nulls_first: true,
1182 },
1183 },
1184 PhysicalSortExpr {
1185 expr: col("c", &schema).unwrap(),
1186 options: SortOptions {
1187 descending: false,
1188 nulls_first: false,
1189 },
1190 },
1191 ]
1192 .into();
1193 let exec =
1194 TestMemoryExec::try_new_exec(&[vec![b1], vec![b2]], schema, None).unwrap();
1195 let merge = Arc::new(SortPreservingMergeExec::new(sort, exec));
1196
1197 let collected = collect(merge, task_ctx).await.unwrap();
1198 assert_eq!(collected.len(), 1);
1199
1200 assert_snapshot!(batches_to_string(collected.as_slice()), @r"
1201 +---+---+-------------------------------+
1202 | a | b | c |
1203 +---+---+-------------------------------+
1204 | 1 | | 1970-01-01T00:00:00.000000008 |
1205 | 1 | | 1970-01-01T00:00:00.000000008 |
1206 | 2 | a | |
1207 | 7 | b | 1970-01-01T00:00:00.000000006 |
1208 | 2 | b | |
1209 | 9 | d | |
1210 | 3 | e | 1970-01-01T00:00:00.000000004 |
1211 | 3 | g | 1970-01-01T00:00:00.000000005 |
1212 | 4 | h | |
1213 | 5 | i | 1970-01-01T00:00:00.000000004 |
1214 +---+---+-------------------------------+
1215 ");
1216 }
1217
1218 #[tokio::test]
1219 async fn test_sort_merge_single_partition_with_fetch() {
1220 let task_ctx = Arc::new(TaskContext::default());
1221 let a: ArrayRef = Arc::new(Int32Array::from(vec![1, 2, 7, 9, 3]));
1222 let b: ArrayRef = Arc::new(StringArray::from(vec!["a", "b", "c", "d", "e"]));
1223 let batch = RecordBatch::try_from_iter(vec![("a", a), ("b", b)]).unwrap();
1224 let schema = batch.schema();
1225
1226 let sort = [PhysicalSortExpr {
1227 expr: col("b", &schema).unwrap(),
1228 options: SortOptions {
1229 descending: false,
1230 nulls_first: true,
1231 },
1232 }]
1233 .into();
1234 let exec = TestMemoryExec::try_new_exec(&[vec![batch]], schema, None).unwrap();
1235 let merge =
1236 Arc::new(SortPreservingMergeExec::new(sort, exec).with_fetch(Some(2)));
1237
1238 let collected = collect(merge, task_ctx).await.unwrap();
1239 assert_eq!(collected.len(), 1);
1240
1241 assert_snapshot!(batches_to_string(collected.as_slice()), @r"
1242 +---+---+
1243 | a | b |
1244 +---+---+
1245 | 1 | a |
1246 | 2 | b |
1247 +---+---+
1248 ");
1249 }
1250
1251 #[tokio::test]
1252 async fn test_sort_merge_single_partition_without_fetch() {
1253 let task_ctx = Arc::new(TaskContext::default());
1254 let a: ArrayRef = Arc::new(Int32Array::from(vec![1, 2, 7, 9, 3]));
1255 let b: ArrayRef = Arc::new(StringArray::from(vec!["a", "b", "c", "d", "e"]));
1256 let batch = RecordBatch::try_from_iter(vec![("a", a), ("b", b)]).unwrap();
1257 let schema = batch.schema();
1258
1259 let sort = [PhysicalSortExpr {
1260 expr: col("b", &schema).unwrap(),
1261 options: SortOptions {
1262 descending: false,
1263 nulls_first: true,
1264 },
1265 }]
1266 .into();
1267 let exec = TestMemoryExec::try_new_exec(&[vec![batch]], schema, None).unwrap();
1268 let merge = Arc::new(SortPreservingMergeExec::new(sort, exec));
1269
1270 let collected = collect(merge, task_ctx).await.unwrap();
1271 assert_eq!(collected.len(), 1);
1272
1273 assert_snapshot!(batches_to_string(collected.as_slice()), @r"
1274 +---+---+
1275 | a | b |
1276 +---+---+
1277 | 1 | a |
1278 | 2 | b |
1279 | 7 | c |
1280 | 9 | d |
1281 | 3 | e |
1282 +---+---+
1283 ");
1284 }
1285
1286 #[tokio::test]
1287 async fn test_async() -> Result<()> {
1288 let task_ctx = Arc::new(TaskContext::default());
1289 let schema = make_partition(11).schema();
1290 let sort: LexOrdering = [PhysicalSortExpr {
1291 expr: col("i", &schema).unwrap(),
1292 options: SortOptions::default(),
1293 }]
1294 .into();
1295
1296 let batches =
1297 sorted_partitioned_input(sort.clone(), &[5, 7, 3], Arc::clone(&task_ctx))
1298 .await?;
1299
1300 let partition_count = batches.output_partitioning().partition_count();
1301 let mut streams = Vec::with_capacity(partition_count);
1302
1303 for partition in 0..partition_count {
1304 let mut builder = RecordBatchReceiverStream::builder(Arc::clone(&schema), 1);
1305
1306 let sender = builder.tx();
1307
1308 let mut stream = batches.execute(partition, Arc::clone(&task_ctx)).unwrap();
1309 builder.spawn(async move {
1310 while let Some(batch) = stream.next().await {
1311 sender.send(batch).await.unwrap();
1312 tokio::time::sleep(Duration::from_millis(10)).await;
1314 }
1315
1316 Ok(())
1317 });
1318
1319 streams.push(builder.build());
1320 }
1321
1322 let metrics = ExecutionPlanMetricsSet::new();
1323 let reservation =
1324 MemoryConsumer::new("test").register(&task_ctx.runtime_env().memory_pool);
1325
1326 let fetch = None;
1327 let merge_stream = StreamingMergeBuilder::new()
1328 .with_streams(streams)
1329 .with_schema(batches.schema())
1330 .with_expressions(&sort)
1331 .with_metrics(BaselineMetrics::new(&metrics, 0))
1332 .with_batch_size(task_ctx.session_config().batch_size())
1333 .with_fetch(fetch)
1334 .with_reservation(reservation)
1335 .build()?;
1336
1337 let mut merged = common::collect(merge_stream).await.unwrap();
1338
1339 assert_eq!(merged.len(), 1);
1340 let merged = merged.remove(0);
1341 let basic = basic_sort(batches, sort.clone(), Arc::clone(&task_ctx)).await;
1342
1343 let basic = arrow::util::pretty::pretty_format_batches(&[basic])
1344 .unwrap()
1345 .to_string();
1346 let partition = arrow::util::pretty::pretty_format_batches(&[merged])
1347 .unwrap()
1348 .to_string();
1349
1350 assert_eq!(
1351 basic, partition,
1352 "basic:\n\n{basic}\n\npartition:\n\n{partition}\n\n"
1353 );
1354
1355 Ok(())
1356 }
1357
1358 #[tokio::test]
1359 async fn test_merge_metrics() {
1360 let task_ctx = Arc::new(TaskContext::default());
1361 let a: ArrayRef = Arc::new(Int32Array::from(vec![1, 2]));
1362 let b: ArrayRef = Arc::new(StringArray::from_iter(vec![Some("a"), Some("c")]));
1363 let b1 = RecordBatch::try_from_iter(vec![("a", a), ("b", b)]).unwrap();
1364
1365 let a: ArrayRef = Arc::new(Int32Array::from(vec![10, 20]));
1366 let b: ArrayRef = Arc::new(StringArray::from_iter(vec![Some("b"), Some("d")]));
1367 let b2 = RecordBatch::try_from_iter(vec![("a", a), ("b", b)]).unwrap();
1368
1369 let schema = b1.schema();
1370 let sort = [PhysicalSortExpr {
1371 expr: col("b", &schema).unwrap(),
1372 options: Default::default(),
1373 }]
1374 .into();
1375 let exec =
1376 TestMemoryExec::try_new_exec(&[vec![b1], vec![b2]], schema, None).unwrap();
1377 let merge = Arc::new(SortPreservingMergeExec::new(sort, exec));
1378
1379 let collected = collect(Arc::clone(&merge) as Arc<dyn ExecutionPlan>, task_ctx)
1380 .await
1381 .unwrap();
1382 assert_snapshot!(batches_to_string(collected.as_slice()), @r"
1383 +----+---+
1384 | a | b |
1385 +----+---+
1386 | 1 | a |
1387 | 10 | b |
1388 | 2 | c |
1389 | 20 | d |
1390 +----+---+
1391 ");
1392
1393 let metrics = merge.metrics().unwrap();
1395
1396 assert_eq!(metrics.output_rows().unwrap(), 4);
1397 assert!(metrics.elapsed_compute().unwrap() > 0);
1398
1399 let mut saw_start = false;
1400 let mut saw_end = false;
1401 metrics.iter().for_each(|m| match m.value() {
1402 MetricValue::StartTimestamp(ts) => {
1403 saw_start = true;
1404 assert!(nanos_from_timestamp(ts) > 0);
1405 }
1406 MetricValue::EndTimestamp(ts) => {
1407 saw_end = true;
1408 assert!(nanos_from_timestamp(ts) > 0);
1409 }
1410 _ => {}
1411 });
1412
1413 assert!(saw_start);
1414 assert!(saw_end);
1415 }
1416
1417 fn nanos_from_timestamp(ts: &Timestamp) -> i64 {
1418 ts.value().unwrap().timestamp_nanos_opt().unwrap()
1419 }
1420
1421 #[tokio::test]
1422 async fn test_drop_cancel() -> Result<()> {
1423 let task_ctx = Arc::new(TaskContext::default());
1424 let schema =
1425 Arc::new(Schema::new(vec![Field::new("a", DataType::Float32, true)]));
1426
1427 let blocking_exec = Arc::new(BlockingExec::new(Arc::clone(&schema), 2));
1428 let refs = blocking_exec.refs();
1429 let sort_preserving_merge_exec = Arc::new(SortPreservingMergeExec::new(
1430 [PhysicalSortExpr {
1431 expr: col("a", &schema)?,
1432 options: SortOptions::default(),
1433 }]
1434 .into(),
1435 blocking_exec,
1436 ));
1437
1438 let fut = collect(sort_preserving_merge_exec, task_ctx);
1439 let mut fut = fut.boxed();
1440
1441 assert_is_pending(&mut fut);
1442 drop(fut);
1443 assert_strong_count_converges_to_zero(refs).await;
1444
1445 Ok(())
1446 }
1447
1448 #[tokio::test]
1449 async fn test_stable_sort() {
1450 let task_ctx = Arc::new(TaskContext::default());
1451
1452 let partitions: Vec<Vec<RecordBatch>> = (0..10)
1460 .map(|batch_number| {
1461 let batch_number: Int32Array =
1462 vec![Some(batch_number), Some(batch_number)]
1463 .into_iter()
1464 .collect();
1465 let value: StringArray = vec![Some("A"), Some("B")].into_iter().collect();
1466
1467 let batch = RecordBatch::try_from_iter(vec![
1468 ("batch_number", Arc::new(batch_number) as ArrayRef),
1469 ("value", Arc::new(value) as ArrayRef),
1470 ])
1471 .unwrap();
1472
1473 vec![batch]
1474 })
1475 .collect();
1476
1477 let schema = partitions[0][0].schema();
1478
1479 let sort = [PhysicalSortExpr {
1480 expr: col("value", &schema).unwrap(),
1481 options: SortOptions {
1482 descending: false,
1483 nulls_first: true,
1484 },
1485 }]
1486 .into();
1487
1488 let exec = TestMemoryExec::try_new_exec(&partitions, schema, None).unwrap();
1489 let merge = Arc::new(SortPreservingMergeExec::new(sort, exec));
1490
1491 let collected = collect(merge, task_ctx).await.unwrap();
1492 assert_eq!(collected.len(), 1);
1493
1494 assert_snapshot!(batches_to_string(collected.as_slice()), @r"
1498 +--------------+-------+
1499 | batch_number | value |
1500 +--------------+-------+
1501 | 0 | A |
1502 | 1 | A |
1503 | 2 | A |
1504 | 3 | A |
1505 | 4 | A |
1506 | 5 | A |
1507 | 6 | A |
1508 | 7 | A |
1509 | 8 | A |
1510 | 9 | A |
1511 | 0 | B |
1512 | 1 | B |
1513 | 2 | B |
1514 | 3 | B |
1515 | 4 | B |
1516 | 5 | B |
1517 | 6 | B |
1518 | 7 | B |
1519 | 8 | B |
1520 | 9 | B |
1521 +--------------+-------+
1522 ");
1523 }
1524
1525 #[derive(Debug)]
1526 struct CongestionState {
1527 wakers: Vec<Waker>,
1528 unpolled_partitions: HashSet<usize>,
1529 }
1530
1531 #[derive(Debug)]
1532 struct Congestion {
1533 congestion_state: Mutex<CongestionState>,
1534 }
1535
1536 impl Congestion {
1537 fn new(partition_count: usize) -> Self {
1538 Congestion {
1539 congestion_state: Mutex::new(CongestionState {
1540 wakers: vec![],
1541 unpolled_partitions: (0usize..partition_count).collect(),
1542 }),
1543 }
1544 }
1545
1546 fn check_congested(&self, partition: usize, cx: &mut Context<'_>) -> Poll<()> {
1547 let mut state = self.congestion_state.lock().unwrap();
1548
1549 state.unpolled_partitions.remove(&partition);
1550
1551 if state.unpolled_partitions.is_empty() {
1552 state.wakers.iter().for_each(|w| w.wake_by_ref());
1553 state.wakers.clear();
1554 Poll::Ready(())
1555 } else {
1556 state.wakers.push(cx.waker().clone());
1557 Poll::Pending
1558 }
1559 }
1560 }
1561
1562 #[derive(Debug, Clone)]
1565 struct CongestedExec {
1566 schema: Schema,
1567 cache: Arc<PlanProperties>,
1568 congestion: Arc<Congestion>,
1569 }
1570
1571 impl CongestedExec {
1572 fn compute_properties(schema: SchemaRef) -> PlanProperties {
1573 let columns = schema
1574 .fields
1575 .iter()
1576 .enumerate()
1577 .map(|(i, f)| Arc::new(Column::new(f.name(), i)) as Arc<dyn PhysicalExpr>)
1578 .collect::<Vec<_>>();
1579 let mut eq_properties = EquivalenceProperties::new(schema);
1580 eq_properties.add_ordering(
1581 columns
1582 .iter()
1583 .map(|expr| PhysicalSortExpr::new_default(Arc::clone(expr))),
1584 );
1585 PlanProperties::new(
1586 eq_properties,
1587 Partitioning::Hash(columns, 3),
1588 EmissionType::Incremental,
1589 Boundedness::Unbounded {
1590 requires_infinite_memory: false,
1591 },
1592 )
1593 }
1594 }
1595
1596 impl ExecutionPlan for CongestedExec {
1597 fn name(&self) -> &'static str {
1598 Self::static_name()
1599 }
1600 fn properties(&self) -> &Arc<PlanProperties> {
1601 &self.cache
1602 }
1603 fn children(&self) -> Vec<&Arc<dyn ExecutionPlan>> {
1604 vec![]
1605 }
1606 fn apply_expressions(
1607 &self,
1608 _f: &mut dyn FnMut(&Arc<dyn PhysicalExpr>) -> Result<TreeNodeRecursion>,
1609 ) -> Result<TreeNodeRecursion> {
1610 Ok(TreeNodeRecursion::Continue)
1611 }
1612
1613 fn replace_children(
1614 self: Arc<Self>,
1615 _: Vec<Arc<dyn ExecutionPlan>>,
1616 _: ReplaceChildrenOptions,
1617 ) -> Result<Arc<dyn ExecutionPlan>> {
1618 Ok(self)
1619 }
1620 fn with_new_children(
1621 self: Arc<Self>,
1622 children: Vec<Arc<dyn ExecutionPlan>>,
1623 ) -> Result<Arc<dyn ExecutionPlan>> {
1624 self.replace_children(
1625 children,
1626 ReplaceChildrenOptions::new(ChildrenPropertiesMode::Recompute),
1627 )
1628 }
1629 fn execute(
1630 &self,
1631 partition: usize,
1632 _context: Arc<TaskContext>,
1633 ) -> Result<SendableRecordBatchStream> {
1634 Ok(Box::pin(CongestedStream {
1635 schema: Arc::new(self.schema.clone()),
1636 none_polled_once: false,
1637 congestion: Arc::clone(&self.congestion),
1638 partition,
1639 }))
1640 }
1641 }
1642
1643 impl DisplayAs for CongestedExec {
1644 fn fmt_as(&self, t: DisplayFormatType, f: &mut Formatter) -> std::fmt::Result {
1645 match t {
1646 DisplayFormatType::Default | DisplayFormatType::Verbose => {
1647 write!(f, "CongestedExec",).unwrap()
1648 }
1649 DisplayFormatType::TreeRender => {
1650 write!(f, "").unwrap()
1652 }
1653 }
1654 Ok(())
1655 }
1656 }
1657
1658 #[derive(Debug)]
1661 pub struct CongestedStream {
1662 schema: SchemaRef,
1663 none_polled_once: bool,
1664 congestion: Arc<Congestion>,
1665 partition: usize,
1666 }
1667
1668 impl Stream for CongestedStream {
1669 type Item = Result<RecordBatch>;
1670 fn poll_next(
1671 mut self: Pin<&mut Self>,
1672 cx: &mut Context<'_>,
1673 ) -> Poll<Option<Self::Item>> {
1674 match self.partition {
1675 0 => {
1676 let _ = self.congestion.check_congested(self.partition, cx);
1677 if self.none_polled_once {
1678 panic!("Exhausted stream is polled more than once")
1679 } else {
1680 self.none_polled_once = true;
1681 Poll::Ready(None)
1682 }
1683 }
1684 _ => {
1685 ready!(self.congestion.check_congested(self.partition, cx));
1686 Poll::Ready(None)
1687 }
1688 }
1689 }
1690 }
1691
1692 impl RecordBatchStream for CongestedStream {
1693 fn schema(&self) -> SchemaRef {
1694 Arc::clone(&self.schema)
1695 }
1696 }
1697
1698 #[tokio::test]
1699 async fn test_spm_congestion() -> Result<()> {
1700 let task_ctx = Arc::new(TaskContext::default());
1701 let schema = Schema::new(vec![Field::new("c1", DataType::UInt64, false)]);
1702 let properties = CongestedExec::compute_properties(Arc::new(schema.clone()));
1703 let partition_count = properties.output_partitioning().partition_count();
1704 let source = CongestedExec {
1705 schema: schema.clone(),
1706 cache: Arc::new(properties),
1707 congestion: Arc::new(Congestion::new(partition_count)),
1708 };
1709 let spm = SortPreservingMergeExec::new(
1710 [PhysicalSortExpr::new_default(Arc::new(Column::new(
1711 "c1", 0,
1712 )))]
1713 .into(),
1714 Arc::new(source),
1715 );
1716 let spm_task = SpawnedTask::spawn(collect(Arc::new(spm), task_ctx));
1717
1718 let result = timeout(Duration::from_secs(3), spm_task.join()).await;
1719 match result {
1720 Ok(Ok(Ok(_batches))) => Ok(()),
1721 Ok(Ok(Err(e))) => Err(e),
1722 Ok(Err(_)) => exec_err!("SortPreservingMerge task panicked or was cancelled"),
1723 Err(_) => exec_err!("SortPreservingMerge caused a deadlock"),
1724 }
1725 }
1726
1727 #[tokio::test]
1728 async fn test_sort_merge_stops_after_error_with_buffered_rows() -> Result<()> {
1729 let task_ctx = Arc::new(TaskContext::default());
1730 let schema = Arc::new(Schema::new(vec![Field::new("i", DataType::Int32, false)]));
1731 let sort: LexOrdering = [PhysicalSortExpr::new_default(Arc::new(Column::new(
1732 "i", 0,
1733 ))
1734 as Arc<dyn PhysicalExpr>)]
1735 .into();
1736
1737 let mut stream0 = RecordBatchReceiverStream::builder(Arc::clone(&schema), 2);
1738 let tx0 = stream0.tx();
1739 let schema0 = Arc::clone(&schema);
1740 stream0.spawn(async move {
1741 let batch =
1742 RecordBatch::try_new(schema0, vec![Arc::new(Int32Array::from(vec![1]))])?;
1743 tx0.send(Ok(batch)).await.unwrap();
1744 tx0.send(exec_err!("stream failure")).await.unwrap();
1745 Ok(())
1746 });
1747
1748 let mut stream1 = RecordBatchReceiverStream::builder(Arc::clone(&schema), 1);
1749 let tx1 = stream1.tx();
1750 let schema1 = Arc::clone(&schema);
1751 stream1.spawn(async move {
1752 let batch =
1753 RecordBatch::try_new(schema1, vec![Arc::new(Int32Array::from(vec![2]))])?;
1754 tx1.send(Ok(batch)).await.unwrap();
1755 Ok(())
1756 });
1757
1758 let metrics = ExecutionPlanMetricsSet::new();
1759 let reservation =
1760 MemoryConsumer::new("test").register(&task_ctx.runtime_env().memory_pool);
1761
1762 let mut merge_stream = StreamingMergeBuilder::new()
1763 .with_streams(vec![stream0.build(), stream1.build()])
1764 .with_schema(Arc::clone(&schema))
1765 .with_expressions(&sort)
1766 .with_metrics(BaselineMetrics::new(&metrics, 0))
1767 .with_batch_size(task_ctx.session_config().batch_size())
1768 .with_fetch(None)
1769 .with_reservation(reservation)
1770 .build()?;
1771
1772 let first = merge_stream.next().await.unwrap();
1773 assert!(first.is_err(), "expected merge stream to surface the error");
1774 assert!(
1775 merge_stream.next().await.is_none(),
1776 "merge stream yielded data after returning an error"
1777 );
1778
1779 Ok(())
1780 }
1781}