Skip to content

Commit

Permalink
types: support sql_mode ALLOW_INVALID_DATES (#9027)
Browse files Browse the repository at this point in the history
  • Loading branch information
bobotu authored and ngaut committed Jan 14, 2019
1 parent 89532cd commit f20c849
Show file tree
Hide file tree
Showing 6 changed files with 66 additions and 18 deletions.
12 changes: 9 additions & 3 deletions executor/executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -1277,23 +1277,26 @@ func ResetContextOfStmt(ctx sessionctx.Context, s ast.StmtNode) (err error) {
sc.BadNullAsWarning = !vars.StrictSQLMode || stmt.IgnoreErr
sc.TruncateAsWarning = !vars.StrictSQLMode || stmt.IgnoreErr
sc.DividedByZeroAsWarning = !vars.StrictSQLMode || stmt.IgnoreErr
sc.IgnoreZeroInDate = !vars.StrictSQLMode || stmt.IgnoreErr
sc.AllowInvalidDate = vars.SQLMode.HasAllowInvalidDatesMode()
sc.IgnoreZeroInDate = !vars.StrictSQLMode || stmt.IgnoreErr || sc.AllowInvalidDate
sc.Priority = stmt.Priority
case *ast.DeleteStmt:
sc.InDeleteStmt = true
sc.DupKeyAsWarning = stmt.IgnoreErr
sc.BadNullAsWarning = !vars.StrictSQLMode || stmt.IgnoreErr
sc.TruncateAsWarning = !vars.StrictSQLMode || stmt.IgnoreErr
sc.DividedByZeroAsWarning = !vars.StrictSQLMode || stmt.IgnoreErr
sc.IgnoreZeroInDate = !vars.StrictSQLMode || stmt.IgnoreErr
sc.AllowInvalidDate = vars.SQLMode.HasAllowInvalidDatesMode()
sc.IgnoreZeroInDate = !vars.StrictSQLMode || stmt.IgnoreErr || sc.AllowInvalidDate
sc.Priority = stmt.Priority
case *ast.InsertStmt:
sc.InInsertStmt = true
sc.DupKeyAsWarning = stmt.IgnoreErr
sc.BadNullAsWarning = !vars.StrictSQLMode || stmt.IgnoreErr
sc.TruncateAsWarning = !vars.StrictSQLMode || stmt.IgnoreErr
sc.DividedByZeroAsWarning = !vars.StrictSQLMode || stmt.IgnoreErr
sc.IgnoreZeroInDate = !vars.StrictSQLMode || stmt.IgnoreErr
sc.AllowInvalidDate = vars.SQLMode.HasAllowInvalidDatesMode()
sc.IgnoreZeroInDate = !vars.StrictSQLMode || stmt.IgnoreErr || sc.AllowInvalidDate
sc.Priority = stmt.Priority
case *ast.CreateTableStmt, *ast.AlterTableStmt:
// Make sure the sql_mode is strict when checking column default value.
Expand All @@ -1314,6 +1317,7 @@ func ResetContextOfStmt(ctx sessionctx.Context, s ast.StmtNode) (err error) {
// Return warning for truncate error in selection.
sc.TruncateAsWarning = true
sc.IgnoreZeroInDate = true
sc.AllowInvalidDate = vars.SQLMode.HasAllowInvalidDatesMode()
if opts := stmt.SelectStmtOpts; opts != nil {
sc.Priority = opts.Priority
sc.NotFillCache = !opts.SQLCache
Expand All @@ -1322,13 +1326,15 @@ func ResetContextOfStmt(ctx sessionctx.Context, s ast.StmtNode) (err error) {
case *ast.ShowStmt:
sc.IgnoreTruncate = true
sc.IgnoreZeroInDate = true
sc.AllowInvalidDate = vars.SQLMode.HasAllowInvalidDatesMode()
if stmt.Tp == ast.ShowWarnings || stmt.Tp == ast.ShowErrors {
sc.InShowWarning = true
sc.SetWarnings(vars.StmtCtx.GetWarnings())
}
default:
sc.IgnoreTruncate = true
sc.IgnoreZeroInDate = true
sc.AllowInvalidDate = vars.SQLMode.HasAllowInvalidDatesMode()
}
vars.PreparedParams = vars.PreparedParams[:0]
if !vars.InRestrictedSQL {
Expand Down
37 changes: 37 additions & 0 deletions executor/insert_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
package executor_test

import (
"fmt"

. "github.com/pingcap/check"
"github.com/pingcap/parser/terror"
"github.com/pingcap/tidb/table"
Expand Down Expand Up @@ -233,3 +235,38 @@ func (s *testSuite3) TestInsertZeroYear(c *C) {
`2000`,
))
}

func (s *testSuite3) TestAllowInvalidDates(c *C) {
tk := testkit.NewTestKit(c, s.store)
tk.MustExec(`use test`)
tk.MustExec(`drop table if exists t1, t2, t3, t4;`)
tk.MustExec(`create table t1(d date);`)
tk.MustExec(`create table t2(d datetime);`)
tk.MustExec(`create table t3(d date);`)
tk.MustExec(`create table t4(d datetime);`)

runWithMode := func(mode string) {
inputs := []string{"0000-00-00", "2019-00-00", "2019-01-00", "2019-00-01", "2019-02-31"}
results := testkit.Rows(`0 0 0`, `2019 0 0`, `2019 1 0`, `2019 0 1`, `2019 2 31`)
oldMode := tk.MustQuery(`select @@sql_mode`).Rows()[0][0]
defer func() {
tk.MustExec(fmt.Sprintf(`set sql_mode='%s'`, oldMode))
}()

tk.MustExec(`truncate t1;truncate t2;truncate t3;truncate t4;`)
tk.MustExec(fmt.Sprintf(`set sql_mode='%s';`, mode))
for _, input := range inputs {
tk.MustExec(fmt.Sprintf(`insert into t1 values ('%s')`, input))
tk.MustExec(fmt.Sprintf(`insert into t2 values ('%s')`, input))
}
tk.MustQuery(`select year(d), month(d), day(d) from t1;`).Check(results)
tk.MustQuery(`select year(d), month(d), day(d) from t2;`).Check(results)
tk.MustExec(`insert t3 select d from t1;`)
tk.MustQuery(`select year(d), month(d), day(d) from t3;`).Check(results)
tk.MustExec(`insert t4 select d from t2;`)
tk.MustQuery(`select year(d), month(d), day(d) from t4;`).Check(results)
}

runWithMode("STRICT_TRANS_TABLES,ALLOW_INVALID_DATES")
runWithMode("ALLOW_INVALID_DATES")
}
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ require (
github.com/pingcap/gofail v0.0.0-20181217135706-6a951c1e42c3
github.com/pingcap/goleveldb v0.0.0-20171020122428-b9ff6c35079e
github.com/pingcap/kvproto v0.0.0-20190110035000-d4fe6b336379
github.com/pingcap/parser v0.0.0-20190114015132-c5c6ec2eb454
github.com/pingcap/parser v0.0.0-20190114060015-cf5695a447d2
github.com/pingcap/pd v2.1.0-rc.4+incompatible
github.com/pingcap/tidb-tools v2.1.3-0.20190104033906-883b07a04a73+incompatible
github.com/pingcap/tipb v0.0.0-20181012112600-11e33c750323
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,8 @@ github.com/pingcap/goleveldb v0.0.0-20171020122428-b9ff6c35079e h1:P73/4dPCL96rG
github.com/pingcap/goleveldb v0.0.0-20171020122428-b9ff6c35079e/go.mod h1:O17XtbryoCJhkKGbT62+L2OlrniwqiGLSqrmdHCMzZw=
github.com/pingcap/kvproto v0.0.0-20190110035000-d4fe6b336379 h1:l4KInBOtxjbgQLjCFHzX66vZgNzsH4a+RiuVZGrO0xk=
github.com/pingcap/kvproto v0.0.0-20190110035000-d4fe6b336379/go.mod h1:QMdbTAXCHzzygQzqcG9uVUgU2fKeSN1GmfMiykdSzzY=
github.com/pingcap/parser v0.0.0-20190114015132-c5c6ec2eb454 h1:8wqFaAY5HLvDH35UkzMhtuxb4Q0fk6/yeiOscfOmMpo=
github.com/pingcap/parser v0.0.0-20190114015132-c5c6ec2eb454/go.mod h1:1FNvfp9+J0wvc4kl8eGNh7Rqrxveg15jJoWo/a0uHwA=
github.com/pingcap/parser v0.0.0-20190114060015-cf5695a447d2 h1:5RdXoZ6io7x1qXu6GSmOn8uqhdBA2RJpl6XCMVLNAEM=
github.com/pingcap/parser v0.0.0-20190114060015-cf5695a447d2/go.mod h1:1FNvfp9+J0wvc4kl8eGNh7Rqrxveg15jJoWo/a0uHwA=
github.com/pingcap/pd v2.1.0-rc.4+incompatible h1:/buwGk04aHO5odk/+O8ZOXGs4qkUjYTJ2UpCJXna8NE=
github.com/pingcap/pd v2.1.0-rc.4+incompatible/go.mod h1:nD3+EoYes4+aNNODO99ES59V83MZSI+dFbhyr667a0E=
github.com/pingcap/tidb-tools v2.1.3-0.20190104033906-883b07a04a73+incompatible h1:Ba48wwPwPq5hd1kkQpgua49dqB5cthC2zXVo7fUUDec=
Expand Down
1 change: 1 addition & 0 deletions sessionctx/stmtctx/stmtctx.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ type StatementContext struct {
PadCharToFullLength bool
BatchCheck bool
InNullRejectCheck bool
AllowInvalidDate bool

// mu struct holds variables that change during execution.
mu struct {
Expand Down
28 changes: 16 additions & 12 deletions types/time.go
Original file line number Diff line number Diff line change
Expand Up @@ -474,18 +474,20 @@ func (t *Time) FromPackedUint(packed uint64) error {
// FIXME: See https://dev.mysql.com/doc/refman/5.7/en/sql-mode.html#sqlmode_no_zero_in_date
func (t *Time) check(sc *stmtctx.StatementContext) error {
allowZeroInDate := false
allowInvalidDate := false
// We should avoid passing sc as nil here as far as possible.
if sc != nil {
allowZeroInDate = sc.IgnoreZeroInDate
allowInvalidDate = sc.AllowInvalidDate
}
var err error
switch t.Type {
case mysql.TypeTimestamp:
err = checkTimestampType(sc, t.Time)
case mysql.TypeDatetime:
err = checkDatetimeType(t.Time, allowZeroInDate)
err = checkDatetimeType(t.Time, allowZeroInDate, allowInvalidDate)
case mysql.TypeDate:
err = checkDateType(t.Time, allowZeroInDate)
err = checkDateType(t.Time, allowZeroInDate, allowInvalidDate)
}
return errors.Trace(err)
}
Expand Down Expand Up @@ -1344,7 +1346,7 @@ func TimeFromDays(num int64) Time {
}
}

func checkDateType(t MysqlTime, allowZeroInDate bool) error {
func checkDateType(t MysqlTime, allowZeroInDate, allowInvalidDate bool) error {
year, month, day := t.Year(), t.Month(), t.Day()
if year == 0 && month == 0 && day == 0 {
return nil
Expand All @@ -1358,7 +1360,7 @@ func checkDateType(t MysqlTime, allowZeroInDate bool) error {
return errors.Trace(err)
}

if err := checkMonthDay(year, month, day); err != nil {
if err := checkMonthDay(year, month, day, allowInvalidDate); err != nil {
return errors.Trace(err)
}

Expand All @@ -1377,17 +1379,19 @@ func checkDateRange(t MysqlTime) error {
return nil
}

func checkMonthDay(year, month, day int) error {
func checkMonthDay(year, month, day int, allowInvalidDate bool) error {
if month < 0 || month > 12 {
return errors.Trace(ErrInvalidTimeFormat.GenWithStackByArgs(month))
}

maxDay := 31
if month > 0 {
maxDay = maxDaysInMonth[month-1]
}
if month == 2 && year%4 != 0 {
maxDay = 28
if !allowInvalidDate {
if month > 0 {
maxDay = maxDaysInMonth[month-1]
}
if month == 2 && year%4 != 0 {
maxDay = 28
}
}

if day < 0 || day > maxDay {
Expand Down Expand Up @@ -1427,8 +1431,8 @@ func checkTimestampType(sc *stmtctx.StatementContext, t MysqlTime) error {
return nil
}

func checkDatetimeType(t MysqlTime, allowZeroInDate bool) error {
if err := checkDateType(t, allowZeroInDate); err != nil {
func checkDatetimeType(t MysqlTime, allowZeroInDate, allowInvalidDate bool) error {
if err := checkDateType(t, allowZeroInDate, allowInvalidDate); err != nil {
return errors.Trace(err)
}

Expand Down

0 comments on commit f20c849

Please sign in to comment.