peter-toth opened a new pull request, #57753:
URL: https://github.com/apache/spark/pull/57753

   ### What changes were proposed in this pull request?
   
   Follow-up to SPARK-40259 (subquery plan merge for DataSource V2 scans).
   
   Today the DSv2 scan merge declines whenever either input scan reports 
key-grouped partitioning or ordering. The rebuilt merged scan carries neither 
on its own, so fusing the two scans could drop a partitioning/ordering the 
original plan relied on and force an extra shuffle or sort. Declining is safe 
but leaves the merge unused for any partitioned/ordered source.
   
   This PR lets the merge proceed and re-derives the merged scan's own report 
instead of declining up front:
   
   - Drop the `keyGroupedPartitioning`/`ordering` conjuncts from the 
`mergeable` gate in `PlanMerger.tryMergeScanRelations`.
   - At the leaf, combine the two inputs' reports (np's remapped into cp's 
relation space) into the single report the merge must preserve:
     - key-grouped partitioning: the two must be equal 
(`combineRequiredKeyGroupedPartitioning`);
     - ordering: the stronger of the two, i.e. the one that satisfies the other 
via `SortOrder.orderingSatisfies` (`combineRequiredOrdering`).
     If the inputs are incompatible (differing non-empty kGP, or neither 
ordering satisfies the other), no rebuilt scan can keep both not-worse, so the 
merge is declined right there -- before rebuilding -- saving a scan rebuild.
   - After `V2ScanRelationPushDown.rebuildScan`, re-derive the merged scan's 
partitioning/ordering by running `V2ScanPartitioningAndOrdering` on the single 
merged scan node, then check it against the combined required report 
(`mergeDegradesReporting`): decline if the merged scan's kGP does not match, or 
its ordering does not satisfy, what was required. The required report is 
carried through `DSv2DeferredScan` for the deferred (under-`Filter`) build.
   
   Two new opt-in configs gate accepting a degradation instead of declining 
(both default `false`), so with the defaults this is a pure improvement -- 
merge when not worse, decline on loss:
   
   - 
`spark.sql.optimizer.mergeSubplans.dsv2ScanMerge.allowKeyGroupedPartitioningDegradation`
   - `spark.sql.optimizer.mergeSubplans.dsv2ScanMerge.allowOrderingDegradation`
   
   Only sources that declare the `SCAN_MERGING` table capability are affected.
   
   ### Why are the changes needed?
   
   The parent feature (SPARK-40259) conservatively declines a merge whenever an 
input reports key-grouped partitioning or ordering, to avoid forcing a 
shuffle/sort that the original plan avoided. That is correct but overly broad: 
when the rebuilt merged scan re-derives the same (or a stronger) report, fusing 
the scans loses nothing and still removes a duplicate scan -- which matters for 
partitioned/ordered sources, e.g. an SPJ join inside the merged subplan. This 
PR keeps the safety (decline on real degradation) while capturing the merge 
when it is genuinely not worse.
   
   ### Does this PR introduce _any_ user-facing change?
   
   No. The two new configs are opt-in and default to `false`, and no built-in 
source declares `SCAN_MERGING`, so there is no behavior change for existing 
sources.
   
   ### How was this patch tested?
   
   New and existing unit tests, all under `sql/core`:
   
   - `MergeSubplansSuite`:
     - default-decline when a single input reports kGP/ordering the rebuilt 
scan drops (degradation);
     - default-decline when the two inputs report incompatible kGP/ordering 
(declined at the leaf, before rebuild);
     - config-allows-degradation: the merge proceeds to the plain column union 
under each `allow...Degradation=true`;
     - the deferred (under-`Filter`) build path enforces the required report 
too: decline by default, merge with the config on;
     - the pre-existing empty-report case still merges.
   - `DSv2PlanMergingSuite`: an end-to-end test that a scan merge preserves the 
sources' reported key-grouped partitioning, using a new `SCAN_MERGING` fixture 
(`InMemoryScanMergingReportingCatalog`/`InMemoryScanMergingReportingTable`) 
that keeps its reported partitioning (no `NonReportingScan` wrapper), an 
identity-partitioned table under `spark.sql.sources.v2.bucketing.enabled=true`, 
and two scalar subqueries reading the partition column.
   
   `build/sbt 'sql/testOnly *MergeSubplansSuite *DSv2PlanMergingSuite 
*PlanMergingSuite'` -- 95 tests pass. `dev/lint-scala` clean.
   
   ### Was this patch authored or co-authored using generative AI tooling?
   
   Generated-by: Claude Code (Opus 4.8)
   


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