From 2493a227ee44ea1fac37644de31bdb9ce12ebfe7 Mon Sep 17 00:00:00 2001
From: imtbkcat <songlingyu@pingcap.com>
Date: Fri, 7 Aug 2020 13:47:39 +0800
Subject: [PATCH 1/8] add warp and cause interface

---
 .gitignore                 |  1 +
 terror_error.go            | 32 +++++++++++++++++++++++++++++---
 terror_test/terror_test.go |  9 +++++++++
 3 files changed, 39 insertions(+), 3 deletions(-)

diff --git a/.gitignore b/.gitignore
index daf913b..0c9e329 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,6 +6,7 @@
 # Folders
 _obj
 _test
+.idea
 
 # Architecture specific extensions/prefixes
 *.[568vq]
diff --git a/terror_error.go b/terror_error.go
index f6b147e..a65a1ee 100644
--- a/terror_error.go
+++ b/terror_error.go
@@ -61,9 +61,11 @@ type Error struct {
 	// and the more detail this field explaining the better,
 	// even some guess of the cause could be included.
 	Description string
-	args        []interface{}
-	file        string
-	line        int
+	// Cause is used to warp some third party error.
+	cause error
+	args  []interface{}
+	file  string
+	line  int
 }
 
 // Class returns ErrClass
@@ -291,3 +293,27 @@ func (e *Error) UnmarshalJSON(data []byte) error {
 	}
 	return nil
 }
+
+func (e *Error) WarpCauseError(err error) *Error {
+	e.cause = err
+	return e
+}
+
+func (e *Error) Cause() error {
+	return e.cause
+}
+
+func (e *Error) FastGenWithCause(args ...interface{}) error {
+	err := *e
+	err.message = e.cause.Error()
+	err.args = args
+	return SuspendStack(&err)
+}
+
+func (e *Error) GenWithStackByCause(args ...interface{}) error {
+	err := *e
+	err.message = e.cause.Error()
+	err.args = args
+	err.fillLineAndFile(1)
+	return AddStack(&err)
+}
diff --git a/terror_test/terror_test.go b/terror_test/terror_test.go
index 71ceffa..757c0f2 100644
--- a/terror_test/terror_test.go
+++ b/terror_test/terror_test.go
@@ -39,6 +39,7 @@ var (
 	ClassParser    = reg.RegisterErrorClass(11, "parser")
 	ClassServer    = reg.RegisterErrorClass(15, "server")
 	ClassTable     = reg.RegisterErrorClass(19, "table")
+	ClassMember    = reg.RegisterErrorClass(20, "member")
 )
 
 const (
@@ -339,3 +340,11 @@ func (*testTErrorSuite) TestLineAndFile(c *C) {
 	c.Assert(file2, Equals, f2)
 	c.Assert(line2, Equals, l2-1)
 }
+
+func (*testTErrorSuite) TestWarpAndField(c *C) {
+	causeErr := errors.New("load from etcd meet error")
+	ErrGetLeader := ClassMember.DefineError().TextualCode("ErrGetLeader").MessageTemplate("fail to get leader").Build()
+	errWithWarpedCause := ErrGetLeader.WarpCauseError(causeErr)
+	c.Assert(errWithWarpedCause.FastGenWithCause().Error(), Equals, "[DB:member:ErrGetLeader] load from etcd meet error")
+	c.Assert(fmt.Sprintf("%v", errWithWarpedCause.FastGenWithCause()), Equals, "[DB:member:ErrGetLeader] fail to get leader")
+}

From b667838eacc34b6b8b6accd33c8b7f310a715be0 Mon Sep 17 00:00:00 2001
From: imtbkcat <songlingyu@pingcap.com>
Date: Fri, 7 Aug 2020 14:25:46 +0800
Subject: [PATCH 2/8] add zap Field produce method

---
 terror_error.go | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/terror_error.go b/terror_error.go
index a65a1ee..289eee8 100644
--- a/terror_error.go
+++ b/terror_error.go
@@ -16,6 +16,8 @@ package errors
 import (
 	"encoding/json"
 	"fmt"
+	"go.uber.org/zap"
+	"go.uber.org/zap/zapcore"
 	"runtime"
 	"strconv"
 	"strings"
@@ -317,3 +319,11 @@ func (e *Error) GenWithStackByCause(args ...interface{}) error {
 	err.fillLineAndFile(1)
 	return AddStack(&err)
 }
+
+func CauseError(err *Error) zap.Field {
+	return zap.Field{Key: "error", Type: zapcore.ErrorType, Interface: err.FastGenWithCause()}
+}
+
+func DetailError(err *Error) zap.Field {
+	return zap.Field{Key: "error", Type: zapcore.ErrorType, Interface: err.FastGenByArgs()}
+}

From b17b426e6c337e0d09230cbd49862078912705de Mon Sep 17 00:00:00 2001
From: imtbkcat <songlingyu@pingcap.com>
Date: Fri, 7 Aug 2020 14:26:40 +0800
Subject: [PATCH 3/8] format import

---
 terror_error.go | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/terror_error.go b/terror_error.go
index 289eee8..8f1fd56 100644
--- a/terror_error.go
+++ b/terror_error.go
@@ -16,11 +16,12 @@ package errors
 import (
 	"encoding/json"
 	"fmt"
-	"go.uber.org/zap"
-	"go.uber.org/zap/zapcore"
 	"runtime"
 	"strconv"
 	"strings"
+
+	"go.uber.org/zap"
+	"go.uber.org/zap/zapcore"
 )
 
 // Error is the 'prototype' of a type of errors.

From 2548b76ac918827a79c0694de222ad9f98b5f4ae Mon Sep 17 00:00:00 2001
From: imtbkcat <songlingyu@pingcap.com>
Date: Mon, 10 Aug 2020 15:38:11 +0800
Subject: [PATCH 4/8] address comment

---
 terror_error.go | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/terror_error.go b/terror_error.go
index 8f1fd56..dbe74f8 100644
--- a/terror_error.go
+++ b/terror_error.go
@@ -297,13 +297,17 @@ func (e *Error) UnmarshalJSON(data []byte) error {
 	return nil
 }
 
-func (e *Error) WarpCauseError(err error) *Error {
+func (e *Error) Warp(err error) *Error {
 	e.cause = err
 	return e
 }
 
 func (e *Error) Cause() error {
-	return e.cause
+	root := Unwrap(e.cause)
+	if root == nil {
+		return e.cause
+	}
+	return root
 }
 
 func (e *Error) FastGenWithCause(args ...interface{}) error {

From a28915e9ff0c38c98f1def88fbb7b686704c5b38 Mon Sep 17 00:00:00 2001
From: imtbkcat <songlingyu@pingcap.com>
Date: Mon, 10 Aug 2020 15:40:55 +0800
Subject: [PATCH 5/8] fix typo

---
 terror_error.go | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/terror_error.go b/terror_error.go
index dbe74f8..473f951 100644
--- a/terror_error.go
+++ b/terror_error.go
@@ -297,7 +297,7 @@ func (e *Error) UnmarshalJSON(data []byte) error {
 	return nil
 }
 
-func (e *Error) Warp(err error) *Error {
+func (e *Error) Wrap(err error) *Error {
 	e.cause = err
 	return e
 }

From 2d79068bad1428a0ea25ca01da3618c37d7c94bc Mon Sep 17 00:00:00 2001
From: imtbkcat <songlingyu@pingcap.com>
Date: Mon, 10 Aug 2020 19:23:59 +0800
Subject: [PATCH 6/8] remove registry and error class

---
 terror_dsl.go              |  73 -------------
 terror_error.go            |  90 +++++++++-------
 terror_gen.go              |  79 --------------
 terror_registry.go         | 176 -------------------------------
 terror_test/terror_test.go | 210 +++----------------------------------
 5 files changed, 64 insertions(+), 564 deletions(-)
 delete mode 100644 terror_dsl.go
 delete mode 100644 terror_gen.go
 delete mode 100644 terror_registry.go

diff --git a/terror_dsl.go b/terror_dsl.go
deleted file mode 100644
index e2e55ee..0000000
--- a/terror_dsl.go
+++ /dev/null
@@ -1,73 +0,0 @@
-// Copyright 2020 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 errors
-
-import (
-	"github.com/pingcap/log"
-	"go.uber.org/zap"
-)
-
-// Builder is the builder of Error.
-type Builder struct {
-	err   *Error
-	class *ErrClass
-}
-
-// TextualCode is is the textual identity of this error internally,
-// note that this error code can only be duplicated in different Registry or ErrorClass.
-func (b *Builder) TextualCode(text ErrCodeText) *Builder {
-	b.err.codeText = text
-	return b
-}
-
-// NumericCode is is the numeric identity of this error internally,
-// note that this error code can only be duplicated in different Registry or ErrorClass.
-func (b *Builder) NumericCode(num ErrCode) *Builder {
-	b.err.code = num
-	return b
-}
-
-// Description is the expanded detail of why this error occurred.
-// This could be written by developer at a static env,
-// and the more detail this field explaining the better,
-// even some guess of the cause could be included.
-func (b *Builder) Description(desc string) *Builder {
-	b.err.Description = desc
-	return b
-}
-
-// Workaround shows how to work around this error.
-// It's used to teach the users how to solve the error if occurring in the real environment.
-func (b *Builder) Workaround(wd string) *Builder {
-	b.err.Workaround = wd
-	return b
-}
-
-// MessageTemplate is the template of the error string that can be formatted after
-// calling `GenWithArgs` method.
-// currently, printf style template is used.
-func (b *Builder) MessageTemplate(template string) *Builder {
-	b.err.message = template
-	return b
-}
-
-// Build ends the define of the error.
-func (b *Builder) Build() *Error {
-	if ok := b.class.RegisterError(b.err); !ok {
-		log.Panic("replicated error prototype created",
-			zap.String("ID", string(b.err.ID())),
-			zap.String("RFCCode", string(b.err.RFCCode())))
-	}
-	return b.err
-}
diff --git a/terror_error.go b/terror_error.go
index 473f951..4f7233f 100644
--- a/terror_error.go
+++ b/terror_error.go
@@ -24,6 +24,16 @@ import (
 	"go.uber.org/zap/zapcore"
 )
 
+// ErrCode represents a specific error type in a error class.
+// Same error code can be used in different error classes.
+type ErrCode int
+
+// ErrCodeText is a textual error code that represents a specific error type in a error class.
+type ErrCodeText string
+
+type ErrorID string
+type RFCErrorCode string
+
 // Error is the 'prototype' of a type of errors.
 // Use DefineError to make a *Error:
 // var ErrUnavailable = ClassRegion.DefineError().
@@ -49,8 +59,7 @@ import (
 //     // handle this error.
 // }
 type Error struct {
-	class *ErrClass
-	code  ErrCode
+	code ErrCode
 	// codeText is the textual describe of the error code
 	codeText ErrCodeText
 	// message is a template of the description of this error.
@@ -71,11 +80,6 @@ type Error struct {
 	line  int
 }
 
-// Class returns ErrClass
-func (e *Error) Class() *ErrClass {
-	return e.class
-}
-
 // Code returns the numeric code of this error.
 // ID() will return textual error if there it is,
 // when you just want to get the purely numeric error
@@ -89,23 +93,7 @@ func (e *Error) Code() ErrCode {
 // The error code is a 3-tuple of abbreviated component name, error class and error code,
 // joined by a colon like {Component}:{ErrorClass}:{InnerErrorCode}.
 func (e *Error) RFCCode() RFCErrorCode {
-	ec := e.Class()
-	if ec == nil {
-		return RFCErrorCode(e.ID())
-	}
-	reg := ec.registry
-	// Maybe top-level errors.
-	if reg.Name == "" {
-		return RFCErrorCode(fmt.Sprintf("%s:%s",
-			ec.Description,
-			e.ID(),
-		))
-	}
-	return RFCErrorCode(fmt.Sprintf("%s:%s:%s",
-		reg.Name,
-		ec.Description,
-		e.ID(),
-	))
+	return RFCErrorCode(e.ID())
 }
 
 // ID returns the ID of this error.
@@ -127,16 +115,50 @@ func (e *Error) MessageTemplate() string {
 	return e.message
 }
 
+// NewError creates a standard error object.
+func NewError(code int, message string) *Error {
+	return &Error{
+		code:    ErrCode(code),
+		message: message,
+	}
+}
+
+// NewErrorWithText creates a standard error object using text error code.
+func NewErrorWithText(code string, message string) *Error {
+	return &Error{
+		codeText: ErrCodeText(code),
+		message:  message,
+	}
+}
+
+// SetWorkaround sets the workaround for standard error.
+func (e *Error) SetWorkaround(workaround string) *Error {
+	e.Workaround = workaround
+	return e
+}
+
+// SetWorkaround sets the description for standard error.
+func (e *Error) SetDescription(description string) *Error {
+	e.Description = description
+	return e
+}
+
+// SetErrCodeText sets the text error code for standard error.
+func (e *Error) SetErrCodeText(codeText string) *Error {
+	e.codeText = ErrCodeText(codeText)
+	return e
+}
+
 // Error implements error interface.
 func (e *Error) Error() string {
 	describe := e.codeText
 	if len(describe) == 0 {
 		describe = ErrCodeText(strconv.Itoa(int(e.code)))
 	}
-	return fmt.Sprintf("[%s] %s", e.RFCCode(), e.getMsg())
+	return fmt.Sprintf("[%s] %s", e.RFCCode(), e.GetMsg())
 }
 
-func (e *Error) getMsg() string {
+func (e *Error) GetMsg() string {
 	if len(e.args) > 0 {
 		return fmt.Sprintf(e.message, e.args...)
 	}
@@ -202,9 +224,8 @@ func (e *Error) Equal(err error) bool {
 	if !ok {
 		return false
 	}
-	classEquals := e.class.Equal(inErr.class)
 	idEquals := e.ID() == inErr.ID()
-	return classEquals && idEquals
+	return idEquals
 }
 
 // NotEqual checks if err is not equal to e.
@@ -244,7 +265,6 @@ type jsonError struct {
 	Error       string       `json:"message"`
 	Description string       `json:"description,omitempty"`
 	Workaround  string       `json:"workaround,omitempty"`
-	Class       ErrClassID   `json:"classID"`
 	File        string       `json:"file"`
 	Line        int          `json:"line"`
 }
@@ -256,11 +276,10 @@ type jsonError struct {
 // This function is reserved for compatibility.
 func (e *Error) MarshalJSON() ([]byte, error) {
 	return json.Marshal(&jsonError{
-		Error:       e.getMsg(),
+		Error:       e.GetMsg(),
 		Description: e.Description,
 		Workaround:  e.Workaround,
 		RFCCode:     e.RFCCode(),
-		Class:       e.class.ID,
 		Line:        e.line,
 		File:        e.file,
 	})
@@ -278,9 +297,7 @@ func (e *Error) UnmarshalJSON(data []byte) error {
 		return Trace(err)
 	}
 	codes := strings.Split(string(err.RFCCode), ":")
-	regName := codes[0]
-	className := codes[1]
-	innerCode := codes[2]
+	innerCode := codes[0]
 	if i, errAtoi := strconv.Atoi(innerCode); errAtoi == nil {
 		e.code = ErrCode(i)
 	} else {
@@ -289,11 +306,6 @@ func (e *Error) UnmarshalJSON(data []byte) error {
 	e.line = err.Line
 	e.file = err.File
 	e.message = err.Error
-	e.class = &ErrClass{
-		Description: className,
-		ID:          err.Class,
-		registry:    &Registry{Name: regName},
-	}
 	return nil
 }
 
diff --git a/terror_gen.go b/terror_gen.go
deleted file mode 100644
index 71d9a90..0000000
--- a/terror_gen.go
+++ /dev/null
@@ -1,79 +0,0 @@
-// Copyright 2020 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 errors
-
-import (
-	"fmt"
-	"github.com/pingcap/log"
-	"go.uber.org/zap"
-	"io"
-	"regexp"
-)
-
-const tomlTemplate = `[error.%s]
-error = '''%s'''
-description = '''%s'''
-workaround = '''%s'''
-`
-
-func (e *Error) exportTo(writer io.Writer) error {
-	desc := e.Description
-	if e.Description == "" {
-		log.Warn("error description missed", zap.String("error", string(e.RFCCode())))
-		desc = "N/A"
-	}
-	workaround := e.Workaround
-	if e.Workaround == "" {
-		log.Warn("error workaround missed", zap.String("error", string(e.RFCCode())))
-		workaround = "N/A"
-	}
-	_, err := fmt.Fprintf(writer, tomlTemplate, e.RFCCode(), replaceFlags(e.MessageTemplate()), desc, workaround)
-	return err
-}
-
-func (ec *ErrClass) exportTo(writer io.Writer) error {
-	for _, e := range ec.AllErrors() {
-		if err := e.exportTo(writer); err != nil {
-			return err
-		}
-	}
-	return nil
-}
-
-// ExportTo export the registry to a writer, as toml format from the RFC.
-func (r *Registry) ExportTo(writer io.Writer) error {
-	for _, ec := range r.errClasses {
-		if err := ec.exportTo(writer); err != nil {
-			return err
-		}
-	}
-	return nil
-}
-
-// CPP printf flag with minimal support for explicit argument indexes.
-// introductory % character: %
-// (optional) one or more flags that modify the behavior of the conversion: [+\-# 0]?
-// (optional) integer value or * that specifies minimum field width: ([0-9]+|(\[[0-9]+])?\*)?
-// (optional) . followed by integer number or *, or neither that specifies precision of the conversion: (\.([0-9]+|(\[[0-9]+])?\*))?
-//     the prepending (\[[0-9]+])? is for golang explicit argument indexes:
-//     The same notation before a '*' for a width or precision selects the argument index holding the value.
-// (optional) the notation [n] immediately before the verb indicates
-//     that the nth one-indexed argument is to be formatted instead: (\[[0-9]+])?
-// conversion format specifier: [vTtbcdoOqxXUeEfFgGsp]
-// %% shouldn't be replaced.
-var flagRe, _ = regexp.Compile(`%[+\-# 0]?([0-9]+|(\[[0-9]+])?\*)?(\.([0-9]+|(\[[0-9]+])?\*))?(\[[0-9]+])?[vTtbcdoOqxXUeEfFgGsp]`)
-
-func replaceFlags(origin string) string {
-	return string(flagRe.ReplaceAll([]byte(origin), []byte("{placeholder}")))
-}
diff --git a/terror_registry.go b/terror_registry.go
deleted file mode 100644
index 1268563..0000000
--- a/terror_registry.go
+++ /dev/null
@@ -1,176 +0,0 @@
-// Copyright 2020 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 errors
-
-import (
-	"fmt"
-)
-
-// Registry is a set of errors for a component.
-type Registry struct {
-	Name       string
-	errClasses map[ErrClassID]ErrClass
-}
-
-// ErrCode represents a specific error type in a error class.
-// Same error code can be used in different error classes.
-type ErrCode int
-
-// ErrCodeText is a textual error code that represents a specific error type in a error class.
-type ErrCodeText string
-
-// ErrClass represents a class of errors.
-// You can create error 'prototypes' of this class.
-type ErrClass struct {
-	ID          ErrClassID
-	Description string
-	errors      map[ErrorID]*Error
-	registry    *Registry
-}
-
-type ErrorID string
-type ErrClassID int
-type RFCErrorCode string
-
-// NewRegistry make a registry, where ErrClasses register to.
-// One component should create only one registry, named by the RFC.
-// For TiDB ecosystem components, when creating registry,
-// please use the component name to name the registry, see below example:
-//
-// TiKV: KV
-// PD: PD
-// DM: DM
-// BR: BR
-// TiCDC: CDC
-// Lightning: LN
-// TiFlash: FLASH
-// Dumpling: DP
-func NewRegistry(name string) *Registry {
-	return &Registry{Name: name, errClasses: map[ErrClassID]ErrClass{}}
-}
-
-// RegisterErrorClass registers new error class for terror.
-func (r *Registry) RegisterErrorClass(classCode int, desc string) ErrClass {
-	code := ErrClassID(classCode)
-	if _, exists := r.errClasses[code]; exists {
-		panic(fmt.Sprintf("duplicate register ClassCode %d - %s", code, desc))
-	}
-	errClass := ErrClass{
-		ID:          code,
-		Description: desc,
-		errors:      map[ErrorID]*Error{},
-		registry:    r,
-	}
-	r.errClasses[code] = errClass
-	return errClass
-}
-
-// String implements fmt.Stringer interface.
-func (ec *ErrClass) String() string {
-	return ec.Description
-}
-
-// Equal tests whether the other error is in this class.
-func (ec *ErrClass) Equal(other *ErrClass) bool {
-	if ec == nil || other == nil {
-		return ec == other
-	}
-	return ec.ID == other.ID
-}
-
-// EqualClass returns true if err is *Error with the same class.
-func (ec *ErrClass) EqualClass(err error) bool {
-	e := Cause(err)
-	if e == nil {
-		return false
-	}
-	if te, ok := e.(*Error); ok {
-		return te.class.Equal(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 defines an *Error with an error code and an error message.
-// Usually used to create base *Error.
-// This function is reserved for compatibility, if possible, use DefineError instead.
-func (ec *ErrClass) New(code ErrCode, message string) *Error {
-	return ec.DefineError().
-		NumericCode(code).
-		MessageTemplate(message).
-		Build()
-}
-
-// DefineError is the entrance of the define error DSL,
-// simple usage:
-// ```
-// ClassExecutor.DefineError().
-//	TextualCode("ExecutorAbsent").
-//	MessageTemplate("executor is taking vacation at %s").
-//	Build()
-// ```
-func (ec *ErrClass) DefineError() *Builder {
-	return &Builder{
-		err:   &Error{},
-		class: ec,
-	}
-}
-
-// RegisterError try to register an error to a class.
-// return true if success.
-func (ec *ErrClass) RegisterError(err *Error) bool {
-	if _, ok := ec.errors[err.ID()]; ok {
-		return false
-	}
-	err.class = ec
-	ec.errors[err.ID()] = err
-	return true
-}
-
-// AllErrors returns all errors of this ErrClass
-// Note this isn't thread-safe.
-// You shouldn't modify the returned slice without copying.
-func (ec *ErrClass) AllErrors() []*Error {
-	all := make([]*Error, 0, len(ec.errors))
-	for _, err := range ec.errors {
-		all = append(all, err)
-	}
-	return all
-}
-
-// AllErrorClasses returns all errClasses that has been registered.
-// Note this isn't thread-safe.
-func (r *Registry) AllErrorClasses() []ErrClass {
-	all := make([]ErrClass, 0, len(r.errClasses))
-	for _, errClass := range r.errClasses {
-		all = append(all, errClass)
-	}
-	return all
-}
-
-// Synthesize synthesizes an *Error in the air
-// it didn't register error into ErrClass
-// 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,
-	}
-}
diff --git a/terror_test/terror_test.go b/terror_test/terror_test.go
index 757c0f2..82b38e8 100644
--- a/terror_test/terror_test.go
+++ b/terror_test/terror_test.go
@@ -14,12 +14,10 @@
 package terror_test
 
 import (
-	"bytes"
 	"encoding/json"
 	"fmt"
 	"os"
 	"runtime"
-	"sort"
 	"strings"
 	"testing"
 	"time"
@@ -28,20 +26,6 @@ import (
 	"github.com/pingcap/errors"
 )
 
-// Error classes.
-// Those fields below are copied from the original version of terror,
-// so that we can reuse those test cases.
-var (
-	reg            = errors.NewRegistry("DB")
-	ClassExecutor  = reg.RegisterErrorClass(5, "executor")
-	ClassKV        = reg.RegisterErrorClass(8, "kv")
-	ClassOptimizer = reg.RegisterErrorClass(10, "planner")
-	ClassParser    = reg.RegisterErrorClass(11, "parser")
-	ClassServer    = reg.RegisterErrorClass(15, "server")
-	ClassTable     = reg.RegisterErrorClass(19, "table")
-	ClassMember    = reg.RegisterErrorClass(20, "member")
-)
-
 const (
 	CodeExecResultIsEmpty  errors.ErrCode = 3
 	CodeMissConnectionID   errors.ErrCode = 1
@@ -63,37 +47,8 @@ func (s *testTErrorSuite) TestErrCode(c *C) {
 	c.Assert(CodeResultUndetermined, Equals, errors.ErrCode(2))
 }
 
-func (s *testTErrorSuite) TestTError(c *C) {
-	c.Assert(ClassParser.String(), Not(Equals), "")
-	c.Assert(ClassOptimizer.String(), Not(Equals), "")
-	c.Assert(ClassKV.String(), Not(Equals), "")
-	c.Assert(ClassServer.String(), Not(Equals), "")
-
-	parserErr := ClassParser.New(errors.ErrCode(100), "error 100")
-	c.Assert(parserErr.Error(), Not(Equals), "")
-	c.Assert(ClassParser.EqualClass(parserErr), IsTrue)
-	c.Assert(ClassParser.NotEqualClass(parserErr), IsFalse)
-
-	c.Assert(ClassOptimizer.EqualClass(parserErr), IsFalse)
-	optimizerErr := ClassOptimizer.New(errors.ErrCode(2), "abc")
-	c.Assert(ClassOptimizer.EqualClass(errors.New("abc")), IsFalse)
-	c.Assert(ClassOptimizer.EqualClass(nil), IsFalse)
-	c.Assert(optimizerErr.Equal(optimizerErr.GenWithStack("def")), IsTrue)
-	c.Assert(optimizerErr.Equal(errors.Trace(optimizerErr.GenWithStack("def"))), IsTrue)
-	c.Assert(optimizerErr.Equal(nil), IsFalse)
-	c.Assert(optimizerErr.Equal(errors.New("abc")), IsFalse)
-
-	// Test case for FastGen.
-	c.Assert(optimizerErr.Equal(optimizerErr.FastGen("def")), IsTrue)
-	c.Assert(optimizerErr.Equal(optimizerErr.FastGen("def: %s", "def")), IsTrue)
-	kvErr := ClassKV.New(1062, "key already exist")
-	e := kvErr.FastGen("Duplicate entry '%d' for key 'PRIMARY'", 1)
-	c.Assert(e, NotNil)
-	c.Assert(e.Error(), Equals, "[DB:kv:1062] Duplicate entry '1' for key 'PRIMARY'")
-}
-
 func (s *testTErrorSuite) TestJson(c *C) {
-	prevTErr := ClassTable.New(CodeExecResultIsEmpty, "json test")
+	prevTErr := errors.NewError(int(CodeExecResultIsEmpty), "json test")
 	buf, err := json.Marshal(prevTErr)
 	c.Assert(err, IsNil)
 	var curTErr errors.Error
@@ -103,11 +58,8 @@ func (s *testTErrorSuite) TestJson(c *C) {
 	c.Assert(isEqual, IsTrue)
 }
 
-var predefinedErr = ClassExecutor.New(errors.ErrCode(123), "predefiend error")
-var predefinedTextualErr = ClassExecutor.DefineError().
-	TextualCode("ExecutorAbsent").
-	MessageTemplate("executor is taking vacation at %s").
-	Build()
+var predefinedErr = errors.NewError(123, "predefiend error")
+var predefinedTextualErr = errors.NewErrorWithText("executor:ExecutorAbsent", "executor is taking vacation at %s")
 
 func example() error {
 	err := call()
@@ -169,160 +121,24 @@ func (s *testTErrorSuite) TestErrorEqual(c *C) {
 
 	c.Assert(errors.ErrorEqual(nil, nil), IsTrue)
 	c.Assert(errors.ErrorNotEqual(e1, e6), IsTrue)
-	code1 := errors.ErrCode(9001)
-	code2 := errors.ErrCode(9002)
-	te1 := ClassParser.Synthesize(code1, "abc")
-	te3 := ClassKV.New(code1, "abc")
-	te4 := ClassKV.New(code2, "abc")
-	c.Assert(errors.ErrorEqual(te1, te3), IsFalse)
-	c.Assert(errors.ErrorEqual(te3, te4), IsFalse)
 }
 
 func (s *testTErrorSuite) TestNewError(c *C) {
 	today := time.Now().Weekday().String()
 	err := predefinedTextualErr.GenWithStackByArgs(today)
 	c.Assert(err, NotNil)
-	c.Assert(err.Error(), Equals, "[DB:executor:ExecutorAbsent] executor is taking vacation at "+today)
-}
-
-func (s *testTErrorSuite) TestAllErrClasses(c *C) {
-	items := []errors.ErrClass{
-		ClassExecutor, ClassKV, ClassOptimizer, ClassParser, ClassServer, ClassTable,
-	}
-	registered := reg.AllErrorClasses()
-
-	// sort it to align them.
-	sort.Slice(items, func(i, j int) bool {
-		return items[i].ID < items[j].ID
-	})
-	sort.Slice(registered, func(i, j int) bool {
-		return registered[i].ID < registered[j].ID
-	})
-
-	for i := range items {
-		c.Assert(items[i].ID, Equals, registered[i].ID)
-	}
-}
-
-func (s *testTErrorSuite) TestErrorExists(c *C) {
-	origin := ClassParser.DefineError().
-		TextualCode("EverythingAlright").
-		MessageTemplate("that was a joke, hoo!").
-		Build()
-
-	c.Assert(func() {
-		_ = ClassParser.DefineError().
-			TextualCode("EverythingAlright").
-			MessageTemplate("that was another joke, hoo!").
-			Build()
-	}, Panics, "replicated error prototype created")
-
-	// difference at either code or text should be different error
-	changeCode := ClassParser.DefineError().
-		NumericCode(4399).
-		MessageTemplate("that was a joke, hoo!").
-		Build()
-	changeText := ClassParser.DefineError().
-		TextualCode("EverythingBad").
-		MessageTemplate("that was not a joke, folks!").
-		Build()
-	containsErr := func(e error) bool {
-		for _, err := range ClassParser.AllErrors() {
-			if err.Equal(e) {
-				return true
-			}
-		}
-		return false
-	}
-	c.Assert(containsErr(origin), IsTrue)
-	c.Assert(containsErr(changeCode), IsTrue)
-	c.Assert(containsErr(changeText), IsTrue)
+	c.Assert(err.Error(), Equals, "[executor:ExecutorAbsent] executor is taking vacation at "+today)
 }
 
 func (s *testTErrorSuite) TestRFCCode(c *C) {
-	reg := errors.NewRegistry("TEST")
-	errc1 := reg.RegisterErrorClass(1, "TestErr1")
-	errc2 := reg.RegisterErrorClass(2, "TestErr2")
-	c1err1 := errc1.DefineError().
-		TextualCode("Err1").
-		MessageTemplate("nothing").
-		Build()
-	c2err2 := errc2.DefineError().
-		TextualCode("Err2").
-		MessageTemplate("nothing").
-		Build()
-	c.Assert(c1err1.RFCCode(), Equals, errors.RFCErrorCode("TEST:TestErr1:Err1"))
-	c.Assert(c2err2.RFCCode(), Equals, errors.RFCErrorCode("TEST:TestErr2:Err2"))
-	blankReg := errors.NewRegistry("")
-	errb := blankReg.RegisterErrorClass(1, "Blank")
-	berr := errb.DefineError().
-		TextualCode("B1").
-		MessageTemplate("nothing").
-		Workaround(`Do nothing`).
-		Build()
+	c1err1 := errors.NewErrorWithText("TestErr1:Err1", "nothing")
+	c2err2 := errors.NewErrorWithText("TestErr2:Err2", "nothing")
+	c.Assert(c1err1.RFCCode(), Equals, errors.RFCErrorCode("TestErr1:Err1"))
+	c.Assert(c2err2.RFCCode(), Equals, errors.RFCErrorCode("TestErr2:Err2"))
+	berr := errors.NewErrorWithText("Blank:B1", "nothing").SetWorkaround(`Do nothing`)
 	c.Assert(berr.RFCCode(), Equals, errors.RFCErrorCode("Blank:B1"))
 }
 
-const (
-	somewhatErrorTOML = `[error.KV:Somewhat:Foo]
-error = '''some {placeholder} thing happened, and some {placeholder} goes verbose. I'm {placeholder} percent confusing...
-Maybe only {placeholder} peaces of placeholders can save me... Oh my {placeholder}.{placeholder}!'''
-description = '''N/A'''
-workaround = '''N/A'''
-`
-	err8005TOML = `[error.KV:2PC:8005]
-error = '''Write Conflict, txnStartTS is stale'''
-description = '''A certain Raft Group is not available, such as the number of replicas is not enough.
-This error usually occurs when the TiKV server is busy or the TiKV node is down.'''
-` + "workaround = '''Check whether `tidb_disable_txn_auto_retry` is set to `on`. If so, set it to `off`; " +
-		"if it is already `off`, increase the value of `tidb_retry_limit` until the error no longer occurs.'''\n"
-	errUnavailableTOML = `[error.KV:Region:Unavailable]
-error = '''Region is unavailable'''
-description = '''A certain Raft Group is not available, such as the number of replicas is not enough.
-This error usually occurs when the TiKV server is busy or the TiKV node is down.'''
-workaround = '''Check the status, monitoring data and log of the TiKV server.'''
-`
-)
-
-func (*testTErrorSuite) TestExport(c *C) {
-	RegKV := errors.NewRegistry("KV")
-	Class2PC := RegKV.RegisterErrorClass(1, "2PC")
-	_ = Class2PC.DefineError().
-		NumericCode(8005).
-		Description("A certain Raft Group is not available, such as the number of replicas is not enough.\n" +
-			"This error usually occurs when the TiKV server is busy or the TiKV node is down.").
-		Workaround("Check whether `tidb_disable_txn_auto_retry` is set to `on`. If so, set it to `off`; " +
-			"if it is already `off`, increase the value of `tidb_retry_limit` until the error no longer occurs.").
-		MessageTemplate("Write Conflict, txnStartTS is stale").
-		Build()
-
-	ClassRegion := RegKV.RegisterErrorClass(2, "Region")
-	_ = ClassRegion.DefineError().
-		TextualCode("Unavailable").
-		Description("A certain Raft Group is not available, such as the number of replicas is not enough.\n" +
-			"This error usually occurs when the TiKV server is busy or the TiKV node is down.").
-		Workaround("Check the status, monitoring data and log of the TiKV server.").
-		MessageTemplate("Region is unavailable").
-		Build()
-
-	ClassSomewhat := RegKV.RegisterErrorClass(3, "Somewhat")
-	_ = ClassSomewhat.DefineError().
-		TextualCode("Foo").
-		MessageTemplate("some %.6s thing happened, and some %#v goes verbose. I'm %6.3f percent confusing...\n" +
-			"Maybe only %[3]*.[2]*[1]f peaces of placeholders can save me... Oh my %s.%d!").
-		Build()
-
-	result := bytes.NewBuffer([]byte{})
-	err := RegKV.ExportTo(result)
-	c.Assert(err, IsNil)
-	resultStr := result.String()
-	fmt.Println("Result: ")
-	fmt.Print(resultStr)
-	c.Assert(strings.Contains(resultStr, somewhatErrorTOML), IsTrue)
-	c.Assert(strings.Contains(resultStr, err8005TOML), IsTrue)
-	c.Assert(strings.Contains(resultStr, errUnavailableTOML), IsTrue)
-}
-
 func (*testTErrorSuite) TestLineAndFile(c *C) {
 	err := predefinedTextualErr.GenWithStackByArgs("everyday")
 	_, f, l, _ := runtime.Caller(0)
@@ -343,8 +159,8 @@ func (*testTErrorSuite) TestLineAndFile(c *C) {
 
 func (*testTErrorSuite) TestWarpAndField(c *C) {
 	causeErr := errors.New("load from etcd meet error")
-	ErrGetLeader := ClassMember.DefineError().TextualCode("ErrGetLeader").MessageTemplate("fail to get leader").Build()
-	errWithWarpedCause := ErrGetLeader.WarpCauseError(causeErr)
-	c.Assert(errWithWarpedCause.FastGenWithCause().Error(), Equals, "[DB:member:ErrGetLeader] load from etcd meet error")
-	c.Assert(fmt.Sprintf("%v", errWithWarpedCause.FastGenWithCause()), Equals, "[DB:member:ErrGetLeader] fail to get leader")
+	ErrGetLeader := errors.NewErrorWithText("member:ErrGetLeader", "fail to get leader")
+	errWithWarpedCause := ErrGetLeader.Wrap(causeErr)
+	c.Assert(errWithWarpedCause.FastGenWithCause().Error(), Equals, "[member:ErrGetLeader] load from etcd meet error")
+	c.Assert(fmt.Sprintf("%v", errWithWarpedCause.FastGenWithCause()), Equals, "[member:ErrGetLeader] fail to get leader")
 }

From 7b34aeb4e57cb3e017604b731916623c8235c709 Mon Sep 17 00:00:00 2001
From: imtbkcat <songlingyu@pingcap.com>
Date: Wed, 12 Aug 2020 17:13:27 +0800
Subject: [PATCH 7/8] add method to generate error instance

---
 terror_error.go | 67 ++++++++++++++++++++++++++-----------------------
 1 file changed, 35 insertions(+), 32 deletions(-)

diff --git a/terror_error.go b/terror_error.go
index 4f7233f..b276475 100644
--- a/terror_error.go
+++ b/terror_error.go
@@ -67,12 +67,12 @@ type Error struct {
 	message string
 	// The workaround field: how to work around this error.
 	// It's used to teach the users how to solve the error if occurring in the real environment.
-	Workaround string
+	workaround string
 	// Description is the expanded detail of why this error occurred.
 	// This could be written by developer at a static env,
 	// and the more detail this field explaining the better,
 	// even some guess of the cause could be included.
-	Description string
+	description string
 	// Cause is used to warp some third party error.
 	cause error
 	args  []interface{}
@@ -115,34 +115,6 @@ func (e *Error) MessageTemplate() string {
 	return e.message
 }
 
-// NewError creates a standard error object.
-func NewError(code int, message string) *Error {
-	return &Error{
-		code:    ErrCode(code),
-		message: message,
-	}
-}
-
-// NewErrorWithText creates a standard error object using text error code.
-func NewErrorWithText(code string, message string) *Error {
-	return &Error{
-		codeText: ErrCodeText(code),
-		message:  message,
-	}
-}
-
-// SetWorkaround sets the workaround for standard error.
-func (e *Error) SetWorkaround(workaround string) *Error {
-	e.Workaround = workaround
-	return e
-}
-
-// SetWorkaround sets the description for standard error.
-func (e *Error) SetDescription(description string) *Error {
-	e.Description = description
-	return e
-}
-
 // SetErrCodeText sets the text error code for standard error.
 func (e *Error) SetErrCodeText(codeText string) *Error {
 	e.codeText = ErrCodeText(codeText)
@@ -277,8 +249,8 @@ type jsonError struct {
 func (e *Error) MarshalJSON() ([]byte, error) {
 	return json.Marshal(&jsonError{
 		Error:       e.GetMsg(),
-		Description: e.Description,
-		Workaround:  e.Workaround,
+		Description: e.description,
+		Workaround:  e.workaround,
 		RFCCode:     e.RFCCode(),
 		Line:        e.line,
 		File:        e.file,
@@ -337,6 +309,37 @@ func (e *Error) GenWithStackByCause(args ...interface{}) error {
 	return AddStack(&err)
 }
 
+type NormalizeOption func(*Error)
+
+func Description(desc string) NormalizeOption {
+	return func(e *Error) {
+		e.description = desc
+	}
+}
+
+func Workaround(wr string) NormalizeOption {
+	return func(e *Error) {
+		e.workaround = wr
+	}
+}
+
+func RFCCodeText(codeText string) NormalizeOption {
+	return func(e *Error) {
+		e.codeText = ErrCodeText(codeText)
+	}
+}
+
+func Normalize(code int, message string, opts ...NormalizeOption) *Error {
+	e := &Error{
+		code:    ErrCode(code),
+		message: message,
+	}
+	for _, opt := range opts {
+		opt(e)
+	}
+	return e
+}
+
 func CauseError(err *Error) zap.Field {
 	return zap.Field{Key: "error", Type: zapcore.ErrorType, Interface: err.FastGenWithCause()}
 }

From 57ec461934ff91ea785a8ccdb48476fcae1a854a Mon Sep 17 00:00:00 2001
From: imtbkcat <songlingyu@pingcap.com>
Date: Wed, 12 Aug 2020 17:38:36 +0800
Subject: [PATCH 8/8] add comment and fix test

---
 terror_error.go            | 16 ++++++++++++++--
 terror_test/terror_test.go | 16 ++++++++--------
 2 files changed, 22 insertions(+), 10 deletions(-)

diff --git a/terror_error.go b/terror_error.go
index b276475..cbd08b3 100644
--- a/terror_error.go
+++ b/terror_error.go
@@ -311,27 +311,37 @@ func (e *Error) GenWithStackByCause(args ...interface{}) error {
 
 type NormalizeOption func(*Error)
 
+// Description returns a NormalizeOption to set description.
 func Description(desc string) NormalizeOption {
 	return func(e *Error) {
 		e.description = desc
 	}
 }
 
+// Workaround returns a NormalizeOption to set workaround.
 func Workaround(wr string) NormalizeOption {
 	return func(e *Error) {
 		e.workaround = wr
 	}
 }
 
+// RFCCodeText returns a NormalizeOption to set RFC error code.
 func RFCCodeText(codeText string) NormalizeOption {
 	return func(e *Error) {
 		e.codeText = ErrCodeText(codeText)
 	}
 }
 
-func Normalize(code int, message string, opts ...NormalizeOption) *Error {
+// MySQLErrorCode returns a NormalizeOption to set error code.
+func MySQLErrorCode(code int) NormalizeOption {
+	return func(e *Error) {
+		e.code = ErrCode(code)
+	}
+}
+
+// Normalize creates a new Error object.
+func Normalize(message string, opts ...NormalizeOption) *Error {
 	e := &Error{
-		code:    ErrCode(code),
 		message: message,
 	}
 	for _, opt := range opts {
@@ -340,10 +350,12 @@ func Normalize(code int, message string, opts ...NormalizeOption) *Error {
 	return e
 }
 
+// CauseError returns zap.Field contains cause error.
 func CauseError(err *Error) zap.Field {
 	return zap.Field{Key: "error", Type: zapcore.ErrorType, Interface: err.FastGenWithCause()}
 }
 
+// CauseError returns zap.Field contains error.
 func DetailError(err *Error) zap.Field {
 	return zap.Field{Key: "error", Type: zapcore.ErrorType, Interface: err.FastGenByArgs()}
 }
diff --git a/terror_test/terror_test.go b/terror_test/terror_test.go
index 82b38e8..3185cba 100644
--- a/terror_test/terror_test.go
+++ b/terror_test/terror_test.go
@@ -48,7 +48,7 @@ func (s *testTErrorSuite) TestErrCode(c *C) {
 }
 
 func (s *testTErrorSuite) TestJson(c *C) {
-	prevTErr := errors.NewError(int(CodeExecResultIsEmpty), "json test")
+	prevTErr := errors.Normalize("json test", errors.MySQLErrorCode(int(CodeExecResultIsEmpty)))
 	buf, err := json.Marshal(prevTErr)
 	c.Assert(err, IsNil)
 	var curTErr errors.Error
@@ -58,8 +58,8 @@ func (s *testTErrorSuite) TestJson(c *C) {
 	c.Assert(isEqual, IsTrue)
 }
 
-var predefinedErr = errors.NewError(123, "predefiend error")
-var predefinedTextualErr = errors.NewErrorWithText("executor:ExecutorAbsent", "executor is taking vacation at %s")
+var predefinedErr = errors.Normalize("predefiend error", errors.MySQLErrorCode(123))
+var predefinedTextualErr = errors.Normalize("executor is taking vacation at %s", errors.RFCCodeText("executor:ExecutorAbsent"))
 
 func example() error {
 	err := call()
@@ -131,11 +131,11 @@ func (s *testTErrorSuite) TestNewError(c *C) {
 }
 
 func (s *testTErrorSuite) TestRFCCode(c *C) {
-	c1err1 := errors.NewErrorWithText("TestErr1:Err1", "nothing")
-	c2err2 := errors.NewErrorWithText("TestErr2:Err2", "nothing")
+	c1err1 := errors.Normalize("nothing", errors.RFCCodeText("TestErr1:Err1"))
+	c2err2 := errors.Normalize("nothing", errors.RFCCodeText("TestErr2:Err2"))
 	c.Assert(c1err1.RFCCode(), Equals, errors.RFCErrorCode("TestErr1:Err1"))
 	c.Assert(c2err2.RFCCode(), Equals, errors.RFCErrorCode("TestErr2:Err2"))
-	berr := errors.NewErrorWithText("Blank:B1", "nothing").SetWorkaround(`Do nothing`)
+	berr := errors.Normalize("nothing", errors.RFCCodeText("Blank:B1"), errors.Workaround(`Do nothing`))
 	c.Assert(berr.RFCCode(), Equals, errors.RFCErrorCode("Blank:B1"))
 }
 
@@ -159,8 +159,8 @@ func (*testTErrorSuite) TestLineAndFile(c *C) {
 
 func (*testTErrorSuite) TestWarpAndField(c *C) {
 	causeErr := errors.New("load from etcd meet error")
-	ErrGetLeader := errors.NewErrorWithText("member:ErrGetLeader", "fail to get leader")
+	ErrGetLeader := errors.Normalize("fail to get leader", errors.RFCCodeText("member:ErrGetLeader"))
 	errWithWarpedCause := ErrGetLeader.Wrap(causeErr)
 	c.Assert(errWithWarpedCause.FastGenWithCause().Error(), Equals, "[member:ErrGetLeader] load from etcd meet error")
-	c.Assert(fmt.Sprintf("%v", errWithWarpedCause.FastGenWithCause()), Equals, "[member:ErrGetLeader] fail to get leader")
+	c.Assert(fmt.Sprintf("%v", errWithWarpedCause.FastGenWithCause()), Equals, "[member:ErrGetLeader] load from etcd meet error")
 }