Skip to content

Commit

Permalink
[parser] using standard error code to replace terror (pingcap#982)
Browse files Browse the repository at this point in the history
  • Loading branch information
Lingyu Song authored and xhebox committed Oct 8, 2021
1 parent b634418 commit 6479f39
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 182 deletions.
6 changes: 3 additions & 3 deletions parser/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 parser/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 parser/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 parser/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

0 comments on commit 6479f39

Please sign in to comment.