package pgconn

import (
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	

	
	
	
	
)

type AfterConnectFunc func(ctx context.Context, pgconn *PgConn) error
type ValidateConnectFunc func(ctx context.Context, pgconn *PgConn) error
Config is the settings used to establish a connection to a PostgreSQL server. It must be created by ParseConfig. A manually initialized Config will cause ConnectConfig to panic.
type Config struct {
	Host           string // host (e.g. localhost) or absolute path to unix domain socket directory (e.g. /private/tmp)
	Port           uint16
	Database       string
	User           string
	Password       string
	TLSConfig      *tls.Config // nil disables TLS
	ConnectTimeout time.Duration
	DialFunc       DialFunc   // e.g. net.Dialer.DialContext
	LookupFunc     LookupFunc // e.g. net.Resolver.LookupHost
	BuildFrontend  BuildFrontendFunc
	RuntimeParams  map[string]string // Run-time parameters to set on connection as session default values (e.g. search_path or application_name)

	Fallbacks []*FallbackConfig
ValidateConnect is called during a connection attempt after a successful authentication with the PostgreSQL server. It can be used to validate that the server is acceptable. If this returns an error the connection is closed and the next fallback config is tried. This allows implementing high availability behavior such as libpq does with target_session_attrs.
AfterConnect is called after ValidateConnect. It can be used to set up the connection (e.g. Set session variables or prepare statements). If this returns an error the connection attempt fails.
OnNotice is a callback function called when a notice response is received.
OnNotification is a callback function called when a notification from the LISTEN/NOTIFY system is received.
	OnNotification NotificationHandler

	createdByParseConfig bool // Used to enforce created by ParseConfig rule.
}
Copy returns a deep copy of the config that is safe to use and modify. The only exception is the TLSConfig field: according to the tls.Config docs it must not be modified after creation.
func ( *Config) () *Config {
	 := new(Config)
	* = *
	if .TLSConfig != nil {
		.TLSConfig = .TLSConfig.Clone()
	}
	if .RuntimeParams != nil {
		.RuntimeParams = make(map[string]string, len(.RuntimeParams))
		for ,  := range .RuntimeParams {
			.RuntimeParams[] = 
		}
	}
	if .Fallbacks != nil {
		.Fallbacks = make([]*FallbackConfig, len(.Fallbacks))
		for ,  := range .Fallbacks {
			 := new(FallbackConfig)
			* = *
			if .TLSConfig != nil {
				.TLSConfig = .TLSConfig.Clone()
			}
			.Fallbacks[] = 
		}
	}
	return 
}
FallbackConfig is additional settings to attempt a connection with when the primary Config fails to establish a network connection. It is used for TLS fallback such as sslmode=prefer and high availability (HA) connections.
type FallbackConfig struct {
	Host      string // host (e.g. localhost) or path to unix domain socket directory (e.g. /private/tmp)
	Port      uint16
	TLSConfig *tls.Config // nil disables TLS
}
NetworkAddress converts a PostgreSQL host and port into network and address suitable for use with net.Dial.
func ( string,  uint16) (,  string) {
	if strings.HasPrefix(, "/") {
		 = "unix"
		 = filepath.Join(, ".s.PGSQL.") + strconv.FormatInt(int64(), 10)
	} else {
		 = "tcp"
		 = net.JoinHostPort(, strconv.Itoa(int()))
	}
	return , 
}
ParseConfig builds a *Config with similar behavior to the PostgreSQL standard C library libpq. It uses the same defaults as libpq (e.g. port=5432) and understands most PG* environment variables. ParseConfig closely matches the parsing behavior of libpq. connString may either be in URL format or keyword = value format (DSN style). See https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING for details. connString also may be empty to only read from the environment. If a password is not supplied it will attempt to read the .pgpass file. # Example DSN user=jack password=secret host=pg.example.com port=5432 dbname=mydb sslmode=verify-ca # Example URL postgres://jack:secret@pg.example.com:5432/mydb?sslmode=verify-ca The returned *Config may be modified. However, it is strongly recommended that any configuration that can be done through the connection string be done there. In particular the fields Host, Port, TLSConfig, and Fallbacks can be interdependent (e.g. TLSConfig needs knowledge of the host to validate the server certificate). These fields should not be modified individually. They should all be modified or all left unchanged. ParseConfig supports specifying multiple hosts in similar manner to libpq. Host and port may include comma separated values that will be tried in order. This can be used as part of a high availability system. See https://www.postgresql.org/docs/11/libpq-connect.html#LIBPQ-MULTIPLE-HOSTS for more information. # Example URL postgres://jack:secret@foo.example.com:5432,bar.example.com:5432/mydb ParseConfig currently recognizes the following environment variable and their parameter key word equivalents passed via database URL or DSN: PGHOST PGPORT PGDATABASE PGUSER PGPASSWORD PGPASSFILE PGSERVICE PGSERVICEFILE PGSSLMODE PGSSLCERT PGSSLKEY PGSSLROOTCERT PGAPPNAME PGCONNECT_TIMEOUT PGTARGETSESSIONATTRS See http://www.postgresql.org/docs/11/static/libpq-envars.html for details on the meaning of environment variables. See https://www.postgresql.org/docs/11/libpq-connect.html#LIBPQ-PARAMKEYWORDS for parameter key word names. They are usually but not always the environment variable name downcased and without the "PG" prefix. Important Security Notes: ParseConfig tries to match libpq behavior with regard to PGSSLMODE. This includes defaulting to "prefer" behavior if not set. See http://www.postgresql.org/docs/11/static/libpq-ssl.html#LIBPQ-SSL-PROTECTION for details on what level of security each sslmode provides. The sslmode "prefer" (the default), sslmode "allow", and multiple hosts are implemented via the Fallbacks field of the Config struct. If TLSConfig is manually changed it will not affect the fallbacks. For example, in the case of sslmode "prefer" this means it will first try the main Config settings which use TLS, then it will try the fallback which does not use TLS. This can lead to an unexpected unencrypted connection if the main TLS config is manually changed later but the unencrypted fallback is present. Ensure there are no stale fallbacks when manually setting TLCConfig. Other known differences with libpq: If a host name resolves into multiple addresses, libpq will try all addresses. pgconn will only try the first. When multiple hosts are specified, libpq allows them to have different passwords set via the .pgpass file. pgconn does not. In addition, ParseConfig accepts the following options: min_read_buffer_size The minimum size of the internal read buffer. Default 8192. servicefile libpq only reads servicefile from the PGSERVICEFILE environment variable. ParseConfig accepts servicefile as a part of the connection string.
func ( string) (*Config, error) {
	 := defaultSettings()
	 := parseEnvSettings()

	 := make(map[string]string)
	if  != "" {
connString may be a database URL or a DSN
		if strings.HasPrefix(, "postgres://") || strings.HasPrefix(, "postgresql://") {
			,  = parseURLSettings()
			if  != nil {
				return nil, &parseConfigError{connString: , msg: "failed to parse as URL", err: }
			}
		} else {
			,  = parseDSNSettings()
			if  != nil {
				return nil, &parseConfigError{connString: , msg: "failed to parse as DSN", err: }
			}
		}
	}

	 := mergeSettings(, , )
	if ,  := ["service"];  {
		,  := parseServiceSettings(["servicefile"], )
		if  != nil {
			return nil, &parseConfigError{connString: , msg: "failed to read service", err: }
		}

		 = mergeSettings(, , , )
	}

	,  := strconv.ParseInt(["min_read_buffer_size"], 10, 32)
	if  != nil {
		return nil, &parseConfigError{connString: , msg: "cannot parse min_read_buffer_size", err: }
	}

	 := &Config{
		createdByParseConfig: true,
		Database:             ["database"],
		User:                 ["user"],
		Password:             ["password"],
		RuntimeParams:        make(map[string]string),
		BuildFrontend:        makeDefaultBuildFrontendFunc(int()),
	}

	if ,  := ["connect_timeout"];  {
		,  := parseConnectTimeoutSetting()
		if  != nil {
			return nil, &parseConfigError{connString: , msg: "invalid connect_timeout", err: }
		}
		.ConnectTimeout = 
		.DialFunc = makeConnectTimeoutDialFunc()
	} else {
		 := makeDefaultDialer()
		.DialFunc = .DialContext
	}

	.LookupFunc = makeDefaultResolver().LookupHost

	 := map[string]struct{}{
		"host":                 struct{}{},
		"port":                 struct{}{},
		"database":             struct{}{},
		"user":                 struct{}{},
		"password":             struct{}{},
		"passfile":             struct{}{},
		"connect_timeout":      struct{}{},
		"sslmode":              struct{}{},
		"sslkey":               struct{}{},
		"sslcert":              struct{}{},
		"sslrootcert":          struct{}{},
		"target_session_attrs": struct{}{},
		"min_read_buffer_size": struct{}{},
		"service":              struct{}{},
		"servicefile":          struct{}{},
	}

	for ,  := range  {
		if ,  := [];  {
			continue
		}
		.RuntimeParams[] = 
	}

	 := []*FallbackConfig{}

	 := strings.Split(["host"], ",")
	 := strings.Split(["port"], ",")

	for ,  := range  {
		var  string
		if  < len() {
			 = []
		} else {
			 = [0]
		}

		,  := parsePort()
		if  != nil {
			return nil, &parseConfigError{connString: , msg: "invalid port", err: }
		}

		var  []*tls.Config
Ignore TLS settings if Unix domain socket like libpq
		if ,  := NetworkAddress(, );  == "unix" {
			 = append(, nil)
		} else {
			var  error
			,  = configTLS()
			if  != nil {
				return nil, &parseConfigError{connString: , msg: "failed to configure TLS", err: }
			}
		}

		for ,  := range  {
			 = append(, &FallbackConfig{
				Host:      ,
				Port:      ,
				TLSConfig: ,
			})
		}
	}

	.Host = [0].Host
	.Port = [0].Port
	.TLSConfig = [0].TLSConfig
	.Fallbacks = [1:]

	,  := pgpassfile.ReadPassfile(["passfile"])
	if  == nil {
		if .Password == "" {
			 := .Host
			if ,  := NetworkAddress(.Host, .Port);  == "unix" {
				 = "localhost"
			}

			.Password = .FindPassword(, strconv.Itoa(int(.Port)), .Database, .User)
		}
	}

	if ["target_session_attrs"] == "read-write" {
		.ValidateConnect = ValidateConnectTargetSessionAttrsReadWrite
	} else if ["target_session_attrs"] != "any" {
		return nil, &parseConfigError{connString: , msg: fmt.Sprintf("unknown target_session_attrs value: %v", ["target_session_attrs"])}
	}

	return , nil
}

func ( ...map[string]string) map[string]string {
	 := make(map[string]string)

	for ,  := range  {
		for ,  := range  {
			[] = 
		}
	}

	return 
}

func () map[string]string {
	 := make(map[string]string)

	 := map[string]string{
		"PGHOST":               "host",
		"PGPORT":               "port",
		"PGDATABASE":           "database",
		"PGUSER":               "user",
		"PGPASSWORD":           "password",
		"PGPASSFILE":           "passfile",
		"PGAPPNAME":            "application_name",
		"PGCONNECT_TIMEOUT":    "connect_timeout",
		"PGSSLMODE":            "sslmode",
		"PGSSLKEY":             "sslkey",
		"PGSSLCERT":            "sslcert",
		"PGSSLROOTCERT":        "sslrootcert",
		"PGTARGETSESSIONATTRS": "target_session_attrs",
		"PGSERVICE":            "service",
		"PGSERVICEFILE":        "servicefile",
	}

	for ,  := range  {
		 := os.Getenv()
		if  != "" {
			[] = 
		}
	}

	return 
}

func ( string) (map[string]string, error) {
	 := make(map[string]string)

	,  := url.Parse()
	if  != nil {
		return nil, 
	}

	if .User != nil {
		["user"] = .User.Username()
		if ,  := .User.Password();  {
			["password"] = 
		}
	}
Handle multiple host:port's in url.Host by splitting them into host,host,host and port,port,port.
	var  []string
	var  []string
	for ,  := range strings.Split(.Host, ",") {
		if  == "" {
			continue
		}
		if isIPOnly() {
			 = append(, strings.Trim(, "[]"))
			continue
		}
		, ,  := net.SplitHostPort()
		if  != nil {
			return nil, fmt.Errorf("failed to split host:port in '%s', err: %w", , )
		}
		 = append(, )
		 = append(, )
	}
	if len() > 0 {
		["host"] = strings.Join(, ",")
	}
	if len() > 0 {
		["port"] = strings.Join(, ",")
	}

	 := strings.TrimLeft(.Path, "/")
	if  != "" {
		["database"] = 
	}

	for ,  := range .Query() {
		[] = [0]
	}

	return , nil
}

func ( string) bool {
	return net.ParseIP(strings.Trim(, "[]")) != nil || !strings.Contains(, ":")
}

var asciiSpace = [256]uint8{'\t': 1, '\n': 1, '\v': 1, '\f': 1, '\r': 1, ' ': 1}

func ( string) (map[string]string, error) {
	 := make(map[string]string)

	 := map[string]string{
		"dbname": "database",
	}

	for len() > 0 {
		var ,  string
		 := strings.IndexRune(, '=')
		if  < 0 {
			return nil, errors.New("invalid dsn")
		}

		 = strings.Trim([:], " \t\n\r\v\f")
		 = strings.TrimLeft([+1:], " \t\n\r\v\f")
		if len() == 0 {
		} else if [0] != '\'' {
			 := 0
			for ;  < len(); ++ {
				if asciiSpace[[]] == 1 {
					break
				}
				if [] == '\\' {
					++
					if  == len() {
						return nil, errors.New("invalid backslash")
					}
				}
			}
			 = strings.Replace(strings.Replace([:], "\\\\", "\\", -1), "\\'", "'", -1)
			if  == len() {
				 = ""
			} else {
				 = [+1:]
			}
		} else { // quoted string
			 = [1:]
			 := 0
			for ;  < len(); ++ {
				if [] == '\'' {
					break
				}
				if [] == '\\' {
					++
				}
			}
			if  == len() {
				return nil, errors.New("unterminated quoted string in connection info string")
			}
			 = strings.Replace(strings.Replace([:], "\\\\", "\\", -1), "\\'", "'", -1)
			if  == len() {
				 = ""
			} else {
				 = [+1:]
			}
		}

		if ,  := [];  {
			 = 
		}

		if  == "" {
			return nil, errors.New("invalid dsn")
		}

		[] = 
	}

	return , nil
}

func (,  string) (map[string]string, error) {
	,  := pgservicefile.ReadServicefile()
	if  != nil {
		return nil, fmt.Errorf("failed to read service file: %v", )
	}

	,  := .GetService()
	if  != nil {
		return nil, fmt.Errorf("unable to find service: %v", )
	}

	 := map[string]string{
		"dbname": "database",
	}

	 := make(map[string]string, len(.Settings))
	for ,  := range .Settings {
		if ,  := [];  {
			 = 
		}
		[] = 
	}

	return , nil
}
configTLS uses libpq's TLS parameters to construct []*tls.Config. It is necessary to allow returning multiple TLS configs as sslmode "allow" and "prefer" allow fallback.
func ( map[string]string) ([]*tls.Config, error) {
	 := ["host"]
	 := ["sslmode"]
	 := ["sslrootcert"]
	 := ["sslcert"]
	 := ["sslkey"]
Match libpq default behavior
	if  == "" {
		 = "prefer"
	}

	 := &tls.Config{}

	switch  {
	case "disable":
		return []*tls.Config{nil}, nil
	case "allow", "prefer":
		.InsecureSkipVerify = true
According to PostgreSQL documentation, if a root CA file exists, the behavior of sslmode=require should be the same as that of verify-ca See https://www.postgresql.org/docs/12/libpq-ssl.html
		if  != "" {
			goto 
		}
		.InsecureSkipVerify = true
		break
	:
		fallthrough
Don't perform the default certificate verification because it will verify the hostname. Instead, verify the server's certificate chain ourselves in VerifyPeerCertificate and ignore the server name. This emulates libpq's verify-ca behavior. See https://github.com/golang/go/issues/21971#issuecomment-332693931 and https://pkg.go.dev/crypto/tls?tab=doc#example-Config-VerifyPeerCertificate for more info.
		.InsecureSkipVerify = true
		.VerifyPeerCertificate = func( [][]byte,  [][]*x509.Certificate) error {
			 := make([]*x509.Certificate, len())
			for ,  := range  {
				,  := x509.ParseCertificate()
				if  != nil {
					return errors.New("failed to parse certificate from server: " + .Error())
				}
				[] = 
			}
Leave DNSName empty to skip hostname verification.
Skip the first cert because it's the leaf. All others are intermediates.
			for ,  := range [1:] {
				.Intermediates.AddCert()
			}
			,  := [0].Verify()
			return 
		}
	case "verify-full":
		.ServerName = 
	default:
		return nil, errors.New("sslmode is invalid")
	}

	if  != "" {
		 := x509.NewCertPool()

		 := 
		,  := ioutil.ReadFile()
		if  != nil {
			return nil, fmt.Errorf("unable to read CA file: %w", )
		}

		if !.AppendCertsFromPEM() {
			return nil, errors.New("unable to add CA to cert pool")
		}

		.RootCAs = 
		.ClientCAs = 
	}

	if ( != "" &&  == "") || ( == "" &&  != "") {
		return nil, errors.New(`both "sslcert" and "sslkey" are required`)
	}

	if  != "" &&  != "" {
		,  := tls.LoadX509KeyPair(, )
		if  != nil {
			return nil, fmt.Errorf("unable to read cert: %w", )
		}

		.Certificates = []tls.Certificate{}
	}

	switch  {
	case "allow":
		return []*tls.Config{nil, }, nil
	case "prefer":
		return []*tls.Config{, nil}, nil
	case "require", "verify-ca", "verify-full":
		return []*tls.Config{}, nil
	default:
		panic("BUG: bad sslmode should already have been caught")
	}
}

func ( string) (uint16, error) {
	,  := strconv.ParseUint(, 10, 16)
	if  != nil {
		return 0, 
	}
	if  < 1 ||  > math.MaxUint16 {
		return 0, errors.New("outside range")
	}
	return uint16(), nil
}

func () *net.Dialer {
	return &net.Dialer{KeepAlive: 5 * time.Minute}
}

func () *net.Resolver {
	return net.DefaultResolver
}

func ( int) BuildFrontendFunc {
	return func( io.Reader,  io.Writer) Frontend {
		,  := chunkreader.NewConfig(, chunkreader.Config{MinBufLen: })
		if  != nil {
			panic(fmt.Sprintf("BUG: chunkreader.NewConfig failed: %v", ))
		}
		 := pgproto3.NewFrontend(, )

		return 
	}
}

func ( string) (time.Duration, error) {
	,  := strconv.ParseInt(, 10, 64)
	if  != nil {
		return 0, 
	}
	if  < 0 {
		return 0, errors.New("negative timeout")
	}
	return time.Duration() * time.Second, nil
}

func ( time.Duration) DialFunc {
	 := makeDefaultDialer()
	.Timeout = 
	return .DialContext
}
ValidateConnectTargetSessionAttrsReadWrite is an ValidateConnectFunc that implements libpq compatible target_session_attrs=read-write.
func ( context.Context,  *PgConn) error {
	 := .ExecParams(, "show transaction_read_only", nil, nil, nil, nil).Read()
	if .Err != nil {
		return .Err
	}

	if string(.Rows[0][0]) == "on" {
		return errors.New("read only connection")
	}

	return nil