Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions sqlx-core/src/connection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,7 @@ pub trait Connection: Send {
where
for<'c> F: FnOnce(&'c mut Transaction<'_, Self::Database>) -> BoxFuture<'c, Result<R, E>>
+ 'a
+ Send
+ Sync,
+ Send,
Self: Sized,
R: Send,
E: From<Error> + Send,
Expand Down
55 changes: 54 additions & 1 deletion sqlx-test/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use sqlx::pool::PoolOptions;
use sqlx::{Connection, Database, Error, Pool};
use sqlx::{Connection, Database, Error, Executor, Pool};
use std::env;

pub fn setup_if_needed() {
Expand Down Expand Up @@ -38,6 +38,59 @@ where
Ok(pool)
}

/// Check that Send-only transaction callbacks can commit and roll back.
pub async fn test_send_only_transaction_callbacks<C>(conn: &mut C) -> anyhow::Result<()>
where
C: Connection,
for<'c> &'c mut C: Executor<'c, Database = C::Database>,
{
fn assert_send(_: &(impl std::future::Future + Send)) {}

sqlx::raw_sql("CREATE TEMPORARY TABLE transaction_test (value BIGINT)")
.execute(&mut *conn)
.await?;

for commit in [true, false] {
// A Receiver is Send but not Sync.
let (sender, receiver) = std::sync::mpsc::channel();
sender.send(42_i64)?;

let future = conn.transaction(move |tx| {
Box::pin(async move {
sqlx::raw_sql("INSERT INTO transaction_test (value) VALUES (42)")
.execute(&mut **tx)
.await?;

// Keep the receiver across an await in the callback future, too.
let value = receiver.try_recv().unwrap();

if commit {
Ok(value)
} else {
Err(sqlx::Error::RowNotFound)
}
})
});

assert_send(&future);
let result = future.await;

if commit {
assert_eq!(result?, 42);
} else {
assert!(matches!(result, Err(sqlx::Error::RowNotFound)));
}

assert!(!conn.is_in_transaction());
let rows = sqlx::raw_sql("SELECT value FROM transaction_test")
.fetch_all(&mut *conn)
.await?;
assert_eq!(rows.len(), 1);
}

Ok(())
}

// Test type encoding and decoding
#[macro_export]
macro_rules! test_type {
Expand Down
8 changes: 8 additions & 0 deletions tests/any/any.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,14 @@ async fn it_connects() -> anyhow::Result<()> {
Ok(())
}

#[sqlx_macros::test]
async fn it_accepts_send_only_transaction_callbacks() -> anyhow::Result<()> {
sqlx::any::install_default_drivers();
let mut conn = new::<Any>().await?;

sqlx_test::test_send_only_transaction_callbacks(&mut conn).await
}

#[sqlx_macros::test]
async fn it_pings() -> anyhow::Result<()> {
sqlx::any::install_default_drivers();
Expand Down
7 changes: 7 additions & 0 deletions tests/mysql/mysql.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,13 @@ async fn it_connects() -> anyhow::Result<()> {
Ok(())
}

#[sqlx_macros::test]
async fn it_accepts_send_only_transaction_callbacks() -> anyhow::Result<()> {
let mut conn = new::<MySql>().await?;

sqlx_test::test_send_only_transaction_callbacks(&mut conn).await
}

#[sqlx_macros::test]
async fn it_connects_without_password() -> anyhow::Result<()> {
setup_if_needed();
Expand Down
7 changes: 7 additions & 0 deletions tests/postgres/postgres.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,13 @@ async fn it_connects() -> anyhow::Result<()> {
Ok(())
}

#[sqlx_macros::test]
async fn it_accepts_send_only_transaction_callbacks() -> anyhow::Result<()> {
let mut conn = new::<Postgres>().await?;

sqlx_test::test_send_only_transaction_callbacks(&mut conn).await
}

#[sqlx_macros::test]
async fn it_can_select_void() -> anyhow::Result<()> {
let mut conn = new::<Postgres>().await?;
Expand Down
7 changes: 7 additions & 0 deletions tests/sqlite/sqlite.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1344,6 +1344,13 @@ async fn test_serialize_invalid_schema() -> anyhow::Result<()> {
Ok(())
}

#[sqlx_macros::test]
async fn it_accepts_send_only_transaction_callbacks() -> anyhow::Result<()> {
let mut conn = SqliteConnection::connect(":memory:").await?;

sqlx_test::test_send_only_transaction_callbacks(&mut conn).await
}

#[sqlx_macros::test]
async fn it_can_use_transaction_options() -> anyhow::Result<()> {
async fn check_txn_state(conn: &mut SqliteConnection, expected: SqliteTransactionState) {
Expand Down
Loading