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

Throw exception for cast of nan and infinity to int types #22917

Merged
merged 1 commit into from
Jun 5, 2024

Conversation

rschlussel
Copy link
Contributor

@rschlussel rschlussel commented Jun 4, 2024

Description

Fix cast of nan and infinity from DOUBLE/REAL to
BIGINT/INT/SMALLINT/TINYTINT. Previously all except double -> bigint would return zero. Now they will all throw an INVALID_CAST_ARGUMENT exception.

Fix silent overflow for casting from REAL to BIGINT type. We now throw
an INVALID_CAST_ARGUMENT if the value is out of the BIGINT range.

Change error code from NUMERIC_VALUE_OUT_OF_RANGE to
INVALID_CAST_ARGUMENT for out of range values in casts from floating
point to integer types. With the library we now use for the
implementaiton for these casts, we can't always tell by the exception
type that the value is out of range, so we return the more generic, but
still relevant INVALID_CAST_ARGUMENT instead.

Motivation and Context

Fixes #22910.
Nan and infinity can't be represented as an integer, so casting to an int type should throw an error.

This also brings Presto and velox behavior in alignment. We are choosing to modify Presto in this case because velox behavior is correct here (we would want to change Presto regardless of the native worker migration)

Impact

CAST of nan and infinity to BIGINT, INTEGER, SMALLINT, and TINYINT types will now return an exception with the INVALID_CAST_ARGUMENT error code

Cast of out of range values for DOUBLE or FLOAT to BIGINT, INTEGER, SMALLINT, or TINYINT will now return an INVALID_CAST_ARGUMENT error code rather than NUMERIC_VALUE_OUT_OF_RANGE error code

CAST of real values outside of BIGINT range will now return a NUMERIC_VALUE_OUT_OF_RANGE error. Previously they would silently overflow.

Test Plan

unit tests

Contributor checklist

  • Please make sure your submission complies with our development, formatting, commit message, and attribution guidelines.
  • PR description addresses the issue accurately and concisely. If the change is non-trivial, a GitHub Issue is referenced.
  • Documented new properties (with its default value), SQL syntax, functions, or other functionality.
  • If release notes are required, they follow the release notes guidelines.
  • Adequate tests were added if applicable.
  • CI passed.

Release Notes

Please follow release notes guidelines and fill in the release notes below.

== RELEASE NOTES ==

General Changes
* Fix cast of NaN and Infinity from DOUBLE or REAL to  BIGINT, INTEGER, SMALLINT, and TINYINT. It will now return an exception with the INVALID_CAST_ARGUMENT error code. Previously it would return zero.
* Fix CAST of REAL values outside of BIGINT range to return an exception with an INVALID_CAST_ARGUMENT error code. Previously they would silently overflow.
* Change error code for cast from DOUBLE or REAL to BIGINT, INTEGER, SMALLINT or TINYINT for out of range values from ``NUMERIC_VALUE_OUT_OF_RANGE`` to ``INVALID_CAST_ARGUMENT``.

@ClarenceThreepwood
Copy link
Contributor

This also brings Presto and velox behavior in alignment. We are choosing to modify Presto in this case because velox behavior is correct here (we would want to change Presto regardless of the native worker migration)

Another data point - this is also the approach that most other db engines take (e.g. Postgres)

Copy link
Contributor

@elharo elharo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Keying off exception messages is very brittle.

}
catch (ArithmeticException e) {
throw new PrestoException(NUMERIC_VALUE_OUT_OF_RANGE, "Out of range for integer: " + value, e);
if (e.getMessage().equals("not in range")) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How can this be done without relying on undocumented exception messages that are subject to change between JDKs and JDK versions?

If it can't, I'd drop the conditional and simply always use an INVALID_CAST_ARGUMENT

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, I didn't love it, but didn't see other options.. I'll switch to always using INVALID_CAST_ARGUMENT instead.

return Shorts.checkedCast(DoubleMath.roundToInt(value, HALF_UP));
}
catch (ArithmeticException e) {
if (e.getMessage().equals("not in range")) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

return SignedBytes.checkedCast(DoubleMath.roundToInt(value, HALF_UP));
}
catch (ArithmeticException e) {
if (e.getMessage().equals("not in range")) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

return DoubleMath.roundToLong(intBitsToFloat((int) value), HALF_UP);
}
catch (ArithmeticException e) {
if (e.getMessage().equals("not in range")) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

}
catch (ArithmeticException e) {
throw new PrestoException(NUMERIC_VALUE_OUT_OF_RANGE, "Out of range for integer: " + value, e);
if (e.getMessage().equals("not in range")) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

Fix cast of nan and infinity from DOUBLE/REAL to
BIGINT/INT/SMALLINT/TINYTINT.  Previously all except double -> bigint
would return zero.  Now they will all throw an INVALID_CAST_ARGUMENT
exception.

Fix silent overflow for casting from REAL to BIGINT type. We now throw
an INVALID_CAST_ARGUMENT if the value is out of the BIGINT range.

Change error code from NUMERIC_VALUE_OUT_OF_RANGE to
INVALID_CAST_ARGUMENT for out of range values in casts from floating
point to integer types.  With the library we now use for the
implementaiton for these casts, we can't always tell by the exception
type that the value is out of range, so we return the more generic, but
still relevant INVALID_CAST_ARGUMENT instead.
@rschlussel rschlussel merged commit b5b0dd8 into prestodb:master Jun 5, 2024
56 checks passed
@wanglinsong wanglinsong mentioned this pull request Jun 25, 2024
36 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Presto silently converts NaN to zero when casting it to an INTEGER but throws when casting to BIGINT.
4 participants