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
78 changes: 71 additions & 7 deletions src/query/functions/src/scalars/decimal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,14 @@ 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;
use common_expression::ColumnBuilder;
use common_expression::DecimalDeserializer;
use common_expression::EvalContext;
use common_expression::Function;
use common_expression::FunctionDomain;
Expand Down Expand Up @@ -381,10 +384,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 +473,72 @@ 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, from_type, dest_type),
_ => unreachable!("to_decimal not support this DataType"),
}
if from_type.is_floating() {
return float_to_decimal(arg, ctx, from_type, dest_type);
}

fn string_to_decimal_internal<T: Decimal>(
ctx: &mut EvalContext,
string_column: &StringColumn,
size: DecimalSize,
dest_type: &DecimalDataType,
) -> DecimalColumn {
let mut builder = DecimalDeserializer::<T>::with_capacity(dest_type, string_column.len());
youngsofun marked this conversation as resolved.
Show resolved Hide resolved
for (row, buf) in string_column.iter().enumerate() {
match read_decimal_with_size::<T>(buf, size, true) {
Ok((d, _)) => builder.values.push(d),
Err(e) => {
ctx.set_error(row, format!("fail to decode string as decimal: {e}"));
builder.values.push(T::zero())
}
}
}
T::to_column(std::mem::take(&mut builder.values), size)
}

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

let mut is_scalar = false;
let column = match arg {
ValueRef::Column(column) => column.clone(),
ValueRef::Scalar(s) => {
is_scalar = true;
let builder = ColumnBuilder::repeat(s, 1, &from_type);
b41sh marked this conversation as resolved.
Show resolved Hide resolved
builder.build()
}
};
let string_column = StringType::try_downcast_column(&column).unwrap();
let result = match dest_type {
DecimalDataType::Decimal128(size) => {
string_to_decimal_internal::<i128>(ctx, &string_column, *size, dest_type)
}
DecimalDataType::Decimal256(size) => {
string_to_decimal_internal::<i256>(ctx, &string_column, *size, dest_type)
}
};

if is_scalar {
let scalar = result.index(0).unwrap();
Value::Scalar(Scalar::Decimal(scalar))
} else {
Value::Column(Column::Decimal(result))
}
decimal_to_decimal(arg, ctx, from_type, dest_type)
}

fn integer_to_decimal(
Expand Down
2 changes: 1 addition & 1 deletion src/query/functions/tests/it/scalars/testdata/array.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1756,7 +1756,7 @@ 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



Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,51 @@
# 1e76 - 1
query I
select 9999999999999999999999999999999999999999999999999999999999999999999999999999;
----
9999999999999999999999999999999999999999999999999999999999999999999999999999

# 1e76 - 1
query I
select -9999999999999999999999999999999999999999999999999999999999999999999999999999;
----
-9999999999999999999999999999999999999999999999999999999999999999999999999999

query I
select typeof(9999999999999999999999999999999999999999999999999999999999999999999999999999);
----
DECIMAL(76, 0)

query I
select typeof(-9999999999999999999999999999999999999999999999999999999999999999999999999999);
----
DECIMAL(76, 0)

query I
select typeof(999999999999999999999999999999999999999999999999999999999999999999999999999.9);
----
DECIMAL(76, 1)

query I
select typeof(1e76);
----
DOUBLE

# 1e38 - 1
query I
select 99999999999999999999999999999999999999;
----
99999999999999999999999999999999999999

query I
select typeof(99999999999999999999999999999999999999);
----
DECIMAL(38, 0)

query I
select typeof(1e38);
----
DECIMAL(39, 0)

statement ok
drop table if exists t;

Expand Down Expand Up @@ -61,6 +109,13 @@ select 2::Decimal(2000, 3);
statement error 1001
select 20000::Decimal(4, 3);

query I
select '010.010'::decimal(6,4);
----
10.0100

statement error 1001
select '010.010'::decimal(5,4);

## tests from chatgpt
## some result should be modified if we parse 1.23 as Decimal instead of float by default, cc @yangsongfeng
Expand Down