1use crate::utils::{calculate_binary_decimal_math_cast, calculate_binary_math};
19
20use arrow::array::{Array, ArrayRef, AsArray};
21use arrow::datatypes::DataType::{
22 Decimal32, Decimal64, Decimal128, Decimal256, Float32, Float64, Int8, Int16, Int32,
23 Int64, UInt8, UInt16, UInt32, UInt64,
24};
25use arrow::datatypes::{
26 ArrowNativeTypeOp, ArrowPrimitiveType, DataType, Decimal32Type, Decimal64Type,
27 Decimal128Type, Decimal256Type, DecimalType, Float32Type, Float64Type, Int8Type,
28 Int16Type, Int32Type, Int64Type, UInt8Type, UInt16Type, UInt32Type, UInt64Type,
29};
30use arrow::datatypes::{Field, FieldRef};
31use arrow::error::ArrowError;
32use datafusion_common::types::{
33 NativeType, logical_float32, logical_float64, logical_int32,
34};
35use datafusion_common::{Result, ScalarValue, exec_err, internal_err};
36use datafusion_expr::sort_properties::{ExprProperties, SortProperties};
37use datafusion_expr::{
38 Coercion, ColumnarValue, Documentation, ReturnFieldArgs, ScalarFunctionArgs,
39 ScalarUDFImpl, Signature, TypeSignature, TypeSignatureClass, Volatility,
40};
41use datafusion_macros::user_doc;
42use num_traits::{PrimInt, Signed, cast, checked_pow};
43use std::sync::Arc;
44
45fn output_scale_for_decimal(precision: u8, input_scale: i8, decimal_places: i32) -> i8 {
46 if input_scale < 0 {
52 let min_scale = -i32::from(precision);
56 let new_scale = i32::from(input_scale).min(decimal_places).max(min_scale);
57 return new_scale as i8;
58 }
59
60 let decimal_places = decimal_places.max(0);
62 i32::from(input_scale).min(decimal_places) as i8
63}
64
65fn normalize_decimal_places_for_decimal(
66 decimal_places: i32,
67 precision: u8,
68 scale: i8,
69) -> Option<i32> {
70 if decimal_places >= 0 {
71 return Some(decimal_places);
72 }
73
74 let max_rounding_pow10 = i64::from(precision) - i64::from(scale);
78 if max_rounding_pow10 <= 0 {
79 return None;
80 }
81
82 let abs_decimal_places = i64::from(decimal_places.unsigned_abs());
83 (abs_decimal_places <= max_rounding_pow10).then_some(decimal_places)
84}
85
86fn validate_decimal_precision<T: DecimalType>(
87 value: T::Native,
88 precision: u8,
89 scale: i8,
90) -> Result<T::Native, ArrowError> {
91 T::validate_decimal_precision(value, precision, scale).map_err(|e| {
92 ArrowError::ComputeError(format!(
93 "Decimal overflow: rounded value exceeds precision {precision}: {e}"
94 ))
95 })?;
96 Ok(value)
97}
98
99fn calculate_new_precision_scale<T: DecimalType>(
100 precision: u8,
101 scale: i8,
102 decimal_places: Option<i32>,
103) -> Result<DataType> {
104 if let Some(decimal_places) = decimal_places {
105 let new_scale = output_scale_for_decimal(precision, scale, decimal_places);
106
107 let abs_decimal_places = decimal_places.unsigned_abs();
111 let new_precision = if scale == 0
112 && decimal_places < 0
113 && abs_decimal_places <= u32::from(precision)
114 {
115 precision.saturating_add(1).min(T::MAX_PRECISION)
116 } else {
117 precision
118 };
119 Ok(T::TYPE_CONSTRUCTOR(new_precision, new_scale))
120 } else {
121 let new_precision = precision.saturating_add(1).min(T::MAX_PRECISION);
122 Ok(T::TYPE_CONSTRUCTOR(new_precision, scale))
123 }
124}
125
126fn decimal_places_from_scalar(scalar: &ScalarValue) -> Result<i32> {
127 let out_of_range = |value: String| {
128 datafusion_common::DataFusionError::Execution(format!(
129 "round decimal_places {value} is out of supported i32 range"
130 ))
131 };
132 match scalar {
133 ScalarValue::Int8(Some(v)) => Ok(i32::from(*v)),
134 ScalarValue::Int16(Some(v)) => Ok(i32::from(*v)),
135 ScalarValue::Int32(Some(v)) => Ok(*v),
136 ScalarValue::Int64(Some(v)) => {
137 i32::try_from(*v).map_err(|_| out_of_range(v.to_string()))
138 }
139 ScalarValue::UInt8(Some(v)) => Ok(i32::from(*v)),
140 ScalarValue::UInt16(Some(v)) => Ok(i32::from(*v)),
141 ScalarValue::UInt32(Some(v)) => {
142 i32::try_from(*v).map_err(|_| out_of_range(v.to_string()))
143 }
144 ScalarValue::UInt64(Some(v)) => {
145 i32::try_from(*v).map_err(|_| out_of_range(v.to_string()))
146 }
147 other => exec_err!(
148 "Unexpected datatype for decimal_places: {}",
149 other.data_type()
150 ),
151 }
152}
153
154#[user_doc(
155 doc_section(label = "Math Functions"),
156 description = "Rounds a number to the nearest integer.",
157 syntax_example = "round(numeric_expression[, decimal_places])",
158 standard_argument(name = "numeric_expression", prefix = "Numeric"),
159 argument(
160 name = "decimal_places",
161 description = "Optional. The number of decimal places to round to. Defaults to 0."
162 ),
163 sql_example = r#"```sql
164> SELECT round(3.14159);
165+--------------+
166| round(3.14159)|
167+--------------+
168| 3.0 |
169+--------------+
170```"#
171)]
172#[derive(Debug, PartialEq, Eq, Hash)]
173pub struct RoundFunc {
174 signature: Signature,
175}
176
177impl Default for RoundFunc {
178 fn default() -> Self {
179 RoundFunc::new()
180 }
181}
182
183impl RoundFunc {
184 pub fn new() -> Self {
185 let decimal = Coercion::new_exact(TypeSignatureClass::Decimal);
186 let decimal_places = Coercion::new_implicit(
187 TypeSignatureClass::Native(logical_int32()),
188 vec![TypeSignatureClass::Integer],
189 NativeType::Int32,
190 );
191 let integer = Coercion::new_exact(TypeSignatureClass::Integer);
192 let float32 = Coercion::new_exact(TypeSignatureClass::Native(logical_float32()));
193 let float64 = Coercion::new_implicit(
194 TypeSignatureClass::Native(logical_float64()),
195 vec![TypeSignatureClass::Numeric],
196 NativeType::Float64,
197 );
198 Self {
199 signature: Signature::one_of(
200 vec![
201 TypeSignature::Coercible(vec![
202 decimal.clone(),
203 decimal_places.clone(),
204 ]),
205 TypeSignature::Coercible(vec![decimal]),
206 TypeSignature::Coercible(vec![
207 integer.clone(),
208 decimal_places.clone(),
209 ]),
210 TypeSignature::Coercible(vec![integer]),
211 TypeSignature::Coercible(vec![
212 float32.clone(),
213 decimal_places.clone(),
214 ]),
215 TypeSignature::Coercible(vec![float32]),
216 TypeSignature::Coercible(vec![float64.clone(), decimal_places]),
217 TypeSignature::Coercible(vec![float64]),
218 ],
219 Volatility::Immutable,
220 ),
221 }
222 }
223}
224
225impl ScalarUDFImpl for RoundFunc {
226 fn name(&self) -> &str {
227 "round"
228 }
229
230 fn is_strict(&self) -> bool {
231 true
232 }
233
234 fn signature(&self) -> &Signature {
235 &self.signature
236 }
237
238 fn return_field_from_args(&self, args: ReturnFieldArgs) -> Result<FieldRef> {
239 let input_field = &args.arg_fields[0];
240 let input_type = input_field.data_type();
241
242 let decimal_places: Option<i32> = match args.scalar_arguments.get(1) {
248 None => Some(0), Some(None) => None, Some(Some(scalar)) if scalar.is_null() => Some(0), Some(Some(scalar)) => Some(decimal_places_from_scalar(scalar)?),
252 };
253
254 let return_type =
260 match input_type {
261 input_type if input_type.is_integer() => input_type.clone(),
262 Float32 => Float32,
263 Decimal32(precision, scale) => calculate_new_precision_scale::<
264 Decimal32Type,
265 >(
266 *precision, *scale, decimal_places
267 )?,
268 Decimal64(precision, scale) => calculate_new_precision_scale::<
269 Decimal64Type,
270 >(
271 *precision, *scale, decimal_places
272 )?,
273 Decimal128(precision, scale) => calculate_new_precision_scale::<
274 Decimal128Type,
275 >(
276 *precision, *scale, decimal_places
277 )?,
278 Decimal256(precision, scale) => calculate_new_precision_scale::<
279 Decimal256Type,
280 >(
281 *precision, *scale, decimal_places
282 )?,
283 _ => Float64,
284 };
285
286 let nullable = args.arg_fields.iter().any(|f| f.is_nullable());
287 Ok(Arc::new(Field::new(self.name(), return_type, nullable)))
288 }
289
290 fn return_type(&self, _arg_types: &[DataType]) -> Result<DataType> {
291 internal_err!("use return_field_from_args instead")
292 }
293
294 fn invoke_with_args(&self, args: ScalarFunctionArgs) -> Result<ColumnarValue> {
295 if args.arg_fields.iter().any(|a| a.data_type().is_null()) {
296 return ColumnarValue::Scalar(ScalarValue::Null)
297 .cast_to(args.return_type(), None);
298 }
299
300 let default_decimal_places = ColumnarValue::Scalar(ScalarValue::Int32(Some(0)));
301 let decimal_places = if args.args.len() == 2 {
302 &args.args[1]
303 } else {
304 &default_decimal_places
305 };
306
307 if let (ColumnarValue::Scalar(value_scalar), ColumnarValue::Scalar(dp_scalar)) =
308 (&args.args[0], decimal_places)
309 {
310 if value_scalar.is_null() || dp_scalar.is_null() {
311 return ColumnarValue::Scalar(ScalarValue::Null)
312 .cast_to(args.return_type(), None);
313 }
314
315 let dp = if let ScalarValue::Int32(Some(dp)) = dp_scalar {
316 *dp
317 } else {
318 return internal_err!(
319 "Unexpected datatype for decimal_places: {}",
320 dp_scalar.data_type()
321 );
322 };
323
324 match (value_scalar, args.return_type()) {
325 (value_scalar, return_type) if return_type.is_integer() => {
326 round_integer_scalar(value_scalar, return_type, dp)
327 }
328 (ScalarValue::Float32(Some(v)), _) => {
329 let rounded = round_float(*v, dp)?;
330 Ok(ColumnarValue::Scalar(ScalarValue::from(rounded)))
331 }
332 (ScalarValue::Float64(Some(v)), _) => {
333 let rounded = round_float(*v, dp)?;
334 Ok(ColumnarValue::Scalar(ScalarValue::from(rounded)))
335 }
336 (
337 ScalarValue::Decimal32(Some(v), in_precision, scale),
338 Decimal32(out_precision, out_scale),
339 ) => {
340 let rounded =
341 round_decimal_or_zero(*v, *in_precision, *scale, *out_scale, dp)?;
342 let rounded = if *out_precision == Decimal32Type::MAX_PRECISION
343 && *scale == 0
344 && dp < 0
345 {
346 validate_decimal_precision::<Decimal32Type>(
350 rounded,
351 *out_precision,
352 *out_scale,
353 )
354 } else {
355 Ok(rounded)
356 }?;
357 let scalar =
358 ScalarValue::Decimal32(Some(rounded), *out_precision, *out_scale);
359 Ok(ColumnarValue::Scalar(scalar))
360 }
361 (
362 ScalarValue::Decimal64(Some(v), in_precision, scale),
363 Decimal64(out_precision, out_scale),
364 ) => {
365 let rounded =
366 round_decimal_or_zero(*v, *in_precision, *scale, *out_scale, dp)?;
367 let rounded = if *out_precision == Decimal64Type::MAX_PRECISION
368 && *scale == 0
369 && dp < 0
370 {
371 validate_decimal_precision::<Decimal64Type>(
373 rounded,
374 *out_precision,
375 *out_scale,
376 )
377 } else {
378 Ok(rounded)
379 }?;
380 let scalar =
381 ScalarValue::Decimal64(Some(rounded), *out_precision, *out_scale);
382 Ok(ColumnarValue::Scalar(scalar))
383 }
384 (
385 ScalarValue::Decimal128(Some(v), in_precision, scale),
386 Decimal128(out_precision, out_scale),
387 ) => {
388 let rounded =
389 round_decimal_or_zero(*v, *in_precision, *scale, *out_scale, dp)?;
390 let rounded = if *out_precision == Decimal128Type::MAX_PRECISION
391 && *scale == 0
392 && dp < 0
393 {
394 validate_decimal_precision::<Decimal128Type>(
396 rounded,
397 *out_precision,
398 *out_scale,
399 )
400 } else {
401 Ok(rounded)
402 }?;
403 let scalar = ScalarValue::Decimal128(
404 Some(rounded),
405 *out_precision,
406 *out_scale,
407 );
408 Ok(ColumnarValue::Scalar(scalar))
409 }
410 (
411 ScalarValue::Decimal256(Some(v), in_precision, scale),
412 Decimal256(out_precision, out_scale),
413 ) => {
414 let rounded =
415 round_decimal_or_zero(*v, *in_precision, *scale, *out_scale, dp)?;
416 let rounded = if *out_precision == Decimal256Type::MAX_PRECISION
417 && *scale == 0
418 && dp < 0
419 {
420 validate_decimal_precision::<Decimal256Type>(
422 rounded,
423 *out_precision,
424 *out_scale,
425 )
426 } else {
427 Ok(rounded)
428 }?;
429 let scalar = ScalarValue::Decimal256(
430 Some(rounded),
431 *out_precision,
432 *out_scale,
433 );
434 Ok(ColumnarValue::Scalar(scalar))
435 }
436 (ScalarValue::Null, _) => ColumnarValue::Scalar(ScalarValue::Null)
437 .cast_to(args.return_type(), None),
438 (value_scalar, return_type) => {
439 internal_err!(
440 "Unexpected datatype for round(value, decimal_places): value {}, return type {}",
441 value_scalar.data_type(),
442 return_type
443 )
444 }
445 }
446 } else {
447 round_columnar(
448 &args.args[0],
449 decimal_places,
450 args.number_rows,
451 args.return_type(),
452 )
453 }
454 }
455
456 fn output_ordering(&self, input: &[ExprProperties]) -> Result<SortProperties> {
457 let value = &input[0];
459 let precision = input.get(1);
460
461 if precision
462 .map(|r| r.sort_properties.eq(&SortProperties::Singleton))
463 .unwrap_or(true)
464 {
465 Ok(value.sort_properties)
466 } else {
467 Ok(SortProperties::Unordered)
468 }
469 }
470
471 fn documentation(&self) -> Option<&Documentation> {
472 self.doc()
473 }
474}
475
476fn round_columnar(
477 value: &ColumnarValue,
478 decimal_places: &ColumnarValue,
479 number_rows: usize,
480 return_type: &DataType,
481) -> Result<ColumnarValue> {
482 let value_array = value.to_array(number_rows)?;
483 let both_scalars = matches!(value, ColumnarValue::Scalar(_))
484 && matches!(decimal_places, ColumnarValue::Scalar(_));
485 let decimal_places_is_array = matches!(decimal_places, ColumnarValue::Array(_));
486
487 let arr: ArrayRef = match (value_array.data_type(), return_type) {
488 (input_type, return_type)
489 if input_type == return_type && return_type.is_integer() =>
490 {
491 match decimal_places {
492 ColumnarValue::Scalar(ScalarValue::Int32(Some(dp))) if *dp >= 0 => {
493 value_array
494 }
495 _ => round_integer_array(
496 value_array.as_ref(),
497 decimal_places,
498 return_type,
499 )?,
500 }
501 }
502 (Float64, _) => round_float_column::<Float64Type>(&value_array, decimal_places)?,
503 (Float32, _) => round_float_column::<Float32Type>(&value_array, decimal_places)?,
504 (Decimal32(input_precision, scale), Decimal32(precision, new_scale)) => {
505 let result = calculate_binary_decimal_math_cast::<
507 Decimal32Type,
508 Int32Type,
509 Decimal32Type,
510 _,
511 >(
512 value_array.as_ref(),
513 decimal_places,
514 |v, dp| {
515 let rounded = round_decimal_or_zero(
516 v,
517 *input_precision,
518 *scale,
519 *new_scale,
520 dp,
521 )?;
522 if *precision == Decimal32Type::MAX_PRECISION
523 && (decimal_places_is_array || (*scale == 0 && dp < 0))
524 {
525 validate_decimal_precision::<Decimal32Type>(
530 rounded, *precision, *new_scale,
531 )
532 } else {
533 Ok(rounded)
534 }
535 },
536 *precision,
537 *new_scale,
538 &Int32,
539 )?;
540 result as _
541 }
542 (Decimal64(input_precision, scale), Decimal64(precision, new_scale)) => {
543 let result = calculate_binary_decimal_math_cast::<
544 Decimal64Type,
545 Int32Type,
546 Decimal64Type,
547 _,
548 >(
549 value_array.as_ref(),
550 decimal_places,
551 |v, dp| {
552 let rounded = round_decimal_or_zero(
553 v,
554 *input_precision,
555 *scale,
556 *new_scale,
557 dp,
558 )?;
559 if *precision == Decimal64Type::MAX_PRECISION
560 && (decimal_places_is_array || (*scale == 0 && dp < 0))
561 {
562 validate_decimal_precision::<Decimal64Type>(
564 rounded, *precision, *new_scale,
565 )
566 } else {
567 Ok(rounded)
568 }
569 },
570 *precision,
571 *new_scale,
572 &Int32,
573 )?;
574 result as _
575 }
576 (Decimal128(input_precision, scale), Decimal128(precision, new_scale)) => {
577 let result = calculate_binary_decimal_math_cast::<
578 Decimal128Type,
579 Int32Type,
580 Decimal128Type,
581 _,
582 >(
583 value_array.as_ref(),
584 decimal_places,
585 |v, dp| {
586 let rounded = round_decimal_or_zero(
587 v,
588 *input_precision,
589 *scale,
590 *new_scale,
591 dp,
592 )?;
593 if *precision == Decimal128Type::MAX_PRECISION
594 && (decimal_places_is_array || (*scale == 0 && dp < 0))
595 {
596 validate_decimal_precision::<Decimal128Type>(
598 rounded, *precision, *new_scale,
599 )
600 } else {
601 Ok(rounded)
602 }
603 },
604 *precision,
605 *new_scale,
606 &Int32,
607 )?;
608 result as _
609 }
610 (Decimal256(input_precision, scale), Decimal256(precision, new_scale)) => {
611 let result = calculate_binary_decimal_math_cast::<
612 Decimal256Type,
613 Int32Type,
614 Decimal256Type,
615 _,
616 >(
617 value_array.as_ref(),
618 decimal_places,
619 |v, dp| {
620 let rounded = round_decimal_or_zero(
621 v,
622 *input_precision,
623 *scale,
624 *new_scale,
625 dp,
626 )?;
627 if *precision == Decimal256Type::MAX_PRECISION
628 && (decimal_places_is_array || (*scale == 0 && dp < 0))
629 {
630 validate_decimal_precision::<Decimal256Type>(
632 rounded, *precision, *new_scale,
633 )
634 } else {
635 Ok(rounded)
636 }
637 },
638 *precision,
639 *new_scale,
640 &Int32,
641 )?;
642 result as _
643 }
644 (other, _) => exec_err!("Unsupported data type {other:?} for function round")?,
645 };
646
647 if both_scalars {
648 ScalarValue::try_from_array(&arr, 0).map(ColumnarValue::Scalar)
649 } else {
650 Ok(ColumnarValue::Array(arr))
651 }
652}
653
654fn round_signed_integer<T>(
655 value: T,
656 decimal_places: i32,
657 type_name: &str,
658) -> Result<T, ArrowError>
659where
660 T: PrimInt + Signed,
661{
662 if decimal_places >= 0 || value == T::zero() {
663 return Ok(value);
664 }
665
666 let ten = cast::<_, T>(10).expect("10 fits in all integer types");
667 let Some(factor) = checked_pow(ten, decimal_places.unsigned_abs() as usize) else {
668 return Ok(T::zero());
669 };
670
671 let two = cast::<_, T>(2).expect("2 fits in all integer types");
672 let one = T::one();
673 let threshold = factor / two;
674 let mut quotient = value / factor;
675 let remainder = value % factor;
676
677 if remainder >= threshold {
678 quotient = quotient.checked_add(&one).ok_or_else(|| {
679 ArrowError::ComputeError(format!("Overflow while rounding {type_name}"))
680 })?;
681 } else if remainder <= -threshold {
682 quotient = quotient.checked_sub(&one).ok_or_else(|| {
683 ArrowError::ComputeError(format!("Overflow while rounding {type_name}"))
684 })?;
685 }
686
687 quotient.checked_mul(&factor).ok_or_else(|| {
688 ArrowError::ComputeError(format!("Overflow while rounding {type_name}"))
689 })
690}
691
692fn round_unsigned_integer<T>(
693 value: T,
694 decimal_places: i32,
695 type_name: &str,
696) -> Result<T, ArrowError>
697where
698 T: PrimInt,
699{
700 if decimal_places >= 0 || value == T::zero() {
701 return Ok(value);
702 }
703
704 let ten = cast::<_, T>(10).expect("10 fits in all integer types");
705 let Some(factor) = checked_pow(ten, decimal_places.unsigned_abs() as usize) else {
706 return Ok(T::zero());
707 };
708
709 let two = cast::<_, T>(2).expect("2 fits in all integer types");
710 let one = T::one();
711 let threshold = factor / two;
712 let mut quotient = value / factor;
713 let remainder = value % factor;
714
715 if remainder >= threshold {
716 quotient = quotient.checked_add(&one).ok_or_else(|| {
717 ArrowError::ComputeError(format!("Overflow while rounding {type_name}"))
718 })?;
719 }
720
721 quotient.checked_mul(&factor).ok_or_else(|| {
722 ArrowError::ComputeError(format!("Overflow while rounding {type_name}"))
723 })
724}
725
726fn round_integer_scalar(
727 value: &ScalarValue,
728 return_type: &DataType,
729 decimal_places: i32,
730) -> Result<ColumnarValue> {
731 match (value, return_type) {
732 (ScalarValue::Int8(Some(v)), Int8) => Ok(ColumnarValue::Scalar(
733 ScalarValue::Int8(Some(round_signed_integer(*v, decimal_places, "Int8")?)),
734 )),
735 (ScalarValue::Int16(Some(v)), Int16) => Ok(ColumnarValue::Scalar(
736 ScalarValue::Int16(Some(round_signed_integer(*v, decimal_places, "Int16")?)),
737 )),
738 (ScalarValue::Int32(Some(v)), Int32) => Ok(ColumnarValue::Scalar(
739 ScalarValue::Int32(Some(round_signed_integer(*v, decimal_places, "Int32")?)),
740 )),
741 (ScalarValue::Int64(Some(v)), Int64) => Ok(ColumnarValue::Scalar(
742 ScalarValue::Int64(Some(round_signed_integer(*v, decimal_places, "Int64")?)),
743 )),
744 (ScalarValue::UInt8(Some(v)), UInt8) => {
745 Ok(ColumnarValue::Scalar(ScalarValue::UInt8(Some(
746 round_unsigned_integer(*v, decimal_places, "UInt8")?,
747 ))))
748 }
749 (ScalarValue::UInt16(Some(v)), UInt16) => {
750 Ok(ColumnarValue::Scalar(ScalarValue::UInt16(Some(
751 round_unsigned_integer(*v, decimal_places, "UInt16")?,
752 ))))
753 }
754 (ScalarValue::UInt32(Some(v)), UInt32) => {
755 Ok(ColumnarValue::Scalar(ScalarValue::UInt32(Some(
756 round_unsigned_integer(*v, decimal_places, "UInt32")?,
757 ))))
758 }
759 (ScalarValue::UInt64(Some(v)), UInt64) => {
760 Ok(ColumnarValue::Scalar(ScalarValue::UInt64(Some(
761 round_unsigned_integer(*v, decimal_places, "UInt64")?,
762 ))))
763 }
764 _ => internal_err!(
765 "Unexpected integer round input/output types: {} -> {}",
766 value.data_type(),
767 return_type
768 ),
769 }
770}
771
772macro_rules! round_integer_array {
773 ($ARRAY:expr, $DP:expr, $ARRAY_TYPE:ty, $ROUND_FN:ident, $TYPE_NAME:expr) => {{
774 let array = $ARRAY.as_primitive::<$ARRAY_TYPE>();
775
776 let result = calculate_binary_math::<$ARRAY_TYPE, Int32Type, $ARRAY_TYPE, _>(
777 array,
778 $DP,
779 |v, dp| $ROUND_FN(v, dp, $TYPE_NAME),
780 )?;
781
782 Ok(result as ArrayRef)
783 }};
784}
785
786fn round_integer_array(
787 value_array: &dyn Array,
788 decimal_places: &ColumnarValue,
789 return_type: &DataType,
790) -> Result<ArrayRef> {
791 match return_type {
792 Int8 => round_integer_array!(
793 value_array,
794 decimal_places,
795 Int8Type,
796 round_signed_integer,
797 "Int8"
798 ),
799 Int16 => round_integer_array!(
800 value_array,
801 decimal_places,
802 Int16Type,
803 round_signed_integer,
804 "Int16"
805 ),
806 Int32 => round_integer_array!(
807 value_array,
808 decimal_places,
809 Int32Type,
810 round_signed_integer,
811 "Int32"
812 ),
813 Int64 => round_integer_array!(
814 value_array,
815 decimal_places,
816 Int64Type,
817 round_signed_integer,
818 "Int64"
819 ),
820 UInt8 => round_integer_array!(
821 value_array,
822 decimal_places,
823 UInt8Type,
824 round_unsigned_integer,
825 "UInt8"
826 ),
827 UInt16 => round_integer_array!(
828 value_array,
829 decimal_places,
830 UInt16Type,
831 round_unsigned_integer,
832 "UInt16"
833 ),
834 UInt32 => round_integer_array!(
835 value_array,
836 decimal_places,
837 UInt32Type,
838 round_unsigned_integer,
839 "UInt32"
840 ),
841 UInt64 => round_integer_array!(
842 value_array,
843 decimal_places,
844 UInt64Type,
845 round_unsigned_integer,
846 "UInt64"
847 ),
848 _ => internal_err!("Unexpected return type for integer round: {return_type}"),
849 }
850}
851
852fn round_float_column<PT>(
862 value_array: &ArrayRef,
863 decimal_places: &ColumnarValue,
864) -> Result<ArrayRef>
865where
866 PT: ArrowPrimitiveType,
867 PT::Native: num_traits::Float,
868{
869 use num_traits::Float;
872
873 if let ColumnarValue::Scalar(ScalarValue::Int32(Some(decimal_places))) =
874 decimal_places
875 {
876 let factor = round_factor::<PT::Native>(*decimal_places)?;
877 let result = value_array
878 .as_primitive::<PT>()
879 .unary::<_, PT>(|value| (value * factor).round() / factor);
880 return Ok(Arc::new(result) as ArrayRef);
881 }
882
883 let result = calculate_binary_math::<PT, Int32Type, PT, _>(
884 value_array.as_ref(),
885 decimal_places,
886 round_float::<PT::Native>,
887 )?;
888 Ok(result as _)
889}
890
891fn round_factor<T: num_traits::Float>(decimal_places: i32) -> Result<T, ArrowError> {
893 T::from(10_f64.powi(decimal_places)).ok_or_else(|| {
894 ArrowError::ComputeError(format!(
895 "Invalid value for decimal places: {decimal_places}"
896 ))
897 })
898}
899
900fn round_float<T>(value: T, decimal_places: i32) -> Result<T, ArrowError>
901where
902 T: num_traits::Float,
903{
904 let factor = round_factor::<T>(decimal_places)?;
905 Ok((value * factor).round() / factor)
906}
907
908fn round_decimal<V: ArrowNativeTypeOp>(
909 value: V,
910 input_scale: i8,
911 output_scale: i8,
912 decimal_places: i32,
913) -> Result<V, ArrowError> {
914 let diff = i64::from(input_scale) - i64::from(decimal_places);
915 if diff <= 0 {
916 return Ok(value);
917 }
918
919 debug_assert!(diff <= i64::from(u32::MAX));
920 let diff = diff as u32;
921
922 let one = V::ONE;
923 let two = V::from_usize(2).ok_or_else(|| {
924 ArrowError::ComputeError("Internal error: could not create constant 2".into())
925 })?;
926 let ten = V::from_usize(10).ok_or_else(|| {
927 ArrowError::ComputeError("Internal error: could not create constant 10".into())
928 })?;
929
930 let factor = ten.pow_checked(diff).map_err(|_| {
931 ArrowError::ComputeError(format!(
932 "Overflow while rounding decimal with scale {input_scale} and decimal places {decimal_places}"
933 ))
934 })?;
935
936 let mut quotient = value.div_wrapping(factor);
937 let remainder = value.mod_wrapping(factor);
938
939 let threshold = factor.div_wrapping(two);
941 if remainder >= threshold {
942 quotient = quotient.add_checked(one).map_err(|_| {
943 ArrowError::ComputeError("Overflow while rounding decimal".into())
944 })?;
945 } else if remainder <= threshold.neg_wrapping() {
946 quotient = quotient.sub_checked(one).map_err(|_| {
947 ArrowError::ComputeError("Overflow while rounding decimal".into())
948 })?;
949 }
950
951 let scale_shift = i64::from(output_scale) - i64::from(decimal_places);
954 if scale_shift == 0 {
955 return Ok(quotient);
956 }
957
958 debug_assert!(scale_shift > 0);
959 debug_assert!(scale_shift <= i64::from(u32::MAX));
960 let scale_shift = scale_shift as u32;
961 let shift_factor = ten.pow_checked(scale_shift).map_err(|_| {
962 ArrowError::ComputeError(format!(
963 "Overflow while rounding decimal with scale {input_scale} and decimal places {decimal_places}"
964 ))
965 })?;
966 quotient
967 .mul_checked(shift_factor)
968 .map_err(|_| ArrowError::ComputeError("Overflow while rounding decimal".into()))
969}
970
971fn round_decimal_or_zero<V: ArrowNativeTypeOp>(
972 value: V,
973 precision: u8,
974 input_scale: i8,
975 output_scale: i8,
976 decimal_places: i32,
977) -> Result<V, ArrowError> {
978 if let Some(dp) =
979 normalize_decimal_places_for_decimal(decimal_places, precision, input_scale)
980 {
981 round_decimal(value, input_scale, output_scale, dp)
982 } else {
983 V::from_usize(0).ok_or_else(|| {
984 ArrowError::ComputeError("Internal error: could not create constant 0".into())
985 })
986 }
987}
988
989#[cfg(test)]
990mod test {
991 use std::sync::Arc;
992
993 use arrow::array::{ArrayRef, Float32Array, Float64Array, Int64Array};
994 use arrow::datatypes::DataType;
995 use datafusion_common::DataFusionError;
996 use datafusion_common::ScalarValue;
997 use datafusion_common::cast::{as_float32_array, as_float64_array};
998 use datafusion_expr::ColumnarValue;
999
1000 fn round_arrays(
1001 value: ArrayRef,
1002 decimal_places: Option<ArrayRef>,
1003 ) -> Result<ArrayRef, DataFusionError> {
1004 let number_rows = value.len();
1005 let return_type = value.data_type().clone();
1009 let value = ColumnarValue::Array(value);
1010 let decimal_places = decimal_places
1011 .map(ColumnarValue::Array)
1012 .unwrap_or_else(|| ColumnarValue::Scalar(ScalarValue::Int32(Some(0))));
1013
1014 let result =
1015 super::round_columnar(&value, &decimal_places, number_rows, &return_type)?;
1016 match result {
1017 ColumnarValue::Array(array) => Ok(array),
1018 ColumnarValue::Scalar(scalar) => scalar.to_array_of_size(1),
1019 }
1020 }
1021
1022 #[test]
1023 fn test_round_f32() {
1024 let args: Vec<ArrayRef> = vec![
1025 Arc::new(Float32Array::from(vec![125.2345; 10])), Arc::new(Int64Array::from(vec![0, 1, 2, 3, 4, 5, -1, -2, -3, -4])), ];
1028
1029 let result = round_arrays(Arc::clone(&args[0]), Some(Arc::clone(&args[1])))
1030 .expect("failed to initialize function round");
1031 let floats =
1032 as_float32_array(&result).expect("failed to initialize function round");
1033
1034 let expected = Float32Array::from(vec![
1035 125.0, 125.2, 125.23, 125.235, 125.2345, 125.2345, 130.0, 100.0, 0.0, 0.0,
1036 ]);
1037
1038 assert_eq!(floats, &expected);
1039 }
1040
1041 #[test]
1042 fn test_round_f64() {
1043 let args: Vec<ArrayRef> = vec![
1044 Arc::new(Float64Array::from(vec![125.2345; 10])), Arc::new(Int64Array::from(vec![0, 1, 2, 3, 4, 5, -1, -2, -3, -4])), ];
1047
1048 let result = round_arrays(Arc::clone(&args[0]), Some(Arc::clone(&args[1])))
1049 .expect("failed to initialize function round");
1050 let floats =
1051 as_float64_array(&result).expect("failed to initialize function round");
1052
1053 let expected = Float64Array::from(vec![
1054 125.0, 125.2, 125.23, 125.235, 125.2345, 125.2345, 130.0, 100.0, 0.0, 0.0,
1055 ]);
1056
1057 assert_eq!(floats, &expected);
1058 }
1059
1060 #[test]
1063 fn test_round_f64_scalar_decimal_places_preserves_nulls() {
1064 let value: ArrayRef = Arc::new(Float64Array::from(vec![
1065 Some(125.2345),
1066 None,
1067 Some(-1.555),
1068 None,
1069 ]));
1070
1071 let result = super::round_columnar(
1072 &ColumnarValue::Array(value),
1073 &ColumnarValue::Scalar(ScalarValue::Int32(Some(2))),
1074 4,
1075 &DataType::Float64,
1076 )
1077 .expect("failed to initialize function round");
1078 let ColumnarValue::Array(result) = result else {
1079 panic!("expected an array result");
1080 };
1081 let floats =
1082 as_float64_array(&result).expect("failed to initialize function round");
1083
1084 let expected = Float64Array::from(vec![Some(125.23), None, Some(-1.56), None]);
1085
1086 assert_eq!(floats, &expected);
1087 }
1088
1089 #[test]
1090 fn test_round_f32_one_input() {
1091 let args: Vec<ArrayRef> = vec![
1092 Arc::new(Float32Array::from(vec![125.2345, 12.345, 1.234, 0.1234])), ];
1094
1095 let result = round_arrays(Arc::clone(&args[0]), None)
1096 .expect("failed to initialize function round");
1097 let floats =
1098 as_float32_array(&result).expect("failed to initialize function round");
1099
1100 let expected = Float32Array::from(vec![125.0, 12.0, 1.0, 0.0]);
1101
1102 assert_eq!(floats, &expected);
1103 }
1104
1105 #[test]
1106 fn test_round_f64_one_input() {
1107 let args: Vec<ArrayRef> = vec![
1108 Arc::new(Float64Array::from(vec![125.2345, 12.345, 1.234, 0.1234])), ];
1110
1111 let result = round_arrays(Arc::clone(&args[0]), None)
1112 .expect("failed to initialize function round");
1113 let floats =
1114 as_float64_array(&result).expect("failed to initialize function round");
1115
1116 let expected = Float64Array::from(vec![125.0, 12.0, 1.0, 0.0]);
1117
1118 assert_eq!(floats, &expected);
1119 }
1120
1121 #[test]
1122 fn test_round_f32_cast_fail() {
1123 let args: Vec<ArrayRef> = vec![
1124 Arc::new(Float64Array::from(vec![125.2345])), Arc::new(Int64Array::from(vec![2147483648])), ];
1127
1128 let result = round_arrays(Arc::clone(&args[0]), Some(Arc::clone(&args[1])));
1129
1130 assert!(result.is_err());
1131 assert!(matches!(
1132 result,
1133 Err(DataFusionError::ArrowError(_, _)) | Err(DataFusionError::Execution(_))
1134 ));
1135 }
1136}