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 afecd7be32 Move filtered SMJ right join out of `join_partial` phase 
(#13053)
afecd7be32 is described below

commit afecd7be32b23dfb5b9adf94793584c22113cc39
Author: Oleks V <[email protected]>
AuthorDate: Tue Oct 22 13:41:53 2024 -0700

    Move filtered SMJ right join out of `join_partial` phase (#13053)
    
    * Move filtered SMJ right join out of `join_partial` phase
---
 datafusion/core/tests/fuzz_cases/join_fuzz.rs      |   4 +-
 .../physical-plan/src/joins/sort_merge_join.rs     | 257 ++++++++-------------
 .../sqllogictest/test_files/sort_merge_join.slt    |  56 +++--
 3 files changed, 122 insertions(+), 195 deletions(-)

diff --git a/datafusion/core/tests/fuzz_cases/join_fuzz.rs 
b/datafusion/core/tests/fuzz_cases/join_fuzz.rs
index 2eab45256d..ca2c2bf4e4 100644
--- a/datafusion/core/tests/fuzz_cases/join_fuzz.rs
+++ b/datafusion/core/tests/fuzz_cases/join_fuzz.rs
@@ -149,8 +149,6 @@ async fn test_right_join_1k() {
 }
 
 #[tokio::test]
-// flaky for HjSmj case
-// https://github.com/apache/datafusion/issues/12359
 async fn test_right_join_1k_filtered() {
     JoinFuzzTestCase::new(
         make_staggered_batches(1000),
@@ -158,7 +156,7 @@ async fn test_right_join_1k_filtered() {
         JoinType::Right,
         Some(Box::new(col_lt_col_filter)),
     )
-    .run_test(&[JoinTestType::NljHj], false)
+    .run_test(&[JoinTestType::HjSmj, JoinTestType::NljHj], false)
     .await
 }
 
diff --git a/datafusion/physical-plan/src/joins/sort_merge_join.rs 
b/datafusion/physical-plan/src/joins/sort_merge_join.rs
index 5e77becd1c..d513485544 100644
--- a/datafusion/physical-plan/src/joins/sort_merge_join.rs
+++ b/datafusion/physical-plan/src/joins/sort_merge_join.rs
@@ -727,15 +727,19 @@ impl RecordBatchStream for SMJStream {
     }
 }
 
+/// True if next index refers to either:
+/// - another batch id
+/// - another row index within same batch id
+/// - end of row indices
 #[inline(always)]
 fn last_index_for_row(
     row_index: usize,
     indices: &UInt64Array,
-    ids: &[usize],
+    batch_ids: &[usize],
     indices_len: usize,
 ) -> bool {
     row_index == indices_len - 1
-        || ids[row_index] != ids[row_index + 1]
+        || batch_ids[row_index] != batch_ids[row_index + 1]
         || indices.value(row_index) != indices.value(row_index + 1)
 }
 
@@ -746,21 +750,21 @@ fn last_index_for_row(
 // `false` - the row sent as NULL joined row
 fn get_corrected_filter_mask(
     join_type: JoinType,
-    indices: &UInt64Array,
-    ids: &[usize],
+    row_indices: &UInt64Array,
+    batch_ids: &[usize],
     filter_mask: &BooleanArray,
     expected_size: usize,
 ) -> Option<BooleanArray> {
-    let streamed_indices_length = indices.len();
+    let row_indices_length = row_indices.len();
     let mut corrected_mask: BooleanBuilder =
-        BooleanBuilder::with_capacity(streamed_indices_length);
+        BooleanBuilder::with_capacity(row_indices_length);
     let mut seen_true = false;
 
     match join_type {
-        JoinType::Left => {
-            for i in 0..streamed_indices_length {
+        JoinType::Left | JoinType::Right => {
+            for i in 0..row_indices_length {
                 let last_index =
-                    last_index_for_row(i, indices, ids, 
streamed_indices_length);
+                    last_index_for_row(i, row_indices, batch_ids, 
row_indices_length);
                 if filter_mask.value(i) {
                     seen_true = true;
                     corrected_mask.append_value(true);
@@ -781,9 +785,9 @@ fn get_corrected_filter_mask(
             Some(corrected_mask.finish())
         }
         JoinType::LeftSemi => {
-            for i in 0..streamed_indices_length {
+            for i in 0..row_indices_length {
                 let last_index =
-                    last_index_for_row(i, indices, ids, 
streamed_indices_length);
+                    last_index_for_row(i, row_indices, batch_ids, 
row_indices_length);
                 if filter_mask.value(i) && !seen_true {
                     seen_true = true;
                     corrected_mask.append_value(true);
@@ -828,7 +832,9 @@ impl Stream for SMJStream {
                                     if self.filter.is_some()
                                         && matches!(
                                             self.join_type,
-                                            JoinType::Left | JoinType::LeftSemi
+                                            JoinType::Left
+                                                | JoinType::LeftSemi
+                                                | JoinType::Right
                                         )
                                     {
                                         self.freeze_all()?;
@@ -904,7 +910,7 @@ impl Stream for SMJStream {
                             let record_batch = if !(self.filter.is_some()
                                 && matches!(
                                     self.join_type,
-                                    JoinType::Left | JoinType::LeftSemi
+                                    JoinType::Left | JoinType::LeftSemi | 
JoinType::Right
                                 )) {
                                 record_batch
                             } else {
@@ -923,7 +929,7 @@ impl Stream for SMJStream {
                         if self.filter.is_some()
                             && matches!(
                                 self.join_type,
-                                JoinType::Left | JoinType::LeftSemi
+                                JoinType::Left | JoinType::LeftSemi | 
JoinType::Right
                             )
                         {
                             let out = self.filter_joined_batch()?;
@@ -1445,7 +1451,6 @@ impl SMJStream {
                 };
 
             let streamed_columns_length = streamed_columns.len();
-            let buffered_columns_length = buffered_columns.len();
 
             // Prepare the columns we apply join filter on later.
             // Only for joined rows between streamed and buffered.
@@ -1512,7 +1517,10 @@ impl SMJStream {
                     };
 
                     // Push the filtered batch which contains rows passing 
join filter to the output
-                    if matches!(self.join_type, JoinType::Left | 
JoinType::LeftSemi) {
+                    if matches!(
+                        self.join_type,
+                        JoinType::Left | JoinType::LeftSemi | JoinType::Right
+                    ) {
                         self.output_record_batches
                             .batches
                             .push(output_batch.clone());
@@ -1534,7 +1542,7 @@ impl SMJStream {
                     // all joined rows are failed on the join filter.
                     // I.e., if all rows joined from a streamed row are failed 
with the join filter,
                     // we need to join it with nulls as buffered side.
-                    if matches!(self.join_type, JoinType::Right | 
JoinType::Full) {
+                    if matches!(self.join_type, JoinType::Full) {
                         // We need to get the mask for row indices that the 
joined rows are failed
                         // on the join filter. I.e., for a row in streamed 
side, if all joined rows
                         // between it and all buffered rows are failed on the 
join filter, we need to
@@ -1552,7 +1560,7 @@ impl SMJStream {
                         let null_joined_batch =
                             filter_record_batch(&output_batch, &not_mask)?;
 
-                        let mut buffered_columns = self
+                        let buffered_columns = self
                             .buffered_schema
                             .fields()
                             .iter()
@@ -1564,18 +1572,7 @@ impl SMJStream {
                             })
                             .collect::<Vec<_>>();
 
-                        let columns = if matches!(self.join_type, 
JoinType::Right) {
-                            let streamed_columns = null_joined_batch
-                                .columns()
-                                .iter()
-                                .skip(buffered_columns_length)
-                                .cloned()
-                                .collect::<Vec<_>>();
-
-                            buffered_columns.extend(streamed_columns);
-                            buffered_columns
-                        } else {
-                            // Left join or full outer join
+                        let columns = {
                             let mut streamed_columns = null_joined_batch
                                 .columns()
                                 .iter()
@@ -1590,6 +1587,7 @@ impl SMJStream {
                         // Push the streamed/buffered batch joined nulls to 
the output
                         let null_joined_streamed_batch =
                             RecordBatch::try_new(Arc::clone(&self.schema), 
columns)?;
+
                         self.output_record_batches
                             .batches
                             .push(null_joined_streamed_batch);
@@ -1654,7 +1652,10 @@ impl SMJStream {
         }
 
         if !(self.filter.is_some()
-            && matches!(self.join_type, JoinType::Left | JoinType::LeftSemi))
+            && matches!(
+                self.join_type,
+                JoinType::Left | JoinType::LeftSemi | JoinType::Right
+            ))
         {
             self.output_record_batches.batches.clear();
         }
@@ -3333,8 +3334,7 @@ mod tests {
         Ok(())
     }
 
-    #[tokio::test]
-    async fn test_left_outer_join_filtered_mask() -> Result<()> {
+    fn build_joined_record_batches() -> Result<JoinedRecordBatches> {
         let schema = Arc::new(Schema::new(vec![
             Field::new("a", DataType::Int32, true),
             Field::new("b", DataType::Int32, true),
@@ -3342,14 +3342,14 @@ mod tests {
             Field::new("y", DataType::Int32, true),
         ]));
 
-        let mut tb = JoinedRecordBatches {
+        let mut batches = JoinedRecordBatches {
             batches: vec![],
             filter_mask: BooleanBuilder::new(),
             row_indices: UInt64Builder::new(),
             batch_ids: vec![],
         };
 
-        tb.batches.push(RecordBatch::try_new(
+        batches.batches.push(RecordBatch::try_new(
             Arc::clone(&schema),
             vec![
                 Arc::new(Int32Array::from(vec![1, 1])),
@@ -3359,7 +3359,7 @@ mod tests {
             ],
         )?);
 
-        tb.batches.push(RecordBatch::try_new(
+        batches.batches.push(RecordBatch::try_new(
             Arc::clone(&schema),
             vec![
                 Arc::new(Int32Array::from(vec![1])),
@@ -3369,7 +3369,7 @@ mod tests {
             ],
         )?);
 
-        tb.batches.push(RecordBatch::try_new(
+        batches.batches.push(RecordBatch::try_new(
             Arc::clone(&schema),
             vec![
                 Arc::new(Int32Array::from(vec![1, 1])),
@@ -3379,7 +3379,7 @@ mod tests {
             ],
         )?);
 
-        tb.batches.push(RecordBatch::try_new(
+        batches.batches.push(RecordBatch::try_new(
             Arc::clone(&schema),
             vec![
                 Arc::new(Int32Array::from(vec![1])),
@@ -3389,7 +3389,7 @@ mod tests {
             ],
         )?);
 
-        tb.batches.push(RecordBatch::try_new(
+        batches.batches.push(RecordBatch::try_new(
             Arc::clone(&schema),
             vec![
                 Arc::new(Int32Array::from(vec![1, 1])),
@@ -3400,41 +3400,62 @@ mod tests {
         )?);
 
         let streamed_indices = vec![0, 0];
-        tb.batch_ids.extend(vec![0; streamed_indices.len()]);
-        tb.row_indices.extend(&UInt64Array::from(streamed_indices));
+        batches.batch_ids.extend(vec![0; streamed_indices.len()]);
+        batches
+            .row_indices
+            .extend(&UInt64Array::from(streamed_indices));
 
         let streamed_indices = vec![1];
-        tb.batch_ids.extend(vec![0; streamed_indices.len()]);
-        tb.row_indices.extend(&UInt64Array::from(streamed_indices));
+        batches.batch_ids.extend(vec![0; streamed_indices.len()]);
+        batches
+            .row_indices
+            .extend(&UInt64Array::from(streamed_indices));
 
         let streamed_indices = vec![0, 0];
-        tb.batch_ids.extend(vec![1; streamed_indices.len()]);
-        tb.row_indices.extend(&UInt64Array::from(streamed_indices));
+        batches.batch_ids.extend(vec![1; streamed_indices.len()]);
+        batches
+            .row_indices
+            .extend(&UInt64Array::from(streamed_indices));
 
         let streamed_indices = vec![0];
-        tb.batch_ids.extend(vec![2; streamed_indices.len()]);
-        tb.row_indices.extend(&UInt64Array::from(streamed_indices));
+        batches.batch_ids.extend(vec![2; streamed_indices.len()]);
+        batches
+            .row_indices
+            .extend(&UInt64Array::from(streamed_indices));
 
         let streamed_indices = vec![0, 0];
-        tb.batch_ids.extend(vec![3; streamed_indices.len()]);
-        tb.row_indices.extend(&UInt64Array::from(streamed_indices));
+        batches.batch_ids.extend(vec![3; streamed_indices.len()]);
+        batches
+            .row_indices
+            .extend(&UInt64Array::from(streamed_indices));
 
-        tb.filter_mask
+        batches
+            .filter_mask
             .extend(&BooleanArray::from(vec![true, false]));
-        tb.filter_mask.extend(&BooleanArray::from(vec![true]));
-        tb.filter_mask
+        batches.filter_mask.extend(&BooleanArray::from(vec![true]));
+        batches
+            .filter_mask
             .extend(&BooleanArray::from(vec![false, true]));
-        tb.filter_mask.extend(&BooleanArray::from(vec![false]));
-        tb.filter_mask
+        batches.filter_mask.extend(&BooleanArray::from(vec![false]));
+        batches
+            .filter_mask
             .extend(&BooleanArray::from(vec![false, false]));
 
-        let output = concat_batches(&schema, &tb.batches)?;
-        let out_mask = tb.filter_mask.finish();
-        let out_indices = tb.row_indices.finish();
+        Ok(batches)
+    }
+
+    #[tokio::test]
+    async fn test_left_outer_join_filtered_mask() -> Result<()> {
+        let mut joined_batches = build_joined_record_batches()?;
+        let schema = joined_batches.batches.first().unwrap().schema();
+
+        let output = concat_batches(&schema, &joined_batches.batches)?;
+        let out_mask = joined_batches.filter_mask.finish();
+        let out_indices = joined_batches.row_indices.finish();
 
         assert_eq!(
             get_corrected_filter_mask(
-                JoinType::Left,
+                Left,
                 &UInt64Array::from(vec![0]),
                 &[0usize],
                 &BooleanArray::from(vec![true]),
@@ -3448,7 +3469,7 @@ mod tests {
 
         assert_eq!(
             get_corrected_filter_mask(
-                JoinType::Left,
+                Left,
                 &UInt64Array::from(vec![0]),
                 &[0usize],
                 &BooleanArray::from(vec![false]),
@@ -3462,7 +3483,7 @@ mod tests {
 
         assert_eq!(
             get_corrected_filter_mask(
-                JoinType::Left,
+                Left,
                 &UInt64Array::from(vec![0, 0]),
                 &[0usize; 2],
                 &BooleanArray::from(vec![true, true]),
@@ -3476,7 +3497,7 @@ mod tests {
 
         assert_eq!(
             get_corrected_filter_mask(
-                JoinType::Left,
+                Left,
                 &UInt64Array::from(vec![0, 0, 0]),
                 &[0usize; 3],
                 &BooleanArray::from(vec![true, true, true]),
@@ -3488,7 +3509,7 @@ mod tests {
 
         assert_eq!(
             get_corrected_filter_mask(
-                JoinType::Left,
+                Left,
                 &UInt64Array::from(vec![0, 0, 0]),
                 &[0usize; 3],
                 &BooleanArray::from(vec![true, false, true]),
@@ -3509,7 +3530,7 @@ mod tests {
 
         assert_eq!(
             get_corrected_filter_mask(
-                JoinType::Left,
+                Left,
                 &UInt64Array::from(vec![0, 0, 0]),
                 &[0usize; 3],
                 &BooleanArray::from(vec![false, false, true]),
@@ -3530,7 +3551,7 @@ mod tests {
 
         assert_eq!(
             get_corrected_filter_mask(
-                JoinType::Left,
+                Left,
                 &UInt64Array::from(vec![0, 0, 0]),
                 &[0usize; 3],
                 &BooleanArray::from(vec![false, true, true]),
@@ -3551,7 +3572,7 @@ mod tests {
 
         assert_eq!(
             get_corrected_filter_mask(
-                JoinType::Left,
+                Left,
                 &UInt64Array::from(vec![0, 0, 0]),
                 &[0usize; 3],
                 &BooleanArray::from(vec![false, false, false]),
@@ -3571,9 +3592,9 @@ mod tests {
         );
 
         let corrected_mask = get_corrected_filter_mask(
-            JoinType::Left,
+            Left,
             &out_indices,
-            &tb.batch_ids,
+            &joined_batches.batch_ids,
             &out_mask,
             output.num_rows(),
         )
@@ -3643,102 +3664,12 @@ mod tests {
 
     #[tokio::test]
     async fn test_left_semi_join_filtered_mask() -> Result<()> {
-        let schema = Arc::new(Schema::new(vec![
-            Field::new("a", DataType::Int32, true),
-            Field::new("b", DataType::Int32, true),
-            Field::new("x", DataType::Int32, true),
-            Field::new("y", DataType::Int32, true),
-        ]));
-
-        let mut tb = JoinedRecordBatches {
-            batches: vec![],
-            filter_mask: BooleanBuilder::new(),
-            row_indices: UInt64Builder::new(),
-            batch_ids: vec![],
-        };
-
-        tb.batches.push(RecordBatch::try_new(
-            Arc::clone(&schema),
-            vec![
-                Arc::new(Int32Array::from(vec![1, 1])),
-                Arc::new(Int32Array::from(vec![10, 10])),
-                Arc::new(Int32Array::from(vec![1, 1])),
-                Arc::new(Int32Array::from(vec![11, 9])),
-            ],
-        )?);
-
-        tb.batches.push(RecordBatch::try_new(
-            Arc::clone(&schema),
-            vec![
-                Arc::new(Int32Array::from(vec![1])),
-                Arc::new(Int32Array::from(vec![11])),
-                Arc::new(Int32Array::from(vec![1])),
-                Arc::new(Int32Array::from(vec![12])),
-            ],
-        )?);
-
-        tb.batches.push(RecordBatch::try_new(
-            Arc::clone(&schema),
-            vec![
-                Arc::new(Int32Array::from(vec![1, 1])),
-                Arc::new(Int32Array::from(vec![12, 12])),
-                Arc::new(Int32Array::from(vec![1, 1])),
-                Arc::new(Int32Array::from(vec![11, 13])),
-            ],
-        )?);
-
-        tb.batches.push(RecordBatch::try_new(
-            Arc::clone(&schema),
-            vec![
-                Arc::new(Int32Array::from(vec![1])),
-                Arc::new(Int32Array::from(vec![13])),
-                Arc::new(Int32Array::from(vec![1])),
-                Arc::new(Int32Array::from(vec![12])),
-            ],
-        )?);
-
-        tb.batches.push(RecordBatch::try_new(
-            Arc::clone(&schema),
-            vec![
-                Arc::new(Int32Array::from(vec![1, 1])),
-                Arc::new(Int32Array::from(vec![14, 14])),
-                Arc::new(Int32Array::from(vec![1, 1])),
-                Arc::new(Int32Array::from(vec![12, 11])),
-            ],
-        )?);
-
-        let streamed_indices = vec![0, 0];
-        tb.batch_ids.extend(vec![0; streamed_indices.len()]);
-        tb.row_indices.extend(&UInt64Array::from(streamed_indices));
-
-        let streamed_indices = vec![1];
-        tb.batch_ids.extend(vec![0; streamed_indices.len()]);
-        tb.row_indices.extend(&UInt64Array::from(streamed_indices));
-
-        let streamed_indices = vec![0, 0];
-        tb.batch_ids.extend(vec![1; streamed_indices.len()]);
-        tb.row_indices.extend(&UInt64Array::from(streamed_indices));
-
-        let streamed_indices = vec![0];
-        tb.batch_ids.extend(vec![2; streamed_indices.len()]);
-        tb.row_indices.extend(&UInt64Array::from(streamed_indices));
-
-        let streamed_indices = vec![0, 0];
-        tb.batch_ids.extend(vec![3; streamed_indices.len()]);
-        tb.row_indices.extend(&UInt64Array::from(streamed_indices));
-
-        tb.filter_mask
-            .extend(&BooleanArray::from(vec![true, false]));
-        tb.filter_mask.extend(&BooleanArray::from(vec![true]));
-        tb.filter_mask
-            .extend(&BooleanArray::from(vec![false, true]));
-        tb.filter_mask.extend(&BooleanArray::from(vec![false]));
-        tb.filter_mask
-            .extend(&BooleanArray::from(vec![false, false]));
+        let mut joined_batches = build_joined_record_batches()?;
+        let schema = joined_batches.batches.first().unwrap().schema();
 
-        let output = concat_batches(&schema, &tb.batches)?;
-        let out_mask = tb.filter_mask.finish();
-        let out_indices = tb.row_indices.finish();
+        let output = concat_batches(&schema, &joined_batches.batches)?;
+        let out_mask = joined_batches.filter_mask.finish();
+        let out_indices = joined_batches.row_indices.finish();
 
         assert_eq!(
             get_corrected_filter_mask(
@@ -3839,7 +3770,7 @@ mod tests {
         let corrected_mask = get_corrected_filter_mask(
             LeftSemi,
             &out_indices,
-            &tb.batch_ids,
+            &joined_batches.batch_ids,
             &out_mask,
             output.num_rows(),
         )
diff --git a/datafusion/sqllogictest/test_files/sort_merge_join.slt 
b/datafusion/sqllogictest/test_files/sort_merge_join.slt
index d00b7d6f0a..051cc6dce3 100644
--- a/datafusion/sqllogictest/test_files/sort_merge_join.slt
+++ b/datafusion/sqllogictest/test_files/sort_merge_join.slt
@@ -100,14 +100,13 @@ Alice 100 Alice 2
 Alice 50 Alice 1
 Alice 50 Alice 2
 
-# Uncomment when filtered RIGHT moved
 # right join with join filter
-#query TITI rowsort
-#SELECT * FROM t1 RIGHT JOIN t2 ON t1.a = t2.a AND t2.b * 50 <= t1.b
-#----
-#Alice 100 Alice 1
-#Alice 100 Alice 2
-#Alice 50 Alice 1
+query TITI rowsort
+SELECT * FROM t1 RIGHT JOIN t2 ON t1.a = t2.a AND t2.b * 50 <= t1.b
+----
+Alice 100 Alice 1
+Alice 100 Alice 2
+Alice 50 Alice 1
 
 query TITI rowsort
 SELECT * FROM t1 RIGHT JOIN t2 ON t1.a = t2.a AND t1.b > t2.b
@@ -137,7 +136,7 @@ Bob 1 NULL NULL
 #Bob 1 NULL NULL
 #NULL NULL Alice 1
 
-# Uncomment when filtered RIGHT moved
+# Uncomment when filtered FULL moved
 #query TITI rowsort
 #SELECT * FROM t1 FULL JOIN t2 ON t1.a = t2.a AND t1.b > t2.b + 50
 #----
@@ -617,27 +616,26 @@ set datafusion.execution.batch_size = 1;
 #) order by 1, 2
 #----
 
-# Uncomment when filtered RIGHT moved
-#query IIII
-#select * from (
-#with t as (
-#    select id, id % 5 id1 from (select unnest(range(0,10)) id)
-#), t1 as (
-#    select id % 10 id, id + 2 id1 from (select unnest(range(0,10)) id)
-#)
-#select * from t right join t1 on t.id1 = t1.id and t.id > t1.id1
-#) order by 1, 2, 3, 4
-#----
-#5 0 0 2
-#6 1 1 3
-#7 2 2 4
-#8 3 3 5
-#9 4 4 6
-#NULL NULL 5 7
-#NULL NULL 6 8
-#NULL NULL 7 9
-#NULL NULL 8 10
-#NULL NULL 9 11
+query IIII
+select * from (
+with t as (
+    select id, id % 5 id1 from (select unnest(range(0,10)) id)
+), t1 as (
+    select id % 10 id, id + 2 id1 from (select unnest(range(0,10)) id)
+)
+select * from t right join t1 on t.id1 = t1.id and t.id > t1.id1
+) order by 1, 2, 3, 4
+----
+5 0 0 2
+6 1 1 3
+7 2 2 4
+8 3 3 5
+9 4 4 6
+NULL NULL 5 7
+NULL NULL 6 8
+NULL NULL 7 9
+NULL NULL 8 10
+NULL NULL 9 11
 
 query IIII
 select * from (


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

Reply via email to