expression: make str_to_date() more compatible with MySQL (#20298)
This commit is contained in:
@ -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
|
||||
|
||||
@ -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));`)
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user