datafusion_spark/function/datetime/
weekday.rs1use std::sync::Arc;
19
20use arrow::array::AsArray;
21use arrow::compute::{DatePart, date_part};
22use arrow::datatypes::{DataType, Field, FieldRef, Int32Type};
23use datafusion_common::types::{NativeType, logical_date};
24use datafusion_common::utils::take_function_args;
25use datafusion_common::{Result, ScalarValue, internal_err};
26use datafusion_expr::{
27 Coercion, ColumnarValue, ReturnFieldArgs, ScalarFunctionArgs, ScalarUDFImpl,
28 Signature, TypeSignatureClass, Volatility,
29};
30
31#[derive(Debug, PartialEq, Eq, Hash)]
39pub struct SparkWeekDay {
40 signature: Signature,
41}
42
43impl Default for SparkWeekDay {
44 fn default() -> Self {
45 Self::new()
46 }
47}
48
49impl SparkWeekDay {
50 pub fn new() -> Self {
51 Self {
52 signature: Signature::coercible(
53 vec![Coercion::new_implicit(
54 TypeSignatureClass::Native(logical_date()),
55 vec![TypeSignatureClass::Timestamp],
56 NativeType::Date,
57 )],
58 Volatility::Immutable,
59 ),
60 }
61 }
62}
63
64impl ScalarUDFImpl for SparkWeekDay {
65 fn name(&self) -> &str {
66 "weekday"
67 }
68
69 fn signature(&self) -> &Signature {
70 &self.signature
71 }
72
73 fn return_type(&self, _arg_types: &[DataType]) -> Result<DataType> {
74 internal_err!("return_field_from_args should be used instead")
75 }
76
77 fn return_field_from_args(&self, args: ReturnFieldArgs) -> Result<FieldRef> {
78 let nullable = args.arg_fields.iter().any(|f| f.is_nullable());
79 Ok(Arc::new(Field::new(self.name(), DataType::Int32, nullable)))
80 }
81
82 fn invoke_with_args(&self, args: ScalarFunctionArgs) -> Result<ColumnarValue> {
83 let [arg] = take_function_args(self.name(), args.args)?;
84 match arg {
85 ColumnarValue::Scalar(scalar) => {
86 if scalar.is_null() {
87 return Ok(ColumnarValue::Scalar(ScalarValue::Int32(None)));
88 }
89 let arr = scalar.to_array_of_size(1)?;
90 let weekday_arr = date_part(&arr, DatePart::DayOfWeekMonday0)?;
93 let value = weekday_arr.as_primitive::<Int32Type>().value(0);
94 Ok(ColumnarValue::Scalar(ScalarValue::Int32(Some(value))))
95 }
96 ColumnarValue::Array(arr) => {
97 let weekday_arr = date_part(&arr, DatePart::DayOfWeekMonday0)?;
98 Ok(ColumnarValue::Array(weekday_arr))
99 }
100 }
101 }
102}
103
104#[cfg(test)]
105mod tests {
106 use super::*;
107 use arrow::array::{Date32Array, Int32Array};
108
109 #[test]
110 fn test_weekday_return_field_nullability_matches_input() {
111 let func = SparkWeekDay::new();
112
113 let non_nullable_arg = Arc::new(Field::new("arg", DataType::Date32, false));
114 let nullable_arg = Arc::new(Field::new("arg", DataType::Date32, true));
115
116 let non_nullable_out = func
117 .return_field_from_args(ReturnFieldArgs {
118 arg_fields: &[Arc::clone(&non_nullable_arg)],
119 scalar_arguments: &[None],
120 })
121 .expect("non-nullable arg should succeed");
122 assert_eq!(non_nullable_out.data_type(), &DataType::Int32);
123 assert!(!non_nullable_out.is_nullable());
124
125 let nullable_out = func
126 .return_field_from_args(ReturnFieldArgs {
127 arg_fields: &[Arc::clone(&nullable_arg)],
128 scalar_arguments: &[None],
129 })
130 .expect("nullable arg should succeed");
131 assert_eq!(nullable_out.data_type(), &DataType::Int32);
132 assert!(nullable_out.is_nullable());
133 }
134
135 #[test]
136 fn test_weekday_scalar() -> Result<()> {
137 let func = SparkWeekDay::new();
138
139 let result = func.invoke_with_args(ScalarFunctionArgs {
141 args: vec![ColumnarValue::Scalar(ScalarValue::Date32(Some(19797)))],
142 arg_fields: vec![Arc::new(Field::new("arg", DataType::Date32, true))],
143 number_rows: 1,
144 return_field: Arc::new(Field::new("weekday", DataType::Int32, true)),
145 config_options: Arc::new(Default::default()),
146 })?;
147 match result {
148 ColumnarValue::Scalar(ScalarValue::Int32(Some(v))) => assert_eq!(v, 4),
149 other => panic!("unexpected result: {other:?}"),
150 }
151
152 let result = func.invoke_with_args(ScalarFunctionArgs {
154 args: vec![ColumnarValue::Scalar(ScalarValue::Date32(None))],
155 arg_fields: vec![Arc::new(Field::new("arg", DataType::Date32, true))],
156 number_rows: 1,
157 return_field: Arc::new(Field::new("weekday", DataType::Int32, true)),
158 config_options: Arc::new(Default::default()),
159 })?;
160 match result {
161 ColumnarValue::Scalar(ScalarValue::Int32(None)) => {}
162 other => panic!("unexpected result: {other:?}"),
163 }
164
165 Ok(())
166 }
167
168 #[test]
169 fn test_weekday_array() -> Result<()> {
170 let func = SparkWeekDay::new();
171
172 let input = Date32Array::from(vec![Some(19723), Some(19728), Some(19729), None]);
174 let result = func.invoke_with_args(ScalarFunctionArgs {
175 args: vec![ColumnarValue::Array(Arc::new(input))],
176 arg_fields: vec![Arc::new(Field::new("arg", DataType::Date32, true))],
177 number_rows: 4,
178 return_field: Arc::new(Field::new("weekday", DataType::Int32, true)),
179 config_options: Arc::new(Default::default()),
180 })?;
181 match result {
182 ColumnarValue::Array(arr) => {
183 let expected = Int32Array::from(vec![Some(0), Some(5), Some(6), None]);
184 assert_eq!(arr.as_primitive::<Int32Type>(), &expected);
185 }
186 other => panic!("unexpected result: {other:?}"),
187 }
188
189 Ok(())
190 }
191}