db_pool/async/
db_pool.rs

1use std::sync::Arc;
2
3use async_trait::async_trait;
4
5use super::{
6    backend::{Error, r#trait::Backend},
7    conn_pool::{ReusableConnectionPool as ReusableConnectionPoolInner, SingleUseConnectionPool},
8    object_pool::{ObjectPool, Reusable},
9};
10
11/// Wrapper for a reusable connection pool wrapped in a reusable object wrapper
12pub type ReusableConnectionPool<'a, B> = Reusable<'a, ReusableConnectionPoolInner<B>>;
13
14/// Database pool
15pub struct DatabasePool<B: Backend> {
16    backend: Arc<B>,
17    object_pool: ObjectPool<ReusableConnectionPoolInner<B>>,
18}
19
20impl<B: Backend> DatabasePool<B> {
21    /// Pulls a reusable connection pool
22    ///
23    /// Privileges are granted only for ``SELECT``, ``INSERT``, ``UPDATE``, and ``DELETE`` operations.
24    /// # Example
25    /// ```
26    /// use bb8::Pool;
27    /// use db_pool::{
28    ///     r#async::{DatabasePoolBuilderTrait, DieselAsyncPostgresBackend, DieselBb8},
29    ///     PrivilegedPostgresConfig,
30    /// };
31    /// use diesel::sql_query;
32    /// use diesel_async::RunQueryDsl;
33    /// use dotenvy::dotenv;
34    ///
35    /// async fn f() {
36    ///     dotenv().ok();
37    ///
38    ///     let config = PrivilegedPostgresConfig::from_env().unwrap();
39    ///
40    ///     let backend = DieselAsyncPostgresBackend::<DieselBb8>::new(
41    ///         config,
42    ///         |_| Pool::builder().max_size(10),
43    ///         |_| Pool::builder().max_size(2),
44    ///         None,
45    ///         move |mut conn| {
46    ///             Box::pin(async {
47    ///                 sql_query("CREATE TABLE book(id SERIAL PRIMARY KEY, title TEXT NOT NULL)")
48    ///                     .execute(&mut conn)
49    ///                     .await
50    ///                     .unwrap();
51    ///                 Some(conn)
52    ///             })
53    ///         },
54    ///     )
55    ///     .await
56    ///     .unwrap();
57    ///
58    ///     let db_pool = backend.create_database_pool().await.unwrap();
59    ///     let conn_pool = db_pool.pull_immutable();
60    /// }
61    ///
62    /// tokio_test::block_on(f());
63    /// ```
64    #[must_use]
65    pub async fn pull_immutable(&self) -> ReusableConnectionPool<B> {
66        self.object_pool.pull().await
67    }
68
69    /// Creates a single-use connection pool
70    ///
71    /// All privileges are granted.
72    /// # Example
73    /// ```
74    /// use bb8::Pool;
75    /// use db_pool::{
76    ///     r#async::{DatabasePoolBuilderTrait, DieselAsyncPostgresBackend, DieselBb8},
77    ///     PrivilegedPostgresConfig,
78    /// };
79    /// use diesel::sql_query;
80    /// use diesel_async::RunQueryDsl;
81    /// use dotenvy::dotenv;
82    ///
83    /// async fn f() {
84    ///     dotenv().ok();
85    ///
86    ///     let config = PrivilegedPostgresConfig::from_env().unwrap();
87    ///
88    ///     let backend = DieselAsyncPostgresBackend::<DieselBb8>::new(
89    ///         config,
90    ///         |_| Pool::builder().max_size(10),
91    ///         |_| Pool::builder().max_size(2),
92    ///         None,
93    ///         move |mut conn| {
94    ///             Box::pin(async {
95    ///                 sql_query("CREATE TABLE book(id SERIAL PRIMARY KEY, title TEXT NOT NULL)")
96    ///                     .execute(&mut conn)
97    ///                     .await
98    ///                     .unwrap();
99    ///                 Some(conn)
100    ///             })
101    ///         },
102    ///     )
103    ///     .await
104    ///     .unwrap();
105    ///
106    ///     let db_pool = backend.create_database_pool().await.unwrap();
107    ///     let conn_pool = db_pool.create_mutable();
108    /// }
109    ///
110    /// tokio_test::block_on(f());
111    /// ```
112    pub async fn create_mutable(
113        &self,
114    ) -> Result<
115        SingleUseConnectionPool<B>,
116        Error<B::BuildError, B::PoolError, B::ConnectionError, B::QueryError>,
117    > {
118        SingleUseConnectionPool::new(self.backend.clone()).await
119    }
120}
121
122/// Database pool builder trait implemented for all async backends
123#[async_trait]
124pub trait DatabasePoolBuilder: Backend {
125    /// Creates a database pool
126    /// # Example
127    /// ```
128    /// use bb8::Pool;
129    /// use db_pool::{
130    ///     r#async::{DatabasePoolBuilderTrait, DieselAsyncPostgresBackend, DieselBb8},
131    ///     PrivilegedPostgresConfig,
132    /// };
133    /// use diesel::sql_query;
134    /// use diesel_async::RunQueryDsl;
135    /// use dotenvy::dotenv;
136    ///
137    /// async fn f() {
138    ///     dotenv().ok();
139    ///
140    ///     let config = PrivilegedPostgresConfig::from_env().unwrap();
141    ///
142    ///     let backend = DieselAsyncPostgresBackend::<DieselBb8>::new(
143    ///         config,
144    ///         |_| Pool::builder().max_size(10),
145    ///         |_| Pool::builder().max_size(2),
146    ///         None,
147    ///         move |mut conn| {
148    ///             Box::pin(async {
149    ///                 sql_query("CREATE TABLE book(id SERIAL PRIMARY KEY, title TEXT NOT NULL)")
150    ///                     .execute(&mut conn)
151    ///                     .await
152    ///                     .unwrap();
153    ///                 Some(conn)
154    ///             })
155    ///         },
156    ///     )
157    ///     .await
158    ///     .unwrap();
159    ///
160    ///     let db_pool = backend.create_database_pool().await.unwrap();
161    /// }
162    ///
163    /// tokio_test::block_on(f());
164    /// ```
165    async fn create_database_pool(
166        self,
167    ) -> Result<
168        DatabasePool<Self>,
169        Error<Self::BuildError, Self::PoolError, Self::ConnectionError, Self::QueryError>,
170    > {
171        self.init().await?;
172        let backend = Arc::new(self);
173        let object_pool = {
174            let backend = backend.clone();
175            ObjectPool::new(
176                move || {
177                    let backend = backend.clone();
178                    Box::pin(async {
179                        ReusableConnectionPoolInner::new(backend)
180                            .await
181                            .expect("connection pool creation must succeed")
182                    })
183                },
184                |mut conn_pool| {
185                    Box::pin(async {
186                        conn_pool
187                            .clean()
188                            .await
189                            .expect("connection pool cleaning must succeed");
190                        conn_pool
191                    })
192                },
193            )
194        };
195        Ok(DatabasePool {
196            backend,
197            object_pool,
198        })
199    }
200}
201
202impl<AB: Backend> DatabasePoolBuilder for AB {}