diff --git a/ddl/bg_worker.go b/ddl/bg_worker.go index 3c239a558f94f..9c39c5d1c534e 100644 --- a/ddl/bg_worker.go +++ b/ddl/bg_worker.go @@ -93,7 +93,7 @@ func (d *ddl) runBgJob(t *meta.Meta, job *model.Job) { log.Errorf("[ddl] run background job err %v", errors.ErrorStack(err)) } - job.Error = err.Error() + job.Error = toTError(err) job.ErrorCount++ } } diff --git a/ddl/ddl_worker.go b/ddl/ddl_worker.go index 8271c4937ca73..03ec667068ac8 100644 --- a/ddl/ddl_worker.go +++ b/ddl/ddl_worker.go @@ -81,7 +81,7 @@ func (d *ddl) doDDLJob(ctx context.Context, job *model.Job) error { return nil } - return errors.Errorf(historyJob.Error) + return errors.Trace(historyJob.Error) } } @@ -379,11 +379,22 @@ func (d *ddl) runDDLJob(t *meta.Meta, job *model.Job) { log.Errorf("run ddl job err %v", errors.ErrorStack(err)) } - job.Error = err.Error() + job.Error = toTError(err) job.ErrorCount++ } } +func toTError(err error) *terror.Error { + originErr := errors.Cause(err) + tErr, ok := originErr.(*terror.Error) + if ok { + return tErr + } + + // TODO: add the error code + return terror.ClassDDL.New(terror.CodeUnknown, err.Error()) +} + // for every lease seconds, we will re-update the whole schema, so we will wait 2 * lease time // to guarantee that all servers have already updated schema. func (d *ddl) waitSchemaChanged(waitTime time.Duration) { diff --git a/ddl/stat.go b/ddl/stat.go index a39fa5717ef55..fe1c3a7864f0c 100644 --- a/ddl/stat.go +++ b/ddl/stat.go @@ -89,7 +89,11 @@ func (d *ddl) Stats() (map[string]interface{}, error) { m[ddlJobAction] = ddlInfo.Job.Type.String() m[ddlJobLastUpdateTS] = ddlInfo.Job.LastUpdateTS / 1e9 m[ddlJobState] = ddlInfo.Job.State.String() - m[ddlJobError] = ddlInfo.Job.Error + if ddlInfo.Job.Error == nil { + m[ddlJobError] = "" + } else { + m[ddlJobError] = ddlInfo.Job.Error.Error() + } m[ddlJobSchemaState] = ddlInfo.Job.SchemaState.String() m[ddlJobSchemaID] = ddlInfo.Job.SchemaID m[ddlJobTableID] = ddlInfo.Job.TableID @@ -110,7 +114,11 @@ func (d *ddl) Stats() (map[string]interface{}, error) { m[bgJobAction] = bgInfo.Job.Type.String() m[bgJobLastUpdateTS] = bgInfo.Job.LastUpdateTS / 1e9 m[bgJobState] = bgInfo.Job.State.String() - m[bgJobError] = bgInfo.Job.Error + if bgInfo.Job.Error == nil { + m[bgJobError] = "" + } else { + m[bgJobError] = bgInfo.Job.Error.Error() + } m[bgJobSchemaState] = bgInfo.Job.SchemaState.String() m[bgJobSchemaID] = bgInfo.Job.SchemaID m[bgJobTableID] = bgInfo.Job.TableID diff --git a/model/ddl.go b/model/ddl.go index 72db198972a9d..f7b4dc435b9ee 100644 --- a/model/ddl.go +++ b/model/ddl.go @@ -18,6 +18,7 @@ import ( "fmt" "github.com/juju/errors" + "github.com/pingcap/tidb/terror" ) // ActionType is the type for DDL action. @@ -67,12 +68,12 @@ func (action ActionType) String() string { // Job is for a DDL operation. type Job struct { - ID int64 `json:"id"` - Type ActionType `json:"type"` - SchemaID int64 `json:"schema_id"` - TableID int64 `json:"table_id"` - State JobState `json:"state"` - Error string `json:"err"` + ID int64 `json:"id"` + Type ActionType `json:"type"` + SchemaID int64 `json:"schema_id"` + TableID int64 `json:"table_id"` + State JobState `json:"state"` + Error *terror.Error `json:"err"` // every time we meet an error when running job, we will increase it ErrorCount int64 `json:"err_count"` Args []interface{} `json:"-"` diff --git a/mysql/error.go b/mysql/error.go index 27bb06353a3a3..5991f80852c57 100644 --- a/mysql/error.go +++ b/mysql/error.go @@ -60,7 +60,7 @@ func NewErr(errCode uint16, args ...interface{}) *SQLError { return e } -// NewErrf creates a SQL error, with an error code and a format specifier +// NewErrf creates a SQL error, with an error code and a format specifier. func NewErrf(errCode uint16, format string, args ...interface{}) *SQLError { e := &SQLError{Code: errCode} diff --git a/terror/terror.go b/terror/terror.go index 8035a9c708580..f9e1ee9d90341 100644 --- a/terror/terror.go +++ b/terror/terror.go @@ -14,6 +14,7 @@ package terror import ( + "encoding/json" "fmt" "runtime" "strconv" @@ -38,7 +39,8 @@ type ErrCode int // Executor error codes. const ( - CodeCommitNotInTransaction ErrCode = 1 + CodeUnknown ErrCode = -1 + CodeCommitNotInTransaction = 1 CodeRollbackNotInTransaction = 2 CodeExecResultIsEmpty = 3 ) @@ -165,6 +167,35 @@ func (e *Error) Code() ErrCode { return e.code } +func (e *Error) MarshalJSON() ([]byte, error) { + return json.Marshal(&struct { + Class ErrClass `json:"class"` + Code ErrCode `json:"code"` + Msg string `json:"message"` + }{ + Class: e.class, + Code: e.code, + Msg: e.message, + }) +} + +func (e *Error) UnmarshalJSON(data []byte) error { + err := &struct { + Class ErrClass `json:"class"` + Code ErrCode `json:"code"` + Msg string `json:"message"` + }{} + + if err := json.Unmarshal(data, &err); err != nil { + return errors.Trace(err) + } + + e.class = err.Class + e.code = err.Code + e.message = err.Msg + return nil +} + // Location returns the location where the error is created, // implements juju/errors locationer interface. func (e *Error) Location() (file string, line int) { diff --git a/terror/terror_test.go b/terror/terror_test.go index 6984aac8088b9..df1fb50e36044 100644 --- a/terror/terror_test.go +++ b/terror/terror_test.go @@ -14,6 +14,7 @@ package terror import ( + "encoding/json" "strings" "testing" @@ -66,6 +67,21 @@ func (s *testTErrorSuite) TestTError(c *C) { c.Assert(sqlErr.Code, Equals, uint16(1062)) } +func (s *testTErrorSuite) TestJson(c *C) { + prevTErr := &Error{ + class: ClassTable, + code: CodeExecResultIsEmpty, + message: "json test", + } + buf, err := json.Marshal(prevTErr) + c.Assert(err, IsNil) + var curTErr Error + err = json.Unmarshal(buf, &curTErr) + c.Assert(err, IsNil) + isEqual := prevTErr.Equal(&curTErr) + c.Assert(isEqual, IsTrue) +} + var predefinedErr = ClassExecutor.New(ErrCode(123), "predefiend error") func example() error {