-
Notifications
You must be signed in to change notification settings - Fork 1.3k
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
fix(expr-common): Coerce to Decimal(20, 0) when combining UInt64 with signed integers #14223
base: main
Are you sure you want to change the base?
Conversation
… signed integers Previously, when combining UInt64 with any signed integer, the resulting type would be Int64, which would result in lost information. Now, combining UInt64 with a signed integer results in a Decimal(20, 0), which is able to encode all (64-bit) integer types.
// accommodates all values of both types. Note that to avoid information | ||
// loss when combining UInt64 with signed integers we use Decimal128(20, 0). | ||
(Decimal128(20, 0), _) | ||
| (_, Decimal128(20, 0)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks not correct. For example, combining Decimal128(20, 0)
with Decimal128(30, 0)
should not result in Decimal128(20, 0)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think when both types are decimal they are handled above before this match, when calling the decimal_coercion
function.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I rechecked it, and decimal_coercion
already covers them in
datafusion/datafusion/expr-common/src/type_coercion/binary.rs
Lines 932 to 940 in 2f28327
fn coerce_numeric_type_to_decimal(numeric_type: &DataType) -> Option<DataType> { | |
use arrow::datatypes::DataType::*; | |
// This conversion rule is from spark | |
// https://github.com/apache/spark/blob/1c81ad20296d34f137238dadd67cc6ae405944eb/sql/catalyst/src/main/scala/org/apache/spark/sql/types/DecimalType.scala#L127 | |
match numeric_type { | |
Int8 => Some(Decimal128(3, 0)), | |
Int16 => Some(Decimal128(5, 0)), | |
Int32 => Some(Decimal128(10, 0)), | |
Int64 => Some(Decimal128(20, 0)), |
Although it doesn't handle unsigned integer types, we can supplement it there, maybe as a follow-up PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So I think we shouldn't combine decimal with integer types here because decimal_coercion
has already handled it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried that initially but then it would not handle UInt64
and Decimal128(20, 0)
:
Cannot infer common argument type for comparison operation UInt64 = Decimal128(20, 0)
So maybe it would be best to add new arms to the coerce_numeric_type_to_decimal
to include unsigned integers as well?
match numeric_type {
Int8 => Some(Decimal128(3, 0)),
Int16 => Some(Decimal128(5, 0)),
Int32 => Some(Decimal128(10, 0)),
Int64 => Some(Decimal128(20, 0)),
Float32 => Some(Decimal128(14, 7)),
Float64 => Some(Decimal128(30, 15)),
_ => None,
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So maybe it would be best to add new arms to the
coerce_numeric_type_to_decimal
to include unsigned integers as well?
I think so.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
Perhaps we should add a sqllogictest test for #14208. |
Done. |
| (UInt32, Int8) | ||
| (Int8, UInt32) | ||
| (UInt32, Int16) | ||
| (Int16, UInt32) | ||
| (UInt32, Int32) | ||
| (Int32, UInt32) => Some(Int64), | ||
(UInt64, _) | (_, UInt64) => Some(UInt64), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need to keep this arm because it can coerce UInt64 vs UInt8/16/32. The following query returns an incorrect result on this PR.
DataFusion CLI v44.0.0
> select arrow_typeof(column1) from values(arrow_cast(1, 'UInt8')), (arrow_cast(1, 'UInt64'));
+-----------------------+
| arrow_typeof(column1) |
+-----------------------+
| UInt8 |
| UInt8 |
+-----------------------+
2 row(s) fetched.
Elapsed 0.007 seconds.
> select arrow_typeof(column1) from values(arrow_cast(1, 'UInt8')), (arrow_cast(1000, 'UInt64'));
Arrow error: Cast error: Can't cast value 1000 to type UInt8
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done. I also added a new test to check that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, thank you @nuno-faria
Previously, when combining
UInt64
with any signed integer, the resulting type would beInt64
, which would result in lost information. Now, combiningUInt64
with a signed integer results in aDecimal(20, 0)
, which is able to encode all (64-bit) integer types. Thanks @jonahgao for the pointers.The function
bitwise_coercion
remains the same, since it's probably not a good idea to introduce decimals when performing bitwise operations. In this case, it converts(UInt64 | _)
toUInt64
.Which issue does this PR close?
Closes #14208.
What changes are included in this PR?
binary_numeric_coercion
inexpr-common/type_coercion/binary.rs
.expr-common/type_coercion/binary.rs
.Are these changes tested?
Yes.
Are there any user-facing changes?
No.