Skip to main content

query/
window_sort.rs

1// Copyright 2023 Greptime Team
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! A physical plan for window sort(Which is sorting multiple sorted ranges according to input `PartitionRange`).
16//!
17
18use std::collections::{BTreeMap, BTreeSet, VecDeque};
19use std::pin::Pin;
20use std::slice::from_ref;
21use std::sync::Arc;
22use std::task::{Context, Poll};
23
24use arrow::array::{Array, ArrayRef};
25use arrow::compute::SortColumn;
26use arrow_schema::{DataType, SchemaRef, SortOptions};
27use common_error::ext::{BoxedError, PlainError};
28use common_error::status_code::StatusCode;
29use common_recordbatch::{DfRecordBatch, DfSendableRecordBatchStream};
30use common_telemetry::error;
31use common_time::Timestamp;
32use common_time::timestamp::TimeUnit as TimestampUnit;
33use datafusion::execution::memory_pool::{MemoryConsumer, MemoryPool};
34use datafusion::execution::{RecordBatchStream, TaskContext};
35use datafusion::physical_plan::memory::MemoryStream;
36use datafusion::physical_plan::metrics::{BaselineMetrics, ExecutionPlanMetricsSet, MetricsSet};
37use datafusion::physical_plan::sorts::streaming_merge::StreamingMergeBuilder;
38use datafusion::physical_plan::{
39    DisplayAs, DisplayFormatType, ExecutionPlan, ExecutionPlanProperties, PlanProperties,
40    apply_expression_roots,
41};
42use datafusion_common::tree_node::TreeNodeRecursion;
43use datafusion_common::utils::bisect;
44use datafusion_common::{DataFusionError, internal_err};
45use datafusion_physical_expr::{PhysicalExpr, PhysicalSortExpr};
46use datatypes::value::Value;
47use futures::Stream;
48use itertools::Itertools;
49use snafu::ResultExt;
50use store_api::region_engine::PartitionRange;
51
52use crate::error::{QueryExecutionSnafu, Result};
53
54/// A complex stream sort execution plan which accepts a list of `PartitionRange` and
55/// merge sort them whenever possible, and emit the sorted result as soon as possible.
56/// This sorting plan only accept sort by ts and will not sort by other fields.
57///
58/// internally, it call [`StreamingMergeBuilder`] multiple times to merge multiple sorted "working ranges"
59///
60/// # Invariant Promise on Input Stream
61/// 1. The input stream must be sorted by timestamp and
62/// 2. in the order of `PartitionRange` in `ranges`
63/// 3. Each `PartitionRange` is sorted within itself(ascending or descending) but no need to be sorted across ranges
64/// 4. There can't be any RecordBatch that is cross multiple `PartitionRange` in the input stream
65///
66///  TODO(discord9): fix item 4, but since only use `PartSort` as input, this might not be a problem
67
68#[derive(Debug, Clone)]
69pub struct WindowedSortExec {
70    /// Physical sort expressions(that is, sort by timestamp)
71    expression: PhysicalSortExpr,
72    /// Optional number of rows to fetch. Stops producing rows after this fetch
73    fetch: Option<usize>,
74    /// The input ranges indicate input stream will be composed of those ranges in given order.
75    ///
76    /// Each partition has one vector of `PartitionRange`.
77    ranges: Vec<Vec<PartitionRange>>,
78    /// All available working ranges and their corresponding working set
79    ///
80    /// working ranges promise once input stream get a value out of current range, future values will never
81    /// be in this range. Each partition has one vector of ranges.
82    all_avail_working_range: Vec<Vec<(TimeRange, BTreeSet<usize>)>>,
83    input: Arc<dyn ExecutionPlan>,
84    /// Execution metrics
85    metrics: ExecutionPlanMetricsSet,
86    properties: Arc<PlanProperties>,
87}
88
89/// Checks that partition ranges are sorted correctly for the given sort direction.
90/// - Descending: sorted by (end DESC, start DESC) - shorter ranges first when ends are equal
91/// - Ascending: sorted by (start ASC, end ASC) - shorter ranges first when starts are equal
92pub fn check_partition_range_monotonicity(
93    ranges: &[Vec<PartitionRange>],
94    descending: bool,
95) -> Result<()> {
96    let is_valid = ranges.iter().all(|r| {
97        if descending {
98            // Primary: end descending, Secondary: start descending (shorter range first)
99            r.windows(2)
100                .all(|w| w[0].end > w[1].end || (w[0].end == w[1].end && w[0].start >= w[1].start))
101        } else {
102            // Primary: start ascending, Secondary: end ascending (shorter range first)
103            r.windows(2).all(|w| {
104                w[0].start < w[1].start || (w[0].start == w[1].start && w[0].end <= w[1].end)
105            })
106        }
107    });
108
109    if !is_valid {
110        let msg = if descending {
111            "Input `PartitionRange`s are not sorted by (end DESC, start DESC)"
112        } else {
113            "Input `PartitionRange`s are not sorted by (start ASC, end ASC)"
114        };
115        let plain_error = PlainError::new(msg.to_string(), StatusCode::Unexpected);
116        Err(BoxedError::new(plain_error)).context(QueryExecutionSnafu {})
117    } else {
118        Ok(())
119    }
120}
121
122impl WindowedSortExec {
123    pub fn try_new(
124        expression: PhysicalSortExpr,
125        fetch: Option<usize>,
126        ranges: Vec<Vec<PartitionRange>>,
127        input: Arc<dyn ExecutionPlan>,
128    ) -> Result<Self> {
129        check_partition_range_monotonicity(&ranges, expression.options.descending)?;
130
131        let mut eq_properties = input.equivalence_properties().clone();
132        eq_properties.reorder(vec![expression.clone()])?;
133
134        let properties = input.properties();
135        let properties = Arc::new(PlanProperties::new(
136            eq_properties,
137            input.output_partitioning().clone(),
138            properties.emission_type,
139            properties.boundedness,
140        ));
141
142        let mut all_avail_working_range = Vec::with_capacity(ranges.len());
143        for r in &ranges {
144            let overlap_counts = split_overlapping_ranges(r);
145            let working_ranges =
146                compute_all_working_ranges(&overlap_counts, expression.options.descending);
147            all_avail_working_range.push(working_ranges);
148        }
149
150        Ok(Self {
151            expression,
152            fetch,
153            ranges,
154            all_avail_working_range,
155            input,
156            metrics: ExecutionPlanMetricsSet::new(),
157            properties,
158        })
159    }
160
161    /// During receiving partial-sorted RecordBatch, we need to update the working set which is the
162    /// `PartitionRange` we think those RecordBatch belongs to. And when we receive something outside
163    /// of working set, we can merge results before whenever possible.
164    pub fn to_stream(
165        &self,
166        context: Arc<TaskContext>,
167        partition: usize,
168    ) -> datafusion_common::Result<DfSendableRecordBatchStream> {
169        let input_stream: DfSendableRecordBatchStream =
170            self.input.execute(partition, context.clone())?;
171
172        let df_stream = Box::pin(WindowedSortStream::new(
173            context,
174            self,
175            input_stream,
176            partition,
177        )) as _;
178
179        Ok(df_stream)
180    }
181}
182
183impl DisplayAs for WindowedSortExec {
184    fn fmt_as(&self, _t: DisplayFormatType, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
185        write!(
186            f,
187            "WindowedSortExec: expr={} num_ranges={}",
188            self.expression,
189            self.ranges.len()
190        )?;
191        if let Some(fetch) = self.fetch {
192            write!(f, " fetch={}", fetch)?;
193        }
194        Ok(())
195    }
196}
197
198impl ExecutionPlan for WindowedSortExec {
199    fn schema(&self) -> SchemaRef {
200        self.input.schema()
201    }
202
203    fn properties(&self) -> &Arc<PlanProperties> {
204        &self.properties
205    }
206
207    fn children(&self) -> Vec<&Arc<dyn ExecutionPlan>> {
208        vec![&self.input]
209    }
210
211    fn apply_expressions(
212        &self,
213        f: &mut dyn FnMut(&Arc<dyn PhysicalExpr>) -> datafusion_common::Result<TreeNodeRecursion>,
214    ) -> datafusion_common::Result<TreeNodeRecursion> {
215        apply_expression_roots([&self.expression.expr], f)
216    }
217
218    fn with_new_children(
219        self: Arc<Self>,
220        children: Vec<Arc<dyn ExecutionPlan>>,
221    ) -> datafusion_common::Result<Arc<dyn ExecutionPlan>> {
222        let new_input = if let Some(first) = children.first() {
223            first
224        } else {
225            internal_err!("No children found")?
226        };
227        let new = Self::try_new(
228            self.expression.clone(),
229            self.fetch,
230            self.ranges.clone(),
231            new_input.clone(),
232        )?;
233        Ok(Arc::new(new))
234    }
235
236    fn execute(
237        &self,
238        partition: usize,
239        context: Arc<TaskContext>,
240    ) -> datafusion_common::Result<DfSendableRecordBatchStream> {
241        self.to_stream(context, partition)
242    }
243
244    fn metrics(&self) -> Option<MetricsSet> {
245        Some(self.metrics.clone_inner())
246    }
247
248    /// # Explain
249    ///
250    /// This plan needs to be executed on each partition independently,
251    /// and is expected to run directly on storage engine's output
252    /// distribution / partition.
253    fn benefits_from_input_partitioning(&self) -> Vec<bool> {
254        vec![false]
255    }
256
257    fn name(&self) -> &str {
258        "WindowedSortExec"
259    }
260}
261
262/// The core logic of merging sort multiple sorted ranges
263///
264/// the flow of data is:
265/// ```md
266/// input --check if sorted--> in_progress --find sorted run--> sorted_input_runs --call merge sort--> merge_stream --> output
267/// ```
268pub struct WindowedSortStream {
269    /// Memory pool for this stream
270    memory_pool: Arc<dyn MemoryPool>,
271    /// currently assembling RecordBatches, will be put to `sort_partition_rbs` when it's done
272    in_progress: Vec<DfRecordBatch>,
273    /// last `Timestamp` of the last input RecordBatch in `in_progress`, use to found partial sorted run's boundary
274    last_value: Option<Timestamp>,
275    /// Current working set of `PartitionRange` sorted RecordBatches
276    sorted_input_runs: Vec<DfSendableRecordBatchStream>,
277    /// Merge-sorted result streams, should be polled to end before start a new merge sort again
278    merge_stream: VecDeque<DfSendableRecordBatchStream>,
279    /// The number of times merge sort has been called
280    merge_count: usize,
281    /// Index into current `working_range` in `all_avail_working_range`
282    working_idx: usize,
283    /// input stream assumed reading in order of `PartitionRange`
284    input: DfSendableRecordBatchStream,
285    /// Whether this stream is terminated. For reasons like limit reached or input stream is done.
286    is_terminated: bool,
287    /// Output Schema, which is the same as input schema, since this is a sort plan
288    schema: SchemaRef,
289    /// Physical sort expressions(that is, sort by timestamp)
290    expression: PhysicalSortExpr,
291    /// Optional number of rows to fetch. Stops producing rows after this fetch
292    fetch: Option<usize>,
293    /// number of rows produced
294    produced: usize,
295    /// Resulting Stream(`merge_stream`)'s batch size, merely a suggestion
296    batch_size: usize,
297    /// All available working ranges and their corresponding working set
298    ///
299    /// working ranges promise once input stream get a value out of current range, future values will never be in this range
300    all_avail_working_range: Vec<(TimeRange, BTreeSet<usize>)>,
301    /// The input partition ranges
302    #[allow(dead_code)] // this is used under #[debug_assertions]
303    ranges: Vec<PartitionRange>,
304    /// Execution metrics
305    metrics: BaselineMetrics,
306}
307
308impl WindowedSortStream {
309    pub fn new(
310        context: Arc<TaskContext>,
311        exec: &WindowedSortExec,
312        input: DfSendableRecordBatchStream,
313        partition: usize,
314    ) -> Self {
315        Self {
316            memory_pool: context.runtime_env().memory_pool.clone(),
317            in_progress: Vec::new(),
318            last_value: None,
319            sorted_input_runs: Vec::new(),
320            merge_stream: VecDeque::new(),
321            merge_count: 0,
322            working_idx: 0,
323            schema: input.schema(),
324            input,
325            is_terminated: false,
326            expression: exec.expression.clone(),
327            fetch: exec.fetch,
328            produced: 0,
329            batch_size: context.session_config().batch_size(),
330            all_avail_working_range: exec.all_avail_working_range[partition].clone(),
331            ranges: exec.ranges[partition].clone(),
332            metrics: BaselineMetrics::new(&exec.metrics, partition),
333        }
334    }
335}
336
337impl WindowedSortStream {
338    #[cfg(debug_assertions)]
339    fn check_subset_ranges(&self, cur_range: &TimeRange) {
340        let cur_is_subset_to = self
341            .ranges
342            .iter()
343            .filter(|r| cur_range.is_subset(&TimeRange::from(*r)))
344            .collect_vec();
345        if cur_is_subset_to.is_empty() {
346            error!("Current range is not a subset of any PartitionRange");
347            // found in all ranges that are subset of current range
348            let subset_ranges = self
349                .ranges
350                .iter()
351                .filter(|r| TimeRange::from(*r).is_subset(cur_range))
352                .collect_vec();
353            let only_overlap = self
354                .ranges
355                .iter()
356                .filter(|r| {
357                    let r = TimeRange::from(*r);
358                    r.is_overlapping(cur_range) && !r.is_subset(cur_range)
359                })
360                .collect_vec();
361            error!(
362                "Bad input, found {} ranges that are subset of current range, also found {} ranges that only overlap, subset ranges are: {:?}; overlap ranges are: {:?}",
363                subset_ranges.len(),
364                only_overlap.len(),
365                subset_ranges,
366                only_overlap
367            );
368        } else {
369            let only_overlap = self
370                .ranges
371                .iter()
372                .filter(|r| {
373                    let r = TimeRange::from(*r);
374                    r.is_overlapping(cur_range) && !cur_range.is_subset(&r)
375                })
376                .collect_vec();
377            error!(
378                "Found current range to be subset of {} ranges, also found {} ranges that only overlap, of subset ranges are:{:?}; overlap ranges are: {:?}",
379                cur_is_subset_to.len(),
380                only_overlap.len(),
381                cur_is_subset_to,
382                only_overlap
383            );
384        }
385        let all_overlap_working_range = self
386            .all_avail_working_range
387            .iter()
388            .filter(|(range, _)| range.is_overlapping(cur_range))
389            .map(|(range, _)| range)
390            .collect_vec();
391        error!(
392            "Found {} working ranges that overlap with current range: {:?}",
393            all_overlap_working_range.len(),
394            all_overlap_working_range
395        );
396    }
397
398    /// Poll the next RecordBatch from the merge-sort's output stream
399    fn poll_result_stream(
400        &mut self,
401        cx: &mut Context<'_>,
402    ) -> Poll<Option<datafusion_common::Result<DfRecordBatch>>> {
403        while let Some(merge_stream) = &mut self.merge_stream.front_mut() {
404            match merge_stream.as_mut().poll_next(cx) {
405                Poll::Ready(Some(Ok(batch))) => {
406                    let ret = if let Some(remaining) = self.remaining_fetch() {
407                        if remaining == 0 {
408                            self.is_terminated = true;
409                            None
410                        } else if remaining < batch.num_rows() {
411                            self.produced += remaining;
412                            Some(Ok(batch.slice(0, remaining)))
413                        } else {
414                            self.produced += batch.num_rows();
415                            Some(Ok(batch))
416                        }
417                    } else {
418                        self.produced += batch.num_rows();
419                        Some(Ok(batch))
420                    };
421                    return Poll::Ready(ret);
422                }
423                Poll::Ready(Some(Err(e))) => {
424                    return Poll::Ready(Some(Err(e)));
425                }
426                Poll::Ready(None) => {
427                    // current merge stream is done, we can start polling the next one
428
429                    self.merge_stream.pop_front();
430                    continue;
431                }
432                Poll::Pending => {
433                    return Poll::Pending;
434                }
435            }
436        }
437        // if no output stream is available
438        Poll::Ready(None)
439    }
440
441    /// The core logic of merging sort multiple sorted ranges
442    ///
443    /// We try to maximize the number of sorted runs we can merge in one go, while emit the result as soon as possible.
444    pub fn poll_next_inner(
445        mut self: Pin<&mut Self>,
446        cx: &mut Context<'_>,
447    ) -> Poll<Option<datafusion_common::Result<DfRecordBatch>>> {
448        // first check and send out the merge result
449        match self.poll_result_stream(cx) {
450            Poll::Ready(None) => {
451                if self.is_terminated {
452                    return Poll::Ready(None);
453                }
454            }
455            x => return x,
456        };
457
458        // consume input stream
459        while !self.is_terminated {
460            // then we get a new RecordBatch from input stream
461            let SortedRunSet {
462                runs_with_batch,
463                sort_column,
464            } = match self.input.as_mut().poll_next(cx) {
465                Poll::Ready(Some(Ok(batch))) => split_batch_to_sorted_run(batch, &self.expression)?,
466                Poll::Ready(Some(Err(e))) => {
467                    return Poll::Ready(Some(Err(e)));
468                }
469                Poll::Ready(None) => {
470                    // input stream is done, we need to merge sort the remaining working set
471                    self.is_terminated = true;
472                    self.build_sorted_stream()?;
473                    self.start_new_merge_sort()?;
474                    break;
475                }
476                Poll::Pending => return Poll::Pending,
477            };
478
479            // The core logic to eargerly merge sort the working set
480
481            // compare with last_value to find boundary, then merge runs if needed
482
483            // iterate over runs_with_batch to merge sort, might create zero or more stream to put to `sort_partition_rbs`
484            let mut last_remaining = None;
485            let mut run_iter = runs_with_batch.into_iter();
486            loop {
487                let Some((sorted_rb, run_info)) = last_remaining.take().or(run_iter.next()) else {
488                    break;
489                };
490                if sorted_rb.num_rows() == 0 {
491                    continue;
492                }
493                // determine if this batch is in current working range
494                let Some(cur_range) = run_info.get_time_range() else {
495                    internal_err!("Found NULL in time index column")?
496                };
497                let Some(working_range) = self.get_working_range() else {
498                    internal_err!("No working range found")?
499                };
500
501                // ensure the current batch is in the working range
502                if sort_column.options.unwrap_or_default().descending {
503                    if cur_range.end > working_range.end {
504                        error!("Invalid range: {:?} > {:?}", cur_range, working_range);
505                        #[cfg(debug_assertions)]
506                        self.check_subset_ranges(&cur_range);
507                        internal_err!(
508                            "Current batch have data on the right side of working range, something is very wrong"
509                        )?;
510                    }
511                } else if cur_range.start < working_range.start {
512                    error!("Invalid range: {:?} < {:?}", cur_range, working_range);
513                    #[cfg(debug_assertions)]
514                    self.check_subset_ranges(&cur_range);
515                    internal_err!(
516                        "Current batch have data on the left side of working range, something is very wrong"
517                    )?;
518                }
519
520                if cur_range.is_subset(&working_range) {
521                    // data still in range, can't merge sort yet
522                    // see if can concat entire sorted rb, merge sort need to wait
523                    self.try_concat_batch(sorted_rb.clone(), &run_info, sort_column.options)?;
524                } else if let Some(intersection) = cur_range.intersection(&working_range) {
525                    // slice rb by intersection and concat it then merge sort
526                    let cur_sort_column = sort_column.values.slice(run_info.offset, run_info.len);
527                    let (offset, len) = find_slice_from_range(
528                        &SortColumn {
529                            values: cur_sort_column.clone(),
530                            options: sort_column.options,
531                        },
532                        &intersection,
533                    )?;
534
535                    if offset != 0 {
536                        internal_err!(
537                            "Current batch have data on the left side of working range, something is very wrong"
538                        )?;
539                    }
540
541                    let sliced_rb = sorted_rb.slice(offset, len);
542
543                    // try to concat the sliced input batch to the current `in_progress` run
544                    self.try_concat_batch(sliced_rb, &run_info, sort_column.options)?;
545                    // since no more sorted data in this range will come in now, build stream now
546                    self.build_sorted_stream()?;
547
548                    // since we are crossing working range, we need to merge sort the working set
549                    self.start_new_merge_sort()?;
550
551                    let (r_offset, r_len) = (offset + len, sorted_rb.num_rows() - offset - len);
552                    if r_len != 0 {
553                        // we have remaining data in this batch, put it back to input queue
554                        let remaining_rb = sorted_rb.slice(r_offset, r_len);
555                        let new_first_val = get_timestamp_from_idx(&cur_sort_column, r_offset)?;
556                        let new_run_info = SucRun {
557                            offset: run_info.offset + r_offset,
558                            len: r_len,
559                            first_val: new_first_val,
560                            last_val: run_info.last_val,
561                        };
562                        last_remaining = Some((remaining_rb, new_run_info));
563                    }
564                    // deal with remaining batch cross working range problem
565                    // i.e: this example require more slice, and we are currently at point A
566                    // |---1---|       |---3---|
567                    // |-------A--2------------|
568                    //  put the remaining batch back to iter and deal it in next loop
569                } else {
570                    // no overlap, we can merge sort the working set
571
572                    self.build_sorted_stream()?;
573                    self.start_new_merge_sort()?;
574
575                    // always put it back to input queue until some batch is in working range
576                    last_remaining = Some((sorted_rb, run_info));
577                }
578            }
579
580            // poll result stream again to see if we can emit more results
581            match self.poll_result_stream(cx) {
582                Poll::Ready(None) => {
583                    if self.is_terminated {
584                        return Poll::Ready(None);
585                    }
586                }
587                x => return x,
588            };
589        }
590        // emit the merge result after terminated(all input stream is done)
591        self.poll_result_stream(cx)
592    }
593
594    fn push_batch(&mut self, batch: DfRecordBatch) {
595        self.in_progress.push(batch);
596    }
597
598    /// Try to concat the input batch to the current `in_progress` run
599    ///
600    /// if the input batch is not sorted, build old run to stream and start a new run with new batch
601    fn try_concat_batch(
602        &mut self,
603        batch: DfRecordBatch,
604        run_info: &SucRun<Timestamp>,
605        opt: Option<SortOptions>,
606    ) -> datafusion_common::Result<()> {
607        let is_ok_to_concat =
608            cmp_with_opts(&self.last_value, &run_info.first_val, &opt) <= std::cmp::Ordering::Equal;
609
610        if is_ok_to_concat {
611            self.push_batch(batch);
612            // next time we get input batch might still be ordered, so not build stream yet
613        } else {
614            // no more sorted data, build stream now
615            self.build_sorted_stream()?;
616            self.push_batch(batch);
617        }
618        self.last_value = run_info.last_val;
619        Ok(())
620    }
621
622    /// Get the current working range
623    fn get_working_range(&self) -> Option<TimeRange> {
624        self.all_avail_working_range
625            .get(self.working_idx)
626            .map(|(range, _)| *range)
627    }
628
629    /// Set current working range to the next working range
630    fn set_next_working_range(&mut self) {
631        self.working_idx += 1;
632    }
633
634    /// make `in_progress` as a new `DfSendableRecordBatchStream` and put into `sorted_input_runs`
635    fn build_sorted_stream(&mut self) -> datafusion_common::Result<()> {
636        if self.in_progress.is_empty() {
637            return Ok(());
638        }
639        let data = std::mem::take(&mut self.in_progress);
640
641        let new_stream = MemoryStream::try_new(data, self.schema(), None)?;
642        self.sorted_input_runs.push(Box::pin(new_stream));
643        Ok(())
644    }
645
646    /// Start merging sort the current working set
647    fn start_new_merge_sort(&mut self) -> datafusion_common::Result<()> {
648        if !self.in_progress.is_empty() {
649            return internal_err!("Starting a merge sort when in_progress is not empty")?;
650        }
651
652        self.set_next_working_range();
653
654        let streams = std::mem::take(&mut self.sorted_input_runs);
655        if streams.is_empty() {
656            return Ok(());
657        } else if streams.len() == 1 {
658            self.merge_stream
659                .push_back(streams.into_iter().next().unwrap());
660            return Ok(());
661        }
662
663        let fetch = self.remaining_fetch();
664        let reservation = MemoryConsumer::new(format!("WindowedSortStream[{}]", self.merge_count))
665            .register(&self.memory_pool);
666        self.merge_count += 1;
667
668        let resulting_stream = StreamingMergeBuilder::new()
669            .with_streams(streams)
670            .with_schema(self.schema())
671            .with_expressions(&[self.expression.clone()].into())
672            .with_metrics(self.metrics.clone())
673            .with_batch_size(self.batch_size)
674            .with_fetch(fetch)
675            .with_reservation(reservation)
676            .build()?;
677        self.merge_stream.push_back(resulting_stream);
678        // this working range is done, move to next working range
679        Ok(())
680    }
681
682    /// Remaining number of rows to fetch, if no fetch limit, return None
683    /// if fetch limit is reached, return Some(0)
684    fn remaining_fetch(&self) -> Option<usize> {
685        let total_now = self.produced;
686        self.fetch.map(|p| p.saturating_sub(total_now))
687    }
688}
689
690impl Stream for WindowedSortStream {
691    type Item = datafusion_common::Result<DfRecordBatch>;
692
693    fn poll_next(
694        mut self: Pin<&mut Self>,
695        cx: &mut Context<'_>,
696    ) -> Poll<Option<datafusion_common::Result<DfRecordBatch>>> {
697        let result = self.as_mut().poll_next_inner(cx);
698        self.metrics.record_poll(result)
699    }
700}
701
702impl RecordBatchStream for WindowedSortStream {
703    fn schema(&self) -> SchemaRef {
704        self.schema.clone()
705    }
706}
707
708/// split batch to sorted runs
709fn split_batch_to_sorted_run(
710    batch: DfRecordBatch,
711    expression: &PhysicalSortExpr,
712) -> datafusion_common::Result<SortedRunSet<Timestamp>> {
713    // split input rb to sorted runs
714    let sort_column = expression.evaluate_to_sort_column(&batch)?;
715    let sorted_runs_offset = get_sorted_runs(sort_column.clone())?;
716    if let Some(run) = sorted_runs_offset.first()
717        && sorted_runs_offset.len() == 1
718    {
719        if !(run.offset == 0 && run.len == batch.num_rows()) {
720            internal_err!(
721                "Invalid run offset and length: offset = {:?}, len = {:?}, num_rows = {:?}",
722                run.offset,
723                run.len,
724                batch.num_rows()
725            )?;
726        }
727        // input rb is already sorted, we can emit it directly
728        Ok(SortedRunSet {
729            runs_with_batch: vec![(batch, run.clone())],
730            sort_column,
731        })
732    } else {
733        // those slice should be zero copy, so supposedly no new reservation needed
734        let mut ret = Vec::with_capacity(sorted_runs_offset.len());
735        for run in sorted_runs_offset {
736            if run.offset + run.len > batch.num_rows() {
737                internal_err!(
738                    "Invalid run offset and length: offset = {:?}, len = {:?}, num_rows = {:?}",
739                    run.offset,
740                    run.len,
741                    batch.num_rows()
742                )?;
743            }
744            let new_rb = batch.slice(run.offset, run.len);
745            ret.push((new_rb, run));
746        }
747        Ok(SortedRunSet {
748            runs_with_batch: ret,
749            sort_column,
750        })
751    }
752}
753
754/// Downcast a temporal array to a specific type
755///
756/// usage similar to `downcast_primitive!` in `arrow-array` crate
757#[macro_export]
758macro_rules! downcast_ts_array {
759    ($data_type:expr => ($m:path $(, $args:tt)*), $($p:pat => $fallback:expr $(,)*)*) =>
760    {
761        match $data_type {
762            arrow_schema::DataType::Timestamp(arrow_schema::TimeUnit::Second, _) => {
763                $m!(arrow::datatypes::TimestampSecondType, arrow_schema::TimeUnit::Second $(, $args)*)
764            }
765            arrow_schema::DataType::Timestamp(arrow_schema::TimeUnit::Millisecond, _) => {
766                $m!(arrow::datatypes::TimestampMillisecondType, arrow_schema::TimeUnit::Millisecond $(, $args)*)
767            }
768            arrow_schema::DataType::Timestamp(arrow_schema::TimeUnit::Microsecond, _) => {
769                $m!(arrow::datatypes::TimestampMicrosecondType, arrow_schema::TimeUnit::Microsecond $(, $args)*)
770            }
771            arrow_schema::DataType::Timestamp(arrow_schema::TimeUnit::Nanosecond, _) => {
772                $m!(arrow::datatypes::TimestampNanosecondType, arrow_schema::TimeUnit::Nanosecond $(, $args)*)
773            }
774            $($p => $fallback,)*
775        }
776    };
777}
778
779/// Find the slice(where start <= data < end and sort by `sort_column.options`) from the given range
780///
781/// Return the offset and length of the slice
782fn find_slice_from_range(
783    sort_column: &SortColumn,
784    range: &TimeRange,
785) -> datafusion_common::Result<(usize, usize)> {
786    let time_unit = sort_timestamp_unit(sort_column.values.data_type())?;
787    let array = &sort_column.values;
788    let opt = &sort_column.options.unwrap_or_default();
789    let descending = opt.descending;
790    let range = convert_time_range_for_sort(range, time_unit)?;
791
792    let typed_sorted_range = [range.start, range.end]
793        .iter()
794        .map(|t| {
795            t.convert_to(time_unit.into())
796                .ok_or_else(|| {
797                    DataFusionError::Internal(format!(
798                        "Failed to convert timestamp from {:?} to {:?}",
799                        t.unit(),
800                        time_unit
801                    ))
802                })
803                .and_then(|typed_ts| {
804                    let value = Value::Timestamp(typed_ts);
805                    value
806                        .try_to_scalar_value(&value.data_type())
807                        .map_err(|e| DataFusionError::External(Box::new(e) as _))
808                })
809        })
810        .collect::<std::result::Result<Vec<_>, _>>()?;
811
812    let (min_val, max_val) = (typed_sorted_range[0].clone(), typed_sorted_range[1].clone());
813
814    // get slice that in which all data that `min_val<=data<max_val`
815    let (start, end) = if descending {
816        // note that `data < max_val`
817        // i,e, for max_val = 4, array = [5,3,2] should be start=1
818        // max_val = 4, array = [5, 4, 3, 2] should be start= 2
819        let start = bisect::<false>(from_ref(array), from_ref(&max_val), &[*opt])?;
820        // min_val = 1, array = [3, 2, 1, 0], end = 3
821        // min_val = 1, array = [3, 2, 0], end = 2
822        let end = bisect::<false>(from_ref(array), from_ref(&min_val), &[*opt])?;
823        (start, end)
824    } else {
825        // min_val = 1, array = [1, 2, 3], start = 0
826        // min_val = 1, array = [0, 2, 3], start = 1
827        let start = bisect::<true>(from_ref(array), from_ref(&min_val), &[*opt])?;
828        // max_val = 3, array = [1, 3, 4], end = 1
829        // max_val = 3, array = [1, 2, 4], end = 2
830        let end = bisect::<true>(from_ref(array), from_ref(&max_val), &[*opt])?;
831        (start, end)
832    };
833
834    Ok((start, end - start))
835}
836
837fn sort_timestamp_unit(data_type: &DataType) -> datafusion_common::Result<arrow_schema::TimeUnit> {
838    if let DataType::Timestamp(unit, _) = data_type {
839        Ok(*unit)
840    } else {
841        Err(DataFusionError::Internal(format!(
842            "Unsupported sort column type: {data_type}"
843        )))
844    }
845}
846
847#[derive(Debug, Clone, Copy)]
848enum RangeBoundKind {
849    InclusiveStart,
850    ExclusiveEnd,
851}
852
853fn convert_time_range_for_sort(
854    range: &TimeRange,
855    time_unit: arrow_schema::TimeUnit,
856) -> datafusion_common::Result<TimeRange> {
857    let target_unit = time_unit.into();
858    Ok(TimeRange::new(
859        convert_timestamp_range_bound(range.start, target_unit, RangeBoundKind::InclusiveStart)?,
860        convert_timestamp_range_bound(range.end, target_unit, RangeBoundKind::ExclusiveEnd)?,
861    ))
862}
863
864fn convert_timestamp_range_bound(
865    timestamp: Timestamp,
866    target_unit: TimestampUnit,
867    bound_kind: RangeBoundKind,
868) -> datafusion_common::Result<Timestamp> {
869    let converted = match bound_kind {
870        RangeBoundKind::InclusiveStart => timestamp.convert_to(target_unit),
871        RangeBoundKind::ExclusiveEnd => timestamp.convert_to_ceil(target_unit),
872    };
873
874    converted.ok_or_else(|| {
875        DataFusionError::Internal(format!(
876            "Failed to convert timestamp from {:?} to {:?}",
877            timestamp.unit(),
878            target_unit
879        ))
880    })
881}
882
883pub(crate) fn project_partition_range_for_sort(
884    range: PartitionRange,
885    sort_data_type: &DataType,
886) -> datafusion_common::Result<PartitionRange> {
887    let target_unit = sort_timestamp_unit(sort_data_type)?.into();
888    Ok(PartitionRange {
889        start: convert_timestamp_range_bound(
890            range.start,
891            target_unit,
892            RangeBoundKind::InclusiveStart,
893        )?,
894        end: convert_timestamp_range_bound(range.end, target_unit, RangeBoundKind::ExclusiveEnd)?,
895        ..range
896    })
897}
898
899fn discrete_exclusive_end(timestamp: Timestamp) -> Timestamp {
900    Timestamp::new(timestamp.value() + 1, timestamp.unit())
901}
902
903/// Get an iterator from a primitive array.
904///
905/// Used with `downcast_ts_array`. The returned iter is wrapped with `.enumerate()`.
906#[macro_export]
907macro_rules! array_iter_helper {
908    ($t:ty, $unit:expr, $arr:expr) => {{
909        let typed = $arr
910            .as_any()
911            .downcast_ref::<arrow::array::PrimitiveArray<$t>>()
912            .unwrap();
913        let iter = typed.iter().enumerate();
914        Box::new(iter) as Box<dyn Iterator<Item = (usize, Option<i64>)>>
915    }};
916}
917
918/// Compare with options, note None is considered as NULL here
919///
920/// default to null first
921fn cmp_with_opts<T: Ord>(
922    a: &Option<T>,
923    b: &Option<T>,
924    opt: &Option<SortOptions>,
925) -> std::cmp::Ordering {
926    let opt = opt.unwrap_or_default();
927
928    if let (Some(a), Some(b)) = (a, b) {
929        if opt.descending { b.cmp(a) } else { a.cmp(b) }
930    } else if opt.nulls_first {
931        // now we know at leatst one of them is None
932        // in rust None < Some(_)
933        a.cmp(b)
934    } else {
935        match (a, b) {
936            (Some(a), Some(b)) => a.cmp(b),
937            (Some(_), None) => std::cmp::Ordering::Less,
938            (None, Some(_)) => std::cmp::Ordering::Greater,
939            (None, None) => std::cmp::Ordering::Equal,
940        }
941    }
942}
943
944#[derive(Debug, Clone)]
945struct SortedRunSet<N: Ord> {
946    /// sorted runs with batch corresponding to them
947    runs_with_batch: Vec<(DfRecordBatch, SucRun<N>)>,
948    /// sorted column from eval sorting expr
949    sort_column: SortColumn,
950}
951
952/// A struct to represent a successive run in the input iterator
953#[derive(Debug, Clone, PartialEq)]
954struct SucRun<N: Ord> {
955    /// offset of the first element in the run
956    offset: usize,
957    /// length of the run
958    len: usize,
959    /// first non-null value in the run
960    first_val: Option<N>,
961    /// last non-null value in the run
962    last_val: Option<N>,
963}
964
965impl SucRun<Timestamp> {
966    /// Get the time range of the run, which is [min_val, max_val + 1)
967    fn get_time_range(&self) -> Option<TimeRange> {
968        let start = self.first_val.min(self.last_val);
969        let end = self
970            .first_val
971            .max(self.last_val)
972            .map(discrete_exclusive_end);
973        start.zip(end).map(|(s, e)| TimeRange::new(s, e))
974    }
975}
976
977/// find all successive runs in the input iterator
978fn find_successive_runs<T: Iterator<Item = (usize, Option<N>)>, N: Ord + Copy>(
979    iter: T,
980    sort_opts: &Option<SortOptions>,
981) -> Vec<SucRun<N>> {
982    let mut runs = Vec::new();
983    let mut last_value = None;
984    let mut iter_len = None;
985
986    let mut last_offset = 0;
987    let mut first_val: Option<N> = None;
988    let mut last_val: Option<N> = None;
989
990    for (idx, t) in iter {
991        if let Some(last_value) = &last_value
992            && cmp_with_opts(last_value, &t, sort_opts) == std::cmp::Ordering::Greater
993        {
994            // we found a boundary
995            let len = idx - last_offset;
996            let run = SucRun {
997                offset: last_offset,
998                len,
999                first_val,
1000                last_val,
1001            };
1002            runs.push(run);
1003            first_val = None;
1004            last_val = None;
1005
1006            last_offset = idx;
1007        }
1008        last_value = Some(t);
1009        if let Some(t) = t {
1010            first_val = first_val.or(Some(t));
1011            last_val = Some(t).or(last_val);
1012        }
1013        iter_len = Some(idx);
1014    }
1015    let run = SucRun {
1016        offset: last_offset,
1017        len: iter_len.map(|l| l - last_offset + 1).unwrap_or(0),
1018        first_val,
1019        last_val,
1020    };
1021    runs.push(run);
1022
1023    runs
1024}
1025
1026/// return a list of non-overlapping (offset, length) which represent sorted runs, and
1027/// can be used to call [`DfRecordBatch::slice`] to get sorted runs
1028/// Returned runs will be as long as possible, and will not overlap with each other
1029fn get_sorted_runs(sort_column: SortColumn) -> datafusion_common::Result<Vec<SucRun<Timestamp>>> {
1030    let ty = sort_column.values.data_type();
1031    if let DataType::Timestamp(unit, _) = ty {
1032        let array = &sort_column.values;
1033        let iter = downcast_ts_array!(
1034            array.data_type() => (array_iter_helper, array),
1035            _ => internal_err!("Unsupported sort column type: {ty}")?
1036        );
1037
1038        let raw = find_successive_runs(iter, &sort_column.options);
1039        let ts_runs = raw
1040            .into_iter()
1041            .map(|run| SucRun {
1042                offset: run.offset,
1043                len: run.len,
1044                first_val: run.first_val.map(|v| Timestamp::new(v, unit.into())),
1045                last_val: run.last_val.map(|v| Timestamp::new(v, unit.into())),
1046            })
1047            .collect_vec();
1048        Ok(ts_runs)
1049    } else {
1050        Err(DataFusionError::Internal(format!(
1051            "Unsupported sort column type: {ty}"
1052        )))
1053    }
1054}
1055
1056/// Left(`start`) inclusive right(`end`) exclusive,
1057///
1058/// This is just tuple with extra methods
1059#[derive(Debug, Clone, Default, Copy, PartialEq, Eq, PartialOrd, Ord)]
1060struct TimeRange {
1061    start: Timestamp,
1062    end: Timestamp,
1063}
1064
1065impl From<&PartitionRange> for TimeRange {
1066    fn from(range: &PartitionRange) -> Self {
1067        Self::new(range.start, range.end)
1068    }
1069}
1070
1071impl From<(Timestamp, Timestamp)> for TimeRange {
1072    fn from(range: (Timestamp, Timestamp)) -> Self {
1073        Self::new(range.0, range.1)
1074    }
1075}
1076
1077impl From<&(Timestamp, Timestamp)> for TimeRange {
1078    fn from(range: &(Timestamp, Timestamp)) -> Self {
1079        Self::new(range.0, range.1)
1080    }
1081}
1082
1083impl TimeRange {
1084    /// Create a new TimeRange, if start is greater than end, swap them
1085    fn new(start: Timestamp, end: Timestamp) -> Self {
1086        if start > end {
1087            Self {
1088                start: end,
1089                end: start,
1090            }
1091        } else {
1092            Self { start, end }
1093        }
1094    }
1095
1096    fn is_subset(&self, other: &Self) -> bool {
1097        self.start >= other.start && self.end <= other.end
1098    }
1099
1100    /// Check if two ranges are overlapping, exclusive(meaning if only boundary is overlapped then range is not overlapping)
1101    fn is_overlapping(&self, other: &Self) -> bool {
1102        !(self.start >= other.end || self.end <= other.start)
1103    }
1104
1105    fn intersection(&self, other: &Self) -> Option<Self> {
1106        if self.is_overlapping(other) {
1107            Some(Self::new(
1108                self.start.max(other.start),
1109                self.end.min(other.end),
1110            ))
1111        } else {
1112            None
1113        }
1114    }
1115
1116    fn difference(&self, other: &Self) -> Vec<Self> {
1117        if !self.is_overlapping(other) {
1118            vec![*self]
1119        } else {
1120            let mut ret = Vec::new();
1121            if self.start < other.start && self.end > other.end {
1122                ret.push(Self::new(self.start, other.start));
1123                ret.push(Self::new(other.end, self.end));
1124            } else if self.start < other.start {
1125                ret.push(Self::new(self.start, other.start));
1126            } else if self.end > other.end {
1127                ret.push(Self::new(other.end, self.end));
1128            }
1129            ret
1130        }
1131    }
1132}
1133
1134/// split input range by `split_by` range to one, two or three parts.
1135fn split_range_by(
1136    input_range: &TimeRange,
1137    input_parts: &[usize],
1138    split_by: &TimeRange,
1139    split_idx: usize,
1140) -> Vec<Action> {
1141    let mut ret = Vec::new();
1142    if input_range.is_overlapping(split_by) {
1143        let input_parts = input_parts.to_vec();
1144        let new_parts = {
1145            let mut new_parts = input_parts.clone();
1146            new_parts.push(split_idx);
1147            new_parts
1148        };
1149
1150        ret.push(Action::Pop(*input_range));
1151        if let Some(intersection) = input_range.intersection(split_by) {
1152            ret.push(Action::Push(intersection, new_parts.clone()));
1153        }
1154        for diff in input_range.difference(split_by) {
1155            ret.push(Action::Push(diff, input_parts.clone()));
1156        }
1157    }
1158    ret
1159}
1160
1161#[derive(Debug, Clone, PartialEq, Eq)]
1162enum Action {
1163    Pop(TimeRange),
1164    Push(TimeRange, Vec<usize>),
1165}
1166
1167/// Compute all working ranges and corresponding working sets from given `overlap_counts` computed from `split_overlapping_ranges`
1168///
1169/// working ranges promise once input stream get a value out of current range, future values will never be in this range
1170///
1171/// hence we can merge sort current working range once that happens
1172///
1173/// if `descending` is true, the working ranges will be in descending order
1174fn compute_all_working_ranges(
1175    overlap_counts: &BTreeMap<TimeRange, Vec<usize>>,
1176    descending: bool,
1177) -> Vec<(TimeRange, BTreeSet<usize>)> {
1178    let mut ret = Vec::new();
1179    let mut cur_range_set: Option<(TimeRange, BTreeSet<usize>)> = None;
1180    let overlap_iter: Box<dyn Iterator<Item = (&TimeRange, &Vec<usize>)>> = if descending {
1181        Box::new(overlap_counts.iter().rev()) as _
1182    } else {
1183        Box::new(overlap_counts.iter()) as _
1184    };
1185    for (range, set) in overlap_iter {
1186        match &mut cur_range_set {
1187            None => cur_range_set = Some((*range, BTreeSet::from_iter(set.iter().cloned()))),
1188            Some((working_range, working_set)) => {
1189                // if next overlap range have Partition that's is not last one in `working_set`(hence need
1190                // to be read before merge sorting), and `working_set` have >1 count
1191                // we have to expand current working range to cover it(and add it's `set` to `working_set`)
1192                // so that merge sort is possible
1193                let need_expand = {
1194                    let last_part = working_set.last();
1195                    let inter: BTreeSet<usize> = working_set
1196                        .intersection(&BTreeSet::from_iter(set.iter().cloned()))
1197                        .cloned()
1198                        .collect();
1199                    if let Some(one) = inter.first()
1200                        && inter.len() == 1
1201                        && Some(one) == last_part
1202                    {
1203                        // if only the last PartitionRange in current working set, we can just emit it so no need to expand working range
1204                        if set.iter().all(|p| Some(p) >= last_part) {
1205                            // if all PartitionRange in next overlap range is after the last one in current working set, we can just emit current working set
1206                            false
1207                        } else {
1208                            // elsewise, we need to expand working range to include next overlap range
1209                            true
1210                        }
1211                    } else if inter.is_empty() {
1212                        // if no common PartitionRange in current working set and next overlap range, we can just emit current working set
1213                        false
1214                    } else {
1215                        // have multiple intersection or intersection is not the last part, either way we need to expand working range to include next overlap range
1216                        true
1217                    }
1218                };
1219
1220                if need_expand {
1221                    if descending {
1222                        working_range.start = range.start;
1223                    } else {
1224                        working_range.end = range.end;
1225                    }
1226                    working_set.extend(set.iter().cloned());
1227                } else {
1228                    ret.push((*working_range, std::mem::take(working_set)));
1229                    cur_range_set = Some((*range, BTreeSet::from_iter(set.iter().cloned())));
1230                }
1231            }
1232        }
1233    }
1234
1235    if let Some(cur_range_set) = cur_range_set {
1236        ret.push(cur_range_set)
1237    }
1238
1239    ret
1240}
1241
1242/// return a map of non-overlapping ranges and their corresponding index
1243/// (not `PartitionRange.identifier` but position in array) in the input `PartitionRange`s that is in those ranges
1244fn split_overlapping_ranges(ranges: &[PartitionRange]) -> BTreeMap<TimeRange, Vec<usize>> {
1245    // invariant: the key ranges should not overlapping with each other by definition from `is_overlapping`
1246    let mut ret: BTreeMap<TimeRange, Vec<usize>> = BTreeMap::new();
1247    for (idx, range) in ranges.iter().enumerate() {
1248        let key: TimeRange = (range.start, range.end).into();
1249        let mut actions = Vec::new();
1250        let mut untouched = vec![key];
1251        // create a forward and backward iterator to find all overlapping ranges
1252        // given that tuple is sorted in lexicographical order and promise to not overlap,
1253        // since range is sorted that way, we can stop when we find a non-overlapping range
1254        let forward_iter = ret
1255            .range(key..)
1256            .take_while(|(range, _)| range.is_overlapping(&key));
1257        let backward_iter = ret
1258            .range(..key)
1259            .rev()
1260            .take_while(|(range, _)| range.is_overlapping(&key));
1261
1262        for (range, parts) in forward_iter.chain(backward_iter) {
1263            untouched = untouched.iter().flat_map(|r| r.difference(range)).collect();
1264            let act = split_range_by(range, parts, &key, idx);
1265            actions.extend(act);
1266        }
1267
1268        for action in actions {
1269            match action {
1270                Action::Pop(range) => {
1271                    ret.remove(&range);
1272                }
1273                Action::Push(range, parts) => {
1274                    ret.insert(range, parts);
1275                }
1276            }
1277        }
1278
1279        // insert untouched ranges
1280        for range in untouched {
1281            ret.insert(range, vec![idx]);
1282        }
1283    }
1284    ret
1285}
1286
1287/// Get timestamp from array at offset
1288fn get_timestamp_from_idx(
1289    array: &ArrayRef,
1290    offset: usize,
1291) -> datafusion_common::Result<Option<Timestamp>> {
1292    let time_unit = if let DataType::Timestamp(unit, _) = array.data_type() {
1293        unit
1294    } else {
1295        return Err(DataFusionError::Internal(format!(
1296            "Unsupported sort column type: {}",
1297            array.data_type()
1298        )));
1299    };
1300    let ty = array.data_type();
1301    let array = array.slice(offset, 1);
1302    let mut iter = downcast_ts_array!(
1303        array.data_type() => (array_iter_helper, array),
1304        _ => internal_err!("Unsupported sort column type: {ty}")?
1305    );
1306    let (_idx, val) = iter.next().ok_or_else(|| {
1307        DataFusionError::Internal("Empty array in get_timestamp_from".to_string())
1308    })?;
1309    let val = if let Some(val) = val {
1310        val
1311    } else {
1312        return Ok(None);
1313    };
1314    let gt_timestamp = Timestamp::new(val, time_unit.into());
1315    Ok(Some(gt_timestamp))
1316}
1317
1318#[cfg(test)]
1319mod test {
1320    use std::io::Write;
1321    use std::sync::Arc;
1322
1323    use arrow::array::{ArrayRef, TimestampMillisecondArray};
1324    use arrow::compute::concat_batches;
1325    use arrow::json::ArrayWriter;
1326    use arrow_schema::{Field, Schema, TimeUnit};
1327    use futures::StreamExt;
1328    use pretty_assertions::assert_eq;
1329    use serde_json::json;
1330
1331    use super::*;
1332    use crate::test_util::{MockInputExec, new_ts_array};
1333
1334    // Test helpers to reduce duplication
1335    mod helpers {
1336        use datafusion::physical_plan::expressions::Column;
1337
1338        use super::*;
1339
1340        pub fn default_sort_opts(descending: bool) -> SortOptions {
1341            SortOptions {
1342                descending,
1343                nulls_first: true,
1344            }
1345        }
1346
1347        pub fn ts_field(unit: TimeUnit) -> Field {
1348            Field::new("ts", DataType::Timestamp(unit, None), false)
1349        }
1350
1351        pub fn ts_column() -> Column {
1352            Column::new("ts", 0)
1353        }
1354
1355        pub fn partition_range(start: i64, end: i64, num_rows: usize, id: usize) -> PartitionRange {
1356            PartitionRange {
1357                start: Timestamp::new_millisecond(start),
1358                end: Timestamp::new_millisecond(end),
1359                num_rows,
1360                identifier: id,
1361            }
1362        }
1363
1364        pub fn ts_array(values: impl IntoIterator<Item = i64>) -> ArrayRef {
1365            Arc::new(TimestampMillisecondArray::from_iter_values(values))
1366        }
1367    }
1368
1369    #[test]
1370    fn test_overlapping() {
1371        let testcases = [
1372            (
1373                (Timestamp::new_second(1), Timestamp::new_millisecond(1001)),
1374                (Timestamp::new_second(0), Timestamp::new_millisecond(1)),
1375                false,
1376            ),
1377            (
1378                (Timestamp::new_second(1), Timestamp::new_millisecond(1001)),
1379                (Timestamp::new_second(0), Timestamp::new_millisecond(1001)),
1380                true,
1381            ),
1382            (
1383                (Timestamp::new_second(1), Timestamp::new_millisecond(1001)),
1384                (Timestamp::new_second(0), Timestamp::new_millisecond(1002)),
1385                true,
1386            ),
1387            (
1388                (Timestamp::new_second(1), Timestamp::new_millisecond(1001)),
1389                (
1390                    Timestamp::new_millisecond(1000),
1391                    Timestamp::new_millisecond(1002),
1392                ),
1393                true,
1394            ),
1395            (
1396                (Timestamp::new_second(1), Timestamp::new_millisecond(1001)),
1397                (
1398                    Timestamp::new_millisecond(1001),
1399                    Timestamp::new_millisecond(1002),
1400                ),
1401                false,
1402            ),
1403            (
1404                (Timestamp::new_second(1), Timestamp::new_millisecond(1001)),
1405                (
1406                    Timestamp::new_millisecond(1002),
1407                    Timestamp::new_millisecond(1003),
1408                ),
1409                false,
1410            ),
1411        ];
1412
1413        for (range1, range2, expected) in testcases.iter() {
1414            assert_eq!(
1415                TimeRange::from(range1).is_overlapping(&range2.into()),
1416                *expected,
1417                "range1: {:?}, range2: {:?}",
1418                range1,
1419                range2
1420            );
1421        }
1422    }
1423
1424    #[test]
1425    fn test_split() {
1426        let testcases = [
1427            // no split
1428            (
1429                (Timestamp::new_second(1), Timestamp::new_millisecond(1001)),
1430                vec![0],
1431                (Timestamp::new_second(0), Timestamp::new_millisecond(1)),
1432                1,
1433                vec![],
1434            ),
1435            // one part
1436            (
1437                (Timestamp::new_second(1), Timestamp::new_millisecond(1001)),
1438                vec![0],
1439                (Timestamp::new_second(0), Timestamp::new_millisecond(1001)),
1440                1,
1441                vec![
1442                    Action::Pop(
1443                        (Timestamp::new_second(1), Timestamp::new_millisecond(1001)).into(),
1444                    ),
1445                    Action::Push(
1446                        (Timestamp::new_second(1), Timestamp::new_millisecond(1001)).into(),
1447                        vec![0, 1],
1448                    ),
1449                ],
1450            ),
1451            (
1452                (Timestamp::new_second(1), Timestamp::new_millisecond(1001)),
1453                vec![0],
1454                (Timestamp::new_second(0), Timestamp::new_millisecond(1002)),
1455                1,
1456                vec![
1457                    Action::Pop(
1458                        (Timestamp::new_second(1), Timestamp::new_millisecond(1001)).into(),
1459                    ),
1460                    Action::Push(
1461                        (Timestamp::new_second(1), Timestamp::new_millisecond(1001)).into(),
1462                        vec![0, 1],
1463                    ),
1464                ],
1465            ),
1466            (
1467                (Timestamp::new_second(1), Timestamp::new_millisecond(1001)),
1468                vec![0],
1469                (
1470                    Timestamp::new_millisecond(1000),
1471                    Timestamp::new_millisecond(1002),
1472                ),
1473                1,
1474                vec![
1475                    Action::Pop(
1476                        (Timestamp::new_second(1), Timestamp::new_millisecond(1001)).into(),
1477                    ),
1478                    Action::Push(
1479                        (
1480                            Timestamp::new_millisecond(1000),
1481                            Timestamp::new_millisecond(1001),
1482                        )
1483                            .into(),
1484                        vec![0, 1],
1485                    ),
1486                ],
1487            ),
1488            // two part
1489            (
1490                (Timestamp::new_second(1), Timestamp::new_millisecond(1002)),
1491                vec![0],
1492                (
1493                    Timestamp::new_millisecond(1001),
1494                    Timestamp::new_millisecond(1002),
1495                ),
1496                1,
1497                vec![
1498                    Action::Pop(
1499                        (Timestamp::new_second(1), Timestamp::new_millisecond(1002)).into(),
1500                    ),
1501                    Action::Push(
1502                        (
1503                            Timestamp::new_millisecond(1001),
1504                            Timestamp::new_millisecond(1002),
1505                        )
1506                            .into(),
1507                        vec![0, 1],
1508                    ),
1509                    Action::Push(
1510                        (Timestamp::new_second(1), Timestamp::new_millisecond(1001)).into(),
1511                        vec![0],
1512                    ),
1513                ],
1514            ),
1515            // three part
1516            (
1517                (Timestamp::new_second(1), Timestamp::new_millisecond(1004)),
1518                vec![0],
1519                (
1520                    Timestamp::new_millisecond(1001),
1521                    Timestamp::new_millisecond(1002),
1522                ),
1523                1,
1524                vec![
1525                    Action::Pop(
1526                        (Timestamp::new_second(1), Timestamp::new_millisecond(1004)).into(),
1527                    ),
1528                    Action::Push(
1529                        (
1530                            Timestamp::new_millisecond(1001),
1531                            Timestamp::new_millisecond(1002),
1532                        )
1533                            .into(),
1534                        vec![0, 1],
1535                    ),
1536                    Action::Push(
1537                        (Timestamp::new_second(1), Timestamp::new_millisecond(1001)).into(),
1538                        vec![0],
1539                    ),
1540                    Action::Push(
1541                        (
1542                            Timestamp::new_millisecond(1002),
1543                            Timestamp::new_millisecond(1004),
1544                        )
1545                            .into(),
1546                        vec![0],
1547                    ),
1548                ],
1549            ),
1550        ];
1551
1552        for (range, parts, split_by, split_idx, expected) in testcases.iter() {
1553            assert_eq!(
1554                split_range_by(&(*range).into(), parts, &split_by.into(), *split_idx),
1555                *expected,
1556                "range: {:?}, parts: {:?}, split_by: {:?}, split_idx: {}",
1557                range,
1558                parts,
1559                split_by,
1560                split_idx
1561            );
1562        }
1563    }
1564
1565    #[test]
1566    fn test_project_partition_range_for_sort_uses_ceil_on_exclusive_end() {
1567        let range = PartitionRange {
1568            start: Timestamp::new_nanosecond(1_000_000),
1569            end: Timestamp::new_nanosecond(1_000_001),
1570            num_rows: 1,
1571            identifier: 0,
1572        };
1573
1574        let projected = project_partition_range_for_sort(
1575            range,
1576            &DataType::Timestamp(TimeUnit::Millisecond, None),
1577        )
1578        .unwrap();
1579
1580        assert_eq!(Timestamp::new_millisecond(1), projected.start);
1581        assert_eq!(Timestamp::new_millisecond(2), projected.end);
1582    }
1583
1584    #[test]
1585    fn test_find_slice_from_range_preserves_last_row_after_precision_drop() {
1586        let sort_column = SortColumn {
1587            values: Arc::new(TimestampMillisecondArray::from_iter_values([1])) as ArrayRef,
1588            options: Some(SortOptions::default()),
1589        };
1590        let range = TimeRange::new(
1591            Timestamp::new_nanosecond(1_000_000),
1592            Timestamp::new_nanosecond(1_000_001),
1593        );
1594
1595        assert_eq!((0, 1), find_slice_from_range(&sort_column, &range).unwrap());
1596    }
1597
1598    #[test]
1599    fn test_discrete_exclusive_end_creates_half_open_upper_bound() {
1600        let timestamp = Timestamp::new_millisecond(42);
1601
1602        assert_eq!(
1603            Timestamp::new_millisecond(43),
1604            discrete_exclusive_end(timestamp)
1605        );
1606    }
1607
1608    #[allow(clippy::type_complexity)]
1609    fn run_compute_working_ranges_test(
1610        testcases: Vec<(
1611            BTreeMap<(Timestamp, Timestamp), Vec<usize>>,
1612            Vec<((Timestamp, Timestamp), BTreeSet<usize>)>,
1613        )>,
1614        descending: bool,
1615    ) {
1616        for (input, expected) in testcases {
1617            let expected = expected
1618                .into_iter()
1619                .map(|(r, s)| (r.into(), s))
1620                .collect_vec();
1621            let input = input.into_iter().map(|(r, s)| (r.into(), s)).collect();
1622            assert_eq!(
1623                compute_all_working_ranges(&input, descending),
1624                expected,
1625                "input: {:?}, descending: {}",
1626                input,
1627                descending
1628            );
1629        }
1630    }
1631
1632    #[test]
1633    fn test_compute_working_ranges_descending() {
1634        let testcases = vec![
1635            (
1636                BTreeMap::from([(
1637                    (Timestamp::new_second(1), Timestamp::new_second(2)),
1638                    vec![0],
1639                )]),
1640                vec![(
1641                    (Timestamp::new_second(1), Timestamp::new_second(2)),
1642                    BTreeSet::from([0]),
1643                )],
1644            ),
1645            (
1646                BTreeMap::from([(
1647                    (Timestamp::new_second(1), Timestamp::new_second(2)),
1648                    vec![0, 1],
1649                )]),
1650                vec![(
1651                    (Timestamp::new_second(1), Timestamp::new_second(2)),
1652                    BTreeSet::from([0, 1]),
1653                )],
1654            ),
1655            (
1656                BTreeMap::from([
1657                    (
1658                        (Timestamp::new_second(2), Timestamp::new_second(3)),
1659                        vec![0],
1660                    ),
1661                    (
1662                        (Timestamp::new_second(1), Timestamp::new_second(2)),
1663                        vec![0, 1],
1664                    ),
1665                ]),
1666                vec![
1667                    (
1668                        (Timestamp::new_second(2), Timestamp::new_second(3)),
1669                        BTreeSet::from([0]),
1670                    ),
1671                    (
1672                        (Timestamp::new_second(1), Timestamp::new_second(2)),
1673                        BTreeSet::from([0, 1]),
1674                    ),
1675                ],
1676            ),
1677            (
1678                BTreeMap::from([
1679                    (
1680                        (Timestamp::new_second(2), Timestamp::new_second(3)),
1681                        vec![0, 1],
1682                    ),
1683                    (
1684                        (Timestamp::new_second(1), Timestamp::new_second(2)),
1685                        vec![1],
1686                    ),
1687                ]),
1688                vec![
1689                    (
1690                        (Timestamp::new_second(2), Timestamp::new_second(3)),
1691                        BTreeSet::from([0, 1]),
1692                    ),
1693                    (
1694                        (Timestamp::new_second(1), Timestamp::new_second(2)),
1695                        BTreeSet::from([1]),
1696                    ),
1697                ],
1698            ),
1699            (
1700                BTreeMap::from([
1701                    (
1702                        (Timestamp::new_second(3), Timestamp::new_second(4)),
1703                        vec![0],
1704                    ),
1705                    (
1706                        (Timestamp::new_second(2), Timestamp::new_second(3)),
1707                        vec![0, 1],
1708                    ),
1709                    (
1710                        (Timestamp::new_second(1), Timestamp::new_second(2)),
1711                        vec![1],
1712                    ),
1713                ]),
1714                vec![
1715                    (
1716                        (Timestamp::new_second(3), Timestamp::new_second(4)),
1717                        BTreeSet::from([0]),
1718                    ),
1719                    (
1720                        (Timestamp::new_second(2), Timestamp::new_second(3)),
1721                        BTreeSet::from([0, 1]),
1722                    ),
1723                    (
1724                        (Timestamp::new_second(1), Timestamp::new_second(2)),
1725                        BTreeSet::from([1]),
1726                    ),
1727                ],
1728            ),
1729            (
1730                BTreeMap::from([
1731                    (
1732                        (Timestamp::new_second(3), Timestamp::new_second(4)),
1733                        vec![0, 2],
1734                    ),
1735                    (
1736                        (Timestamp::new_second(2), Timestamp::new_second(3)),
1737                        vec![0, 1, 2],
1738                    ),
1739                    (
1740                        (Timestamp::new_second(1), Timestamp::new_second(2)),
1741                        vec![1, 2],
1742                    ),
1743                ]),
1744                vec![(
1745                    (Timestamp::new_second(1), Timestamp::new_second(4)),
1746                    BTreeSet::from([0, 1, 2]),
1747                )],
1748            ),
1749            (
1750                BTreeMap::from([
1751                    (
1752                        (Timestamp::new_second(2), Timestamp::new_second(3)),
1753                        vec![0, 2],
1754                    ),
1755                    (
1756                        (Timestamp::new_second(1), Timestamp::new_second(2)),
1757                        vec![1, 2],
1758                    ),
1759                ]),
1760                vec![(
1761                    (Timestamp::new_second(1), Timestamp::new_second(3)),
1762                    BTreeSet::from([0, 1, 2]),
1763                )],
1764            ),
1765            (
1766                BTreeMap::from([
1767                    (
1768                        (Timestamp::new_second(3), Timestamp::new_second(4)),
1769                        vec![0, 1],
1770                    ),
1771                    (
1772                        (Timestamp::new_second(2), Timestamp::new_second(3)),
1773                        vec![0, 1, 2],
1774                    ),
1775                    (
1776                        (Timestamp::new_second(1), Timestamp::new_second(2)),
1777                        vec![1, 2],
1778                    ),
1779                ]),
1780                vec![(
1781                    (Timestamp::new_second(1), Timestamp::new_second(4)),
1782                    BTreeSet::from([0, 1, 2]),
1783                )],
1784            ),
1785            (
1786                BTreeMap::from([
1787                    (
1788                        (Timestamp::new_second(2), Timestamp::new_second(3)),
1789                        vec![0, 1],
1790                    ),
1791                    (
1792                        (Timestamp::new_second(1), Timestamp::new_second(2)),
1793                        vec![1, 2],
1794                    ),
1795                ]),
1796                vec![
1797                    (
1798                        (Timestamp::new_second(2), Timestamp::new_second(3)),
1799                        BTreeSet::from([0, 1]),
1800                    ),
1801                    (
1802                        (Timestamp::new_second(1), Timestamp::new_second(2)),
1803                        BTreeSet::from([1, 2]),
1804                    ),
1805                ],
1806            ),
1807            // non-overlapping
1808            (
1809                BTreeMap::from([
1810                    (
1811                        (Timestamp::new_second(2), Timestamp::new_second(3)),
1812                        vec![0],
1813                    ),
1814                    (
1815                        (Timestamp::new_second(1), Timestamp::new_second(2)),
1816                        vec![1, 2],
1817                    ),
1818                ]),
1819                vec![
1820                    (
1821                        (Timestamp::new_second(2), Timestamp::new_second(3)),
1822                        BTreeSet::from([0]),
1823                    ),
1824                    (
1825                        (Timestamp::new_second(1), Timestamp::new_second(2)),
1826                        BTreeSet::from([1, 2]),
1827                    ),
1828                ],
1829            ),
1830        ];
1831
1832        run_compute_working_ranges_test(testcases, true);
1833    }
1834
1835    #[test]
1836    fn test_compute_working_ranges_ascending() {
1837        let testcases = vec![
1838            (
1839                BTreeMap::from([(
1840                    (Timestamp::new_second(1), Timestamp::new_second(2)),
1841                    vec![0],
1842                )]),
1843                vec![(
1844                    (Timestamp::new_second(1), Timestamp::new_second(2)),
1845                    BTreeSet::from([0]),
1846                )],
1847            ),
1848            (
1849                BTreeMap::from([(
1850                    (Timestamp::new_second(1), Timestamp::new_second(2)),
1851                    vec![0, 1],
1852                )]),
1853                vec![(
1854                    (Timestamp::new_second(1), Timestamp::new_second(2)),
1855                    BTreeSet::from([0, 1]),
1856                )],
1857            ),
1858            (
1859                BTreeMap::from([
1860                    (
1861                        (Timestamp::new_second(1), Timestamp::new_second(2)),
1862                        vec![0, 1],
1863                    ),
1864                    (
1865                        (Timestamp::new_second(2), Timestamp::new_second(3)),
1866                        vec![1],
1867                    ),
1868                ]),
1869                vec![
1870                    (
1871                        (Timestamp::new_second(1), Timestamp::new_second(2)),
1872                        BTreeSet::from([0, 1]),
1873                    ),
1874                    (
1875                        (Timestamp::new_second(2), Timestamp::new_second(3)),
1876                        BTreeSet::from([1]),
1877                    ),
1878                ],
1879            ),
1880            (
1881                BTreeMap::from([
1882                    (
1883                        (Timestamp::new_second(1), Timestamp::new_second(2)),
1884                        vec![0],
1885                    ),
1886                    (
1887                        (Timestamp::new_second(2), Timestamp::new_second(3)),
1888                        vec![0, 1],
1889                    ),
1890                ]),
1891                vec![
1892                    (
1893                        (Timestamp::new_second(1), Timestamp::new_second(2)),
1894                        BTreeSet::from([0]),
1895                    ),
1896                    (
1897                        (Timestamp::new_second(2), Timestamp::new_second(3)),
1898                        BTreeSet::from([0, 1]),
1899                    ),
1900                ],
1901            ),
1902            // test if only one count in working set get it's own working range
1903            (
1904                BTreeMap::from([
1905                    (
1906                        (Timestamp::new_second(1), Timestamp::new_second(2)),
1907                        vec![0],
1908                    ),
1909                    (
1910                        (Timestamp::new_second(2), Timestamp::new_second(3)),
1911                        vec![0, 1],
1912                    ),
1913                    (
1914                        (Timestamp::new_second(3), Timestamp::new_second(4)),
1915                        vec![1],
1916                    ),
1917                ]),
1918                vec![
1919                    (
1920                        (Timestamp::new_second(1), Timestamp::new_second(2)),
1921                        BTreeSet::from([0]),
1922                    ),
1923                    (
1924                        (Timestamp::new_second(2), Timestamp::new_second(3)),
1925                        BTreeSet::from([0, 1]),
1926                    ),
1927                    (
1928                        (Timestamp::new_second(3), Timestamp::new_second(4)),
1929                        BTreeSet::from([1]),
1930                    ),
1931                ],
1932            ),
1933            (
1934                BTreeMap::from([
1935                    (
1936                        (Timestamp::new_second(1), Timestamp::new_second(2)),
1937                        vec![0, 2],
1938                    ),
1939                    (
1940                        (Timestamp::new_second(2), Timestamp::new_second(3)),
1941                        vec![0, 1, 2],
1942                    ),
1943                    (
1944                        (Timestamp::new_second(3), Timestamp::new_second(4)),
1945                        vec![1, 2],
1946                    ),
1947                ]),
1948                vec![(
1949                    (Timestamp::new_second(1), Timestamp::new_second(4)),
1950                    BTreeSet::from([0, 1, 2]),
1951                )],
1952            ),
1953            (
1954                BTreeMap::from([
1955                    (
1956                        (Timestamp::new_second(1), Timestamp::new_second(2)),
1957                        vec![0, 2],
1958                    ),
1959                    (
1960                        (Timestamp::new_second(2), Timestamp::new_second(3)),
1961                        vec![1, 2],
1962                    ),
1963                ]),
1964                vec![(
1965                    (Timestamp::new_second(1), Timestamp::new_second(3)),
1966                    BTreeSet::from([0, 1, 2]),
1967                )],
1968            ),
1969            (
1970                BTreeMap::from([
1971                    (
1972                        (Timestamp::new_second(1), Timestamp::new_second(2)),
1973                        vec![0, 1],
1974                    ),
1975                    (
1976                        (Timestamp::new_second(2), Timestamp::new_second(3)),
1977                        vec![0, 1, 2],
1978                    ),
1979                    (
1980                        (Timestamp::new_second(3), Timestamp::new_second(4)),
1981                        vec![1, 2],
1982                    ),
1983                ]),
1984                vec![(
1985                    (Timestamp::new_second(1), Timestamp::new_second(4)),
1986                    BTreeSet::from([0, 1, 2]),
1987                )],
1988            ),
1989            (
1990                BTreeMap::from([
1991                    (
1992                        (Timestamp::new_second(1), Timestamp::new_second(2)),
1993                        vec![0, 1],
1994                    ),
1995                    (
1996                        (Timestamp::new_second(2), Timestamp::new_second(3)),
1997                        vec![1, 2],
1998                    ),
1999                ]),
2000                vec![
2001                    (
2002                        (Timestamp::new_second(1), Timestamp::new_second(2)),
2003                        BTreeSet::from([0, 1]),
2004                    ),
2005                    (
2006                        (Timestamp::new_second(2), Timestamp::new_second(3)),
2007                        BTreeSet::from([1, 2]),
2008                    ),
2009                ],
2010            ),
2011            // non-overlapping
2012            (
2013                BTreeMap::from([
2014                    (
2015                        (Timestamp::new_second(1), Timestamp::new_second(2)),
2016                        vec![0, 1],
2017                    ),
2018                    (
2019                        (Timestamp::new_second(2), Timestamp::new_second(3)),
2020                        vec![2],
2021                    ),
2022                ]),
2023                vec![
2024                    (
2025                        (Timestamp::new_second(1), Timestamp::new_second(2)),
2026                        BTreeSet::from([0, 1]),
2027                    ),
2028                    (
2029                        (Timestamp::new_second(2), Timestamp::new_second(3)),
2030                        BTreeSet::from([2]),
2031                    ),
2032                ],
2033            ),
2034        ];
2035
2036        run_compute_working_ranges_test(testcases, false);
2037    }
2038
2039    #[test]
2040    fn test_split_overlap_range() {
2041        let testcases = vec![
2042            // simple one range
2043            (
2044                vec![PartitionRange {
2045                    start: Timestamp::new_second(1),
2046                    end: Timestamp::new_second(2),
2047                    num_rows: 2,
2048                    identifier: 0,
2049                }],
2050                BTreeMap::from_iter(
2051                    vec![(
2052                        (Timestamp::new_second(1), Timestamp::new_second(2)),
2053                        vec![0],
2054                    )]
2055                    .into_iter(),
2056                ),
2057            ),
2058            // two overlapping range
2059            (
2060                vec![
2061                    PartitionRange {
2062                        start: Timestamp::new_second(1),
2063                        end: Timestamp::new_second(2),
2064                        num_rows: 2,
2065                        identifier: 0,
2066                    },
2067                    PartitionRange {
2068                        start: Timestamp::new_second(1),
2069                        end: Timestamp::new_second(2),
2070                        num_rows: 2,
2071                        identifier: 1,
2072                    },
2073                ],
2074                BTreeMap::from_iter(
2075                    vec![(
2076                        (Timestamp::new_second(1), Timestamp::new_second(2)),
2077                        vec![0, 1],
2078                    )]
2079                    .into_iter(),
2080                ),
2081            ),
2082            (
2083                vec![
2084                    PartitionRange {
2085                        start: Timestamp::new_second(1),
2086                        end: Timestamp::new_second(3),
2087                        num_rows: 2,
2088                        identifier: 0,
2089                    },
2090                    PartitionRange {
2091                        start: Timestamp::new_second(2),
2092                        end: Timestamp::new_second(4),
2093                        num_rows: 2,
2094                        identifier: 1,
2095                    },
2096                ],
2097                BTreeMap::from_iter(
2098                    vec![
2099                        (
2100                            (Timestamp::new_second(1), Timestamp::new_second(2)),
2101                            vec![0],
2102                        ),
2103                        (
2104                            (Timestamp::new_second(2), Timestamp::new_second(3)),
2105                            vec![0, 1],
2106                        ),
2107                        (
2108                            (Timestamp::new_second(3), Timestamp::new_second(4)),
2109                            vec![1],
2110                        ),
2111                    ]
2112                    .into_iter(),
2113                ),
2114            ),
2115            // three or more overlapping range
2116            (
2117                vec![
2118                    PartitionRange {
2119                        start: Timestamp::new_second(1),
2120                        end: Timestamp::new_second(3),
2121                        num_rows: 2,
2122                        identifier: 0,
2123                    },
2124                    PartitionRange {
2125                        start: Timestamp::new_second(2),
2126                        end: Timestamp::new_second(4),
2127                        num_rows: 2,
2128                        identifier: 1,
2129                    },
2130                    PartitionRange {
2131                        start: Timestamp::new_second(1),
2132                        end: Timestamp::new_second(4),
2133                        num_rows: 2,
2134                        identifier: 2,
2135                    },
2136                ],
2137                BTreeMap::from_iter(
2138                    vec![
2139                        (
2140                            (Timestamp::new_second(1), Timestamp::new_second(2)),
2141                            vec![0, 2],
2142                        ),
2143                        (
2144                            (Timestamp::new_second(2), Timestamp::new_second(3)),
2145                            vec![0, 1, 2],
2146                        ),
2147                        (
2148                            (Timestamp::new_second(3), Timestamp::new_second(4)),
2149                            vec![1, 2],
2150                        ),
2151                    ]
2152                    .into_iter(),
2153                ),
2154            ),
2155            (
2156                vec![
2157                    PartitionRange {
2158                        start: Timestamp::new_second(1),
2159                        end: Timestamp::new_second(3),
2160                        num_rows: 2,
2161                        identifier: 0,
2162                    },
2163                    PartitionRange {
2164                        start: Timestamp::new_second(1),
2165                        end: Timestamp::new_second(4),
2166                        num_rows: 2,
2167                        identifier: 1,
2168                    },
2169                    PartitionRange {
2170                        start: Timestamp::new_second(2),
2171                        end: Timestamp::new_second(4),
2172                        num_rows: 2,
2173                        identifier: 2,
2174                    },
2175                ],
2176                BTreeMap::from_iter(
2177                    vec![
2178                        (
2179                            (Timestamp::new_second(1), Timestamp::new_second(2)),
2180                            vec![0, 1],
2181                        ),
2182                        (
2183                            (Timestamp::new_second(2), Timestamp::new_second(3)),
2184                            vec![0, 1, 2],
2185                        ),
2186                        (
2187                            (Timestamp::new_second(3), Timestamp::new_second(4)),
2188                            vec![1, 2],
2189                        ),
2190                    ]
2191                    .into_iter(),
2192                ),
2193            ),
2194        ];
2195
2196        for (input, expected) in testcases {
2197            let expected = expected.into_iter().map(|(r, s)| (r.into(), s)).collect();
2198            assert_eq!(split_overlapping_ranges(&input), expected);
2199        }
2200    }
2201
2202    impl From<(i32, i32, Option<i32>, Option<i32>)> for SucRun<i32> {
2203        fn from((offset, len, min_val, max_val): (i32, i32, Option<i32>, Option<i32>)) -> Self {
2204            Self {
2205                offset: offset as usize,
2206                len: len as usize,
2207                first_val: min_val,
2208                last_val: max_val,
2209            }
2210        }
2211    }
2212
2213    #[test]
2214    fn test_find_successive_runs() {
2215        let testcases = vec![
2216            (
2217                vec![Some(1), Some(1), Some(2), Some(1), Some(3)],
2218                Some(SortOptions {
2219                    descending: false,
2220                    nulls_first: false,
2221                }),
2222                vec![(0, 3, Some(1), Some(2)), (3, 2, Some(1), Some(3))],
2223            ),
2224            (
2225                vec![Some(1), Some(2), Some(2), Some(1), Some(3)],
2226                Some(SortOptions {
2227                    descending: false,
2228                    nulls_first: false,
2229                }),
2230                vec![(0, 3, Some(1), Some(2)), (3, 2, Some(1), Some(3))],
2231            ),
2232            (
2233                vec![Some(1), Some(2), None, None, Some(1), Some(3)],
2234                Some(SortOptions {
2235                    descending: false,
2236                    nulls_first: false,
2237                }),
2238                vec![(0, 4, Some(1), Some(2)), (4, 2, Some(1), Some(3))],
2239            ),
2240            (
2241                vec![Some(1), Some(2), Some(1), Some(3)],
2242                Some(SortOptions {
2243                    descending: false,
2244                    nulls_first: false,
2245                }),
2246                vec![(0, 2, Some(1), Some(2)), (2, 2, Some(1), Some(3))],
2247            ),
2248            (
2249                vec![Some(1), Some(2), Some(1), Some(3)],
2250                Some(SortOptions {
2251                    descending: true,
2252                    nulls_first: false,
2253                }),
2254                vec![
2255                    (0, 1, Some(1), Some(1)),
2256                    (1, 2, Some(2), Some(1)),
2257                    (3, 1, Some(3), Some(3)),
2258                ],
2259            ),
2260            (
2261                vec![Some(1), Some(2), None, Some(3)],
2262                Some(SortOptions {
2263                    descending: false,
2264                    nulls_first: true,
2265                }),
2266                vec![(0, 2, Some(1), Some(2)), (2, 2, Some(3), Some(3))],
2267            ),
2268            (
2269                vec![Some(1), Some(2), None, Some(3)],
2270                Some(SortOptions {
2271                    descending: false,
2272                    nulls_first: false,
2273                }),
2274                vec![(0, 3, Some(1), Some(2)), (3, 1, Some(3), Some(3))],
2275            ),
2276            (
2277                vec![Some(2), Some(1), None, Some(3)],
2278                Some(SortOptions {
2279                    descending: true,
2280                    nulls_first: true,
2281                }),
2282                vec![(0, 2, Some(2), Some(1)), (2, 2, Some(3), Some(3))],
2283            ),
2284            (
2285                vec![],
2286                Some(SortOptions {
2287                    descending: false,
2288                    nulls_first: true,
2289                }),
2290                vec![(0, 0, None, None)],
2291            ),
2292            (
2293                vec![None, None, Some(2), Some(2), Some(1), Some(5), Some(4)],
2294                Some(SortOptions {
2295                    descending: true,
2296                    nulls_first: true,
2297                }),
2298                vec![(0, 5, Some(2), Some(1)), (5, 2, Some(5), Some(4))],
2299            ),
2300            (
2301                vec![None, None, Some(2), Some(2), Some(1), Some(5), Some(4)],
2302                Some(SortOptions {
2303                    descending: true,
2304                    nulls_first: false,
2305                }),
2306                vec![
2307                    (0, 2, None, None),
2308                    (2, 3, Some(2), Some(1)),
2309                    (5, 2, Some(5), Some(4)),
2310                ],
2311            ),
2312        ];
2313        for (input, sort_opts, expected) in testcases {
2314            let ret = find_successive_runs(input.clone().into_iter().enumerate(), &sort_opts);
2315            let expected = expected.into_iter().map(SucRun::<i32>::from).collect_vec();
2316            assert_eq!(
2317                ret, expected,
2318                "input: {:?}, opt: {:?},expected: {:?}",
2319                input, sort_opts, expected
2320            );
2321        }
2322    }
2323
2324    #[test]
2325    fn test_cmp_with_opts() {
2326        let testcases = vec![
2327            // Test ascending vs descending for Some values
2328            (
2329                Some(1),
2330                Some(2),
2331                Some(SortOptions {
2332                    descending: false,
2333                    nulls_first: false,
2334                }),
2335                std::cmp::Ordering::Less,
2336            ),
2337            (
2338                Some(1),
2339                Some(2),
2340                Some(SortOptions {
2341                    descending: true,
2342                    nulls_first: false,
2343                }),
2344                std::cmp::Ordering::Greater,
2345            ),
2346            // Test Some vs None with nulls_first
2347            (
2348                Some(1),
2349                None,
2350                Some(SortOptions {
2351                    descending: false,
2352                    nulls_first: true,
2353                }),
2354                std::cmp::Ordering::Greater,
2355            ),
2356            (
2357                Some(1),
2358                None,
2359                Some(SortOptions {
2360                    descending: true,
2361                    nulls_first: true,
2362                }),
2363                std::cmp::Ordering::Greater,
2364            ),
2365            // Test Some vs None with nulls_last
2366            (
2367                Some(1),
2368                None,
2369                Some(SortOptions {
2370                    descending: true,
2371                    nulls_first: false,
2372                }),
2373                std::cmp::Ordering::Less,
2374            ),
2375            (
2376                Some(1),
2377                None,
2378                Some(SortOptions {
2379                    descending: false,
2380                    nulls_first: false,
2381                }),
2382                std::cmp::Ordering::Less,
2383            ),
2384            // Test None vs None - always Equal regardless of sort options
2385            (
2386                None,
2387                None,
2388                Some(SortOptions {
2389                    descending: false,
2390                    nulls_first: true,
2391                }),
2392                std::cmp::Ordering::Equal,
2393            ),
2394        ];
2395        for (a, b, opts, expected) in testcases {
2396            assert_eq!(
2397                cmp_with_opts(&a, &b, &opts),
2398                expected,
2399                "a: {:?}, b: {:?}, opts: {:?}",
2400                a,
2401                b,
2402                opts
2403            );
2404        }
2405    }
2406
2407    #[test]
2408    fn test_find_slice_from_range() {
2409        let test_cases = vec![
2410            // test for off by one case
2411            (
2412                Arc::new(TimestampMillisecondArray::from_iter_values([1, 2, 3, 4, 5])) as ArrayRef,
2413                false,
2414                TimeRange {
2415                    start: Timestamp::new_millisecond(2),
2416                    end: Timestamp::new_millisecond(4),
2417                },
2418                Ok((1, 2)),
2419            ),
2420            (
2421                Arc::new(TimestampMillisecondArray::from_iter_values([
2422                    -2, -1, 0, 1, 2, 3, 4, 5,
2423                ])) as ArrayRef,
2424                false,
2425                TimeRange {
2426                    start: Timestamp::new_millisecond(-1),
2427                    end: Timestamp::new_millisecond(4),
2428                },
2429                Ok((1, 5)),
2430            ),
2431            (
2432                Arc::new(TimestampMillisecondArray::from_iter_values([1, 3, 4, 6])) as ArrayRef,
2433                false,
2434                TimeRange {
2435                    start: Timestamp::new_millisecond(2),
2436                    end: Timestamp::new_millisecond(5),
2437                },
2438                Ok((1, 2)),
2439            ),
2440            (
2441                Arc::new(TimestampMillisecondArray::from_iter_values([1, 2, 3, 4, 6])) as ArrayRef,
2442                false,
2443                TimeRange {
2444                    start: Timestamp::new_millisecond(2),
2445                    end: Timestamp::new_millisecond(5),
2446                },
2447                Ok((1, 3)),
2448            ),
2449            (
2450                Arc::new(TimestampMillisecondArray::from_iter_values([1, 3, 4, 5, 6])) as ArrayRef,
2451                false,
2452                TimeRange {
2453                    start: Timestamp::new_millisecond(2),
2454                    end: Timestamp::new_millisecond(5),
2455                },
2456                Ok((1, 2)),
2457            ),
2458            (
2459                Arc::new(TimestampMillisecondArray::from_iter_values([1, 2, 3, 4, 5])) as ArrayRef,
2460                false,
2461                TimeRange {
2462                    start: Timestamp::new_millisecond(6),
2463                    end: Timestamp::new_millisecond(7),
2464                },
2465                Ok((5, 0)),
2466            ),
2467            // descending off by one cases
2468            (
2469                Arc::new(TimestampMillisecondArray::from_iter_values([5, 3, 2, 1])) as ArrayRef,
2470                true,
2471                TimeRange {
2472                    end: Timestamp::new_millisecond(4),
2473                    start: Timestamp::new_millisecond(1),
2474                },
2475                Ok((1, 3)),
2476            ),
2477            (
2478                Arc::new(TimestampMillisecondArray::from_iter_values([
2479                    5, 4, 3, 2, 1, 0,
2480                ])) as ArrayRef,
2481                true,
2482                TimeRange {
2483                    end: Timestamp::new_millisecond(4),
2484                    start: Timestamp::new_millisecond(1),
2485                },
2486                Ok((2, 3)),
2487            ),
2488            (
2489                Arc::new(TimestampMillisecondArray::from_iter_values([5, 3, 2, 0])) as ArrayRef,
2490                true,
2491                TimeRange {
2492                    end: Timestamp::new_millisecond(4),
2493                    start: Timestamp::new_millisecond(1),
2494                },
2495                Ok((1, 2)),
2496            ),
2497            (
2498                Arc::new(TimestampMillisecondArray::from_iter_values([5, 4, 3, 2, 0])) as ArrayRef,
2499                true,
2500                TimeRange {
2501                    end: Timestamp::new_millisecond(4),
2502                    start: Timestamp::new_millisecond(1),
2503                },
2504                Ok((2, 2)),
2505            ),
2506            (
2507                Arc::new(TimestampMillisecondArray::from_iter_values([5, 4, 3, 2, 1])) as ArrayRef,
2508                true,
2509                TimeRange {
2510                    end: Timestamp::new_millisecond(5),
2511                    start: Timestamp::new_millisecond(2),
2512                },
2513                Ok((1, 3)),
2514            ),
2515            (
2516                Arc::new(TimestampMillisecondArray::from_iter_values([5, 4, 3, 1])) as ArrayRef,
2517                true,
2518                TimeRange {
2519                    end: Timestamp::new_millisecond(5),
2520                    start: Timestamp::new_millisecond(2),
2521                },
2522                Ok((1, 2)),
2523            ),
2524            (
2525                Arc::new(TimestampMillisecondArray::from_iter_values([6, 4, 3, 2, 1])) as ArrayRef,
2526                true,
2527                TimeRange {
2528                    end: Timestamp::new_millisecond(5),
2529                    start: Timestamp::new_millisecond(2),
2530                },
2531                Ok((1, 3)),
2532            ),
2533            (
2534                Arc::new(TimestampMillisecondArray::from_iter_values([6, 4, 3, 1])) as ArrayRef,
2535                true,
2536                TimeRange {
2537                    end: Timestamp::new_millisecond(5),
2538                    start: Timestamp::new_millisecond(2),
2539                },
2540                Ok((1, 2)),
2541            ),
2542            (
2543                Arc::new(TimestampMillisecondArray::from_iter_values([
2544                    10, 9, 8, 7, 6,
2545                ])) as ArrayRef,
2546                true,
2547                TimeRange {
2548                    end: Timestamp::new_millisecond(5),
2549                    start: Timestamp::new_millisecond(2),
2550                },
2551                Ok((5, 0)),
2552            ),
2553            // test off by one case
2554            (
2555                Arc::new(TimestampMillisecondArray::from_iter_values([3, 2, 1, 0])) as ArrayRef,
2556                true,
2557                TimeRange {
2558                    end: Timestamp::new_millisecond(4),
2559                    start: Timestamp::new_millisecond(3),
2560                },
2561                Ok((0, 1)),
2562            ),
2563            (
2564                Arc::new(TimestampMillisecondArray::from_iter_values([5, 3, 2])) as ArrayRef,
2565                true,
2566                TimeRange {
2567                    end: Timestamp::new_millisecond(4),
2568                    start: Timestamp::new_millisecond(3),
2569                },
2570                Ok((1, 1)),
2571            ),
2572            (
2573                Arc::new(TimestampMillisecondArray::from_iter_values([5, 4, 3, 2])) as ArrayRef,
2574                true,
2575                TimeRange {
2576                    end: Timestamp::new_millisecond(4),
2577                    start: Timestamp::new_millisecond(3),
2578                },
2579                Ok((2, 1)),
2580            ),
2581        ];
2582
2583        for (sort_vals, descending, range, expected) in test_cases {
2584            let sort_column = SortColumn {
2585                values: sort_vals,
2586                options: Some(SortOptions {
2587                    descending,
2588                    ..Default::default()
2589                }),
2590            };
2591            let ret = find_slice_from_range(&sort_column, &range);
2592            match (ret, expected) {
2593                (Ok(ret), Ok(expected)) => {
2594                    assert_eq!(
2595                        ret, expected,
2596                        "sort_vals: {:?}, range: {:?}",
2597                        sort_column, range
2598                    )
2599                }
2600                (Err(err), Err(expected)) => {
2601                    let expected: &str = expected;
2602                    assert!(
2603                        err.to_string().contains(expected),
2604                        "err: {:?}, expected: {:?}",
2605                        err,
2606                        expected
2607                    );
2608                }
2609                (r, e) => panic!("unexpected result: {:?}, expected: {:?}", r, e),
2610            }
2611        }
2612    }
2613
2614    #[derive(Debug)]
2615    struct TestStream {
2616        expression: PhysicalSortExpr,
2617        fetch: Option<usize>,
2618        input: Vec<(PartitionRange, DfRecordBatch)>,
2619        output: Vec<DfRecordBatch>,
2620        schema: SchemaRef,
2621    }
2622
2623    impl TestStream {
2624        fn new(
2625            opt: SortOptions,
2626            fetch: Option<usize>,
2627            unit: TimeUnit,
2628            input: Vec<(PartitionRange, Vec<ArrayRef>)>,
2629            expected: Vec<Vec<ArrayRef>>,
2630        ) -> Self {
2631            let expression = PhysicalSortExpr {
2632                expr: Arc::new(helpers::ts_column()),
2633                options: opt,
2634            };
2635            let schema = Schema::new(vec![helpers::ts_field(unit)]);
2636            let schema = Arc::new(schema);
2637            let input = input
2638                .into_iter()
2639                .map(|(k, v)| (k, DfRecordBatch::try_new(schema.clone(), v).unwrap()))
2640                .collect_vec();
2641            let output_batchs = expected
2642                .into_iter()
2643                .map(|v| DfRecordBatch::try_new(schema.clone(), v).unwrap())
2644                .collect_vec();
2645            Self {
2646                expression,
2647                fetch,
2648                input,
2649                output: output_batchs,
2650                schema,
2651            }
2652        }
2653
2654        fn new_simple(
2655            descending: bool,
2656            fetch: Option<usize>,
2657            input: Vec<(PartitionRange, Vec<ArrayRef>)>,
2658            expected: Vec<Vec<ArrayRef>>,
2659        ) -> Self {
2660            Self::new(
2661                helpers::default_sort_opts(descending),
2662                fetch,
2663                TimeUnit::Millisecond,
2664                input,
2665                expected,
2666            )
2667        }
2668
2669        async fn run_test(&self) -> Vec<DfRecordBatch> {
2670            let (ranges, batches): (Vec<_>, Vec<_>) = self.input.clone().into_iter().unzip();
2671
2672            let mock_input = MockInputExec::new(vec![batches], self.schema.clone());
2673
2674            let exec = WindowedSortExec::try_new(
2675                self.expression.clone(),
2676                self.fetch,
2677                vec![ranges],
2678                Arc::new(mock_input),
2679            )
2680            .unwrap();
2681
2682            let exec_stream = exec.execute(0, Arc::new(TaskContext::default())).unwrap();
2683
2684            let real_output = exec_stream.collect::<Vec<_>>().await;
2685            let real_output: Vec<_> = real_output
2686                .into_iter()
2687                .collect::<std::result::Result<Vec<_>, _>>()
2688                .unwrap();
2689            real_output
2690        }
2691    }
2692
2693    #[tokio::test]
2694    async fn test_window_sort_empty_and_minimal() {
2695        use helpers::*;
2696        let test_cases = [
2697            // Empty input
2698            TestStream::new_simple(false, None, vec![], vec![]),
2699            // One empty batch, one with data
2700            TestStream::new_simple(
2701                false,
2702                None,
2703                vec![
2704                    (partition_range(1, 2, 1, 0), vec![ts_array([])]),
2705                    (partition_range(1, 3, 1, 0), vec![ts_array([2])]),
2706                ],
2707                vec![vec![ts_array([2])]],
2708            ),
2709            // Both batches empty
2710            TestStream::new_simple(
2711                false,
2712                None,
2713                vec![
2714                    (partition_range(1, 2, 1, 0), vec![ts_array([])]),
2715                    (partition_range(1, 3, 1, 0), vec![ts_array([])]),
2716                ],
2717                vec![],
2718            ),
2719            // Indistinguishable boundary case - value at exact boundary
2720            TestStream::new_simple(
2721                false,
2722                None,
2723                vec![
2724                    (partition_range(1, 2, 1, 0), vec![ts_array([1])]),
2725                    (partition_range(1, 3, 1, 0), vec![ts_array([2])]),
2726                ],
2727                vec![vec![ts_array([1])], vec![ts_array([2])]],
2728            ),
2729        ];
2730
2731        for (idx, testcase) in test_cases.iter().enumerate() {
2732            let output = testcase.run_test().await;
2733            assert_eq!(output, testcase.output, "empty/minimal case {idx} failed");
2734        }
2735    }
2736
2737    #[tokio::test]
2738    async fn test_window_sort_overlapping() {
2739        use helpers::*;
2740        let test_cases = [
2741            // Direct emit - overlapping ranges without merge
2742            TestStream::new_simple(
2743                false,
2744                None,
2745                vec![
2746                    (partition_range(1, 3, 1, 0), vec![ts_array([1, 2])]),
2747                    (partition_range(1, 4, 1, 0), vec![ts_array([2, 3])]),
2748                ],
2749                vec![
2750                    vec![ts_array([1, 2])],
2751                    vec![ts_array([2])],
2752                    vec![ts_array([3])],
2753                ],
2754            ),
2755            // Cross working range batch intersection - triggers merge
2756            TestStream::new_simple(
2757                false,
2758                None,
2759                vec![
2760                    (partition_range(1, 3, 1, 0), vec![ts_array([1, 2])]),
2761                    (partition_range(1, 4, 1, 1), vec![ts_array([1, 2, 3])]),
2762                ],
2763                vec![vec![ts_array([1, 1, 2, 2])], vec![ts_array([3])]],
2764            ),
2765            // No overlap case - separate ranges
2766            TestStream::new_simple(
2767                false,
2768                None,
2769                vec![
2770                    (partition_range(1, 3, 1, 0), vec![ts_array([1, 2])]),
2771                    (partition_range(1, 4, 1, 1), vec![ts_array([1, 2, 3])]),
2772                    (partition_range(4, 6, 1, 1), vec![ts_array([4, 5])]),
2773                ],
2774                vec![
2775                    vec![ts_array([1, 1, 2, 2])],
2776                    vec![ts_array([3])],
2777                    vec![ts_array([4, 5])],
2778                ],
2779            ),
2780        ];
2781
2782        for (idx, testcase) in test_cases.iter().enumerate() {
2783            let output = testcase.run_test().await;
2784            assert_eq!(output, testcase.output, "overlapping case {idx} failed");
2785        }
2786    }
2787
2788    #[tokio::test]
2789    async fn test_window_sort_with_fetch() {
2790        use helpers::*;
2791        let test_cases = [
2792            // Fetch limit stops at 6 rows
2793            TestStream::new_simple(
2794                false,
2795                Some(6),
2796                vec![
2797                    (partition_range(1, 3, 1, 0), vec![ts_array([1, 2])]),
2798                    (partition_range(1, 4, 1, 1), vec![ts_array([1, 2, 3])]),
2799                    (partition_range(3, 6, 1, 1), vec![ts_array([4, 5])]),
2800                ],
2801                vec![
2802                    vec![ts_array([1, 1, 2, 2])],
2803                    vec![ts_array([3])],
2804                    vec![ts_array([4])],
2805                ],
2806            ),
2807            // Fetch limit stops at 3 rows
2808            TestStream::new_simple(
2809                false,
2810                Some(3),
2811                vec![
2812                    (partition_range(1, 3, 1, 0), vec![ts_array([1, 2])]),
2813                    (partition_range(1, 4, 1, 1), vec![ts_array([1, 2, 3])]),
2814                    (partition_range(3, 6, 1, 1), vec![ts_array([4, 5])]),
2815                ],
2816                vec![vec![ts_array([1, 1, 2])]],
2817            ),
2818        ];
2819
2820        for (idx, testcase) in test_cases.iter().enumerate() {
2821            let output = testcase.run_test().await;
2822            assert_eq!(output, testcase.output, "fetch case {idx} failed");
2823        }
2824    }
2825
2826    #[tokio::test]
2827    async fn test_window_sort_descending() {
2828        use helpers::*;
2829        let test_cases = [
2830            // Descending order sort
2831            TestStream::new_simple(
2832                true,
2833                None,
2834                vec![
2835                    (partition_range(3, 6, 1, 1), vec![ts_array([5, 4])]),
2836                    (partition_range(1, 4, 1, 1), vec![ts_array([3, 2, 1])]),
2837                    (partition_range(1, 3, 1, 0), vec![ts_array([2, 1])]),
2838                ],
2839                vec![
2840                    vec![ts_array([5, 4])],
2841                    vec![ts_array([3])],
2842                    vec![ts_array([2, 2, 1, 1])],
2843                ],
2844            ),
2845        ];
2846
2847        for (idx, testcase) in test_cases.iter().enumerate() {
2848            let output = testcase.run_test().await;
2849            assert_eq!(output, testcase.output, "descending case {idx} failed");
2850        }
2851    }
2852
2853    #[tokio::test]
2854    async fn test_window_sort_complex() {
2855        use helpers::*;
2856        let test_cases = [
2857            // Long range with subset short run
2858            TestStream::new_simple(
2859                false,
2860                None,
2861                vec![
2862                    (partition_range(1, 10, 1, 0), vec![ts_array([1, 5, 9])]),
2863                    (partition_range(3, 7, 1, 1), vec![ts_array([3, 4, 5, 6])]),
2864                ],
2865                vec![vec![ts_array([1])], vec![ts_array([3, 4, 5, 5, 6, 9])]],
2866            ),
2867            // Complex multi-range overlap
2868            TestStream::new_simple(
2869                false,
2870                None,
2871                vec![
2872                    (partition_range(1, 3, 1, 0), vec![ts_array([1, 2])]),
2873                    (
2874                        partition_range(1, 10, 1, 1),
2875                        vec![ts_array([1, 3, 4, 5, 6, 8])],
2876                    ),
2877                    (partition_range(7, 10, 1, 1), vec![ts_array([7, 8, 9])]),
2878                ],
2879                vec![
2880                    vec![ts_array([1, 1, 2])],
2881                    vec![ts_array([3, 4, 5, 6])],
2882                    vec![ts_array([7, 8, 8, 9])],
2883                ],
2884            ),
2885            // Subset with duplicate datapoints
2886            TestStream::new_simple(
2887                false,
2888                None,
2889                vec![
2890                    (
2891                        partition_range(1, 11, 1, 0),
2892                        vec![ts_array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])],
2893                    ),
2894                    (partition_range(5, 7, 1, 1), vec![ts_array([5, 6])]),
2895                ],
2896                vec![
2897                    vec![ts_array([1, 2, 3, 4])],
2898                    vec![ts_array([5, 5, 6, 6, 7, 8, 9, 10])],
2899                ],
2900            ),
2901        ];
2902
2903        for (idx, testcase) in test_cases.iter().enumerate() {
2904            let output = testcase.run_test().await;
2905            assert_eq!(output, testcase.output, "complex case {idx} failed");
2906        }
2907    }
2908
2909    #[tokio::test]
2910    async fn fuzzy_ish_test_window_sort_stream() {
2911        let test_cnt = 100;
2912        let part_cnt_bound = 100;
2913        let range_size_bound = 100;
2914        let range_offset_bound = 100;
2915        let in_range_datapoint_cnt_bound = 100;
2916        let fetch_bound = 100;
2917
2918        let mut rng = fastrand::Rng::new();
2919        let rng_seed = rng.u64(..);
2920        rng.seed(rng_seed);
2921        let mut bound_val = None;
2922        // construct testcases
2923        type CmpFn<T> = Box<dyn FnMut(&T, &T) -> std::cmp::Ordering>;
2924        let mut full_testcase_list = Vec::new();
2925        for _case_id in 0..test_cnt {
2926            let descending = rng.bool();
2927            fn ret_cmp_fn<T: Ord>(descending: bool) -> CmpFn<T> {
2928                if descending {
2929                    return Box::new(|a: &T, b: &T| b.cmp(a));
2930                }
2931                Box::new(|a: &T, b: &T| a.cmp(b))
2932            }
2933            let unit = match rng.u8(0..3) {
2934                0 => TimeUnit::Second,
2935                1 => TimeUnit::Millisecond,
2936                2 => TimeUnit::Microsecond,
2937                _ => TimeUnit::Nanosecond,
2938            };
2939            let fetch = if rng.bool() {
2940                Some(rng.usize(0..fetch_bound))
2941            } else {
2942                None
2943            };
2944
2945            let mut input_ranged_data = vec![];
2946            let mut output_data: Vec<i64> = vec![];
2947            // generate input data
2948            for part_id in 0..rng.usize(0..part_cnt_bound) {
2949                let (start, end) = if descending {
2950                    // Use 1..=range_offset_bound to ensure strictly decreasing end values
2951                    let end = bound_val
2952                        .map(|i| i - rng.i64(1..=range_offset_bound))
2953                        .unwrap_or_else(|| rng.i64(..));
2954                    bound_val = Some(end);
2955                    let start = end - rng.i64(1..range_size_bound);
2956                    let start = Timestamp::new(start, unit.into());
2957                    let end = Timestamp::new(end, unit.into());
2958                    (start, end)
2959                } else {
2960                    // Use 1..=range_offset_bound to ensure strictly increasing start values
2961                    let start = bound_val
2962                        .map(|i| i + rng.i64(1..=range_offset_bound))
2963                        .unwrap_or_else(|| rng.i64(..));
2964                    bound_val = Some(start);
2965                    let end = start + rng.i64(1..range_size_bound);
2966                    let start = Timestamp::new(start, unit.into());
2967                    let end = Timestamp::new(end, unit.into());
2968                    (start, end)
2969                };
2970
2971                let iter = 0..rng.usize(0..in_range_datapoint_cnt_bound);
2972                let data_gen = iter
2973                    .map(|_| rng.i64(start.value()..end.value()))
2974                    .sorted_by(ret_cmp_fn(descending))
2975                    .collect_vec();
2976                output_data.extend(data_gen.clone());
2977                let arr = new_ts_array(unit, data_gen);
2978                let range = PartitionRange {
2979                    start,
2980                    end,
2981                    num_rows: arr.len(),
2982                    identifier: part_id,
2983                };
2984                input_ranged_data.push((range, vec![arr]));
2985            }
2986
2987            output_data.sort_by(ret_cmp_fn(descending));
2988            if let Some(fetch) = fetch {
2989                output_data.truncate(fetch);
2990            }
2991            let output_arr = new_ts_array(unit, output_data);
2992
2993            let test_stream = TestStream::new(
2994                helpers::default_sort_opts(descending),
2995                fetch,
2996                unit,
2997                input_ranged_data.clone(),
2998                vec![vec![output_arr]],
2999            );
3000            full_testcase_list.push(test_stream);
3001        }
3002
3003        for (case_id, test_stream) in full_testcase_list.into_iter().enumerate() {
3004            let res = test_stream.run_test().await;
3005            let res_concat = concat_batches(&test_stream.schema, &res).unwrap();
3006            let expected = test_stream.output;
3007            let expected_concat = concat_batches(&test_stream.schema, &expected).unwrap();
3008
3009            if res_concat != expected_concat {
3010                {
3011                    let mut f_input = std::io::stderr();
3012                    f_input.write_all(b"[").unwrap();
3013                    for (input_range, input_arr) in test_stream.input {
3014                        let range_json = json!({
3015                            "start": input_range.start.to_chrono_datetime().unwrap().to_string(),
3016                            "end": input_range.end.to_chrono_datetime().unwrap().to_string(),
3017                            "num_rows": input_range.num_rows,
3018                            "identifier": input_range.identifier,
3019                        });
3020                        let buf = Vec::new();
3021                        let mut input_writer = ArrayWriter::new(buf);
3022                        input_writer.write(&input_arr).unwrap();
3023                        input_writer.finish().unwrap();
3024                        let res_str =
3025                            String::from_utf8_lossy(&input_writer.into_inner()).to_string();
3026                        let whole_json =
3027                            format!(r#"{{"range": {}, "data": {}}},"#, range_json, res_str);
3028                        f_input.write_all(whole_json.as_bytes()).unwrap();
3029                    }
3030                    f_input.write_all(b"]").unwrap();
3031                }
3032                {
3033                    let mut f_res = std::io::stderr();
3034                    f_res.write_all(b"[").unwrap();
3035                    for batch in &res {
3036                        let mut res_writer = ArrayWriter::new(f_res);
3037                        res_writer.write(batch).unwrap();
3038                        res_writer.finish().unwrap();
3039                        f_res = res_writer.into_inner();
3040                        f_res.write_all(b",").unwrap();
3041                    }
3042                    f_res.write_all(b"]").unwrap();
3043
3044                    let f_res_concat = std::io::stderr();
3045                    let mut res_writer = ArrayWriter::new(f_res_concat);
3046                    res_writer.write(&res_concat).unwrap();
3047                    res_writer.finish().unwrap();
3048
3049                    let f_expected = std::io::stderr();
3050                    let mut expected_writer = ArrayWriter::new(f_expected);
3051                    expected_writer.write(&expected_concat).unwrap();
3052                    expected_writer.finish().unwrap();
3053                }
3054                panic!(
3055                    "case failed, case id: {0}, output and expected output to stderr",
3056                    case_id
3057                );
3058            }
3059            assert_eq!(
3060                res_concat, expected_concat,
3061                "case failed, case id: {}, rng seed: {}",
3062                case_id, rng_seed
3063            );
3064        }
3065    }
3066}