1#![doc = include_str!("../README.md")]
2#![cfg_attr(docsrs, feature(doc_cfg))]
3#![deny(
4 nonstandard_style,
5 rust_2018_idioms,
6 rustdoc::broken_intra_doc_links,
7 rustdoc::private_intra_doc_links
8)]
9#![forbid(non_ascii_idents, unsafe_code)]
10#![warn(
11 deprecated_in_future,
12 missing_copy_implementations,
13 missing_debug_implementations,
14 missing_docs,
15 unreachable_pub,
16 unused_import_braces,
17 unused_labels,
18 unused_lifetimes,
19 unused_qualifications,
20 unused_results
21)]
22
23mod config;
24mod generic_client;
25mod statement_cache;
26
27use std::{
28 fmt,
29 future::Future,
30 ops::{Deref, DerefMut},
31 pin::Pin,
32 sync::Arc,
33};
34
35use deadpool::managed;
36#[cfg(not(target_arch = "wasm32"))]
37use tokio::spawn;
38use tokio::task::JoinHandle;
39use tokio_postgres::{
40 Client as PgClient, Config as PgConfig, Error, IsolationLevel, Statement,
41 Transaction as PgTransaction, TransactionBuilder as PgTransactionBuilder, types::Type,
42};
43
44#[cfg(not(target_arch = "wasm32"))]
45use tokio_postgres::{
46 Socket,
47 tls::{MakeTlsConnect, TlsConnect},
48};
49
50pub use tokio_postgres;
51
52pub use crate::statement_cache::{StatementCache, StatementCaches};
53
54pub use self::config::{
55 ChannelBinding, Config, ConfigError, LoadBalanceHosts, ManagerConfig, RecyclingMethod, SslMode,
56 TargetSessionAttrs,
57};
58
59pub use self::generic_client::GenericClient;
60
61pub use deadpool::managed::reexports::*;
62deadpool::managed_reexports!(
63 "tokio_postgres",
64 Manager,
65 managed::Object<Manager>,
66 Error,
67 ConfigError
68);
69
70type BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
71
72pub type Client = Object;
74
75type RecycleResult = managed::RecycleResult<Error>;
76type RecycleError = managed::RecycleError<Error>;
77
78pub struct Manager {
82 config: ManagerConfig,
83 pg_config: PgConfig,
84 connect: Box<dyn Connect>,
85 pub statement_caches: StatementCaches,
87}
88
89impl Manager {
90 #[cfg(not(target_arch = "wasm32"))]
91 pub fn new<T>(pg_config: tokio_postgres::Config, tls: T) -> Self
94 where
95 T: MakeTlsConnect<Socket> + Clone + Sync + Send + 'static,
96 T::Stream: Sync + Send,
97 T::TlsConnect: Sync + Send,
98 <T::TlsConnect as TlsConnect<Socket>>::Future: Send,
99 {
100 Self::from_config(pg_config, tls, ManagerConfig::default())
101 }
102
103 #[cfg(not(target_arch = "wasm32"))]
104 pub fn from_config<T>(pg_config: tokio_postgres::Config, tls: T, config: ManagerConfig) -> Self
107 where
108 T: MakeTlsConnect<Socket> + Clone + Sync + Send + 'static,
109 T::Stream: Sync + Send,
110 T::TlsConnect: Sync + Send,
111 <T::TlsConnect as TlsConnect<Socket>>::Future: Send,
112 {
113 Self::from_connect(pg_config, ConfigConnectImpl { tls }, config)
114 }
115
116 pub fn from_connect(
119 pg_config: tokio_postgres::Config,
120 connect: impl Connect + 'static,
121 config: ManagerConfig,
122 ) -> Self {
123 Self {
124 config,
125 pg_config,
126 connect: Box::new(connect),
127 statement_caches: StatementCaches::default(),
128 }
129 }
130}
131
132impl fmt::Debug for Manager {
133 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
134 f.debug_struct("Manager")
135 .field("config", &self.config)
136 .field("pg_config", &self.pg_config)
137 .field("statement_caches", &self.statement_caches)
139 .finish()
140 }
141}
142
143impl managed::Manager for Manager {
144 type Type = ClientWrapper;
145 type Error = Error;
146
147 async fn create(&self) -> Result<ClientWrapper, Error> {
148 let (client, conn_task) = self.connect.connect(&self.pg_config).await?;
149 let client_wrapper = ClientWrapper::new(client, conn_task);
150 self.statement_caches
151 .attach(&client_wrapper.statement_cache);
152 Ok(client_wrapper)
153 }
154
155 async fn recycle(&self, client: &mut ClientWrapper, _: &Metrics) -> RecycleResult {
156 if client.is_closed() {
157 tracing::warn!(target: "deadpool.postgres", "Connection could not be recycled: Connection closed");
158 return Err(RecycleError::message("Connection closed"));
159 }
160 match self.config.recycling_method.query() {
161 Some(sql) => match client.simple_query(sql).await {
162 Ok(_) => Ok(()),
163 Err(e) => {
164 tracing::warn!(target: "deadpool.postgres", "Connection could not be recycled: {}", e);
165 Err(e.into())
166 }
167 },
168 None => Ok(()),
169 }
170 }
171
172 fn detach(&self, object: &mut ClientWrapper) {
173 self.statement_caches.detach(&object.statement_cache);
174 }
175}
176
177pub trait Connect: Sync + Send {
180 fn connect(
184 &self,
185 pg_config: &PgConfig,
186 ) -> BoxFuture<'_, Result<(PgClient, JoinHandle<()>), Error>>;
187}
188
189#[cfg(not(target_arch = "wasm32"))]
190#[derive(Debug)]
193pub struct ConfigConnectImpl<T>
194where
195 T: MakeTlsConnect<Socket> + Clone + Sync + Send + 'static,
196 T::Stream: Sync + Send,
197 T::TlsConnect: Sync + Send,
198 <T::TlsConnect as TlsConnect<Socket>>::Future: Send,
199{
200 pub tls: T,
202}
203
204#[cfg(not(target_arch = "wasm32"))]
205impl<T> Connect for ConfigConnectImpl<T>
206where
207 T: MakeTlsConnect<Socket> + Clone + Sync + Send + 'static,
208 T::Stream: Sync + Send,
209 T::TlsConnect: Sync + Send,
210 <T::TlsConnect as TlsConnect<Socket>>::Future: Send,
211{
212 fn connect(
213 &self,
214 pg_config: &PgConfig,
215 ) -> BoxFuture<'_, Result<(PgClient, JoinHandle<()>), Error>> {
216 let tls = self.tls.clone();
217 let pg_config = pg_config.clone();
218 Box::pin(async move {
219 let fut = pg_config.connect(tls);
220 let (client, connection) = fut.await?;
221 let conn_task = spawn(async move {
222 if let Err(e) = connection.await {
223 tracing::warn!(target: "deadpool.postgres", "Connection error: {}", e);
224 }
225 });
226 Ok((client, conn_task))
227 })
228 }
229}
230
231#[derive(Debug)]
233pub struct ClientWrapper {
234 client: PgClient,
236
237 conn_task: JoinHandle<()>,
240
241 pub statement_cache: Arc<StatementCache>,
243}
244
245impl ClientWrapper {
246 #[must_use]
249 pub fn new(client: PgClient, conn_task: JoinHandle<()>) -> Self {
250 Self {
251 client,
252 conn_task,
253 statement_cache: Arc::new(StatementCache::new()),
254 }
255 }
256
257 pub async fn prepare_cached(&self, query: &str) -> Result<Statement, Error> {
260 self.statement_cache.prepare(&self.client, query).await
261 }
262
263 pub async fn prepare_typed_cached(
266 &self,
267 query: &str,
268 types: &[Type],
269 ) -> Result<Statement, Error> {
270 self.statement_cache
271 .prepare_typed(&self.client, query, types)
272 .await
273 }
274
275 #[allow(unused_lifetimes)] pub async fn transaction(&mut self) -> Result<Transaction<'_>, Error> {
279 Ok(Transaction {
280 txn: PgClient::transaction(&mut self.client).await?,
281 statement_cache: self.statement_cache.clone(),
282 })
283 }
284
285 pub fn build_transaction(&mut self) -> TransactionBuilder<'_> {
288 TransactionBuilder {
289 builder: self.client.build_transaction(),
290 statement_cache: self.statement_cache.clone(),
291 }
292 }
293}
294
295impl Deref for ClientWrapper {
296 type Target = PgClient;
297
298 fn deref(&self) -> &PgClient {
299 &self.client
300 }
301}
302
303impl DerefMut for ClientWrapper {
304 fn deref_mut(&mut self) -> &mut PgClient {
305 &mut self.client
306 }
307}
308
309impl Drop for ClientWrapper {
310 fn drop(&mut self) {
311 self.conn_task.abort()
312 }
313}
314
315pub struct Transaction<'a> {
318 txn: PgTransaction<'a>,
320
321 pub statement_cache: Arc<StatementCache>,
323}
324
325impl Transaction<'_> {
326 pub async fn prepare_cached(&self, query: &str) -> Result<Statement, Error> {
329 self.statement_cache.prepare(self.client(), query).await
330 }
331
332 pub async fn prepare_typed_cached(
335 &self,
336 query: &str,
337 types: &[Type],
338 ) -> Result<Statement, Error> {
339 self.statement_cache
340 .prepare_typed(self.client(), query, types)
341 .await
342 }
343
344 pub async fn commit(self) -> Result<(), Error> {
346 self.txn.commit().await
347 }
348
349 pub async fn rollback(self) -> Result<(), Error> {
351 self.txn.rollback().await
352 }
353
354 #[allow(unused_lifetimes)] pub async fn transaction(&mut self) -> Result<Transaction<'_>, Error> {
358 Ok(Transaction {
359 txn: PgTransaction::transaction(&mut self.txn).await?,
360 statement_cache: self.statement_cache.clone(),
361 })
362 }
363
364 #[allow(unused_lifetimes)] pub async fn savepoint<I>(&mut self, name: I) -> Result<Transaction<'_>, Error>
368 where
369 I: Into<String>,
370 {
371 Ok(Transaction {
372 txn: PgTransaction::savepoint(&mut self.txn, name).await?,
373 statement_cache: self.statement_cache.clone(),
374 })
375 }
376}
377
378impl fmt::Debug for Transaction<'_> {
379 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
380 f.debug_struct("Transaction")
381 .field("statement_cache", &self.statement_cache)
383 .finish()
384 }
385}
386
387impl<'a> Deref for Transaction<'a> {
388 type Target = PgTransaction<'a>;
389
390 fn deref(&self) -> &PgTransaction<'a> {
391 &self.txn
392 }
393}
394
395impl<'a> DerefMut for Transaction<'a> {
396 fn deref_mut(&mut self) -> &mut PgTransaction<'a> {
397 &mut self.txn
398 }
399}
400
401#[must_use = "builder does nothing itself, use `.start()` to use it"]
404pub struct TransactionBuilder<'a> {
405 builder: PgTransactionBuilder<'a>,
407
408 statement_cache: Arc<StatementCache>,
410}
411
412impl<'a> TransactionBuilder<'a> {
413 pub fn isolation_level(self, isolation_level: IsolationLevel) -> Self {
417 Self {
418 builder: self.builder.isolation_level(isolation_level),
419 statement_cache: self.statement_cache,
420 }
421 }
422
423 pub fn read_only(self, read_only: bool) -> Self {
427 Self {
428 builder: self.builder.read_only(read_only),
429 statement_cache: self.statement_cache,
430 }
431 }
432
433 pub fn deferrable(self, deferrable: bool) -> Self {
442 Self {
443 builder: self.builder.deferrable(deferrable),
444 statement_cache: self.statement_cache,
445 }
446 }
447
448 pub async fn start(self) -> Result<Transaction<'a>, Error> {
455 Ok(Transaction {
456 txn: self.builder.start().await?,
457 statement_cache: self.statement_cache,
458 })
459 }
460}
461
462impl fmt::Debug for TransactionBuilder<'_> {
463 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
464 f.debug_struct("TransactionBuilder")
465 .field("statement_cache", &self.statement_cache)
467 .finish()
468 }
469}
470
471impl<'a> Deref for TransactionBuilder<'a> {
472 type Target = PgTransactionBuilder<'a>;
473
474 fn deref(&self) -> &Self::Target {
475 &self.builder
476 }
477}
478
479impl DerefMut for TransactionBuilder<'_> {
480 fn deref_mut(&mut self) -> &mut Self::Target {
481 &mut self.builder
482 }
483}