From bcbc886c5d299444448536ea3df7869ed22b3eea Mon Sep 17 00:00:00 2001 From: Arenatlx Date: Wed, 11 Nov 2020 15:22:37 +0800 Subject: [PATCH 1/3] cherry pick #20862 to release-4.0 Signed-off-by: ti-srebot --- ddl/column.go | 80 +++ ddl/column_test.go | 4 + ddl/column_type_change_test.go | 989 +++++++++++++++++++++++++++++++++ ddl/ddl_api.go | 27 + ddl/ddl_worker_test.go | 71 ++- 5 files changed, 1170 insertions(+), 1 deletion(-) create mode 100644 ddl/column_type_change_test.go diff --git a/ddl/column.go b/ddl/column.go index a35d2740a0f96..ddd13aa14b5cf 100644 --- a/ddl/column.go +++ b/ddl/column.go @@ -352,6 +352,7 @@ func onSetDefaultValue(t *meta.Meta, job *model.Job) (ver int64, _ error) { return updateColumnDefaultValue(t, job, newCol, &newCol.Name) } +<<<<<<< HEAD func (w *worker) onModifyColumn(t *meta.Meta, job *model.Job) (ver int64, _ error) { newCol := &model.ColumnInfo{} oldColName := &model.CIStr{} @@ -359,6 +360,85 @@ func (w *worker) onModifyColumn(t *meta.Meta, job *model.Job) (ver int64, _ erro var modifyColumnTp byte var updatedAutoRandomBits uint64 err := job.DecodeArgs(newCol, oldColName, pos, &modifyColumnTp, &updatedAutoRandomBits) +======= +func needChangeColumnData(oldCol, newCol *model.ColumnInfo) bool { + toUnsigned := mysql.HasUnsignedFlag(newCol.Flag) + originUnsigned := mysql.HasUnsignedFlag(oldCol.Flag) + needTruncationOrToggleSign := func() bool { + return (newCol.Flen > 0 && newCol.Flen < oldCol.Flen) || (toUnsigned != originUnsigned) + } + // Ignore the potential max display length represented by integer's flen, use default flen instead. + oldColFlen, _ := mysql.GetDefaultFieldLengthAndDecimal(oldCol.Tp) + newColFlen, _ := mysql.GetDefaultFieldLengthAndDecimal(newCol.Tp) + needTruncationOrToggleSignForInteger := func() bool { + return (newColFlen > 0 && newColFlen < oldColFlen) || (toUnsigned != originUnsigned) + } + + // Deal with the same type. + if oldCol.Tp == newCol.Tp { + switch oldCol.Tp { + case mysql.TypeNewDecimal: + // Since type decimal will encode the precision, frac, negative(signed) and wordBuf into storage together, there is no short + // cut to eliminate data reorg change for column type change between decimal. + return oldCol.Flen != newCol.Flen || oldCol.Decimal != newCol.Decimal || toUnsigned != originUnsigned + case mysql.TypeEnum, mysql.TypeSet: + return isElemsChangedToModifyColumn(oldCol.Elems, newCol.Elems) + case mysql.TypeTiny, mysql.TypeShort, mysql.TypeInt24, mysql.TypeLong, mysql.TypeLonglong: + return toUnsigned != originUnsigned + } + + return needTruncationOrToggleSign() + } + + // Deal with the different type. + switch oldCol.Tp { + case mysql.TypeVarchar, mysql.TypeString, mysql.TypeVarString, mysql.TypeBlob, mysql.TypeTinyBlob, mysql.TypeMediumBlob, mysql.TypeLongBlob: + switch newCol.Tp { + case mysql.TypeVarchar, mysql.TypeString, mysql.TypeVarString, mysql.TypeBlob, mysql.TypeTinyBlob, mysql.TypeMediumBlob, mysql.TypeLongBlob: + return needTruncationOrToggleSign() + } + case mysql.TypeTiny, mysql.TypeShort, mysql.TypeInt24, mysql.TypeLong, mysql.TypeLonglong: + switch newCol.Tp { + case mysql.TypeTiny, mysql.TypeShort, mysql.TypeInt24, mysql.TypeLong, mysql.TypeLonglong: + return needTruncationOrToggleSignForInteger() + } + case mysql.TypeFloat, mysql.TypeDouble: + switch newCol.Tp { + case mysql.TypeFloat, mysql.TypeDouble: + return needTruncationOrToggleSign() + } + } + + return true +} + +func isElemsChangedToModifyColumn(oldElems, newElems []string) bool { + if len(newElems) < len(oldElems) { + return true + } + for index, oldElem := range oldElems { + newElem := newElems[index] + if oldElem != newElem { + return true + } + } + return false +} + +type modifyColumnJobParameter struct { + newCol *model.ColumnInfo + oldColName *model.CIStr + modifyColumnTp byte + updatedAutoRandomBits uint64 + changingCol *model.ColumnInfo + changingIdxs []*model.IndexInfo + pos *ast.ColumnPosition +} + +func getModifyColumnInfo(t *meta.Meta, job *model.Job) (*model.DBInfo, *model.TableInfo, *model.ColumnInfo, *modifyColumnJobParameter, error) { + jobParam := &modifyColumnJobParameter{pos: &ast.ColumnPosition{}} + err := job.DecodeArgs(&jobParam.newCol, &jobParam.oldColName, jobParam.pos, &jobParam.modifyColumnTp, &jobParam.updatedAutoRandomBits, &jobParam.changingCol, &jobParam.changingIdxs) +>>>>>>> 38f876044... ddl: ignore integer zerofill size attribute when changing the column types (#20862) if err != nil { job.State = model.JobStateCancelled return ver, errors.Trace(err) diff --git a/ddl/column_test.go b/ddl/column_test.go index af373407c0f82..1ac16bbcf8d5a 100644 --- a/ddl/column_test.go +++ b/ddl/column_test.go @@ -938,7 +938,11 @@ func (s *testColumnSuite) TestModifyColumn(c *C) { err error }{ {"int", "bigint", nil}, +<<<<<<< HEAD {"int", "int unsigned", errUnsupportedModifyColumn.GenWithStackByArgs("length 10 is less than origin 11")}, +======= + {"int", "int unsigned", errUnsupportedModifyColumn.GenWithStackByArgs("can't change unsigned integer to signed or vice versa, and tidb_enable_change_column_type is false")}, +>>>>>>> 38f876044... ddl: ignore integer zerofill size attribute when changing the column types (#20862) {"varchar(10)", "text", nil}, {"varbinary(10)", "blob", nil}, {"text", "blob", errUnsupportedModifyCharset.GenWithStackByArgs("charset from utf8mb4 to binary")}, diff --git a/ddl/column_type_change_test.go b/ddl/column_type_change_test.go new file mode 100644 index 0000000000000..88a7333cda763 --- /dev/null +++ b/ddl/column_type_change_test.go @@ -0,0 +1,989 @@ +// Copyright 2020 PingCAP, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// See the License for the specific language governing permissions and +// limitations under the License. + +package ddl_test + +import ( + "errors" + "time" + + . "github.com/pingcap/check" + "github.com/pingcap/parser/model" + parser_mysql "github.com/pingcap/parser/mysql" + "github.com/pingcap/parser/terror" + "github.com/pingcap/tidb/ddl" + "github.com/pingcap/tidb/domain" + mysql "github.com/pingcap/tidb/errno" + "github.com/pingcap/tidb/kv" + "github.com/pingcap/tidb/session" + "github.com/pingcap/tidb/sessionctx" + "github.com/pingcap/tidb/store/mockstore" + "github.com/pingcap/tidb/table" + "github.com/pingcap/tidb/table/tables" + "github.com/pingcap/tidb/util/dbterror" + "github.com/pingcap/tidb/util/testkit" +) + +var _ = SerialSuites(&testColumnTypeChangeSuite{}) + +type testColumnTypeChangeSuite struct { + store kv.Storage + dbInfo *model.DBInfo + dom *domain.Domain +} + +func (s *testColumnTypeChangeSuite) SetUpSuite(c *C) { + var err error + ddl.SetWaitTimeWhenErrorOccurred(1 * time.Microsecond) + s.store, err = mockstore.NewMockStore() + c.Assert(err, IsNil) + s.dom, err = session.BootstrapSession(s.store) + c.Assert(err, IsNil) +} + +func (s *testColumnTypeChangeSuite) TearDownSuite(c *C) { + s.dom.Close() + c.Assert(s.store.Close(), IsNil) +} + +func (s *testColumnTypeChangeSuite) TestColumnTypeChangeBetweenInteger(c *C) { + tk := testkit.NewTestKit(c, s.store) + tk.MustExec("use test") + // Enable column change variable. + tk.Se.GetSessionVars().EnableChangeColumnType = true + defer func() { + tk.Se.GetSessionVars().EnableChangeColumnType = false + }() + + // Modify column from null to not null. + tk.MustExec("drop table if exists t") + tk.MustExec("create table t (a int null, b int null)") + tk.MustExec("alter table t modify column b int not null") + + tk.MustExec("insert into t(a, b) values (null, 1)") + // Modify column from null to not null in same type will cause ErrInvalidUseOfNull + tk.MustGetErrCode("alter table t modify column a int not null", mysql.ErrInvalidUseOfNull) + + // Modify column from null to not null in different type will cause WarnDataTruncated. + tk.MustGetErrCode("alter table t modify column a tinyint not null", mysql.WarnDataTruncated) + tk.MustGetErrCode("alter table t modify column a bigint not null", mysql.WarnDataTruncated) + + // Modify column not null to null. + tk.MustExec("drop table if exists t") + tk.MustExec("create table t (a int not null, b int not null)") + tk.MustExec("alter table t modify column b int null") + + tk.MustExec("insert into t(a, b) values (1, null)") + tk.MustExec("alter table t modify column a int null") + + // Modify column from unsigned to signed and from signed to unsigned. + tk.MustExec("drop table if exists t") + tk.MustExec("create table t (a int unsigned, b int signed)") + tk.MustExec("insert into t(a, b) values (1, 1)") + tk.MustExec("alter table t modify column a int signed") + tk.MustExec("alter table t modify column b int unsigned") + + // Modify column from small type to big type. + tk.MustExec("drop table if exists t") + tk.MustExec("create table t (a tinyint)") + tk.MustExec("alter table t modify column a smallint") + + tk.MustExec("drop table if exists t") + tk.MustExec("create table t (a tinyint)") + tk.MustExec("insert into t(a) values (127)") + tk.MustExec("alter table t modify column a smallint") + tk.MustExec("alter table t modify column a mediumint") + tk.MustExec("alter table t modify column a int") + tk.MustExec("alter table t modify column a bigint") + + // Modify column from big type to small type. + tk.MustExec("drop table if exists t") + tk.MustExec("create table t (a bigint)") + tk.MustExec("alter table t modify column a int") + tk.MustExec("alter table t modify column a mediumint") + tk.MustExec("alter table t modify column a smallint") + tk.MustExec("alter table t modify column a tinyint") + + tk.MustExec("drop table if exists t") + tk.MustExec("create table t (a bigint)") + tk.MustExec("insert into t(a) values (9223372036854775807)") + tk.MustGetErrCode("alter table t modify column a int", mysql.ErrDataOutOfRange) + tk.MustGetErrCode("alter table t modify column a mediumint", mysql.ErrDataOutOfRange) + tk.MustGetErrCode("alter table t modify column a smallint", mysql.ErrDataOutOfRange) + tk.MustGetErrCode("alter table t modify column a tinyint", mysql.ErrDataOutOfRange) + _, err := tk.Exec("admin check table t") + c.Assert(err, IsNil) +} + +func (s *testColumnTypeChangeSuite) TestColumnTypeChangeStateBetweenInteger(c *C) { + tk := testkit.NewTestKit(c, s.store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t (c1 int, c2 int)") + tk.MustExec("insert into t(c1, c2) values (1, 1)") + // Enable column change variable. + tk.Se.GetSessionVars().EnableChangeColumnType = true + defer func() { + tk.Se.GetSessionVars().EnableChangeColumnType = false + }() + + // use new session to check meta in callback function. + internalTK := testkit.NewTestKit(c, s.store) + internalTK.MustExec("use test") + + tbl := testGetTableByName(c, tk.Se, "test", "t") + c.Assert(tbl, NotNil) + c.Assert(len(tbl.Cols()), Equals, 2) + c.Assert(getModifyColumn(c, tk.Se.(sessionctx.Context), "test", "t", "c2", false), NotNil) + + originalHook := s.dom.DDL().GetHook() + defer s.dom.DDL().(ddl.DDLForTest).SetHook(originalHook) + + hook := &ddl.TestDDLCallback{} + var checkErr error + hook.OnJobRunBeforeExported = func(job *model.Job) { + if checkErr != nil { + return + } + if tbl.Meta().ID != job.TableID { + return + } + switch job.SchemaState { + case model.StateNone: + tbl = testGetTableByName(c, internalTK.Se, "test", "t") + if tbl == nil { + checkErr = errors.New("tbl is nil") + } else if len(tbl.Cols()) != 2 { + checkErr = errors.New("len(cols) is not right") + } + case model.StateDeleteOnly, model.StateWriteOnly, model.StateWriteReorganization: + tbl = testGetTableByName(c, internalTK.Se, "test", "t") + if tbl == nil { + checkErr = errors.New("tbl is nil") + } else if len(tbl.(*tables.TableCommon).Columns) != 3 { + // changingCols has been added into meta. + checkErr = errors.New("len(cols) is not right") + } else if getModifyColumn(c, internalTK.Se.(sessionctx.Context), "test", "t", "c2", true).Flag&parser_mysql.PreventNullInsertFlag == uint(0) { + checkErr = errors.New("old col's flag is not right") + } else if getModifyColumn(c, internalTK.Se.(sessionctx.Context), "test", "t", "_Col$_c2_0", true) == nil { + checkErr = errors.New("changingCol is nil") + } + } + } + s.dom.DDL().(ddl.DDLForTest).SetHook(hook) + // Alter sql will modify column c2 to tinyint not null. + SQL := "alter table t modify column c2 tinyint not null" + tk.MustExec(SQL) + // Assert the checkErr in the job of every state. + c.Assert(checkErr, IsNil) + + // Check the col meta after the column type change. + tbl = testGetTableByName(c, tk.Se, "test", "t") + c.Assert(tbl, NotNil) + c.Assert(len(tbl.Cols()), Equals, 2) + col := getModifyColumn(c, tk.Se.(sessionctx.Context), "test", "t", "c2", false) + c.Assert(col, NotNil) + c.Assert(parser_mysql.HasNotNullFlag(col.Flag), Equals, true) + c.Assert(col.Flag&parser_mysql.NoDefaultValueFlag, Not(Equals), uint(0)) + c.Assert(col.Tp, Equals, parser_mysql.TypeTiny) + c.Assert(col.ChangeStateInfo, IsNil) + tk.MustQuery("select * from t").Check(testkit.Rows("1 1")) +} + +func (s *testColumnTypeChangeSuite) TestRollbackColumnTypeChangeBetweenInteger(c *C) { + tk := testkit.NewTestKit(c, s.store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t (c1 bigint, c2 bigint)") + tk.MustExec("insert into t(c1, c2) values (1, 1)") + // Enable column change variable. + tk.Se.GetSessionVars().EnableChangeColumnType = true + defer func() { + tk.Se.GetSessionVars().EnableChangeColumnType = false + }() + + tbl := testGetTableByName(c, tk.Se, "test", "t") + c.Assert(tbl, NotNil) + c.Assert(len(tbl.Cols()), Equals, 2) + c.Assert(getModifyColumn(c, tk.Se.(sessionctx.Context), "test", "t", "c2", false), NotNil) + + originalHook := s.dom.DDL().GetHook() + defer s.dom.DDL().(ddl.DDLForTest).SetHook(originalHook) + + hook := &ddl.TestDDLCallback{} + // Mock roll back at model.StateNone. + customizeHookRollbackAtState(hook, tbl, model.StateNone) + s.dom.DDL().(ddl.DDLForTest).SetHook(hook) + // Alter sql will modify column c2 to bigint not null. + SQL := "alter table t modify column c2 int not null" + _, err := tk.Exec(SQL) + c.Assert(err, NotNil) + c.Assert(err.Error(), Equals, "[ddl:1]MockRollingBackInCallBack-none") + assertRollBackedColUnchanged(c, tk) + + // Mock roll back at model.StateDeleteOnly. + customizeHookRollbackAtState(hook, tbl, model.StateDeleteOnly) + s.dom.DDL().(ddl.DDLForTest).SetHook(hook) + _, err = tk.Exec(SQL) + c.Assert(err, NotNil) + c.Assert(err.Error(), Equals, "[ddl:1]MockRollingBackInCallBack-delete only") + assertRollBackedColUnchanged(c, tk) + + // Mock roll back at model.StateWriteOnly. + customizeHookRollbackAtState(hook, tbl, model.StateWriteOnly) + s.dom.DDL().(ddl.DDLForTest).SetHook(hook) + _, err = tk.Exec(SQL) + c.Assert(err, NotNil) + c.Assert(err.Error(), Equals, "[ddl:1]MockRollingBackInCallBack-write only") + assertRollBackedColUnchanged(c, tk) + + // Mock roll back at model.StateWriteReorg. + customizeHookRollbackAtState(hook, tbl, model.StateWriteReorganization) + s.dom.DDL().(ddl.DDLForTest).SetHook(hook) + _, err = tk.Exec(SQL) + c.Assert(err, NotNil) + c.Assert(err.Error(), Equals, "[ddl:1]MockRollingBackInCallBack-write reorganization") + assertRollBackedColUnchanged(c, tk) +} + +func customizeHookRollbackAtState(hook *ddl.TestDDLCallback, tbl table.Table, state model.SchemaState) { + hook.OnJobRunBeforeExported = func(job *model.Job) { + if tbl.Meta().ID != job.TableID { + return + } + if job.SchemaState == state { + job.State = model.JobStateRollingback + job.Error = mockTerrorMap[state.String()] + } + } +} + +func assertRollBackedColUnchanged(c *C, tk *testkit.TestKit) { + tbl := testGetTableByName(c, tk.Se, "test", "t") + c.Assert(tbl, NotNil) + c.Assert(len(tbl.Cols()), Equals, 2) + col := getModifyColumn(c, tk.Se.(sessionctx.Context), "test", "t", "c2", false) + c.Assert(col, NotNil) + c.Assert(col.Flag, Equals, uint(0)) + c.Assert(col.Tp, Equals, parser_mysql.TypeLonglong) + c.Assert(col.ChangeStateInfo, IsNil) + tk.MustQuery("select * from t").Check(testkit.Rows("1 1")) +} + +var mockTerrorMap = make(map[string]*terror.Error) + +func init() { + // Since terror new action will cause data race with other test suite (getTerrorCode) in parallel, we init it all here. + mockTerrorMap[model.StateNone.String()] = dbterror.ClassDDL.New(1, "MockRollingBackInCallBack-"+model.StateNone.String()) + mockTerrorMap[model.StateDeleteOnly.String()] = dbterror.ClassDDL.New(1, "MockRollingBackInCallBack-"+model.StateDeleteOnly.String()) + mockTerrorMap[model.StateWriteOnly.String()] = dbterror.ClassDDL.New(1, "MockRollingBackInCallBack-"+model.StateWriteOnly.String()) + mockTerrorMap[model.StateWriteReorganization.String()] = dbterror.ClassDDL.New(1, "MockRollingBackInCallBack-"+model.StateWriteReorganization.String()) +} + +func (s *testColumnTypeChangeSuite) TestColumnTypeChangeFromIntegerToOthers(c *C) { + tk := testkit.NewTestKit(c, s.store) + tk.MustExec("use test") + // Enable column change variable. + tk.Se.GetSessionVars().EnableChangeColumnType = true + defer func() { + tk.Se.GetSessionVars().EnableChangeColumnType = false + }() + + prepare := func(tk *testkit.TestKit) { + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a tinyint, b smallint, c mediumint, d int, e bigint, f bigint)") + tk.MustExec("insert into t values(1, 11, 111, 1111, 11111, 111111)") + } + prepareForEnumSet := func(tk *testkit.TestKit) { + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a tinyint, b smallint, c mediumint, d int, e bigint)") + tk.MustExec("insert into t values(1, 1, 1, 11111, 11111)") + } + + // integer to string + prepare(tk) + tk.MustExec("alter table t modify a varchar(10)") + modifiedColumn := getModifyColumn(c, tk.Se, "test", "t", "a", false) + c.Assert(modifiedColumn, NotNil) + c.Assert(modifiedColumn.Tp, Equals, parser_mysql.TypeVarchar) + tk.MustQuery("select a from t").Check(testkit.Rows("1")) + + tk.MustExec("alter table t modify b char(10)") + modifiedColumn = getModifyColumn(c, tk.Se, "test", "t", "b", false) + c.Assert(modifiedColumn, NotNil) + c.Assert(modifiedColumn.Tp, Equals, parser_mysql.TypeString) + tk.MustQuery("select b from t").Check(testkit.Rows("11")) + + tk.MustExec("alter table t modify c binary(10)") + modifiedColumn = getModifyColumn(c, tk.Se, "test", "t", "c", false) + c.Assert(modifiedColumn, NotNil) + c.Assert(modifiedColumn.Tp, Equals, parser_mysql.TypeString) + tk.MustQuery("select c from t").Check(testkit.Rows("111\x00\x00\x00\x00\x00\x00\x00")) + + tk.MustExec("alter table t modify d varbinary(10)") + modifiedColumn = getModifyColumn(c, tk.Se, "test", "t", "d", false) + c.Assert(modifiedColumn, NotNil) + c.Assert(modifiedColumn.Tp, Equals, parser_mysql.TypeVarchar) + tk.MustQuery("select d from t").Check(testkit.Rows("1111")) + + tk.MustExec("alter table t modify e blob(10)") + modifiedColumn = getModifyColumn(c, tk.Se, "test", "t", "e", false) + c.Assert(modifiedColumn, NotNil) + c.Assert(modifiedColumn.Tp, Equals, parser_mysql.TypeBlob) + tk.MustQuery("select e from t").Check(testkit.Rows("11111")) + + tk.MustExec("alter table t modify f text(10)") + modifiedColumn = getModifyColumn(c, tk.Se, "test", "t", "f", false) + c.Assert(modifiedColumn, NotNil) + c.Assert(modifiedColumn.Tp, Equals, parser_mysql.TypeBlob) + tk.MustQuery("select f from t").Check(testkit.Rows("111111")) + + // integer to decimal + prepare(tk) + tk.MustExec("alter table t modify a decimal(2,1)") + modifiedColumn = getModifyColumn(c, tk.Se, "test", "t", "a", false) + c.Assert(modifiedColumn, NotNil) + c.Assert(modifiedColumn.Tp, Equals, parser_mysql.TypeNewDecimal) + tk.MustQuery("select a from t").Check(testkit.Rows("1.0")) + + // integer to year + // For year(2), MySQL converts values in the ranges '0' to '69' and '70' to '99' to YEAR values in the ranges 2000 to 2069 and 1970 to 1999. + tk.MustExec("alter table t modify b year") + modifiedColumn = getModifyColumn(c, tk.Se, "test", "t", "b", false) + c.Assert(modifiedColumn, NotNil) + c.Assert(modifiedColumn.Tp, Equals, parser_mysql.TypeYear) + tk.MustQuery("select b from t").Check(testkit.Rows("2011")) + + // integer to time + tk.MustExec("alter table t modify c time") + modifiedColumn = getModifyColumn(c, tk.Se, "test", "t", "c", false) + c.Assert(modifiedColumn, NotNil) + c.Assert(modifiedColumn.Tp, Equals, parser_mysql.TypeDuration) // mysql.TypeTime has rename to TypeDuration. + tk.MustQuery("select c from t").Check(testkit.Rows("00:01:11")) + + // integer to date (mysql will throw `Incorrect date value: '1111' for column 'd' at row 1` error) + tk.MustExec("alter table t modify d date") + modifiedColumn = getModifyColumn(c, tk.Se, "test", "t", "d", false) + c.Assert(modifiedColumn, NotNil) + c.Assert(modifiedColumn.Tp, Equals, parser_mysql.TypeDate) + tk.MustQuery("select d from t").Check(testkit.Rows("2000-11-11")) // the given number will be left-forward used. + + // integer to timestamp (according to what timezone you have set) + tk.MustExec("alter table t modify e timestamp") + tk.MustExec("set @@session.time_zone=UTC") + modifiedColumn = getModifyColumn(c, tk.Se, "test", "t", "e", false) + c.Assert(modifiedColumn, NotNil) + c.Assert(modifiedColumn.Tp, Equals, parser_mysql.TypeTimestamp) + tk.MustQuery("select e from t").Check(testkit.Rows("2001-11-11 00:00:00")) // the given number will be left-forward used. + + // integer to datetime + tk.MustExec("alter table t modify f datetime") + modifiedColumn = getModifyColumn(c, tk.Se, "test", "t", "f", false) + c.Assert(modifiedColumn, NotNil) + c.Assert(modifiedColumn.Tp, Equals, parser_mysql.TypeDatetime) + tk.MustQuery("select f from t").Check(testkit.Rows("2011-11-11 00:00:00")) // the given number will be left-forward used. + + // integer to floating-point values + prepare(tk) + tk.MustExec("alter table t modify a float") + modifiedColumn = getModifyColumn(c, tk.Se, "test", "t", "a", false) + c.Assert(modifiedColumn, NotNil) + c.Assert(modifiedColumn.Tp, Equals, parser_mysql.TypeFloat) + tk.MustQuery("select a from t").Check(testkit.Rows("1")) + + tk.MustExec("alter table t modify b double") + modifiedColumn = getModifyColumn(c, tk.Se, "test", "t", "b", false) + c.Assert(modifiedColumn, NotNil) + c.Assert(modifiedColumn.Tp, Equals, parser_mysql.TypeDouble) + tk.MustQuery("select b from t").Check(testkit.Rows("11")) + + // integer to bit + tk.MustExec("alter table t modify c bit(10)") + modifiedColumn = getModifyColumn(c, tk.Se, "test", "t", "c", false) + c.Assert(modifiedColumn, NotNil) + c.Assert(modifiedColumn.Tp, Equals, parser_mysql.TypeBit) + // 111 will be stored ad 0x00,0110,1111 = 0x6F, which will be shown as ASCII('o')=111 as well. + tk.MustQuery("select c from t").Check(testkit.Rows("\x00o")) + + // integer to json + tk.MustExec("alter table t modify d json") + modifiedColumn = getModifyColumn(c, tk.Se, "test", "t", "d", false) + c.Assert(modifiedColumn, NotNil) + c.Assert(modifiedColumn.Tp, Equals, parser_mysql.TypeJSON) + tk.MustQuery("select d from t").Check(testkit.Rows("1111")) + + // integer to enum + prepareForEnumSet(tk) + // TiDB take integer as the enum element offset to cast. + tk.MustExec("alter table t modify a enum(\"a\", \"b\")") + modifiedColumn = getModifyColumn(c, tk.Se, "test", "t", "a", false) + c.Assert(modifiedColumn, NotNil) + c.Assert(modifiedColumn.Tp, Equals, parser_mysql.TypeEnum) + tk.MustQuery("select a from t").Check(testkit.Rows("a")) + + // TiDB take integer as the set element offset to cast. + tk.MustExec("alter table t modify b set(\"a\", \"b\")") + modifiedColumn = getModifyColumn(c, tk.Se, "test", "t", "b", false) + c.Assert(modifiedColumn, NotNil) + c.Assert(modifiedColumn.Tp, Equals, parser_mysql.TypeSet) + tk.MustQuery("select b from t").Check(testkit.Rows("a")) + + // TiDB can't take integer as the enum element string to cast, while the MySQL can. + tk.MustGetErrCode("alter table t modify d enum(\"11111\", \"22222\")", mysql.WarnDataTruncated) + + // TiDB can't take integer as the set element string to cast, while the MySQL can. + tk.MustGetErrCode("alter table t modify e set(\"11111\", \"22222\")", mysql.WarnDataTruncated) +} + +func (s *testColumnTypeChangeSuite) TestColumnTypeChangeFromStringToOthers(c *C) { + tk := testkit.NewTestKit(c, s.store) + tk.MustExec("use test") + // Enable column change variable. + tk.Se.GetSessionVars().EnableChangeColumnType = true + + // Set time zone to UTC. + originalTz := tk.Se.GetSessionVars().TimeZone + tk.Se.GetSessionVars().TimeZone = time.UTC + defer func() { + tk.Se.GetSessionVars().EnableChangeColumnType = false + tk.Se.GetSessionVars().TimeZone = originalTz + }() + + // Init string date type table. + reset := func(tk *testkit.TestKit) { + tk.MustExec("drop table if exists t") + tk.MustExec(` + create table t ( + c char(8), + vc varchar(8), + bny binary(8), + vbny varbinary(8), + bb blob, + txt text, + e enum('123', '2020-07-15 18:32:17.888', 'str', '{"k1": "value"}'), + s set('123', '2020-07-15 18:32:17.888', 'str', '{"k1": "value"}') + ) + `) + } + + // To numeric data types. + // tinyint + reset(tk) + tk.MustExec("insert into t values ('123', '123', '123', '123', '123', '123', '123', '123')") + tk.MustExec("alter table t modify c tinyint") + tk.MustExec("alter table t modify vc tinyint") + tk.MustExec("alter table t modify bny tinyint") + tk.MustExec("alter table t modify vbny tinyint") + tk.MustExec("alter table t modify bb tinyint") + tk.MustExec("alter table t modify txt tinyint") + tk.MustExec("alter table t modify e tinyint") + tk.MustExec("alter table t modify s tinyint") + tk.MustQuery("select * from t").Check(testkit.Rows("123 123 123 123 123 123 1 1")) + // int + reset(tk) + tk.MustExec("insert into t values ('17305', '17305', '17305', '17305', '17305', '17305', '123', '123')") + tk.MustExec("alter table t modify c int") + tk.MustExec("alter table t modify vc int") + tk.MustExec("alter table t modify bny int") + tk.MustExec("alter table t modify vbny int") + tk.MustExec("alter table t modify bb int") + tk.MustExec("alter table t modify txt int") + tk.MustExec("alter table t modify e int") + tk.MustExec("alter table t modify s int") + tk.MustQuery("select * from t").Check(testkit.Rows("17305 17305 17305 17305 17305 17305 1 1")) + // bigint + reset(tk) + tk.MustExec("insert into t values ('17305867', '17305867', '17305867', '17305867', '17305867', '17305867', '123', '123')") + tk.MustExec("alter table t modify c bigint") + tk.MustExec("alter table t modify vc bigint") + tk.MustExec("alter table t modify bny bigint") + tk.MustExec("alter table t modify vbny bigint") + tk.MustExec("alter table t modify bb bigint") + tk.MustExec("alter table t modify txt bigint") + tk.MustExec("alter table t modify e bigint") + tk.MustExec("alter table t modify s bigint") + tk.MustQuery("select * from t").Check(testkit.Rows("17305867 17305867 17305867 17305867 17305867 17305867 1 1")) + // bit + reset(tk) + tk.MustExec("insert into t values ('1', '1', '1', '1', '1', '1', '123', '123')") + tk.MustGetErrCode("alter table t modify c bit", mysql.ErrUnsupportedDDLOperation) + tk.MustGetErrCode("alter table t modify vc bit", mysql.ErrUnsupportedDDLOperation) + tk.MustGetErrCode("alter table t modify bny bit", mysql.ErrUnsupportedDDLOperation) + tk.MustGetErrCode("alter table t modify vbny bit", mysql.ErrUnsupportedDDLOperation) + tk.MustGetErrCode("alter table t modify bb bit", mysql.ErrUnsupportedDDLOperation) + tk.MustGetErrCode("alter table t modify txt bit", mysql.ErrUnsupportedDDLOperation) + tk.MustExec("alter table t modify e bit") + tk.MustExec("alter table t modify s bit") + tk.MustQuery("select * from t").Check(testkit.Rows("1 1 1\x00\x00\x00\x00\x00\x00\x00 1 1 1 \x01 \x01")) + // decimal + reset(tk) + tk.MustExec("insert into t values ('123.45', '123.45', '123.45', '123.45', '123.45', '123.45', '123', '123')") + tk.MustExec("alter table t modify c decimal(7, 4)") + tk.MustExec("alter table t modify vc decimal(7, 4)") + tk.MustExec("alter table t modify bny decimal(7, 4)") + tk.MustExec("alter table t modify vbny decimal(7, 4)") + tk.MustExec("alter table t modify bb decimal(7, 4)") + tk.MustExec("alter table t modify txt decimal(7, 4)") + tk.MustExec("alter table t modify e decimal(7, 4)") + tk.MustExec("alter table t modify s decimal(7, 4)") + tk.MustQuery("select * from t").Check(testkit.Rows("123.4500 123.4500 123.4500 123.4500 123.4500 123.4500 1.0000 1.0000")) + // double + reset(tk) + tk.MustExec("insert into t values ('123.45', '123.45', '123.45', '123.45', '123.45', '123.45', '123', '123')") + tk.MustExec("alter table t modify c double(7, 4)") + tk.MustExec("alter table t modify vc double(7, 4)") + tk.MustExec("alter table t modify bny double(7, 4)") + tk.MustExec("alter table t modify vbny double(7, 4)") + tk.MustExec("alter table t modify bb double(7, 4)") + tk.MustExec("alter table t modify txt double(7, 4)") + tk.MustExec("alter table t modify e double(7, 4)") + tk.MustExec("alter table t modify s double(7, 4)") + tk.MustQuery("select * from t").Check(testkit.Rows("123.45 123.45 123.45 123.45 123.45 123.45 1 1")) + + // To date and time data types. + // date + reset(tk) + tk.MustExec("insert into t values ('20200826', '2008261', '20200826', '200826', '2020-08-26', '08-26 19:35:41', '2020-07-15 18:32:17.888', '2020-07-15 18:32:17.888')") + tk.MustExec("alter table t modify c date") + tk.MustExec("alter table t modify vc date") + tk.MustExec("alter table t modify bny date") + tk.MustExec("alter table t modify vbny date") + tk.MustExec("alter table t modify bb date") + // Alter text '08-26 19:35:41' to date will error. (same as mysql does) + tk.MustGetErrCode("alter table t modify txt date", mysql.ErrTruncatedWrongValue) + tk.MustGetErrCode("alter table t modify e date", mysql.ErrUnsupportedDDLOperation) + tk.MustGetErrCode("alter table t modify s date", mysql.ErrUnsupportedDDLOperation) + tk.MustQuery("select * from t").Check(testkit.Rows("2020-08-26 2020-08-26 2020-08-26 2020-08-26 2020-08-26 08-26 19:35:41 2020-07-15 18:32:17.888 2020-07-15 18:32:17.888")) + // time + reset(tk) + tk.MustExec("insert into t values ('19:35:41', '19:35:41', '19:35:41', '19:35:41', '19:35:41.45678', '19:35:41.45678', '2020-07-15 18:32:17.888', '2020-07-15 18:32:17.888')") + tk.MustExec("alter table t modify c time") + tk.MustExec("alter table t modify vc time") + tk.MustExec("alter table t modify bny time") + tk.MustExec("alter table t modify vbny time") + tk.MustExec("alter table t modify bb time") + tk.MustExec("alter table t modify txt time") + tk.MustGetErrCode("alter table t modify e time", mysql.ErrUnsupportedDDLOperation) + tk.MustGetErrCode("alter table t modify s time", mysql.ErrUnsupportedDDLOperation) + tk.MustQuery("select * from t").Check(testkit.Rows("19:35:41 19:35:41 19:35:41 19:35:41 19:35:41 19:35:41 2020-07-15 18:32:17.888 2020-07-15 18:32:17.888")) + // datetime + reset(tk) + tk.MustExec("alter table t modify c char(23)") + tk.MustExec("alter table t modify vc varchar(23)") + tk.MustExec("alter table t modify bny binary(23)") + tk.MustExec("alter table t modify vbny varbinary(23)") + tk.MustExec("insert into t values ('2020-07-15 18:32:17.888', '2020-07-15 18:32:17.888', '2020-07-15 18:32:17.888', '2020-07-15 18:32:17.888', '2020-07-15 18:32:17.888', '2020-07-15 18:32:17.888', '2020-07-15 18:32:17.888', '2020-07-15 18:32:17.888')") + tk.MustExec("alter table t modify c datetime") + tk.MustExec("alter table t modify vc datetime") + tk.MustExec("alter table t modify bny datetime") + tk.MustExec("alter table t modify vbny datetime") + tk.MustExec("alter table t modify bb datetime") + tk.MustExec("alter table t modify txt datetime") + tk.MustGetErrCode("alter table t modify e datetime", mysql.ErrUnsupportedDDLOperation) + tk.MustGetErrCode("alter table t modify s datetime", mysql.ErrUnsupportedDDLOperation) + tk.MustQuery("select * from t").Check(testkit.Rows("2020-07-15 18:32:18 2020-07-15 18:32:18 2020-07-15 18:32:18 2020-07-15 18:32:18 2020-07-15 18:32:18 2020-07-15 18:32:18 2020-07-15 18:32:17.888 2020-07-15 18:32:17.888")) + // timestamp + reset(tk) + tk.MustExec("alter table t modify c char(23)") + tk.MustExec("alter table t modify vc varchar(23)") + tk.MustExec("alter table t modify bny binary(23)") + tk.MustExec("alter table t modify vbny varbinary(23)") + tk.MustExec("insert into t values ('2020-07-15 18:32:17.888', '2020-07-15 18:32:17.888', '2020-07-15 18:32:17.888', '2020-07-15 18:32:17.888', '2020-07-15 18:32:17.888', '2020-07-15 18:32:17.888', '2020-07-15 18:32:17.888', '2020-07-15 18:32:17.888')") + tk.MustExec("alter table t modify c timestamp") + tk.MustExec("alter table t modify vc timestamp") + tk.MustExec("alter table t modify bny timestamp") + tk.MustExec("alter table t modify vbny timestamp") + tk.MustExec("alter table t modify bb timestamp") + tk.MustExec("alter table t modify txt timestamp") + tk.MustGetErrCode("alter table t modify e timestamp", mysql.ErrUnsupportedDDLOperation) + tk.MustGetErrCode("alter table t modify s timestamp", mysql.ErrUnsupportedDDLOperation) + tk.MustQuery("select * from t").Check(testkit.Rows("2020-07-15 18:32:18 2020-07-15 18:32:18 2020-07-15 18:32:18 2020-07-15 18:32:18 2020-07-15 18:32:18 2020-07-15 18:32:18 2020-07-15 18:32:17.888 2020-07-15 18:32:17.888")) + // year + reset(tk) + tk.MustExec("insert into t values ('2020', '91', '2', '2020', '20', '99', '2020-07-15 18:32:17.888', '2020-07-15 18:32:17.888')") + tk.MustExec("alter table t modify c year") + tk.MustExec("alter table t modify vc year") + tk.MustExec("alter table t modify bny year") + tk.MustExec("alter table t modify vbny year") + tk.MustExec("alter table t modify bb year") + tk.MustExec("alter table t modify txt year") + tk.MustExec("alter table t modify e year") + tk.MustExec("alter table t modify s year") + tk.MustQuery("select * from t").Check(testkit.Rows("2020 1991 2002 2020 2020 1999 2002 2002")) + + // To json data type. + reset(tk) + tk.MustExec("alter table t modify c char(15)") + tk.MustExec("alter table t modify vc varchar(15)") + tk.MustExec("alter table t modify bny binary(15)") + tk.MustExec("alter table t modify vbny varbinary(15)") + tk.MustExec("insert into t values ('{\"k1\": \"value\"}', '{\"k1\": \"value\"}', '{\"k1\": \"value\"}', '{\"k1\": \"value\"}', '{\"k1\": \"value\"}', '{\"k1\": \"value\"}', '{\"k1\": \"value\"}', '{\"k1\": \"value\"}')") + tk.MustExec("alter table t modify c json") + tk.MustExec("alter table t modify vc json") + tk.MustExec("alter table t modify bny json") + tk.MustExec("alter table t modify vbny json") + tk.MustExec("alter table t modify bb json") + tk.MustExec("alter table t modify txt json") + tk.MustExec("alter table t modify e json") + tk.MustExec("alter table t modify s json") + tk.MustQuery("select * from t").Check(testkit.Rows("{\"k1\": \"value\"} {\"k1\": \"value\"} {\"k1\": \"value\"} {\"k1\": \"value\"} {\"k1\": \"value\"} {\"k1\": \"value\"} \"{\\\"k1\\\": \\\"value\\\"}\" \"{\\\"k1\\\": \\\"value\\\"}\"")) + + reset(tk) + tk.MustExec("insert into t values ('123x', 'x123', 'abc', 'datetime', 'timestamp', 'date', '123', '123')") + tk.MustGetErrCode("alter table t modify c int", mysql.ErrTruncatedWrongValue) + + tk.MustGetErrCode("alter table t modify vc smallint", mysql.ErrTruncatedWrongValue) + + tk.MustGetErrCode("alter table t modify bny bigint", mysql.ErrTruncatedWrongValue) + + tk.MustGetErrCode("alter table t modify vbny datetime", mysql.ErrTruncatedWrongValue) + + tk.MustGetErrCode("alter table t modify bb timestamp", mysql.ErrTruncatedWrongValue) + + tk.MustGetErrCode("alter table t modify txt date", mysql.ErrTruncatedWrongValue) + + reset(tk) + tk.MustExec("alter table t modify vc varchar(20)") + tk.MustExec("insert into t(c, vc) values ('1x', '20200915110836')") + tk.MustGetErrCode("alter table t modify c year", mysql.ErrTruncatedWrongValue) + + // Special cases about different behavior between TiDB and MySQL. + // MySQL will get warning but TiDB not. + // MySQL will get "Warning 1292 Incorrect time value: '20200915110836' for column 'vc'" + tk.MustExec("alter table t modify vc time") + tk.MustQuery("select vc from t").Check(testkit.Rows("11:08:36")) + + // Both error but different error message. + // MySQL will get "ERROR 3140 (22032): Invalid JSON text: "The document root must not be followed by other values." at position 1 in value for column '#sql-5b_42.c'." error. + reset(tk) + tk.MustExec("alter table t modify c char(15)") + tk.MustExec("insert into t(c) values ('{\"k1\": \"value\"')") + tk.MustGetErrCode("alter table t modify c json", mysql.ErrInvalidJSONText) + + // MySQL will get "ERROR 1366 (HY000): Incorrect DECIMAL value: '0' for column '' at row -1" error. + tk.MustExec("insert into t(vc) values ('abc')") + tk.MustGetErrCode("alter table t modify vc decimal(5,3)", mysql.ErrBadNumber) +} + +func (s *testColumnTypeChangeSuite) TestColumnTypeChangeFromNumericToOthers(c *C) { + tk := testkit.NewTestKit(c, s.store) + tk.MustExec("use test") + // Enable column change variable. + tk.Se.GetSessionVars().EnableChangeColumnType = true + + // Set time zone to UTC. + originalTz := tk.Se.GetSessionVars().TimeZone + tk.Se.GetSessionVars().TimeZone = time.UTC + defer func() { + tk.Se.GetSessionVars().EnableChangeColumnType = false + tk.Se.GetSessionVars().TimeZone = originalTz + }() + + // Init string date type table. + reset := func(tk *testkit.TestKit) { + tk.MustExec("drop table if exists t") + tk.MustExec(` + create table t ( + d decimal(13, 7), + n numeric(5, 2), + r real(20, 12), + db real(32, 11), + f32 float(23), + f64 double(46), + b bit(5) + ) + `) + } + + // To integer data types. + // tinyint + reset(tk) + tk.MustExec("insert into t values (-258.12345, 333.33, 2000000.20000002, 323232323.3232323232, -111.11111111, -222222222222.222222222222222, b'10101')") + tk.MustGetErrCode("alter table t modify d tinyint", mysql.ErrDataOutOfRange) + tk.MustGetErrCode("alter table t modify n tinyint", mysql.ErrDataOutOfRange) + tk.MustGetErrCode("alter table t modify r tinyint", mysql.ErrDataOutOfRange) + tk.MustGetErrCode("alter table t modify db tinyint", mysql.ErrDataOutOfRange) + tk.MustExec("alter table t modify f32 tinyint") + tk.MustGetErrCode("alter table t modify f64 tinyint", mysql.ErrDataOutOfRange) + tk.MustExec("alter table t modify b tinyint") + tk.MustQuery("select * from t").Check(testkit.Rows("-258.1234500 333.33 2000000.20000002 323232323.32323235 -111 -222222222222.22223 21")) + // int + reset(tk) + tk.MustExec("insert into t values (-258.12345, 333.33, 2000000.20000002, 323232323.3232323232, -111.11111111, -222222222222.222222222222222, b'10101')") + tk.MustExec("alter table t modify d int") + tk.MustExec("alter table t modify n int") + tk.MustExec("alter table t modify r int") + tk.MustExec("alter table t modify db int") + tk.MustExec("alter table t modify f32 int") + tk.MustGetErrCode("alter table t modify f64 int", mysql.ErrDataOutOfRange) + tk.MustExec("alter table t modify b int") + tk.MustQuery("select * from t").Check(testkit.Rows("-258 333 2000000 323232323 -111 -222222222222.22223 21")) + // bigint + reset(tk) + tk.MustExec("insert into t values (-258.12345, 333.33, 2000000.20000002, 323232323.3232323232, -111.11111111, -222222222222.222222222222222, b'10101')") + tk.MustExec("alter table t modify d bigint") + tk.MustExec("alter table t modify n bigint") + tk.MustExec("alter table t modify r bigint") + tk.MustExec("alter table t modify db bigint") + tk.MustExec("alter table t modify f32 bigint") + tk.MustExec("alter table t modify f64 bigint") + tk.MustExec("alter table t modify b bigint") + tk.MustQuery("select * from t").Check(testkit.Rows("-258 333 2000000 323232323 -111 -222222222222 21")) + // unsigned bigint + reset(tk) + tk.MustExec("insert into t values (-258.12345, 333.33, 2000000.20000002, 323232323.3232323232, -111.11111111, -222222222222.222222222222222, b'10101')") + // MySQL will get "ERROR 1264 (22001): Data truncation: Out of range value for column 'd' at row 1". + tk.MustGetErrCode("alter table t modify d bigint unsigned", mysql.ErrDataOutOfRange) + tk.MustExec("alter table t modify n bigint unsigned") + tk.MustExec("alter table t modify r bigint unsigned") + tk.MustExec("alter table t modify db bigint unsigned") + // MySQL will get "ERROR 1264 (22001): Data truncation: Out of range value for column 'f32' at row 1". + tk.MustGetErrCode("alter table t modify f32 bigint unsigned", mysql.ErrDataOutOfRange) + // MySQL will get "ERROR 1264 (22001): Data truncation: Out of range value for column 'f64' at row 1". + tk.MustGetErrCode("alter table t modify f64 bigint unsigned", mysql.ErrDataOutOfRange) + tk.MustExec("alter table t modify b int") + tk.MustQuery("select * from t").Check(testkit.Rows("-258.1234500 333 2000000 323232323 -111.111115 -222222222222.22223 21")) + + // To string data types. + // char + reset(tk) + tk.MustExec("insert into t values (-258.12345, 333.33, 2000000.20000002, 323232323.3232323232, -111.11111111, -222222222222.222222222222222, b'10101')") + tk.MustExec("alter table t modify d char(20)") + tk.MustExec("alter table t modify n char(20)") + tk.MustExec("alter table t modify r char(20)") + // MySQL will get "ERROR 1406 (22001): Data truncation: Data too long for column 'db' at row 1". + tk.MustExec("alter table t modify db char(20)") + // MySQL will get "-111.111" rather than "-111.111115" at TiDB. + tk.MustExec("alter table t modify f32 char(20)") + // MySQL will get "ERROR 1406 (22001): Data truncation: Data too long for column 'f64' at row 1". + tk.MustExec("alter table t modify f64 char(20)") + tk.MustExec("alter table t modify b char(20)") + tk.MustQuery("select * from t").Check(testkit.Rows("-258.1234500 333.33 2000000.20000002 323232323.32323235 -111.111115 -222222222222.22223 \x15")) + + // varchar + reset(tk) + tk.MustExec("insert into t values (-258.12345, 333.33, 2000000.20000002, 323232323.3232323232, -111.11111111, -222222222222.222222222222222, b'10101')") + tk.MustExec("alter table t modify d varchar(30)") + tk.MustExec("alter table t modify n varchar(30)") + tk.MustExec("alter table t modify r varchar(30)") + tk.MustExec("alter table t modify db varchar(30)") + // MySQL will get "-111.111" rather than "-111.111115" at TiDB. + tk.MustExec("alter table t modify f32 varchar(30)") + // MySQL will get "ERROR 1406 (22001): Data truncation: Data too long for column 'f64' at row 1". + tk.MustExec("alter table t modify f64 varchar(30)") + tk.MustExec("alter table t modify b varchar(30)") + tk.MustQuery("select * from t").Check(testkit.Rows("-258.1234500 333.33 2000000.20000002 323232323.32323235 -111.111115 -222222222222.22223 \x15")) + + // binary + reset(tk) + tk.MustExec("insert into t values (-258.12345, 333.33, 2000000.20000002, 323232323.3232323232, -111.11111111, -222222222222.222222222222222, b'10101')") + tk.MustGetErrCode("alter table t modify d binary(10)", mysql.ErrDataTooLong) + tk.MustExec("alter table t modify n binary(10)") + tk.MustGetErrCode("alter table t modify r binary(10)", mysql.ErrDataTooLong) + tk.MustGetErrCode("alter table t modify db binary(10)", mysql.ErrDataTooLong) + // MySQL will run with no error. + tk.MustGetErrCode("alter table t modify f32 binary(10)", mysql.ErrDataTooLong) + tk.MustGetErrCode("alter table t modify f64 binary(10)", mysql.ErrDataTooLong) + tk.MustExec("alter table t modify b binary(10)") + tk.MustQuery("select * from t").Check(testkit.Rows("-258.1234500 333.33\x00\x00\x00\x00 2000000.20000002 323232323.32323235 -111.111115 -222222222222.22223 \x15\x00\x00\x00\x00\x00\x00\x00\x00\x00")) + + // varbinary + reset(tk) + tk.MustExec("insert into t values (-258.12345, 333.33, 2000000.20000002, 323232323.3232323232, -111.11111111, -222222222222.222222222222222, b'10101')") + tk.MustExec("alter table t modify d varbinary(30)") + tk.MustExec("alter table t modify n varbinary(30)") + tk.MustExec("alter table t modify r varbinary(30)") + tk.MustExec("alter table t modify db varbinary(30)") + // MySQL will get "-111.111" rather than "-111.111115" at TiDB. + tk.MustExec("alter table t modify f32 varbinary(30)") + // MySQL will get "ERROR 1406 (22001): Data truncation: Data too long for column 'f64' at row 1". + tk.MustExec("alter table t modify f64 varbinary(30)") + tk.MustExec("alter table t modify b varbinary(30)") + tk.MustQuery("select * from t").Check(testkit.Rows("-258.1234500 333.33 2000000.20000002 323232323.32323235 -111.111115 -222222222222.22223 \x15")) + + // blob + reset(tk) + tk.MustExec("insert into t values (-258.12345, 333.33, 2000000.20000002, 323232323.3232323232, -111.11111111, -222222222222.222222222222222, b'10101')") + tk.MustExec("alter table t modify d blob") + tk.MustExec("alter table t modify n blob") + tk.MustExec("alter table t modify r blob") + tk.MustExec("alter table t modify db blob") + // MySQL will get "-111.111" rather than "-111.111115" at TiDB. + tk.MustExec("alter table t modify f32 blob") + tk.MustExec("alter table t modify f64 blob") + tk.MustExec("alter table t modify b blob") + tk.MustQuery("select * from t").Check(testkit.Rows("-258.1234500 333.33 2000000.20000002 323232323.32323235 -111.111115 -222222222222.22223 \x15")) + + // text + reset(tk) + tk.MustExec("insert into t values (-258.12345, 333.33, 2000000.20000002, 323232323.3232323232, -111.11111111, -222222222222.222222222222222, b'10101')") + tk.MustExec("alter table t modify d text") + tk.MustExec("alter table t modify n text") + tk.MustExec("alter table t modify r text") + tk.MustExec("alter table t modify db text") + // MySQL will get "-111.111" rather than "-111.111115" at TiDB. + tk.MustExec("alter table t modify f32 text") + tk.MustExec("alter table t modify f64 text") + tk.MustExec("alter table t modify b text") + tk.MustQuery("select * from t").Check(testkit.Rows("-258.1234500 333.33 2000000.20000002 323232323.32323235 -111.111115 -222222222222.22223 \x15")) + + // enum + reset(tk) + tk.MustExec("insert into t values (-258.12345, 333.33, 2000000.20000002, 323232323.3232323232, -111.111, -222222222222.222222222222222, b'10101')") + tk.MustGetErrCode("alter table t modify d enum('-258.12345', '333.33', '2000000.20000002', '323232323.3232323232', '-111.111', '-222222222222.222222222222222', b'10101')", mysql.ErrUnsupportedDDLOperation) + tk.MustGetErrCode("alter table t modify n enum('-258.12345', '333.33', '2000000.20000002', '323232323.3232323232', '-111.111', '-222222222222.222222222222222', b'10101')", mysql.ErrUnsupportedDDLOperation) + tk.MustGetErrCode("alter table t modify r enum('-258.12345', '333.33', '2000000.20000002', '323232323.3232323232', '-111.111', '-222222222222.222222222222222', b'10101')", mysql.ErrUnsupportedDDLOperation) + tk.MustGetErrCode("alter table t modify db enum('-258.12345', '333.33', '2000000.20000002', '323232323.3232323232', '-111.111', '-222222222222.222222222222222', b'10101')", mysql.ErrUnsupportedDDLOperation) + tk.MustGetErrCode("alter table t modify f32 enum('-258.12345', '333.33', '2000000.20000002', '323232323.3232323232', '-111.111', '-222222222222.222222222222222', b'10101')", mysql.ErrUnsupportedDDLOperation) + tk.MustGetErrCode("alter table t modify f64 enum('-258.12345', '333.33', '2000000.20000002', '323232323.3232323232', '-111.111', '-222222222222.222222222222222', b'10101')", mysql.ErrUnsupportedDDLOperation) + tk.MustGetErrCode("alter table t modify b enum('-258.12345', '333.33', '2000000.20000002', '323232323.3232323232', '-111.111', '-222222222222.222222222222222', b'10101')", mysql.ErrUnsupportedDDLOperation) + tk.MustQuery("select * from t").Check(testkit.Rows("-258.1234500 333.33 2000000.20000002 323232323.32323235 -111.111 -222222222222.22223 \x15")) + + // set + reset(tk) + tk.MustExec("insert into t values (-258.12345, 333.33, 2000000.20000002, 323232323.3232323232, -111.111, -222222222222.222222222222222, b'10101')") + tk.MustGetErrCode("alter table t modify d set('-258.12345', '333.33', '2000000.20000002', '323232323.3232323232', '-111.111', '-222222222222.222222222222222', b'10101')", mysql.ErrUnsupportedDDLOperation) + tk.MustGetErrCode("alter table t modify n set('-258.12345', '333.33', '2000000.20000002', '323232323.3232323232', '-111.111', '-222222222222.222222222222222', b'10101')", mysql.ErrUnsupportedDDLOperation) + tk.MustGetErrCode("alter table t modify r set('-258.12345', '333.33', '2000000.20000002', '323232323.3232323232', '-111.111', '-222222222222.222222222222222', b'10101')", mysql.ErrUnsupportedDDLOperation) + tk.MustGetErrCode("alter table t modify db set('-258.12345', '333.33', '2000000.20000002', '323232323.3232323232', '-111.111', '-222222222222.222222222222222', b'10101')", mysql.ErrUnsupportedDDLOperation) + tk.MustGetErrCode("alter table t modify f32 set('-258.12345', '333.33', '2000000.20000002', '323232323.3232323232', '-111.111', '-222222222222.222222222222222', b'10101')", mysql.ErrUnsupportedDDLOperation) + tk.MustGetErrCode("alter table t modify f64 set('-258.12345', '333.33', '2000000.20000002', '323232323.3232323232', '-111.111', '-222222222222.222222222222222', b'10101')", mysql.ErrUnsupportedDDLOperation) + tk.MustGetErrCode("alter table t modify b set('-258.12345', '333.33', '2000000.20000002', '323232323.3232323232', '-111.111', '-222222222222.222222222222222', b'10101')", mysql.ErrUnsupportedDDLOperation) + tk.MustQuery("select * from t").Check(testkit.Rows("-258.1234500 333.33 2000000.20000002 323232323.32323235 -111.111 -222222222222.22223 \x15")) + + // To date and time data types. + // datetime + reset(tk) + tk.MustExec("insert into t values (200805.11, 307.333, 20200805.11111111, 20200805111307.11111111, 200805111307.11111111, 20200805111307.11111111, b'10101')") + // MySQL will get "ERROR 1292 (22001) Data truncation: Incorrect datetime value: '200805.1100000' for column 'd' at row 1". + tk.MustExec("alter table t modify d datetime") + // MySQL will get "ERROR 1292 (22001) Data truncation: Incorrect datetime value: '307.33' for column 'n' at row 1". + tk.MustExec("alter table t modify n datetime") + tk.MustGetErrCode("alter table t modify r datetime", mysql.ErrUnsupportedDDLOperation) + tk.MustGetErrCode("alter table t modify db datetime", mysql.ErrUnsupportedDDLOperation) + tk.MustGetErrCode("alter table t modify f32 datetime", mysql.ErrUnsupportedDDLOperation) + tk.MustGetErrCode("alter table t modify f64 datetime", mysql.ErrUnsupportedDDLOperation) + tk.MustGetErrCode("alter table t modify b datetime", mysql.ErrUnsupportedDDLOperation) + tk.MustQuery("select * from t").Check(testkit.Rows("2020-08-05 00:00:00 2000-03-07 00:00:00 20200805.11111111 20200805111307.11 200805100000 20200805111307.11 \x15")) + // time + reset(tk) + tk.MustExec("insert into t values (200805.11, 307.333, 20200805.11111111, 20200805111307.11111111, 200805111307.11111111, 20200805111307.11111111, b'10101')") + tk.MustExec("alter table t modify d time") + tk.MustExec("alter table t modify n time") + tk.MustGetErrCode("alter table t modify r time", mysql.ErrTruncatedWrongValue) + tk.MustExec("alter table t modify db time") + tk.MustExec("alter table t modify f32 time") + tk.MustExec("alter table t modify f64 time") + tk.MustGetErrCode("alter table t modify b time", mysql.ErrUnsupportedDDLOperation) + tk.MustQuery("select * from t").Check(testkit.Rows("20:08:05 00:03:07 20200805.11111111 11:13:07 10:00:00 11:13:07 \x15")) + // date + reset(tk) + tk.MustExec("insert into t values (200805.11, 307.333, 20200805.11111111, 20200805111307.11111111, 200805111307.11111111, 20200805111307.11111111, b'10101')") + // MySQL will get "ERROR 1292 (22001) Data truncation: Incorrect date value: '200805.1100000' for column 'd' at row 1". + tk.MustExec("alter table t modify d date") + // MySQL will get "ERROR 1292 (22001) Data truncation: Incorrect date value: '307.33' for column 'n' at row 1". + tk.MustExec("alter table t modify n date") + tk.MustGetErrCode("alter table t modify r date", mysql.ErrUnsupportedDDLOperation) + tk.MustGetErrCode("alter table t modify db date", mysql.ErrUnsupportedDDLOperation) + tk.MustGetErrCode("alter table t modify f32 date", mysql.ErrUnsupportedDDLOperation) + tk.MustGetErrCode("alter table t modify f64 date", mysql.ErrUnsupportedDDLOperation) + tk.MustGetErrCode("alter table t modify b date", mysql.ErrUnsupportedDDLOperation) + tk.MustQuery("select * from t").Check(testkit.Rows("2020-08-05 2000-03-07 20200805.11111111 20200805111307.11 200805100000 20200805111307.11 \x15")) + // timestamp + reset(tk) + tk.MustExec("insert into t values (200805.11, 307.333, 20200805.11111111, 20200805111307.11111111, 200805111307.11111111, 20200805111307.11111111, b'10101')") + // MySQL will get "ERROR 1292 (22001) Data truncation: Incorrect datetime value: '200805.1100000' for column 'd' at row 1". + tk.MustExec("alter table t modify d timestamp") + // MySQL will get "ERROR 1292 (22001) Data truncation: Incorrect datetime value: '307.33' for column 'n' at row 1". + tk.MustExec("alter table t modify n timestamp") + tk.MustGetErrCode("alter table t modify r timestamp", mysql.ErrUnsupportedDDLOperation) + tk.MustGetErrCode("alter table t modify db timestamp", mysql.ErrUnsupportedDDLOperation) + tk.MustGetErrCode("alter table t modify f32 timestamp", mysql.ErrUnsupportedDDLOperation) + tk.MustGetErrCode("alter table t modify f64 timestamp", mysql.ErrUnsupportedDDLOperation) + tk.MustGetErrCode("alter table t modify b timestamp", mysql.ErrUnsupportedDDLOperation) + tk.MustQuery("select * from t").Check(testkit.Rows("2020-08-05 00:00:00 2000-03-07 00:00:00 20200805.11111111 20200805111307.11 200805100000 20200805111307.11 \x15")) + // year + reset(tk) + tk.MustExec("insert into t values (200805.11, 307.333, 2.55555, 98.1111111, 2154.00001, 20200805111307.11111111, b'10101')") + tk.MustGetErrCode("alter table t modify d year", mysql.ErrDataOutOfRange) + tk.MustGetErrCode("alter table t modify n year", mysql.ErrDataOutOfRange) + // MySQL will get "ERROR 1264 (22001) Data truncation: Out of range value for column 'r' at row 1". + tk.MustExec("alter table t modify r year") + // MySQL will get "ERROR 1264 (22001) Data truncation: Out of range value for column 'db' at row 1". + tk.MustExec("alter table t modify db year") + // MySQL will get "ERROR 1264 (22001) Data truncation: Out of range value for column 'f32' at row 1". + tk.MustExec("alter table t modify f32 year") + tk.MustGetErrCode("alter table t modify f64 year", mysql.ErrDataOutOfRange) + tk.MustExec("alter table t modify b year") + tk.MustQuery("select * from t").Check(testkit.Rows("200805.1100000 307.33 2003 1998 2154 20200805111307.11 2021")) + + // To json data type. + reset(tk) + tk.MustExec("insert into t values (-258.12345, 333.33, 2000000.20000002, 323232323.3232323232, -111.11111111, -222222222222.222222222222222, b'10101')") + tk.MustExec("alter table t modify d json") + tk.MustExec("alter table t modify n json") + tk.MustExec("alter table t modify r json") + tk.MustExec("alter table t modify db json") + tk.MustExec("alter table t modify f32 json") + tk.MustExec("alter table t modify f64 json") + tk.MustExec("alter table t modify b json") + tk.MustQuery("select * from t").Check(testkit.Rows("-258.12345 333.33 2000000.20000002 323232323.32323235 -111.11111450195312 -222222222222.22223 \"\\u0015\"")) +} + +// Test issue #20529. +func (s *testColumnTypeChangeSuite) TestColumnTypeChangeIgnoreDisplayLength(c *C) { + tk := testkit.NewTestKit(c, s.store) + tk.MustExec("use test") + tk.Se.GetSessionVars().EnableChangeColumnType = true + defer func() { + tk.Se.GetSessionVars().EnableChangeColumnType = false + }() + + originalHook := s.dom.DDL().GetHook() + defer s.dom.DDL().(ddl.DDLForTest).SetHook(originalHook) + + var assertResult bool + assertHasAlterWriteReorg := func(tbl table.Table) { + // Restore the assert result to false. + assertResult = false + hook := &ddl.TestDDLCallback{} + hook.OnJobRunBeforeExported = func(job *model.Job) { + if tbl.Meta().ID != job.TableID { + return + } + if job.SchemaState == model.StateWriteReorganization { + assertResult = true + } + } + s.dom.DDL().(ddl.DDLForTest).SetHook(hook) + } + + // Change int to tinyint. + // Although display length is increased, the default flen is decreased, reorg is needed. + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int(1))") + tbl := testGetTableByName(c, tk.Se, "test", "t") + assertHasAlterWriteReorg(tbl) + tk.MustExec("alter table t modify column a tinyint(3)") + c.Assert(assertResult, Equals, true) + + // Change tinyint to tinyint + // Although display length is decreased, default flen is the same, reorg is not needed. + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a tinyint(3))") + tbl = testGetTableByName(c, tk.Se, "test", "t") + assertHasAlterWriteReorg(tbl) + tk.MustExec("alter table t modify column a tinyint(1)") + c.Assert(assertResult, Equals, false) + tk.MustExec("drop table if exists t") +} diff --git a/ddl/ddl_api.go b/ddl/ddl_api.go index 94a5d34b71105..dbf27a46ef4e3 100644 --- a/ddl/ddl_api.go +++ b/ddl/ddl_api.go @@ -2704,7 +2704,15 @@ func checkModifyCharsetAndCollation(toCharset, toCollate, origCharset, origColla // CheckModifyTypeCompatible checks whether changes column type to another is compatible considering // field length and precision. +<<<<<<< HEAD func CheckModifyTypeCompatible(origin *types.FieldType, to *types.FieldType) error { +======= +func CheckModifyTypeCompatible(origin *types.FieldType, to *types.FieldType) (allowedChangeColumnValueMsg string, err error) { + var ( + toFlen = to.Flen + originFlen = origin.Flen + ) +>>>>>>> 38f876044... ddl: ignore integer zerofill size attribute when changing the column types (#20862) unsupportedMsg := fmt.Sprintf("type %v not match origin %v", to.CompactStr(), origin.CompactStr()) switch origin.Tp { case mysql.TypeVarchar, mysql.TypeString, mysql.TypeVarString, @@ -2718,6 +2726,16 @@ func CheckModifyTypeCompatible(origin *types.FieldType, to *types.FieldType) err case mysql.TypeTiny, mysql.TypeShort, mysql.TypeInt24, mysql.TypeLong, mysql.TypeLonglong: switch to.Tp { case mysql.TypeTiny, mysql.TypeShort, mysql.TypeInt24, mysql.TypeLong, mysql.TypeLonglong: +<<<<<<< HEAD +======= + // For integers, we should ignore the potential display length represented by flen, using + // the default flen of the type. + originFlen, _ = mysql.GetDefaultFieldLengthAndDecimal(origin.Tp) + toFlen, _ = mysql.GetDefaultFieldLengthAndDecimal(to.Tp) + // Changing integer to integer, whether reorg is necessary is depend on the flen/decimal/signed. + skipSignCheck = true + skipLenCheck = true +>>>>>>> 38f876044... ddl: ignore integer zerofill size attribute when changing the column types (#20862) default: return errUnsupportedModifyColumn.GenWithStackByArgs(unsupportedMsg) } @@ -2757,9 +2775,18 @@ func CheckModifyTypeCompatible(origin *types.FieldType, to *types.FieldType) err } } +<<<<<<< HEAD if to.Flen > 0 && to.Flen < origin.Flen { msg := fmt.Sprintf("length %d is less than origin %d", to.Flen, origin.Flen) return errUnsupportedModifyColumn.GenWithStackByArgs(msg) +======= + if toFlen > 0 && toFlen < originFlen { + msg := fmt.Sprintf("length %d is less than origin %d", toFlen, originFlen) + if skipLenCheck { + return msg, errUnsupportedModifyColumn.GenWithStackByArgs(msg) + } + return "", errUnsupportedModifyColumn.GenWithStackByArgs(msg) +>>>>>>> 38f876044... ddl: ignore integer zerofill size attribute when changing the column types (#20862) } if to.Decimal > 0 && to.Decimal < origin.Decimal { msg := fmt.Sprintf("decimal %d is less than origin %d", to.Decimal, origin.Decimal) diff --git a/ddl/ddl_worker_test.go b/ddl/ddl_worker_test.go index 8c1c5f591c518..04a408f66eb77 100644 --- a/ddl/ddl_worker_test.go +++ b/ddl/ddl_worker_test.go @@ -439,6 +439,7 @@ func buildCancelJobTests(firstID int64) []testCancelJob { {act: model.ActionShardRowID, jobIDs: []int64{firstID + 17}, cancelRetErrs: noErrs, cancelState: model.StateNone}, {act: model.ActionModifyColumn, jobIDs: []int64{firstID + 18}, cancelRetErrs: noErrs, cancelState: model.StateNone}, +<<<<<<< HEAD {act: model.ActionAddForeignKey, jobIDs: []int64{firstID + 19}, cancelRetErrs: noErrs, cancelState: model.StateNone}, {act: model.ActionAddForeignKey, jobIDs: []int64{firstID + 20}, cancelRetErrs: []error{admin.ErrCancelFinishedDDLJob.GenWithStackByArgs(firstID + 20)}, cancelState: model.StatePublic}, {act: model.ActionDropForeignKey, jobIDs: []int64{firstID + 21}, cancelRetErrs: noErrs, cancelState: model.StateNone}, @@ -464,6 +465,57 @@ func buildCancelJobTests(firstID int64) []testCancelJob { {act: model.ActionAddTablePartition, jobIDs: []int64{firstID + 42}, cancelRetErrs: noErrs, cancelState: model.StateNone}, {act: model.ActionAddTablePartition, jobIDs: []int64{firstID + 43}, cancelRetErrs: noErrs, cancelState: model.StateReplicaOnly}, {act: model.ActionAddTablePartition, jobIDs: []int64{firstID + 44}, cancelRetErrs: []error{admin.ErrCancelFinishedDDLJob}, cancelState: model.StatePublic}, +======= + {act: model.ActionModifyColumn, jobIDs: []int64{firstID + 19}, cancelRetErrs: noErrs, cancelState: model.StateDeleteOnly}, + + {act: model.ActionAddForeignKey, jobIDs: []int64{firstID + 20}, cancelRetErrs: noErrs, cancelState: model.StateNone}, + {act: model.ActionAddForeignKey, jobIDs: []int64{firstID + 21}, cancelRetErrs: []error{admin.ErrCancelFinishedDDLJob.GenWithStackByArgs(firstID + 21)}, cancelState: model.StatePublic}, + {act: model.ActionDropForeignKey, jobIDs: []int64{firstID + 22}, cancelRetErrs: noErrs, cancelState: model.StateNone}, + {act: model.ActionDropForeignKey, jobIDs: []int64{firstID + 23}, cancelRetErrs: []error{admin.ErrCancelFinishedDDLJob.GenWithStackByArgs(firstID + 23)}, cancelState: model.StatePublic}, + + {act: model.ActionRenameTable, jobIDs: []int64{firstID + 24}, cancelRetErrs: noErrs, cancelState: model.StateNone}, + {act: model.ActionRenameTable, jobIDs: []int64{firstID + 25}, cancelRetErrs: []error{admin.ErrCancelFinishedDDLJob.GenWithStackByArgs(firstID + 25)}, cancelState: model.StatePublic}, + + {act: model.ActionModifyTableCharsetAndCollate, jobIDs: []int64{firstID + 26}, cancelRetErrs: noErrs, cancelState: model.StateNone}, + {act: model.ActionModifyTableCharsetAndCollate, jobIDs: []int64{firstID + 27}, cancelRetErrs: []error{admin.ErrCancelFinishedDDLJob.GenWithStackByArgs(firstID + 27)}, cancelState: model.StatePublic}, + {act: model.ActionTruncateTablePartition, jobIDs: []int64{firstID + 28}, cancelRetErrs: noErrs, cancelState: model.StateNone}, + {act: model.ActionTruncateTablePartition, jobIDs: []int64{firstID + 29}, cancelRetErrs: []error{admin.ErrCancelFinishedDDLJob.GenWithStackByArgs(firstID + 29)}, cancelState: model.StatePublic}, + {act: model.ActionModifySchemaCharsetAndCollate, jobIDs: []int64{firstID + 31}, cancelRetErrs: noErrs, cancelState: model.StateNone}, + {act: model.ActionModifySchemaCharsetAndCollate, jobIDs: []int64{firstID + 32}, cancelRetErrs: []error{admin.ErrCancelFinishedDDLJob.GenWithStackByArgs(firstID + 32)}, cancelState: model.StatePublic}, + + {act: model.ActionAddPrimaryKey, jobIDs: []int64{firstID + 33}, cancelRetErrs: noErrs, cancelState: model.StateDeleteOnly}, + {act: model.ActionAddPrimaryKey, jobIDs: []int64{firstID + 34}, cancelRetErrs: noErrs, cancelState: model.StateWriteOnly}, + {act: model.ActionAddPrimaryKey, jobIDs: []int64{firstID + 35}, cancelRetErrs: noErrs, cancelState: model.StateWriteReorganization}, + {act: model.ActionAddPrimaryKey, jobIDs: []int64{firstID + 36}, cancelRetErrs: []error{admin.ErrCancelFinishedDDLJob.GenWithStackByArgs(firstID + 36)}, cancelState: model.StatePublic}, + {act: model.ActionDropPrimaryKey, jobIDs: []int64{firstID + 37}, cancelRetErrs: noErrs, cancelState: model.StateWriteOnly}, + {act: model.ActionDropPrimaryKey, jobIDs: []int64{firstID + 38}, cancelRetErrs: []error{admin.ErrCannotCancelDDLJob.GenWithStackByArgs(firstID + 38)}, cancelState: model.StateDeleteOnly}, + + {act: model.ActionAddColumns, jobIDs: []int64{firstID + 39}, cancelRetErrs: noErrs, cancelState: model.StateDeleteOnly}, + {act: model.ActionAddColumns, jobIDs: []int64{firstID + 40}, cancelRetErrs: noErrs, cancelState: model.StateWriteOnly}, + {act: model.ActionAddColumns, jobIDs: []int64{firstID + 41}, cancelRetErrs: noErrs, cancelState: model.StateWriteReorganization}, + {act: model.ActionAddColumns, jobIDs: []int64{firstID + 42}, cancelRetErrs: []error{admin.ErrCancelFinishedDDLJob.GenWithStackByArgs(firstID + 42)}, cancelState: model.StatePublic}, + + {act: model.ActionDropColumns, jobIDs: []int64{firstID + 43}, cancelRetErrs: []error{admin.ErrCannotCancelDDLJob.GenWithStackByArgs(firstID + 43)}, cancelState: model.StateDeleteOnly}, + {act: model.ActionDropColumns, jobIDs: []int64{firstID + 44}, cancelRetErrs: []error{admin.ErrCannotCancelDDLJob.GenWithStackByArgs(firstID + 44)}, cancelState: model.StateWriteOnly}, + {act: model.ActionDropColumns, jobIDs: []int64{firstID + 45}, cancelRetErrs: []error{admin.ErrCannotCancelDDLJob.GenWithStackByArgs(firstID + 45)}, cancelState: model.StateWriteReorganization}, + + {act: model.ActionAlterIndexVisibility, jobIDs: []int64{firstID + 47}, cancelRetErrs: noErrs, cancelState: model.StateNone}, + {act: model.ActionAlterIndexVisibility, jobIDs: []int64{firstID + 48}, cancelRetErrs: []error{admin.ErrCancelFinishedDDLJob.GenWithStackByArgs(firstID + 48)}, cancelState: model.StatePublic}, + + {act: model.ActionExchangeTablePartition, jobIDs: []int64{firstID + 54}, cancelRetErrs: noErrs, cancelState: model.StateNone}, + {act: model.ActionExchangeTablePartition, jobIDs: []int64{firstID + 55}, cancelRetErrs: []error{admin.ErrCancelFinishedDDLJob.GenWithStackByArgs(firstID + 55)}, cancelState: model.StatePublic}, + + {act: model.ActionAddTablePartition, jobIDs: []int64{firstID + 60}, cancelRetErrs: noErrs, cancelState: model.StateNone}, + {act: model.ActionAddTablePartition, jobIDs: []int64{firstID + 61}, cancelRetErrs: noErrs, cancelState: model.StateReplicaOnly}, + {act: model.ActionAddTablePartition, jobIDs: []int64{firstID + 62}, cancelRetErrs: []error{admin.ErrCancelFinishedDDLJob}, cancelState: model.StatePublic}, + + // modify column has two different types, normal-type and reorg-type. The latter has 5 states and it can be cancelled except the public state. + {act: model.ActionModifyColumn, jobIDs: []int64{firstID + 65}, cancelRetErrs: noErrs, cancelState: model.StateNone}, + {act: model.ActionModifyColumn, jobIDs: []int64{firstID + 66}, cancelRetErrs: noErrs, cancelState: model.StateDeleteOnly}, + {act: model.ActionModifyColumn, jobIDs: []int64{firstID + 67}, cancelRetErrs: noErrs, cancelState: model.StateWriteOnly}, + {act: model.ActionModifyColumn, jobIDs: []int64{firstID + 68}, cancelRetErrs: noErrs, cancelState: model.StateWriteReorganization}, + {act: model.ActionModifyColumn, jobIDs: []int64{firstID + 69}, cancelRetErrs: []error{admin.ErrCancelFinishedDDLJob}, cancelState: model.StatePublic}, +>>>>>>> 38f876044... ddl: ignore integer zerofill size attribute when changing the column types (#20862) } return tests @@ -723,13 +775,30 @@ func (s *testDDLSuite) TestCancelJob(c *C) { // modify column col.DefaultValue = "1" updateTest(&tests[15]) - modifyColumnArgs := []interface{}{col, col.Name, &ast.ColumnPosition{}, byte(0)} + modifyColumnArgs := []interface{}{col, col.Name, &ast.ColumnPosition{}, byte(0), uint64(0)} doDDLJobErrWithSchemaState(ctx, d, c, dbInfo.ID, tblInfo.ID, test.act, modifyColumnArgs, &test.cancelState) c.Check(checkErr, IsNil) changedTable = testGetTable(c, d, dbInfo.ID, tblInfo.ID) changedCol := model.FindColumnInfo(changedTable.Meta().Columns, col.Name.L) c.Assert(changedCol.DefaultValue, IsNil) +<<<<<<< HEAD +======= + // modify delete-only-state column, + col.FieldType.Tp = mysql.TypeTiny + col.FieldType.Flen = col.FieldType.Flen - 1 + updateTest(&tests[16]) + modifyColumnArgs = []interface{}{col, col.Name, &ast.ColumnPosition{}, byte(0), uint64(0)} + cancelState = model.StateNone + doDDLJobErrWithSchemaState(ctx, d, c, dbInfo.ID, tblInfo.ID, test.act, modifyColumnArgs, &cancelState) + c.Check(checkErr, IsNil) + changedTable = testGetTable(c, d, dbInfo.ID, tblInfo.ID) + changedCol = model.FindColumnInfo(changedTable.Meta().Columns, col.Name.L) + c.Assert(changedCol.FieldType.Tp, Equals, mysql.TypeLonglong) + c.Assert(changedCol.FieldType.Flen, Equals, col.FieldType.Flen+1) + col.FieldType.Flen++ + +>>>>>>> 38f876044... ddl: ignore integer zerofill size attribute when changing the column types (#20862) // Test add foreign key failed cause by canceled. updateTest(&tests[16]) addForeignKeyArgs := []interface{}{model.FKInfo{Name: model.NewCIStr("fk1")}} From a52e9d66238117c64d4509604b863616da9d7b5d Mon Sep 17 00:00:00 2001 From: AilinKid <314806019@qq.com> Date: Tue, 17 Nov 2020 17:57:40 +0800 Subject: [PATCH 2/3] resolve the conflicts Signed-off-by: AilinKid <314806019@qq.com> --- ddl/column_type_change_test.go | 989 --------------------------------- 1 file changed, 989 deletions(-) delete mode 100644 ddl/column_type_change_test.go diff --git a/ddl/column_type_change_test.go b/ddl/column_type_change_test.go deleted file mode 100644 index 88a7333cda763..0000000000000 --- a/ddl/column_type_change_test.go +++ /dev/null @@ -1,989 +0,0 @@ -// Copyright 2020 PingCAP, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// See the License for the specific language governing permissions and -// limitations under the License. - -package ddl_test - -import ( - "errors" - "time" - - . "github.com/pingcap/check" - "github.com/pingcap/parser/model" - parser_mysql "github.com/pingcap/parser/mysql" - "github.com/pingcap/parser/terror" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/domain" - mysql "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/store/mockstore" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/table/tables" - "github.com/pingcap/tidb/util/dbterror" - "github.com/pingcap/tidb/util/testkit" -) - -var _ = SerialSuites(&testColumnTypeChangeSuite{}) - -type testColumnTypeChangeSuite struct { - store kv.Storage - dbInfo *model.DBInfo - dom *domain.Domain -} - -func (s *testColumnTypeChangeSuite) SetUpSuite(c *C) { - var err error - ddl.SetWaitTimeWhenErrorOccurred(1 * time.Microsecond) - s.store, err = mockstore.NewMockStore() - c.Assert(err, IsNil) - s.dom, err = session.BootstrapSession(s.store) - c.Assert(err, IsNil) -} - -func (s *testColumnTypeChangeSuite) TearDownSuite(c *C) { - s.dom.Close() - c.Assert(s.store.Close(), IsNil) -} - -func (s *testColumnTypeChangeSuite) TestColumnTypeChangeBetweenInteger(c *C) { - tk := testkit.NewTestKit(c, s.store) - tk.MustExec("use test") - // Enable column change variable. - tk.Se.GetSessionVars().EnableChangeColumnType = true - defer func() { - tk.Se.GetSessionVars().EnableChangeColumnType = false - }() - - // Modify column from null to not null. - tk.MustExec("drop table if exists t") - tk.MustExec("create table t (a int null, b int null)") - tk.MustExec("alter table t modify column b int not null") - - tk.MustExec("insert into t(a, b) values (null, 1)") - // Modify column from null to not null in same type will cause ErrInvalidUseOfNull - tk.MustGetErrCode("alter table t modify column a int not null", mysql.ErrInvalidUseOfNull) - - // Modify column from null to not null in different type will cause WarnDataTruncated. - tk.MustGetErrCode("alter table t modify column a tinyint not null", mysql.WarnDataTruncated) - tk.MustGetErrCode("alter table t modify column a bigint not null", mysql.WarnDataTruncated) - - // Modify column not null to null. - tk.MustExec("drop table if exists t") - tk.MustExec("create table t (a int not null, b int not null)") - tk.MustExec("alter table t modify column b int null") - - tk.MustExec("insert into t(a, b) values (1, null)") - tk.MustExec("alter table t modify column a int null") - - // Modify column from unsigned to signed and from signed to unsigned. - tk.MustExec("drop table if exists t") - tk.MustExec("create table t (a int unsigned, b int signed)") - tk.MustExec("insert into t(a, b) values (1, 1)") - tk.MustExec("alter table t modify column a int signed") - tk.MustExec("alter table t modify column b int unsigned") - - // Modify column from small type to big type. - tk.MustExec("drop table if exists t") - tk.MustExec("create table t (a tinyint)") - tk.MustExec("alter table t modify column a smallint") - - tk.MustExec("drop table if exists t") - tk.MustExec("create table t (a tinyint)") - tk.MustExec("insert into t(a) values (127)") - tk.MustExec("alter table t modify column a smallint") - tk.MustExec("alter table t modify column a mediumint") - tk.MustExec("alter table t modify column a int") - tk.MustExec("alter table t modify column a bigint") - - // Modify column from big type to small type. - tk.MustExec("drop table if exists t") - tk.MustExec("create table t (a bigint)") - tk.MustExec("alter table t modify column a int") - tk.MustExec("alter table t modify column a mediumint") - tk.MustExec("alter table t modify column a smallint") - tk.MustExec("alter table t modify column a tinyint") - - tk.MustExec("drop table if exists t") - tk.MustExec("create table t (a bigint)") - tk.MustExec("insert into t(a) values (9223372036854775807)") - tk.MustGetErrCode("alter table t modify column a int", mysql.ErrDataOutOfRange) - tk.MustGetErrCode("alter table t modify column a mediumint", mysql.ErrDataOutOfRange) - tk.MustGetErrCode("alter table t modify column a smallint", mysql.ErrDataOutOfRange) - tk.MustGetErrCode("alter table t modify column a tinyint", mysql.ErrDataOutOfRange) - _, err := tk.Exec("admin check table t") - c.Assert(err, IsNil) -} - -func (s *testColumnTypeChangeSuite) TestColumnTypeChangeStateBetweenInteger(c *C) { - tk := testkit.NewTestKit(c, s.store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t (c1 int, c2 int)") - tk.MustExec("insert into t(c1, c2) values (1, 1)") - // Enable column change variable. - tk.Se.GetSessionVars().EnableChangeColumnType = true - defer func() { - tk.Se.GetSessionVars().EnableChangeColumnType = false - }() - - // use new session to check meta in callback function. - internalTK := testkit.NewTestKit(c, s.store) - internalTK.MustExec("use test") - - tbl := testGetTableByName(c, tk.Se, "test", "t") - c.Assert(tbl, NotNil) - c.Assert(len(tbl.Cols()), Equals, 2) - c.Assert(getModifyColumn(c, tk.Se.(sessionctx.Context), "test", "t", "c2", false), NotNil) - - originalHook := s.dom.DDL().GetHook() - defer s.dom.DDL().(ddl.DDLForTest).SetHook(originalHook) - - hook := &ddl.TestDDLCallback{} - var checkErr error - hook.OnJobRunBeforeExported = func(job *model.Job) { - if checkErr != nil { - return - } - if tbl.Meta().ID != job.TableID { - return - } - switch job.SchemaState { - case model.StateNone: - tbl = testGetTableByName(c, internalTK.Se, "test", "t") - if tbl == nil { - checkErr = errors.New("tbl is nil") - } else if len(tbl.Cols()) != 2 { - checkErr = errors.New("len(cols) is not right") - } - case model.StateDeleteOnly, model.StateWriteOnly, model.StateWriteReorganization: - tbl = testGetTableByName(c, internalTK.Se, "test", "t") - if tbl == nil { - checkErr = errors.New("tbl is nil") - } else if len(tbl.(*tables.TableCommon).Columns) != 3 { - // changingCols has been added into meta. - checkErr = errors.New("len(cols) is not right") - } else if getModifyColumn(c, internalTK.Se.(sessionctx.Context), "test", "t", "c2", true).Flag&parser_mysql.PreventNullInsertFlag == uint(0) { - checkErr = errors.New("old col's flag is not right") - } else if getModifyColumn(c, internalTK.Se.(sessionctx.Context), "test", "t", "_Col$_c2_0", true) == nil { - checkErr = errors.New("changingCol is nil") - } - } - } - s.dom.DDL().(ddl.DDLForTest).SetHook(hook) - // Alter sql will modify column c2 to tinyint not null. - SQL := "alter table t modify column c2 tinyint not null" - tk.MustExec(SQL) - // Assert the checkErr in the job of every state. - c.Assert(checkErr, IsNil) - - // Check the col meta after the column type change. - tbl = testGetTableByName(c, tk.Se, "test", "t") - c.Assert(tbl, NotNil) - c.Assert(len(tbl.Cols()), Equals, 2) - col := getModifyColumn(c, tk.Se.(sessionctx.Context), "test", "t", "c2", false) - c.Assert(col, NotNil) - c.Assert(parser_mysql.HasNotNullFlag(col.Flag), Equals, true) - c.Assert(col.Flag&parser_mysql.NoDefaultValueFlag, Not(Equals), uint(0)) - c.Assert(col.Tp, Equals, parser_mysql.TypeTiny) - c.Assert(col.ChangeStateInfo, IsNil) - tk.MustQuery("select * from t").Check(testkit.Rows("1 1")) -} - -func (s *testColumnTypeChangeSuite) TestRollbackColumnTypeChangeBetweenInteger(c *C) { - tk := testkit.NewTestKit(c, s.store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t (c1 bigint, c2 bigint)") - tk.MustExec("insert into t(c1, c2) values (1, 1)") - // Enable column change variable. - tk.Se.GetSessionVars().EnableChangeColumnType = true - defer func() { - tk.Se.GetSessionVars().EnableChangeColumnType = false - }() - - tbl := testGetTableByName(c, tk.Se, "test", "t") - c.Assert(tbl, NotNil) - c.Assert(len(tbl.Cols()), Equals, 2) - c.Assert(getModifyColumn(c, tk.Se.(sessionctx.Context), "test", "t", "c2", false), NotNil) - - originalHook := s.dom.DDL().GetHook() - defer s.dom.DDL().(ddl.DDLForTest).SetHook(originalHook) - - hook := &ddl.TestDDLCallback{} - // Mock roll back at model.StateNone. - customizeHookRollbackAtState(hook, tbl, model.StateNone) - s.dom.DDL().(ddl.DDLForTest).SetHook(hook) - // Alter sql will modify column c2 to bigint not null. - SQL := "alter table t modify column c2 int not null" - _, err := tk.Exec(SQL) - c.Assert(err, NotNil) - c.Assert(err.Error(), Equals, "[ddl:1]MockRollingBackInCallBack-none") - assertRollBackedColUnchanged(c, tk) - - // Mock roll back at model.StateDeleteOnly. - customizeHookRollbackAtState(hook, tbl, model.StateDeleteOnly) - s.dom.DDL().(ddl.DDLForTest).SetHook(hook) - _, err = tk.Exec(SQL) - c.Assert(err, NotNil) - c.Assert(err.Error(), Equals, "[ddl:1]MockRollingBackInCallBack-delete only") - assertRollBackedColUnchanged(c, tk) - - // Mock roll back at model.StateWriteOnly. - customizeHookRollbackAtState(hook, tbl, model.StateWriteOnly) - s.dom.DDL().(ddl.DDLForTest).SetHook(hook) - _, err = tk.Exec(SQL) - c.Assert(err, NotNil) - c.Assert(err.Error(), Equals, "[ddl:1]MockRollingBackInCallBack-write only") - assertRollBackedColUnchanged(c, tk) - - // Mock roll back at model.StateWriteReorg. - customizeHookRollbackAtState(hook, tbl, model.StateWriteReorganization) - s.dom.DDL().(ddl.DDLForTest).SetHook(hook) - _, err = tk.Exec(SQL) - c.Assert(err, NotNil) - c.Assert(err.Error(), Equals, "[ddl:1]MockRollingBackInCallBack-write reorganization") - assertRollBackedColUnchanged(c, tk) -} - -func customizeHookRollbackAtState(hook *ddl.TestDDLCallback, tbl table.Table, state model.SchemaState) { - hook.OnJobRunBeforeExported = func(job *model.Job) { - if tbl.Meta().ID != job.TableID { - return - } - if job.SchemaState == state { - job.State = model.JobStateRollingback - job.Error = mockTerrorMap[state.String()] - } - } -} - -func assertRollBackedColUnchanged(c *C, tk *testkit.TestKit) { - tbl := testGetTableByName(c, tk.Se, "test", "t") - c.Assert(tbl, NotNil) - c.Assert(len(tbl.Cols()), Equals, 2) - col := getModifyColumn(c, tk.Se.(sessionctx.Context), "test", "t", "c2", false) - c.Assert(col, NotNil) - c.Assert(col.Flag, Equals, uint(0)) - c.Assert(col.Tp, Equals, parser_mysql.TypeLonglong) - c.Assert(col.ChangeStateInfo, IsNil) - tk.MustQuery("select * from t").Check(testkit.Rows("1 1")) -} - -var mockTerrorMap = make(map[string]*terror.Error) - -func init() { - // Since terror new action will cause data race with other test suite (getTerrorCode) in parallel, we init it all here. - mockTerrorMap[model.StateNone.String()] = dbterror.ClassDDL.New(1, "MockRollingBackInCallBack-"+model.StateNone.String()) - mockTerrorMap[model.StateDeleteOnly.String()] = dbterror.ClassDDL.New(1, "MockRollingBackInCallBack-"+model.StateDeleteOnly.String()) - mockTerrorMap[model.StateWriteOnly.String()] = dbterror.ClassDDL.New(1, "MockRollingBackInCallBack-"+model.StateWriteOnly.String()) - mockTerrorMap[model.StateWriteReorganization.String()] = dbterror.ClassDDL.New(1, "MockRollingBackInCallBack-"+model.StateWriteReorganization.String()) -} - -func (s *testColumnTypeChangeSuite) TestColumnTypeChangeFromIntegerToOthers(c *C) { - tk := testkit.NewTestKit(c, s.store) - tk.MustExec("use test") - // Enable column change variable. - tk.Se.GetSessionVars().EnableChangeColumnType = true - defer func() { - tk.Se.GetSessionVars().EnableChangeColumnType = false - }() - - prepare := func(tk *testkit.TestKit) { - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a tinyint, b smallint, c mediumint, d int, e bigint, f bigint)") - tk.MustExec("insert into t values(1, 11, 111, 1111, 11111, 111111)") - } - prepareForEnumSet := func(tk *testkit.TestKit) { - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a tinyint, b smallint, c mediumint, d int, e bigint)") - tk.MustExec("insert into t values(1, 1, 1, 11111, 11111)") - } - - // integer to string - prepare(tk) - tk.MustExec("alter table t modify a varchar(10)") - modifiedColumn := getModifyColumn(c, tk.Se, "test", "t", "a", false) - c.Assert(modifiedColumn, NotNil) - c.Assert(modifiedColumn.Tp, Equals, parser_mysql.TypeVarchar) - tk.MustQuery("select a from t").Check(testkit.Rows("1")) - - tk.MustExec("alter table t modify b char(10)") - modifiedColumn = getModifyColumn(c, tk.Se, "test", "t", "b", false) - c.Assert(modifiedColumn, NotNil) - c.Assert(modifiedColumn.Tp, Equals, parser_mysql.TypeString) - tk.MustQuery("select b from t").Check(testkit.Rows("11")) - - tk.MustExec("alter table t modify c binary(10)") - modifiedColumn = getModifyColumn(c, tk.Se, "test", "t", "c", false) - c.Assert(modifiedColumn, NotNil) - c.Assert(modifiedColumn.Tp, Equals, parser_mysql.TypeString) - tk.MustQuery("select c from t").Check(testkit.Rows("111\x00\x00\x00\x00\x00\x00\x00")) - - tk.MustExec("alter table t modify d varbinary(10)") - modifiedColumn = getModifyColumn(c, tk.Se, "test", "t", "d", false) - c.Assert(modifiedColumn, NotNil) - c.Assert(modifiedColumn.Tp, Equals, parser_mysql.TypeVarchar) - tk.MustQuery("select d from t").Check(testkit.Rows("1111")) - - tk.MustExec("alter table t modify e blob(10)") - modifiedColumn = getModifyColumn(c, tk.Se, "test", "t", "e", false) - c.Assert(modifiedColumn, NotNil) - c.Assert(modifiedColumn.Tp, Equals, parser_mysql.TypeBlob) - tk.MustQuery("select e from t").Check(testkit.Rows("11111")) - - tk.MustExec("alter table t modify f text(10)") - modifiedColumn = getModifyColumn(c, tk.Se, "test", "t", "f", false) - c.Assert(modifiedColumn, NotNil) - c.Assert(modifiedColumn.Tp, Equals, parser_mysql.TypeBlob) - tk.MustQuery("select f from t").Check(testkit.Rows("111111")) - - // integer to decimal - prepare(tk) - tk.MustExec("alter table t modify a decimal(2,1)") - modifiedColumn = getModifyColumn(c, tk.Se, "test", "t", "a", false) - c.Assert(modifiedColumn, NotNil) - c.Assert(modifiedColumn.Tp, Equals, parser_mysql.TypeNewDecimal) - tk.MustQuery("select a from t").Check(testkit.Rows("1.0")) - - // integer to year - // For year(2), MySQL converts values in the ranges '0' to '69' and '70' to '99' to YEAR values in the ranges 2000 to 2069 and 1970 to 1999. - tk.MustExec("alter table t modify b year") - modifiedColumn = getModifyColumn(c, tk.Se, "test", "t", "b", false) - c.Assert(modifiedColumn, NotNil) - c.Assert(modifiedColumn.Tp, Equals, parser_mysql.TypeYear) - tk.MustQuery("select b from t").Check(testkit.Rows("2011")) - - // integer to time - tk.MustExec("alter table t modify c time") - modifiedColumn = getModifyColumn(c, tk.Se, "test", "t", "c", false) - c.Assert(modifiedColumn, NotNil) - c.Assert(modifiedColumn.Tp, Equals, parser_mysql.TypeDuration) // mysql.TypeTime has rename to TypeDuration. - tk.MustQuery("select c from t").Check(testkit.Rows("00:01:11")) - - // integer to date (mysql will throw `Incorrect date value: '1111' for column 'd' at row 1` error) - tk.MustExec("alter table t modify d date") - modifiedColumn = getModifyColumn(c, tk.Se, "test", "t", "d", false) - c.Assert(modifiedColumn, NotNil) - c.Assert(modifiedColumn.Tp, Equals, parser_mysql.TypeDate) - tk.MustQuery("select d from t").Check(testkit.Rows("2000-11-11")) // the given number will be left-forward used. - - // integer to timestamp (according to what timezone you have set) - tk.MustExec("alter table t modify e timestamp") - tk.MustExec("set @@session.time_zone=UTC") - modifiedColumn = getModifyColumn(c, tk.Se, "test", "t", "e", false) - c.Assert(modifiedColumn, NotNil) - c.Assert(modifiedColumn.Tp, Equals, parser_mysql.TypeTimestamp) - tk.MustQuery("select e from t").Check(testkit.Rows("2001-11-11 00:00:00")) // the given number will be left-forward used. - - // integer to datetime - tk.MustExec("alter table t modify f datetime") - modifiedColumn = getModifyColumn(c, tk.Se, "test", "t", "f", false) - c.Assert(modifiedColumn, NotNil) - c.Assert(modifiedColumn.Tp, Equals, parser_mysql.TypeDatetime) - tk.MustQuery("select f from t").Check(testkit.Rows("2011-11-11 00:00:00")) // the given number will be left-forward used. - - // integer to floating-point values - prepare(tk) - tk.MustExec("alter table t modify a float") - modifiedColumn = getModifyColumn(c, tk.Se, "test", "t", "a", false) - c.Assert(modifiedColumn, NotNil) - c.Assert(modifiedColumn.Tp, Equals, parser_mysql.TypeFloat) - tk.MustQuery("select a from t").Check(testkit.Rows("1")) - - tk.MustExec("alter table t modify b double") - modifiedColumn = getModifyColumn(c, tk.Se, "test", "t", "b", false) - c.Assert(modifiedColumn, NotNil) - c.Assert(modifiedColumn.Tp, Equals, parser_mysql.TypeDouble) - tk.MustQuery("select b from t").Check(testkit.Rows("11")) - - // integer to bit - tk.MustExec("alter table t modify c bit(10)") - modifiedColumn = getModifyColumn(c, tk.Se, "test", "t", "c", false) - c.Assert(modifiedColumn, NotNil) - c.Assert(modifiedColumn.Tp, Equals, parser_mysql.TypeBit) - // 111 will be stored ad 0x00,0110,1111 = 0x6F, which will be shown as ASCII('o')=111 as well. - tk.MustQuery("select c from t").Check(testkit.Rows("\x00o")) - - // integer to json - tk.MustExec("alter table t modify d json") - modifiedColumn = getModifyColumn(c, tk.Se, "test", "t", "d", false) - c.Assert(modifiedColumn, NotNil) - c.Assert(modifiedColumn.Tp, Equals, parser_mysql.TypeJSON) - tk.MustQuery("select d from t").Check(testkit.Rows("1111")) - - // integer to enum - prepareForEnumSet(tk) - // TiDB take integer as the enum element offset to cast. - tk.MustExec("alter table t modify a enum(\"a\", \"b\")") - modifiedColumn = getModifyColumn(c, tk.Se, "test", "t", "a", false) - c.Assert(modifiedColumn, NotNil) - c.Assert(modifiedColumn.Tp, Equals, parser_mysql.TypeEnum) - tk.MustQuery("select a from t").Check(testkit.Rows("a")) - - // TiDB take integer as the set element offset to cast. - tk.MustExec("alter table t modify b set(\"a\", \"b\")") - modifiedColumn = getModifyColumn(c, tk.Se, "test", "t", "b", false) - c.Assert(modifiedColumn, NotNil) - c.Assert(modifiedColumn.Tp, Equals, parser_mysql.TypeSet) - tk.MustQuery("select b from t").Check(testkit.Rows("a")) - - // TiDB can't take integer as the enum element string to cast, while the MySQL can. - tk.MustGetErrCode("alter table t modify d enum(\"11111\", \"22222\")", mysql.WarnDataTruncated) - - // TiDB can't take integer as the set element string to cast, while the MySQL can. - tk.MustGetErrCode("alter table t modify e set(\"11111\", \"22222\")", mysql.WarnDataTruncated) -} - -func (s *testColumnTypeChangeSuite) TestColumnTypeChangeFromStringToOthers(c *C) { - tk := testkit.NewTestKit(c, s.store) - tk.MustExec("use test") - // Enable column change variable. - tk.Se.GetSessionVars().EnableChangeColumnType = true - - // Set time zone to UTC. - originalTz := tk.Se.GetSessionVars().TimeZone - tk.Se.GetSessionVars().TimeZone = time.UTC - defer func() { - tk.Se.GetSessionVars().EnableChangeColumnType = false - tk.Se.GetSessionVars().TimeZone = originalTz - }() - - // Init string date type table. - reset := func(tk *testkit.TestKit) { - tk.MustExec("drop table if exists t") - tk.MustExec(` - create table t ( - c char(8), - vc varchar(8), - bny binary(8), - vbny varbinary(8), - bb blob, - txt text, - e enum('123', '2020-07-15 18:32:17.888', 'str', '{"k1": "value"}'), - s set('123', '2020-07-15 18:32:17.888', 'str', '{"k1": "value"}') - ) - `) - } - - // To numeric data types. - // tinyint - reset(tk) - tk.MustExec("insert into t values ('123', '123', '123', '123', '123', '123', '123', '123')") - tk.MustExec("alter table t modify c tinyint") - tk.MustExec("alter table t modify vc tinyint") - tk.MustExec("alter table t modify bny tinyint") - tk.MustExec("alter table t modify vbny tinyint") - tk.MustExec("alter table t modify bb tinyint") - tk.MustExec("alter table t modify txt tinyint") - tk.MustExec("alter table t modify e tinyint") - tk.MustExec("alter table t modify s tinyint") - tk.MustQuery("select * from t").Check(testkit.Rows("123 123 123 123 123 123 1 1")) - // int - reset(tk) - tk.MustExec("insert into t values ('17305', '17305', '17305', '17305', '17305', '17305', '123', '123')") - tk.MustExec("alter table t modify c int") - tk.MustExec("alter table t modify vc int") - tk.MustExec("alter table t modify bny int") - tk.MustExec("alter table t modify vbny int") - tk.MustExec("alter table t modify bb int") - tk.MustExec("alter table t modify txt int") - tk.MustExec("alter table t modify e int") - tk.MustExec("alter table t modify s int") - tk.MustQuery("select * from t").Check(testkit.Rows("17305 17305 17305 17305 17305 17305 1 1")) - // bigint - reset(tk) - tk.MustExec("insert into t values ('17305867', '17305867', '17305867', '17305867', '17305867', '17305867', '123', '123')") - tk.MustExec("alter table t modify c bigint") - tk.MustExec("alter table t modify vc bigint") - tk.MustExec("alter table t modify bny bigint") - tk.MustExec("alter table t modify vbny bigint") - tk.MustExec("alter table t modify bb bigint") - tk.MustExec("alter table t modify txt bigint") - tk.MustExec("alter table t modify e bigint") - tk.MustExec("alter table t modify s bigint") - tk.MustQuery("select * from t").Check(testkit.Rows("17305867 17305867 17305867 17305867 17305867 17305867 1 1")) - // bit - reset(tk) - tk.MustExec("insert into t values ('1', '1', '1', '1', '1', '1', '123', '123')") - tk.MustGetErrCode("alter table t modify c bit", mysql.ErrUnsupportedDDLOperation) - tk.MustGetErrCode("alter table t modify vc bit", mysql.ErrUnsupportedDDLOperation) - tk.MustGetErrCode("alter table t modify bny bit", mysql.ErrUnsupportedDDLOperation) - tk.MustGetErrCode("alter table t modify vbny bit", mysql.ErrUnsupportedDDLOperation) - tk.MustGetErrCode("alter table t modify bb bit", mysql.ErrUnsupportedDDLOperation) - tk.MustGetErrCode("alter table t modify txt bit", mysql.ErrUnsupportedDDLOperation) - tk.MustExec("alter table t modify e bit") - tk.MustExec("alter table t modify s bit") - tk.MustQuery("select * from t").Check(testkit.Rows("1 1 1\x00\x00\x00\x00\x00\x00\x00 1 1 1 \x01 \x01")) - // decimal - reset(tk) - tk.MustExec("insert into t values ('123.45', '123.45', '123.45', '123.45', '123.45', '123.45', '123', '123')") - tk.MustExec("alter table t modify c decimal(7, 4)") - tk.MustExec("alter table t modify vc decimal(7, 4)") - tk.MustExec("alter table t modify bny decimal(7, 4)") - tk.MustExec("alter table t modify vbny decimal(7, 4)") - tk.MustExec("alter table t modify bb decimal(7, 4)") - tk.MustExec("alter table t modify txt decimal(7, 4)") - tk.MustExec("alter table t modify e decimal(7, 4)") - tk.MustExec("alter table t modify s decimal(7, 4)") - tk.MustQuery("select * from t").Check(testkit.Rows("123.4500 123.4500 123.4500 123.4500 123.4500 123.4500 1.0000 1.0000")) - // double - reset(tk) - tk.MustExec("insert into t values ('123.45', '123.45', '123.45', '123.45', '123.45', '123.45', '123', '123')") - tk.MustExec("alter table t modify c double(7, 4)") - tk.MustExec("alter table t modify vc double(7, 4)") - tk.MustExec("alter table t modify bny double(7, 4)") - tk.MustExec("alter table t modify vbny double(7, 4)") - tk.MustExec("alter table t modify bb double(7, 4)") - tk.MustExec("alter table t modify txt double(7, 4)") - tk.MustExec("alter table t modify e double(7, 4)") - tk.MustExec("alter table t modify s double(7, 4)") - tk.MustQuery("select * from t").Check(testkit.Rows("123.45 123.45 123.45 123.45 123.45 123.45 1 1")) - - // To date and time data types. - // date - reset(tk) - tk.MustExec("insert into t values ('20200826', '2008261', '20200826', '200826', '2020-08-26', '08-26 19:35:41', '2020-07-15 18:32:17.888', '2020-07-15 18:32:17.888')") - tk.MustExec("alter table t modify c date") - tk.MustExec("alter table t modify vc date") - tk.MustExec("alter table t modify bny date") - tk.MustExec("alter table t modify vbny date") - tk.MustExec("alter table t modify bb date") - // Alter text '08-26 19:35:41' to date will error. (same as mysql does) - tk.MustGetErrCode("alter table t modify txt date", mysql.ErrTruncatedWrongValue) - tk.MustGetErrCode("alter table t modify e date", mysql.ErrUnsupportedDDLOperation) - tk.MustGetErrCode("alter table t modify s date", mysql.ErrUnsupportedDDLOperation) - tk.MustQuery("select * from t").Check(testkit.Rows("2020-08-26 2020-08-26 2020-08-26 2020-08-26 2020-08-26 08-26 19:35:41 2020-07-15 18:32:17.888 2020-07-15 18:32:17.888")) - // time - reset(tk) - tk.MustExec("insert into t values ('19:35:41', '19:35:41', '19:35:41', '19:35:41', '19:35:41.45678', '19:35:41.45678', '2020-07-15 18:32:17.888', '2020-07-15 18:32:17.888')") - tk.MustExec("alter table t modify c time") - tk.MustExec("alter table t modify vc time") - tk.MustExec("alter table t modify bny time") - tk.MustExec("alter table t modify vbny time") - tk.MustExec("alter table t modify bb time") - tk.MustExec("alter table t modify txt time") - tk.MustGetErrCode("alter table t modify e time", mysql.ErrUnsupportedDDLOperation) - tk.MustGetErrCode("alter table t modify s time", mysql.ErrUnsupportedDDLOperation) - tk.MustQuery("select * from t").Check(testkit.Rows("19:35:41 19:35:41 19:35:41 19:35:41 19:35:41 19:35:41 2020-07-15 18:32:17.888 2020-07-15 18:32:17.888")) - // datetime - reset(tk) - tk.MustExec("alter table t modify c char(23)") - tk.MustExec("alter table t modify vc varchar(23)") - tk.MustExec("alter table t modify bny binary(23)") - tk.MustExec("alter table t modify vbny varbinary(23)") - tk.MustExec("insert into t values ('2020-07-15 18:32:17.888', '2020-07-15 18:32:17.888', '2020-07-15 18:32:17.888', '2020-07-15 18:32:17.888', '2020-07-15 18:32:17.888', '2020-07-15 18:32:17.888', '2020-07-15 18:32:17.888', '2020-07-15 18:32:17.888')") - tk.MustExec("alter table t modify c datetime") - tk.MustExec("alter table t modify vc datetime") - tk.MustExec("alter table t modify bny datetime") - tk.MustExec("alter table t modify vbny datetime") - tk.MustExec("alter table t modify bb datetime") - tk.MustExec("alter table t modify txt datetime") - tk.MustGetErrCode("alter table t modify e datetime", mysql.ErrUnsupportedDDLOperation) - tk.MustGetErrCode("alter table t modify s datetime", mysql.ErrUnsupportedDDLOperation) - tk.MustQuery("select * from t").Check(testkit.Rows("2020-07-15 18:32:18 2020-07-15 18:32:18 2020-07-15 18:32:18 2020-07-15 18:32:18 2020-07-15 18:32:18 2020-07-15 18:32:18 2020-07-15 18:32:17.888 2020-07-15 18:32:17.888")) - // timestamp - reset(tk) - tk.MustExec("alter table t modify c char(23)") - tk.MustExec("alter table t modify vc varchar(23)") - tk.MustExec("alter table t modify bny binary(23)") - tk.MustExec("alter table t modify vbny varbinary(23)") - tk.MustExec("insert into t values ('2020-07-15 18:32:17.888', '2020-07-15 18:32:17.888', '2020-07-15 18:32:17.888', '2020-07-15 18:32:17.888', '2020-07-15 18:32:17.888', '2020-07-15 18:32:17.888', '2020-07-15 18:32:17.888', '2020-07-15 18:32:17.888')") - tk.MustExec("alter table t modify c timestamp") - tk.MustExec("alter table t modify vc timestamp") - tk.MustExec("alter table t modify bny timestamp") - tk.MustExec("alter table t modify vbny timestamp") - tk.MustExec("alter table t modify bb timestamp") - tk.MustExec("alter table t modify txt timestamp") - tk.MustGetErrCode("alter table t modify e timestamp", mysql.ErrUnsupportedDDLOperation) - tk.MustGetErrCode("alter table t modify s timestamp", mysql.ErrUnsupportedDDLOperation) - tk.MustQuery("select * from t").Check(testkit.Rows("2020-07-15 18:32:18 2020-07-15 18:32:18 2020-07-15 18:32:18 2020-07-15 18:32:18 2020-07-15 18:32:18 2020-07-15 18:32:18 2020-07-15 18:32:17.888 2020-07-15 18:32:17.888")) - // year - reset(tk) - tk.MustExec("insert into t values ('2020', '91', '2', '2020', '20', '99', '2020-07-15 18:32:17.888', '2020-07-15 18:32:17.888')") - tk.MustExec("alter table t modify c year") - tk.MustExec("alter table t modify vc year") - tk.MustExec("alter table t modify bny year") - tk.MustExec("alter table t modify vbny year") - tk.MustExec("alter table t modify bb year") - tk.MustExec("alter table t modify txt year") - tk.MustExec("alter table t modify e year") - tk.MustExec("alter table t modify s year") - tk.MustQuery("select * from t").Check(testkit.Rows("2020 1991 2002 2020 2020 1999 2002 2002")) - - // To json data type. - reset(tk) - tk.MustExec("alter table t modify c char(15)") - tk.MustExec("alter table t modify vc varchar(15)") - tk.MustExec("alter table t modify bny binary(15)") - tk.MustExec("alter table t modify vbny varbinary(15)") - tk.MustExec("insert into t values ('{\"k1\": \"value\"}', '{\"k1\": \"value\"}', '{\"k1\": \"value\"}', '{\"k1\": \"value\"}', '{\"k1\": \"value\"}', '{\"k1\": \"value\"}', '{\"k1\": \"value\"}', '{\"k1\": \"value\"}')") - tk.MustExec("alter table t modify c json") - tk.MustExec("alter table t modify vc json") - tk.MustExec("alter table t modify bny json") - tk.MustExec("alter table t modify vbny json") - tk.MustExec("alter table t modify bb json") - tk.MustExec("alter table t modify txt json") - tk.MustExec("alter table t modify e json") - tk.MustExec("alter table t modify s json") - tk.MustQuery("select * from t").Check(testkit.Rows("{\"k1\": \"value\"} {\"k1\": \"value\"} {\"k1\": \"value\"} {\"k1\": \"value\"} {\"k1\": \"value\"} {\"k1\": \"value\"} \"{\\\"k1\\\": \\\"value\\\"}\" \"{\\\"k1\\\": \\\"value\\\"}\"")) - - reset(tk) - tk.MustExec("insert into t values ('123x', 'x123', 'abc', 'datetime', 'timestamp', 'date', '123', '123')") - tk.MustGetErrCode("alter table t modify c int", mysql.ErrTruncatedWrongValue) - - tk.MustGetErrCode("alter table t modify vc smallint", mysql.ErrTruncatedWrongValue) - - tk.MustGetErrCode("alter table t modify bny bigint", mysql.ErrTruncatedWrongValue) - - tk.MustGetErrCode("alter table t modify vbny datetime", mysql.ErrTruncatedWrongValue) - - tk.MustGetErrCode("alter table t modify bb timestamp", mysql.ErrTruncatedWrongValue) - - tk.MustGetErrCode("alter table t modify txt date", mysql.ErrTruncatedWrongValue) - - reset(tk) - tk.MustExec("alter table t modify vc varchar(20)") - tk.MustExec("insert into t(c, vc) values ('1x', '20200915110836')") - tk.MustGetErrCode("alter table t modify c year", mysql.ErrTruncatedWrongValue) - - // Special cases about different behavior between TiDB and MySQL. - // MySQL will get warning but TiDB not. - // MySQL will get "Warning 1292 Incorrect time value: '20200915110836' for column 'vc'" - tk.MustExec("alter table t modify vc time") - tk.MustQuery("select vc from t").Check(testkit.Rows("11:08:36")) - - // Both error but different error message. - // MySQL will get "ERROR 3140 (22032): Invalid JSON text: "The document root must not be followed by other values." at position 1 in value for column '#sql-5b_42.c'." error. - reset(tk) - tk.MustExec("alter table t modify c char(15)") - tk.MustExec("insert into t(c) values ('{\"k1\": \"value\"')") - tk.MustGetErrCode("alter table t modify c json", mysql.ErrInvalidJSONText) - - // MySQL will get "ERROR 1366 (HY000): Incorrect DECIMAL value: '0' for column '' at row -1" error. - tk.MustExec("insert into t(vc) values ('abc')") - tk.MustGetErrCode("alter table t modify vc decimal(5,3)", mysql.ErrBadNumber) -} - -func (s *testColumnTypeChangeSuite) TestColumnTypeChangeFromNumericToOthers(c *C) { - tk := testkit.NewTestKit(c, s.store) - tk.MustExec("use test") - // Enable column change variable. - tk.Se.GetSessionVars().EnableChangeColumnType = true - - // Set time zone to UTC. - originalTz := tk.Se.GetSessionVars().TimeZone - tk.Se.GetSessionVars().TimeZone = time.UTC - defer func() { - tk.Se.GetSessionVars().EnableChangeColumnType = false - tk.Se.GetSessionVars().TimeZone = originalTz - }() - - // Init string date type table. - reset := func(tk *testkit.TestKit) { - tk.MustExec("drop table if exists t") - tk.MustExec(` - create table t ( - d decimal(13, 7), - n numeric(5, 2), - r real(20, 12), - db real(32, 11), - f32 float(23), - f64 double(46), - b bit(5) - ) - `) - } - - // To integer data types. - // tinyint - reset(tk) - tk.MustExec("insert into t values (-258.12345, 333.33, 2000000.20000002, 323232323.3232323232, -111.11111111, -222222222222.222222222222222, b'10101')") - tk.MustGetErrCode("alter table t modify d tinyint", mysql.ErrDataOutOfRange) - tk.MustGetErrCode("alter table t modify n tinyint", mysql.ErrDataOutOfRange) - tk.MustGetErrCode("alter table t modify r tinyint", mysql.ErrDataOutOfRange) - tk.MustGetErrCode("alter table t modify db tinyint", mysql.ErrDataOutOfRange) - tk.MustExec("alter table t modify f32 tinyint") - tk.MustGetErrCode("alter table t modify f64 tinyint", mysql.ErrDataOutOfRange) - tk.MustExec("alter table t modify b tinyint") - tk.MustQuery("select * from t").Check(testkit.Rows("-258.1234500 333.33 2000000.20000002 323232323.32323235 -111 -222222222222.22223 21")) - // int - reset(tk) - tk.MustExec("insert into t values (-258.12345, 333.33, 2000000.20000002, 323232323.3232323232, -111.11111111, -222222222222.222222222222222, b'10101')") - tk.MustExec("alter table t modify d int") - tk.MustExec("alter table t modify n int") - tk.MustExec("alter table t modify r int") - tk.MustExec("alter table t modify db int") - tk.MustExec("alter table t modify f32 int") - tk.MustGetErrCode("alter table t modify f64 int", mysql.ErrDataOutOfRange) - tk.MustExec("alter table t modify b int") - tk.MustQuery("select * from t").Check(testkit.Rows("-258 333 2000000 323232323 -111 -222222222222.22223 21")) - // bigint - reset(tk) - tk.MustExec("insert into t values (-258.12345, 333.33, 2000000.20000002, 323232323.3232323232, -111.11111111, -222222222222.222222222222222, b'10101')") - tk.MustExec("alter table t modify d bigint") - tk.MustExec("alter table t modify n bigint") - tk.MustExec("alter table t modify r bigint") - tk.MustExec("alter table t modify db bigint") - tk.MustExec("alter table t modify f32 bigint") - tk.MustExec("alter table t modify f64 bigint") - tk.MustExec("alter table t modify b bigint") - tk.MustQuery("select * from t").Check(testkit.Rows("-258 333 2000000 323232323 -111 -222222222222 21")) - // unsigned bigint - reset(tk) - tk.MustExec("insert into t values (-258.12345, 333.33, 2000000.20000002, 323232323.3232323232, -111.11111111, -222222222222.222222222222222, b'10101')") - // MySQL will get "ERROR 1264 (22001): Data truncation: Out of range value for column 'd' at row 1". - tk.MustGetErrCode("alter table t modify d bigint unsigned", mysql.ErrDataOutOfRange) - tk.MustExec("alter table t modify n bigint unsigned") - tk.MustExec("alter table t modify r bigint unsigned") - tk.MustExec("alter table t modify db bigint unsigned") - // MySQL will get "ERROR 1264 (22001): Data truncation: Out of range value for column 'f32' at row 1". - tk.MustGetErrCode("alter table t modify f32 bigint unsigned", mysql.ErrDataOutOfRange) - // MySQL will get "ERROR 1264 (22001): Data truncation: Out of range value for column 'f64' at row 1". - tk.MustGetErrCode("alter table t modify f64 bigint unsigned", mysql.ErrDataOutOfRange) - tk.MustExec("alter table t modify b int") - tk.MustQuery("select * from t").Check(testkit.Rows("-258.1234500 333 2000000 323232323 -111.111115 -222222222222.22223 21")) - - // To string data types. - // char - reset(tk) - tk.MustExec("insert into t values (-258.12345, 333.33, 2000000.20000002, 323232323.3232323232, -111.11111111, -222222222222.222222222222222, b'10101')") - tk.MustExec("alter table t modify d char(20)") - tk.MustExec("alter table t modify n char(20)") - tk.MustExec("alter table t modify r char(20)") - // MySQL will get "ERROR 1406 (22001): Data truncation: Data too long for column 'db' at row 1". - tk.MustExec("alter table t modify db char(20)") - // MySQL will get "-111.111" rather than "-111.111115" at TiDB. - tk.MustExec("alter table t modify f32 char(20)") - // MySQL will get "ERROR 1406 (22001): Data truncation: Data too long for column 'f64' at row 1". - tk.MustExec("alter table t modify f64 char(20)") - tk.MustExec("alter table t modify b char(20)") - tk.MustQuery("select * from t").Check(testkit.Rows("-258.1234500 333.33 2000000.20000002 323232323.32323235 -111.111115 -222222222222.22223 \x15")) - - // varchar - reset(tk) - tk.MustExec("insert into t values (-258.12345, 333.33, 2000000.20000002, 323232323.3232323232, -111.11111111, -222222222222.222222222222222, b'10101')") - tk.MustExec("alter table t modify d varchar(30)") - tk.MustExec("alter table t modify n varchar(30)") - tk.MustExec("alter table t modify r varchar(30)") - tk.MustExec("alter table t modify db varchar(30)") - // MySQL will get "-111.111" rather than "-111.111115" at TiDB. - tk.MustExec("alter table t modify f32 varchar(30)") - // MySQL will get "ERROR 1406 (22001): Data truncation: Data too long for column 'f64' at row 1". - tk.MustExec("alter table t modify f64 varchar(30)") - tk.MustExec("alter table t modify b varchar(30)") - tk.MustQuery("select * from t").Check(testkit.Rows("-258.1234500 333.33 2000000.20000002 323232323.32323235 -111.111115 -222222222222.22223 \x15")) - - // binary - reset(tk) - tk.MustExec("insert into t values (-258.12345, 333.33, 2000000.20000002, 323232323.3232323232, -111.11111111, -222222222222.222222222222222, b'10101')") - tk.MustGetErrCode("alter table t modify d binary(10)", mysql.ErrDataTooLong) - tk.MustExec("alter table t modify n binary(10)") - tk.MustGetErrCode("alter table t modify r binary(10)", mysql.ErrDataTooLong) - tk.MustGetErrCode("alter table t modify db binary(10)", mysql.ErrDataTooLong) - // MySQL will run with no error. - tk.MustGetErrCode("alter table t modify f32 binary(10)", mysql.ErrDataTooLong) - tk.MustGetErrCode("alter table t modify f64 binary(10)", mysql.ErrDataTooLong) - tk.MustExec("alter table t modify b binary(10)") - tk.MustQuery("select * from t").Check(testkit.Rows("-258.1234500 333.33\x00\x00\x00\x00 2000000.20000002 323232323.32323235 -111.111115 -222222222222.22223 \x15\x00\x00\x00\x00\x00\x00\x00\x00\x00")) - - // varbinary - reset(tk) - tk.MustExec("insert into t values (-258.12345, 333.33, 2000000.20000002, 323232323.3232323232, -111.11111111, -222222222222.222222222222222, b'10101')") - tk.MustExec("alter table t modify d varbinary(30)") - tk.MustExec("alter table t modify n varbinary(30)") - tk.MustExec("alter table t modify r varbinary(30)") - tk.MustExec("alter table t modify db varbinary(30)") - // MySQL will get "-111.111" rather than "-111.111115" at TiDB. - tk.MustExec("alter table t modify f32 varbinary(30)") - // MySQL will get "ERROR 1406 (22001): Data truncation: Data too long for column 'f64' at row 1". - tk.MustExec("alter table t modify f64 varbinary(30)") - tk.MustExec("alter table t modify b varbinary(30)") - tk.MustQuery("select * from t").Check(testkit.Rows("-258.1234500 333.33 2000000.20000002 323232323.32323235 -111.111115 -222222222222.22223 \x15")) - - // blob - reset(tk) - tk.MustExec("insert into t values (-258.12345, 333.33, 2000000.20000002, 323232323.3232323232, -111.11111111, -222222222222.222222222222222, b'10101')") - tk.MustExec("alter table t modify d blob") - tk.MustExec("alter table t modify n blob") - tk.MustExec("alter table t modify r blob") - tk.MustExec("alter table t modify db blob") - // MySQL will get "-111.111" rather than "-111.111115" at TiDB. - tk.MustExec("alter table t modify f32 blob") - tk.MustExec("alter table t modify f64 blob") - tk.MustExec("alter table t modify b blob") - tk.MustQuery("select * from t").Check(testkit.Rows("-258.1234500 333.33 2000000.20000002 323232323.32323235 -111.111115 -222222222222.22223 \x15")) - - // text - reset(tk) - tk.MustExec("insert into t values (-258.12345, 333.33, 2000000.20000002, 323232323.3232323232, -111.11111111, -222222222222.222222222222222, b'10101')") - tk.MustExec("alter table t modify d text") - tk.MustExec("alter table t modify n text") - tk.MustExec("alter table t modify r text") - tk.MustExec("alter table t modify db text") - // MySQL will get "-111.111" rather than "-111.111115" at TiDB. - tk.MustExec("alter table t modify f32 text") - tk.MustExec("alter table t modify f64 text") - tk.MustExec("alter table t modify b text") - tk.MustQuery("select * from t").Check(testkit.Rows("-258.1234500 333.33 2000000.20000002 323232323.32323235 -111.111115 -222222222222.22223 \x15")) - - // enum - reset(tk) - tk.MustExec("insert into t values (-258.12345, 333.33, 2000000.20000002, 323232323.3232323232, -111.111, -222222222222.222222222222222, b'10101')") - tk.MustGetErrCode("alter table t modify d enum('-258.12345', '333.33', '2000000.20000002', '323232323.3232323232', '-111.111', '-222222222222.222222222222222', b'10101')", mysql.ErrUnsupportedDDLOperation) - tk.MustGetErrCode("alter table t modify n enum('-258.12345', '333.33', '2000000.20000002', '323232323.3232323232', '-111.111', '-222222222222.222222222222222', b'10101')", mysql.ErrUnsupportedDDLOperation) - tk.MustGetErrCode("alter table t modify r enum('-258.12345', '333.33', '2000000.20000002', '323232323.3232323232', '-111.111', '-222222222222.222222222222222', b'10101')", mysql.ErrUnsupportedDDLOperation) - tk.MustGetErrCode("alter table t modify db enum('-258.12345', '333.33', '2000000.20000002', '323232323.3232323232', '-111.111', '-222222222222.222222222222222', b'10101')", mysql.ErrUnsupportedDDLOperation) - tk.MustGetErrCode("alter table t modify f32 enum('-258.12345', '333.33', '2000000.20000002', '323232323.3232323232', '-111.111', '-222222222222.222222222222222', b'10101')", mysql.ErrUnsupportedDDLOperation) - tk.MustGetErrCode("alter table t modify f64 enum('-258.12345', '333.33', '2000000.20000002', '323232323.3232323232', '-111.111', '-222222222222.222222222222222', b'10101')", mysql.ErrUnsupportedDDLOperation) - tk.MustGetErrCode("alter table t modify b enum('-258.12345', '333.33', '2000000.20000002', '323232323.3232323232', '-111.111', '-222222222222.222222222222222', b'10101')", mysql.ErrUnsupportedDDLOperation) - tk.MustQuery("select * from t").Check(testkit.Rows("-258.1234500 333.33 2000000.20000002 323232323.32323235 -111.111 -222222222222.22223 \x15")) - - // set - reset(tk) - tk.MustExec("insert into t values (-258.12345, 333.33, 2000000.20000002, 323232323.3232323232, -111.111, -222222222222.222222222222222, b'10101')") - tk.MustGetErrCode("alter table t modify d set('-258.12345', '333.33', '2000000.20000002', '323232323.3232323232', '-111.111', '-222222222222.222222222222222', b'10101')", mysql.ErrUnsupportedDDLOperation) - tk.MustGetErrCode("alter table t modify n set('-258.12345', '333.33', '2000000.20000002', '323232323.3232323232', '-111.111', '-222222222222.222222222222222', b'10101')", mysql.ErrUnsupportedDDLOperation) - tk.MustGetErrCode("alter table t modify r set('-258.12345', '333.33', '2000000.20000002', '323232323.3232323232', '-111.111', '-222222222222.222222222222222', b'10101')", mysql.ErrUnsupportedDDLOperation) - tk.MustGetErrCode("alter table t modify db set('-258.12345', '333.33', '2000000.20000002', '323232323.3232323232', '-111.111', '-222222222222.222222222222222', b'10101')", mysql.ErrUnsupportedDDLOperation) - tk.MustGetErrCode("alter table t modify f32 set('-258.12345', '333.33', '2000000.20000002', '323232323.3232323232', '-111.111', '-222222222222.222222222222222', b'10101')", mysql.ErrUnsupportedDDLOperation) - tk.MustGetErrCode("alter table t modify f64 set('-258.12345', '333.33', '2000000.20000002', '323232323.3232323232', '-111.111', '-222222222222.222222222222222', b'10101')", mysql.ErrUnsupportedDDLOperation) - tk.MustGetErrCode("alter table t modify b set('-258.12345', '333.33', '2000000.20000002', '323232323.3232323232', '-111.111', '-222222222222.222222222222222', b'10101')", mysql.ErrUnsupportedDDLOperation) - tk.MustQuery("select * from t").Check(testkit.Rows("-258.1234500 333.33 2000000.20000002 323232323.32323235 -111.111 -222222222222.22223 \x15")) - - // To date and time data types. - // datetime - reset(tk) - tk.MustExec("insert into t values (200805.11, 307.333, 20200805.11111111, 20200805111307.11111111, 200805111307.11111111, 20200805111307.11111111, b'10101')") - // MySQL will get "ERROR 1292 (22001) Data truncation: Incorrect datetime value: '200805.1100000' for column 'd' at row 1". - tk.MustExec("alter table t modify d datetime") - // MySQL will get "ERROR 1292 (22001) Data truncation: Incorrect datetime value: '307.33' for column 'n' at row 1". - tk.MustExec("alter table t modify n datetime") - tk.MustGetErrCode("alter table t modify r datetime", mysql.ErrUnsupportedDDLOperation) - tk.MustGetErrCode("alter table t modify db datetime", mysql.ErrUnsupportedDDLOperation) - tk.MustGetErrCode("alter table t modify f32 datetime", mysql.ErrUnsupportedDDLOperation) - tk.MustGetErrCode("alter table t modify f64 datetime", mysql.ErrUnsupportedDDLOperation) - tk.MustGetErrCode("alter table t modify b datetime", mysql.ErrUnsupportedDDLOperation) - tk.MustQuery("select * from t").Check(testkit.Rows("2020-08-05 00:00:00 2000-03-07 00:00:00 20200805.11111111 20200805111307.11 200805100000 20200805111307.11 \x15")) - // time - reset(tk) - tk.MustExec("insert into t values (200805.11, 307.333, 20200805.11111111, 20200805111307.11111111, 200805111307.11111111, 20200805111307.11111111, b'10101')") - tk.MustExec("alter table t modify d time") - tk.MustExec("alter table t modify n time") - tk.MustGetErrCode("alter table t modify r time", mysql.ErrTruncatedWrongValue) - tk.MustExec("alter table t modify db time") - tk.MustExec("alter table t modify f32 time") - tk.MustExec("alter table t modify f64 time") - tk.MustGetErrCode("alter table t modify b time", mysql.ErrUnsupportedDDLOperation) - tk.MustQuery("select * from t").Check(testkit.Rows("20:08:05 00:03:07 20200805.11111111 11:13:07 10:00:00 11:13:07 \x15")) - // date - reset(tk) - tk.MustExec("insert into t values (200805.11, 307.333, 20200805.11111111, 20200805111307.11111111, 200805111307.11111111, 20200805111307.11111111, b'10101')") - // MySQL will get "ERROR 1292 (22001) Data truncation: Incorrect date value: '200805.1100000' for column 'd' at row 1". - tk.MustExec("alter table t modify d date") - // MySQL will get "ERROR 1292 (22001) Data truncation: Incorrect date value: '307.33' for column 'n' at row 1". - tk.MustExec("alter table t modify n date") - tk.MustGetErrCode("alter table t modify r date", mysql.ErrUnsupportedDDLOperation) - tk.MustGetErrCode("alter table t modify db date", mysql.ErrUnsupportedDDLOperation) - tk.MustGetErrCode("alter table t modify f32 date", mysql.ErrUnsupportedDDLOperation) - tk.MustGetErrCode("alter table t modify f64 date", mysql.ErrUnsupportedDDLOperation) - tk.MustGetErrCode("alter table t modify b date", mysql.ErrUnsupportedDDLOperation) - tk.MustQuery("select * from t").Check(testkit.Rows("2020-08-05 2000-03-07 20200805.11111111 20200805111307.11 200805100000 20200805111307.11 \x15")) - // timestamp - reset(tk) - tk.MustExec("insert into t values (200805.11, 307.333, 20200805.11111111, 20200805111307.11111111, 200805111307.11111111, 20200805111307.11111111, b'10101')") - // MySQL will get "ERROR 1292 (22001) Data truncation: Incorrect datetime value: '200805.1100000' for column 'd' at row 1". - tk.MustExec("alter table t modify d timestamp") - // MySQL will get "ERROR 1292 (22001) Data truncation: Incorrect datetime value: '307.33' for column 'n' at row 1". - tk.MustExec("alter table t modify n timestamp") - tk.MustGetErrCode("alter table t modify r timestamp", mysql.ErrUnsupportedDDLOperation) - tk.MustGetErrCode("alter table t modify db timestamp", mysql.ErrUnsupportedDDLOperation) - tk.MustGetErrCode("alter table t modify f32 timestamp", mysql.ErrUnsupportedDDLOperation) - tk.MustGetErrCode("alter table t modify f64 timestamp", mysql.ErrUnsupportedDDLOperation) - tk.MustGetErrCode("alter table t modify b timestamp", mysql.ErrUnsupportedDDLOperation) - tk.MustQuery("select * from t").Check(testkit.Rows("2020-08-05 00:00:00 2000-03-07 00:00:00 20200805.11111111 20200805111307.11 200805100000 20200805111307.11 \x15")) - // year - reset(tk) - tk.MustExec("insert into t values (200805.11, 307.333, 2.55555, 98.1111111, 2154.00001, 20200805111307.11111111, b'10101')") - tk.MustGetErrCode("alter table t modify d year", mysql.ErrDataOutOfRange) - tk.MustGetErrCode("alter table t modify n year", mysql.ErrDataOutOfRange) - // MySQL will get "ERROR 1264 (22001) Data truncation: Out of range value for column 'r' at row 1". - tk.MustExec("alter table t modify r year") - // MySQL will get "ERROR 1264 (22001) Data truncation: Out of range value for column 'db' at row 1". - tk.MustExec("alter table t modify db year") - // MySQL will get "ERROR 1264 (22001) Data truncation: Out of range value for column 'f32' at row 1". - tk.MustExec("alter table t modify f32 year") - tk.MustGetErrCode("alter table t modify f64 year", mysql.ErrDataOutOfRange) - tk.MustExec("alter table t modify b year") - tk.MustQuery("select * from t").Check(testkit.Rows("200805.1100000 307.33 2003 1998 2154 20200805111307.11 2021")) - - // To json data type. - reset(tk) - tk.MustExec("insert into t values (-258.12345, 333.33, 2000000.20000002, 323232323.3232323232, -111.11111111, -222222222222.222222222222222, b'10101')") - tk.MustExec("alter table t modify d json") - tk.MustExec("alter table t modify n json") - tk.MustExec("alter table t modify r json") - tk.MustExec("alter table t modify db json") - tk.MustExec("alter table t modify f32 json") - tk.MustExec("alter table t modify f64 json") - tk.MustExec("alter table t modify b json") - tk.MustQuery("select * from t").Check(testkit.Rows("-258.12345 333.33 2000000.20000002 323232323.32323235 -111.11111450195312 -222222222222.22223 \"\\u0015\"")) -} - -// Test issue #20529. -func (s *testColumnTypeChangeSuite) TestColumnTypeChangeIgnoreDisplayLength(c *C) { - tk := testkit.NewTestKit(c, s.store) - tk.MustExec("use test") - tk.Se.GetSessionVars().EnableChangeColumnType = true - defer func() { - tk.Se.GetSessionVars().EnableChangeColumnType = false - }() - - originalHook := s.dom.DDL().GetHook() - defer s.dom.DDL().(ddl.DDLForTest).SetHook(originalHook) - - var assertResult bool - assertHasAlterWriteReorg := func(tbl table.Table) { - // Restore the assert result to false. - assertResult = false - hook := &ddl.TestDDLCallback{} - hook.OnJobRunBeforeExported = func(job *model.Job) { - if tbl.Meta().ID != job.TableID { - return - } - if job.SchemaState == model.StateWriteReorganization { - assertResult = true - } - } - s.dom.DDL().(ddl.DDLForTest).SetHook(hook) - } - - // Change int to tinyint. - // Although display length is increased, the default flen is decreased, reorg is needed. - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int(1))") - tbl := testGetTableByName(c, tk.Se, "test", "t") - assertHasAlterWriteReorg(tbl) - tk.MustExec("alter table t modify column a tinyint(3)") - c.Assert(assertResult, Equals, true) - - // Change tinyint to tinyint - // Although display length is decreased, default flen is the same, reorg is not needed. - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a tinyint(3))") - tbl = testGetTableByName(c, tk.Se, "test", "t") - assertHasAlterWriteReorg(tbl) - tk.MustExec("alter table t modify column a tinyint(1)") - c.Assert(assertResult, Equals, false) - tk.MustExec("drop table if exists t") -} From b3bccc6516284221958cdb4e8b20dab9bc2db131 Mon Sep 17 00:00:00 2001 From: AilinKid <314806019@qq.com> Date: Tue, 17 Nov 2020 18:01:49 +0800 Subject: [PATCH 3/3] resolve the conflicts Signed-off-by: AilinKid <314806019@qq.com> --- ddl/column.go | 80 ------------------------------------------ ddl/column_test.go | 6 +--- ddl/db_test.go | 19 ++++++++++ ddl/ddl_api.go | 24 ++----------- ddl/ddl_worker_test.go | 69 ------------------------------------ 5 files changed, 22 insertions(+), 176 deletions(-) diff --git a/ddl/column.go b/ddl/column.go index ddd13aa14b5cf..a35d2740a0f96 100644 --- a/ddl/column.go +++ b/ddl/column.go @@ -352,7 +352,6 @@ func onSetDefaultValue(t *meta.Meta, job *model.Job) (ver int64, _ error) { return updateColumnDefaultValue(t, job, newCol, &newCol.Name) } -<<<<<<< HEAD func (w *worker) onModifyColumn(t *meta.Meta, job *model.Job) (ver int64, _ error) { newCol := &model.ColumnInfo{} oldColName := &model.CIStr{} @@ -360,85 +359,6 @@ func (w *worker) onModifyColumn(t *meta.Meta, job *model.Job) (ver int64, _ erro var modifyColumnTp byte var updatedAutoRandomBits uint64 err := job.DecodeArgs(newCol, oldColName, pos, &modifyColumnTp, &updatedAutoRandomBits) -======= -func needChangeColumnData(oldCol, newCol *model.ColumnInfo) bool { - toUnsigned := mysql.HasUnsignedFlag(newCol.Flag) - originUnsigned := mysql.HasUnsignedFlag(oldCol.Flag) - needTruncationOrToggleSign := func() bool { - return (newCol.Flen > 0 && newCol.Flen < oldCol.Flen) || (toUnsigned != originUnsigned) - } - // Ignore the potential max display length represented by integer's flen, use default flen instead. - oldColFlen, _ := mysql.GetDefaultFieldLengthAndDecimal(oldCol.Tp) - newColFlen, _ := mysql.GetDefaultFieldLengthAndDecimal(newCol.Tp) - needTruncationOrToggleSignForInteger := func() bool { - return (newColFlen > 0 && newColFlen < oldColFlen) || (toUnsigned != originUnsigned) - } - - // Deal with the same type. - if oldCol.Tp == newCol.Tp { - switch oldCol.Tp { - case mysql.TypeNewDecimal: - // Since type decimal will encode the precision, frac, negative(signed) and wordBuf into storage together, there is no short - // cut to eliminate data reorg change for column type change between decimal. - return oldCol.Flen != newCol.Flen || oldCol.Decimal != newCol.Decimal || toUnsigned != originUnsigned - case mysql.TypeEnum, mysql.TypeSet: - return isElemsChangedToModifyColumn(oldCol.Elems, newCol.Elems) - case mysql.TypeTiny, mysql.TypeShort, mysql.TypeInt24, mysql.TypeLong, mysql.TypeLonglong: - return toUnsigned != originUnsigned - } - - return needTruncationOrToggleSign() - } - - // Deal with the different type. - switch oldCol.Tp { - case mysql.TypeVarchar, mysql.TypeString, mysql.TypeVarString, mysql.TypeBlob, mysql.TypeTinyBlob, mysql.TypeMediumBlob, mysql.TypeLongBlob: - switch newCol.Tp { - case mysql.TypeVarchar, mysql.TypeString, mysql.TypeVarString, mysql.TypeBlob, mysql.TypeTinyBlob, mysql.TypeMediumBlob, mysql.TypeLongBlob: - return needTruncationOrToggleSign() - } - case mysql.TypeTiny, mysql.TypeShort, mysql.TypeInt24, mysql.TypeLong, mysql.TypeLonglong: - switch newCol.Tp { - case mysql.TypeTiny, mysql.TypeShort, mysql.TypeInt24, mysql.TypeLong, mysql.TypeLonglong: - return needTruncationOrToggleSignForInteger() - } - case mysql.TypeFloat, mysql.TypeDouble: - switch newCol.Tp { - case mysql.TypeFloat, mysql.TypeDouble: - return needTruncationOrToggleSign() - } - } - - return true -} - -func isElemsChangedToModifyColumn(oldElems, newElems []string) bool { - if len(newElems) < len(oldElems) { - return true - } - for index, oldElem := range oldElems { - newElem := newElems[index] - if oldElem != newElem { - return true - } - } - return false -} - -type modifyColumnJobParameter struct { - newCol *model.ColumnInfo - oldColName *model.CIStr - modifyColumnTp byte - updatedAutoRandomBits uint64 - changingCol *model.ColumnInfo - changingIdxs []*model.IndexInfo - pos *ast.ColumnPosition -} - -func getModifyColumnInfo(t *meta.Meta, job *model.Job) (*model.DBInfo, *model.TableInfo, *model.ColumnInfo, *modifyColumnJobParameter, error) { - jobParam := &modifyColumnJobParameter{pos: &ast.ColumnPosition{}} - err := job.DecodeArgs(&jobParam.newCol, &jobParam.oldColName, jobParam.pos, &jobParam.modifyColumnTp, &jobParam.updatedAutoRandomBits, &jobParam.changingCol, &jobParam.changingIdxs) ->>>>>>> 38f876044... ddl: ignore integer zerofill size attribute when changing the column types (#20862) if err != nil { job.State = model.JobStateCancelled return ver, errors.Trace(err) diff --git a/ddl/column_test.go b/ddl/column_test.go index 1ac16bbcf8d5a..7dabc57219b1d 100644 --- a/ddl/column_test.go +++ b/ddl/column_test.go @@ -938,11 +938,7 @@ func (s *testColumnSuite) TestModifyColumn(c *C) { err error }{ {"int", "bigint", nil}, -<<<<<<< HEAD - {"int", "int unsigned", errUnsupportedModifyColumn.GenWithStackByArgs("length 10 is less than origin 11")}, -======= - {"int", "int unsigned", errUnsupportedModifyColumn.GenWithStackByArgs("can't change unsigned integer to signed or vice versa, and tidb_enable_change_column_type is false")}, ->>>>>>> 38f876044... ddl: ignore integer zerofill size attribute when changing the column types (#20862) + {"int", "int unsigned", errUnsupportedModifyColumn.GenWithStackByArgs("can't change unsigned integer to signed or vice versa")}, {"varchar(10)", "text", nil}, {"varbinary(10)", "blob", nil}, {"text", "blob", errUnsupportedModifyCharset.GenWithStackByArgs("charset from utf8mb4 to binary")}, diff --git a/ddl/db_test.go b/ddl/db_test.go index b401bd7d9a346..879692f8696d7 100644 --- a/ddl/db_test.go +++ b/ddl/db_test.go @@ -4930,3 +4930,22 @@ func init() { domain.SchemaOutOfDateRetryInterval = int64(50 * time.Millisecond) domain.SchemaOutOfDateRetryTimes = int32(50) } + +// Test issue #20529. +func (s *testSerialDBSuite) TestColumnTypeChangeIgnoreDisplayLength(c *C) { + tk := testkit.NewTestKit(c, s.store) + tk.MustExec("use test") + + // Change int to int(3). + // Although display length is increased, the default flen is decreased, reorg is needed. + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int)") + tk.MustExec("alter table t modify column a int(3)") + + // Change int to bigint(1) + // Although display length is decreased, default flen is the same, reorg is not needed. + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int)") + tk.MustExec("alter table t modify column a bigint(1)") + tk.MustExec("drop table if exists t") +} diff --git a/ddl/ddl_api.go b/ddl/ddl_api.go index dbf27a46ef4e3..cb2e509831d5a 100644 --- a/ddl/ddl_api.go +++ b/ddl/ddl_api.go @@ -2704,16 +2704,12 @@ func checkModifyCharsetAndCollation(toCharset, toCollate, origCharset, origColla // CheckModifyTypeCompatible checks whether changes column type to another is compatible considering // field length and precision. -<<<<<<< HEAD func CheckModifyTypeCompatible(origin *types.FieldType, to *types.FieldType) error { -======= -func CheckModifyTypeCompatible(origin *types.FieldType, to *types.FieldType) (allowedChangeColumnValueMsg string, err error) { + unsupportedMsg := fmt.Sprintf("type %v not match origin %v", to.CompactStr(), origin.CompactStr()) var ( toFlen = to.Flen originFlen = origin.Flen ) ->>>>>>> 38f876044... ddl: ignore integer zerofill size attribute when changing the column types (#20862) - unsupportedMsg := fmt.Sprintf("type %v not match origin %v", to.CompactStr(), origin.CompactStr()) switch origin.Tp { case mysql.TypeVarchar, mysql.TypeString, mysql.TypeVarString, mysql.TypeBlob, mysql.TypeTinyBlob, mysql.TypeMediumBlob, mysql.TypeLongBlob: @@ -2726,16 +2722,10 @@ func CheckModifyTypeCompatible(origin *types.FieldType, to *types.FieldType) (al case mysql.TypeTiny, mysql.TypeShort, mysql.TypeInt24, mysql.TypeLong, mysql.TypeLonglong: switch to.Tp { case mysql.TypeTiny, mysql.TypeShort, mysql.TypeInt24, mysql.TypeLong, mysql.TypeLonglong: -<<<<<<< HEAD -======= // For integers, we should ignore the potential display length represented by flen, using // the default flen of the type. originFlen, _ = mysql.GetDefaultFieldLengthAndDecimal(origin.Tp) toFlen, _ = mysql.GetDefaultFieldLengthAndDecimal(to.Tp) - // Changing integer to integer, whether reorg is necessary is depend on the flen/decimal/signed. - skipSignCheck = true - skipLenCheck = true ->>>>>>> 38f876044... ddl: ignore integer zerofill size attribute when changing the column types (#20862) default: return errUnsupportedModifyColumn.GenWithStackByArgs(unsupportedMsg) } @@ -2774,19 +2764,9 @@ func CheckModifyTypeCompatible(origin *types.FieldType, to *types.FieldType) (al return errUnsupportedModifyColumn.GenWithStackByArgs(unsupportedMsg) } } - -<<<<<<< HEAD - if to.Flen > 0 && to.Flen < origin.Flen { - msg := fmt.Sprintf("length %d is less than origin %d", to.Flen, origin.Flen) - return errUnsupportedModifyColumn.GenWithStackByArgs(msg) -======= if toFlen > 0 && toFlen < originFlen { msg := fmt.Sprintf("length %d is less than origin %d", toFlen, originFlen) - if skipLenCheck { - return msg, errUnsupportedModifyColumn.GenWithStackByArgs(msg) - } - return "", errUnsupportedModifyColumn.GenWithStackByArgs(msg) ->>>>>>> 38f876044... ddl: ignore integer zerofill size attribute when changing the column types (#20862) + return errUnsupportedModifyColumn.GenWithStackByArgs(msg) } if to.Decimal > 0 && to.Decimal < origin.Decimal { msg := fmt.Sprintf("decimal %d is less than origin %d", to.Decimal, origin.Decimal) diff --git a/ddl/ddl_worker_test.go b/ddl/ddl_worker_test.go index 04a408f66eb77..8a96101ab3010 100644 --- a/ddl/ddl_worker_test.go +++ b/ddl/ddl_worker_test.go @@ -439,7 +439,6 @@ func buildCancelJobTests(firstID int64) []testCancelJob { {act: model.ActionShardRowID, jobIDs: []int64{firstID + 17}, cancelRetErrs: noErrs, cancelState: model.StateNone}, {act: model.ActionModifyColumn, jobIDs: []int64{firstID + 18}, cancelRetErrs: noErrs, cancelState: model.StateNone}, -<<<<<<< HEAD {act: model.ActionAddForeignKey, jobIDs: []int64{firstID + 19}, cancelRetErrs: noErrs, cancelState: model.StateNone}, {act: model.ActionAddForeignKey, jobIDs: []int64{firstID + 20}, cancelRetErrs: []error{admin.ErrCancelFinishedDDLJob.GenWithStackByArgs(firstID + 20)}, cancelState: model.StatePublic}, {act: model.ActionDropForeignKey, jobIDs: []int64{firstID + 21}, cancelRetErrs: noErrs, cancelState: model.StateNone}, @@ -465,57 +464,6 @@ func buildCancelJobTests(firstID int64) []testCancelJob { {act: model.ActionAddTablePartition, jobIDs: []int64{firstID + 42}, cancelRetErrs: noErrs, cancelState: model.StateNone}, {act: model.ActionAddTablePartition, jobIDs: []int64{firstID + 43}, cancelRetErrs: noErrs, cancelState: model.StateReplicaOnly}, {act: model.ActionAddTablePartition, jobIDs: []int64{firstID + 44}, cancelRetErrs: []error{admin.ErrCancelFinishedDDLJob}, cancelState: model.StatePublic}, -======= - {act: model.ActionModifyColumn, jobIDs: []int64{firstID + 19}, cancelRetErrs: noErrs, cancelState: model.StateDeleteOnly}, - - {act: model.ActionAddForeignKey, jobIDs: []int64{firstID + 20}, cancelRetErrs: noErrs, cancelState: model.StateNone}, - {act: model.ActionAddForeignKey, jobIDs: []int64{firstID + 21}, cancelRetErrs: []error{admin.ErrCancelFinishedDDLJob.GenWithStackByArgs(firstID + 21)}, cancelState: model.StatePublic}, - {act: model.ActionDropForeignKey, jobIDs: []int64{firstID + 22}, cancelRetErrs: noErrs, cancelState: model.StateNone}, - {act: model.ActionDropForeignKey, jobIDs: []int64{firstID + 23}, cancelRetErrs: []error{admin.ErrCancelFinishedDDLJob.GenWithStackByArgs(firstID + 23)}, cancelState: model.StatePublic}, - - {act: model.ActionRenameTable, jobIDs: []int64{firstID + 24}, cancelRetErrs: noErrs, cancelState: model.StateNone}, - {act: model.ActionRenameTable, jobIDs: []int64{firstID + 25}, cancelRetErrs: []error{admin.ErrCancelFinishedDDLJob.GenWithStackByArgs(firstID + 25)}, cancelState: model.StatePublic}, - - {act: model.ActionModifyTableCharsetAndCollate, jobIDs: []int64{firstID + 26}, cancelRetErrs: noErrs, cancelState: model.StateNone}, - {act: model.ActionModifyTableCharsetAndCollate, jobIDs: []int64{firstID + 27}, cancelRetErrs: []error{admin.ErrCancelFinishedDDLJob.GenWithStackByArgs(firstID + 27)}, cancelState: model.StatePublic}, - {act: model.ActionTruncateTablePartition, jobIDs: []int64{firstID + 28}, cancelRetErrs: noErrs, cancelState: model.StateNone}, - {act: model.ActionTruncateTablePartition, jobIDs: []int64{firstID + 29}, cancelRetErrs: []error{admin.ErrCancelFinishedDDLJob.GenWithStackByArgs(firstID + 29)}, cancelState: model.StatePublic}, - {act: model.ActionModifySchemaCharsetAndCollate, jobIDs: []int64{firstID + 31}, cancelRetErrs: noErrs, cancelState: model.StateNone}, - {act: model.ActionModifySchemaCharsetAndCollate, jobIDs: []int64{firstID + 32}, cancelRetErrs: []error{admin.ErrCancelFinishedDDLJob.GenWithStackByArgs(firstID + 32)}, cancelState: model.StatePublic}, - - {act: model.ActionAddPrimaryKey, jobIDs: []int64{firstID + 33}, cancelRetErrs: noErrs, cancelState: model.StateDeleteOnly}, - {act: model.ActionAddPrimaryKey, jobIDs: []int64{firstID + 34}, cancelRetErrs: noErrs, cancelState: model.StateWriteOnly}, - {act: model.ActionAddPrimaryKey, jobIDs: []int64{firstID + 35}, cancelRetErrs: noErrs, cancelState: model.StateWriteReorganization}, - {act: model.ActionAddPrimaryKey, jobIDs: []int64{firstID + 36}, cancelRetErrs: []error{admin.ErrCancelFinishedDDLJob.GenWithStackByArgs(firstID + 36)}, cancelState: model.StatePublic}, - {act: model.ActionDropPrimaryKey, jobIDs: []int64{firstID + 37}, cancelRetErrs: noErrs, cancelState: model.StateWriteOnly}, - {act: model.ActionDropPrimaryKey, jobIDs: []int64{firstID + 38}, cancelRetErrs: []error{admin.ErrCannotCancelDDLJob.GenWithStackByArgs(firstID + 38)}, cancelState: model.StateDeleteOnly}, - - {act: model.ActionAddColumns, jobIDs: []int64{firstID + 39}, cancelRetErrs: noErrs, cancelState: model.StateDeleteOnly}, - {act: model.ActionAddColumns, jobIDs: []int64{firstID + 40}, cancelRetErrs: noErrs, cancelState: model.StateWriteOnly}, - {act: model.ActionAddColumns, jobIDs: []int64{firstID + 41}, cancelRetErrs: noErrs, cancelState: model.StateWriteReorganization}, - {act: model.ActionAddColumns, jobIDs: []int64{firstID + 42}, cancelRetErrs: []error{admin.ErrCancelFinishedDDLJob.GenWithStackByArgs(firstID + 42)}, cancelState: model.StatePublic}, - - {act: model.ActionDropColumns, jobIDs: []int64{firstID + 43}, cancelRetErrs: []error{admin.ErrCannotCancelDDLJob.GenWithStackByArgs(firstID + 43)}, cancelState: model.StateDeleteOnly}, - {act: model.ActionDropColumns, jobIDs: []int64{firstID + 44}, cancelRetErrs: []error{admin.ErrCannotCancelDDLJob.GenWithStackByArgs(firstID + 44)}, cancelState: model.StateWriteOnly}, - {act: model.ActionDropColumns, jobIDs: []int64{firstID + 45}, cancelRetErrs: []error{admin.ErrCannotCancelDDLJob.GenWithStackByArgs(firstID + 45)}, cancelState: model.StateWriteReorganization}, - - {act: model.ActionAlterIndexVisibility, jobIDs: []int64{firstID + 47}, cancelRetErrs: noErrs, cancelState: model.StateNone}, - {act: model.ActionAlterIndexVisibility, jobIDs: []int64{firstID + 48}, cancelRetErrs: []error{admin.ErrCancelFinishedDDLJob.GenWithStackByArgs(firstID + 48)}, cancelState: model.StatePublic}, - - {act: model.ActionExchangeTablePartition, jobIDs: []int64{firstID + 54}, cancelRetErrs: noErrs, cancelState: model.StateNone}, - {act: model.ActionExchangeTablePartition, jobIDs: []int64{firstID + 55}, cancelRetErrs: []error{admin.ErrCancelFinishedDDLJob.GenWithStackByArgs(firstID + 55)}, cancelState: model.StatePublic}, - - {act: model.ActionAddTablePartition, jobIDs: []int64{firstID + 60}, cancelRetErrs: noErrs, cancelState: model.StateNone}, - {act: model.ActionAddTablePartition, jobIDs: []int64{firstID + 61}, cancelRetErrs: noErrs, cancelState: model.StateReplicaOnly}, - {act: model.ActionAddTablePartition, jobIDs: []int64{firstID + 62}, cancelRetErrs: []error{admin.ErrCancelFinishedDDLJob}, cancelState: model.StatePublic}, - - // modify column has two different types, normal-type and reorg-type. The latter has 5 states and it can be cancelled except the public state. - {act: model.ActionModifyColumn, jobIDs: []int64{firstID + 65}, cancelRetErrs: noErrs, cancelState: model.StateNone}, - {act: model.ActionModifyColumn, jobIDs: []int64{firstID + 66}, cancelRetErrs: noErrs, cancelState: model.StateDeleteOnly}, - {act: model.ActionModifyColumn, jobIDs: []int64{firstID + 67}, cancelRetErrs: noErrs, cancelState: model.StateWriteOnly}, - {act: model.ActionModifyColumn, jobIDs: []int64{firstID + 68}, cancelRetErrs: noErrs, cancelState: model.StateWriteReorganization}, - {act: model.ActionModifyColumn, jobIDs: []int64{firstID + 69}, cancelRetErrs: []error{admin.ErrCancelFinishedDDLJob}, cancelState: model.StatePublic}, ->>>>>>> 38f876044... ddl: ignore integer zerofill size attribute when changing the column types (#20862) } return tests @@ -782,23 +730,6 @@ func (s *testDDLSuite) TestCancelJob(c *C) { changedCol := model.FindColumnInfo(changedTable.Meta().Columns, col.Name.L) c.Assert(changedCol.DefaultValue, IsNil) -<<<<<<< HEAD -======= - // modify delete-only-state column, - col.FieldType.Tp = mysql.TypeTiny - col.FieldType.Flen = col.FieldType.Flen - 1 - updateTest(&tests[16]) - modifyColumnArgs = []interface{}{col, col.Name, &ast.ColumnPosition{}, byte(0), uint64(0)} - cancelState = model.StateNone - doDDLJobErrWithSchemaState(ctx, d, c, dbInfo.ID, tblInfo.ID, test.act, modifyColumnArgs, &cancelState) - c.Check(checkErr, IsNil) - changedTable = testGetTable(c, d, dbInfo.ID, tblInfo.ID) - changedCol = model.FindColumnInfo(changedTable.Meta().Columns, col.Name.L) - c.Assert(changedCol.FieldType.Tp, Equals, mysql.TypeLonglong) - c.Assert(changedCol.FieldType.Flen, Equals, col.FieldType.Flen+1) - col.FieldType.Flen++ - ->>>>>>> 38f876044... ddl: ignore integer zerofill size attribute when changing the column types (#20862) // Test add foreign key failed cause by canceled. updateTest(&tests[16]) addForeignKeyArgs := []interface{}{model.FKInfo{Name: model.NewCIStr("fk1")}}