diff --git a/executor/executor.go b/executor/executor.go index 91a58b8ea6ab7..aa7e2235fbd50 100644 --- a/executor/executor.go +++ b/executor/executor.go @@ -1250,7 +1250,8 @@ 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 @@ -1258,7 +1259,8 @@ 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.InsertStmt: sc.InInsertStmt = true @@ -1266,7 +1268,8 @@ 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.CreateTableStmt, *ast.AlterTableStmt: // Make sure the sql_mode is strict when checking column default value. @@ -1287,6 +1290,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 @@ -1295,6 +1299,7 @@ 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()) @@ -1302,6 +1307,7 @@ func ResetContextOfStmt(ctx sessionctx.Context, s ast.StmtNode) (err error) { default: sc.IgnoreTruncate = true sc.IgnoreZeroInDate = true + sc.AllowInvalidDate = vars.SQLMode.HasAllowInvalidDatesMode() } vars.PreparedParams = vars.PreparedParams[:0] if !vars.InRestrictedSQL { diff --git a/executor/insert_test.go b/executor/insert_test.go index 6a43c0ba300d9..b8f92f0e4a55a 100644 --- a/executor/insert_test.go +++ b/executor/insert_test.go @@ -14,6 +14,8 @@ package executor_test import ( + "fmt" + . "github.com/pingcap/check" "github.com/pingcap/parser/terror" "github.com/pingcap/tidb/table" @@ -174,3 +176,38 @@ func (s *testSuite) TestInsertZeroYear(c *C) { `2000`, )) } + +func (s *testSuite) 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") +} diff --git a/go.mod b/go.mod index e61ce19bda3a5..3ee36f73a54bd 100644 --- a/go.mod +++ b/go.mod @@ -48,14 +48,14 @@ require ( github.com/pingcap/gofail v0.0.0-20181217135706-6a951c1e42c3 github.com/pingcap/goleveldb v0.0.0-20171020084629-8d44bfdf1030 github.com/pingcap/kvproto v0.0.0-20181109035735-8e3f33ac4929 - github.com/pingcap/parser v0.0.0-20190116142258-00e692951ce1 + github.com/pingcap/parser v0.0.0-20190118033454-a52e5bde3bd2 github.com/pingcap/pd v2.1.0-rc.4+incompatible github.com/pingcap/tidb-tools v2.1.1-0.20181218072513-b2235d442b06+incompatible github.com/pingcap/tipb v0.0.0-20180910045846-371b48b15d93 github.com/pkg/errors v0.8.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_golang v0.8.0 - github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5 // indirect + github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5 github.com/prometheus/common v0.0.0-20180426121432-d811d2e9bf89 // indirect github.com/prometheus/procfs v0.0.0-20180408092902-8b1c2da0d56d // indirect github.com/sirupsen/logrus v0.0.0-20170323161349-3bcb09397d6d diff --git a/go.sum b/go.sum index 20c5ed4e79a1c..5bce265112f77 100644 --- a/go.sum +++ b/go.sum @@ -92,10 +92,8 @@ github.com/pingcap/goleveldb v0.0.0-20171020084629-8d44bfdf1030 h1:XJLuW0lsP7vAt github.com/pingcap/goleveldb v0.0.0-20171020084629-8d44bfdf1030/go.mod h1:O17XtbryoCJhkKGbT62+L2OlrniwqiGLSqrmdHCMzZw= github.com/pingcap/kvproto v0.0.0-20181109035735-8e3f33ac4929 h1:NAq95+VGsS2G7SjzZ5LP9iUlCMNAs13QUzbNY3G90v8= github.com/pingcap/kvproto v0.0.0-20181109035735-8e3f33ac4929/go.mod h1:0gwbe1F2iBIjuQ9AH0DbQhL+Dpr5GofU8fgYyXk+ykk= -github.com/pingcap/parser v0.0.0-20190107034620-db064135d7b0 h1:t/xlCk9karOvR8xrq7da4FAGLo3IHhbDeSTcA6taiUc= -github.com/pingcap/parser v0.0.0-20190107034620-db064135d7b0/go.mod h1:1FNvfp9+J0wvc4kl8eGNh7Rqrxveg15jJoWo/a0uHwA= -github.com/pingcap/parser v0.0.0-20190116142258-00e692951ce1 h1:fis1l2hPX1piYENHn5B9sMmpbUURp8l2IJJpKHP1aUk= -github.com/pingcap/parser v0.0.0-20190116142258-00e692951ce1/go.mod h1:1FNvfp9+J0wvc4kl8eGNh7Rqrxveg15jJoWo/a0uHwA= +github.com/pingcap/parser v0.0.0-20190118033454-a52e5bde3bd2 h1:7YGx4hF6M0nlFJVZrLF3EbMRI+XOizL+9aB8Txe745U= +github.com/pingcap/parser v0.0.0-20190118033454-a52e5bde3bd2/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.1-0.20181218072513-b2235d442b06+incompatible h1:Bsd+NHosPVowEGB3BCx+2d8wUQGDTXSSC5ljeNS6cXo= @@ -120,8 +118,6 @@ github.com/spaolacci/murmur3 v0.0.0-20150829172844-0d12bf811670 h1:hKP4ACPoBBCnB github.com/spaolacci/murmur3 v0.0.0-20150829172844-0d12bf811670/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/tiancaiamao/parser v0.0.0-20190116115114-e2ff6aa2040c h1:Jca3Kh29wy4T8etEUU+/SWbYoQHFqDJIzS0kIjuoCXU= -github.com/tiancaiamao/parser v0.0.0-20190116115114-e2ff6aa2040c/go.mod h1:vqvanuOAAZ9O2rVI51fUrA9P3nV7HoILjLby0/OKOqA= github.com/twinj/uuid v0.0.0-20150629100731-70cac2bcd273 h1:YqFyfcgqxQqjpRr0SEG0Z555J/3kPqDL/xmRyeAaX/0= github.com/twinj/uuid v0.0.0-20150629100731-70cac2bcd273/go.mod h1:mMgcE1RHFUFqe5AfiwlINXisXfDGro23fWdPUfOMjRY= github.com/uber/jaeger-client-go v2.8.0+incompatible h1:7DGH8Hqk6PirD+GE+bvCf0cLnspLuae7N1NcwMeQcyg= diff --git a/sessionctx/stmtctx/stmtctx.go b/sessionctx/stmtctx/stmtctx.go index 6819b2eaeb8d2..825dffc8d9df1 100644 --- a/sessionctx/stmtctx/stmtctx.go +++ b/sessionctx/stmtctx/stmtctx.go @@ -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 { diff --git a/types/time.go b/types/time.go index 73a20ea3040f0..2ea24cf5057cb 100644 --- a/types/time.go +++ b/types/time.go @@ -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) } @@ -1341,7 +1343,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 @@ -1355,7 +1357,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) } @@ -1374,17 +1376,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 { @@ -1424,8 +1428,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) }