diff --git a/conn.go b/conn.go index 27135861..4b7d65de 100644 --- a/conn.go +++ b/conn.go @@ -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 @@ -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) } diff --git a/conn_test.go b/conn_test.go index 2cba7ef2..afec3a57 100644 --- a/conn_test.go +++ b/conn_test.go @@ -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}, diff --git a/doc.go b/doc.go index f36d4738..b5155801 100644 --- a/doc.go +++ b/doc.go @@ -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 diff --git a/url.go b/url.go index 7a29d52c..b83e806b 100644 --- a/url.go +++ b/url.go @@ -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)) } } diff --git a/url_test.go b/url_test.go index fce97900..29f4a7c7 100644 --- a/url_test.go +++ b/url_test.go @@ -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) }