Hoist ExecuteReturnedResults and make sure it behaves consistently across SQLite and PG.

This commit is contained in:
Sebastian Jeltsch
2026-05-08 15:58:47 +02:00
parent 22a7a8bd4c
commit a427047b15
5 changed files with 57 additions and 14 deletions
+3
View File
@@ -16,6 +16,9 @@ pub enum Error {
decl_type: Option<crate::rows::ValueType>,
},
#[error("ExecuteReturnedResults")]
ExecuteReturnedResults,
#[error("FromSql: {0}")]
FromSql(#[from] crate::from_sql::FromSqlError),
+23 -11
View File
@@ -783,6 +783,15 @@ mod tests {
.await
.unwrap()
);
// Make sure queries returning rows fail.
assert!(matches!(
conn.execute("SELECT 5;", ()).await,
Err(Error::ExecuteReturnedResults)
));
// Batch succeeds (consistent with rusqlite's execute_batch).
conn.execute_batch("SELECT 5;").await.unwrap();
}
#[tokio::test]
@@ -851,17 +860,20 @@ mod tests {
.await
.unwrap();
conn
.execute(
r#"INSERT INTO foo ("bool", "uuid", "text") VALUES (:b, :u, :t)"#,
named_params! {
":b": true,
":u": [0u8; 16],
":t": "test",
},
)
.await
.unwrap();
assert_eq!(
1,
conn
.execute(
r#"INSERT INTO foo ("bool", "uuid", "text") VALUES (:b, :u, :t)"#,
named_params! {
":b": true,
":u": [0u8; 16],
":t": "test",
},
)
.await
.unwrap()
);
}
#[tokio::test]
+4 -1
View File
@@ -31,7 +31,10 @@ impl SyncConnectionTrait for postgres::Client {
fn execute(&mut self, sql: impl AsRef<str>, params: impl Params) -> Result<usize, Error> {
let (sql, params) = PgStatement::new(sql.as_ref())?.bind(params)?;
let mut row_iter = self.query_raw(&sql, params)?;
row_iter.next()?;
// Actually execute query.
if row_iter.next()?.is_some() {
return Err(Error::ExecuteReturnedResults);
}
return Ok(row_iter.rows_affected().unwrap_or_default() as usize);
}
+12 -2
View File
@@ -81,7 +81,11 @@ pub(super) fn execute(
) -> Result<usize, Error> {
let mut stmt = conn.prepare_cached(sql.as_ref())?;
params.bind(&mut stmt)?;
return Ok(stmt.raw_execute()?);
return match stmt.raw_execute() {
Err(rusqlite::Error::ExecuteReturnedResults) => Err(Error::ExecuteReturnedResults),
r => Ok(r?),
};
}
#[inline]
@@ -96,7 +100,13 @@ pub(super) fn execute_batch(
// NOTE: We must use `raw_query` instead of `raw_execute`, otherwise queries
// returning rows (e.g. SELECT) will return an error. Rusqlite's batch_execute
// behaves consistently.
let _row = stmt.raw_query().next()?;
match stmt.raw_query().next() {
Err(rusqlite::Error::ExecuteReturnedResults) => return Err(Error::ExecuteReturnedResults),
Err(err) => {
return Err(err.into());
}
Ok(_) => {}
};
}
return Ok(());
}
+15
View File
@@ -644,3 +644,18 @@ fn test_busy() {
}
});
}
#[tokio::test]
async fn test_execute_returning_rows() {
let conn = Connection::open_in_memory().unwrap();
assert!(matches!(
conn.execute("SELECT 4;", ()).await,
Err(Error::ExecuteReturnedResults)
));
// Make sure rusqlite and trailabse_sqlite consistently succeed for `execute_batch()`.
conn.execute_batch("SELECT 4;").await.unwrap();
let c = rusqlite::Connection::open_in_memory().unwrap();
c.execute_batch("SELECT 4;").unwrap();
}