diff --git a/executor/executor_test.go b/executor/executor_test.go index 5bce66b69be28..b7913f86988c1 100644 --- a/executor/executor_test.go +++ b/executor/executor_test.go @@ -615,7 +615,9 @@ func (s *testSuite) TestUnion(c *C) { tk.MustExec("CREATE TABLE t (a DECIMAL(4,2))") tk.MustExec("INSERT INTO t VALUE(12.34)") r = tk.MustQuery("SELECT 1 AS c UNION select a FROM t") + r.Check(testkit.Rows("1.00", "12.34")) r.Sort().Check(testkit.Rows("1.00", "12.34")) + // #issue3771 r = tk.MustQuery("SELECT 'a' UNION SELECT CONCAT('a', -4)") r.Sort().Check(testkit.Rows("a", "a-4")) @@ -1189,6 +1191,75 @@ func (s *testSuite) TestBuiltin(c *C) { result = tk.MustQuery("select cast(-1 as unsigned)") result.Check(testkit.Rows("18446744073709551615")) + // Fix issue #3691, cast compability. + result = tk.MustQuery("select cast('18446744073709551616' as unsigned);") + result.Check(testkit.Rows("18446744073709551615")) + result = tk.MustQuery("select cast('18446744073709551616' as signed);") + result.Check(testkit.Rows("-1")) + result = tk.MustQuery("select cast('9223372036854775808' as signed);") + result.Check(testkit.Rows("-9223372036854775808")) + result = tk.MustQuery("select cast('9223372036854775809' as signed);") + result.Check(testkit.Rows("-9223372036854775807")) + result = tk.MustQuery("select cast('9223372036854775807' as signed);") + result.Check(testkit.Rows("9223372036854775807")) + result = tk.MustQuery("select cast('18446744073709551615' as signed);") + result.Check(testkit.Rows("-1")) + result = tk.MustQuery("select cast('18446744073709551614' as signed);") + result.Check(testkit.Rows("-2")) + result = tk.MustQuery("select cast(18446744073709551615 as unsigned);") + result.Check(testkit.Rows("18446744073709551615")) + result = tk.MustQuery("select cast(18446744073709551616 as unsigned);") + result.Check(testkit.Rows("18446744073709551615")) + result = tk.MustQuery("select cast(18446744073709551616 as signed);") + result.Check(testkit.Rows("9223372036854775807")) + result = tk.MustQuery("select cast(18446744073709551617 as signed);") + result.Check(testkit.Rows("9223372036854775807")) + result = tk.MustQuery("select cast(18446744073709551615 as signed);") + result.Check(testkit.Rows("-1")) + result = tk.MustQuery("select cast(18446744073709551614 as signed);") + result.Check(testkit.Rows("-2")) + result = tk.MustQuery("select cast(-18446744073709551616 as signed);") + result.Check(testkit.Rows("-9223372036854775808")) + result = tk.MustQuery("select cast(18446744073709551614.9 as unsigned);") // Round up + result.Check(testkit.Rows("18446744073709551615")) + result = tk.MustQuery("select cast(18446744073709551614.4 as unsigned);") // Round down + result.Check(testkit.Rows("18446744073709551614")) + result = tk.MustQuery("select cast(-9223372036854775809 as signed);") + result.Check(testkit.Rows("-9223372036854775808")) + result = tk.MustQuery("select cast(-9223372036854775809 as unsigned);") + result.Check(testkit.Rows("0")) + result = tk.MustQuery("select cast(-9223372036854775808 as unsigned);") + result.Check(testkit.Rows("9223372036854775808")) + result = tk.MustQuery("select cast('-9223372036854775809' as unsigned);") + result.Check(testkit.Rows("9223372036854775808")) + result = tk.MustQuery("select cast('-9223372036854775807' as unsigned);") + result.Check(testkit.Rows("9223372036854775809")) + result = tk.MustQuery("select cast('-2' as unsigned);") + result.Check(testkit.Rows("18446744073709551614")) + result = tk.MustQuery("select cast(cast(1-2 as unsigned) as signed integer);") + result.Check(testkit.Rows("-1")) + result = tk.MustQuery("select cast(1 as signed int)") + result.Check(testkit.Rows("1")) + + // test cast time as decimal overflow + tk.MustExec("drop table if exists t1") + tk.MustExec("create table t1(s1 time);") + tk.MustExec("insert into t1 values('11:11:11');") + result = tk.MustQuery("select cast(s1 as decimal(7, 2)) from t1;") + result.Check(testkit.Rows("99999.99")) + result = tk.MustQuery("select cast(s1 as decimal(8, 2)) from t1;") + result.Check(testkit.Rows("111111.00")) + _, err := tk.Exec("insert into t1 values(cast('111111.00' as decimal(7, 2)));") + c.Assert(err, NotNil) + + result = tk.MustQuery(`select CAST(0x8fffffffffffffff as signed) a, + CAST(0xfffffffffffffffe as signed) b, + CAST(0xffffffffffffffff as unsigned) c;`) + result.Check(testkit.Rows("-8070450532247928833 -2 18446744073709551615")) + + result = tk.MustQuery(`select cast("1:2:3" as TIME) = "1:02:03"`) + result.Check(testkit.Rows("0")) + // fixed issue #3471 tk.MustExec("drop table if exists t") tk.MustExec("create table t(a time(6));") @@ -1206,7 +1277,7 @@ func (s *testSuite) TestBuiltin(c *C) { tk.MustExec("drop table if exists t") tk.MustExec("create table t(a bigint(30));") - _, err := tk.Exec("insert into t values(-9223372036854775809)") + _, err = tk.Exec("insert into t values(-9223372036854775809)") c.Assert(err, NotNil) // test unhex and hex diff --git a/executor/prepared.go b/executor/prepared.go index 2803fce600eb6..dbe4a859ebec3 100644 --- a/executor/prepared.go +++ b/executor/prepared.go @@ -333,34 +333,41 @@ func ResetStmtCtx(ctx context.Context, s ast.StmtNode) { sessVars := ctx.GetSessionVars() sc := new(variable.StatementContext) sc.TimeZone = sessVars.GetTimeZone() + switch s.(type) { case *ast.UpdateStmt, *ast.DeleteStmt: sc.IgnoreTruncate = false - sc.IgnoreOverflow = false + sc.OverflowAsWarning = false sc.TruncateAsWarning = !sessVars.StrictSQLMode sc.InUpdateOrDeleteStmt = true case *ast.InsertStmt: sc.IgnoreTruncate = false - sc.IgnoreOverflow = false sc.TruncateAsWarning = !sessVars.StrictSQLMode sc.InInsertStmt = true case *ast.CreateTableStmt, *ast.AlterTableStmt: // Make sure the sql_mode is strict when checking column default value. sc.IgnoreTruncate = false - sc.IgnoreOverflow = false + sc.OverflowAsWarning = false sc.TruncateAsWarning = false case *ast.LoadDataStmt: sc.IgnoreTruncate = false - sc.IgnoreOverflow = false + sc.OverflowAsWarning = false sc.TruncateAsWarning = !sessVars.StrictSQLMode case *ast.SelectStmt: - sc.IgnoreOverflow = true + sc.InSelectStmt = true + + // see https://dev.mysql.com/doc/refman/5.7/en/sql-mode.html#sql-mode-strict + // said "For statements such as SELECT that do not change data, invalid values + // generate a warning in strict mode, not an error." + // and https://dev.mysql.com/doc/refman/5.7/en/out-of-range-and-overflow.html + sc.OverflowAsWarning = true + // Return warning for truncate error in selection. sc.IgnoreTruncate = false sc.TruncateAsWarning = true default: sc.IgnoreTruncate = true - sc.IgnoreOverflow = false + sc.OverflowAsWarning = false if show, ok := s.(*ast.ShowStmt); ok { if show.Tp == ast.ShowWarnings { sc.InShowWarning = true diff --git a/expression/builtin_cast.go b/expression/builtin_cast.go index 2393b446c2e94..75eb2c5d38bf0 100644 --- a/expression/builtin_cast.go +++ b/expression/builtin_cast.go @@ -22,13 +22,16 @@ package expression import ( + "math" "strconv" + "strings" "github.com/juju/errors" "github.com/pingcap/tidb/ast" "github.com/pingcap/tidb/context" "github.com/pingcap/tidb/model" "github.com/pingcap/tidb/mysql" + "github.com/pingcap/tidb/terror" "github.com/pingcap/tidb/util/charset" "github.com/pingcap/tidb/util/types" ) @@ -544,22 +547,24 @@ func (b *builtinCastDecimalAsIntSig) evalInt(row []types.Datum) (res int64, isNu if isNull || err != nil { return res, isNull, errors.Trace(err) } + + // Round is needed for both unsigned and signed. + var to types.MyDecimal + val.Round(&to, 0, types.ModeHalfEven) + if mysql.HasUnsignedFlag(b.tp.Flag) { - var ( - floatVal float64 - uintRes uint64 - ) - floatVal, err = val.ToFloat64() - if err != nil { - return res, false, errors.Trace(err) - } - uintRes, err = types.ConvertFloatToUint(sc, floatVal, types.UnsignedUpperBound[mysql.TypeLonglong], mysql.TypeDouble) + var uintRes uint64 + uintRes, err = to.ToUint() res = int64(uintRes) } else { - var to types.MyDecimal - val.Round(&to, 0, types.ModeHalfEven) res, err = to.ToInt() } + + if terror.ErrorEqual(err, types.ErrOverflow) { + warnErr := types.ErrTruncatedWrongVal.GenByArgs("DECIMAL", val) + err = sc.HandleOverflow(err, warnErr) + } + return res, false, errors.Trace(err) } @@ -638,6 +643,30 @@ type builtinCastStringAsIntSig struct { baseIntBuiltinFunc } +// handleOverflow handles the overflow caused by cast string as int, +// see https://dev.mysql.com/doc/refman/5.7/en/out-of-range-and-overflow.html. +// When an out-of-range value is assigned to an integer column, MySQL stores the value representing the corresponding endpoint of the column data type range. If it is in select statement, it will return the +// endpoint value with a warning. +func (b *builtinCastStringAsIntSig) handleOverflow(origRes int64, origStr string, origErr error, isNegative bool) (res int64, err error) { + res, err = origRes, origErr + if err == nil { + return + } + + sc := b.getCtx().GetSessionVars().StmtCtx + if sc.InSelectStmt && terror.ErrorEqual(origErr, types.ErrOverflow) { + if isNegative { + res = math.MinInt64 + } else { + uval := uint64(math.MaxUint64) + res = int64(uval) + } + warnErr := types.ErrTruncatedWrongVal.GenByArgs("INTEGER", origStr) + err = sc.HandleOverflow(origErr, warnErr) + } + return +} + func (b *builtinCastStringAsIntSig) evalInt(row []types.Datum) (res int64, isNull bool, err error) { sc := b.getCtx().GetSessionVars().StmtCtx if IsHybridType(b.args[0]) { @@ -647,13 +676,30 @@ func (b *builtinCastStringAsIntSig) evalInt(row []types.Datum) (res int64, isNul if isNull || err != nil { return res, isNull, errors.Trace(err) } - if mysql.HasUnsignedFlag(b.tp.Flag) { - var ures uint64 + + val = strings.TrimSpace(val) + isNegative := false + if len(val) > 1 && val[0] == '-' { // negative number + isNegative = true + } + + var ures uint64 + if isNegative { + res, err = types.StrToInt(sc, val) + if err == nil { + // If overflow, don't append this warnings + sc.AppendWarning(types.ErrCastNegIntAsUnsigned) + } + } else { ures, err = types.StrToUint(sc, val) res = int64(ures) - } else { - res, err = types.StrToInt(sc, val) + + if err == nil && !mysql.HasUnsignedFlag(b.tp.Flag) && ures > uint64(math.MaxInt64) { + sc.AppendWarning(types.ErrCastAsSignedOverflow) + } } + + res, err = b.handleOverflow(res, val, err, isNegative) return res, false, errors.Trace(err) } diff --git a/expression/builtin_cast_test.go b/expression/builtin_cast_test.go index d418baecdb250..e1d8d7ce92fb3 100644 --- a/expression/builtin_cast_test.go +++ b/expression/builtin_cast_test.go @@ -21,6 +21,7 @@ import ( . "github.com/pingcap/check" "github.com/pingcap/tidb/mysql" + "github.com/pingcap/tidb/terror" "github.com/pingcap/tidb/util/charset" "github.com/pingcap/tidb/util/testleak" "github.com/pingcap/tidb/util/types" @@ -75,6 +76,92 @@ func (s *testEvaluatorSuite) TestCast(c *C) { c.Assert(len(res.GetString()), Equals, 5) c.Assert(res.GetString(), Equals, string([]byte{'a', 0x00, 0x00, 0x00, 0x00})) + origSc := sc + sc.InSelectStmt = true + sc.OverflowAsWarning = true + + // cast('18446744073709551616' as unsigned); + tp1 := &types.FieldType{ + Tp: mysql.TypeLonglong, + Flag: mysql.BinaryFlag, + Charset: charset.CharsetBin, + Collate: charset.CollationBin, + Flen: mysql.MaxIntWidth, + } + f = NewCastFunc(tp1, &Constant{Value: types.NewDatum("18446744073709551616"), RetType: types.NewFieldType(mysql.TypeString)}, ctx) + res, err = f.Eval(nil) + c.Assert(err, IsNil) + c.Assert(res.GetUint64() == math.MaxUint64, IsTrue) + + warnings := sc.GetWarnings() + lastWarn := warnings[len(warnings)-1] + c.Assert(terror.ErrorEqual(types.ErrTruncatedWrongVal, lastWarn), IsTrue) + + f = NewCastFunc(tp1, &Constant{Value: types.NewDatum("-1"), RetType: types.NewFieldType(mysql.TypeString)}, ctx) + res, err = f.Eval(nil) + c.Assert(err, IsNil) + c.Assert(res.GetUint64() == 18446744073709551615, IsTrue) + + warnings = sc.GetWarnings() + lastWarn = warnings[len(warnings)-1] + c.Assert(terror.ErrorEqual(types.ErrCastNegIntAsUnsigned, lastWarn), IsTrue) + + f = NewCastFunc(tp1, &Constant{Value: types.NewDatum("-18446744073709551616"), RetType: types.NewFieldType(mysql.TypeString)}, ctx) + res, err = f.Eval(nil) + c.Assert(err, IsNil) + t := math.MinInt64 + // 9223372036854775808 + c.Assert(res.GetUint64() == uint64(t), IsTrue) + + warnings = sc.GetWarnings() + lastWarn = warnings[len(warnings)-1] + c.Assert(terror.ErrorEqual(types.ErrTruncatedWrongVal, lastWarn), IsTrue) + + // cast('18446744073709551616' as signed); + mask := ^mysql.UnsignedFlag + tp1.Flag &= uint(mask) + f = NewCastFunc(tp1, &Constant{Value: types.NewDatum("18446744073709551616"), RetType: types.NewFieldType(mysql.TypeString)}, ctx) + res, err = f.Eval(nil) + c.Assert(err, IsNil) + c.Check(res.GetInt64(), Equals, int64(-1)) + + warnings = sc.GetWarnings() + lastWarn = warnings[len(warnings)-1] + c.Assert(terror.ErrorEqual(types.ErrTruncatedWrongVal, lastWarn), IsTrue) + + // cast('18446744073709551614' as signed); + f = NewCastFunc(tp1, &Constant{Value: types.NewDatum("18446744073709551614"), RetType: types.NewFieldType(mysql.TypeString)}, ctx) + res, err = f.Eval(nil) + c.Assert(err, IsNil) + c.Check(res.GetInt64(), Equals, int64(-2)) + + warnings = sc.GetWarnings() + lastWarn = warnings[len(warnings)-1] + c.Assert(terror.ErrorEqual(types.ErrCastAsSignedOverflow, lastWarn), IsTrue) + + // create table t1(s1 time); + // insert into t1 values('11:11:11'); + // select cast(s1 as decimal(7, 2)) from t1; + tpDecimal := &types.FieldType{ + Tp: mysql.TypeNewDecimal, + Flag: mysql.BinaryFlag | mysql.UnsignedFlag, + Charset: charset.CharsetBin, + Collate: charset.CollationBin, + Flen: 7, + Decimal: 2, + } + f = NewCastFunc(tpDecimal, &Constant{Value: timeDatum, RetType: types.NewFieldType(mysql.TypeDatetime)}, ctx) + res, err = f.Eval(nil) + c.Assert(err, IsNil) + resDecimal := new(types.MyDecimal) + resDecimal.FromString([]byte("99999.99")) + c.Assert(res.GetMysqlDecimal().Compare(resDecimal), Equals, 0) + + warnings = sc.GetWarnings() + lastWarn = warnings[len(warnings)-1] + c.Assert(terror.ErrorEqual(types.ErrOverflow, lastWarn), IsTrue) + sc = origSc + // cast(bad_string as decimal) for _, s := range []string{"hello", ""} { f = NewCastFunc(tp, &Constant{Value: types.NewDatum(s), RetType: types.NewFieldType(mysql.TypeDecimal)}, ctx) diff --git a/expression/builtin_op.go b/expression/builtin_op.go index 3a5520b77baf3..f97a148c6451c 100644 --- a/expression/builtin_op.go +++ b/expression/builtin_op.go @@ -547,7 +547,7 @@ func (b *unaryMinusFunctionClass) typeInfer(argExpr Expression, ctx context.Cont sc := ctx.GetSessionVars().StmtCtx overflow := false // TODO: Handle float overflow. - if arg, ok := argExpr.(*Constant); sc.IgnoreOverflow && ok && + if arg, ok := argExpr.(*Constant); sc.InSelectStmt && ok && arg.GetTypeClass() == types.ClassInt { overflow = b.handleIntOverflow(arg) if overflow { diff --git a/expression/builtin_op_test.go b/expression/builtin_op_test.go index 61bf2d492e1c2..e1442abc9d385 100644 --- a/expression/builtin_op_test.go +++ b/expression/builtin_op_test.go @@ -37,10 +37,10 @@ func (s *testEvaluatorSuite) TestUnary(c *C) { {int64(math.MinInt64), "9223372036854775808", true, false}, // --9223372036854775808 } sc := s.ctx.GetSessionVars().StmtCtx - origin := sc.IgnoreOverflow - sc.IgnoreOverflow = true + origin := sc.InSelectStmt + sc.InSelectStmt = true defer func() { - sc.IgnoreOverflow = origin + sc.InSelectStmt = origin }() for _, t := range cases { diff --git a/expression/builtin_time.go b/expression/builtin_time.go index ff0cefa633963..b5cc6238ef629 100644 --- a/expression/builtin_time.go +++ b/expression/builtin_time.go @@ -323,7 +323,6 @@ func (b *builtinTimeDiffSig) eval(row []types.Datum) (d types.Datum, err error) if err != nil { return d, errors.Trace(err) } - t := t1.Sub(&t2) d.SetMysqlDuration(t) return diff --git a/expression/typeinferer.go b/expression/typeinferer.go index 5d7149ce1a199..3bf28d1726b84 100644 --- a/expression/typeinferer.go +++ b/expression/typeinferer.go @@ -599,6 +599,7 @@ func IsHybridType(expr Expression) bool { case mysql.TypeEnum, mysql.TypeBit, mysql.TypeSet: return true } + // For a constant, the field type will be inferred as `VARCHAR` when the kind of it is `HEX` or `BIT`. if con, ok := expr.(*Constant); ok { switch con.Value.Kind() { diff --git a/parser/parser.y b/parser/parser.y index 3b134bdbbe207..3cff9dadce816 100644 --- a/parser/parser.y +++ b/parser/parser.y @@ -5918,6 +5918,7 @@ IntegerType: OptInteger: {} | "INTEGER" +| "INT" FixedPointType: "DECIMAL" diff --git a/parser/parser_test.go b/parser/parser_test.go index 96893cb10e116..14217515e9f53 100644 --- a/parser/parser_test.go +++ b/parser/parser_test.go @@ -662,6 +662,9 @@ func (s *testParserSuite) TestBuiltin(c *C) { // for cast as JSON {"SELECT *, CAST(data AS JSON) FROM t;", true}, + // for cast as signed int, fix issue #3691. + {"select cast(1 as signed int);", true}, + // for last_insert_id {"SELECT last_insert_id();", true}, {"SELECT last_insert_id(1);", true}, diff --git a/sessionctx/variable/session.go b/sessionctx/variable/session.go index 38bdf39f136a9..37a86e09cdccb 100644 --- a/sessionctx/variable/session.go +++ b/sessionctx/variable/session.go @@ -322,9 +322,10 @@ type StatementContext struct { InInsertStmt bool InUpdateOrDeleteStmt bool - IgnoreOverflow bool + InSelectStmt bool IgnoreTruncate bool TruncateAsWarning bool + OverflowAsWarning bool InShowWarning bool // mu struct holds variables that change during execution. @@ -422,6 +423,19 @@ func (sc *StatementContext) HandleTruncate(err error) error { return err } +// HandleOverflow treats ErrOverflow as warnings or returns the error based on the StmtCtx.OverflowAsWarning state. +func (sc *StatementContext) HandleOverflow(err error, warnErr error) error { + if err == nil { + return nil + } + + if sc.OverflowAsWarning { + sc.AppendWarning(warnErr) + return nil + } + return err +} + // ResetForRetry resets the changed states during execution. func (sc *StatementContext) ResetForRetry() { sc.mu.Lock() diff --git a/util/types/convert.go b/util/types/convert.go index b3569ba989ea9..1d60d9ae431e2 100644 --- a/util/types/convert.go +++ b/util/types/convert.go @@ -222,6 +222,7 @@ func floatStrToIntStr(validFloat string) (string, error) { if intCnt <= len(digits) { validInt = string(digits[:intCnt]) } else { + // convert scientific notation decimal number extraZeroCount := intCnt - len(digits) if extraZeroCount > 20 { // Return overflow to avoid allocating too much memory. diff --git a/util/types/datum.go b/util/types/datum.go index 96b6ec4fe2f88..61f6c8e626fbc 100644 --- a/util/types/datum.go +++ b/util/types/datum.go @@ -24,6 +24,7 @@ import ( "github.com/juju/errors" "github.com/pingcap/tidb/mysql" "github.com/pingcap/tidb/sessionctx/variable" + "github.com/pingcap/tidb/terror" "github.com/pingcap/tidb/util/charset" "github.com/pingcap/tidb/util/hack" "github.com/pingcap/tidb/util/types/json" @@ -1114,7 +1115,6 @@ func ProduceDecWithSpecifiedTp(dec *MyDecimal, tp *FieldType, sc *variable.State prec, frac := dec.PrecisionAndFrac() if !dec.IsZero() && prec-frac > flen-decimal { dec = NewMaxOrMinDec(dec.IsNegative(), flen, decimal) - // TODO: we may need a OverlowAsWarning. // select (cast 111 as decimal(1)) causes a warning in MySQL. err = ErrOverflow.GenByArgs("DECIMAL", fmt.Sprintf("(%d, %d)", flen, decimal)) } else if frac != decimal { @@ -1131,6 +1131,11 @@ func ProduceDecWithSpecifiedTp(dec *MyDecimal, tp *FieldType, sc *variable.State } } } + + if terror.ErrorEqual(err, ErrOverflow) { + // TODO: warnErr need to be ErrWarnDataOutOfRange + err = sc.HandleOverflow(err, err) + } return dec, errors.Trace(err) } @@ -1427,8 +1432,7 @@ func (d *Datum) toSignedInteger(sc *variable.StatementContext, tp byte) (int64, } return ival, err case KindMysqlHex: - fval := d.GetMysqlHex().ToNumber() - return ConvertFloatToInt(sc, fval, lowerBound, upperBound, tp) + return d.GetMysqlHex().Value, nil case KindMysqlBit: fval := d.GetMysqlBit().ToNumber() return ConvertFloatToInt(sc, fval, lowerBound, upperBound, tp) diff --git a/util/types/errors.go b/util/types/errors.go index fd011e6e63af5..673336074e53a 100644 --- a/util/types/errors.go +++ b/util/types/errors.go @@ -23,6 +23,8 @@ var ( ErrDataTooLong = terror.ClassTypes.New(codeDataTooLong, "Data Too Long") // ErrTruncated is returned when data has been truncated during conversion. ErrTruncated = terror.ClassTypes.New(codeTruncated, "Data Truncated") + // ErrTruncatedWrongVal is returned when data has been truncated during conversion. + ErrTruncatedWrongVal = terror.ClassTypes.New(codeTruncatedWrongValue, msgTruncatedWrongVal) // ErrOverflow is returned when data is out of range for a field type. ErrOverflow = terror.ClassTypes.New(codeOverflow, msgOverflow) // ErrDivByZero is return when do division by 0. @@ -37,35 +39,46 @@ var ( ErrWrongFieldSpec = terror.ClassTypes.New(codeWrongFieldSpec, "Wrong Field Spec") // ErrBadNumber is return when parsing an invalid binary decimal number. ErrBadNumber = terror.ClassTypes.New(codeBadNumber, "Bad Number") + // ErrCastAsSignedOverflow is returned when positive out-of-range integer, and convert to it's negative complement. + ErrCastAsSignedOverflow = terror.ClassTypes.New(codeUnknown, msgCastAsSignedOverflow) + // ErrCastNegIntAsUnsigned is returned when a negative integer be casted to an unsigned int. + ErrCastNegIntAsUnsigned = terror.ClassTypes.New(codeUnknown, msgCastNegIntAsUnsigned) ) const ( codeBadNumber terror.ErrCode = 1 - codeDataTooLong terror.ErrCode = terror.ErrCode(mysql.ErrDataTooLong) - codeTruncated terror.ErrCode = terror.ErrCode(mysql.WarnDataTruncated) - codeOverflow terror.ErrCode = terror.ErrCode(mysql.ErrDataOutOfRange) - codeDivByZero terror.ErrCode = terror.ErrCode(mysql.ErrDivisionByZero) - codeTooBigDisplayWidth terror.ErrCode = terror.ErrCode(mysql.ErrTooBigDisplaywidth) - codeTooBigFieldLength terror.ErrCode = terror.ErrCode(mysql.ErrTooBigFieldlength) - codeTooBigSet terror.ErrCode = terror.ErrCode(mysql.ErrTooBigSet) - codeWrongFieldSpec terror.ErrCode = terror.ErrCode(mysql.ErrWrongFieldSpec) + codeDataTooLong terror.ErrCode = terror.ErrCode(mysql.ErrDataTooLong) + codeTruncated terror.ErrCode = terror.ErrCode(mysql.WarnDataTruncated) + codeOverflow terror.ErrCode = terror.ErrCode(mysql.ErrDataOutOfRange) + codeDivByZero terror.ErrCode = terror.ErrCode(mysql.ErrDivisionByZero) + codeTooBigDisplayWidth terror.ErrCode = terror.ErrCode(mysql.ErrTooBigDisplaywidth) + codeTooBigFieldLength terror.ErrCode = terror.ErrCode(mysql.ErrTooBigFieldlength) + codeTooBigSet terror.ErrCode = terror.ErrCode(mysql.ErrTooBigSet) + codeWrongFieldSpec terror.ErrCode = terror.ErrCode(mysql.ErrWrongFieldSpec) + codeTruncatedWrongValue terror.ErrCode = terror.ErrCode(mysql.ErrTruncatedWrongValue) + codeUnknown terror.ErrCode = terror.ErrCode(mysql.ErrUnknown) ) var ( - msgOverflow = mysql.MySQLErrName[mysql.ErrDataOutOfRange] + msgOverflow = mysql.MySQLErrName[mysql.ErrDataOutOfRange] + msgTruncatedWrongVal = mysql.MySQLErrName[mysql.ErrTruncatedWrongValue] + msgCastAsSignedOverflow = "Cast to signed converted positive out-of-range integer to it's negative complement" + msgCastNegIntAsUnsigned = "Cast to unsigned converted negative integer to it's positive complement" ) func init() { typesMySQLErrCodes := map[terror.ErrCode]uint16{ - codeDataTooLong: mysql.ErrDataTooLong, - codeTruncated: mysql.WarnDataTruncated, - codeOverflow: mysql.ErrDataOutOfRange, - codeDivByZero: mysql.ErrDivisionByZero, - codeTooBigDisplayWidth: mysql.ErrTooBigDisplaywidth, - codeTooBigFieldLength: mysql.ErrTooBigFieldlength, - codeTooBigSet: mysql.ErrTooBigSet, - codeWrongFieldSpec: mysql.ErrWrongFieldSpec, + codeDataTooLong: mysql.ErrDataTooLong, + codeTruncated: mysql.WarnDataTruncated, + codeOverflow: mysql.ErrDataOutOfRange, + codeDivByZero: mysql.ErrDivisionByZero, + codeTooBigDisplayWidth: mysql.ErrTooBigDisplaywidth, + codeTooBigFieldLength: mysql.ErrTooBigFieldlength, + codeTooBigSet: mysql.ErrTooBigSet, + codeWrongFieldSpec: mysql.ErrWrongFieldSpec, + codeTruncatedWrongValue: mysql.ErrTruncatedWrongValue, + codeUnknown: mysql.ErrUnknown, } terror.ErrClassToMySQLCodes[terror.ClassTypes] = typesMySQLErrCodes } diff --git a/util/types/hex.go b/util/types/hex.go index 21664c22a0643..e0bfd60b45fe6 100644 --- a/util/types/hex.go +++ b/util/types/hex.go @@ -84,12 +84,12 @@ func ParseHex(s string) (Hex, error) { if err != nil { return Hex{}, errors.Trace(err) } - n, err := strconv.ParseInt(s, 0, 64) + n, err := strconv.ParseUint(s, 0, 64) if err != nil { return Hex{}, errors.Trace(err) } - return Hex{Value: n}, nil + return Hex{Value: int64(n)}, nil } // ParseHexStr parses hexadecimal literal as string.