gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

errors.go (5524B)


      1 package pgconn
      2 
      3 import (
      4 	"context"
      5 	"errors"
      6 	"fmt"
      7 	"net"
      8 	"net/url"
      9 	"regexp"
     10 	"strings"
     11 )
     12 
     13 // SafeToRetry checks if the err is guaranteed to have occurred before sending any data to the server.
     14 func SafeToRetry(err error) bool {
     15 	if e, ok := err.(interface{ SafeToRetry() bool }); ok {
     16 		return e.SafeToRetry()
     17 	}
     18 	return false
     19 }
     20 
     21 // Timeout checks if err was was caused by a timeout. To be specific, it is true if err was caused within pgconn by a
     22 // context.DeadlineExceeded or an implementer of net.Error where Timeout() is true.
     23 func Timeout(err error) bool {
     24 	var timeoutErr *errTimeout
     25 	return errors.As(err, &timeoutErr)
     26 }
     27 
     28 // PgError represents an error reported by the PostgreSQL server. See
     29 // http://www.postgresql.org/docs/11/static/protocol-error-fields.html for
     30 // detailed field description.
     31 type PgError struct {
     32 	Severity         string
     33 	Code             string
     34 	Message          string
     35 	Detail           string
     36 	Hint             string
     37 	Position         int32
     38 	InternalPosition int32
     39 	InternalQuery    string
     40 	Where            string
     41 	SchemaName       string
     42 	TableName        string
     43 	ColumnName       string
     44 	DataTypeName     string
     45 	ConstraintName   string
     46 	File             string
     47 	Line             int32
     48 	Routine          string
     49 }
     50 
     51 func (pe *PgError) Error() string {
     52 	return pe.Severity + ": " + pe.Message + " (SQLSTATE " + pe.Code + ")"
     53 }
     54 
     55 // SQLState returns the SQLState of the error.
     56 func (pe *PgError) SQLState() string {
     57 	return pe.Code
     58 }
     59 
     60 type connectError struct {
     61 	config *Config
     62 	msg    string
     63 	err    error
     64 }
     65 
     66 func (e *connectError) Error() string {
     67 	sb := &strings.Builder{}
     68 	fmt.Fprintf(sb, "failed to connect to `host=%s user=%s database=%s`: %s", e.config.Host, e.config.User, e.config.Database, e.msg)
     69 	if e.err != nil {
     70 		fmt.Fprintf(sb, " (%s)", e.err.Error())
     71 	}
     72 	return sb.String()
     73 }
     74 
     75 func (e *connectError) Unwrap() error {
     76 	return e.err
     77 }
     78 
     79 type connLockError struct {
     80 	status string
     81 }
     82 
     83 func (e *connLockError) SafeToRetry() bool {
     84 	return true // a lock failure by definition happens before the connection is used.
     85 }
     86 
     87 func (e *connLockError) Error() string {
     88 	return e.status
     89 }
     90 
     91 type parseConfigError struct {
     92 	connString string
     93 	msg        string
     94 	err        error
     95 }
     96 
     97 func (e *parseConfigError) Error() string {
     98 	connString := redactPW(e.connString)
     99 	if e.err == nil {
    100 		return fmt.Sprintf("cannot parse `%s`: %s", connString, e.msg)
    101 	}
    102 	return fmt.Sprintf("cannot parse `%s`: %s (%s)", connString, e.msg, e.err.Error())
    103 }
    104 
    105 func (e *parseConfigError) Unwrap() error {
    106 	return e.err
    107 }
    108 
    109 func normalizeTimeoutError(ctx context.Context, err error) error {
    110 	if err, ok := err.(net.Error); ok && err.Timeout() {
    111 		if ctx.Err() == context.Canceled {
    112 			// Since the timeout was caused by a context cancellation, the actual error is context.Canceled not the timeout error.
    113 			return context.Canceled
    114 		} else if ctx.Err() == context.DeadlineExceeded {
    115 			return &errTimeout{err: ctx.Err()}
    116 		} else {
    117 			return &errTimeout{err: err}
    118 		}
    119 	}
    120 	return err
    121 }
    122 
    123 type pgconnError struct {
    124 	msg         string
    125 	err         error
    126 	safeToRetry bool
    127 }
    128 
    129 func (e *pgconnError) Error() string {
    130 	if e.msg == "" {
    131 		return e.err.Error()
    132 	}
    133 	if e.err == nil {
    134 		return e.msg
    135 	}
    136 	return fmt.Sprintf("%s: %s", e.msg, e.err.Error())
    137 }
    138 
    139 func (e *pgconnError) SafeToRetry() bool {
    140 	return e.safeToRetry
    141 }
    142 
    143 func (e *pgconnError) Unwrap() error {
    144 	return e.err
    145 }
    146 
    147 // errTimeout occurs when an error was caused by a timeout. Specifically, it wraps an error which is
    148 // context.Canceled, context.DeadlineExceeded, or an implementer of net.Error where Timeout() is true.
    149 type errTimeout struct {
    150 	err error
    151 }
    152 
    153 func (e *errTimeout) Error() string {
    154 	return fmt.Sprintf("timeout: %s", e.err.Error())
    155 }
    156 
    157 func (e *errTimeout) SafeToRetry() bool {
    158 	return SafeToRetry(e.err)
    159 }
    160 
    161 func (e *errTimeout) Unwrap() error {
    162 	return e.err
    163 }
    164 
    165 type contextAlreadyDoneError struct {
    166 	err error
    167 }
    168 
    169 func (e *contextAlreadyDoneError) Error() string {
    170 	return fmt.Sprintf("context already done: %s", e.err.Error())
    171 }
    172 
    173 func (e *contextAlreadyDoneError) SafeToRetry() bool {
    174 	return true
    175 }
    176 
    177 func (e *contextAlreadyDoneError) Unwrap() error {
    178 	return e.err
    179 }
    180 
    181 // newContextAlreadyDoneError double-wraps a context error in `contextAlreadyDoneError` and `errTimeout`.
    182 func newContextAlreadyDoneError(ctx context.Context) (err error) {
    183 	return &errTimeout{&contextAlreadyDoneError{err: ctx.Err()}}
    184 }
    185 
    186 func redactPW(connString string) string {
    187 	if strings.HasPrefix(connString, "postgres://") || strings.HasPrefix(connString, "postgresql://") {
    188 		if u, err := url.Parse(connString); err == nil {
    189 			return redactURL(u)
    190 		}
    191 	}
    192 	quotedDSN := regexp.MustCompile(`password='[^']*'`)
    193 	connString = quotedDSN.ReplaceAllLiteralString(connString, "password=xxxxx")
    194 	plainDSN := regexp.MustCompile(`password=[^ ]*`)
    195 	connString = plainDSN.ReplaceAllLiteralString(connString, "password=xxxxx")
    196 	brokenURL := regexp.MustCompile(`:[^:@]+?@`)
    197 	connString = brokenURL.ReplaceAllLiteralString(connString, ":xxxxxx@")
    198 	return connString
    199 }
    200 
    201 func redactURL(u *url.URL) string {
    202 	if u == nil {
    203 		return ""
    204 	}
    205 	if _, pwSet := u.User.Password(); pwSet {
    206 		u.User = url.UserPassword(u.User.Username(), "xxxxx")
    207 	}
    208 	return u.String()
    209 }
    210 
    211 type NotPreferredError struct {
    212 	err         error
    213 	safeToRetry bool
    214 }
    215 
    216 func (e *NotPreferredError) Error() string {
    217 	return fmt.Sprintf("standby server not found: %s", e.err.Error())
    218 }
    219 
    220 func (e *NotPreferredError) SafeToRetry() bool {
    221 	return e.safeToRetry
    222 }
    223 
    224 func (e *NotPreferredError) Unwrap() error {
    225 	return e.err
    226 }