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