You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@datafusion.apache.org by gi...@apache.org on 2026/09/10 11:16:00 UTC

(datafusion) branch main updated: fix: report correct number of items left in the iterator for `IncrementalSortIterator` (#25107)

This is an automated email from the ASF dual-hosted git repository.

github-merge-queue[bot] pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/datafusion.git


The following commit(s) were added to refs/heads/main by this push:
     new c0b2ebe178 fix: report correct number of items left in the iterator for `IncrementalSortIterator` (#25107)
c0b2ebe178 is described below

commit c0b2ebe17853d681b002bbbe5066629da25b642f
Author: Raz Luvaton <16...@users.noreply.github.com>
AuthorDate: Thu Sep 10 11:05:59 2026 +0000

    fix: report correct number of items left in the iterator for `IncrementalSortIterator` (#25107)
    
    ## Which issue does this PR close?
    
    N/A
    
    ## Rationale for this change
    `size_hint` for `IncrementalSortIterator` returns the initial number of
    items rather than how many items left in the iterator
    
    ## What changes are included in this PR?
    
    update size hint to consider the number of elements already produced,
    impl `ExactSizeIterator` and added test
    
    ## What is the testing strategy for this PR?
    unit test
    
    ## Are there any user-facing changes?
    no
---
 datafusion/physical-plan/src/sorts/stream.rs | 82 +++++++++++++++++++++++-----
 1 file changed, 69 insertions(+), 13 deletions(-)

diff --git a/datafusion/physical-plan/src/sorts/stream.rs b/datafusion/physical-plan/src/sorts/stream.rs
index bb9c009493..e2a840fded 100644
--- a/datafusion/physical-plan/src/sorts/stream.rs
+++ b/datafusion/physical-plan/src/sorts/stream.rs
@@ -376,8 +376,10 @@ impl Iterator for IncrementalSortIterator {
         }
     }
 
+    // Not implementing ExactSizeIterator since in case of an error we stop and don't emit any more
+    // so the length would be wrong
     fn size_hint(&self) -> (usize, Option<usize>) {
-        let num_rows = self.batch.num_rows();
+        let num_rows = self.batch.num_rows().saturating_sub(self.cursor);
         let batch_size = self.batch_size;
         let num_batches = num_rows.div_ceil(batch_size);
         (num_batches, Some(num_batches))
@@ -398,19 +400,14 @@ mod tests {
     use futures::Stream;
     use std::pin::Pin;
 
-    /// Verifies that `take_record_batch` in `IncrementalSortIterator` actually
-    /// copies the data into a new allocation rather than returning a zero-copy
-    /// slice of the original batch. If the output arrays were slices, their
-    /// underlying buffer length would match the original array's length; a true
-    /// copy will have a buffer sized to fit only the chunk.
-    #[test]
-    fn incremental_sort_iterator_copies_data() -> Result<()> {
-        let original_len = 10;
-        let batch_size = 3;
-
+    fn create_incremental_sort_iter_on(
+        input_batch_len: usize,
+        output_batch_size: usize,
+    ) -> Result<(IncrementalSortIterator, RecordBatch)> {
         // Build a batch with a single Int32 column of descending values
         let schema = Arc::new(Schema::new(vec![Field::new("a", DataType::Int32, false)]));
-        let col_a: Int32Array = Int32Array::from(vec![0; original_len]);
+        let col_a: Int32Array =
+            Int32Array::from_iter_values((0..input_batch_len as i32).rev());
         let batch = RecordBatch::try_new(schema, vec![Arc::new(col_a)])?;
 
         // Sort ascending on column "a"
@@ -420,8 +417,27 @@ mod tests {
         )?)])
         .unwrap();
 
+        let iter =
+            IncrementalSortIterator::new(batch.clone(), expressions, output_batch_size);
+
+        Ok((iter, batch))
+    }
+
+    /// Verifies that `take_record_batch` in `IncrementalSortIterator` actually
+    /// copies the data into a new allocation rather than returning a zero-copy
+    /// slice of the original batch. If the output arrays were slices, their
+    /// underlying buffer length would match the original array's length; a true
+    /// copy will have a buffer sized to fit only the chunk.
+    #[test]
+    fn incremental_sort_iterator_copies_data() -> Result<()> {
+        let original_len = 10;
+        let batch_size = 3;
+
+        let (mut iter, batch) =
+            create_incremental_sort_iter_on(original_len, batch_size)?;
+
         let mut total_rows = 0;
-        IncrementalSortIterator::new(batch.clone(), expressions, batch_size).try_for_each(
+        iter.try_for_each(
             |result| {
                 let chunk = result?;
                 total_rows += chunk.num_rows();
@@ -531,4 +547,44 @@ mod tests {
             assert_eq!(Arc::strong_count(&hold_ref), 1);
         }
     }
+
+    fn assert_iterator_size_hint(iter: &IncrementalSortIterator, expected_len: usize) {
+        assert_eq!(iter.size_hint(), (expected_len, Some(expected_len)));
+    }
+
+    #[test]
+    fn incremental_sort_iterator_report_correct_len() -> Result<()> {
+        let original_len = 10;
+        let batch_size = 3;
+
+        let (mut iterator, _) =
+            create_incremental_sort_iter_on(original_len, batch_size)?;
+
+        assert_iterator_size_hint(&iterator, 4);
+
+        let batch = iterator.next().unwrap()?;
+        assert_eq!(batch.num_rows(), batch_size);
+
+        assert_iterator_size_hint(&iterator, 3);
+
+        let batch = iterator.next().unwrap()?;
+        assert_eq!(batch.num_rows(), batch_size);
+
+        assert_iterator_size_hint(&iterator, 2);
+
+        let batch = iterator.next().unwrap()?;
+        assert_eq!(batch.num_rows(), batch_size);
+
+        assert_iterator_size_hint(&iterator, 1);
+
+        let batch = iterator.next().unwrap()?;
+        // left over
+        assert_eq!(batch.num_rows(), 1);
+
+        assert_iterator_size_hint(&iterator, 0);
+
+        assert!(iterator.next().is_none());
+
+        Ok(())
+    }
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@datafusion.apache.org
For additional commands, e-mail: commits-help@datafusion.apache.org