diff --git a/ddl/db_test.go b/ddl/db_test.go index a26f82a7ae972..d7e00e1381491 100644 --- a/ddl/db_test.go +++ b/ddl/db_test.go @@ -2671,3 +2671,40 @@ func (s *testDBSuite) TestModifyColumnCharset(c *C) { ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin")) } + +func (s *testDBSuite) TestAlterShardRowIDBits(c *C) { + s.tk = testkit.NewTestKit(c, s.store) + tk := s.tk + + tk.MustExec("use test") + // Test alter shard_row_id_bits + tk.MustExec("drop table if exists t1") + defer tk.MustExec("drop table if exists t1") + tk.MustExec("create table t1 (a int) shard_row_id_bits = 5") + tk.MustExec(fmt.Sprintf("alter table t1 auto_increment = %d;", 1<<56)) + tk.MustExec("insert into t1 set a=1;") + + // Test increase shard_row_id_bits failed by overflow global auto ID. + _, err := tk.Exec("alter table t1 SHARD_ROW_ID_BITS = 10;") + c.Assert(err, NotNil) + c.Assert(err.Error(), Equals, "[autoid:1467]shard_row_id_bits 10 will cause next global auto ID overflow") + + // Test reduce shard_row_id_bits will be ok. + tk.MustExec("alter table t1 SHARD_ROW_ID_BITS = 3;") + checkShardRowID := func(maxShardRowIDBits, shardRowIDBits uint64) { + tbl := testGetTableByName(c, tk.Se, "test", "t1") + c.Assert(tbl.Meta().MaxShardRowIDBits == maxShardRowIDBits, IsTrue) + c.Assert(tbl.Meta().ShardRowIDBits == shardRowIDBits, IsTrue) + } + checkShardRowID(5, 3) + + // Test reduce shard_row_id_bits but calculate overflow should use the max record shard_row_id_bits. + tk.MustExec("drop table if exists t1") + tk.MustExec("create table t1 (a int) shard_row_id_bits = 10") + tk.MustExec("alter table t1 SHARD_ROW_ID_BITS = 5;") + checkShardRowID(10, 5) + tk.MustExec(fmt.Sprintf("alter table t1 auto_increment = %d;", 1<<56)) + _, err = tk.Exec("insert into t1 set a=1;") + c.Assert(err, NotNil) + c.Assert(err.Error(), Equals, "[autoid:1467]Failed to read auto-increment value from storage engine") +} diff --git a/ddl/ddl_api.go b/ddl/ddl_api.go index 8a83294f627a9..c7c79bf614136 100644 --- a/ddl/ddl_api.go +++ b/ddl/ddl_api.go @@ -1563,6 +1563,7 @@ func handleTableOptions(options []*ast.TableOption, tbInfo *model.TableInfo) err if tbInfo.ShardRowIDBits > shardRowIDBitsMax { tbInfo.ShardRowIDBits = shardRowIDBitsMax } + tbInfo.MaxShardRowIDBits = tbInfo.ShardRowIDBits } } @@ -1804,6 +1805,14 @@ func (d *ddl) ShardRowID(ctx sessionctx.Context, tableIdent ast.Ident, uVal uint if ok && uVal != 0 { return errUnsupportedShardRowIDBits } + if uVal == t.Meta().ShardRowIDBits { + // Nothing need to do. + return nil + } + err = verifyNoOverflowShardBits(d.sessPool, t, uVal) + if err != nil { + return err + } job := &model.Job{ Type: model.ActionShardRowID, SchemaID: schema.ID, diff --git a/ddl/ddl_worker.go b/ddl/ddl_worker.go index 3a0e9c8f74459..62ed299cc45b1 100644 --- a/ddl/ddl_worker.go +++ b/ddl/ddl_worker.go @@ -525,7 +525,7 @@ func (w *worker) runDDLJob(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, case model.ActionRenameTable: ver, err = onRenameTable(t, job) case model.ActionShardRowID: - ver, err = onShardRowID(t, job) + ver, err = w.onShardRowID(d, t, job) case model.ActionModifyTableComment: ver, err = onModifyTableComment(t, job) case model.ActionAddTablePartition: diff --git a/ddl/table.go b/ddl/table.go index d0dd67816d7db..7cb63bf6e6915 100644 --- a/ddl/table.go +++ b/ddl/table.go @@ -28,6 +28,7 @@ import ( "github.com/pingcap/tidb/meta" "github.com/pingcap/tidb/meta/autoid" "github.com/pingcap/tidb/table" + "github.com/pingcap/tidb/table/tables" "github.com/pingcap/tidb/tablecodec" "github.com/pingcap/tidb/util/gcutil" "github.com/pingcap/tidb/util/logutil" @@ -469,7 +470,7 @@ func onRebaseAutoID(store kv.Storage, t *meta.Meta, job *model.Job) (ver int64, return ver, nil } -func onShardRowID(t *meta.Meta, job *model.Job) (ver int64, _ error) { +func (w *worker) onShardRowID(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, _ error) { var shardRowIDBits uint64 err := job.DecodeArgs(&shardRowIDBits) if err != nil { @@ -481,7 +482,22 @@ func onShardRowID(t *meta.Meta, job *model.Job) (ver int64, _ error) { job.State = model.JobStateCancelled return ver, errors.Trace(err) } - tblInfo.ShardRowIDBits = shardRowIDBits + if shardRowIDBits < tblInfo.ShardRowIDBits { + tblInfo.ShardRowIDBits = shardRowIDBits + } else { + tbl, err := getTable(d.store, job.SchemaID, tblInfo) + if err != nil { + return ver, errors.Trace(err) + } + err = verifyNoOverflowShardBits(w.sessPool, tbl, shardRowIDBits) + if err != nil { + job.State = model.JobStateCancelled + return ver, err + } + tblInfo.ShardRowIDBits = shardRowIDBits + // MaxShardRowIDBits use to check the overflow of auto ID. + tblInfo.MaxShardRowIDBits = shardRowIDBits + } ver, err = updateVersionAndTableInfo(t, job, tblInfo, true) if err != nil { job.State = model.JobStateCancelled @@ -491,6 +507,24 @@ func onShardRowID(t *meta.Meta, job *model.Job) (ver int64, _ error) { return ver, nil } +func verifyNoOverflowShardBits(s *sessionPool, tbl table.Table, shardRowIDBits uint64) error { + ctx, err := s.get() + if err != nil { + return errors.Trace(err) + } + defer s.put(ctx) + + // Check next global max auto ID first. + autoIncID, err := tbl.Allocator(ctx).NextGlobalAutoID(tbl.Meta().ID) + if err != nil { + return errors.Trace(err) + } + if tables.OverflowShardBits(autoIncID, shardRowIDBits) { + return autoid.ErrAutoincReadFailed.GenWithStack("shard_row_id_bits %d will cause next global auto ID overflow", shardRowIDBits) + } + return nil +} + func onRenameTable(t *meta.Meta, job *model.Job) (ver int64, _ error) { var oldSchemaID int64 var tableName model.CIStr diff --git a/go.mod b/go.mod index 6fa5728227e4d..ca535624af6d6 100644 --- a/go.mod +++ b/go.mod @@ -51,7 +51,7 @@ require ( github.com/pingcap/goleveldb v0.0.0-20171020122428-b9ff6c35079e github.com/pingcap/kvproto v0.0.0-20190215154024-7f2fc73ef562 github.com/pingcap/log v0.0.0-20190307075452-bd41d9273596 - github.com/pingcap/parser v0.0.0-20190408064140-cdceeb2c5476 + github.com/pingcap/parser v0.0.0-20190409044748-a0b301443a30 github.com/pingcap/pd v2.1.0-rc.4+incompatible github.com/pingcap/tidb-tools v2.1.3-0.20190321065848-1e8b48f5c168+incompatible github.com/pingcap/tipb v0.0.0-20190107072121-abbec73437b7 diff --git a/go.sum b/go.sum index d482caa82c799..6cf70f55d8c18 100644 --- a/go.sum +++ b/go.sum @@ -119,8 +119,8 @@ github.com/pingcap/kvproto v0.0.0-20190215154024-7f2fc73ef562 h1:32oF1/8lVnBR2JV github.com/pingcap/kvproto v0.0.0-20190215154024-7f2fc73ef562/go.mod h1:QMdbTAXCHzzygQzqcG9uVUgU2fKeSN1GmfMiykdSzzY= github.com/pingcap/log v0.0.0-20190307075452-bd41d9273596 h1:t2OQTpPJnrPDGlvA+3FwJptMTt6MEPdzK1Wt99oaefQ= github.com/pingcap/log v0.0.0-20190307075452-bd41d9273596/go.mod h1:WpHUKhNZ18v116SvGrmjkA9CBhYmuUTKL+p8JC9ANEw= -github.com/pingcap/parser v0.0.0-20190408064140-cdceeb2c5476 h1:qKFG6B26Zfgpb7rUYB8PCGQzWB+USDCTmH+rR7rV+ow= -github.com/pingcap/parser v0.0.0-20190408064140-cdceeb2c5476/go.mod h1:1FNvfp9+J0wvc4kl8eGNh7Rqrxveg15jJoWo/a0uHwA= +github.com/pingcap/parser v0.0.0-20190409044748-a0b301443a30 h1:Cu+VJBHLUqI0TFj/0Kya4L1iHIJZ3VbtZcEwv+3zOxQ= +github.com/pingcap/parser v0.0.0-20190409044748-a0b301443a30/go.mod h1:1FNvfp9+J0wvc4kl8eGNh7Rqrxveg15jJoWo/a0uHwA= github.com/pingcap/pd v2.1.0-rc.4+incompatible h1:/buwGk04aHO5odk/+O8ZOXGs4qkUjYTJ2UpCJXna8NE= github.com/pingcap/pd v2.1.0-rc.4+incompatible/go.mod h1:nD3+EoYes4+aNNODO99ES59V83MZSI+dFbhyr667a0E= github.com/pingcap/tidb-tools v2.1.3-0.20190321065848-1e8b48f5c168+incompatible h1:MkWCxgZpJBgY2f4HtwWMMFzSBb3+JPzeJgF3VrXE/bU= diff --git a/table/tables/tables.go b/table/tables/tables.go index 3afdc2b0247ae..dec149c6a3bcc 100644 --- a/table/tables/tables.go +++ b/table/tables/tables.go @@ -925,7 +925,8 @@ func (t *tableCommon) AllocAutoID(ctx sessionctx.Context) (int64, error) { return 0, err } if t.meta.ShardRowIDBits > 0 { - if t.overflowShardBits(rowID) { + // Use max record ShardRowIDBits to check overflow. + if OverflowShardBits(rowID, t.meta.MaxShardRowIDBits) { // If overflow, the rowID may be duplicated. For examples, // t.meta.ShardRowIDBits = 4 // rowID = 0010111111111111111111111111111111111111111111111111111111111111 @@ -945,9 +946,9 @@ func (t *tableCommon) AllocAutoID(ctx sessionctx.Context) (int64, error) { return rowID, nil } -// overflowShardBits check whether the rowID overflow `1<<(64-t.meta.ShardRowIDBits-1) -1`. -func (t *tableCommon) overflowShardBits(rowID int64) bool { - mask := (1< 0 }