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(®isterFinish, 1) 122 } 123 124 func frozen() bool { 125 return atomic.LoadUint32(®isterFinish) != 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 }