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 146f16a0c1 Move filtered SMJ Left Anti filtered join out of
`join_partial` phase (#13111)
146f16a0c1 is described below
commit 146f16a0c14f0fe65e2bd8b7226508f27ced3f13
Author: Oleks V <[email protected]>
AuthorDate: Sat Oct 26 09:16:01 2024 -0700
Move filtered SMJ Left Anti filtered join out of `join_partial` phase
(#13111)
* Move filtered SMJ Left Anti filtered join out of `join_partial` phase
---
datafusion/core/tests/fuzz_cases/join_fuzz.rs | 6 +-
.../physical-plan/src/joins/sort_merge_join.rs | 245 ++++++++++++-
.../sqllogictest/test_files/sort_merge_join.slt | 383 ++++++++++-----------
3 files changed, 414 insertions(+), 220 deletions(-)
diff --git a/datafusion/core/tests/fuzz_cases/join_fuzz.rs
b/datafusion/core/tests/fuzz_cases/join_fuzz.rs
index ca2c2bf4e4..44d34b674b 100644
--- a/datafusion/core/tests/fuzz_cases/join_fuzz.rs
+++ b/datafusion/core/tests/fuzz_cases/join_fuzz.rs
@@ -41,6 +41,7 @@ use datafusion::physical_plan::joins::{
};
use datafusion::physical_plan::memory::MemoryExec;
+use crate::fuzz_cases::join_fuzz::JoinTestType::NljHj;
use datafusion::prelude::{SessionConfig, SessionContext};
use test_utils::stagger_batch_with_seed;
@@ -223,9 +224,6 @@ async fn test_anti_join_1k() {
}
#[tokio::test]
-// flaky for HjSmj case, giving 1 rows difference sometimes
-// https://github.com/apache/datafusion/issues/11555
-#[ignore]
async fn test_anti_join_1k_filtered() {
JoinFuzzTestCase::new(
make_staggered_batches(1000),
@@ -233,7 +231,7 @@ async fn test_anti_join_1k_filtered() {
JoinType::LeftAnti,
Some(Box::new(col_lt_col_filter)),
)
- .run_test(&[JoinTestType::NljHj], false)
+ .run_test(&[JoinTestType::HjSmj, 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 d513485544..7b7b7462f7 100644
--- a/datafusion/physical-plan/src/joins/sort_merge_join.rs
+++ b/datafusion/physical-plan/src/joins/sort_merge_join.rs
@@ -802,6 +802,32 @@ fn get_corrected_filter_mask(
Some(corrected_mask.finish())
}
+ JoinType::LeftAnti => {
+ for i in 0..row_indices_length {
+ let last_index =
+ last_index_for_row(i, row_indices, batch_ids,
row_indices_length);
+
+ if filter_mask.value(i) {
+ seen_true = true;
+ }
+
+ if last_index {
+ if !seen_true {
+ corrected_mask.append_value(true);
+ } else {
+ corrected_mask.append_null();
+ }
+
+ seen_true = false;
+ } else {
+ corrected_mask.append_null();
+ }
+ }
+
+ let null_matched = expected_size - corrected_mask.len();
+ corrected_mask.extend(vec![Some(true); null_matched]);
+ Some(corrected_mask.finish())
+ }
// Only outer joins needs to keep track of processed rows and apply
corrected filter mask
_ => None,
}
@@ -835,15 +861,18 @@ impl Stream for SMJStream {
JoinType::Left
| JoinType::LeftSemi
| JoinType::Right
+ | JoinType::LeftAnti
)
{
self.freeze_all()?;
if
!self.output_record_batches.batches.is_empty()
- &&
self.buffered_data.scanning_finished()
{
- let out_batch =
self.filter_joined_batch()?;
- return
Poll::Ready(Some(Ok(out_batch)));
+ let out_filtered_batch =
+ self.filter_joined_batch()?;
+ return Poll::Ready(Some(Ok(
+ out_filtered_batch,
+ )));
}
}
@@ -907,15 +936,17 @@ impl Stream for SMJStream {
// because target output batch size can be hit in
the middle of
// filtering causing the filtering to be
incomplete and causing
// correctness issues
- let record_batch = if !(self.filter.is_some()
+ if self.filter.is_some()
&& matches!(
self.join_type,
- JoinType::Left | JoinType::LeftSemi |
JoinType::Right
- )) {
- record_batch
- } else {
+ JoinType::Left
+ | JoinType::LeftSemi
+ | JoinType::Right
+ | JoinType::LeftAnti
+ )
+ {
continue;
- };
+ }
return Poll::Ready(Some(Ok(record_batch)));
}
@@ -929,7 +960,10 @@ impl Stream for SMJStream {
if self.filter.is_some()
&& matches!(
self.join_type,
- JoinType::Left | JoinType::LeftSemi |
JoinType::Right
+ JoinType::Left
+ | JoinType::LeftSemi
+ | JoinType::Right
+ | JoinType::LeftAnti
)
{
let out = self.filter_joined_batch()?;
@@ -1273,11 +1307,7 @@ impl SMJStream {
};
if matches!(self.join_type, JoinType::LeftAnti) &&
self.filter.is_some() {
- join_streamed = !self
- .streamed_batch
- .join_filter_matched_idxs
- .contains(&(self.streamed_batch.idx as u64))
- && !self.streamed_joined;
+ join_streamed = !self.streamed_joined;
join_buffered = join_streamed;
}
}
@@ -1519,7 +1549,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 | JoinType::Right
+ JoinType::Left
+ | JoinType::LeftSemi
+ | JoinType::Right
+ | JoinType::LeftAnti
) {
self.output_record_batches
.batches
@@ -1654,7 +1687,10 @@ impl SMJStream {
if !(self.filter.is_some()
&& matches!(
self.join_type,
- JoinType::Left | JoinType::LeftSemi | JoinType::Right
+ JoinType::Left
+ | JoinType::LeftSemi
+ | JoinType::Right
+ | JoinType::LeftAnti
))
{
self.output_record_batches.batches.clear();
@@ -1727,7 +1763,7 @@ impl SMJStream {
&self.schema,
&[filtered_record_batch, null_joined_streamed_batch],
)?;
- } else if matches!(self.join_type, JoinType::LeftSemi) {
+ } else if matches!(self.join_type, JoinType::LeftSemi |
JoinType::LeftAnti) {
let output_column_indices =
(0..streamed_columns_length).collect::<Vec<_>>();
filtered_record_batch =
filtered_record_batch.project(&output_column_indices)?;
@@ -3349,6 +3385,7 @@ mod tests {
batch_ids: vec![],
};
+ // Insert already prejoined non-filtered rows
batches.batches.push(RecordBatch::try_new(
Arc::clone(&schema),
vec![
@@ -3835,6 +3872,178 @@ mod tests {
Ok(())
}
+ #[tokio::test]
+ async fn test_left_anti_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(
+ LeftAnti,
+ &UInt64Array::from(vec![0]),
+ &[0usize],
+ &BooleanArray::from(vec![true]),
+ 1
+ )
+ .unwrap(),
+ BooleanArray::from(vec![None])
+ );
+
+ assert_eq!(
+ get_corrected_filter_mask(
+ LeftAnti,
+ &UInt64Array::from(vec![0]),
+ &[0usize],
+ &BooleanArray::from(vec![false]),
+ 1
+ )
+ .unwrap(),
+ BooleanArray::from(vec![Some(true)])
+ );
+
+ assert_eq!(
+ get_corrected_filter_mask(
+ LeftAnti,
+ &UInt64Array::from(vec![0, 0]),
+ &[0usize; 2],
+ &BooleanArray::from(vec![true, true]),
+ 2
+ )
+ .unwrap(),
+ BooleanArray::from(vec![None, None])
+ );
+
+ assert_eq!(
+ get_corrected_filter_mask(
+ LeftAnti,
+ &UInt64Array::from(vec![0, 0, 0]),
+ &[0usize; 3],
+ &BooleanArray::from(vec![true, true, true]),
+ 3
+ )
+ .unwrap(),
+ BooleanArray::from(vec![None, None, None])
+ );
+
+ assert_eq!(
+ get_corrected_filter_mask(
+ LeftAnti,
+ &UInt64Array::from(vec![0, 0, 0]),
+ &[0usize; 3],
+ &BooleanArray::from(vec![true, false, true]),
+ 3
+ )
+ .unwrap(),
+ BooleanArray::from(vec![None, None, None])
+ );
+
+ assert_eq!(
+ get_corrected_filter_mask(
+ LeftAnti,
+ &UInt64Array::from(vec![0, 0, 0]),
+ &[0usize; 3],
+ &BooleanArray::from(vec![false, false, true]),
+ 3
+ )
+ .unwrap(),
+ BooleanArray::from(vec![None, None, None])
+ );
+
+ assert_eq!(
+ get_corrected_filter_mask(
+ LeftAnti,
+ &UInt64Array::from(vec![0, 0, 0]),
+ &[0usize; 3],
+ &BooleanArray::from(vec![false, true, true]),
+ 3
+ )
+ .unwrap(),
+ BooleanArray::from(vec![None, None, None])
+ );
+
+ assert_eq!(
+ get_corrected_filter_mask(
+ LeftAnti,
+ &UInt64Array::from(vec![0, 0, 0]),
+ &[0usize; 3],
+ &BooleanArray::from(vec![false, false, false]),
+ 3
+ )
+ .unwrap(),
+ BooleanArray::from(vec![None, None, Some(true)])
+ );
+
+ let corrected_mask = get_corrected_filter_mask(
+ LeftAnti,
+ &out_indices,
+ &joined_batches.batch_ids,
+ &out_mask,
+ output.num_rows(),
+ )
+ .unwrap();
+
+ assert_eq!(
+ corrected_mask,
+ BooleanArray::from(vec![
+ None,
+ None,
+ None,
+ None,
+ None,
+ Some(true),
+ None,
+ Some(true)
+ ])
+ );
+
+ let filtered_rb = filter_record_batch(&output, &corrected_mask)?;
+
+ assert_batches_eq!(
+ &[
+ "+---+----+---+----+",
+ "| a | b | x | y |",
+ "+---+----+---+----+",
+ "| 1 | 13 | 1 | 12 |",
+ "| 1 | 14 | 1 | 11 |",
+ "+---+----+---+----+",
+ ],
+ &[filtered_rb]
+ );
+
+ // output null rows
+ let null_mask = arrow::compute::not(&corrected_mask)?;
+ assert_eq!(
+ null_mask,
+ BooleanArray::from(vec![
+ None,
+ None,
+ None,
+ None,
+ None,
+ Some(false),
+ None,
+ Some(false),
+ ])
+ );
+
+ let null_joined_batch = filter_record_batch(&output, &null_mask)?;
+
+ assert_batches_eq!(
+ &[
+ "+---+---+---+---+",
+ "| a | b | x | y |",
+ "+---+---+---+---+",
+ "+---+---+---+---+",
+ ],
+ &[null_joined_batch]
+ );
+ Ok(())
+ }
+
/// Returns the column names on the schema
fn columns(schema: &Schema) -> Vec<String> {
schema.fields().iter().map(|f| f.name().clone()).collect()
diff --git a/datafusion/sqllogictest/test_files/sort_merge_join.slt
b/datafusion/sqllogictest/test_files/sort_merge_join.slt
index 051cc6dce3..f4cc888d6b 100644
--- a/datafusion/sqllogictest/test_files/sort_merge_join.slt
+++ b/datafusion/sqllogictest/test_files/sort_merge_join.slt
@@ -407,214 +407,201 @@ select t1.* from t1 where exists (select 1 from t2
where t2.a = t1.a and t2.b !=
statement ok
set datafusion.execution.batch_size = 10;
-# Uncomment when filtered LEFTANTI moved
-#query II
-#select * from (
-#with
-#t1 as (
-# select 11 a, 12 b),
-#t2 as (
-# select 11 a, 13 c union all
-# select 11 a, 14 c
-# )
-#select t1.* from t1 where not exists (select 1 from t2 where t2.a = t1.a and
t1.b > t2.c)
-#) order by 1, 2
-#----
-#11 12
-
-# Uncomment when filtered LEFTANTI moved
-#query III
-#select * from (
-#with
-#t1 as (
-# select 11 a, 12 b, 1 c union all
-# select 11 a, 13 b, 2 c),
-#t2 as (
-# select 11 a, 12 b, 3 c union all
-# select 11 a, 14 b, 4 c
-# )
-#select t1.* from t1 where not exists (select 1 from t2 where t2.a = t1.a and
t2.b != t1.b and t1.c > t2.c)
-#) order by 1, 2;
-#----
-#11 12 1
-#11 13 2
-
-# Uncomment when filtered LEFTANTI moved
-#query III
-#select * from (
-#with
-#t1 as (
-# select 11 a, 12 b, 1 c union all
-# select 11 a, 13 b, 2 c),
-#t2 as (
-# select 11 a, 12 b, 3 c where false
-# )
-#select t1.* from t1 where not exists (select 1 from t2 where t2.a = t1.a and
t2.b != t1.b and t1.c > t2.c)
-#) order by 1, 2;
-#----
-#11 12 1
-#11 13 2
-
-# Uncomment when filtered LEFTANTI moved
-#query II
-#select * from (
-#with
-#t1 as (
-# select 11 a, 12 b),
-#t2 as (
-# select 11 a, 13 c union all
-# select 11 a, 14 c union all
-# select 11 a, 15 c
-# )
-#select t1.* from t1 where not exists (select 1 from t2 where t2.a = t1.a and
t1.b > t2.c)
-#) order by 1, 2
-#----
-#11 12
-
-# Uncomment when filtered LEFTANTI moved
-#query II
-#select * from (
-#with
-#t1 as (
-# select 11 a, 12 b),
-#t2 as (
-# select 11 a, 11 c union all
-# select 11 a, 14 c union all
-# select 11 a, 15 c
-# )
-#select t1.* from t1 where not exists (select 1 from t2 where t2.a = t1.a and
t1.b > t2.c)
-#) order by 1, 2
-#----
+query II
+select * from (
+with
+t1 as (
+ select 11 a, 12 b),
+t2 as (
+ select 11 a, 13 c union all
+ select 11 a, 14 c
+ )
+select t1.* from t1 where not exists (select 1 from t2 where t2.a = t1.a and
t1.b > t2.c)
+) order by 1, 2
+----
+11 12
-# Uncomment when filtered LEFTANTI moved
-#query II
-#select * from (
-#with
-#t1 as (
-# select 11 a, 12 b),
-#t2 as (
-# select 11 a, 12 c union all
-# select 11 a, 11 c union all
-# select 11 a, 15 c
-# )
-#select t1.* from t1 where not exists (select 1 from t2 where t2.a = t1.a and
t1.b > t2.c)
-#) order by 1, 2
-#----
+query III
+select * from (
+with
+t1 as (
+ select 11 a, 12 b, 1 c union all
+ select 11 a, 13 b, 2 c),
+t2 as (
+ select 11 a, 12 b, 3 c union all
+ select 11 a, 14 b, 4 c
+ )
+select t1.* from t1 where not exists (select 1 from t2 where t2.a = t1.a and
t2.b != t1.b and t1.c > t2.c)
+) order by 1, 2;
+----
+11 12 1
+11 13 2
+query III
+select * from (
+with
+t1 as (
+ select 11 a, 12 b, 1 c union all
+ select 11 a, 13 b, 2 c),
+t2 as (
+ select 11 a, 12 b, 3 c where false
+ )
+select t1.* from t1 where not exists (select 1 from t2 where t2.a = t1.a and
t2.b != t1.b and t1.c > t2.c)
+) order by 1, 2;
+----
+11 12 1
+11 13 2
-# Uncomment when filtered LEFTANTI moved
-#query II
-#select * from (
-#with
-#t1 as (
-# select 11 a, 12 b),
-#t2 as (
-# select 11 a, 12 c union all
-# select 11 a, 14 c union all
-# select 11 a, 11 c
-# )
-#select t1.* from t1 where not exists (select 1 from t2 where t2.a = t1.a and
t1.b > t2.c)
-#) order by 1, 2
-#----
+query II
+select * from (
+with
+t1 as (
+ select 11 a, 12 b),
+t2 as (
+ select 11 a, 13 c union all
+ select 11 a, 14 c union all
+ select 11 a, 15 c
+ )
+select t1.* from t1 where not exists (select 1 from t2 where t2.a = t1.a and
t1.b > t2.c)
+) order by 1, 2
+----
+11 12
+
+query II
+select * from (
+with
+t1 as (
+ select 11 a, 12 b),
+t2 as (
+ select 11 a, 11 c union all
+ select 11 a, 14 c union all
+ select 11 a, 15 c
+ )
+select t1.* from t1 where not exists (select 1 from t2 where t2.a = t1.a and
t1.b > t2.c)
+) order by 1, 2
+----
+
+query II
+select * from (
+with
+t1 as (
+ select 11 a, 12 b),
+t2 as (
+ select 11 a, 12 c union all
+ select 11 a, 11 c union all
+ select 11 a, 15 c
+ )
+select t1.* from t1 where not exists (select 1 from t2 where t2.a = t1.a and
t1.b > t2.c)
+) order by 1, 2
+----
+
+
+query II
+select * from (
+with
+t1 as (
+ select 11 a, 12 b),
+t2 as (
+ select 11 a, 12 c union all
+ select 11 a, 14 c union all
+ select 11 a, 11 c
+ )
+select t1.* from t1 where not exists (select 1 from t2 where t2.a = t1.a and
t1.b > t2.c)
+) order by 1, 2
+----
# Test LEFT ANTI with cross batch data distribution
statement ok
set datafusion.execution.batch_size = 1;
-# Uncomment when filtered LEFTANTI moved
-#query II
-#select * from (
-#with
-#t1 as (
-# select 11 a, 12 b),
-#t2 as (
-# select 11 a, 13 c union all
-# select 11 a, 14 c
-# )
-#select t1.* from t1 where not exists (select 1 from t2 where t2.a = t1.a and
t1.b > t2.c)
-#) order by 1, 2
-#----
-#11 12
-
-# Uncomment when filtered LEFTANTI moved
-#query III
-#select * from (
-#with
-#t1 as (
-# select 11 a, 12 b, 1 c union all
-# select 11 a, 13 b, 2 c),
-#t2 as (
-# select 11 a, 12 b, 3 c union all
-# select 11 a, 14 b, 4 c
-# )
-#select t1.* from t1 where not exists (select 1 from t2 where t2.a = t1.a and
t2.b != t1.b and t1.c > t2.c)
-#) order by 1, 2;
-#----
-#11 12 1
-#11 13 2
-
-# Uncomment when filtered LEFTANTI moved
-#query III
-#select * from (
-#with
-#t1 as (
-# select 11 a, 12 b, 1 c union all
-# select 11 a, 13 b, 2 c),
-#t2 as (
-# select 11 a, 12 b, 3 c where false
-# )
-#select t1.* from t1 where not exists (select 1 from t2 where t2.a = t1.a and
t2.b != t1.b and t1.c > t2.c)
-#) order by 1, 2;
-#----
-#11 12 1
-#11 13 2
-
-# Uncomment when filtered LEFTANTI moved
-#query II
-#select * from (
-#with
-#t1 as (
-# select 11 a, 12 b),
-#t2 as (
-# select 11 a, 13 c union all
-# select 11 a, 14 c union all
-# select 11 a, 15 c
-# )
-#select t1.* from t1 where not exists (select 1 from t2 where t2.a = t1.a and
t1.b > t2.c)
-#) order by 1, 2
-#----
-#11 12
-
-# Uncomment when filtered LEFTANTI moved
-#query II
-#select * from (
-#with
-#t1 as (
-# select 11 a, 12 b),
-#t2 as (
-# select 11 a, 12 c union all
-# select 11 a, 11 c union all
-# select 11 a, 15 c
-# )
-#select t1.* from t1 where not exists (select 1 from t2 where t2.a = t1.a and
t1.b > t2.c)
-#) order by 1, 2
-#----
+query II
+select * from (
+with
+t1 as (
+ select 11 a, 12 b),
+t2 as (
+ select 11 a, 13 c union all
+ select 11 a, 14 c
+ )
+select t1.* from t1 where not exists (select 1 from t2 where t2.a = t1.a and
t1.b > t2.c)
+) order by 1, 2
+----
+11 12
-# Uncomment when filtered LEFTANTI moved
-#query II
-#select * from (
-#with
-#t1 as (
-# select 11 a, 12 b),
-#t2 as (
-# select 11 a, 12 c union all
-# select 11 a, 14 c union all
-# select 11 a, 11 c
-# )
-#select t1.* from t1 where not exists (select 1 from t2 where t2.a = t1.a and
t1.b > t2.c)
-#) order by 1, 2
-#----
+query III
+select * from (
+with
+t1 as (
+ select 11 a, 12 b, 1 c union all
+ select 11 a, 13 b, 2 c),
+t2 as (
+ select 11 a, 12 b, 3 c union all
+ select 11 a, 14 b, 4 c
+ )
+select t1.* from t1 where not exists (select 1 from t2 where t2.a = t1.a and
t2.b != t1.b and t1.c > t2.c)
+) order by 1, 2;
+----
+11 12 1
+11 13 2
+
+query III
+select * from (
+with
+t1 as (
+ select 11 a, 12 b, 1 c union all
+ select 11 a, 13 b, 2 c),
+t2 as (
+ select 11 a, 12 b, 3 c where false
+ )
+select t1.* from t1 where not exists (select 1 from t2 where t2.a = t1.a and
t2.b != t1.b and t1.c > t2.c)
+) order by 1, 2;
+----
+11 12 1
+11 13 2
+
+query II
+select * from (
+with
+t1 as (
+ select 11 a, 12 b),
+t2 as (
+ select 11 a, 13 c union all
+ select 11 a, 14 c union all
+ select 11 a, 15 c
+ )
+select t1.* from t1 where not exists (select 1 from t2 where t2.a = t1.a and
t1.b > t2.c)
+) order by 1, 2
+----
+11 12
+
+query II
+select * from (
+with
+t1 as (
+ select 11 a, 12 b),
+t2 as (
+ select 11 a, 12 c union all
+ select 11 a, 11 c union all
+ select 11 a, 15 c
+ )
+select t1.* from t1 where not exists (select 1 from t2 where t2.a = t1.a and
t1.b > t2.c)
+) order by 1, 2
+----
+
+query II
+select * from (
+with
+t1 as (
+ select 11 a, 12 b),
+t2 as (
+ select 11 a, 12 c union all
+ select 11 a, 14 c union all
+ select 11 a, 11 c
+ )
+select t1.* from t1 where not exists (select 1 from t2 where t2.a = t1.a and
t1.b > t2.c)
+) order by 1, 2
+----
query IIII
select * from (
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]