mirror of https://github.com/jackc/pgx.git
Support comma separated hosts and ports like libpq
Also add test and fix the fallback config implementation.pull/483/head
parent
c672c0d595
commit
afd3583558
114
pgconn/config.go
114
pgconn/config.go
|
@ -55,21 +55,23 @@ func NetworkAddress(host string, port uint16) (network, address string) {
|
||||||
return network, address
|
return network, address
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseConfig builds a []*Config with similar behavior to the PostgreSQL standard C library libpq.
|
// ParseConfig builds a []*Config with similar behavior to the PostgreSQL standard C library libpq. It uses the same
|
||||||
// It uses the same defaults as libpq (e.g. port=5432) and understands most PG* environment
|
// defaults as libpq (e.g. port=5432) and understands most PG* environment variables. connString may be a URL or a DSN.
|
||||||
// variables. connString may be a URL or a DSN. It also may be empty to only read from the
|
// It also may be empty to only read from the environment. If a password is not supplied it will attempt to read the
|
||||||
// environment. If a password is not supplied it will attempt to read the .pgpass file.
|
// .pgpass file.
|
||||||
//
|
//
|
||||||
// Example DSN: "user=jack password=secret host=1.2.3.4 port=5432 dbname=mydb sslmode=verify-ca"
|
// Example DSN: "user=jack password=secret host=pg.example.com port=5432 dbname=mydb sslmode=verify-ca"
|
||||||
//
|
//
|
||||||
// Example URL: "postgres://jack:secret@1.2.3.4:5432/mydb?sslmode=verify-ca"
|
// Example URL: "postgres://jack:secret@pg.example.com:5432/mydb?sslmode=verify-ca"
|
||||||
//
|
//
|
||||||
// Multiple configs may be returned due to sslmode settings with fallback options (e.g.
|
// ParseConfig supports specifying multiple hosts in similar manner to libpq. Host and port may include comma separated
|
||||||
// sslmode=prefer). Future implementations may also support multiple hosts
|
// 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).
|
// https://www.postgresql.org/docs/11/libpq-connect.html#LIBPQ-MULTIPLE-HOSTS for more information.
|
||||||
//
|
//
|
||||||
// ParseConfig currently recognizes the following environment variable and their parameter key word
|
// Example URL: "postgres://jack:secret@foo.example.com:5432,bar.example.com:5432/mydb"
|
||||||
// equivalents passed via database URL or DSN:
|
//
|
||||||
|
// ParseConfig currently recognizes the following environment variable and their parameter key word equivalents passed
|
||||||
|
// via database URL or DSN:
|
||||||
//
|
//
|
||||||
// PGHOST
|
// PGHOST
|
||||||
// PGPORT
|
// PGPORT
|
||||||
|
@ -84,20 +86,18 @@ func NetworkAddress(host string, port uint16) (network, address string) {
|
||||||
// PGAPPNAME
|
// PGAPPNAME
|
||||||
// PGCONNECT_TIMEOUT
|
// PGCONNECT_TIMEOUT
|
||||||
//
|
//
|
||||||
// See http://www.postgresql.org/docs/11/static/libpq-envars.html for details on the meaning of
|
// See http://www.postgresql.org/docs/11/static/libpq-envars.html for details on the meaning of environment variables.
|
||||||
// environment variables.
|
|
||||||
//
|
//
|
||||||
// See https://www.postgresql.org/docs/11/libpq-connect.html#LIBPQ-PARAMKEYWORDS for parameter key
|
// See https://www.postgresql.org/docs/11/libpq-connect.html#LIBPQ-PARAMKEYWORDS for parameter key word names. They are
|
||||||
// word names. They are usually but not always the environment variable name downcased and without
|
// usually but not always the environment variable name downcased and without the "PG" prefix.
|
||||||
// the "PG" prefix.
|
|
||||||
//
|
//
|
||||||
// Important TLS Security Notes:
|
// Important TLS Security Notes:
|
||||||
//
|
//
|
||||||
// ParseConfig tries to match libpq behavior with regard to PGSSLMODE. This includes defaulting to
|
// ParseConfig tries to match libpq behavior with regard to PGSSLMODE. This includes defaulting to "prefer" behavior if
|
||||||
// "prefer" behavior if not set.
|
// not set.
|
||||||
//
|
//
|
||||||
// See http://www.postgresql.org/docs/11/static/libpq-ssl.html#LIBPQ-SSL-PROTECTION for details on
|
// See http://www.postgresql.org/docs/11/static/libpq-ssl.html#LIBPQ-SSL-PROTECTION for details on what level of
|
||||||
// what level of security each sslmode provides.
|
// security each sslmode provides.
|
||||||
//
|
//
|
||||||
// "verify-ca" mode currently is treated as "verify-full". e.g. It has stronger
|
// "verify-ca" mode currently is treated as "verify-full". e.g. It has stronger
|
||||||
// security guarantees than it would with libpq. Do not rely on this behavior as it
|
// security guarantees than it would with libpq. Do not rely on this behavior as it
|
||||||
|
@ -110,12 +110,7 @@ func ParseConfig(connString string) (*Config, error) {
|
||||||
if connString != "" {
|
if connString != "" {
|
||||||
// connString may be a database URL or a DSN
|
// connString may be a database URL or a DSN
|
||||||
if strings.HasPrefix(connString, "postgres://") {
|
if strings.HasPrefix(connString, "postgres://") {
|
||||||
url, err := url.Parse(connString)
|
err := addURLSettings(settings, connString)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = addURLSettings(settings, url)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -128,19 +123,12 @@ func ParseConfig(connString string) (*Config, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
config := &Config{
|
config := &Config{
|
||||||
Host: settings["host"],
|
|
||||||
Database: settings["database"],
|
Database: settings["database"],
|
||||||
User: settings["user"],
|
User: settings["user"],
|
||||||
Password: settings["password"],
|
Password: settings["password"],
|
||||||
RuntimeParams: make(map[string]string),
|
RuntimeParams: make(map[string]string),
|
||||||
}
|
}
|
||||||
|
|
||||||
if port, err := parsePort(settings["port"]); err == nil {
|
|
||||||
config.Port = port
|
|
||||||
} else {
|
|
||||||
return nil, fmt.Errorf("invalid port: %v", settings["port"])
|
|
||||||
}
|
|
||||||
|
|
||||||
if connectTimeout, present := settings["connect_timeout"]; present {
|
if connectTimeout, present := settings["connect_timeout"]; present {
|
||||||
dialFunc, err := makeConnectTimeoutDialFunc(connectTimeout)
|
dialFunc, err := makeConnectTimeoutDialFunc(connectTimeout)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -173,10 +161,28 @@ func ParseConfig(connString string) (*Config, error) {
|
||||||
config.RuntimeParams[k] = v
|
config.RuntimeParams[k] = v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fallbacks := []*FallbackConfig{}
|
||||||
|
|
||||||
|
hosts := strings.Split(settings["host"], ",")
|
||||||
|
ports := strings.Split(settings["port"], ",")
|
||||||
|
|
||||||
|
for i, host := range hosts {
|
||||||
|
var portStr string
|
||||||
|
if i < len(ports) {
|
||||||
|
portStr = ports[i]
|
||||||
|
} else {
|
||||||
|
portStr = ports[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
port, err := parsePort(portStr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("invalid port: %v", settings["port"])
|
||||||
|
}
|
||||||
|
|
||||||
var tlsConfigs []*tls.Config
|
var tlsConfigs []*tls.Config
|
||||||
|
|
||||||
// Ignore TLS settings if Unix domain socket like libpq
|
// Ignore TLS settings if Unix domain socket like libpq
|
||||||
if network, _ := NetworkAddress(config.Host, config.Port); network == "unix" {
|
if network, _ := NetworkAddress(host, port); network == "unix" {
|
||||||
tlsConfigs = append(tlsConfigs, nil)
|
tlsConfigs = append(tlsConfigs, nil)
|
||||||
} else {
|
} else {
|
||||||
var err error
|
var err error
|
||||||
|
@ -186,15 +192,19 @@ func ParseConfig(connString string) (*Config, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
config.TLSConfig = tlsConfigs[0]
|
for _, tlsConfig := range tlsConfigs {
|
||||||
|
fallbacks = append(fallbacks, &FallbackConfig{
|
||||||
for _, tlsConfig := range tlsConfigs[1:] {
|
Host: host,
|
||||||
config.Fallbacks = append(config.Fallbacks, &FallbackConfig{
|
Port: port,
|
||||||
Host: config.Host,
|
|
||||||
Port: config.Port,
|
|
||||||
TLSConfig: tlsConfig,
|
TLSConfig: tlsConfig,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
config.Host = fallbacks[0].Host
|
||||||
|
config.Port = fallbacks[0].Port
|
||||||
|
config.TLSConfig = fallbacks[0].TLSConfig
|
||||||
|
config.Fallbacks = fallbacks[1:]
|
||||||
|
|
||||||
passfile, err := pgpassfile.ReadPassfile(settings["passfile"])
|
passfile, err := pgpassfile.ReadPassfile(settings["passfile"])
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
@ -272,7 +282,12 @@ func addEnvSettings(settings map[string]string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func addURLSettings(settings map[string]string, url *url.URL) error {
|
func addURLSettings(settings map[string]string, connString string) error {
|
||||||
|
url, err := url.Parse(connString)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
if url.User != nil {
|
if url.User != nil {
|
||||||
settings["user"] = url.User.Username()
|
settings["user"] = url.User.Username()
|
||||||
if password, present := url.User.Password(); present {
|
if password, present := url.User.Password(); present {
|
||||||
|
@ -280,12 +295,23 @@ func addURLSettings(settings map[string]string, url *url.URL) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
parts := strings.SplitN(url.Host, ":", 2)
|
// Handle multiple host:port's in url.Host by splitting them into host,host,host and port,port,port.
|
||||||
|
var hosts []string
|
||||||
|
var ports []string
|
||||||
|
for _, host := range strings.Split(url.Host, ",") {
|
||||||
|
parts := strings.SplitN(host, ":", 2)
|
||||||
if parts[0] != "" {
|
if parts[0] != "" {
|
||||||
settings["host"] = parts[0]
|
hosts = append(hosts, parts[0])
|
||||||
}
|
}
|
||||||
if len(parts) == 2 {
|
if len(parts) == 2 {
|
||||||
settings["port"] = parts[1]
|
ports = append(ports, parts[1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(hosts) > 0 {
|
||||||
|
settings["host"] = strings.Join(hosts, ",")
|
||||||
|
}
|
||||||
|
if len(ports) > 0 {
|
||||||
|
settings["port"] = strings.Join(ports, ",")
|
||||||
}
|
}
|
||||||
|
|
||||||
database := strings.TrimLeft(url.Path, "/")
|
database := strings.TrimLeft(url.Path, "/")
|
||||||
|
|
|
@ -230,6 +230,150 @@ func TestParseConfig(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "URL multiple hosts",
|
||||||
|
connString: "postgres://jack:secret@foo,bar,baz/mydb?sslmode=disable",
|
||||||
|
config: &pgconn.Config{
|
||||||
|
User: "jack",
|
||||||
|
Password: "secret",
|
||||||
|
Host: "foo",
|
||||||
|
Port: 5432,
|
||||||
|
Database: "mydb",
|
||||||
|
TLSConfig: nil,
|
||||||
|
RuntimeParams: map[string]string{},
|
||||||
|
Fallbacks: []*pgconn.FallbackConfig{
|
||||||
|
&pgconn.FallbackConfig{
|
||||||
|
Host: "bar",
|
||||||
|
Port: 5432,
|
||||||
|
TLSConfig: nil,
|
||||||
|
},
|
||||||
|
&pgconn.FallbackConfig{
|
||||||
|
Host: "baz",
|
||||||
|
Port: 5432,
|
||||||
|
TLSConfig: nil,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "URL multiple hosts and ports",
|
||||||
|
connString: "postgres://jack:secret@foo:1,bar:2,baz:3/mydb?sslmode=disable",
|
||||||
|
config: &pgconn.Config{
|
||||||
|
User: "jack",
|
||||||
|
Password: "secret",
|
||||||
|
Host: "foo",
|
||||||
|
Port: 1,
|
||||||
|
Database: "mydb",
|
||||||
|
TLSConfig: nil,
|
||||||
|
RuntimeParams: map[string]string{},
|
||||||
|
Fallbacks: []*pgconn.FallbackConfig{
|
||||||
|
&pgconn.FallbackConfig{
|
||||||
|
Host: "bar",
|
||||||
|
Port: 2,
|
||||||
|
TLSConfig: nil,
|
||||||
|
},
|
||||||
|
&pgconn.FallbackConfig{
|
||||||
|
Host: "baz",
|
||||||
|
Port: 3,
|
||||||
|
TLSConfig: nil,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "DSN multiple hosts one port",
|
||||||
|
connString: "user=jack password=secret host=foo,bar,baz port=5432 database=mydb sslmode=disable",
|
||||||
|
config: &pgconn.Config{
|
||||||
|
User: "jack",
|
||||||
|
Password: "secret",
|
||||||
|
Host: "foo",
|
||||||
|
Port: 5432,
|
||||||
|
Database: "mydb",
|
||||||
|
TLSConfig: nil,
|
||||||
|
RuntimeParams: map[string]string{},
|
||||||
|
Fallbacks: []*pgconn.FallbackConfig{
|
||||||
|
&pgconn.FallbackConfig{
|
||||||
|
Host: "bar",
|
||||||
|
Port: 5432,
|
||||||
|
TLSConfig: nil,
|
||||||
|
},
|
||||||
|
&pgconn.FallbackConfig{
|
||||||
|
Host: "baz",
|
||||||
|
Port: 5432,
|
||||||
|
TLSConfig: nil,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "DSN multiple hosts multiple ports",
|
||||||
|
connString: "user=jack password=secret host=foo,bar,baz port=1,2,3 database=mydb sslmode=disable",
|
||||||
|
config: &pgconn.Config{
|
||||||
|
User: "jack",
|
||||||
|
Password: "secret",
|
||||||
|
Host: "foo",
|
||||||
|
Port: 1,
|
||||||
|
Database: "mydb",
|
||||||
|
TLSConfig: nil,
|
||||||
|
RuntimeParams: map[string]string{},
|
||||||
|
Fallbacks: []*pgconn.FallbackConfig{
|
||||||
|
&pgconn.FallbackConfig{
|
||||||
|
Host: "bar",
|
||||||
|
Port: 2,
|
||||||
|
TLSConfig: nil,
|
||||||
|
},
|
||||||
|
&pgconn.FallbackConfig{
|
||||||
|
Host: "baz",
|
||||||
|
Port: 3,
|
||||||
|
TLSConfig: nil,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "multiple hosts and fallback tsl",
|
||||||
|
connString: "user=jack password=secret host=foo,bar,baz database=mydb sslmode=prefer",
|
||||||
|
config: &pgconn.Config{
|
||||||
|
User: "jack",
|
||||||
|
Password: "secret",
|
||||||
|
Host: "foo",
|
||||||
|
Port: 5432,
|
||||||
|
Database: "mydb",
|
||||||
|
TLSConfig: &tls.Config{
|
||||||
|
InsecureSkipVerify: true,
|
||||||
|
},
|
||||||
|
RuntimeParams: map[string]string{},
|
||||||
|
Fallbacks: []*pgconn.FallbackConfig{
|
||||||
|
&pgconn.FallbackConfig{
|
||||||
|
Host: "foo",
|
||||||
|
Port: 5432,
|
||||||
|
TLSConfig: nil,
|
||||||
|
},
|
||||||
|
&pgconn.FallbackConfig{
|
||||||
|
Host: "bar",
|
||||||
|
Port: 5432,
|
||||||
|
TLSConfig: &tls.Config{
|
||||||
|
InsecureSkipVerify: true,
|
||||||
|
}},
|
||||||
|
&pgconn.FallbackConfig{
|
||||||
|
Host: "bar",
|
||||||
|
Port: 5432,
|
||||||
|
TLSConfig: nil,
|
||||||
|
},
|
||||||
|
&pgconn.FallbackConfig{
|
||||||
|
Host: "baz",
|
||||||
|
Port: 5432,
|
||||||
|
TLSConfig: &tls.Config{
|
||||||
|
InsecureSkipVerify: true,
|
||||||
|
}},
|
||||||
|
&pgconn.FallbackConfig{
|
||||||
|
Host: "baz",
|
||||||
|
Port: 5432,
|
||||||
|
TLSConfig: nil,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, tt := range tests {
|
for i, tt := range tests {
|
||||||
|
@ -243,6 +387,13 @@ func TestParseConfig(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func assertConfigsEqual(t *testing.T, expected, actual *pgconn.Config, testName string) {
|
func assertConfigsEqual(t *testing.T, expected, actual *pgconn.Config, testName string) {
|
||||||
|
if !assert.NotNil(t, expected) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !assert.NotNil(t, actual) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
assert.Equalf(t, expected.Host, actual.Host, "%s - Host", testName)
|
assert.Equalf(t, expected.Host, actual.Host, "%s - Host", testName)
|
||||||
assert.Equalf(t, expected.Database, actual.Database, "%s - Database", testName)
|
assert.Equalf(t, expected.Database, actual.Database, "%s - Database", testName)
|
||||||
assert.Equalf(t, expected.Port, actual.Port, "%s - Port", testName)
|
assert.Equalf(t, expected.Port, actual.Port, "%s - Port", testName)
|
||||||
|
@ -257,12 +408,12 @@ func assertConfigsEqual(t *testing.T, expected, actual *pgconn.Config, testName
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if assert.Equalf(t, len(expected.Fallbacks), len(actual.Fallbacks), "%s - Fallbacks %v", testName) {
|
if assert.Equalf(t, len(expected.Fallbacks), len(actual.Fallbacks), "%s - Fallbacks", testName) {
|
||||||
for i := range expected.Fallbacks {
|
for i := range expected.Fallbacks {
|
||||||
assert.Equalf(t, expected.Fallbacks[i].Host, actual.Fallbacks[i].Host, "%s - Fallback %d - Host", testName, i)
|
assert.Equalf(t, expected.Fallbacks[i].Host, actual.Fallbacks[i].Host, "%s - Fallback %d - Host", testName, i)
|
||||||
assert.Equalf(t, expected.Fallbacks[i].Port, actual.Fallbacks[i].Port, "%s - Fallback %d - Port", testName, i)
|
assert.Equalf(t, expected.Fallbacks[i].Port, actual.Fallbacks[i].Port, "%s - Fallback %d - Port", testName, i)
|
||||||
|
|
||||||
if assert.Equalf(t, expected.Fallbacks[i].TLSConfig == nil, actual.Fallbacks[i].TLSConfig == nil, "%s - Fallback %d - TLSConfig", testName) {
|
if assert.Equalf(t, expected.Fallbacks[i].TLSConfig == nil, actual.Fallbacks[i].TLSConfig == nil, "%s - Fallback %d - TLSConfig", testName, i) {
|
||||||
if expected.Fallbacks[i].TLSConfig != nil {
|
if expected.Fallbacks[i].TLSConfig != nil {
|
||||||
assert.Equalf(t, expected.Fallbacks[i].TLSConfig.InsecureSkipVerify, actual.Fallbacks[i].TLSConfig.InsecureSkipVerify, "%s - Fallback %d - TLSConfig InsecureSkipVerify", testName)
|
assert.Equalf(t, expected.Fallbacks[i].TLSConfig.InsecureSkipVerify, actual.Fallbacks[i].TLSConfig.InsecureSkipVerify, "%s - Fallback %d - TLSConfig InsecureSkipVerify", testName)
|
||||||
assert.Equalf(t, expected.Fallbacks[i].TLSConfig.ServerName, actual.Fallbacks[i].TLSConfig.ServerName, "%s - Fallback %d - TLSConfig ServerName", testName)
|
assert.Equalf(t, expected.Fallbacks[i].TLSConfig.ServerName, actual.Fallbacks[i].TLSConfig.ServerName, "%s - Fallback %d - TLSConfig ServerName", testName)
|
||||||
|
|
|
@ -127,7 +127,7 @@ func connect(ctx context.Context, config *Config, fallbackConfig *FallbackConfig
|
||||||
pgConn.Config = config
|
pgConn.Config = config
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
network, address := NetworkAddress(config.Host, config.Port)
|
network, address := NetworkAddress(fallbackConfig.Host, fallbackConfig.Port)
|
||||||
pgConn.NetConn, err = config.DialFunc(ctx, network, address)
|
pgConn.NetConn, err = config.DialFunc(ctx, network, address)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -157,6 +157,37 @@ func TestConnectWithRuntimeParams(t *testing.T) {
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestConnectWithFallback(t *testing.T) {
|
||||||
|
config, err := pgconn.ParseConfig(os.Getenv("PGX_TEST_DATABASE"))
|
||||||
|
require.Nil(t, err)
|
||||||
|
|
||||||
|
// Prepend current primary config to fallbacks
|
||||||
|
config.Fallbacks = append([]*pgconn.FallbackConfig{
|
||||||
|
&pgconn.FallbackConfig{
|
||||||
|
Host: config.Host,
|
||||||
|
Port: config.Port,
|
||||||
|
TLSConfig: config.TLSConfig,
|
||||||
|
},
|
||||||
|
}, config.Fallbacks...)
|
||||||
|
|
||||||
|
// Make primary config bad
|
||||||
|
config.Host = "localhost"
|
||||||
|
config.Port = 1 // presumably nothing listening here
|
||||||
|
|
||||||
|
// Prepend bad first fallback
|
||||||
|
config.Fallbacks = append([]*pgconn.FallbackConfig{
|
||||||
|
&pgconn.FallbackConfig{
|
||||||
|
Host: "localhost",
|
||||||
|
Port: 1,
|
||||||
|
TLSConfig: config.TLSConfig,
|
||||||
|
},
|
||||||
|
}, config.Fallbacks...)
|
||||||
|
|
||||||
|
conn, err := pgconn.ConnectConfig(context.Background(), config)
|
||||||
|
require.Nil(t, err)
|
||||||
|
closeConn(t, conn)
|
||||||
|
}
|
||||||
|
|
||||||
func TestSimple(t *testing.T) {
|
func TestSimple(t *testing.T) {
|
||||||
pgConn, err := pgconn.Connect(context.Background(), os.Getenv("PGX_TEST_DATABASE"))
|
pgConn, err := pgconn.Connect(context.Background(), os.Getenv("PGX_TEST_DATABASE"))
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
|
|
4
v4.md
4
v4.md
|
@ -42,3 +42,7 @@ Test configuration now done with environment variables instead of `.gitignore`'d
|
||||||
* Connect method now takes context and connection string.
|
* Connect method now takes context and connection string.
|
||||||
* ConnectConfig takes context and config object.
|
* ConnectConfig takes context and config object.
|
||||||
* `RuntimeParams` `pgx.Conn`. Server reported status can now be queried with the `ParameterStatus` method. The rename aligns with the PostgreSQL protocol and standard libpq naming. Access via a method instead of direct access to the map protects against outside modification.
|
* `RuntimeParams` `pgx.Conn`. Server reported status can now be queried with the `ParameterStatus` method. The rename aligns with the PostgreSQL protocol and standard libpq naming. Access via a method instead of direct access to the map protects against outside modification.
|
||||||
|
|
||||||
|
## New Features
|
||||||
|
|
||||||
|
* Specifying multiple hosts for connecting to HA systems.
|
||||||
|
|
Loading…
Reference in New Issue