Files
tidb/terror/terror.go
Han Fei ea894e8b77 *: Add a function GenByArgs for Error (#2033)
*: Add a function GenByArgs for Error
2016-11-27 13:18:43 +08:00

321 lines
7.3 KiB
Go

// 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 (
"encoding/json"
"fmt"
"runtime"
"strconv"
"github.com/juju/errors"
"github.com/ngaut/log"
"github.com/pingcap/tidb/mysql"
)
// Common base error instances.
var (
CommitNotInTransaction = ClassExecutor.New(CodeCommitNotInTransaction, "commit not in transaction")
RollbackNotInTransaction = ClassExecutor.New(CodeRollbackNotInTransaction, "rollback not in transaction")
ExecResultIsEmpty = ClassExecutor.New(CodeExecResultIsEmpty, "exec result is empty")
MissConnectionID = ClassExpression.New(CodeMissConnectionID, "miss connection id information")
)
// ErrCode represents a specific error type in a error class.
// Same error code can be used in different error classes.
type ErrCode int
// Executor error codes.
const (
CodeUnknown ErrCode = -1
CodeCommitNotInTransaction = 1
CodeRollbackNotInTransaction = 2
CodeExecResultIsEmpty = 3
)
// Expression error codes.
const (
CodeMissConnectionID ErrCode = iota + 1
)
// ErrClass represents a class of errors.
type ErrClass int
// Error classes.
const (
ClassAutoid ErrClass = iota + 1
ClassDDL
ClassDomain
ClassEvaluator
ClassExecutor
ClassExpression
ClassInspectkv
ClassKV
ClassMeta
ClassOptimizer
ClassOptimizerPlan
ClassParser
ClassPerfSchema
ClassPrivilege
ClassSchema
ClassServer
ClassStructure
ClassVariable
ClassXEval
ClassTable
ClassTypes
// Add more as needed.
)
// String implements fmt.Stringer interface.
func (ec ErrClass) String() string {
switch ec {
case ClassAutoid:
return "autoid"
case ClassDDL:
return "ddl"
case ClassDomain:
return "domain"
case ClassExecutor:
return "executor"
case ClassExpression:
return "expression"
case ClassInspectkv:
return "inspectkv"
case ClassMeta:
return "meta"
case ClassKV:
return "kv"
case ClassOptimizer:
return "optimizer"
case ClassParser:
return "parser"
case ClassPerfSchema:
return "perfschema"
case ClassPrivilege:
return "privilege"
case ClassSchema:
return "schema"
case ClassServer:
return "server"
case ClassStructure:
return "structure"
case ClassVariable:
return "variable"
case ClassTable:
return "table"
case ClassTypes:
return "types"
}
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 {
return te.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)
}
// New creates an *Error with an error code and an error message.
// Usually used to create base *Error.
func (ec ErrClass) New(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
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
}
// 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
}
// Gen generates a new *Error with the same class and code, and a new formatted message.
func (e *Error) Gen(format string, args ...interface{}) *Error {
err := *e
err.message = format
err.args = args
_, err.file, err.line, _ = runtime.Caller(1)
return &err
}
// GenByArgs generates a new *Error with the same class and code, and new arguments.
func (e *Error) GenByArgs(args ...interface{}) *Error {
err := *e
err.args = args
_, err.file, err.line, _ = runtime.Caller(1)
return &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 &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
}
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)
}
// ToSQLError convert Error to mysql.SQLError.
func (e *Error) ToSQLError() *mysql.SQLError {
code := e.getMySQLErrorCode()
return mysql.NewErrf(code, e.getMsg())
}
var defaultMySQLErrorCode uint16
func (e *Error) getMySQLErrorCode() uint16 {
codeMap, ok := ErrClassToMySQLCodes[e.class]
if !ok {
log.Warnf("Unknown error class: %v", e.class)
return defaultMySQLErrorCode
}
code, ok := codeMap[e.code]
if !ok {
log.Warnf("Unknown error class: %v code: %v", e.class, e.code)
return defaultMySQLErrorCode
}
return code
}
var (
// ErrClassToMySQLCodes is the map of ErrClass to code-map.
ErrClassToMySQLCodes map[ErrClass](map[ErrCode]uint16)
)
func init() {
ErrClassToMySQLCodes = make(map[ErrClass](map[ErrCode]uint16))
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.class == te2.class && te1.code == te2.code
}
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)
}