Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

executor: fix last_insert_id in auto_random mode #15145

Merged
merged 10 commits into from
Mar 5, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion ddl/ddl_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -1160,7 +1160,7 @@ func setTableAutoRandomBits(tbInfo *model.TableInfo, colDefs []*ast.ColumnDef) e
if autoRandBits == 0 {
return ErrInvalidAutoRandom.GenWithStackByArgs(autoid.AutoRandomNonPositive)
} else if autoRandBits >= maxFieldTypeBitsLength {
return ErrInvalidAutoRandom.GenWithStackByArgs(fmt.Sprintf(autoid.AutoRandomOverflowErrMsg, autoRandBits, maxFieldTypeBitsLength))
return ErrInvalidAutoRandom.GenWithStackByArgs(fmt.Sprintf(autoid.AutoRandomOverflowErrMsg, col.Name.Name.L, maxFieldTypeBitsLength, autoRandBits, col.Name.Name.L, maxFieldTypeBitsLength-1))
}
tbInfo.AutoRandomBits = autoRandBits
}
Expand Down
15 changes: 8 additions & 7 deletions ddl/serial_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import (
"github.com/pingcap/tidb/util/testutil"
)

// Make it serial because config is modified in test cases.
var _ = SerialSuites(&testSerialSuite{})

type testSerialSuite struct {
Expand Down Expand Up @@ -749,8 +750,8 @@ func (s *testSerialSuite) TestAutoRandom(c *C) {
assertWithAutoInc := func(sql string) {
assertInvalidAutoRandomErr(sql, autoid.AutoRandomIncompatibleWithAutoIncErrMsg)
}
assertOverflow := func(sql string, autoRandBits, maxFieldLength uint64) {
assertInvalidAutoRandomErr(sql, autoid.AutoRandomOverflowErrMsg, autoRandBits, maxFieldLength)
assertOverflow := func(sql, colType string, autoRandBits, maxFieldLength uint64) {
assertInvalidAutoRandomErr(sql, autoid.AutoRandomOverflowErrMsg, colType, maxFieldLength, autoRandBits, colType, maxFieldLength-1)
}
assertModifyColType := func(sql string) {
assertInvalidAutoRandomErr(sql, autoid.AutoRandomModifyColTypeErrMsg)
Expand Down Expand Up @@ -794,11 +795,11 @@ func (s *testSerialSuite) TestAutoRandom(c *C) {
assertWithAutoInc("create table t (a bigint auto_random(3) auto_increment, primary key (a))")

// Overflow data type max length.
assertOverflow("create table t (a bigint auto_random(65) primary key)", 65, 64)
assertOverflow("create table t (a int auto_random(33) primary key)", 33, 32)
assertOverflow("create table t (a mediumint auto_random(25) primary key)", 25, 24)
assertOverflow("create table t (a smallint auto_random(17) primary key)", 17, 16)
assertOverflow("create table t (a tinyint auto_random(9) primary key)", 9, 8)
assertOverflow("create table t (a bigint auto_random(65) primary key)", "a", 65, 64)
assertOverflow("create table t (a int auto_random(33) primary key)", "a", 33, 32)
assertOverflow("create table t (a mediumint auto_random(25) primary key)", "a", 25, 24)
assertOverflow("create table t (a smallint auto_random(17) primary key)", "a", 17, 16)
assertOverflow("create table t (a tinyint auto_random(9) primary key)", "a", 9, 8)

assertNonPositive("create table t (a bigint auto_random(0) primary key)")

Expand Down
6 changes: 3 additions & 3 deletions executor/ddl_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -784,7 +784,7 @@ func (s *testAutoRandomSuite) TestAutoRandomBitsData(c *C) {

// Test explicit insert.
tk.MustExec("create table t (a tinyint primary key auto_random(2), b int)")
for i := 0; i < 100; i++ {
for i := 1; i <= 100; i++ {
tk.MustExec("insert into t values (?, ?)", i, i)
}
_, err = tk.Exec("insert into t (b) values (0)")
Expand Down Expand Up @@ -813,8 +813,8 @@ func (s *testAutoRandomSuite) TestAutoRandomBitsData(c *C) {
tk.MustExec("drop table t")

tk.MustExec("create table t (a tinyint primary key auto_random(2), b int)")
tk.MustExec("insert into t values (0, 2)")
tk.MustExec("update t set a = 31 where a = 0")
tk.MustExec("insert into t values (1, 2)")
tk.MustExec("update t set a = 31 where a = 1")
_, err = tk.Exec("insert into t (b) values (0)")
c.Assert(err, NotNil)
c.Assert(err.Error(), Equals, autoid.ErrAutoRandReadFailed.GenWithStackByArgs().Error())
Expand Down
44 changes: 32 additions & 12 deletions executor/insert_common.go
Original file line number Diff line number Diff line change
Expand Up @@ -847,30 +847,50 @@ func (e *InsertValues) adjustAutoRandomDatum(ctx context.Context, d types.Datum,
return d, nil
}

if !hasValue || d.IsNull() {
_, err := e.ctx.Txn(true)
AilinKid marked this conversation as resolved.
Show resolved Hide resolved
var err error
var recordID int64
if !hasValue {
d.SetNull()
}
if !d.IsNull() {
recordID, err = getAutoRecordID(d, &c.FieldType, true)
if err != nil {
return types.Datum{}, errors.Trace(err)
return types.Datum{}, err
}
autoRandomID, err := e.allocAutoRandomID(&c.FieldType)
}
// Use the value if it's not null and not 0.
if recordID != 0 {
err = e.rebaseAutoRandomID(recordID, &c.FieldType)
if err != nil {
return types.Datum{}, err
}
d.SetAutoID(autoRandomID, c.Flag)
retryInfo.AddAutoRandomID(autoRandomID)
} else {
recordID, err := getAutoRecordID(d, &c.FieldType, true)
e.ctx.GetSessionVars().StmtCtx.InsertID = uint64(recordID)
d.SetAutoID(recordID, c.Flag)
retryInfo.AddAutoRandomID(recordID)
return d, nil
}

// Change NULL to auto id.
// Change value 0 to auto id, if NoAutoValueOnZero SQL mode is not set.
if d.IsNull() || e.ctx.GetSessionVars().SQLMode&mysql.ModeNoAutoValueOnZero == 0 {
_, err := e.ctx.Txn(true)
if err != nil {
return types.Datum{}, err
return types.Datum{}, errors.Trace(err)
}
err = e.rebaseAutoRandomID(recordID, &c.FieldType)
recordID, err = e.allocAutoRandomID(&c.FieldType)
if err != nil {
return types.Datum{}, err
}
d.SetAutoID(recordID, c.Flag)
retryInfo.AddAutoRandomID(recordID)
// It's compatible with mysql setting the first allocated autoID to lastInsertID.
// Cause autoID may be specified by user, judge only the first row is not suitable.
if e.lastInsertID == 0 {
e.lastInsertID = uint64(recordID)
}
}

d.SetAutoID(recordID, c.Flag)
retryInfo.AddAutoRandomID(recordID)

casted, err := table.CastValue(e.ctx, d, c.ToInfo())
if err != nil {
return types.Datum{}, err
Expand Down
164 changes: 164 additions & 0 deletions executor/insert_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (

. "github.com/pingcap/check"
"github.com/pingcap/parser/terror"
"github.com/pingcap/tidb/config"
"github.com/pingcap/tidb/sessionctx/variable"
"github.com/pingcap/tidb/table"
"github.com/pingcap/tidb/types"
Expand Down Expand Up @@ -958,3 +959,166 @@ func (s *testSuite3) TestAutoIDIncrementAndOffset(c *C) {
c.Assert(err, NotNil)
c.Assert(err.Error(), Equals, "[autoid:8060]Invalid auto_increment settings: auto_increment_increment: 65536, auto_increment_offset: 65536, both of them must be in range [1..65535]")
}

var _ = SerialSuites(&testSuite9{&baseTestSuite{}})

type testSuite9 struct {
*baseTestSuite
}

func (s *testSuite9) TestAutoRandomID(c *C) {
allowAutoRandom := config.GetGlobalConfig().Experimental.AllowAutoRandom
if !allowAutoRandom {
config.GetGlobalConfig().Experimental.AllowAutoRandom = true
defer func() {
config.GetGlobalConfig().Experimental.AllowAutoRandom = false
}()
}

tk := testkit.NewTestKit(c, s.store)
tk.MustExec(`use test`)
tk.MustExec(`drop table if exists ar`)
tk.MustExec(`create table ar (id int key auto_random, name char(10))`)

tk.MustExec(`insert into ar(id) values (null)`)
rs := tk.MustQuery(`select id from ar`)
c.Assert(len(rs.Rows()), Equals, 1)
firstValue, err := strconv.Atoi(rs.Rows()[0][0].(string))
c.Assert(err, IsNil)
c.Assert(firstValue, Greater, 0)
tk.MustQuery(`select last_insert_id()`).Check(testkit.Rows(fmt.Sprintf("%d", firstValue)))
tk.MustExec(`delete from ar`)

tk.MustExec(`insert into ar(id) values (0)`)
rs = tk.MustQuery(`select id from ar`)
c.Assert(len(rs.Rows()), Equals, 1)
firstValue, err = strconv.Atoi(rs.Rows()[0][0].(string))
c.Assert(err, IsNil)
c.Assert(firstValue, Greater, 0)
tk.MustQuery(`select last_insert_id()`).Check(testkit.Rows(fmt.Sprintf("%d", firstValue)))
tk.MustExec(`delete from ar`)

tk.MustExec(`insert into ar(name) values ('a')`)
rs = tk.MustQuery(`select id from ar`)
c.Assert(len(rs.Rows()), Equals, 1)
firstValue, err = strconv.Atoi(rs.Rows()[0][0].(string))
c.Assert(err, IsNil)
c.Assert(firstValue, Greater, 0)
tk.MustQuery(`select last_insert_id()`).Check(testkit.Rows(fmt.Sprintf("%d", firstValue)))

tk.MustExec(`drop table ar`)
djshow832 marked this conversation as resolved.
Show resolved Hide resolved
}

func (s *testSuite9) TestMultiAutoRandomID(c *C) {
allowAutoRandom := config.GetGlobalConfig().Experimental.AllowAutoRandom
if !allowAutoRandom {
config.GetGlobalConfig().Experimental.AllowAutoRandom = true
defer func() {
config.GetGlobalConfig().Experimental.AllowAutoRandom = false
}()
}

tk := testkit.NewTestKit(c, s.store)
tk.MustExec(`use test`)
tk.MustExec(`drop table if exists ar`)
tk.MustExec(`create table ar (id int key auto_random, name char(10))`)

tk.MustExec(`insert into ar(id) values (null),(null),(null)`)
rs := tk.MustQuery(`select id from ar order by id`)
c.Assert(len(rs.Rows()), Equals, 3)
firstValue, err := strconv.Atoi(rs.Rows()[0][0].(string))
c.Assert(err, IsNil)
c.Assert(firstValue, Greater, 0)
c.Assert(rs.Rows()[1][0].(string), Equals, fmt.Sprintf("%d", firstValue+1))
c.Assert(rs.Rows()[2][0].(string), Equals, fmt.Sprintf("%d", firstValue+2))
tk.MustQuery(`select last_insert_id()`).Check(testkit.Rows(fmt.Sprintf("%d", firstValue)))
tk.MustExec(`delete from ar`)

tk.MustExec(`insert into ar(id) values (0),(0),(0)`)
rs = tk.MustQuery(`select id from ar order by id`)
c.Assert(len(rs.Rows()), Equals, 3)
firstValue, err = strconv.Atoi(rs.Rows()[0][0].(string))
c.Assert(err, IsNil)
c.Assert(firstValue, Greater, 0)
c.Assert(rs.Rows()[1][0].(string), Equals, fmt.Sprintf("%d", firstValue+1))
c.Assert(rs.Rows()[2][0].(string), Equals, fmt.Sprintf("%d", firstValue+2))
tk.MustQuery(`select last_insert_id()`).Check(testkit.Rows(fmt.Sprintf("%d", firstValue)))
tk.MustExec(`delete from ar`)

tk.MustExec(`insert into ar(name) values ('a'),('a'),('a')`)
rs = tk.MustQuery(`select id from ar order by id`)
c.Assert(len(rs.Rows()), Equals, 3)
firstValue, err = strconv.Atoi(rs.Rows()[0][0].(string))
c.Assert(err, IsNil)
c.Assert(firstValue, Greater, 0)
c.Assert(rs.Rows()[1][0].(string), Equals, fmt.Sprintf("%d", firstValue+1))
c.Assert(rs.Rows()[2][0].(string), Equals, fmt.Sprintf("%d", firstValue+2))
tk.MustQuery(`select last_insert_id()`).Check(testkit.Rows(fmt.Sprintf("%d", firstValue)))

tk.MustExec(`drop table ar`)
}

func (s *testSuite9) TestAutoRandomIDAllowZero(c *C) {
allowAutoRandom := config.GetGlobalConfig().Experimental.AllowAutoRandom
if !allowAutoRandom {
config.GetGlobalConfig().Experimental.AllowAutoRandom = true
defer func() {
config.GetGlobalConfig().Experimental.AllowAutoRandom = false
}()
}

tk := testkit.NewTestKit(c, s.store)
tk.MustExec(`use test`)
tk.MustExec(`drop table if exists ar`)
tk.MustExec(`create table ar (id int key auto_random, name char(10))`)

rs := tk.MustQuery(`select @@session.sql_mode`)
sqlMode := rs.Rows()[0][0].(string)
tk.MustExec(fmt.Sprintf(`set session sql_mode="%s,%s"`, sqlMode, "NO_AUTO_VALUE_ON_ZERO"))

tk.MustExec(`insert into ar(id) values (0)`)
rs = tk.MustQuery(`select id from ar`)
c.Assert(len(rs.Rows()), Equals, 1)
firstValue, err := strconv.Atoi(rs.Rows()[0][0].(string))
c.Assert(err, IsNil)
c.Assert(firstValue, Equals, 0)
tk.MustQuery(`select last_insert_id()`).Check(testkit.Rows(fmt.Sprintf("%d", firstValue)))
tk.MustExec(`delete from ar`)

tk.MustExec(`insert into ar(id) values (null)`)
rs = tk.MustQuery(`select id from ar`)
c.Assert(len(rs.Rows()), Equals, 1)
firstValue, err = strconv.Atoi(rs.Rows()[0][0].(string))
c.Assert(err, IsNil)
c.Assert(firstValue, Greater, 0)
tk.MustQuery(`select last_insert_id()`).Check(testkit.Rows(fmt.Sprintf("%d", firstValue)))

tk.MustExec(`drop table ar`)
}

func (s *testSuite9) TestAutoRandomIDExplicit(c *C) {
allowAutoRandom := config.GetGlobalConfig().Experimental.AllowAutoRandom
if !allowAutoRandom {
config.GetGlobalConfig().Experimental.AllowAutoRandom = true
defer func() {
config.GetGlobalConfig().Experimental.AllowAutoRandom = false
}()
}

tk := testkit.NewTestKit(c, s.store)
tk.MustExec(`use test`)
tk.MustExec(`drop table if exists ar`)
tk.MustExec(`create table ar (id int key auto_random, name char(10))`)

tk.MustExec(`insert into ar(id) values (1)`)
tk.MustQuery(`select id from ar`).Check(testkit.Rows("1"))
tk.MustQuery(`select last_insert_id()`).Check(testkit.Rows("0"))
tk.MustExec(`delete from ar`)

tk.MustExec(`insert into ar(id) values (1), (2)`)
tk.MustQuery(`select id from ar`).Check(testkit.Rows("1", "2"))
tk.MustQuery(`select last_insert_id()`).Check(testkit.Rows("0"))
tk.MustExec(`delete from ar`)

tk.MustExec(`drop table ar`)
}
2 changes: 1 addition & 1 deletion meta/autoid/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ const (
// AutoRandomIncompatibleWithDefaultValueErrMsg is reported when auto_random and default are specified on the same column.
AutoRandomIncompatibleWithDefaultValueErrMsg = "auto_random is incompatible with default"
// AutoRandomOverflowErrMsg is reported when auto_random is greater than max length of a MySQL data type.
AutoRandomOverflowErrMsg = "auto_random = %d will overflow. The max length of bits is %d"
AutoRandomOverflowErrMsg = "Bits of column `%s` is %d, but auto_random bits is %d. Max allowed auto_random bits for column `%s` is %d"
// AutoRandomModifyColTypeErrMsg is reported when a user is trying to modify the type of a column specified with auto_random.
AutoRandomModifyColTypeErrMsg = "modifying the auto_random column type is not supported"
// AutoRandomAlterErrMsg is reported when a user is trying to add/drop/modify the value of auto_random attribute.
Expand Down