From 214f77cdb510c7f4ba65872774a185f5dceabd61 Mon Sep 17 00:00:00 2001 From: kanchairen Date: Mon, 15 Jul 2019 05:45:10 -0700 Subject: [PATCH] Function SUBTIME should return NULL with a warning if its argument is an invalid time value. (#11175) --- expression/builtin_time.go | 64 +++++++++++++++++++++++++++++---- expression/builtin_time_test.go | 56 +++++++++++++++++++++++++++++ expression/integration_test.go | 35 +++++++++++++++++- 3 files changed, 148 insertions(+), 7 deletions(-) diff --git a/expression/builtin_time.go b/expression/builtin_time.go index 52954e7aeb16f..343496f53ae44 100644 --- a/expression/builtin_time.go +++ b/expression/builtin_time.go @@ -4801,6 +4801,10 @@ func (b *builtinAddDatetimeAndStringSig) evalTime(row chunk.Row) (types.Time, bo sc := b.ctx.GetSessionVars().StmtCtx arg1, err := types.ParseDuration(sc, s, types.GetFsp(s)) if err != nil { + if terror.ErrorEqual(err, types.ErrTruncatedWrongVal) { + sc.AppendWarning(err) + return types.ZeroDatetime, true, nil + } return types.ZeroDatetime, true, err } result, err := arg0.Add(sc, arg1) @@ -4875,8 +4879,13 @@ func (b *builtinAddDurationAndStringSig) evalDuration(row chunk.Row) (types.Dura if !isDuration(s) { return types.ZeroDuration, true, nil } - arg1, err := types.ParseDuration(b.ctx.GetSessionVars().StmtCtx, s, types.GetFsp(s)) + sc := b.ctx.GetSessionVars().StmtCtx + arg1, err := types.ParseDuration(sc, s, types.GetFsp(s)) if err != nil { + if terror.ErrorEqual(err, types.ErrTruncatedWrongVal) { + sc.AppendWarning(err) + return types.ZeroDuration, true, nil + } return types.ZeroDuration, true, err } result, err := arg0.Add(arg1) @@ -4931,6 +4940,10 @@ func (b *builtinAddStringAndDurationSig) evalString(row chunk.Row) (result strin if isDuration(arg0) { result, err = strDurationAddDuration(sc, arg0, arg1) if err != nil { + if terror.ErrorEqual(err, types.ErrTruncatedWrongVal) { + sc.AppendWarning(err) + return "", true, nil + } return "", true, err } return result, false, nil @@ -4971,11 +4984,19 @@ func (b *builtinAddStringAndStringSig) evalString(row chunk.Row) (result string, sc := b.ctx.GetSessionVars().StmtCtx arg1, err = types.ParseDuration(sc, arg1Str, getFsp4TimeAddSub(arg1Str)) if err != nil { + if terror.ErrorEqual(err, types.ErrTruncatedWrongVal) { + sc.AppendWarning(err) + return "", true, nil + } return "", true, err } if isDuration(arg0) { result, err = strDurationAddDuration(sc, arg0, arg1) if err != nil { + if terror.ErrorEqual(err, types.ErrTruncatedWrongVal) { + sc.AppendWarning(err) + return "", true, nil + } return "", true, err } return result, false, nil @@ -5033,8 +5054,13 @@ func (b *builtinAddDateAndStringSig) evalString(row chunk.Row) (string, bool, er if !isDuration(s) { return "", true, nil } - arg1, err := types.ParseDuration(b.ctx.GetSessionVars().StmtCtx, s, getFsp4TimeAddSub(s)) + sc := b.ctx.GetSessionVars().StmtCtx + arg1, err := types.ParseDuration(sc, s, getFsp4TimeAddSub(s)) if err != nil { + if terror.ErrorEqual(err, types.ErrTruncatedWrongVal) { + sc.AppendWarning(err) + return "", true, nil + } return "", true, err } result, err := arg0.Add(arg1) @@ -5705,6 +5731,10 @@ func (b *builtinSubDatetimeAndStringSig) evalTime(row chunk.Row) (types.Time, bo sc := b.ctx.GetSessionVars().StmtCtx arg1, err := types.ParseDuration(sc, s, types.GetFsp(s)) if err != nil { + if terror.ErrorEqual(err, types.ErrTruncatedWrongVal) { + sc.AppendWarning(err) + return types.ZeroDatetime, true, nil + } return types.ZeroDatetime, true, err } arg1time, err := arg1.ConvertToTime(sc, mysql.TypeDatetime) @@ -5761,6 +5791,10 @@ func (b *builtinSubStringAndDurationSig) evalString(row chunk.Row) (result strin if isDuration(arg0) { result, err = strDurationSubDuration(sc, arg0, arg1) if err != nil { + if terror.ErrorEqual(err, types.ErrTruncatedWrongVal) { + sc.AppendWarning(err) + return "", true, nil + } return "", true, err } return result, false, nil @@ -5798,14 +5832,22 @@ func (b *builtinSubStringAndStringSig) evalString(row chunk.Row) (result string, if isNull || err != nil { return "", isNull, err } - arg1, err = types.ParseDuration(b.ctx.GetSessionVars().StmtCtx, s, getFsp4TimeAddSub(s)) + sc := b.ctx.GetSessionVars().StmtCtx + arg1, err = types.ParseDuration(sc, s, getFsp4TimeAddSub(s)) if err != nil { + if terror.ErrorEqual(err, types.ErrTruncatedWrongVal) { + sc.AppendWarning(err) + return "", true, nil + } return "", true, err } - sc := b.ctx.GetSessionVars().StmtCtx if isDuration(arg0) { result, err = strDurationSubDuration(sc, arg0, arg1) if err != nil { + if terror.ErrorEqual(err, types.ErrTruncatedWrongVal) { + sc.AppendWarning(err) + return "", true, nil + } return "", true, err } return result, false, nil @@ -5882,8 +5924,13 @@ func (b *builtinSubDurationAndStringSig) evalDuration(row chunk.Row) (types.Dura if !isDuration(s) { return types.ZeroDuration, true, nil } - arg1, err := types.ParseDuration(b.ctx.GetSessionVars().StmtCtx, s, types.GetFsp(s)) + sc := b.ctx.GetSessionVars().StmtCtx + arg1, err := types.ParseDuration(sc, s, types.GetFsp(s)) if err != nil { + if terror.ErrorEqual(err, types.ErrTruncatedWrongVal) { + sc.AppendWarning(err) + return types.ZeroDuration, true, nil + } return types.ZeroDuration, true, err } result, err := arg0.Sub(arg1) @@ -5955,8 +6002,13 @@ func (b *builtinSubDateAndStringSig) evalString(row chunk.Row) (string, bool, er if !isDuration(s) { return "", true, nil } - arg1, err := types.ParseDuration(b.ctx.GetSessionVars().StmtCtx, s, getFsp4TimeAddSub(s)) + sc := b.ctx.GetSessionVars().StmtCtx + arg1, err := types.ParseDuration(sc, s, getFsp4TimeAddSub(s)) if err != nil { + if terror.ErrorEqual(err, types.ErrTruncatedWrongVal) { + sc.AppendWarning(err) + return "", true, nil + } return "", true, err } result, err := arg0.Sub(arg1) diff --git a/expression/builtin_time_test.go b/expression/builtin_time_test.go index 60dbbf504523b..d3bff3f275dfe 100644 --- a/expression/builtin_time_test.go +++ b/expression/builtin_time_test.go @@ -24,6 +24,7 @@ import ( "github.com/pingcap/parser/ast" "github.com/pingcap/parser/charset" "github.com/pingcap/parser/mysql" + "github.com/pingcap/parser/terror" "github.com/pingcap/tidb/sessionctx" "github.com/pingcap/tidb/sessionctx/stmtctx" "github.com/pingcap/tidb/sessionctx/variable" @@ -937,6 +938,33 @@ func (s *testEvaluatorSuite) TestAddTimeSig(c *C) { c.Assert(result, Equals, t.expect) } + tblWarning := []struct { + Input interface{} + InputDuration interface{} + warning *terror.Error + }{ + {"0", "-32073", types.ErrTruncatedWrongVal}, + {"-32073", "0", types.ErrTruncatedWrongVal}, + {types.ZeroDuration, "-32073", types.ErrTruncatedWrongVal}, + {"-32073", types.ZeroDuration, types.ErrTruncatedWrongVal}, + {types.CurrentTime(mysql.TypeTimestamp), "-32073", types.ErrTruncatedWrongVal}, + {types.CurrentTime(mysql.TypeDate), "-32073", types.ErrTruncatedWrongVal}, + {types.CurrentTime(mysql.TypeDatetime), "-32073", types.ErrTruncatedWrongVal}, + } + for i, t := range tblWarning { + tmpInput := types.NewDatum(t.Input) + tmpInputDuration := types.NewDatum(t.InputDuration) + f, err := fc.getFunction(s.ctx, s.datumsToConstants([]types.Datum{tmpInput, tmpInputDuration})) + c.Assert(err, IsNil) + d, err := evalBuiltinFunc(f, chunk.Row{}) + c.Assert(err, IsNil) + result, _ := d.ToString() + c.Assert(result, Equals, "") + c.Assert(d.IsNull(), Equals, true) + warnings := s.ctx.GetSessionVars().StmtCtx.GetWarnings() + c.Assert(len(warnings), Equals, i+1) + c.Assert(terror.ErrorEqual(t.warning, warnings[i].Err), IsTrue, Commentf("err %v", warnings[i].Err)) + } } func (s *testEvaluatorSuite) TestSubTimeSig(c *C) { @@ -1002,6 +1030,34 @@ func (s *testEvaluatorSuite) TestSubTimeSig(c *C) { result, _ := d.ToString() c.Assert(result, Equals, t.expect) } + + tblWarning := []struct { + Input interface{} + InputDuration interface{} + warning *terror.Error + }{ + {"0", "-32073", types.ErrTruncatedWrongVal}, + {"-32073", "0", types.ErrTruncatedWrongVal}, + {types.ZeroDuration, "-32073", types.ErrTruncatedWrongVal}, + {"-32073", types.ZeroDuration, types.ErrTruncatedWrongVal}, + {types.CurrentTime(mysql.TypeTimestamp), "-32073", types.ErrTruncatedWrongVal}, + {types.CurrentTime(mysql.TypeDate), "-32073", types.ErrTruncatedWrongVal}, + {types.CurrentTime(mysql.TypeDatetime), "-32073", types.ErrTruncatedWrongVal}, + } + for i, t := range tblWarning { + tmpInput := types.NewDatum(t.Input) + tmpInputDuration := types.NewDatum(t.InputDuration) + f, err := fc.getFunction(s.ctx, s.datumsToConstants([]types.Datum{tmpInput, tmpInputDuration})) + c.Assert(err, IsNil) + d, err := evalBuiltinFunc(f, chunk.Row{}) + c.Assert(err, IsNil) + result, _ := d.ToString() + c.Assert(result, Equals, "") + c.Assert(d.IsNull(), Equals, true) + warnings := s.ctx.GetSessionVars().StmtCtx.GetWarnings() + c.Assert(len(warnings), Equals, i+1) + c.Assert(terror.ErrorEqual(t.warning, warnings[i].Err), IsTrue, Commentf("err %v", warnings[i].Err)) + } } func (s *testEvaluatorSuite) TestSysDate(c *C) { diff --git a/expression/integration_test.go b/expression/integration_test.go index 36da17c5e4a2a..cc2f9cb27e709 100644 --- a/expression/integration_test.go +++ b/expression/integration_test.go @@ -1430,7 +1430,6 @@ func (s *testIntegrationSuite) TestTimeBuiltin(c *C) { result.Check(testkit.Rows(" ")) result = tk.MustQuery("select addtime('2017-01-01', 1), addtime('2017-01-01 01:01:01', 1), addtime(cast('2017-01-01' as date), 1)") result.Check(testkit.Rows("2017-01-01 00:00:01 2017-01-01 01:01:02 00:00:01")) - result = tk.MustQuery("select subtime(a, e), subtime(b, e), subtime(c, e), subtime(d, e) from t") result.Check(testkit.Rows(" ")) result = tk.MustQuery("select subtime('2017-01-01 01:01:01', 0b1), subtime('2017-01-01', b'1'), subtime('01:01:01', 0b1011)") @@ -1438,6 +1437,40 @@ func (s *testIntegrationSuite) TestTimeBuiltin(c *C) { result = tk.MustQuery("select subtime('2017-01-01', 1), subtime('2017-01-01 01:01:01', 1), subtime(cast('2017-01-01' as date), 1)") result.Check(testkit.Rows("2016-12-31 23:59:59 2017-01-01 01:01:00 -00:00:01")) + result = tk.MustQuery("select addtime(-32073, 0), addtime(0, -32073);") + result.Check(testkit.Rows(" ")) + tk.MustQuery("show warnings").Check(testutil.RowsWithSep("|", + "Warning|1292|Truncated incorrect time value: '-32073'", + "Warning|1292|Truncated incorrect time value: '-32073'")) + result = tk.MustQuery("select addtime(-32073, c), addtime(c, -32073) from t;") + result.Check(testkit.Rows(" ")) + tk.MustQuery("show warnings").Check(testutil.RowsWithSep("|", + "Warning|1292|Truncated incorrect time value: '-32073'", + "Warning|1292|Truncated incorrect time value: '-32073'")) + result = tk.MustQuery("select addtime(a, -32073), addtime(b, -32073), addtime(d, -32073) from t;") + result.Check(testkit.Rows(" ")) + tk.MustQuery("show warnings").Check(testutil.RowsWithSep("|", + "Warning|1292|Truncated incorrect time value: '-32073'", + "Warning|1292|Truncated incorrect time value: '-32073'", + "Warning|1292|Truncated incorrect time value: '-32073'")) + + result = tk.MustQuery("select subtime(-32073, 0), subtime(0, -32073);") + result.Check(testkit.Rows(" ")) + tk.MustQuery("show warnings").Check(testutil.RowsWithSep("|", + "Warning|1292|Truncated incorrect time value: '-32073'", + "Warning|1292|Truncated incorrect time value: '-32073'")) + result = tk.MustQuery("select subtime(-32073, c), subtime(c, -32073) from t;") + result.Check(testkit.Rows(" ")) + tk.MustQuery("show warnings").Check(testutil.RowsWithSep("|", + "Warning|1292|Truncated incorrect time value: '-32073'", + "Warning|1292|Truncated incorrect time value: '-32073'")) + result = tk.MustQuery("select subtime(a, -32073), subtime(b, -32073), subtime(d, -32073) from t;") + result.Check(testkit.Rows(" ")) + tk.MustQuery("show warnings").Check(testutil.RowsWithSep("|", + "Warning|1292|Truncated incorrect time value: '-32073'", + "Warning|1292|Truncated incorrect time value: '-32073'", + "Warning|1292|Truncated incorrect time value: '-32073'")) + // fixed issue #3986 tk.MustExec("SET SQL_MODE='NO_ENGINE_SUBSTITUTION';") tk.MustExec("SET TIME_ZONE='+03:00';")