-
Notifications
You must be signed in to change notification settings - Fork 2.2k
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
coprocessor, dag: add more math functions. #2213
Conversation
@@ -835,6 +838,42 @@ pub enum RoundMode { | |||
} | |||
|
|||
impl Decimal { | |||
/// abs the Decimal into a new Decimal. | |||
#[inline] | |||
pub fn abs(&self) -> Res<Decimal> { |
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.
Let's move it instead.
@@ -1428,6 +1466,35 @@ impl Decimal { | |||
Res::Ok(x) | |||
} | |||
|
|||
/// `int_part` returns int part of the decimal. It's temporary and | |||
/// after we adjust `as_i64`, it will be removed. | |||
pub fn int_part(&self, ctx: &EvalContext) -> ::std::result::Result<i64, ExprError> { |
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 don't get it, what's wrong with as_i64
?
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_i64
doesn't retrieve StatementContext
argument, so that if any Truncate
occurs, it can only return it by Res::Truncate
. It causes that we need to test that everywhere (e.g. ceil/floor
and cast
). We need to write code everywhere like this:
fn ceil_dec_to_int(...) {
match dec.as_i64() {
Res::Ok(t) => t,
Truncate(t) => None, // something here.
_ => None, // some other thing.
}
}
So I think we can pass Statement
into as_i64
. If any truncate
occurs inside that function, we just need to check the flag, and return Ok(t)
or Err
directly.
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 you can write a function to deal with both Res
and truncate strategy instead.
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 the old behavior doesn't have to change.
src/coprocessor/dag/expr/mod.rs
Outdated
@@ -228,6 +230,13 @@ impl Expression { | |||
ScalarFuncSig::TimeIsNull => f.time_is_null(ctx, row), | |||
ScalarFuncSig::DurationIsNull => f.duration_is_null(ctx, row), | |||
|
|||
ScalarFuncSig::AbsInt => f.abs_int(ctx, row), | |||
ScalarFuncSig::AbsUInt => f.abs_uint(ctx, row), |
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.
Why not handle unsigned int and signed int together just like plus?
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.
TiDB pushdown both AbsInt
and AbsUint
. So here I follow 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.
Can we always merge int and uint or split them? /cc @XuHuaiyu
|
||
/// ceil the Decimal into a new Decimal. | ||
pub fn ceil(&self) -> Res<Decimal> { | ||
let mut target = if self.frac_cnt > 0 && !self.negative { |
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.
Why not use t.round(0, RoundMode::Ceiling)
directly?
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.
because ceil(-5.5)
should return -5
, but round(-5.5, RoundMode::Ceiling)
will return -6
.
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.
Can we fix the bug in t.round(0, RoundMode::Ceiling)
directly?
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.
Can we fix the bug in t.round(0, RoundMode::Ceiling) directly? @hicqu
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.
round
is used many place, and it's different from math.Ceil
semantically. I prefer use different RoundMode
name, such as RoundUp
and RoundDown
. They are more clearly. If it's OK, I will do it in next PR.
|
||
/// floor the Decimal into a new Decimal. | ||
pub fn floor(&self) -> Res<Decimal> { | ||
let mut target = if self.frac_cnt > 0 && self.negative { |
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.
Why not use t.round(0, RoundMode::Truncate)` directly?
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.
because floor(-5.5)
should return -6
, but that will return -5
.
|
||
/// ceil the Decimal into a new Decimal. | ||
pub fn ceil(&self) -> Res<Decimal> { | ||
let mut target = if self.frac_cnt > 0 && !self.negative { |
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.
Can we fix the bug in t.round(0, RoundMode::Ceiling) directly? @hicqu
pub fn as_i64_with_ctx(&self, ctx: &EvalContext) -> ::std::result::Result<i64, ExprError> { | ||
let res = self.as_i64(); | ||
if ctx.ignore_truncate || ctx.truncate_as_warning { | ||
if let Res::Truncated(ref i) = res { |
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.
Could we use convert::handle_truncate
here since one day we may need to process truncate as warning?
src/coprocessor/dag/expr/math.rs
Outdated
} | ||
|
||
#[inline] | ||
pub fn ceil_int_dec<'a, 'b: 'a>( |
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.
ceil_int_to_dec/ceil_int_as_dec?
@AndreMouche @BusyJay , PTAL, thanks. |
} | ||
|
||
/// ceil the Decimal into a new Decimal. | ||
pub fn ceil(&self) -> Res<Decimal> { |
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.
Any test to cover this?
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, but they are in coprocessor/dag/expr/math.rs
.
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.
They are different. The unit test for this function should focus on its functionality, while tests in math.rs is used to validate if the eval is implemented correctly.
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.
fixed.
("18446744073709551616", "18446744073709551616"), | ||
("-18446744073709551615", "-18446744073709551615"), | ||
("-18446744073709551616", "-18446744073709551616"), | ||
("-1", "-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.
How about 1.000
?
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.
fixed.
@BusyJay , I fixed. PTAL again, thanks. |
|
||
/// ceil the Decimal into a new Decimal. | ||
pub fn ceil(&self) -> Res<Decimal> { | ||
let mut target = if !self.frac_part_is_zero() && !self.negative { |
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.
How about use t.round(0, RoundMode::Ceiling)
when it's positive otherwise use t.round(0, RoundMode::Truncate)
?
|
||
/// floor the Decimal into a new Decimal. | ||
pub fn floor(&self) -> Res<Decimal> { | ||
let mut target = if !self.frac_part_is_zero() && self.negative { |
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.
How about use t.round(0, RoundMode::Truncate)
when it's positive otherwise use t.round(0, RoundMode::Ceiling)
?
pub fn floor(&self) -> Res<Decimal> { | ||
let mut target = if !self.frac_part_is_zero() && self.negative { | ||
let dec1 = Decimal::from(1i64); | ||
self - &dec1 |
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.
Will it overflow/truncate if self is 999999.999999(enough 9)
?
"10000000000000000000000000", | ||
), | ||
]; | ||
for case in cases { |
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.
for (input, exp) in cases
is more readable.
src/coprocessor/dag/expr/math.rs
Outdated
row: &'a [Datum], | ||
) -> Result<Option<Cow<'a, Decimal>>> { | ||
let d = try_opt!(self.children[0].eval_decimal(ctx, row)); | ||
let result: Result<Decimal> = d.as_ref().clone().abs().into(); |
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.
Why not use into_owned
?
|
||
#[inline] | ||
pub fn abs_int(&self, ctx: &StatementContext, row: &[Datum]) -> Result<Option<i64>> { | ||
let n = try_opt!(self.children[0].eval_int(ctx, row)); |
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.
Can it contain unsigned flag?
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 it contains unsigned flag, TiDB will push down AbsUintSig
.
src/coprocessor/dag/expr/math.rs
Outdated
|
||
#[inline] | ||
pub fn ceil_int_to_int(&self, ctx: &StatementContext, row: &[Datum]) -> Result<Option<i64>> { | ||
self.children[0].eval_int(ctx, row) |
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 what's the purpose of this 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.
When parsing SQL, TiDB won't remove ceil
operator on int
, it will calculate that. So it also push CeilIntIntSig
down to TiKV. Although we know it does nothing, but we should deal with 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.
Can optimizer optimize this? /cc @hanfei1991
src/coprocessor/dag/expr/math.rs
Outdated
ctx: &StatementContext, | ||
row: &'a [Datum], | ||
) -> Result<Option<Cow<'a, Decimal>>> { | ||
let n = try_opt!(self.children[0].eval_int(ctx, row)); |
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.
Why not just use cast_int_as_dec
?
src/coprocessor/dag/expr/math.rs
Outdated
} | ||
|
||
#[inline] | ||
pub fn floor_int_dec<'a, 'b: 'a>( |
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.
s/floor_int_dec/floor_int_to_dec/
src/coprocessor/dag/expr/math.rs
Outdated
for tt in tests { | ||
let arg = datum_expr(tt.1); | ||
let op = Expression::build(fncall_expr(tt.0, &[arg]), 0, &ctx).unwrap(); | ||
let expected = Expression::build(datum_expr(tt.2), 0, &ctx).unwrap(); |
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 is redundant. See #2215.
src/coprocessor/dag/expr/mod.rs
Outdated
@@ -228,6 +230,13 @@ impl Expression { | |||
ScalarFuncSig::TimeIsNull => f.time_is_null(ctx, row), | |||
ScalarFuncSig::DurationIsNull => f.duration_is_null(ctx, row), | |||
|
|||
ScalarFuncSig::AbsInt => f.abs_int(ctx, row), | |||
ScalarFuncSig::AbsUInt => f.abs_uint(ctx, row), |
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.
Can we always merge int and uint or split them? /cc @XuHuaiyu
let exp = exp.eval_real(&ctx, &[]).unwrap(); | ||
assert_eq!(got, exp); | ||
} | ||
ScalarFuncSig::CeilIntToInt | ScalarFuncSig::CeilDecToInt => { |
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.
Here I keep old code because op.eval_int(...)
returns an i64
with sign. If we put it into a Datum
, We can't compare it with another Datum::U64(number)
. @BusyJay
@BusyJay , PTAL, thanks. |
src/coprocessor/dag/expr/math.rs
Outdated
ctx: &StatementContext, | ||
row: &'a [Datum], | ||
) -> Result<Option<Cow<'a, Decimal>>> { | ||
self.cast_int_as_decimal(ctx, row) |
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 tidb
should push down CastIntAsDec
instead of CeilIntToDec
to increase match
's efficiency @XuHuaiyu
LGTM |
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
No description provided.