123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303 |
- // Copyright 2015 PingCAP, Inc.
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // See the License for the specific language governing permissions and
- // limitations under the License.
- package terror
- import (
- "fmt"
- "strconv"
- "strings"
- "sync"
- "github.com/pingcap/errors"
- "github.com/pingcap/log"
- "github.com/pingcap/parser/mysql"
- "go.uber.org/zap"
- )
- // ErrCode represents a specific error type in a error class.
- // Same error code can be used in different error classes.
- type ErrCode int
- const (
- // Executor error codes.
- // CodeUnknown is for errors of unknown reason.
- CodeUnknown ErrCode = -1
- // CodeExecResultIsEmpty indicates execution result is empty.
- CodeExecResultIsEmpty ErrCode = 3
- // Expression error codes.
- // CodeMissConnectionID indicates connection id is missing.
- CodeMissConnectionID ErrCode = 1
- // Special error codes.
- // CodeResultUndetermined indicates the sql execution result is undetermined.
- CodeResultUndetermined ErrCode = 2
- )
- // ErrClass represents a class of errors.
- type ErrClass int
- type Error = errors.Error
- // Error classes.
- var (
- ClassAutoid = RegisterErrorClass(1, "autoid")
- ClassDDL = RegisterErrorClass(2, "ddl")
- ClassDomain = RegisterErrorClass(3, "domain")
- ClassEvaluator = RegisterErrorClass(4, "evaluator")
- ClassExecutor = RegisterErrorClass(5, "executor")
- ClassExpression = RegisterErrorClass(6, "expression")
- ClassAdmin = RegisterErrorClass(7, "admin")
- ClassKV = RegisterErrorClass(8, "kv")
- ClassMeta = RegisterErrorClass(9, "meta")
- ClassOptimizer = RegisterErrorClass(10, "planner")
- ClassParser = RegisterErrorClass(11, "parser")
- ClassPerfSchema = RegisterErrorClass(12, "perfschema")
- ClassPrivilege = RegisterErrorClass(13, "privilege")
- ClassSchema = RegisterErrorClass(14, "schema")
- ClassServer = RegisterErrorClass(15, "server")
- ClassStructure = RegisterErrorClass(16, "structure")
- ClassVariable = RegisterErrorClass(17, "variable")
- ClassXEval = RegisterErrorClass(18, "xeval")
- ClassTable = RegisterErrorClass(19, "table")
- ClassTypes = RegisterErrorClass(20, "types")
- ClassGlobal = RegisterErrorClass(21, "global")
- ClassMockTikv = RegisterErrorClass(22, "mocktikv")
- ClassJSON = RegisterErrorClass(23, "json")
- ClassTiKV = RegisterErrorClass(24, "tikv")
- ClassSession = RegisterErrorClass(25, "session")
- ClassPlugin = RegisterErrorClass(26, "plugin")
- ClassUtil = RegisterErrorClass(27, "util")
- // Add more as needed.
- )
- var errClass2Desc = make(map[ErrClass]string)
- var rfcCode2errClass = newCode2ErrClassMap()
- type code2ErrClassMap struct {
- data sync.Map
- }
- func newCode2ErrClassMap() *code2ErrClassMap {
- return &code2ErrClassMap{
- data: sync.Map{},
- }
- }
- func (m *code2ErrClassMap) Get(key string) (ErrClass, bool) {
- ret, have := m.data.Load(key)
- return ret.(ErrClass), have
- }
- func (m *code2ErrClassMap) Put(key string, err ErrClass) {
- m.data.Store(key, err)
- }
- // RegisterErrorClass registers new error class for terror.
- func RegisterErrorClass(classCode int, desc string) ErrClass {
- errClass := ErrClass(classCode)
- if _, exists := errClass2Desc[errClass]; exists {
- panic(fmt.Sprintf("duplicate register ClassCode %d - %s", classCode, desc))
- }
- errClass2Desc[errClass] = desc
- return errClass
- }
- // String implements fmt.Stringer interface.
- func (ec ErrClass) String() string {
- if s, exists := errClass2Desc[ec]; exists {
- return s
- }
- return strconv.Itoa(int(ec))
- }
- // EqualClass returns true if err is *Error with the same class.
- func (ec ErrClass) EqualClass(err error) bool {
- e := errors.Cause(err)
- if e == nil {
- return false
- }
- if te, ok := e.(*Error); ok {
- rfcCode := te.RFCCode()
- if index := strings.Index(string(rfcCode), ":"); index > 0 {
- if class, has := rfcCode2errClass.Get(string(rfcCode)[:index]); has {
- return class == ec
- }
- }
- }
- return false
- }
- // NotEqualClass returns true if err is not *Error with the same class.
- func (ec ErrClass) NotEqualClass(err error) bool {
- return !ec.EqualClass(err)
- }
- func (ec ErrClass) initError(code ErrCode) string {
- clsMap, ok := ErrClassToMySQLCodes[ec]
- if !ok {
- clsMap = make(map[ErrCode]struct{})
- ErrClassToMySQLCodes[ec] = clsMap
- }
- clsMap[code] = struct{}{}
- class := errClass2Desc[ec]
- rfcCode := fmt.Sprintf("%s:%d", class, code)
- rfcCode2errClass.Put(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
- //
- // Deprecated: use NewStd or NewStdErr instead.
- func (ec ErrClass) New(code ErrCode, message string) *Error {
- rfcCode := ec.initError(code)
- err := errors.Normalize(message, errors.MySQLErrorCode(int(code)), errors.RFCCodeText(rfcCode))
- 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 *mysql.ErrMessage) *Error {
- rfcCode := ec.initError(code)
- err := errors.Normalize(message.Raw, errors.RedactArgs(message.RedactArgPos), errors.MySQLErrorCode(int(code)), errors.RFCCodeText(rfcCode))
- return err
- }
- // NewStd calls New using the standard message for the error code
- // Attention:
- // this method is not goroutine-safe and
- // usually be used in global variable initializer
- func (ec ErrClass) NewStd(code ErrCode) *Error {
- return ec.NewStdErr(code, mysql.MySQLErrName[uint16(code)])
- }
- // Synthesize synthesizes an *Error in the air
- // it didn't register error into ErrClassToMySQLCodes
- // 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 errors.Normalize(message, errors.MySQLErrorCode(int(code)), errors.RFCCodeText(fmt.Sprintf("%s:%d", errClass2Desc[ec], code)))
- }
- // ToSQLError convert Error to mysql.SQLError.
- func ToSQLError(e *Error) *mysql.SQLError {
- code := getMySQLErrorCode(e)
- return mysql.NewErrf(code, "%s", nil, e.GetMsg())
- }
- var defaultMySQLErrorCode uint16
- func getMySQLErrorCode(e *Error) uint16 {
- rfcCode := e.RFCCode()
- var class ErrClass
- if index := strings.Index(string(rfcCode), ":"); index > 0 {
- if ec, has := rfcCode2errClass.Get(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(class)))
- return defaultMySQLErrorCode
- }
- _, ok = codeMap[ErrCode(e.Code())]
- if !ok {
- log.Debug("Unknown error code", zap.Int("class", int(class)), zap.Int("code", int(e.Code())))
- return defaultMySQLErrorCode
- }
- return uint16(e.Code())
- }
- var (
- // ErrClassToMySQLCodes is the map of ErrClass to code-set.
- ErrClassToMySQLCodes = make(map[ErrClass]map[ErrCode]struct{})
- ErrCritical = ClassGlobal.NewStdErr(CodeExecResultIsEmpty, mysql.Message("critical error %v", nil))
- ErrResultUndetermined = ClassGlobal.NewStdErr(CodeResultUndetermined, mysql.Message("execution result undetermined", nil))
- )
- func init() {
- defaultMySQLErrorCode = mysql.ErrUnknown
- }
- // ErrorEqual returns a boolean indicating whether err1 is equal to err2.
- func ErrorEqual(err1, err2 error) bool {
- e1 := errors.Cause(err1)
- e2 := errors.Cause(err2)
- if e1 == e2 {
- return true
- }
- if e1 == nil || e2 == nil {
- return e1 == e2
- }
- te1, ok1 := e1.(*Error)
- te2, ok2 := e2.(*Error)
- if ok1 && ok2 {
- return te1.RFCCode() == te2.RFCCode()
- }
- return e1.Error() == e2.Error()
- }
- // ErrorNotEqual returns a boolean indicating whether err1 isn't equal to err2.
- func ErrorNotEqual(err1, err2 error) bool {
- return !ErrorEqual(err1, err2)
- }
- // MustNil cleans up and fatals if err is not nil.
- func MustNil(err error, closeFuns ...func()) {
- if err != nil {
- for _, f := range closeFuns {
- f()
- }
- log.Fatal("unexpected error", zap.Error(err), zap.Stack("stack"))
- }
- }
- // Call executes a function and checks the returned err.
- func Call(fn func() error) {
- err := fn()
- if err != nil {
- log.Error("function call errored", zap.Error(err), zap.Stack("stack"))
- }
- }
- // Log logs the error if it is not nil.
- func Log(err error) {
- if err != nil {
- log.Error("encountered error", zap.Error(err), zap.Stack("stack"))
- }
- }
- func GetErrClass(e *Error) ErrClass {
- rfcCode := e.RFCCode()
- if index := strings.Index(string(rfcCode), ":"); index > 0 {
- if class, has := rfcCode2errClass.Get(string(rfcCode)[:index]); has {
- return class
- }
- }
- return ErrClass(-1)
- }
|