diff --git a/.travis.yml b/.travis.yml index 2c547abf..abff8515 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,15 +17,15 @@ env: - GOPROXY=https://proxy.golang.org - GOFLAGS=-mod=readonly - PGX_TEST_CONN_STRING=postgres://pgx_md5:secret@127.0.0.1/pgx_test - - PGX_TEST_UNIX_SOCKET_CONN_STRING="host=/var/run/postgresql database=pgx_test" + - PGX_TEST_UNIX_SOCKET_CONN_STRING="host=/var/run/postgresql dbname=pgx_test" - PGX_TEST_TCP_CONN_STRING=postgres://pgx_md5:secret@127.0.0.1/pgx_test - PGX_TEST_TLS_CONN_STRING=postgres://pgx_md5:secret@127.0.0.1/pgx_test?sslmode=require - PGX_TEST_MD5_PASSWORD_CONN_STRING=postgres://pgx_md5:secret@127.0.0.1/pgx_test - PGX_TEST_PLAIN_PASSWORD_CONN_STRING=postgres://pgx_pw:secret@127.0.0.1/pgx_test matrix: - - CRATEVERSION=2.1 PGX_TEST_CRATEDB_CONN_STRING="host=127.0.0.1 port=6543 user=pgx database=pgx_test" - - PGVERSION=10 PGX_TEST_REPLICATION_CONN_STRING="host=127.0.0.1 port=6543 user=pgx_replication password=secret database=pgx_test" - - PGVERSION=9.6 PGX_TEST_REPLICATION_CONN_STRING="host=127.0.0.1 port=6543 user=pgx_replication password=secret database=pgx_test" + - CRATEVERSION=2.1 PGX_TEST_CRATEDB_CONN_STRING="host=127.0.0.1 port=6543 user=pgx dbname=pgx_test" + - PGVERSION=10 PGX_TEST_REPLICATION_CONN_STRING="host=127.0.0.1 port=6543 user=pgx_replication password=secret dbname=pgx_test" + - PGVERSION=9.6 PGX_TEST_REPLICATION_CONN_STRING="host=127.0.0.1 port=6543 user=pgx_replication password=secret dbname=pgx_test" - PGVERSION=9.5 - PGVERSION=9.4 - PGVERSION=9.3 diff --git a/README.md b/README.md index 9e35a0f5..aa980b6d 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ create database pgx_test; Now you can run the tests: ``` -PGX_TEST_CONN_STRING="host=/var/run/postgresql database=pgx_test" go test ./... +PGX_TEST_CONN_STRING="host=/var/run/postgresql dbname=pgx_test" go test ./... ``` ### Connection and Authentication Tests diff --git a/config.go b/config.go index 2ec6ae3f..6eb0065a 100644 --- a/config.go +++ b/config.go @@ -13,7 +13,6 @@ import ( "os" "os/user" "path/filepath" - "regexp" "strconv" "strings" "time" @@ -389,13 +388,65 @@ func addURLSettings(settings map[string]string, connString string) error { return nil } -var dsnRegexp = regexp.MustCompile(`([a-zA-Z_]+)=((?:"[^"]+")|(?:[^ ]+))`) +var asciiSpace = [256]uint8{'\t': 1, '\n': 1, '\v': 1, '\f': 1, '\r': 1, ' ': 1} func addDSNSettings(settings map[string]string, s string) error { - m := dsnRegexp.FindAllStringSubmatch(s, -1) + nameMap := map[string]string{ + "dbname": "database", + } - for _, b := range m { - settings[b[1]] = b[2] + for len(s) > 0 { + var key, val string + eqIdx := strings.IndexRune(s, '=') + if eqIdx < 0 { + return errors.New("invalid dsn") + } + + key = strings.Trim(s[:eqIdx], " \t\n\r\v\f") + s = strings.TrimLeft(s[eqIdx+1:], " \t\n\r\v\f") + if s[0] != '\'' { + end := 0 + for ; end < len(s); end++ { + if asciiSpace[s[end]] == 1 { + break + } + if s[end] == '\\' { + end++ + } + } + val = strings.Replace(strings.Replace(s[:end], "\\\\", "\\", -1), "\\'", "'", -1) + if end == len(s) { + s = "" + } else { + s = s[end+1:] + } + } else { // quoted string + s = s[1:] + end := 0 + for ; end < len(s); end++ { + if s[end] == '\'' { + break + } + if s[end] == '\\' { + end++ + } + } + if end == len(s) { + return errors.New("unterminated quoted string in connection info string") + } + val = strings.Replace(strings.Replace(s[:end], "\\\\", "\\", -1), "\\'", "'", -1) + if end == len(s) { + s = "" + } else { + s = s[end+1:] + } + } + + if k, ok := nameMap[key]; ok { + key = k + } + + settings[key] = val } return nil diff --git a/config_test.go b/config_test.go index 090302a2..9eb5df2f 100644 --- a/config_test.go +++ b/config_test.go @@ -228,7 +228,7 @@ func TestParseConfig(t *testing.T) { }, { name: "DSN everything", - connString: "user=jack password=secret host=localhost port=5432 database=mydb sslmode=disable application_name=pgxtest search_path=myschema", + connString: "user=jack password=secret host=localhost port=5432 dbname=mydb sslmode=disable application_name=pgxtest search_path=myschema", config: &pgconn.Config{ User: "jack", Password: "secret", @@ -242,6 +242,80 @@ func TestParseConfig(t *testing.T) { }, }, }, + { + name: "DSN with escaped single quote", + connString: "user=jack\\'s password=secret host=localhost port=5432 dbname=mydb sslmode=disable", + config: &pgconn.Config{ + User: "jack's", + Password: "secret", + Host: "localhost", + Port: 5432, + Database: "mydb", + TLSConfig: nil, + RuntimeParams: map[string]string{}, + }, + }, + { + name: "DSN with escaped backslash", + connString: "user=jack password=sooper\\\\secret host=localhost port=5432 dbname=mydb sslmode=disable", + config: &pgconn.Config{ + User: "jack", + Password: "sooper\\secret", + Host: "localhost", + Port: 5432, + Database: "mydb", + TLSConfig: nil, + RuntimeParams: map[string]string{}, + }, + }, + { + name: "DSN with single quoted values", + connString: "user='jack' host='localhost' dbname='mydb' sslmode='disable'", + config: &pgconn.Config{ + User: "jack", + Host: "localhost", + Port: 5432, + Database: "mydb", + TLSConfig: nil, + RuntimeParams: map[string]string{}, + }, + }, + { + name: "DSN with single quoted value with escaped single quote", + connString: "user='jack\\'s' host='localhost' dbname='mydb' sslmode='disable'", + config: &pgconn.Config{ + User: "jack's", + Host: "localhost", + Port: 5432, + Database: "mydb", + TLSConfig: nil, + RuntimeParams: map[string]string{}, + }, + }, + { + name: "DSN with empty single quoted value", + connString: "user='jack' password='' host='localhost' dbname='mydb' sslmode='disable'", + config: &pgconn.Config{ + User: "jack", + Host: "localhost", + Port: 5432, + Database: "mydb", + TLSConfig: nil, + RuntimeParams: map[string]string{}, + }, + }, + { + name: "DSN with space between key and value", + connString: "user = 'jack' password = '' host = 'localhost' dbname = 'mydb' sslmode='disable'", + config: &pgconn.Config{ + User: "jack", + Host: "localhost", + Port: 5432, + Database: "mydb", + TLSConfig: nil, + RuntimeParams: map[string]string{}, + }, + }, { name: "URL multiple hosts", connString: "postgres://jack:secret@foo,bar,baz/mydb?sslmode=disable", @@ -294,7 +368,7 @@ func TestParseConfig(t *testing.T) { }, { name: "DSN multiple hosts one port", - connString: "user=jack password=secret host=foo,bar,baz port=5432 database=mydb sslmode=disable", + connString: "user=jack password=secret host=foo,bar,baz port=5432 dbname=mydb sslmode=disable", config: &pgconn.Config{ User: "jack", Password: "secret", @@ -319,7 +393,7 @@ func TestParseConfig(t *testing.T) { }, { name: "DSN multiple hosts multiple ports", - connString: "user=jack password=secret host=foo,bar,baz port=1,2,3 database=mydb sslmode=disable", + connString: "user=jack password=secret host=foo,bar,baz port=1,2,3 dbname=mydb sslmode=disable", config: &pgconn.Config{ User: "jack", Password: "secret", @@ -344,7 +418,7 @@ func TestParseConfig(t *testing.T) { }, { name: "multiple hosts and fallback tsl", - connString: "user=jack password=secret host=foo,bar,baz database=mydb sslmode=prefer", + connString: "user=jack password=secret host=foo,bar,baz dbname=mydb sslmode=prefer", config: &pgconn.Config{ User: "jack", Password: "secret",