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

coprocessor, dag: add more math functions. #2213

Merged
merged 29 commits into from
Aug 29, 2017
Merged

Conversation

hicqu
Copy link
Contributor

@hicqu hicqu commented Aug 24, 2017

No description provided.

@@ -835,6 +838,42 @@ pub enum RoundMode {
}

impl Decimal {
/// abs the Decimal into a new Decimal.
#[inline]
pub fn abs(&self) -> Res<Decimal> {
Copy link
Member

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> {
Copy link
Member

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?

Copy link
Contributor Author

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.

Copy link
Member

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.

Copy link
Member

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.

@@ -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),
Copy link
Member

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?

Copy link
Contributor Author

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.

Copy link
Member

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 {
Copy link
Member

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?

Copy link
Contributor Author

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.

Copy link
Member

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?

Copy link
Member

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

Copy link
Contributor Author

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 {
Copy link
Member

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?

Copy link
Contributor Author

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 {
Copy link
Member

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 {
Copy link
Member

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?

}

#[inline]
pub fn ceil_int_dec<'a, 'b: 'a>(
Copy link
Member

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?

@hicqu
Copy link
Contributor Author

hicqu commented Aug 28, 2017

@AndreMouche @BusyJay , PTAL, thanks.

}

/// ceil the Decimal into a new Decimal.
pub fn ceil(&self) -> Res<Decimal> {
Copy link
Member

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?

Copy link
Contributor Author

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.

Copy link
Member

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.

Copy link
Contributor Author

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"),
Copy link
Member

Choose a reason for hiding this comment

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

How about 1.000?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed.

@hicqu
Copy link
Contributor Author

hicqu commented Aug 28, 2017

@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 {
Copy link
Member

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 {
Copy link
Member

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
Copy link
Member

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 {
Copy link
Member

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.

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();
Copy link
Member

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));
Copy link
Member

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?

Copy link
Contributor Author

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.


#[inline]
pub fn ceil_int_to_int(&self, ctx: &StatementContext, row: &[Datum]) -> Result<Option<i64>> {
self.children[0].eval_int(ctx, row)
Copy link
Member

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?

Copy link
Contributor Author

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.

Copy link
Member

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

ctx: &StatementContext,
row: &'a [Datum],
) -> Result<Option<Cow<'a, Decimal>>> {
let n = try_opt!(self.children[0].eval_int(ctx, row));
Copy link
Member

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?

}

#[inline]
pub fn floor_int_dec<'a, 'b: 'a>(
Copy link
Member

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/

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();
Copy link
Member

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.

@@ -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),
Copy link
Member

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 => {
Copy link
Contributor Author

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

@hicqu
Copy link
Contributor Author

hicqu commented Aug 29, 2017

@BusyJay , PTAL, thanks.

ctx: &StatementContext,
row: &'a [Datum],
) -> Result<Option<Cow<'a, Decimal>>> {
self.cast_int_as_decimal(ctx, row)
Copy link
Member

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

@BusyJay
Copy link
Member

BusyJay commented Aug 29, 2017

LGTM

Copy link
Member

@AndreMouche AndreMouche left a comment

Choose a reason for hiding this comment

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

LGTM

@AndreMouche AndreMouche merged commit 41d612e into master Aug 29, 2017
@AndreMouche AndreMouche deleted the qupeng/expr-math branch August 29, 2017 13:37
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.

3 participants