diff --git a/planner/core/integration_test.go b/planner/core/integration_test.go index 6395df84403a8..dba4b0d27df96 100644 --- a/planner/core/integration_test.go +++ b/planner/core/integration_test.go @@ -4570,10 +4570,48 @@ func (s *testIntegrationSuite) TestIssue27242(c *C) { tk.MustExec("use test") tk.MustExec("drop table if exists UK_MU16407") tk.MustExec("CREATE TABLE UK_MU16407 (COL3 timestamp NULL DEFAULT NULL, UNIQUE KEY U3(COL3));") + defer tk.MustExec("DROP TABLE UK_MU16407") tk.MustExec(`insert into UK_MU16407 values("1985-08-31 18:03:27");`) - err := tk.ExecToErr(`SELECT COL3 FROM UK_MU16407 WHERE COL3>_utf8mb4'2039-1-19 3:14:40';`) - c.Assert(err, NotNil) - c.Assert(err.Error(), Matches, ".*Incorrect timestamp value.*") + tk.MustExec(`SELECT COL3 FROM UK_MU16407 WHERE COL3>_utf8mb4'2039-1-19 3:14:40';`) +} + +func verifyTimestampOutOfRange(tk *testkit.TestKit) { + tk.MustQuery(`select * from t28424 where t != "2038-1-19 3:14:08"`).Sort().Check(testkit.Rows("1970-01-01 00:00:01]\n[2038-01-19 03:14:07")) + tk.MustQuery(`select * from t28424 where t < "2038-1-19 3:14:08"`).Sort().Check(testkit.Rows("1970-01-01 00:00:01]\n[2038-01-19 03:14:07")) + tk.MustQuery(`select * from t28424 where t <= "2038-1-19 3:14:08"`).Sort().Check(testkit.Rows("1970-01-01 00:00:01]\n[2038-01-19 03:14:07")) + tk.MustQuery(`select * from t28424 where t >= "2038-1-19 3:14:08"`).Check(testkit.Rows()) + tk.MustQuery(`select * from t28424 where t > "2038-1-19 3:14:08"`).Check(testkit.Rows()) + tk.MustQuery(`select * from t28424 where t != "1970-1-1 0:0:0"`).Sort().Check(testkit.Rows("1970-01-01 00:00:01]\n[2038-01-19 03:14:07")) + tk.MustQuery(`select * from t28424 where t < "1970-1-1 0:0:0"`).Check(testkit.Rows()) + tk.MustQuery(`select * from t28424 where t <= "1970-1-1 0:0:0"`).Check(testkit.Rows()) + tk.MustQuery(`select * from t28424 where t >= "1970-1-1 0:0:0"`).Sort().Check(testkit.Rows("1970-01-01 00:00:01]\n[2038-01-19 03:14:07")) + tk.MustQuery(`select * from t28424 where t > "1970-1-1 0:0:0"`).Sort().Check(testkit.Rows("1970-01-01 00:00:01]\n[2038-01-19 03:14:07")) +} + +func (s *testIntegrationSuite) TestIssue28424(c *C) { + tk := testkit.NewTestKit(c, s.store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t28424, dt28242") + + tk.MustExec(`set time_zone='+00:00'`) + tk.MustExec(`drop table if exists t28424,dt28424`) + tk.MustExec(`create table t28424 (t timestamp)`) + defer tk.MustExec("DROP TABLE t28424") + tk.MustExec(`insert into t28424 values ("2038-01-19 03:14:07"), ("1970-01-01 00:00:01")`) + + verifyTimestampOutOfRange(tk) + tk.MustExec(`alter table t28424 add unique index (t)`) + verifyTimestampOutOfRange(tk) + tk.MustExec(`create table dt28424 (dt datetime)`) + defer tk.MustExec("DROP TABLE dt28424") + tk.MustExec(`insert into dt28424 values ("2038-01-19 03:14:07"), ("1970-01-01 00:00:01")`) + tk.MustExec(`insert into dt28424 values ("1969-12-31 23:59:59"), ("1970-01-01 00:00:00"), ("2038-03-19 03:14:08")`) + tk.MustQuery(`select * from t28424 right join dt28424 on t28424.t = dt28424.dt`).Sort().Check(testkit.Rows( + "1970-01-01 00:00:01 1970-01-01 00:00:01]\n" + + "[2038-01-19 03:14:07 2038-01-19 03:14:07]\n" + + "[ 1969-12-31 23:59:59]\n" + + "[ 1970-01-01 00:00:00]\n" + + "[ 2038-03-19 03:14:08")) } func (s *testIntegrationSerialSuite) TestTemporaryTableForCte(c *C) { diff --git a/table/column.go b/table/column.go index 827087ad1acf2..dc8d55ceeacb8 100644 --- a/table/column.go +++ b/table/column.go @@ -197,6 +197,12 @@ func handleWrongCharsetValue(ctx sessionctx.Context, col *model.ColumnInfo, cast return truncateVal, err } +// handleZeroDatetime handles Timestamp/Datetime/Date zero date and invalid dates. +// Currently only called from CastValue. +// returns: +// value (possibly adjusted) +// boolean; true if break error/warning handling in CastValue and return what was returned from this +// error func handleZeroDatetime(ctx sessionctx.Context, col *model.ColumnInfo, casted types.Datum, str string, tmIsInvalid bool) (types.Datum, bool, error) { sc := ctx.GetSessionVars().StmtCtx tm := casted.GetMysqlTime() @@ -247,6 +253,14 @@ func handleZeroDatetime(ctx sessionctx.Context, col *model.ColumnInfo, casted ty sc.AppendWarning(innerErr) } return types.NewDatum(zeroV), true, nil + } else if tmIsInvalid && col.Tp == mysql.TypeTimestamp { + // Prevent from being stored! Invalid timestamp! + if mode.HasStrictMode() { + return types.NewDatum(zeroV), true, types.ErrWrongValue.GenWithStackByArgs(zeroT, str) + } + // no strict mode, truncate to 0000-00-00 00:00:00 + sc.AppendWarning(types.ErrWrongValue.GenWithStackByArgs(zeroT, str)) + return types.NewDatum(zeroV), true, nil } else if tm.IsZero() || tm.InvalidZero() { if tm.IsZero() { // Don't care NoZeroDate mode if time val is invalid. diff --git a/types/datum.go b/types/datum.go index 9dfae69f68307..e32d2bd46cf27 100644 --- a/types/datum.go +++ b/types/datum.go @@ -1131,7 +1131,8 @@ func (d *Datum) convertToMysqlTimestamp(sc *stmtctx.StatementContext, target *Fi case KindMysqlTime: t, err = d.GetMysqlTime().Convert(sc, target.Tp) if err != nil { - ret.SetMysqlTime(ZeroTimestamp) + // t might be an invalid Timestamp, but should still be comparable, since same representation (KindMysqlTime) + ret.SetMysqlTime(t) return ret, errors.Trace(ErrWrongValue.GenWithStackByArgs(TimestampStr, t.String())) } t, err = t.RoundFrac(sc, fsp) diff --git a/util/ranger/ranger.go b/util/ranger/ranger.go index 7947696597e71..bb32b4111b7c8 100644 --- a/util/ranger/ranger.go +++ b/util/ranger/ranger.go @@ -102,6 +102,9 @@ func convertPoint(sc *stmtctx.StatementContext, point *point, tp *types.FieldTyp // Ignore the types.ErrOverflow when we convert TypeNewDecimal values. // A trimmed valid boundary point value would be returned then. Accordingly, the `excl` of the point // would be adjusted. Impossible ranges would be skipped by the `validInterval` call later. + } else if point.value.Kind() == types.KindMysqlTime && tp.Tp == mysql.TypeTimestamp && terror.ErrorEqual(err, types.ErrWrongValue) { + // See issue #28424: query failed after add index + // Ignore conversion from Date[Time] to Timestamp since it must be either out of range or impossible date, which will not match a point select } else if tp.Tp == mysql.TypeEnum && terror.ErrorEqual(err, types.ErrTruncated) { // Ignore the types.ErrorTruncated when we convert TypeEnum values. // We should cover Enum upper overflow, and convert to the biggest value.