-
Notifications
You must be signed in to change notification settings - Fork 29.3k
[SPARK-32220][SQL]SHUFFLE_REPLICATE_NL Hint should not change Non-Cartesian Product join result #29035
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[SPARK-32220][SQL]SHUFFLE_REPLICATE_NL Hint should not change Non-Cartesian Product join result #29035
Changes from 3 commits
5f1269f
27b41e5
aeccc25
36ff4ca
a65d332
d9b4dcd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -239,7 +239,7 @@ class JoinHintSuite extends PlanTest with SharedSparkSession with AdaptiveSparkP | |
| .hint("shuffle_replicate_nl") | ||
| .join(df, "id"), | ||
| JoinHint( | ||
| Some(HintInfo(strategy = Some(SHUFFLE_REPLICATE_NL))), | ||
| None, | ||
| None) :: | ||
| JoinHint( | ||
| None, | ||
|
|
@@ -291,7 +291,7 @@ class JoinHintSuite extends PlanTest with SharedSparkSession with AdaptiveSparkP | |
| Some(HintInfo(strategy = Some(BROADCAST))), | ||
| Some(HintInfo(strategy = Some(SHUFFLE_MERGE)))) :: | ||
| JoinHint( | ||
| Some(HintInfo(strategy = Some(SHUFFLE_REPLICATE_NL))), | ||
| None, | ||
| Some(HintInfo(strategy = Some(SHUFFLE_HASH)))) :: Nil, | ||
| msgNoHintRelationFound("c", "broadcast(a, c)") :: | ||
| msgJoinHintOverridden("merge") :: | ||
|
|
@@ -336,6 +336,10 @@ class JoinHintSuite extends PlanTest with SharedSparkSession with AdaptiveSparkP | |
| hints.map("/*+ " + _ + " */").mkString( | ||
| "SELECT ", " ", s" * FROM t1 $joinType JOIN t2 ON t1.key = t2.key") | ||
|
|
||
| def nonCondJoinQueryWithHint(hints: Seq[String], joinType: String = "INNER"): String = | ||
| hints.map("/*+ " + _ + " */").mkString( | ||
| "SELECT ", " ", s" * FROM t1 $joinType JOIN t2") | ||
|
|
||
| def nonEquiJoinQueryWithHint(hints: Seq[String], joinType: String = "INNER"): String = | ||
| hints.map("/*+ " + _ + " */").mkString( | ||
| "SELECT ", " ", s" * FROM t1 $joinType JOIN t2 ON t1.key > t2.key") | ||
|
|
@@ -375,12 +379,12 @@ class JoinHintSuite extends PlanTest with SharedSparkSession with AdaptiveSparkP | |
| assert(shuffleMergeJoins.size == 1) | ||
| } | ||
|
|
||
| private def assertShuffleReplicateNLJoin(df: DataFrame): Unit = { | ||
| private def assertShuffleReplicateNLJoin(df: DataFrame, size: Int = 1): Unit = { | ||
| val executedPlan = df.queryExecution.executedPlan | ||
| val shuffleReplicateNLJoins = collect(executedPlan) { | ||
| case c: CartesianProductExec => c | ||
| } | ||
| assert(shuffleReplicateNLJoins.size == 1) | ||
| assert(shuffleReplicateNLJoins.size == size) | ||
| } | ||
|
|
||
| test("join strategy hint - broadcast") { | ||
|
|
@@ -523,29 +527,51 @@ class JoinHintSuite extends PlanTest with SharedSparkSession with AdaptiveSparkP | |
| withSQLConf(SQLConf.AUTO_BROADCASTJOIN_THRESHOLD.key -> Int.MaxValue.toString) { | ||
| // Shuffle-replicate-nl hint specified on one side | ||
| assertShuffleReplicateNLJoin( | ||
| sql(equiJoinQueryWithHint("SHUFFLE_REPLICATE_NL(t1)" :: Nil))) | ||
| sql(nonCondJoinQueryWithHint("SHUFFLE_REPLICATE_NL(t1)" :: Nil))) | ||
| assertShuffleReplicateNLJoin( | ||
| sql(equiJoinQueryWithHint("SHUFFLE_REPLICATE_NL(t2)" :: Nil))) | ||
| sql(nonCondJoinQueryWithHint("SHUFFLE_REPLICATE_NL(t2)" :: Nil))) | ||
|
|
||
| // Shuffle-replicate-nl hint specified on both sides | ||
| assertShuffleReplicateNLJoin( | ||
| sql(equiJoinQueryWithHint("SHUFFLE_REPLICATE_NL(t1, t2)" :: Nil))) | ||
| sql(nonCondJoinQueryWithHint("SHUFFLE_REPLICATE_NL(t1, t2)" :: Nil))) | ||
|
|
||
| // Shuffle-merge hint prioritized over shuffle-replicate-nl hint, but shuffle-merge hint | ||
| // is not applicable | ||
| assertShuffleReplicateNLJoin( | ||
| sql(nonEquiJoinQueryWithHint("MERGE(t1)" :: "SHUFFLE_REPLICATE_NL(t2)" :: Nil))) | ||
| sql(nonCondJoinQueryWithHint("MERGE(t1)" :: "SHUFFLE_REPLICATE_NL(t2)" :: Nil))) | ||
|
|
||
| // Shuffle-hash hint prioritized over shuffle-replicate-nl hint, but shuffle-hash hint is | ||
| // not applicable | ||
| assertShuffleReplicateNLJoin( | ||
| sql(nonEquiJoinQueryWithHint("SHUFFLE_HASH(t2)" :: "SHUFFLE_REPLICATE_NL(t1)" :: Nil))) | ||
| sql(nonCondJoinQueryWithHint("SHUFFLE_HASH(t2)" :: "SHUFFLE_REPLICATE_NL(t1)" :: Nil))) | ||
|
|
||
| // Shuffle-replicate-nl hint specified but not doable | ||
| assertBroadcastHashJoin( | ||
| sql(equiJoinQueryWithHint("SHUFFLE_REPLICATE_NL(t1, t2)" :: Nil, "left")), BuildRight) | ||
| assertBroadcastNLJoin( | ||
| sql(nonEquiJoinQueryWithHint("SHUFFLE_REPLICATE_NL(t1, t2)" :: Nil, "right")), BuildLeft) | ||
|
|
||
|
|
||
| // Verify remove error shuffle_replicate_nl hint | ||
| // Shuffle-replicate-nl hint specified on one side | ||
| assertShuffleReplicateNLJoin( | ||
| sql(equiJoinQueryWithHint("SHUFFLE_REPLICATE_NL(t1)" :: Nil)), 0) | ||
| assertShuffleReplicateNLJoin( | ||
| sql(equiJoinQueryWithHint("SHUFFLE_REPLICATE_NL(t2)" :: Nil)), 0) | ||
|
|
||
| // Shuffle-replicate-nl hint specified on both sides | ||
| assertShuffleReplicateNLJoin( | ||
| sql(equiJoinQueryWithHint("SHUFFLE_REPLICATE_NL(t1, t2)" :: Nil)), 0) | ||
|
|
||
| // Shuffle-merge hint prioritized over shuffle-replicate-nl hint, but shuffle-merge hint | ||
| // is not applicable | ||
| assertShuffleReplicateNLJoin( | ||
| sql(nonEquiJoinQueryWithHint("MERGE(t1)" :: "SHUFFLE_REPLICATE_NL(t2)" :: Nil)), 0) | ||
|
|
||
| // Shuffle-hash hint prioritized over shuffle-replicate-nl hint, but shuffle-hash hint is | ||
| // not applicable | ||
| assertShuffleReplicateNLJoin( | ||
| sql(nonEquiJoinQueryWithHint("SHUFFLE_HASH(t2)" :: "SHUFFLE_REPLICATE_NL(t1)" :: Nil)), 0) | ||
| } | ||
| } | ||
| } | ||
|
|
@@ -570,4 +596,22 @@ class JoinHintSuite extends PlanTest with SharedSparkSession with AdaptiveSparkP | |
| assert(joinHints == expectedHints) | ||
| } | ||
| } | ||
|
|
||
| test("SPARK-32220: Non Cartesian Product Join Result Correct with SHUFFLE_REPLICATE_NL hint") { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So, is this a correctness issue, @AngersZhuuuu ?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yea, I think so. Nice catch, @AngersZhuuuu
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yea, when I try new join hint, I found this result is non-correct. |
||
| withTempView("t1", "t2") { | ||
| Seq((1, "4"), (2, "2")).toDF("key", "value").createTempView("t1") | ||
| Seq((1, "1"), (2, "12.3"), (2, "123")).toDF("key", "value").createTempView("t2") | ||
| val df1 = sql("SELECT /*+ shuffle_replicate_nl(t1) */ * from t1 join t2 ON t1.key = t2.key") | ||
| val df2 = sql("SELECT * from t1 join t2 ON t1.key = t2.key") | ||
| assert(df1.collect().size == df2.collect().size) | ||
|
|
||
| val df3 = sql("SELECT /*+ shuffle_replicate_nl(t1) */ * from t1 join t2") | ||
| val df4 = sql("SELECT * from t1 join t2") | ||
| assert(df3.collect().size == df4.collect().size) | ||
|
|
||
| val df5 = sql("SELECT /*+ shuffle_replicate_nl(t1) */ * from t1 join t2 ON t1.key < t2.key") | ||
| val df6 = sql("SELECT * from t1 join t2 ON t1.key < t2.key") | ||
| assert(df5.collect().size == df6.collect().size) | ||
| } | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should pass a correct condition (
leftKeysandrightKeys) intoCartesianProductExecinstead of removing the hint:spark/sql/core/src/main/scala/org/apache/spark/sql/execution/SparkStrategies.scala
Line 202 in ac6406e
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems the spark strategy incrrectly removes
a#0 = a#2;There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yea, in default Cartesian Product Join situation, it didn't need condition at all. So in default, it seems don't have condition when build data.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@maropu See latest change, it's ok to do like this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since we don't know the join keys's condition is
EqualToorEaultNullSafeso it's better just not remove it inExtractEqualJoinKeysThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You cannot use the original condition in logical.Join?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't know we can write like this....updated..