Skip to main content

sea_orm/database/
mod.rs

1use std::{sync::Arc, time::Duration};
2
3#[cfg(not(feature = "sync"))]
4use futures_util::future::BoxFuture;
5#[cfg(feature = "sqlx-mysql")]
6use sqlx::mysql::MySqlConnectOptions;
7#[cfg(feature = "sqlx-postgres")]
8use sqlx::postgres::PgConnectOptions;
9#[cfg(feature = "sqlx-sqlite")]
10use sqlx::sqlite::SqliteConnectOptions;
11
12mod connection;
13mod db_connection;
14mod executor;
15#[cfg(feature = "mock")]
16#[cfg_attr(docsrs, doc(cfg(feature = "mock")))]
17mod mock;
18#[cfg(feature = "proxy")]
19#[cfg_attr(docsrs, doc(cfg(feature = "proxy")))]
20mod proxy;
21#[cfg(feature = "rbac")]
22mod restricted_connection;
23#[cfg(all(feature = "schema-sync", feature = "rusqlite"))]
24mod sea_schema_rusqlite;
25#[cfg(all(feature = "schema-sync", feature = "sqlx-dep"))]
26mod sea_schema_shim;
27mod statement;
28#[cfg(feature = "stream")]
29mod stream;
30mod tracing_spans;
31mod transaction;
32
33pub use connection::*;
34pub use db_connection::*;
35pub use executor::*;
36#[cfg(feature = "mock")]
37#[cfg_attr(docsrs, doc(cfg(feature = "mock")))]
38pub use mock::*;
39#[cfg(feature = "proxy")]
40#[cfg_attr(docsrs, doc(cfg(feature = "proxy")))]
41pub use proxy::*;
42#[cfg(feature = "rbac")]
43pub use restricted_connection::*;
44pub use statement::*;
45use std::borrow::Cow;
46#[cfg(feature = "stream")]
47pub use stream::*;
48use tracing::instrument;
49pub use transaction::*;
50
51use crate::error::*;
52
53/// Entry point for opening a [`DatabaseConnection`]; see [`Database::connect`].
54#[derive(Debug, Default)]
55pub struct Database;
56
57#[cfg(feature = "sync")]
58type BoxFuture<'a, T> = T;
59
60#[cfg(feature = "sqlx-mysql")]
61type MapMySqlPoolOptsFn = Arc<
62    dyn Fn(sqlx::pool::PoolOptions<sqlx::MySql>) -> sqlx::pool::PoolOptions<sqlx::MySql>
63        + Send
64        + Sync,
65>;
66
67#[cfg(feature = "sqlx-postgres")]
68type MapPgPoolOptsFn = Arc<
69    dyn Fn(sqlx::pool::PoolOptions<sqlx::Postgres>) -> sqlx::pool::PoolOptions<sqlx::Postgres>
70        + Send
71        + Sync,
72>;
73
74#[cfg(feature = "sqlx-sqlite")]
75type MapSqlitePoolOptsFn = Option<
76    Arc<
77        dyn Fn(sqlx::pool::PoolOptions<sqlx::Sqlite>) -> sqlx::pool::PoolOptions<sqlx::Sqlite>
78            + Send
79            + Sync,
80    >,
81>;
82
83type AfterConnectCallback = Option<
84    Arc<
85        dyn Fn(DatabaseConnection) -> BoxFuture<'static, Result<(), DbErr>> + Send + Sync + 'static,
86    >,
87>;
88
89/// Configuration for opening a [`DatabaseConnection`]: connection URL, pool
90/// sizing, timeouts, logging, and backend-specific options.
91///
92/// Construct with [`ConnectOptions::new`] (or directly from a URL `&str`),
93/// then chain the setter methods before passing to [`Database::connect`].
94#[derive(derive_more::Debug, Clone)]
95pub struct ConnectOptions {
96    /// The URI of the database
97    pub(crate) url: String,
98    /// Maximum number of connections for a pool
99    pub(crate) max_connections: Option<u32>,
100    /// Minimum number of connections for a pool
101    pub(crate) min_connections: Option<u32>,
102    /// The connection timeout for a packet connection
103    pub(crate) connect_timeout: Option<Duration>,
104    /// Maximum idle time for a particular connection to prevent
105    /// network resource exhaustion
106    pub(crate) idle_timeout: Option<Option<Duration>>,
107    /// Set the maximum amount of time to spend waiting for acquiring a connection
108    pub(crate) acquire_timeout: Option<Duration>,
109    /// Set the maximum lifetime of individual connections
110    pub(crate) max_lifetime: Option<Option<Duration>>,
111    /// Enable SQLx statement logging
112    pub(crate) sqlx_logging: bool,
113    /// Record SQL statements in tracing spans
114    pub(crate) record_stmt_in_spans: bool,
115    /// SQLx statement logging level (ignored if `sqlx_logging` is false)
116    pub(crate) sqlx_logging_level: log::LevelFilter,
117    /// SQLx slow statements logging level (ignored if `sqlx_logging` is false)
118    pub(crate) sqlx_slow_statements_logging_level: log::LevelFilter,
119    /// SQLx slow statements duration threshold (ignored if `sqlx_logging` is false)
120    pub(crate) sqlx_slow_statements_logging_threshold: Duration,
121    /// set sqlcipher key
122    pub(crate) sqlcipher_key: Option<Cow<'static, str>>,
123    /// Schema search path (PostgreSQL only)
124    pub(crate) schema_search_path: Option<String>,
125    /// Application name (PostgreSQL only)
126    pub(crate) application_name: Option<String>,
127    /// Statement timeout (PostgreSQL only)
128    pub(crate) statement_timeout: Option<Duration>,
129    pub(crate) test_before_acquire: bool,
130    /// If set, a pooled connection is pinged before being handed out only when it has been
131    /// idle for at least this long (see [`ConnectOptions::test_before_acquire_if_idle_for`]).
132    pub(crate) test_before_acquire_if_idle_for: Option<Duration>,
133    /// Only establish connections to the DB as needed. If set to `true`, the db connection will
134    /// be created using SQLx's [connect_lazy](https://docs.rs/sqlx/latest/sqlx/struct.Pool.html#method.connect_lazy)
135    /// method.
136    pub(crate) connect_lazy: bool,
137
138    #[debug(skip)]
139    pub(crate) after_connect: AfterConnectCallback,
140
141    #[cfg(feature = "sqlx-mysql")]
142    #[debug(skip)]
143    pub(crate) mysql_pool_opts_fn: Option<MapMySqlPoolOptsFn>,
144    #[cfg(feature = "sqlx-postgres")]
145    #[debug(skip)]
146    pub(crate) pg_pool_opts_fn: Option<MapPgPoolOptsFn>,
147    #[cfg(feature = "sqlx-sqlite")]
148    #[debug(skip)]
149    pub(crate) sqlite_pool_opts_fn: MapSqlitePoolOptsFn,
150    #[cfg(feature = "sqlx-mysql")]
151    #[debug(skip)]
152    pub(crate) mysql_opts_fn:
153        Option<Arc<dyn Fn(MySqlConnectOptions) -> MySqlConnectOptions + Send + Sync>>,
154    #[cfg(feature = "sqlx-postgres")]
155    #[debug(skip)]
156    pub(crate) pg_opts_fn: Option<Arc<dyn Fn(PgConnectOptions) -> PgConnectOptions + Send + Sync>>,
157    #[cfg(feature = "sqlx-sqlite")]
158    #[debug(skip)]
159    pub(crate) sqlite_opts_fn:
160        Option<Arc<dyn Fn(SqliteConnectOptions) -> SqliteConnectOptions + Send + Sync>>,
161
162    #[cfg(feature = "sqlx-mysql")]
163    #[debug(skip)]
164    pub(crate) mysql_before_acquire_fn: Option<crate::driver::BeforeAcquireFn<sqlx::MySql>>,
165    #[cfg(feature = "sqlx-postgres")]
166    #[debug(skip)]
167    pub(crate) pg_before_acquire_fn: Option<crate::driver::BeforeAcquireFn<sqlx::Postgres>>,
168    #[cfg(feature = "sqlx-sqlite")]
169    #[debug(skip)]
170    pub(crate) sqlite_before_acquire_fn: Option<crate::driver::BeforeAcquireFn<sqlx::Sqlite>>,
171}
172
173impl Database {
174    /// Method to create a [DatabaseConnection] on a database. This method will return an error
175    /// if the database is not available.
176    #[instrument(level = "trace", skip(opt))]
177    pub async fn connect<C>(opt: C) -> Result<DatabaseConnection, DbErr>
178    where
179        C: Into<ConnectOptions>,
180    {
181        let opt: ConnectOptions = opt.into();
182
183        if url::Url::parse(&opt.url).is_err() {
184            return Err(conn_err(format!(
185                "The connection string '{}' cannot be parsed.",
186                opt.url
187            )));
188        }
189
190        #[cfg(feature = "sqlx-mysql")]
191        if DbBackend::MySql.is_prefix_of(&opt.url) {
192            return crate::SqlxMySqlConnector::connect(opt).await;
193        }
194        #[cfg(feature = "sqlx-postgres")]
195        if DbBackend::Postgres.is_prefix_of(&opt.url) {
196            return crate::SqlxPostgresConnector::connect(opt).await;
197        }
198        #[cfg(feature = "sqlx-sqlite")]
199        if DbBackend::Sqlite.is_prefix_of(&opt.url) {
200            return crate::SqlxSqliteConnector::connect(opt).await;
201        }
202        #[cfg(feature = "rusqlite")]
203        if DbBackend::Sqlite.is_prefix_of(&opt.url) {
204            return crate::driver::rusqlite::RusqliteConnector::connect(opt);
205        }
206        #[cfg(feature = "mock")]
207        if crate::MockDatabaseConnector::accepts(&opt.url) {
208            return crate::MockDatabaseConnector::connect(&opt.url).await;
209        }
210
211        Err(conn_err(format!(
212            "The connection string '{}' has no supporting driver.",
213            opt.url
214        )))
215    }
216
217    /// Method to create a [DatabaseConnection] on a proxy database
218    #[cfg(feature = "proxy")]
219    #[instrument(level = "trace", skip(proxy_func_arc))]
220    pub async fn connect_proxy(
221        db_type: DbBackend,
222        proxy_func_arc: std::sync::Arc<Box<dyn ProxyDatabaseTrait>>,
223    ) -> Result<DatabaseConnection, DbErr> {
224        match db_type {
225            DbBackend::MySql => {
226                return crate::ProxyDatabaseConnector::connect(
227                    DbBackend::MySql,
228                    proxy_func_arc.to_owned(),
229                );
230            }
231            DbBackend::Postgres => {
232                return crate::ProxyDatabaseConnector::connect(
233                    DbBackend::Postgres,
234                    proxy_func_arc.to_owned(),
235                );
236            }
237            DbBackend::Sqlite => {
238                return crate::ProxyDatabaseConnector::connect(
239                    DbBackend::Sqlite,
240                    proxy_func_arc.to_owned(),
241                );
242            }
243        }
244    }
245}
246
247impl<T> From<T> for ConnectOptions
248where
249    T: Into<String>,
250{
251    fn from(s: T) -> ConnectOptions {
252        ConnectOptions::new(s.into())
253    }
254}
255
256impl ConnectOptions {
257    /// Create new [ConnectOptions] for a [Database] by passing in a URI string
258    pub fn new<T>(url: T) -> Self
259    where
260        T: Into<String>,
261    {
262        Self {
263            url: url.into(),
264            max_connections: None,
265            min_connections: None,
266            connect_timeout: None,
267            idle_timeout: None,
268            acquire_timeout: None,
269            max_lifetime: None,
270            sqlx_logging: true,
271            record_stmt_in_spans: true,
272            sqlx_logging_level: log::LevelFilter::Info,
273            sqlx_slow_statements_logging_level: log::LevelFilter::Off,
274            sqlx_slow_statements_logging_threshold: Duration::from_secs(1),
275            sqlcipher_key: None,
276            schema_search_path: None,
277            application_name: None,
278            statement_timeout: None,
279            test_before_acquire: true,
280            test_before_acquire_if_idle_for: None,
281            connect_lazy: false,
282            after_connect: None,
283            #[cfg(feature = "sqlx-mysql")]
284            mysql_pool_opts_fn: None,
285            #[cfg(feature = "sqlx-postgres")]
286            pg_pool_opts_fn: None,
287            #[cfg(feature = "sqlx-sqlite")]
288            sqlite_pool_opts_fn: None,
289            #[cfg(feature = "sqlx-mysql")]
290            mysql_opts_fn: None,
291            #[cfg(feature = "sqlx-postgres")]
292            pg_opts_fn: None,
293            #[cfg(feature = "sqlx-sqlite")]
294            sqlite_opts_fn: None,
295            #[cfg(feature = "sqlx-mysql")]
296            mysql_before_acquire_fn: None,
297            #[cfg(feature = "sqlx-postgres")]
298            pg_before_acquire_fn: None,
299            #[cfg(feature = "sqlx-sqlite")]
300            sqlite_before_acquire_fn: None,
301        }
302    }
303
304    /// Get the database URL of the pool
305    pub fn get_url(&self) -> &str {
306        &self.url
307    }
308
309    /// Set the maximum number of connections of the pool
310    pub fn max_connections(&mut self, value: u32) -> &mut Self {
311        self.max_connections = Some(value);
312        self
313    }
314
315    /// Get the maximum number of connections of the pool, if set
316    pub fn get_max_connections(&self) -> Option<u32> {
317        self.max_connections
318    }
319
320    /// Set the minimum number of connections of the pool
321    pub fn min_connections(&mut self, value: u32) -> &mut Self {
322        self.min_connections = Some(value);
323        self
324    }
325
326    /// Get the minimum number of connections of the pool, if set
327    pub fn get_min_connections(&self) -> Option<u32> {
328        self.min_connections
329    }
330
331    /// Set the timeout duration when acquiring a connection
332    pub fn connect_timeout(&mut self, value: Duration) -> &mut Self {
333        self.connect_timeout = Some(value);
334        self
335    }
336
337    /// Get the timeout duration when acquiring a connection, if set
338    pub fn get_connect_timeout(&self) -> Option<Duration> {
339        self.connect_timeout
340    }
341
342    /// Set the idle duration before closing a connection.
343    pub fn idle_timeout<T>(&mut self, value: T) -> &mut Self
344    where
345        T: Into<Option<Duration>>,
346    {
347        self.idle_timeout = Some(value.into());
348        self
349    }
350
351    /// Get the idle duration before closing a connection, if set
352    pub fn get_idle_timeout(&self) -> Option<Option<Duration>> {
353        self.idle_timeout
354    }
355
356    /// Set the maximum amount of time to spend waiting for acquiring a connection
357    pub fn acquire_timeout(&mut self, value: Duration) -> &mut Self {
358        self.acquire_timeout = Some(value);
359        self
360    }
361
362    /// Get the maximum amount of time to spend waiting for acquiring a connection
363    pub fn get_acquire_timeout(&self) -> Option<Duration> {
364        self.acquire_timeout
365    }
366
367    /// Set the maximum lifetime of individual connections.
368    pub fn max_lifetime<T>(&mut self, lifetime: T) -> &mut Self
369    where
370        T: Into<Option<Duration>>,
371    {
372        self.max_lifetime = Some(lifetime.into());
373        self
374    }
375
376    /// Get the maximum lifetime of individual connections, if set
377    pub fn get_max_lifetime(&self) -> Option<Option<Duration>> {
378        self.max_lifetime
379    }
380
381    /// Enable SQLx statement logging (default true)
382    pub fn sqlx_logging(&mut self, value: bool) -> &mut Self {
383        self.sqlx_logging = value;
384        self
385    }
386
387    /// Get whether SQLx statement logging is enabled
388    pub fn get_sqlx_logging(&self) -> bool {
389        self.sqlx_logging
390    }
391
392    /// Enable recording `db.statement` in tracing spans (default true).
393    pub fn record_stmt_in_spans(&mut self, value: bool) -> &mut Self {
394        self.record_stmt_in_spans = value;
395        self
396    }
397
398    /// Get whether `db.statement` recording in tracing spans is enabled.
399    pub fn get_record_stmt_in_spans(&self) -> bool {
400        self.record_stmt_in_spans
401    }
402
403    /// Set SQLx statement logging level (default INFO).
404    /// (ignored if `sqlx_logging` is `false`)
405    pub fn sqlx_logging_level(&mut self, level: log::LevelFilter) -> &mut Self {
406        self.sqlx_logging_level = level;
407        self
408    }
409
410    /// Set SQLx slow statements logging level and duration threshold (default `LevelFilter::Off`).
411    /// (ignored if `sqlx_logging` is `false`)
412    pub fn sqlx_slow_statements_logging_settings(
413        &mut self,
414        level: log::LevelFilter,
415        duration: Duration,
416    ) -> &mut Self {
417        self.sqlx_slow_statements_logging_level = level;
418        self.sqlx_slow_statements_logging_threshold = duration;
419        self
420    }
421
422    /// Get the level of SQLx statement logging
423    pub fn get_sqlx_logging_level(&self) -> log::LevelFilter {
424        self.sqlx_logging_level
425    }
426
427    /// Get the SQLx slow statements logging settings
428    pub fn get_sqlx_slow_statements_logging_settings(&self) -> (log::LevelFilter, Duration) {
429        (
430            self.sqlx_slow_statements_logging_level,
431            self.sqlx_slow_statements_logging_threshold,
432        )
433    }
434
435    /// set key for sqlcipher
436    pub fn sqlcipher_key<T>(&mut self, value: T) -> &mut Self
437    where
438        T: Into<Cow<'static, str>>,
439    {
440        self.sqlcipher_key = Some(value.into());
441        self
442    }
443
444    /// Set schema search path (PostgreSQL only)
445    pub fn set_schema_search_path<T>(&mut self, schema_search_path: T) -> &mut Self
446    where
447        T: Into<String>,
448    {
449        self.schema_search_path = Some(schema_search_path.into());
450        self
451    }
452
453    /// Set application name (PostgreSQL only)
454    pub fn set_application_name<T>(&mut self, application_name: T) -> &mut Self
455    where
456        T: Into<String>,
457    {
458        self.application_name = Some(application_name.into());
459        self
460    }
461
462    /// Set the statement timeout (PostgreSQL only).
463    ///
464    /// This sets the PostgreSQL `statement_timeout` parameter via the connection options,
465    /// causing the server to abort any statement that exceeds the specified duration.
466    /// The timeout is applied at connection time and does not require an extra roundtrip.
467    ///
468    /// Has no effect on MySQL or SQLite connections.
469    pub fn statement_timeout(&mut self, value: Duration) -> &mut Self {
470        self.statement_timeout = Some(value);
471        self
472    }
473
474    /// Get the statement timeout, if set
475    pub fn get_statement_timeout(&self) -> Option<Duration> {
476        self.statement_timeout
477    }
478
479    /// If true, the connection will be pinged upon acquiring from the pool (default true).
480    ///
481    /// See [`test_before_acquire_if_idle_for`](Self::test_before_acquire_if_idle_for) for the
482    /// cheaper "only ping stale connections" variant.
483    pub fn test_before_acquire(&mut self, value: bool) -> &mut Self {
484        self.test_before_acquire = value;
485        self
486    }
487
488    /// Get whether a pooled connection is pinged on every acquire (default true).
489    pub fn get_test_before_acquire(&self) -> bool {
490        self.test_before_acquire
491    }
492
493    /// Ping a pooled connection before handing it out, but only once it has been idle for at
494    /// least `idle`.
495    ///
496    /// [`test_before_acquire`](Self::test_before_acquire) pings on *every* acquire, which adds
497    /// a round-trip to each checkout. This shorthand instead pings only connections that have
498    /// been idle long enough to plausibly have been dropped by the server, a proxy, or a
499    /// firewall — the common failure mode — while letting hot connections through untouched.
500    ///
501    /// Calling this sets [`test_before_acquire`](Self::test_before_acquire) to `false` (SQLx
502    /// runs the per-acquire ping *and* this hook, so leaving it enabled would ping on every
503    /// acquire regardless and defeat the threshold).
504    ///
505    /// It composes with a per-backend [`map_sqlx_postgres_before_acquire`] callback (and its
506    /// MySQL / SQLite counterparts): the idle-ping runs first, then your callback.
507    ///
508    /// # Expands to
509    /// With no per-backend callback set, this is exactly the following configuration on the
510    /// underlying [`sqlx::pool::PoolOptions`]:
511    ///
512    /// ```ignore
513    /// pool_options
514    ///     .test_before_acquire(false)
515    ///     .before_acquire(move |conn, meta| {
516    ///         Box::pin(async move {
517    ///             if meta.idle_for >= idle {
518    ///                 conn.ping().await?;
519    ///             }
520    ///             Ok(true)
521    ///         })
522    ///     })
523    /// ```
524    ///
525    /// Applies only to pools built through [`Database::connect`]. Pools adopted via
526    /// `SqlxPostgresConnector::from_sqlx_postgres_pool` (and the MySQL / SQLite equivalents)
527    /// bypass [`ConnectOptions`] entirely — configure `before_acquire` on your own
528    /// [`sqlx::pool::PoolOptions`] in that case.
529    ///
530    /// # Example
531    /// ```
532    /// # use sea_orm::ConnectOptions;
533    /// # use std::time::Duration;
534    /// let mut opt = ConnectOptions::new("postgres://localhost/db");
535    /// opt.test_before_acquire_if_idle_for(Duration::from_secs(30));
536    /// assert_eq!(opt.get_test_before_acquire(), false);
537    /// ```
538    ///
539    /// [`map_sqlx_postgres_before_acquire`]: Self::map_sqlx_postgres_before_acquire
540    pub fn test_before_acquire_if_idle_for(&mut self, idle: Duration) -> &mut Self {
541        self.test_before_acquire = false;
542        self.test_before_acquire_if_idle_for = Some(idle);
543        self
544    }
545
546    /// Get the idle threshold set by
547    /// [`test_before_acquire_if_idle_for`](Self::test_before_acquire_if_idle_for), if any.
548    pub fn get_test_before_acquire_if_idle_for(&self) -> Option<Duration> {
549        self.test_before_acquire_if_idle_for
550    }
551
552    /// If set to `true`, the db connection pool will be created using SQLx's
553    /// [connect_lazy](https://docs.rs/sqlx/latest/sqlx/struct.Pool.html#method.connect_lazy) method.
554    pub fn connect_lazy(&mut self, value: bool) -> &mut Self {
555        self.connect_lazy = value;
556        self
557    }
558
559    /// Get whether DB connections will be established when the pool is created or only as needed.
560    pub fn get_connect_lazy(&self) -> bool {
561        self.connect_lazy
562    }
563
564    /// Set a callback function that will be called after a new connection is established.
565    pub fn after_connect<F>(&mut self, f: F) -> &mut Self
566    where
567        F: Fn(DatabaseConnection) -> BoxFuture<'static, Result<(), DbErr>> + Send + Sync + 'static,
568    {
569        self.after_connect = Some(Arc::new(f));
570
571        self
572    }
573
574    #[cfg(feature = "sqlx-mysql")]
575    #[cfg_attr(docsrs, doc(cfg(feature = "sqlx-mysql")))]
576    /// Apply a function to modify the underlying [`MySqlConnectOptions`] before
577    /// creating the connection pool.
578    pub fn map_sqlx_mysql_opts<F>(&mut self, f: F) -> &mut Self
579    where
580        F: Fn(MySqlConnectOptions) -> MySqlConnectOptions + Send + Sync + 'static,
581    {
582        self.mysql_opts_fn = Some(Arc::new(f));
583        self
584    }
585
586    #[cfg(feature = "sqlx-mysql")]
587    #[cfg_attr(docsrs, doc(cfg(feature = "sqlx-mysql")))]
588    /// Apply a function to modify the underlying [`sqlx::pool::PoolOptions<sqlx::MySql>`]
589    /// before creating the connection pool.
590    pub fn map_sqlx_mysql_pool_opts<F>(&mut self, f: F) -> &mut Self
591    where
592        F: Fn(sqlx::pool::PoolOptions<sqlx::MySql>) -> sqlx::pool::PoolOptions<sqlx::MySql>
593            + Send
594            + Sync
595            + 'static,
596    {
597        self.mysql_pool_opts_fn = Some(Arc::new(f));
598        self
599    }
600
601    #[cfg(feature = "sqlx-postgres")]
602    #[cfg_attr(docsrs, doc(cfg(feature = "sqlx-postgres")))]
603    /// Apply a function to modify the underlying [`PgConnectOptions`] before
604    /// creating the connection pool.
605    pub fn map_sqlx_postgres_opts<F>(&mut self, f: F) -> &mut Self
606    where
607        F: Fn(PgConnectOptions) -> PgConnectOptions + Send + Sync + 'static,
608    {
609        self.pg_opts_fn = Some(Arc::new(f));
610        self
611    }
612
613    #[cfg(feature = "sqlx-postgres")]
614    #[cfg_attr(docsrs, doc(cfg(feature = "sqlx-postgres")))]
615    /// Apply a function to modify the underlying [`sqlx::pool::PoolOptions<sqlx::Postgres>`]
616    /// before creating the connection pool.
617    pub fn map_sqlx_postgres_pool_opts<F>(&mut self, f: F) -> &mut Self
618    where
619        F: Fn(sqlx::pool::PoolOptions<sqlx::Postgres>) -> sqlx::pool::PoolOptions<sqlx::Postgres>
620            + Send
621            + Sync
622            + 'static,
623    {
624        self.pg_pool_opts_fn = Some(Arc::new(f));
625        self
626    }
627
628    #[cfg(feature = "sqlx-sqlite")]
629    #[cfg_attr(docsrs, doc(cfg(feature = "sqlx-sqlite")))]
630    /// Apply a function to modify the underlying [`SqliteConnectOptions`] before
631    /// creating the connection pool.
632    pub fn map_sqlx_sqlite_opts<F>(&mut self, f: F) -> &mut Self
633    where
634        F: Fn(SqliteConnectOptions) -> SqliteConnectOptions + Send + Sync + 'static,
635    {
636        self.sqlite_opts_fn = Some(Arc::new(f));
637        self
638    }
639
640    #[cfg(feature = "sqlx-sqlite")]
641    #[cfg_attr(docsrs, doc(cfg(feature = "sqlx-sqlite")))]
642    /// Apply a function to modify the underlying [`sqlx::pool::PoolOptions<sqlx::Sqlite>`]
643    /// before creating the connection pool.
644    pub fn map_sqlx_sqlite_pool_opts<F>(&mut self, f: F) -> &mut Self
645    where
646        F: Fn(sqlx::pool::PoolOptions<sqlx::Sqlite>) -> sqlx::pool::PoolOptions<sqlx::Sqlite>
647            + Send
648            + Sync
649            + 'static,
650    {
651        self.sqlite_pool_opts_fn = Some(Arc::new(f));
652        self
653    }
654
655    #[cfg(feature = "sqlx-mysql")]
656    #[cfg_attr(docsrs, doc(cfg(feature = "sqlx-mysql")))]
657    /// Set a `before_acquire` callback run on an idle pooled MySQL connection before it is
658    /// handed out.
659    ///
660    /// Return `Ok(true)` to use the connection, or `Ok(false)`/`Err(_)` to discard it and let
661    /// the pool try another (opening a fresh one if needed). Composes after the idle-ping
662    /// installed by
663    /// [`test_before_acquire_if_idle_for`](Self::test_before_acquire_if_idle_for) — that runs
664    /// first, then this callback.
665    ///
666    /// Applies only to pools built through [`Database::connect`], not to pools adopted via
667    /// `SqlxMySqlConnector::from_sqlx_mysql_pool`.
668    pub fn map_sqlx_mysql_before_acquire<F>(&mut self, f: F) -> &mut Self
669    where
670        F: for<'c> Fn(
671                &'c mut sqlx::mysql::MySqlConnection,
672                sqlx::pool::PoolConnectionMetadata,
673            )
674                -> futures_util::future::BoxFuture<'c, Result<bool, sqlx::Error>>
675            + Send
676            + Sync
677            + 'static,
678    {
679        self.mysql_before_acquire_fn = Some(Arc::new(f));
680        self
681    }
682
683    #[cfg(feature = "sqlx-postgres")]
684    #[cfg_attr(docsrs, doc(cfg(feature = "sqlx-postgres")))]
685    /// Set a `before_acquire` callback run on an idle pooled PostgreSQL connection before it
686    /// is handed out.
687    ///
688    /// Return `Ok(true)` to use the connection, or `Ok(false)`/`Err(_)` to discard it and let
689    /// the pool try another (opening a fresh one if needed). Composes after the idle-ping
690    /// installed by
691    /// [`test_before_acquire_if_idle_for`](Self::test_before_acquire_if_idle_for) — that runs
692    /// first, then this callback.
693    ///
694    /// Applies only to pools built through [`Database::connect`], not to pools adopted via
695    /// `SqlxPostgresConnector::from_sqlx_postgres_pool`.
696    ///
697    /// # Example
698    /// ```
699    /// # use sea_orm::ConnectOptions;
700    /// # use std::time::Duration;
701    /// let mut opt = ConnectOptions::new("postgres://localhost/db");
702    /// opt.map_sqlx_postgres_before_acquire(|_conn, meta| {
703    ///     Box::pin(async move {
704    ///         // Discard (and transparently replace) connections older than 10 minutes,
705    ///         // rather than pinging on every acquire.
706    ///         Ok(meta.age < Duration::from_secs(600))
707    ///     })
708    /// });
709    /// ```
710    pub fn map_sqlx_postgres_before_acquire<F>(&mut self, f: F) -> &mut Self
711    where
712        F: for<'c> Fn(
713                &'c mut sqlx::postgres::PgConnection,
714                sqlx::pool::PoolConnectionMetadata,
715            )
716                -> futures_util::future::BoxFuture<'c, Result<bool, sqlx::Error>>
717            + Send
718            + Sync
719            + 'static,
720    {
721        self.pg_before_acquire_fn = Some(Arc::new(f));
722        self
723    }
724
725    #[cfg(feature = "sqlx-sqlite")]
726    #[cfg_attr(docsrs, doc(cfg(feature = "sqlx-sqlite")))]
727    /// Set a `before_acquire` callback run on an idle pooled SQLite connection before it is
728    /// handed out.
729    ///
730    /// Return `Ok(true)` to use the connection, or `Ok(false)`/`Err(_)` to discard it and let
731    /// the pool try another (opening a fresh one if needed). Composes after the idle-ping
732    /// installed by
733    /// [`test_before_acquire_if_idle_for`](Self::test_before_acquire_if_idle_for) — that runs
734    /// first, then this callback.
735    ///
736    /// Applies only to pools built through [`Database::connect`], not to pools adopted via
737    /// `SqlxSqliteConnector::from_sqlx_sqlite_pool`.
738    pub fn map_sqlx_sqlite_before_acquire<F>(&mut self, f: F) -> &mut Self
739    where
740        F: for<'c> Fn(
741                &'c mut sqlx::sqlite::SqliteConnection,
742                sqlx::pool::PoolConnectionMetadata,
743            )
744                -> futures_util::future::BoxFuture<'c, Result<bool, sqlx::Error>>
745            + Send
746            + Sync
747            + 'static,
748    {
749        self.sqlite_before_acquire_fn = Some(Arc::new(f));
750        self
751    }
752}