Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support function to_decimal(string). #10369

Merged
merged 12 commits into from
Mar 7, 2023
1 change: 1 addition & 0 deletions src/query/expression/src/type_check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -517,6 +517,7 @@ pub fn can_auto_cast_to(
.all(|(src_ty, dest_ty)| can_auto_cast_to(src_ty, dest_ty, auto_cast_rules))
}
(DataType::Number(n), DataType::Decimal(_)) if !n.is_float() => true,
(DataType::String, DataType::Decimal(_)) => true,
youngsofun marked this conversation as resolved.
Show resolved Hide resolved
(DataType::Decimal(x), DataType::Decimal(y)) => x.precision() <= y.precision(),
(DataType::Decimal(_), DataType::Number(NumberDataType::Float32)) => true,
(DataType::Decimal(_), DataType::Number(NumberDataType::Float64)) => true,
Expand Down
10 changes: 10 additions & 0 deletions src/query/expression/src/types/decimal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,8 @@ pub trait Decimal:
Self::to_column_from_buffer(value.into(), size)
}

fn to_scalar(self, size: DecimalSize) -> DecimalScalar;

fn with_size(&self, size: DecimalSize) -> Option<Self> {
let multiplier = Self::e(size.scale as u32);
let min_for_precision = Self::min_for_precision(size.precision);
Expand Down Expand Up @@ -392,6 +394,10 @@ impl Decimal for i128 {
self as f64 / div
}

fn to_scalar(self, size: DecimalSize) -> DecimalScalar {
DecimalScalar::Decimal128(self, size)
}

fn try_downcast_column(column: &Column) -> Option<(Buffer<Self>, DecimalSize)> {
let column = column.as_decimal()?;
match column {
Expand Down Expand Up @@ -529,6 +535,10 @@ impl Decimal for i256 {
self.as_f64() / div
}

fn to_scalar(self, size: DecimalSize) -> DecimalScalar {
DecimalScalar::Decimal256(self, size)
}

fn try_downcast_column(column: &Column) -> Option<(Buffer<Self>, DecimalSize)> {
let column = column.as_decimal()?;
match column {
Expand Down
91 changes: 84 additions & 7 deletions src/query/functions/src/scalars/decimal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ use std::ops::*;
use std::sync::Arc;

use common_arrow::arrow::buffer::Buffer;
use common_expression::read_decimal_with_size;
use common_expression::types::decimal::*;
use common_expression::types::string::StringColumn;
use common_expression::types::*;
use common_expression::with_integer_mapped_type;
use common_expression::Column;
Expand Down Expand Up @@ -381,10 +383,12 @@ pub fn register(registry: &mut FunctionRegistry) {
if args_type.len() != 1 {
return None;
}
if !args_type[0].is_decimal() && !args_type[0].is_numeric() {
if !matches!(
args_type[0],
DataType::Number(_) | DataType::Decimal(_) | DataType::String
) {
return None;
}

if params.len() != 2 {
return None;
}
Expand Down Expand Up @@ -468,13 +472,86 @@ fn convert_to_decimal(
dest_type: DataType,
) -> Value<AnyType> {
let arg = &args[0];
if from_type.is_integer() {
return integer_to_decimal(arg, ctx, from_type, dest_type);
match from_type {
DataType::Number(ty) => {
if ty.is_float() {
float_to_decimal(arg, ctx, from_type, dest_type)
} else {
integer_to_decimal(arg, ctx, from_type, dest_type)
}
}
DataType::Decimal(_) => decimal_to_decimal(arg, ctx, from_type, dest_type),
DataType::String => string_to_decimal(arg, ctx, dest_type),
_ => unreachable!("to_decimal not support this DataType"),
}
}

fn string_to_decimal_column<T: Decimal>(
ctx: &mut EvalContext,
string_column: &StringColumn,
size: DecimalSize,
) -> DecimalColumn {
let mut values = Vec::<T>::with_capacity(string_column.len());
for (row, buf) in string_column.iter().enumerate() {
match read_decimal_with_size::<T>(buf, size, true) {
Ok((d, _)) => values.push(d),
Err(e) => {
ctx.set_error(row, format!("fail to decode string as decimal: {e}"));
values.push(T::zero())
}
}
}
if from_type.is_floating() {
return float_to_decimal(arg, ctx, from_type, dest_type);
T::to_column(values, size)
}

fn string_to_decimal_scalar<T: Decimal>(
ctx: &mut EvalContext,
string_buf: &[u8],
size: DecimalSize,
) -> DecimalScalar {
let value = match read_decimal_with_size::<T>(string_buf, size, true) {
Ok((d, _)) => d,
Err(e) => {
ctx.set_error(0, format!("fail to decode string as decimal: {e}"));
T::zero()
}
};
T::to_scalar(value, size)
}

fn string_to_decimal(
arg: &ValueRef<AnyType>,
ctx: &mut EvalContext,
dest_type: DataType,
) -> Value<AnyType> {
let dest_type = dest_type.as_decimal().unwrap();

match arg {
ValueRef::Column(column) => {
let string_column = StringType::try_downcast_column(column).unwrap();
let column = match dest_type {
DecimalDataType::Decimal128(size) => {
string_to_decimal_column::<i128>(ctx, &string_column, *size)
}
DecimalDataType::Decimal256(size) => {
string_to_decimal_column::<i256>(ctx, &string_column, *size)
}
};
Value::Column(Column::Decimal(column))
}
ValueRef::Scalar(scalar) => {
let buf = StringType::try_downcast_scalar(scalar).unwrap();
let scalar = match dest_type {
DecimalDataType::Decimal128(size) => {
string_to_decimal_scalar::<i128>(ctx, buf, *size)
}
DecimalDataType::Decimal256(size) => {
string_to_decimal_scalar::<i128>(ctx, buf, *size)
}
};
Value::Scalar(Scalar::Decimal(scalar))
}
}
decimal_to_decimal(arg, ctx, from_type, dest_type)
}

fn integer_to_decimal(
Expand Down
2 changes: 2 additions & 0 deletions src/query/functions/src/scalars/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,8 @@ pub const CAST_FROM_STRING_RULES: AutoCastRules = &[
(DataType::String, DataType::Number(NumberDataType::Int16)),
(DataType::String, DataType::Number(NumberDataType::Int32)),
(DataType::String, DataType::Number(NumberDataType::Int64)),
(DataType::String, DataType::Number(NumberDataType::Float32)),
(DataType::String, DataType::Number(NumberDataType::Float64)),
];

#[allow(non_snake_case)]
Expand Down
34 changes: 34 additions & 0 deletions src/query/functions/tests/it/scalars/cast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ fn test_cast() {
test_cast_number_to_date(file, is_try);
test_cast_between_number_and_string(file, is_try);
test_cast_between_boolean_and_string(file, is_try);
test_cast_between_string_and_decimal(file, is_try);
test_cast_between_number_and_boolean(file, is_try);
test_cast_between_date_and_timestamp(file, is_try);
test_cast_between_string_and_timestamp(file, is_try);
Expand Down Expand Up @@ -622,3 +623,36 @@ fn test_cast_to_nested_type(file: &mut impl Write, is_try: bool) {
&[],
);
}

fn test_cast_between_string_and_decimal(file: &mut impl Write, is_try: bool) {
let prefix = if is_try { "TRY_" } else { "" };

run_ast(file, format!("{prefix}CAST('010.010' AS DECIMAL(5,3))"), &[
]);
run_ast(file, format!("{prefix}CAST('010.010' AS DECIMAL(5,4))"), &[
]);
run_ast(file, format!("{prefix}CAST('010.010' AS DECIMAL(5,2))"), &[
]);
run_ast(file, format!("{prefix}CAST('010.010' AS DECIMAL(4,3))"), &[
]);
run_ast(file, format!("{prefix}CAST('010.010' AS DECIMAL(4,2))"), &[
]);
run_ast(
file,
format!("{prefix}CAST('-1010.010' AS DECIMAL(7,3))"),
&[],
);
run_ast(file, format!("{prefix}CAST('00' AS DECIMAL(2,1))"), &[]);
run_ast(file, format!("{prefix}CAST('0.0' AS DECIMAL(2,0))"), &[]);
run_ast(file, format!("{prefix}CAST('.0' AS DECIMAL(1,0))"), &[]);
run_ast(
file,
format!("{prefix}CAST('+1.0e-10' AS DECIMAL(11, 10))"),
&[],
);
run_ast(
file,
format!("{prefix}CAST('-1.0e+10' AS DECIMAL(11, 0))"),
&[],
);
}
50 changes: 22 additions & 28 deletions src/query/functions/tests/it/scalars/testdata/array.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1756,19 +1756,17 @@ error:
--> SQL:1:12
|
1 | array_sort(['x', 0, 1.2, 3.4, 5.6, 7.8])
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unable to cast type `String` to type `Decimal(2, 1)`
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ fail to decode string as decimal: Code: 1006, displayText = bad decimal literal: unexpected char. while evaluating function `to_decimal("x")`
sundy-li marked this conversation as resolved.
Show resolved Hide resolved



error:
--> SQL:1:12
|
1 | array_sort([1.2, NULL, 3.4, 5.6, '2.2', NULL], 'DESC', 'NULLS FIRST')
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no overload satisfies `array(Decimal(2, 1), NULL, Decimal(2, 1), Decimal(2, 1), String, NULL)`

has tried possible overloads:
array(T0, T0, T0, T0, T0, T0) :: Array(T0) : unable to find a common super type for `Decimal(2, 1) NULL` and `String`

ast : array_sort([1.2, NULL, 3.4, 5.6, '2.2', NULL], 'DESC', 'NULLS FIRST')
raw expr : array_sort_desc_null_first(array(1.2_decimal(2, 1), NULL, 3.4_decimal(2, 1), 5.6_decimal(2, 1), "2.2", NULL))
checked expr : array_sort_desc_null_first<T0=Decimal(2, 1) NULL><Array(T0)>(array<T0=Decimal(2, 1) NULL><T0, T0, T0, T0, T0, T0>(CAST(1.2_d128(2,1) AS Decimal(2, 1) NULL), CAST(NULL AS Decimal(2, 1) NULL), CAST(3.4_d128(2,1) AS Decimal(2, 1) NULL), CAST(5.6_d128(2,1) AS Decimal(2, 1) NULL), CAST("2.2" AS Decimal(2, 1) NULL), CAST(NULL AS Decimal(2, 1) NULL)))
optimized expr : [NULL, NULL, 5.6, 3.4, 2.2, 1.2]
output type : Array(Decimal(2, 1) NULL)
output domain : [{0.0..=5.6} ∪ {NULL}]
output : [NULL, NULL, 5.6, 3.4, 2.2, 1.2]


ast : array_sort([], 'DESC', 'NULLS FIRST')
Expand All @@ -1780,15 +1778,13 @@ output domain : []
output : []


error:
--> SQL:1:12
|
1 | array_sort([1.2, NULL, 3.4, 5.6, '2.2', NULL], 'DESC', 'NULLS LAST')
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no overload satisfies `array(Decimal(2, 1), NULL, Decimal(2, 1), Decimal(2, 1), String, NULL)`

has tried possible overloads:
array(T0, T0, T0, T0, T0, T0) :: Array(T0) : unable to find a common super type for `Decimal(2, 1) NULL` and `String`

ast : array_sort([1.2, NULL, 3.4, 5.6, '2.2', NULL], 'DESC', 'NULLS LAST')
raw expr : array_sort_desc_null_last(array(1.2_decimal(2, 1), NULL, 3.4_decimal(2, 1), 5.6_decimal(2, 1), "2.2", NULL))
checked expr : array_sort_desc_null_last<T0=Decimal(2, 1) NULL><Array(T0)>(array<T0=Decimal(2, 1) NULL><T0, T0, T0, T0, T0, T0>(CAST(1.2_d128(2,1) AS Decimal(2, 1) NULL), CAST(NULL AS Decimal(2, 1) NULL), CAST(3.4_d128(2,1) AS Decimal(2, 1) NULL), CAST(5.6_d128(2,1) AS Decimal(2, 1) NULL), CAST("2.2" AS Decimal(2, 1) NULL), CAST(NULL AS Decimal(2, 1) NULL)))
optimized expr : [5.6, 3.4, 2.2, 1.2, NULL, NULL]
output type : Array(Decimal(2, 1) NULL)
output domain : [{0.0..=5.6} ∪ {NULL}]
output : [5.6, 3.4, 2.2, 1.2, NULL, NULL]


ast : array_sort([], 'DESC', 'NULLS LAST')
Expand All @@ -1800,15 +1796,13 @@ output domain : []
output : []


error:
--> SQL:1:12
|
1 | array_sort([1.2, NULL, 3.4, 5.6, '2.2', NULL], 'ASC', 'NULLS FIRST')
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no overload satisfies `array(Decimal(2, 1), NULL, Decimal(2, 1), Decimal(2, 1), String, NULL)`

has tried possible overloads:
array(T0, T0, T0, T0, T0, T0) :: Array(T0) : unable to find a common super type for `Decimal(2, 1) NULL` and `String`

ast : array_sort([1.2, NULL, 3.4, 5.6, '2.2', NULL], 'ASC', 'NULLS FIRST')
raw expr : array_sort_asc_null_first(array(1.2_decimal(2, 1), NULL, 3.4_decimal(2, 1), 5.6_decimal(2, 1), "2.2", NULL))
youngsofun marked this conversation as resolved.
Show resolved Hide resolved
checked expr : array_sort_asc_null_first<T0=Decimal(2, 1) NULL><Array(T0)>(array<T0=Decimal(2, 1) NULL><T0, T0, T0, T0, T0, T0>(CAST(1.2_d128(2,1) AS Decimal(2, 1) NULL), CAST(NULL AS Decimal(2, 1) NULL), CAST(3.4_d128(2,1) AS Decimal(2, 1) NULL), CAST(5.6_d128(2,1) AS Decimal(2, 1) NULL), CAST("2.2" AS Decimal(2, 1) NULL), CAST(NULL AS Decimal(2, 1) NULL)))
optimized expr : [NULL, NULL, 1.2, 2.2, 3.4, 5.6]
output type : Array(Decimal(2, 1) NULL)
output domain : [{0.0..=5.6} ∪ {NULL}]
output : [NULL, NULL, 1.2, 2.2, 3.4, 5.6]


ast : array_sort([], 'ASC', 'NULLS FIRST')
Expand Down
Loading