-
Notifications
You must be signed in to change notification settings - Fork 28.2k
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-30298][SQL] Respect aliases in output partitioning of projects and aggregates #26943
Conversation
Test build #115540 has finished for PR 26943 at commit
|
retest this please |
Test build #115545 has finished for PR 26943 at commit
|
retest this please |
Test build #115576 has finished for PR 26943 at commit
|
Have you checked |
sql/core/src/test/scala/org/apache/spark/sql/sources/BucketedReadSuite.scala
Outdated
Show resolved
Hide resolved
Thanks @maropu for the info! Adding the same |
I think we just couldn't reach a consensus about how-to-fix for this issue at that time. |
yea, of course not! You can feel free to take them over. |
@cloud-fan @gatorsmile @viirya This addresses the same issues brought up in #22957 and #17400. I understand those two PRs didn't get merged, but wanted to give another shot at it. (We had few customers asking why bucket join was not respected when aliases were used). Could you help reviewing? Thanks in advance! |
Test build #116062 has finished for PR 26943 at commit
|
retest this please |
sql/core/src/main/scala/org/apache/spark/sql/execution/AliasAwareOutputPartitioning.scala
Outdated
Show resolved
Hide resolved
sql/core/src/test/scala/org/apache/spark/sql/execution/PlannerSuite.scala
Outdated
Show resolved
Hide resolved
Test build #116066 has finished for PR 26943 at commit
|
looks fine to me. Shall we also consider some corner cases like |
Test build #116197 has finished for PR 26943 at commit
|
Test build #116198 has finished for PR 26943 at commit
|
|
||
final override def outputPartitioning: Partitioning = { | ||
child.outputPartitioning match { | ||
case HashPartitioning(expressions, numPartitions) => |
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.
a corner case is that: the child output partitioning is a + 1
and the project list has a + 1 as b
, then the final output partitioning should be b
.
I'm not sure how common it is, maybe it's fine to ignore it.
case other => other | ||
} | ||
HashPartitioning(newExpressions, numPartitions) | ||
case other => other |
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.
How about the other partitioning cases, e.g., range?
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.
PartitioningCollection
is constructed as PartitioningCollection(Seq(left.outputPartitioning, right.outputPartitioning))
, so aliases should have been already removed if the partitioning was HashPartitioning
. But we could add one similar to your solution (https://github.com/apache/spark/pull/17400/files#diff-342789ab9c8c0154b412dd1c719c9397R82-R86) for future proof.
For RangePartitioning
, your change (https://github.com/apache/spark/pull/17400/files#diff-342789ab9c8c0154b412dd1c719c9397R72-R78) makes sense, but I couldn't come up with an actual example to test against. Do you have one in mind?
withSQLConf(SQLConf.AUTO_BROADCASTJOIN_THRESHOLD.key -> "0") { | ||
withTable("t") { | ||
withView("v") { | ||
val df = (0 until 20).map(i => (i, i)).toDF("i", "j").as("df") |
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.
nit: We need .as("df")
for this test? I think you can just write it like;
spark.range(20).selectExpr("id as i", "id as j").write.bucketBy(8, "I").saveAsTable("t")
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.
Thanks, updated.
val plan1 = sql("SELECT * FROM t a JOIN t b ON a.i = b.i").queryExecution.executedPlan | ||
assert(plan1.collect { case exchange: ShuffleExchangeExec => exchange }.isEmpty) | ||
|
||
val plan2 = sql("SELECT * FROM t a JOIN v b ON a.i = b.i").queryExecution.executedPlan |
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.
What does this test means? This test can improve the test coverage?
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 will remove plan1
which is a benign case.
sql/core/src/test/scala/org/apache/spark/sql/execution/PlannerSuite.scala
Show resolved
Hide resolved
sql/core/src/main/scala/org/apache/spark/sql/execution/AliasAwareOutputPartitioning.scala
Show resolved
Hide resolved
Test build #117276 has finished for PR 26943 at commit
|
| (SELECT key AS k from df2) t2 | ||
|ON t1.k = t2.k | ||
""".stripMargin).queryExecution.executedPlan | ||
val exchanges = planned.collect { case s: ShuffleExchangeExec => s } |
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 was confused about why only one shuffle, then realized it's exchange reuse.
Can we join different data frames? e.g. spark.range(10)
and spark.range(20)
.
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.
Thanks for pointing that out. I updated it and it now generates two ShuffleExchangeExec
instead of four.
| (SELECT key + 1 AS k2 from df2) t2 | ||
|ON t1.k1 = t2.k2 | ||
|""".stripMargin).queryExecution.executedPlan | ||
val exchanges = planned.collect { case s: ShuffleExchangeExec => s } |
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.
ditto
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.
LGTM except a minor comments for the test
Test build #117319 has finished for PR 26943 at commit
|
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.
Looks nice and thanks for the work, @imback82 !
Thanks! Merged to master. |
@imback82 I think |
Yes, I will work on it. Thanks @cloud-fan and @maropu for review and guidance! |
Thanks in advance, @imback82 ! |
Yes! I tried the failing example in the comment, but that was working fine in the latest Spark. I will look into this further. |
yea, thanks! |
What changes were proposed in this pull request?
Currently, in the following scenario, bucket join is not utilized:
Notice that
Exchange
is present. This is becauseProject
introduces aliases andoutputPartitioning
andrequiredChildDistribution
do not consider aliases while considering bucket join inEnsureRequirements
. This PR addresses to allow this scenario.Why are the changes needed?
This allows bucket join to be utilized in the above example.
Does this PR introduce any user-facing change?
Yes, now with the fix, the
explain
out is as follows:Note that the
Exchange
is no longer present.How was this patch tested?