Skip to content

Add Domain Bounds for Range Partitioning #25569

Description

@gene-bordegaray

Epic: #25421

Is your feature request related to a problem or challenge?

Currently, RangePartitioning's first and last partitions always extend to infinity:

ordering = [x ASC]
split_points = [10, 20, 30]

P0: (-inf, 10)
P1: [10, 20)
P2: [20, 30)
P3: [30, +inf)

This correctly describes a range layout that may contain any value in the key space. It cannot represent an output whose keys are known to lie within finite outer bounds.

This distinction matters when a table provider or optimizer rule has proved a finite output domain. DataFusion currently discards that proof and replaces it with a weaker, unbounded property.

For example, #25437 proposes statically pruning range partitions. Consider the following layout:

P0: (-inf, 10)
P1: [10, 20)
P2: [20, 30)
P3: [30, 40)
P4: [40, 50)
P5: [50, +inf)

The predicate x >= 20 AND x < 40 keep only P2 and P3:

P0: (-inf, 10) -> pruned
P1: [10, 20)   -> pruned
P2: [20, 30)   -> keep
P3: [30, 40)   -> keep
P4: [40, 50)   -> pruned
P5: [50, +inf) -> pruned

When partition-index stability is required, #25437 can preserve the original layout by replacing pruned streams with EmptyExec. When alignment is not required, it can compact and renumber the two surviving streams as local partitions 0 and 1.

The current API can describe that compacted output only as:

split_points = [30]

local P0: (-inf, 30)
local P1: [30, +inf)

This property is safe, but it is weaker than what the optimizer proved. The output is bounded by [20, 40), but RangePartitioning cannot retain that fact. With explicit outer bounds, it could advertise:

domain = [20, 40)
split_points = [30]

local P0: [20, 30)
local P1: [30, 40)

Why retain this information?

An unbounded domain forces downstream optimizer rules to assume that endpoint partitions may contain values outside the actual output domain. This can prevent them from proving useful range relationships.

Continuing the #25437 example, suppose a downstream operator requires the original six-partition layout. With the current unbounded property, a range-layout comparison concludes:

local P0: (-inf, 30) -> may overlap original P0, P1, or P2
local P1: [30, +inf) -> may overlap original P3, P4, or P5

With explicit bounds, the relationship is exact:

local P0: [20, 30) -> original P2: [20, 30)
local P1: [30, 40) -> original P3: [30, 40)

We are going to see more downstream consumer such as #25483 that could use that proof to connect each input only to its actual destination. When an input has one destination, execution can forward whole batches instead of evaluating the range key and routing every row.

This issue only is only the introduction of the domain. It is just meant to make this informatioin available. It is things like #25437 which removes empty streams, and consumers such as #25483 may use the new domain to add optimizations.

Desired behavior

RangePartitioning should optionally describe the outer bounds of its output key domain:

ordering = [x ASC]
domain = [20, 40)
split_points = [30]

P0: [20, 30)
P1: [30, 40)

Existing construction should preserve the current semantics by defaulting both bounds to unbounded.

Proposed solution

Add outer bounds to RangePartitioning, conceptually:

struct RangePartitioning {
    ordering: LexOrdering,
    domain: RangeDomain,
    split_points: Vec<SplitPoint>,
}

struct RangeDomain {
    start: Bound<SplitPoint>,
    end: Bound<SplitPoint>,
}

start and end are interpreted according to the declared lexicographic ordering, including sort direction and null ordering.

Existing constructors should create an unbounded domain. A new constructor should accept explicit bounds and validate the bounds.

Projections and other property-preserving operators should retain the domain. A rewrite may narrow it only when it proves the new bounds and updates the physical streams and split points consistently. Otherwise fall back to an unbounded domain or another appropriate partitioning property.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions