Skip to content

Commit

Permalink
ddl: add alter index visibility DDL support (pingcap#16914)
Browse files Browse the repository at this point in the history
  • Loading branch information
Deardrops authored May 8, 2020
1 parent 664db4e commit 65b7f0f
Show file tree
Hide file tree
Showing 10 changed files with 216 additions and 13 deletions.
17 changes: 9 additions & 8 deletions bindinfo/bind_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1147,6 +1147,8 @@ func (s *testSuite) TestInvisibleIndex(c *C) {
tk.MustGetErrMsg(
"create global binding for select * from t using select * from t use index(idx_b) ",
"[planner:1176]Key 'idx_b' doesn't exist in table 't'")

// Create bind using index
tk.MustExec("create global binding for select * from t using select * from t use index(idx_a) ")

tk.MustQuery("select * from t")
Expand All @@ -1158,14 +1160,13 @@ func (s *testSuite) TestInvisibleIndex(c *C) {
c.Assert(len(tk.Se.GetSessionVars().StmtCtx.IndexNames), Equals, 1)
c.Assert(tk.Se.GetSessionVars().StmtCtx.IndexNames[0], Equals, "t:idx_a")

// TODO: Add these test
//tk.MustExec("alter table t alter index idx_a invisible")
//tk.MustQuery("select * from t where a > 3")
//c.Assert(tk.Se.GetSessionVars().StmtCtx.IndexNames[0], Equals, "t:idx_a")
//c.Assert(tk.MustUseIndex("select * from t where a > 3", "idx_a(a)"), IsTrue)
//
//tk.MustExec("execute stmt1")
//c.Assert(len(tk.Se.GetSessionVars().StmtCtx.IndexNames), Equals, 0)
// And then make this index invisible
tk.MustExec("alter table t alter index idx_a invisible")
tk.MustQuery("select * from t")
c.Assert(len(tk.Se.GetSessionVars().StmtCtx.IndexNames), Equals, 0)

tk.MustExec("execute stmt1")
c.Assert(len(tk.Se.GetSessionVars().StmtCtx.IndexNames), Equals, 0)

tk.MustExec("drop binding for select * from t")
}
43 changes: 43 additions & 0 deletions ddl/db_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2260,3 +2260,46 @@ func (s *testIntegrationSuite3) TestCreateTableWithAutoIdCache(c *C) {
c.Assert(err, NotNil)
c.Assert(err.Error(), Equals, "table option auto_id_cache overflows int64")
}

func (s *testIntegrationSuite4) TestAlterIndexVisibility(c *C) {
config.GetGlobalConfig().Experimental.AllowsExpressionIndex = true
tk := testkit.NewTestKit(c, s.store)
tk.MustExec("create database if not exists alter_index_test")
tk.MustExec("USE alter_index_test;")
tk.MustExec("drop table if exists t, t1, t2, t3;")

tk.MustExec("create table t(a int NOT NULL, b int, key(a), unique(b) invisible)")
queryIndexOnTable := func(tableName string) string {
return fmt.Sprintf("select index_name, is_visible from information_schema.statistics where table_schema = 'alter_index_test' and table_name = '%s' order by index_name", tableName)
}
query := queryIndexOnTable("t")
tk.MustQuery(query).Check(testkit.Rows("a YES", "b NO"))

tk.MustExec("alter table t alter index a invisible")
tk.MustQuery(query).Check(testkit.Rows("a NO", "b NO"))

tk.MustExec("alter table t alter index b visible")
tk.MustQuery(query).Check(testkit.Rows("a NO", "b YES"))

tk.MustExec("alter table t alter index b invisible")
tk.MustQuery(query).Check(testkit.Rows("a NO", "b NO"))

tk.MustGetErrMsg("alter table t alter index non_exists_idx visible", "[schema:1176]Key 'non_exists_idx' doesn't exist in table 't'")

// Alter implicit primary key to invisible index should throw error
tk.MustExec("create table t1(a int NOT NULL, unique(a))")
tk.MustGetErrMsg("alter table t1 alter index a invisible", "[ddl:3522]A primary key index cannot be invisible")

// Alter explicit primary key to invisible index should throw error
tk.MustExec("create table t2(a int, primary key(a))")
tk.MustGetErrMsg("alter table t2 alter index PRIMARY invisible", `[parser:1064]You have an error in your SQL syntax; check the manual that corresponds to your TiDB version for the right syntax to use line 1 column 34 near "PRIMARY invisible" `)

// Alter expression index
tk.MustExec("create table t3(a int NOT NULL, b int)")
tk.MustExec("alter table t3 add index idx((a+b));")
query = queryIndexOnTable("t3")
tk.MustQuery(query).Check(testkit.Rows("idx YES"))

tk.MustExec("alter table t3 alter index idx invisible")
tk.MustQuery(query).Check(testkit.Rows("idx NO"))
}
35 changes: 35 additions & 0 deletions ddl/ddl_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -2251,6 +2251,8 @@ func (d *ddl) AlterTable(ctx sessionctx.Context, ident ast.Ident, specs []*ast.A
err = d.AlterTableSetTiFlashReplica(ctx, ident, spec.TiFlashReplica)
case ast.AlterTableOrderByColumns:
err = d.OrderByColumns(ctx, ident)
case ast.AlterTableIndexInvisible:
err = d.AlterIndexVisibility(ctx, ident, spec.IndexName, spec.Visibility)
default:
// Nothing to do now.
}
Expand Down Expand Up @@ -4774,3 +4776,36 @@ func (d *ddl) DropSequence(ctx sessionctx.Context, ti ast.Ident, ifExists bool)
err = d.callHookOnChanged(err)
return errors.Trace(err)
}

func (d *ddl) AlterIndexVisibility(ctx sessionctx.Context, ident ast.Ident, indexName model.CIStr, visibility ast.IndexVisibility) error {
schema, tb, err := d.getSchemaAndTableByIdent(ctx, ident)
if err != nil {
return err
}

invisible := false
if visibility == ast.IndexVisibilityInvisible {
invisible = true
}

skip, err := validateAlterIndexVisibility(indexName, invisible, tb.Meta())
if err != nil {
return errors.Trace(err)
}
if skip {
return nil
}

job := &model.Job{
SchemaID: schema.ID,
TableID: tb.Meta().ID,
SchemaName: schema.Name.L,
Type: model.ActionAlterIndexVisibility,
BinlogInfo: &model.HistoryInfo{},
Args: []interface{}{indexName, invisible},
}

err = d.doDDLJob(ctx, job)
err = d.callHookOnChanged(err)
return errors.Trace(err)
}
2 changes: 2 additions & 0 deletions ddl/ddl_worker.go
Original file line number Diff line number Diff line change
Expand Up @@ -655,6 +655,8 @@ func (w *worker) runDDLJob(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64,
ver, err = onUpdateFlashReplicaStatus(t, job)
case model.ActionCreateSequence:
ver, err = onCreateSequence(d, t, job)
case model.ActionAlterIndexVisibility:
ver, err = onAlterIndexVisibility(t, job)
default:
// Invalid job, cancel it.
job.State = model.JobStateCancelled
Expand Down
36 changes: 36 additions & 0 deletions ddl/ddl_worker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -475,6 +475,9 @@ func buildCancelJobTests(firstID int64) []testCancelJob {
{act: model.ActionDropColumns, jobIDs: []int64{firstID + 42}, cancelRetErrs: []error{admin.ErrCannotCancelDDLJob.GenWithStackByArgs(firstID + 42)}, cancelState: model.StateDeleteOnly},
{act: model.ActionDropColumns, jobIDs: []int64{firstID + 43}, cancelRetErrs: []error{admin.ErrCannotCancelDDLJob.GenWithStackByArgs(firstID + 43)}, cancelState: model.StateWriteOnly},
{act: model.ActionDropColumns, jobIDs: []int64{firstID + 44}, cancelRetErrs: []error{admin.ErrCannotCancelDDLJob.GenWithStackByArgs(firstID + 44)}, cancelState: model.StateWriteReorganization},

{act: model.ActionAlterIndexVisibility, jobIDs: []int64{firstID + 46}, cancelRetErrs: noErrs, cancelState: model.StateNone},
{act: model.ActionAlterIndexVisibility, jobIDs: []int64{firstID + 47}, cancelRetErrs: []error{admin.ErrCancelFinishedDDLJob.GenWithStackByArgs(firstID + 47)}, cancelState: model.StatePublic},
}

return tests
Expand Down Expand Up @@ -524,6 +527,15 @@ func checkColumnsNotFound(t table.Table, colNames []string) bool {
return notFound
}

func checkIdxVisibility(changedTable table.Table, idxName string, expected bool) bool {
for _, idxInfo := range changedTable.Meta().Indices {
if idxInfo.Name.O == idxName && idxInfo.Invisible == expected {
return true
}
}
return false
}

func (s *testDDLSuite) TestCancelJob(c *C) {
store := testCreateStore(c, "test_cancel_job")
defer store.Close()
Expand Down Expand Up @@ -939,6 +951,30 @@ func (s *testDDLSuite) TestCancelJob(c *C) {
testDropColumns(c, ctx, d, dbInfo, tblInfo, dropColNames, false)
c.Check(errors.ErrorStack(checkErr), Equals, "")
s.checkCancelDropColumns(c, d, dbInfo.ID, tblInfo.ID, dropColNames, true)

// test alter index visibility failed caused by canceled.
indexName := "idx_c3"
testCreateIndex(c, ctx, d, dbInfo, tblInfo, false, indexName, "c3")
c.Check(errors.ErrorStack(checkErr), Equals, "")
txn, err = ctx.Txn(true)
c.Assert(err, IsNil)
c.Assert(txn.Commit(context.Background()), IsNil)
s.checkAddIdx(c, d, dbInfo.ID, tblInfo.ID, indexName, true)

updateTest(&tests[41])
alterIndexVisibility := []interface{}{model.NewCIStr(indexName), true}
doDDLJobErrWithSchemaState(ctx, d, c, dbInfo.ID, tblInfo.ID, test.act, alterIndexVisibility, &test.cancelState)
c.Check(checkErr, IsNil)
changedTable = testGetTable(c, d, dbInfo.ID, tblInfo.ID)
c.Assert(checkIdxVisibility(changedTable, indexName, false), IsTrue)

// cancel alter index visibility successfully
updateTest(&tests[42])
alterIndexVisibility = []interface{}{model.NewCIStr(indexName), true}
doDDLJobSuccess(ctx, d, c, dbInfo.ID, tblInfo.ID, test.act, alterIndexVisibility)
c.Check(checkErr, IsNil)
changedTable = testGetTable(c, d, dbInfo.ID, tblInfo.ID)
c.Assert(checkIdxVisibility(changedTable, indexName, true), IsTrue)
}

func (s *testDDLSuite) TestIgnorableSpec(c *C) {
Expand Down
54 changes: 53 additions & 1 deletion ddl/index.go
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,7 @@ func validateRenameIndex(from, to model.CIStr, tbl *model.TableInfo) (ignore boo

func onRenameIndex(t *meta.Meta, job *model.Job) (ver int64, _ error) {
tblInfo, from, to, err := checkRenameIndex(t, job)
if err != nil {
if err != nil || tblInfo == nil {
return ver, errors.Trace(err)
}

Expand All @@ -310,6 +310,30 @@ func onRenameIndex(t *meta.Meta, job *model.Job) (ver int64, _ error) {
return ver, nil
}

func validateAlterIndexVisibility(indexName model.CIStr, invisible bool, tbl *model.TableInfo) (bool, error) {
if idx := tbl.FindIndexByName(indexName.L); idx == nil {
return false, errors.Trace(infoschema.ErrKeyNotExists.GenWithStackByArgs(indexName.O, tbl.Name))
} else if idx.Invisible == invisible {
return true, nil
}
return false, nil
}

func onAlterIndexVisibility(t *meta.Meta, job *model.Job) (ver int64, _ error) {
tblInfo, from, invisible, err := checkAlterIndexVisibility(t, job)
if err != nil || tblInfo == nil {
return ver, errors.Trace(err)
}
idx := tblInfo.FindIndexByName(from.L)
idx.Invisible = invisible
if ver, err = updateVersionAndTableInfoWithCheck(t, job, tblInfo, true); err != nil {
job.State = model.JobStateCancelled
return ver, errors.Trace(err)
}
job.FinishTableJob(model.JobStateDone, model.StatePublic, ver, tblInfo)
return ver, nil
}

func getNullColInfos(tblInfo *model.TableInfo, indexInfo *model.IndexInfo) ([]*model.ColumnInfo, error) {
nullCols := make([]*model.ColumnInfo, 0, len(indexInfo.Columns))
for _, colName := range indexInfo.Columns {
Expand Down Expand Up @@ -712,6 +736,34 @@ func checkRenameIndex(t *meta.Meta, job *model.Job) (*model.TableInfo, model.CIS
return tblInfo, from, to, errors.Trace(err)
}

func checkAlterIndexVisibility(t *meta.Meta, job *model.Job) (*model.TableInfo, model.CIStr, bool, error) {
var (
indexName model.CIStr
invisible bool
)

schemaID := job.SchemaID
tblInfo, err := getTableInfoAndCancelFaultJob(t, job, schemaID)
if err != nil {
return nil, indexName, invisible, errors.Trace(err)
}

if err := job.DecodeArgs(&indexName, &invisible); err != nil {
job.State = model.JobStateCancelled
return nil, indexName, invisible, errors.Trace(err)
}

skip, err := validateAlterIndexVisibility(indexName, invisible, tblInfo)
if err != nil {
job.State = model.JobStateCancelled
return nil, indexName, invisible, errors.Trace(err)
}
if skip {
return nil, indexName, invisible, nil
}
return tblInfo, indexName, invisible, nil
}

const (
// DefaultTaskHandleCnt is default batch size of adding indices.
DefaultTaskHandleCnt = 128
Expand Down
3 changes: 2 additions & 1 deletion ddl/rollingback.go
Original file line number Diff line number Diff line change
Expand Up @@ -349,7 +349,8 @@ func convertJob2RollbackJob(w *worker, d *ddlCtx, t *meta.Meta, job *model.Job)
model.ActionModifyColumn, model.ActionAddForeignKey,
model.ActionDropForeignKey, model.ActionRenameTable,
model.ActionModifyTableCharsetAndCollate, model.ActionTruncateTablePartition,
model.ActionModifySchemaCharsetAndCollate, model.ActionRepairTable, model.ActionModifyTableAutoIdCache:
model.ActionModifySchemaCharsetAndCollate, model.ActionRepairTable,
model.ActionModifyTableAutoIdCache, model.ActionAlterIndexVisibility:
ver, err = cancelOnlyNotHandledJob(job)
default:
job.State = model.JobStateCancelled
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ require (
github.com/pingcap/goleveldb v0.0.0-20191226122134-f82aafb29989
github.com/pingcap/kvproto v0.0.0-20200424032552-6650270c39c3
github.com/pingcap/log v0.0.0-20200117041106-d28c14d3b1cd
github.com/pingcap/parser v0.0.0-20200430071110-a1bca4f6cf2a
github.com/pingcap/parser v0.0.0-20200507022230-f3bf29096657
github.com/pingcap/pd/v4 v4.0.0-rc.1.0.20200422143320-428acd53eba2
github.com/pingcap/sysutil v0.0.0-20200408114249-ed3bd6f7fdb1
github.com/pingcap/tidb-tools v4.0.0-rc.1.0.20200421113014-507d2bb3a15e+incompatible
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -370,8 +370,8 @@ github.com/pingcap/log v0.0.0-20200117041106-d28c14d3b1cd h1:CV3VsP3Z02MVtdpTMfE
github.com/pingcap/log v0.0.0-20200117041106-d28c14d3b1cd/go.mod h1:4rbK1p9ILyIfb6hU7OG2CiWSqMXnp3JMbiaVJ6mvoY8=
github.com/pingcap/log v0.0.0-20200117041106-d28c14d3b1cd/go.mod h1:4rbK1p9ILyIfb6hU7OG2CiWSqMXnp3JMbiaVJ6mvoY8=
github.com/pingcap/parser v0.0.0-20200424075042-8222d8b724a4/go.mod h1:9v0Edh8IbgjGYW2ArJr19E+bvL8zKahsFp+ixWeId+4=
github.com/pingcap/parser v0.0.0-20200430071110-a1bca4f6cf2a h1:e/Lcw6VDQgtErYKszjeCi+nN4pB9zMzY0bTwMqfazps=
github.com/pingcap/parser v0.0.0-20200430071110-a1bca4f6cf2a/go.mod h1:9v0Edh8IbgjGYW2ArJr19E+bvL8zKahsFp+ixWeId+4=
github.com/pingcap/parser v0.0.0-20200507022230-f3bf29096657 h1:2ceTso30kmgMeddZ4iZ6zrK8N9eFF8zmCa1hSSE1tXc=
github.com/pingcap/parser v0.0.0-20200507022230-f3bf29096657/go.mod h1:9v0Edh8IbgjGYW2ArJr19E+bvL8zKahsFp+ixWeId+4=
github.com/pingcap/pd/v4 v4.0.0-rc.1.0.20200422143320-428acd53eba2 h1:JTzYYukREvxVSKW/ncrzNjFitd8snoQ/Xz32pw8i+s8=
github.com/pingcap/pd/v4 v4.0.0-rc.1.0.20200422143320-428acd53eba2/go.mod h1:s+utZtXDznOiL24VK0qGmtoHjjXNsscJx3m1n8cC56s=
github.com/pingcap/sysutil v0.0.0-20200206130906-2bfa6dc40bcd/go.mod h1:EB/852NMQ+aRKioCpToQ94Wl7fktV+FNnxf3CX/TTXI=
Expand Down
33 changes: 33 additions & 0 deletions planner/core/prepare_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -748,3 +748,36 @@ func (s *testPrepareSuite) TestPrepareForGroupByMultiItems(c *C) {
tk.MustExec(`prepare stmt2 from "select sum(b) from t group by ?, ?"`)
tk.MustQuery(`execute stmt2 using @v1, @v2`).Check(testkit.Rows("10"))
}

func (s *testPrepareSuite) TestInvisibleIndex(c *C) {
defer testleak.AfterTest(c)()
store, dom, err := newStoreWithBootstrap()
c.Assert(err, IsNil)
tk := testkit.NewTestKit(c, store)
defer func() {
dom.Close()
store.Close()
}()

tk.MustExec("use test")
tk.MustExec("drop table if exists t")
tk.MustExec("create table t(a int, unique idx_a(a))")
tk.MustExec("insert into t values(1)")
tk.MustExec(`prepare stmt1 from "select a from t order by a"`)

tk.MustQuery("execute stmt1").Check(testkit.Rows("1"))
tk.MustQuery("execute stmt1").Check(testkit.Rows("1"))
c.Assert(len(tk.Se.GetSessionVars().StmtCtx.IndexNames), Equals, 1)
c.Assert(tk.Se.GetSessionVars().StmtCtx.IndexNames[0], Equals, "t:idx_a")

tk.MustExec("alter table t alter index idx_a invisible")
tk.MustQuery("execute stmt1").Check(testkit.Rows("1"))
tk.MustQuery("execute stmt1").Check(testkit.Rows("1"))
c.Assert(len(tk.Se.GetSessionVars().StmtCtx.IndexNames), Equals, 0)

tk.MustExec("alter table t alter index idx_a visible")
tk.MustQuery("execute stmt1").Check(testkit.Rows("1"))
tk.MustQuery("execute stmt1").Check(testkit.Rows("1"))
c.Assert(len(tk.Se.GetSessionVars().StmtCtx.IndexNames), Equals, 1)
c.Assert(tk.Se.GetSessionVars().StmtCtx.IndexNames[0], Equals, "t:idx_a")
}

0 comments on commit 65b7f0f

Please sign in to comment.