Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

using standard error code to replace terror #982

Merged
merged 12 commits into from
Sep 8, 2020
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ require (
github.com/cznic/y v0.0.0-20170802143616-045f81c6662a
github.com/go-sql-driver/mysql v1.5.0
github.com/pingcap/check v0.0.0-20190102082844-67f458068fc8
github.com/pingcap/errors v0.11.4
github.com/pingcap/log v0.0.0-20191012051959-b742a5d432e9
github.com/pingcap/errors v0.11.5-0.20200902104258-eba4f1d8f6de
github.com/pingcap/log v0.0.0-20200511115504-543df19646ad
github.com/remyoudompheng/bigfft v0.0.0-20190728182440-6a916e37a237 // indirect
go.uber.org/zap v1.12.0
go.uber.org/zap v1.15.0
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2
)

Expand Down
11 changes: 11 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,15 @@ github.com/pingcap/check v0.0.0-20190102082844-67f458068fc8/go.mod h1:B1+S9LNcuM
github.com/pingcap/errors v0.11.0/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8=
github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4=
github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8=
github.com/pingcap/errors v0.11.5-0.20200902104258-eba4f1d8f6de h1:mW8hC2yXTpflfyTeJgcN4aJQfwcYODde8YgjBgAy6do=
github.com/pingcap/errors v0.11.5-0.20200902104258-eba4f1d8f6de/go.mod h1:g4vx//d6VakjJ0mk7iLBlKA8LFavV/sAVINT/1PFxeQ=
github.com/pingcap/log v0.0.0-20191012051959-b742a5d432e9 h1:AJD9pZYm72vMgPcQDww9rkZ1DnWfl0pXV3BOWlkYIjA=
github.com/pingcap/log v0.0.0-20191012051959-b742a5d432e9/go.mod h1:4rbK1p9ILyIfb6hU7OG2CiWSqMXnp3JMbiaVJ6mvoY8=
github.com/pingcap/log v0.0.0-20200511115504-543df19646ad h1:SveG82rmu/GFxYanffxsSF503SiQV+2JLnWEiGiF+Tc=
github.com/pingcap/log v0.0.0-20200511115504-543df19646ad/go.mod h1:4rbK1p9ILyIfb6hU7OG2CiWSqMXnp3JMbiaVJ6mvoY8=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/remyoudompheng/bigfft v0.0.0-20190728182440-6a916e37a237 h1:HQagqIiBmr8YXawX/le3+O26N+vPPC1PtjaF3mwnook=
Expand All @@ -45,14 +50,20 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.5.0 h1:OI5t8sDa1Or+q8AeE+yKeB/SDYioSHAgcVljj9JIETY=
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk=
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/multierr v1.3.0 h1:sFPn2GLc3poCkfrpIXGhBD2X0CMIo4Q/zSULXrj/+uc=
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A=
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.12.0 h1:dySoUQPFBGj6xwjmBzageVL8jGi8uxc6bEmJQjA06bw=
go.uber.org/zap v1.12.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
go.uber.org/zap v1.15.0 h1:ZZCA22JRF2gQE5FoNmhmrf7jeJJ2uhqDUNRYKm8dvmM=
go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=
Expand Down
233 changes: 61 additions & 172 deletions terror/terror.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,9 @@
package terror

import (
"encoding/json"
"fmt"
"os"
"strconv"
"strings"

"github.com/pingcap/errors"
"github.com/pingcap/log"
Expand Down Expand Up @@ -51,6 +50,8 @@ const (
// ErrClass represents a class of errors.
type ErrClass int

type Error = errors.Error

// Error classes.
var (
ClassAutoid = RegisterErrorClass(1, "autoid")
Expand Down Expand Up @@ -85,6 +86,7 @@ var (

var errClass2Desc = make(map[ErrClass]string)
var errCodeMap = make(map[ErrCode]*Error)
var rfcCode2errClass = make(map[string]ErrClass)

// RegisterErrorClass registers new error class for terror.
func RegisterErrorClass(classCode int, desc string) ErrClass {
Expand All @@ -111,7 +113,12 @@ func (ec ErrClass) EqualClass(err error) bool {
return false
}
if te, ok := e.(*Error); ok {
return te.class == ec
rfcCode := te.RFCCode()
if index := strings.Index(string(rfcCode), ":"); index > 0 {
if class, has := rfcCode2errClass[string(rfcCode)[:index]]; has {
return class == ec
}
}
}
return false
}
Expand All @@ -121,23 +128,36 @@ func (ec ErrClass) NotEqualClass(err error) bool {
return !ec.EqualClass(err)
}

// New defines an *Error with an error code and an error message.
// Usually used to create base *Error.
// Attention:
// this method is not goroutine-safe and
// usually be used in global variable initializer
func (ec ErrClass) New(code ErrCode, message string) *Error {
func (ec ErrClass) initError(code ErrCode) string {
clsMap, ok := ErrClassToMySQLCodes[ec]
if !ok {
clsMap = make(map[ErrCode]struct{})
ErrClassToMySQLCodes[ec] = clsMap
}
clsMap[code] = struct{}{}
err := &Error{
class: ec,
code: code,
message: message,
}
class := errClass2Desc[ec]
rfcCode := fmt.Sprintf("%s:%d", class, code)
rfcCode2errClass[class] = ec
return rfcCode
}

// New defines an *Error with an error code and an error message.
// Usually used to create base *Error.
// Attention:
// this method is not goroutine-safe and
// usually be used in global variable initializer
func (ec ErrClass) New(code ErrCode, message string) *Error {
rfcCode := ec.initError(code)
err := errors.Normalize(message, errors.MySQLErrorCode(int(code)), errors.RFCCodeText(rfcCode))
errCodeMap[code] = err
return err
}

// NewStdErr defines an *Error with an error code, an error
// message and workaround to create standard error.
func (ec ErrClass) NewStdErr(code ErrCode, message string, desc string, workaround string) *Error {
rfcCode := ec.initError(code)
err := errors.Normalize(message, errors.MySQLErrorCode(int(code)), errors.RFCCodeText(rfcCode), errors.Description(desc), errors.Workaround(workaround))
errCodeMap[code] = err
return err
}
Expand All @@ -155,162 +175,39 @@ func (ec ErrClass) NewStd(code ErrCode) *Error {
// so it's goroutine-safe
// and often be used to create Error came from other systems like TiKV.
func (ec ErrClass) Synthesize(code ErrCode, message string) *Error {
return &Error{
class: ec,
code: code,
message: message,
}
}

// Error implements error interface and adds integer Class and Code, so
// errors with different message can be compared.
type Error struct {
class ErrClass
code ErrCode
message string
workaround string
args []interface{}
file string
line int
}

// Class returns ErrClass
func (e *Error) Class() ErrClass {
return e.class
}

// Code returns ErrCode
func (e *Error) Code() ErrCode {
return e.code
}

// SetWorkaround is a decorator like method which add a workaround to
// error which is convenient for user to search.
func (e *Error) SetWorkaround(workaround string) *Error {
e.workaround = workaround
return e
}

// MarshalJSON implements json.Marshaler interface.
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.getMsg(),
})
}

// UnmarshalJSON implements json.Unmarshaler interface.
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) {
return e.file, e.line
}

// Error implements error interface.
func (e *Error) Error() string {
return fmt.Sprintf("[%s:%d]%s", e.class, e.code, e.getMsg())
}

func (e *Error) getMsg() string {
if len(e.args) > 0 {
return fmt.Sprintf(e.message, e.args...)
}
return e.message
}

// GenWithStack generates a new *Error with the same class and code, and a new formatted message.
func (e *Error) GenWithStack(format string, args ...interface{}) error {
err := *e
err.message = format
err.args = args
return errors.AddStack(&err)
}

// GenWithStackByArgs generates a new *Error with the same class and code, and new arguments.
func (e *Error) GenWithStackByArgs(args ...interface{}) error {
err := *e
err.args = args
return errors.AddStack(&err)
}

// FastGen generates a new *Error with the same class and code, and a new formatted message.
// This will not call runtime.Caller to get file and line.
func (e *Error) FastGen(format string, args ...interface{}) error {
err := *e
err.message = format
err.args = args
return errors.SuspendStack(&err)
}

// FastGen generates a new *Error with the same class and code, and a new arguments.
// This will not call runtime.Caller to get file and line.
func (e *Error) FastGenByArgs(args ...interface{}) error {
err := *e
err.args = args
return errors.SuspendStack(&err)
}

// Equal checks if err is equal to e.
func (e *Error) Equal(err error) bool {
originErr := errors.Cause(err)
if originErr == nil {
return false
}

if error(e) == originErr {
return true
}
inErr, ok := originErr.(*Error)
return ok && e.class == inErr.class && e.code == inErr.code
}

// NotEqual checks if err is not equal to e.
func (e *Error) NotEqual(err error) bool {
return !e.Equal(err)
return errors.Normalize(message, errors.MySQLErrorCode(int(code)), errors.RFCCodeText(fmt.Sprintf("%s:%d", errClass2Desc[ec], code)))
}

// ToSQLError convert Error to mysql.SQLError.
func (e *Error) ToSQLError() *mysql.SQLError {
code := e.getMySQLErrorCode()
return mysql.NewErrf(code, "%s", e.getMsg())
func ToSQLError(e *Error) *mysql.SQLError {
code := getMySQLErrorCode(e)
return mysql.NewErrf(code, "%s", e.GetMsg())
}

var defaultMySQLErrorCode uint16

func (e *Error) getMySQLErrorCode() uint16 {
codeMap, ok := ErrClassToMySQLCodes[e.class]
func getMySQLErrorCode(e *Error) uint16 {
rfcCode := e.RFCCode()
var class ErrClass
if index := strings.Index(string(rfcCode), ":"); index > 0 {
if ec, has := rfcCode2errClass[string(rfcCode)[:index]]; has {
class = ec
} else {
log.Warn("Unknown error class", zap.String("class", string(rfcCode)[:index]))
return defaultMySQLErrorCode
}
}
codeMap, ok := ErrClassToMySQLCodes[class]
if !ok {
log.Warn("Unknown error class", zap.Int("class", int(e.class)))
log.Warn("Unknown error class", zap.Int("class", int(class)))
return defaultMySQLErrorCode
}
_, ok = codeMap[e.code]
_, ok = codeMap[ErrCode(e.Code())]
if !ok {
log.Debug("Unknown error code", zap.Int("class", int(e.class)), zap.Int("code", int(e.code)))
log.Debug("Unknown error code", zap.Int("class", int(class)), zap.Int("code", int(e.Code())))
return defaultMySQLErrorCode
}
return uint16(e.code)
return uint16(e.Code())
}

var (
Expand Down Expand Up @@ -340,7 +237,7 @@ func ErrorEqual(err1, err2 error) bool {
te1, ok1 := e1.(*Error)
te2, ok2 := e2.(*Error)
if ok1 && ok2 {
return te1.class == te2.class && te1.code == te2.code
return te1.RFCCode() == te2.RFCCode()
}

return e1.Error() == e2.Error()
Expand Down Expand Up @@ -376,20 +273,12 @@ func Log(err error) {
}
}

// ExportErrorCodeAndWorkaround is used to produce error workaround.
func ExportErrorCodeAndWorkaround(fileName string) error {
file, err := os.Create(fileName)
if err != nil {
return err
}
for code, e := range errCodeMap {
workaround := fmt.Sprintf(
"[error.%v]\nerror = '''%v'''\nworkaround = '''%v'''\n\n",
code, e.message, e.workaround)
_, err = file.WriteString(workaround)
if err != nil {
return err
func GetErrClass(e *Error) ErrClass {
rfcCode := e.RFCCode()
if index := strings.Index(string(rfcCode), ":"); index > 0 {
if class, has := rfcCode2errClass[string(rfcCode)[:index]]; has {
return class
}
}
return nil
return ErrClass(-1)
}
10 changes: 3 additions & 7 deletions terror/terror_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ func (s *testTErrorSuite) TestTError(c *C) {
kvErr := ClassKV.New(1062, "key already exist")
e := kvErr.FastGen("Duplicate entry '%d' for key 'PRIMARY'", 1)
c.Assert(e.Error(), Equals, "[kv:1062]Duplicate entry '1' for key 'PRIMARY'")
sqlErr := errors.Cause(e).(*Error).ToSQLError()
sqlErr := ToSQLError(errors.Cause(e).(*Error))
c.Assert(sqlErr.Message, Equals, "Duplicate entry '1' for key 'PRIMARY'")
c.Assert(sqlErr.Code, Equals, uint16(1062))

Expand All @@ -77,14 +77,10 @@ func (s *testTErrorSuite) TestTError(c *C) {
}

func (s *testTErrorSuite) TestJson(c *C) {
prevTErr := &Error{
class: ClassTable,
code: CodeExecResultIsEmpty,
message: "json test",
}
prevTErr := errors.Normalize("json test", errors.MySQLErrorCode(int(CodeExecResultIsEmpty)))
buf, err := json.Marshal(prevTErr)
c.Assert(err, IsNil)
var curTErr Error
var curTErr errors.Error
err = json.Unmarshal(buf, &curTErr)
c.Assert(err, IsNil)
isEqual := prevTErr.Equal(&curTErr)
Expand Down