diff --git a/sqlx-core/src/postgres/options/parse.rs b/sqlx-core/src/postgres/options/parse.rs index e5dd27fd..dce2bad5 100644 --- a/sqlx-core/src/postgres/options/parse.rs +++ b/sqlx-core/src/postgres/options/parse.rs @@ -1,5 +1,6 @@ use crate::error::Error; use crate::postgres::PgConnectOptions; +use percent_encoding::percent_decode_str; use std::str::FromStr; use url::Url; @@ -21,11 +22,19 @@ impl FromStr for PgConnectOptions { let username = url.username(); if !username.is_empty() { - options = options.username(username); + options = options.username( + &*percent_decode_str(username) + .decode_utf8() + .map_err(Error::config)?, + ); } if let Some(password) = url.password() { - options = options.password(password); + options = options.password( + &*percent_decode_str(password) + .decode_utf8() + .map_err(Error::config)?, + ); } let path = url.path().trim_start_matches('/'); @@ -80,3 +89,19 @@ fn it_parses_host_correctly_from_parameter() { assert_eq!(None, opts.socket); assert_eq!("google.database.com", &opts.host); } + +#[test] +fn it_parses_username_with_at_sign_correctly() { + let uri = "postgres://user@hostname:password@hostname:5432/database"; + let opts = PgConnectOptions::from_str(uri).unwrap(); + + assert_eq!("user@hostname", &opts.username); +} + +#[test] +fn it_parses_password_with_non_ascii_chars_correctly() { + let uri = "postgres://username:p@ssw0rd@hostname:5432/database"; + let opts = PgConnectOptions::from_str(uri).unwrap(); + + assert_eq!(Some("p@ssw0rd".into()), opts.password); +}