dongjoon-hyun commented on code in PR #58338:
URL: https://github.com/apache/spark/pull/58338#discussion_r3872577862


##########
sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/plans/physical/partitioning.scala:
##########
@@ -579,7 +581,17 @@ case class KeyedPartitioning(
   def groupedSatisfies(required: Distribution): Boolean = {
     required match {
       case c @ ClusteredDistribution(requiredClustering, 
requireAllClusterKeys, _, _) =>
-        if (requireAllClusterKeys) {
+        if (isNarrowed && !isGrouped &&
+            !SQLConf.get.v2BucketingAllowKeysSubsetOfPartitionKeys) {
+          // A narrowed, non-grouped partitioning carries the same skew risk 
as using a subset of
+          // partition keys for a join: GroupPartitionsExec will merge 
partitions that held

Review Comment:
   [Nit] "GroupPartitionsExec will merge partitions that held distinct keys in 
the original finer-grained partitioning" states the risky case as fact, but 
when the source was already ungrouped (multiple splits per identical key) and 
the narrowing collapsed nothing, the merged partitions held the *same* original 
key. Since `isNarrowed && !isGrouped` is a proxy (as the PR description itself 
notes), "may merge" would be accurate here and in the `isNarrowed` scaladoc. As 
written, the docs make the guard look exact, which could make the promised 
follow-up tightening look unnecessary to a future reader.



##########
sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/plans/physical/partitioning.scala:
##########
@@ -579,7 +581,17 @@ case class KeyedPartitioning(
   def groupedSatisfies(required: Distribution): Boolean = {
     required match {
       case c @ ClusteredDistribution(requiredClustering, 
requireAllClusterKeys, _, _) =>
-        if (requireAllClusterKeys) {
+        if (isNarrowed && !isGrouped &&

Review Comment:
   [Non-blocking] The `!isGrouped` conjunct here is load-bearing in a way the 
class-level doc contradicts. The doc above ("`groupedSatisfies()`: called on 
non-grouped KPs ...") predates this change, but `satisfies0` also calls 
`groupedSatisfies` on *grouped* KPs (`isGrouped && 
groupedSatisfies(required)`), and grouped-but-narrowed KPs are constructible: 
`PartitioningPreservingUnaryExecNode` recomputes `isGrouped` from the projected 
keys while `isNarrowed` stays sticky, which is exactly the state exercised by 
the existing test "SPARK-46367: narrowing projection with distinct projected 
keys does not require allowKeysSubsetOfPartitionKeys".
   
   If a later cleanup trusts the class doc and drops the "redundant" 
`!isGrouped`, grouped narrowed KPs would stop satisfying 
`ClusteredDistribution` with the config off and pick up unnecessary shuffles 
(that test would fail). Could you touch up the class-level doc (or add a short 
note here) so the conjunct's purpose is recorded?



##########
sql/core/src/test/scala/org/apache/spark/sql/connector/KeyGroupedPartitioningSuite.scala:
##########
@@ -4727,4 +4728,76 @@ class KeyGroupedPartitioningSuite extends 
DistributionAndOrderingSuiteBase with
     assert(shuffles.isEmpty, "should not contain any shuffle")
     checkAnswer(df, Seq(Row(1, "aa", 40.0, 42.0), Row(2, "bb", 10.0, 19.5)))
   }
+
+  test("SPARK-58974: the narrowing skew guard applies regardless of 
requireAllClusterKeys") {
+    // The narrowing guard describes a skew risk that does not depend on which 
key sets count as
+    // matching, so it must apply for either value of `requireAllClusterKeys`. 
It used to sit inside
+    // the `requireAllClusterKeys = false` arm of `groupedSatisfies`, so with 
that setting enabled a
+    // partitioning whose narrowing collapsed distinct keys was grouped anyway 
-- taking exactly the
+    // exposure `allowKeysSubsetOfPartitionKeys` exists to gate, with nobody 
opting in.
+    val cols = Array(
+      Column.create("id", LongType),
+      Column.create("dept", StringType),
+      Column.create("data", StringType))
+    val t2cols = Array(Column.create("id", LongType), Column.create("data", 
StringType))
+    withTable("t1", "t2") {
+      createTable("t1", cols, Array(identity("id"), identity("dept")))
+      sql("INSERT INTO testcat.ns.t1 VALUES (1, 'x', 'a1'), (1, 'y', 'a2'), 
(2, 'z', 'a3')")

Review Comment:
   [Nit] The directly analogous test "SPARK-46367: narrowing projection 
requires allowKeysSubsetOfPartitionKeys" builds this same shape from the 
suite's standing `items`/`itemsColumns` + `purchases`/`purchasesColumns` 
fixtures and the `selectWithMergeJoinHint` helper (it only generates the hint 
prefix from alias strings, so the subquery alias `u` works with it too -- the 
SPARK-46367 test itself passes `"sub"`). Reusing those here would keep the 
pre-fix and post-fix narrowing tests directly comparable instead of introducing 
a parallel `t1`/`t2` schema.



##########
sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/plans/physical/partitioning.scala:
##########
@@ -579,7 +581,17 @@ case class KeyedPartitioning(
   def groupedSatisfies(required: Distribution): Boolean = {
     required match {
       case c @ ClusteredDistribution(requiredClustering, 
requireAllClusterKeys, _, _) =>
-        if (requireAllClusterKeys) {
+        if (isNarrowed && !isGrouped &&
+            !SQLConf.get.v2BucketingAllowKeysSubsetOfPartitionKeys) {

Review Comment:
   [Nit] `SQLConf.get.v2BucketingAllowKeysSubsetOfPartitionKeys` is now read 
twice in this match arm (here and in the `else` branch below). Hoisting it into 
a local val at the top of the `case c @ ClusteredDistribution(...)` arm would 
make it explicit that both branches are gated by the same switch, which is the 
core of this fix, and would keep a future edit from decoupling the two reads.



##########
sql/core/src/test/scala/org/apache/spark/sql/execution/ProjectedOrderingAndPartitioningSuite.scala:
##########
@@ -491,6 +491,36 @@ class ProjectedOrderingAndPartitioningSuite
     }
   }
 
+  test("SPARK-58974: the narrowing guard applies for either value of 
requireAllClusterKeys") {

Review Comment:
   [Nit] The `requireAll = false` iteration exactly duplicates Scenario 1 of 
the existing test "SPARK-46367: narrowing projection with duplicate keys 
requires allowKeysSubsetOfPartitionKeys to satisfy ClusteredDistribution" (same 
keys, same `ProjectExec` fixture, same `groupedSatisfies` asserts; 
`ClusteredDistribution`'s `requireAllClusterKeys` defaults to false). If the 
explicit both-values contrast pair is intentional, fine -- otherwise this test 
could cover only `requireAll = true`, or the loop could be folded into the 
existing test so the two don't have to be kept in lockstep.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to