@@ -5,6 +5,7 @@ use crate::pool::inner::PoolInner;
55use crate :: pool:: Pool ;
66use futures_core:: future:: BoxFuture ;
77use log:: LevelFilter ;
8+ use std:: borrow:: Cow ;
89use std:: fmt:: { self , Debug , Formatter } ;
910use std:: sync:: Arc ;
1011use std:: time:: { Duration , Instant } ;
@@ -44,6 +45,18 @@ use std::time::{Duration, Instant};
4445/// the perspectives of both API designer and consumer.
4546pub struct PoolOptions < DB : Database > {
4647 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+ > ,
4760 pub ( crate ) after_connect : Option <
4861 Arc <
4962 dyn Fn ( & mut DB :: Connection , PoolConnectionMetadata ) -> BoxFuture < ' _ , Result < ( ) , Error > >
@@ -94,6 +107,7 @@ impl<DB: Database> Clone for PoolOptions<DB> {
94107 fn clone ( & self ) -> Self {
95108 PoolOptions {
96109 test_before_acquire : self . test_before_acquire ,
110+ before_connect : self . before_connect . clone ( ) ,
97111 after_connect : self . after_connect . clone ( ) ,
98112 before_acquire : self . before_acquire . clone ( ) ,
99113 after_release : self . after_release . clone ( ) ,
@@ -143,6 +157,7 @@ impl<DB: Database> PoolOptions<DB> {
143157 pub fn new ( ) -> Self {
144158 Self {
145159 // User-specifiable routines
160+ before_connect : None ,
146161 after_connect : None ,
147162 before_acquire : None ,
148163 after_release : None ,
@@ -339,6 +354,54 @@ impl<DB: Database> PoolOptions<DB> {
339354 self
340355 }
341356
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+
342405 /// Perform an asynchronous action after connecting to the database.
343406 ///
344407 /// If the operation returns with an error then the error is logged, the connection is closed
0 commit comments