Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ddl: ignore integer zerofill size attribute when changing the column types #20862

Merged
merged 11 commits into from
Nov 11, 2020
12 changes: 10 additions & 2 deletions ddl/column.go
Original file line number Diff line number Diff line change
Expand Up @@ -688,7 +688,13 @@ 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
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.
Expand All @@ -700,6 +706,8 @@ func needChangeColumnData(oldCol, newCol *model.ColumnInfo) bool {
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()
Expand All @@ -715,7 +723,7 @@ func needChangeColumnData(oldCol, newCol *model.ColumnInfo) bool {
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 needTruncationOrToggleSign()
return needTruncationOrToggleSignForInteger()
}
case mysql.TypeFloat, mysql.TypeDouble:
switch newCol.Tp {
Expand Down
2 changes: 1 addition & 1 deletion ddl/column_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1152,7 +1152,7 @@ func (s *testColumnSuite) TestModifyColumn(c *C) {
err error
}{
{"int", "bigint", nil},
{"int", "int unsigned", errUnsupportedModifyColumn.GenWithStackByArgs("length 10 is less than origin 11, and tidb_enable_change_column_type is false")},
{"int", "int unsigned", errUnsupportedModifyColumn.GenWithStackByArgs("can't change unsigned integer to signed or vice versa, and tidb_enable_change_column_type is false")},
{"varchar(10)", "text", nil},
{"varbinary(10)", "blob", nil},
{"text", "blob", errUnsupportedModifyCharset.GenWithStackByArgs("charset from utf8mb4 to binary")},
Expand Down
48 changes: 48 additions & 0 deletions ddl/column_type_change_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -939,3 +939,51 @@ func (s *testColumnTypeChangeSuite) TestColumnTypeChangeFromNumericToOthers(c *C
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")
}
12 changes: 10 additions & 2 deletions ddl/ddl_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -3385,6 +3385,10 @@ func checkModifyCharsetAndCollation(toCharset, toCollate, origCharset, origColla
// CheckModifyTypeCompatible checks whether changes column type to another is compatible considering
// field length and precision.
func CheckModifyTypeCompatible(origin *types.FieldType, to *types.FieldType) (allowedChangeColumnValueMsg string, err error) {
var (
toFlen = to.Flen
originFlen = origin.Flen
)
unsupportedMsg := fmt.Sprintf("type %v not match origin %v", to.CompactStr(), origin.CompactStr())
var skipSignCheck bool
var skipLenCheck bool
Expand All @@ -3405,6 +3409,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:
// 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
Expand Down Expand Up @@ -3527,8 +3535,8 @@ func CheckModifyTypeCompatible(origin *types.FieldType, to *types.FieldType) (al
}
}

if to.Flen > 0 && to.Flen < origin.Flen {
msg := fmt.Sprintf("length %d is less than origin %d", to.Flen, origin.Flen)
if toFlen > 0 && toFlen < originFlen {
msg := fmt.Sprintf("length %d is less than origin %d", toFlen, originFlen)
if skipLenCheck {
return msg, errUnsupportedModifyColumn.GenWithStackByArgs(msg)
}
Expand Down
13 changes: 7 additions & 6 deletions ddl/ddl_worker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -456,7 +456,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},
{act: model.ActionModifyColumn, jobIDs: []int64{firstID + 19}, cancelRetErrs: []error{admin.ErrCancelFinishedDDLJob.GenWithStackByArgs(firstID + 19)}, cancelState: model.StateDeleteOnly},
{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},
Expand Down Expand Up @@ -773,7 +773,7 @@ func (s *testDDLSerialSuite) TestCancelJob(c *C) {
// modify none-state 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)
Expand All @@ -784,13 +784,14 @@ func (s *testDDLSerialSuite) TestCancelJob(c *C) {
col.FieldType.Tp = mysql.TypeTiny
col.FieldType.Flen = col.FieldType.Flen - 1
updateTest(&tests[16])
modifyColumnArgs = []interface{}{col, col.Name, &ast.ColumnPosition{}, byte(0)}
doDDLJobSuccess(ctx, d, c, dbInfo.ID, tblInfo.ID, test.act, modifyColumnArgs)
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.TypeTiny)
c.Assert(changedCol.FieldType.Flen, Equals, col.FieldType.Flen)
c.Assert(changedCol.FieldType.Tp, Equals, mysql.TypeLonglong)
c.Assert(changedCol.FieldType.Flen, Equals, col.FieldType.Flen+1)
col.FieldType.Flen++

// Test add foreign key failed cause by canceled.
Expand Down