Skip to content

Commit

Permalink
pkg/expression: fix errors setting date and time precision match mysql
Browse files Browse the repository at this point in the history
ref #56451
  • Loading branch information
chagelo committed Oct 7, 2024
1 parent 74034d4 commit a2b0d7b
Show file tree
Hide file tree
Showing 8 changed files with 98 additions and 61 deletions.
4 changes: 2 additions & 2 deletions errors.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
Expand Down Expand Up @@ -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"]
Expand Down
2 changes: 1 addition & 1 deletion pkg/errno/errname.go
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
61 changes: 32 additions & 29 deletions pkg/expression/builtin_time.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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)
fspu8 := uint8(num)
if !isNull && fspu8 > uint8(types.MaxFsp) {
return types.ZeroTime, true, types.ErrTooBigPrecision.GenWithStackByArgs(fspu8, "utc_timestamp", types.MaxFsp)
}

result, isNull, err := evalUTCTimestampWithFsp(ctx, int(num))
result, isNull, err := evalUTCTimestampWithFsp(ctx, int(fspu8))
return result, isNull, err
}

Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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)
fspu8 := 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)
fspu8 = 0
} else if fspu8 > uint8(types.MaxFsp) {
return types.ZeroTime, true, types.ErrTooBigPrecision.GenWithStackByArgs(fspu8, "now", types.MaxFsp)
}

result, isNull, err := evalNowWithFsp(ctx, int(fsp))
result, isNull, err := evalNowWithFsp(ctx, int(fspu8))
return result, isNull, err
}

Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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)
fspu8 := uint8(fsp)
if fspu8 > uint8(types.MaxFsp) {
return types.Duration{}, true, types.ErrTooBigPrecision.GenWithStackByArgs(fspu8, "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(fspu8))
return v, false, err
}

Expand Down Expand Up @@ -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
}
Expand All @@ -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)
fspu8 := uint8(fsp)
if fspu8 > uint8(types.MaxFsp) {
return 0, types.ErrTooBigPrecision.GenWithStackByArgs(fspu8, funcName, types.MaxFsp)
}
return int(fsp), nil
}
// Should no happen. But our tests may generate non-constant input.
return 0, nil
Expand Down
38 changes: 17 additions & 21 deletions pkg/expression/builtin_time_vec.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion pkg/parser/mysql/errname.go
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
20 changes: 19 additions & 1 deletion tests/integrationtest/r/expression/builtin.result
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.
10 changes: 5 additions & 5 deletions tests/integrationtest/r/expression/cast.result
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down
22 changes: 21 additions & 1 deletion tests/integrationtest/t/expression/builtin.test
Original file line number Diff line number Diff line change
Expand Up @@ -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);

0 comments on commit a2b0d7b

Please sign in to comment.