package pgconn

import (
	
	
	
	
	
	
	
)

// SafeToRetry checks if the err is guaranteed to have occurred before sending any data to the server.
func ( error) bool {
	if ,  := .(interface{ () bool });  {
		return .()
	}
	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.DeadlineExceeded or an implementer of net.Error where Timeout() is true.
func ( error) bool {
	var  *errTimeout
	return errors.As(, &)
}

// 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 ( *PgError) () string {
	return .Severity + ": " + .Message + " (SQLSTATE " + .Code + ")"
}

// SQLState returns the SQLState of the error.
func ( *PgError) () string {
	return .Code
}

type connectError struct {
	config *Config
	msg    string
	err    error
}

func ( *connectError) () string {
	 := &strings.Builder{}
	fmt.Fprintf(, "failed to connect to `host=%s user=%s database=%s`: %s", .config.Host, .config.User, .config.Database, .msg)
	if .err != nil {
		fmt.Fprintf(, " (%s)", .err.Error())
	}
	return .String()
}

func ( *connectError) () error {
	return .err
}

type connLockError struct {
	status string
}

func ( *connLockError) () bool {
	return true // a lock failure by definition happens before the connection is used.
}

func ( *connLockError) () string {
	return .status
}

type parseConfigError struct {
	connString string
	msg        string
	err        error
}

func ( *parseConfigError) () string {
	 := redactPW(.connString)
	if .err == nil {
		return fmt.Sprintf("cannot parse `%s`: %s", , .msg)
	}
	return fmt.Sprintf("cannot parse `%s`: %s (%s)", , .msg, .err.Error())
}

func ( *parseConfigError) () error {
	return .err
}

func normalizeTimeoutError( context.Context,  error) error {
	if ,  := .(net.Error);  && .Timeout() {
		if .Err() == context.Canceled {
			// Since the timeout was caused by a context cancellation, the actual error is context.Canceled not the timeout error.
			return context.Canceled
		} else if .Err() == context.DeadlineExceeded {
			return &errTimeout{err: .Err()}
		} else {
			return &errTimeout{err: }
		}
	}
	return 
}

type pgconnError struct {
	msg         string
	err         error
	safeToRetry bool
}

func ( *pgconnError) () string {
	if .msg == "" {
		return .err.Error()
	}
	if .err == nil {
		return .msg
	}
	return fmt.Sprintf("%s: %s", .msg, .err.Error())
}

func ( *pgconnError) () bool {
	return .safeToRetry
}

func ( *pgconnError) () error {
	return .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 ( *errTimeout) () string {
	return fmt.Sprintf("timeout: %s", .err.Error())
}

func ( *errTimeout) () bool {
	return SafeToRetry(.err)
}

func ( *errTimeout) () error {
	return .err
}

type contextAlreadyDoneError struct {
	err error
}

func ( *contextAlreadyDoneError) () string {
	return fmt.Sprintf("context already done: %s", .err.Error())
}

func ( *contextAlreadyDoneError) () bool {
	return true
}

func ( *contextAlreadyDoneError) () error {
	return .err
}

// newContextAlreadyDoneError double-wraps a context error in `contextAlreadyDoneError` and `errTimeout`.
func newContextAlreadyDoneError( context.Context) ( error) {
	return &errTimeout{&contextAlreadyDoneError{err: .Err()}}
}

func redactPW( string) string {
	if strings.HasPrefix(, "postgres://") || strings.HasPrefix(, "postgresql://") {
		if ,  := url.Parse();  == nil {
			return redactURL()
		}
	}
	 := regexp.MustCompile(`password='[^']*'`)
	 = .ReplaceAllLiteralString(, "password=xxxxx")
	 := regexp.MustCompile(`password=[^ ]*`)
	 = .ReplaceAllLiteralString(, "password=xxxxx")
	 := regexp.MustCompile(`:[^:@]+?@`)
	 = .ReplaceAllLiteralString(, ":xxxxxx@")
	return 
}

func redactURL( *url.URL) string {
	if  == nil {
		return ""
	}
	if ,  := .User.Password();  {
		.User = url.UserPassword(.User.Username(), "xxxxx")
	}
	return .String()
}

type NotPreferredError struct {
	err         error
	safeToRetry bool
}

func ( *NotPreferredError) () string {
	return fmt.Sprintf("standby server not found: %s", .err.Error())
}

func ( *NotPreferredError) () bool {
	return .safeToRetry
}

func ( *NotPreferredError) () error {
	return .err
}