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

ddl: fix expression index with insert causes losing index data #26248

Merged
merged 11 commits into from
Jul 22, 2021
93 changes: 93 additions & 0 deletions ddl/db_change_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1797,3 +1797,96 @@ func (s *testStateChangeSuite) TestWriteReorgForColumnTypeChange(c *C) {
query := &expectQuery{sql: "admin check table t_ctc;", rows: nil}
s.runTestInSchemaState(c, model.StateWriteReorganization, false, dropColumnsSQL, sqls, query)
}

func (s *serialTestStateChangeSuite) TestCreateExpressionIndex(c *C) {
originalVal := config.GetGlobalConfig().Experimental.AllowsExpressionIndex
config.GetGlobalConfig().Experimental.AllowsExpressionIndex = true
defer func() {
config.GetGlobalConfig().Experimental.AllowsExpressionIndex = originalVal
}()

tk := testkit.NewTestKit(c, s.store)
tk.MustExec("use test_db_state")
tk.MustExec("drop table if exists t")
tk.MustExec("create table t(a int default 0, b int default 0)")
defer func() {
tk.MustExec("drop table t")
}()
tk.MustExec("insert into t values (1, 1), (2, 2), (3, 3), (4, 4)")

tk1 := testkit.NewTestKit(c, s.store)
tk1.MustExec("use test_db_state")

var checkErr error
d := s.dom.DDL()
originalCallback := d.GetHook()
callback := &ddl.TestDDLCallback{}
callback.OnJobUpdatedExported = func(job *model.Job) {
if checkErr != nil {
return
}
err := originalCallback.OnChanged(nil)
c.Assert(err, IsNil)
switch job.SchemaState {
case model.StateDeleteOnly:
_, checkErr = tk1.Exec("insert into t values (5, 5)")
if checkErr != nil {
return
}
_, checkErr = tk1.Exec("insert into t set b = 6")
if checkErr != nil {
return
}
_, checkErr = tk1.Exec("update t set b = 7 where a = 1")
if checkErr != nil {
return
}
_, checkErr = tk1.Exec("delete from t where b = 4")
if checkErr != nil {
return
}
// (1, 7), (2, 2), (3, 3), (5, 5), (0, 6)
case model.StateWriteOnly:
_, checkErr = tk1.Exec("insert into t values (8, 8)")
if checkErr != nil {
return
}
_, checkErr = tk1.Exec("insert into t set b = 9")
if checkErr != nil {
return
}
_, checkErr = tk1.Exec("update t set b = 7 where a = 2")
if checkErr != nil {
return
}
_, checkErr = tk1.Exec("delete from t where b = 3")
if checkErr != nil {
return
}
// (1, 7), (2, 7), (5, 5), (0, 6), (8, 8), (0, 9)
case model.StateWriteReorganization:
_, checkErr = tk1.Exec("insert into t values (10, 10)")
if checkErr != nil {
return
}
_, checkErr = tk1.Exec("insert into t set b = 11")
if checkErr != nil {
return
}
_, checkErr = tk1.Exec("update t set b = 7 where a = 5")
if checkErr != nil {
return
}
_, checkErr = tk1.Exec("delete from t where b = 6")
if checkErr != nil {
return
}
// (1, 7), (2, 7), (5, 7), (8, 8), (0, 9), (10, 10), (10, 10), (0, 11), (0, 11)
}
}

d.(ddl.DDLForTest).SetHook(callback)
tk.MustExec("alter table t add index idx((b+1))")
tk.MustExec("admin check table t")
tk.MustQuery("select * from t order by a, b").Check(testkit.Rows("0 9", "0 11", "0 11", "1 7", "2 7", "5 7", "8 8", "10 10", "10 10"))
}
38 changes: 31 additions & 7 deletions ddl/db_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -410,22 +410,46 @@ func (s *testSerialDBSuite) TestAddExpressionIndexRollback(c *C) {
ctx.Store = s.store
times := 0
hook.OnJobUpdatedExported = func(job *model.Job) {
if job.SchemaState == model.StateDeleteOnly {
switch job.SchemaState {
case model.StateDeleteOnly:
if job.SchemaState == model.StateDeleteOnly {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This if statement is duplicated with line414.

if checkErr != nil {
return
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can put line416-418 after line412, then we can remove this check after every case state.

_, checkErr = tk1.Exec("delete from t1 where c1 = 40;")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we add insert and update statements here? This schema state will be running two times.

}
case model.StateWriteOnly:
if checkErr != nil {
return
}
_, checkErr = tk1.Exec("delete from t1 where c1 = 40;")
}
if checkErr == nil && job.SchemaState == model.StateWriteReorganization && times == 0 {
currJob = job
times++
_, checkErr = tk1.Exec("insert into t1 values (2, 2, 2)")
if checkErr != nil {
return
}
_, checkErr = tk1.Exec("update t1 set c1 = 3 where c2 = 80")
if checkErr != nil {
return
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
_, checkErr = tk1.Exec("insert into t1 values (2, 2, 2)")
if checkErr != nil {
return
}
_, checkErr = tk1.Exec("update t1 set c1 = 3 where c2 = 80")
if checkErr != nil {
return
}
_, checkErr = tk1.Exec("insert into t1 values (2, 2, 2)")
if checkErr == nil {
_, checkErr = tk1.Exec("update t1 set c1 = 3 where c2 = 80")
}

case model.StateWriteReorganization:
if checkErr == nil && job.SchemaState == model.StateWriteReorganization && times == 0 {
_, checkErr = tk1.Exec("insert into t1 values (4, 4, 4)")
if checkErr != nil {
return
}
_, checkErr = tk1.Exec("update t1 set c1 = 5 where c2 = 80")
if checkErr != nil {
return
}
currJob = job
times++
}
}
}
d.(ddl.DDLForTest).SetHook(hook)

tk.MustGetErrMsg("alter table t1 add index expr_idx ((pow(c1, c2)));", "[ddl:8202]Cannot decode index value, because [types:1690]DOUBLE value is out of range in 'pow(160, 160)'")
c.Assert(checkErr, IsNil)
tk.MustQuery("select * from t1;").Check(testkit.Rows("20 20 20", "80 80 80", "160 160 160"))
tk.MustQuery("select * from t1 order by c1;").Check(testkit.Rows("2 2 2", "4 4 4", "5 80 80", "20 20 20", "160 160 160"))

// Check whether the reorg information is cleaned up.
err := ctx.NewTxn(context.Background())
Expand Down
22 changes: 11 additions & 11 deletions ddl/index.go
Original file line number Diff line number Diff line change
Expand Up @@ -420,13 +420,16 @@ func (w *worker) onCreateIndex(d *ddlCtx, t *meta.Meta, job *model.Job, isPK boo
}
return ver, err
}
for _, hiddenCol := range hiddenCols {
columnInfo := model.FindColumnInfo(tblInfo.Columns, hiddenCol.Name.L)
if columnInfo != nil && columnInfo.State == model.StatePublic {
// We already have a column with the same column name.
job.State = model.JobStateCancelled
// TODO: refine the error message
return ver, infoschema.ErrColumnExists.GenWithStackByArgs(hiddenCol.Name)

if indexInfo == nil {
for _, hiddenCol := range hiddenCols {
columnInfo := model.FindColumnInfo(tblInfo.Columns, hiddenCol.Name.L)
if columnInfo != nil && columnInfo.State == model.StatePublic {
// We already have a column with the same column name.
job.State = model.JobStateCancelled
// TODO: refine the error message
return ver, infoschema.ErrColumnExists.GenWithStackByArgs(hiddenCol.Name)
}
}
}

Expand Down Expand Up @@ -495,7 +498,7 @@ func (w *worker) onCreateIndex(d *ddlCtx, t *meta.Meta, job *model.Job, isPK boo
case model.StateNone:
// none -> delete only
indexInfo.State = model.StateDeleteOnly
updateHiddenColumns(tblInfo, indexInfo, model.StateDeleteOnly)
updateHiddenColumns(tblInfo, indexInfo, model.StatePublic)
wjhuang2016 marked this conversation as resolved.
Show resolved Hide resolved
ver, err = updateVersionAndTableInfoWithCheck(t, job, tblInfo, originalState != indexInfo.State)
if err != nil {
return ver, err
Expand All @@ -505,7 +508,6 @@ func (w *worker) onCreateIndex(d *ddlCtx, t *meta.Meta, job *model.Job, isPK boo
case model.StateDeleteOnly:
// delete only -> write only
indexInfo.State = model.StateWriteOnly
updateHiddenColumns(tblInfo, indexInfo, model.StateWriteOnly)
_, err = checkPrimaryKeyNotNull(w, sqlMode, t, job, tblInfo, indexInfo)
if err != nil {
break
Expand All @@ -518,7 +520,6 @@ func (w *worker) onCreateIndex(d *ddlCtx, t *meta.Meta, job *model.Job, isPK boo
case model.StateWriteOnly:
// write only -> reorganization
indexInfo.State = model.StateWriteReorganization
updateHiddenColumns(tblInfo, indexInfo, model.StateWriteReorganization)
_, err = checkPrimaryKeyNotNull(w, sqlMode, t, job, tblInfo, indexInfo)
if err != nil {
break
Expand All @@ -532,7 +533,6 @@ func (w *worker) onCreateIndex(d *ddlCtx, t *meta.Meta, job *model.Job, isPK boo
job.SchemaState = model.StateWriteReorganization
case model.StateWriteReorganization:
// reorganization -> public
updateHiddenColumns(tblInfo, indexInfo, model.StatePublic)
tbl, err := getTable(d.store, schemaID, tblInfo)
if err != nil {
return ver, errors.Trace(err)
Expand Down