Skip to content

Commit

Permalink
ddl,executor: add auto_random column option (#13127, #14489) (#14555)
Browse files Browse the repository at this point in the history
  • Loading branch information
tangenta authored Feb 12, 2020
1 parent 2b917dd commit 1516653
Show file tree
Hide file tree
Showing 36 changed files with 916 additions and 135 deletions.
21 changes: 18 additions & 3 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,9 +94,10 @@ type Config struct {
AlterPrimaryKey bool `toml:"alter-primary-key" json:"alter-primary-key"`
// TreatOldVersionUTF8AsUTF8MB4 is use to treat old version table/column UTF8 charset as UTF8MB4. This is for compatibility.
// Currently not support dynamic modify, because this need to reload all old version schema.
TreatOldVersionUTF8AsUTF8MB4 bool `toml:"treat-old-version-utf8-as-utf8mb4" json:"treat-old-version-utf8-as-utf8mb4"`
SplitRegionMaxNum uint64 `toml:"split-region-max-num" json:"split-region-max-num"`
StmtSummary StmtSummary `toml:"stmt-summary" json:"stmt-summary"`
TreatOldVersionUTF8AsUTF8MB4 bool `toml:"treat-old-version-utf8-as-utf8mb4" json:"treat-old-version-utf8-as-utf8mb4"`
SplitRegionMaxNum uint64 `toml:"split-region-max-num" json:"split-region-max-num"`
StmtSummary StmtSummary `toml:"stmt-summary" json:"stmt-summary"`
Experimental Experimental `toml:"experimental" json:"experimental"`
}

// Log is the log section of config.
Expand Down Expand Up @@ -335,6 +336,13 @@ type StmtSummary struct {
HistorySize int `toml:"history-size" json:"history-size"`
}

// Experimental controls the features that are still experimental: their semantics, interfaces are subject to change.
// Using these features in the production environment is not recommended.
type Experimental struct {
// Whether enable the syntax like `auto_random(3)` on the primary key column.
AllowAutoRandom bool `toml:"allow-auto-random" json:"allow-auto-random"`
}

var defaultConf = Config{
Host: "0.0.0.0",
AdvertiseAddress: "",
Expand Down Expand Up @@ -440,6 +448,9 @@ var defaultConf = Config{
RefreshInterval: 1800,
HistorySize: 24,
},
Experimental: Experimental{
AllowAutoRandom: false,
},
}

var (
Expand Down Expand Up @@ -619,6 +630,10 @@ func (c *Config) Valid() error {
if c.StmtSummary.RefreshInterval <= 0 {
return fmt.Errorf("refresh-interval in [stmt-summary] should be greater than 0")
}

if c.AlterPrimaryKey && c.Experimental.AllowAutoRandom {
return fmt.Errorf("allow-auto-random is unavailable when alter-primary-key is enabled")
}
return nil
}

Expand Down
6 changes: 6 additions & 0 deletions config/config.toml.example
Original file line number Diff line number Diff line change
Expand Up @@ -353,3 +353,9 @@ refresh-interval = 1800

# the maximum history size of statement summary.
history-size = 24

# experimental section controls the features that are still experimental: their semantics,
# interfaces are subject to change, using these features in the production environment is not recommended.
[experimental]
# enable column attribute `auto_random` to be defined on the primary key column.
allow-auto-random = false
16 changes: 16 additions & 0 deletions config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ max-stmt-count=1000
max-sql-length=1024
refresh-interval=100
history-size=100
[experimental]
allow-auto-random = true
`)

c.Assert(err, IsNil)
Expand Down Expand Up @@ -112,6 +114,7 @@ history-size=100
c.Assert(conf.StmtSummary.MaxSQLLength, Equals, uint(1024))
c.Assert(conf.StmtSummary.RefreshInterval, Equals, 100)
c.Assert(conf.StmtSummary.HistorySize, Equals, 100)
c.Assert(conf.Experimental.AllowAutoRandom, IsTrue)
c.Assert(f.Close(), IsNil)
c.Assert(os.Remove(configFile), IsNil)

Expand Down Expand Up @@ -246,3 +249,16 @@ func (s *testConfigSuite) TestOOMActionValid(c *C) {
c.Assert(c1.Valid() == nil, Equals, tt.valid)
}
}

func (s *testConfigSuite) TestAllowAutoRandomValid(c *C) {
conf := NewConfig()
checkValid := func(allowAlterPK, allowAutoRand, shouldBeValid bool) {
conf.AlterPrimaryKey = allowAlterPK
conf.Experimental.AllowAutoRandom = allowAutoRand
c.Assert(conf.Valid() == nil, Equals, shouldBeValid)
}
checkValid(true, true, false)
checkValid(true, false, true)
checkValid(false, true, true)
checkValid(false, false, true)
}
4 changes: 2 additions & 2 deletions ddl/column_change_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -352,8 +352,8 @@ func getCurrentTable(d *ddl, schemaID, tableID int64) (table.Table, error) {
if err != nil {
return nil, errors.Trace(err)
}
alloc := autoid.NewAllocator(d.store, schemaID, false)
tbl, err := table.TableFromMeta(alloc, tblInfo)
alloc := autoid.NewAllocator(d.store, schemaID, false, autoid.RowIDAllocType)
tbl, err := table.TableFromMeta(autoid.NewAllocators(alloc), tblInfo)
if err != nil {
return nil, errors.Trace(err)
}
Expand Down
4 changes: 4 additions & 0 deletions ddl/ddl.go
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,8 @@ var (
ErrFieldNotFoundPart = terror.ClassDDL.New(codeFieldNotFoundPart, mysql.MySQLErrName[mysql.ErrFieldNotFoundPart])
// ErrWrongTypeColumnValue returns 'Partition column values of incorrect type'
ErrWrongTypeColumnValue = terror.ClassDDL.New(codeWrongTypeColumnValue, mysql.MySQLErrName[mysql.ErrWrongTypeColumnValue])
// ErrInvalidAutoRandom returns when auto_random is used incorrectly.
ErrInvalidAutoRandom = terror.ClassDDL.New(codeInvalidAutoRandom, mysql.MySQLErrName[mysql.ErrInvalidAutoRandom])
)

// DDL is responsible for updating schema in data store and maintaining in-memory InfoSchema cache.
Expand Down Expand Up @@ -757,6 +759,7 @@ const (
codeSystemVersioningWrongPartitions = terror.ErrCode(mysql.ErrSystemVersioningWrongPartitions)
codeWrongPartitionTypeExpectedSystemTime = terror.ErrCode(mysql.ErrWrongPartitionTypeExpectedSystemTime)
codeWrongTypeColumnValue = terror.ErrCode(mysql.ErrWrongTypeColumnValue)
codeInvalidAutoRandom = terror.ErrCode(mysql.ErrInvalidAutoRandom)
)

func init() {
Expand Down Expand Up @@ -831,6 +834,7 @@ func init() {
codeWrongTypeColumnValue: mysql.ErrWrongTypeColumnValue,
mysql.ErrFkColumnCannotDrop: mysql.ErrFkColumnCannotDrop,
mysql.ErrFKIncompatibleColumns: mysql.ErrFKIncompatibleColumns,
mysql.ErrInvalidAutoRandom: mysql.ErrInvalidAutoRandom,
}
terror.ErrClassToMySQLCodes[terror.ClassDDL] = ddlMySQLErrCodes
}
108 changes: 100 additions & 8 deletions ddl/ddl_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -876,9 +876,9 @@ func checkDuplicateColumn(cols []interface{}) error {
return nil
}

func checkIsAutoIncrementColumn(colDefs *ast.ColumnDef) bool {
for _, option := range colDefs.Options {
if option.Tp == ast.ColumnOptionAutoIncrement {
func containsColumnOption(colDef *ast.ColumnDef, opTp ast.ColumnOptionType) bool {
for _, option := range colDef.Options {
if option.Tp == opTp {
return true
}
}
Expand All @@ -897,7 +897,7 @@ func checkGeneratedColumn(colDefs []*ast.ColumnDef) error {
}
}
}
if checkIsAutoIncrementColumn(colDef) {
if containsColumnOption(colDef, ast.ColumnOptionAutoIncrement) {
exists, autoIncrementColumn = true, colDef.Name.Name.L
}
generated, depCols := findDependedColumnNames(colDef)
Expand Down Expand Up @@ -1076,6 +1076,58 @@ func checkConstraintNames(constraints []*ast.Constraint) error {
return nil
}

func setTableAutoRandomBits(tbInfo *model.TableInfo, colDefs []*ast.ColumnDef) error {
allowAutoRandom := config.GetGlobalConfig().Experimental.AllowAutoRandom
pkColName := tbInfo.GetPkName()
for _, col := range colDefs {
if containsColumnOption(col, ast.ColumnOptionAutoRandom) {
if !allowAutoRandom {
return ErrInvalidAutoRandom.GenWithStackByArgs(autoid.AutoRandomExperimentalDisabledErrMsg)
}
if !tbInfo.PKIsHandle || col.Name.Name.L != pkColName.L {
return ErrInvalidAutoRandom.GenWithStackByArgs(fmt.Sprintf(autoid.AutoRandomPKisNotHandleErrMsg, col.Name.Name.O))
}
if containsColumnOption(col, ast.ColumnOptionAutoIncrement) {
return ErrInvalidAutoRandom.GenWithStackByArgs(autoid.AutoRandomIncompatibleWithAutoIncErrMsg)
}
if containsColumnOption(col, ast.ColumnOptionDefaultValue) {
return ErrInvalidAutoRandom.GenWithStackByArgs(autoid.AutoRandomIncompatibleWithDefaultValueErrMsg)
}

autoRandBits, err := extractAutoRandomBitsFromColDef(col)
if err != nil {
return errors.Trace(err)
}
maxFieldTypeBitsLength := uint64(mysql.DefaultLengthOfMysqlTypes[col.Tp.Tp] * 8)
if autoRandBits == 0 {
return ErrInvalidAutoRandom.GenWithStackByArgs(autoid.AutoRandomNonPositive)
} else if autoRandBits >= maxFieldTypeBitsLength {
return ErrInvalidAutoRandom.GenWithStackByArgs(fmt.Sprintf(autoid.AutoRandomOverflowErrMsg, autoRandBits, maxFieldTypeBitsLength))
}
tbInfo.AutoRandomBits = autoRandBits
}
}
return nil
}

func extractAutoRandomBitsFromColDef(colDef *ast.ColumnDef) (uint64, error) {
for _, op := range colDef.Options {
if op.Tp == ast.ColumnOptionAutoRandom {
return convertAutoRandomBitsToUnsigned(op.AutoRandomBitLength)
}
}
return 0, nil
}

func convertAutoRandomBitsToUnsigned(autoRandomBits int) (uint64, error) {
if autoRandomBits == types.UnspecifiedLength {
return autoid.DefaultAutoRandomBits, nil
} else if autoRandomBits < 0 {
return 0, ErrInvalidAutoRandom.GenWithStackByArgs(autoid.AutoRandomNonPositive)
}
return uint64(autoRandomBits), nil
}

func buildTableInfo(ctx sessionctx.Context, d *ddl, tableName model.CIStr, cols []*table.Column, constraints []*ast.Constraint) (tbInfo *model.TableInfo, err error) {
tbInfo = &model.TableInfo{
Name: tableName,
Expand Down Expand Up @@ -1152,7 +1204,7 @@ func buildTableInfo(ctx sessionctx.Context, d *ddl, tableName model.CIStr, cols
if err != nil {
return nil, errors.Trace(err)
}
//check if the index is primary or uniqiue.
// check if the index is primary or unique.
switch constr.Tp {
case ast.ConstraintPrimaryKey:
idxInfo.Primary = true
Expand Down Expand Up @@ -1323,6 +1375,10 @@ func buildTableInfoWithCheck(ctx sessionctx.Context, d *ddl, s *ast.CreateTableS
tbInfo.Collate = tableCollate
tbInfo.Charset = tableCharset

if err = setTableAutoRandomBits(tbInfo, colDefs); err != nil {
return nil, errors.Trace(err)
}

pi, err := buildTablePartitionInfo(ctx, d, s)
if err != nil {
return nil, errors.Trace(err)
Expand Down Expand Up @@ -1719,9 +1775,9 @@ func checkCharsetAndCollation(cs string, co string) error {
// handleAutoIncID handles auto_increment option in DDL. It creates a ID counter for the table and initiates the counter to a proper value.
// For example if the option sets auto_increment to 10. The counter will be set to 9. So the next allocated ID will be 10.
func (d *ddl) handleAutoIncID(tbInfo *model.TableInfo, schemaID int64) error {
alloc := autoid.NewAllocator(d.store, tbInfo.GetDBID(schemaID), tbInfo.IsAutoIncColUnsigned())
allocs := autoid.NewAllocatorsFromTblInfo(d.store, schemaID, tbInfo)
tbInfo.State = model.StatePublic
tb, err := table.TableFromMeta(alloc, tbInfo)
tb, err := table.TableFromMeta(allocs, tbInfo)
if err != nil {
return errors.Trace(err)
}
Expand Down Expand Up @@ -1978,7 +2034,7 @@ func (d *ddl) RebaseAutoID(ctx sessionctx.Context, ident ast.Ident, newBase int6
if err != nil {
return errors.Trace(err)
}
autoIncID, err := t.Allocator(ctx).NextGlobalAutoID(t.Meta().ID)
autoIncID, err := t.Allocator(ctx, autoid.RowIDAllocType).NextGlobalAutoID(t.Meta().ID)
if err != nil {
return errors.Trace(err)
}
Expand Down Expand Up @@ -2524,6 +2580,8 @@ func processColumnOptions(ctx sessionctx.Context, col *table.Column, options []*
return errors.Trace(errUnsupportedModifyColumn.GenWithStackByArgs("with references"))
case ast.ColumnOptionFulltext:
return errors.Trace(errUnsupportedModifyColumn.GenWithStackByArgs("with full text"))
// Ignore ColumnOptionAutoRandom. It will be handled later.
case ast.ColumnOptionAutoRandom:
default:
return errors.Trace(errUnsupportedModifyColumn.GenWithStackByArgs(fmt.Sprintf("unknown column option type: %d", opt.Tp)))
}
Expand Down Expand Up @@ -2668,6 +2726,10 @@ func (d *ddl) getModifiableColumnJob(ctx sessionctx.Context, ident ast.Ident, or
return nil, errors.Trace(err)
}

if err = checkAutoRandom(t.Meta(), col, specNewColumn); err != nil {
return nil, errors.Trace(err)
}

job := &model.Job{
SchemaID: schema.ID,
TableID: t.Meta().ID,
Expand Down Expand Up @@ -2715,6 +2777,36 @@ func checkColumnWithIndexConstraint(tbInfo *model.TableInfo, originalCol, newCol
return nil
}

func checkAutoRandom(tableInfo *model.TableInfo, originCol *table.Column, specNewColumn *ast.ColumnDef) error {
if !config.GetGlobalConfig().Experimental.AllowAutoRandom && containsColumnOption(specNewColumn, ast.ColumnOptionAutoRandom) {
return ErrInvalidAutoRandom.GenWithStackByArgs(autoid.AutoRandomExperimentalDisabledErrMsg)
}
// Disallow add/drop/modify actions on auto_random.
newAutoRandomBit, err := extractAutoRandomBitsFromColDef(specNewColumn)
if err != nil {
return errors.Trace(err)
}
if tableInfo.AutoRandomBits != newAutoRandomBit {
return ErrInvalidAutoRandom.GenWithStackByArgs(autoid.AutoRandomAlterErrMsg)
}

if tableInfo.AutoRandomBits != 0 {
// Disallow changing the column field type.
if originCol.Tp != specNewColumn.Tp.Tp {
return ErrInvalidAutoRandom.GenWithStackByArgs(autoid.AutoRandomModifyColTypeErrMsg)
}
// Disallow changing auto_increment on auto_random column.
if containsColumnOption(specNewColumn, ast.ColumnOptionAutoIncrement) != mysql.HasAutoIncrementFlag(originCol.Flag) {
return ErrInvalidAutoRandom.GenWithStackByArgs(autoid.AutoRandomIncompatibleWithAutoIncErrMsg)
}
// Disallow specifying a default value on auto_random column.
if containsColumnOption(specNewColumn, ast.ColumnOptionDefaultValue) {
return ErrInvalidAutoRandom.GenWithStackByArgs(autoid.AutoRandomIncompatibleWithDefaultValueErrMsg)
}
}
return nil
}

// ChangeColumn renames an existing column and modifies the column's definition,
// currently we only support limited kind of changes
// that do not need to change or check data on the table.
Expand Down
Loading

0 comments on commit 1516653

Please sign in to comment.