Skip to content

Commit 54eac49

Browse files
committed
pgconn: fix parseKeywordValueSettings rejecting trailing whitespace
The key-value connection string parser failed with "invalid keyword/value" whenever the string ended with more than one space, or when a quoted value was followed by any trailing spaces. This is because after consuming each value the code only stripped a single byte (s[end+1:]), leaving extra leading whitespace that caused the next loop iteration to see a partial string without an '=' character. Fix by trimming all leading whitespace from the remainder of the string after consuming each value (unquoted and quoted), and also trimming the string once before entering the loop. This matches libpq's behaviour, which accepts any amount of whitespace between and after key-value pairs. Fixes #2284 Signed-off-by: alliasgher <alliasgher123@gmail.com>
1 parent a5680bc commit 54eac49

2 files changed

Lines changed: 46 additions & 10 deletions

File tree

pgconn/config.go

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -631,6 +631,9 @@ func parseKeywordValueSettings(s string) (map[string]string, error) {
631631
"dbname": "database",
632632
}
633633

634+
// Trim any leading whitespace so that the loop exits cleanly when only
635+
// spaces remain (e.g. trailing spaces after the last value).
636+
s = strings.TrimLeft(s, " \t\n\r\v\f")
634637
for len(s) > 0 {
635638
var key, val string
636639
eqIdx := strings.IndexRune(s, '=')
@@ -655,11 +658,9 @@ func parseKeywordValueSettings(s string) (map[string]string, error) {
655658
}
656659
}
657660
val = strings.Replace(strings.Replace(s[:end], "\\\\", "\\", -1), "\\'", "'", -1)
658-
if end == len(s) {
659-
s = ""
660-
} else {
661-
s = s[end+1:]
662-
}
661+
// Consume the value and trim any subsequent whitespace so that
662+
// multiple trailing spaces don't cause a spurious parse failure.
663+
s = strings.TrimLeft(s[end:], " \t\n\r\v\f")
663664
} else { // quoted string
664665
s = s[1:]
665666
end := 0
@@ -675,11 +676,8 @@ func parseKeywordValueSettings(s string) (map[string]string, error) {
675676
return nil, errors.New("unterminated quoted string in connection info string")
676677
}
677678
val = strings.Replace(strings.Replace(s[:end], "\\\\", "\\", -1), "\\'", "'", -1)
678-
if end == len(s) {
679-
s = ""
680-
} else {
681-
s = s[end+1:]
682-
}
679+
// Consume the closing quote and any subsequent whitespace.
680+
s = strings.TrimLeft(s[end+1:], " \t\n\r\v\f")
683681
}
684682

685683
if k, ok := nameMap[key]; ok {

pgconn/config_test.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -799,6 +799,44 @@ func TestParseConfigKVTrailingBackslash(t *testing.T) {
799799
assert.Contains(t, err.Error(), "invalid backslash")
800800
}
801801

802+
// https://github.com/jackc/pgx/issues/2284
803+
// Multiple trailing spaces and trailing spaces after quoted values must be
804+
// accepted as valid keyword/value connection strings.
805+
func TestParseConfigKVTrailingWhitespace(t *testing.T) {
806+
tests := []struct {
807+
name string
808+
connString string
809+
}{
810+
{
811+
name: "single trailing space",
812+
connString: "dbname=foo ",
813+
},
814+
{
815+
name: "multiple trailing spaces",
816+
connString: "dbname=foo ",
817+
},
818+
{
819+
name: "trailing tab",
820+
connString: "dbname=foo\t",
821+
},
822+
{
823+
name: "quoted value with trailing spaces",
824+
connString: "dbname='foo' ",
825+
},
826+
{
827+
name: "two key-value pairs with trailing spaces",
828+
connString: "port=5432 dbname=foo ",
829+
},
830+
}
831+
832+
for _, tt := range tests {
833+
t.Run(tt.name, func(t *testing.T) {
834+
_, err := pgconn.ParseConfig(tt.connString)
835+
require.NoErrorf(t, err, "conn string %q should not produce an error", tt.connString)
836+
})
837+
}
838+
}
839+
802840
func TestConfigCopyReturnsEqualConfig(t *testing.T) {
803841
connString := "postgres://jack:secret@localhost:5432/mydb?application_name=pgxtest&search_path=myschema&connect_timeout=5"
804842
original, err := pgconn.ParseConfig(connString)

0 commit comments

Comments
 (0)