Skip to content

feat: allow Partitioning::Range to satisfy window Distribution::KeyPartitioned requirements#23416

Open
mithuncy wants to merge 4 commits into
apache:mainfrom
mithuncy:issue-23289-window-range-on-23184
Open

feat: allow Partitioning::Range to satisfy window Distribution::KeyPartitioned requirements#23416
mithuncy wants to merge 4 commits into
apache:mainfrom
mithuncy:issue-23289-window-range-on-23184

Conversation

@mithuncy

@mithuncy mithuncy commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Which issue does this PR close?

Rebased on main now that #23184 has merged. Overlaps with #23355, which also opts the window execs into range satisfaction as part of its PartitionedTopK work — happy to rebase whichever lands second.

What changes are included in this PR?

In one commit:

  • Opt WindowAggExec and BoundedWindowAggExec with partition keys into range satisfaction via InputDistributionRequirements::allow_range_satisfaction_for_key_partitioning, mirroring AggregateExec. Compatible range-partitioned inputs then satisfy the window key requirement without a hash repartition; subset satisfaction and the hash fallback for incompatible keys come from the existing satisfaction machinery. Windows without PARTITION BY keep requiring a single partition.

Are these changes tested?

Yes: new slt tests in range_partitioning.slt (exact and subset reuse, rehash on incompatible keys, subset_repartition_threshold / preserve_file_partitions / target_partitions behavior, WindowAggExec via an unbounded frame, and no-PARTITION BY), plus plan-shape tests in enforce_distribution.rs and acceptance/rejection tests in sanity_checker.rs.

Are there any user-facing changes?

No: only plan changes.

@github-actions github-actions Bot added physical-expr Changes to the physical-expr crates optimizer Optimizer rules core Core DataFusion crate sqllogictest SQL Logic Tests (.slt) datasource Changes to the datasource crate physical-plan Changes to the physical-plan crate auto detected api change Auto detected API change labels Jul 9, 2026
@gene-bordegaray

Copy link
Copy Markdown
Contributor

This is in my queue, just waiting for #23184 to land 🙇

@mithuncy mithuncy force-pushed the issue-23289-window-range-on-23184 branch from 3a4b586 to f125c39 Compare July 11, 2026 02:36
@github-actions github-actions Bot removed physical-expr Changes to the physical-expr crates optimizer Optimizer rules datasource Changes to the datasource crate auto detected api change Auto detected API change labels Jul 11, 2026
@mithuncy mithuncy force-pushed the issue-23289-window-range-on-23184 branch from f125c39 to 2995df1 Compare July 11, 2026 03:44
…rtitioned requirements

Windows got a hash repartition even when the input was already range
partitioned on the PARTITION BY keys. Opt WindowAggExec and
BoundedWindowAggExec into range satisfaction for KeyPartitioned, the
same way AggregateExec does. Incompatible keys still fall back to hash
repartitioning, and windows without PARTITION BY keep requiring a
single partition.

Covered by enforce_distribution and sanity_checker tests plus a window
section in range_partitioning.slt.

Closes apache#23289
@mithuncy mithuncy force-pushed the issue-23289-window-range-on-23184 branch from 2995df1 to a065d66 Compare July 11, 2026 07:46
@mithuncy mithuncy marked this pull request as ready for review July 11, 2026 10:36

@gene-bordegaray gene-bordegaray 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.

looks good 👍 had one comment about test maintainability for this effort.

I think this guy should land before #23355 @stuhood as it seems like the natural predecessor

thank you for this work 🙇

----
physical_plan
01)ProjectionExec: expr=[non_range_key@0 as non_range_key, sum(range_partitioned.value) PARTITION BY [range_partitioned.non_range_key] ORDER BY [range_partitioned.value ASC NULLS LAST] RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW@2 as sum(range_partitioned.value) PARTITION BY [range_partitioned.non_range_key] ORDER BY [range_partitioned.value ASC NULLS LAST] RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW]
02)--BoundedWindowAggExec: wdw=[sum(range_partitioned.value) PARTITION BY [range_partitioned.non_range_key] ORDER BY [range_partitioned.value ASC NULLS LAST] RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW: Field { "sum(range_partitioned.value) PARTITION BY [range_partitioned.non_range_key] ORDER BY [range_partitioned.value ASC NULLS LAST] RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW": nullable Int64 }, frame: RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW], mode=[Sorted]

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.

what do you think about doing one negative case for WindowAggExec

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.

added as TEST 22 right after the bounded negative

set datafusion.optimizer.preserve_file_partitions = 0;


##########

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.

I think the coverage is great here, I do want to avoid this file from getting too noisy with checking file preserving and subset satisfaction for each operator though. What if we did something like test:

  1. Bounded window accepts exact Range(a) (test 19)
  2. General window accepts exact Range(a) (test 20)
  3. Range(a) does not satisfy PARTITION BY b, and a hash is added for bounded and unbounded (test 21 and new test)
  4. Safe range subset, Range(a) satisfies PARTITION BY (a, b) when subset is enabled (test 22)
  5. Subset disabled, the same query gets a hash exchange (test 24)
  6. No PARTITION BY, gets one logical window (test 28)
    Then we should add a matrix test for these configurations as a unit / integration test in enforce_distribution.rs to ensure we are inserting repartitions as expeced according to these. I think this is fitting as those configs are tightly coupled with that file. I know @2010YOUY01 had some good guidance on test, would love your two cents here too 👍

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.

this is a non-blocker for this PR though, something I am taking note of

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.

Agreed on keeping this file lean. Will do either in this PR or as an immediate follow-up — whichever you and @2010YOUY01 prefer once he's had a look.

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.

I think lets keep fil lean in this PR and make follow up for the enforce_distribution.rs matrix tests 😄 . Lmk thoughts @2010YOUY01

if self.partition_keys().is_empty() {
debug!("No partition defined for BoundedWindowAggExec!!!");
vec![Distribution::SinglePartition]
crate::InputDistributionRequirements::new(vec![Distribution::SinglePartition])

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.

nit: can we also import InputDistributionRequirements

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.

Done — imported InputDistributionRequirements in both window execs

@mithuncy mithuncy requested a review from gene-bordegaray July 11, 2026 16:51

@gene-bordegaray gene-bordegaray 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.

looks good, thank you 😄

cc: @gabotechs

set datafusion.optimizer.preserve_file_partitions = 0;


##########

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.

I think lets keep fil lean in this PR and make follow up for the enforce_distribution.rs matrix tests 😄 . Lmk thoughts @2010YOUY01

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

core Core DataFusion crate physical-plan Changes to the physical-plan crate sqllogictest SQL Logic Tests (.slt)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Allow Partitioning::Range to satisfy window Distribution::KeyPartitioned requirements

2 participants