jayzhan211 commented on code in PR #26835:
URL: https://github.com/apache/datafusion/pull/26835#discussion_r4413009026


##########
datafusion/expr/src/udf.rs:
##########
@@ -1015,6 +1030,26 @@ pub trait ScalarUDFImpl: Debug + DynEq + DynHash + Send 
+ Sync + Any {
         false
     }
 
+    /// Returns whether this function has been audited for range-key locality
+    /// analysis with these argument types and literal values.
+    ///
+    /// The safety requirements of 
[`Self::supports_range_key_locality_analysis`]
+    /// apply to every admitted combination. Override this method when 
admission
+    /// depends on literal values rather than argument types alone.
+    ///
+    /// `literal_arguments` has one entry per argument. `None` means the 
argument
+    /// is not a literal; `Some(value)` supplies its value, which may itself be
+    /// NULL. Non-literal expressions are not evaluated to obtain these values.
+    ///
+    /// The default delegates to the type-only hook, preserving existing 
opt-ins.
+    fn supports_range_key_locality_analysis_with_args(

Review Comment:
   @benbellick thanks for this! The type-only 
`supports_range_key_locality_analysis` hook from #26449 hasn't been released 
yet (it's not on `branch-55`). So instead of adding a second hook that 
delegates to it, could we add `literal_arguments` to that hook directly? That 
leaves one method for implementors, removes both `_with_args` forwarding 
wrappers (`ScalarUDF` and `AliasedScalarUDFImpl`), and needs only a signature 
change in `date_trunc`. Once 56 ships, folding the two together would be a 
breaking change, so it's easiest to do here. I tried it locally: about −21 
lines, and the date_bin/date_trunc/partitioning/udf tests and clippy all pass.
   
   ```diff
   -    fn supports_range_key_locality_analysis(&self, _argument_types: 
&[DataType]) -> bool {
   -        false
   -    }
   -
   -    /// Returns whether this function has been audited for range-key 
locality
   -    /// analysis with these argument types and literal values.
   -    ///
   -    /// The safety requirements of 
[`Self::supports_range_key_locality_analysis`]
   -    /// apply to every admitted combination. Override this method when 
admission
   -    /// depends on literal values rather than argument types alone.
   -    ///
   -    /// `literal_arguments` has one entry per argument. `None` means the 
argument
   -    /// is not a literal; `Some(value)` supplies its value, which may 
itself be
   -    /// NULL. Non-literal expressions are not evaluated to obtain these 
values.
        ///
   -    /// The default delegates to the type-only hook, preserving existing 
opt-ins.
   -    fn supports_range_key_locality_analysis_with_args(
   +    /// `literal_arguments` has one entry per argument: `Some(value)` for a
   +    /// literal (which may itself be NULL) and `None` otherwise.
   +    fn supports_range_key_locality_analysis(
            &self,
   -        argument_types: &[DataType],
   +        _argument_types: &[DataType],
            _literal_arguments: &[Option<&ScalarValue>],
        ) -> bool {
   -        self.supports_range_key_locality_analysis(argument_types)
   +        false
        }
   ```
   
   and in `date_trunc.rs`:
   
   ```diff
   -    fn supports_range_key_locality_analysis(&self, argument_types: 
&[DataType]) -> bool {
   +    fn supports_range_key_locality_analysis(
   +        &self,
   +        argument_types: &[DataType],
   +        _literal_arguments: &[Option<&ScalarValue>],
   +    ) -> bool {
   ```



##########
datafusion/functions/src/datetime/date_bin.rs:
##########
@@ -289,6 +289,25 @@ impl ScalarUDFImpl for DateBinFunc {
         }
     }
 
+    fn supports_range_key_locality_analysis_with_args(
+        &self,
+        argument_types: &[DataType],
+        literal_arguments: &[Option<&ScalarValue>],
+    ) -> bool {
+        // With the epoch origin, a positive nanosecond stride rounds down
+        // monotonically. Timestamp binning preserves NULLs or returns an 
error.
+        match (argument_types, literal_arguments) {
+            (
+                [
+                    DataType::Interval(MonthDayNano),
+                    Timestamp(Nanosecond, None),
+                ],
+                [Some(ScalarValue::IntervalMonthDayNano(Some(stride))), None],
+            ) => stride.months == 0 && stride.days == 0 && stride.nanoseconds 
> 0,

Review Comment:
   Optional: `date_bin_impl` converts `days` to a fixed 24 hours, so for 
timezone-less input a day stride bins exactly like the same number of 
nanoseconds. Admitting it would cover `INTERVAL '1 day'`, which is probably the 
most common bucket. Computing the stride the same way `date_bin_impl` does 
keeps the two in sync:
   
   ```rs
   ) => {
       stride.months == 0
           && TimeDelta::try_days(i64::from(stride.days))
               .and_then(|days| 
days.checked_add(&Duration::nanoseconds(stride.nanoseconds)))
               .and_then(|stride| stride.num_nanoseconds())
               .is_some_and(|stride| stride > 0)
   }
   ```
   
   With these cases in 
`range_key_locality_requires_positive_nanosecond_only_stride`, the tests pass 
locally: `(0, 1, 0)` → true, `(0, 1, -1)` → true, `(0, -1, 1)` → false, `(1, 1, 
0)` → false.



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