Skip to content

Commit

Permalink
Improve option parsing
Browse files Browse the repository at this point in the history
The existing code did not provide for escaping characters, which made
certain option values impossible to represent, for example:

 password='spaces and ' a quote'

In addition, ParseUrl would not allow even a single space in any value.

Add standard backslash escaping, matching libpq's own conninfo_parse.

Apply this escaping to special characters in ParseURL.
  • Loading branch information
Lann Martin committed May 22, 2014
1 parent c808a1b commit d932666
Show file tree
Hide file tree
Showing 5 changed files with 23 additions and 7 deletions.
12 changes: 8 additions & 4 deletions conn.go
Original file line number Diff line number Diff line change
Expand Up @@ -308,9 +308,12 @@ func parseOpts(name string, o values) error {

if r != '\'' {
for !unicode.IsSpace(r) {
if r != '\\' {
valRunes = append(valRunes, r)
if r == '\\' {
if r, ok = s.Next(); !ok {
return fmt.Errorf(`missing character after backslash`)
}
}
valRunes = append(valRunes, r)

if r, ok = s.Next(); !ok {
break
Expand All @@ -323,10 +326,11 @@ func parseOpts(name string, o values) error {
return fmt.Errorf(`unterminated quoted string literal in connection string`)
}
switch r {
case '\\':
continue
case '\'':
break quote
case '\\':
r, _ = s.Next()
fallthrough
default:
valRunes = append(valRunes, r)
}
Expand Down
7 changes: 7 additions & 0 deletions conn_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -935,6 +935,13 @@ func TestParseOpts(t *testing.T) {
// The parser ignores spaces after = and interprets the next set of non-whitespace characters as the value.
{"user= password=foo", values{"user": "password=foo"}, true},

// Backslash escapes next char
{`user=a\ \'\\b`, values{"user": `a '\b`}, true},
{`user='a \'b'`, values{"user": `a 'b`}, true},

// Incomplete escape
{`user=x\`, values{}, false},

// No '=' after the key
{"postgre://marko@internet", values{}, false},
{"dbname user=goodbye", values{}, false},
Expand Down
4 changes: 4 additions & 0 deletions doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ Use single quotes for values that contain whitespace:
"user=pqgotest password='with spaces'"
A backslash will escape the next character in values:
"user=space\ man password='it\'s valid'
Note that the connection parameter client_encoding (which sets the
text encoding for the connection) may be set but must be "UTF8",
matching with the same rules as Postgres. It is an error to provide
Expand Down
3 changes: 2 additions & 1 deletion url.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,10 @@ func ParseURL(url string) (string, error) {
}

var kvs []string
escaper := strings.NewReplacer(` `, `\ `, `'`, `\'`, `\`, `\\`)
accrue := func(k, v string) {
if v != "" {
kvs = append(kvs, k+"="+v)
kvs = append(kvs, k+"="+escaper.Replace(v))
}
}

Expand Down
4 changes: 2 additions & 2 deletions url_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ func TestSimpleParseURL(t *testing.T) {
}

func TestFullParseURL(t *testing.T) {
expected := "dbname=database host=hostname.remote password=secret port=1234 user=username"
str, err := ParseURL("postgres://username:secret@hostname.remote:1234/database")
expected := `dbname=database host=hostname.remote password=top\ secret port=1234 user=username`
str, err := ParseURL("postgres://username:top%20secret@hostname.remote:1234/database")
if err != nil {
t.Fatal(err)
}
Expand Down

0 comments on commit d932666

Please sign in to comment.