-
Notifications
You must be signed in to change notification settings - Fork 2.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
[CALCITE-6111] Explicit cast from expression to numeric type doesn't check overflow #3522
Conversation
The CI failure is from the Druid tests, so I guess this PR is ready for review. |
9d2cf51
to
617867f
Compare
I already realize the custom logic for such a cases, you can take only tests, check: |
These tests are very useful. They cover many more cases than this PR handles. Here we only do casts. |
".* out of range", true); | ||
f.checkFails("SELECT cast(1e60+30 as int)", | ||
".* out of range", true); | ||
f.checkFails("SELECT cast(1e60+30 as bigint)", |
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.
what about: SELECT CAST(2147483648 as BIGINT) ?
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.
These cases should be covered by the test testCastExactNumericLimits
, where I re-enabled some of the tests that were disabled.
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.
Well, this cast in particular should not fail, so I guess it's not covered by the NumericLimits test.
I have checked that this test would pass.
Are you suggesting adding tests where I use the numeric limits for a type (INT) when casting to a different type (BIGINT)?
This is a bugfix PR, can someone please review it? |
* Explicit cast from expression to numeric type doesn't check overflow</a>. */ | ||
@Test public void testOverflow() { | ||
final SqlOperatorFixture f = fixture(); | ||
f.checkFails("SELECT cast(100+30 as tinyint)", |
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.
as for me - it`s hard to parse such a tests.
f.checkFails("SELECT cast(100+30 as tinyint)", | |
f.checkFails(String.format("SELECT cast(%d+30 as tinyint)", Byte.MAX_VALUE), |
and the same for all near
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.
what about implicit casts, is it all ok there ? or it out of scope ?
SELECT 9223372036854775807 + 1;
SELECT 2147483647 + 1;
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 PR is only about casts. I plan a follow up PR about arithmetic, which has been broken for many years. There are many disabled tests because of this. That PR will handle the expressions you describe.
Regarding implicit casts: in a normal compiler, after validation the compiler should convert all implicit casts into explicit casts. This leaves many fewer cases to worry about for the subsequent stages. Unfortunately it doesn't look like Calcite works this way. I don't know enough about validation to promise I will fix this part.
However, implicit casts usually convert values from a narrow type to a wider type, so they should not be a cause of runtime failures.
But only writing lots of tests will ensure that all cases are properly handled.
aadcdcd
to
4325f1e
Compare
@zstan I have rewritten the tests you suggested to make the intent clearer. |
@zstan, I would like to merge this PR, since the fixes I plan to address depend on these being merged. |
@mihaibudiu honestly, overall looks good but as for me this issue need additional eyes here, can you found one more reviewer on dev list ? |
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, few comments but nothing major
testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java
Outdated
Show resolved
Hide resolved
f.checkCastFails("'" + numeric.maxOverflowNumericString + "'", | ||
type, OUT_OF_RANGE_MESSAGE, true, castType); | ||
type, "Number has wrong format.*", true, castType); |
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.
Nit: maybe we can introduce a variable WRONG_FORMAT_MESSAGE
in the same spirit of OUT_OF_RANGE_MESSAGE
and others?
testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java
Outdated
Show resolved
Hide resolved
f.checkFails(String.format(Locale.US, "SELECT cast(%d+30 as tinyint)", Byte.MAX_VALUE), | ||
".* out of range", true); |
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 to see what happens if I locally change expectedError to something else...
Surprisingly the tests continue passing
it seems that the reason is that this method is called
calcite/testkit/src/main/java/org/apache/calcite/test/SqlOperatorFixtureImpl.java
Lines 157 to 171 in 0bec957
@Override public void checkFails(StringAndPos sap, String expectedError, | |
boolean runtime) { | |
final String sql = "values (" + sap.addCarets() + ")"; | |
if (runtime) { | |
// We need to test that the expression fails at runtime. | |
// Ironically, that means that it must succeed at prepare time. | |
SqlValidator validator = factory.createValidator(); | |
SqlNode n = parseAndValidate(validator, sql); | |
assertNotNull(n); | |
tester.checkFails(factory, sap, expectedError, runtime); | |
} else { | |
checkQueryFails(StringAndPos.of(sql), | |
expectedError); | |
} | |
} |
which means it checks for failure only if it is not runtime...
so it could be any expected message and any number under cast
and it continues passing...
Or did I do something wrong?
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.
@snuyanzin, I think it should fail as you say, there seems to be something wrong.
I also tried changing runtime
to false
just to see, in this way it always fails (with a cast with overflow, and cast without, in both cases parsing succeeds, so the check fail throws an error), I guess there is something broken in the test auxiliary methods we rely on here.
The source of the problem lies outside the present PR, but it's nonetheless a blocker for merging (if confirmed), as we can't rely on the unit tests.
Maybe @mihaibudiu has more familiarity with this part of the code, and can shed some light.
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 was expecting these tests to exercise the new code added to RexToLixTranslator
, but this doesn't seem to be the case (a breakpoint there isn't hit when debugging such tests).
If that's a wrong assumption, what tests are covering the new code added to RexToLixTranslator
, and what part of the code is specifically tested by above tests?
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.
If you call the test from SqlOperatorTest it doesn't involve the evaluation.
You have to call the test from CalciteSqlOperatorTest. That class extends SqlOperatorTest but uses a test fixture which also evaluates the expressions. In the IDE you can do this easiest if you paste the following in CalciteSqlOperatorTest:
@Test
public void testOverflow() {
super.testOverflow();
}
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.
Thanks for the refresher Mihai, I always forget about this behaviour in some of our test classes.
I have verified the tests, they correctly fail if a non-overflowing value/expression is passed, so they do their job.
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.
Was able to test, thanks for clarification
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.
After @snuyanzin's comment I feel we need to get to the bottom of the test issue before merging, therefore marking the PR as "request changes" until clarified
I have made the fixes you suggested. The tests should be run from the class CalciteSqlOperatorTest to exercise the evaluator. |
Thanks Mihai, the changes and the patch LGTM, can you please address the Sonar violation regarding the repeated occurrence of the string PS: please don't force-push while the review process is still on-going, it makes reviewers' life more difficult for not much gain, thanks! |
// Casting a floating point value larger than the maximum allowed value. | ||
// 1e60 is larger than the largest BIGINT value allowed. | ||
f.checkFails("SELECT cast(1e60+30 as tinyint)", | ||
".* out of range", true); |
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.
nit:
since there is already existing org.apache.calcite.sql.test.SqlOperatorFixture#OUT_OF_RANGE_MESSAGE
would it make sense to reuse it here?
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.
My last commit does exactly that. It removes one sonarcloud complaint, as pointed out by @asolimando.
The other sonarcloud complaint is about a complex method which I didn't really write, but I slightly modified, so I didn't try to reduce the complexity. This is after all just a test method.
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.
Yes, Sonar has false positives sometimes, it's fine to address only what's reasonable (I consider fixing the complexity of an existing test method as as nice-to-have and totally optional), but most of the time it provides good hints for keeping the code quality high.
Quality Gate passedThe SonarCloud Quality Gate passed, but some issues were introduced. 1 New issue |
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, Mihai!
…check overflow Signed-off-by: Mihai Budiu <mbudiu@feldera.com>
This also fixes a few more tests which were disabled in SqlOperatorTests.
I still have to handle casts to decimal values, but hopefully this fixes the behavior of casts to integer types.
This behavior also has to be documented, so a future PR will describe in more details how Calcite evaluates casts.