diff --git a/ddl/db_test.go b/ddl/db_test.go index d4d116cf54450..d62d82a1c78ab 100644 --- a/ddl/db_test.go +++ b/ddl/db_test.go @@ -6590,3 +6590,23 @@ func (s *testSerialDBSuite) TestIssue22819(c *C) { _, err := tk1.Exec("commit") c.Assert(err, ErrorMatches, ".*8028.*Information schema is changed during the execution of the statement.*") } + +// Close issue #23321. +// See https://github.com/pingcap/tidb/issues/23321 +func (s *testSerialDBSuite) TestJsonUnmarshalErrWhenPanicInCancellingPath(c *C) { + tk := testkit.NewTestKit(c, s.store) + tk.MustExec("use test") + + tk.MustExec("drop table if exists test_add_index_after_add_col") + tk.MustExec("create table test_add_index_after_add_col(a int, b int not null default '0');") + tk.MustExec("insert into test_add_index_after_add_col values(1, 2),(2,2);") + tk.MustExec("alter table test_add_index_after_add_col add column c int not null default '0';") + + c.Assert(failpoint.Enable("github.com/pingcap/tidb/ddl/mockExceedErrorLimit", `return(true)`), IsNil) + defer func() { + c.Assert(failpoint.Disable("github.com/pingcap/tidb/ddl/mockExceedErrorLimit"), IsNil) + }() + + _, err := tk.Exec("alter table test_add_index_after_add_col add unique index cc(c);") + c.Assert(err.Error(), Equals, "[kv:1062]DDL job cancelled by panic in rollingback, error msg: Duplicate entry '0' for key 'cc'") +} diff --git a/ddl/ddl_worker.go b/ddl/ddl_worker.go index 1c263821b932b..326c69cccbfd0 100644 --- a/ddl/ddl_worker.go +++ b/ddl/ddl_worker.go @@ -624,6 +624,13 @@ func chooseLeaseTime(t, max time.Duration) time.Duration { // countForPanic records the error count for DDL job. func (w *worker) countForPanic(job *model.Job) { // If run DDL job panic, just cancel the DDL jobs. + if job.State == model.JobStateRollingback { + job.State = model.JobStateCancelled + msg := fmt.Sprintf("DDL job cancelled by panic in rollingback, error msg: %s", terror.ToSQLError(job.Error).Message) + job.Error = terror.GetErrClass(job.Error).Synthesize(terror.ErrCode(job.Error.Code()), msg) + logutil.Logger(w.logCtx).Warn(msg) + return + } job.State = model.JobStateCancelling job.ErrorCount++