errors.go (5721B)
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.Canceled, 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 // preferContextOverNetTimeoutError returns ctx.Err() if ctx.Err() is present and err is a net.Error with Timeout() == 110 // true. Otherwise returns err. 111 func preferContextOverNetTimeoutError(ctx context.Context, err error) error { 112 if err, ok := err.(net.Error); ok && err.Timeout() && ctx.Err() != nil { 113 return &errTimeout{err: ctx.Err()} 114 } 115 return err 116 } 117 118 type pgconnError struct { 119 msg string 120 err error 121 safeToRetry bool 122 } 123 124 func (e *pgconnError) Error() string { 125 if e.msg == "" { 126 return e.err.Error() 127 } 128 if e.err == nil { 129 return e.msg 130 } 131 return fmt.Sprintf("%s: %s", e.msg, e.err.Error()) 132 } 133 134 func (e *pgconnError) SafeToRetry() bool { 135 return e.safeToRetry 136 } 137 138 func (e *pgconnError) Unwrap() error { 139 return e.err 140 } 141 142 // errTimeout occurs when an error was caused by a timeout. Specifically, it wraps an error which is 143 // context.Canceled, context.DeadlineExceeded, or an implementer of net.Error where Timeout() is true. 144 type errTimeout struct { 145 err error 146 } 147 148 func (e *errTimeout) Error() string { 149 return fmt.Sprintf("timeout: %s", e.err.Error()) 150 } 151 152 func (e *errTimeout) SafeToRetry() bool { 153 return SafeToRetry(e.err) 154 } 155 156 func (e *errTimeout) Unwrap() error { 157 return e.err 158 } 159 160 type contextAlreadyDoneError struct { 161 err error 162 } 163 164 func (e *contextAlreadyDoneError) Error() string { 165 return fmt.Sprintf("context already done: %s", e.err.Error()) 166 } 167 168 func (e *contextAlreadyDoneError) SafeToRetry() bool { 169 return true 170 } 171 172 func (e *contextAlreadyDoneError) Unwrap() error { 173 return e.err 174 } 175 176 // newContextAlreadyDoneError double-wraps a context error in `contextAlreadyDoneError` and `errTimeout`. 177 func newContextAlreadyDoneError(ctx context.Context) (err error) { 178 return &errTimeout{&contextAlreadyDoneError{err: ctx.Err()}} 179 } 180 181 type writeError struct { 182 err error 183 safeToRetry bool 184 } 185 186 func (e *writeError) Error() string { 187 return fmt.Sprintf("write failed: %s", e.err.Error()) 188 } 189 190 func (e *writeError) SafeToRetry() bool { 191 return e.safeToRetry 192 } 193 194 func (e *writeError) Unwrap() error { 195 return e.err 196 } 197 198 func redactPW(connString string) string { 199 if strings.HasPrefix(connString, "postgres://") || strings.HasPrefix(connString, "postgresql://") { 200 if u, err := url.Parse(connString); err == nil { 201 return redactURL(u) 202 } 203 } 204 quotedDSN := regexp.MustCompile(`password='[^']*'`) 205 connString = quotedDSN.ReplaceAllLiteralString(connString, "password=xxxxx") 206 plainDSN := regexp.MustCompile(`password=[^ ]*`) 207 connString = plainDSN.ReplaceAllLiteralString(connString, "password=xxxxx") 208 brokenURL := regexp.MustCompile(`:[^:@]+?@`) 209 connString = brokenURL.ReplaceAllLiteralString(connString, ":xxxxxx@") 210 return connString 211 } 212 213 func redactURL(u *url.URL) string { 214 if u == nil { 215 return "" 216 } 217 if _, pwSet := u.User.Password(); pwSet { 218 u.User = url.UserPassword(u.User.Username(), "xxxxx") 219 } 220 return u.String() 221 } 222 223 type NotPreferredError struct { 224 err error 225 safeToRetry bool 226 } 227 228 func (e *NotPreferredError) Error() string { 229 return fmt.Sprintf("standby server not found: %s", e.err.Error()) 230 } 231 232 func (e *NotPreferredError) SafeToRetry() bool { 233 return e.safeToRetry 234 } 235 236 func (e *NotPreferredError) Unwrap() error { 237 return e.err 238 }