diff --git a/ddl/db_test.go b/ddl/db_test.go index 30ca271169..447b8d9f3d 100644 --- a/ddl/db_test.go +++ b/ddl/db_test.go @@ -4269,6 +4269,44 @@ func (s *testDBSuite4) TestIssue9100(c *C) { c.Assert(err.Error(), Equals, "[ddl:1503]A PRIMARY must include all columns in the table's partitioning function") } +func (s *testSerialDBSuite) TestProcessColumnFlags(c *C) { + // check `processColumnFlags()` + tk := testkit.NewTestKit(c, s.store) + tk.MustExec("use test_db") + tk.MustExec("create table t(a year(4) comment 'xxx', b year, c bit)") + defer s.mustExec(tk, c, "drop table t;") + + check := func(n string, f func(uint) bool) { + t := testGetTableByName(c, tk.Se, "test_db", "t") + for _, col := range t.Cols() { + if strings.EqualFold(col.Name.L, n) { + c.Assert(f(col.Flag), IsTrue) + break + } + } + } + + yearcheck := func(f uint) bool { + return mysql.HasUnsignedFlag(f) && mysql.HasZerofillFlag(f) && !mysql.HasBinaryFlag(f) + } + + tk.MustExec("alter table t modify a year(4)") + check("a", yearcheck) + + tk.MustExec("alter table t modify a year(4) unsigned") + check("a", yearcheck) + + tk.MustExec("alter table t modify a year(4) zerofill") + + tk.MustExec("alter table t modify b year") + check("b", yearcheck) + + tk.MustExec("alter table t modify c bit") + check("c", func(f uint) bool { + return mysql.HasUnsignedFlag(f) && !mysql.HasBinaryFlag(f) + }) +} + func (s *testSerialDBSuite) TestModifyColumnCharset(c *C) { tk := testkit.NewTestKit(c, s.store) tk.MustExec("use test_db") diff --git a/ddl/ddl_api.go b/ddl/ddl_api.go index ee6d9d40b4..3c2594015d 100644 --- a/ddl/ddl_api.go +++ b/ddl/ddl_api.go @@ -491,6 +491,30 @@ func isExplicitTimeStamp() bool { return true } +// processColumnFlags is used by columnDefToCol and processColumnOptions. It is intended to unify behaviors on `create/add` and `modify/change` statements. Check tidb#issue#19342. +func processColumnFlags(col *table.Column) { + if col.FieldType.EvalType().IsStringKind() && col.Charset == charset.CharsetBin { + col.Flag |= mysql.BinaryFlag + } + if col.Tp == mysql.TypeBit { + // For BIT field, it's charset is binary but does not have binary flag. + col.Flag &= ^mysql.BinaryFlag + col.Flag |= mysql.UnsignedFlag + } + if col.Tp == mysql.TypeYear { + // For Year field, it's charset is binary but does not have binary flag. + col.Flag &= ^mysql.BinaryFlag + col.Flag |= mysql.ZerofillFlag + } + + // If you specify ZEROFILL for a numeric column, MySQL automatically adds the UNSIGNED attribute to the column. + // See https://dev.mysql.com/doc/refman/5.7/en/numeric-type-overview.html for more details. + // But some types like bit and year, won't show its unsigned flag in `show create table`. + if mysql.HasZerofillFlag(col.Flag) { + col.Flag |= mysql.UnsignedFlag + } +} + // columnDefToCol converts ColumnDef to Col and TableConstraints. // outPriKeyConstraint is the primary key constraint out of column definition. such as: create table t1 (id int , age int, primary key(id)); func columnDefToCol(ctx sessionctx.Context, offset int, colDef *ast.ColumnDef, outPriKeyConstraint *ast.Constraint) (*table.Column, []*ast.Constraint, error) { @@ -603,26 +627,9 @@ func columnDefToCol(ctx sessionctx.Context, offset int, colDef *ast.ColumnDef, o // Set `NoDefaultValueFlag` if this field doesn't have a default value and // it is `not null` and not an `AUTO_INCREMENT` field or `TIMESTAMP` field. setNoDefaultValueFlag(col, hasDefaultValue) - if col.FieldType.EvalType().IsStringKind() && col.Charset == charset.CharsetBin { - col.Flag |= mysql.BinaryFlag - } - if col.Tp == mysql.TypeBit { - // For BIT field, it's charset is binary but does not have binary flag. - col.Flag &= ^mysql.BinaryFlag - col.Flag |= mysql.UnsignedFlag - } - if col.Tp == mysql.TypeYear { - // For Year field, it's charset is binary but does not have binary flag. - col.Flag &= ^mysql.BinaryFlag - col.Flag |= mysql.ZerofillFlag - } - // If you specify ZEROFILL for a numeric column, MySQL automatically adds the UNSIGNED attribute to the column. - // See https://dev.mysql.com/doc/refman/5.7/en/numeric-type-overview.html for more details. - // But some types like bit and year, won't show its unsigned flag in `show create table`. - if mysql.HasZerofillFlag(col.Flag) { - col.Flag |= mysql.UnsignedFlag - } + processColumnFlags(col) + err = checkPriKeyConstraint(col, hasDefaultValue, hasNullFlag, outPriKeyConstraint) if err != nil { return nil, nil, errors.Trace(err) @@ -3399,9 +3406,7 @@ func processColumnOptions(ctx sessionctx.Context, col *table.Column, options []* // it is `not null` and not an `AUTO_INCREMENT` field or `TIMESTAMP` field. setNoDefaultValueFlag(col, hasDefaultValue) - if col.Tp == mysql.TypeBit { - col.Flag |= mysql.UnsignedFlag - } + processColumnFlags(col) if hasDefaultValue { return errors.Trace(checkDefaultValue(ctx, col, true))