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