diff --git a/errors.toml b/errors.toml index b30f404faa03a..f3c7ae991964f 100644 --- a/errors.toml +++ b/errors.toml @@ -2018,7 +2018,7 @@ Division by 0 ["expression:1426"] error = ''' -Too big precision %d specified for column '%-.192s'. Maximum is %d. +Too-big precision %d specified for '%-.192s'. Maximum is %d. ''' ["expression:1582"] @@ -3288,7 +3288,7 @@ Too big scale %d specified for column '%-.192s'. Maximum is %d. ["types:1426"] error = ''' -Too big precision %d specified for column '%-.192s'. Maximum is %d. +Too-big precision %d specified for '%-.192s'. Maximum is %d. ''' ["types:1427"] diff --git a/pkg/errno/errname.go b/pkg/errno/errname.go index c9a9b21736495..3259373293505 100644 --- a/pkg/errno/errname.go +++ b/pkg/errno/errname.go @@ -432,7 +432,7 @@ var MySQLErrName = map[uint16]*mysql.ErrMessage{ ErrNoDefaultForViewField: mysql.Message("Field of view '%-.192s.%-.192s' underlying table doesn't have a default value", nil), ErrSpNoRecursion: mysql.Message("Recursive stored functions and triggers are not allowed.", nil), ErrTooBigScale: mysql.Message("Too big scale %d specified for column '%-.192s'. Maximum is %d.", nil), - ErrTooBigPrecision: mysql.Message("Too big precision %d specified for column '%-.192s'. Maximum is %d.", nil), + ErrTooBigPrecision: mysql.Message("Too-big precision %d specified for '%-.192s'. Maximum is %d.", nil), ErrMBiggerThanD: mysql.Message("For float(M,D), double(M,D) or decimal(M,D), M must be >= D (column '%-.192s').", nil), ErrWrongLockOfSystemTable: mysql.Message("You can't combine write-locking of system tables with other tables or lock types", nil), ErrConnectToForeignDataSource: mysql.Message("Unable to connect to foreign data source: %.64s", nil), diff --git a/pkg/expression/builtin_time.go b/pkg/expression/builtin_time.go index 4100f705d63ad..af3225c248449 100644 --- a/pkg/expression/builtin_time.go +++ b/pkg/expression/builtin_time.go @@ -2013,7 +2013,7 @@ func (c *sysDateFunctionClass) getFunction(ctx BuildContext, args []Expression) if err := c.verifyArgs(args); err != nil { return nil, err } - fsp, err := getFspByIntArg(ctx, args) + fsp, err := getFspByIntArg(ctx, args, c.funcName) if err != nil { return nil, err } @@ -2138,7 +2138,7 @@ func (c *currentTimeFunctionClass) getFunction(ctx BuildContext, args []Expressi return nil, err } - fsp, err := getFspByIntArg(ctx, args) + fsp, err := getFspByIntArg(ctx, args, c.funcName) if err != nil { return nil, err } @@ -2383,7 +2383,7 @@ func (c *utcTimestampFunctionClass) getFunction(ctx BuildContext, args []Express return nil, err } - fsp, err := getFspByIntArg(ctx, args) + fsp, err := getFspByIntArg(ctx, args, c.funcName) if err != nil { return nil, err } @@ -2429,14 +2429,14 @@ func (b *builtinUTCTimestampWithArgSig) evalTime(ctx EvalContext, row chunk.Row) return types.ZeroTime, true, err } - if !isNull && num > int64(types.MaxFsp) { - return types.ZeroTime, true, errors.Errorf("Too-big precision %v specified for 'utc_timestamp'. Maximum is %v", num, types.MaxFsp) - } - if !isNull && num < int64(types.MinFsp) { - return types.ZeroTime, true, errors.Errorf("Invalid negative %d specified, must in [0, 6]", num) + // Here fsp must be greater than or equal to zero, there is a bug of parser for negetive precision now. + // There is a bug of MySQL, but not fixed now, so we keep it be compatible with MySQL. (#56453) + fsp_u8 := uint8(num) + if !isNull && fsp_u8 > uint8(types.MaxFsp) { + return types.ZeroTime, true, types.ErrTooBigPrecision.GenWithStackByArgs(fsp_u8, "utc_timestamp", types.MaxFsp) } - result, isNull, err := evalUTCTimestampWithFsp(ctx, int(num)) + result, isNull, err := evalUTCTimestampWithFsp(ctx, int(fsp_u8)) return result, isNull, err } @@ -2474,7 +2474,7 @@ func (c *nowFunctionClass) getFunction(ctx BuildContext, args []Expression) (bui return nil, err } - fsp, err := getFspByIntArg(ctx, args) + fsp, err := getFspByIntArg(ctx, args, c.funcName) if err != nil { return nil, err } @@ -2551,15 +2551,16 @@ func (b *builtinNowWithArgSig) evalTime(ctx EvalContext, row chunk.Row) (types.T return types.ZeroTime, true, err } + // Here fsp must be greater than or equal to zero, there is a bug of parser for negetive precision now. + // There is a bug of MySQL, but not fixed now, so we keep it be compatible with MySQL. (#56453) + fsp_u8 := uint8(fsp) if isNull { - fsp = 0 - } else if fsp > int64(types.MaxFsp) { - return types.ZeroTime, true, errors.Errorf("Too-big precision %v specified for 'now'. Maximum is %v", fsp, types.MaxFsp) - } else if fsp < int64(types.MinFsp) { - return types.ZeroTime, true, errors.Errorf("Invalid negative %d specified, must in [0, 6]", fsp) + fsp_u8 = 0 + } else if fsp_u8 > uint8(types.MaxFsp) { + return types.ZeroTime, true, types.ErrTooBigPrecision.GenWithStackByArgs(fsp_u8, "now", types.MaxFsp) } - result, isNull, err := evalNowWithFsp(ctx, int(fsp)) + result, isNull, err := evalNowWithFsp(ctx, int(fsp_u8)) return result, isNull, err } @@ -6455,7 +6456,7 @@ func (c *utcTimeFunctionClass) getFunction(ctx BuildContext, args []Expression) if err != nil { return nil, err } - fsp, err := getFspByIntArg(ctx, args) + fsp, err := getFspByIntArg(ctx, args, c.funcName) if err != nil { return nil, err } @@ -6513,17 +6514,18 @@ func (b *builtinUTCTimeWithArgSig) evalDuration(ctx EvalContext, row chunk.Row) if isNull || err != nil { return types.Duration{}, isNull, err } - if fsp > int64(types.MaxFsp) { - return types.Duration{}, true, errors.Errorf("Too-big precision %v specified for 'utc_time'. Maximum is %v", fsp, types.MaxFsp) - } - if fsp < int64(types.MinFsp) { - return types.Duration{}, true, errors.Errorf("Invalid negative %d specified, must in [0, 6]", fsp) + // Here fsp must be greater than or equal to zero, there is a bug of parser for negetive precision now. + // There is a bug of MySQL, but not fixed now, so we keep it be compatible with MySQL. (#56453) + fsp_u8 := uint8(fsp) + if fsp_u8 > uint8(types.MaxFsp) { + return types.Duration{}, true, types.ErrTooBigPrecision.GenWithStackByArgs(fsp_u8, "utc_time", types.MaxFsp) } + nowTs, err := getStmtTimestamp(ctx) if err != nil { return types.Duration{}, true, err } - v, _, err := types.ParseDuration(typeCtx(ctx), nowTs.UTC().Format(types.TimeFSPFormat), int(fsp)) + v, _, err := types.ParseDuration(typeCtx(ctx), nowTs.UTC().Format(types.TimeFSPFormat), int(fsp_u8)) return v, false, err } @@ -6804,7 +6806,7 @@ func calAppropriateTime(minTime, maxTime, minSafeTime time.Time) time.Time { } // getFspByIntArg is used by some time functions to get the result fsp. If len(expr) == 0, then the fsp is not explicit set, use 0 as default. -func getFspByIntArg(ctx BuildContext, exps []Expression) (int, error) { +func getFspByIntArg(ctx BuildContext, exps []Expression, funcName string) (int, error) { if len(exps) == 0 { return 0, nil } @@ -6818,12 +6820,13 @@ func getFspByIntArg(ctx BuildContext, exps []Expression) (int, error) { // If isNULL, it may be a bug of parser. Return 0 to be compatible with old version. return 0, err } - if fsp > int64(types.MaxFsp) { - return 0, errors.Errorf("Too-big precision %v specified for 'curtime'. Maximum is %v", fsp, types.MaxFsp) - } else if fsp < int64(types.MinFsp) { - return 0, errors.Errorf("Invalid negative %d specified, must in [0, 6]", fsp) + + // Here fsp must be greater than or equal to zero, there is a bug of parser for negetive precision now. + // There is a bug of MySQL, but not fixed now, so we keep it be compatible with MySQL. (#56453) + fsp_u8 := uint8(fsp) + if fsp_u8 > uint8(types.MaxFsp) { + return 0, types.ErrTooBigPrecision.GenWithStackByArgs(fsp_u8, funcName, types.MaxFsp) } - return int(fsp), nil } // Should no happen. But our tests may generate non-constant input. return 0, nil diff --git a/pkg/expression/builtin_time_vec.go b/pkg/expression/builtin_time_vec.go index b965952a7108e..71ebf6781d923 100644 --- a/pkg/expression/builtin_time_vec.go +++ b/pkg/expression/builtin_time_vec.go @@ -21,7 +21,6 @@ import ( "strings" "time" - "github.com/pingcap/errors" "github.com/pingcap/tidb/pkg/parser/mysql" "github.com/pingcap/tidb/pkg/parser/terror" "github.com/pingcap/tidb/pkg/types" @@ -404,12 +403,11 @@ func (b *builtinUTCTimeWithArgSig) vecEvalDuration(ctx EvalContext, input *chunk if result.IsNull(i) { continue } - fsp := i64s[i] - if fsp > int64(types.MaxFsp) { - return errors.Errorf("Too-big precision %v specified for 'utc_time'. Maximum is %v", fsp, types.MaxFsp) - } - if fsp < int64(types.MinFsp) { - return errors.Errorf("Invalid negative %d specified, must in [0, 6]", fsp) + // Here fsp must be greater than or equal to zero, there is a bug of parser for negetive precision now. + // There is a bug of MySQL, but not fixed now, so we keep it be compatible with MySQL. (#56453) + fsp := uint8(i64s[i]) + if fsp > uint8(types.MaxFsp) { + return types.ErrTooBigPrecision.GenWithStackByArgs(fsp, "utc_time", types.MaxFsp) } res, _, err := types.ParseDuration(tc, utc, int(fsp)) if err != nil { @@ -544,18 +542,17 @@ func (b *builtinNowWithArgSig) vecEvalTime(ctx EvalContext, input *chunk.Chunk, fsps := bufFsp.Int64s() for i := 0; i < n; i++ { - fsp := 0 + var fsp uint8 = 0 if !bufFsp.IsNull(i) { - if fsps[i] > int64(types.MaxFsp) { - return errors.Errorf("Too-big precision %v specified for 'now'. Maximum is %v", fsps[i], types.MaxFsp) + // Here fsp must be greater than or equal to zero, there is a bug of parser for negetive precision now. + // There is a bug of MySQL, but not fixed now, so we keep it be compatible with MySQL. (#56453) + fsp = uint8(fsps[i]) + if fsp > uint8(types.MaxFsp) { + return types.ErrTooBigPrecision.GenWithStackByArgs(fsp, "now", types.MaxFsp) } - if fsps[i] < int64(types.MinFsp) { - return errors.Errorf("Invalid negative %d specified, must in [0, 6]", fsps[i]) - } - fsp = int(fsps[i]) } - t, isNull, err := evalNowWithFsp(ctx, fsp) + t, isNull, err := evalNowWithFsp(ctx, int(fsp)) if err != nil { return err } @@ -1407,12 +1404,11 @@ func (b *builtinUTCTimestampWithArgSig) vecEvalTime(ctx EvalContext, input *chun if result.IsNull(i) { continue } - fsp := i64s[i] - if fsp > int64(types.MaxFsp) { - return errors.Errorf("Too-big precision %v specified for 'utc_timestamp'. Maximum is %v", fsp, types.MaxFsp) - } - if fsp < int64(types.MinFsp) { - return errors.Errorf("Invalid negative %d specified, must in [0, 6]", fsp) + // Here fsp must be greater than or equal to zero, there is a bug of parser for negetive precision now. + // There is a bug of MySQL, but not fixed now, so we keep it be compatible with MySQL. (#56453) + fsp := uint8(i64s[i]) + if fsp > uint8(types.MaxFsp) { + return types.ErrTooBigPrecision.GenWithStackByArgs(fsp, "utc_timestamp", types.MaxFsp) } res, isNull, err := evalUTCTimestampWithFsp(ctx, int(fsp)) if err != nil { diff --git a/pkg/parser/mysql/errname.go b/pkg/parser/mysql/errname.go index c105a8446f23c..f757f825da43e 100644 --- a/pkg/parser/mysql/errname.go +++ b/pkg/parser/mysql/errname.go @@ -454,7 +454,7 @@ var MySQLErrName = map[uint16]*ErrMessage{ ErrNoDefaultForViewField: Message("Field of view '%-.192s.%-.192s' underlying table doesn't have a default value", nil), ErrSpNoRecursion: Message("Recursive stored functions and triggers are not allowed.", nil), ErrTooBigScale: Message("Too big scale %d specified for column '%-.192s'. Maximum is %d.", nil), - ErrTooBigPrecision: Message("Too big precision %d specified for column '%-.192s'. Maximum is %d.", nil), + ErrTooBigPrecision: Message("Too-big precision %d specified for '%-.192s'. Maximum is %d.", nil), ErrMBiggerThanD: Message("For float(M,D), double(M,D) or decimal(M,D), M must be >= D (column '%-.192s').", nil), ErrWrongLockOfSystemTable: Message("You can't combine write-locking of system tables with other tables or lock types", nil), ErrConnectToForeignDataSource: Message("Unable to connect to foreign data source: %.64s", nil), diff --git a/tests/integrationtest/r/expression/builtin.result b/tests/integrationtest/r/expression/builtin.result index 010b4458469b2..39c35ca6846e1 100644 --- a/tests/integrationtest/r/expression/builtin.result +++ b/tests/integrationtest/r/expression/builtin.result @@ -1181,7 +1181,7 @@ Error 1264 (22003): Out of range value for column 'a' at row 1 select cast(12.1 as decimal(3, 4)); Error 1427 (42000): For float(M,D), double(M,D) or decimal(M,D), M must be >= D (column '12.1'). SELECT CAST(1 AS DATETIME(7)); -Error 1426 (42000): Too big precision 7 specified for column 'CAST'. Maximum is 6. +Error 1426 (42000): Too-big precision 7 specified for 'CAST'. Maximum is 6. select unhex('4D7953514C'); unhex('4D7953514C') MySQL @@ -3339,3 +3339,21 @@ b SELECT MID('abc',2); MID('abc',2) bc +SELECT CURRENT_TIME(7); +Error 1426 (42000): Too-big precision 7 specified for 'current_time'. Maximum is 6. +SELECT CURRENT_TIMESTAMP(7); +Error 1426 (42000): Too-big precision 7 specified for 'current_timestamp'. Maximum is 6. +SELECT CURTIME(7); +Error 1426 (42000): Too-big precision 7 specified for 'curtime'. Maximum is 6. +SELECT LOCALTIME(7); +Error 1426 (42000): Too-big precision 7 specified for 'localtime'. Maximum is 6. +SELECT LOCALTIMESTAMP(7); +Error 1426 (42000): Too-big precision 7 specified for 'localtimestamp'. Maximum is 6. +SELECT NOW(7); +Error 1426 (42000): Too-big precision 7 specified for 'now'. Maximum is 6. +SELECT SYSDATE(7); +Error 1426 (42000): Too-big precision 7 specified for 'sysdate'. Maximum is 6. +SELECT UTC_TIME(7); +Error 1426 (42000): Too-big precision 7 specified for 'utc_time'. Maximum is 6. +SELECT UTC_TIMESTAMP(7); +Error 1426 (42000): Too-big precision 7 specified for 'utc_timestamp'. Maximum is 6. diff --git a/tests/integrationtest/r/expression/cast.result b/tests/integrationtest/r/expression/cast.result index a919c1e53ccf2..1d237ebfd87e0 100644 --- a/tests/integrationtest/r/expression/cast.result +++ b/tests/integrationtest/r/expression/cast.result @@ -69,15 +69,15 @@ select cast(col1 as time), cast(col2 as time), cast(col3 as time), cast(col4 as cast(col1 as time) cast(col2 as time) cast(col3 as time) cast(col4 as time) cast(col5 as time) NULL NULL NULL NULL NULL select cast(col1 as time(31)) from t where col1 is null; -Error 1426 (42000): Too big precision 31 specified for column 'CAST'. Maximum is 6. +Error 1426 (42000): Too-big precision 31 specified for 'CAST'. Maximum is 6. select cast(col2 as time(31)) from t where col1 is null; -Error 1426 (42000): Too big precision 31 specified for column 'CAST'. Maximum is 6. +Error 1426 (42000): Too-big precision 31 specified for 'CAST'. Maximum is 6. select cast(col3 as time(31)) from t where col1 is null; -Error 1426 (42000): Too big precision 31 specified for column 'CAST'. Maximum is 6. +Error 1426 (42000): Too-big precision 31 specified for 'CAST'. Maximum is 6. select cast(col4 as time(31)) from t where col1 is null; -Error 1426 (42000): Too big precision 31 specified for column 'CAST'. Maximum is 6. +Error 1426 (42000): Too-big precision 31 specified for 'CAST'. Maximum is 6. select cast(col5 as time(31)) from t where col1 is null; -Error 1426 (42000): Too big precision 31 specified for column 'CAST'. Maximum is 6. +Error 1426 (42000): Too-big precision 31 specified for 'CAST'. Maximum is 6. drop table if exists t; create table t(a varchar(50)); insert into t values ('2020-01-01 12:00:00.123456 +0600 PST'); diff --git a/tests/integrationtest/t/expression/builtin.test b/tests/integrationtest/t/expression/builtin.test index 79da88a70a6a2..1a92f60d7e422 100644 --- a/tests/integrationtest/t/expression/builtin.test +++ b/tests/integrationtest/t/expression/builtin.test @@ -1579,6 +1579,26 @@ select hex(r) as r0 from (select ELT(2, col1, col2) as r from t3 group by ELT(2, select hex(r) as r0 from (select distinct ELT(2, col1, col2) as r from t3) as t order by r0; drop table t, t2, t3; -# Issue $52420 +# Issue #52420 SELECT MID('abc',2,1); SELECT MID('abc',2); + +# Issue #56451 +-- error 1426 +SELECT CURRENT_TIME(7); +-- error 1426 +SELECT CURRENT_TIMESTAMP(7); +-- error 1426 +SELECT CURTIME(7); +-- error 1426 +SELECT LOCALTIME(7); +-- error 1426 +SELECT LOCALTIMESTAMP(7); +-- error 1426 +SELECT NOW(7); +-- error 1426 +SELECT SYSDATE(7); +-- error 1426 +SELECT UTC_TIME(7); +-- error 1426 +SELECT UTC_TIMESTAMP(7);