sea_orm/database/
mod.rs

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