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


##########
sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/plans/physical/partitioning.scala:
##########
@@ -719,16 +755,18 @@ case class KeyedPartitioning(
       }
       copy(
         expressions = positions.map(expressions),
-        partitionKeys = projectedKeys,
-        isGrouped = !collapses && sourceOf.size == projectedKeys.length,
-        isCollapsed = isCollapsed || collapses)
+        layout = KeyLayout(

Review Comment:
   `project` used to go through `copy(...)`, which carried 
`mayContainUnknownPartitionKeys` along with the other fields. Building a fresh 
`KeyLayout` here leaves it at the default `false`, so a marked partitioning 
projected with any non-identity position list comes out unmarked, and 
`satisfies(ClusteredDistribution(subset))` would then accept it under 
`v2BucketingAllowKeysSubsetOfPartitionKeys`.
   
   No caller reaches this today (`joinKeyPositions` / `projectablePositions` 
are always ascending, so a marked input either narrows and is refused, or is 
the identity and returns `this`), but the comment in `createShuffleSpec` 
("`project` carries the unknown-keys marker across") no longer describes what 
`project` does. Could we pass `mayContainUnknownPartitionKeys = 
mayContainUnknownPartitionKeys` here so the contract holds regardless of the 
caller?
   



##########
sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/plans/physical/partitioning.scala:
##########
@@ -572,58 +642,37 @@ case class CoalescedNullAwareHashPartitioning(
  * }}}
  *
  * @param expressions Partition transform expressions (e.g., `years(col)`, 
`bucket(10, col)`).
- * @param partitionKeys Partition keys wrapped in InternalRowComparableWrapper 
for efficient
- *                      comparison and grouping. One per partition. Typically 
in sorted order when
- *                      produced by a data source or `GroupPartitionsExec`, 
but this is not
- *                      guaranteed after projection. May contain duplicates 
when ungrouped.
- * @param isGrouped Whether partition keys are unique (no duplicates). 
Computed on first
- *                  creation, then preserved through copy operations to avoid 
recomputation.
- * @param isCollapsed Whether a projection or a reduction mapped keys that 
were distinct in the
- *                    partitioning this one was derived from onto the same 
key, so one key here can
- *                    stand for several of the original ones. Sticky. See "Key 
Collapse" above for
- *                    what it gates and how it travels.
- * @param mayContainUnknownPartitionKeys Whether the data may contain rows 
whose partition key is
- *                                 not among the declared `partitionKeys`. 
`KeyGroupedPartitioner`
- *                                 routes such rows by a deterministic hash 
when a side is
- *                                 re-shuffled onto this partitioning (see
- *                                 `KeyedShuffleSpec.createPartitioning`), so 
co-location holds
- *                                 for whole keys only: two marked 
partitionings declaring the
- *                                 same keys in the same order and using the 
same partition
- *                                 function per position still pair (equal 
undeclared keys hash
- *                                 to the same partition), but a row of an 
undeclared key sits in
- *                                 the partition of some other declared key 
and need not be
- *                                 co-located with rows sharing only a subset 
of its columns.
- *                                 `satisfies` and 
`KeyedShuffleSpec.areKeysCompatible` therefore
- *                                 accept a marked partitioning only for 
full-key clustering,
- *                                 never for a subset of its partition columns 
and never for a
- *                                 global ordering across several partitions. 
Two carry rules:
- *                                 (1) a node that changes the declared key 
set must drop the
- *                                 keyed partitioning, whether it coarsens it 
(a key-dropping
- *                                 projection, a key-changing reduction, a 
join-key projection)
- *                                 or expands it over a marked leg (a union, 
where another leg
- *                                 may declare exactly the key that leg holds 
out-of-set);
- *                                 (2) marker agreement is enforced by 
`PartitioningCollection`:
- *                                 the constructor requires it and 
`fromPartitionings` normalizes
- *                                 by OR, so members are uniformly marked or 
unmarked and
- *                                 consumers may read one member. 
`ShuffledJoin`'s `InnerLike`
- *                                 arm, the only site that meets a marked 
input with an unmarked
- *                                 one, clears the markers first. That is 
precision, not the
- *                                 guarantee: without it the OR would spread 
the spurious marker
- *                                 onto the accurate side.
+ * @param layout The partitions this one describes, which is everything about 
them except the
+ *               expressions naming them. See [[KeyLayout]].
  */
 case class KeyedPartitioning(
     expressions: Seq[Expression],
-    @transient partitionKeys: Seq[InternalRowComparableWrapper],
-    isGrouped: Boolean,
-    isCollapsed: Boolean,
-    mayContainUnknownPartitionKeys: Boolean = false)
-  extends Expression with Partitioning with Unevaluable {
-  override val numPartitions = partitionKeys.length
+    layout: KeyLayout) extends Expression with Partitioning with Unevaluable {
+  override val numPartitions = layout.partitionKeys.length
+
+  def partitionKeys: Seq[InternalRowComparableWrapper] = layout.partitionKeys
+  def isGrouped: Boolean = layout.isGrouped
+  def isCollapsed: Boolean = layout.isCollapsed
+  def mayContainUnknownPartitionKeys: Boolean = 
layout.mayContainUnknownPartitionKeys
+
+  /** This partitioning over a changed layout, e.g. 
`withLayout(_.copy(isGrouped = false))`. */
+  def withLayout(f: KeyLayout => KeyLayout): KeyedPartitioning = copy(layout = 
f(layout))
 
   override def children: Seq[Expression] = expressions
   override def nullable: Boolean = false
   override def dataType: DataType = IntegerType
 
+  /**
+   * Prints the layout's contents where the value object would print, which 
keeps the plan string as
+   * it was before the layout held them. The list is curated rather than the 
layout's own fields,
+   * for two reasons. `partitionKeys` has to be printed as a `Seq` for 
`maxFields` to truncate it,
+   * and a partitioning can hold one key per split. And `dataTypes` has its 
naming erased, so
+   * printing it would put a struct field name into a plan that appears 
nowhere in the query. A
+   * field the layout grows is therefore a decision here.
+   */
+  override protected def stringArgs: Iterator[Any] =

Review Comment:
   Before this PR there was no `stringArgs` override, so the default 
`productIterator` printed all five fields, including 
`mayContainUnknownPartitionKeys`. This list drops the marker, so the plan 
string did change: a partitioning built by 
`KeyedShuffleSpec.createPartitioning` (always marked) now prints the same as an 
unmarked one, which loses the one visible signal in `EXPLAIN` for why a 
subset-key window/aggregate still shuffles.
   
   Either append `mayContainUnknownPartitionKeys` here 
(`TransformExpression.stringArgs` is a precedent for printing a trailing flag 
conditionally), or update this comment and the PR description, which says there 
are no plan string changes.
   



-- 
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