Skip to content

Commit b1a27dd

Browse files
committed
Add a Transaction type to simplify dealing with Transactions
1 parent 28ed854 commit b1a27dd

File tree

10 files changed

+272
-41
lines changed

10 files changed

+272
-41
lines changed

examples/realworld-postgres/src/main.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,9 @@ async fn register(mut req: Request<PgPool>) -> Response {
5050
let body: RegisterRequestBody = req.body_json().await.unwrap();
5151
let hash = hash_password(&body.password).unwrap();
5252

53-
let mut pool = req.state();
53+
// Make a new transaction
54+
let pool = req.state();
55+
let mut tx = pool.begin().await.unwrap();
5456

5557
let rec = sqlx::query!(
5658
r#"
@@ -62,12 +64,15 @@ RETURNING id, username, email
6264
body.email,
6365
hash,
6466
)
65-
.fetch_one(&mut pool)
67+
.fetch_one(&mut tx)
6668
.await
6769
.unwrap();
6870

6971
let token = generate_token(rec.id).unwrap();
7072

73+
// Explicitly commit
74+
tx.commit().await.unwrap();
75+
7176
#[derive(serde::Serialize)]
7277
struct RegisterResponseBody {
7378
user: User,

sqlx-core/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ mod database;
1616
mod executor;
1717
mod query;
1818
mod query_as;
19+
mod transaction;
1920
mod url;
2021

2122
#[macro_use]
@@ -47,6 +48,7 @@ pub use connection::{Connect, Connection};
4748
pub use executor::Executor;
4849
pub use query::{query, Query};
4950
pub use query_as::{query_as, QueryAs};
51+
pub use transaction::Transaction;
5052

5153
#[doc(hidden)]
5254
pub use query_as::query_as_mapped;

sqlx-core/src/mysql/connection.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use futures_core::future::BoxFuture;
77
use sha1::Sha1;
88

99
use crate::cache::StatementCache;
10-
use crate::connection::Connection;
10+
use crate::connection::{Connect, Connection};
1111
use crate::io::{Buf, BufMut, BufStream, MaybeTlsStream};
1212
use crate::mysql::error::MySqlError;
1313
use crate::mysql::protocol::{
@@ -475,7 +475,7 @@ impl MySqlConnection {
475475
}
476476

477477
impl MySqlConnection {
478-
pub(super) async fn open(url: crate::Result<Url>) -> crate::Result<Self> {
478+
pub(super) async fn establish(url: crate::Result<Url>) -> crate::Result<Self> {
479479
let url = url?;
480480
let mut self_ = Self::new(&url).await?;
481481

@@ -598,19 +598,19 @@ impl MySqlConnection {
598598
T: TryInto<Url, Error = crate::Error>,
599599
Self: Sized,
600600
{
601-
Box::pin(MySqlConnection::open(url.try_into()))
601+
Box::pin(MySqlConnection::establish(url.try_into()))
602602
}
603603
}
604604

605605
impl Connect for MySqlConnection {
606606
type Connection = MySqlConnection;
607607

608-
fn connect<T>(url: T) -> BoxFuture<'static, Result<MySqlConnection>>
608+
fn connect<T>(url: T) -> BoxFuture<'static, crate::Result<MySqlConnection>>
609609
where
610610
T: TryInto<Url, Error = crate::Error>,
611611
Self: Sized,
612612
{
613-
Box::pin(PgConnection::open(url.try_into()))
613+
Box::pin(MySqlConnection::establish(url.try_into()))
614614
}
615615
}
616616

sqlx-core/src/mysql/mod.rs

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -26,16 +26,3 @@ pub use row::MySqlRow;
2626

2727
/// An alias for [`Pool`], specialized for **MySQL**.
2828
pub type MySqlPool = super::Pool<MySql>;
29-
30-
use std::convert::TryInto;
31-
32-
use crate::url::Url;
33-
34-
// used in tests and hidden code in examples
35-
#[doc(hidden)]
36-
pub async fn connect<T>(url: T) -> crate::Result<MySqlConnection>
37-
where
38-
T: TryInto<Url, Error = crate::Error>,
39-
{
40-
MySqlConnection::open(url.try_into()).await
41-
}

sqlx-core/src/pool/executor.rs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use std::ops::DerefMut;
2+
13
use futures_core::{future::BoxFuture, stream::BoxStream};
24
use futures_util::StreamExt;
35

@@ -9,6 +11,8 @@ use crate::{
911
Database,
1012
};
1113

14+
use super::PoolConnection;
15+
1216
impl<C> Executor for Pool<C>
1317
where
1418
C: Connection + Connect<Connection = C>,
@@ -108,3 +112,45 @@ where
108112
Box::pin(async move { self.acquire().await?.describe(query).await })
109113
}
110114
}
115+
116+
impl<C> Executor for PoolConnection<C>
117+
where
118+
C: Connection + Connect<Connection = C>,
119+
{
120+
type Database = <C as Executor>::Database;
121+
122+
fn send<'e, 'q: 'e>(&'e mut self, commands: &'q str) -> BoxFuture<'e, crate::Result<()>> {
123+
self.deref_mut().send(commands)
124+
}
125+
126+
fn execute<'e, 'q: 'e>(
127+
&'e mut self,
128+
query: &'q str,
129+
args: <<C as Executor>::Database as Database>::Arguments,
130+
) -> BoxFuture<'e, crate::Result<u64>> {
131+
self.deref_mut().execute(query, args)
132+
}
133+
134+
fn fetch<'e, 'q: 'e>(
135+
&'e mut self,
136+
query: &'q str,
137+
args: <<C as Executor>::Database as Database>::Arguments,
138+
) -> BoxStream<'e, crate::Result<<<C as Executor>::Database as Database>::Row>> {
139+
self.deref_mut().fetch(query, args)
140+
}
141+
142+
fn fetch_optional<'e, 'q: 'e>(
143+
&'e mut self,
144+
query: &'q str,
145+
args: <<C as Executor>::Database as Database>::Arguments,
146+
) -> BoxFuture<'e, crate::Result<Option<<<C as Executor>::Database as Database>::Row>>> {
147+
self.deref_mut().fetch_optional(query, args)
148+
}
149+
150+
fn describe<'e, 'q: 'e>(
151+
&'e mut self,
152+
query: &'q str,
153+
) -> BoxFuture<'e, crate::Result<Describe<Self::Database>>> {
154+
self.deref_mut().describe(query)
155+
}
156+
}

sqlx-core/src/pool/mod.rs

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,26 @@
22
33
use std::{
44
fmt,
5+
mem,
56
ops::{Deref, DerefMut},
67
sync::Arc,
78
time::{Duration, Instant},
89
};
910

11+
use futures_core::future::BoxFuture;
12+
1013
use crate::connection::{Connect, Connection};
14+
use crate::transaction::Transaction;
1115

1216
use self::inner::SharedPool;
13-
pub use self::options::Builder;
1417
use self::options::Options;
1518

1619
mod executor;
1720
mod inner;
1821
mod options;
1922

23+
pub use self::options::Builder;
24+
2025
/// A pool of database connections.
2126
pub struct Pool<C>(Arc<SharedPool<C>>);
2227

@@ -84,6 +89,11 @@ where
8489
})
8590
}
8691

92+
/// Retrieves a new connection and immediately begins a new transaction.
93+
pub async fn begin(&self) -> crate::Result<Transaction<PoolConnection<C>>> {
94+
Ok(Transaction::new(0, self.acquire().await?).await?)
95+
}
96+
8797
/// Ends the use of a connection pool. Prevents any new connections
8898
/// and will close all active connections when they are returned to the pool.
8999
///
@@ -172,6 +182,27 @@ where
172182
}
173183
}
174184

185+
impl<C> Connection for PoolConnection<C>
186+
where
187+
C: Connection + Connect<Connection = C>,
188+
{
189+
fn close(mut self) -> BoxFuture<'static, crate::Result<()>> {
190+
Box::pin(async move {
191+
if let Some(live) = self.live.take() {
192+
let raw = live.raw;
193+
194+
// Explicitly close the connection
195+
raw.close().await?;
196+
}
197+
198+
// Forget ourself so it does not go back to the pool
199+
mem::forget(self);
200+
201+
Ok(())
202+
})
203+
}
204+
}
205+
175206
impl<C> Drop for PoolConnection<C>
176207
where
177208
C: Connection + Connect<Connection = C>,

sqlx-core/src/postgres/connection.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use rand::Rng;
88
use sha2::{Digest, Sha256};
99

1010
use crate::cache::StatementCache;
11-
use crate::connection::Connection;
11+
use crate::connection::{Connect, Connection};
1212
use crate::io::{Buf, BufStream, MaybeTlsStream};
1313
use crate::postgres::protocol::{
1414
self, hi, Authentication, Decode, Encode, Message, SaslInitialResponse, SaslResponse,
@@ -334,7 +334,7 @@ impl PgConnection {
334334
}
335335

336336
impl PgConnection {
337-
pub(super) async fn open(url: Result<Url>) -> Result<Self> {
337+
pub(super) async fn establish(url: Result<Url>) -> Result<Self> {
338338
let url = url?;
339339

340340
let stream = MaybeTlsStream::connect(&url, 5432).await?;
@@ -402,7 +402,7 @@ impl PgConnection {
402402
T: TryInto<Url, Error = crate::Error>,
403403
Self: Sized,
404404
{
405-
Box::pin(PgConnection::open(url.try_into()))
405+
Box::pin(PgConnection::establish(url.try_into()))
406406
}
407407
}
408408

@@ -414,7 +414,7 @@ impl Connect for PgConnection {
414414
T: TryInto<Url, Error = crate::Error>,
415415
Self: Sized,
416416
{
417-
Box::pin(PgConnection::open(url.try_into()))
417+
Box::pin(PgConnection::establish(url.try_into()))
418418
}
419419
}
420420

sqlx-core/src/postgres/mod.rs

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,3 @@ mod types;
1818

1919
/// An alias for [`Pool`], specialized for **Postgres**.
2020
pub type PgPool = super::Pool<Postgres>;
21-
22-
use std::convert::TryInto;
23-
24-
use crate::url::Url;
25-
26-
// used in tests and hidden code in examples
27-
#[doc(hidden)]
28-
pub async fn connect<T>(url: T) -> crate::Result<PgConnection>
29-
where
30-
T: TryInto<Url, Error = crate::Error>,
31-
{
32-
PgConnection::open(url.try_into()).await
33-
}

0 commit comments

Comments
 (0)