Skip to main content

sea_orm/database/
mod.rs

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