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: Supports transitions between the same type (String types) #20032

Merged
merged 34 commits into from
Oct 16, 2020
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
026364e
Supports transitions between the same type (String types)
Patrick0308 Sep 16, 2020
a4b223c
Supports transitions between the ENUM and Set and Strings type
Patrick0308 Sep 16, 2020
9b1a095
modify needChangeColumn
Patrick0308 Sep 16, 2020
5a5b7f9
optimize needChangeColumnData
Patrick0308 Sep 16, 2020
34f1edd
optimize needChangeColumnData use switch case
Patrick0308 Sep 17, 2020
aac09e4
Merge remote-tracking branch 'upstream/master' into 19571
Patrick0308 Sep 20, 2020
e1b96ee
add some test
Patrick0308 Sep 22, 2020
4edf90e
modify tests
Patrick0308 Sep 25, 2020
c360e0d
Merge branch 'master' into 19571
Patrick0308 Sep 27, 2020
2ba8aa3
Merge branch 'master' into 19571
AilinKid Sep 27, 2020
8ab072f
Merge branch 'master' into 19571
AilinKid Sep 27, 2020
38ceb01
Merge remote-tracking branch 'upstream/master' into 19571
Patrick0308 Sep 29, 2020
1b2c383
fix some problem
Patrick0308 Sep 29, 2020
2978b36
optimize
Patrick0308 Sep 29, 2020
8431f0d
add isErrorToRollback
Patrick0308 Sep 29, 2020
c759b88
modify tests
Patrick0308 Oct 1, 2020
ce0263f
modify tests
Patrick0308 Oct 1, 2020
233d5f1
Merge remote-tracking branch 'upstream/master' into 19571
Patrick0308 Oct 12, 2020
14defb4
Merge branch 'master' into 19571
AilinKid Oct 13, 2020
8070455
Merge branch 'master' into 19571
AilinKid Oct 13, 2020
b08c775
Merge branch 'master' into 19571
AilinKid Oct 14, 2020
3560637
fix review problems
Patrick0308 Oct 14, 2020
bc0b9d6
Merge remote-tracking branch 'origin/19571' into 19571
Patrick0308 Oct 14, 2020
0e92d60
fix ErrOverflow error duplicate
Patrick0308 Oct 14, 2020
136dee6
Merge branch 'master' into 19571
ti-srebot Oct 14, 2020
50ab573
Merge branch 'master' into 19571
AilinKid Oct 15, 2020
994af5c
Merge branch 'master' into 19571
AilinKid Oct 15, 2020
a4f1a02
fix TypeEnum convert compatible with old version
Patrick0308 Oct 15, 2020
b923e4d
Merge remote-tracking branch 'origin/19571' into 19571
Patrick0308 Oct 15, 2020
8061ee8
Merge branch 'master' into 19571
AilinKid Oct 15, 2020
3e7a992
fix convert compatible with old version
Patrick0308 Oct 15, 2020
e80ff5c
Merge remote-tracking branch 'origin/19571' into 19571
Patrick0308 Oct 15, 2020
bde8729
Merge branch 'master' into 19571
AilinKid Oct 16, 2020
176fece
Merge branch 'master' into 19571
AilinKid Oct 16, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 47 additions & 2 deletions ddl/column.go
Original file line number Diff line number Diff line change
Expand Up @@ -645,10 +645,49 @@ func onSetDefaultValue(t *meta.Meta, job *model.Job) (ver int64, _ error) {
func needChangeColumnData(oldCol, newCol *model.ColumnInfo) bool {
toUnsigned := mysql.HasUnsignedFlag(newCol.Flag)
originUnsigned := mysql.HasUnsignedFlag(oldCol.Flag)
switch oldCol.Tp {
case mysql.TypeEnum:
switch newCol.Tp {
Patrick0308 marked this conversation as resolved.
Show resolved Hide resolved
Patrick0308 marked this conversation as resolved.
Show resolved Hide resolved
case mysql.TypeEnum:
if len(newCol.Elems) < len(oldCol.Elems) {
return true
}
for index, oldElem := range oldCol.Elems {
newElem := newCol.Elems[index]
if oldElem != newElem {
return true
}
}
return false
default:
return true
}
case mysql.TypeSet:
switch newCol.Tp {
case mysql.TypeSet:
if len(newCol.Elems) < len(oldCol.Elems) {
return true
}
for _, oldElem := range oldCol.Elems {
contain := false
for _, newElem := range newCol.Elems {
if oldElem == newElem {
contain = true
break
}
}
if !contain {
return true
}
}
return false
default:
return true
}
}
if newCol.Flen > 0 && newCol.Flen < oldCol.Flen || toUnsigned != originUnsigned {
return true
}

return false
}

Expand Down Expand Up @@ -894,7 +933,8 @@ func (w *worker) doModifyColumnTypeWithData(
// If timeout, we should return, check for the owner and re-wait job done.
return ver, nil
}
if kv.ErrKeyExists.Equal(err) || errCancelledDDLJob.Equal(err) || errCantDecodeRecord.Equal(err) || types.ErrOverflow.Equal(err) {
if kv.ErrKeyExists.Equal(err) || errCancelledDDLJob.Equal(err) || errCantDecodeRecord.Equal(err) || types.ErrOverflow.Equal(err) ||
Patrick0308 marked this conversation as resolved.
Show resolved Hide resolved
types.ErrOverflow.Equal(err) || types.ErrDataTooLong.Equal(err) || types.ErrTruncated.Equal(err) {
if err1 := t.RemoveDDLReorgHandle(job, reorgInfo.elements); err1 != nil {
logutil.BgLogger().Warn("[ddl] run modify column job failed, RemoveDDLReorgHandle failed, can't convert job to rollback",
zap.String("job", job.String()), zap.Error(err1))
Expand Down Expand Up @@ -1085,8 +1125,10 @@ func (w *updateColumnWorker) fetchRowColVals(txn kv.Transaction, taskRange reorg
taskDone := false
var lastAccessedHandle kv.Handle
oprStartTime := startTime
var rowIdx int
err := iterateSnapshotRows(w.sessCtx.GetStore(), w.priority, w.table, txn.StartTS(), taskRange.startHandle, taskRange.endHandle, taskRange.endIncluded,
func(handle kv.Handle, recordKey kv.Key, rawRow []byte) (bool, error) {
rowIdx++
Patrick0308 marked this conversation as resolved.
Show resolved Hide resolved
oprEndTime := time.Now()
logSlowOperations(oprEndTime.Sub(oprStartTime), "iterateSnapshotRows in updateColumnWorker fetchRowColVals", 0)
oprStartTime = oprEndTime
Expand All @@ -1102,6 +1144,9 @@ func (w *updateColumnWorker) fetchRowColVals(txn kv.Transaction, taskRange reorg
}

if err1 := w.getRowRecord(handle, recordKey, rawRow); err1 != nil {
if types.ErrTruncated.Equal(err1) {
err1 = types.ErrTruncated.GenWithStackByArgs(w.oldColInfo.Name, rowIdx)
}
Patrick0308 marked this conversation as resolved.
Show resolved Hide resolved
return false, errors.Trace(err1)
}
lastAccessedHandle = handle
Expand Down
2 changes: 1 addition & 1 deletion ddl/column_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1156,7 +1156,7 @@ func (s *testColumnSuite) TestModifyColumn(c *C) {
{"varchar(10)", "text", nil},
{"varbinary(10)", "blob", nil},
{"text", "blob", errUnsupportedModifyCharset.GenWithStackByArgs("charset from utf8mb4 to binary")},
{"varchar(10)", "varchar(8)", errUnsupportedModifyColumn.GenWithStackByArgs("length 8 is less than origin 10")},
{"varchar(10)", "varchar(8)", errUnsupportedModifyColumn.GenWithStackByArgs("length 8 is less than origin 10, and tidb_enable_change_column_type is false")},
{"varchar(10)", "varchar(11)", nil},
{"varchar(10) character set utf8 collate utf8_bin", "varchar(10) character set utf8", nil},
{"decimal(2,1)", "decimal(3,2)", errUnsupportedModifyColumn.GenWithStackByArgs("can't change decimal column precision")},
Expand Down
7 changes: 3 additions & 4 deletions ddl/db_change_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -480,12 +480,11 @@ func (s *testStateChangeSuite) TestAppendEnum(c *C) {

_, err = s.se.Execute(context.Background(), "insert into t values('a', 'A', '2018-09-19', 9)")
c.Assert(err.Error(), Equals, "[types:1265]Data truncated for column 'c2' at row 1")
failAlterTableSQL1 := "alter table t change c2 c2 enum('N') DEFAULT 'N'"
_, err = s.se.Execute(context.Background(), failAlterTableSQL1)
c.Assert(err.Error(), Equals, "[ddl:8200]Unsupported modify column: the number of enum column's elements is less than the original: 2")
_, err = s.se.Execute(context.Background(), "alter table t change c2 c2 enum('N') DEFAULT 'N'")
c.Assert(err, IsNil)
failAlterTableSQL2 := "alter table t change c2 c2 int default 0"
_, err = s.se.Execute(context.Background(), failAlterTableSQL2)
c.Assert(err.Error(), Equals, "[ddl:8200]Unsupported modify column: cannot modify enum type column's to type int(11)")
c.Assert(err.Error(), Equals, "[ddl:8200]Unsupported modify column: type int(11) not match origin enum('N')")
alterTableSQL := "alter table t change c2 c2 enum('N','Y','A') DEFAULT 'A'"
_, err = s.se.Execute(context.Background(), alterTableSQL)
c.Assert(err, IsNil)
Expand Down
51 changes: 50 additions & 1 deletion ddl/db_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2322,7 +2322,7 @@ func (s *testDBSuite4) TestChangeColumn(c *C) {
sql = "alter table t4 change c2 a bigint not null;"
tk.MustGetErrCode(sql, mysql.WarnDataTruncated)
sql = "alter table t3 modify en enum('a', 'z', 'b', 'c') not null default 'a'"
tk.MustGetErrCode(sql, errno.ErrUnsupportedDDLOperation)
tk.MustExec(sql)
// Rename to an existing column.
s.mustExec(tk, c, "alter table t3 add column a bigint")
sql = "alter table t3 change aa a bigint"
Expand Down Expand Up @@ -3855,6 +3855,7 @@ func (s *testSerialDBSuite) TestModifyColumnNullToNotNullWithChangingVal2(c *C)
failpoint.Disable("github.com/pingcap/tidb/ddl/mockInsertValueAfterCheckNull")
}()

tk.MustExec("drop table if exists tt;")
tk.MustExec(`create table tt (a bigint, b int, unique index idx(a));`)
tk.MustExec("insert into tt values (1,1),(2,2),(3,3);")
_, err := tk.Exec("alter table tt modify a int not null;")
Expand All @@ -3876,6 +3877,54 @@ func (s *testSerialDBSuite) TestModifyColumnNullToNotNullWithChangingVal(c *C) {
c.Assert(c2.FieldType.Tp, Equals, mysql.TypeTiny)
}

func (s *testSerialDBSuite) TestModifyColumnBetweenStringTypes(c *C) {
tk := testkit.NewTestKitWithInit(c, s.store)
tk.Se.GetSessionVars().EnableChangeColumnType = true

//varchar to varchar
tk.MustExec("drop table if exists tt;")
tk.MustExec("create table tt (a varchar(10));")
tk.MustExec("insert into tt values ('111'),('10000');")
tk.MustExec("alter table tt change a a varchar(5);")
c2 := getModifyColumn(c, s.s.(sessionctx.Context), "test", "tt", "a", false)
c.Assert(c2.FieldType.Flen, Equals, 5)
tk.MustQuery("select * from tt").Check(testkit.Rows("111", "10000"))
tk.MustGetErrMsg("alter table tt change a a varchar(4);", "[types:1406]Data Too Long, field len 4, data len 5")
Patrick0308 marked this conversation as resolved.
Show resolved Hide resolved
tk.MustExec("alter table tt change a a varchar(100);")

// varchar to char
tk.MustExec("alter table tt change a a char(10);")
c2 = getModifyColumn(c, s.s.(sessionctx.Context), "test", "tt", "a", false)
c.Assert(c2.FieldType.Tp, Equals, mysql.TypeString)
c.Assert(c2.FieldType.Flen, Equals, 10)
tk.MustQuery("select * from tt").Check(testkit.Rows("111", "10000"))
tk.MustGetErrMsg("alter table tt change a a char(4);", "[types:1406]Data Too Long, field len 4, data len 5")

// char to text
tk.MustExec("alter table tt change a a text;")
c2 = getModifyColumn(c, s.s.(sessionctx.Context), "test", "tt", "a", false)
c.Assert(c2.FieldType.Tp, Equals, mysql.TypeBlob)

// text to set
tk.MustGetErrMsg("alter table tt change a a set('111', '2222');", "[types:1265]Data truncated for column 'a' at row 2")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If sql_mode is set to "", the job will be succeeded and warnings will be returned instead and 10000 will be converted to an empty value.

Since we don't have sql_mode here, you can choose to update your code if #20012 is merged before this PR(it's up to you). For now, let's skip the sql_mode.

tk.MustExec("alter table tt change a a set('111', '10000');")
c2 = getModifyColumn(c, s.s.(sessionctx.Context), "test", "tt", "a", false)
c.Assert(c2.FieldType.Tp, Equals, mysql.TypeSet)
tk.MustQuery("select * from tt").Check(testkit.Rows("111", "10000"))

// set to enum
tk.MustGetErrMsg("alter table tt change a a enum('111', '2222');", "[types:1265]Data truncated for column 'a' at row 2")
tk.MustExec("alter table tt change a a enum('111', '10000');")
c2 = getModifyColumn(c, s.s.(sessionctx.Context), "test", "tt", "a", false)
c.Assert(c2.FieldType.Tp, Equals, mysql.TypeEnum)
tk.MustQuery("select * from tt").Check(testkit.Rows("111", "10000"))
tk.MustExec("alter table tt change a a enum('10000', '111');")
tk.MustQuery("select * from tt where a = 1").Check(testkit.Rows("10000"))
tk.MustQuery("select * from tt where a = 2").Check(testkit.Rows("111"))

tk.MustExec("drop table tt;")
}

func getModifyColumn(c *C, ctx sessionctx.Context, db, tbl, colName string, allColumn bool) *table.Column {
t := testGetTableByName(c, ctx, db, tbl)
colName = strings.ToLower(colName)
Expand Down
50 changes: 24 additions & 26 deletions ddl/ddl_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -3261,44 +3261,33 @@ func checkModifyCharsetAndCollation(toCharset, toCollate, origCharset, origColla
// field length and precision.
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 isIntType bool
var canChange bool
switch origin.Tp {
case mysql.TypeVarchar, mysql.TypeString, mysql.TypeVarString,
mysql.TypeBlob, mysql.TypeTinyBlob, mysql.TypeMediumBlob, mysql.TypeLongBlob:
switch to.Tp {
case mysql.TypeVarchar, mysql.TypeString, mysql.TypeVarString,
mysql.TypeBlob, mysql.TypeTinyBlob, mysql.TypeMediumBlob, mysql.TypeLongBlob:
mysql.TypeBlob, mysql.TypeTinyBlob, mysql.TypeMediumBlob, mysql.TypeLongBlob,
mysql.TypeEnum, mysql.TypeSet:
canChange = true
default:
return "", errUnsupportedModifyColumn.GenWithStackByArgs(unsupportedMsg)
}
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:
isIntType = true
canChange = true
default:
return "", errUnsupportedModifyColumn.GenWithStackByArgs(unsupportedMsg)
}
case mysql.TypeEnum, mysql.TypeSet:
var typeVar string
if origin.Tp == mysql.TypeEnum {
typeVar = "enum"
} else {
typeVar = "set"
}
if origin.Tp != to.Tp {
msg := fmt.Sprintf("cannot modify %s type column's to type %s", typeVar, to.String())
return "", errUnsupportedModifyColumn.GenWithStackByArgs(msg)
}
if len(to.Elems) < len(origin.Elems) {
msg := fmt.Sprintf("the number of %s column's elements is less than the original: %d", typeVar, len(origin.Elems))
return "", errUnsupportedModifyColumn.GenWithStackByArgs(msg)
}
for index, originElem := range origin.Elems {
toElem := to.Elems[index]
if originElem != toElem {
msg := fmt.Sprintf("cannot modify %s column value %s to %s", typeVar, originElem, toElem)
return "", errUnsupportedModifyColumn.GenWithStackByArgs(msg)
}
case mysql.TypeEnum:
switch to.Tp {
case mysql.TypeVarchar, mysql.TypeString, mysql.TypeVarString,
mysql.TypeBlob, mysql.TypeTinyBlob, mysql.TypeMediumBlob, mysql.TypeLongBlob,
mysql.TypeSet, mysql.TypeEnum:
canChange = true
default:
return "", errUnsupportedModifyColumn.GenWithStackByArgs(unsupportedMsg)
Patrick0308 marked this conversation as resolved.
Show resolved Hide resolved
}
case mysql.TypeNewDecimal:
if origin.Tp != to.Tp {
Expand All @@ -3308,6 +3297,15 @@ func CheckModifyTypeCompatible(origin *types.FieldType, to *types.FieldType) (al
if to.Flen != origin.Flen || to.Decimal != origin.Decimal {
return "", errUnsupportedModifyColumn.GenWithStackByArgs("can't change decimal column precision")
}
case mysql.TypeSet:
switch to.Tp {
case mysql.TypeVarchar, mysql.TypeString, mysql.TypeVarString,
mysql.TypeBlob, mysql.TypeTinyBlob, mysql.TypeMediumBlob, mysql.TypeLongBlob,
mysql.TypeSet, mysql.TypeEnum:
canChange = true
default:
return "", errUnsupportedModifyColumn.GenWithStackByArgs(unsupportedMsg)
}
Patrick0308 marked this conversation as resolved.
Show resolved Hide resolved
default:
if origin.Tp != to.Tp {
return "", errUnsupportedModifyColumn.GenWithStackByArgs(unsupportedMsg)
Expand All @@ -3316,7 +3314,7 @@ 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 isIntType {
if canChange {
return msg, errUnsupportedModifyColumn.GenWithStackByArgs(msg)
}
return "", errUnsupportedModifyColumn.GenWithStackByArgs(msg)
Expand All @@ -3330,7 +3328,7 @@ func CheckModifyTypeCompatible(origin *types.FieldType, to *types.FieldType) (al
originUnsigned := mysql.HasUnsignedFlag(origin.Flag)
if originUnsigned != toUnsigned {
msg := fmt.Sprintf("can't change unsigned integer to signed or vice versa")
if isIntType {
if canChange {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

canChange is different with isIntType. Even if to.Type is not int, it still reports integer type change error.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's judge originUnsigned != toUnsigned for integer.

return msg, errUnsupportedModifyColumn.GenWithStackByArgs(msg)
}
return "", errUnsupportedModifyColumn.GenWithStackByArgs(msg)
Expand Down
3 changes: 2 additions & 1 deletion expression/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7329,7 +7329,8 @@ func (s *testIntegrationSerialSuite) TestIssue19804(c *C) {
tk.MustExec(`drop table if exists t;`)
tk.MustExec(`create table t(a set('a', 'b', 'c'));`)
tk.MustExec(`alter table t change a a set('a', 'b', 'c', 'd');`)
tk.MustGetErrMsg(`alter table t change a a set('a', 'b', 'c', 'e', 'f');`, "[ddl:8200]Unsupported modify column: cannot modify set column value d to e")
tk.MustExec(`insert into t values('d');`)
tk.MustGetErrMsg(`alter table t change a a set('a', 'b', 'c', 'e', 'f');`, "[types:1265]Data truncated for column 'a' at row 1")
}

func (s *testIntegrationSerialSuite) TestIssue18949(c *C) {
Expand Down