diff --git a/ddl/ddl.go b/ddl/ddl.go index 337a3b5c59580..de710af4a07eb 100644 --- a/ddl/ddl.go +++ b/ddl/ddl.go @@ -88,7 +88,6 @@ var ( errTooLongKey = terror.ClassDDL.New(codeTooLongKey, fmt.Sprintf("Specified key was too long; max key length is %d bytes", maxPrefixLength)) errKeyColumnDoesNotExits = terror.ClassDDL.New(codeKeyColumnDoesNotExits, "this key column doesn't exist in table") - errDupKeyName = terror.ClassDDL.New(codeDupKeyName, "duplicate key name") errUnknownTypeLength = terror.ClassDDL.New(codeUnknownTypeLength, "Unknown length for type tp %d") errUnknownFractionLength = terror.ClassDDL.New(codeUnknownFractionLength, "Unknown Length for type tp %d and fraction %d") errInvalidJobVersion = terror.ClassDDL.New(codeInvalidJobVersion, "DDL job with version %d greater than current %d") @@ -114,6 +113,8 @@ var ( errBlobCantHaveDefault = terror.ClassDDL.New(codeBlobCantHaveDefault, mysql.MySQLErrName[mysql.ErrBlobCantHaveDefault]) errTooLongIndexComment = terror.ClassDDL.New(codeErrTooLongIndexComment, mysql.MySQLErrName[mysql.ErrTooLongIndexComment]) + // ErrDupKeyName returns for duplicated key name + ErrDupKeyName = terror.ClassDDL.New(codeDupKeyName, "duplicate key name") // ErrInvalidDBState returns for invalid database state. ErrInvalidDBState = terror.ClassDDL.New(codeInvalidDBState, "invalid database state") // ErrInvalidTableState returns for invalid Table state. diff --git a/ddl/ddl_api.go b/ddl/ddl_api.go index 8836a23f451fe..bc6415fbf6a1a 100644 --- a/ddl/ddl_api.go +++ b/ddl/ddl_api.go @@ -545,7 +545,7 @@ func checkDuplicateConstraint(namesMap map[string]bool, name string, foreign boo if foreign { return infoschema.ErrCannotAddForeign } - return errDupKeyName.Gen("duplicate key name %s", name) + return ErrDupKeyName.Gen("duplicate key name %s", name) } namesMap[nameLower] = true return nil @@ -1665,7 +1665,7 @@ func (d *ddl) CreateIndex(ctx sessionctx.Context, ti ast.Ident, unique bool, ind } if indexInfo := findIndexByName(indexName.L, t.Meta().Indices); indexInfo != nil { - return errDupKeyName.Gen("index already exist %s", indexName) + return ErrDupKeyName.Gen("index already exist %s", indexName) } if err = checkTooLongIndex(indexName); err != nil { diff --git a/ddl/index.go b/ddl/index.go index d77417e9423d7..b3ea5bcfe9a1e 100644 --- a/ddl/index.go +++ b/ddl/index.go @@ -207,7 +207,7 @@ func (d *ddl) onCreateIndex(t *meta.Meta, job *model.Job) (ver int64, err error) indexInfo := findIndexByName(indexName.L, tblInfo.Indices) if indexInfo != nil && indexInfo.State == model.StatePublic { job.State = model.JobStateCancelled - return ver, errDupKeyName.Gen("index already exist %s", indexName) + return ver, ErrDupKeyName.Gen("index already exist %s", indexName) } if indexInfo == nil { diff --git a/ddl/util/util.go b/ddl/util/util.go index 042d6d4c54174..db3e09fdb3d3f 100644 --- a/ddl/util/util.go +++ b/ddl/util/util.go @@ -27,9 +27,10 @@ import ( ) const ( - loadDeleteRangeSQL = `SELECT HIGH_PRIORITY job_id, element_id, start_key, end_key FROM mysql.gc_delete_range WHERE ts < %v ORDER BY ts` - completeDeleteRangeSQL = `DELETE FROM mysql.gc_delete_range WHERE job_id = %d AND element_id = %d` - updateDeleteRangeSQL = `UPDATE mysql.gc_delete_range SET start_key = "%s" WHERE job_id = %d AND element_id = %d AND start_key = "%s"` + loadDeleteRangeSQL = `SELECT HIGH_PRIORITY job_id, element_id, start_key, end_key FROM mysql.gc_delete_range WHERE ts < %v` + recordDoneDeletedRangeSQL = `INSERT IGNORE INTO mysql.gc_delete_range_done SELECT * FROM mysql.gc_delete_range WHERE job_id = %d AND element_id = %d` + completeDeleteRangeSQL = `DELETE FROM mysql.gc_delete_range WHERE job_id = %d AND element_id = %d` + updateDeleteRangeSQL = `UPDATE mysql.gc_delete_range SET start_key = "%s" WHERE job_id = %d AND element_id = %d AND start_key = "%s"` ) // DelRangeTask is for run delete-range command in gc_worker. @@ -86,11 +87,17 @@ func LoadDeleteRanges(ctx sessionctx.Context, safePoint uint64) (ranges []DelRan return ranges, nil } -// CompleteDeleteRange deletes a record from gc_delete_range table. +// CompleteDeleteRange moves a record from gc_delete_range table to gc_delete_range_done table. // NOTE: This function WILL NOT start and run in a new transaction internally. func CompleteDeleteRange(ctx sessionctx.Context, dr DelRangeTask) error { - sql := fmt.Sprintf(completeDeleteRangeSQL, dr.JobID, dr.ElementID) + sql := fmt.Sprintf(recordDoneDeletedRangeSQL, dr.JobID, dr.ElementID) _, err := ctx.(sqlexec.SQLExecutor).Execute(context.TODO(), sql) + if err != nil { + return errors.Trace(err) + } + + sql = fmt.Sprintf(completeDeleteRangeSQL, dr.JobID, dr.ElementID) + _, err = ctx.(sqlexec.SQLExecutor).Execute(context.TODO(), sql) return errors.Trace(err) } diff --git a/executor/aggregate_test.go b/executor/aggregate_test.go index 0b3453dc6a7b8..5fd81c3244e87 100644 --- a/executor/aggregate_test.go +++ b/executor/aggregate_test.go @@ -240,7 +240,7 @@ func (s *testSuite) TestAggregation(c *C) { result = tk.MustQuery("select count(*) from information_schema.columns") // When adding new memory columns in information_schema, please update this variable. - columnCountOfAllInformationSchemaTables := "741" + columnCountOfAllInformationSchemaTables := "746" result.Check(testkit.Rows(columnCountOfAllInformationSchemaTables)) tk.MustExec("drop table if exists t1") diff --git a/session/bootstrap.go b/session/bootstrap.go index fcd0e930d1a7a..98942841bdb6a 100644 --- a/session/bootstrap.go +++ b/session/bootstrap.go @@ -185,8 +185,17 @@ const ( start_key VARCHAR(255) NOT NULL COMMENT "encoded in hex", end_key VARCHAR(255) NOT NULL COMMENT "encoded in hex", ts BIGINT NOT NULL COMMENT "timestamp in int64", - UNIQUE KEY (element_id), - KEY (job_id, element_id) + UNIQUE KEY delete_range_index (job_id, element_id) + );` + + // CreateGCDeleteRangeDoneTable stores schemas which are already deleted by DeleteRange. + CreateGCDeleteRangeDoneTable = `CREATE TABLE IF NOT EXISTS mysql.gc_delete_range_done ( + job_id BIGINT NOT NULL COMMENT "the DDL job ID", + element_id BIGINT NOT NULL COMMENT "the schema element ID", + start_key VARCHAR(255) NOT NULL COMMENT "encoded in hex", + end_key VARCHAR(255) NOT NULL COMMENT "encoded in hex", + ts BIGINT NOT NULL COMMENT "timestamp in int64", + UNIQUE KEY delete_range_done_index (job_id, element_id) );` // CreateStatsFeedbackTable stores the feedback info which is used to update stats. @@ -243,6 +252,7 @@ const ( version18 = 18 version19 = 19 version20 = 20 + version21 = 21 ) func checkBootstrapped(s Session) (bool, error) { @@ -381,6 +391,10 @@ func upgrade(s Session) { upgradeToVer20(s) } + if ver < version21 { + upgradeToVer21(s) + } + updateBootstrapVer(s) _, err = s.Execute(context.Background(), "COMMIT") @@ -607,6 +621,14 @@ func upgradeToVer20(s Session) { doReentrantDDL(s, CreateStatsFeedbackTable) } +func upgradeToVer21(s Session) { + mustExecute(s, CreateGCDeleteRangeDoneTable) + + doReentrantDDL(s, "ALTER TABLE mysql.gc_delete_range DROP INDEX job_id", ddl.ErrCantDropFieldOrKey) + doReentrantDDL(s, "ALTER TABLE mysql.gc_delete_range ADD UNIQUE INDEX delete_range_index (job_id, element_id)", ddl.ErrDupKeyName) + doReentrantDDL(s, "ALTER TABLE mysql.gc_delete_range DROP INDEX element_id", ddl.ErrCantDropFieldOrKey) +} + // updateBootstrapVer updates bootstrap version variable in mysql.TiDB table. func updateBootstrapVer(s Session) { // Update bootstrap version. @@ -653,6 +675,8 @@ func doDDLWorks(s Session) { mustExecute(s, CreateStatsBucketsTable) // Create gc_delete_range table. mustExecute(s, CreateGCDeleteRangeTable) + // Create gc_delete_range_done table. + mustExecute(s, CreateGCDeleteRangeDoneTable) // Create stats_feedback table. mustExecute(s, CreateStatsFeedbackTable) } diff --git a/session/session.go b/session/session.go index 7bd920e4590c6..f5c657a8ecd1a 100644 --- a/session/session.go +++ b/session/session.go @@ -1208,7 +1208,7 @@ func createSessionWithDomain(store kv.Storage, dom *domain.Domain) (*session, er const ( notBootstrapped = 0 - currentBootstrapVersion = 20 + currentBootstrapVersion = 21 ) func getStoreBootstrapVersion(store kv.Storage) int64 {