Skip to content

Commit f9ebc39

Browse files
authored
types: fix the behavior when inserting a big scientific notation number to keep it same with mysql (#47803) (#47834)
close #47787
1 parent 7ddeb88 commit f9ebc39

File tree

3 files changed

+64
-21
lines changed

3 files changed

+64
-21
lines changed

executor/insert_test.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1593,3 +1593,25 @@ func TestInsertLockUnchangedKeys(t *testing.T) {
15931593
}
15941594
}
15951595
}
1596+
1597+
// see issue https://github.com/pingcap/tidb/issues/47787
1598+
func TestInsertBigScientificNotation(t *testing.T) {
1599+
store := testkit.CreateMockStore(t)
1600+
tk := testkit.NewTestKit(t, store)
1601+
tk.MustExec(`use test`)
1602+
tk.MustExec("create table t1(id int, a int)")
1603+
1604+
tk.MustExec("set @@SQL_MODE='STRICT_TRANS_TABLES'")
1605+
err := tk.ExecToErr("insert into t1 values(1, '1e100')")
1606+
require.EqualError(t, err, "[types:1264]Out of range value for column 'a' at row 1")
1607+
err = tk.ExecToErr("insert into t1 values(2, '-1e100')")
1608+
require.EqualError(t, err, "[types:1264]Out of range value for column 'a' at row 1")
1609+
tk.MustQuery("select id, a from t1").Check(testkit.Rows())
1610+
1611+
tk.MustExec("set @@SQL_MODE=''")
1612+
tk.MustExec("insert into t1 values(1, '1e100')")
1613+
tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1264 Out of range value for column 'a' at row 1"))
1614+
tk.MustExec("insert into t1 values(2, '-1e100')")
1615+
tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1264 Out of range value for column 'a' at row 1"))
1616+
tk.MustQuery("select id, a from t1 order by id asc").Check(testkit.Rows("1 2147483647", "2 -2147483648"))
1617+
}

types/convert.go

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -435,14 +435,17 @@ func roundIntStr(numNextDot byte, intStr string) string {
435435
return string(retStr)
436436
}
437437

438+
var maxUintStr = strconv.FormatUint(math.MaxUint64, 10)
439+
var minIntStr = strconv.FormatInt(math.MinInt64, 10)
440+
438441
// floatStrToIntStr converts a valid float string into valid integer string which can be parsed by
439442
// strconv.ParseInt, we can't parse float first then convert it to string because precision will
440443
// be lost. For example, the string value "18446744073709551615" which is the max number of unsigned
441444
// int will cause some precision to lose. intStr[0] may be a positive and negative sign like '+' or '-'.
442445
//
443446
// This func will find serious overflow such as the len of intStr > 20 (without prefix `+/-`)
444447
// however, it will not check whether the intStr overflow BIGINT.
445-
func floatStrToIntStr(sc *stmtctx.StatementContext, validFloat string, oriStr string) (intStr string, _ error) {
448+
func floatStrToIntStr(_ *stmtctx.StatementContext, validFloat string, oriStr string) (intStr string, _ error) {
446449
var dotIdx = -1
447450
var eIdx = -1
448451
for i := 0; i < len(validFloat); i++ {
@@ -490,16 +493,25 @@ func floatStrToIntStr(sc *stmtctx.StatementContext, validFloat string, oriStr st
490493
}
491494
exp, err := strconv.Atoi(validFloat[eIdx+1:])
492495
if err != nil {
493-
return validFloat, errors.Trace(err)
496+
if digits[0] == '-' {
497+
intStr = minIntStr
498+
} else {
499+
intStr = maxUintStr
500+
}
501+
return intStr, ErrOverflow.GenWithStackByArgs("BIGINT", oriStr)
494502
}
495503
intCnt += exp
496504
if exp >= 0 && (intCnt > 21 || intCnt < 0) {
497505
// MaxInt64 has 19 decimal digits.
498506
// MaxUint64 has 20 decimal digits.
499507
// And the intCnt may contain the len of `+/-`,
500508
// so I use 21 here as the early detection.
501-
sc.AppendWarning(ErrOverflow.GenWithStackByArgs("BIGINT", oriStr))
502-
return validFloat[:eIdx], nil
509+
if digits[0] == '-' {
510+
intStr = minIntStr
511+
} else {
512+
intStr = maxUintStr
513+
}
514+
return intStr, ErrOverflow.GenWithStackByArgs("BIGINT", oriStr)
503515
}
504516
if intCnt <= 0 {
505517
intStr = "0"

types/convert_test.go

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -975,26 +975,35 @@ func TestGetValidFloat(t *testing.T) {
975975
tests2 := []struct {
976976
origin string
977977
expected string
978+
overflow bool
978979
}{
979-
{"1e9223372036854775807", "1"},
980-
{"125e342", "125"},
981-
{"1e21", "1"},
982-
{"1e5", "100000"},
983-
{"-123.45678e5", "-12345678"},
984-
{"+0.5", "1"},
985-
{"-0.5", "-1"},
986-
{".5e0", "1"},
987-
{"+.5e0", "+1"},
988-
{"-.5e0", "-1"},
989-
{".5", "1"},
990-
{"123.456789e5", "12345679"},
991-
{"123.456784e5", "12345678"},
992-
{"+999.9999e2", "+100000"},
980+
{"1e29223372036854775807", "18446744073709551615", true},
981+
{"1e9223372036854775807", "18446744073709551615", true},
982+
{"125e342", "18446744073709551615", true},
983+
{"1e21", "18446744073709551615", true},
984+
{"-1e29223372036854775807", "-9223372036854775808", true},
985+
{"-1e9223372036854775807", "-9223372036854775808", true},
986+
{"1e5", "100000", false},
987+
{"-123.45678e5", "-12345678", false},
988+
{"+0.5", "1", false},
989+
{"-0.5", "-1", false},
990+
{".5e0", "1", false},
991+
{"+.5e0", "+1", false},
992+
{"-.5e0", "-1", false},
993+
{".5", "1", false},
994+
{"123.456789e5", "12345679", false},
995+
{"123.456784e5", "12345678", false},
996+
{"+999.9999e2", "+100000", false},
993997
}
994-
for _, tt := range tests2 {
998+
for i, tt := range tests2 {
999+
msg := fmt.Sprintf("%d: %v, %v", i, tt.origin, tt.expected)
9951000
str, err := floatStrToIntStr(sc, tt.origin, tt.origin)
996-
require.NoError(t, err)
997-
require.Equalf(t, tt.expected, str, "%v, %v", tt.origin, tt.expected)
1001+
if tt.overflow {
1002+
require.True(t, terror.ErrorEqual(err, ErrOverflow), msg)
1003+
} else {
1004+
require.NoError(t, err, msg)
1005+
}
1006+
require.Equalf(t, tt.expected, str, msg)
9981007
}
9991008
}
10001009

0 commit comments

Comments
 (0)