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 }