Skip to content

Commit f8ea232

Browse files
committed
expression: fix issue that date_add and date_sub is incompatible with MySQL (pingcap#9702)
1 parent 92096dd commit f8ea232

File tree

4 files changed

+104
-8
lines changed

4 files changed

+104
-8
lines changed

expression/builtin_time.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2651,6 +2651,13 @@ func (du *baseDateArithmitical) add(ctx sessionctx.Context, date types.Time, int
26512651
}
26522652

26532653
date.Time = types.FromGoTime(goTime)
2654+
overflow, err := types.DateTimeIsOverflow(ctx.GetSessionVars().StmtCtx, date)
2655+
if err != nil {
2656+
return types.Time{}, true, err
2657+
}
2658+
if overflow {
2659+
return types.Time{}, true, handleInvalidTimeError(ctx, types.ErrDatetimeFunctionOverflow.GenWithStackByArgs("datetime"))
2660+
}
26542661
return date, false, nil
26552662
}
26562663

@@ -2677,6 +2684,13 @@ func (du *baseDateArithmitical) sub(ctx sessionctx.Context, date types.Time, int
26772684
}
26782685

26792686
date.Time = types.FromGoTime(goTime)
2687+
overflow, err := types.DateTimeIsOverflow(ctx.GetSessionVars().StmtCtx, date)
2688+
if err != nil {
2689+
return types.Time{}, true, err
2690+
}
2691+
if overflow {
2692+
return types.Time{}, true, handleInvalidTimeError(ctx, types.ErrDatetimeFunctionOverflow.GenWithStackByArgs("datetime"))
2693+
}
26802694
return date, false, nil
26812695
}
26822696

expression/errors.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,8 @@ func init() {
6969
// handleInvalidTimeError reports error or warning depend on the context.
7070
func handleInvalidTimeError(ctx sessionctx.Context, err error) error {
7171
if err == nil || !(terror.ErrorEqual(err, types.ErrInvalidTimeFormat) || types.ErrIncorrectDatetimeValue.Equal(err) ||
72-
types.ErrTruncatedWrongValue.Equal(err) || types.ErrInvalidWeekModeFormat.Equal(err)) {
72+
types.ErrTruncatedWrongValue.Equal(err) || types.ErrInvalidWeekModeFormat.Equal(err) ||
73+
types.ErrDatetimeFunctionOverflow.Equal(err)) {
7374
return err
7475
}
7576
sc := ctx.GetSessionVars().StmtCtx

expression/integration_test.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1881,6 +1881,40 @@ func (s *testIntegrationSuite) TestOpBuiltin(c *C) {
18811881
result.Check(testkit.Rows("1 0 -9 -0.001 0.999 <nil> aaa"))
18821882
}
18831883

1884+
func (s *testIntegrationSuite) TestDatetimeOverflow(c *C) {
1885+
defer s.cleanEnv(c)
1886+
tk := testkit.NewTestKit(c, s.store)
1887+
tk.MustExec("use test")
1888+
1889+
tk.MustExec("create table t1 (d date)")
1890+
tk.MustExec("set sql_mode='traditional'")
1891+
overflowSQLs := []string{
1892+
"insert into t1 (d) select date_add('2000-01-01',interval 8000 year)",
1893+
"insert into t1 (d) select date_sub('2000-01-01', INTERVAL 2001 YEAR)",
1894+
"insert into t1 (d) select date_add('9999-12-31',interval 1 year)",
1895+
"insert into t1 (d) select date_sub('1000-01-01', INTERVAL 1 YEAR)",
1896+
"insert into t1 (d) select date_add('9999-12-31',interval 1 day)",
1897+
"insert into t1 (d) select date_sub('1000-01-01', INTERVAL 1 day)",
1898+
"insert into t1 (d) select date_sub('1000-01-01', INTERVAL 1 second)",
1899+
}
1900+
1901+
for _, sql := range overflowSQLs {
1902+
_, err := tk.Exec(sql)
1903+
c.Assert(err.Error(), Equals, "[types:1441]Datetime function: datetime field overflow")
1904+
}
1905+
1906+
tk.MustExec("set sql_mode=''")
1907+
for _, sql := range overflowSQLs {
1908+
tk.MustExec(sql)
1909+
}
1910+
1911+
rows := make([]string, 0, len(overflowSQLs))
1912+
for range overflowSQLs {
1913+
rows = append(rows, "<nil>")
1914+
}
1915+
tk.MustQuery("select * from t1").Check(testkit.Rows(rows...))
1916+
}
1917+
18841918
func (s *testIntegrationSuite) TestBuiltin(c *C) {
18851919
defer s.cleanEnv(c)
18861920
tk := testkit.NewTestKit(c, s.store)

types/time.go

Lines changed: 54 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,14 @@ import (
3232

3333
// Portable analogs of some common call errors.
3434
var (
35-
ErrInvalidTimeFormat = terror.ClassTypes.New(mysql.ErrTruncatedWrongValue, "invalid time format: '%v'")
36-
ErrInvalidWeekModeFormat = terror.ClassTypes.New(mysql.ErrTruncatedWrongValue, "invalid week mode format: '%v'")
37-
ErrInvalidYearFormat = errors.New("invalid year format")
38-
ErrInvalidYear = errors.New("invalid year")
39-
ErrZeroDate = errors.New("datetime zero in date")
40-
ErrIncorrectDatetimeValue = terror.ClassTypes.New(mysql.ErrTruncatedWrongValue, "Incorrect datetime value: '%s'")
41-
ErrTruncatedWrongValue = terror.ClassTypes.New(mysql.ErrTruncatedWrongValue, mysql.MySQLErrName[mysql.ErrTruncatedWrongValue])
35+
ErrInvalidTimeFormat = terror.ClassTypes.New(mysql.ErrTruncatedWrongValue, "invalid time format: '%v'")
36+
ErrInvalidWeekModeFormat = terror.ClassTypes.New(mysql.ErrTruncatedWrongValue, "invalid week mode format: '%v'")
37+
ErrInvalidYearFormat = errors.New("invalid year format")
38+
ErrInvalidYear = errors.New("invalid year")
39+
ErrZeroDate = errors.New("datetime zero in date")
40+
ErrIncorrectDatetimeValue = terror.ClassTypes.New(mysql.ErrTruncatedWrongValue, "Incorrect datetime value: '%s'")
41+
ErrDatetimeFunctionOverflow = terror.ClassTypes.New(mysql.ErrDatetimeFunctionOverflow, mysql.MySQLErrName[mysql.ErrDatetimeFunctionOverflow])
42+
ErrTruncatedWrongValue = terror.ClassTypes.New(mysql.ErrTruncatedWrongValue, mysql.MySQLErrName[mysql.ErrTruncatedWrongValue])
4243
)
4344

4445
// Time format without fractional seconds precision.
@@ -2450,3 +2451,49 @@ func DateFSP(date string) (fsp int) {
24502451
}
24512452
return
24522453
}
2454+
2455+
// DateTimeIsOverflow return if this date is overflow.
2456+
// See: https://dev.mysql.com/doc/refman/8.0/en/datetime.html
2457+
func DateTimeIsOverflow(sc *stmtctx.StatementContext, date Time) (bool, error) {
2458+
tz := sc.TimeZone
2459+
if tz == nil {
2460+
tz = gotime.Local
2461+
}
2462+
2463+
var err error
2464+
var b, e, t gotime.Time
2465+
switch date.Type {
2466+
case mysql.TypeDate, mysql.TypeDatetime:
2467+
if b, err = MinDatetime.GoTime(tz); err != nil {
2468+
return false, err
2469+
}
2470+
if e, err = MaxDatetime.GoTime(tz); err != nil {
2471+
return false, err
2472+
}
2473+
case mysql.TypeTimestamp:
2474+
minTS, maxTS := MinTimestamp, MaxTimestamp
2475+
if tz != gotime.UTC {
2476+
if err = minTS.ConvertTimeZone(gotime.UTC, tz); err != nil {
2477+
return false, err
2478+
}
2479+
if err = maxTS.ConvertTimeZone(gotime.UTC, tz); err != nil {
2480+
return false, err
2481+
}
2482+
}
2483+
if b, err = minTS.Time.GoTime(tz); err != nil {
2484+
return false, err
2485+
}
2486+
if e, err = maxTS.Time.GoTime(tz); err != nil {
2487+
return false, err
2488+
}
2489+
default:
2490+
return false, nil
2491+
}
2492+
2493+
if t, err = date.Time.GoTime(tz); err != nil {
2494+
return false, err
2495+
}
2496+
2497+
inRange := (t.After(b) || t.Equal(b)) && (t.Before(e) || t.Equal(e))
2498+
return !inRange, nil
2499+
}

0 commit comments

Comments
 (0)