forked from vitessio/vitess
-
Notifications
You must be signed in to change notification settings - Fork 1
/
sql_error.go
162 lines (140 loc) · 4.15 KB
/
sql_error.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
/*
Copyright 2017 Google 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 mysql
import (
"bytes"
"fmt"
"regexp"
"strconv"
"vitess.io/vitess/go/vt/sqlparser"
"vitess.io/vitess/go/vt/vterrors"
vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc"
)
// SQLError is the error structure returned from calling a db library function
type SQLError struct {
Num int
State string
Message string
Query string
}
// NewSQLError creates a new SQLError.
// If sqlState is left empty, it will default to "HY000" (general error).
func NewSQLError(number int, sqlState string, format string, args ...interface{}) *SQLError {
if sqlState == "" {
sqlState = SSUnknownSQLState
}
return &SQLError{
Num: number,
State: sqlState,
Message: fmt.Sprintf(format, args...),
}
}
// Error implements the error interface
func (se *SQLError) Error() string {
buf := &bytes.Buffer{}
buf.WriteString(se.Message)
// Add MySQL errno and SQLSTATE in a format that we can later parse.
// There's no avoiding string parsing because all errors
// are converted to strings anyway at RPC boundaries.
// See NewSQLErrorFromError.
fmt.Fprintf(buf, " (errno %v) (sqlstate %v)", se.Num, se.State)
if se.Query != "" {
fmt.Fprintf(buf, " during query: %s", sqlparser.TruncateForLog(se.Query))
}
return buf.String()
}
// Number returns the internal MySQL error code.
func (se *SQLError) Number() int {
return se.Num
}
// SQLState returns the SQLSTATE value.
func (se *SQLError) SQLState() string {
return se.State
}
var errExtract = regexp.MustCompile(`.*\(errno ([0-9]*)\) \(sqlstate ([0-9a-zA-Z]{5})\).*`)
// NewSQLErrorFromError returns a *SQLError from the provided error.
// If it's not the right type, it still tries to get it from a regexp.
func NewSQLErrorFromError(err error) error {
if err == nil {
return nil
}
if serr, ok := err.(*SQLError); ok {
return serr
}
msg := err.Error()
match := errExtract.FindStringSubmatch(msg)
if len(match) < 2 {
// Map vitess error codes into the mysql equivalent
code := vterrors.Code(err)
num := ERUnknownError
switch code {
case vtrpcpb.Code_CANCELED:
num = ERQueryInterrupted
case vtrpcpb.Code_UNKNOWN:
num = ERUnknownError
case vtrpcpb.Code_INVALID_ARGUMENT:
// TODO/demmer there are several more appropriate mysql error
// codes for the various invalid argument cases.
// it would be better to change the call sites to use
// the mysql style "(errno X) (sqlstate Y)" format rather than
// trying to add vitess error codes for all these cases
num = ERUnknownError
case vtrpcpb.Code_DEADLINE_EXCEEDED:
num = ERQueryInterrupted
case vtrpcpb.Code_NOT_FOUND:
num = ERUnknownError
case vtrpcpb.Code_ALREADY_EXISTS:
num = ERUnknownError
case vtrpcpb.Code_PERMISSION_DENIED:
num = ERAccessDeniedError
case vtrpcpb.Code_UNAUTHENTICATED:
num = ERAccessDeniedError
case vtrpcpb.Code_RESOURCE_EXHAUSTED:
num = ERTooManyUserConnections
case vtrpcpb.Code_FAILED_PRECONDITION:
num = ERUnknownError
case vtrpcpb.Code_ABORTED:
num = ERQueryInterrupted
case vtrpcpb.Code_OUT_OF_RANGE:
num = ERUnknownError
case vtrpcpb.Code_UNIMPLEMENTED:
num = ERNotSupportedYet
case vtrpcpb.Code_INTERNAL:
num = ERUnknownError
case vtrpcpb.Code_UNAVAILABLE:
num = ERUnknownError
case vtrpcpb.Code_DATA_LOSS:
num = ERUnknownError
}
// Not found, build a generic SQLError.
return &SQLError{
Num: num,
State: SSUnknownSQLState,
Message: msg,
}
}
num, err := strconv.Atoi(match[1])
if err != nil {
return &SQLError{
Num: ERUnknownError,
State: SSUnknownSQLState,
Message: msg,
}
}
serr := &SQLError{
Num: num,
State: match[2],
Message: msg,
}
return serr
}