martin-g commented on code in PR #19340:
URL: https://github.com/apache/datafusion/pull/19340#discussion_r2623355621


##########
datafusion/spark/src/function/datetime/next_day.rs:
##########
@@ -63,7 +64,27 @@ impl ScalarUDFImpl for SparkNextDay {
     }
 
     fn return_type(&self, _arg_types: &[DataType]) -> Result<DataType> {
-        Ok(DataType::Date32)
+        internal_err!("return_field_from_args should be used instead")
+    }
+
+    fn return_field_from_args(&self, args: ReturnFieldArgs) -> 
Result<FieldRef> {
+        let [date_field, weekday_field] = args.arg_fields else {
+            return internal_err!("Spark `next_day` expects exactly two 
arguments");
+        };
+
+        let nullable =
+            date_field.is_nullable()
+                || weekday_field.is_nullable()
+                || args
+                    .scalar_arguments
+                    .iter()
+                    .any(|arg| matches!(arg, Some(v) if v.is_null()));

Review Comment:
   This should also check that the passed scalar arguments are valid because 
invalid values lead to Null results.



##########
datafusion/spark/src/function/datetime/next_day.rs:
##########
@@ -191,57 +207,84 @@ where
     let result = date_array
         .iter()
         .zip(day_of_week_array.iter())
-        .map(|(days, day_of_week)| {
-            if let Some(days) = days {
-                if let Some(day_of_week) = day_of_week {
-                    spark_next_day(days, day_of_week)
-                } else {
-                    // TODO: if spark.sql.ansi.enabled is false,
-                    //  returns NULL instead of an error for a malformed 
dayOfWeek.
-                    None
-                }
-            } else {
-                None
-            }
+        .map(|(days, day_of_week)| match (days, day_of_week) {
+            (Some(days), Some(day_of_week)) => spark_next_day(days, 
day_of_week),
+            _ => None,
         })
         .collect::<Date32Array>();
+
     Ok(Arc::new(result) as ArrayRef)
 }
 
 fn spark_next_day(days: i32, day_of_week: &str) -> Option<i32> {
     let date = Date32Type::to_naive_date(days);
 
-    let day_of_week = day_of_week.trim().to_uppercase();
-    let day_of_week = match day_of_week.as_str() {
-        "MO" | "MON" | "MONDAY" => Some("MONDAY"),
-        "TU" | "TUE" | "TUESDAY" => Some("TUESDAY"),
-        "WE" | "WED" | "WEDNESDAY" => Some("WEDNESDAY"),
-        "TH" | "THU" | "THURSDAY" => Some("THURSDAY"),
-        "FR" | "FRI" | "FRIDAY" => Some("FRIDAY"),
-        "SA" | "SAT" | "SATURDAY" => Some("SATURDAY"),
-        "SU" | "SUN" | "SUNDAY" => Some("SUNDAY"),
-        _ => {
-            // TODO: if spark.sql.ansi.enabled is false,
-            //  returns NULL instead of an error for a malformed dayOfWeek.
-            None
-        }
+    let day_of_week = match day_of_week.trim().to_uppercase().as_str() {
+        "MO" | "MON" | "MONDAY" => Weekday::Mon,
+        "TU" | "TUE" | "TUESDAY" => Weekday::Tue,
+        "WE" | "WED" | "WEDNESDAY" => Weekday::Wed,
+        "TH" | "THU" | "THURSDAY" => Weekday::Thu,
+        "FR" | "FRI" | "FRIDAY" => Weekday::Fri,
+        "SA" | "SAT" | "SATURDAY" => Weekday::Sat,
+        "SU" | "SUN" | "SUNDAY" => Weekday::Sun,
+        _ => return None,
     };
 
-    if let Some(day_of_week) = day_of_week {
-        let day_of_week = day_of_week.parse::<Weekday>();
-        match day_of_week {
-            Ok(day_of_week) => Some(Date32Type::from_naive_date(
-                date + Duration::days(
-                    (7 - date.weekday().days_since(day_of_week)) as i64,
-                ),
-            )),
-            Err(_) => {
-                // TODO: if spark.sql.ansi.enabled is false,
-                //  returns NULL instead of an error for a malformed dayOfWeek.
-                None
-            }
-        }
-    } else {
-        None
+    Some(Date32Type::from_naive_date(
+        date + Duration::days((7 - date.weekday().days_since(day_of_week)) as 
i64),
+    ))
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use datafusion_expr::ReturnFieldArgs;
+
+    #[test]
+    fn return_type_is_not_used() {
+        let func = SparkNextDay::new();
+        let err = func
+            .return_type(&[DataType::Date32, DataType::Utf8])
+            .unwrap_err();
+        assert!(err
+            .to_string()
+            .contains("return_field_from_args should be used instead"));
+    }
+
+    #[test]
+    fn next_day_nullability_derived_from_inputs() {
+        let func = SparkNextDay::new();
+
+        let non_nullable_date =
+            Arc::new(Field::new("date", DataType::Date32, false));
+        let non_nullable_weekday =
+            Arc::new(Field::new("weekday", DataType::Utf8, false));
+
+        let field = func
+            .return_field_from_args(ReturnFieldArgs {
+                arg_fields: &[
+                    Arc::clone(&non_nullable_date),
+                    Arc::clone(&non_nullable_weekday),
+                ],
+                scalar_arguments: &[None, None],
+            })
+            .unwrap();
+
+        assert!(!field.is_nullable());
+
+        let nullable_date =
+            Arc::new(Field::new("date", DataType::Date32, true));
+
+        let field = func
+            .return_field_from_args(ReturnFieldArgs {
+                arg_fields: &[
+                    Arc::clone(&nullable_date),
+                    Arc::clone(&non_nullable_weekday),
+                ],
+                scalar_arguments: &[None, None],
+            })
+            .unwrap();
+
+        assert!(field.is_nullable());

Review Comment:
   Please also add tests for non-None scalar arguments and for invalid scalar 
arguments.



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