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    /// Only establish connections to the DB as needed. If set to `true`, the db connection will
117    /// be created using SQLx's [connect_lazy](https://docs.rs/sqlx/latest/sqlx/struct.Pool.html#method.connect_lazy)
118    /// method.
119    pub(crate) connect_lazy: bool,
120
121    #[debug(skip)]
122    pub(crate) after_connect: AfterConnectCallback,
123
124    #[cfg(feature = "sqlx-mysql")]
125    #[debug(skip)]
126    pub(crate) mysql_pool_opts_fn: Option<MapMySqlPoolOptsFn>,
127    #[cfg(feature = "sqlx-postgres")]
128    #[debug(skip)]
129    pub(crate) pg_pool_opts_fn: Option<MapPgPoolOptsFn>,
130    #[cfg(feature = "sqlx-sqlite")]
131    #[debug(skip)]
132    pub(crate) sqlite_pool_opts_fn: MapSqlitePoolOptsFn,
133    #[cfg(feature = "sqlx-mysql")]
134    #[debug(skip)]
135    pub(crate) mysql_opts_fn: Option<Arc<dyn Fn(MySqlConnectOptions) -> MySqlConnectOptions>>,
136    #[cfg(feature = "sqlx-postgres")]
137    #[debug(skip)]
138    pub(crate) pg_opts_fn: Option<Arc<dyn Fn(PgConnectOptions) -> PgConnectOptions>>,
139    #[cfg(feature = "sqlx-sqlite")]
140    #[debug(skip)]
141    pub(crate) sqlite_opts_fn: Option<Arc<dyn Fn(SqliteConnectOptions) -> SqliteConnectOptions>>,
142}
143
144impl Database {
145    /// Method to create a [DatabaseConnection] on a database. This method will return an error
146    /// if the database is not available.
147    #[instrument(level = "trace", skip(opt))]
148    pub fn connect<C>(opt: C) -> Result<DatabaseConnection, DbErr>
149    where
150        C: Into<ConnectOptions>,
151    {
152        let opt: ConnectOptions = opt.into();
153
154        if url::Url::parse(&opt.url).is_err() {
155            return Err(conn_err(format!(
156                "The connection string '{}' cannot be parsed.",
157                opt.url
158            )));
159        }
160
161        #[cfg(feature = "sqlx-mysql")]
162        if DbBackend::MySql.is_prefix_of(&opt.url) {
163            return crate::SqlxMySqlConnector::connect(opt);
164        }
165        #[cfg(feature = "sqlx-postgres")]
166        if DbBackend::Postgres.is_prefix_of(&opt.url) {
167            return crate::SqlxPostgresConnector::connect(opt);
168        }
169        #[cfg(feature = "sqlx-sqlite")]
170        if DbBackend::Sqlite.is_prefix_of(&opt.url) {
171            return crate::SqlxSqliteConnector::connect(opt);
172        }
173        #[cfg(feature = "rusqlite")]
174        if DbBackend::Sqlite.is_prefix_of(&opt.url) {
175            return crate::driver::rusqlite::RusqliteConnector::connect(opt);
176        }
177        #[cfg(feature = "mock")]
178        if crate::MockDatabaseConnector::accepts(&opt.url) {
179            return crate::MockDatabaseConnector::connect(&opt.url);
180        }
181
182        Err(conn_err(format!(
183            "The connection string '{}' has no supporting driver.",
184            opt.url
185        )))
186    }
187
188    /// Method to create a [DatabaseConnection] on a proxy database
189    #[cfg(feature = "proxy")]
190    #[instrument(level = "trace", skip(proxy_func_arc))]
191    pub fn connect_proxy(
192        db_type: DbBackend,
193        proxy_func_arc: std::sync::Arc<Box<dyn ProxyDatabaseTrait>>,
194    ) -> Result<DatabaseConnection, DbErr> {
195        match db_type {
196            DbBackend::MySql => {
197                return crate::ProxyDatabaseConnector::connect(
198                    DbBackend::MySql,
199                    proxy_func_arc.to_owned(),
200                );
201            }
202            DbBackend::Postgres => {
203                return crate::ProxyDatabaseConnector::connect(
204                    DbBackend::Postgres,
205                    proxy_func_arc.to_owned(),
206                );
207            }
208            DbBackend::Sqlite => {
209                return crate::ProxyDatabaseConnector::connect(
210                    DbBackend::Sqlite,
211                    proxy_func_arc.to_owned(),
212                );
213            }
214        }
215    }
216}
217
218impl<T> From<T> for ConnectOptions
219where
220    T: Into<String>,
221{
222    fn from(s: T) -> ConnectOptions {
223        ConnectOptions::new(s.into())
224    }
225}
226
227impl ConnectOptions {
228    /// Create new [ConnectOptions] for a [Database] by passing in a URI string
229    pub fn new<T>(url: T) -> Self
230    where
231        T: Into<String>,
232    {
233        Self {
234            url: url.into(),
235            max_connections: None,
236            min_connections: None,
237            connect_timeout: None,
238            idle_timeout: None,
239            acquire_timeout: None,
240            max_lifetime: None,
241            sqlx_logging: true,
242            record_stmt_in_spans: true,
243            sqlx_logging_level: log::LevelFilter::Info,
244            sqlx_slow_statements_logging_level: log::LevelFilter::Off,
245            sqlx_slow_statements_logging_threshold: Duration::from_secs(1),
246            sqlcipher_key: None,
247            schema_search_path: None,
248            application_name: None,
249            statement_timeout: None,
250            test_before_acquire: true,
251            connect_lazy: false,
252            after_connect: None,
253            #[cfg(feature = "sqlx-mysql")]
254            mysql_pool_opts_fn: None,
255            #[cfg(feature = "sqlx-postgres")]
256            pg_pool_opts_fn: None,
257            #[cfg(feature = "sqlx-sqlite")]
258            sqlite_pool_opts_fn: None,
259            #[cfg(feature = "sqlx-mysql")]
260            mysql_opts_fn: None,
261            #[cfg(feature = "sqlx-postgres")]
262            pg_opts_fn: None,
263            #[cfg(feature = "sqlx-sqlite")]
264            sqlite_opts_fn: None,
265        }
266    }
267
268    /// Get the database URL of the pool
269    pub fn get_url(&self) -> &str {
270        &self.url
271    }
272
273    /// Set the maximum number of connections of the pool
274    pub fn max_connections(&mut self, value: u32) -> &mut Self {
275        self.max_connections = Some(value);
276        self
277    }
278
279    /// Get the maximum number of connections of the pool, if set
280    pub fn get_max_connections(&self) -> Option<u32> {
281        self.max_connections
282    }
283
284    /// Set the minimum number of connections of the pool
285    pub fn min_connections(&mut self, value: u32) -> &mut Self {
286        self.min_connections = Some(value);
287        self
288    }
289
290    /// Get the minimum number of connections of the pool, if set
291    pub fn get_min_connections(&self) -> Option<u32> {
292        self.min_connections
293    }
294
295    /// Set the timeout duration when acquiring a connection
296    pub fn connect_timeout(&mut self, value: Duration) -> &mut Self {
297        self.connect_timeout = Some(value);
298        self
299    }
300
301    /// Get the timeout duration when acquiring a connection, if set
302    pub fn get_connect_timeout(&self) -> Option<Duration> {
303        self.connect_timeout
304    }
305
306    /// Set the idle duration before closing a connection.
307    pub fn idle_timeout<T>(&mut self, value: T) -> &mut Self
308    where
309        T: Into<Option<Duration>>,
310    {
311        self.idle_timeout = Some(value.into());
312        self
313    }
314
315    /// Get the idle duration before closing a connection, if set
316    pub fn get_idle_timeout(&self) -> Option<Option<Duration>> {
317        self.idle_timeout
318    }
319
320    /// Set the maximum amount of time to spend waiting for acquiring a connection
321    pub fn acquire_timeout(&mut self, value: Duration) -> &mut Self {
322        self.acquire_timeout = Some(value);
323        self
324    }
325
326    /// Get the maximum amount of time to spend waiting for acquiring a connection
327    pub fn get_acquire_timeout(&self) -> Option<Duration> {
328        self.acquire_timeout
329    }
330
331    /// Set the maximum lifetime of individual connections.
332    pub fn max_lifetime<T>(&mut self, lifetime: T) -> &mut Self
333    where
334        T: Into<Option<Duration>>,
335    {
336        self.max_lifetime = Some(lifetime.into());
337        self
338    }
339
340    /// Get the maximum lifetime of individual connections, if set
341    pub fn get_max_lifetime(&self) -> Option<Option<Duration>> {
342        self.max_lifetime
343    }
344
345    /// Enable SQLx statement logging (default true)
346    pub fn sqlx_logging(&mut self, value: bool) -> &mut Self {
347        self.sqlx_logging = value;
348        self
349    }
350
351    /// Get whether SQLx statement logging is enabled
352    pub fn get_sqlx_logging(&self) -> bool {
353        self.sqlx_logging
354    }
355
356    /// Enable recording `db.statement` in tracing spans (default true).
357    pub fn record_stmt_in_spans(&mut self, value: bool) -> &mut Self {
358        self.record_stmt_in_spans = value;
359        self
360    }
361
362    /// Get whether `db.statement` recording in tracing spans is enabled.
363    pub fn get_record_stmt_in_spans(&self) -> bool {
364        self.record_stmt_in_spans
365    }
366
367    /// Set SQLx statement logging level (default INFO).
368    /// (ignored if `sqlx_logging` is `false`)
369    pub fn sqlx_logging_level(&mut self, level: log::LevelFilter) -> &mut Self {
370        self.sqlx_logging_level = level;
371        self
372    }
373
374    /// Set SQLx slow statements logging level and duration threshold (default `LevelFilter::Off`).
375    /// (ignored if `sqlx_logging` is `false`)
376    pub fn sqlx_slow_statements_logging_settings(
377        &mut self,
378        level: log::LevelFilter,
379        duration: Duration,
380    ) -> &mut Self {
381        self.sqlx_slow_statements_logging_level = level;
382        self.sqlx_slow_statements_logging_threshold = duration;
383        self
384    }
385
386    /// Get the level of SQLx statement logging
387    pub fn get_sqlx_logging_level(&self) -> log::LevelFilter {
388        self.sqlx_logging_level
389    }
390
391    /// Get the SQLx slow statements logging settings
392    pub fn get_sqlx_slow_statements_logging_settings(&self) -> (log::LevelFilter, Duration) {
393        (
394            self.sqlx_slow_statements_logging_level,
395            self.sqlx_slow_statements_logging_threshold,
396        )
397    }
398
399    /// set key for sqlcipher
400    pub fn sqlcipher_key<T>(&mut self, value: T) -> &mut Self
401    where
402        T: Into<Cow<'static, str>>,
403    {
404        self.sqlcipher_key = Some(value.into());
405        self
406    }
407
408    /// Set schema search path (PostgreSQL only)
409    pub fn set_schema_search_path<T>(&mut self, schema_search_path: T) -> &mut Self
410    where
411        T: Into<String>,
412    {
413        self.schema_search_path = Some(schema_search_path.into());
414        self
415    }
416
417    /// Set application name (PostgreSQL only)
418    pub fn set_application_name<T>(&mut self, application_name: T) -> &mut Self
419    where
420        T: Into<String>,
421    {
422        self.application_name = Some(application_name.into());
423        self
424    }
425
426    /// Set the statement timeout (PostgreSQL only).
427    ///
428    /// This sets the PostgreSQL `statement_timeout` parameter via the connection options,
429    /// causing the server to abort any statement that exceeds the specified duration.
430    /// The timeout is applied at connection time and does not require an extra roundtrip.
431    ///
432    /// Has no effect on MySQL or SQLite connections.
433    pub fn statement_timeout(&mut self, value: Duration) -> &mut Self {
434        self.statement_timeout = Some(value);
435        self
436    }
437
438    /// Get the statement timeout, if set
439    pub fn get_statement_timeout(&self) -> Option<Duration> {
440        self.statement_timeout
441    }
442
443    /// If true, the connection will be pinged upon acquiring from the pool (default true).
444    pub fn test_before_acquire(&mut self, value: bool) -> &mut Self {
445        self.test_before_acquire = value;
446        self
447    }
448
449    /// If set to `true`, the db connection pool will be created using SQLx's
450    /// [connect_lazy](https://docs.rs/sqlx/latest/sqlx/struct.Pool.html#method.connect_lazy) method.
451    pub fn connect_lazy(&mut self, value: bool) -> &mut Self {
452        self.connect_lazy = value;
453        self
454    }
455
456    /// Get whether DB connections will be established when the pool is created or only as needed.
457    pub fn get_connect_lazy(&self) -> bool {
458        self.connect_lazy
459    }
460
461    /// Set a callback function that will be called after a new connection is established.
462    pub fn after_connect<F>(&mut self, f: F) -> &mut Self
463    where
464        F: Fn(DatabaseConnection) -> BoxFuture<'static, Result<(), DbErr>> + 'static,
465    {
466        self.after_connect = Some(Arc::new(f));
467
468        self
469    }
470
471    #[cfg(feature = "sqlx-mysql")]
472    #[cfg_attr(docsrs, doc(cfg(feature = "sqlx-mysql")))]
473    /// Apply a function to modify the underlying [`MySqlConnectOptions`] before
474    /// creating the connection pool.
475    pub fn map_sqlx_mysql_opts<F>(&mut self, f: F) -> &mut Self
476    where
477        F: Fn(MySqlConnectOptions) -> MySqlConnectOptions + 'static,
478    {
479        self.mysql_opts_fn = Some(Arc::new(f));
480        self
481    }
482
483    #[cfg(feature = "sqlx-mysql")]
484    #[cfg_attr(docsrs, doc(cfg(feature = "sqlx-mysql")))]
485    /// Apply a function to modify the underlying [`sqlx::pool::PoolOptions<sqlx::MySql>`]
486    /// before creating the connection pool.
487    pub fn map_sqlx_mysql_pool_opts<F>(&mut self, f: F) -> &mut Self
488    where
489        F: Fn(sqlx::pool::PoolOptions<sqlx::MySql>) -> sqlx::pool::PoolOptions<sqlx::MySql>
490            + 'static,
491    {
492        self.mysql_pool_opts_fn = Some(Arc::new(f));
493        self
494    }
495
496    #[cfg(feature = "sqlx-postgres")]
497    #[cfg_attr(docsrs, doc(cfg(feature = "sqlx-postgres")))]
498    /// Apply a function to modify the underlying [`PgConnectOptions`] before
499    /// creating the connection pool.
500    pub fn map_sqlx_postgres_opts<F>(&mut self, f: F) -> &mut Self
501    where
502        F: Fn(PgConnectOptions) -> PgConnectOptions + 'static,
503    {
504        self.pg_opts_fn = Some(Arc::new(f));
505        self
506    }
507
508    #[cfg(feature = "sqlx-postgres")]
509    #[cfg_attr(docsrs, doc(cfg(feature = "sqlx-postgres")))]
510    /// Apply a function to modify the underlying [`sqlx::pool::PoolOptions<sqlx::Postgres>`]
511    /// before creating the connection pool.
512    pub fn map_sqlx_postgres_pool_opts<F>(&mut self, f: F) -> &mut Self
513    where
514        F: Fn(sqlx::pool::PoolOptions<sqlx::Postgres>) -> sqlx::pool::PoolOptions<sqlx::Postgres>
515            + 'static,
516    {
517        self.pg_pool_opts_fn = Some(Arc::new(f));
518        self
519    }
520
521    #[cfg(feature = "sqlx-sqlite")]
522    #[cfg_attr(docsrs, doc(cfg(feature = "sqlx-sqlite")))]
523    /// Apply a function to modify the underlying [`SqliteConnectOptions`] before
524    /// creating the connection pool.
525    pub fn map_sqlx_sqlite_opts<F>(&mut self, f: F) -> &mut Self
526    where
527        F: Fn(SqliteConnectOptions) -> SqliteConnectOptions + 'static,
528    {
529        self.sqlite_opts_fn = Some(Arc::new(f));
530        self
531    }
532
533    #[cfg(feature = "sqlx-sqlite")]
534    #[cfg_attr(docsrs, doc(cfg(feature = "sqlx-sqlite")))]
535    /// Apply a function to modify the underlying [`sqlx::pool::PoolOptions<sqlx::Sqlite>`]
536    /// before creating the connection pool.
537    pub fn map_sqlx_sqlite_pool_opts<F>(&mut self, f: F) -> &mut Self
538    where
539        F: Fn(sqlx::pool::PoolOptions<sqlx::Sqlite>) -> sqlx::pool::PoolOptions<sqlx::Sqlite>
540            + 'static,
541    {
542        self.sqlite_pool_opts_fn = Some(Arc::new(f));
543        self
544    }
545}