Skip to content

Commit

Permalink
expression: QUARTER/DATE_FORMAT compatibility with mysql for 0/0.0 va…
Browse files Browse the repository at this point in the history
…lues (#12488)
  • Loading branch information
ekalinin authored and ngaut committed Nov 9, 2019
1 parent b3bf319 commit b1aad07
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 6 deletions.
7 changes: 6 additions & 1 deletion expression/builtin_cast.go
Original file line number Diff line number Diff line change
Expand Up @@ -823,8 +823,13 @@ func (b *builtinCastRealAsTimeSig) evalTime(row chunk.Row) (types.Time, bool, er
if isNull || err != nil {
return types.Time{}, true, err
}
// MySQL compatibility: 0 should not be converted to null, see #11203
fv := strconv.FormatFloat(val, 'f', -1, 64)
if fv == "0" {
return types.Time{}, false, nil
}
sc := b.ctx.GetSessionVars().StmtCtx
res, err := types.ParseTime(sc, strconv.FormatFloat(val, 'f', -1, 64), b.tp.Tp, int8(b.tp.Decimal))
res, err := types.ParseTime(sc, fv, b.tp.Tp, int8(b.tp.Decimal))
if err != nil {
return types.Time{}, true, handleInvalidTimeError(b.ctx, err)
}
Expand Down
30 changes: 27 additions & 3 deletions expression/builtin_time.go
Original file line number Diff line number Diff line change
Expand Up @@ -791,13 +791,28 @@ func (b *builtinDateFormatSig) evalString(row chunk.Row) (string, bool, error) {
if isNull || err != nil {
return "", isNull, handleInvalidTimeError(b.ctx, err)
}
if t.InvalidZero() {
return "", true, handleInvalidTimeError(b.ctx, types.ErrIncorrectDatetimeValue.GenWithStackByArgs(t.String()))
}
formatMask, isNull, err := b.args[1].EvalString(b.ctx, row)
if isNull || err != nil {
return "", isNull, err
}
// MySQL compatibility, #11203
// If format mask is 0 then return 0 without warnings
if formatMask == "0" {
return "0", false, nil
}

if t.InvalidZero() {
// MySQL compatibility, #11203
// 0 | 0.0 should be converted to null without warnings
n, isNullInt, errInt := b.args[0].EvalInt(b.ctx, row)
isOriginalIntOrDecimalZero := n == 0 && !isNullInt && errInt == nil
// Args like "0000-00-00", "0000-00-00 00:00:00" set Fsp to 6
isOriginalStringZero := t.Fsp > 0
if isOriginalIntOrDecimalZero && !isOriginalStringZero {
return "", true, nil
}
return "", true, handleInvalidTimeError(b.ctx, types.ErrIncorrectDatetimeValue.GenWithStackByArgs(t.String()))
}

res, err := t.DateFormat(formatMask)
return res, isNull, err
Expand Down Expand Up @@ -5544,6 +5559,15 @@ func (b *builtinQuarterSig) evalInt(row chunk.Row) (int64, bool, error) {
}

if date.IsZero() {
// MySQL compatibility, #11203
// 0 | 0.0 should be converted to 0 value (not null)
n, err := date.ToNumber().ToInt()
isOriginalIntOrDecimalZero := err == nil && n == 0
// Args like "0000-00-00", "0000-00-00 00:00:00" set Fsp to 6
isOriginalStringZero := date.Fsp > 0
if isOriginalIntOrDecimalZero && !isOriginalStringZero {
return 0, false, nil
}
return 0, true, handleInvalidTimeError(b.ctx, types.ErrIncorrectDatetimeValue.GenWithStackByArgs(date.String()))
}

Expand Down
22 changes: 20 additions & 2 deletions expression/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1282,8 +1282,16 @@ func (s *testIntegrationSuite2) TestTimeBuiltin(c *C) {
// for quarter
result = tk.MustQuery(`select quarter("2012-00-20"), quarter("2012-01-21"), quarter("2012-03-22"), quarter("2012-05-23"), quarter("2012-08-24"), quarter("2012-09-25"), quarter("2012-11-26"), quarter("2012-12-27");`)
result.Check(testkit.Rows("0 1 1 2 3 3 4 4"))
result = tk.MustQuery(`select quarter("2012-14-20"), quarter("0000-00-00"), quarter("aa"), quarter(null), quarter(11), quarter(12.99);`)
result.Check(testkit.Rows("<nil> <nil> <nil> <nil> <nil> <nil>"))
result = tk.MustQuery(`select quarter("2012-14-20"), quarter("aa"), quarter(null), quarter(11), quarter(12.99);`)
result.Check(testkit.Rows("<nil> <nil> <nil> <nil> <nil>"))
result = tk.MustQuery(`select quarter("0000-00-00"), quarter("0000-00-00 00:00:00");`)
result.Check(testkit.Rows("<nil> <nil>"))
tk.MustQuery("show warnings").Check(testutil.RowsWithSep("|",
"Warning|1292|Incorrect datetime value: '0000-00-00 00:00:00.000000'",
"Warning|1292|Incorrect datetime value: '0000-00-00 00:00:00.000000'"))
result = tk.MustQuery(`select quarter(0), quarter(0.0), quarter(0e1), quarter(0.00);`)
result.Check(testkit.Rows("0 0 0 0"))
tk.MustQuery("show warnings").Check(testkit.Rows())

// for from_days
result = tk.MustQuery(`select from_days(0), from_days(-199), from_days(1111), from_days(120), from_days(1), from_days(1111111), from_days(9999999), from_days(22222);`)
Expand Down Expand Up @@ -1634,6 +1642,16 @@ func (s *testIntegrationSuite2) TestTimeBuiltin(c *C) {
result.Check(testkit.Rows("Friday November 13 2015 10:20:19 AM 15"))
result = tk.MustQuery(`SELECT DATE_FORMAT('0000-00-00', '%W %M %e %Y %r %y');`)
result.Check(testkit.Rows("<nil>"))
tk.MustQuery("show warnings").Check(testutil.RowsWithSep("|",
"Warning|1292|Incorrect datetime value: '0000-00-00 00:00:00.000000'"))
result = tk.MustQuery(`SELECT DATE_FORMAT('0', '%W %M %e %Y %r %y'), DATE_FORMAT('0.0', '%W %M %e %Y %r %y'), DATE_FORMAT(0, 0);`)
result.Check(testkit.Rows("<nil> <nil> 0"))
tk.MustQuery("show warnings").Check(testutil.RowsWithSep("|",
"Warning|1292|invalid time format: '0'",
"Warning|1292|invalid time format: '0.0'"))
result = tk.MustQuery(`SELECT DATE_FORMAT(0, '%W %M %e %Y %r %y'), DATE_FORMAT(0.0, '%W %M %e %Y %r %y');`)
result.Check(testkit.Rows("<nil> <nil>"))
tk.MustQuery("show warnings").Check(testkit.Rows())

// for yearweek
result = tk.MustQuery(`select yearweek("2014-12-27"), yearweek("2014-29-27"), yearweek("2014-00-27"), yearweek("2014-12-27 12:38:32"), yearweek("2014-12-27 12:38:32.1111111"), yearweek("2014-12-27 12:90:32"), yearweek("2014-12-27 89:38:32.1111111");`)
Expand Down
8 changes: 8 additions & 0 deletions types/time.go
Original file line number Diff line number Diff line change
Expand Up @@ -1355,6 +1355,10 @@ func ParseTime(sc *stmtctx.StatementContext, str string, tp byte, fsp int8) (Tim

// ParseTimeFromFloatString is similar to ParseTime, except that it's used to parse a float converted string.
func ParseTimeFromFloatString(sc *stmtctx.StatementContext, str string, tp byte, fsp int8) (Time, error) {
// MySQL compatibility: 0.0 should not be converted to null, see #11203
if len(str) >= 3 && str[:3] == "0.0" {
return Time{Time: ZeroTime, Type: tp}, nil
}
return parseTime(sc, str, tp, fsp, true)
}

Expand Down Expand Up @@ -1395,6 +1399,10 @@ func ParseDate(sc *stmtctx.StatementContext, str string) (Time, error) {
// ParseTimeFromNum parses a formatted int64,
// returns the value which type is tp.
func ParseTimeFromNum(sc *stmtctx.StatementContext, num int64, tp byte, fsp int8) (Time, error) {
// MySQL compatibility: 0 should not be converted to null, see #11203
if num == 0 {
return Time{Time: ZeroTime, Type: tp}, nil
}
fsp, err := CheckFsp(int(fsp))
if err != nil {
return Time{Time: ZeroTime, Type: tp}, errors.Trace(err)
Expand Down

0 comments on commit b1aad07

Please sign in to comment.