Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -192,30 +192,35 @@ class EnumerablesTest {
newArrayList(1, 3, 4),
newArrayList(1, 4),
equalTo("[3]"),
equalTo("[3, null]"),
JoinType.ANTI);
// Matching key at start and end of right, not of left
testIntersect(
newArrayList(0, 1, 3, 4, 5),
newArrayList(1, 4),
equalTo("[0, 3, 5]"),
equalTo("[0, 3, 5, null]"),
JoinType.ANTI);
// Matching key at start and end of left, not right
testIntersect(
newArrayList(1, 3, 4),
newArrayList(0, 1, 4, 5),
equalTo("[3]"),
equalTo("[3, null]"),
JoinType.ANTI);
// Matching key not at start or end of left or right
testIntersect(
newArrayList(0, 2, 3, 4, 5),
newArrayList(1, 3, 4, 6),
equalTo("[0, 2, 5]"),
equalTo("[0, 2, 5, null]"),
JoinType.ANTI);
// Matching duplicated keys
testIntersect(
newArrayList(1, 3, 4),
newArrayList(1, 1, 4, 4),
equalTo("[3]"),
equalTo("[3, null]"),
JoinType.ANTI);

// LEFT join tests:
Expand Down Expand Up @@ -291,24 +296,28 @@ class EnumerablesTest {
newArrayList(0, 2, 4),
newArrayList(1, 3, 5),
equalTo("[0, 2, 4]"),
equalTo("[0, 2, 4, null]"),
JoinType.ANTI);
// Left empty
testIntersect(
new ArrayList<>(),
newArrayList(1, 3, 4, 6),
equalTo("[]"),
equalTo("[null]"),
JoinType.ANTI);
// Right empty
testIntersect(
newArrayList(3, 7),
new ArrayList<>(),
equalTo("[3, 7]"),
equalTo("[3, 7, null]"),
JoinType.ANTI);
// Both empty
testIntersect(
new ArrayList<Integer>(),
new ArrayList<>(),
equalTo("[]"),
equalTo("[null]"),
JoinType.ANTI);

// LEFT join tests:
Expand Down Expand Up @@ -342,6 +351,21 @@ class EnumerablesTest {
JoinType.LEFT);
}

@Test void testMergeAntiJoinWithRepeatedNullKeys() {
testIntersect(
newArrayList(1, 2, null, null),
newArrayList(1),
equalTo("[2, null, null]"),
equalTo("[2, null, null, null]"),
JoinType.ANTI);
testIntersect(
newArrayList(1, null, null),
newArrayList(1, 2),
equalTo("[null, null]"),
equalTo("[null, null, null]"),
JoinType.ANTI);
}

private static <T extends Comparable<T>> void testIntersect(
List<T> list0, List<T> list1, org.hamcrest.Matcher<String> matcher, JoinType joinType) {
testIntersect(list0, list1, matcher, matcher, joinType);
Expand Down Expand Up @@ -636,7 +660,8 @@ private static <T extends Comparable<T>> Enumerable<String> intersect(
(v0, v1) -> v0,
JoinType.ANTI,
null, null).toList(),
hasToString("[Emp(30, Fred), Emp(20, Sebastian), Emp(20, Zoey)]"));
hasToString("[Emp(30, Fred), Emp(20, Sebastian), Emp(20, Zoey), "
+ "Emp(40, null), Emp(30, null)]"));
}

@Test void testMergeLeftJoin() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5047,11 +5047,11 @@ private Enumerator<TInner> getRightEnumerator() {
}

/** Returns whether the left enumerator was successfully advanced to the next
* element, and it does not have a null key (except for LEFT join, that needs to process
* all elements from left. */
* element, and it does not have a null key (except for LEFT and ANTI joins, which need to
* process all elements from left). */
private boolean leftMoveNext() {
return getLeftEnumerator().moveNext()
&& (joinType == JoinType.LEFT
&& (isLeftOrAntiJoin()
|| outerKeySelector.apply(getLeftEnumerator().current()) != null);
}

Expand Down Expand Up @@ -5125,7 +5125,7 @@ private boolean advance() {
// mergeJoin assumes inputs sorted in ascending order with nulls last,
// if we reach a null key, we are done.
if (leftKey == null || rightKey == null) {
if (joinType == JoinType.LEFT || (joinType == JoinType.ANTI && leftKey != null)) {
if (isLeftOrAntiJoin()) {
// all remaining items in left are results for left/anti join
remainingLeft = true;
return true;
Expand Down Expand Up @@ -5234,9 +5234,10 @@ private boolean advanceLeft(TSource left, TKey leftKey) {
while (getLeftEnumerator().moveNext()) {
left = getLeftEnumerator().current();
TKey leftKey2 = outerKeySelector.apply(left);
if (leftKey2 == null && joinType != JoinType.LEFT) {
if (leftKey2 == null && !isLeftOrAntiJoin()) {
// mergeJoin assumes inputs sorted in ascending order with nulls last,
// if we reach a null key, we are done (except LEFT join, that needs to process LHS fully)
// if we reach a null key, we are done (except LEFT and ANTI joins, which
// process LHS fully)
break;
}
if (!compareEquals(leftKey, leftKey2)) {
Expand Down
Loading