sea_orm/database/
mod.rs

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