Skip to content

Commit d82bd8e

Browse files
committed
refactor(odbc): improve SQL execution handling and row count extraction
- Streamlined SQL execution logic to handle results immediately, reducing borrowing conflicts. - Introduced a new function to extract the number of affected rows from ODBC statements, enhancing error handling and logging. - Added a test to verify the correct reporting of affected rows for various SQL operations.
1 parent 2a98014 commit d82bd8e

File tree

2 files changed

+65
-20
lines changed

2 files changed

+65
-20
lines changed

sqlx-core/src/odbc/connection/worker.rs

Lines changed: 34 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -449,31 +449,45 @@ fn execute_sql<'conn>(
449449
let params = prepare_parameters(args);
450450
let stmt = stmt_manager.get_or_create_direct_stmt()?;
451451
log::trace!("Starting execution of SQL: {}", sql);
452-
let cursor_result = stmt.execute(sql, &params[..]);
453-
log::trace!("Received cursor result for SQL: {}", sql);
454-
send_exec_result(cursor_result, tx)?;
452+
453+
// Execute and handle result immediately to avoid borrowing conflicts
454+
if let Some(mut cursor) = stmt.execute(sql, &params[..])? {
455+
handle_cursor(&mut cursor, tx);
456+
return Ok(());
457+
}
458+
459+
// Execution completed without result set, get affected rows
460+
let affected = extract_rows_affected(stmt);
461+
let _ = send_done(tx, affected);
455462
Ok(())
456463
}
457464

458-
// Unified execution logic for both direct and prepared statements
459-
fn send_exec_result<C>(
460-
execution_result: Result<Option<C>, odbc_api::Error>,
461-
tx: &ExecuteSender,
462-
) -> Result<(), Error>
463-
where
464-
C: Cursor + ResultSetMetadata,
465-
{
466-
match execution_result {
467-
Ok(Some(mut cursor)) => {
468-
handle_cursor(&mut cursor, tx);
469-
Ok(())
465+
fn extract_rows_affected(stmt: &mut Preallocated<StatementImpl<'_>>) -> u64 {
466+
let count_opt = match stmt.row_count() {
467+
Ok(count_opt) => count_opt,
468+
Err(e) => {
469+
log::warn!("Failed to get ODBC row count: {}", e);
470+
return 0;
470471
}
471-
Ok(None) => {
472-
let _ = send_done(tx, 0);
473-
Ok(())
472+
};
473+
474+
let count = match count_opt {
475+
Some(count) => count,
476+
None => {
477+
log::debug!("ODBC row count is not available");
478+
return 0;
474479
}
475-
Err(e) => Err(Error::from(e)),
476-
}
480+
};
481+
482+
let affected = match u64::try_from(count) {
483+
Ok(count) => count,
484+
Err(e) => {
485+
log::warn!("Failed to convert ODBC row count to u64: {}", e);
486+
return 0;
487+
}
488+
};
489+
log::trace!("ODBC statement affected {} rows", affected);
490+
affected
477491
}
478492

479493
fn prepare_parameters(

tests/any/any.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,3 +276,34 @@ async fn it_has_unsigned_integers() -> anyhow::Result<()> {
276276
assert_eq!(max_value, get_val::<u64>(&expr).await?);
277277
Ok(())
278278
}
279+
280+
#[sqlx_macros::test]
281+
async fn it_reports_rows_affected() -> anyhow::Result<()> {
282+
let mut conn = new::<Any>().await?;
283+
284+
let dbms = conn.dbms_name().await.unwrap_or_default().to_lowercase();
285+
let (create_sql, insert_sql, delete_sql) =
286+
if dbms.contains("mssql") || dbms.contains("sql server") {
287+
(
288+
"CREATE TABLE #temp_rows_affected (id INT PRIMARY KEY)",
289+
"INSERT INTO #temp_rows_affected (id) VALUES (1)",
290+
"DELETE FROM #temp_rows_affected WHERE id = 1",
291+
)
292+
} else {
293+
(
294+
"CREATE TEMPORARY TABLE temp_rows_affected (id INTEGER PRIMARY KEY)",
295+
"INSERT INTO temp_rows_affected (id) VALUES (1)",
296+
"DELETE FROM temp_rows_affected WHERE id = 1",
297+
)
298+
};
299+
300+
conn.execute(create_sql).await?;
301+
302+
let insert_done = conn.execute(insert_sql).await?;
303+
assert_eq!(insert_done.rows_affected(), 1);
304+
305+
let delete_done = conn.execute(delete_sql).await?;
306+
assert_eq!(delete_done.rows_affected(), 1);
307+
308+
Ok(())
309+
}

0 commit comments

Comments
 (0)