github.com/pingcap/tidb/parser@v0.0.0-20231013125129-93a834a6bf8d/terror/terror.go (about)

     1  // Copyright 2015 PingCAP, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package terror
    15  
    16  import (
    17  	"fmt"
    18  	"runtime/debug"
    19  	"strconv"
    20  	"strings"
    21  	"sync"
    22  	"sync/atomic"
    23  
    24  	"github.com/pingcap/errors"
    25  	"github.com/pingcap/log"
    26  	"github.com/pingcap/tidb/parser/mysql"
    27  	"go.uber.org/zap"
    28  )
    29  
    30  // ErrCode represents a specific error type in a error class.
    31  // Same error code can be used in different error classes.
    32  type ErrCode int
    33  
    34  const (
    35  	// Executor error codes.
    36  
    37  	// CodeUnknown is for errors of unknown reason.
    38  	CodeUnknown ErrCode = -1
    39  	// CodeExecResultIsEmpty indicates execution result is empty.
    40  	CodeExecResultIsEmpty ErrCode = 3
    41  
    42  	// Expression error codes.
    43  
    44  	// CodeMissConnectionID indicates connection id is missing.
    45  	CodeMissConnectionID ErrCode = 1
    46  
    47  	// Special error codes.
    48  
    49  	// CodeResultUndetermined indicates the sql execution result is undetermined.
    50  	CodeResultUndetermined ErrCode = 2
    51  )
    52  
    53  // ErrClass represents a class of errors.
    54  type ErrClass int
    55  
    56  // Error implements error interface.
    57  type Error = errors.Error
    58  
    59  // Error classes.
    60  var (
    61  	ClassAutoid     = RegisterErrorClass(1, "autoid")
    62  	ClassDDL        = RegisterErrorClass(2, "ddl")
    63  	ClassDomain     = RegisterErrorClass(3, "domain")
    64  	ClassEvaluator  = RegisterErrorClass(4, "evaluator")
    65  	ClassExecutor   = RegisterErrorClass(5, "executor")
    66  	ClassExpression = RegisterErrorClass(6, "expression")
    67  	ClassAdmin      = RegisterErrorClass(7, "admin")
    68  	ClassKV         = RegisterErrorClass(8, "kv")
    69  	ClassMeta       = RegisterErrorClass(9, "meta")
    70  	ClassOptimizer  = RegisterErrorClass(10, "planner")
    71  	ClassParser     = RegisterErrorClass(11, "parser")
    72  	ClassPerfSchema = RegisterErrorClass(12, "perfschema")
    73  	ClassPrivilege  = RegisterErrorClass(13, "privilege")
    74  	ClassSchema     = RegisterErrorClass(14, "schema")
    75  	ClassServer     = RegisterErrorClass(15, "server")
    76  	ClassStructure  = RegisterErrorClass(16, "structure")
    77  	ClassVariable   = RegisterErrorClass(17, "variable")
    78  	ClassXEval      = RegisterErrorClass(18, "xeval")
    79  	ClassTable      = RegisterErrorClass(19, "table")
    80  	ClassTypes      = RegisterErrorClass(20, "types")
    81  	ClassGlobal     = RegisterErrorClass(21, "global")
    82  	ClassMockTikv   = RegisterErrorClass(22, "mocktikv")
    83  	ClassJSON       = RegisterErrorClass(23, "json")
    84  	ClassTiKV       = RegisterErrorClass(24, "tikv")
    85  	ClassSession    = RegisterErrorClass(25, "session")
    86  	ClassPlugin     = RegisterErrorClass(26, "plugin")
    87  	ClassUtil       = RegisterErrorClass(27, "util")
    88  	// Add more as needed.
    89  )
    90  
    91  var errClass2Desc = make(map[ErrClass]string)
    92  var rfcCode2errClass = newCode2ErrClassMap()
    93  
    94  type code2ErrClassMap struct {
    95  	data sync.Map
    96  }
    97  
    98  func newCode2ErrClassMap() *code2ErrClassMap {
    99  	return &code2ErrClassMap{
   100  		data: sync.Map{},
   101  	}
   102  }
   103  
   104  func (m *code2ErrClassMap) Get(key string) (ErrClass, bool) {
   105  	ret, have := m.data.Load(key)
   106  	if !have {
   107  		return ErrClass(-1), false
   108  	}
   109  	return ret.(ErrClass), true
   110  }
   111  
   112  func (m *code2ErrClassMap) Put(key string, err ErrClass) {
   113  	m.data.Store(key, err)
   114  }
   115  
   116  var registerFinish uint32
   117  
   118  // RegisterFinish makes the register of new error panic.
   119  // The use pattern should be register all the errors during initialization, and then call RegisterFinish.
   120  func RegisterFinish() {
   121  	atomic.StoreUint32(&registerFinish, 1)
   122  }
   123  
   124  func frozen() bool {
   125  	return atomic.LoadUint32(&registerFinish) != 0
   126  }
   127  
   128  // RegisterErrorClass registers new error class for terror.
   129  func RegisterErrorClass(classCode int, desc string) ErrClass {
   130  	errClass := ErrClass(classCode)
   131  	if _, exists := errClass2Desc[errClass]; exists {
   132  		panic(fmt.Sprintf("duplicate register ClassCode %d - %s", classCode, desc))
   133  	}
   134  	errClass2Desc[errClass] = desc
   135  	return errClass
   136  }
   137  
   138  // String implements fmt.Stringer interface.
   139  func (ec ErrClass) String() string {
   140  	if s, exists := errClass2Desc[ec]; exists {
   141  		return s
   142  	}
   143  	return strconv.Itoa(int(ec))
   144  }
   145  
   146  // EqualClass returns true if err is *Error with the same class.
   147  func (ec ErrClass) EqualClass(err error) bool {
   148  	e := errors.Cause(err)
   149  	if e == nil {
   150  		return false
   151  	}
   152  	if te, ok := e.(*Error); ok {
   153  		rfcCode := te.RFCCode()
   154  		if index := strings.Index(string(rfcCode), ":"); index > 0 {
   155  			if class, has := rfcCode2errClass.Get(string(rfcCode)[:index]); has {
   156  				return class == ec
   157  			}
   158  		}
   159  	}
   160  	return false
   161  }
   162  
   163  // NotEqualClass returns true if err is not *Error with the same class.
   164  func (ec ErrClass) NotEqualClass(err error) bool {
   165  	return !ec.EqualClass(err)
   166  }
   167  
   168  func (ec ErrClass) initError(code ErrCode) string {
   169  	if frozen() {
   170  		debug.PrintStack()
   171  		panic("register error after initialized is prohibited")
   172  	}
   173  	clsMap, ok := ErrClassToMySQLCodes[ec]
   174  	if !ok {
   175  		clsMap = make(map[ErrCode]struct{})
   176  		ErrClassToMySQLCodes[ec] = clsMap
   177  	}
   178  	clsMap[code] = struct{}{}
   179  	class := errClass2Desc[ec]
   180  	rfcCode := fmt.Sprintf("%s:%d", class, code)
   181  	rfcCode2errClass.Put(class, ec)
   182  	return rfcCode
   183  }
   184  
   185  // New defines an *Error with an error code and an error message.
   186  // Usually used to create base *Error.
   187  // Attention:
   188  // this method is not goroutine-safe and
   189  // usually be used in global variable initializer
   190  //
   191  // Deprecated: use NewStd or NewStdErr instead.
   192  func (ec ErrClass) New(code ErrCode, message string) *Error {
   193  	rfcCode := ec.initError(code)
   194  	err := errors.Normalize(message, errors.MySQLErrorCode(int(code)), errors.RFCCodeText(rfcCode))
   195  	return err
   196  }
   197  
   198  // NewStdErr defines an *Error with an error code, an error
   199  // message and workaround to create standard error.
   200  func (ec ErrClass) NewStdErr(code ErrCode, message *mysql.ErrMessage) *Error {
   201  	rfcCode := ec.initError(code)
   202  	err := errors.Normalize(
   203  		message.Raw, errors.RedactArgs(message.RedactArgPos),
   204  		errors.MySQLErrorCode(int(code)), errors.RFCCodeText(rfcCode),
   205  	)
   206  	return err
   207  }
   208  
   209  // NewStd calls New using the standard message for the error code
   210  // Attention:
   211  // this method is not goroutine-safe and
   212  // usually be used in global variable initializer
   213  func (ec ErrClass) NewStd(code ErrCode) *Error {
   214  	return ec.NewStdErr(code, mysql.MySQLErrName[uint16(code)])
   215  }
   216  
   217  // Synthesize synthesizes an *Error in the air
   218  // it didn't register error into ErrClassToMySQLCodes
   219  // so it's goroutine-safe
   220  // and often be used to create Error came from other systems like TiKV.
   221  func (ec ErrClass) Synthesize(code ErrCode, message string) *Error {
   222  	return errors.Normalize(
   223  		message, errors.MySQLErrorCode(int(code)),
   224  		errors.RFCCodeText(fmt.Sprintf("%s:%d", errClass2Desc[ec], code)),
   225  	)
   226  }
   227  
   228  // ToSQLError convert Error to mysql.SQLError.
   229  func ToSQLError(e *Error) *mysql.SQLError {
   230  	code := getMySQLErrorCode(e)
   231  	return mysql.NewErrf(code, "%s", nil, e.GetMsg())
   232  }
   233  
   234  var defaultMySQLErrorCode uint16
   235  
   236  func getMySQLErrorCode(e *Error) uint16 {
   237  	rfcCode := e.RFCCode()
   238  	var class ErrClass
   239  	if index := strings.Index(string(rfcCode), ":"); index > 0 {
   240  		ec, has := rfcCode2errClass.Get(string(rfcCode)[:index])
   241  		if !has {
   242  			log.Warn("Unknown error class", zap.String("class", string(rfcCode)[:index]))
   243  			return defaultMySQLErrorCode
   244  		}
   245  		class = ec
   246  	}
   247  	codeMap, ok := ErrClassToMySQLCodes[class]
   248  	if !ok {
   249  		log.Warn("Unknown error class", zap.Int("class", int(class)))
   250  		return defaultMySQLErrorCode
   251  	}
   252  	_, ok = codeMap[ErrCode(e.Code())]
   253  	if !ok {
   254  		log.Debug("Unknown error code", zap.Int("class", int(class)), zap.Int("code", int(e.Code())))
   255  		return defaultMySQLErrorCode
   256  	}
   257  	return uint16(e.Code())
   258  }
   259  
   260  var (
   261  	// ErrClassToMySQLCodes is the map of ErrClass to code-set.
   262  	ErrClassToMySQLCodes = make(map[ErrClass]map[ErrCode]struct{})
   263  	// ErrCritical is the critical error class.
   264  	ErrCritical = ClassGlobal.NewStdErr(CodeExecResultIsEmpty, mysql.Message("critical error %v", nil))
   265  	// ErrResultUndetermined is the error when execution result is unknown.
   266  	ErrResultUndetermined = ClassGlobal.NewStdErr(
   267  		CodeResultUndetermined,
   268  		mysql.Message("execution result undetermined", nil),
   269  	)
   270  )
   271  
   272  func init() {
   273  	defaultMySQLErrorCode = mysql.ErrUnknown
   274  }
   275  
   276  // ErrorEqual returns a boolean indicating whether err1 is equal to err2.
   277  func ErrorEqual(err1, err2 error) bool {
   278  	e1 := errors.Cause(err1)
   279  	e2 := errors.Cause(err2)
   280  
   281  	if e1 == e2 {
   282  		return true
   283  	}
   284  
   285  	if e1 == nil || e2 == nil {
   286  		return e1 == e2
   287  	}
   288  
   289  	te1, ok1 := e1.(*Error)
   290  	te2, ok2 := e2.(*Error)
   291  	if ok1 && ok2 {
   292  		return te1.RFCCode() == te2.RFCCode()
   293  	}
   294  
   295  	return e1.Error() == e2.Error()
   296  }
   297  
   298  // ErrorNotEqual returns a boolean indicating whether err1 isn't equal to err2.
   299  func ErrorNotEqual(err1, err2 error) bool {
   300  	return !ErrorEqual(err1, err2)
   301  }
   302  
   303  // MustNil cleans up and fatals if err is not nil.
   304  func MustNil(err error, closeFuns ...func()) {
   305  	if err != nil {
   306  		for _, f := range closeFuns {
   307  			f()
   308  		}
   309  		log.Fatal("unexpected error", zap.Error(err), zap.Stack("stack"))
   310  	}
   311  }
   312  
   313  // Call executes a function and checks the returned err.
   314  func Call(fn func() error) {
   315  	err := fn()
   316  	if err != nil {
   317  		log.Error("function call errored", zap.Error(err), zap.Stack("stack"))
   318  	}
   319  }
   320  
   321  // Log logs the error if it is not nil.
   322  func Log(err error) {
   323  	if err != nil {
   324  		log.Error("encountered error", zap.Error(err), zap.Stack("stack"))
   325  	}
   326  }
   327  
   328  // GetErrClass returns the error class of the error.
   329  func GetErrClass(e *Error) ErrClass {
   330  	rfcCode := e.RFCCode()
   331  	if index := strings.Index(string(rfcCode), ":"); index > 0 {
   332  		if class, has := rfcCode2errClass.Get(string(rfcCode)[:index]); has {
   333  			return class
   334  		}
   335  	}
   336  	return ErrClass(-1)
   337  }