github.com/vedadiyan/sqlparser@v1.0.0/pkg/vterrors/vterrors.go (about) 1 /* 2 Copyright 2019 The Vitess Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 // Package vterrors provides simple error handling primitives for Vitess 18 // 19 // In all Vitess code, errors should be propagated using vterrors.Wrapf() 20 // and not fmt.Errorf(). This makes sure that stacktraces are kept and 21 // propagated correctly. 22 // 23 // # New errors should be created using vterrors.New or vterrors.Errorf 24 // 25 // Vitess uses canonical error codes for error reporting. This is based 26 // on years of industry experience with error reporting. This idea is 27 // that errors should be classified into a small set of errors (10 or so) 28 // with very specific meaning. Each error has a code, and a message. When 29 // errors are passed around (even through RPCs), the code is 30 // propagated. To handle errors, only the code should be looked at (and 31 // not string-matching on the error message). 32 // 33 // Error codes are defined in /proto/vtrpc.proto. Along with an 34 // RPCError message that can be used to transmit errors through RPCs, in 35 // the message payloads. These codes match the names and numbers defined 36 // by gRPC. 37 // 38 // A standardized error implementation that allows you to build an error 39 // with an associated canonical code is also defined. 40 // While sending an error through gRPC, these codes are transmitted 41 // using gRPC's error propagation mechanism and decoded back to 42 // the original code on the other end. 43 // 44 // # Retrieving the cause of an error 45 // 46 // Using vterrors.Wrap constructs a stack of errors, adding context to the 47 // preceding error, instead of simply building up a string. 48 // Depending on the nature of the error it may be necessary to reverse the 49 // operation of errors.Wrap to retrieve the original error for inspection. 50 // Any error value which implements this interface 51 // 52 // type causer interface { 53 // Cause() error 54 // } 55 // 56 // can be inspected by vterrors.Cause and vterrors.RootCause. 57 // 58 // - vterrors.Cause will find the immediate cause if one is available, or nil 59 // if the error is not a `causer` or if no cause is available. 60 // 61 // - vterrors.RootCause will recursively retrieve 62 // the topmost error which does not implement causer, which is assumed to be 63 // the original cause. For example: 64 // 65 // switch err := errors.RootCause(err).(type) { 66 // case *MyError: 67 // // handle specifically 68 // default: 69 // // unknown error 70 // } 71 // 72 // causer interface is not exported by this package, but is considered a part 73 // of stable public API. 74 // 75 // # Formatted printing of errors 76 // 77 // All error values returned from this package implement fmt.Formatter and can 78 // be formatted by the fmt package. The following verbs are supported 79 // 80 // %s print the error. If the error has a Cause it will be 81 // printed recursively 82 // %v extended format. Each Frame of the error's StackTrace will 83 // be printed in detail. 84 // 85 // Most but not all of the code in this file was originally copied from 86 // https://github.com/pkg/errors/blob/v0.8.0/errors.go 87 package vterrors 88 89 import ( 90 "context" 91 "fmt" 92 "io" 93 94 "github.com/spf13/pflag" 95 96 vtrpcpb "github.com/vedadiyan/sqlparser/pkg/vtrpc" 97 ) 98 99 // logErrStacks controls whether or not printing errors includes the 100 // embedded stack trace in the output. 101 var logErrStacks bool 102 103 // RegisterFlags registers the command-line options that control vterror 104 // behavior on the provided FlagSet. 105 func RegisterFlags(fs *pflag.FlagSet) { 106 fs.BoolVar(&logErrStacks, "log_err_stacks", false, "log stack traces for errors") 107 } 108 109 // New returns an error with the supplied message. 110 // New also records the stack trace at the point it was called. 111 func New(code vtrpcpb.Code, message string) error { 112 return &fundamental{ 113 msg: message, 114 code: code, 115 stack: callers(), 116 } 117 } 118 119 // Errorf formats according to a format specifier and returns the string 120 // as a value that satisfies error. 121 // Errorf also records the stack trace at the point it was called. 122 // Use this for Vitess-specific errors that don't have a MySQL counterpart 123 func Errorf(code vtrpcpb.Code, format string, args ...any) error { 124 return &fundamental{ 125 msg: fmt.Sprintf(format, args...), 126 code: code, 127 stack: callers(), 128 } 129 } 130 131 // NewErrorf formats according to a format specifier and returns the string 132 // as a value that satisfies error. 133 // NewErrorf also records the stack trace at the point it was called. 134 // Use this for errors in Vitess that we eventually want to mimic as a MySQL error 135 func NewErrorf(code vtrpcpb.Code, state State, format string, args ...any) error { 136 msg := format 137 if len(args) != 0 { 138 msg = fmt.Sprintf(format, args...) 139 } 140 return &fundamental{ 141 msg: msg, 142 code: code, 143 state: state, 144 stack: callers(), 145 } 146 } 147 148 // fundamental is an error that has a message and a stack, but no caller. 149 type fundamental struct { 150 msg string 151 code vtrpcpb.Code 152 state State 153 *stack 154 } 155 156 func (f *fundamental) Error() string { return f.msg } 157 158 func (f *fundamental) Format(s fmt.State, verb rune) { 159 switch verb { 160 case 'v': 161 panicIfError(io.WriteString(s, "Code: "+f.code.String()+"\n")) 162 panicIfError(io.WriteString(s, f.msg+"\n")) 163 if logErrStacks { 164 f.stack.Format(s, verb) 165 } 166 return 167 case 's': 168 panicIfError(io.WriteString(s, f.msg)) 169 case 'q': 170 panicIfError(fmt.Fprintf(s, "%q", f.msg)) 171 } 172 } 173 174 // Code returns the error code if it's a vtError. 175 // If err is nil, it returns ok. 176 func Code(err error) vtrpcpb.Code { 177 if err == nil { 178 return vtrpcpb.Code_OK 179 } 180 if err, ok := err.(ErrorWithCode); ok { 181 return err.ErrorCode() 182 } 183 184 cause := Cause(err) 185 if cause != err && cause != nil { 186 // If we did not find an error code at the outer level, let's find the cause and check it's code 187 return Code(cause) 188 } 189 190 // Handle some special cases. 191 switch err { 192 case context.Canceled: 193 return vtrpcpb.Code_CANCELED 194 case context.DeadlineExceeded: 195 return vtrpcpb.Code_DEADLINE_EXCEEDED 196 } 197 return vtrpcpb.Code_UNKNOWN 198 } 199 200 // ErrState returns the error state if it's a vtError. 201 // If err is nil, it returns Undefined. 202 func ErrState(err error) State { 203 if err == nil { 204 return Undefined 205 } 206 207 if err, ok := err.(ErrorWithState); ok { 208 return err.ErrorState() 209 } 210 211 cause := Cause(err) 212 if cause != err && cause != nil { 213 // If we did not find an error state at the outer level, let's find the cause and check it's state 214 return ErrState(cause) 215 } 216 return Undefined 217 } 218 219 // Wrap returns an error annotating err with a stack trace 220 // at the point Wrap is called, and the supplied message. 221 // If err is nil, Wrap returns nil. 222 func Wrap(err error, message string) error { 223 if err == nil { 224 return nil 225 } 226 return &wrapping{ 227 cause: err, 228 msg: message, 229 stack: callers(), 230 } 231 } 232 233 // Wrapf returns an error annotating err with a stack trace 234 // at the point Wrapf is call, and the format specifier. 235 // If err is nil, Wrapf returns nil. 236 func Wrapf(err error, format string, args ...any) error { 237 if err == nil { 238 return nil 239 } 240 return &wrapping{ 241 cause: err, 242 msg: fmt.Sprintf(format, args...), 243 stack: callers(), 244 } 245 } 246 247 type wrapping struct { 248 cause error 249 msg string 250 stack *stack 251 } 252 253 func (w *wrapping) Error() string { return w.msg + ": " + w.cause.Error() } 254 func (w *wrapping) Cause() error { return w.cause } 255 256 func (w *wrapping) Format(s fmt.State, verb rune) { 257 if rune('v') == verb { 258 panicIfError(fmt.Fprintf(s, "%v\n", w.Cause())) 259 panicIfError(io.WriteString(s, w.msg)) 260 if logErrStacks { 261 w.stack.Format(s, verb) 262 } 263 return 264 } 265 266 if rune('s') == verb || rune('q') == verb { 267 panicIfError(io.WriteString(s, w.Error())) 268 } 269 } 270 271 // since we can't return an error, let's panic if something goes wrong here 272 func panicIfError(_ int, err error) { 273 if err != nil { 274 panic(err) 275 } 276 } 277 278 // RootCause returns the underlying cause of the error, if possible. 279 // An error value has a cause if it implements the following 280 // interface: 281 // 282 // type causer interface { 283 // Cause() error 284 // } 285 // 286 // If the error does not implement Cause, the original error will 287 // be returned. If the error is nil, nil will be returned without further 288 // investigation. 289 func RootCause(err error) error { 290 for { 291 cause := Cause(err) 292 if cause == nil { 293 return err 294 } 295 err = cause 296 } 297 } 298 299 // Cause will return the immediate cause, if possible. 300 // An error value has a cause if it implements the following 301 // interface: 302 // 303 // type causer interface { 304 // Cause() error 305 // } 306 // 307 // If the error does not implement Cause, nil will be returned 308 func Cause(err error) error { 309 type causer interface { 310 Cause() error 311 } 312 313 causerObj, ok := err.(causer) 314 if !ok { 315 return nil 316 } 317 318 return causerObj.Cause() 319 } 320 321 // Equals returns true iff the error message and the code returned by Code() 322 // are equal. 323 func Equals(a, b error) bool { 324 if a == nil && b == nil { 325 // Both are nil. 326 return true 327 } 328 329 if a == nil || b == nil { 330 // One of the two is nil, since we know both are not nil. 331 return false 332 } 333 334 return a.Error() == b.Error() && Code(a) == Code(b) 335 } 336 337 // Print is meant to print the vtError object in test failures. 338 // For comparing two vterrors, use Equals() instead. 339 func Print(err error) string { 340 return fmt.Sprintf("%v: %v\n", Code(err), err.Error()) 341 } 342 343 func (f *fundamental) ErrorState() State { return f.state } 344 func (f *fundamental) ErrorCode() vtrpcpb.Code { return f.code }