func (d *ddl) onDropTable(t *meta.Meta, job *model.Job) error { schemaID := job.SchemaID tableID := job.TableID // Check this table's database. tblInfo, err := t.GetTable(schemaID, tableID) if terror.ErrorEqual(err, meta.ErrDBNotExists) { job.State = model.JobCancelled return errors.Trace(infoschema.ErrDatabaseNotExists) } else if err != nil { return errors.Trace(err) } // Check the table. if tblInfo == nil { job.State = model.JobCancelled return errors.Trace(infoschema.ErrTableNotExists) } ver, err := updateSchemaVersion(t, job) if err != nil { return errors.Trace(err) } switch tblInfo.State { case model.StatePublic: // public -> write only job.SchemaState = model.StateWriteOnly tblInfo.State = model.StateWriteOnly err = t.UpdateTable(schemaID, tblInfo) case model.StateWriteOnly: // write only -> delete only job.SchemaState = model.StateDeleteOnly tblInfo.State = model.StateDeleteOnly err = t.UpdateTable(schemaID, tblInfo) case model.StateDeleteOnly: tblInfo.State = model.StateNone err = t.UpdateTable(schemaID, tblInfo) if err = t.DropTable(job.SchemaID, job.TableID); err != nil { break } // Finish this job. job.State = model.JobDone job.SchemaState = model.StateNone addTableHistoryInfo(job, ver, tblInfo) startKey := tablecodec.EncodeTablePrefix(tableID) job.Args = append(job.Args, startKey) default: err = ErrInvalidTableState.Gen("invalid table state %v", tblInfo.State) } return errors.Trace(err) }
func (s *testDBSuite) TestTruncateTable(c *C) { defer testleak.AfterTest(c) store, err := tidb.NewStore("memory://truncate_table") c.Assert(err, IsNil) tk := testkit.NewTestKit(c, store) tk.MustExec("use test") tk.MustExec("create table t (c1 int, c2 int)") tk.MustExec("insert t values (1, 1), (2, 2)") ctx := tk.Se.(context.Context) is := sessionctx.GetDomain(ctx).InfoSchema() oldTblInfo, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) c.Assert(err, IsNil) oldTblID := oldTblInfo.Meta().ID tk.MustExec("truncate table t") tk.MustExec("insert t values (3, 3), (4, 4)") tk.MustQuery("select * from t").Check(testkit.Rows("3 3", "4 4")) is = sessionctx.GetDomain(ctx).InfoSchema() newTblInfo, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) c.Assert(err, IsNil) c.Assert(newTblInfo.Meta().ID, Greater, oldTblID) // verify that the old table data has been deleted by background worker. tablePrefix := tablecodec.EncodeTablePrefix(oldTblID) hasOldTableData := true for i := 0; i < 30; i++ { err = kv.RunInNewTxn(store, false, func(txn kv.Transaction) error { it, err1 := txn.Seek(tablePrefix) if err1 != nil { return err1 } if !it.Valid() { hasOldTableData = false } else { hasOldTableData = it.Key().HasPrefix(tablePrefix) } it.Close() return nil }) c.Assert(err, IsNil) if !hasOldTableData { break } time.Sleep(time.Millisecond * 100) } c.Assert(hasOldTableData, IsFalse) }
// onTruncateTable delete old table meta, and creates a new table identical to old table except for table ID. // As all the old data is encoded with old table ID, it can not be accessed any more. // A background job will be created to delete old data. func (d *ddl) onTruncateTable(t *meta.Meta, job *model.Job) error { schemaID := job.SchemaID tableID := job.TableID var newTableID int64 err := job.DecodeArgs(&newTableID) if err != nil { job.State = model.JobCancelled return errors.Trace(err) } tblInfo, err := t.GetTable(schemaID, tableID) if terror.ErrorEqual(err, meta.ErrDBNotExists) { job.State = model.JobCancelled return errors.Trace(infoschema.ErrDatabaseNotExists) } else if err != nil { return errors.Trace(err) } if tblInfo == nil { job.State = model.JobCancelled return errors.Trace(infoschema.ErrTableNotExists) } err = t.DropTable(schemaID, tableID) if err != nil { job.State = model.JobCancelled return errors.Trace(err) } tblInfo.ID = newTableID err = t.CreateTable(schemaID, tblInfo) if err != nil { job.State = model.JobCancelled return errors.Trace(err) } ver, err := updateSchemaVersion(t, job) if err != nil { return errors.Trace(err) } job.State = model.JobDone addTableHistoryInfo(job, ver, tblInfo) startKey := tablecodec.EncodeTablePrefix(tableID) job.Args = append(job.Args, startKey) return nil }
func (d *ddl) dropSchemaData(tIDs []int64, startKey kv.Key, job *model.Job, m *meta.Meta) (bool, error) { if len(tIDs) == 0 { return true, nil } var isFinished bool var nextStartKey kv.Key for i, id := range tIDs { job.TableID = id if startKey == nil { startKey = tablecodec.EncodeTablePrefix(id) } limit := defaultBatchSize delCount, err := d.dropTableData(startKey, job, limit) if err != nil { return false, errors.Trace(err) } if delCount == limit { isFinished = false nextStartKey = job.Args[len(job.Args)-1].(kv.Key) break } if i < len(tIDs)-1 { tIDs = tIDs[i+1:] } else { tIDs = nil } startKey = nil isFinished = true continue } job.TableID = 0 job.Args = []interface{}{tIDs, nextStartKey} return isFinished, nil }
// dropTableData deletes data in a limited number. If limit < 0, deletes all data. func (d *ddl) dropTableData(startKey kv.Key, job *model.Job, limit int) (int, error) { prefix := tablecodec.EncodeTablePrefix(job.TableID) delCount, nextStartKey, err := d.delKeysWithStartKey(prefix, startKey, bgJobFlag, job, limit) job.Args = []interface{}{nextStartKey} return delCount, errors.Trace(err) }
func (d *ddl) dropTableData(t table.Table) error { err := d.delKeysWithPrefix(tablecodec.EncodeTablePrefix(t.Meta().ID)) return errors.Trace(err) }