feat(usable): package gui-host validation snapshot
Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
@@ -420,7 +420,10 @@ pub fn inspect(
|
||||
})
|
||||
}
|
||||
|
||||
pub fn query(profile: &ConnectionProfile, request: &QueryRequest) -> Result<QueryResponse, AppError> {
|
||||
pub fn query(
|
||||
profile: &ConnectionProfile,
|
||||
request: &QueryRequest,
|
||||
) -> Result<QueryResponse, AppError> {
|
||||
validate_profile("query", profile)?;
|
||||
request
|
||||
.validate()
|
||||
|
||||
@@ -15,6 +15,7 @@ use std::path::Path;
|
||||
use std::{error::Error, fmt};
|
||||
|
||||
const SQLITE_DATABASE_HEADER: &[u8; 16] = b"SQLite format 3\0";
|
||||
const MYSQL_UTF8MB4_SESSION_INIT: &str = "SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci";
|
||||
|
||||
pub trait DatabaseDriver {
|
||||
fn kind(&self) -> DatabaseKind;
|
||||
@@ -430,17 +431,38 @@ fn connect_mysql(target: &ConnectionTarget) -> Result<MySqlConn, DriverError> {
|
||||
.as_deref()
|
||||
.ok_or_else(|| DriverError::Connection("mysql requires a database".to_string()))?;
|
||||
|
||||
let builder = MySqlOptsBuilder::new()
|
||||
.ip_or_hostname(Some(host))
|
||||
.tcp_port(port)
|
||||
.user(target.username.as_deref())
|
||||
.pass(target.password.as_deref())
|
||||
.db_name(Some(database))
|
||||
.prefer_socket(false);
|
||||
let builder = mysql_opts_builder(
|
||||
host,
|
||||
port,
|
||||
target.username.as_deref(),
|
||||
target.password.as_deref(),
|
||||
database,
|
||||
);
|
||||
|
||||
MySqlConn::new(builder).map_err(map_mysql_error)
|
||||
}
|
||||
|
||||
fn mysql_opts_builder(
|
||||
host: &str,
|
||||
port: u16,
|
||||
username: Option<&str>,
|
||||
password: Option<&str>,
|
||||
database: &str,
|
||||
) -> MySqlOptsBuilder {
|
||||
MySqlOptsBuilder::new()
|
||||
.ip_or_hostname(Some(host))
|
||||
.tcp_port(port)
|
||||
.user(username)
|
||||
.pass(password)
|
||||
.db_name(Some(database))
|
||||
.prefer_socket(false)
|
||||
.init(mysql_session_init_commands())
|
||||
}
|
||||
|
||||
fn mysql_session_init_commands() -> Vec<&'static str> {
|
||||
vec![MYSQL_UTF8MB4_SESSION_INIT]
|
||||
}
|
||||
|
||||
fn connect_sqlite(target: &ConnectionTarget) -> Result<SqliteConnection, DriverError> {
|
||||
let path = match &target.transport {
|
||||
ConnectionTransport::File { path } => path,
|
||||
@@ -636,7 +658,7 @@ fn consume_mysql_query_result<T: MySqlProtocol>(
|
||||
let mut last_rows = Vec::new();
|
||||
let mut last_rows_affected = 0;
|
||||
|
||||
while let Some(result_set) = result.iter() {
|
||||
while let Some(mut result_set) = result.iter() {
|
||||
let columns = result_set
|
||||
.columns()
|
||||
.as_ref()
|
||||
@@ -645,18 +667,17 @@ fn consume_mysql_query_result<T: MySqlProtocol>(
|
||||
.collect::<Vec<_>>();
|
||||
let rows_affected = result_set.affected_rows();
|
||||
|
||||
if columns.is_empty() {
|
||||
last_rows_affected = rows_affected;
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut rows = Vec::new();
|
||||
for row in result_set {
|
||||
while let Some(row) = result_set.next() {
|
||||
rows.push(mysql_row_to_strings(row.map_err(map_mysql_error)?));
|
||||
}
|
||||
|
||||
last_columns = columns;
|
||||
last_rows = rows;
|
||||
if columns.is_empty() {
|
||||
last_rows_affected = rows_affected;
|
||||
} else {
|
||||
last_columns = columns;
|
||||
last_rows = rows;
|
||||
}
|
||||
}
|
||||
|
||||
if last_columns.is_empty() {
|
||||
@@ -925,6 +946,24 @@ mod tests {
|
||||
assert_eq!(error.to_string(), "mysql authentication failed");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mysql_connection_enforces_utf8mb4_session() {
|
||||
let opts: mysql::Opts =
|
||||
mysql_opts_builder("127.0.0.1", 3306, Some("dbtool"), Some("secret"), "qa_demo").into();
|
||||
|
||||
assert_eq!(
|
||||
opts.get_init(),
|
||||
vec![String::from(MYSQL_UTF8MB4_SESSION_INIT)]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mysql_string_values_preserve_multilingual_utf8() {
|
||||
let rendered = mysql_value_to_string(&MySqlValue::Bytes("张敏😀".as_bytes().to_vec()));
|
||||
|
||||
assert_eq!(rendered, "张敏😀");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sqlite_driver_supports_connect_inspect_and_query() {
|
||||
let unique = SystemTime::now()
|
||||
|
||||
Reference in New Issue
Block a user