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

expression: fix the corner case of CAST int as unsigned real/decimal #13637

Merged
merged 13 commits into from
Nov 26, 2019
11 changes: 7 additions & 4 deletions expression/builtin_cast.go
Original file line number Diff line number Diff line change
Expand Up @@ -461,9 +461,10 @@ func (b *builtinCastIntAsRealSig) evalReal(row chunk.Row) (res float64, isNull b
if isNull || err != nil {
return res, isNull, err
}
if !mysql.HasUnsignedFlag(b.tp.Flag) && !mysql.HasUnsignedFlag(b.args[0].GetType().Flag) {
if unsignedArgs0 := mysql.HasUnsignedFlag(b.args[0].GetType().Flag); !mysql.HasUnsignedFlag(b.tp.Flag) && !unsignedArgs0 {
Copy link
Contributor

Choose a reason for hiding this comment

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

How about:

	} else if b.inUnion && !unsignedArgs0 && val < 0 {
		// Round up to 0 if the value is negative but the expression eval type is unsigned in `UNION` statement
		// NOTE: the following expressions are equal (so choose the more efficient one):
		// `b.inUnion && mysql.HasUnsignedFlag(b.tp.Flag) && !unsignedArgs0 && val < 0`
		// `b.inUnion && !unsignedArgs0 && val < 0`
		res = 0
	} else {

res = float64(val)
} else if b.inUnion && val < 0 {
// Special case: In union statement, if output is unsigned, but input is signed, input value < 0, then result = 0
} else if b.inUnion && !unsignedArgs0 && val < 0 {
res = 0
} else {
// recall that, int to float is different from uint to float
Expand All @@ -487,9 +488,11 @@ func (b *builtinCastIntAsDecimalSig) evalDecimal(row chunk.Row) (res *types.MyDe
if isNull || err != nil {
return res, isNull, err
}
if !mysql.HasUnsignedFlag(b.tp.Flag) && !mysql.HasUnsignedFlag(b.args[0].GetType().Flag) {

if unsignedArgs0 := mysql.HasUnsignedFlag(b.args[0].GetType().Flag); !mysql.HasUnsignedFlag(b.tp.Flag) && !unsignedArgs0 {
res = types.NewDecFromInt(val)
} else if b.inUnion && val < 0 {
// Special case: In union statement, if output is unsigned, but input is signed, input value < 0, then result = 0
} else if b.inUnion && !unsignedArgs0 && val < 0 {
res = &types.MyDecimal{}
} else {
res = types.NewDecFromUint(uint64(val))
Expand Down
3 changes: 2 additions & 1 deletion expression/builtin_cast_vec.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,8 @@ func (b *builtinCastIntAsRealSig) vecEvalReal(input *chunk.Chunk, result *chunk.
}
if !hasUnsignedFlag0 && !hasUnsignedFlag1 {
rs[i] = float64(i64s[i])
} else if b.inUnion && i64s[i] < 0 {
// Special case: In union statement, if output is unsigned, but input is signed, input value < 0, then result = 0
} else if b.inUnion && !hasUnsignedFlag1 && i64s[i] < 0 {
Copy link
Contributor

Choose a reason for hiding this comment

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

ditto

Copy link
Contributor

Choose a reason for hiding this comment

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

It's in a fop loop, maybe it's acceptable here.

Copy link
Contributor

@lonng lonng Nov 26, 2019

Choose a reason for hiding this comment

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

@SunRunAway I am not mean the local variable, see: #13637 (comment), I mean this logic is not easy to understand.

Copy link
Contributor

Choose a reason for hiding this comment

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

I do not get you. What is your suggestion?

rs[i] = 0
} else {
// recall that, int to float is different from uint to float
Expand Down
16 changes: 16 additions & 0 deletions expression/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2308,6 +2308,22 @@ func (s *testIntegrationSuite2) TestBuiltin(c *C) {
result.Check(testkit.Rows("9223372036854775808 9223372036854775808", "9223372036854775808 9223372036854775808"))
tk.MustExec(`drop table tb5;`)

// test builtinCastIntAsDecimalSig
tk.MustExec(`drop table if exists tb5`)
tk.MustExec(`create table tb5 (a decimal(65), b bigint(64) unsigned);`)
tk.MustExec(`insert into tb5 (a, b) values (9223372036854775808, 9223372036854775808);`)
result = tk.MustQuery(`select cast(b as decimal(64)) from tb5 union all select b from tb5;`)
result.Check(testkit.Rows("9223372036854775808", "9223372036854775808"))
tk.MustExec(`drop table tb5`)

// test builtinCastIntAsRealSig
tk.MustExec(`drop table if exists tb5`)
tk.MustExec(`create table tb5 (a bigint(64) unsigned, b double(64, 10));`)
tk.MustExec(`insert into tb5 (a, b) values (9223372036854775808, 9223372036854775808);`)
result = tk.MustQuery(`select a from tb5 where a = b union all select b from tb5;`)
result.Check(testkit.Rows("9223372036854776000", "9223372036854776000"))
tk.MustExec(`drop table tb5`)

// Test corner cases of cast string as datetime
result = tk.MustQuery(`select cast("170102034" as datetime);`)
result.Check(testkit.Rows("2017-01-02 03:04:00"))
Expand Down