diff --git a/ddl/db_cache_test.go b/ddl/db_cache_test.go index bc51630e92..9c775dd0b1 100644 --- a/ddl/db_cache_test.go +++ b/ddl/db_cache_test.go @@ -19,6 +19,7 @@ import ( "github.com/pingcap/tidb/ddl" "github.com/pingcap/tidb/domain" "github.com/pingcap/tidb/errno" + "github.com/pingcap/tidb/parser/model" "github.com/pingcap/tidb/parser/terror" "github.com/pingcap/tidb/util/testkit" ) @@ -35,7 +36,7 @@ func (s *testDBSuite2) TestAlterTableCache(c *C) { tk.MustGetErrCode("alter table t1 ca", errno.ErrParse) tk.MustGetErrCode("alter table t2 cache", errno.ErrNoSuchTable) tk.MustExec("alter table t1 cache") - checkTableCache(c, tk.Se, "test", "t1") + checkTableCacheStatus(c, tk.Se, "test", "t1", model.TableCacheStatusEnable) tk.MustExec("drop table if exists t1") /*Test can't skip schema checker*/ tk.MustExec("drop table if exists t1,t2") @@ -110,3 +111,24 @@ func (s *testDBSuite2) TestAlterViewTableCache(c *C) { tk.MustExec("create view v as select * from cache_view_t") tk.MustGetErrCode("alter table v cache", errno.ErrWrongObject) } + +func (s *testDBSuite2) TestAlterTableNoCache(c *C) { + tk := testkit.NewTestKit(c, s.store) + tk.MustExec("use test") + tk.MustExec("drop table if exists nocache_t1") + /* Test of cache table */ + tk.MustExec("create table nocache_t1 ( n int auto_increment primary key)") + tk.MustExec("alter table nocache_t1 cache") + checkTableCacheStatus(c, tk.Se, "test", "nocache_t1", model.TableCacheStatusEnable) + tk.MustExec("alter table nocache_t1 nocache") + checkTableCacheStatus(c, tk.Se, "test", "nocache_t1", model.TableCacheStatusDisable) + tk.MustExec("drop table if exists t1") + // Test if a table is not exists + tk.MustExec("drop table if exists nocache_t") + tk.MustGetErrCode("alter table nocache_t cache", errno.ErrNoSuchTable) + tk.MustExec("create table nocache_t (a int)") + tk.MustExec("alter table nocache_t nocache") + // Multiple no alter cache is okay + tk.MustExec("alter table nocache_t nocache") + tk.MustExec("alter table nocache_t nocache") +} diff --git a/ddl/db_test.go b/ddl/db_test.go index 40544a3b0b..cb1c90b54d 100644 --- a/ddl/db_test.go +++ b/ddl/db_test.go @@ -6391,13 +6391,15 @@ func checkTableLock(c *C, se session.Session, dbName, tableName string, lockTp m c.Assert(tb.Meta().Lock, IsNil) } } -func checkTableCache(c *C, se session.Session, dbName, tableName string) { + +func checkTableCacheStatus(c *C, se session.Session, dbName, tableName string, status model.TableCacheStatusType) { tb := testGetTableByName(c, se, dbName, tableName) dom := domain.GetDomain(se) err := dom.Reload() c.Assert(err, IsNil) - c.Assert(tb.Meta().TableCacheStatusType, Equals, model.TableCacheStatusEnable) + c.Assert(tb.Meta().TableCacheStatusType, Equals, status) } + func (s *testDBSuite2) TestDDLWithInvalidTableInfo(c *C) { tk := testkit.NewTestKit(c, s.store) diff --git a/ddl/ddl_api.go b/ddl/ddl_api.go index 56300c607e..9c35956331 100644 --- a/ddl/ddl_api.go +++ b/ddl/ddl_api.go @@ -2857,6 +2857,8 @@ func (d *ddl) AlterTable(ctx context.Context, sctx sessionctx.Context, ident ast err = d.AlterTablePartitionOptions(sctx, ident, spec) case ast.AlterTableCache: err = d.AlterTableCache(sctx, ident) + case ast.AlterTableNoCache: + err = d.AlterTableNoCache(sctx, ident) default: // Nothing to do now. } @@ -6568,6 +6570,7 @@ func (d *ddl) AlterPlacementPolicy(ctx sessionctx.Context, stmt *ast.AlterPlacem err = d.callHookOnChanged(err) return errors.Trace(err) } + func (d *ddl) AlterTableCache(ctx sessionctx.Context, ti ast.Ident) (err error) { schema, t, err := d.getSchemaAndTableByIdent(ctx, ti) if err != nil { @@ -6598,3 +6601,27 @@ func (d *ddl) AlterTableCache(ctx sessionctx.Context, ti ast.Ident) (err error) err = d.callHookOnChanged(err) return errors.Trace(err) } + +func (d *ddl) AlterTableNoCache(ctx sessionctx.Context, ti ast.Ident) (err error) { + schema, t, err := d.getSchemaAndTableByIdent(ctx, ti) + if err != nil { + return err + } + // if a table is not in cache state, return directly + if t.Meta().TableCacheStatusType == model.TableCacheStatusDisable { + return nil + } + + job := &model.Job{ + SchemaID: schema.ID, + SchemaName: schema.Name.L, + TableID: t.Meta().ID, + Type: model.ActionAlterNoCacheTable, + BinlogInfo: &model.HistoryInfo{}, + Args: []interface{}{}, + } + + err = d.doDDLJob(ctx, job) + err = d.callHookOnChanged(err) + return errors.Trace(err) +} diff --git a/ddl/ddl_worker.go b/ddl/ddl_worker.go index 06c39207f3..6cc3a77461 100644 --- a/ddl/ddl_worker.go +++ b/ddl/ddl_worker.go @@ -835,6 +835,8 @@ func (w *worker) runDDLJob(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, ver, err = onAlterTablePlacement(d, t, job) case model.ActionAlterCacheTable: ver, err = onAlterCacheTable(t, job) + case model.ActionAlterNoCacheTable: + ver, err = onAlterNoCacheTable(t, job) default: // Invalid job, cancel it. job.State = model.JobStateCancelled diff --git a/ddl/table.go b/ddl/table.go index 191bf895ea..22613ed6b3 100644 --- a/ddl/table.go +++ b/ddl/table.go @@ -1477,6 +1477,7 @@ func updateLabelRules(job *model.Job, tblInfo *model.TableInfo, oldRules map[str patch := label.NewRulePatch(newRules, oldRuleIDs) return infosync.UpdateLabelRules(context.TODO(), patch) } + func onAlterCacheTable(t *meta.Meta, job *model.Job) (ver int64, err error) { tbInfo, err := getTableInfoAndCancelFaultJob(t, job, job.SchemaID) if err != nil { @@ -1519,3 +1520,38 @@ func onAlterCacheTable(t *meta.Meta, job *model.Job) (ver int64, err error) { } return ver, err } + +func onAlterNoCacheTable(t *meta.Meta, job *model.Job) (ver int64, err error) { + tbInfo, err := getTableInfoAndCancelFaultJob(t, job, job.SchemaID) + if err != nil { + return 0, errors.Trace(err) + } + // If the table is not in the cache state + if tbInfo.TableCacheStatusType == model.TableCacheStatusDisable { + job.FinishTableJob(model.JobStateDone, model.StatePublic, ver, tbInfo) + return ver, nil + } + + switch tbInfo.TableCacheStatusType { + case model.TableCacheStatusEnable: + // enable -> switching + tbInfo.TableCacheStatusType = model.TableCacheStatusSwitching + ver, err = updateVersionAndTableInfoWithCheck(t, job, tbInfo, true) + if err != nil { + return ver, err + } + case model.TableCacheStatusSwitching: + // switching -> disable + tbInfo.TableCacheStatusType = model.TableCacheStatusDisable + ver, err = updateVersionAndTableInfoWithCheck(t, job, tbInfo, true) + if err != nil { + return ver, err + } + // Finish this job. + job.FinishTableJob(model.JobStateDone, model.StatePublic, ver, tbInfo) + default: + job.State = model.JobStateCancelled + err = ErrInvalidDDLState.GenWithStackByArgs("alter table no cache", tbInfo.TableCacheStatusType.String()) + } + return ver, err +} diff --git a/ddl/table_test.go b/ddl/table_test.go index 02bf8c41c6..7f577c8d1a 100644 --- a/ddl/table_test.go +++ b/ddl/table_test.go @@ -417,6 +417,11 @@ func (s *testTableSuite) TestTable(c *C) { testCheckTableState(c, d, dbInfo1, tblInfo, model.StatePublic) testCheckJobDone(c, d, job, true) checkTableCacheTest(c, d, dbInfo1, tblInfo) + // for alter no cache table + job = testAlterNoCacheTable(c, ctx, d, dbInfo1.ID, tblInfo) + testCheckTableState(c, d, dbInfo1, tblInfo, model.StatePublic) + testCheckJobDone(c, d, job, true) + checkTableNoCacheTest(c, d, dbInfo1, tblInfo) } func checkTableCacheTest(c *C, d *ddl, dbInfo *model.DBInfo, tblInfo *model.TableInfo) { @@ -432,6 +437,18 @@ func checkTableCacheTest(c *C, d *ddl, dbInfo *model.DBInfo, tblInfo *model.Tabl c.Assert(err, IsNil) } +func checkTableNoCacheTest(c *C, d *ddl, dbInfo *model.DBInfo, tblInfo *model.TableInfo) { + err := kv.RunInNewTxn(context.Background(), d.store, false, func(ctx context.Context, txn kv.Transaction) error { + t := meta.NewMeta(txn) + info, err := t.GetTable(dbInfo.ID, tblInfo.ID) + c.Assert(err, IsNil) + c.Assert(info, NotNil) + c.Assert(info.TableCacheStatusType, Equals, model.TableCacheStatusDisable) + return nil + }) + c.Assert(err, IsNil) +} + func testAlterCacheTable(c *C, ctx sessionctx.Context, d *ddl, newSchemaID int64, tblInfo *model.TableInfo) *model.Job { job := &model.Job{ @@ -449,6 +466,23 @@ func testAlterCacheTable(c *C, ctx sessionctx.Context, d *ddl, newSchemaID int64 return job } +func testAlterNoCacheTable(c *C, ctx sessionctx.Context, d *ddl, newSchemaID int64, tblInfo *model.TableInfo) *model.Job { + + job := &model.Job{ + SchemaID: newSchemaID, + TableID: tblInfo.ID, + Type: model.ActionAlterNoCacheTable, + BinlogInfo: &model.HistoryInfo{}, + Args: []interface{}{}, + } + err := d.doDDLJob(ctx, job) + c.Assert(err, IsNil) + + v := getSchemaVer(c, ctx) + checkHistoryJobArgs(c, ctx, job.ID, &historyJobArgs{ver: v}) + return job +} + // for drop indexes func createTestTableForDropIndexes(c *C, ctx sessionctx.Context, d *ddl, dbInfo *model.DBInfo, name string, num int) *model.TableInfo { tableInfo := testTableInfo(c, d, name, num) diff --git a/parser/model/ddl.go b/parser/model/ddl.go index acc430c20d..622b2c4607 100644 --- a/parser/model/ddl.go +++ b/parser/model/ddl.go @@ -93,6 +93,7 @@ const ( ActionAlterTablePlacement ActionType = 56 ActionAlterCacheTable ActionType = 57 ActionAlterTableStatsOptions ActionType = 58 + ActionAlterNoCacheTable ActionType = 59 ) var actionMap = map[ActionType]string{