@@ -5,6 +5,7 @@ use crate::pool::inner::PoolInner;
5
5
use crate :: pool:: Pool ;
6
6
use futures_core:: future:: BoxFuture ;
7
7
use log:: LevelFilter ;
8
+ use std:: borrow:: Cow ;
8
9
use std:: fmt:: { self , Debug , Formatter } ;
9
10
use std:: sync:: Arc ;
10
11
use std:: time:: { Duration , Instant } ;
@@ -44,6 +45,18 @@ use std::time::{Duration, Instant};
44
45
/// the perspectives of both API designer and consumer.
45
46
pub struct PoolOptions < DB : Database > {
46
47
pub ( crate ) test_before_acquire : bool ,
48
+ pub ( crate ) before_connect : Option <
49
+ Arc <
50
+ dyn Fn (
51
+ & <DB :: Connection as Connection >:: Options ,
52
+ u32 ,
53
+ )
54
+ -> BoxFuture < ' _ , Result < Cow < ' _ , <DB :: Connection as Connection >:: Options > , Error > >
55
+ + ' static
56
+ + Send
57
+ + Sync ,
58
+ > ,
59
+ > ,
47
60
pub ( crate ) after_connect : Option <
48
61
Arc <
49
62
dyn Fn ( & mut DB :: Connection , PoolConnectionMetadata ) -> BoxFuture < ' _ , Result < ( ) , Error > >
@@ -94,6 +107,7 @@ impl<DB: Database> Clone for PoolOptions<DB> {
94
107
fn clone ( & self ) -> Self {
95
108
PoolOptions {
96
109
test_before_acquire : self . test_before_acquire ,
110
+ before_connect : self . before_connect . clone ( ) ,
97
111
after_connect : self . after_connect . clone ( ) ,
98
112
before_acquire : self . before_acquire . clone ( ) ,
99
113
after_release : self . after_release . clone ( ) ,
@@ -143,6 +157,7 @@ impl<DB: Database> PoolOptions<DB> {
143
157
pub fn new ( ) -> Self {
144
158
Self {
145
159
// User-specifiable routines
160
+ before_connect : None ,
146
161
after_connect : None ,
147
162
before_acquire : None ,
148
163
after_release : None ,
@@ -339,6 +354,54 @@ impl<DB: Database> PoolOptions<DB> {
339
354
self
340
355
}
341
356
357
+ /// Perform an asynchronous action before connecting to the database.
358
+ ///
359
+ /// This operation is performed on every attempt to connect, including retries. The
360
+ /// current `ConnectOptions` is passed, and this may be passed unchanged, or modified
361
+ /// after cloning. The current connection attempt is passed as the second parameter
362
+ /// (starting at 1).
363
+ ///
364
+ /// If the operation returns with an error, then the connection attempt fails without
365
+ /// attempting further retries. The operation therefore may need to implement error
366
+ /// handling and/or value caching to avoid failing the connection attempt.
367
+ ///
368
+ /// # Example: Per-Request Authentication
369
+ /// This callback may be used to modify values in the database's `ConnectOptions`, before
370
+ /// connecting to the database.
371
+ ///
372
+ /// This example is written for PostgreSQL but can likely be adapted to other databases.
373
+ ///
374
+ /// ```no_run
375
+ /// # async fn f() -> Result<(), Box<dyn std::error::Error>> {
376
+ /// use std::borrow::Cow;
377
+ /// use sqlx::Executor;
378
+ /// use sqlx::postgres::PgPoolOptions;
379
+ ///
380
+ /// let pool = PgPoolOptions::new()
381
+ /// .before_connect(move |opts, _num_attempts| Box::pin(async move {
382
+ /// Ok(Cow::Owned(opts.clone().password("abc")))
383
+ /// }))
384
+ /// .connect("postgres:// …").await?;
385
+ /// # Ok(())
386
+ /// # }
387
+ /// ```
388
+ ///
389
+ /// For a discussion on why `Box::pin()` is required, see [the type-level docs][Self].
390
+ pub fn before_connect < F > ( mut self , callback : F ) -> Self
391
+ where
392
+ for < ' c > F : Fn (
393
+ & ' c <DB :: Connection as Connection >:: Options ,
394
+ u32 ,
395
+ )
396
+ -> BoxFuture < ' c , crate :: Result < Cow < ' c , <DB :: Connection as Connection >:: Options > > >
397
+ + ' static
398
+ + Send
399
+ + Sync ,
400
+ {
401
+ self . before_connect = Some ( Arc :: new ( callback) ) ;
402
+ self
403
+ }
404
+
342
405
/// Perform an asynchronous action after connecting to the database.
343
406
///
344
407
/// If the operation returns with an error then the error is logged, the connection is closed
0 commit comments