types: fix the behavior when inserting a big scientific notation number to keep it same with mysql (#47803)
close pingcap/tidb#47787
This commit is contained in:
@ -1593,3 +1593,25 @@ func TestInsertLockUnchangedKeys(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// see issue https://github.com/pingcap/tidb/issues/47787
|
||||
func TestInsertBigScientificNotation(t *testing.T) {
|
||||
store := testkit.CreateMockStore(t)
|
||||
tk := testkit.NewTestKit(t, store)
|
||||
tk.MustExec(`use test`)
|
||||
tk.MustExec("create table t1(id int, a int)")
|
||||
|
||||
tk.MustExec("set @@SQL_MODE='STRICT_TRANS_TABLES'")
|
||||
err := tk.ExecToErr("insert into t1 values(1, '1e100')")
|
||||
require.EqualError(t, err, "[types:1264]Out of range value for column 'a' at row 1")
|
||||
err = tk.ExecToErr("insert into t1 values(2, '-1e100')")
|
||||
require.EqualError(t, err, "[types:1264]Out of range value for column 'a' at row 1")
|
||||
tk.MustQuery("select id, a from t1").Check(testkit.Rows())
|
||||
|
||||
tk.MustExec("set @@SQL_MODE=''")
|
||||
tk.MustExec("insert into t1 values(1, '1e100')")
|
||||
tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1264 Out of range value for column 'a' at row 1"))
|
||||
tk.MustExec("insert into t1 values(2, '-1e100')")
|
||||
tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1264 Out of range value for column 'a' at row 1"))
|
||||
tk.MustQuery("select id, a from t1 order by id asc").Check(testkit.Rows("1 2147483647", "2 -2147483648"))
|
||||
}
|
||||
|
||||
@ -389,7 +389,7 @@ func getValidIntPrefix(ctx Context, str string, isFuncCast bool) (string, error)
|
||||
if err != nil {
|
||||
return floatPrefix, errors.Trace(err)
|
||||
}
|
||||
return floatStrToIntStr(ctx, floatPrefix, str)
|
||||
return floatStrToIntStr(floatPrefix, str)
|
||||
}
|
||||
|
||||
validLen := 0
|
||||
@ -445,6 +445,9 @@ func roundIntStr(numNextDot byte, intStr string) string {
|
||||
return string(retStr)
|
||||
}
|
||||
|
||||
var maxUintStr = strconv.FormatUint(math.MaxUint64, 10)
|
||||
var minIntStr = strconv.FormatInt(math.MinInt64, 10)
|
||||
|
||||
// floatStrToIntStr converts a valid float string into valid integer string which can be parsed by
|
||||
// strconv.ParseInt, we can't parse float first then convert it to string because precision will
|
||||
// be lost. For example, the string value "18446744073709551615" which is the max number of unsigned
|
||||
@ -452,7 +455,7 @@ func roundIntStr(numNextDot byte, intStr string) string {
|
||||
//
|
||||
// This func will find serious overflow such as the len of intStr > 20 (without prefix `+/-`)
|
||||
// however, it will not check whether the intStr overflow BIGINT.
|
||||
func floatStrToIntStr(ctx Context, validFloat string, oriStr string) (intStr string, _ error) {
|
||||
func floatStrToIntStr(validFloat string, oriStr string) (intStr string, _ error) {
|
||||
var dotIdx = -1
|
||||
var eIdx = -1
|
||||
for i := 0; i < len(validFloat); i++ {
|
||||
@ -500,7 +503,12 @@ func floatStrToIntStr(ctx Context, validFloat string, oriStr string) (intStr str
|
||||
}
|
||||
exp, err := strconv.Atoi(validFloat[eIdx+1:])
|
||||
if err != nil {
|
||||
return validFloat, errors.Trace(err)
|
||||
if digits[0] == '-' {
|
||||
intStr = minIntStr
|
||||
} else {
|
||||
intStr = maxUintStr
|
||||
}
|
||||
return intStr, ErrOverflow.GenWithStackByArgs("BIGINT", oriStr)
|
||||
}
|
||||
intCnt += exp
|
||||
if exp >= 0 && (intCnt > 21 || intCnt < 0) {
|
||||
@ -508,8 +516,12 @@ func floatStrToIntStr(ctx Context, validFloat string, oriStr string) (intStr str
|
||||
// MaxUint64 has 20 decimal digits.
|
||||
// And the intCnt may contain the len of `+/-`,
|
||||
// so I use 21 here as the early detection.
|
||||
ctx.AppendWarning(ErrOverflow.GenWithStackByArgs("BIGINT", oriStr))
|
||||
return validFloat[:eIdx], nil
|
||||
if digits[0] == '-' {
|
||||
intStr = minIntStr
|
||||
} else {
|
||||
intStr = maxUintStr
|
||||
}
|
||||
return intStr, ErrOverflow.GenWithStackByArgs("BIGINT", oriStr)
|
||||
}
|
||||
if intCnt <= 0 {
|
||||
intStr = "0"
|
||||
|
||||
@ -974,26 +974,35 @@ func TestGetValidFloat(t *testing.T) {
|
||||
tests2 := []struct {
|
||||
origin string
|
||||
expected string
|
||||
overflow bool
|
||||
}{
|
||||
{"1e9223372036854775807", "1"},
|
||||
{"125e342", "125"},
|
||||
{"1e21", "1"},
|
||||
{"1e5", "100000"},
|
||||
{"-123.45678e5", "-12345678"},
|
||||
{"+0.5", "1"},
|
||||
{"-0.5", "-1"},
|
||||
{".5e0", "1"},
|
||||
{"+.5e0", "+1"},
|
||||
{"-.5e0", "-1"},
|
||||
{".5", "1"},
|
||||
{"123.456789e5", "12345679"},
|
||||
{"123.456784e5", "12345678"},
|
||||
{"+999.9999e2", "+100000"},
|
||||
{"1e29223372036854775807", "18446744073709551615", true},
|
||||
{"1e9223372036854775807", "18446744073709551615", true},
|
||||
{"125e342", "18446744073709551615", true},
|
||||
{"1e21", "18446744073709551615", true},
|
||||
{"-1e29223372036854775807", "-9223372036854775808", true},
|
||||
{"-1e9223372036854775807", "-9223372036854775808", true},
|
||||
{"1e5", "100000", false},
|
||||
{"-123.45678e5", "-12345678", false},
|
||||
{"+0.5", "1", false},
|
||||
{"-0.5", "-1", false},
|
||||
{".5e0", "1", false},
|
||||
{"+.5e0", "+1", false},
|
||||
{"-.5e0", "-1", false},
|
||||
{".5", "1", false},
|
||||
{"123.456789e5", "12345679", false},
|
||||
{"123.456784e5", "12345678", false},
|
||||
{"+999.9999e2", "+100000", false},
|
||||
}
|
||||
for _, tt := range tests2 {
|
||||
str, err := floatStrToIntStr(ctx, tt.origin, tt.origin)
|
||||
require.NoError(t, err)
|
||||
require.Equalf(t, tt.expected, str, "%v, %v", tt.origin, tt.expected)
|
||||
for i, tt := range tests2 {
|
||||
msg := fmt.Sprintf("%d: %v, %v", i, tt.origin, tt.expected)
|
||||
str, err := floatStrToIntStr(tt.origin, tt.origin)
|
||||
if tt.overflow {
|
||||
require.True(t, terror.ErrorEqual(err, ErrOverflow), msg)
|
||||
} else {
|
||||
require.NoError(t, err, msg)
|
||||
}
|
||||
require.Equalf(t, tt.expected, str, msg)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user