Skip to content

Commit

Permalink
ddl: modify different character sets varchar type column maximum leng…
Browse files Browse the repository at this point in the history
…th limit (#8818)
  • Loading branch information
ciscoxll authored Jan 14, 2019
1 parent 53707ce commit 44fd7d4
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 12 deletions.
20 changes: 20 additions & 0 deletions ddl/db_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2038,6 +2038,26 @@ func (s *testDBSuite) TestColumnModifyingDefinition(c *C) {
s.testErrorCode(c, "alter table test2 change c1 a1 bigint not null;", tmysql.WarnDataTruncated)
}

func (s *testDBSuite) TestCheckTooBigFieldLength(c *C) {
s.tk = testkit.NewTestKit(c, s.store)
s.tk.MustExec("use test")
s.tk.MustExec("drop table if exists tr_01;")
s.tk.MustExec("create table tr_01 (id int, name varchar(20000), purchased date ) default charset=utf8 collate=utf8_bin;")

s.tk.MustExec("drop table if exists tr_02;")
s.tk.MustExec("create table tr_02 (id int, name varchar(16000), purchased date ) default charset=utf8mb4 collate=utf8mb4_bin;")

s.tk.MustExec("drop table if exists tr_03;")
s.tk.MustExec("create table tr_03 (id int, name varchar(65534), purchased date ) default charset=latin1;")

s.tk.MustExec("drop table if exists tr_04;")
s.tk.MustExec("create table tr_04 (a varchar(20000) ) default charset utf8;")
s.testErrorCode(c, "alter table tr_04 convert to character set utf8mb4;", tmysql.ErrTooBigFieldlength)
s.testErrorCode(c, "create table tr (id int, name varchar(30000), purchased date ) default charset=utf8 collate=utf8_bin;", tmysql.ErrTooBigFieldlength)
s.testErrorCode(c, "create table tr (id int, name varchar(20000) charset utf8mb4, purchased date ) default charset=utf8 collate=utf8;", tmysql.ErrTooBigFieldlength)
s.testErrorCode(c, "create table tr (id int, name varchar(65536), purchased date ) default charset=latin1;", tmysql.ErrTooBigFieldlength)
}

func (s *testDBSuite) TestModifyColumnRollBack(c *C) {
s.tk = testkit.NewTestKit(c, s.store)
s.mustExec(c, "use test_db")
Expand Down
51 changes: 51 additions & 0 deletions ddl/ddl_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -657,6 +657,42 @@ func checkColumnsAttributes(colDefs []*ast.ColumnDef) error {
return nil
}

// checkColumnFieldLength check the maximum length limit for different character set varchar type columns.
func checkColumnFieldLength(schema *model.DBInfo, colDefs []*ast.ColumnDef, tbInfo *model.TableInfo) error {
for _, colDef := range colDefs {
if colDef.Tp.Tp == mysql.TypeVarchar {
var setCharset string
setCharset = mysql.DefaultCharset
if len(schema.Charset) != 0 {
setCharset = schema.Charset
}
if len(tbInfo.Charset) != 0 {
setCharset = tbInfo.Charset
}

err := IsTooBigFieldLength(colDef.Tp.Flen, colDef.Name.Name.O, setCharset)
if err != nil {
return errors.Trace(err)
}
}
}
return nil
}

// IsTooBigFieldLength check if the varchar type column exceeds the maximum length limit.
func IsTooBigFieldLength(colDefTpFlen int, colDefName, setCharset string) error {
desc, err := charset.GetCharsetDesc(setCharset)
if err != nil {
return errors.Trace(err)
}
maxFlen := mysql.MaxFieldVarCharLength
maxFlen /= desc.Maxlen
if colDefTpFlen != types.UnspecifiedLength && colDefTpFlen > maxFlen {
return types.ErrTooBigFieldLength.GenWithStack("Column length too big for column '%s' (max = %d); use BLOB or TEXT instead", colDefName, maxFlen)
}
return nil
}

// checkColumnAttributes check attributes for single column.
func checkColumnAttributes(colName string, tp *types.FieldType) error {
switch tp.Tp {
Expand Down Expand Up @@ -1006,6 +1042,10 @@ func (d *ddl) CreateTable(ctx sessionctx.Context, s *ast.CreateTableStmt) (err e
if err != nil {
return errors.Trace(err)
}
if err = checkColumnFieldLength(schema, s.Cols, tbInfo); err != nil {
return errors.Trace(err)
}

err = d.doDDLJob(ctx, job)
if err == nil {
if tbInfo.AutoIncID > 1 {
Expand Down Expand Up @@ -1972,6 +2012,11 @@ func (d *ddl) getModifiableColumnJob(ctx sessionctx.Context, ident ast.Ident, or
// `modifyColumnTp` indicates that there is a type modification.
modifyColumnTp = mysql.TypeNull
}

if err = checkColumnFieldLength(schema, spec.NewColumns, t.Meta()); err != nil {
return nil, errors.Trace(err)
}

// As same with MySQL, we don't support modifying the stored status for generated columns.
if err = checkModifyGeneratedColumn(t.Cols(), col, newCol); err != nil {
return nil, errors.Trace(err)
Expand Down Expand Up @@ -2152,6 +2197,12 @@ func (d *ddl) AlterTableCharsetAndCollate(ctx sessionctx.Context, ident ast.Iden
return errors.Trace(err)
}

for _, col := range tb.Meta().Cols() {
if col.Tp == mysql.TypeVarchar {
err = IsTooBigFieldLength(col.Flen, col.Name.O, toCharset)
return errors.Trace(err)
}
}
job := &model.Job{
SchemaID: schema.ID,
TableID: tb.Meta().ID,
Expand Down
17 changes: 5 additions & 12 deletions planner/core/preprocess.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import (
"github.com/pingcap/errors"
"github.com/pingcap/parser"
"github.com/pingcap/parser/ast"
"github.com/pingcap/parser/charset"
"github.com/pingcap/parser/model"
"github.com/pingcap/parser/mysql"
"github.com/pingcap/tidb/ddl"
Expand Down Expand Up @@ -489,22 +488,16 @@ func checkColumn(colDef *ast.ColumnDef) error {
return types.ErrTooBigFieldLength.GenWithStack("Column length too big for column '%s' (max = %d); use BLOB or TEXT instead", colDef.Name.Name.O, mysql.MaxFieldCharLength)
}
case mysql.TypeVarchar:
maxFlen := mysql.MaxFieldVarCharLength
cs := tp.Charset
// TODO: TableDefaultCharset-->DatabaseDefaultCharset-->SystemDefaultCharset.
// TODO: Change TableOption parser to parse collate.
// Reference https://github.com/pingcap/tidb/blob/b091e828cfa1d506b014345fb8337e424a4ab905/ddl/ddl_api.go#L185-L204
if len(tp.Charset) == 0 {
cs = mysql.DefaultCharset
// It's not easy to get the schema charset and table charset here.
// The charset is determined by the order ColumnDefaultCharset --> TableDefaultCharset-->DatabaseDefaultCharset-->SystemDefaultCharset.
// return nil, to make the check in the ddl.CreateTable.
return nil
}
desc, err := charset.GetCharsetDesc(cs)
err := ddl.IsTooBigFieldLength(colDef.Tp.Flen, colDef.Name.Name.O, tp.Charset)
if err != nil {
return errors.Trace(err)
}
maxFlen /= desc.Maxlen
if tp.Flen != types.UnspecifiedLength && tp.Flen > maxFlen {
return types.ErrTooBigFieldLength.GenWithStack("Column length too big for column '%s' (max = %d); use BLOB or TEXT instead", colDef.Name.Name.O, maxFlen)
}
case mysql.TypeFloat, mysql.TypeDouble:
if tp.Decimal > mysql.MaxFloatingTypeScale {
return types.ErrTooBigScale.GenWithStackByArgs(tp.Decimal, colDef.Name.Name.O, mysql.MaxFloatingTypeScale)
Expand Down

0 comments on commit 44fd7d4

Please sign in to comment.