1use std::sync::Arc;
19
20use arrow::array::builder::StringBuilder;
21use arrow::array::cast::AsArray;
22use arrow::array::{Array, ArrayRef};
23use arrow::compute::cast;
24use arrow::datatypes::DataType;
25use arrow::datatypes::DataType::{
26 Date32, Date64, Duration, Time32, Time64, Timestamp, Utf8,
27};
28use arrow::datatypes::TimeUnit::{Microsecond, Millisecond, Nanosecond, Second};
29use arrow::util::display::{ArrayFormatter, DurationFormat, FormatOptions};
30use datafusion_common::{Result, ScalarValue, exec_err, utils::take_function_args};
31use datafusion_expr::TypeSignature::Exact;
32use datafusion_expr::{
33 ColumnarValue, Documentation, ScalarFunctionArgs, ScalarUDFImpl, Signature,
34 TIMEZONE_WILDCARD, Volatility,
35};
36use datafusion_macros::user_doc;
37
38#[user_doc(
39 doc_section(label = "Time and Date Functions"),
40 description = "Returns a string representation of a date, time, timestamp or duration based on a [Chrono format](https://docs.rs/chrono/latest/chrono/format/strftime/index.html). Unlike the PostgreSQL equivalent of this function numerical formatting is not supported.",
41 syntax_example = "to_char(expression, format)",
42 sql_example = r#"```sql
43> select to_char('2023-03-01'::date, '%d-%m-%Y');
44+----------------------------------------------+
45| to_char(Utf8("2023-03-01"),Utf8("%d-%m-%Y")) |
46+----------------------------------------------+
47| 01-03-2023 |
48+----------------------------------------------+
49```
50
51Additional examples can be found [here](https://github.com/apache/datafusion/blob/main/datafusion-examples/examples/builtin_functions/date_time.rs)
52"#,
53 argument(
54 name = "expression",
55 description = "Expression to operate on. Can be a constant, column, or function that results in a date, time, timestamp or duration."
56 ),
57 argument(
58 name = "format",
59 description = "A [Chrono format](https://docs.rs/chrono/latest/chrono/format/strftime/index.html) string to use to convert the expression."
60 ),
61 argument(
62 name = "day",
63 description = "Day to use when making the date. Can be a constant, column or function, and any combination of arithmetic operators."
64 )
65)]
66#[derive(Debug, PartialEq, Eq, Hash)]
67pub struct ToCharFunc {
68 signature: Signature,
69 aliases: Vec<String>,
70}
71
72impl Default for ToCharFunc {
73 fn default() -> Self {
74 Self::new()
75 }
76}
77
78impl ToCharFunc {
79 pub fn new() -> Self {
80 Self {
81 signature: Signature::one_of(
82 vec![
83 Exact(vec![Date32, Utf8]),
84 Exact(vec![Date64, Utf8]),
85 Exact(vec![Time64(Nanosecond), Utf8]),
86 Exact(vec![Time64(Microsecond), Utf8]),
87 Exact(vec![Time32(Millisecond), Utf8]),
88 Exact(vec![Time32(Second), Utf8]),
89 Exact(vec![
90 Timestamp(Nanosecond, Some(TIMEZONE_WILDCARD.into())),
91 Utf8,
92 ]),
93 Exact(vec![Timestamp(Nanosecond, None), Utf8]),
94 Exact(vec![
95 Timestamp(Microsecond, Some(TIMEZONE_WILDCARD.into())),
96 Utf8,
97 ]),
98 Exact(vec![Timestamp(Microsecond, None), Utf8]),
99 Exact(vec![
100 Timestamp(Millisecond, Some(TIMEZONE_WILDCARD.into())),
101 Utf8,
102 ]),
103 Exact(vec![Timestamp(Millisecond, None), Utf8]),
104 Exact(vec![
105 Timestamp(Second, Some(TIMEZONE_WILDCARD.into())),
106 Utf8,
107 ]),
108 Exact(vec![Timestamp(Second, None), Utf8]),
109 Exact(vec![Duration(Nanosecond), Utf8]),
110 Exact(vec![Duration(Microsecond), Utf8]),
111 Exact(vec![Duration(Millisecond), Utf8]),
112 Exact(vec![Duration(Second), Utf8]),
113 ],
114 Volatility::Immutable,
115 ),
116 aliases: vec![String::from("date_format")],
117 }
118 }
119}
120
121impl ScalarUDFImpl for ToCharFunc {
122 fn name(&self) -> &str {
123 "to_char"
124 }
125
126 fn signature(&self) -> &Signature {
127 &self.signature
128 }
129
130 fn return_type(&self, _arg_types: &[DataType]) -> Result<DataType> {
131 Ok(Utf8)
132 }
133
134 fn invoke_with_args(&self, args: ScalarFunctionArgs) -> Result<ColumnarValue> {
135 let args = args.args;
136 let [date_time, format] = take_function_args(self.name(), &args)?;
137
138 match format {
139 ColumnarValue::Scalar(ScalarValue::Null | ScalarValue::Utf8(None)) => {
140 Ok(ColumnarValue::Scalar(ScalarValue::Utf8(None)))
141 }
142 ColumnarValue::Scalar(ScalarValue::Utf8(Some(fmt))) => {
143 to_char_scalar(date_time, fmt)
144 }
145 ColumnarValue::Array(_) => to_char_array(&args),
146 _ => exec_err!(
147 "Format for `to_char` must be non-null Utf8, received {}",
148 format.data_type()
149 ),
150 }
151 }
152
153 fn aliases(&self) -> &[String] {
154 &self.aliases
155 }
156
157 fn documentation(&self) -> Option<&Documentation> {
158 self.doc()
159 }
160}
161
162fn build_format_options<'a>(
163 data_type: &DataType,
164 format: &'a str,
165) -> Result<FormatOptions<'a>> {
166 let format_options = match data_type {
167 Date32 => FormatOptions::new()
168 .with_date_format(Some(format))
169 .with_datetime_format(Some(format)),
170 Date64 => FormatOptions::new().with_datetime_format(Some(format)),
171 Time32(_) => FormatOptions::new().with_time_format(Some(format)),
172 Time64(_) => FormatOptions::new().with_time_format(Some(format)),
173 Timestamp(_, _) => FormatOptions::new()
174 .with_timestamp_format(Some(format))
175 .with_timestamp_tz_format(Some(format)),
176 Duration(_) => FormatOptions::new().with_duration_format(
177 if "ISO8601".eq_ignore_ascii_case(format) {
178 DurationFormat::ISO8601
179 } else {
180 DurationFormat::Pretty
181 },
182 ),
183 other => {
184 return exec_err!(
185 "to_char only supports date, time, timestamp and duration data types, received {other:?}"
186 );
187 }
188 };
189 Ok(format_options)
190}
191
192fn to_char_scalar(expression: &ColumnarValue, format: &str) -> Result<ColumnarValue> {
194 let data_type = &expression.data_type();
197 let is_scalar_expression = matches!(&expression, ColumnarValue::Scalar(_));
198 let array = expression.to_array(1)?;
199
200 let format_options = build_format_options(data_type, format)?;
201 let formatter = ArrayFormatter::try_new(array.as_ref(), &format_options)?;
202
203 let fmt_len = format.len() + 10;
206 let mut builder = StringBuilder::with_capacity(array.len(), array.len() * fmt_len);
207
208 for i in 0..array.len() {
209 if array.is_null(i) {
210 builder.append_null();
211 } else {
212 match formatter.value(i).write(&mut builder) {
215 Ok(()) => builder.append_value(""),
216 Err(_) if data_type == &Date32 => {
223 return to_char_scalar(&expression.cast_to(&Date64, None)?, format);
224 }
225 Err(e) => return Err(e.into()),
226 }
227 }
228 }
229
230 let result = builder.finish();
231 if is_scalar_expression {
232 let val = result.is_valid(0).then(|| result.value(0).to_string());
233 Ok(ColumnarValue::Scalar(ScalarValue::Utf8(val)))
234 } else {
235 Ok(ColumnarValue::Array(Arc::new(result) as ArrayRef))
236 }
237}
238
239fn to_char_array(args: &[ColumnarValue]) -> Result<ColumnarValue> {
240 let arrays = ColumnarValue::values_to_arrays(args)?;
241 let data_array = &arrays[0];
242 let format_array = arrays[1].as_string::<i32>();
243 let data_type = data_array.data_type();
244
245 let fmt_len = 30;
247 let mut builder =
248 StringBuilder::with_capacity(data_array.len(), data_array.len() * fmt_len);
249 let mut buffer = String::with_capacity(fmt_len);
250
251 let mut date64_array: Option<ArrayRef> = None;
255
256 for idx in 0..data_array.len() {
257 if format_array.is_null(idx) || data_array.is_null(idx) {
258 builder.append_null();
259 continue;
260 }
261
262 let format = format_array.value(idx);
263 let format_options = build_format_options(data_type, format)?;
264 let formatter = ArrayFormatter::try_new(data_array.as_ref(), &format_options)?;
265
266 buffer.clear();
267
268 match formatter.value(idx).write(&mut buffer) {
273 Ok(()) => builder.append_value(&buffer),
274 Err(_) if data_type == &Date32 => {
275 buffer.clear();
276 let date64_ref = match &date64_array {
277 Some(arr) => arr.as_ref(),
278 None => {
279 date64_array = Some(cast(data_array.as_ref(), &Date64)?);
280 date64_array.as_ref().unwrap().as_ref()
281 }
282 };
283 let retry_options = build_format_options(&Date64, format)?;
284 let retry_fmt = ArrayFormatter::try_new(date64_ref, &retry_options)?;
285 retry_fmt.value(idx).write(&mut buffer)?;
286 builder.append_value(&buffer);
287 }
288 Err(e) => return Err(e.into()),
289 }
290 }
291
292 let result = builder.finish();
293 match args[0] {
294 ColumnarValue::Scalar(_) => {
295 let val = result.is_valid(0).then(|| result.value(0).to_string());
296 Ok(ColumnarValue::Scalar(ScalarValue::Utf8(val)))
297 }
298 ColumnarValue::Array(_) => Ok(ColumnarValue::Array(Arc::new(result) as ArrayRef)),
299 }
300}
301
302#[cfg(test)]
303mod tests {
304 use crate::datetime::to_char::ToCharFunc;
305 use arrow::array::{
306 Array, ArrayRef, Date32Array, Date64Array, StringArray, Time32MillisecondArray,
307 Time32SecondArray, Time64MicrosecondArray, Time64NanosecondArray,
308 TimestampMicrosecondArray, TimestampMillisecondArray, TimestampNanosecondArray,
309 TimestampSecondArray,
310 };
311 use arrow::datatypes::{DataType, Field, TimeUnit};
312 use chrono::{NaiveDateTime, Timelike};
313 use datafusion_common::ScalarValue;
314 use datafusion_common::config::ConfigOptions;
315 use datafusion_expr::{ColumnarValue, ScalarFunctionArgs, ScalarUDFImpl};
316 use std::sync::Arc;
317
318 #[test]
319 fn test_array_array() {
320 let array_array_data = vec![
321 (
322 Arc::new(Date32Array::from(vec![18506, 18507])) as ArrayRef,
323 StringArray::from(vec!["%Y::%m::%d", "%Y::%m::%d %S::%M::%H %f"]),
324 StringArray::from(vec![
325 "2020::09::01",
326 "2020::09::02 00::00::00 000000000",
327 ]),
328 ),
329 (
330 Arc::new(Date32Array::from(vec![18506, 18507])) as ArrayRef,
331 StringArray::from(vec!["%Y::%m::%d %H:%M:%S", "%d-%m-%Y %H:%M"]),
332 StringArray::from(vec!["2020::09::01 00:00:00", "02-09-2020 00:00"]),
333 ),
334 ];
335
336 for (value, format, expected) in array_array_data {
337 let batch_len = value.len();
338 let value_data_type = value.data_type().clone();
339 let format_data_type = format.data_type().clone();
340
341 let args = ScalarFunctionArgs {
342 args: vec![
343 ColumnarValue::Array(value),
344 ColumnarValue::Array(Arc::new(format) as ArrayRef),
345 ],
346 arg_fields: vec![
347 Field::new("a", value_data_type, true).into(),
348 Field::new("b", format_data_type, true).into(),
349 ],
350 number_rows: batch_len,
351 return_field: Field::new("f", DataType::Utf8, true).into(),
352 config_options: Arc::clone(&Arc::new(ConfigOptions::default())),
353 };
354 let result = ToCharFunc::new()
355 .invoke_with_args(args)
356 .expect("that to_char parsed values without error");
357
358 if let ColumnarValue::Array(result) = result {
359 assert_eq!(result.len(), 2);
360 assert_eq!(&expected as &dyn Array, result.as_ref());
361 } else {
362 panic!("Expected an array value")
363 }
364 }
365 }
366
367 #[test]
368 fn test_to_char() {
369 let date = "2020-01-02T03:04:05"
370 .parse::<NaiveDateTime>()
371 .unwrap()
372 .with_nanosecond(12345)
373 .unwrap();
374 let date2 = "2026-07-08T09:10:11"
375 .parse::<NaiveDateTime>()
376 .unwrap()
377 .with_nanosecond(56789)
378 .unwrap();
379
380 let scalar_data = vec![
381 (
382 ScalarValue::Date32(Some(18506)),
383 ScalarValue::Utf8(Some("%Y::%m::%d".to_string())),
384 "2020::09::01".to_string(),
385 ),
386 (
387 ScalarValue::Date32(Some(18506)),
388 ScalarValue::Utf8(Some("%Y::%m::%d %S::%M::%H %f".to_string())),
389 "2020::09::01 00::00::00 000000000".to_string(),
390 ),
391 (
392 ScalarValue::Date64(Some(date.and_utc().timestamp_millis())),
393 ScalarValue::Utf8(Some("%Y::%m::%d".to_string())),
394 "2020::01::02".to_string(),
395 ),
396 (
397 ScalarValue::Time32Second(Some(31851)),
398 ScalarValue::Utf8(Some("%H-%M-%S".to_string())),
399 "08-50-51".to_string(),
400 ),
401 (
402 ScalarValue::Time32Millisecond(Some(18506000)),
403 ScalarValue::Utf8(Some("%H-%M-%S".to_string())),
404 "05-08-26".to_string(),
405 ),
406 (
407 ScalarValue::Time64Microsecond(Some(12344567000)),
408 ScalarValue::Utf8(Some("%H-%M-%S %f".to_string())),
409 "03-25-44 567000000".to_string(),
410 ),
411 (
412 ScalarValue::Time64Nanosecond(Some(12344567890000)),
413 ScalarValue::Utf8(Some("%H-%M-%S %f".to_string())),
414 "03-25-44 567890000".to_string(),
415 ),
416 (
417 ScalarValue::TimestampSecond(Some(date.and_utc().timestamp()), None),
418 ScalarValue::Utf8(Some("%Y::%m::%d %S::%M::%H".to_string())),
419 "2020::01::02 05::04::03".to_string(),
420 ),
421 (
422 ScalarValue::TimestampMillisecond(
423 Some(date.and_utc().timestamp_millis()),
424 None,
425 ),
426 ScalarValue::Utf8(Some("%Y::%m::%d %S::%M::%H".to_string())),
427 "2020::01::02 05::04::03".to_string(),
428 ),
429 (
430 ScalarValue::TimestampMicrosecond(
431 Some(date.and_utc().timestamp_micros()),
432 None,
433 ),
434 ScalarValue::Utf8(Some("%Y::%m::%d %S::%M::%H %f".to_string())),
435 "2020::01::02 05::04::03 000012000".to_string(),
436 ),
437 (
438 ScalarValue::TimestampNanosecond(
439 Some(date.and_utc().timestamp_nanos_opt().unwrap()),
440 None,
441 ),
442 ScalarValue::Utf8(Some("%Y::%m::%d %S::%M::%H %f".to_string())),
443 "2020::01::02 05::04::03 000012345".to_string(),
444 ),
445 ];
446
447 for (value, format, expected) in scalar_data {
448 let arg_fields = vec![
449 Field::new("a", value.data_type(), false).into(),
450 Field::new("a", format.data_type(), false).into(),
451 ];
452 let args = ScalarFunctionArgs {
453 args: vec![ColumnarValue::Scalar(value), ColumnarValue::Scalar(format)],
454 arg_fields,
455 number_rows: 1,
456 return_field: Field::new("f", DataType::Utf8, true).into(),
457 config_options: Arc::new(ConfigOptions::default()),
458 };
459 let result = ToCharFunc::new()
460 .invoke_with_args(args)
461 .expect("that to_char parsed values without error");
462
463 if let ColumnarValue::Scalar(ScalarValue::Utf8(date)) = result {
464 assert_eq!(expected, date.unwrap());
465 } else {
466 panic!("Expected a scalar value")
467 }
468 }
469
470 let scalar_array_data = vec![
471 (
472 ScalarValue::Date32(Some(18506)),
473 StringArray::from(vec!["%Y::%m::%d".to_string()]),
474 "2020::09::01".to_string(),
475 ),
476 (
477 ScalarValue::Date32(Some(18506)),
478 StringArray::from(vec!["%Y::%m::%d %S::%M::%H %f".to_string()]),
479 "2020::09::01 00::00::00 000000000".to_string(),
480 ),
481 (
482 ScalarValue::Date64(Some(date.and_utc().timestamp_millis())),
483 StringArray::from(vec!["%Y::%m::%d".to_string()]),
484 "2020::01::02".to_string(),
485 ),
486 (
487 ScalarValue::Time32Second(Some(31851)),
488 StringArray::from(vec!["%H-%M-%S".to_string()]),
489 "08-50-51".to_string(),
490 ),
491 (
492 ScalarValue::Time32Millisecond(Some(18506000)),
493 StringArray::from(vec!["%H-%M-%S".to_string()]),
494 "05-08-26".to_string(),
495 ),
496 (
497 ScalarValue::Time64Microsecond(Some(12344567000)),
498 StringArray::from(vec!["%H-%M-%S %f".to_string()]),
499 "03-25-44 567000000".to_string(),
500 ),
501 (
502 ScalarValue::Time64Nanosecond(Some(12344567890000)),
503 StringArray::from(vec!["%H-%M-%S %f".to_string()]),
504 "03-25-44 567890000".to_string(),
505 ),
506 (
507 ScalarValue::TimestampSecond(Some(date.and_utc().timestamp()), None),
508 StringArray::from(vec!["%Y::%m::%d %S::%M::%H".to_string()]),
509 "2020::01::02 05::04::03".to_string(),
510 ),
511 (
512 ScalarValue::TimestampMillisecond(
513 Some(date.and_utc().timestamp_millis()),
514 None,
515 ),
516 StringArray::from(vec!["%Y::%m::%d %S::%M::%H".to_string()]),
517 "2020::01::02 05::04::03".to_string(),
518 ),
519 (
520 ScalarValue::TimestampMicrosecond(
521 Some(date.and_utc().timestamp_micros()),
522 None,
523 ),
524 StringArray::from(vec!["%Y::%m::%d %S::%M::%H %f".to_string()]),
525 "2020::01::02 05::04::03 000012000".to_string(),
526 ),
527 (
528 ScalarValue::TimestampNanosecond(
529 Some(date.and_utc().timestamp_nanos_opt().unwrap()),
530 None,
531 ),
532 StringArray::from(vec!["%Y::%m::%d %S::%M::%H %f".to_string()]),
533 "2020::01::02 05::04::03 000012345".to_string(),
534 ),
535 ];
536
537 for (value, format, expected) in scalar_array_data {
538 let batch_len = format.len();
539 let arg_fields = vec![
540 Field::new("a", value.data_type(), false).into(),
541 Field::new("a", format.data_type().to_owned(), false).into(),
542 ];
543 let args = ScalarFunctionArgs {
544 args: vec![
545 ColumnarValue::Scalar(value),
546 ColumnarValue::Array(Arc::new(format) as ArrayRef),
547 ],
548 arg_fields,
549 number_rows: batch_len,
550 return_field: Field::new("f", DataType::Utf8, true).into(),
551 config_options: Arc::new(ConfigOptions::default()),
552 };
553 let result = ToCharFunc::new()
554 .invoke_with_args(args)
555 .expect("that to_char parsed values without error");
556
557 if let ColumnarValue::Scalar(ScalarValue::Utf8(date)) = result {
558 assert_eq!(expected, date.unwrap());
559 } else {
560 panic!("Expected a scalar value")
561 }
562 }
563
564 let array_scalar_data = vec![
565 (
566 Arc::new(Date32Array::from(vec![18506, 18507])) as ArrayRef,
567 ScalarValue::Utf8(Some("%Y::%m::%d".to_string())),
568 StringArray::from(vec!["2020::09::01", "2020::09::02"]),
569 ),
570 (
571 Arc::new(Date32Array::from(vec![18506, 18507])) as ArrayRef,
572 ScalarValue::Utf8(Some("%Y::%m::%d %S::%M::%H %f".to_string())),
573 StringArray::from(vec![
574 "2020::09::01 00::00::00 000000000",
575 "2020::09::02 00::00::00 000000000",
576 ]),
577 ),
578 (
579 Arc::new(Date64Array::from(vec![
580 date.and_utc().timestamp_millis(),
581 date2.and_utc().timestamp_millis(),
582 ])) as ArrayRef,
583 ScalarValue::Utf8(Some("%Y::%m::%d".to_string())),
584 StringArray::from(vec!["2020::01::02", "2026::07::08"]),
585 ),
586 ];
587
588 let array_array_data = vec![
589 (
590 Arc::new(Date32Array::from(vec![18506, 18507])) as ArrayRef,
591 StringArray::from(vec!["%Y::%m::%d", "%d::%m::%Y"]),
592 StringArray::from(vec!["2020::09::01", "02::09::2020"]),
593 ),
594 (
595 Arc::new(Date32Array::from(vec![18506, 18507])) as ArrayRef,
596 StringArray::from(vec![
597 "%Y::%m::%d %S::%M::%H %f",
598 "%Y::%m::%d %S::%M::%H %f",
599 ]),
600 StringArray::from(vec![
601 "2020::09::01 00::00::00 000000000",
602 "2020::09::02 00::00::00 000000000",
603 ]),
604 ),
605 (
606 Arc::new(Date32Array::from(vec![18506, 18507])) as ArrayRef,
607 StringArray::from(vec!["%Y::%m::%d", "%Y::%m::%d %S::%M::%H %f"]),
608 StringArray::from(vec![
609 "2020::09::01",
610 "2020::09::02 00::00::00 000000000",
611 ]),
612 ),
613 (
614 Arc::new(Date64Array::from(vec![
615 date.and_utc().timestamp_millis(),
616 date2.and_utc().timestamp_millis(),
617 ])) as ArrayRef,
618 StringArray::from(vec!["%Y::%m::%d", "%d::%m::%Y"]),
619 StringArray::from(vec!["2020::01::02", "08::07::2026"]),
620 ),
621 (
622 Arc::new(Time32MillisecondArray::from(vec![1850600, 1860700]))
623 as ArrayRef,
624 StringArray::from(vec!["%H:%M:%S", "%H::%M::%S"]),
625 StringArray::from(vec!["00:30:50", "00::31::00"]),
626 ),
627 (
628 Arc::new(Time32SecondArray::from(vec![18506, 18507])) as ArrayRef,
629 StringArray::from(vec!["%H:%M:%S", "%H::%M::%S"]),
630 StringArray::from(vec!["05:08:26", "05::08::27"]),
631 ),
632 (
633 Arc::new(Time64MicrosecondArray::from(vec![12344567000, 22244567000]))
634 as ArrayRef,
635 StringArray::from(vec!["%H:%M:%S", "%H::%M::%S"]),
636 StringArray::from(vec!["03:25:44", "06::10::44"]),
637 ),
638 (
639 Arc::new(Time64NanosecondArray::from(vec![
640 1234456789000,
641 2224456789000,
642 ])) as ArrayRef,
643 StringArray::from(vec!["%H:%M:%S", "%H::%M::%S"]),
644 StringArray::from(vec!["00:20:34", "00::37::04"]),
645 ),
646 (
647 Arc::new(TimestampSecondArray::from(vec![
648 date.and_utc().timestamp(),
649 date2.and_utc().timestamp(),
650 ])) as ArrayRef,
651 StringArray::from(vec!["%Y::%m::%d %S::%M::%H", "%d::%m::%Y %S-%M-%H"]),
652 StringArray::from(vec![
653 "2020::01::02 05::04::03",
654 "08::07::2026 11-10-09",
655 ]),
656 ),
657 (
658 Arc::new(TimestampMillisecondArray::from(vec![
659 date.and_utc().timestamp_millis(),
660 date2.and_utc().timestamp_millis(),
661 ])) as ArrayRef,
662 StringArray::from(vec![
663 "%Y::%m::%d %S::%M::%H %f",
664 "%d::%m::%Y %S-%M-%H %f",
665 ]),
666 StringArray::from(vec![
667 "2020::01::02 05::04::03 000000000",
668 "08::07::2026 11-10-09 000000000",
669 ]),
670 ),
671 (
672 Arc::new(TimestampMicrosecondArray::from(vec![
673 date.and_utc().timestamp_micros(),
674 date2.and_utc().timestamp_micros(),
675 ])) as ArrayRef,
676 StringArray::from(vec![
677 "%Y::%m::%d %S::%M::%H %f",
678 "%d::%m::%Y %S-%M-%H %f",
679 ]),
680 StringArray::from(vec![
681 "2020::01::02 05::04::03 000012000",
682 "08::07::2026 11-10-09 000056000",
683 ]),
684 ),
685 (
686 Arc::new(TimestampNanosecondArray::from(vec![
687 date.and_utc().timestamp_nanos_opt().unwrap(),
688 date2.and_utc().timestamp_nanos_opt().unwrap(),
689 ])) as ArrayRef,
690 StringArray::from(vec![
691 "%Y::%m::%d %S::%M::%H %f",
692 "%d::%m::%Y %S-%M-%H %f",
693 ]),
694 StringArray::from(vec![
695 "2020::01::02 05::04::03 000012345",
696 "08::07::2026 11-10-09 000056789",
697 ]),
698 ),
699 ];
700
701 for (value, format, expected) in array_scalar_data {
702 let batch_len = value.len();
703 let arg_fields = vec![
704 Field::new("a", value.data_type().clone(), false).into(),
705 Field::new("a", format.data_type(), false).into(),
706 ];
707 let args = ScalarFunctionArgs {
708 args: vec![
709 ColumnarValue::Array(value as ArrayRef),
710 ColumnarValue::Scalar(format),
711 ],
712 arg_fields,
713 number_rows: batch_len,
714 return_field: Field::new("f", DataType::Utf8, true).into(),
715 config_options: Arc::new(ConfigOptions::default()),
716 };
717 let result = ToCharFunc::new()
718 .invoke_with_args(args)
719 .expect("that to_char parsed values without error");
720
721 if let ColumnarValue::Array(result) = result {
722 assert_eq!(result.len(), 2);
723 assert_eq!(&expected as &dyn Array, result.as_ref());
724 } else {
725 panic!("Expected an array value")
726 }
727 }
728
729 for (value, format, expected) in array_array_data {
730 let batch_len = value.len();
731 let arg_fields = vec![
732 Field::new("a", value.data_type().clone(), false).into(),
733 Field::new("a", format.data_type().clone(), false).into(),
734 ];
735 let args = ScalarFunctionArgs {
736 args: vec![
737 ColumnarValue::Array(value),
738 ColumnarValue::Array(Arc::new(format) as ArrayRef),
739 ],
740 arg_fields,
741 number_rows: batch_len,
742 return_field: Field::new("f", DataType::Utf8, true).into(),
743 config_options: Arc::new(ConfigOptions::default()),
744 };
745 let result = ToCharFunc::new()
746 .invoke_with_args(args)
747 .expect("that to_char parsed values without error");
748
749 if let ColumnarValue::Array(result) = result {
750 assert_eq!(result.len(), 2);
751 assert_eq!(&expected as &dyn Array, result.as_ref());
752 } else {
753 panic!("Expected an array value")
754 }
755 }
756
757 let arg_field = Field::new("a", DataType::Int32, true).into();
763 let args = ScalarFunctionArgs {
764 args: vec![ColumnarValue::Scalar(ScalarValue::Int32(Some(1)))],
765 arg_fields: vec![arg_field],
766 number_rows: 1,
767 return_field: Field::new("f", DataType::Utf8, true).into(),
768 config_options: Arc::new(ConfigOptions::default()),
769 };
770 let result = ToCharFunc::new().invoke_with_args(args);
771 assert_eq!(
772 result.err().unwrap().strip_backtrace(),
773 "Execution error: to_char function requires 2 arguments, got 1"
774 );
775
776 let arg_fields = vec![
778 Field::new("a", DataType::Utf8, true).into(),
779 Field::new("a", DataType::Timestamp(TimeUnit::Nanosecond, None), true).into(),
780 ];
781 let args = ScalarFunctionArgs {
782 args: vec![
783 ColumnarValue::Scalar(ScalarValue::Int32(Some(1))),
784 ColumnarValue::Scalar(ScalarValue::TimestampNanosecond(Some(1), None)),
785 ],
786 arg_fields,
787 number_rows: 1,
788 return_field: Field::new("f", DataType::Utf8, true).into(),
789 config_options: Arc::new(ConfigOptions::default()),
790 };
791 let result = ToCharFunc::new().invoke_with_args(args);
792 assert_eq!(
793 result.err().unwrap().strip_backtrace(),
794 "Execution error: Format for `to_char` must be non-null Utf8, received Timestamp(ns)"
795 );
796 }
797}