forked from pingcap/tidb
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcontext.go
179 lines (153 loc) · 5.64 KB
/
context.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
// Copyright 2023 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,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package errctx
import (
"github.com/pingcap/errors"
"github.com/pingcap/tidb/pkg/errno"
contextutil "github.com/pingcap/tidb/pkg/util/context"
"github.com/pingcap/tidb/pkg/util/intest"
)
// Level defines the behavior for each error
type Level uint8
const (
// LevelError means the error will be returned
LevelError Level = iota
// LevelWarn means it will be regarded as a warning
LevelWarn
// LevelIgnore means the error will be ignored
LevelIgnore
)
// Context defines how to handle an error
type Context struct {
levelMap [errGroupCount]Level
warnHandler contextutil.WarnHandler
}
// WithStrictErrGroupLevel makes the context to return the error directly for any kinds of errors.
func (ctx *Context) WithStrictErrGroupLevel() Context {
newCtx := Context{
warnHandler: ctx.warnHandler,
}
return newCtx
}
// WithErrGroupLevel sets a `Level` for an `ErrGroup`
func (ctx *Context) WithErrGroupLevel(eg ErrGroup, l Level) Context {
newCtx := Context{
levelMap: ctx.levelMap,
warnHandler: ctx.warnHandler,
}
newCtx.levelMap[eg] = l
return newCtx
}
// appendWarning appends the error to warning. If the inner `warnHandler` is nil, do nothing.
func (ctx *Context) appendWarning(err error) {
intest.Assert(ctx.warnHandler != nil)
if w := ctx.warnHandler; w != nil {
// warnHandler should always not be nil, check fn != nil here to just make code safe.
w.AppendWarning(err)
}
}
// HandleError handles the error according to the contextutil. See the comment of `HandleErrorWithAlias` for detailed logic.
//
// It also allows using `errors.ErrorGroup`, in this case, it'll handle each error in order, and return the first error
// it founds.
func (ctx *Context) HandleError(err error) error {
// The function of handling `errors.ErrorGroup` is placed in `HandleError` but not in `HandleErrorWithAlias`, because
// it's hard to give a proper error and warn alias for an error group.
if errs, ok := err.(errors.ErrorGroup); ok {
for _, singleErr := range errs.Errors() {
singleErr = ctx.HandleError(singleErr)
// If the one error is found, just return it.
// TODO: consider whether it's more appropriate to continue to handle other errors. For example, other errors
// may need to append warnings. The current behavior is same with TiDB original behavior before using
// `errctx` to handle multiple errors.
if singleErr != nil {
return singleErr
}
}
return nil
}
return ctx.HandleErrorWithAlias(err, err, err)
}
// HandleErrorWithAlias handles the error according to the contextutil.
// 1. If the `internalErr` is not `"pingcap/errors".Error`, or the error code is not defined in the `errGroupMap`, or the error
// level is set to `LevelError`(0), the `err` will be returned directly.
// 2. If the error level is set to `LevelWarn`, the `warnErr` will be appended as a warning.
// 3. If the error level is set to `LevelIgnore`, this function will return a `nil`.
//
// In most cases, these three should be the same. If there are many different kinds of error internally, but they are expected
// to give the same error to users, the `err` can be different form `internalErr`. Also, if the warning is expected to be
// different from the initial error, you can also use the `warnErr` argument.
//
// TODO: is it good to give an error code for internal only errors? Or should we use another way to distinguish different
// group of errors?
// TODO: both `types.Context` and `errctx.Context` can handle truncate error now. Refractor them.
func (ctx *Context) HandleErrorWithAlias(internalErr error, err error, warnErr error) error {
if internalErr == nil {
return nil
}
internalErr = errors.Cause(internalErr)
e, ok := internalErr.(*errors.Error)
if !ok {
return err
}
eg, ok := errGroupMap[e.Code()]
if !ok {
return err
}
switch ctx.levelMap[eg] {
case LevelError:
return err
case LevelWarn:
ctx.appendWarning(warnErr)
case LevelIgnore:
}
return nil
}
// NewContext creates an error context to handle the errors and warnings
func NewContext(handler contextutil.WarnHandler) Context {
intest.Assert(handler != nil)
return Context{
warnHandler: handler,
}
}
// StrictNoWarningContext returns all errors directly, and ignore all errors
var StrictNoWarningContext = NewContext(contextutil.IgnoreWarn)
var errGroupMap = make(map[errors.ErrCode]ErrGroup)
// ErrGroup groups the error according to the behavior of handling errors
type ErrGroup int
const (
// ErrGroupTruncate is the group of truncated errors
ErrGroupTruncate ErrGroup = iota
// ErrGroupOverflow is the group of overflow errors
ErrGroupOverflow
// errGroupCount is the count of all `ErrGroup`. Please leave it at the end of the list.
errGroupCount
)
func init() {
truncateErrCodes := []errors.ErrCode{
errno.ErrTruncatedWrongValue,
errno.ErrDataTooLong,
errno.ErrTruncatedWrongValueForField,
errno.ErrWarnDataOutOfRange,
errno.ErrDataOutOfRange,
errno.ErrBadNumber,
errno.ErrWrongValueForType,
errno.ErrDatetimeFunctionOverflow,
errno.WarnDataTruncated,
errno.ErrIncorrectDatetimeValue,
}
for _, errCode := range truncateErrCodes {
errGroupMap[errCode] = ErrGroupTruncate
}
}