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

comphead 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 e041b6a37a Minor: SortMergeJoin small refactoring (#13398)
e041b6a37a is described below

commit e041b6a37a9458dae73743a33ecb11a4f92fec5c
Author: Oleks V <[email protected]>
AuthorDate: Wed Nov 13 12:42:11 2024 -0800

    Minor: SortMergeJoin small refactoring (#13398)
    
    * SortMergeJoin: small refactoring
---
 .../physical-plan/src/joins/sort_merge_join.rs     | 186 +++++++++++----------
 1 file changed, 96 insertions(+), 90 deletions(-)

diff --git a/datafusion/physical-plan/src/joins/sort_merge_join.rs 
b/datafusion/physical-plan/src/joins/sort_merge_join.rs
index 9307caf1c6..a01cd348f0 100644
--- a/datafusion/physical-plan/src/joins/sort_merge_join.rs
+++ b/datafusion/physical-plan/src/joins/sort_merge_join.rs
@@ -371,7 +371,7 @@ impl ExecutionPlan for SortMergeJoinExec {
             .register(context.memory_pool());
 
         // create join stream
-        Ok(Box::pin(SMJStream::try_new(
+        Ok(Box::pin(SortMergeJoinStream::try_new(
             Arc::clone(&self.schema),
             self.sort_options.clone(),
             self.null_equals_null,
@@ -461,7 +461,7 @@ impl SortMergeJoinMetrics {
 
 /// State of SMJ stream
 #[derive(Debug, PartialEq, Eq)]
-enum SMJState {
+enum SortMergeJoinState {
     /// Init joining with a new streamed row or a new buffered batches
     Init,
     /// Polling one streamed row or one buffered batch, or both
@@ -597,11 +597,11 @@ struct BufferedBatch {
     pub null_joined: Vec<usize>,
     /// Size estimation used for reserving / releasing memory
     pub size_estimation: usize,
-    /// The indices of buffered batch that failed the join filter.
-    /// This is a map between buffered row index and a boolean value 
indicating whether all joined row
-    /// of the buffered row failed the join filter.
+    /// The indices of buffered batch that the join filter doesn't satisfy.
+    /// This is a map between right row index and a boolean value indicating 
whether all joined row
+    /// of the right row does not satisfy the filter .
     /// When dequeuing the buffered batch, we need to produce null joined rows 
for these indices.
-    pub join_filter_failed_map: HashMap<u64, bool>,
+    pub join_filter_not_matched_map: HashMap<u64, bool>,
     /// Current buffered batch number of rows. Equal to batch.num_rows()
     /// but if batch is spilled to disk this property is preferable
     /// and less expensive
@@ -642,7 +642,7 @@ impl BufferedBatch {
             join_arrays,
             null_joined: vec![],
             size_estimation,
-            join_filter_failed_map: HashMap::new(),
+            join_filter_not_matched_map: HashMap::new(),
             num_rows,
             spill_file: None,
         }
@@ -651,9 +651,9 @@ impl BufferedBatch {
 
 /// Sort-merge join stream that consumes streamed and buffered data stream
 /// and produces joined output
-struct SMJStream {
+struct SortMergeJoinStream {
     /// Current state of the stream
-    pub state: SMJState,
+    pub state: SortMergeJoinState,
     /// Output schema
     pub schema: SchemaRef,
     /// Sort options of join columns used to sort streamed and buffered data 
stream
@@ -722,7 +722,15 @@ struct JoinedRecordBatches {
     pub batch_ids: Vec<usize>,
 }
 
-impl RecordBatchStream for SMJStream {
+impl JoinedRecordBatches {
+    fn clear(&mut self) {
+        self.batches.clear();
+        self.batch_ids.clear();
+        self.filter_mask = BooleanBuilder::new();
+        self.row_indices = UInt64Builder::new();
+    }
+}
+impl RecordBatchStream for SortMergeJoinStream {
     fn schema(&self) -> SchemaRef {
         Arc::clone(&self.schema)
     }
@@ -865,6 +873,7 @@ fn get_corrected_filter_mask(
                 let is_null = filter_mask.is_null(i);
 
                 if val {
+                    // memoize the first seen matched row
                     if !seen_true {
                         last_true_idx = i;
                     }
@@ -884,6 +893,8 @@ fn get_corrected_filter_mask(
                 }
 
                 if last_index {
+                    // If the left row seen as true its needed to output it 
once
+                    // To do that we mark all other matches for same row as 
null to avoid the output
                     if seen_true {
                         #[allow(clippy::needless_range_loop)]
                         for j in first_row_idx..last_true_idx {
@@ -905,7 +916,7 @@ fn get_corrected_filter_mask(
     }
 }
 
-impl Stream for SMJStream {
+impl Stream for SortMergeJoinStream {
     type Item = Result<RecordBatch>;
 
     fn poll_next(
@@ -916,13 +927,13 @@ impl Stream for SMJStream {
         let _timer = join_time.timer();
         loop {
             match &self.state {
-                SMJState::Init => {
+                SortMergeJoinState::Init => {
                     let streamed_exhausted =
                         self.streamed_state == StreamedState::Exhausted;
                     let buffered_exhausted =
                         self.buffered_state == BufferedState::Exhausted;
                     self.state = if streamed_exhausted && buffered_exhausted {
-                        SMJState::Exhausted
+                        SortMergeJoinState::Exhausted
                     } else {
                         match self.current_ordering {
                             Ordering::Less | Ordering::Equal => {
@@ -961,10 +972,10 @@ impl Stream for SMJStream {
                                 }
                             }
                         }
-                        SMJState::Polling
+                        SortMergeJoinState::Polling
                     };
                 }
-                SMJState::Polling => {
+                SortMergeJoinState::Polling => {
                     if ![StreamedState::Exhausted, StreamedState::Ready]
                         .contains(&self.streamed_state)
                     {
@@ -987,19 +998,19 @@ impl Stream for SMJStream {
                     let buffered_exhausted =
                         self.buffered_state == BufferedState::Exhausted;
                     if streamed_exhausted && buffered_exhausted {
-                        self.state = SMJState::Exhausted;
+                        self.state = SortMergeJoinState::Exhausted;
                         continue;
                     }
                     self.current_ordering = self.compare_streamed_buffered()?;
-                    self.state = SMJState::JoinOutput;
+                    self.state = SortMergeJoinState::JoinOutput;
                 }
-                SMJState::JoinOutput => {
+                SortMergeJoinState::JoinOutput => {
                     self.join_partial()?;
 
                     if self.output_size < self.batch_size {
                         if self.buffered_data.scanning_finished() {
                             self.buffered_data.scanning_reset();
-                            self.state = SMJState::Init;
+                            self.state = SortMergeJoinState::Init;
                         }
                     } else {
                         self.freeze_all()?;
@@ -1029,7 +1040,7 @@ impl Stream for SMJStream {
                         return Poll::Pending;
                     }
                 }
-                SMJState::Exhausted => {
+                SortMergeJoinState::Exhausted => {
                     self.freeze_all()?;
 
                     if !self.output_record_batches.batches.is_empty() {
@@ -1059,7 +1070,7 @@ impl Stream for SMJStream {
     }
 }
 
-impl SMJStream {
+impl SortMergeJoinStream {
     #[allow(clippy::too_many_arguments)]
     pub fn try_new(
         schema: SchemaRef,
@@ -1079,7 +1090,7 @@ impl SMJStream {
         let streamed_schema = streamed.schema();
         let buffered_schema = buffered.schema();
         Ok(Self {
-            state: SMJState::Init,
+            state: SortMergeJoinState::Init,
             sort_options,
             null_equals_null,
             schema,
@@ -1517,7 +1528,7 @@ impl SMJStream {
         // For buffered row which is joined with streamed side rows but all 
joined rows
         // don't satisfy the join filter
         let not_matched_buffered_indices = buffered_batch
-            .join_filter_failed_map
+            .join_filter_not_matched_map
             .iter()
             .filter_map(|(idx, failed)| if *failed { Some(*idx) } else { None 
})
             .collect::<Vec<_>>();
@@ -1531,7 +1542,6 @@ impl SMJStream {
             &buffered_indices,
             buffered_batch,
         )? {
-            //print_batches(&[record_batch.clone()]);
             let num_rows = record_batch.num_rows();
 
             self.output_record_batches
@@ -1545,8 +1555,7 @@ impl SMJStream {
                 .extend(vec![0; num_rows]);
             self.output_record_batches.batches.push(record_batch);
         }
-        //dbg!(&buffered_batch.join_filter_failed_map);
-        buffered_batch.join_filter_failed_map.clear();
+        buffered_batch.join_filter_not_matched_map.clear();
 
         Ok(())
     }
@@ -1556,31 +1565,31 @@ impl SMJStream {
     fn freeze_streamed(&mut self) -> Result<()> {
         for chunk in self.streamed_batch.output_indices.iter_mut() {
             // The row indices of joined streamed batch
-            let streamed_indices = chunk.streamed_indices.finish();
+            let left_indices = chunk.streamed_indices.finish();
 
-            if streamed_indices.is_empty() {
+            if left_indices.is_empty() {
                 continue;
             }
 
-            let mut streamed_columns = self
+            let mut left_columns = self
                 .streamed_batch
                 .batch
                 .columns()
                 .iter()
-                .map(|column| take(column, &streamed_indices, None))
+                .map(|column| take(column, &left_indices, None))
                 .collect::<Result<Vec<_>, ArrowError>>()?;
 
             // The row indices of joined buffered batch
-            let buffered_indices: UInt64Array = 
chunk.buffered_indices.finish();
-            let mut buffered_columns = if matches!(self.join_type, 
JoinType::LeftMark) {
-                vec![Arc::new(is_not_null(&buffered_indices)?) as ArrayRef]
+            let right_indices: UInt64Array = chunk.buffered_indices.finish();
+            let mut right_columns = if matches!(self.join_type, 
JoinType::LeftMark) {
+                vec![Arc::new(is_not_null(&right_indices)?) as ArrayRef]
             } else if matches!(self.join_type, JoinType::LeftSemi | 
JoinType::LeftAnti) {
                 vec![]
             } else if let Some(buffered_idx) = chunk.buffered_batch_idx {
-                get_buffered_columns(
+                fetch_right_columns_by_idxs(
                     &self.buffered_data,
                     buffered_idx,
-                    &buffered_indices,
+                    &right_indices,
                 )?
             } else {
                 // If buffered batch none, meaning it is null joined batch.
@@ -1588,29 +1597,30 @@ impl SMJStream {
                 create_unmatched_columns(
                     self.join_type,
                     &self.buffered_schema,
-                    buffered_indices.len(),
+                    right_indices.len(),
                 )
             };
 
             // Prepare the columns we apply join filter on later.
             // Only for joined rows between streamed and buffered.
             let filter_columns = if chunk.buffered_batch_idx.is_some() {
-                if matches!(self.join_type, JoinType::Right) {
-                    get_filter_column(&self.filter, &buffered_columns, 
&streamed_columns)
-                } else if matches!(
-                    self.join_type,
-                    JoinType::LeftSemi | JoinType::LeftAnti | 
JoinType::LeftMark
-                ) {
-                    // unwrap is safe here as we check is_some on top of if 
statement
-                    let buffered_columns = get_buffered_columns(
-                        &self.buffered_data,
-                        chunk.buffered_batch_idx.unwrap(),
-                        &buffered_indices,
-                    )?;
+                if !matches!(self.join_type, JoinType::Right) {
+                    if matches!(
+                        self.join_type,
+                        JoinType::LeftSemi | JoinType::LeftAnti | 
JoinType::LeftMark
+                    ) {
+                        let right_cols = fetch_right_columns_by_idxs(
+                            &self.buffered_data,
+                            chunk.buffered_batch_idx.unwrap(),
+                            &right_indices,
+                        )?;
 
-                    get_filter_column(&self.filter, &streamed_columns, 
&buffered_columns)
+                        get_filter_column(&self.filter, &left_columns, 
&right_cols)
+                    } else {
+                        get_filter_column(&self.filter, &left_columns, 
&right_columns)
+                    }
                 } else {
-                    get_filter_column(&self.filter, &streamed_columns, 
&buffered_columns)
+                    get_filter_column(&self.filter, &right_columns, 
&left_columns)
                 }
             } else {
                 // This chunk is totally for null joined rows (outer join), we 
don't need to apply join filter.
@@ -1618,12 +1628,12 @@ impl SMJStream {
                 vec![]
             };
 
-            let columns = if matches!(self.join_type, JoinType::Right) {
-                buffered_columns.extend(streamed_columns);
-                buffered_columns
+            let columns = if !matches!(self.join_type, JoinType::Right) {
+                left_columns.extend(right_columns);
+                left_columns
             } else {
-                streamed_columns.extend(buffered_columns);
-                streamed_columns
+                right_columns.extend(left_columns);
+                right_columns
             };
 
             let output_batch = RecordBatch::try_new(Arc::clone(&self.schema), 
columns)?;
@@ -1665,9 +1675,7 @@ impl SMJStream {
                             | JoinType::LeftMark
                             | JoinType::Full
                     ) {
-                        self.output_record_batches
-                            .batches
-                            .push(output_batch.clone());
+                        self.output_record_batches.batches.push(output_batch);
                     } else {
                         let filtered_batch = 
filter_record_batch(&output_batch, &mask)?;
                         
self.output_record_batches.batches.push(filtered_batch);
@@ -1678,12 +1686,10 @@ impl SMJStream {
                     } else {
                         
self.output_record_batches.filter_mask.extend(pre_mask);
                     }
-                    self.output_record_batches
-                        .row_indices
-                        .extend(&streamed_indices);
+                    
self.output_record_batches.row_indices.extend(&left_indices);
                     self.output_record_batches.batch_ids.extend(vec![
-                            self.streamed_batch_counter.load(Relaxed);
-                            streamed_indices.len()
+                        self.streamed_batch_counter.load(Relaxed);
+                        left_indices.len()
                         ]);
 
                     // For outer joins, we need to push the null joined rows 
to the output if
@@ -1697,16 +1703,16 @@ impl SMJStream {
                         for i in 0..pre_mask.len() {
                             // If the buffered row is not joined with streamed 
side,
                             // skip it.
-                            if buffered_indices.is_null(i) {
+                            if right_indices.is_null(i) {
                                 continue;
                             }
 
-                            let buffered_index = buffered_indices.value(i);
+                            let buffered_index = right_indices.value(i);
 
-                            buffered_batch.join_filter_failed_map.insert(
+                            buffered_batch.join_filter_not_matched_map.insert(
                                 buffered_index,
                                 *buffered_batch
-                                    .join_filter_failed_map
+                                    .join_filter_not_matched_map
                                     .get(&buffered_index)
                                     .unwrap_or(&true)
                                     && !pre_mask.value(i),
@@ -1765,6 +1771,9 @@ impl SMJStream {
         let mut batch_ids = &self.output_record_batches.batch_ids;
         let default_batch_ids = vec![0; record_batch.num_rows()];
 
+        // If only nulls come in and indices sizes doesn't match with expected 
record batch count
+        // generate missing indices
+        // Happens for null joined batches for Full Join
         if out_indices.null_count() == out_indices.len()
             && out_indices.len() != record_batch.num_rows()
         {
@@ -1804,32 +1813,32 @@ impl SMJStream {
             let null_mask = compute::not(corrected_mask)?;
             let null_joined_batch = filter_record_batch(&record_batch, 
&null_mask)?;
 
-            let mut buffered_columns = create_unmatched_columns(
+            let mut right_columns = create_unmatched_columns(
                 self.join_type,
                 &self.buffered_schema,
                 null_joined_batch.num_rows(),
             );
 
-            let columns = if matches!(self.join_type, JoinType::Right) {
-                let streamed_columns = null_joined_batch
+            let columns = if !matches!(self.join_type, JoinType::Right) {
+                let mut left_columns = null_joined_batch
                     .columns()
                     .iter()
-                    .skip(left_columns_length)
+                    .take(right_columns_length)
                     .cloned()
                     .collect::<Vec<_>>();
 
-                buffered_columns.extend(streamed_columns);
-                buffered_columns
+                left_columns.extend(right_columns);
+                left_columns
             } else {
-                let mut streamed_columns = null_joined_batch
+                let left_columns = null_joined_batch
                     .columns()
                     .iter()
-                    .take(right_columns_length)
+                    .skip(left_columns_length)
                     .cloned()
                     .collect::<Vec<_>>();
 
-                streamed_columns.extend(buffered_columns);
-                streamed_columns
+                right_columns.extend(left_columns);
+                right_columns
             };
 
             // Push the streamed/buffered batch joined nulls to the output
@@ -1905,10 +1914,7 @@ impl SMJStream {
             )?;
         }
 
-        self.output_record_batches.batches.clear();
-        self.output_record_batches.batch_ids.clear();
-        self.output_record_batches.filter_mask = BooleanBuilder::new();
-        self.output_record_batches.row_indices = UInt64Builder::new();
+        self.output_record_batches.clear();
 
         Ok(filtered_record_batch)
     }
@@ -1971,39 +1977,39 @@ fn produce_buffered_null_batch(
     }
 
     // Take buffered (right) columns
-    let buffered_columns =
-        get_buffered_columns_from_batch(buffered_batch, buffered_indices)?;
+    let right_columns =
+        fetch_right_columns_from_batch_by_idxs(buffered_batch, 
buffered_indices)?;
 
     // Create null streamed (left) columns
-    let mut streamed_columns = streamed_schema
+    let mut left_columns = streamed_schema
         .fields()
         .iter()
         .map(|f| new_null_array(f.data_type(), buffered_indices.len()))
         .collect::<Vec<_>>();
 
-    streamed_columns.extend(buffered_columns);
+    left_columns.extend(right_columns);
 
     Ok(Some(RecordBatch::try_new(
         Arc::clone(schema),
-        streamed_columns,
+        left_columns,
     )?))
 }
 
-/// Get `buffered_indices` rows for `buffered_data[buffered_batch_idx]`
+/// Get `buffered_indices` rows for `buffered_data[buffered_batch_idx]` by 
specific column indices
 #[inline(always)]
-fn get_buffered_columns(
+fn fetch_right_columns_by_idxs(
     buffered_data: &BufferedData,
     buffered_batch_idx: usize,
     buffered_indices: &UInt64Array,
 ) -> Result<Vec<ArrayRef>> {
-    get_buffered_columns_from_batch(
+    fetch_right_columns_from_batch_by_idxs(
         &buffered_data.batches[buffered_batch_idx],
         buffered_indices,
     )
 }
 
 #[inline(always)]
-fn get_buffered_columns_from_batch(
+fn fetch_right_columns_from_batch_by_idxs(
     buffered_batch: &BufferedBatch,
     buffered_indices: &UInt64Array,
 ) -> Result<Vec<ArrayRef>> {


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to