From 0e90154696ce4946c050dd6bb8b9486831d64361 Mon Sep 17 00:00:00 2001 From: Jian Zhang Date: Wed, 2 Jan 2019 17:24:04 +0800 Subject: [PATCH 1/4] expression: throw "too big precision" error for CAST(AS TIME(x)) --- expression/builtin_cast.go | 17 ++++++++++++- expression/errors.go | 2 ++ expression/integration_test.go | 45 ++++++++++++++++++++++++++++++++++ 3 files changed, 63 insertions(+), 1 deletion(-) diff --git a/expression/builtin_cast.go b/expression/builtin_cast.go index e1550ef7c3012..4c97642e17fa2 100644 --- a/expression/builtin_cast.go +++ b/expression/builtin_cast.go @@ -570,6 +570,9 @@ func (b *builtinCastIntAsDurationSig) Clone() builtinFunc { } func (b *builtinCastIntAsDurationSig) evalDuration(row chunk.Row) (res types.Duration, isNull bool, err error) { + if b.tp.Decimal > types.MaxFsp { + return res, true, errTooBigPrecision.GenWithStackByArgs(b.tp.Decimal, "CAST", types.MaxFsp) + } val, isNull, err := b.args[0].EvalInt(b.ctx, row) if isNull || err != nil { return res, isNull, err @@ -842,6 +845,9 @@ func (b *builtinCastRealAsDurationSig) Clone() builtinFunc { } func (b *builtinCastRealAsDurationSig) evalDuration(row chunk.Row) (res types.Duration, isNull bool, err error) { + if b.tp.Decimal > types.MaxFsp { + return res, true, errTooBigPrecision.GenWithStackByArgs(b.tp.Decimal, "CAST", types.MaxFsp) + } val, isNull, err := b.args[0].EvalReal(b.ctx, row) if isNull || err != nil { return res, isNull, err @@ -999,9 +1005,12 @@ func (b *builtinCastDecimalAsDurationSig) Clone() builtinFunc { } func (b *builtinCastDecimalAsDurationSig) evalDuration(row chunk.Row) (res types.Duration, isNull bool, err error) { + if b.tp.Decimal > types.MaxFsp { + return res, true, errTooBigPrecision.GenWithStackByArgs(b.tp.Decimal, "CAST", types.MaxFsp) + } val, isNull, err := b.args[0].EvalDecimal(b.ctx, row) if isNull || err != nil { - return res, false, err + return res, true, err } res, err = types.ParseDuration(b.ctx.GetSessionVars().StmtCtx, string(val.ToString()), b.tp.Decimal) if types.ErrTruncatedWrongVal.Equal(err) { @@ -1207,6 +1216,9 @@ func (b *builtinCastStringAsDurationSig) Clone() builtinFunc { } func (b *builtinCastStringAsDurationSig) evalDuration(row chunk.Row) (res types.Duration, isNull bool, err error) { + if b.tp.Decimal > types.MaxFsp { + return res, true, errTooBigPrecision.GenWithStackByArgs(b.tp.Decimal, "CAST", types.MaxFsp) + } val, isNull, err := b.args[0].EvalString(b.ctx, row) if isNull || err != nil { return res, isNull, err @@ -1646,6 +1658,9 @@ func (b *builtinCastJSONAsDurationSig) Clone() builtinFunc { } func (b *builtinCastJSONAsDurationSig) evalDuration(row chunk.Row) (res types.Duration, isNull bool, err error) { + if b.tp.Decimal > types.MaxFsp { + return res, true, errTooBigPrecision.GenWithStackByArgs(b.tp.Decimal, "CAST", types.MaxFsp) + } val, isNull, err := b.args[0].EvalJSON(b.ctx, row) if isNull || err != nil { return res, isNull, err diff --git a/expression/errors.go b/expression/errors.go index 8a18acfa2ad5a..7218563957838 100644 --- a/expression/errors.go +++ b/expression/errors.go @@ -42,6 +42,7 @@ var ( errWarnOptionIgnored = terror.ClassExpression.New(mysql.WarnOptionIgnored, mysql.MySQLErrName[mysql.WarnOptionIgnored]) errTruncatedWrongValue = terror.ClassExpression.New(mysql.ErrTruncatedWrongValue, mysql.MySQLErrName[mysql.ErrTruncatedWrongValue]) errUnknownLocale = terror.ClassExpression.New(mysql.ErrUnknownLocale, mysql.MySQLErrName[mysql.ErrUnknownLocale]) + errTooBigPrecision = terror.ClassExpression.New(mysql.ErrTooBigPrecision, mysql.MySQLErrName[mysql.ErrTooBigPrecision]) ) func init() { @@ -62,6 +63,7 @@ func init() { mysql.WarnOptionIgnored: mysql.WarnOptionIgnored, mysql.ErrTruncatedWrongValue: mysql.ErrTruncatedWrongValue, mysql.ErrUnknownLocale: mysql.ErrUnknownLocale, + mysql.ErrTooBigPrecision: mysql.ErrTooBigPrecision, } terror.ErrClassToMySQLCodes[terror.ClassExpression] = expressionMySQLErrCodes } diff --git a/expression/integration_test.go b/expression/integration_test.go index 5e5f73d7770f9..ad691039371f0 100644 --- a/expression/integration_test.go +++ b/expression/integration_test.go @@ -3797,3 +3797,48 @@ func (s *testIntegrationSuite) TestUserVarMockWindFunc(c *C) { `3 6 3 key3-value6 insert_order6`, )) } + +func (s *testIntegrationSuite) TestCastAsTime(c *C) { + tk := testkit.NewTestKit(c, s.store) + tk.MustExec(`use test;`) + tk.MustExec(`drop table if exists t;`) + tk.MustExec(`create table t (col1 bigint, col2 double, col3 decimal, col4 varchar(20), col5 json);`) + tk.MustExec(`insert into t values (1, 1, 1, "1", "1");`) + tk.MustExec(`insert into t values (null, null, null, null, null);`) + tk.MustQuery(`select cast(col1 as time), cast(col2 as time), cast(col3 as time), cast(col4 as time), cast(col5 as time) from t where col1 = 1;`).Check(testkit.Rows( + `00:00:01 00:00:01 00:00:01 00:00:01 00:00:01`, + )) + tk.MustQuery(`select cast(col1 as time), cast(col2 as time), cast(col3 as time), cast(col4 as time), cast(col5 as time) from t where col1 is null;`).Check(testkit.Rows( + ` `, + )) + + rs, err := tk.Exec(`select cast(col1 as time(31)) from t where col1 is null;`) + c.Assert(err, IsNil) + _, err = session.GetRows4Test(context.TODO(), tk.Se, rs) + c.Assert(err.Error(), Equals, "[expression:1426]Too big precision 31 specified for column 'CAST'. Maximum is 6.") + c.Assert(rs.Close(), IsNil) + + rs, err = tk.Exec(`select cast(col2 as time(31)) from t where col1 is null;`) + c.Assert(err, IsNil) + _, err = session.GetRows4Test(context.TODO(), tk.Se, rs) + c.Assert(err.Error(), Equals, "[expression:1426]Too big precision 31 specified for column 'CAST'. Maximum is 6.") + c.Assert(rs.Close(), IsNil) + + rs, err = tk.Exec(`select cast(col3 as time(31)) from t where col1 is null;`) + c.Assert(err, IsNil) + _, err = session.GetRows4Test(context.TODO(), tk.Se, rs) + c.Assert(err.Error(), Equals, "[expression:1426]Too big precision 31 specified for column 'CAST'. Maximum is 6.") + c.Assert(rs.Close(), IsNil) + + rs, err = tk.Exec(`select cast(col4 as time(31)) from t where col1 is null;`) + c.Assert(err, IsNil) + _, err = session.GetRows4Test(context.TODO(), tk.Se, rs) + c.Assert(err.Error(), Equals, "[expression:1426]Too big precision 31 specified for column 'CAST'. Maximum is 6.") + c.Assert(rs.Close(), IsNil) + + rs, err = tk.Exec(`select cast(col5 as time(31)) from t where col1 is null;`) + c.Assert(err, IsNil) + _, err = session.GetRows4Test(context.TODO(), tk.Se, rs) + c.Assert(err.Error(), Equals, "[expression:1426]Too big precision 31 specified for column 'CAST'. Maximum is 6.") + c.Assert(rs.Close(), IsNil) +} From 2b78190125f9a4a6a56f70ecc64153d22c52df73 Mon Sep 17 00:00:00 2001 From: Jian Zhang Date: Tue, 8 Jan 2019 15:31:15 +0800 Subject: [PATCH 2/4] address comment --- expression/builtin_cast.go | 15 --------------- expression/errors.go | 2 -- planner/core/errors.go | 3 +++ planner/core/expression_rewriter.go | 15 +++++++++++++++ 4 files changed, 18 insertions(+), 17 deletions(-) diff --git a/expression/builtin_cast.go b/expression/builtin_cast.go index 4c97642e17fa2..6a052e8244bf4 100644 --- a/expression/builtin_cast.go +++ b/expression/builtin_cast.go @@ -570,9 +570,6 @@ func (b *builtinCastIntAsDurationSig) Clone() builtinFunc { } func (b *builtinCastIntAsDurationSig) evalDuration(row chunk.Row) (res types.Duration, isNull bool, err error) { - if b.tp.Decimal > types.MaxFsp { - return res, true, errTooBigPrecision.GenWithStackByArgs(b.tp.Decimal, "CAST", types.MaxFsp) - } val, isNull, err := b.args[0].EvalInt(b.ctx, row) if isNull || err != nil { return res, isNull, err @@ -845,9 +842,6 @@ func (b *builtinCastRealAsDurationSig) Clone() builtinFunc { } func (b *builtinCastRealAsDurationSig) evalDuration(row chunk.Row) (res types.Duration, isNull bool, err error) { - if b.tp.Decimal > types.MaxFsp { - return res, true, errTooBigPrecision.GenWithStackByArgs(b.tp.Decimal, "CAST", types.MaxFsp) - } val, isNull, err := b.args[0].EvalReal(b.ctx, row) if isNull || err != nil { return res, isNull, err @@ -1005,9 +999,6 @@ func (b *builtinCastDecimalAsDurationSig) Clone() builtinFunc { } func (b *builtinCastDecimalAsDurationSig) evalDuration(row chunk.Row) (res types.Duration, isNull bool, err error) { - if b.tp.Decimal > types.MaxFsp { - return res, true, errTooBigPrecision.GenWithStackByArgs(b.tp.Decimal, "CAST", types.MaxFsp) - } val, isNull, err := b.args[0].EvalDecimal(b.ctx, row) if isNull || err != nil { return res, true, err @@ -1216,9 +1207,6 @@ func (b *builtinCastStringAsDurationSig) Clone() builtinFunc { } func (b *builtinCastStringAsDurationSig) evalDuration(row chunk.Row) (res types.Duration, isNull bool, err error) { - if b.tp.Decimal > types.MaxFsp { - return res, true, errTooBigPrecision.GenWithStackByArgs(b.tp.Decimal, "CAST", types.MaxFsp) - } val, isNull, err := b.args[0].EvalString(b.ctx, row) if isNull || err != nil { return res, isNull, err @@ -1658,9 +1646,6 @@ func (b *builtinCastJSONAsDurationSig) Clone() builtinFunc { } func (b *builtinCastJSONAsDurationSig) evalDuration(row chunk.Row) (res types.Duration, isNull bool, err error) { - if b.tp.Decimal > types.MaxFsp { - return res, true, errTooBigPrecision.GenWithStackByArgs(b.tp.Decimal, "CAST", types.MaxFsp) - } val, isNull, err := b.args[0].EvalJSON(b.ctx, row) if isNull || err != nil { return res, isNull, err diff --git a/expression/errors.go b/expression/errors.go index 7218563957838..8a18acfa2ad5a 100644 --- a/expression/errors.go +++ b/expression/errors.go @@ -42,7 +42,6 @@ var ( errWarnOptionIgnored = terror.ClassExpression.New(mysql.WarnOptionIgnored, mysql.MySQLErrName[mysql.WarnOptionIgnored]) errTruncatedWrongValue = terror.ClassExpression.New(mysql.ErrTruncatedWrongValue, mysql.MySQLErrName[mysql.ErrTruncatedWrongValue]) errUnknownLocale = terror.ClassExpression.New(mysql.ErrUnknownLocale, mysql.MySQLErrName[mysql.ErrUnknownLocale]) - errTooBigPrecision = terror.ClassExpression.New(mysql.ErrTooBigPrecision, mysql.MySQLErrName[mysql.ErrTooBigPrecision]) ) func init() { @@ -63,7 +62,6 @@ func init() { mysql.WarnOptionIgnored: mysql.WarnOptionIgnored, mysql.ErrTruncatedWrongValue: mysql.ErrTruncatedWrongValue, mysql.ErrUnknownLocale: mysql.ErrUnknownLocale, - mysql.ErrTooBigPrecision: mysql.ErrTooBigPrecision, } terror.ErrClassToMySQLCodes[terror.ClassExpression] = expressionMySQLErrCodes } diff --git a/planner/core/errors.go b/planner/core/errors.go index 562657db2b97b..4f43ca6670f11 100644 --- a/planner/core/errors.go +++ b/planner/core/errors.go @@ -60,6 +60,7 @@ const ( codeWindowNoInherentFrame = mysql.ErrWindowNoInherentFrame codeWindowNoRedefineOrderBy = mysql.ErrWindowNoRedefineOrderBy codeWindowDuplicateName = mysql.ErrWindowDuplicateName + codeErrTooBigPrecision = mysql.ErrTooBigPrecision ) // error definitions. @@ -106,6 +107,7 @@ var ( ErrWindowNoInherentFrame = terror.ClassOptimizer.New(codeWindowNoInherentFrame, mysql.MySQLErrName[mysql.ErrWindowNoInherentFrame]) ErrWindowNoRedefineOrderBy = terror.ClassOptimizer.New(codeWindowNoRedefineOrderBy, mysql.MySQLErrName[mysql.ErrWindowNoRedefineOrderBy]) ErrWindowDuplicateName = terror.ClassOptimizer.New(codeWindowDuplicateName, mysql.MySQLErrName[mysql.ErrWindowDuplicateName]) + errTooBigPrecision = terror.ClassExpression.New(mysql.ErrTooBigPrecision, mysql.MySQLErrName[mysql.ErrTooBigPrecision]) ) func init() { @@ -142,6 +144,7 @@ func init() { codeWindowNoInherentFrame: mysql.ErrWindowNoInherentFrame, codeWindowNoRedefineOrderBy: mysql.ErrWindowNoRedefineOrderBy, codeWindowDuplicateName: mysql.ErrWindowDuplicateName, + codeErrTooBigPrecision: mysql.ErrTooBigPrecision, } terror.ErrClassToMySQLCodes[terror.ClassOptimizer] = mysqlErrCodeMap } diff --git a/planner/core/expression_rewriter.go b/planner/core/expression_rewriter.go index 4f00fbb23c962..39f8508b34217 100644 --- a/planner/core/expression_rewriter.go +++ b/planner/core/expression_rewriter.go @@ -793,10 +793,18 @@ func (er *expressionRewriter) Leave(originInNode ast.Node) (retNode ast.Node, ok er.caseToExpression(v) case *ast.FuncCastExpr: arg := er.ctxStack[len(er.ctxStack)-1] + er.err = expression.CheckArgsNotMultiColumnRow(arg) if er.err != nil { return retNode, false } + + // check the decimal precision of "CAST(AS TIME)". + er.err = er.checkTimePrecision(v.Tp) + if er.err != nil { + return retNode, false + } + er.ctxStack[len(er.ctxStack)-1] = expression.BuildCastFunction(er.ctx, arg, v.Tp) case *ast.PatternLikeExpr: er.likeToScalarFunc(v) @@ -827,6 +835,13 @@ func (er *expressionRewriter) Leave(originInNode ast.Node) (retNode ast.Node, ok return originInNode, true } +func (er *expressionRewriter) checkTimePrecision(ft *types.FieldType) error { + if ft.EvalType() == types.ETDuration && ft.Decimal > types.MaxFsp { + return errTooBigPrecision.GenWithStackByArgs(ft.Decimal, "CAST", types.MaxFsp) + } + return nil +} + func (er *expressionRewriter) useCache() bool { return er.ctx.GetSessionVars().StmtCtx.UseCache } From 7fb68eda56599758abc2d4d51358a69c1d699449 Mon Sep 17 00:00:00 2001 From: Jian Zhang Date: Tue, 8 Jan 2019 15:43:12 +0800 Subject: [PATCH 3/4] rewrite test --- expression/integration_test.go | 25 +++++-------------------- 1 file changed, 5 insertions(+), 20 deletions(-) diff --git a/expression/integration_test.go b/expression/integration_test.go index 29f3c127d2f1e..77935ff6ab82c 100644 --- a/expression/integration_test.go +++ b/expression/integration_test.go @@ -3812,33 +3812,18 @@ func (s *testIntegrationSuite) TestCastAsTime(c *C) { ` `, )) - rs, err := tk.Exec(`select cast(col1 as time(31)) from t where col1 is null;`) - c.Assert(err, IsNil) - _, err = session.GetRows4Test(context.TODO(), tk.Se, rs) + err := tk.ExecToErr(`select cast(col1 as time(31)) from t where col1 is null;`) c.Assert(err.Error(), Equals, "[expression:1426]Too big precision 31 specified for column 'CAST'. Maximum is 6.") - c.Assert(rs.Close(), IsNil) - rs, err = tk.Exec(`select cast(col2 as time(31)) from t where col1 is null;`) - c.Assert(err, IsNil) - _, err = session.GetRows4Test(context.TODO(), tk.Se, rs) + err = tk.ExecToErr(`select cast(col2 as time(31)) from t where col1 is null;`) c.Assert(err.Error(), Equals, "[expression:1426]Too big precision 31 specified for column 'CAST'. Maximum is 6.") - c.Assert(rs.Close(), IsNil) - rs, err = tk.Exec(`select cast(col3 as time(31)) from t where col1 is null;`) - c.Assert(err, IsNil) - _, err = session.GetRows4Test(context.TODO(), tk.Se, rs) + err = tk.ExecToErr(`select cast(col3 as time(31)) from t where col1 is null;`) c.Assert(err.Error(), Equals, "[expression:1426]Too big precision 31 specified for column 'CAST'. Maximum is 6.") - c.Assert(rs.Close(), IsNil) - rs, err = tk.Exec(`select cast(col4 as time(31)) from t where col1 is null;`) - c.Assert(err, IsNil) - _, err = session.GetRows4Test(context.TODO(), tk.Se, rs) + err = tk.ExecToErr(`select cast(col4 as time(31)) from t where col1 is null;`) c.Assert(err.Error(), Equals, "[expression:1426]Too big precision 31 specified for column 'CAST'. Maximum is 6.") - c.Assert(rs.Close(), IsNil) - rs, err = tk.Exec(`select cast(col5 as time(31)) from t where col1 is null;`) - c.Assert(err, IsNil) - _, err = session.GetRows4Test(context.TODO(), tk.Se, rs) + err = tk.ExecToErr(`select cast(col5 as time(31)) from t where col1 is null;`) c.Assert(err.Error(), Equals, "[expression:1426]Too big precision 31 specified for column 'CAST'. Maximum is 6.") - c.Assert(rs.Close(), IsNil) } From 4a5175b9366b2ec00a4443980ba5e2ce4ab932a6 Mon Sep 17 00:00:00 2001 From: Jian Zhang Date: Wed, 9 Jan 2019 10:59:09 +0800 Subject: [PATCH 4/4] address comment --- planner/core/expression_rewriter.go | 1 - 1 file changed, 1 deletion(-) diff --git a/planner/core/expression_rewriter.go b/planner/core/expression_rewriter.go index 39f8508b34217..362693812cdab 100644 --- a/planner/core/expression_rewriter.go +++ b/planner/core/expression_rewriter.go @@ -793,7 +793,6 @@ func (er *expressionRewriter) Leave(originInNode ast.Node) (retNode ast.Node, ok er.caseToExpression(v) case *ast.FuncCastExpr: arg := er.ctxStack[len(er.ctxStack)-1] - er.err = expression.CheckArgsNotMultiColumnRow(arg) if er.err != nil { return retNode, false