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