From 201ae81d76f6f5e04b71d4a7a703497963789eb8 Mon Sep 17 00:00:00 2001 From: tangenta Date: Thu, 1 Jul 2021 17:29:58 +0800 Subject: [PATCH 1/5] ddl, meta: support altering auto_increment ID to a smaller value --- ddl/db_integration_test.go | 61 ++++++++++++++++++++++++ ddl/ddl_api.go | 37 +++++++++----- ddl/table.go | 13 +++-- go.mod | 8 ++-- go.sum | 16 ++++--- meta/autoid/autoid.go | 37 +++++++++++++- meta/autoid/memid.go | 6 +++ sessionctx/binloginfo/binloginfo_test.go | 8 ++++ types/parser_driver/special_cmt_ctrl.go | 4 ++ 9 files changed, 162 insertions(+), 28 deletions(-) diff --git a/ddl/db_integration_test.go b/ddl/db_integration_test.go index 0c64aa3e2bdac..101648bf61b4d 100644 --- a/ddl/db_integration_test.go +++ b/ddl/db_integration_test.go @@ -16,6 +16,7 @@ package ddl_test import ( "context" "fmt" + "math" "strconv" "strings" "sync/atomic" @@ -2621,6 +2622,66 @@ func (s *testSerialDBSuite1) TestAutoIncrementTableOption(c *C) { tk.MustQuery("select * from t;").Check(testkit.Rows("12345678901234567890")) } +func (s *testIntegrationSuite3) TestAutoIncrementForce(c *C) { + tk := testkit.NewTestKit(c, s.store) + tk.MustExec("drop database if exists auto_inc_force;") + tk.MustExec("create database auto_inc_force;") + tk.MustExec("use auto_inc_force;") + getNextGlobalID := func() uint64 { + gidStr := tk.MustQuery("show table t next_row_id").Rows()[0][3] + gid, err := strconv.ParseUint(gidStr.(string), 10, 64) + c.Assert(err, IsNil) + return gid + } + // rebase _tidb_row_id. + tk.MustExec("create table t (a int);") + tk.MustExec("insert into t values (1),(2);") + tk.MustQuery("select a, _tidb_rowid from t;").Check(testkit.Rows("1 1", "2 2")) + tk.MustExec("alter table t force auto_increment = 1;") + c.Assert(getNextGlobalID(), Equals, uint64(1)) + // inserting new rows can overwrite the existing data. + tk.MustExec("insert into t values (3);") + tk.MustExec("insert into t values (3);") + tk.MustQuery("select a, _tidb_rowid from t;").Check(testkit.Rows("3 1", "3 2")) + + // rebase auto_increment. + tk.MustExec("drop table if exists t;") + tk.MustExec("create table t (a int primary key auto_increment, b int);") + tk.MustExec("insert into t values (1, 1);") + tk.MustExec("insert into t values (100000000, 1);") + tk.MustExec("delete from t where a = 100000000;") + c.Assert(getNextGlobalID(), Greater, uint64(100000000)) + tk.MustExec("alter table t /*T![force_inc] force */ auto_increment = 2;") + c.Assert(getNextGlobalID(), Equals, uint64(2)) + tk.MustExec("insert into t(b) values (2);") + tk.MustQuery("select a, b from t;").Check(testkit.Rows("1 1", "2 2")) + + // rebase auto_random. + tk.MustExec("drop table if exists t;") + tk.MustExec("create table t (a bigint primary key auto_random(5));") + tk.MustExec("insert into t values ();") + tk.MustExec("set @@allow_auto_random_explicit_insert = true") + tk.MustExec("insert into t values (100000000);") + tk.MustExec("delete from t where a = 100000000;") + c.Assert(getNextGlobalID(), Greater, uint64(100000000)) + tk.MustExec("alter table t force auto_random_base = 2;") + c.Assert(getNextGlobalID(), Equals, uint64(2)) + tk.MustExec("insert into t values ();") + tk.MustQuery("select (a & 3) from t order by 1;").Check(testkit.Rows("1", "2")) + + // change next_global_id. + tk.MustExec("drop table if exists t;") + tk.MustExec("create table t (a bigint primary key auto_increment);") + tk.MustExec("insert into t values (1);") + bases := []uint64{1, 65535, 10, math.MaxUint64, math.MaxInt64 + 1, 1, math.MaxUint64, math.MaxInt64, 2} + for _, b := range bases { + tk.MustExec(fmt.Sprintf("alter table t force auto_increment = %d;", b)) + c.Assert(getNextGlobalID(), Equals, b) + } + tk.MustExec("insert into t values ();") + tk.MustQuery("select a from t;").Check(testkit.Rows("1", "2")) +} + func (s *testIntegrationSuite3) TestIssue20490(c *C) { tk := testkit.NewTestKit(c, s.store) tk.MustExec("use test;") diff --git a/ddl/ddl_api.go b/ddl/ddl_api.go index 5cec63b7d51b2..945b67991930b 100644 --- a/ddl/ddl_api.go +++ b/ddl/ddl_api.go @@ -2534,7 +2534,7 @@ func (d *ddl) AlterTable(ctx sessionctx.Context, ident ast.Ident, specs []*ast.A } err = d.ShardRowID(ctx, ident, opt.UintValue) case ast.TableOptionAutoIncrement: - err = d.RebaseAutoID(ctx, ident, int64(opt.UintValue), autoid.RowIDAllocType) + err = d.RebaseAutoID(ctx, ident, int64(opt.UintValue), autoid.RowIDAllocType, opt.BoolValue) case ast.TableOptionAutoIdCache: if opt.UintValue > uint64(math.MaxInt64) { // TODO: Refine this error. @@ -2542,7 +2542,7 @@ func (d *ddl) AlterTable(ctx sessionctx.Context, ident ast.Ident, specs []*ast.A } err = d.AlterTableAutoIDCache(ctx, ident, int64(opt.UintValue)) case ast.TableOptionAutoRandomBase: - err = d.RebaseAutoID(ctx, ident, int64(opt.UintValue), autoid.AutoRandomType) + err = d.RebaseAutoID(ctx, ident, int64(opt.UintValue), autoid.AutoRandomType, opt.BoolValue) case ast.TableOptionComment: spec.Comment = opt.StrValue err = d.AlterTableComment(ctx, ident, spec) @@ -2598,7 +2598,7 @@ func (d *ddl) AlterTable(ctx sessionctx.Context, ident ast.Ident, specs []*ast.A return nil } -func (d *ddl) RebaseAutoID(ctx sessionctx.Context, ident ast.Ident, newBase int64, tp autoid.AllocatorType) error { +func (d *ddl) RebaseAutoID(ctx sessionctx.Context, ident ast.Ident, newBase int64, tp autoid.AllocatorType, force bool) error { schema, t, err := d.getSchemaAndTableByIdent(ctx, ident) if err != nil { return errors.Trace(err) @@ -2627,17 +2627,11 @@ func (d *ddl) RebaseAutoID(ctx sessionctx.Context, ident ast.Ident, newBase int6 actionType = model.ActionRebaseAutoID } - if alloc := t.Allocators(ctx).Get(tp); alloc != nil { - autoID, err := alloc.NextGlobalAutoID(t.Meta().ID) + if !force { + newBase, err = adjustNewBaseToNextGlobalID(ctx, t, tp, newBase) if err != nil { - return errors.Trace(err) + return err } - // If newBase < autoID, we need to do a rebase before returning. - // Assume there are 2 TiDB servers: TiDB-A with allocator range of 0 ~ 30000; TiDB-B with allocator range of 30001 ~ 60000. - // If the user sends SQL `alter table t1 auto_increment = 100` to TiDB-B, - // and TiDB-B finds 100 < 30001 but returns without any handling, - // then TiDB-A may still allocate 99 for auto_increment column. This doesn't make sense for the user. - newBase = int64(mathutil.MaxUint64(uint64(newBase), uint64(autoID))) } job := &model.Job{ SchemaID: schema.ID, @@ -2645,13 +2639,30 @@ func (d *ddl) RebaseAutoID(ctx sessionctx.Context, ident ast.Ident, newBase int6 SchemaName: schema.Name.L, Type: actionType, BinlogInfo: &model.HistoryInfo{}, - Args: []interface{}{newBase}, + Args: []interface{}{newBase, force}, } err = d.doDDLJob(ctx, job) err = d.callHookOnChanged(err) return errors.Trace(err) } +func adjustNewBaseToNextGlobalID(ctx sessionctx.Context, t table.Table, tp autoid.AllocatorType, newBase int64) (int64, error) { + alloc := t.Allocators(ctx).Get(tp) + if alloc == nil { + return newBase, nil + } + autoID, err := alloc.NextGlobalAutoID(t.Meta().ID) + if err != nil { + return newBase, errors.Trace(err) + } + // If newBase < autoID, we need to do a rebase before returning. + // Assume there are 2 TiDB servers: TiDB-A with allocator range of 0 ~ 30000; TiDB-B with allocator range of 30001 ~ 60000. + // If the user sends SQL `alter table t1 auto_increment = 100` to TiDB-B, + // and TiDB-B finds 100 < 30001 but returns without any handling, + // then TiDB-A may still allocate 99 for auto_increment column. This doesn't make sense for the user. + return int64(mathutil.MaxUint64(uint64(newBase), uint64(autoID))), nil +} + // ShardRowID shards the implicit row ID by adding shard value to the row ID's first few bits. func (d *ddl) ShardRowID(ctx sessionctx.Context, tableIdent ast.Ident, uVal uint64) error { schema, t, err := d.getSchemaAndTableByIdent(ctx, tableIdent) diff --git a/ddl/table.go b/ddl/table.go index 264f396404d4a..aa98f6467f400 100644 --- a/ddl/table.go +++ b/ddl/table.go @@ -556,8 +556,11 @@ func onRebaseAutoRandomType(store kv.Storage, t *meta.Meta, job *model.Job) (ver func onRebaseAutoID(store kv.Storage, t *meta.Meta, job *model.Job, tp autoid.AllocatorType) (ver int64, _ error) { schemaID := job.SchemaID - var newBase int64 - err := job.DecodeArgs(&newBase) + var ( + newBase int64 + force bool + ) + err := job.DecodeArgs(&newBase, &force) if err != nil { job.State = model.JobStateCancelled return ver, errors.Trace(err) @@ -582,7 +585,11 @@ func onRebaseAutoID(store kv.Storage, t *meta.Meta, job *model.Job, tp autoid.Al if alloc := tbl.Allocators(nil).Get(tp); alloc != nil { // The next value to allocate is `newBase`. newEnd := newBase - 1 - err = alloc.Rebase(tblInfo.ID, newEnd, false) + if force { + err = alloc.ForceRebase(tblInfo.ID, newEnd) + } else { + err = alloc.Rebase(tblInfo.ID, newEnd, false) + } if err != nil { return ver, errors.Trace(err) } diff --git a/go.mod b/go.mod index 1014a369a1bc1..96e4ebea2da7e 100644 --- a/go.mod +++ b/go.mod @@ -39,12 +39,12 @@ require ( github.com/pingcap/badger v1.5.1-0.20200908111422-2e78ee155d19 github.com/pingcap/br v5.2.0-alpha.0.20210611153635-74f18bcbe19d+incompatible github.com/pingcap/check v0.0.0-20200212061837-5e12011dc712 - github.com/pingcap/errors v0.11.5-0.20201126102027-b0a155152ca3 + github.com/pingcap/errors v0.11.5-0.20210513014640-40f9a1999b3b github.com/pingcap/failpoint v0.0.0-20210316064728-7acb0f0a3dfd github.com/pingcap/fn v0.0.0-20200306044125-d5540d389059 github.com/pingcap/kvproto v0.0.0-20210611081648-a215b4e61d2f - github.com/pingcap/log v0.0.0-20210317133921-96f4fcab92a4 - github.com/pingcap/parser v0.0.0-20210618053735-57843e8185c4 + github.com/pingcap/log v0.0.0-20210625125904-98ed8e2eb1c7 + github.com/pingcap/parser v0.0.0-20210630155204-f88b9d764298 github.com/pingcap/sysutil v0.0.0-20210315073920-cc0985d983a3 github.com/pingcap/tidb-tools v4.0.9-0.20201127090955-2707c97b3853+incompatible github.com/pingcap/tipb v0.0.0-20210628060001-1793e022b962 @@ -68,7 +68,7 @@ require ( go.etcd.io/etcd v0.5.0-alpha.5.0.20200824191128-ae9734ed278b go.uber.org/atomic v1.8.0 go.uber.org/automaxprocs v1.4.0 - go.uber.org/zap v1.17.0 + go.uber.org/zap v1.18.1 golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c golang.org/x/sys v0.0.0-20210510120138-977fb7262007 diff --git a/go.sum b/go.sum index 5acdf43775659..b4c5ca9fecc36 100644 --- a/go.sum +++ b/go.sum @@ -60,6 +60,8 @@ github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5 github.com/aws/aws-sdk-go v1.30.19/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= github.com/aws/aws-sdk-go v1.35.3 h1:r0puXncSaAfRt7Btml2swUo74Kao+vKhO3VLjwDjK54= github.com/aws/aws-sdk-go v1.35.3/go.mod h1:H7NKnBqNVzoTJpGfLrQkkD+ytBA93eiDYi/+8rV9s48= +github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= +github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= @@ -421,8 +423,9 @@ github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTw github.com/pingcap/errors v0.11.5-0.20190809092503-95897b64e011/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= github.com/pingcap/errors v0.11.5-0.20200917111840-a15ef68f753d/go.mod h1:g4vx//d6VakjJ0mk7iLBlKA8LFavV/sAVINT/1PFxeQ= github.com/pingcap/errors v0.11.5-0.20201029093017-5a7df2af2ac7/go.mod h1:G7x87le1poQzLB/TqvTJI2ILrSgobnq4Ut7luOwvfvI= -github.com/pingcap/errors v0.11.5-0.20201126102027-b0a155152ca3 h1:LllgC9eGfqzkfubMgjKIDyZYaa609nNWAyNZtpy2B3M= github.com/pingcap/errors v0.11.5-0.20201126102027-b0a155152ca3/go.mod h1:G7x87le1poQzLB/TqvTJI2ILrSgobnq4Ut7luOwvfvI= +github.com/pingcap/errors v0.11.5-0.20210513014640-40f9a1999b3b h1:j9MP8ma4e75tckq11+n4EhB2xq0xwYNoxL8w9JTZRhs= +github.com/pingcap/errors v0.11.5-0.20210513014640-40f9a1999b3b/go.mod h1:X2r9ueLEUZgtx2cIogM0v4Zj5uvvzhuuiu7Pn8HzMPg= github.com/pingcap/failpoint v0.0.0-20191029060244-12f4ac2fd11d/go.mod h1:DNS3Qg7bEDhU6EXNHF+XSv/PGznQaMJ5FWvctpm6pQI= github.com/pingcap/failpoint v0.0.0-20200702092429-9f69995143ce/go.mod h1:w4PEZ5y16LeofeeGwdgZB4ddv9bLyDuIX+ljstgKZyk= github.com/pingcap/failpoint v0.0.0-20210316064728-7acb0f0a3dfd h1:I8IeI8MNiZVKnwuXhcIIzz6pREcOSbq18Q31KYIzFVM= @@ -440,11 +443,12 @@ github.com/pingcap/kvproto v0.0.0-20210611081648-a215b4e61d2f/go.mod h1:IOdRDPLy github.com/pingcap/log v0.0.0-20191012051959-b742a5d432e9/go.mod h1:4rbK1p9ILyIfb6hU7OG2CiWSqMXnp3JMbiaVJ6mvoY8= github.com/pingcap/log v0.0.0-20200511115504-543df19646ad/go.mod h1:4rbK1p9ILyIfb6hU7OG2CiWSqMXnp3JMbiaVJ6mvoY8= github.com/pingcap/log v0.0.0-20201112100606-8f1e84a3abc8/go.mod h1:4rbK1p9ILyIfb6hU7OG2CiWSqMXnp3JMbiaVJ6mvoY8= -github.com/pingcap/log v0.0.0-20210317133921-96f4fcab92a4 h1:ERrF0fTuIOnwfGbt71Ji3DKbOEaP189tjym50u8gpC8= github.com/pingcap/log v0.0.0-20210317133921-96f4fcab92a4/go.mod h1:4rbK1p9ILyIfb6hU7OG2CiWSqMXnp3JMbiaVJ6mvoY8= +github.com/pingcap/log v0.0.0-20210625125904-98ed8e2eb1c7 h1:k2BbABz9+TNpYRwsCCFS8pEEnFVOdbgEjL/kTlLuzZQ= +github.com/pingcap/log v0.0.0-20210625125904-98ed8e2eb1c7/go.mod h1:8AanEdAHATuRurdGxZXBz0At+9avep+ub7U1AGYLIMM= github.com/pingcap/parser v0.0.0-20210525032559-c37778aff307/go.mod h1:xZC8I7bug4GJ5KtHhgAikjTfU4kBv1Sbo3Pf1MZ6lVw= -github.com/pingcap/parser v0.0.0-20210618053735-57843e8185c4 h1:NASsbyMTNW8pbYfoO/YTykO6MQJiNRa094lwCPU6R2Q= -github.com/pingcap/parser v0.0.0-20210618053735-57843e8185c4/go.mod h1:xZC8I7bug4GJ5KtHhgAikjTfU4kBv1Sbo3Pf1MZ6lVw= +github.com/pingcap/parser v0.0.0-20210630155204-f88b9d764298 h1:eR0JcoYs5WJ/mqacOT0UoNN3DuC/5l0KzNkXWerLguI= +github.com/pingcap/parser v0.0.0-20210630155204-f88b9d764298/go.mod h1:0JrGN/1YAvHx/ruYM0Uhv86s5HVfkmsn0SyhC58Tj0Y= github.com/pingcap/sysutil v0.0.0-20200206130906-2bfa6dc40bcd/go.mod h1:EB/852NMQ+aRKioCpToQ94Wl7fktV+FNnxf3CX/TTXI= github.com/pingcap/sysutil v0.0.0-20210315073920-cc0985d983a3 h1:A9KL9R+lWSVPH8IqUuH1QSTRJ5FGoY1bT2IcfPKsWD8= github.com/pingcap/sysutil v0.0.0-20210315073920-cc0985d983a3/go.mod h1:tckvA041UWP+NqYzrJ3fMgC/Hw9wnmQ/tUkp/JaHly8= @@ -646,8 +650,8 @@ go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.12.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ= -go.uber.org/zap v1.17.0 h1:MTjgFu6ZLKvY6Pvaqk97GlxNBuMpV4Hy/3P6tRGlI2U= -go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= +go.uber.org/zap v1.18.1 h1:CSUJ2mjFszzEWt4CdKISEuChVIXGBn3lAPwkRGyVrc4= +go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= golang.org/x/crypto v0.0.0-20180723164146-c126467f60eb/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= diff --git a/meta/autoid/autoid.go b/meta/autoid/autoid.go index c4447c54f8844..1bf12eacd6400 100644 --- a/meta/autoid/autoid.go +++ b/meta/autoid/autoid.go @@ -135,6 +135,9 @@ type Allocator interface { // If allocIDs is false, it will not allocate IDs. Rebase(tableID, newBase int64, allocIDs bool) error + // ForceRebase set the next global auto ID to newBase. + ForceRebase(tableID, newBase int64) error + // RebaseSeq rebases the sequence value in number axis with tableID and the new base value. RebaseSeq(table, newBase int64) (int64, bool, error) @@ -370,16 +373,46 @@ func (alloc *allocator) Rebase(tableID, requiredBase int64, allocIDs bool) error if tableID == 0 { return errInvalidTableID.GenWithStack("Invalid tableID") } - alloc.mu.Lock() defer alloc.mu.Unlock() - if alloc.isUnsigned { return alloc.rebase4Unsigned(tableID, uint64(requiredBase), allocIDs) } return alloc.rebase4Signed(tableID, requiredBase, allocIDs) } +// ForceRebase implements autoid.Allocator ForceRebase interface. +func (alloc *allocator) ForceRebase(tableID, requiredBase int64) error { + if tableID <= 0 { + return errInvalidTableID.GenWithStack("Invalid tableID") + } + alloc.mu.Lock() + defer alloc.mu.Unlock() + startTime := time.Now() + err := kv.RunInNewTxn(context.Background(), alloc.store, true, func(ctx context.Context, txn kv.Transaction) error { + m := meta.NewMeta(txn) + currentEnd, err1 := GetAutoID(m, alloc.dbID, tableID, alloc.allocType) + if err1 != nil { + return err1 + } + var step int64 + if !alloc.isUnsigned { + step = requiredBase - currentEnd + } else { + uRequiredBase, uCurrentEnd := uint64(requiredBase), uint64(currentEnd) + step = int64(uRequiredBase - uCurrentEnd) + } + _, err1 = GenerateAutoID(m, alloc.dbID, tableID, step, alloc.allocType) + return err1 + }) + metrics.AutoIDHistogram.WithLabelValues(metrics.TableAutoIDRebase, metrics.RetLabel(err)).Observe(time.Since(startTime).Seconds()) + if err != nil { + return err + } + alloc.base, alloc.end = requiredBase, requiredBase + return nil +} + // Rebase implements autoid.Allocator RebaseSeq interface. // The return value is quite same as expression function, bool means whether it should be NULL, // here it will be used in setval expression function (true meaning the set value has been satisfied, return NULL). diff --git a/meta/autoid/memid.go b/meta/autoid/memid.go index d9a6be9f6058a..39db68e3bc33d 100644 --- a/meta/autoid/memid.go +++ b/meta/autoid/memid.go @@ -98,6 +98,12 @@ func (alloc *inMemoryAllocator) Rebase(tableID, requiredBase int64, allocIDs boo return nil } +// ForceRebase implements autoid.Allocator ForceRebase interface. +func (alloc *inMemoryAllocator) ForceRebase(tableID, requiredBase int64) error { + alloc.base = requiredBase + return nil +} + func (alloc *inMemoryAllocator) alloc4Signed(n uint64, increment, offset int64) (int64, int64, error) { // Check offset rebase if necessary. if offset-1 > alloc.base { diff --git a/sessionctx/binloginfo/binloginfo_test.go b/sessionctx/binloginfo/binloginfo_test.go index 6a586615bd8a0..4e52d2b100da6 100644 --- a/sessionctx/binloginfo/binloginfo_test.go +++ b/sessionctx/binloginfo/binloginfo_test.go @@ -666,6 +666,14 @@ func (s *testBinlogSuite) TestAddSpecialComment(c *C) { "create table t1 (id int, a varchar(255) key clustered);", "create table t1 (id int, a varchar(255) key /*T![clustered_index] clustered */ );", }, + { + "alter table t force auto_increment = 12;", + "alter table t /*T![force_inc] force */ auto_increment = 12;", + }, + { + "alter table t force, auto_increment = 12;", + "alter table t force, auto_increment = 12;", + }, } for _, ca := range testCase { re := binloginfo.AddSpecialComment(ca.input) diff --git a/types/parser_driver/special_cmt_ctrl.go b/types/parser_driver/special_cmt_ctrl.go index 6fa7ebf67d6cd..31505ebc40f71 100644 --- a/types/parser_driver/special_cmt_ctrl.go +++ b/types/parser_driver/special_cmt_ctrl.go @@ -35,6 +35,7 @@ func init() { parser.SpecialCommentsController.Register(string(FeatureIDAutoIDCache)) parser.SpecialCommentsController.Register(string(FeatureIDAutoRandomBase)) parser.SpecialCommentsController.Register(string(FeatureClusteredIndex)) + parser.SpecialCommentsController.Register(string(FeatureForceAutoInc)) } // SpecialCommentVersionPrefix is the prefix of TiDB executable comments. @@ -61,6 +62,8 @@ const ( FeatureIDAutoRandomBase featureID = "auto_rand_base" // FeatureClusteredIndex is the `clustered_index` feature. FeatureClusteredIndex featureID = "clustered_index" + // FeatureForceAutoInc is the `force auto_increment` feature. + FeatureForceAutoInc featureID = "force_inc" ) // FeatureIDPatterns is used to record special comments patterns. @@ -69,4 +72,5 @@ var FeatureIDPatterns = map[featureID]*regexp.Regexp{ FeatureIDAutoIDCache: regexp.MustCompile(`(?P(?i)AUTO_ID_CACHE\s*=?\s*\d+\s*)`), FeatureIDAutoRandomBase: regexp.MustCompile(`(?P(?i)AUTO_RANDOM_BASE\s*=?\s*\d+\s*)`), FeatureClusteredIndex: regexp.MustCompile(`(?i)(PRIMARY)?\s+KEY(\s*\(.*\))?\s+(?P(NON)?CLUSTERED\b)`), + FeatureForceAutoInc: regexp.MustCompile(`(?P(?i)FORCE)\b\s*AUTO_INCREMENT\s*`), } From 2ab4014d752825d7bab5788777bbc8b5b0e6f943 Mon Sep 17 00:00:00 2001 From: tangenta Date: Mon, 12 Jul 2021 10:35:46 +0800 Subject: [PATCH 2/5] disallow setting next global ID to 0 --- ddl/db_integration_test.go | 3 +++ ddl/table.go | 1 + meta/autoid/autoid.go | 3 +++ 3 files changed, 7 insertions(+) diff --git a/ddl/db_integration_test.go b/ddl/db_integration_test.go index 101648bf61b4d..ebb0fdf6fe06b 100644 --- a/ddl/db_integration_test.go +++ b/ddl/db_integration_test.go @@ -2680,6 +2680,9 @@ func (s *testIntegrationSuite3) TestAutoIncrementForce(c *C) { } tk.MustExec("insert into t values ();") tk.MustQuery("select a from t;").Check(testkit.Rows("1", "2")) + + // cannot set next global ID to 0. + tk.MustGetErrCode("alter table t force auto_increment = 0;", errno.ErrAutoincReadFailed) } func (s *testIntegrationSuite3) TestIssue20490(c *C) { diff --git a/ddl/table.go b/ddl/table.go index aa98f6467f400..dc503ca804a6b 100644 --- a/ddl/table.go +++ b/ddl/table.go @@ -591,6 +591,7 @@ func onRebaseAutoID(store kv.Storage, t *meta.Meta, job *model.Job, tp autoid.Al err = alloc.Rebase(tblInfo.ID, newEnd, false) } if err != nil { + job.State = model.JobStateCancelled return ver, errors.Trace(err) } } diff --git a/meta/autoid/autoid.go b/meta/autoid/autoid.go index 1bf12eacd6400..cf61beaff9781 100644 --- a/meta/autoid/autoid.go +++ b/meta/autoid/autoid.go @@ -386,6 +386,9 @@ func (alloc *allocator) ForceRebase(tableID, requiredBase int64) error { if tableID <= 0 { return errInvalidTableID.GenWithStack("Invalid tableID") } + if requiredBase == -1 { + return ErrAutoincReadFailed.GenWithStack("Cannot force rebase the next global ID to '0'") + } alloc.mu.Lock() defer alloc.mu.Unlock() startTime := time.Now() From 9684418e3dcc267479ff467b6479ba61498b74a7 Mon Sep 17 00:00:00 2001 From: tangenta Date: Tue, 13 Jul 2021 14:33:10 +0800 Subject: [PATCH 3/5] add test for unsigned auto_increment column --- ddl/db_integration_test.go | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/ddl/db_integration_test.go b/ddl/db_integration_test.go index b920623ed6df7..917d8a62bf7ec 100644 --- a/ddl/db_integration_test.go +++ b/ddl/db_integration_test.go @@ -2637,6 +2637,8 @@ func (s *testIntegrationSuite3) TestAutoIncrementForce(c *C) { tk.MustExec("create table t (a int);") tk.MustExec("insert into t values (1),(2);") tk.MustQuery("select a, _tidb_rowid from t;").Check(testkit.Rows("1 1", "2 2")) + // cannot set next global ID to 0. + tk.MustGetErrCode("alter table t force auto_increment = 0;", errno.ErrAutoincReadFailed) tk.MustExec("alter table t force auto_increment = 1;") c.Assert(getNextGlobalID(), Equals, uint64(1)) // inserting new rows can overwrite the existing data. @@ -2651,6 +2653,8 @@ func (s *testIntegrationSuite3) TestAutoIncrementForce(c *C) { tk.MustExec("insert into t values (100000000, 1);") tk.MustExec("delete from t where a = 100000000;") c.Assert(getNextGlobalID(), Greater, uint64(100000000)) + // cannot set next global ID to 0. + tk.MustGetErrCode("alter table t /*T![force_inc] force */ auto_increment = 0;", errno.ErrAutoincReadFailed) tk.MustExec("alter table t /*T![force_inc] force */ auto_increment = 2;") c.Assert(getNextGlobalID(), Equals, uint64(2)) tk.MustExec("insert into t(b) values (2);") @@ -2664,25 +2668,36 @@ func (s *testIntegrationSuite3) TestAutoIncrementForce(c *C) { tk.MustExec("insert into t values (100000000);") tk.MustExec("delete from t where a = 100000000;") c.Assert(getNextGlobalID(), Greater, uint64(100000000)) + // cannot set next global ID to 0. + tk.MustGetErrCode("alter table t force auto_random_base = 0;", errno.ErrAutoincReadFailed) tk.MustExec("alter table t force auto_random_base = 2;") c.Assert(getNextGlobalID(), Equals, uint64(2)) tk.MustExec("insert into t values ();") tk.MustQuery("select (a & 3) from t order by 1;").Check(testkit.Rows("1", "2")) - // change next_global_id. + // change next global ID. tk.MustExec("drop table if exists t;") tk.MustExec("create table t (a bigint primary key auto_increment);") tk.MustExec("insert into t values (1);") bases := []uint64{1, 65535, 10, math.MaxUint64, math.MaxInt64 + 1, 1, math.MaxUint64, math.MaxInt64, 2} + lastBase := fmt.Sprintf("%d", bases[len(bases)-1]) for _, b := range bases { tk.MustExec(fmt.Sprintf("alter table t force auto_increment = %d;", b)) c.Assert(getNextGlobalID(), Equals, b) } tk.MustExec("insert into t values ();") - tk.MustQuery("select a from t;").Check(testkit.Rows("1", "2")) - - // cannot set next global ID to 0. - tk.MustGetErrCode("alter table t force auto_increment = 0;", errno.ErrAutoincReadFailed) + tk.MustQuery("select a from t;").Check(testkit.Rows("1", lastBase)) + // Test alter unsigned int auto_increment column. + tk.MustExec("drop table if exists t;") + tk.MustExec("create table t (a bigint unsigned primary key auto_increment);") + for _, b := range bases { + tk.MustExec(fmt.Sprintf("alter table t force auto_increment = %d;", b)) + c.Assert(getNextGlobalID(), Equals, b) + tk.MustExec("insert into t values ();") + tk.MustQuery("select a from t;").Check(testkit.Rows(fmt.Sprintf("%d", b))) + tk.MustExec("delete from t;") + } + tk.MustExec("drop table if exists t;") } func (s *testIntegrationSuite3) TestIssue20490(c *C) { From fb176b52f25a432133e8c001a5ca9d1d6bd29f8e Mon Sep 17 00:00:00 2001 From: tangenta Date: Tue, 13 Jul 2021 15:49:27 +0800 Subject: [PATCH 4/5] add test for auto_inc_inc and auto_inc_offset --- ddl/db_integration_test.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/ddl/db_integration_test.go b/ddl/db_integration_test.go index 917d8a62bf7ec..7a0f0686ccb3a 100644 --- a/ddl/db_integration_test.go +++ b/ddl/db_integration_test.go @@ -2697,6 +2697,17 @@ func (s *testIntegrationSuite3) TestAutoIncrementForce(c *C) { tk.MustQuery("select a from t;").Check(testkit.Rows(fmt.Sprintf("%d", b))) tk.MustExec("delete from t;") } + + // @@auto_increment_increment and @@auto_increment_offset. + tk.MustExec("drop table if exists t;") + tk.MustExec("create table t(a int key auto_increment);") + tk.MustExec("set @@auto_increment_offset=2;") + tk.MustExec("set @@auto_increment_increment = 11;") + tk.MustExec("insert into t values (500);") + tk.MustExec("alter table t force auto_increment=100;") + tk.MustExec("insert into t values (), ();") + tk.MustQuery("select * from t;").Check(testkit.Rows("101", "112", "500")) + tk.MustQuery("select * from t order by a;").Check(testkit.Rows("101", "112", "500")) tk.MustExec("drop table if exists t;") } From 75a772f4dca6caecffc8b40a99a932adf90e8603 Mon Sep 17 00:00:00 2001 From: tangenta Date: Tue, 13 Jul 2021 15:57:04 +0800 Subject: [PATCH 5/5] refine comments --- ddl/db_integration_test.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/ddl/db_integration_test.go b/ddl/db_integration_test.go index 7a0f0686ccb3a..a37a87a25f961 100644 --- a/ddl/db_integration_test.go +++ b/ddl/db_integration_test.go @@ -2633,11 +2633,11 @@ func (s *testIntegrationSuite3) TestAutoIncrementForce(c *C) { c.Assert(err, IsNil) return gid } - // rebase _tidb_row_id. + // Rebase _tidb_row_id. tk.MustExec("create table t (a int);") tk.MustExec("insert into t values (1),(2);") tk.MustQuery("select a, _tidb_rowid from t;").Check(testkit.Rows("1 1", "2 2")) - // cannot set next global ID to 0. + // Cannot set next global ID to 0. tk.MustGetErrCode("alter table t force auto_increment = 0;", errno.ErrAutoincReadFailed) tk.MustExec("alter table t force auto_increment = 1;") c.Assert(getNextGlobalID(), Equals, uint64(1)) @@ -2646,21 +2646,21 @@ func (s *testIntegrationSuite3) TestAutoIncrementForce(c *C) { tk.MustExec("insert into t values (3);") tk.MustQuery("select a, _tidb_rowid from t;").Check(testkit.Rows("3 1", "3 2")) - // rebase auto_increment. + // Rebase auto_increment. tk.MustExec("drop table if exists t;") tk.MustExec("create table t (a int primary key auto_increment, b int);") tk.MustExec("insert into t values (1, 1);") tk.MustExec("insert into t values (100000000, 1);") tk.MustExec("delete from t where a = 100000000;") c.Assert(getNextGlobalID(), Greater, uint64(100000000)) - // cannot set next global ID to 0. + // Cannot set next global ID to 0. tk.MustGetErrCode("alter table t /*T![force_inc] force */ auto_increment = 0;", errno.ErrAutoincReadFailed) tk.MustExec("alter table t /*T![force_inc] force */ auto_increment = 2;") c.Assert(getNextGlobalID(), Equals, uint64(2)) tk.MustExec("insert into t(b) values (2);") tk.MustQuery("select a, b from t;").Check(testkit.Rows("1 1", "2 2")) - // rebase auto_random. + // Rebase auto_random. tk.MustExec("drop table if exists t;") tk.MustExec("create table t (a bigint primary key auto_random(5));") tk.MustExec("insert into t values ();") @@ -2668,14 +2668,14 @@ func (s *testIntegrationSuite3) TestAutoIncrementForce(c *C) { tk.MustExec("insert into t values (100000000);") tk.MustExec("delete from t where a = 100000000;") c.Assert(getNextGlobalID(), Greater, uint64(100000000)) - // cannot set next global ID to 0. + // Cannot set next global ID to 0. tk.MustGetErrCode("alter table t force auto_random_base = 0;", errno.ErrAutoincReadFailed) tk.MustExec("alter table t force auto_random_base = 2;") c.Assert(getNextGlobalID(), Equals, uint64(2)) tk.MustExec("insert into t values ();") tk.MustQuery("select (a & 3) from t order by 1;").Check(testkit.Rows("1", "2")) - // change next global ID. + // Change next global ID. tk.MustExec("drop table if exists t;") tk.MustExec("create table t (a bigint primary key auto_increment);") tk.MustExec("insert into t values (1);") @@ -2687,7 +2687,7 @@ func (s *testIntegrationSuite3) TestAutoIncrementForce(c *C) { } tk.MustExec("insert into t values ();") tk.MustQuery("select a from t;").Check(testkit.Rows("1", lastBase)) - // Test alter unsigned int auto_increment column. + // Force alter unsigned int auto_increment column. tk.MustExec("drop table if exists t;") tk.MustExec("create table t (a bigint unsigned primary key auto_increment);") for _, b := range bases { @@ -2698,7 +2698,7 @@ func (s *testIntegrationSuite3) TestAutoIncrementForce(c *C) { tk.MustExec("delete from t;") } - // @@auto_increment_increment and @@auto_increment_offset. + // Force alter with @@auto_increment_increment and @@auto_increment_offset. tk.MustExec("drop table if exists t;") tk.MustExec("create table t(a int key auto_increment);") tk.MustExec("set @@auto_increment_offset=2;")