github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/colexecbase/colexecerror/error.go (about)

     1  // Copyright 2019 The Cockroach Authors.
     2  //
     3  // Use of this software is governed by the Business Source License
     4  // included in the file licenses/BSL.txt.
     5  //
     6  // As of the Change Date specified in that file, in accordance with
     7  // the Business Source License, use of this software will be governed
     8  // by the Apache License, Version 2.0, included in the file
     9  // licenses/APL.txt.
    10  
    11  package colexecerror
    12  
    13  import (
    14  	"bufio"
    15  	"context"
    16  	"fmt"
    17  	"runtime/debug"
    18  	"strings"
    19  
    20  	"github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgcode"
    21  	"github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgerror"
    22  	"github.com/cockroachdb/errors"
    23  	"github.com/gogo/protobuf/proto"
    24  )
    25  
    26  const panicLineSubstring = "runtime/panic.go"
    27  
    28  // CatchVectorizedRuntimeError executes operation, catches a runtime error if
    29  // it is coming from the vectorized engine, and returns it. If an error not
    30  // related to the vectorized engine occurs, it is not recovered from.
    31  func CatchVectorizedRuntimeError(operation func()) (retErr error) {
    32  	defer func() {
    33  		panicObj := recover()
    34  		if panicObj == nil {
    35  			// No panic happened, so the operation must have been executed
    36  			// successfully.
    37  			return
    38  		}
    39  
    40  		// Find where the panic came from and only proceed if it is related to the
    41  		// vectorized engine.
    42  		stackTrace := string(debug.Stack())
    43  		scanner := bufio.NewScanner(strings.NewReader(stackTrace))
    44  		panicLineFound := false
    45  		for scanner.Scan() {
    46  			if strings.Contains(scanner.Text(), panicLineSubstring) {
    47  				panicLineFound = true
    48  				break
    49  			}
    50  		}
    51  		if !panicLineFound {
    52  			panic(fmt.Sprintf("panic line %q not found in the stack trace\n%s", panicLineSubstring, stackTrace))
    53  		}
    54  		if !scanner.Scan() {
    55  			panic(fmt.Sprintf("unexpectedly there is no line below the panic line in the stack trace\n%s", stackTrace))
    56  		}
    57  		panicEmittedFrom := strings.TrimSpace(scanner.Text())
    58  		if !isPanicFromVectorizedEngine(panicEmittedFrom) {
    59  			// Do not recover from the panic not related to the vectorized
    60  			// engine.
    61  			panic(panicObj)
    62  		}
    63  
    64  		err, ok := panicObj.(error)
    65  		if !ok {
    66  			// Not an error object. Definitely unexpected.
    67  			retErr = errors.AssertionFailedf("unexpected error from the vectorized runtime: %+v", panicObj)
    68  			return
    69  		}
    70  		retErr = err
    71  
    72  		if _, ok := panicObj.(*StorageError); ok {
    73  			// A StorageError was caused by something below SQL, and represents
    74  			// an error that we'd simply like to propagate along.
    75  			// Do nothing.
    76  			return
    77  		}
    78  
    79  		annotateErrorWithoutCode := true
    80  		var nie *notInternalError
    81  		if errors.As(err, &nie) {
    82  			// A notInternalError was not caused by the vectorized engine and
    83  			// represents an error that we don't want to annotate in case it
    84  			// doesn't have a valid PG code.
    85  			annotateErrorWithoutCode = false
    86  		}
    87  		if code := pgerror.GetPGCode(err); annotateErrorWithoutCode && code == pgcode.Uncategorized {
    88  			// Any error without a code already is "surprising" and
    89  			// needs to be annotated to indicate that it was
    90  			// unexpected.
    91  			retErr = errors.NewAssertionErrorWithWrappedErrf(err, "unexpected error from the vectorized engine")
    92  		}
    93  	}()
    94  	operation()
    95  	return retErr
    96  }
    97  
    98  const (
    99  	colPackagesPrefix      = "github.com/cockroachdb/cockroach/pkg/col"
   100  	execinfraPackagePrefix = "github.com/cockroachdb/cockroach/pkg/sql/execinfra"
   101  	rowexecPackagePrefix   = "github.com/cockroachdb/cockroach/pkg/sql/rowexec"
   102  	sqlColPackagesPrefix   = "github.com/cockroachdb/cockroach/pkg/sql/col"
   103  	treePackagePrefix      = "github.com/cockroachdb/cockroach/pkg/sql/sem/tree"
   104  )
   105  
   106  // isPanicFromVectorizedEngine checks whether the panic that was emitted from
   107  // panicEmittedFrom line of code (which includes package name as well as the
   108  // file name and the line number) came from the vectorized engine.
   109  // panicEmittedFrom must be trimmed to not have any white spaces in the prefix.
   110  func isPanicFromVectorizedEngine(panicEmittedFrom string) bool {
   111  	const testExceptionPrefix = "github.com/cockroachdb/cockroach/pkg/sql/colflow_test.(*testNonVectorizedPanicEmitter)"
   112  	if strings.HasPrefix(panicEmittedFrom, testExceptionPrefix) {
   113  		// Although the panic appears to be coming from the vectorized engine, it
   114  		// is intended to not be caught in order to test the panic propagation, so
   115  		// we say that the panic is not from the vectorized engine.
   116  		return false
   117  	}
   118  	return strings.HasPrefix(panicEmittedFrom, colPackagesPrefix) ||
   119  		strings.HasPrefix(panicEmittedFrom, execinfraPackagePrefix) ||
   120  		strings.HasPrefix(panicEmittedFrom, rowexecPackagePrefix) ||
   121  		strings.HasPrefix(panicEmittedFrom, sqlColPackagesPrefix) ||
   122  		strings.HasPrefix(panicEmittedFrom, treePackagePrefix)
   123  }
   124  
   125  // StorageError is an error that was created by a component below the sql
   126  // stack, such as the network or storage layers. A StorageError will be bubbled
   127  // up all the way past the SQL layer unchanged.
   128  type StorageError struct {
   129  	error
   130  }
   131  
   132  // Cause implements the Causer interface.
   133  func (s *StorageError) Cause() error {
   134  	return s.error
   135  }
   136  
   137  // NewStorageError returns a new storage error. This can be used to propagate
   138  // an error through the exec subsystem unchanged.
   139  func NewStorageError(err error) *StorageError {
   140  	return &StorageError{error: err}
   141  }
   142  
   143  // notInternalError is an error that occurs not because the vectorized engine
   144  // happens to be in an unexpected state (for example, it was caused by a
   145  // non-columnar builtin).
   146  // notInternalError will be returned to the client not as an
   147  // "internal error" and without the stack trace.
   148  type notInternalError struct {
   149  	cause error
   150  }
   151  
   152  func newNotInternalError(err error) *notInternalError {
   153  	return &notInternalError{cause: err}
   154  }
   155  
   156  var (
   157  	_ errors.Wrapper = &notInternalError{}
   158  )
   159  
   160  func (e *notInternalError) Error() string { return e.cause.Error() }
   161  func (e *notInternalError) Cause() error  { return e.cause }
   162  func (e *notInternalError) Unwrap() error { return e.Cause() }
   163  
   164  func decodeNotInternalError(
   165  	_ context.Context, cause error, _ string, _ []string, _ proto.Message,
   166  ) error {
   167  	return newNotInternalError(cause)
   168  }
   169  
   170  func init() {
   171  	errors.RegisterWrapperDecoder(errors.GetTypeKey((*notInternalError)(nil)), decodeNotInternalError)
   172  }
   173  
   174  // InternalError simply panics with the provided object. It will always be
   175  // caught and returned as internal error to the client with the corresponding
   176  // stack trace. This method should be called to propagate errors that resulted
   177  // in the vectorized engine being in an *unexpected* state.
   178  func InternalError(err interface{}) {
   179  	panic(err)
   180  }
   181  
   182  // ExpectedError panics with the error that is wrapped by
   183  // notInternalError which will not be treated as internal error and
   184  // will not have a printed out stack trace. This method should be called to
   185  // propagate errors that the vectorized engine *expects* to occur.
   186  func ExpectedError(err error) {
   187  	panic(newNotInternalError(err))
   188  }