From deadb33d1a7bed1badae127abc1ad55839ea5385 Mon Sep 17 00:00:00 2001 From: crazycs520 Date: Thu, 17 Jan 2019 21:51:37 +0800 Subject: [PATCH 01/18] update parser go.mod --- go.mod | 2 ++ go.sum | 3 +++ 2 files changed, 5 insertions(+) diff --git a/go.mod b/go.mod index 6fed4379a49ca..d45675e151d51 100644 --- a/go.mod +++ b/go.mod @@ -88,3 +88,5 @@ require ( sourcegraph.com/sourcegraph/appdash v0.0.0-20180531100431-4c381bd170b4 sourcegraph.com/sourcegraph/appdash-data v0.0.0-20151005221446-73f23eafcf67 ) + +replace github.com/pingcap/parser => github.com/crazycs520/parser v0.0.0-20190117163513-d4e90e647da0 diff --git a/go.sum b/go.sum index ddbe4c0f41a25..0d08db1db54b8 100644 --- a/go.sum +++ b/go.sum @@ -22,6 +22,9 @@ github.com/coreos/go-systemd v0.0.0-20181031085051-9002847aa142 h1:3jFq2xL4ZajGK github.com/coreos/go-systemd v0.0.0-20181031085051-9002847aa142/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f h1:lBNOc5arjvs8E5mO2tbpBpLoyyu8B6e44T7hJy6potg= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/crazycs520/parser v0.0.0-20190117131849-0e3a62b35fbf/go.mod h1:xLjI+gnWYexq011WPMEvCNS8rFM9qe1vdojIEzSKPuc= +github.com/crazycs520/parser v0.0.0-20190117163513-d4e90e647da0 h1:VbwlcYWo7zZm4Amau+5H/yAhJMuQMv1A+/8gMNorKEE= +github.com/crazycs520/parser v0.0.0-20190117163513-d4e90e647da0/go.mod h1:xLjI+gnWYexq011WPMEvCNS8rFM9qe1vdojIEzSKPuc= github.com/cznic/mathutil v0.0.0-20181021201202-eba54fb065b7 h1:y+DH9ARrWiiNBV+6waYP2IPcsRbxdU1qsnycPfShF4c= github.com/cznic/mathutil v0.0.0-20181021201202-eba54fb065b7/go.mod h1:e6NPNENfs9mPDVNRekM7lKScauxd5kXTr1Mfyig6TDM= github.com/cznic/sortutil v0.0.0-20150617083342-4c7342852e65 h1:hxuZop6tSoOi0sxFzoGGYdRqNrPubyaIf9KoBG9tPiE= From d0382517987e6674e8809b755a1cce4b8f305e76 Mon Sep 17 00:00:00 2001 From: crazycs Date: Fri, 18 Jan 2019 02:44:04 +0800 Subject: [PATCH 02/18] add version field in ColumnInfo --- ddl/column.go | 3 ++- ddl/ddl_api.go | 39 ++++++++++++++++++++++++++++++++++----- ddl/index.go | 8 ++++++++ executor/show.go | 12 ++++++++++++ table/column.go | 9 +++++++++ 5 files changed, 65 insertions(+), 6 deletions(-) diff --git a/ddl/column.go b/ddl/column.go index 7854e6b0df739..f4041731f7ab0 100644 --- a/ddl/column.go +++ b/ddl/column.go @@ -572,7 +572,8 @@ func generateOriginDefaultValue(col *model.ColumnInfo) (interface{}, error) { if odValue == strings.ToUpper(ast.CurrentTimestamp) && (col.Tp == mysql.TypeTimestamp || col.Tp == mysql.TypeDatetime) { - odValue = time.Now().Format(types.TimeFormat) + odValue = time.Now().UTC().Format(types.TimeFormat) + col.Version = model.ColumnInfoVersion1 } return odValue, nil } diff --git a/ddl/ddl_api.go b/ddl/ddl_api.go index 3f24d81ed74b1..191484f57b37a 100644 --- a/ddl/ddl_api.go +++ b/ddl/ddl_api.go @@ -22,6 +22,7 @@ import ( "fmt" "strings" "sync/atomic" + "time" "github.com/cznic/mathutil" "github.com/pingcap/errors" @@ -255,6 +256,7 @@ func buildColumnAndConstraint(ctx sessionctx.Context, offset int, // In NO_ZERO_DATE SQL mode, TIMESTAMP/DATE/DATETIME type can't have zero date like '0000-00-00' or '0000-00-00 00:00:00'. func checkColumnDefaultValue(ctx sessionctx.Context, col *table.Column, value interface{}) (bool, interface{}, error) { hasDefaultValue := true + fmt.Printf("\n%v, value: %v\n\n=======\n", 1, value) if value != nil && (col.Tp == mysql.TypeJSON || col.Tp == mysql.TypeTinyBlob || col.Tp == mysql.TypeMediumBlob || col.Tp == mysql.TypeLongBlob || col.Tp == mysql.TypeBlob) { @@ -275,20 +277,41 @@ func checkColumnDefaultValue(ctx sessionctx.Context, col *table.Column, value in // In strict SQL mode or default value is not an empty string. return hasDefaultValue, value, errBlobCantHaveDefault.GenWithStackByArgs(col.Name.O) } + fmt.Printf("\n%v, ok: %v\n\n=======\n", 2, value) if value != nil && ctx.GetSessionVars().SQLMode.HasNoZeroDateMode() && ctx.GetSessionVars().SQLMode.HasStrictMode() && types.IsTypeTime(col.Tp) { if vv, ok := value.(string); ok { - t, err := types.ParseTime(nil, vv, col.Tp, 6) + timeValue, err := expression.GetTimeValue(ctx, vv, col.Tp, col.Decimal) if err != nil { - // Ignores ParseTime error, because ParseTime error has been dealt in getDefaultValue - // Some builtin function like CURRENT_TIMESTAMP() will cause ParseTime error. - return hasDefaultValue, value, nil + return hasDefaultValue, value, errors.Trace(err) } - if t.Time == types.ZeroTime { + if timeValue.GetMysqlTime().Time == types.ZeroTime { return hasDefaultValue, value, types.ErrInvalidDefault.GenWithStackByArgs(col.Name.O) } } } + fmt.Printf("\n%v\n=======\n", 3) + fmt.Printf("\n%v, value: %v\n=======\n", col.FieldType, value) + if value != nil && col.Tp == mysql.TypeTimestamp { + if vv, ok := value.(string); ok { + upperX := strings.ToUpper(vv) + if upperX != strings.ToUpper(ast.CurrentTimestamp) && upperX != types.ZeroDatetimeStr { + t, err := types.ParseTime(ctx.GetSessionVars().StmtCtx, vv, col.Tp, col.Decimal) + if err != nil { + return hasDefaultValue, value, errors.Trace(err) + } + if t.Time != types.ZeroTime { + err = t.ConvertTimeZone(ctx.GetSessionVars().Location(), time.UTC) + if err != nil { + return hasDefaultValue, value, errors.Trace(err) + } + fmt.Printf("convert: old: %v, new: %v\n\n\n", value, t.String()) + value = t.String() + col.Version = model.ColumnInfoVersion1 + } + } + } + } return hasDefaultValue, value, nil } @@ -352,12 +375,14 @@ func columnDefToCol(ctx sessionctx.Context, offset int, colDef *ast.ColumnDef, o col.Flag |= mysql.UniqueKeyFlag case ast.ColumnOptionDefaultValue: value, err := getDefaultValue(ctx, v, colDef.Tp.Tp, colDef.Tp.Decimal) + fmt.Printf("\ngetDefaultValue: value: %s, session.timezone: %v\n\n\n", value, ctx.GetSessionVars().TimeZone) if err != nil { return nil, nil, ErrColumnBadNull.GenWithStack("invalid default value - %s", err) } if hasDefaultValue, value, err = checkColumnDefaultValue(ctx, col, value); err != nil { return nil, nil, errors.Trace(err) } + fmt.Printf("\ncheckColumnDefaultValue: value: %s, session.timezone: %v, col.tp: %v\n\n\n", value, ctx.GetSessionVars().TimeZone, col.Tp) if err = col.SetDefaultValue(value); err != nil { return nil, nil, errors.Trace(err) } @@ -531,6 +556,7 @@ func checkDefaultValue(ctx sessionctx.Context, c *table.Column, hasDefaultValue } if c.GetDefaultValue() != nil { + fmt.Printf("check default value: %v\n\n", c.GetDefaultValue()) if _, err := table.GetColDefaultValue(ctx, c.ToInfo()); err != nil { return types.ErrInvalidDefault.GenWithStackByArgs(c.Name) } @@ -1891,6 +1917,9 @@ func setDefaultValue(ctx sessionctx.Context, col *table.Column, option *ast.Colu if err != nil { return ErrColumnBadNull.GenWithStack("invalid default value - %s", err) } + if _, value, err = checkColumnDefaultValue(ctx, col, value); err != nil { + return errors.Trace(err) + } err = col.SetDefaultValue(value) if err != nil { return errors.Trace(err) diff --git a/ddl/index.go b/ddl/index.go index 14eec5c8a0448..9613f12f547df 100644 --- a/ddl/index.go +++ b/ddl/index.go @@ -15,6 +15,7 @@ package ddl import ( "context" + "fmt" "math" "sync/atomic" "time" @@ -585,6 +586,13 @@ func (w *addIndexWorker) getIndexRecord(handle int64, recordKey []byte, rawRecor } } idxVal[j] = idxColumnVal + ss := "" + for _, vv := range idxVal { + str, _ := vv.ToString() + ss += str + ss += " " + } + fmt.Printf("\n index value: %v, system_zone: %v\n\n\n>>>>>>>>>>\n", ss, sysZone) } // If there are generated column, rowDecoder will use column value that not in idxInfo.Columns to calculate // the generated value, so we need to clear up the reusing map. diff --git a/executor/show.go b/executor/show.go index 269c9939b5362..9601517cd43c7 100644 --- a/executor/show.go +++ b/executor/show.go @@ -628,6 +628,18 @@ func (e *ShowExec) fetchShowCreateTable() error { buf.WriteString(" DEFAULT CURRENT_TIMESTAMP") default: defaultValStr := fmt.Sprintf("%v", defaultValue) + if col.Tp == mysql.TypeTimestamp && col.Version == model.ColumnInfoVersion1 { + t, err := types.ParseTime(e.ctx.GetSessionVars().StmtCtx, defaultValStr, col.Tp, col.Decimal) + if err != nil { + return errors.Trace(err) + } + err = t.ConvertTimeZone(time.UTC, e.ctx.GetSessionVars().TimeZone) + if err != nil { + return errors.Trace(err) + } + defaultValStr = t.String() + } + if col.Tp == mysql.TypeBit { defaultValBinaryLiteral := types.BinaryLiteral(defaultValStr) fmt.Fprintf(&buf, " DEFAULT %s", defaultValBinaryLiteral.ToBitLiteralString(true)) diff --git a/table/column.go b/table/column.go index 2c578417f3c34..f6aa67c77e000 100644 --- a/table/column.go +++ b/table/column.go @@ -19,6 +19,7 @@ package table import ( "strings" + "time" "unicode/utf8" "github.com/pingcap/errors" @@ -356,6 +357,14 @@ func getColDefaultValue(ctx sessionctx.Context, col *model.ColumnInfo, defaultVa return types.Datum{}, errGetDefaultFailed.GenWithStack("Field '%s' get default value fail - %s", col.Name, errors.Trace(err)) } + if col.Tp == mysql.TypeTimestamp && col.Version == model.ColumnInfoVersion1 { + t := value.GetMysqlTime() + err = t.ConvertTimeZone(time.UTC, ctx.GetSessionVars().Location()) + if err != nil { + return value, errors.Trace(err) + } + value.SetMysqlTime(t) + } return value, nil } value, err := CastValue(ctx, types.NewDatum(defaultVal), col) From c8a22bba7e2b3d58ae6fa225d55ad6d81bf9687e Mon Sep 17 00:00:00 2001 From: crazycs520 Date: Fri, 18 Jan 2019 12:17:40 +0800 Subject: [PATCH 03/18] add test --- ddl/ddl_api.go | 18 ++++------- ddl/index.go | 2 -- executor/executor_test.go | 63 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 68 insertions(+), 15 deletions(-) diff --git a/ddl/ddl_api.go b/ddl/ddl_api.go index 191484f57b37a..7cf6e45ab6898 100644 --- a/ddl/ddl_api.go +++ b/ddl/ddl_api.go @@ -256,7 +256,6 @@ func buildColumnAndConstraint(ctx sessionctx.Context, offset int, // In NO_ZERO_DATE SQL mode, TIMESTAMP/DATE/DATETIME type can't have zero date like '0000-00-00' or '0000-00-00 00:00:00'. func checkColumnDefaultValue(ctx sessionctx.Context, col *table.Column, value interface{}) (bool, interface{}, error) { hasDefaultValue := true - fmt.Printf("\n%v, value: %v\n\n=======\n", 1, value) if value != nil && (col.Tp == mysql.TypeJSON || col.Tp == mysql.TypeTinyBlob || col.Tp == mysql.TypeMediumBlob || col.Tp == mysql.TypeLongBlob || col.Tp == mysql.TypeBlob) { @@ -277,7 +276,6 @@ func checkColumnDefaultValue(ctx sessionctx.Context, col *table.Column, value in // In strict SQL mode or default value is not an empty string. return hasDefaultValue, value, errBlobCantHaveDefault.GenWithStackByArgs(col.Name.O) } - fmt.Printf("\n%v, ok: %v\n\n=======\n", 2, value) if value != nil && ctx.GetSessionVars().SQLMode.HasNoZeroDateMode() && ctx.GetSessionVars().SQLMode.HasStrictMode() && types.IsTypeTime(col.Tp) { if vv, ok := value.(string); ok { @@ -290,8 +288,6 @@ func checkColumnDefaultValue(ctx sessionctx.Context, col *table.Column, value in } } } - fmt.Printf("\n%v\n=======\n", 3) - fmt.Printf("\n%v, value: %v\n=======\n", col.FieldType, value) if value != nil && col.Tp == mysql.TypeTimestamp { if vv, ok := value.(string); ok { upperX := strings.ToUpper(vv) @@ -300,15 +296,12 @@ func checkColumnDefaultValue(ctx sessionctx.Context, col *table.Column, value in if err != nil { return hasDefaultValue, value, errors.Trace(err) } - if t.Time != types.ZeroTime { - err = t.ConvertTimeZone(ctx.GetSessionVars().Location(), time.UTC) - if err != nil { - return hasDefaultValue, value, errors.Trace(err) - } - fmt.Printf("convert: old: %v, new: %v\n\n\n", value, t.String()) - value = t.String() - col.Version = model.ColumnInfoVersion1 + err = t.ConvertTimeZone(ctx.GetSessionVars().Location(), time.UTC) + if err != nil { + return hasDefaultValue, value, errors.Trace(err) } + value = t.String() + col.Version = model.ColumnInfoVersion1 } } } @@ -382,7 +375,6 @@ func columnDefToCol(ctx sessionctx.Context, offset int, colDef *ast.ColumnDef, o if hasDefaultValue, value, err = checkColumnDefaultValue(ctx, col, value); err != nil { return nil, nil, errors.Trace(err) } - fmt.Printf("\ncheckColumnDefaultValue: value: %s, session.timezone: %v, col.tp: %v\n\n\n", value, ctx.GetSessionVars().TimeZone, col.Tp) if err = col.SetDefaultValue(value); err != nil { return nil, nil, errors.Trace(err) } diff --git a/ddl/index.go b/ddl/index.go index 9613f12f547df..25f83df2657ad 100644 --- a/ddl/index.go +++ b/ddl/index.go @@ -15,7 +15,6 @@ package ddl import ( "context" - "fmt" "math" "sync/atomic" "time" @@ -592,7 +591,6 @@ func (w *addIndexWorker) getIndexRecord(handle int64, recordKey []byte, rawRecor ss += str ss += " " } - fmt.Printf("\n index value: %v, system_zone: %v\n\n\n>>>>>>>>>>\n", ss, sysZone) } // If there are generated column, rowDecoder will use column value that not in idxInfo.Columns to calculate // the generated value, so we need to clear up the reusing map. diff --git a/executor/executor_test.go b/executor/executor_test.go index 7cff9f597ac50..7bcbbb6dd101e 100644 --- a/executor/executor_test.go +++ b/executor/executor_test.go @@ -37,6 +37,7 @@ import ( "github.com/pingcap/tidb/config" "github.com/pingcap/tidb/domain" "github.com/pingcap/tidb/executor" + "github.com/pingcap/tidb/expression" "github.com/pingcap/tidb/kv" "github.com/pingcap/tidb/meta/autoid" "github.com/pingcap/tidb/planner" @@ -2192,6 +2193,68 @@ func (s *testSuite) TestTimestampTimeZone(c *C) { r.Check(testkit.Rows("2014-03-31 08:57:10")) } +func (s *testSuite) TestTimestampDefaultValueTimeZone(c *C) { + tk := testkit.NewTestKit(c, s.store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("set time_zone = '+08:00'") + tk.MustExec(`create table t (a int, b timestamp default "2019-01-17 14:46:14")`) + tk.MustExec("insert into t set a=1") + r := tk.MustQuery(`show create table t`) + r.Check(testkit.Rows("t CREATE TABLE `t` (\n" + " `a` int(11) DEFAULT NULL,\n" + " `b` timestamp DEFAULT '2019-01-17 14:46:14'\n" + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin")) + tk.MustExec("set time_zone = '+00:00'") + tk.MustExec("insert into t set a=2") + r = tk.MustQuery(`show create table t`) + r.Check(testkit.Rows("t CREATE TABLE `t` (\n" + " `a` int(11) DEFAULT NULL,\n" + " `b` timestamp DEFAULT '2019-01-17 06:46:14'\n" + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin")) + r = tk.MustQuery(`select a,b from t order by a`) + r.Check(testkit.Rows("1 2019-01-17 06:46:14", "2 2019-01-17 06:46:14")) + tk.MustExec("set time_zone = '+08:00'") + r = tk.MustQuery(`select a,b from t order by a`) + r.Check(testkit.Rows("1 2019-01-17 14:46:14", "2 2019-01-17 14:46:14")) + tk.MustExec("set time_zone = '-08:00'") + r = tk.MustQuery(`show create table t`) + r.Check(testkit.Rows("t CREATE TABLE `t` (\n" + " `a` int(11) DEFAULT NULL,\n" + " `b` timestamp DEFAULT '2019-01-16 22:46:14'\n" + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin")) + + // test zero default value in multiple time zone. + defer tk.MustExec(fmt.Sprintf("set @@sql_mode='%s'", tk.MustQuery("select @@sql_mode").Rows()[0][0])) + tk.MustExec("set @@sql_mode='STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION';") + tk.MustExec("drop table if exists t") + tk.MustExec("set time_zone = '+08:00'") + tk.MustExec(`create table t (a int, b timestamp default "0000-00-00 00")`) + tk.MustExec("insert into t set a=1") + r = tk.MustQuery(`show create table t`) + r.Check(testkit.Rows("t CREATE TABLE `t` (\n" + " `a` int(11) DEFAULT NULL,\n" + " `b` timestamp DEFAULT '0000-00-00 00:00:00'\n" + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin")) + tk.MustExec("set time_zone = '+00:00'") + tk.MustExec("insert into t set a=2") + r = tk.MustQuery(`show create table t`) + r.Check(testkit.Rows("t CREATE TABLE `t` (\n" + " `a` int(11) DEFAULT NULL,\n" + " `b` timestamp DEFAULT '0000-00-00 00:00:00'\n" + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin")) + tk.MustExec("set time_zone = '-08:00'") + tk.MustExec("insert into t set a=3") + r = tk.MustQuery(`show create table t`) + r.Check(testkit.Rows("t CREATE TABLE `t` (\n" + " `a` int(11) DEFAULT NULL,\n" + " `b` timestamp DEFAULT '0000-00-00 00:00:00'\n" + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin")) + r = tk.MustQuery(`select a,b from t order by a`) + r.Check(testkit.Rows("1 0000-00-00 00:00:00", "2 0000-00-00 00:00:00", "3 0000-00-00 00:00:00")) + + // test add timestamp column default current_timestamp. + tk.MustExec(`drop table if exists t`) + tk.MustExec(`set time_zone = 'Asia/Shanghai'`) + tk.MustExec(`create table t (a int)`) + tk.MustExec(`insert into t set a=1`) + tk.MustExec(`alter table t add column b timestamp not null default current_timestamp;`) + time_in_8 := tk.MustQuery("select b from t").Rows()[0][0] + tk.MustExec(`set time_zone = '+00:00'`) + time_in_0 := tk.MustQuery("select b from t").Rows()[0][0] + c.Assert(time_in_8 != time_in_0, IsTrue, Commentf("%v == %v", time_in_8, time_in_0)) + datumTimeIn8, err := expression.GetTimeValue(tk.Se, time_in_8, mysql.TypeTimestamp, 0) + c.Assert(err, IsNil) + tIn8To0 := datumTimeIn8.GetMysqlTime() + timeZoneIn8, err := time.LoadLocation("Asia/Shanghai") + c.Assert(err, IsNil) + err = tIn8To0.ConvertTimeZone(timeZoneIn8, time.UTC) + c.Assert(err, IsNil) + c.Assert(time_in_0 == tIn8To0.String(), IsTrue, Commentf("%v != %v", time_in_0, tIn8To0.String())) +} + func (s *testSuite) TestTiDBCurrentTS(c *C) { tk := testkit.NewTestKit(c, s.store) tk.MustQuery("select @@tidb_current_ts").Check(testkit.Rows("0")) From b1a5d27d394fa061c4fd02739d2a42f6f52c56f8 Mon Sep 17 00:00:00 2001 From: crazycs520 Date: Fri, 18 Jan 2019 13:28:37 +0800 Subject: [PATCH 04/18] refine code --- ddl/ddl_api.go | 2 -- ddl/index.go | 6 ------ 2 files changed, 8 deletions(-) diff --git a/ddl/ddl_api.go b/ddl/ddl_api.go index 7cf6e45ab6898..b3f714ba6e80b 100644 --- a/ddl/ddl_api.go +++ b/ddl/ddl_api.go @@ -368,7 +368,6 @@ func columnDefToCol(ctx sessionctx.Context, offset int, colDef *ast.ColumnDef, o col.Flag |= mysql.UniqueKeyFlag case ast.ColumnOptionDefaultValue: value, err := getDefaultValue(ctx, v, colDef.Tp.Tp, colDef.Tp.Decimal) - fmt.Printf("\ngetDefaultValue: value: %s, session.timezone: %v\n\n\n", value, ctx.GetSessionVars().TimeZone) if err != nil { return nil, nil, ErrColumnBadNull.GenWithStack("invalid default value - %s", err) } @@ -548,7 +547,6 @@ func checkDefaultValue(ctx sessionctx.Context, c *table.Column, hasDefaultValue } if c.GetDefaultValue() != nil { - fmt.Printf("check default value: %v\n\n", c.GetDefaultValue()) if _, err := table.GetColDefaultValue(ctx, c.ToInfo()); err != nil { return types.ErrInvalidDefault.GenWithStackByArgs(c.Name) } diff --git a/ddl/index.go b/ddl/index.go index 25f83df2657ad..14eec5c8a0448 100644 --- a/ddl/index.go +++ b/ddl/index.go @@ -585,12 +585,6 @@ func (w *addIndexWorker) getIndexRecord(handle int64, recordKey []byte, rawRecor } } idxVal[j] = idxColumnVal - ss := "" - for _, vv := range idxVal { - str, _ := vv.ToString() - ss += str - ss += " " - } } // If there are generated column, rowDecoder will use column value that not in idxInfo.Columns to calculate // the generated value, so we need to clear up the reusing map. From c97e6b8161d6763e9ce8e8c5c72f2e4f2f28a65e Mon Sep 17 00:00:00 2001 From: crazycs520 Date: Fri, 18 Jan 2019 13:52:04 +0800 Subject: [PATCH 05/18] refine code --- executor/executor_test.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/executor/executor_test.go b/executor/executor_test.go index 7bcbbb6dd101e..1d572c8b01498 100644 --- a/executor/executor_test.go +++ b/executor/executor_test.go @@ -2241,18 +2241,18 @@ func (s *testSuite) TestTimestampDefaultValueTimeZone(c *C) { tk.MustExec(`create table t (a int)`) tk.MustExec(`insert into t set a=1`) tk.MustExec(`alter table t add column b timestamp not null default current_timestamp;`) - time_in_8 := tk.MustQuery("select b from t").Rows()[0][0] + timeIn8 := tk.MustQuery("select b from t").Rows()[0][0] tk.MustExec(`set time_zone = '+00:00'`) - time_in_0 := tk.MustQuery("select b from t").Rows()[0][0] - c.Assert(time_in_8 != time_in_0, IsTrue, Commentf("%v == %v", time_in_8, time_in_0)) - datumTimeIn8, err := expression.GetTimeValue(tk.Se, time_in_8, mysql.TypeTimestamp, 0) + timeIn0 := tk.MustQuery("select b from t").Rows()[0][0] + c.Assert(timeIn8 != timeIn0, IsTrue, Commentf("%v == %v", timeIn8, timeIn0)) + datumTimeIn8, err := expression.GetTimeValue(tk.Se, timeIn8, mysql.TypeTimestamp, 0) c.Assert(err, IsNil) tIn8To0 := datumTimeIn8.GetMysqlTime() timeZoneIn8, err := time.LoadLocation("Asia/Shanghai") c.Assert(err, IsNil) err = tIn8To0.ConvertTimeZone(timeZoneIn8, time.UTC) c.Assert(err, IsNil) - c.Assert(time_in_0 == tIn8To0.String(), IsTrue, Commentf("%v != %v", time_in_0, tIn8To0.String())) + c.Assert(timeIn0 == tIn8To0.String(), IsTrue, Commentf("%v != %v", timeIn0, tIn8To0.String())) } func (s *testSuite) TestTiDBCurrentTS(c *C) { From b3305cb15a875c0182cc15b78458b72d9dc97d3c Mon Sep 17 00:00:00 2001 From: crazycs520 Date: Fri, 18 Jan 2019 17:37:12 +0800 Subject: [PATCH 06/18] fix old version timestamp multiple zone bugs. --- executor/show.go | 10 +++------- table/column.go | 9 +++++++-- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/executor/show.go b/executor/show.go index 9601517cd43c7..5bc51632d8f9b 100644 --- a/executor/show.go +++ b/executor/show.go @@ -628,16 +628,12 @@ func (e *ShowExec) fetchShowCreateTable() error { buf.WriteString(" DEFAULT CURRENT_TIMESTAMP") default: defaultValStr := fmt.Sprintf("%v", defaultValue) - if col.Tp == mysql.TypeTimestamp && col.Version == model.ColumnInfoVersion1 { - t, err := types.ParseTime(e.ctx.GetSessionVars().StmtCtx, defaultValStr, col.Tp, col.Decimal) + if col.Tp == mysql.TypeTimestamp { + timeValue, err := table.GetColDefaultValue(e.ctx, col.ToInfo()) if err != nil { return errors.Trace(err) } - err = t.ConvertTimeZone(time.UTC, e.ctx.GetSessionVars().TimeZone) - if err != nil { - return errors.Trace(err) - } - defaultValStr = t.String() + defaultValStr = timeValue.GetMysqlTime().String() } if col.Tp == mysql.TypeBit { diff --git a/table/column.go b/table/column.go index f6aa67c77e000..73880e0995def 100644 --- a/table/column.go +++ b/table/column.go @@ -33,6 +33,7 @@ import ( "github.com/pingcap/tidb/types" "github.com/pingcap/tidb/types/json" "github.com/pingcap/tidb/util/hack" + "github.com/pingcap/tidb/util/timeutil" log "github.com/sirupsen/logrus" ) @@ -357,9 +358,13 @@ func getColDefaultValue(ctx sessionctx.Context, col *model.ColumnInfo, defaultVa return types.Datum{}, errGetDefaultFailed.GenWithStack("Field '%s' get default value fail - %s", col.Name, errors.Trace(err)) } - if col.Tp == mysql.TypeTimestamp && col.Version == model.ColumnInfoVersion1 { + if col.Tp == mysql.TypeTimestamp { t := value.GetMysqlTime() - err = t.ConvertTimeZone(time.UTC, ctx.GetSessionVars().Location()) + defaultTimeZone := timeutil.SystemLocation() + if col.Version == model.ColumnInfoVersion1 { + defaultTimeZone = time.UTC + } + err = t.ConvertTimeZone(defaultTimeZone, ctx.GetSessionVars().Location()) if err != nil { return value, errors.Trace(err) } From 905503ff26ffe0dd22e416ead852ccac051d0194 Mon Sep 17 00:00:00 2001 From: crazycs520 Date: Fri, 18 Jan 2019 21:29:40 +0800 Subject: [PATCH 07/18] fix mysql test --- executor/show.go | 7 +++++++ table/column.go | 22 +++++++++++++--------- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/executor/show.go b/executor/show.go index 5bc51632d8f9b..56df01fa5ca7d 100644 --- a/executor/show.go +++ b/executor/show.go @@ -357,6 +357,13 @@ func (e *ShowExec) fetchShowColumns() error { if desc.DefaultValue != nil { // SHOW COLUMNS result expects string value defaultValStr := fmt.Sprintf("%v", desc.DefaultValue) + if col.Tp == mysql.TypeTimestamp && strings.ToUpper(defaultValStr) != strings.ToUpper(ast.CurrentTimestamp) && defaultValStr != types.ZeroDatetimeStr { + timeValue, err := table.GetColDefaultValue(e.ctx, col.ToInfo()) + if err != nil { + return errors.Trace(err) + } + defaultValStr = timeValue.GetMysqlTime().String() + } if col.Tp == mysql.TypeBit { defaultValBinaryLiteral := types.BinaryLiteral(defaultValStr) columnDefault = defaultValBinaryLiteral.ToBitLiteralString(true) diff --git a/table/column.go b/table/column.go index 73880e0995def..f0ba40ffe4d8f 100644 --- a/table/column.go +++ b/table/column.go @@ -359,16 +359,20 @@ func getColDefaultValue(ctx sessionctx.Context, col *model.ColumnInfo, defaultVa col.Name, errors.Trace(err)) } if col.Tp == mysql.TypeTimestamp { - t := value.GetMysqlTime() - defaultTimeZone := timeutil.SystemLocation() - if col.Version == model.ColumnInfoVersion1 { - defaultTimeZone = time.UTC + if vv, ok := defaultVal.(string); ok { + if strings.ToUpper(vv) != strings.ToUpper(ast.CurrentTimestamp) && vv != types.ZeroDatetimeStr { + t := value.GetMysqlTime() + defaultTimeZone := timeutil.SystemLocation() + if col.Version == model.ColumnInfoVersion1 { + defaultTimeZone = time.UTC + } + err = t.ConvertTimeZone(defaultTimeZone, ctx.GetSessionVars().Location()) + if err != nil { + return value, errors.Trace(err) + } + value.SetMysqlTime(t) + } } - err = t.ConvertTimeZone(defaultTimeZone, ctx.GetSessionVars().Location()) - if err != nil { - return value, errors.Trace(err) - } - value.SetMysqlTime(t) } return value, nil } From 095aa255e411a4939671c59572bd9e06cacf354e Mon Sep 17 00:00:00 2001 From: crazycs520 Date: Mon, 21 Jan 2019 10:22:04 +0800 Subject: [PATCH 08/18] address comment --- ddl/column.go | 2 ++ ddl/ddl_api.go | 24 ++++++++++++++++-------- executor/show.go | 6 ++++-- table/column.go | 4 +++- 4 files changed, 25 insertions(+), 11 deletions(-) diff --git a/ddl/column.go b/ddl/column.go index f4041731f7ab0..3a826ed09cb30 100644 --- a/ddl/column.go +++ b/ddl/column.go @@ -573,6 +573,8 @@ func generateOriginDefaultValue(col *model.ColumnInfo) (interface{}, error) { if odValue == strings.ToUpper(ast.CurrentTimestamp) && (col.Tp == mysql.TypeTimestamp || col.Tp == mysql.TypeDatetime) { odValue = time.Now().UTC().Format(types.TimeFormat) + // Version = 1: For OriginDefaultValue and DefaultValue of timestamp column will stores the default time in UTC time zone. + // This will fix bug in version 0. col.Version = model.ColumnInfoVersion1 } return odValue, nil diff --git a/ddl/ddl_api.go b/ddl/ddl_api.go index b3f714ba6e80b..1709791081f25 100644 --- a/ddl/ddl_api.go +++ b/ddl/ddl_api.go @@ -288,24 +288,32 @@ func checkColumnDefaultValue(ctx sessionctx.Context, col *table.Column, value in } } } - if value != nil && col.Tp == mysql.TypeTimestamp { - if vv, ok := value.(string); ok { - upperX := strings.ToUpper(vv) - if upperX != strings.ToUpper(ast.CurrentTimestamp) && upperX != types.ZeroDatetimeStr { + value, err := convertTimestampDelfaultValToUTC(ctx, value, col) + if err != nil { + return hasDefaultValue, value, errors.Trace(err) + + } + return hasDefaultValue, value, nil +} + +func convertTimestampDelfaultValToUTC(ctx sessionctx.Context, defaultVal interface{}, col *table.Column) (interface{}, error) { + if defaultVal != nil && col.Tp == mysql.TypeTimestamp { + if vv, ok := defaultVal.(string); ok { + if vv != types.ZeroDatetimeStr && strings.ToUpper(vv) != strings.ToUpper(ast.CurrentTimestamp) { t, err := types.ParseTime(ctx.GetSessionVars().StmtCtx, vv, col.Tp, col.Decimal) if err != nil { - return hasDefaultValue, value, errors.Trace(err) + return defaultVal, errors.Trace(err) } err = t.ConvertTimeZone(ctx.GetSessionVars().Location(), time.UTC) if err != nil { - return hasDefaultValue, value, errors.Trace(err) + return defaultVal, errors.Trace(err) } - value = t.String() + defaultVal = t.String() col.Version = model.ColumnInfoVersion1 } } } - return hasDefaultValue, value, nil + return defaultVal, nil } // isExplicitTimeStamp is used to check if explicit_defaults_for_timestamp is on or off. diff --git a/executor/show.go b/executor/show.go index 56df01fa5ca7d..3163498434086 100644 --- a/executor/show.go +++ b/executor/show.go @@ -357,7 +357,8 @@ func (e *ShowExec) fetchShowColumns() error { if desc.DefaultValue != nil { // SHOW COLUMNS result expects string value defaultValStr := fmt.Sprintf("%v", desc.DefaultValue) - if col.Tp == mysql.TypeTimestamp && strings.ToUpper(defaultValStr) != strings.ToUpper(ast.CurrentTimestamp) && defaultValStr != types.ZeroDatetimeStr { + // If column is timestamp, and default value is not current_timestamp, should convert the default value to the current session time zone. + if col.Tp == mysql.TypeTimestamp && defaultValStr != types.ZeroDatetimeStr && strings.ToUpper(defaultValStr) != strings.ToUpper(ast.CurrentTimestamp) { timeValue, err := table.GetColDefaultValue(e.ctx, col.ToInfo()) if err != nil { return errors.Trace(err) @@ -635,7 +636,8 @@ func (e *ShowExec) fetchShowCreateTable() error { buf.WriteString(" DEFAULT CURRENT_TIMESTAMP") default: defaultValStr := fmt.Sprintf("%v", defaultValue) - if col.Tp == mysql.TypeTimestamp { + // If column is timestamp, and default value is not current_timestamp, should convert the default value to the current session time zone. + if col.Tp == mysql.TypeTimestamp && defaultValStr != types.ZeroDatetimeStr { timeValue, err := table.GetColDefaultValue(e.ctx, col.ToInfo()) if err != nil { return errors.Trace(err) diff --git a/table/column.go b/table/column.go index f0ba40ffe4d8f..c7586c2a194a1 100644 --- a/table/column.go +++ b/table/column.go @@ -358,10 +358,12 @@ func getColDefaultValue(ctx sessionctx.Context, col *model.ColumnInfo, defaultVa return types.Datum{}, errGetDefaultFailed.GenWithStack("Field '%s' get default value fail - %s", col.Name, errors.Trace(err)) } + // If column is timestamp, and default value is not current_timestamp, should convert the default value to the current session time zone. if col.Tp == mysql.TypeTimestamp { if vv, ok := defaultVal.(string); ok { - if strings.ToUpper(vv) != strings.ToUpper(ast.CurrentTimestamp) && vv != types.ZeroDatetimeStr { + if vv != types.ZeroDatetimeStr && strings.ToUpper(vv) != strings.ToUpper(ast.CurrentTimestamp) { t := value.GetMysqlTime() + // For col.Version = 0, the timezone information of default value is already lost, so use the system timezone as the default value timezone. defaultTimeZone := timeutil.SystemLocation() if col.Version == model.ColumnInfoVersion1 { defaultTimeZone = time.UTC From 3a216c7f8e654578b7bcf149f0948e07657dbfbd Mon Sep 17 00:00:00 2001 From: crazycs520 Date: Mon, 21 Jan 2019 10:44:26 +0800 Subject: [PATCH 09/18] address comment --- ddl/ddl_api.go | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/ddl/ddl_api.go b/ddl/ddl_api.go index 1709791081f25..df079effb91f3 100644 --- a/ddl/ddl_api.go +++ b/ddl/ddl_api.go @@ -288,11 +288,6 @@ func checkColumnDefaultValue(ctx sessionctx.Context, col *table.Column, value in } } } - value, err := convertTimestampDelfaultValToUTC(ctx, value, col) - if err != nil { - return hasDefaultValue, value, errors.Trace(err) - - } return hasDefaultValue, value, nil } @@ -382,6 +377,11 @@ func columnDefToCol(ctx sessionctx.Context, offset int, colDef *ast.ColumnDef, o if hasDefaultValue, value, err = checkColumnDefaultValue(ctx, col, value); err != nil { return nil, nil, errors.Trace(err) } + value, err = convertTimestampDelfaultValToUTC(ctx, value, col) + if err != nil { + return nil, nil, errors.Trace(err) + + } if err = col.SetDefaultValue(value); err != nil { return nil, nil, errors.Trace(err) } @@ -1918,6 +1918,11 @@ func setDefaultValue(ctx sessionctx.Context, col *table.Column, option *ast.Colu if _, value, err = checkColumnDefaultValue(ctx, col, value); err != nil { return errors.Trace(err) } + value, err = convertTimestampDelfaultValToUTC(ctx, value, col) + if err != nil { + return errors.Trace(err) + + } err = col.SetDefaultValue(value) if err != nil { return errors.Trace(err) @@ -1950,6 +1955,11 @@ func setDefaultAndComment(ctx sessionctx.Context, col *table.Column, options []* if hasDefaultValue, value, err = checkColumnDefaultValue(ctx, col, value); err != nil { return errors.Trace(err) } + value, err = convertTimestampDelfaultValToUTC(ctx, value, col) + if err != nil { + return errors.Trace(err) + + } if err = col.SetDefaultValue(value); err != nil { return errors.Trace(err) } From 2eac1672fb8fa1e1361b5f8436bc498465ab1918 Mon Sep 17 00:00:00 2001 From: crazycs520 Date: Mon, 21 Jan 2019 15:04:02 +0800 Subject: [PATCH 10/18] address comment --- ddl/column.go | 1 + ddl/ddl_api.go | 3 +++ 2 files changed, 4 insertions(+) diff --git a/ddl/column.go b/ddl/column.go index 3a826ed09cb30..269586656e8c1 100644 --- a/ddl/column.go +++ b/ddl/column.go @@ -575,6 +575,7 @@ func generateOriginDefaultValue(col *model.ColumnInfo) (interface{}, error) { odValue = time.Now().UTC().Format(types.TimeFormat) // Version = 1: For OriginDefaultValue and DefaultValue of timestamp column will stores the default time in UTC time zone. // This will fix bug in version 0. + // TODO: remove this version field after there is no old version 0. col.Version = model.ColumnInfoVersion1 } return odValue, nil diff --git a/ddl/ddl_api.go b/ddl/ddl_api.go index df079effb91f3..3faedcdde601c 100644 --- a/ddl/ddl_api.go +++ b/ddl/ddl_api.go @@ -304,6 +304,9 @@ func convertTimestampDelfaultValToUTC(ctx sessionctx.Context, defaultVal interfa return defaultVal, errors.Trace(err) } defaultVal = t.String() + // Version = 1: For OriginDefaultValue and DefaultValue of timestamp column will stores the default time in UTC time zone. + // This will fix bug in version 0. + // TODO: remove this version field after there is no old version 0. col.Version = model.ColumnInfoVersion1 } } From 9f3ce161177b42655b032cec8c98dd590b7e1f66 Mon Sep 17 00:00:00 2001 From: crazycs Date: Wed, 23 Jan 2019 14:39:52 +0800 Subject: [PATCH 11/18] update go.mod for parser --- go.mod | 4 +--- go.sum | 7 ++----- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/go.mod b/go.mod index 843bfecc685d3..831d52dd83d11 100644 --- a/go.mod +++ b/go.mod @@ -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-20190117104731-c02a7ccd3d6b + github.com/pingcap/parser v0.0.0-20190123063514-f8c3dff115d5 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 @@ -88,5 +88,3 @@ require ( sourcegraph.com/sourcegraph/appdash v0.0.0-20180531100431-4c381bd170b4 sourcegraph.com/sourcegraph/appdash-data v0.0.0-20151005221446-73f23eafcf67 ) - -replace github.com/pingcap/parser => github.com/crazycs520/parser v0.0.0-20190117163513-d4e90e647da0 diff --git a/go.sum b/go.sum index b0cef63d68410..f1531172bddfe 100644 --- a/go.sum +++ b/go.sum @@ -22,9 +22,6 @@ github.com/coreos/go-systemd v0.0.0-20181031085051-9002847aa142 h1:3jFq2xL4ZajGK github.com/coreos/go-systemd v0.0.0-20181031085051-9002847aa142/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f h1:lBNOc5arjvs8E5mO2tbpBpLoyyu8B6e44T7hJy6potg= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/crazycs520/parser v0.0.0-20190117131849-0e3a62b35fbf/go.mod h1:xLjI+gnWYexq011WPMEvCNS8rFM9qe1vdojIEzSKPuc= -github.com/crazycs520/parser v0.0.0-20190117163513-d4e90e647da0 h1:VbwlcYWo7zZm4Amau+5H/yAhJMuQMv1A+/8gMNorKEE= -github.com/crazycs520/parser v0.0.0-20190117163513-d4e90e647da0/go.mod h1:xLjI+gnWYexq011WPMEvCNS8rFM9qe1vdojIEzSKPuc= github.com/cznic/mathutil v0.0.0-20181021201202-eba54fb065b7 h1:y+DH9ARrWiiNBV+6waYP2IPcsRbxdU1qsnycPfShF4c= github.com/cznic/mathutil v0.0.0-20181021201202-eba54fb065b7/go.mod h1:e6NPNENfs9mPDVNRekM7lKScauxd5kXTr1Mfyig6TDM= github.com/cznic/sortutil v0.0.0-20150617083342-4c7342852e65 h1:hxuZop6tSoOi0sxFzoGGYdRqNrPubyaIf9KoBG9tPiE= @@ -113,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-20190117104731-c02a7ccd3d6b h1:Z2qM+gP9BkJA1lg6Kai7Uy9kHT0EXF9U8BqYzP4Jcj4= -github.com/pingcap/parser v0.0.0-20190117104731-c02a7ccd3d6b/go.mod h1:1FNvfp9+J0wvc4kl8eGNh7Rqrxveg15jJoWo/a0uHwA= +github.com/pingcap/parser v0.0.0-20190123063514-f8c3dff115d5 h1:WBU3Axs+DW+2GxtyDsyNUSd5Sp0C0SVAZ5GNuDsfhGU= +github.com/pingcap/parser v0.0.0-20190123063514-f8c3dff115d5/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= From 7cafc8d15ae23bd3b7ee2ccc100cabdbf2e59e38 Mon Sep 17 00:00:00 2001 From: crazycs Date: Thu, 24 Jan 2019 09:27:46 +0800 Subject: [PATCH 12/18] add test for add index --- executor/executor_test.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/executor/executor_test.go b/executor/executor_test.go index d78b2e15614e3..cbd673ed51fcc 100644 --- a/executor/executor_test.go +++ b/executor/executor_test.go @@ -2249,6 +2249,12 @@ func (s *testSuite) TestTimestampDefaultValueTimeZone(c *C) { err = tIn8To0.ConvertTimeZone(timeZoneIn8, time.UTC) c.Assert(err, IsNil) c.Assert(timeIn0 == tIn8To0.String(), IsTrue, Commentf("%v != %v", timeIn0, tIn8To0.String())) + + // test add index. + tk.MustExec(`alter table t add index(b);`) + tk.MustExec("admin check table t") + tk.MustExec(`set time_zone = '+05:00'`) + tk.MustExec("admin check table t") } func (s *testSuite) TestTiDBCurrentTS(c *C) { From b70ad8344e3cf144b2c944b18ef72e604f25a325 Mon Sep 17 00:00:00 2001 From: crazycs520 Date: Mon, 28 Jan 2019 16:37:48 +0800 Subject: [PATCH 13/18] address comment --- ddl/column.go | 17 ++++++++++------- ddl/ddl_api.go | 41 +++++++++++++++++++++-------------------- table/column.go | 49 ++++++++++++++++++++++++------------------------- 3 files changed, 55 insertions(+), 52 deletions(-) diff --git a/ddl/column.go b/ddl/column.go index 269586656e8c1..5f992ecb84cbb 100644 --- a/ddl/column.go +++ b/ddl/column.go @@ -570,13 +570,16 @@ func generateOriginDefaultValue(col *model.ColumnInfo) (interface{}, error) { } } - if odValue == strings.ToUpper(ast.CurrentTimestamp) && - (col.Tp == mysql.TypeTimestamp || col.Tp == mysql.TypeDatetime) { - odValue = time.Now().UTC().Format(types.TimeFormat) - // Version = 1: For OriginDefaultValue and DefaultValue of timestamp column will stores the default time in UTC time zone. - // This will fix bug in version 0. - // TODO: remove this version field after there is no old version 0. - col.Version = model.ColumnInfoVersion1 + if odValue == strings.ToUpper(ast.CurrentTimestamp) { + if col.Tp == mysql.TypeTimestamp { + odValue = time.Now().UTC().Format(types.TimeFormat) + // Version = 1: For OriginDefaultValue and DefaultValue of timestamp column will stores the default time in UTC time zone. + // This will fix bug in version 0. + // TODO: remove this version field after there is no old version 0. + col.Version = model.ColumnInfoVersion1 + } else if col.Tp == mysql.TypeDatetime { + odValue = time.Now().Format(types.TimeFormat) + } } return odValue, nil } diff --git a/ddl/ddl_api.go b/ddl/ddl_api.go index 401bd0316ceb4..1394d45670a83 100644 --- a/ddl/ddl_api.go +++ b/ddl/ddl_api.go @@ -318,24 +318,25 @@ func checkColumnDefaultValue(ctx sessionctx.Context, col *table.Column, value in return hasDefaultValue, value, nil } -func convertTimestampDelfaultValToUTC(ctx sessionctx.Context, defaultVal interface{}, col *table.Column) (interface{}, error) { - if defaultVal != nil && col.Tp == mysql.TypeTimestamp { - if vv, ok := defaultVal.(string); ok { - if vv != types.ZeroDatetimeStr && strings.ToUpper(vv) != strings.ToUpper(ast.CurrentTimestamp) { - t, err := types.ParseTime(ctx.GetSessionVars().StmtCtx, vv, col.Tp, col.Decimal) - if err != nil { - return defaultVal, errors.Trace(err) - } - err = t.ConvertTimeZone(ctx.GetSessionVars().Location(), time.UTC) - if err != nil { - return defaultVal, errors.Trace(err) - } - defaultVal = t.String() - // Version = 1: For OriginDefaultValue and DefaultValue of timestamp column will stores the default time in UTC time zone. - // This will fix bug in version 0. - // TODO: remove this version field after there is no old version 0. - col.Version = model.ColumnInfoVersion1 +func convertTimestampDefaultValToUTC(ctx sessionctx.Context, defaultVal interface{}, col *table.Column) (interface{}, error) { + if defaultVal == nil || col.Tp != mysql.TypeTimestamp { + return defaultVal, nil + } + if vv, ok := defaultVal.(string); ok { + if vv != types.ZeroDatetimeStr && strings.ToUpper(vv) != strings.ToUpper(ast.CurrentTimestamp) { + t, err := types.ParseTime(ctx.GetSessionVars().StmtCtx, vv, col.Tp, col.Decimal) + if err != nil { + return defaultVal, errors.Trace(err) + } + err = t.ConvertTimeZone(ctx.GetSessionVars().Location(), time.UTC) + if err != nil { + return defaultVal, errors.Trace(err) } + defaultVal = t.String() + // Version = 1: For OriginDefaultValue and DefaultValue of timestamp column will stores the default time in UTC time zone. + // This will fix bug in version 0. + // TODO: remove this version field after there is no old version 0. + col.Version = model.ColumnInfoVersion1 } } return defaultVal, nil @@ -407,7 +408,7 @@ func columnDefToCol(ctx sessionctx.Context, offset int, colDef *ast.ColumnDef, o if hasDefaultValue, value, err = checkColumnDefaultValue(ctx, col, value); err != nil { return nil, nil, errors.Trace(err) } - value, err = convertTimestampDelfaultValToUTC(ctx, value, col) + value, err = convertTimestampDefaultValToUTC(ctx, value, col) if err != nil { return nil, nil, errors.Trace(err) @@ -1968,7 +1969,7 @@ func setDefaultValue(ctx sessionctx.Context, col *table.Column, option *ast.Colu if _, value, err = checkColumnDefaultValue(ctx, col, value); err != nil { return errors.Trace(err) } - value, err = convertTimestampDelfaultValToUTC(ctx, value, col) + value, err = convertTimestampDefaultValToUTC(ctx, value, col) if err != nil { return errors.Trace(err) @@ -2005,7 +2006,7 @@ func setDefaultAndComment(ctx sessionctx.Context, col *table.Column, options []* if hasDefaultValue, value, err = checkColumnDefaultValue(ctx, col, value); err != nil { return errors.Trace(err) } - value, err = convertTimestampDelfaultValToUTC(ctx, value, col) + value, err = convertTimestampDefaultValToUTC(ctx, value, col) if err != nil { return errors.Trace(err) diff --git a/table/column.go b/table/column.go index 486d1b4e69267..9a5280b941051 100644 --- a/table/column.go +++ b/table/column.go @@ -352,36 +352,35 @@ func getColDefaultValue(ctx sessionctx.Context, col *model.ColumnInfo, defaultVa return getColDefaultValueFromNil(ctx, col) } - // Check and get timestamp/datetime default value. - if col.Tp == mysql.TypeTimestamp || col.Tp == mysql.TypeDatetime { - value, err := expression.GetTimeValue(ctx, defaultVal, col.Tp, col.Decimal) + if col.Tp != mysql.TypeTimestamp && col.Tp != mysql.TypeDatetime { + value, err := CastValue(ctx, types.NewDatum(defaultVal), col) if err != nil { - return types.Datum{}, errGetDefaultFailed.GenWithStack("Field '%s' get default value fail - %s", - col.Name, errors.Trace(err)) - } - // If column is timestamp, and default value is not current_timestamp, should convert the default value to the current session time zone. - if col.Tp == mysql.TypeTimestamp { - if vv, ok := defaultVal.(string); ok { - if vv != types.ZeroDatetimeStr && strings.ToUpper(vv) != strings.ToUpper(ast.CurrentTimestamp) { - t := value.GetMysqlTime() - // For col.Version = 0, the timezone information of default value is already lost, so use the system timezone as the default value timezone. - defaultTimeZone := timeutil.SystemLocation() - if col.Version == model.ColumnInfoVersion1 { - defaultTimeZone = time.UTC - } - err = t.ConvertTimeZone(defaultTimeZone, ctx.GetSessionVars().Location()) - if err != nil { - return value, errors.Trace(err) - } - value.SetMysqlTime(t) - } - } + return types.Datum{}, errors.Trace(err) } return value, nil + } - value, err := CastValue(ctx, types.NewDatum(defaultVal), col) + // Check and get timestamp/datetime default value. + value, err := expression.GetTimeValue(ctx, defaultVal, col.Tp, col.Decimal) if err != nil { - return types.Datum{}, errors.Trace(err) + return types.Datum{}, errGetDefaultFailed.GenWithStack("Field '%s' get default value fail - %s", + col.Name, errors.Trace(err)) + } + // If column is timestamp, and default value is not current_timestamp, should convert the default value to the current session time zone. + if col.Tp == mysql.TypeTimestamp { + if vv, ok := defaultVal.(string); ok && vv != types.ZeroDatetimeStr && strings.ToUpper(vv) != strings.ToUpper(ast.CurrentTimestamp) { + t := value.GetMysqlTime() + // For col.Version = 0, the timezone information of default value is already lost, so use the system timezone as the default value timezone. + defaultTimeZone := timeutil.SystemLocation() + if col.Version == model.ColumnInfoVersion1 { + defaultTimeZone = time.UTC + } + err = t.ConvertTimeZone(defaultTimeZone, ctx.GetSessionVars().Location()) + if err != nil { + return value, errors.Trace(err) + } + value.SetMysqlTime(t) + } } return value, nil } From f61efb653ced166469f3639506a4672f369cc851 Mon Sep 17 00:00:00 2001 From: crazycs Date: Mon, 28 Jan 2019 21:15:35 +0800 Subject: [PATCH 14/18] address comment --- ddl/ddl_api.go | 49 +++++++++++++++---------------------------------- 1 file changed, 15 insertions(+), 34 deletions(-) diff --git a/ddl/ddl_api.go b/ddl/ddl_api.go index 1394d45670a83..aad3ab6188f58 100644 --- a/ddl/ddl_api.go +++ b/ddl/ddl_api.go @@ -368,7 +368,7 @@ func columnDefToCol(ctx sessionctx.Context, offset int, colDef *ast.ColumnDef, o col.Flag |= mysql.NotNullFlag } } - + var err error setOnUpdateNow := false hasDefaultValue := false hasNullFlag := false @@ -401,20 +401,9 @@ func columnDefToCol(ctx sessionctx.Context, offset int, colDef *ast.ColumnDef, o constraints = append(constraints, constraint) col.Flag |= mysql.UniqueKeyFlag case ast.ColumnOptionDefaultValue: - value, err := getDefaultValue(ctx, v, colDef.Tp.Tp, colDef.Tp.Decimal) - if err != nil { - return nil, nil, ErrColumnBadNull.GenWithStack("invalid default value - %s", err) - } - if hasDefaultValue, value, err = checkColumnDefaultValue(ctx, col, value); err != nil { - return nil, nil, errors.Trace(err) - } - value, err = convertTimestampDefaultValToUTC(ctx, value, col) + hasDefaultValue, err = setDefaultValue(ctx, col, v) if err != nil { return nil, nil, errors.Trace(err) - - } - if err = col.SetDefaultValue(value); err != nil { - return nil, nil, errors.Trace(err) } removeOnUpdateNowFlag(col) case ast.ColumnOptionOnUpdate: @@ -470,7 +459,7 @@ func columnDefToCol(ctx sessionctx.Context, offset int, colDef *ast.ColumnDef, o if mysql.HasZerofillFlag(col.Flag) { col.Flag |= mysql.UnsignedFlag } - err := checkPriKeyConstraint(col, hasDefaultValue, hasNullFlag, outPriKeyConstraint) + err = checkPriKeyConstraint(col, hasDefaultValue, hasNullFlag, outPriKeyConstraint) if err != nil { return nil, nil, errors.Trace(err) } @@ -1961,24 +1950,26 @@ func modifiable(origin *types.FieldType, to *types.FieldType) error { return errUnsupportedModifyColumn.GenWithStackByArgs(msg) } -func setDefaultValue(ctx sessionctx.Context, col *table.Column, option *ast.ColumnOption) error { +func setDefaultValue(ctx sessionctx.Context, col *table.Column, option *ast.ColumnOption) (bool, error) { + hasDefaultValue := false value, err := getDefaultValue(ctx, option, col.Tp, col.Decimal) if err != nil { - return ErrColumnBadNull.GenWithStack("invalid default value - %s", err) + return hasDefaultValue, ErrColumnBadNull.GenWithStack("invalid default value - %s", err) } - if _, value, err = checkColumnDefaultValue(ctx, col, value); err != nil { - return errors.Trace(err) + + if hasDefaultValue, value, err = checkColumnDefaultValue(ctx, col, value); err != nil { + return hasDefaultValue, errors.Trace(err) } value, err = convertTimestampDefaultValToUTC(ctx, value, col) if err != nil { - return errors.Trace(err) + return hasDefaultValue, errors.Trace(err) } err = col.SetDefaultValue(value) if err != nil { - return errors.Trace(err) + return hasDefaultValue, errors.Trace(err) } - return errors.Trace(checkDefaultValue(ctx, col, true)) + return hasDefaultValue, errors.Trace(checkDefaultValue(ctx, col, hasDefaultValue)) } func setColumnComment(ctx sessionctx.Context, col *table.Column, option *ast.ColumnOption) error { @@ -1996,23 +1987,13 @@ func setDefaultAndComment(ctx sessionctx.Context, col *table.Column, options []* return nil } var hasDefaultValue, setOnUpdateNow bool + var err error for _, opt := range options { switch opt.Tp { case ast.ColumnOptionDefaultValue: - value, err := getDefaultValue(ctx, opt, col.Tp, col.Decimal) - if err != nil { - return ErrColumnBadNull.GenWithStack("invalid default value - %s", err) - } - if hasDefaultValue, value, err = checkColumnDefaultValue(ctx, col, value); err != nil { - return errors.Trace(err) - } - value, err = convertTimestampDefaultValToUTC(ctx, value, col) + hasDefaultValue, err = setDefaultValue(ctx, col, opt) if err != nil { return errors.Trace(err) - - } - if err = col.SetDefaultValue(value); err != nil { - return errors.Trace(err) } case ast.ColumnOptionComment: err := setColumnComment(ctx, col, opt) @@ -2251,7 +2232,7 @@ func (d *ddl) AlterColumn(ctx sessionctx.Context, ident ast.Ident, spec *ast.Alt } setNoDefaultValueFlag(col, false) } else { - err = setDefaultValue(ctx, col, specNewColumn.Options[0]) + _, err = setDefaultValue(ctx, col, specNewColumn.Options[0]) if err != nil { return errors.Trace(err) } From 08eb3b0d5a706d954f8947f93405f24089916cb6 Mon Sep 17 00:00:00 2001 From: crazycs520 Date: Tue, 29 Jan 2019 20:50:55 +0800 Subject: [PATCH 15/18] address comment --- ddl/ddl_api.go | 7 +++++-- executor/write_test.go | 9 +++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/ddl/ddl_api.go b/ddl/ddl_api.go index aad3ab6188f58..3ed1f6eeb64b0 100644 --- a/ddl/ddl_api.go +++ b/ddl/ddl_api.go @@ -1969,7 +1969,7 @@ func setDefaultValue(ctx sessionctx.Context, col *table.Column, option *ast.Colu if err != nil { return hasDefaultValue, errors.Trace(err) } - return hasDefaultValue, errors.Trace(checkDefaultValue(ctx, col, hasDefaultValue)) + return hasDefaultValue, nil } func setColumnComment(ctx sessionctx.Context, col *table.Column, option *ast.ColumnOption) error { @@ -2232,10 +2232,13 @@ func (d *ddl) AlterColumn(ctx sessionctx.Context, ident ast.Ident, spec *ast.Alt } setNoDefaultValueFlag(col, false) } else { - _, err = setDefaultValue(ctx, col, specNewColumn.Options[0]) + hasDefaultValue, err := setDefaultValue(ctx, col, specNewColumn.Options[0]) if err != nil { return errors.Trace(err) } + if err = checkDefaultValue(ctx, col, hasDefaultValue); err != nil { + return errors.Trace(err) + } } job := &model.Job{ diff --git a/executor/write_test.go b/executor/write_test.go index a8b1114738c4e..36777820fc767 100644 --- a/executor/write_test.go +++ b/executor/write_test.go @@ -2106,6 +2106,15 @@ func (s *testSuite2) TestNullDefault(c *C) { tk.MustQuery("select * from test_null_default").Check(testkit.Rows("", "1970-01-01 08:20:34")) } +func (s *testSuite2) TestNotNullDefault(c *C) { + tk := testkit.NewTestKit(c, s.store) + tk.MustExec("use test; drop table if exists t1,t2;") + defer tk.MustExec("drop table t1,t2") + tk.MustExec("create table t1 (a int not null default null default 1);") + tk.MustExec("create table t2 (a int);") + tk.MustExec("alter table t2 change column a a int not null default null default 1;") +} + func (s *testBypassSuite) TestLatch(c *C) { store, err := mockstore.NewMockTikvStore( // Small latch slot size to make conflicts. From 56e56103a01afe87e2b981c0d452ef2c84959cb5 Mon Sep 17 00:00:00 2001 From: crazycs520 Date: Tue, 12 Feb 2019 11:22:36 +0800 Subject: [PATCH 16/18] add comment --- ddl/column.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ddl/column.go b/ddl/column.go index 5f992ecb84cbb..7cb4cdf482229 100644 --- a/ddl/column.go +++ b/ddl/column.go @@ -242,6 +242,12 @@ func onDropColumn(t *meta.Meta, job *model.Job) (ver int64, _ error) { // NOTE: If the state of StateWriteOnly can be rollbacked, we'd better reconsider the original default value. // And we need consider the column without not-null flag. if colInfo.OriginDefaultValue == nil && mysql.HasNotNullFlag(colInfo.Flag) { + // If the column is timestamp default current_timestamp, and DDL owner is new version TiDB that set column.Version to 1, + // then old TiDB update record in the column write only stage will uses the wrong default value of the dropping column. + // Because new version of the column default value is UTC time, but old version TiDB will think the default value is the time in system timezone. + // But currently will be ok, because we can't cancel the drop column job when the job is running, + // so the column will be dropped succeed and client will never see the wrong default value of the dropped column. + // More info about this problem, see PR#9115. colInfo.OriginDefaultValue, err = generateOriginDefaultValue(colInfo) if err != nil { return ver, errors.Trace(err) From 930fce9da42543249e694bfc0e3bedc40d6f2b29 Mon Sep 17 00:00:00 2001 From: crazycs520 Date: Tue, 12 Feb 2019 11:30:15 +0800 Subject: [PATCH 17/18] refine code --- table/column.go | 1 - 1 file changed, 1 deletion(-) diff --git a/table/column.go b/table/column.go index 9a5280b941051..00fee1e699bcb 100644 --- a/table/column.go +++ b/table/column.go @@ -358,7 +358,6 @@ func getColDefaultValue(ctx sessionctx.Context, col *model.ColumnInfo, defaultVa return types.Datum{}, errors.Trace(err) } return value, nil - } // Check and get timestamp/datetime default value. value, err := expression.GetTimeValue(ctx, defaultVal, col.Tp, col.Decimal) From 438c3dd50cac79fd227191d9e071ee9f4095f23c Mon Sep 17 00:00:00 2001 From: crazycs520 Date: Tue, 12 Feb 2019 11:35:45 +0800 Subject: [PATCH 18/18] remove blank line --- ddl/ddl_api.go | 1 - 1 file changed, 1 deletion(-) diff --git a/ddl/ddl_api.go b/ddl/ddl_api.go index d9e7323d2cc5a..d398440e137ba 100644 --- a/ddl/ddl_api.go +++ b/ddl/ddl_api.go @@ -1967,7 +1967,6 @@ func setDefaultValue(ctx sessionctx.Context, col *table.Column, option *ast.Colu value, err = convertTimestampDefaultValToUTC(ctx, value, col) if err != nil { return hasDefaultValue, errors.Trace(err) - } err = col.SetDefaultValue(value) if err != nil {