mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-11-03 21:42:25 -06:00 
			
		
		
		
	* start moving to bun * changing more stuff * more * and yet more * tests passing * seems stable now * more big changes * small fix * little fixes
		
			
				
	
	
		
			221 lines
		
	
	
	
		
			5.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			221 lines
		
	
	
	
		
			5.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package pgconn
 | 
						|
 | 
						|
import (
 | 
						|
	"context"
 | 
						|
	"errors"
 | 
						|
	"fmt"
 | 
						|
	"net"
 | 
						|
	"net/url"
 | 
						|
	"regexp"
 | 
						|
	"strings"
 | 
						|
)
 | 
						|
 | 
						|
// 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 was caused within pgconn by a
 | 
						|
// context.Canceled, context.DeadlineExceeded or an implementer of net.Error where Timeout() is true.
 | 
						|
func Timeout(err error) bool {
 | 
						|
	var timeoutErr *errTimeout
 | 
						|
	return errors.As(err, &timeoutErr)
 | 
						|
}
 | 
						|
 | 
						|
// 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 {
 | 
						|
	connString := redactPW(e.connString)
 | 
						|
	if e.err == nil {
 | 
						|
		return fmt.Sprintf("cannot parse `%s`: %s", connString, e.msg)
 | 
						|
	}
 | 
						|
	return fmt.Sprintf("cannot parse `%s`: %s (%s)", connString, e.msg, e.err.Error())
 | 
						|
}
 | 
						|
 | 
						|
func (e *parseConfigError) Unwrap() error {
 | 
						|
	return e.err
 | 
						|
}
 | 
						|
 | 
						|
// preferContextOverNetTimeoutError returns ctx.Err() if ctx.Err() is present and err is a net.Error with Timeout() ==
 | 
						|
// true. Otherwise returns err.
 | 
						|
func preferContextOverNetTimeoutError(ctx context.Context, err error) error {
 | 
						|
	if err, ok := err.(net.Error); ok && err.Timeout() && ctx.Err() != nil {
 | 
						|
		return &errTimeout{err: ctx.Err()}
 | 
						|
	}
 | 
						|
	return 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
 | 
						|
}
 | 
						|
 | 
						|
// errTimeout occurs when an error was caused by a timeout. Specifically, it wraps an error which is
 | 
						|
// context.Canceled, context.DeadlineExceeded, or an implementer of net.Error where Timeout() is true.
 | 
						|
type errTimeout struct {
 | 
						|
	err error
 | 
						|
}
 | 
						|
 | 
						|
func (e *errTimeout) Error() string {
 | 
						|
	return fmt.Sprintf("timeout: %s", e.err.Error())
 | 
						|
}
 | 
						|
 | 
						|
func (e *errTimeout) SafeToRetry() bool {
 | 
						|
	return SafeToRetry(e.err)
 | 
						|
}
 | 
						|
 | 
						|
func (e *errTimeout) 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
 | 
						|
}
 | 
						|
 | 
						|
// newContextAlreadyDoneError double-wraps a context error in `contextAlreadyDoneError` and `errTimeout`.
 | 
						|
func newContextAlreadyDoneError(ctx context.Context) (err error) {
 | 
						|
	return &errTimeout{&contextAlreadyDoneError{err: ctx.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
 | 
						|
}
 | 
						|
 | 
						|
func redactPW(connString string) string {
 | 
						|
	if strings.HasPrefix(connString, "postgres://") || strings.HasPrefix(connString, "postgresql://") {
 | 
						|
		if u, err := url.Parse(connString); err == nil {
 | 
						|
			return redactURL(u)
 | 
						|
		}
 | 
						|
	}
 | 
						|
	quotedDSN := regexp.MustCompile(`password='[^']*'`)
 | 
						|
	connString = quotedDSN.ReplaceAllLiteralString(connString, "password=xxxxx")
 | 
						|
	plainDSN := regexp.MustCompile(`password=[^ ]*`)
 | 
						|
	connString = plainDSN.ReplaceAllLiteralString(connString, "password=xxxxx")
 | 
						|
	brokenURL := regexp.MustCompile(`:[^:@]+?@`)
 | 
						|
	connString = brokenURL.ReplaceAllLiteralString(connString, ":xxxxxx@")
 | 
						|
	return connString
 | 
						|
}
 | 
						|
 | 
						|
func redactURL(u *url.URL) string {
 | 
						|
	if u == nil {
 | 
						|
		return ""
 | 
						|
	}
 | 
						|
	if _, pwSet := u.User.Password(); pwSet {
 | 
						|
		u.User = url.UserPassword(u.User.Username(), "xxxxx")
 | 
						|
	}
 | 
						|
	return u.String()
 | 
						|
}
 |