mirror of https://github.com/jackc/pgx.git
167 lines
3.5 KiB
Go
167 lines
3.5 KiB
Go
package pgconn
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net"
|
|
"strings"
|
|
|
|
errors "golang.org/x/xerrors"
|
|
)
|
|
|
|
// SafeToRetry checks if the err is guaranteed to have occurred before sending any data to the server.
|
|
func SafeToRetry(err error) bool {
|
|
if e, ok := err.(interface{ SafeToRetry() bool }); ok {
|
|
return e.SafeToRetry()
|
|
}
|
|
return false
|
|
}
|
|
|
|
// Timeout checks if err was was caused by a timeout. To be specific, it is true if err is or was caused by a
|
|
// context.Canceled, context.Canceled or an implementer of net.Error where Timeout() is true.
|
|
func Timeout(err error) bool {
|
|
if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {
|
|
return true
|
|
}
|
|
|
|
var netErr net.Error
|
|
return errors.As(err, &netErr) && netErr.Timeout()
|
|
}
|
|
|
|
// PgError represents an error reported by the PostgreSQL server. See
|
|
// http://www.postgresql.org/docs/11/static/protocol-error-fields.html for
|
|
// detailed field description.
|
|
type PgError struct {
|
|
Severity string
|
|
Code string
|
|
Message string
|
|
Detail string
|
|
Hint string
|
|
Position int32
|
|
InternalPosition int32
|
|
InternalQuery string
|
|
Where string
|
|
SchemaName string
|
|
TableName string
|
|
ColumnName string
|
|
DataTypeName string
|
|
ConstraintName string
|
|
File string
|
|
Line int32
|
|
Routine string
|
|
}
|
|
|
|
func (pe *PgError) Error() string {
|
|
return pe.Severity + ": " + pe.Message + " (SQLSTATE " + pe.Code + ")"
|
|
}
|
|
|
|
// SQLState returns the SQLState of the error.
|
|
func (pe *PgError) SQLState() string {
|
|
return pe.Code
|
|
}
|
|
|
|
type connectError struct {
|
|
config *Config
|
|
msg string
|
|
err error
|
|
}
|
|
|
|
func (e *connectError) Error() string {
|
|
sb := &strings.Builder{}
|
|
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)
|
|
if e.err != nil {
|
|
fmt.Fprintf(sb, " (%s)", e.err.Error())
|
|
}
|
|
return sb.String()
|
|
}
|
|
|
|
func (e *connectError) Unwrap() error {
|
|
return e.err
|
|
}
|
|
|
|
type connLockError struct {
|
|
status string
|
|
}
|
|
|
|
func (e *connLockError) SafeToRetry() bool {
|
|
return true // a lock failure by definition happens before the connection is used.
|
|
}
|
|
|
|
func (e *connLockError) Error() string {
|
|
return e.status
|
|
}
|
|
|
|
type parseConfigError struct {
|
|
connString string
|
|
msg string
|
|
err error
|
|
}
|
|
|
|
func (e *parseConfigError) Error() string {
|
|
if e.err == nil {
|
|
return fmt.Sprintf("cannot parse `%s`: %s", e.connString, e.msg)
|
|
}
|
|
return fmt.Sprintf("cannot parse `%s`: %s (%s)", e.connString, e.msg, e.err.Error())
|
|
}
|
|
|
|
func (e *parseConfigError) Unwrap() error {
|
|
return e.err
|
|
}
|
|
|
|
type pgconnError struct {
|
|
msg string
|
|
err error
|
|
safeToRetry bool
|
|
}
|
|
|
|
func (e *pgconnError) Error() string {
|
|
if e.msg == "" {
|
|
return e.err.Error()
|
|
}
|
|
if e.err == nil {
|
|
return e.msg
|
|
}
|
|
return fmt.Sprintf("%s: %s", e.msg, e.err.Error())
|
|
}
|
|
|
|
func (e *pgconnError) SafeToRetry() bool {
|
|
return e.safeToRetry
|
|
}
|
|
|
|
func (e *pgconnError) Unwrap() error {
|
|
return e.err
|
|
}
|
|
|
|
type contextAlreadyDoneError struct {
|
|
err error
|
|
}
|
|
|
|
func (e *contextAlreadyDoneError) Error() string {
|
|
return fmt.Sprintf("context already done: %s", e.err.Error())
|
|
}
|
|
|
|
func (e *contextAlreadyDoneError) SafeToRetry() bool {
|
|
return true
|
|
}
|
|
|
|
func (e *contextAlreadyDoneError) Unwrap() error {
|
|
return e.err
|
|
}
|
|
|
|
type writeError struct {
|
|
err error
|
|
safeToRetry bool
|
|
}
|
|
|
|
func (e *writeError) Error() string {
|
|
return fmt.Sprintf("write failed: %s", e.err.Error())
|
|
}
|
|
|
|
func (e *writeError) SafeToRetry() bool {
|
|
return e.safeToRetry
|
|
}
|
|
|
|
func (e *writeError) Unwrap() error {
|
|
return e.err
|
|
}
|