-
Notifications
You must be signed in to change notification settings - Fork 2.1k
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
evalengine: Internal cleanup and consistency fixes #14854
evalengine: Internal cleanup and consistency fixes #14854
Conversation
While working on vitessio#14841 and running some tests, I ran into some other issues / consistency problems in the evalengine that we can clean up and fix. First we use `time.Duration` for intervals, which already provides constants and we don't have to deal with nanoseconds then separately but they are part of durations. We clean up the tinyweight function which does a bunch of casting which works but isn't as clear and would actually break on 32 bit (but we don't support that anyway). It now also returns 0, 1 or -1 which is more how other Go `Cmp` functions work. We remove `int` from `dataOutOfRangeError` since the `evalengine` only works with `int64` or `uint64` anyway, so any usage of `int` would really be a bug (and we didn't deal with `uint` either so it was inconsistent anyway). The bit shift operations also need to operate on int64 explicitly, since that's what the inputs are in the `evalengine`. So we should keep the types consistent. Next, we were missing a now possible optimization which is that we have size for temporal times at compile time. This means we know if we need to convert to integer or decimal. We don't hit the deoptimize path anymore, and now also error hard if that happens since compilation is broken in that case. Lastly we were not dealing with underflow / overflow checks correctly in `FROM_UNIXTIME` between the evaluator and compiler. We need to check before conversions, because specifically float64 to int64 conversions have badly defined behavior for large float64 values. It behaves differently on amd64 vs arm64 vs i386 for example already. Some convert large values to negative ints, others positive or even other values. By checking before casting we avoid this and can behave consistently. Signed-off-by: Dirkjan Bussink <[email protected]>
Review ChecklistHello reviewers! 👋 Please follow this checklist when reviewing this Pull Request. General
Tests
Documentation
New flags
If a workflow is added or modified:
Backward compatibility
|
@@ -559,52 +559,81 @@ func (b *builtinFromUnixtime) eval(env *ExpressionEnv) (eval, error) { | |||
|
|||
switch ts := ts.(type) { | |||
case *evalInt64: | |||
if ts.i < 0 || ts.i >= maxUnixtime { | |||
return nil, nil | |||
} |
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.
It's repetitive, but we have to check up front here. Some fun things:
amd64
Large float turns into negative int64.
int64(1.2345678912345678e300)
-9223372036854775808
arm64
Large float64 turns into max int64.
int64(1.2345678912345678e+300)
9223372036854775807
This means we can't convert before checking overflow / underflow since it won't be consistent.
length = len(num) | ||
bits = int64(shift % 8) | ||
bytes = int64(shift / 8) | ||
length = int64(len(num)) |
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.
bit shifting operates on int64
so we should use that consistently.
Signed-off-by: Dirkjan Bussink <[email protected]>
Signed-off-by: Dirkjan Bussink <[email protected]>
return (dt.Date.Day()-1)*secondsPerDay + dt.Time.toSeconds() | ||
func (dt DateTime) toDuration() time.Duration { | ||
dur := dt.Time.toDuration() | ||
if !dt.Date.IsZero() { |
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.
When we have a zero date (and thus we're a Time type), we don't include the day bit to have a correct duration.
dur := dt.toDuration() | ||
dur += itv.toDuration() | ||
days := time.Duration(0) | ||
if !dt.Date.IsZero() { |
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.
Rounding shouldn't move days if we're a Time instance. We then can have more than 24 hours.
Signed-off-by: Dirkjan Bussink <[email protected]>
Signed-off-by: Dirkjan Bussink <[email protected]>
While working on #14841 and running some tests, I ran into some other issues / consistency problems in the evalengine that we can clean up and fix.
First we use
time.Duration
for intervals, which already provides constants and we don't have to deal with nanoseconds then separately but they are part of durations.We clean up the tinyweight function which does a bunch of casting which works but isn't as clear and would actually break on 32 bit (but we don't support that anyway). It now also returns 0, 1 or -1 which is more how other Go
Cmp
functions work.We remove
int
fromdataOutOfRangeError
since theevalengine
only works withint64
oruint64
anyway, so any usage ofint
would really be a bug (and we didn't deal withuint
either so it was inconsistent anyway).The bit shift operations also need to operate on int64 explicitly, since that's what the inputs are in the
evalengine
. So we should keep the types consistent.Next, we were missing a now possible optimization which is that we have size for temporal times at compile time. This means we know if we need to convert to integer or decimal. We don't hit the deoptimize path anymore, and now also error hard if that happens since compilation is broken in that case.
Lastly we were not dealing with underflow / overflow checks correctly in
FROM_UNIXTIME
between the evaluator and compiler. We need to check before conversions, because specifically float64 to int64 conversions have badly defined behavior for large float64 values. It behaves differently on amd64 vs arm64 vs i386 for example already. Some convert large values to negative ints, others positive or even other values. By checking before casting we avoid this and can behave consistently.Related Issue(s)
Found when working on #14841
Checklist