mbutrovich commented on code in PR #26593:
URL: https://github.com/apache/datafusion/pull/26593#discussion_r4447697157


##########
datafusion/physical-expr/src/partitioning.rs:
##########
@@ -409,7 +486,8 @@ impl RangePartitioning {
         }
     }
 
-    /// Adapts this range partitioning to the given expressions, preserving 
split points and sort options.
+    /// Adapts this range partitioning to the given expressions, preserving 
split points, bounds, and sort options.
+    /// The new expressions must describe the same keys for the bounds to 
remain valid.

Review Comment:
   Should `RangePartitioning::adapt` drop the bounds itself? Its only caller in 
the tree is `Partitioning::adapt`, which clears them right after the call, 
because the other input may contain keys outside this input's range:
   
   
https://github.com/apache/datafusion/blob/67ad9002074c58ed45962c7adcd67c021feaa2d7/datafusion/physical-expr/src/partitioning.rs#L824-L830
   
   `adapt` exists to describe a different input with this input's layout. An 
external caller that uses `RangePartitioning::adapt` directly, for example a 
distributed planner matching the other side of a join, gets bounds that input 
doesn't satisfy. A `RepartitionExec` built from that partitioning now fails 
with "outside the declared bounds" on the first key past either end. Neither 
method is in a release yet, so changing the semantics here costs nothing 
downstream.
   
   If `adapt` sets `start_bound: None, end_bound: None` in the struct it 
returns, `Partitioning::adapt` can go back to `.map(Partitioning::Range)`, and 
`test_range_partitioning_adapt` would assert `None` for both bounds instead of 
the source's bounds:
   
   
https://github.com/apache/datafusion/blob/67ad9002074c58ed45962c7adcd67c021feaa2d7/datafusion/physical-expr/src/partitioning.rs#L1950-L1951



##########
datafusion/physical-expr/src/partitioning.rs:
##########
@@ -293,9 +318,58 @@ impl RangePartitioning {
             ordering,
             samples,
             split_points,
+            start_bound: None,
+            end_bound: None,
         })
     }
 
+    /// Sets the inclusive start and exclusive end of the covered key space.
+    ///
+    /// `None` leaves that end unbounded. Bounds use the same lexicographic 
order
+    /// as the split points, including descending and null ordering. Every
+    /// retained sample must lie between the bounds, inclusive. Equality 
permits
+    /// empty first or last partitions; equal start and end bounds describe an
+    /// entirely empty range.
+    ///
+    /// This changes metadata only. The caller must ensure that every row falls
+    /// within the declared bounds and in the partition described by the split 
points.

Review Comment:
   Could we add a runnable example to `try_with_bounds`? The struct docs show 
bounds only as a `text` block. A doctest that splits `[10, 20, 30]` across two 
tasks would check the use case from the PR description and also cover 
`start_bound` and `end_bound`. Something like:
   
   ```suggestion
       /// This changes metadata only. The caller must ensure that every row 
falls
       /// within the declared bounds and in the partition described by the 
split points.
       ///
       /// # Example
       ///
       /// Split `[10, 20, 30]` across two tasks without losing the boundary at 
`20`:
       ///
       /// ```
       /// # use arrow::datatypes::{DataType, Field, Schema};
       /// # use datafusion_common::{Result, ScalarValue};
       /// # use datafusion_physical_expr::expressions::col;
       /// # use datafusion_physical_expr::{LexOrdering, PhysicalSortExpr, 
RangePartitioning, SplitPoint};
       /// # fn main() -> Result<()> {
       /// let schema = Schema::new(vec![Field::new("x", DataType::Int64, 
false)]);
       /// let ordering: LexOrdering =
       ///     [PhysicalSortExpr::new_default(col("x", &schema)?)].into();
       /// let point = |v| SplitPoint::new(vec![ScalarValue::Int64(Some(v))]);
       ///
       /// // x < 10 and 10 <= x < 20
       /// let first = RangePartitioning::try_new(ordering.clone(), 
vec![point(10)])?
       ///     .try_with_bounds(None, Some(point(20)))?;
       /// // 20 <= x < 30 and 30 <= x
       /// let second = RangePartitioning::try_new(ordering.clone(), 
vec![point(30)])?
       ///     .try_with_bounds(Some(point(20)), None)?;
       /// assert_eq!(first.end_bound(), second.start_bound());
       /// assert_eq!(first.start_bound(), None);
       ///
       /// // A bound must not fall inside the retained samples.
       /// assert!(RangePartitioning::try_new(ordering, vec![point(30)])?
       ///     .try_with_bounds(Some(point(35)), None)
       ///     .is_err());
       /// # Ok(())
       /// # }
       /// ```
   ```



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