Skip to content

[SPARK-40429][SQL] Only set KeyGroupedPartitioning when the referenced column is in the output - #37886

Closed
huaxingao wants to merge 3 commits into
apache:masterfrom
huaxingao:keyGroupedPartitioning
Closed

huaxingao wants to merge 3 commits into
apache:masterfrom
huaxingao:keyGroupedPartitioning

Conversation

@huaxingao

Copy link
Copy Markdown
Contributor

What changes were proposed in this pull request?

Only set KeyGroupedPartitioning when the referenced column is in the output

Why are the changes needed?

bug fixing

Does this PR introduce any user-facing change?

no

How was this patch tested?

new test

@github-actions github-actions Bot added the SQL label Sep 14, 2022
@huaxingao

Copy link
Copy Markdown
Contributor Author

cc @cloud-fan @sunchao

None
} else {
val ref = AttributeSet.fromAttributeSets(partitioning.get.map(_.references))
if (ref.subsetOf(AttributeSet(d.output))) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
if (ref.subsetOf(AttributeSet(d.output))) {
if (ref.subsetOf(d.outputSet)) {

if (partitioning.isEmpty) {
None
} else {
val ref = AttributeSet.fromAttributeSets(partitioning.get.map(_.references))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

how about partitioning.get.forall(p => p.references.subsetOf(...))

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Sounds good! Changed.

@cloud-fan cloud-fan left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

good catch!

@dongjoon-hyun dongjoon-hyun left a comment •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

+1, LGTM. Thank you, @huaxingao and @cloud-fan .
Merged to master.

@dongjoon-hyun

Copy link
Copy Markdown
Member

Could you make a backporting PR, @huaxingao ?

@huaxingao

Copy link
Copy Markdown
Contributor Author

Thanks @cloud-fan @dongjoon-hyun

@huaxingao
huaxingao deleted the keyGroupedPartitioning branch September 15, 2022 16:05
dongjoon-hyun pushed a commit that referenced this pull request Sep 15, 2022
…renced column is in the output

### What changes were proposed in this pull request?
back porting [PR](#37886) to 3.3.
Only set `KeyGroupedPartitioning` when the referenced column is in the output

### Why are the changes needed?
bug fixing

### Does this PR introduce _any_ user-facing change?
No

### How was this patch tested?
New test

Closes #37901 from huaxingao/3.3.

Authored-by: huaxingao <huaxin_gao@apple.com>
Signed-off-by: Dongjoon Hyun <dongjoon@apache.org>
szehon-ho added a commit to szehon-ho/spark that referenced this pull request Aug 24, 2023
…keys

  ### What changes were proposed in this pull request?
- Add new conf spark.sql.sources.v2.bucketing.allowJoinKeysSubsetOfPartitionKeys.enabled
- Change key compatibility checks in EnsureRequirements.  Remove checks where all partition keys must be in join keys to allow isKeyCompatible = true in this case.
- Change BatchScanExec/DataSourceV2Relation to group splits by join keys (previously grouped only by partition values)
- Implement partiallyClustered skew-handling.
  - Group only the replicate side (now by join key as well)
  - add an additional sort in the end of partitions based on join key, as when we group the non-replicate side, partition ordering becomes out of order.

  ### Why are the changes needed?
- Support Storage Partition Join in cases where the join condition does not contain all the partition keys, but just some of them

  ### Does this PR introduce _any_ user-facing change?
No

  ### How was this patch tested?
-Added tests in KeyGroupedPartitioningSuite
-Found two problems, will address in separate PR:
- apache#37886  made another change so that we have to select all join keys, otherwise DSV2 scan does not report KeyGroupedPartitioning and SPJ does not get triggered.  Need to see how to relax this.
- https://issues.apache.org/jira/browse/SPARK-44641 was found when testing this change.  This pr refactors some of those code to add group-by-join-key, but doesnt change the underlying logic, so issue continues to exist.  Hopefully this will also get fixed in another way.
szehon-ho added a commit to szehon-ho/spark that referenced this pull request Aug 24, 2023
…keys

  ### What changes were proposed in this pull request?
- Add new conf spark.sql.sources.v2.bucketing.allowJoinKeysSubsetOfPartitionKeys.enabled
- Change key compatibility checks in EnsureRequirements.  Remove checks where all partition keys must be in join keys to allow isKeyCompatible = true in this case.
- Change BatchScanExec/DataSourceV2Relation to group splits by join keys (previously grouped only by partition values)
- Implement partiallyClustered skew-handling.
  - Group only the replicate side (now by join key as well)
  - add an additional sort in the end of partitions based on join key, as when we group the non-replicate side, partition ordering becomes out of order.

  ### Why are the changes needed?
- Support Storage Partition Join in cases where the join condition does not contain all the partition keys, but just some of them

  ### Does this PR introduce _any_ user-facing change?
No

  ### How was this patch tested?
-Added tests in KeyGroupedPartitioningSuite
-Found two problems, will address in separate PR:
- apache#37886  made another change so that we have to select all join keys, otherwise DSV2 scan does not report KeyGroupedPartitioning and SPJ does not get triggered.  Need to see how to relax this.
- https://issues.apache.org/jira/browse/SPARK-44641 was found when testing this change.  This pr refactors some of those code to add group-by-join-key, but doesnt change the underlying logic, so issue continues to exist.  Hopefully this will also get fixed in another way.
szehon-ho added a commit to szehon-ho/spark that referenced this pull request Sep 8, 2023
…keys

 ### What changes were proposed in this pull request?
- Add new conf spark.sql.sources.v2.bucketing.allowJoinKeysSubsetOfPartitionKeys.enabled
- Change key compatibility checks in EnsureRequirements.  Remove checks where all partition keys must be in join keys to allow isKeyCompatible = true in this case (if this flag is enabled)
- "Project" partitions by join keys in KeyGroupedPartitioning/KeyGroupedShuffleSpec
- Add join key grouping to the partition grouping in BatchScanExec

   ### Why are the changes needed?
- Support Storage Partition Join in cases where the join condition does not contain all the partition keys, but just some of them

    ### Does this PR introduce _any_ user-facing change?
No

    ### How was this patch tested?
-Added tests in KeyGroupedPartitioningSuite
-Because of apache#37886   we have to select all join keys to trigger SPJ in this case, otherwise DSV2 scan does not report KeyGroupedPartitioning and SPJ does not get triggered.  Need to see how to relax this in separate PR.
dongjoon-hyun pushed a commit that referenced this pull request Sep 11, 2023
…keys

### What changes were proposed in this pull request?
- Add new conf spark.sql.sources.v2.bucketing.allowJoinKeysSubsetOfPartitionKeys.enabled
- Change key compatibility checks in EnsureRequirements.  Remove checks where all partition keys must be in join keys to allow isKeyCompatible = true in this case (if this flag is enabled)
- Change BatchScanExec/DataSourceV2Relation to group splits by join keys if they differ from partition keys (previously grouped only by partition values).  Do same for all auxiliary data structure, like commonPartValues.
- Implement partiallyClustered skew-handling.
  - Group only the replicate side (now by join key as well), replicate by the total size of other-side partitions that share the join key.
  - add an additional sort for partitions based on join key, as when we group the replicate side, partition ordering becomes out of order from the non-replicate side.

### Why are the changes needed?
- Support Storage Partition Join in cases where the join condition does not contain all the partition keys, but just some of them

### Does this PR introduce _any_ user-facing change?
No

### How was this patch tested?
-Added tests in KeyGroupedPartitioningSuite
-Found two existing problems, will address in separate PR:
- Because of #37886   we have to select all join keys to trigger SPJ in this case, otherwise DSV2 scan does not report KeyGroupedPartitioning and SPJ does not get triggered.  Need to see how to relax this.
- https://issues.apache.org/jira/browse/SPARK-44641 was found when testing this change.  This pr refactors some of those code to add group-by-join-key, but doesnt change the underlying logic, so issue continues to exist.  Hopefully this will also get fixed in another way.

Closes #42306 from szehon-ho/spj_attempt_master.

Authored-by: Szehon Ho <szehon.apache@gmail.com>
Signed-off-by: Dongjoon Hyun <dhyun@apple.com>
snmvaughan pushed a commit to snmvaughan/spark that referenced this pull request Mar 26, 2024
…keys

- Add new conf spark.sql.sources.v2.bucketing.allowJoinKeysSubsetOfPartitionKeys.enabled
- Change key compatibility checks in EnsureRequirements.  Remove checks where all partition keys must be in join keys to allow isKeyCompatible = true in this case (if this flag is enabled)
- Change BatchScanExec/DataSourceV2Relation to group splits by join keys if they differ from partition keys (previously grouped only by partition values).  Do same for all auxiliary data structure, like commonPartValues.
- Implement partiallyClustered skew-handling.
  - Group only the replicate side (now by join key as well), replicate by the total size of other-side partitions that share the join key.
  - add an additional sort for partitions based on join key, as when we group the replicate side, partition ordering becomes out of order from the non-replicate side.

- Support Storage Partition Join in cases where the join condition does not contain all the partition keys, but just some of them

No

-Added tests in KeyGroupedPartitioningSuite
-Found two existing problems, will address in separate PR:
- Because of apache#37886   we have to select all join keys to trigger SPJ in this case, otherwise DSV2 scan does not report KeyGroupedPartitioning and SPJ does not get triggered.  Need to see how to relax this.
- https://issues.apache.org/jira/browse/SPARK-44641 was found when testing this change.  This pr refactors some of those code to add group-by-join-key, but doesnt change the underlying logic, so issue continues to exist.  Hopefully this will also get fixed in another way.

Closes apache#42306 from szehon-ho/spj_attempt_master.

Authored-by: Szehon Ho <szehon.apache@gmail.com>
Signed-off-by: Dongjoon Hyun <dhyun@apple.com>
ulysses-you added a commit that referenced this pull request Sep 8, 2026
…are pruned from the scan output

### What changes were proposed in this pull request?

Keep the reported key-grouped partitioning when some of its keys are column-pruned out of the scan
output, instead of dropping the whole partitioning, so a storage-partitioned join still applies.

- `V2ScanPartitioningAndOrdering` keeps the reported keys as long as at least one of them is still in
  the scan output. The full key list is kept, not the surviving subset, to stay positionally aligned
  with the raw partition-key rows.
- `DataSourceV2ScanExecBase` splits the two views of that report. `reportedKeyedPartitioning` is the
  source's own, at full key width, and is what reads and orders the raw `HasPartitionKey` rows
  (`BatchScanExec.filteredPartitions`). `outputPartitioning` projects the pruned key positions away,
  so the partitioning Spark plans against only references output columns. `replanWithRuntimeFilters`
  takes `Option[KeyedPartitioning]`, so the projected view cannot reach the raw key rows.
- `DataSourceV2ScanRelation.doCanonicalize` and `BatchScanExec` (`doCanonicalize` / `equals` /
  `hashCode`, through a shared `prunedKeyGroupedPartitioning`) ignore partitioning keys that
  reference pruned columns, and the canonicalized ordering keeps only its leading run of sort orders
  over output columns. Otherwise a dangling attribute's exprId keeps otherwise-equivalent scans
  unequal and defeats subplan merging and exchange reuse.

Whether the surviving projection is usable is decided one layer down, by `KeyedPartitioning`:
pruning a key can map keys that were distinct onto the same key, and such a projection is marked
`isCollapsed`, so grouping it stays gated on
`spark.sql.sources.v2.bucketing.allowKeysSubsetOfPartitionKeys.enabled` in `mayGroupToSatisfy`. A
projection that collapses nothing leaves the keys unique and needs no opt-in. No config doc changes:
the config keeps the roles it already has.

### Why are the changes needed?

An extra partition key that is column-pruned out of the scan output should not disable a
storage-partitioned join. Dropping the whole report also lost it for the case where the surviving
keys are still unique, which is sound with no opt-in at all.

Background: SPARK-40429 (#37886) introduced the drop-to-`None` guard to avoid a "missing inputs"
plan-validation error when a partitioning key referenced a pruned column. That failure mode no
longer applies: SPARK-40259 made `DataSourceV2ScanRelation.references` return empty (partitioning,
ordering and pushed filters are scan metadata, not references to resolve), so a dangling
partitioning key no longer trips plan validation. This change relies on that. SPARK-40259 is on
`master` only, so this change is master-only too and is not backportable to `branch-4.x` as it
stands.

### Does this PR introduce _any_ user-facing change?

Yes. When a partition key is pruned out of the scan output, Spark now reports the projected
partitioning instead of no partitioning, so a query that previously shuffled can plan a
storage-partitioned join:

- with `spark.sql.sources.v2.bucketing.allowKeysSubsetOfPartitionKeys.enabled` off (default), when
  the surviving keys are still unique;
- with it on, also when pruning collapses distinct keys onto the same key.

Results are unchanged either way.

### How was this patch tested?

New tests in `KeyGroupedPartitioningSuite`:

- a join whose key is a subset of the partition keys, with the extra key pruned, triggers SPJ with
  `allowKeysSubsetOfPartitionKeys` on and shuffles with it off;
- a pruned key that collapses nothing keeps SPJ with the config off;
- a pruned *leading* key keeps the raw key rows aligned: one test on row loss, one on the
  `ClassCastException` from reading a leading `String` key at the projected `Integer` type;
- runtime filtering with a pruned leading key re-plans the scan's partitions against the full-width
  key rows. This one lives in `KeyGroupedPartitioningRuntimeFilterTests`, so it runs against both
  runtime-filtering fixtures; only the V2 one prunes a column, since the Catalyst fixture's scan
  keeps the full table schema;
- all partition keys pruned reports no partitioning, even with the config on;
- a self-join with a pruned key stays canonicalizable, and a pruned key does not defeat plan reuse.

Pointing `filteredPartitions` at the projected partitioning fails exactly the three tests that pin
it (row loss, `ClassCastException`, runtime filtering) and nothing else.

`MergeSubplansSuite`: identical scans whose reported ordering is on a pruned column are
deduplicated, not fused. Verified this test fails without the ordering half of the canonicalization
change.

### Was this patch authored or co-authored using generative AI tooling?

Generated-by: Claude Code (qwen3.8-max, Claude Opus 5)

Closes #58522 from ulysses-you/worktree-spj-columnprune.

Authored-by: Xiduo You <ulyssesyou18@gmail.com>
Signed-off-by: Xiduo You <ulyssesyou@apache.org>
ulysses-you added a commit that referenced this pull request Sep 8, 2026
…are pruned from the scan output

### What changes were proposed in this pull request?

Keep the reported key-grouped partitioning when some of its keys are column-pruned out of the scan
output, instead of dropping the whole partitioning, so a storage-partitioned join still applies.

- `V2ScanPartitioningAndOrdering` keeps the reported keys as long as at least one of them is still in
  the scan output. The full key list is kept, not the surviving subset, to stay positionally aligned
  with the raw partition-key rows.
- `DataSourceV2ScanExecBase` splits the two views of that report. `reportedKeyedPartitioning` is the
  source's own, at full key width, and is what reads and orders the raw `HasPartitionKey` rows
  (`BatchScanExec.filteredPartitions`). `outputPartitioning` projects the pruned key positions away,
  so the partitioning Spark plans against only references output columns. `replanWithRuntimeFilters`
  takes `Option[KeyedPartitioning]`, so the projected view cannot reach the raw key rows.
- `DataSourceV2ScanRelation.doCanonicalize` and `BatchScanExec` (`doCanonicalize` / `equals` /
  `hashCode`, through a shared `prunedKeyGroupedPartitioning`) ignore partitioning keys that
  reference pruned columns, and the canonicalized ordering keeps only its leading run of sort orders
  over output columns. Otherwise a dangling attribute's exprId keeps otherwise-equivalent scans
  unequal and defeats subplan merging and exchange reuse.

Whether the surviving projection is usable is decided one layer down, by `KeyedPartitioning`:
pruning a key can map keys that were distinct onto the same key, and such a projection is marked
`isCollapsed`, so grouping it stays gated on
`spark.sql.sources.v2.bucketing.allowKeysSubsetOfPartitionKeys.enabled` in `mayGroupToSatisfy`. A
projection that collapses nothing leaves the keys unique and needs no opt-in. No config doc changes:
the config keeps the roles it already has.

### Why are the changes needed?

An extra partition key that is column-pruned out of the scan output should not disable a
storage-partitioned join. Dropping the whole report also lost it for the case where the surviving
keys are still unique, which is sound with no opt-in at all.

Background: SPARK-40429 (#37886) introduced the drop-to-`None` guard to avoid a "missing inputs"
plan-validation error when a partitioning key referenced a pruned column. That failure mode no
longer applies: SPARK-40259 made `DataSourceV2ScanRelation.references` return empty (partitioning,
ordering and pushed filters are scan metadata, not references to resolve), so a dangling
partitioning key no longer trips plan validation. This change relies on that. SPARK-40259 is on
`master` only, so this change is master-only too and is not backportable to `branch-4.x` as it
stands.

### Does this PR introduce _any_ user-facing change?

Yes. When a partition key is pruned out of the scan output, Spark now reports the projected
partitioning instead of no partitioning, so a query that previously shuffled can plan a
storage-partitioned join:

- with `spark.sql.sources.v2.bucketing.allowKeysSubsetOfPartitionKeys.enabled` off (default), when
  the surviving keys are still unique;
- with it on, also when pruning collapses distinct keys onto the same key.

Results are unchanged either way.

### How was this patch tested?

New tests in `KeyGroupedPartitioningSuite`:

- a join whose key is a subset of the partition keys, with the extra key pruned, triggers SPJ with
  `allowKeysSubsetOfPartitionKeys` on and shuffles with it off;
- a pruned key that collapses nothing keeps SPJ with the config off;
- a pruned *leading* key keeps the raw key rows aligned: one test on row loss, one on the
  `ClassCastException` from reading a leading `String` key at the projected `Integer` type;
- runtime filtering with a pruned leading key re-plans the scan's partitions against the full-width
  key rows. This one lives in `KeyGroupedPartitioningRuntimeFilterTests`, so it runs against both
  runtime-filtering fixtures; only the V2 one prunes a column, since the Catalyst fixture's scan
  keeps the full table schema;
- all partition keys pruned reports no partitioning, even with the config on;
- a self-join with a pruned key stays canonicalizable, and a pruned key does not defeat plan reuse.

Pointing `filteredPartitions` at the projected partitioning fails exactly the three tests that pin
it (row loss, `ClassCastException`, runtime filtering) and nothing else.

`MergeSubplansSuite`: identical scans whose reported ordering is on a pruned column are
deduplicated, not fused. Verified this test fails without the ordering half of the canonicalization
change.

### Was this patch authored or co-authored using generative AI tooling?

Generated-by: Claude Code (qwen3.8-max, Claude Opus 5)

Closes #58522 from ulysses-you/worktree-spj-columnprune.

Authored-by: Xiduo You <ulyssesyou18@gmail.com>
Signed-off-by: Xiduo You <ulyssesyou@apache.org>
(cherry picked from commit cfbec6d)
Signed-off-by: Xiduo You <ulyssesyou@apache.org>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants