fix(mysql): select database during URL connection setup

This commit is contained in:
t8y2 2026-05-28 22:57:52 +08:00
parent 539faef23d
commit 39b960c46c
1 changed files with 48 additions and 4 deletions

View File

@ -23,6 +23,10 @@ fn quote_value(s: &str) -> String {
format!("'{}'", s.replace('\\', "\\\\").replace('\'', "\\'"))
}
fn quote_identifier(s: &str) -> String {
format!("`{}`", s.replace('`', "``"))
}
fn row_get<T, I>(row: &mysql_async::Row, index: I) -> Option<T>
where
T: mysql_async::prelude::FromValue,
@ -411,7 +415,12 @@ fn mysql_ssl_opts(
fn mysql_setup_queries(url: &str) -> Vec<String> {
let charset = mysql_connection_charset(url).unwrap_or("utf8mb4");
vec![format!("SET NAMES {charset}")]
let mut queries = Vec::new();
if let Some(database) = mysql_connection_database(url) {
queries.push(format!("USE {}", quote_identifier(&database)));
}
queries.push(format!("SET NAMES {charset}"));
queries
}
fn should_enable_explicit_timestamp_defaults(sql: &str) -> bool {
@ -479,6 +488,17 @@ fn mysql_connection_charset(url: &str) -> Option<&str> {
})
}
fn mysql_connection_database(url: &str) -> Option<String> {
let rest = url.strip_prefix("mysql://")?;
let (_, path_and_query) = rest.split_once('/')?;
let path = path_and_query.split(['?', '#']).next().unwrap_or(path_and_query);
let database = path.trim_start_matches('/').split('/').next().unwrap_or("").trim();
if database.is_empty() {
return None;
}
percent_decode_str(database).decode_utf8().ok().map(|value| value.into_owned())
}
fn is_safe_mysql_charset_name(value: &str) -> bool {
!value.is_empty() && value.bytes().all(|byte| byte.is_ascii_alphanumeric() || byte == b'_')
}
@ -1257,6 +1277,27 @@ mod tests {
assert!(mysql_error_should_retry_with_text_protocol(error));
}
#[test]
fn mysql_setup_queries_select_requested_database_before_session_init() {
let queries = mysql_setup_queries("mysql://root:secret@localhost:3306/app?charset=utf8mb4");
assert_eq!(queries, vec!["USE `app`", "SET NAMES utf8mb4"]);
}
#[test]
fn mysql_setup_queries_skip_use_when_database_missing() {
let queries = mysql_setup_queries("mysql://root:secret@localhost:3306?charset=utf8mb4");
assert_eq!(queries, vec!["SET NAMES utf8mb4"]);
}
#[test]
fn mysql_setup_queries_decode_database_name_from_url() {
let queries = mysql_setup_queries("mysql://root:secret@localhost:3306/db%2Fname?charset=utf8mb4");
assert_eq!(queries, vec!["USE `db/name`", "SET NAMES utf8mb4"]);
}
#[test]
fn mysql_datetime_utc_values_display_without_rfc3339_offset() {
let value = NaiveDateTime::new(
@ -1331,15 +1372,18 @@ mod tests {
#[test]
fn mysql_setup_queries_default_to_utf8mb4() {
assert_eq!(mysql_setup_queries("mysql://host:3306/db"), vec!["SET NAMES utf8mb4"]);
assert_eq!(mysql_setup_queries("mysql://host:3306/db"), vec!["USE `db`", "SET NAMES utf8mb4"]);
}
#[test]
fn mysql_setup_queries_use_safe_custom_charset() {
assert_eq!(mysql_setup_queries("mysql://host:3306/db?ssl-mode=preferred&charset=gbk"), vec!["SET NAMES gbk"]);
assert_eq!(
mysql_setup_queries("mysql://host:3306/db?ssl-mode=preferred&charset=gbk"),
vec!["USE `db`", "SET NAMES gbk"]
);
assert_eq!(
mysql_setup_queries("mysql://host:3306/db?charset=utf8mb4;DROP TABLE users"),
vec!["SET NAMES utf8mb4"]
vec!["USE `db`", "SET NAMES utf8mb4"]
);
}
}