@@ -5,7 +5,8 @@ use crate::{
55 error:: { Error , ErrorKind } ,
66} ;
77use connection_string:: JdbcString ;
8- use std:: { fmt, str:: FromStr , time:: Duration } ;
8+ use percent_encoding:: percent_decode;
9+ use std:: { borrow:: Cow , fmt, str:: FromStr , time:: Duration } ;
910
1011/// Wraps a connection url and exposes the parsing logic used by Quaint,
1112/// including default values.
@@ -97,9 +98,16 @@ impl MssqlUrl {
9798 self . query_params . transaction_isolation_level
9899 }
99100
100- /// Name of the database.
101- pub fn dbname ( & self ) -> & str {
102- self . query_params . database ( )
101+ /// Decoded database name. Defaults to `master`.
102+ pub fn dbname ( & self ) -> Cow < ' _ , str > {
103+ let db = self . query_params . database ( ) ;
104+ match percent_decode ( db. as_bytes ( ) ) . decode_utf8 ( ) {
105+ Ok ( decoded) => decoded,
106+ Err ( _) => {
107+ tracing:: warn!( "Couldn't decode dbname to UTF-8, using the non-decoded version." ) ;
108+ Cow :: Borrowed ( db)
109+ }
110+ }
103111 }
104112
105113 /// The prefix which to use when querying database.
@@ -368,6 +376,7 @@ impl MssqlUrl {
368376
369377#[ cfg( test) ]
370378mod tests {
379+ use super :: * ;
371380 use crate :: tests:: test_api:: mssql:: CONN_STR ;
372381 use crate :: { error:: * , single:: Quaint } ;
373382
@@ -381,4 +390,42 @@ mod tests {
381390 let err = res. unwrap_err ( ) ;
382391 assert ! ( matches!( err. kind( ) , ErrorKind :: AuthenticationFailed { user } if user == & Name :: available( "WRONG" ) ) ) ;
383392 }
393+
394+ #[ test]
395+ fn should_decode_percent_encoded_dbname ( ) {
396+ // Chinese characters: 测试库 (test database)
397+ let url = MssqlUrl :: new ( "sqlserver://localhost:1433;database=%E6%B5%8B%E8%AF%95%E5%BA%93;user=SA;password=pass;trustServerCertificate=true" ) . unwrap ( ) ;
398+ assert_eq ! ( "测试库" , url. dbname( ) ) ;
399+ }
400+
401+ #[ test]
402+ fn should_decode_dbname_with_spaces ( ) {
403+ let url = MssqlUrl :: new ( "sqlserver://localhost:1433;database=my%20database;user=SA;password=pass;trustServerCertificate=true" ) . unwrap ( ) ;
404+ assert_eq ! ( "my database" , url. dbname( ) ) ;
405+ }
406+
407+ #[ test]
408+ fn should_decode_dbname_with_special_characters ( ) {
409+ // test-db_name
410+ let url = MssqlUrl :: new ( "sqlserver://localhost:1433;database=test%2Ddb%5Fname;user=SA;password=pass;trustServerCertificate=true" ) . unwrap ( ) ;
411+ assert_eq ! ( "test-db_name" , url. dbname( ) ) ;
412+ }
413+
414+ #[ test]
415+ fn should_return_master_as_default_dbname ( ) {
416+ let url = MssqlUrl :: new ( "sqlserver://localhost:1433;user=SA;password=pass;trustServerCertificate=true" ) . unwrap ( ) ;
417+ assert_eq ! ( "master" , url. dbname( ) ) ;
418+ }
419+
420+ #[ tokio:: test]
421+ async fn should_connect_to_percent_encoded_chinese_dbname ( ) {
422+ // 测试库 = %E6%B5%8B%E8%AF%95%E5%BA%93
423+ let url = CONN_STR . replace ( "database=master" , "database=%E6%B5%8B%E8%AF%95%E5%BA%93" ) ;
424+ let conn = Quaint :: new ( & url) . await . unwrap ( ) ;
425+
426+ use crate :: prelude:: Queryable ;
427+ let result = conn. query_raw ( "SELECT DB_NAME() AS db_name" , & [ ] ) . await . unwrap ( ) ;
428+ let db_name = result. first ( ) . unwrap ( ) . get ( "db_name" ) . unwrap ( ) . to_string ( ) . unwrap ( ) ;
429+ assert_eq ! ( "测试库" , db_name) ;
430+ }
384431}
0 commit comments