package pgconnimport ()type (AfterConnectFuncfunc(ctx context.Context, pgconn *PgConn) errorValidateConnectFuncfunc(ctx context.Context, pgconn *PgConn) errorGetSSLPasswordFuncfunc(ctx context.Context) string)// 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.typeConfigstruct {Hoststring// host (e.g. localhost) or absolute path to unix domain socket directory (e.g. /private/tmp)Portuint16DatabasestringUserstringPasswordstringTLSConfig *tls.Config// nil disables TLSConnectTimeouttime.DurationDialFuncDialFunc// e.g. net.Dialer.DialContextLookupFuncLookupFunc// e.g. net.Resolver.LookupHostBuildFrontendBuildFrontendFunc// BuildContextWatcherHandler is called to create a ContextWatcherHandler for a connection. The handler is called // when a context passed to a PgConn method is canceled.BuildContextWatcherHandlerfunc(*PgConn) ctxwatch.HandlerRuntimeParamsmap[string]string// Run-time parameters to set on connection as session default values (e.g. search_path or application_name)KerberosSrvNamestringKerberosSpnstringFallbacks []*FallbackConfigSSLNegotiationstring// sslnegotiation=postgres or sslnegotiation=direct// AfterNetConnect is called after the network connection, including TLS if applicable, is established but before any // PostgreSQL protocol communication. It takes the established net.Conn and returns a net.Conn that will be used in // its place. It can be used to wrap the net.Conn (e.g. for logging, diagnostics, or testing). Its functionality has // some overlap with DialFunc. However, DialFunc takes place before TLS is established and cannot be used to control // the final net.Conn used for PostgreSQL protocol communication while AfterNetConnect can.AfterNetConnectfunc(ctx context.Context, config *Config, conn net.Conn) (net.Conn, error)// 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.ValidateConnectValidateConnectFunc// 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.AfterConnectAfterConnectFunc// OnNotice is a callback function called when a notice response is received.OnNoticeNoticeHandler// OnNotification is a callback function called when a notification from the LISTEN/NOTIFY system is received.OnNotificationNotificationHandler// OnPgError is a callback function called when a Postgres error is received by the server. The default handler will close // the connection on any FATAL errors. If you override this handler you should call the previously set handler or ensure // that you close on FATAL errors by returning false.OnPgErrorPgErrorHandler// OAuthTokenProvider is a function that returns an OAuth token for authentication. If set, it will be used for // OAUTHBEARER SASL authentication when the server requests it.OAuthTokenProviderfunc(context.Context) (string, error)// MinProtocolVersion is the minimum acceptable PostgreSQL protocol version. // If the server does not support at least this version, the connection will fail. // Valid values: "3.0", "3.2", "latest". Defaults to "3.0".MinProtocolVersionstring// MaxProtocolVersion is the maximum PostgreSQL protocol version to request from the server. // Valid values: "3.0", "3.2", "latest". Defaults to "3.0" for compatibility.MaxProtocolVersionstring// ChannelBinding is the channel_binding parameter for SCRAM-SHA-256-PLUS authentication. // Valid values: "disable", "prefer", "require". Defaults to "prefer".ChannelBindingstringcreatedByParseConfigbool// Used to enforce created by ParseConfig rule.}// ParseConfigOptions contains options that control how a config is built such as GetSSLPassword.typeParseConfigOptionsstruct {// GetSSLPassword gets the password to decrypt a SSL client certificate. This is analogous to the libpq function // PQsetSSLKeyPassHook_OpenSSL.GetSSLPasswordGetSSLPasswordFunc}// 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))maps.Copy(.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.typeFallbackConfigstruct {Hoststring// host (e.g. localhost) or path to unix domain socket directory (e.g. /private/tmp)Portuint16TLSConfig *tls.Config// nil disables TLS}// connectOneConfig is the configuration for a single attempt to connect to a single host.typeconnectOneConfigstruct {networkstringaddressstringoriginalHostnamestring// original hostname before resolvingtlsConfig *tls.Config// nil disables TLS}// isAbsolutePath checks if the provided value is an absolute path either// beginning with a forward slash (as on Linux-based systems) or with a capital// letter A-Z followed by a colon and a backslash, e.g., "C:\", (as on Windows).func ( string) bool { := func( string) bool {iflen() < 3 {returnfalse } := [0] := [1] := [2]if >= 'A' && <= 'Z' && == ':' && == '\\' {returntrue }returnfalse }returnstrings.HasPrefix(, "/") || ()}// NetworkAddress converts a PostgreSQL host and port into network and address suitable for use with// net.Dial.func ( string, uint16) (, string) {ifisAbsolutePath() { = "unix" = filepath.Join(, ".s.PGSQL.") + strconv.FormatInt(int64(), 10) } else { = "tcp" = net.JoinHostPort(, strconv.Itoa(int())) }return , }// ParseConfig builds a *Config from connString 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. 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 Keyword/Value// 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/current/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 keyword/value://// PGHOST// PGPORT// PGDATABASE// PGUSER// PGPASSWORD// PGPASSFILE// PGSERVICE// PGSERVICEFILE// PGSSLMODE// PGSSLCERT// PGSSLKEY// PGSSLROOTCERT// PGSSLPASSWORD// PGOPTIONS// PGAPPNAME// PGCONNECT_TIMEOUT// PGTARGETSESSIONATTRS// PGTZ// PGMINPROTOCOLVERSION// PGMAXPROTOCOLVERSION//// See http://www.postgresql.org/docs/current/static/libpq-envars.html for details on the meaning of environment variables.//// See https://www.postgresql.org/docs/current/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/current/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// TLSConfig.//// Other known differences with libpq://// 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://// - servicefile.// libpq only reads servicefile from the PGSERVICEFILE environment variable. ParseConfig accepts servicefile as a// part of the connection string.func ( string) (*Config, error) {varParseConfigOptionsreturnParseConfigWithOptions(, )}// ParseConfigWithOptions builds a *Config from connString and options with similar behavior to the PostgreSQL standard// C library libpq. options contains settings that cannot be specified in a connString such as providing a function to// get the SSL password.func ( string, ParseConfigOptions) (*Config, error) { := defaultSettings() := parseEnvSettings() := make(map[string]string)if != "" {varerror// connString may be a database URL or in PostgreSQL keyword/value formatifstrings.HasPrefix(, "postgres://") || strings.HasPrefix(, "postgresql://") { , = parseURLSettings()if != nil {returnnil, &ParseConfigError{ConnString: , msg: "failed to parse as URL", err: } } } else { , = parseKeywordValueSettings()if != nil {returnnil, &ParseConfigError{ConnString: , msg: "failed to parse as keyword/value", err: } } } } := mergeSettings(, , )if , := ["service"]; { , := parseServiceSettings(["servicefile"], )if != nil {returnnil, &ParseConfigError{ConnString: , msg: "failed to read service", err: } } = mergeSettings(, , , ) } := &Config{createdByParseConfig: true,Database: ["database"],User: ["user"],Password: ["password"],RuntimeParams: make(map[string]string),BuildFrontend: func( io.Reader, io.Writer) *pgproto3.Frontend {returnpgproto3.NewFrontend(, ) },BuildContextWatcherHandler: func( *PgConn) ctxwatch.Handler {return &DeadlineContextWatcherHandler{Conn: .conn} },OnPgError: func( *PgConn, *PgError) bool {// we want to automatically close any fatal errorsifstrings.EqualFold(.Severity, "FATAL") {returnfalse }returntrue }, }if , := ["connect_timeout"]; { , := parseConnectTimeoutSetting()if != nil {returnnil, &ParseConfigError{ConnString: , msg: "invalid connect_timeout", err: } } .ConnectTimeout = .DialFunc = makeConnectTimeoutDialFunc() } else { := makeDefaultDialer() .DialFunc = .DialContext } .LookupFunc = makeDefaultResolver().LookupHost := map[string]struct{}{"host": {},"port": {},"database": {},"user": {},"password": {},"passfile": {},"connect_timeout": {},"sslmode": {},"sslkey": {},"sslcert": {},"sslrootcert": {},"sslnegotiation": {},"sslpassword": {},"sslsni": {},"krbspn": {},"krbsrvname": {},"target_session_attrs": {},"service": {},"servicefile": {},"min_protocol_version": {},"max_protocol_version": {},"channel_binding": {}, }// Adding kerberos configurationif , := ["krbsrvname"]; { .KerberosSrvName = ["krbsrvname"] }if , := ["krbspn"]; { .KerberosSpn = ["krbspn"] }for , := range {if , := []; {continue } .RuntimeParams[] = } := []*FallbackConfig{} := strings.Split(["host"], ",") := strings.Split(["port"], ",")for , := range {varstringif < len() { = [] } else { = [0] } , := parsePort()if != nil {returnnil, &ParseConfigError{ConnString: , msg: "invalid port", err: } }var []*tls.Config// Ignore TLS settings if Unix domain socket like libpqif , := NetworkAddress(, ); == "unix" { = append(, nil) } else {varerror , = configTLS(, , )if != nil {returnnil, &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:] .SSLNegotiation = ["sslnegotiation"] , := pgpassfile.ReadPassfile(["passfile"])if == nil {if .Password == "" { := .Hostif , := NetworkAddress(.Host, .Port); == "unix" { = "localhost" } .Password = .FindPassword(, strconv.Itoa(int(.Port)), .Database, .User) } }switch := ["target_session_attrs"]; {case"read-write": .ValidateConnect = ValidateConnectTargetSessionAttrsReadWritecase"read-only": .ValidateConnect = ValidateConnectTargetSessionAttrsReadOnlycase"primary": .ValidateConnect = ValidateConnectTargetSessionAttrsPrimarycase"standby": .ValidateConnect = ValidateConnectTargetSessionAttrsStandbycase"prefer-standby": .ValidateConnect = ValidateConnectTargetSessionAttrsPreferStandbycase"any":// do nothingdefault:returnnil, &ParseConfigError{ConnString: , msg: fmt.Sprintf("unknown target_session_attrs value: %v", )} } , := parseProtocolVersion(["min_protocol_version"])if != nil {returnnil, &ParseConfigError{ConnString: , msg: "invalid min_protocol_version", err: } } , := parseProtocolVersion(["max_protocol_version"])if != nil {returnnil, &ParseConfigError{ConnString: , msg: "invalid max_protocol_version", err: } }if > {returnnil, &ParseConfigError{ConnString: , msg: "min_protocol_version cannot be greater than max_protocol_version"} } .MinProtocolVersion = ["min_protocol_version"] .MaxProtocolVersion = ["max_protocol_version"]if .MinProtocolVersion == "" { .MinProtocolVersion = "3.0" }if .MaxProtocolVersion == "" { .MaxProtocolVersion = "3.0" }switch := ["channel_binding"]; {case"", "prefer": .ChannelBinding = "prefer"case"disable": .ChannelBinding = "disable"case"require": .ChannelBinding = "require"default:returnnil, &ParseConfigError{ConnString: , msg: fmt.Sprintf("unknown channel_binding value: %v", )} }return , nil}func ( ...map[string]string) map[string]string { := make(map[string]string)for , := range {maps.Copy(, ) }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","PGSSLSNI": "sslsni","PGSSLROOTCERT": "sslrootcert","PGSSLPASSWORD": "sslpassword","PGSSLNEGOTIATION": "sslnegotiation","PGTARGETSESSIONATTRS": "target_session_attrs","PGSERVICE": "service","PGSERVICEFILE": "servicefile","PGTZ": "timezone","PGOPTIONS": "options","PGMINPROTOCOLVERSION": "min_protocol_version","PGMAXPROTOCOLVERSION": "max_protocol_version", }for , := range { := os.Getenv()if != "" { [] = } }return}func ( string) (map[string]string, error) { := make(map[string]string) , := url.Parse()if != nil {if := new(url.Error); errors.As(, &) {returnnil, .Err }returnnil, }if .User != nil {if := .User.Username(); != "" { ["user"] = }if , := .User.Password(); { ["password"] = } }// Handle multiple host:port's in url.Host by splitting them into host,host,host and port,port,port.var []stringvar []stringfor := rangestrings.SplitSeq(.Host, ",") {if == "" {continue }ifisIPOnly() { = append(, strings.Trim(, "[]"))continue } , , := net.SplitHostPort()if != nil {returnnil, fmt.Errorf("failed to split host:port in '%s', err: %w", , ) }if != "" { = append(, ) }if != "" { = append(, ) } }iflen() > 0 { ["host"] = strings.Join(, ",") }iflen() > 0 { ["port"] = strings.Join(, ",") } := strings.TrimLeft(.Path, "/")if != "" { ["database"] = } := map[string]string{"dbname": "database", }for , := range .Query() {if , := []; { = } [] = [0] }return , nil}func ( string) bool {returnnet.ParseIP(strings.Trim(, "[]")) != nil || !strings.Contains(, ":")}varasciiSpace = [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", }forlen() > 0 {var , string := strings.IndexRune(, '=')if < 0 {returnnil, errors.New("invalid keyword/value") } = strings.Trim([:], " \t\n\r\v\f") = strings.TrimLeft([+1:], " \t\n\r\v\f")iflen() == 0 { } elseif [0] != '\'' { := 0for ; < len(); ++ {ifasciiSpace[[]] == 1 {break }if [] == '\\' { ++if == len() {returnnil, errors.New("invalid backslash") } } } = strings.Replace(strings.Replace([:], "\\\\", "\\", -1), "\\'", "'", -1)if == len() { = "" } else { = [+1:] } } else { // quoted string = [1:] := 0for ; < len(); ++ {if [] == '\'' {break }if [] == '\\' { ++ } }if == len() {returnnil, errors.New("unterminated quoted string in connection info string") } = strings.Replace(strings.Replace([:], "\\\\", "\\", -1), "\\'", "'", -1)if == len() { = "" } else { = [+1:] } }if , := []; { = }if == "" {returnnil, errors.New("invalid keyword/value") }if == "user" && == "" {continue } [] = }return , nil}func (, string) (map[string]string, error) { , := pgservicefile.ReadServicefile()if != nil {returnnil, fmt.Errorf("failed to read service file: %v", ) } , := .GetService()if != nil {returnnil, 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, string, ParseConfigOptions) ([]*tls.Config, error) { := := ["sslmode"] := ["sslrootcert"] := ["sslcert"] := ["sslkey"] := ["sslpassword"] := ["sslsni"] := ["sslnegotiation"]// Match libpq default behaviorif == "" { = "prefer" }if == "" { = "1" } := &tls.Config{}if == "direct" { .NextProtos = []string{"postgresql"}if == "prefer" { = "require" } }if != "" {var *x509.CertPoolif == "system" {varerror , = x509.SystemCertPool()if != nil {returnnil, fmt.Errorf("unable to load system certificate pool: %w", ) } = "verify-full" } else { = x509.NewCertPool() := , := os.ReadFile()if != nil {returnnil, fmt.Errorf("unable to read CA file: %w", ) }if !.AppendCertsFromPEM() {returnnil, errors.New("unable to add CA to cert pool") } } .RootCAs = .ClientCAs = }switch {case"disable":return []*tls.Config{nil}, nilcase"allow", "prefer": .InsecureSkipVerify = truecase"require":// 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/current/libpq-ssl.htmlif != "" {goto } .InsecureSkipVerify = truebreak :fallthroughcase"verify-ca":// 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 {returnerrors.New("failed to parse certificate from server: " + .Error()) } [] = }// Leave DNSName empty to skip hostname verification. := x509.VerifyOptions{Roots: .RootCAs,Intermediates: x509.NewCertPool(), }// 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:returnnil, errors.New("sslmode is invalid") }if ( != "" && == "") || ( == "" && != "") {returnnil, errors.New(`both "sslcert" and "sslkey" are required`) }if != "" && != "" { , := os.ReadFile()if != nil {returnnil, fmt.Errorf("unable to read sslkey: %w", ) } , := pem.Decode()if == nil {returnnil, errors.New("failed to decode sslkey") }var []bytevar []bytevarerror// If PEM is encrypted, attempt to decrypt using pass phraseifx509.IsEncryptedPEMBlock() {// Attempt decryption with pass phrase // NOTE: only supports RSA (PKCS#1)if != "" { , = x509.DecryptPEMBlock(, []byte()) //nolint:ineffassign }// if sslpassword not provided or has decryption error when use it // try to find sslpassword with callback functionif == "" || != nil {if .GetSSLPassword != nil { = .GetSSLPassword(context.Background()) }if == "" {returnnil, fmt.Errorf("unable to find sslpassword") } } , = x509.DecryptPEMBlock(, []byte())// Should we also provide warning for PKCS#1 needed?if != nil {returnnil, fmt.Errorf("unable to decrypt key: %w", ) } := pem.Block{Type: "RSA PRIVATE KEY",Bytes: , } = pem.EncodeToMemory(&) } else { = pem.EncodeToMemory() } , := os.ReadFile()if != nil {returnnil, fmt.Errorf("unable to read cert: %w", ) } , := tls.X509KeyPair(, )if != nil {returnnil, fmt.Errorf("unable to load cert: %w", ) } .Certificates = []tls.Certificate{} }// Set Server Name Indication (SNI), if enabled by connection parameters. // Per RFC 6066, do not set it if the host is a literal IP address (IPv4 // or IPv6).if == "1" && net.ParseIP() == nil { .ServerName = }switch {case"allow":return []*tls.Config{nil, }, nilcase"prefer":return []*tls.Config{, nil}, nilcase"require", "verify-ca", "verify-full":return []*tls.Config{}, nildefault:panic("BUG: bad sslmode should already have been caught") }}func ( string) (uint16, error) { , := strconv.ParseUint(, 10, 16)if != nil {return0, }if < 1 || > math.MaxUint16 {return0, errors.New("outside range") }returnuint16(), nil}func () *net.Dialer {// rely on GOLANG KeepAlive settingsreturn &net.Dialer{}}func () *net.Resolver {returnnet.DefaultResolver}func ( string) (time.Duration, error) { , := strconv.ParseInt(, 10, 64)if != nil {return0, }if < 0 {return0, errors.New("negative timeout") }returntime.Duration() * time.Second, nil}func ( time.Duration) DialFunc { := makeDefaultDialer() .Timeout = return .DialContext}// ValidateConnectTargetSessionAttrsReadWrite is a ValidateConnectFunc that implements libpq compatible// target_session_attrs=read-write.func ( context.Context, *PgConn) error { , := .Exec(, "show transaction_read_only").ReadAll()if != nil {return }ifstring([0].Rows[0][0]) == "on" {returnerrors.New("read only connection") }returnnil}// ValidateConnectTargetSessionAttrsReadOnly is a ValidateConnectFunc that implements libpq compatible// target_session_attrs=read-only.func ( context.Context, *PgConn) error { , := .Exec(, "show transaction_read_only").ReadAll()if != nil {return }ifstring([0].Rows[0][0]) != "on" {returnerrors.New("connection is not read only") }returnnil}// ValidateConnectTargetSessionAttrsStandby is a ValidateConnectFunc that implements libpq compatible// target_session_attrs=standby.func ( context.Context, *PgConn) error { , := .Exec(, "select pg_is_in_recovery()").ReadAll()if != nil {return }ifstring([0].Rows[0][0]) != "t" {returnerrors.New("server is not in hot standby mode") }returnnil}// ValidateConnectTargetSessionAttrsPrimary is a ValidateConnectFunc that implements libpq compatible// target_session_attrs=primary.func ( context.Context, *PgConn) error { , := .Exec(, "select pg_is_in_recovery()").ReadAll()if != nil {return }ifstring([0].Rows[0][0]) == "t" {returnerrors.New("server is in standby mode") }returnnil}// ValidateConnectTargetSessionAttrsPreferStandby is a ValidateConnectFunc that implements libpq compatible// target_session_attrs=prefer-standby.func ( context.Context, *PgConn) error { , := .Exec(, "select pg_is_in_recovery()").ReadAll()if != nil {return }ifstring([0].Rows[0][0]) != "t" {return &NotPreferredError{err: errors.New("server is not in hot standby mode")} }returnnil}func ( string) (uint32, error) {switch {case"", "3.0":returnpgproto3.ProtocolVersion30, nilcase"3.2", "latest":returnpgproto3.ProtocolVersion32, nildefault:return0, fmt.Errorf("invalid protocol version: %q", ) }}
The pages are generated with Goldsv0.7.6. (GOOS=linux GOARCH=amd64)