Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Next Next commit
Code review
  • Loading branch information
dilipbiswal committed Apr 16, 2019
commit fe3e168ecdcbe201dab832f111df6c3d8c6a8241
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ object PushLeftSemiLeftAntiThroughJoin extends Rule[LogicalPlan] with PredicateH
val rightOutput = rightChild.outputSet

if (joinCond.nonEmpty) {
val noPushdown = (PushdownDirection.NONE, None)
val noPushdown = PushdownDirection.NONE
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can use PushdownDirection.NONE directly.

val conditions = splitConjunctivePredicates(joinCond.get)
val (leftConditions, rest) =
conditions.partition(_.references.subsetOf(left.outputSet ++ rightOutput))
Expand All @@ -212,11 +212,11 @@ object PushLeftSemiLeftAntiThroughJoin extends Rule[LogicalPlan] with PredicateH
if (rest.isEmpty && leftConditions.nonEmpty) {
// When the join conditions can be computed based on the left leg of
// leftsemi/anti join then push the leftsemi/anti join to the left side.
(PushdownDirection.TO_LEFT_BRANCH, leftConditions.reduceLeftOption(And))
(PushdownDirection.TO_LEFT_BRANCH)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need to add (...)

} else if (leftConditions.isEmpty && rightConditions.nonEmpty && commonConditions.isEmpty) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what if commonConditions is not empty? can we add a filter at top?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cloud-fan Please refer to my answer above.

// When the join conditions can be computed based on the attributes from right leg of
// leftsemi/anti join then push the leftsemi/anti join to the right side.
(PushdownDirection.TO_RIGHT_BRANCH, rightConditions.reduceLeftOption(And))
(PushdownDirection.TO_RIGHT_BRANCH)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

} else {
noPushdown
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PushPredicateThroughJoin may pushdown to both sides, do we have such a case here?

Copy link
Contributor Author

@dilipbiswal dilipbiswal Apr 11, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cloud-fan To the best of my knowledge, we don't have this case. I actually tried to get a subquery to push down to both legs of the join but couldn't. Normal filter conditions can trigger pushing down to both legs currently though.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we add a TODO and say we will revisit it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cloud-fan Sure. I will add a TODO

}
Expand All @@ -227,15 +227,14 @@ object PushLeftSemiLeftAntiThroughJoin extends Rule[LogicalPlan] with PredicateH
* to the left leg of join.
* 2) if a right outer join, to the right leg of join,
*/
val action = joinType match {
joinType match {
case _: InnerLike | LeftOuter =>
PushdownDirection.TO_LEFT_BRANCH
case RightOuter =>
PushdownDirection.TO_RIGHT_BRANCH
case _ =>
PushdownDirection.NONE
}
(action, None)
}
}

Expand All @@ -244,18 +243,18 @@ object PushLeftSemiLeftAntiThroughJoin extends Rule[LogicalPlan] with PredicateH
case j @ Join(AllowedJoin(left), right, LeftSemiOrAnti(joinType), joinCond, parentHint) =>
val (childJoinType, childLeft, childRight, childCondition, childHint) =
(left.joinType, left.left, left.right, left.condition, left.hint)
val (action, newJoinCond) = pushTo(left, right, joinCond)
val action = pushTo(left, right, joinCond)

action match {
case PushdownDirection.TO_LEFT_BRANCH
if (childJoinType == LeftOuter || childJoinType.isInstanceOf[InnerLike]) =>
// push down leftsemi/anti join to the left table
val newLeft = Join(childLeft, right, joinType, newJoinCond, parentHint)
val newLeft = Join(childLeft, right, joinType, joinCond, parentHint)
Join(newLeft, childRight, childJoinType, childCondition, childHint)
case PushdownDirection.TO_RIGHT_BRANCH
if (childJoinType == RightOuter || childJoinType.isInstanceOf[InnerLike]) =>
// push down leftsemi/anti join to the right table
val newRight = Join(childRight, right, joinType, newJoinCond, parentHint)
val newRight = Join(childRight, right, joinType, joinCond, parentHint)
Join(childLeft, newRight, childJoinType, childCondition, childHint)
case _ =>
// Do nothing
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

when can this happen?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cloud-fan When we decide that we can't pushdown the parent join. For example this test should exercise the default case.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -317,62 +317,58 @@ class LeftSemiPushdownSuite extends PlanTest {
}

Seq(LeftSemi, LeftAnti).foreach { case outerJT =>
Seq(Inner, LeftOuter, Cross).foreach { case innerJT =>
Seq(Inner, LeftOuter, Cross, RightOuter).foreach { case innerJT =>
test(s"$outerJT pushdown with empty join condition join type $innerJT") {
val joinedRelation = testRelation1.join(testRelation2, joinType = innerJT, None)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the inner join can have a join condition, right?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it supports, we should add Seq(Some('d === 'e), None).foreach at the begining too

val originalQuery = joinedRelation.join(testRelation, joinType = outerJT, None)
val optimized = Optimize.execute(originalQuery.analyze)

val pushedDownJoin = testRelation1.join(testRelation, joinType = outerJT, None)
val correctAnswer = pushedDownJoin.join(testRelation2, joinType = innerJT, None)
val correctAnswer = if (innerJT == RightOuter) {
val pushedDownJoin = testRelation2.join(testRelation, joinType = outerJT, None)
testRelation1.join(pushedDownJoin, joinType = innerJT, None)
} else {
val pushedDownJoin = testRelation1.join(testRelation, joinType = outerJT, None)
pushedDownJoin.join(testRelation2, joinType = innerJT, None)
}
comparePlans(optimized, correctAnswer)
}
}
}

Seq(LeftSemi, LeftAnti).foreach { case jt =>
test(s"$jt pushdown with empty join condition join type RightOuter") {
val joinedRelation = testRelation1.join(testRelation2, joinType = RightOuter, None)
val originalQuery = joinedRelation.join(testRelation, joinType = jt, None)
val optimized = Optimize.execute(originalQuery.analyze)

val pushedDownJoin = testRelation2.join(testRelation, joinType = jt, None)
val correctAnswer = testRelation1.join(pushedDownJoin, joinType = RightOuter, None)
comparePlans(optimized, correctAnswer)
}
}

Seq(LeftSemi, LeftAnti).foreach { case outerJT =>
Seq(Inner, LeftOuter, Cross).foreach { case innerJT =>
test(s"$outerJT pushdown with join condition referring to left leg of join type $innerJT") {
val joinedRelation = testRelation1.join(testRelation2, joinType = innerJT, None)
val originalQuery =
joinedRelation.join(testRelation, joinType = outerJT, condition = Some('a === 'd))
val optimized = Optimize.execute(originalQuery.analyze)

val pushedDownJoin =
testRelation1.join(testRelation, joinType = outerJT, condition = Some('a === 'd))
val correctAnswer = pushedDownJoin.join(testRelation2, joinType = innerJT, None).analyze
comparePlans(optimized, correctAnswer)
Seq(Some('d === 'e), None).foreach { case innerJoinCond =>
Seq(LeftSemi, LeftAnti).foreach { case outerJT =>
Seq(Inner, LeftOuter, Cross).foreach { case innerJT =>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These test cases almost cover the above one, except the RightOuter join type. Can we merge them.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nvm, I was wrong

test(s"$outerJT pushdown to left of join type: $innerJT join condition $innerJoinCond") {
val joinedRelation = testRelation1.join(testRelation2, joinType = innerJT, innerJoinCond)
val originalQuery =
joinedRelation.join(testRelation, joinType = outerJT, condition = Some('a === 'd))
val optimized = Optimize.execute(originalQuery.analyze)

val pushedDownJoin =
testRelation1.join(testRelation, joinType = outerJT, condition = Some('a === 'd))
val correctAnswer =
pushedDownJoin.join(testRelation2, joinType = innerJT, innerJoinCond).analyze
comparePlans(optimized, correctAnswer)
}
}
}
}

Seq(LeftSemi, LeftAnti).foreach { case outerJT =>
Seq(Inner, LeftOuter, Cross).foreach { case innerJT =>
test(s"$outerJT pushdown with outer and inner join condition for join type $innerJT") {
val joinedRelation =
testRelation1.join(testRelation2, joinType = innerJT, condition = Some('d === 'e))
val originalQuery =
joinedRelation.join(testRelation, joinType = outerJT, condition = Some('a === 'd))
val optimized = Optimize.execute(originalQuery.analyze)

val pushedDownJoin =
testRelation1.join(testRelation, joinType = outerJT, condition = Some('a === 'd))
val correctAnswer = pushedDownJoin
.join(testRelation2, joinType = innerJT, condition = Some('d === 'e))
.analyze
comparePlans(optimized, correctAnswer)
Seq(Some('e === 'd), None).foreach { case innerJoinCond =>
Seq(LeftSemi, LeftAnti).foreach { case outerJT =>
Seq(Inner, RightOuter, Cross).foreach { case innerJT =>
test(s"$outerJT pushdown to right of join type: $innerJT join condition $innerJoinCond") {
val joinedRelation = testRelation1.join(testRelation2, joinType = innerJT, innerJoinCond)
val originalQuery =
joinedRelation.join(testRelation, joinType = outerJT, condition = Some('a === 'e))
val optimized = Optimize.execute(originalQuery.analyze)

val pushedDownJoin =
testRelation2.join(testRelation, joinType = outerJT, condition = Some('a === 'e))
val correctAnswer =
testRelation1.join(pushedDownJoin, joinType = innerJT, innerJoinCond).analyze
comparePlans(optimized, correctAnswer)
}
}
}
}
Expand All @@ -387,41 +383,6 @@ class LeftSemiPushdownSuite extends PlanTest {
}
}

Seq(LeftSemi, LeftAnti).foreach { case outerJT =>
Seq(Inner, RightOuter, Cross).foreach { case innerJT =>
test(s"$outerJT pushdown with join condition referring to right leg - join type $innerJT") {
val joinedRelation = testRelation1.join(testRelation2, joinType = innerJT, None)
val originalQuery =
joinedRelation.join(testRelation, joinType = outerJT, condition = Some('a === 'e))
val optimized = Optimize.execute(originalQuery.analyze)

val pushedDownJoin =
testRelation2.join(testRelation, joinType = outerJT, condition = Some('a === 'e))
val correctAnswer = testRelation1.join(pushedDownJoin, joinType = innerJT, None).analyze
comparePlans(optimized, correctAnswer)
}
}
}

Seq(LeftSemi, LeftAnti).foreach { case outerJT =>
Seq(Inner, RightOuter, Cross).foreach { case innerJT =>
test(s"$outerJT pushdown with outer and inner join conditions for join type $innerJT") {
val joinedRelation = testRelation1.
join(testRelation2, joinType = innerJT, condition = Some('e === 'd))
val originalQuery =
joinedRelation.join(testRelation, joinType = outerJT, condition = Some('a === 'e))
val optimized = Optimize.execute(originalQuery.analyze)

val pushedDownJoin =
testRelation2.join(testRelation, joinType = outerJT, condition = Some('a === 'e))
val correctAnswer = testRelation1.
join(pushedDownJoin, joinType = innerJT, condition = Some('e === 'd))
.analyze
comparePlans(optimized, correctAnswer)
}
}
}

Seq(LeftSemi, LeftAnti).foreach { case jt =>
test(s"$jt no pushdown - join condition refers right leg - join type for LeftOuter") {
val joinedRelation = testRelation1.join(testRelation2, joinType = LeftOuter, None)
Expand Down