expression: make str_to_date() more compatible with MySQL (#20298)

This commit is contained in:
Zhou Kunqin
2020-10-28 19:06:25 +08:00
committed by GitHub
parent b468a8a4fb
commit ccd48eeb4d
3 changed files with 41 additions and 13 deletions

View File

@ -2005,9 +2005,6 @@ func (b *builtinStrToDateDurationSig) evalDuration(row chunk.Row) (types.Duratio
if !succ {
return types.Duration{}, true, handleInvalidTimeError(b.ctx, types.ErrWrongValue.GenWithStackByArgs(types.DateTimeStr, t.String()))
}
if b.ctx.GetSessionVars().SQLMode.HasNoZeroDateMode() && (t.Year() == 0 || t.Month() == 0 || t.Day() == 0) {
return types.Duration{}, true, handleInvalidTimeError(b.ctx, types.ErrWrongValue.GenWithStackByArgs(types.DateTimeStr, t.String()))
}
t.SetFsp(int8(b.tp.Decimal))
dur, err := t.ConvertToDuration()
return dur, err != nil, err

View File

@ -1938,13 +1938,28 @@ func (s *testIntegrationSuite2) TestTimeBuiltin(c *C) {
result.Check(testkit.Rows("2017-01-01 2017-01-01 12:20:59 12:20:59"))
result = tk.MustQuery("select str_to_date('aaa01-01-2017', 'aaa%d-%m-%Y'), str_to_date('59:20:12 aaa01-01-2017', '%s:%i:%H aaa%d-%m-%Y'), str_to_date('59:20:12aaa', '%s:%i:%Haaa')")
result.Check(testkit.Rows("2017-01-01 2017-01-01 12:20:59 12:20:59"))
result = tk.MustQuery("select str_to_date('01-01-2017', '%d'), str_to_date('59', '%d-%Y')")
// TODO: MySQL returns "<nil> <nil>".
result.Check(testkit.Rows("0000-00-01 <nil>"))
tk.MustQuery("show warnings").Check(testutil.RowsWithSep("|", "Warning|1292|Incorrect datetime value: '0000-00-00 00:00:00'"))
result = tk.MustQuery("show warnings")
result.Sort().Check(testutil.RowsWithSep("|",
"Warning|1292|Incorrect datetime value: '0000-00-00 00:00:00'",
"Warning|1292|Truncated incorrect datetime value: '01-01-2017'"))
result = tk.MustQuery("select str_to_date('2018-6-1', '%Y-%m-%d'), str_to_date('2018-6-1', '%Y-%c-%d'), str_to_date('59:20:1', '%s:%i:%k'), str_to_date('59:20:1', '%s:%i:%l')")
result.Check(testkit.Rows("2018-06-01 2018-06-01 01:20:59 01:20:59"))
result = tk.MustQuery("select str_to_date('2020-07-04 11:22:33 PM c', '%Y-%m-%d %r')")
result.Check(testkit.Rows("2020-07-04 23:22:33"))
result = tk.MustQuery("show warnings")
result.Check(testutil.RowsWithSep("|", "Warning|1292|Truncated incorrect datetime value: '2020-07-04 11:22:33 PM c'"))
result = tk.MustQuery("select str_to_date('11:22:33 PM', ' %r')")
result.Check(testkit.Rows("23:22:33"))
result = tk.MustQuery("show warnings")
result.Check(testkit.Rows())
// for maketime
tk.MustExec(`drop table if exists t`)
tk.MustExec(`create table t(a double, b float, c decimal(10,4));`)

View File

@ -2678,7 +2678,8 @@ func abbrDayOfMonth(day int) string {
func (t *Time) StrToDate(sc *stmtctx.StatementContext, date, format string) bool {
ctx := make(map[string]int)
var tm CoreTime
if !strToDate(&tm, date, format, ctx) {
success, warning := strToDate(&tm, date, format, ctx)
if !success {
t.SetCoreTime(ZeroCoreTime)
t.SetType(mysql.TypeDatetime)
t.SetFsp(0)
@ -2690,7 +2691,15 @@ func (t *Time) StrToDate(sc *stmtctx.StatementContext, date, format string) bool
t.SetCoreTime(tm)
t.SetType(mysql.TypeDatetime)
return t.check(sc) == nil
if t.check(sc) != nil {
return false
}
if warning {
// Only append this warning when success but still need warning.
// Currently this only happens when `date` has extra characters at the end.
sc.AppendWarning(ErrTruncatedWrongVal.GenWithStackByArgs(DateTimeStr, date))
}
return true
}
// mysqlTimeFix fixes the Time use the values in the context.
@ -2728,30 +2737,35 @@ func mysqlTimeFix(t *CoreTime, ctx map[string]int) error {
return nil
}
// strToDate converts date string according to format, returns true on success,
// strToDate converts date string according to format,
// the value will be stored in argument t or ctx.
func strToDate(t *CoreTime, date string, format string, ctx map[string]int) bool {
// The second return value is true when success but still need to append a warning.
func strToDate(t *CoreTime, date string, format string, ctx map[string]int) (success bool, warning bool) {
date = skipWhiteSpace(date)
format = skipWhiteSpace(format)
token, formatRemain, succ := getFormatToken(format)
if !succ {
return false
return false, false
}
if token == "" {
// Extra characters at the end of date are ignored.
return true
if len(date) != 0 {
// Extra characters at the end of date are ignored, but a warning should be reported at this case.
return true, true
}
// Normal case. Both token and date are empty now.
return true, false
}
if len(date) == 0 {
ctx[token] = 0
return true
return true, false
}
dateRemain, succ := matchDateWithToken(t, date, token, ctx)
if !succ {
return false
return false, false
}
return strToDate(t, dateRemain, formatRemain, ctx)
@ -2948,8 +2962,10 @@ func time12Hour(t *CoreTime, input string, ctx map[string]int) (string, bool) {
switch {
case strings.HasPrefix(remain, "AM"):
t.setHour(uint8(hour))
remain = strings.TrimPrefix(remain, "AM")
case strings.HasPrefix(remain, "PM"):
t.setHour(uint8(hour + 12))
remain = strings.TrimPrefix(remain, "PM")
default:
return input, false
}