Skip to content

Commit 8713645

Browse files
committed
add test for i8,i16,i32,i64,f32,f64 casted to decimal
1 parent 5061ec6 commit 8713645

File tree

1 file changed

+189
-6
lines changed

1 file changed

+189
-6
lines changed

arrow/src/compute/kernels/cast.rs

Lines changed: 189 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -69,10 +69,7 @@ pub fn can_cast_types(from_type: &DataType, to_type: &DataType) -> bool {
6969

7070
match (from_type, to_type) {
7171
// TODO now just support signed numeric to decimal, support decimal to numeric later
72-
// support one decimal data type to another decimal data type
73-
// or UTF-8 to decimal
74-
// numeric to decimal
75-
(Int8 | Int16 | Int32 | Int64 | Float32 | Float64 | Decimal(_, _), Decimal(_, _))
72+
(Int8 | Int16 | Int32 | Int64 | Float32 | Float64, Decimal(_, _))
7673
| (
7774
Null,
7875
Boolean
@@ -245,6 +242,45 @@ pub fn cast(array: &ArrayRef, to_type: &DataType) -> Result<ArrayRef> {
245242
cast_with_options(array, to_type, &DEFAULT_CAST_OPTIONS)
246243
}
247244

245+
// cast the integer array to defined decimal data type array
246+
macro_rules! cast_integer_to_decimal {
247+
($ARRAY: expr, $ARRAY_TYPE: ident, $PRECISION : ident, $SCALE : ident) => {{
248+
let mut decimal_builder = DecimalBuilder::new($ARRAY.len(), *$PRECISION, *$SCALE);
249+
let array = $ARRAY.as_any().downcast_ref::<$ARRAY_TYPE>().unwrap();
250+
let mul: i128 = 10_i128.pow(*$SCALE as u32);
251+
for i in 0..array.len() {
252+
if array.is_null(i) {
253+
decimal_builder.append_null()?;
254+
} else {
255+
// convert i128 first
256+
let v = array.value(i) as i128;
257+
// if the input value is overflow, it will throw an error.
258+
decimal_builder.append_value(mul * v)?;
259+
}
260+
}
261+
Ok(Arc::new(decimal_builder.finish()))
262+
}};
263+
}
264+
265+
// cast the floating-point array to defined decimal data type array
266+
macro_rules! cast_floating_point_to_decimal {
267+
($ARRAY: expr, $ARRAY_TYPE: ident, $PRECISION : ident, $SCALE : ident) => {{
268+
let mut decimal_builder = DecimalBuilder::new($ARRAY.len(), *$PRECISION, *$SCALE);
269+
let array = $ARRAY.as_any().downcast_ref::<$ARRAY_TYPE>().unwrap();
270+
let mul = 10_f64.powi(*$SCALE as i32);
271+
for i in 0..array.len() {
272+
if array.is_null(i) {
273+
decimal_builder.append_null()?;
274+
} else {
275+
let v = ((array.value(i) as f64) * mul) as i128;
276+
// if the input value is overflow, it will throw an error.
277+
decimal_builder.append_value(v)?;
278+
}
279+
}
280+
Ok(Arc::new(decimal_builder.finish()))
281+
}};
282+
}
283+
248284
/// Cast `array` to the provided data type and return a new Array with
249285
/// type `to_type`, if possible. It accepts `CastOptions` to allow consumers
250286
/// to configure cast behavior.
@@ -279,6 +315,34 @@ pub fn cast_with_options(
279315
return Ok(array.clone());
280316
}
281317
match (from_type, to_type) {
318+
(_, Decimal(precision, scale)) => {
319+
// cast data to decimal
320+
match from_type {
321+
// TODO now just support signed numeric to decimal, support decimal to numeric later
322+
Int8 => {
323+
cast_integer_to_decimal!(array, Int8Array, precision, scale)
324+
}
325+
Int16 => {
326+
cast_integer_to_decimal!(array, Int16Array, precision, scale)
327+
}
328+
Int32 => {
329+
cast_integer_to_decimal!(array, Int32Array, precision, scale)
330+
}
331+
Int64 => {
332+
cast_integer_to_decimal!(array, Int64Array, precision, scale)
333+
}
334+
Float32 => {
335+
cast_floating_point_to_decimal!(array, Float32Array, precision, scale)
336+
}
337+
Float64 => {
338+
cast_floating_point_to_decimal!(array, Float64Array, precision, scale)
339+
}
340+
_ => Err(ArrowError::CastError(format!(
341+
"Casting from {:?} to {:?} not supported",
342+
from_type, to_type
343+
))),
344+
}
345+
}
282346
(
283347
Null,
284348
Boolean
@@ -1257,7 +1321,7 @@ fn cast_string_to_date64<Offset: StringOffsetSizeTrait>(
12571321
if string_array.is_null(i) {
12581322
Ok(None)
12591323
} else {
1260-
let string = string_array
1324+
let string = string_array
12611325
.value(i);
12621326

12631327
let result = string
@@ -1476,7 +1540,7 @@ fn dictionary_cast<K: ArrowDictionaryKeyType>(
14761540
return Err(ArrowError::CastError(format!(
14771541
"Unsupported type {:?} for dictionary index",
14781542
to_index_type
1479-
)))
1543+
)));
14801544
}
14811545
};
14821546

@@ -1842,6 +1906,125 @@ where
18421906
mod tests {
18431907
use super::*;
18441908
use crate::{buffer::Buffer, util::display::array_value_to_string};
1909+
use num::traits::Pow;
1910+
1911+
#[test]
1912+
fn test_cast_numeric_to_decimal() {
1913+
let data_types = vec![
1914+
DataType::Int8,
1915+
DataType::Int16,
1916+
DataType::Int32,
1917+
DataType::Int64,
1918+
DataType::Float32,
1919+
DataType::Float64,
1920+
];
1921+
let decimal_type = DataType::Decimal(38, 6);
1922+
for data_type in data_types {
1923+
assert!(can_cast_types(&data_type, &decimal_type))
1924+
}
1925+
assert!(!can_cast_types(&DataType::UInt64, &decimal_type));
1926+
1927+
// test i8 to decimal type
1928+
let array = Int8Array::from(vec![1, 2, 3, 4, 5]);
1929+
let array = Arc::new(array) as ArrayRef;
1930+
let casted_array = cast(&array, &decimal_type).unwrap();
1931+
let decimal_array = casted_array
1932+
.as_any()
1933+
.downcast_ref::<DecimalArray>()
1934+
.unwrap();
1935+
assert_eq!(&decimal_type, decimal_array.data_type());
1936+
for i in 0..array.len() {
1937+
assert_eq!(
1938+
10_i128.pow(6) * (i as i128 + 1),
1939+
decimal_array.value(i as usize)
1940+
);
1941+
}
1942+
// test i8 to decimal type with overflow the result type
1943+
// the 100 will be converted to 1000_i128, but it is out of range for max value in the precision 3.
1944+
let array = Int8Array::from(vec![1, 2, 3, 4, 100]);
1945+
let array = Arc::new(array) as ArrayRef;
1946+
let casted_array = cast(&array, &DataType::Decimal(3, 1));
1947+
assert!(casted_array.is_err());
1948+
assert_eq!("Invalid argument error: The value of 1000 i128 is not compatible with Decimal(3,1)", casted_array.unwrap_err().to_string());
1949+
1950+
// test i16 to decimal type
1951+
let array = Int16Array::from(vec![1, 2, 3, 4, 5]);
1952+
let array = Arc::new(array) as ArrayRef;
1953+
let casted_array = cast(&array, &decimal_type).unwrap();
1954+
let decimal_array = casted_array
1955+
.as_any()
1956+
.downcast_ref::<DecimalArray>()
1957+
.unwrap();
1958+
assert_eq!(&decimal_type, decimal_array.data_type());
1959+
for i in 0..array.len() {
1960+
assert_eq!(
1961+
10_i128.pow(6) * (i as i128 + 1),
1962+
decimal_array.value(i as usize)
1963+
);
1964+
}
1965+
1966+
// test i32 to decimal type
1967+
let array = Int32Array::from(vec![1, 2, 3, 4, 5]);
1968+
let array = Arc::new(array) as ArrayRef;
1969+
let casted_array = cast(&array, &decimal_type).unwrap();
1970+
let decimal_array = casted_array
1971+
.as_any()
1972+
.downcast_ref::<DecimalArray>()
1973+
.unwrap();
1974+
assert_eq!(&decimal_type, decimal_array.data_type());
1975+
for i in 0..array.len() {
1976+
assert_eq!(
1977+
10_i128.pow(6) * (i as i128 + 1),
1978+
decimal_array.value(i as usize)
1979+
);
1980+
}
1981+
1982+
// test i64 to decimal type
1983+
let array = Int64Array::from(vec![1, 2, 3, 4, 5]);
1984+
let array = Arc::new(array) as ArrayRef;
1985+
let casted_array = cast(&array, &decimal_type).unwrap();
1986+
let decimal_array = casted_array
1987+
.as_any()
1988+
.downcast_ref::<DecimalArray>()
1989+
.unwrap();
1990+
assert_eq!(&decimal_type, decimal_array.data_type());
1991+
for i in 0..array.len() {
1992+
assert_eq!(
1993+
10_i128.pow(6) * (i as i128 + 1),
1994+
decimal_array.value(i as usize)
1995+
);
1996+
}
1997+
1998+
// test f32 to decimal type
1999+
let f_data: Vec<f32> = vec![1.1, 2.2, 4.4, 1.1234567891234];
2000+
let array = Float32Array::from(f_data.clone());
2001+
let array = Arc::new(array) as ArrayRef;
2002+
let casted_array = cast(&array, &decimal_type).unwrap();
2003+
let decimal_array = casted_array
2004+
.as_any()
2005+
.downcast_ref::<DecimalArray>()
2006+
.unwrap();
2007+
assert_eq!(&decimal_type, decimal_array.data_type());
2008+
for i in 0..array.len() {
2009+
let left = (f_data[i] as f64) * 10_f64.pow(6);
2010+
assert_eq!(left as i128, decimal_array.value(i as usize));
2011+
}
2012+
2013+
// test f64 to decimal type
2014+
let f_data: Vec<f64> = vec![1.1, 2.2, 4.4, 1.1234567891234];
2015+
let array = Float64Array::from(f_data.clone());
2016+
let array = Arc::new(array) as ArrayRef;
2017+
let casted_array = cast(&array, &decimal_type).unwrap();
2018+
let decimal_array = casted_array
2019+
.as_any()
2020+
.downcast_ref::<DecimalArray>()
2021+
.unwrap();
2022+
assert_eq!(&decimal_type, decimal_array.data_type());
2023+
for i in 0..array.len() {
2024+
let left = (f_data[i] as f64) * 10_f64.pow(6);
2025+
assert_eq!(left as i128, decimal_array.value(i as usize));
2026+
}
2027+
}
18452028

18462029
#[test]
18472030
fn test_cast_i32_to_f64() {

0 commit comments

Comments
 (0)