1use std::fmt::Write;
19use std::sync::Arc;
20
21use core::num::FpCategory;
22
23use arrow::{
24 array::{Array, ArrayRef, LargeStringArray, StringArray, StringViewArray},
25 datatypes::{DataType, Field, FieldRef},
26};
27use bigdecimal::{
28 BigDecimal, ToPrimitive,
29 num_bigint::{BigInt, Sign},
30};
31use chrono::{DateTime, Datelike, Timelike, Utc};
32use datafusion_common::{
33 DataFusionError, Result, ScalarValue, exec_datafusion_err, exec_err, plan_err,
34};
35use datafusion_expr::{
36 ColumnarValue, ReturnFieldArgs, ScalarFunctionArgs, ScalarUDFImpl, Signature,
37 TypeSignature, Volatility,
38};
39
40#[derive(Debug, PartialEq, Eq, Hash)]
43pub struct FormatStringFunc {
44 signature: Signature,
45 aliases: Vec<String>,
46}
47
48impl Default for FormatStringFunc {
49 fn default() -> Self {
50 Self::new()
51 }
52}
53
54impl FormatStringFunc {
55 pub fn new() -> Self {
56 Self {
57 signature: Signature::new(TypeSignature::VariadicAny, Volatility::Immutable),
58 aliases: vec![String::from("printf")],
59 }
60 }
61}
62
63impl ScalarUDFImpl for FormatStringFunc {
64 fn name(&self) -> &str {
65 "format_string"
66 }
67
68 fn aliases(&self) -> &[String] {
69 &self.aliases
70 }
71
72 fn signature(&self) -> &Signature {
73 &self.signature
74 }
75
76 fn return_type(&self, _arg_types: &[DataType]) -> Result<DataType> {
77 datafusion_common::internal_err!(
78 "return_type should not be called, use return_field_from_args instead"
79 )
80 }
81
82 fn return_field_from_args(&self, args: ReturnFieldArgs) -> Result<FieldRef> {
83 match args.arg_fields[0].data_type() {
84 DataType::Null => {
85 Ok(Arc::new(Field::new("format_string", DataType::Utf8, true)))
86 }
87 DataType::Utf8 | DataType::LargeUtf8 | DataType::Utf8View => {
88 Ok(Arc::clone(&args.arg_fields[0]))
89 }
90 _ => exec_err!(
91 "format_string expects the first argument to be Utf8, LargeUtf8 or Utf8View, got {} instead",
92 args.arg_fields[0].data_type()
93 ),
94 }
95 }
96
97 fn invoke_with_args(&self, args: ScalarFunctionArgs) -> Result<ColumnarValue> {
98 let len = args.args.iter().find_map(|arg| match arg {
99 ColumnarValue::Scalar(_) => None,
100 ColumnarValue::Array(a) => Some(a.len()),
101 });
102 let is_scalar = len.is_none();
103 let data_types = args.args[1..]
104 .iter()
105 .map(|arg| arg.data_type())
106 .collect::<Vec<_>>();
107 let fmt_type = args.args[0].data_type();
108
109 match &args.args[0] {
110 ColumnarValue::Scalar(ScalarValue::Null) => {
111 Ok(ColumnarValue::Scalar(ScalarValue::Utf8(None)))
112 }
113 ColumnarValue::Scalar(ScalarValue::Utf8(None)) => {
114 Ok(ColumnarValue::Scalar(ScalarValue::Utf8(None)))
115 }
116 ColumnarValue::Scalar(ScalarValue::LargeUtf8(None)) => {
117 Ok(ColumnarValue::Scalar(ScalarValue::LargeUtf8(None)))
118 }
119 ColumnarValue::Scalar(ScalarValue::Utf8View(None)) => {
120 Ok(ColumnarValue::Scalar(ScalarValue::Utf8View(None)))
121 }
122 ColumnarValue::Scalar(ScalarValue::Utf8(Some(fmt)))
123 | ColumnarValue::Scalar(ScalarValue::LargeUtf8(Some(fmt)))
124 | ColumnarValue::Scalar(ScalarValue::Utf8View(Some(fmt))) => {
125 let formatter = Formatter::parse(fmt, &data_types)?;
126 let mut result = Vec::with_capacity(len.unwrap_or(1));
127 for i in 0..len.unwrap_or(1) {
128 let scalars = args.args[1..]
129 .iter()
130 .map(|arg| try_to_scalar(arg.clone(), i))
131 .collect::<Result<Vec<_>>>()?;
132 let formatted = formatter.format(&scalars)?;
133 result.push(formatted);
134 }
135 if is_scalar {
136 let scalar_result = result.pop().unwrap();
137 match fmt_type {
138 DataType::Utf8 => Ok(ColumnarValue::Scalar(ScalarValue::Utf8(
139 Some(scalar_result),
140 ))),
141 DataType::LargeUtf8 => Ok(ColumnarValue::Scalar(
142 ScalarValue::LargeUtf8(Some(scalar_result)),
143 )),
144 DataType::Utf8View => Ok(ColumnarValue::Scalar(
145 ScalarValue::Utf8View(Some(scalar_result)),
146 )),
147 _ => unreachable!(),
148 }
149 } else {
150 let array: ArrayRef = match fmt_type {
151 DataType::Utf8 => Arc::new(StringArray::from(result)),
152 DataType::LargeUtf8 => Arc::new(LargeStringArray::from(result)),
153 DataType::Utf8View => Arc::new(StringViewArray::from(result)),
154 _ => unreachable!(),
155 };
156 Ok(ColumnarValue::Array(array))
157 }
158 }
159 ColumnarValue::Array(fmts) => {
160 let mut result = Vec::with_capacity(len.unwrap());
161 for i in 0..len.unwrap() {
162 let fmt = ScalarValue::try_from_array(fmts, i)?;
163 match fmt.try_as_str() {
164 Some(Some(fmt)) => {
165 let formatter = Formatter::parse(fmt, &data_types)?;
166 let scalars = args.args[1..]
167 .iter()
168 .map(|arg| try_to_scalar(arg.clone(), i))
169 .collect::<Result<Vec<_>>>()?;
170 let formatted = formatter.format(&scalars)?;
171 result.push(Some(formatted));
172 }
173 Some(None) => {
174 result.push(None);
175 }
176 _ => unreachable!(),
177 }
178 }
179 let array: ArrayRef = match fmt_type {
180 DataType::Utf8 => Arc::new(StringArray::from(result)),
181 DataType::LargeUtf8 => Arc::new(LargeStringArray::from(result)),
182 DataType::Utf8View => Arc::new(StringViewArray::from(result)),
183 _ => unreachable!(),
184 };
185 Ok(ColumnarValue::Array(array))
186 }
187 _ => exec_err!(
188 "The format_string function expects the first argument to be a string"
189 ),
190 }
191 }
192}
193
194fn try_to_scalar(arg: ColumnarValue, index: usize) -> Result<ScalarValue> {
195 match arg {
196 ColumnarValue::Scalar(scalar) => Ok(scalar),
197 ColumnarValue::Array(array) => ScalarValue::try_from_array(&array, index),
198 }
199}
200
201#[derive(Debug)]
203pub struct Formatter<'a> {
204 pub elements: Vec<FormatElement<'a>>,
205 pub arg_num: usize,
206}
207
208impl<'a> Formatter<'a> {
209 pub fn new(elements: Vec<FormatElement<'a>>) -> Self {
210 let arg_num = elements
211 .iter()
212 .map(|element| match element {
213 FormatElement::Format(spec) => spec.argument_index,
214 _ => 0,
215 })
216 .max()
217 .unwrap_or(0);
218 Self { elements, arg_num }
219 }
220
221 pub fn parse(fmt: &'a str, arg_types: &[DataType]) -> Result<Self> {
286 let mut res = Vec::new();
288
289 let mut rem = fmt;
290 let mut argument_index = 0;
291
292 let mut prev: Option<usize> = None;
293
294 while !rem.is_empty() {
295 if let Some((verbatim_prefix, rest)) = rem.split_once('%') {
296 if !verbatim_prefix.is_empty() {
297 res.push(FormatElement::Verbatim(verbatim_prefix));
298 }
299 if let Some(rest) = rest.strip_prefix('%') {
300 res.push(FormatElement::Verbatim("%"));
301 rem = rest;
302 continue;
303 }
304 if let Some(rest) = rest.strip_prefix('n') {
305 res.push(FormatElement::Verbatim("\n"));
306 rem = rest;
307 continue;
308 }
309 if let Some(rest) = rest.strip_prefix('<') {
310 let Some(p) = prev else {
312 return exec_err!("No previous argument to reference");
313 };
314 let (spec, rest) =
315 take_conversion_specifier(rest, p, &arg_types[p - 1])?;
316 res.push(FormatElement::Format(spec));
317 rem = rest;
318 continue;
319 }
320
321 let (current_argument_index, rest2) = take_numeric_param(rest, false);
322 let (current_argument_index, rest) =
323 match (current_argument_index, rest2.starts_with('$')) {
324 (NumericParam::Literal(index), true) => {
325 (index as usize, &rest2[1..])
326 }
327 (NumericParam::FromArgument, true) => {
328 return exec_err!("Invalid numeric parameter");
329 }
330 (_, false) => {
331 argument_index += 1;
332 (argument_index, rest)
333 }
334 };
335 if current_argument_index == 0 || current_argument_index > arg_types.len()
336 {
337 return exec_err!(
338 "Argument index {} is out of bounds",
339 current_argument_index
340 );
341 }
342
343 let (spec, rest) = take_conversion_specifier(
344 rest,
345 current_argument_index,
346 &arg_types[current_argument_index - 1],
347 )
348 .map_err(|e| exec_datafusion_err!("{:?}, format string: {:?}", e, fmt))?;
349 res.push(FormatElement::Format(spec));
350 prev = Some(spec.argument_index);
351 rem = rest;
352 } else {
353 res.push(FormatElement::Verbatim(rem));
354 break;
355 }
356 }
357
358 Ok(Self::new(res))
359 }
360
361 pub fn format(&self, args: &[ScalarValue]) -> Result<String> {
362 if args.len() < self.arg_num {
363 return exec_err!(
364 "Expected at least {} arguments, got {}",
365 self.arg_num,
366 args.len()
367 );
368 }
369 let mut string = String::new();
370 for element in &self.elements {
371 match element {
372 FormatElement::Verbatim(text) => {
373 string.push_str(text);
374 }
375 FormatElement::Format(spec) => {
376 spec.format(&mut string, &args[spec.argument_index - 1])?;
377 }
378 }
379 }
380 Ok(string)
381 }
382}
383
384#[derive(Debug)]
385pub enum FormatElement<'a> {
386 Verbatim(&'a str),
388 Format(ConversionSpecifier),
390}
391
392#[derive(Debug, Clone, Copy, PartialEq, Eq)]
394pub struct ConversionSpecifier {
395 pub argument_index: usize,
396 pub alt_form: bool,
398 pub zero_pad: bool,
400 pub left_adj: bool,
402 pub space_sign: bool,
404 pub force_sign: bool,
406 pub grouping_separator: bool,
408 pub negative_in_parentheses: bool,
410 pub width: NumericParam,
412 pub precision: NumericParam,
414 pub conversion_type: ConversionType,
416}
417
418#[derive(Debug, Clone, Copy, PartialEq, Eq)]
420pub enum NumericParam {
421 Literal(i32),
423 FromArgument,
425}
426
427#[derive(Debug, Clone, Copy, PartialEq, Eq)]
429pub enum ConversionType {
430 BooleanUpper,
432 BooleanLower,
434 HexHashLower,
437 HexHashUpper,
439 DecInt,
441 OctInt,
443 HexIntLower,
445 HexIntUpper,
447 SciFloatLower,
449 SciFloatUpper,
451 DecFloatLower,
453 CompactFloatLower,
455 CompactFloatUpper,
457 HexFloatLower,
459 HexFloatUpper,
461 TimeLower(TimeFormat),
463 TimeUpper(TimeFormat),
465 CharLower,
467 CharUpper,
469 StringLower,
471 StringUpper,
473}
474
475#[derive(Debug, Clone, Copy, PartialEq, Eq)]
476pub enum TimeFormat {
477 HUpper,
480 IUpper,
483 KLower,
486 LLower,
489 MUpper,
491 SUpper,
494 LUpper,
496 NUpper,
499 PLower,
503 ZLower,
507 ZUpper,
511 SLower,
514 QUpper,
517 BUpper,
519 BLower,
521 AUpper,
523 ALower,
525 CUpper,
527 YUpper,
529 YLower,
531 JLower,
533 MLower,
535 DLower,
537 ELower,
539 RUpper,
541 TUpper,
543 RLower,
545 DUpper,
547 FUpper,
549 CLower,
551}
552
553impl TryFrom<char> for TimeFormat {
554 type Error = DataFusionError;
555 fn try_from(value: char) -> Result<Self, Self::Error> {
556 match value {
557 'H' => Ok(TimeFormat::HUpper),
558 'I' => Ok(TimeFormat::IUpper),
559 'k' => Ok(TimeFormat::KLower),
560 'l' => Ok(TimeFormat::LLower),
561 'M' => Ok(TimeFormat::MUpper),
562 'S' => Ok(TimeFormat::SUpper),
563 'L' => Ok(TimeFormat::LUpper),
564 'N' => Ok(TimeFormat::NUpper),
565 'p' => Ok(TimeFormat::PLower),
566 'z' => Ok(TimeFormat::ZLower),
567 'Z' => Ok(TimeFormat::ZUpper),
568 's' => Ok(TimeFormat::SLower),
569 'Q' => Ok(TimeFormat::QUpper),
570 'B' => Ok(TimeFormat::BUpper),
571 'b' | 'h' => Ok(TimeFormat::BLower),
572 'A' => Ok(TimeFormat::AUpper),
573 'a' => Ok(TimeFormat::ALower),
574 'C' => Ok(TimeFormat::CUpper),
575 'Y' => Ok(TimeFormat::YUpper),
576 'y' => Ok(TimeFormat::YLower),
577 'j' => Ok(TimeFormat::JLower),
578 'm' => Ok(TimeFormat::MLower),
579 'd' => Ok(TimeFormat::DLower),
580 'e' => Ok(TimeFormat::ELower),
581 'R' => Ok(TimeFormat::RUpper),
582 'T' => Ok(TimeFormat::TUpper),
583 'r' => Ok(TimeFormat::RLower),
584 'D' => Ok(TimeFormat::DUpper),
585 'F' => Ok(TimeFormat::FUpper),
586 'c' => Ok(TimeFormat::CLower),
587 _ => exec_err!("Invalid time format: {}", value),
588 }
589 }
590}
591
592impl ConversionType {
593 pub fn validate(&self, arg_type: &DataType) -> Result<()> {
594 match self {
595 ConversionType::BooleanLower | ConversionType::BooleanUpper
596 if *arg_type != DataType::Boolean =>
597 {
598 return exec_err!(
599 "Invalid argument type for boolean conversion: {:?}",
600 arg_type
601 );
602 }
603 ConversionType::CharLower | ConversionType::CharUpper
604 if !matches!(
605 arg_type,
606 DataType::Int8
607 | DataType::UInt8
608 | DataType::Int16
609 | DataType::UInt16
610 | DataType::Int32
611 | DataType::UInt32
612 | DataType::Int64
613 | DataType::UInt64
614 ) =>
615 {
616 return exec_err!(
617 "Invalid argument type for char conversion: {:?}",
618 arg_type
619 );
620 }
621 ConversionType::DecInt
622 | ConversionType::OctInt
623 | ConversionType::HexIntLower
624 | ConversionType::HexIntUpper
625 if !arg_type.is_integer() =>
626 {
627 return exec_err!(
628 "Invalid argument type for integer conversion: {:?}",
629 arg_type
630 );
631 }
632 ConversionType::SciFloatLower
633 | ConversionType::SciFloatUpper
634 | ConversionType::DecFloatLower
635 | ConversionType::CompactFloatLower
636 | ConversionType::CompactFloatUpper
637 | ConversionType::HexFloatLower
638 | ConversionType::HexFloatUpper
639 if !arg_type.is_numeric() =>
640 {
641 return exec_err!(
642 "Invalid argument type for float conversion: {:?}",
643 arg_type
644 );
645 }
646 ConversionType::TimeLower(_) | ConversionType::TimeUpper(_)
647 if !arg_type.is_temporal() =>
648 {
649 return exec_err!(
650 "Invalid argument type for time conversion: {:?}",
651 arg_type
652 );
653 }
654 _ => {}
655 }
656 Ok(())
657 }
658
659 fn supports_integer(&self) -> bool {
660 matches!(
661 self,
662 ConversionType::DecInt
663 | ConversionType::HexIntLower
664 | ConversionType::HexIntUpper
665 | ConversionType::OctInt
666 | ConversionType::CharLower
667 | ConversionType::CharUpper
668 | ConversionType::StringLower
669 | ConversionType::StringUpper
670 )
671 }
672
673 fn supports_float(&self) -> bool {
674 matches!(
675 self,
676 ConversionType::DecFloatLower
677 | ConversionType::SciFloatLower
678 | ConversionType::SciFloatUpper
679 | ConversionType::CompactFloatLower
680 | ConversionType::CompactFloatUpper
681 | ConversionType::StringLower
682 | ConversionType::StringUpper
683 | ConversionType::HexFloatLower
684 | ConversionType::HexFloatUpper
685 )
686 }
687
688 fn supports_decimal(&self) -> bool {
689 matches!(
690 self,
691 ConversionType::DecFloatLower
692 | ConversionType::SciFloatLower
693 | ConversionType::SciFloatUpper
694 | ConversionType::CompactFloatLower
695 | ConversionType::CompactFloatUpper
696 | ConversionType::StringLower
697 | ConversionType::StringUpper
698 )
699 }
700
701 fn supports_time(&self) -> bool {
702 matches!(
703 self,
704 ConversionType::TimeLower(_)
705 | ConversionType::TimeUpper(_)
706 | ConversionType::StringLower
707 | ConversionType::StringUpper
708 )
709 }
710
711 fn is_upper(&self) -> bool {
712 matches!(
713 self,
714 ConversionType::BooleanUpper
715 | ConversionType::HexHashUpper
716 | ConversionType::HexIntUpper
717 | ConversionType::SciFloatUpper
718 | ConversionType::CompactFloatUpper
719 | ConversionType::HexFloatUpper
720 | ConversionType::TimeUpper(_)
721 | ConversionType::CharUpper
722 | ConversionType::StringUpper
723 )
724 }
725}
726
727fn take_conversion_specifier<'a>(
728 mut s: &'a str,
729 argument_index: usize,
730 arg_type: &DataType,
731) -> Result<(ConversionSpecifier, &'a str)> {
732 let mut spec = ConversionSpecifier {
733 argument_index,
734 alt_form: false,
735 zero_pad: false,
736 left_adj: false,
737 space_sign: false,
738 force_sign: false,
739 grouping_separator: false,
740 negative_in_parentheses: false,
741 width: NumericParam::Literal(0),
742 precision: NumericParam::FromArgument, conversion_type: ConversionType::DecInt,
745 };
746
747 loop {
749 match s.chars().next() {
750 Some('#') => {
751 spec.alt_form = true;
752 }
753 Some('0') => {
754 if spec.left_adj {
755 return exec_err!("Invalid flag combination: '0' and '-'");
756 }
757 spec.zero_pad = true;
758 }
759 Some('-') => {
760 spec.left_adj = true;
761 }
762 Some(' ') => {
763 if spec.force_sign {
764 return exec_err!("Invalid flag combination: '+' and ' '");
765 }
766 spec.space_sign = true;
767 }
768 Some('+') => {
769 if spec.space_sign {
770 return exec_err!("Invalid flag combination: '+' and ' '");
771 }
772 spec.force_sign = true;
773 }
774 Some(',') => {
775 spec.grouping_separator = true;
776 }
777 Some('(') => {
778 spec.negative_in_parentheses = true;
779 }
780 _ => {
781 break;
782 }
783 }
784 s = &s[1..];
785 }
786 let (w, mut s) = take_numeric_param(s, false);
788 spec.width = w;
789 if matches!(s.chars().next(), Some('.')) {
791 s = &s[1..];
792 let (p, s2) = take_numeric_param(s, true);
793 spec.precision = p;
794 s = s2;
795 }
796 let mut chars = s.chars();
797 let mut offset = 1;
798 spec.conversion_type = match chars.next() {
800 Some('b') => ConversionType::BooleanLower,
801 Some('B') => ConversionType::BooleanUpper,
802 Some('h') => ConversionType::HexHashLower,
803 Some('H') => ConversionType::HexHashUpper,
804 Some('s') => ConversionType::StringLower,
805 Some('S') => ConversionType::StringUpper,
806 Some('c') => ConversionType::CharLower,
807 Some('C') => ConversionType::CharUpper,
808 Some('d') => ConversionType::DecInt,
809 Some('o') => ConversionType::OctInt,
810 Some('x') => ConversionType::HexIntLower,
811 Some('X') => ConversionType::HexIntUpper,
812 Some('e') => ConversionType::SciFloatLower,
813 Some('E') => ConversionType::SciFloatUpper,
814 Some('f') => ConversionType::DecFloatLower,
815 Some('g') => ConversionType::CompactFloatLower,
816 Some('G') => ConversionType::CompactFloatUpper,
817 Some('a') => ConversionType::HexFloatLower,
818 Some('A') => ConversionType::HexFloatUpper,
819 Some('t') => {
820 let Some(chr) = chars.next() else {
821 return exec_err!("Invalid time format: {}", s);
822 };
823 offset += 1;
824 ConversionType::TimeLower(chr.try_into()?)
825 }
826 Some('T') => {
827 let Some(chr) = chars.next() else {
828 return exec_err!("Invalid time format: {}", s);
829 };
830 offset += 1;
831 ConversionType::TimeUpper(chr.try_into()?)
832 }
833 chr => {
834 return plan_err!("Invalid conversion type: {:?}", chr);
835 }
836 };
837
838 spec.conversion_type.validate(arg_type)?;
839 Ok((spec, &s[offset..]))
840}
841
842fn take_numeric_param(s: &str, zero: bool) -> (NumericParam, &str) {
843 match s.chars().next() {
844 Some(digit) if (if zero { '0'..='9' } else { '1'..='9' }).contains(&digit) => {
845 let mut s = s;
846 let mut w = 0;
847 loop {
848 match s.chars().next() {
849 Some(digit) if digit.is_ascii_digit() => {
850 w = 10 * w + (digit as i32 - '0' as i32);
851 }
852 _ => {
853 break;
854 }
855 }
856 s = &s[1..];
857 }
858 (NumericParam::Literal(w), s)
859 }
860 _ => (NumericParam::FromArgument, s),
861 }
862}
863
864fn codepoint_to_char(value: u32) -> Result<char> {
868 char::from_u32(value).ok_or_else(|| {
869 exec_datafusion_err!("invalid Unicode scalar value for %c: {value:#x}")
870 })
871}
872
873fn signed_to_char(value: i64) -> Result<char> {
877 let codepoint = u32::try_from(value).map_err(|_| {
878 exec_datafusion_err!("invalid Unicode scalar value for %c: {value}")
879 })?;
880 codepoint_to_char(codepoint)
881}
882
883fn unsigned_to_char(value: u64) -> Result<char> {
888 let codepoint = u32::try_from(value).map_err(|_| {
889 exec_datafusion_err!("invalid Unicode scalar value for %c: {value:#x}")
890 })?;
891 codepoint_to_char(codepoint)
892}
893
894trait IntegerFormatValue {
898 fn unsigned_bits(self) -> u64;
899
900 fn to_char(self) -> Result<char>;
901
902 fn format_decimal(
903 self,
904 spec: &ConversionSpecifier,
905 writer: &mut String,
906 ) -> Result<()>;
907
908 fn decimal_string(self) -> String;
909}
910
911macro_rules! signed_integer_value {
912 ($source:ty, $unsigned:ty) => {
913 impl IntegerFormatValue for $source {
914 fn unsigned_bits(self) -> u64 {
915 (self as $unsigned) as u64
916 }
917
918 fn to_char(self) -> Result<char> {
919 signed_to_char(self as i64)
920 }
921
922 fn format_decimal(
923 self,
924 spec: &ConversionSpecifier,
925 writer: &mut String,
926 ) -> Result<()> {
927 spec.format_signed(writer, self as i64)
928 }
929
930 fn decimal_string(self) -> String {
931 self.to_string()
932 }
933 }
934 };
935}
936
937signed_integer_value!(i8, u8);
938signed_integer_value!(i16, u16);
939signed_integer_value!(i32, u32);
940signed_integer_value!(i64, u64);
941
942macro_rules! unsigned_integer_value {
943 ($source:ty) => {
944 impl IntegerFormatValue for $source {
945 fn unsigned_bits(self) -> u64 {
946 self as u64
947 }
948
949 fn to_char(self) -> Result<char> {
950 unsigned_to_char(self as u64)
951 }
952
953 fn format_decimal(
954 self,
955 spec: &ConversionSpecifier,
956 writer: &mut String,
957 ) -> Result<()> {
958 spec.format_unsigned(writer, self as u64)
959 }
960
961 fn decimal_string(self) -> String {
962 self.to_string()
963 }
964 }
965 };
966}
967
968unsigned_integer_value!(u8);
969unsigned_integer_value!(u16);
970unsigned_integer_value!(u32);
971unsigned_integer_value!(u64);
972
973impl ConversionSpecifier {
974 fn validate_grouping_separator(&self) -> Result<()> {
978 if self.grouping_separator
979 && matches!(
980 self.conversion_type,
981 ConversionType::SciFloatLower | ConversionType::SciFloatUpper
982 )
983 {
984 return exec_err!(
985 "Grouping separator ',' flag is not compatible with scientific notation conversion '{}'",
986 if self.conversion_type == ConversionType::SciFloatUpper {
987 'E'
988 } else {
989 'e'
990 }
991 );
992 }
993 Ok(())
994 }
995
996 pub fn format(&self, string: &mut String, value: &ScalarValue) -> Result<()> {
997 match value {
998 ScalarValue::Boolean(value) => match self.conversion_type {
999 ConversionType::StringLower | ConversionType::StringUpper => {
1000 self.format_string(string, &value.unwrap_or(false).to_string())
1001 }
1002
1003 _ => self.format_boolean(string, value),
1004 },
1005 ScalarValue::Int8(value) => self.format_integer(string, value, "Int8"),
1006 ScalarValue::Int16(value) => self.format_integer(string, value, "Int16"),
1007 ScalarValue::Int32(value) => self.format_integer(string, value, "Int32"),
1008 ScalarValue::Int64(value) => self.format_integer(string, value, "Int64"),
1009 ScalarValue::UInt8(value) => self.format_integer(string, value, "UInt8"),
1010 ScalarValue::UInt16(value) => self.format_integer(string, value, "UInt16"),
1011 ScalarValue::UInt32(value) => self.format_integer(string, value, "UInt32"),
1012 ScalarValue::UInt64(value) => self.format_integer(string, value, "UInt64"),
1013 ScalarValue::Float16(value) => match (self.conversion_type, value) {
1014 (
1015 ConversionType::DecFloatLower
1016 | ConversionType::SciFloatLower
1017 | ConversionType::SciFloatUpper
1018 | ConversionType::CompactFloatLower
1019 | ConversionType::CompactFloatUpper,
1020 Some(value),
1021 ) => self.format_float(string, value.to_f64().unwrap()),
1022 (
1023 ConversionType::StringLower | ConversionType::StringUpper,
1024 Some(value),
1025 ) => self.format_string(string, &value.to_f32().unwrap().spark_string()),
1026 (
1027 ConversionType::HexFloatLower | ConversionType::HexFloatUpper,
1028 Some(value),
1029 ) => self.format_hex_float(string, value.to_f64().unwrap()),
1030 (t, None) if t.supports_float() => self.format_string(string, "null"),
1031 _ => {
1032 exec_err!(
1033 "Invalid conversion type: {:?} for Float16",
1034 self.conversion_type
1035 )
1036 }
1037 },
1038 ScalarValue::Float32(value) => match (self.conversion_type, value) {
1039 (
1040 ConversionType::DecFloatLower
1041 | ConversionType::SciFloatLower
1042 | ConversionType::SciFloatUpper
1043 | ConversionType::CompactFloatLower
1044 | ConversionType::CompactFloatUpper,
1045 Some(value),
1046 ) => self.format_float(string, *value as f64),
1047 (
1048 ConversionType::StringLower | ConversionType::StringUpper,
1049 Some(value),
1050 ) => self.format_string(string, &value.spark_string()),
1051 (
1052 ConversionType::HexFloatLower | ConversionType::HexFloatUpper,
1053 Some(value),
1054 ) => self.format_hex_float(string, *value as f64),
1055 (t, None) if t.supports_float() => self.format_string(string, "null"),
1056 _ => {
1057 exec_err!(
1058 "Invalid conversion type: {:?} for Float32",
1059 self.conversion_type
1060 )
1061 }
1062 },
1063 ScalarValue::Float64(value) => match (self.conversion_type, value) {
1064 (
1065 ConversionType::DecFloatLower
1066 | ConversionType::SciFloatLower
1067 | ConversionType::SciFloatUpper
1068 | ConversionType::CompactFloatLower
1069 | ConversionType::CompactFloatUpper,
1070 Some(value),
1071 ) => self.format_float(string, *value),
1072 (
1073 ConversionType::StringLower | ConversionType::StringUpper,
1074 Some(value),
1075 ) => self.format_string(string, &value.spark_string()),
1076 (
1077 ConversionType::HexFloatLower | ConversionType::HexFloatUpper,
1078 Some(value),
1079 ) => self.format_hex_float(string, *value),
1080 (t, None) if t.supports_float() => self.format_string(string, "null"),
1081 _ => {
1082 exec_err!(
1083 "Invalid conversion type: {:?} for Float64",
1084 self.conversion_type
1085 )
1086 }
1087 },
1088 ScalarValue::Utf8(value) => {
1089 let value: &str = match value {
1090 Some(value) => value.as_str(),
1091 None => "null",
1092 };
1093 if matches!(
1094 self.conversion_type,
1095 ConversionType::StringLower | ConversionType::StringUpper
1096 ) {
1097 self.format_string(string, value)
1098 } else {
1099 exec_err!(
1100 "Invalid conversion type: {:?} for Utf8",
1101 self.conversion_type
1102 )
1103 }
1104 }
1105 ScalarValue::LargeUtf8(value) => {
1106 let value: &str = match value {
1107 Some(value) => value.as_str(),
1108 None => "null",
1109 };
1110 if matches!(
1111 self.conversion_type,
1112 ConversionType::StringLower | ConversionType::StringUpper
1113 ) {
1114 self.format_string(string, value)
1115 } else {
1116 exec_err!(
1117 "Invalid conversion type: {:?} for LargeUtf8",
1118 self.conversion_type
1119 )
1120 }
1121 }
1122 ScalarValue::Utf8View(value) => {
1123 let value: &str = match value {
1124 Some(value) => value.as_str(),
1125 None => "null",
1126 };
1127 self.format_string(string, value)
1128 }
1129 ScalarValue::Decimal128(value, _, scale) => {
1130 match (self.conversion_type, value) {
1131 (
1132 ConversionType::DecFloatLower
1133 | ConversionType::SciFloatLower
1134 | ConversionType::SciFloatUpper
1135 | ConversionType::CompactFloatLower
1136 | ConversionType::CompactFloatUpper,
1137 Some(value),
1138 ) => self.format_decimal(string, &value.to_string(), *scale as i64),
1139 (
1140 ConversionType::StringLower | ConversionType::StringUpper,
1141 Some(value),
1142 ) => self.format_string(string, &value.to_string()),
1143 (t, None) if t.supports_decimal() => {
1144 self.format_string(string, "null")
1145 }
1146
1147 _ => {
1148 exec_err!(
1149 "Invalid conversion type: {:?} for Decimal128",
1150 self.conversion_type
1151 )
1152 }
1153 }
1154 }
1155 ScalarValue::Decimal256(value, _, scale) => {
1156 match (self.conversion_type, value) {
1157 (
1158 ConversionType::DecFloatLower
1159 | ConversionType::SciFloatLower
1160 | ConversionType::SciFloatUpper
1161 | ConversionType::CompactFloatLower
1162 | ConversionType::CompactFloatUpper,
1163 Some(value),
1164 ) => self.format_decimal(string, &value.to_string(), *scale as i64),
1165 (
1166 ConversionType::StringLower | ConversionType::StringUpper,
1167 Some(value),
1168 ) => self.format_string(string, &value.to_string()),
1169 (t, None) if t.supports_decimal() => {
1170 self.format_string(string, "null")
1171 }
1172
1173 _ => {
1174 exec_err!(
1175 "Invalid conversion type: {:?} for Decimal256",
1176 self.conversion_type
1177 )
1178 }
1179 }
1180 }
1181
1182 ScalarValue::Time32Second(value) => match (self.conversion_type, value) {
1183 (
1184 ConversionType::TimeLower(_) | ConversionType::TimeUpper(_),
1185 Some(value),
1186 ) => self.format_time(string, *value as i64 * 1000000000, &None),
1187 (
1188 ConversionType::StringLower | ConversionType::StringUpper,
1189 Some(value),
1190 ) => self.format_string(string, &value.to_string()),
1191 (t, None) if t.supports_time() => self.format_string(string, "null"),
1192 _ => {
1193 exec_err!(
1194 "Invalid conversion type: {:?} for Time32Second",
1195 self.conversion_type
1196 )
1197 }
1198 },
1199 ScalarValue::Time32Millisecond(value) => {
1200 match (self.conversion_type, value) {
1201 (
1202 ConversionType::TimeLower(_) | ConversionType::TimeUpper(_),
1203 Some(value),
1204 ) => self.format_time(string, *value as i64 * 1000000, &None),
1205 (
1206 ConversionType::StringLower | ConversionType::StringUpper,
1207 Some(value),
1208 ) => self.format_string(string, &value.to_string()),
1209 (t, None) if t.supports_time() => self.format_string(string, "null"),
1210 _ => {
1211 exec_err!(
1212 "Invalid conversion type: {:?} for Time32Millisecond",
1213 self.conversion_type
1214 )
1215 }
1216 }
1217 }
1218 ScalarValue::Time64Microsecond(value) => {
1219 match (self.conversion_type, value) {
1220 (
1221 ConversionType::TimeLower(_) | ConversionType::TimeUpper(_),
1222 Some(value),
1223 ) => self.format_time(string, *value * 1000, &None),
1224 (
1225 ConversionType::StringLower | ConversionType::StringUpper,
1226 Some(value),
1227 ) => self.format_string(string, &value.to_string()),
1228 (t, None) if t.supports_time() => self.format_string(string, "null"),
1229 _ => {
1230 exec_err!(
1231 "Invalid conversion type: {:?} for Time64Microsecond",
1232 self.conversion_type
1233 )
1234 }
1235 }
1236 }
1237 ScalarValue::Time64Nanosecond(value) => match (self.conversion_type, value) {
1238 (
1239 ConversionType::TimeLower(_) | ConversionType::TimeUpper(_),
1240 Some(value),
1241 ) => self.format_time(string, *value, &None),
1242 (
1243 ConversionType::StringLower | ConversionType::StringUpper,
1244 Some(value),
1245 ) => self.format_string(string, &value.to_string()),
1246 (t, None) if t.supports_time() => self.format_string(string, "null"),
1247 _ => {
1248 exec_err!(
1249 "Invalid conversion type: {:?} for Time64Nanosecond",
1250 self.conversion_type
1251 )
1252 }
1253 },
1254 ScalarValue::TimestampSecond(value, zone) => {
1255 match (self.conversion_type, value) {
1256 (
1257 ConversionType::TimeLower(_) | ConversionType::TimeUpper(_),
1258 Some(value),
1259 ) => self.format_time(string, value * 1000000000, zone),
1260 (
1261 ConversionType::StringLower | ConversionType::StringUpper,
1262 Some(value),
1263 ) => self.format_string(string, &value.to_string()),
1264 (t, None) if t.supports_time() => self.format_string(string, "null"),
1265 _ => {
1266 exec_err!(
1267 "Invalid conversion type: {:?} for TimestampSecond",
1268 self.conversion_type
1269 )
1270 }
1271 }
1272 }
1273 ScalarValue::TimestampMillisecond(value, zone) => {
1274 match (self.conversion_type, value) {
1275 (
1276 ConversionType::TimeLower(_) | ConversionType::TimeUpper(_),
1277 Some(value),
1278 ) => self.format_time(string, *value * 1000000, zone),
1279 (
1280 ConversionType::StringLower | ConversionType::StringUpper,
1281 Some(value),
1282 ) => self.format_string(string, &value.to_string()),
1283
1284 (t, None) if t.supports_time() => self.format_string(string, "null"),
1285 _ => {
1286 exec_err!(
1287 "Invalid conversion type: {:?} for TimestampMillisecond",
1288 self.conversion_type
1289 )
1290 }
1291 }
1292 }
1293 ScalarValue::TimestampMicrosecond(value, zone) => {
1294 match (self.conversion_type, value) {
1295 (
1296 ConversionType::TimeLower(_) | ConversionType::TimeUpper(_),
1297 Some(value),
1298 ) => self.format_time(string, value * 1000, zone),
1299 (
1300 ConversionType::StringLower | ConversionType::StringUpper,
1301 Some(value),
1302 ) => self.format_string(string, &value.to_string()),
1303 (t, None) if t.supports_time() => self.format_string(string, "null"),
1304 _ => {
1305 exec_err!(
1306 "Invalid conversion type: {:?} for timestampmicrosecond",
1307 self.conversion_type
1308 )
1309 }
1310 }
1311 }
1312
1313 ScalarValue::TimestampNanosecond(value, zone) => {
1314 match (self.conversion_type, value) {
1315 (
1316 ConversionType::TimeLower(_) | ConversionType::TimeUpper(_),
1317 Some(value),
1318 ) => self.format_time(string, *value, zone),
1319 (
1320 ConversionType::StringLower | ConversionType::StringUpper,
1321 Some(value),
1322 ) => self.format_string(string, &value.to_string()),
1323 (t, None) if t.supports_time() => self.format_string(string, "null"),
1324 _ => {
1325 exec_err!(
1326 "Invalid conversion type: {:?} for TimestampNanosecond",
1327 self.conversion_type
1328 )
1329 }
1330 }
1331 }
1332 ScalarValue::Date32(value) => match (self.conversion_type, value) {
1333 (
1334 ConversionType::TimeLower(_) | ConversionType::TimeUpper(_),
1335 Some(value),
1336 ) => self.format_date(string, *value as i64),
1337 (
1338 ConversionType::StringLower | ConversionType::StringUpper,
1339 Some(value),
1340 ) => self.format_string(string, &value.to_string()),
1341 (t, None) if t.supports_time() => self.format_string(string, "null"),
1342 _ => {
1343 exec_err!(
1344 "Invalid conversion type: {:?} for Date32",
1345 self.conversion_type
1346 )
1347 }
1348 },
1349 ScalarValue::Date64(value) => match (self.conversion_type, value) {
1350 (
1351 ConversionType::TimeLower(_) | ConversionType::TimeUpper(_),
1352 Some(value),
1353 ) => self.format_date(string, *value),
1354 (
1355 ConversionType::StringLower | ConversionType::StringUpper,
1356 Some(value),
1357 ) => self.format_string(string, &value.to_string()),
1358 (t, None) if t.supports_time() => self.format_string(string, "null"),
1359 _ => {
1360 exec_err!(
1361 "Invalid conversion type: {:?} for Date64",
1362 self.conversion_type
1363 )
1364 }
1365 },
1366 ScalarValue::Null => {
1367 let value = "null".to_string();
1368 self.format_string(string, &value)
1369 }
1370 _ => exec_err!("Invalid scalar value: {value}"),
1371 }
1372 }
1373
1374 fn format_integer<T>(
1375 &self,
1376 writer: &mut String,
1377 value: &Option<T>,
1378 type_name: &str,
1379 ) -> Result<()>
1380 where
1381 T: Copy + IntegerFormatValue,
1382 {
1383 let Some(value) = *value else {
1384 return if self.conversion_type.supports_integer() {
1385 self.format_string(writer, "null")
1386 } else {
1387 self.invalid_integer_conversion(type_name)
1388 };
1389 };
1390
1391 match self.conversion_type {
1392 ConversionType::DecInt => value.format_decimal(self, writer),
1393 ConversionType::HexIntLower
1394 | ConversionType::HexIntUpper
1395 | ConversionType::OctInt => {
1396 self.format_unsigned(writer, value.unsigned_bits())
1397 }
1398 ConversionType::CharLower | ConversionType::CharUpper => {
1399 self.format_char(writer, value.to_char()?)
1400 }
1401 ConversionType::StringLower | ConversionType::StringUpper => {
1402 self.format_string(writer, &value.decimal_string())
1403 }
1404 _ => self.invalid_integer_conversion(type_name),
1405 }
1406 }
1407
1408 fn invalid_integer_conversion<T>(&self, type_name: &str) -> Result<T> {
1409 exec_err!(
1410 "Invalid conversion type: {:?} for {}",
1411 self.conversion_type,
1412 type_name
1413 )
1414 }
1415
1416 fn format_hex_float(&self, writer: &mut String, value: f64) -> Result<()> {
1417 let (sign, raw_exponent, mantissa) = value.to_parts();
1419 let is_subnormal = raw_exponent == 0;
1420
1421 let precision = match self.precision {
1422 NumericParam::FromArgument => None,
1423 NumericParam::Literal(p) => Some(p),
1424 };
1425
1426 let mantissa_hex_digits = f64::MANTISSA_BITS.div_ceil(4); let should_normalize = is_subnormal
1430 && precision.is_some()
1431 && precision.unwrap() < mantissa_hex_digits as i32;
1432
1433 let (value, raw_exponent, mantissa) = if should_normalize {
1434 let value = value * f64::SCALEUP;
1435 let (_, raw_exponent, mantissa) = value.to_parts();
1436 (value, raw_exponent, mantissa)
1437 } else {
1438 (value, raw_exponent, mantissa)
1439 };
1440
1441 let mut temp = String::new();
1442
1443 let sign_char = if sign {
1444 "-"
1445 } else if self.force_sign {
1446 "+"
1447 } else if self.space_sign {
1448 " "
1449 } else {
1450 ""
1451 };
1452 match value.category() {
1453 FpCategory::Nan => {
1454 write!(&mut temp, "NaN")?;
1455 }
1456 FpCategory::Infinite => {
1457 write!(&mut temp, "{sign_char}Infinity")?;
1458 }
1459 FpCategory::Zero => {
1460 write!(&mut temp, "{sign_char}0x0.0p0")?;
1461 }
1462 _ => {
1463 let bias = i32::from(f64::EXPONENT_BIAS);
1464 let exponent = if is_subnormal && !should_normalize {
1467 1 - bias
1468 } else {
1469 raw_exponent as i32 - bias
1470 };
1471
1472 let final_mantissa = if let Some(p) = precision {
1474 if p == 0 {
1475 let shift_distance = f64::MANTISSA_BITS as i32 - 4; let shifted = mantissa >> shift_distance;
1479 let rounding_bits = mantissa & ((1u64 << shift_distance) - 1);
1480 let round_bit = 1u64 << (shift_distance - 1);
1481
1482 if rounding_bits > round_bit
1484 || (rounding_bits == round_bit && (shifted & 1) != 0)
1485 {
1486 (shifted + 1) << shift_distance
1487 } else {
1488 shifted << shift_distance
1489 }
1490 } else {
1491 let precision_bits = p * 4; let keep_bits = f64::MANTISSA_BITS as i32;
1494 let shift_distance = keep_bits - precision_bits;
1495
1496 if shift_distance > 0 {
1497 let shifted = mantissa >> shift_distance;
1498 let rounding_bits = mantissa & ((1u64 << shift_distance) - 1);
1499 let round_bit = 1u64 << (shift_distance - 1);
1500
1501 if rounding_bits > round_bit
1503 || (rounding_bits == round_bit && (shifted & 1) != 0)
1504 {
1505 (shifted + 1) << shift_distance
1506 } else {
1507 shifted << shift_distance
1508 }
1509 } else {
1510 mantissa
1511 }
1512 }
1513 } else {
1514 mantissa
1515 };
1516
1517 if is_subnormal && !should_normalize {
1518 if precision.is_some() {
1520 let full_hex = format!(
1522 "{:0width$x}",
1523 final_mantissa,
1524 width = mantissa_hex_digits as usize
1525 );
1526 write!(&mut temp, "{sign_char}0x0.{full_hex}p{exponent}")?;
1527 } else {
1528 let hex_digits = format!(
1530 "{:0width$x}",
1531 final_mantissa,
1532 width = mantissa_hex_digits as usize
1533 );
1534 write!(&mut temp, "{sign_char}0x0.{hex_digits}p{exponent}")?;
1535 }
1536 } else {
1537 if let Some(p) = precision {
1539 let p = if p == 0 { 1 } else { p };
1540 let hex_digits = format!("{final_mantissa:x}");
1541 let formatted_digits = if p as usize >= hex_digits.len() {
1542 format!("{:0<width$}", hex_digits, width = p as usize)
1544 } else {
1545 hex_digits[..p as usize].to_string()
1546 };
1547 write!(
1548 &mut temp,
1549 "{sign_char}0x1.{formatted_digits}p{exponent}"
1550 )?;
1551 } else {
1552 let mut hex_digits = format!("{final_mantissa:x}");
1554 hex_digits = trim_trailing_0s_hex(&hex_digits).to_owned();
1555 if hex_digits.is_empty() {
1556 write!(&mut temp, "{sign_char}0x1.0p{exponent}")?;
1557 } else {
1558 write!(&mut temp, "{sign_char}0x1.{hex_digits}p{exponent}")?;
1559 }
1560 }
1561 }
1562 if should_normalize {
1563 let (prefix, exp) = temp.split_once('p').unwrap();
1564 let iexp = exp.parse::<i32>().unwrap() - f64::SCALEUP_POWER as i32;
1565 temp = format!("{prefix}p{iexp}");
1566 }
1567 }
1568 };
1569
1570 if self.conversion_type.is_upper() {
1571 temp = temp.to_ascii_uppercase();
1572 }
1573
1574 let NumericParam::Literal(width) = self.width else {
1575 writer.push_str(&temp);
1576 return Ok(());
1577 };
1578 if self.left_adj {
1579 writer.push_str(&temp);
1580 for _ in temp.len()..width as usize {
1581 writer.push(' ');
1582 }
1583 } else if self.zero_pad && value.is_finite() {
1584 let delimiter = if self.conversion_type.is_upper() {
1585 "0X"
1586 } else {
1587 "0x"
1588 };
1589 let (prefix, suffix) = temp.split_once(delimiter).unwrap();
1590 writer.push_str(prefix);
1591 writer.push_str(delimiter);
1592 for _ in temp.len()..width as usize {
1593 writer.push('0');
1594 }
1595 writer.push_str(suffix);
1596 } else {
1597 while temp.len() < width as usize {
1598 temp = " ".to_owned() + &temp;
1599 }
1600 writer.push_str(&temp);
1601 };
1602 Ok(())
1603 }
1604
1605 fn format_char(&self, writer: &mut String, value: char) -> Result<()> {
1606 let upper = self.conversion_type.is_upper();
1607 match self.conversion_type {
1608 ConversionType::CharLower | ConversionType::CharUpper => {
1609 let NumericParam::Literal(width) = self.width else {
1610 if upper {
1611 writer.push(value.to_ascii_uppercase());
1612 } else {
1613 writer.push(value);
1614 }
1615 return Ok(());
1616 };
1617
1618 let start_len = writer.len();
1619 if self.left_adj {
1620 if upper {
1621 writer.push(value.to_ascii_uppercase());
1622 } else {
1623 writer.push(value);
1624 }
1625 while writer.len() - start_len < width as usize {
1626 writer.push(' ');
1627 }
1628 } else {
1629 while writer.len() - start_len + value.len_utf8() < width as usize {
1630 writer.push(' ');
1631 }
1632 if upper {
1633 writer.push(value.to_ascii_uppercase());
1634 } else {
1635 writer.push(value);
1636 }
1637 }
1638 Ok(())
1639 }
1640 _ => exec_err!(
1641 "Invalid conversion type: {:?} for char",
1642 self.conversion_type
1643 ),
1644 }
1645 }
1646
1647 fn format_boolean(&self, writer: &mut String, value: &Option<bool>) -> Result<()> {
1648 let value = value.unwrap_or(false);
1649
1650 let formatted = match self.conversion_type {
1651 ConversionType::BooleanUpper => {
1652 if value {
1653 "TRUE"
1654 } else {
1655 "FALSE"
1656 }
1657 }
1658 ConversionType::BooleanLower => {
1659 if value {
1660 "true"
1661 } else {
1662 "false"
1663 }
1664 }
1665 _ => {
1666 return exec_err!(
1667 "Invalid conversion type: {:?} for boolean array",
1668 self.conversion_type
1669 );
1670 }
1671 };
1672 self.format_str(writer, formatted)
1673 }
1674
1675 fn format_float(&self, writer: &mut String, value: f64) -> Result<()> {
1676 self.validate_grouping_separator()?;
1677
1678 let mut prefix = String::new();
1679 let mut suffix = String::new();
1680 let mut number = String::new();
1681 let upper = self.conversion_type.is_upper();
1682
1683 if value.is_sign_negative() {
1685 if self.negative_in_parentheses {
1686 prefix.push('(');
1687 suffix.push(')');
1688 } else {
1689 prefix.push('-');
1690 }
1691 } else if self.space_sign {
1692 prefix.push(' ');
1693 } else if self.force_sign {
1694 prefix.push('+');
1695 }
1696
1697 if value.is_finite() {
1698 let mut use_scientific = false;
1699 let mut strip_trailing_0s = false;
1700 let mut abs = value.abs();
1701 let mut exponent = abs.log10().floor() as i32;
1702 let mut precision = match self.precision {
1703 NumericParam::Literal(p) => p,
1704 _ => 6,
1705 };
1706 match self.conversion_type {
1707 ConversionType::DecFloatLower => {
1708 }
1710 ConversionType::SciFloatLower => {
1711 use_scientific = true;
1712 }
1713 ConversionType::SciFloatUpper => {
1714 use_scientific = true;
1715 }
1716 ConversionType::CompactFloatLower | ConversionType::CompactFloatUpper => {
1717 strip_trailing_0s = true;
1718 if precision == 0 {
1719 precision = 1;
1720 }
1721 let rounding_factor =
1724 10.0_f64.powf((precision - 1 - exponent) as f64);
1725 let rounded_fixed = (abs * rounding_factor).round();
1726 abs = rounded_fixed / rounding_factor;
1727 exponent = abs.log10().floor() as i32;
1728 if exponent < -4 || exponent >= precision {
1729 use_scientific = true;
1730 precision -= 1;
1731 } else {
1732 precision -= 1 + exponent;
1734 }
1735 }
1736 _ => {
1737 return exec_err!(
1738 "Invalid conversion type: {:?} for float",
1739 self.conversion_type
1740 );
1741 }
1742 }
1743
1744 if use_scientific {
1745 let mantissa = abs / 10.0_f64.powf(exponent as f64);
1747 let exp_char = if upper { 'E' } else { 'e' };
1748 number = format!("{mantissa:.prec$}", prec = precision as usize);
1749 if strip_trailing_0s {
1750 number = trim_trailing_0s(&number).to_owned();
1751 }
1752 number = format!("{number}{exp_char}{exponent:+03}");
1753 } else {
1754 number = format!("{abs:.prec$}", prec = precision as usize);
1755 if strip_trailing_0s {
1756 number = trim_trailing_0s(&number).to_owned();
1757 }
1758 if self.grouping_separator {
1759 number = insert_thousands_separator(&number);
1760 }
1761 }
1762 if self.alt_form && !number.contains('.') {
1763 number += ".";
1764 }
1765 } else {
1766 match self.conversion_type {
1768 ConversionType::DecFloatLower
1769 | ConversionType::SciFloatLower
1770 | ConversionType::CompactFloatLower => {
1771 if value.is_infinite() {
1772 number.push_str("Infinity")
1773 } else {
1774 number.push_str("NaN")
1775 }
1776 }
1777 ConversionType::SciFloatUpper | ConversionType::CompactFloatUpper => {
1778 if value.is_infinite() {
1779 number.push_str("INFINITY")
1780 } else {
1781 number.push_str("NAN")
1782 }
1783 }
1784 _ => {
1785 return exec_err!(
1786 "Invalid conversion type: {:?} for float",
1787 self.conversion_type
1788 );
1789 }
1790 }
1791 }
1792
1793 self.write_numeric_parts(writer, prefix, &number, &suffix, value.is_finite());
1794 Ok(())
1795 }
1796
1797 fn format_signed(&self, writer: &mut String, value: i64) -> Result<()> {
1798 let negative = value < 0;
1799 let abs_val = value.abs();
1800
1801 let (sign_prefix, sign_suffix) = if negative && self.negative_in_parentheses {
1802 ("(".to_owned(), ")".to_owned())
1803 } else if negative {
1804 ("-".to_owned(), "".to_owned())
1805 } else if self.force_sign {
1806 ("+".to_owned(), "".to_owned())
1807 } else if self.space_sign {
1808 (" ".to_owned(), "".to_owned())
1809 } else {
1810 ("".to_owned(), "".to_owned())
1811 };
1812
1813 let mut mod_spec = *self;
1814 mod_spec.width = match self.width {
1815 NumericParam::Literal(w) => NumericParam::Literal(
1816 w - sign_prefix.len() as i32 - sign_suffix.len() as i32,
1817 ),
1818 _ => NumericParam::FromArgument,
1819 };
1820 let mut formatted = String::new();
1821 mod_spec.format_unsigned(&mut formatted, abs_val as u64)?;
1822 let mut actual_number = &formatted[0..];
1824 let mut leading_spaces = &formatted[0..0];
1825 if let Some(first_non_space) = formatted.find(|c| c != ' ') {
1826 actual_number = &formatted[first_non_space..];
1827 leading_spaces = &formatted[0..first_non_space];
1828 }
1829 write!(
1830 writer,
1831 "{}{}{}{}",
1832 leading_spaces.to_owned(),
1833 sign_prefix,
1834 actual_number,
1835 sign_suffix
1836 )
1837 .map_err(|e| exec_datafusion_err!("Write error: {}", e))?;
1838 Ok(())
1839 }
1840
1841 fn format_unsigned(&self, writer: &mut String, value: u64) -> Result<()> {
1842 let mut s = String::new();
1843 let mut alt_prefix = "";
1844 match self.conversion_type {
1845 ConversionType::DecInt => {
1846 let num_str = format!("{value}");
1847 s = if self.grouping_separator {
1848 insert_thousands_separator(&num_str)
1849 } else {
1850 num_str
1851 };
1852 }
1853 ConversionType::HexIntLower => {
1854 alt_prefix = "0x";
1855 write!(&mut s, "{value:x}")
1856 .map_err(|e| exec_datafusion_err!("Write error: {}", e))?;
1857 }
1858 ConversionType::HexIntUpper => {
1859 alt_prefix = "0X";
1860 write!(&mut s, "{value:X}")
1861 .map_err(|e| exec_datafusion_err!("Write error: {}", e))?;
1862 }
1863 ConversionType::OctInt => {
1864 alt_prefix = "0";
1865 write!(&mut s, "{value:o}")
1866 .map_err(|e| exec_datafusion_err!("Write error: {}", e))?;
1867 }
1868 _ => {
1869 return exec_err!(
1870 "Invalid conversion type: {:?} for u64",
1871 self.conversion_type
1872 );
1873 }
1874 }
1875 let mut prefix = if self.alt_form {
1876 alt_prefix.to_owned()
1877 } else {
1878 String::new()
1879 };
1880
1881 let formatted = if let NumericParam::Literal(width) = self.width {
1882 if self.left_adj {
1883 let mut num_str = prefix + &s;
1884 while num_str.len() < width as usize {
1885 num_str.push(' ');
1886 }
1887 num_str
1888 } else if self.zero_pad {
1889 while prefix.len() + s.len() < width as usize {
1890 prefix.push('0');
1891 }
1892 prefix + &s
1893 } else {
1894 let mut num_str = prefix + &s;
1895 while num_str.len() < width as usize {
1896 num_str = " ".to_owned() + &num_str;
1897 }
1898 num_str
1899 }
1900 } else {
1901 prefix + &s
1902 };
1903 write!(writer, "{formatted}")
1904 .map_err(|e| exec_datafusion_err!("Write error: {}", e))?;
1905 Ok(())
1906 }
1907
1908 fn format_str(&self, writer: &mut String, value: &str) -> Result<()> {
1909 let precision: usize = match self.precision {
1911 NumericParam::Literal(p) => p,
1912 _ => i32::MAX,
1913 }
1914 .try_into()
1915 .unwrap_or_default();
1916 let content_len = {
1917 let mut content_len = precision.min(value.len());
1918 while !value.is_char_boundary(content_len) {
1919 content_len -= 1;
1920 }
1921 content_len
1922 };
1923 let content = &value[..content_len];
1924
1925 if let NumericParam::Literal(width) = self.width {
1928 let start_len = writer.len();
1929 if self.left_adj {
1930 writer.push_str(content);
1931 while writer.len() - start_len < width as usize {
1932 writer.push(' ');
1933 }
1934 } else {
1935 while writer.len() - start_len + content.len() < width as usize {
1936 writer.push(' ');
1937 }
1938 writer.push_str(content);
1939 }
1940 } else {
1941 writer.push_str(content);
1942 }
1943 Ok(())
1944 }
1945
1946 fn format_string(&self, writer: &mut String, value: &str) -> Result<()> {
1947 if self.conversion_type.is_upper() {
1948 let upper = value.to_ascii_uppercase();
1949 self.format_str(writer, &upper)
1950 } else {
1951 self.format_str(writer, value)
1952 }
1953 }
1954
1955 fn format_decimal(&self, writer: &mut String, value: &str, scale: i64) -> Result<()> {
1956 self.validate_grouping_separator()?;
1957
1958 let mut prefix = String::new();
1959 let mut suffix = String::new();
1960 let upper = self.conversion_type.is_upper();
1961
1962 let decimal = value
1964 .parse::<BigInt>()
1965 .map_err(|e| exec_datafusion_err!("Failed to parse decimal: {}", e))?;
1966 let decimal = BigDecimal::from_bigint(decimal, scale);
1967
1968 let is_negative = decimal.sign() == Sign::Minus;
1970 let abs_decimal = decimal.abs();
1971
1972 if is_negative {
1973 if self.negative_in_parentheses {
1974 prefix.push('(');
1975 suffix.push(')');
1976 } else {
1977 prefix.push('-');
1978 }
1979 } else if self.space_sign {
1980 prefix.push(' ');
1981 } else if self.force_sign {
1982 prefix.push('+');
1983 }
1984
1985 let exp_symb = if upper { 'E' } else { 'e' };
1986 let mut strip_trailing_0s = false;
1987
1988 let mut precision = match self.precision {
1990 NumericParam::Literal(p) => p,
1991 _ => 6,
1992 };
1993
1994 let number = match self.conversion_type {
1995 ConversionType::DecFloatLower => {
1996 let mut n = self.format_decimal_fixed(
1998 &abs_decimal,
1999 precision,
2000 strip_trailing_0s,
2001 )?;
2002 if self.grouping_separator {
2003 n = insert_thousands_separator(&n);
2004 }
2005 n
2006 }
2007 ConversionType::SciFloatLower => self.format_decimal_scientific(
2008 &abs_decimal,
2009 precision,
2010 'e',
2011 strip_trailing_0s,
2012 )?,
2013 ConversionType::SciFloatUpper => self.format_decimal_scientific(
2014 &abs_decimal,
2015 precision,
2016 'E',
2017 strip_trailing_0s,
2018 )?,
2019 ConversionType::CompactFloatLower | ConversionType::CompactFloatUpper => {
2020 strip_trailing_0s = true;
2021 if precision == 0 {
2022 precision = 1;
2023 }
2024 let log10_val = abs_decimal.to_f64().map(|f| f.log10()).unwrap_or(0.0);
2026 if log10_val < -4.0 || log10_val >= precision as f64 {
2027 self.format_decimal_scientific(
2028 &abs_decimal,
2029 precision - 1,
2030 exp_symb,
2031 strip_trailing_0s,
2032 )?
2033 } else {
2034 let mut n = self.format_decimal_fixed(
2035 &abs_decimal,
2036 precision - 1 - log10_val.floor() as i32,
2037 strip_trailing_0s,
2038 )?;
2039 if self.grouping_separator {
2040 n = insert_thousands_separator(&n);
2041 }
2042 n
2043 }
2044 }
2045 _ => {
2046 return exec_err!(
2047 "Invalid conversion type: {:?} for decimal",
2048 self.conversion_type
2049 );
2050 }
2051 };
2052
2053 self.write_numeric_parts(writer, prefix, &number, &suffix, true);
2054 Ok(())
2055 }
2056
2057 fn format_decimal_fixed(
2058 &self,
2059 decimal: &BigDecimal,
2060 precision: i32,
2061 strip_trailing_0s: bool,
2062 ) -> Result<String> {
2063 if precision <= 0 {
2064 Ok(decimal.round(0).to_string())
2065 } else {
2066 let scaled = decimal.round(precision as i64);
2068 let mut number = scaled.to_string();
2069 if strip_trailing_0s {
2070 number = trim_trailing_0s(&number).to_owned();
2071 }
2072 Ok(number)
2073 }
2074 }
2075
2076 fn format_decimal_scientific(
2077 &self,
2078 decimal: &BigDecimal,
2079 precision: i32,
2080 exp_char: char,
2081 strip_trailing_0s: bool,
2082 ) -> Result<String> {
2083 let float_val = decimal.to_f64().unwrap_or(0.0);
2085 if float_val == 0.0 {
2086 return Ok(format!("0{exp_char}+00"));
2087 }
2088
2089 let abs_val = float_val.abs();
2090 let exponent = abs_val.log10().floor() as i32;
2091 let mantissa = abs_val / 10.0_f64.powf(exponent as f64);
2092
2093 let mut number = if precision <= 0 {
2094 format!("{mantissa:.0}")
2095 } else {
2096 format!("{mantissa:.prec$}", prec = precision as usize)
2097 };
2098
2099 if strip_trailing_0s {
2100 number = trim_trailing_0s(&number).to_owned();
2101 }
2102
2103 Ok(format!("{number}{exp_char}{exponent:+03}"))
2104 }
2105
2106 fn format_time(
2107 &self,
2108 writer: &mut String,
2109 timestamp_nanos: i64,
2110 timezone: &Option<Arc<str>>,
2111 ) -> Result<()> {
2112 let upper = self.conversion_type.is_upper();
2113 match &self.conversion_type {
2114 ConversionType::TimeLower(time_format)
2115 | ConversionType::TimeUpper(time_format) => {
2116 let formatted =
2117 self.format_time_component(timestamp_nanos, *time_format, timezone)?;
2118 let result = if upper {
2119 formatted.to_uppercase()
2120 } else {
2121 formatted
2122 };
2123 write!(writer, "{result}")
2124 .map_err(|e| exec_datafusion_err!("Write error: {}", e))?;
2125 Ok(())
2126 }
2127 _ => exec_err!(
2128 "Invalid conversion type for time: {:?}",
2129 self.conversion_type
2130 ),
2131 }
2132 }
2133
2134 fn format_date(&self, writer: &mut String, date_days: i64) -> Result<()> {
2135 let timestamp_nanos = date_days * 24 * 60 * 60 * 1_000_000_000;
2137 self.format_time(writer, timestamp_nanos, &None)
2138 }
2139
2140 fn format_time_component(
2141 &self,
2142 timestamp_nanos: i64,
2143 time_format: TimeFormat,
2144 _timezone: &Option<Arc<str>>,
2145 ) -> Result<String> {
2146 let secs = timestamp_nanos / 1_000_000_000;
2148 let nanos = (timestamp_nanos % 1_000_000_000) as u32;
2149
2150 let dt = DateTime::<Utc>::from_timestamp(secs, nanos).ok_or_else(|| {
2152 exec_datafusion_err!("Invalid timestamp: {}", timestamp_nanos)
2153 })?;
2154
2155 match time_format {
2156 TimeFormat::HUpper => Ok(format!("{:02}", dt.hour())),
2157 TimeFormat::IUpper => {
2158 let hour_12 = match dt.hour12() {
2159 (true, h) => h, (false, h) => h, };
2162 Ok(format!("{hour_12:02}"))
2163 }
2164 TimeFormat::KLower => Ok(format!("{}", dt.hour())),
2165 TimeFormat::LLower => {
2166 let hour_12 = match dt.hour12() {
2167 (true, h) => h, (false, h) => h, };
2170 Ok(format!("{hour_12}"))
2171 }
2172 TimeFormat::MUpper => Ok(format!("{:02}", dt.minute())),
2173 TimeFormat::SUpper => Ok(format!("{:02}", dt.second())),
2174 TimeFormat::LUpper => Ok(format!("{:03}", dt.timestamp_millis() % 1000)),
2175 TimeFormat::NUpper => Ok(format!("{:09}", dt.nanosecond())),
2176 TimeFormat::PLower => {
2177 let (is_pm, _) = dt.hour12();
2178 Ok(if is_pm {
2179 "pm".to_string()
2180 } else {
2181 "am".to_string()
2182 })
2183 }
2184 TimeFormat::ZLower => Ok("+0000".to_string()), TimeFormat::ZUpper => Ok("UTC".to_string()), TimeFormat::SLower => Ok(format!("{}", dt.timestamp())),
2187 TimeFormat::QUpper => Ok(format!("{}", dt.timestamp_millis())),
2188 TimeFormat::BUpper => Ok(dt.format("%B").to_string()), TimeFormat::BLower => Ok(dt.format("%b").to_string()), TimeFormat::AUpper => Ok(dt.format("%A").to_string()), TimeFormat::ALower => Ok(dt.format("%a").to_string()), TimeFormat::CUpper => Ok(format!("{:02}", dt.year() / 100)),
2193 TimeFormat::YUpper => Ok(format!("{:04}", dt.year())),
2194 TimeFormat::YLower => Ok(format!("{:02}", dt.year() % 100)),
2195 TimeFormat::JLower => Ok(format!("{:03}", dt.ordinal())), TimeFormat::MLower => Ok(format!("{:02}", dt.month())),
2197 TimeFormat::DLower => Ok(format!("{:02}", dt.day())),
2198 TimeFormat::ELower => Ok(format!("{}", dt.day())),
2199 TimeFormat::RUpper => Ok(dt.format("%H:%M").to_string()),
2200 TimeFormat::TUpper => Ok(dt.format("%H:%M:%S").to_string()),
2201 TimeFormat::RLower => {
2202 let (is_pm, hour_12) = dt.hour12();
2203 let am_pm = if is_pm { "PM" } else { "AM" };
2204 Ok(format!(
2205 "{:02}:{:02}:{:02} {}",
2206 hour_12,
2207 dt.minute(),
2208 dt.second(),
2209 am_pm
2210 ))
2211 }
2212 TimeFormat::DUpper => Ok(dt.format("%m/%d/%y").to_string()),
2213 TimeFormat::FUpper => Ok(dt.format("%Y-%m-%d").to_string()),
2214 TimeFormat::CLower => Ok(dt.format("%a %b %d %H:%M:%S UTC %Y").to_string()),
2215 }
2216 }
2217
2218 fn write_numeric_parts(
2219 &self,
2220 writer: &mut String,
2221 mut prefix: String,
2222 number: &str,
2223 suffix: &str,
2224 zero_pad_allowed: bool,
2225 ) {
2226 let NumericParam::Literal(width) = self.width else {
2228 writer.push_str(&prefix);
2229 writer.push_str(number);
2230 writer.push_str(suffix);
2231 return;
2232 };
2233
2234 if self.left_adj {
2235 let mut full_num = prefix + number + suffix;
2236 while full_num.len() < width as usize {
2237 full_num.push(' ');
2238 }
2239 writer.push_str(&full_num);
2240 } else if self.zero_pad && zero_pad_allowed {
2241 while prefix.len() + number.len() + suffix.len() < width as usize {
2242 prefix.push('0');
2243 }
2244 writer.push_str(&prefix);
2245 writer.push_str(number);
2246 writer.push_str(suffix);
2247 } else {
2248 let mut full_num = prefix + number + suffix;
2249 while full_num.len() < width as usize {
2250 full_num = " ".to_owned() + &full_num;
2251 }
2252 writer.push_str(&full_num);
2253 }
2254 }
2255}
2256
2257trait FloatFormattable: std::fmt::Display {
2258 fn category(&self) -> FpCategory;
2259
2260 fn spark_string(&self) -> String {
2261 match self.category() {
2262 FpCategory::Nan => "NaN".to_string(),
2263 FpCategory::Infinite => {
2264 if self.negative() {
2265 "-Infinity".to_string()
2266 } else {
2267 "Infinity".to_string()
2268 }
2269 }
2270 _ => self.to_string(),
2271 }
2272 }
2273 fn negative(&self) -> bool;
2274}
2275
2276impl FloatFormattable for f32 {
2277 fn category(&self) -> FpCategory {
2278 self.classify()
2279 }
2280
2281 fn negative(&self) -> bool {
2282 self.is_sign_negative()
2283 }
2284}
2285
2286impl FloatFormattable for f64 {
2287 fn category(&self) -> FpCategory {
2288 self.classify()
2289 }
2290
2291 fn negative(&self) -> bool {
2292 self.is_sign_negative()
2293 }
2294}
2295
2296trait FloatBits: FloatFormattable {
2297 const MANTISSA_BITS: u8;
2298 const EXPONENT_BIAS: u16;
2299 const SCALEUP_POWER: u8;
2300 const SCALEUP: Self;
2301
2302 fn to_parts(&self) -> (bool, u16, u64);
2303}
2304
2305impl FloatBits for f64 {
2306 const MANTISSA_BITS: u8 = 52;
2307 const EXPONENT_BIAS: u16 = 1023;
2308 const SCALEUP_POWER: u8 = 54;
2309 const SCALEUP: f64 = (1_i64 << Self::SCALEUP_POWER) as f64;
2310
2311 fn to_parts(&self) -> (bool, u16, u64) {
2312 let bits = self.to_bits();
2313 let sign: bool = (bits >> 63) == 1;
2314 let exponent = ((bits >> 52) & 0x7FF) as u16;
2315 let mantissa = bits & 0x000F_FFFF_FFFF_FFFF;
2316 (sign, exponent, mantissa)
2317 }
2318}
2319
2320fn insert_thousands_separator(number: &str) -> String {
2323 let (int_part, frac_part) = match number.find('.') {
2324 Some(pos) => (&number[..pos], &number[pos..]),
2325 None => (number, ""),
2326 };
2327 let mut result = String::with_capacity(number.len() + number.len() / 3);
2328 for (i, c) in int_part.char_indices() {
2329 if i > 0 && (int_part.len() - i) % 3 == 0 {
2330 result.push(',');
2331 }
2332 result.push(c);
2333 }
2334 result.push_str(frac_part);
2335 result
2336}
2337
2338fn trim_trailing_0s(number: &str) -> &str {
2339 if number.contains('.') {
2340 for (i, c) in number.chars().rev().enumerate() {
2341 if c != '0' {
2342 return &number[..number.len() - i];
2343 }
2344 }
2345 }
2346 number
2347}
2348
2349fn trim_trailing_0s_hex(number: &str) -> &str {
2350 for (i, c) in number.chars().rev().enumerate() {
2351 if c != '0' {
2352 return &number[..number.len() - i];
2353 }
2354 }
2355 number
2356}
2357
2358#[cfg(test)]
2359mod tests {
2360 use super::*;
2361 use crate::function::utils::test::test_scalar_function;
2362 use arrow::array::StringArray;
2363 use arrow::datatypes::{DataType::Utf8, i256};
2364
2365 #[test]
2366 fn test_format_string_nullability() -> Result<()> {
2367 let func = FormatStringFunc::new();
2368 let nullable_format: FieldRef = Arc::new(Field::new("fmt", Utf8, true));
2369
2370 let out_nullable = func.return_field_from_args(ReturnFieldArgs {
2371 arg_fields: &[nullable_format],
2372 scalar_arguments: &[None],
2373 })?;
2374
2375 assert!(
2376 out_nullable.is_nullable(),
2377 "format_string(fmt, ...) should be nullable when fmt is nullable"
2378 );
2379 let non_nullable_format: FieldRef = Arc::new(Field::new("fmt", Utf8, false));
2380
2381 let out_non_nullable = func.return_field_from_args(ReturnFieldArgs {
2382 arg_fields: &[non_nullable_format],
2383 scalar_arguments: &[None],
2384 })?;
2385
2386 assert!(
2387 !out_non_nullable.is_nullable(),
2388 "format_string(fmt, ...) should NOT be nullable when fmt is NOT nullable"
2389 );
2390
2391 Ok(())
2392 }
2393
2394 #[test]
2395 fn test_format_char_invalid_codepoint_errors() {
2396 use arrow::datatypes::Field;
2397 use datafusion_common::config::ConfigOptions;
2398
2399 let func = FormatStringFunc::new();
2400 let cases: Vec<(&str, ScalarValue)> = vec![
2405 ("Int8(-1)", ScalarValue::Int8(Some(-1))),
2406 ("Int16(-1)", ScalarValue::Int16(Some(-1))),
2407 ("Int16(-10000)", ScalarValue::Int16(Some(-10000))),
2408 ("Int32(-1)", ScalarValue::Int32(Some(-1))),
2409 ("Int32(0x110000)", ScalarValue::Int32(Some(0x110000))),
2410 ("Int64(0x1FFFFFFFF)", ScalarValue::Int64(Some(0x1FFFFFFFF))),
2411 ("Int64(-1)", ScalarValue::Int64(Some(-1))),
2412 ("UInt16(0xD800)", ScalarValue::UInt16(Some(0xD800))),
2413 ("UInt32(0x110000)", ScalarValue::UInt32(Some(0x110000))),
2414 (
2415 "UInt64(0x1_0000_0000)",
2416 ScalarValue::UInt64(Some(0x1_0000_0000)),
2417 ),
2418 ];
2419
2420 for (label, value) in cases {
2421 let fmt = ColumnarValue::Scalar(ScalarValue::Utf8(Some("[%c]".to_string())));
2422 let arg_data_type = value.data_type();
2423 let arg = ColumnarValue::Scalar(value);
2424 let arg_fields = vec![
2425 Arc::new(Field::new("fmt", Utf8, false)),
2426 Arc::new(Field::new("v", arg_data_type, false)),
2427 ];
2428 let res = func.invoke_with_args(ScalarFunctionArgs {
2429 args: vec![fmt, arg],
2430 number_rows: 1,
2431 arg_fields,
2432 return_field: Arc::new(Field::new("o", Utf8, false)),
2433 config_options: Arc::new(ConfigOptions::default()),
2434 });
2435 assert!(
2436 res.is_err(),
2437 "format_string('[%c]', {label}) should error, got Ok"
2438 );
2439 let err = res.unwrap_err().to_string();
2440 assert!(
2441 err.contains("invalid Unicode scalar value for %c"),
2442 "unexpected error for {label}: {err}"
2443 );
2444 }
2445 }
2446
2447 #[test]
2448 fn test_format_char_valid_codepoint_succeeds() {
2449 test_scalar_function!(
2450 FormatStringFunc::new(),
2451 vec![
2452 ColumnarValue::Scalar(ScalarValue::Utf8(Some("[%c]".to_string()))),
2453 ColumnarValue::Scalar(ScalarValue::Int32(Some(0x1F680))),
2454 ],
2455 Ok(Some("[\u{1F680}]")),
2456 &str,
2457 Utf8,
2458 StringArray
2459 );
2460 test_scalar_function!(
2461 FormatStringFunc::new(),
2462 vec![
2463 ColumnarValue::Scalar(ScalarValue::Utf8(Some("[%c]".to_string()))),
2464 ColumnarValue::Scalar(ScalarValue::UInt32(Some(0x10FFFF))),
2465 ],
2466 Ok(Some("[\u{10FFFF}]")),
2467 &str,
2468 Utf8,
2469 StringArray
2470 );
2471 test_scalar_function!(
2472 FormatStringFunc::new(),
2473 vec![
2474 ColumnarValue::Scalar(ScalarValue::Utf8(Some("[%c]".to_string()))),
2475 ColumnarValue::Scalar(ScalarValue::Int16(Some(65))),
2476 ],
2477 Ok(Some("[A]")),
2478 &str,
2479 Utf8,
2480 StringArray
2481 );
2482 test_scalar_function!(
2485 FormatStringFunc::new(),
2486 vec![
2487 ColumnarValue::Scalar(ScalarValue::Utf8(Some("[%c]".to_string()))),
2488 ColumnarValue::Scalar(ScalarValue::Int8(Some(97))),
2489 ],
2490 Ok(Some("[a]")),
2491 &str,
2492 Utf8,
2493 StringArray
2494 );
2495 test_scalar_function!(
2496 FormatStringFunc::new(),
2497 vec![
2498 ColumnarValue::Scalar(ScalarValue::Utf8(Some("[%c]".to_string()))),
2499 ColumnarValue::Scalar(ScalarValue::UInt8(Some(255))),
2500 ],
2501 Ok(Some("[\u{00FF}]")),
2502 &str,
2503 Utf8,
2504 StringArray
2505 );
2506 }
2507
2508 #[test]
2509 fn test_integer_formatting_across_widths() -> Result<()> {
2510 let cases = [
2511 (
2512 ScalarValue::Int8(Some(-1)),
2513 "%d|%x|%o|%s",
2514 4,
2515 "-1|ff|377|-1",
2516 ),
2517 (
2518 ScalarValue::Int16(Some(-1)),
2519 "%d|%x|%o|%s",
2520 4,
2521 "-1|ffff|177777|-1",
2522 ),
2523 (
2524 ScalarValue::Int32(Some(-1)),
2525 "%d|%x|%o|%s",
2526 4,
2527 "-1|ffffffff|37777777777|-1",
2528 ),
2529 (
2530 ScalarValue::Int64(Some(-1)),
2531 "%d|%x|%o|%s",
2532 4,
2533 "-1|ffffffffffffffff|1777777777777777777777|-1",
2534 ),
2535 (
2536 ScalarValue::UInt8(Some(255)),
2537 "%d|%x|%o|%s|%c",
2538 5,
2539 "255|ff|377|255|ÿ",
2540 ),
2541 (
2542 ScalarValue::UInt16(Some(65535)),
2543 "%d|%x|%o|%s",
2544 4,
2545 "65535|ffff|177777|65535",
2546 ),
2547 (
2548 ScalarValue::UInt32(Some(u32::MAX)),
2549 "%d|%x|%o|%s",
2550 4,
2551 "4294967295|ffffffff|37777777777|4294967295",
2552 ),
2553 (
2554 ScalarValue::UInt64(Some(u64::MAX)),
2555 "%d|%x|%o|%s",
2556 4,
2557 "18446744073709551615|ffffffffffffffff|1777777777777777777777|18446744073709551615",
2558 ),
2559 (
2560 ScalarValue::Int32(None),
2561 "%d|%x|%o|%s|%c",
2562 5,
2563 "null|null|null|null|null",
2564 ),
2565 ];
2566
2567 for (value, fmt, arg_count, expected) in cases {
2568 let data_types = vec![value.data_type(); arg_count];
2569 let formatter = Formatter::parse(fmt, &data_types)?;
2570 let args = vec![value; arg_count];
2571 assert_eq!(formatter.format(&args)?, expected, "{fmt}");
2572 }
2573 Ok(())
2574 }
2575
2576 #[test]
2577 fn test_insert_thousands_separator() {
2578 assert_eq!(insert_thousands_separator("1234567.89"), "1,234,567.89");
2579 assert_eq!(insert_thousands_separator("123.45"), "123.45");
2580 assert_eq!(insert_thousands_separator("1234"), "1,234");
2581 assert_eq!(insert_thousands_separator("12"), "12");
2582 assert_eq!(insert_thousands_separator("0.5"), "0.5");
2583 assert_eq!(
2584 insert_thousands_separator("1234567890.1234"),
2585 "1,234,567,890.1234"
2586 );
2587 assert_eq!(insert_thousands_separator("1000"), "1,000");
2588 assert_eq!(insert_thousands_separator("100"), "100");
2589 }
2590
2591 #[test]
2592 fn test_grouping_separator_float() -> Result<()> {
2593 test_scalar_function!(
2594 FormatStringFunc::new(),
2595 vec![
2596 ColumnarValue::Scalar(ScalarValue::Utf8(Some("%,.2f".to_string()))),
2597 ColumnarValue::Scalar(ScalarValue::Float64(Some(1234567.89))),
2598 ],
2599 Ok(Some("1,234,567.89")),
2600 &str,
2601 Utf8,
2602 StringArray
2603 );
2604 Ok(())
2605 }
2606
2607 #[test]
2608 fn test_grouping_separator_decimal() -> Result<()> {
2609 test_scalar_function!(
2610 FormatStringFunc::new(),
2611 vec![
2612 ColumnarValue::Scalar(ScalarValue::Utf8(Some("%,.2f".to_string()))),
2613 ColumnarValue::Scalar(ScalarValue::Decimal128(Some(123456789), 10, 2)),
2614 ],
2615 Ok(Some("1,234,567.89")),
2616 &str,
2617 Utf8,
2618 StringArray
2619 );
2620 Ok(())
2621 }
2622
2623 #[test]
2624 fn test_grouping_separator_scientific_float() -> Result<()> {
2625 test_scalar_function!(
2627 FormatStringFunc::new(),
2628 vec![
2629 ColumnarValue::Scalar(ScalarValue::Utf8(Some("%,e".to_string()))),
2630 ColumnarValue::Scalar(ScalarValue::Float64(Some(1234567.89))),
2631 ],
2632 Err(DataFusionError::Execution(
2633 "Grouping separator ',' flag is not compatible with scientific notation conversion 'e'".to_string(),
2634 )),
2635 &str,
2636 Utf8,
2637 StringArray
2638 );
2639 test_scalar_function!(
2641 FormatStringFunc::new(),
2642 vec![
2643 ColumnarValue::Scalar(ScalarValue::Utf8(Some("%,E".to_string()))),
2644 ColumnarValue::Scalar(ScalarValue::Float64(Some(1234567.89))),
2645 ],
2646 Err(DataFusionError::Execution(
2647 "Grouping separator ',' flag is not compatible with scientific notation conversion 'E'".to_string(),
2648 )),
2649 &str,
2650 Utf8,
2651 StringArray
2652 );
2653 test_scalar_function!(
2655 FormatStringFunc::new(),
2656 vec![
2657 ColumnarValue::Scalar(ScalarValue::Utf8(Some("%,.0e".to_string()))),
2658 ColumnarValue::Scalar(ScalarValue::Float64(Some(1234567.89))),
2659 ],
2660 Err(DataFusionError::Execution(
2661 "Grouping separator ',' flag is not compatible with scientific notation conversion 'e'".to_string(),
2662 )),
2663 &str,
2664 Utf8,
2665 StringArray
2666 );
2667 Ok(())
2668 }
2669
2670 #[test]
2671 fn test_grouping_separator_compact_float() -> Result<()> {
2672 test_scalar_function!(
2674 FormatStringFunc::new(),
2675 vec![
2676 ColumnarValue::Scalar(ScalarValue::Utf8(Some("%,g".to_string()))),
2677 ColumnarValue::Scalar(ScalarValue::Float64(Some(1234567.89))),
2678 ],
2679 Ok(Some("1.23457e+06")),
2680 &str,
2681 Utf8,
2682 StringArray
2683 );
2684 test_scalar_function!(
2686 FormatStringFunc::new(),
2687 vec![
2688 ColumnarValue::Scalar(ScalarValue::Utf8(Some("%,g".to_string()))),
2689 ColumnarValue::Scalar(ScalarValue::Float64(Some(12345.6))),
2690 ],
2691 Ok(Some("12,345.6")),
2692 &str,
2693 Utf8,
2694 StringArray
2695 );
2696 test_scalar_function!(
2698 FormatStringFunc::new(),
2699 vec![
2700 ColumnarValue::Scalar(ScalarValue::Utf8(Some("%,.0g".to_string()))),
2701 ColumnarValue::Scalar(ScalarValue::Float64(Some(1234567.89))),
2702 ],
2703 Ok(Some("1e+06")),
2704 &str,
2705 Utf8,
2706 StringArray
2707 );
2708 test_scalar_function!(
2710 FormatStringFunc::new(),
2711 vec![
2712 ColumnarValue::Scalar(ScalarValue::Utf8(Some("%,G".to_string()))),
2713 ColumnarValue::Scalar(ScalarValue::Float64(Some(1234567.89))),
2714 ],
2715 Ok(Some("1.23457E+06")),
2716 &str,
2717 Utf8,
2718 StringArray
2719 );
2720 Ok(())
2721 }
2722
2723 #[test]
2724 fn test_grouping_separator_scientific_decimal() -> Result<()> {
2725 test_scalar_function!(
2727 FormatStringFunc::new(),
2728 vec![
2729 ColumnarValue::Scalar(ScalarValue::Utf8(Some("%,e".to_string()))),
2730 ColumnarValue::Scalar(ScalarValue::Decimal128(Some(123456789), 10, 2)),
2731 ],
2732 Err(DataFusionError::Execution(
2733 "Grouping separator ',' flag is not compatible with scientific notation conversion 'e'".to_string(),
2734 )),
2735 &str,
2736 Utf8,
2737 StringArray
2738 );
2739 test_scalar_function!(
2741 FormatStringFunc::new(),
2742 vec![
2743 ColumnarValue::Scalar(ScalarValue::Utf8(Some("%,.0e".to_string()))),
2744 ColumnarValue::Scalar(ScalarValue::Decimal128(Some(123456789), 10, 2)),
2745 ],
2746 Err(DataFusionError::Execution(
2747 "Grouping separator ',' flag is not compatible with scientific notation conversion 'e'".to_string(),
2748 )),
2749 &str,
2750 Utf8,
2751 StringArray
2752 );
2753 Ok(())
2754 }
2755
2756 #[test]
2757 fn test_grouping_separator_compact_decimal() -> Result<()> {
2758 test_scalar_function!(
2760 FormatStringFunc::new(),
2761 vec![
2762 ColumnarValue::Scalar(ScalarValue::Utf8(Some("%,g".to_string()))),
2763 ColumnarValue::Scalar(ScalarValue::Decimal128(Some(123456789), 10, 2)),
2764 ],
2765 Ok(Some("1.23457e+06")),
2766 &str,
2767 Utf8,
2768 StringArray
2769 );
2770 test_scalar_function!(
2772 FormatStringFunc::new(),
2773 vec![
2774 ColumnarValue::Scalar(ScalarValue::Utf8(Some("%,g".to_string()))),
2775 ColumnarValue::Scalar(ScalarValue::Decimal128(Some(1234560), 10, 2)),
2776 ],
2777 Ok(Some("12,345.6")),
2778 &str,
2779 Utf8,
2780 StringArray
2781 );
2782 test_scalar_function!(
2784 FormatStringFunc::new(),
2785 vec![
2786 ColumnarValue::Scalar(ScalarValue::Utf8(Some("%,.0g".to_string()))),
2787 ColumnarValue::Scalar(ScalarValue::Decimal128(Some(123456789), 10, 2)),
2788 ],
2789 Ok(Some("1e+06")),
2790 &str,
2791 Utf8,
2792 StringArray
2793 );
2794 Ok(())
2795 }
2796
2797 #[test]
2798 fn test_grouping_separator_width_sign_float() -> Result<()> {
2799 test_scalar_function!(
2801 FormatStringFunc::new(),
2802 vec![
2803 ColumnarValue::Scalar(ScalarValue::Utf8(Some("%0,15.2f".to_string()))),
2804 ColumnarValue::Scalar(ScalarValue::Float64(Some(1234567.89))),
2805 ],
2806 Ok(Some("0001,234,567.89")),
2807 &str,
2808 Utf8,
2809 StringArray
2810 );
2811 test_scalar_function!(
2813 FormatStringFunc::new(),
2814 vec![
2815 ColumnarValue::Scalar(ScalarValue::Utf8(Some("%+,15.2f".to_string()))),
2816 ColumnarValue::Scalar(ScalarValue::Float64(Some(1234567.89))),
2817 ],
2818 Ok(Some(" +1,234,567.89")),
2819 &str,
2820 Utf8,
2821 StringArray
2822 );
2823 test_scalar_function!(
2825 FormatStringFunc::new(),
2826 vec![
2827 ColumnarValue::Scalar(ScalarValue::Utf8(Some("%-,15.2f".to_string()))),
2828 ColumnarValue::Scalar(ScalarValue::Float64(Some(1234567.89))),
2829 ],
2830 Ok(Some("1,234,567.89 ")),
2831 &str,
2832 Utf8,
2833 StringArray
2834 );
2835 Ok(())
2836 }
2837
2838 #[test]
2839 fn test_grouping_separator_width_sign_decimal() -> Result<()> {
2840 test_scalar_function!(
2842 FormatStringFunc::new(),
2843 vec![
2844 ColumnarValue::Scalar(ScalarValue::Utf8(Some("%0,15.2f".to_string()))),
2845 ColumnarValue::Scalar(ScalarValue::Decimal128(Some(123456789), 10, 2)),
2846 ],
2847 Ok(Some("0001,234,567.89")),
2848 &str,
2849 Utf8,
2850 StringArray
2851 );
2852 test_scalar_function!(
2854 FormatStringFunc::new(),
2855 vec![
2856 ColumnarValue::Scalar(ScalarValue::Utf8(Some("%+,15.2f".to_string()))),
2857 ColumnarValue::Scalar(ScalarValue::Decimal128(Some(123456789), 10, 2)),
2858 ],
2859 Ok(Some(" +1,234,567.89")),
2860 &str,
2861 Utf8,
2862 StringArray
2863 );
2864 Ok(())
2865 }
2866
2867 #[test]
2868 fn test_grouping_separator_parentheses_float() -> Result<()> {
2869 test_scalar_function!(
2872 FormatStringFunc::new(),
2873 vec![
2874 ColumnarValue::Scalar(ScalarValue::Utf8(Some("%(,15.2f".to_string()))),
2875 ColumnarValue::Scalar(ScalarValue::Float64(Some(-1234.5))),
2876 ],
2877 Ok(Some(" (1,234.50)")),
2878 &str,
2879 Utf8,
2880 StringArray
2881 );
2882 Ok(())
2883 }
2884
2885 #[test]
2886 fn test_grouping_separator_parentheses_decimal() -> Result<()> {
2887 test_scalar_function!(
2888 FormatStringFunc::new(),
2889 vec![
2890 ColumnarValue::Scalar(ScalarValue::Utf8(Some("%(,.2f".to_string()))),
2891 ColumnarValue::Scalar(ScalarValue::Decimal128(Some(-123450), 10, 2)),
2892 ],
2893 Ok(Some("(1,234.50)")),
2894 &str,
2895 Utf8,
2896 StringArray
2897 );
2898
2899 test_scalar_function!(
2900 FormatStringFunc::new(),
2901 vec![
2902 ColumnarValue::Scalar(ScalarValue::Utf8(Some("%(,.2f".to_string()))),
2903 ColumnarValue::Scalar(ScalarValue::Decimal256(
2904 Some(i256::from(-123450)),
2905 10,
2906 2,
2907 )),
2908 ],
2909 Ok(Some("(1,234.50)")),
2910 &str,
2911 Utf8,
2912 StringArray
2913 );
2914
2915 test_scalar_function!(
2917 FormatStringFunc::new(),
2918 vec![
2919 ColumnarValue::Scalar(ScalarValue::Utf8(Some("%(,15.2f".to_string()))),
2920 ColumnarValue::Scalar(ScalarValue::Decimal128(Some(-123450), 10, 2)),
2921 ],
2922 Ok(Some(" (1,234.50)")),
2923 &str,
2924 Utf8,
2925 StringArray
2926 );
2927 Ok(())
2928 }
2929
2930 #[test]
2931 fn test_grouping_separator_ignore_zero_padding_for_float_nan() -> Result<()> {
2932 test_scalar_function!(
2933 FormatStringFunc::new(),
2934 vec![
2935 ColumnarValue::Scalar(ScalarValue::Utf8(Some("%010.2f".to_string()))),
2936 ColumnarValue::Scalar(ScalarValue::Float64(Some(f64::NAN))),
2937 ],
2938 Ok(Some(" NaN")),
2939 &str,
2940 Utf8,
2941 StringArray
2942 );
2943 Ok(())
2944 }
2945
2946 #[test]
2947 fn test_grouping_separator_ignore_zero_padding_for_float_inf() -> Result<()> {
2948 test_scalar_function!(
2949 FormatStringFunc::new(),
2950 vec![
2951 ColumnarValue::Scalar(ScalarValue::Utf8(Some("%010.2f".to_string()))),
2952 ColumnarValue::Scalar(ScalarValue::Float64(Some(f64::INFINITY))),
2953 ],
2954 Ok(Some(" Infinity")),
2955 &str,
2956 Utf8,
2957 StringArray
2958 );
2959 Ok(())
2960 }
2961
2962 #[test]
2963 fn test_grouping_separator_parentheses_zero_padding_decimal() -> Result<()> {
2964 test_scalar_function!(
2965 FormatStringFunc::new(),
2966 vec![
2967 ColumnarValue::Scalar(ScalarValue::Utf8(Some("%(0,15.2f".to_string()))),
2968 ColumnarValue::Scalar(ScalarValue::Decimal128(Some(-123450), 2, 2)),
2969 ],
2970 Ok(Some("(000001,234.50)")),
2971 &str,
2972 Utf8,
2973 StringArray
2974 );
2975 Ok(())
2976 }
2977}