github.com/cockroachdb/cockroachdb-parser@v0.23.3-0.20240213214944-911057d40c9a/pkg/sql/pgwire/pgerror/errors.go (about)

     1  // Copyright 2016 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 pgerror
    12  
    13  import (
    14  	"fmt"
    15  	"regexp"
    16  	"strings"
    17  
    18  	"github.com/cockroachdb/cockroachdb-parser/pkg/sql/pgwire/pgcode"
    19  	"github.com/cockroachdb/errors"
    20  	"github.com/lib/pq"
    21  )
    22  
    23  var _ error = (*Error)(nil)
    24  var _ errors.ErrorHinter = (*Error)(nil)
    25  var _ errors.ErrorDetailer = (*Error)(nil)
    26  var _ fmt.Formatter = (*Error)(nil)
    27  
    28  // Error implements the error interface.
    29  func (pg *Error) Error() string { return pg.Message }
    30  
    31  // ErrorHint implements the hintdetail.ErrorHinter interface.
    32  func (pg *Error) ErrorHint() string { return pg.Hint }
    33  
    34  // ErrorDetail implements the hintdetail.ErrorDetailer interface.
    35  func (pg *Error) ErrorDetail() string { return pg.Detail }
    36  
    37  // FullError can be used when the hint and/or detail are to be tested.
    38  func FullError(err error) string {
    39  	var errString string
    40  	if err == nil {
    41  		panic("FullError should not be called with nil input")
    42  	}
    43  	if pqErr := (*pq.Error)(nil); errors.As(err, &pqErr) {
    44  		errString = formatMsgHintDetail("pq", pqErr.Message, pqErr.Hint, pqErr.Detail)
    45  	} else {
    46  		pg := Flatten(err)
    47  		errString = formatMsgHintDetail(pg.Severity, err.Error(), pg.Hint, pg.Detail)
    48  	}
    49  	return errString
    50  }
    51  
    52  func formatMsgHintDetail(prefix, msg, hint, detail string) string {
    53  	var b strings.Builder
    54  	b.WriteString(prefix)
    55  	b.WriteString(": ")
    56  	b.WriteString(msg)
    57  	if hint != "" {
    58  		b.WriteString("\nHINT: ")
    59  		b.WriteString(hint)
    60  	}
    61  	if detail != "" {
    62  		b.WriteString("\nDETAIL: ")
    63  		b.WriteString(detail)
    64  	}
    65  	return b.String()
    66  }
    67  
    68  // NewWithDepthf creates an error with a pg code and extracts the context
    69  // information at the specified depth level.
    70  func NewWithDepthf(depth int, code pgcode.Code, format string, args ...interface{}) error {
    71  	err := errors.NewWithDepthf(1+depth, format, args...)
    72  	err = WithCandidateCode(err, code)
    73  	return err
    74  }
    75  
    76  // New creates an error with a code.
    77  func New(code pgcode.Code, msg string) error {
    78  	err := errors.NewWithDepth(1, msg)
    79  	err = WithCandidateCode(err, code)
    80  	return err
    81  }
    82  
    83  // Newf creates an Error with a format string.
    84  func Newf(code pgcode.Code, format string, args ...interface{}) error {
    85  	err := errors.NewWithDepthf(1, format, args...)
    86  	err = WithCandidateCode(err, code)
    87  	return err
    88  }
    89  
    90  // DangerousStatementf creates a new error for "rejected dangerous
    91  // statements".
    92  func DangerousStatementf(format string, args ...interface{}) error {
    93  	err := errors.Newf(format, args...)
    94  	err = errors.WithMessage(err, "rejected (sql_safe_updates = true)")
    95  	err = WithCandidateCode(err, pgcode.Warning)
    96  	return err
    97  }
    98  
    99  // WrongNumberOfPreparedStatements creates new an Error for trying to prepare
   100  // a query string containing more than one statement.
   101  func WrongNumberOfPreparedStatements(n int) error {
   102  	err := errors.NewWithDepthf(1, "prepared statement had %d statements, expected 1", errors.Safe(n))
   103  	err = WithCandidateCode(err, pgcode.InvalidPreparedStatementDefinition)
   104  	return err
   105  }
   106  
   107  var _ fmt.Formatter = &Error{}
   108  
   109  // Format implements the fmt.Formatter interface.
   110  //
   111  // %v/%s prints the error as usual.
   112  // %#v adds the pg error code at the beginning.
   113  // %+v prints all the details, including the embedded stack traces.
   114  func (pg *Error) Format(s fmt.State, verb rune) {
   115  	switch {
   116  	case verb == 'v' && s.Flag('+'):
   117  		// %+v prints all details.
   118  		if pg.Source != nil {
   119  			fmt.Fprintf(s, "%s:%d in %s(): ", pg.Source.File, pg.Source.Line, pg.Source.Function)
   120  		}
   121  		fmt.Fprintf(s, "(%s) %s", pg.Code, pg.Message)
   122  		return
   123  	case verb == 'v' && s.Flag('#'):
   124  		// %#v spells out the code as prefix.
   125  		fmt.Fprintf(s, "(%s) %s", pg.Code, pg.Message)
   126  	case verb == 'v':
   127  		fallthrough
   128  	case verb == 's':
   129  		fmt.Fprintf(s, "%s", pg.Message)
   130  	case verb == 'q':
   131  		fmt.Fprintf(s, "%q", pg.Message)
   132  	}
   133  }
   134  
   135  var _ errors.SafeFormatter = (*Error)(nil)
   136  
   137  // SafeFormatError implements the errors.SafeFormatter interface.
   138  func (pg *Error) SafeFormatError(s errors.Printer) (next error) {
   139  	s.Print(pg.Message)
   140  	if s.Detail() {
   141  		if pg.Source != nil {
   142  			s.Printf("Source: %s:%d in %s()",
   143  				errors.Safe(pg.Source.File), errors.Safe(pg.Source.Line), errors.Safe(pg.Source.Function))
   144  		}
   145  		s.Printf("SQLSTATE ", errors.Safe(pg.Code))
   146  	}
   147  	return nil
   148  }
   149  
   150  // IsSQLRetryableError returns true if err is retryable. This is true
   151  // for errors that show a connection issue or an issue with the node
   152  // itself. This can occur when a node is restarting or is unstable in
   153  // some other way. Note that retryable errors may occur event in cases
   154  // where the SQL execution ran to completion.
   155  //
   156  // TODO(bdarnell): Why are RPC errors in this list? These should
   157  // generally be retried on the server side or transformed into
   158  // ambiguous result errors ("connection reset/refused" are needed for
   159  // the pgwire connection, but anything RPC-related should be handled
   160  // within the cluster).
   161  // TODO(knz): This should really use the errors library. Investigate
   162  // how to get rid of the error message comparison.
   163  func IsSQLRetryableError(err error) bool {
   164  	// Don't forget to update the corresponding test when making adjustments
   165  	// here.
   166  	errString := FullError(err)
   167  	matched, merr := regexp.MatchString(
   168  		"(no inbound stream connection|connection reset by peer|connection refused|failed to send RPC|rpc error: code = Unavailable|(^|\\s)EOF|result is ambiguous)",
   169  		errString)
   170  	if merr != nil {
   171  		return false
   172  	}
   173  	return matched
   174  }