mirror of
https://github.com/t8y2/dbx.git
synced 2026-10-20 00:17:44 +00:00
fix(mysql): return result sets for EXECUTE statements
This commit is contained in:
@@ -6612,6 +6612,29 @@ async fn execute_manual_txn_mysql_statement(
|
||||
let mut result = conn.query_iter(sql).await.map_err(|e| format!("Query failed: {e}"))?;
|
||||
let columns: Vec<String> = result.columns_ref().iter().map(|c| c.name_str().to_string()).collect();
|
||||
let column_types: Vec<String> = result.columns_ref().iter().map(db::mysql::mysql_column_type_name).collect();
|
||||
// Some statements only *may* return rows — `EXECUTE` of a prepared DML statement
|
||||
// finishes with a plain OK packet instead — so report the affected rows rather than
|
||||
// failing on the missing result set.
|
||||
if columns.is_empty() {
|
||||
let affected_rows = result.affected_rows();
|
||||
result.drop_result().await.map_err(|e| format!("Query failed: {e}"))?;
|
||||
return Ok(db::QueryResult {
|
||||
columns: Vec::new(),
|
||||
column_types: Vec::new(),
|
||||
column_sortables: vec![],
|
||||
spatial_columns: vec![],
|
||||
spatial_values: vec![],
|
||||
rows: vec![],
|
||||
affected_rows,
|
||||
execution_time_ms: start.elapsed().as_millis(),
|
||||
server_execute_time_us: None,
|
||||
truncated: false,
|
||||
session_id: None,
|
||||
has_more: false,
|
||||
elasticsearch_raw_body: None,
|
||||
messages: Vec::new(),
|
||||
});
|
||||
}
|
||||
let mut data: Vec<Vec<serde_json::Value>> = Vec::with_capacity(row_limit.min(1024));
|
||||
let mut stream = result
|
||||
.stream::<mysql_async::Row>()
|
||||
|
||||
@@ -5878,11 +5878,15 @@ fn prefers_text_protocol_query(sql: &str, dialect: MySqlQueryDialect) -> bool {
|
||||
pub fn is_result_set_query(sql: &str, dialect: MySqlQueryDialect) -> bool {
|
||||
// MySQL 的表维护语句虽然不是 SELECT,但服务器会返回包含表名和执行结果的表格。
|
||||
// 如果把它们当成普通写入语句,后续 drop_result 会直接丢弃这些返回行。
|
||||
//
|
||||
// `EXECUTE` 同理:它执行的是运行时才确定的动态语句,`PREPARE` 的是查询时服务器已经
|
||||
// 把结果集发回来了,当成普通写入语句处理就会把这些行丢掉(#10005)。而且 MySQL 不
|
||||
// 允许在预处理协议里执行 `EXECUTE`(ERROR 1295),所以它必须走文本协议。
|
||||
starts_with_executable_sql_keyword_for_database(
|
||||
sql,
|
||||
&[
|
||||
"SELECT", "SHOW", "DESCRIBE", "EXPLAIN", "WITH", "CALL", "CHECKSUM", "ANALYZE", "CHECK", "OPTIMIZE",
|
||||
"REPAIR",
|
||||
"REPAIR", "EXECUTE",
|
||||
],
|
||||
DatabaseType::Mysql,
|
||||
) || mysql_statement_returns_rows(sql)
|
||||
@@ -6958,6 +6962,20 @@ mod tests {
|
||||
assert!(prefers_text_protocol_query("CALL proc_test1()", dialect));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mysql_execute_statements_are_treated_as_text_result_sets_per_issue_10005() {
|
||||
let dialect = MySqlQueryDialect::default();
|
||||
|
||||
// `EXECUTE` runs a dynamic statement whose result set the server already sent, and it
|
||||
// is rejected by the prepared-statement protocol, so it must take the text-protocol
|
||||
// result-set path instead of the write path that drops rows.
|
||||
for sql in ["EXECUTE stmt", "execute stmt;", "-- run dynamic sql\nEXECUTE stmt", "EXECUTE IMMEDIATE 'SELECT 1'"]
|
||||
{
|
||||
assert!(is_result_set_query(sql, dialect), "{sql}");
|
||||
assert!(prefers_text_protocol_query(sql, dialect), "{sql}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mysql_xa_recover_queries_are_treated_as_text_result_sets() {
|
||||
let dialect = MySqlQueryDialect::default();
|
||||
|
||||
Reference in New Issue
Block a user