sea_orm/database/
db_connection.rs

1use crate::{
2    AccessMode, ConnectionTrait, DatabaseTransaction, ExecResult, IsolationLevel, QueryResult,
3    Schema, SchemaBuilder, Statement, StatementBuilder, StreamTrait, TransactionError,
4    TransactionTrait, error::*,
5};
6use std::fmt::Debug;
7use tracing::instrument;
8use url::Url;
9
10#[cfg(feature = "sqlx-dep")]
11use sqlx::pool::PoolConnection;
12
13#[cfg(feature = "rusqlite")]
14use crate::driver::rusqlite::{RusqliteInnerConnection, RusqliteSharedConnection};
15
16#[cfg(any(feature = "mock", feature = "proxy"))]
17use std::sync::Arc;
18
19/// Handle a database connection depending on the backend enabled by the feature
20/// flags. This creates a connection pool internally (for SQLx connections),
21/// and so is cheap to clone.
22#[derive(Debug, Clone)]
23#[non_exhaustive]
24pub struct DatabaseConnection {
25    /// `DatabaseConnection` used to be a enum. Now it's moved into inner,
26    /// because we have to attach other contexts.
27    pub inner: DatabaseConnectionType,
28    #[cfg(feature = "rbac")]
29    pub(crate) rbac: crate::RbacEngineMount,
30}
31
32/// The underlying database connection type.
33#[derive(Clone)]
34pub enum DatabaseConnectionType {
35    /// MySql database connection pool
36    #[cfg(feature = "sqlx-mysql")]
37    SqlxMySqlPoolConnection(crate::SqlxMySqlPoolConnection),
38
39    /// PostgreSQL database connection pool
40    #[cfg(feature = "sqlx-postgres")]
41    SqlxPostgresPoolConnection(crate::SqlxPostgresPoolConnection),
42
43    /// SQLite database connection pool
44    #[cfg(feature = "sqlx-sqlite")]
45    SqlxSqlitePoolConnection(crate::SqlxSqlitePoolConnection),
46
47    /// SQLite database connection sharable across threads
48    #[cfg(feature = "rusqlite")]
49    RusqliteSharedConnection(RusqliteSharedConnection),
50
51    /// Mock database connection useful for testing
52    #[cfg(feature = "mock")]
53    MockDatabaseConnection(Arc<crate::MockDatabaseConnection>),
54
55    /// Proxy database connection
56    #[cfg(feature = "proxy")]
57    ProxyDatabaseConnection(Arc<crate::ProxyDatabaseConnection>),
58
59    /// The connection has never been established
60    Disconnected,
61}
62
63/// The same as a [DatabaseConnection]
64pub type DbConn = DatabaseConnection;
65
66impl Default for DatabaseConnection {
67    fn default() -> Self {
68        DatabaseConnectionType::Disconnected.into()
69    }
70}
71
72impl From<DatabaseConnectionType> for DatabaseConnection {
73    fn from(inner: DatabaseConnectionType) -> Self {
74        Self {
75            inner,
76            #[cfg(feature = "rbac")]
77            rbac: Default::default(),
78        }
79    }
80}
81
82/// The type of database backend for real world databases.
83/// This is enabled by feature flags as specified in the crate documentation
84#[derive(Debug, Copy, Clone, PartialEq, Eq)]
85#[non_exhaustive]
86pub enum DatabaseBackend {
87    /// A MySQL backend
88    MySql,
89    /// A PostgreSQL backend
90    Postgres,
91    /// A SQLite backend
92    Sqlite,
93}
94
95/// A shorthand for [DatabaseBackend].
96pub type DbBackend = DatabaseBackend;
97
98#[derive(Debug)]
99pub(crate) enum InnerConnection {
100    #[cfg(feature = "sqlx-mysql")]
101    MySql(PoolConnection<sqlx::MySql>),
102    #[cfg(feature = "sqlx-postgres")]
103    Postgres(PoolConnection<sqlx::Postgres>),
104    #[cfg(feature = "sqlx-sqlite")]
105    Sqlite(PoolConnection<sqlx::Sqlite>),
106    #[cfg(feature = "rusqlite")]
107    Rusqlite(RusqliteInnerConnection),
108    #[cfg(feature = "mock")]
109    Mock(Arc<crate::MockDatabaseConnection>),
110    #[cfg(feature = "proxy")]
111    Proxy(Arc<crate::ProxyDatabaseConnection>),
112}
113
114impl Debug for DatabaseConnectionType {
115    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
116        write!(
117            f,
118            "{}",
119            match self {
120                #[cfg(feature = "sqlx-mysql")]
121                Self::SqlxMySqlPoolConnection(_) => "SqlxMySqlPoolConnection",
122                #[cfg(feature = "sqlx-postgres")]
123                Self::SqlxPostgresPoolConnection(_) => "SqlxPostgresPoolConnection",
124                #[cfg(feature = "sqlx-sqlite")]
125                Self::SqlxSqlitePoolConnection(_) => "SqlxSqlitePoolConnection",
126                #[cfg(feature = "rusqlite")]
127                Self::RusqliteSharedConnection(_) => "RusqliteSharedConnection",
128                #[cfg(feature = "mock")]
129                Self::MockDatabaseConnection(_) => "MockDatabaseConnection",
130                #[cfg(feature = "proxy")]
131                Self::ProxyDatabaseConnection(_) => "ProxyDatabaseConnection",
132                Self::Disconnected => "Disconnected",
133            }
134        )
135    }
136}
137
138impl ConnectionTrait for DatabaseConnection {
139    fn get_database_backend(&self) -> DbBackend {
140        self.get_database_backend()
141    }
142
143    #[instrument(level = "trace")]
144    #[allow(unused_variables)]
145    fn execute_raw(&self, stmt: Statement) -> Result<ExecResult, DbErr> {
146        match &self.inner {
147            #[cfg(feature = "sqlx-mysql")]
148            DatabaseConnectionType::SqlxMySqlPoolConnection(conn) => conn.execute(stmt),
149            #[cfg(feature = "sqlx-postgres")]
150            DatabaseConnectionType::SqlxPostgresPoolConnection(conn) => conn.execute(stmt),
151            #[cfg(feature = "sqlx-sqlite")]
152            DatabaseConnectionType::SqlxSqlitePoolConnection(conn) => conn.execute(stmt),
153            #[cfg(feature = "rusqlite")]
154            DatabaseConnectionType::RusqliteSharedConnection(conn) => conn.execute(stmt),
155            #[cfg(feature = "mock")]
156            DatabaseConnectionType::MockDatabaseConnection(conn) => conn.execute(stmt),
157            #[cfg(feature = "proxy")]
158            DatabaseConnectionType::ProxyDatabaseConnection(conn) => conn.execute(stmt),
159            DatabaseConnectionType::Disconnected => Err(conn_err("Disconnected")),
160        }
161    }
162
163    #[instrument(level = "trace")]
164    #[allow(unused_variables)]
165    fn execute_unprepared(&self, sql: &str) -> Result<ExecResult, DbErr> {
166        match &self.inner {
167            #[cfg(feature = "sqlx-mysql")]
168            DatabaseConnectionType::SqlxMySqlPoolConnection(conn) => conn.execute_unprepared(sql),
169            #[cfg(feature = "sqlx-postgres")]
170            DatabaseConnectionType::SqlxPostgresPoolConnection(conn) => {
171                conn.execute_unprepared(sql)
172            }
173            #[cfg(feature = "sqlx-sqlite")]
174            DatabaseConnectionType::SqlxSqlitePoolConnection(conn) => conn.execute_unprepared(sql),
175            #[cfg(feature = "rusqlite")]
176            DatabaseConnectionType::RusqliteSharedConnection(conn) => conn.execute_unprepared(sql),
177            #[cfg(feature = "mock")]
178            DatabaseConnectionType::MockDatabaseConnection(conn) => {
179                let db_backend = conn.get_database_backend();
180                let stmt = Statement::from_string(db_backend, sql);
181                conn.execute(stmt)
182            }
183            #[cfg(feature = "proxy")]
184            DatabaseConnectionType::ProxyDatabaseConnection(conn) => {
185                let db_backend = conn.get_database_backend();
186                let stmt = Statement::from_string(db_backend, sql);
187                conn.execute(stmt)
188            }
189            DatabaseConnectionType::Disconnected => Err(conn_err("Disconnected")),
190        }
191    }
192
193    #[instrument(level = "trace")]
194    #[allow(unused_variables)]
195    fn query_one_raw(&self, stmt: Statement) -> Result<Option<QueryResult>, DbErr> {
196        match &self.inner {
197            #[cfg(feature = "sqlx-mysql")]
198            DatabaseConnectionType::SqlxMySqlPoolConnection(conn) => conn.query_one(stmt),
199            #[cfg(feature = "sqlx-postgres")]
200            DatabaseConnectionType::SqlxPostgresPoolConnection(conn) => conn.query_one(stmt),
201            #[cfg(feature = "sqlx-sqlite")]
202            DatabaseConnectionType::SqlxSqlitePoolConnection(conn) => conn.query_one(stmt),
203            #[cfg(feature = "rusqlite")]
204            DatabaseConnectionType::RusqliteSharedConnection(conn) => conn.query_one(stmt),
205            #[cfg(feature = "mock")]
206            DatabaseConnectionType::MockDatabaseConnection(conn) => conn.query_one(stmt),
207            #[cfg(feature = "proxy")]
208            DatabaseConnectionType::ProxyDatabaseConnection(conn) => conn.query_one(stmt),
209            DatabaseConnectionType::Disconnected => Err(conn_err("Disconnected")),
210        }
211    }
212
213    #[instrument(level = "trace")]
214    #[allow(unused_variables)]
215    fn query_all_raw(&self, stmt: Statement) -> Result<Vec<QueryResult>, DbErr> {
216        match &self.inner {
217            #[cfg(feature = "sqlx-mysql")]
218            DatabaseConnectionType::SqlxMySqlPoolConnection(conn) => conn.query_all(stmt),
219            #[cfg(feature = "sqlx-postgres")]
220            DatabaseConnectionType::SqlxPostgresPoolConnection(conn) => conn.query_all(stmt),
221            #[cfg(feature = "sqlx-sqlite")]
222            DatabaseConnectionType::SqlxSqlitePoolConnection(conn) => conn.query_all(stmt),
223            #[cfg(feature = "rusqlite")]
224            DatabaseConnectionType::RusqliteSharedConnection(conn) => conn.query_all(stmt),
225            #[cfg(feature = "mock")]
226            DatabaseConnectionType::MockDatabaseConnection(conn) => conn.query_all(stmt),
227            #[cfg(feature = "proxy")]
228            DatabaseConnectionType::ProxyDatabaseConnection(conn) => conn.query_all(stmt),
229            DatabaseConnectionType::Disconnected => Err(conn_err("Disconnected")),
230        }
231    }
232
233    #[cfg(feature = "mock")]
234    fn is_mock_connection(&self) -> bool {
235        matches!(
236            self,
237            DatabaseConnection {
238                inner: DatabaseConnectionType::MockDatabaseConnection(_),
239                ..
240            }
241        )
242    }
243}
244
245impl StreamTrait for DatabaseConnection {
246    type Stream<'a> = crate::QueryStream;
247
248    fn get_database_backend(&self) -> DbBackend {
249        self.get_database_backend()
250    }
251
252    #[instrument(level = "trace")]
253    #[allow(unused_variables)]
254    fn stream_raw<'a>(&'a self, stmt: Statement) -> Result<Self::Stream<'a>, DbErr> {
255        ({
256            match &self.inner {
257                #[cfg(feature = "sqlx-mysql")]
258                DatabaseConnectionType::SqlxMySqlPoolConnection(conn) => conn.stream(stmt),
259                #[cfg(feature = "sqlx-postgres")]
260                DatabaseConnectionType::SqlxPostgresPoolConnection(conn) => conn.stream(stmt),
261                #[cfg(feature = "sqlx-sqlite")]
262                DatabaseConnectionType::SqlxSqlitePoolConnection(conn) => conn.stream(stmt),
263                #[cfg(feature = "rusqlite")]
264                DatabaseConnectionType::RusqliteSharedConnection(conn) => conn.stream(stmt),
265                #[cfg(feature = "mock")]
266                DatabaseConnectionType::MockDatabaseConnection(conn) => {
267                    Ok(crate::QueryStream::from((Arc::clone(conn), stmt, None)))
268                }
269                #[cfg(feature = "proxy")]
270                DatabaseConnectionType::ProxyDatabaseConnection(conn) => {
271                    Ok(crate::QueryStream::from((Arc::clone(conn), stmt, None)))
272                }
273                DatabaseConnectionType::Disconnected => Err(conn_err("Disconnected")),
274            }
275        })
276    }
277}
278
279impl TransactionTrait for DatabaseConnection {
280    type Transaction = DatabaseTransaction;
281
282    #[instrument(level = "trace")]
283    fn begin(&self) -> Result<DatabaseTransaction, DbErr> {
284        match &self.inner {
285            #[cfg(feature = "sqlx-mysql")]
286            DatabaseConnectionType::SqlxMySqlPoolConnection(conn) => conn.begin(None, None),
287            #[cfg(feature = "sqlx-postgres")]
288            DatabaseConnectionType::SqlxPostgresPoolConnection(conn) => conn.begin(None, None),
289            #[cfg(feature = "sqlx-sqlite")]
290            DatabaseConnectionType::SqlxSqlitePoolConnection(conn) => conn.begin(None, None),
291            #[cfg(feature = "rusqlite")]
292            DatabaseConnectionType::RusqliteSharedConnection(conn) => conn.begin(None, None),
293            #[cfg(feature = "mock")]
294            DatabaseConnectionType::MockDatabaseConnection(conn) => {
295                DatabaseTransaction::new_mock(Arc::clone(conn), None)
296            }
297            #[cfg(feature = "proxy")]
298            DatabaseConnectionType::ProxyDatabaseConnection(conn) => {
299                DatabaseTransaction::new_proxy(conn.clone(), None)
300            }
301            DatabaseConnectionType::Disconnected => Err(conn_err("Disconnected")),
302        }
303    }
304
305    #[instrument(level = "trace")]
306    fn begin_with_config(
307        &self,
308        _isolation_level: Option<IsolationLevel>,
309        _access_mode: Option<AccessMode>,
310    ) -> Result<DatabaseTransaction, DbErr> {
311        match &self.inner {
312            #[cfg(feature = "sqlx-mysql")]
313            DatabaseConnectionType::SqlxMySqlPoolConnection(conn) => {
314                conn.begin(_isolation_level, _access_mode)
315            }
316            #[cfg(feature = "sqlx-postgres")]
317            DatabaseConnectionType::SqlxPostgresPoolConnection(conn) => {
318                conn.begin(_isolation_level, _access_mode)
319            }
320            #[cfg(feature = "sqlx-sqlite")]
321            DatabaseConnectionType::SqlxSqlitePoolConnection(conn) => {
322                conn.begin(_isolation_level, _access_mode)
323            }
324            #[cfg(feature = "rusqlite")]
325            DatabaseConnectionType::RusqliteSharedConnection(conn) => {
326                conn.begin(_isolation_level, _access_mode)
327            }
328            #[cfg(feature = "mock")]
329            DatabaseConnectionType::MockDatabaseConnection(conn) => {
330                DatabaseTransaction::new_mock(Arc::clone(conn), None)
331            }
332            #[cfg(feature = "proxy")]
333            DatabaseConnectionType::ProxyDatabaseConnection(conn) => {
334                DatabaseTransaction::new_proxy(conn.clone(), None)
335            }
336            DatabaseConnectionType::Disconnected => Err(conn_err("Disconnected")),
337        }
338    }
339
340    /// Execute the function inside a transaction.
341    /// If the function returns an error, the transaction will be rolled back. If it does not return an error, the transaction will be committed.
342    #[instrument(level = "trace", skip(_callback))]
343    fn transaction<F, T, E>(&self, _callback: F) -> Result<T, TransactionError<E>>
344    where
345        F: for<'c> FnOnce(&'c DatabaseTransaction) -> Result<T, E>,
346        E: std::fmt::Display + std::fmt::Debug,
347    {
348        match &self.inner {
349            #[cfg(feature = "sqlx-mysql")]
350            DatabaseConnectionType::SqlxMySqlPoolConnection(conn) => {
351                conn.transaction(_callback, None, None)
352            }
353            #[cfg(feature = "sqlx-postgres")]
354            DatabaseConnectionType::SqlxPostgresPoolConnection(conn) => {
355                conn.transaction(_callback, None, None)
356            }
357            #[cfg(feature = "sqlx-sqlite")]
358            DatabaseConnectionType::SqlxSqlitePoolConnection(conn) => {
359                conn.transaction(_callback, None, None)
360            }
361            #[cfg(feature = "rusqlite")]
362            DatabaseConnectionType::RusqliteSharedConnection(conn) => {
363                conn.transaction(_callback, None, None)
364            }
365            #[cfg(feature = "mock")]
366            DatabaseConnectionType::MockDatabaseConnection(conn) => {
367                let transaction = DatabaseTransaction::new_mock(Arc::clone(conn), None)
368                    .map_err(TransactionError::Connection)?;
369                transaction.run(_callback)
370            }
371            #[cfg(feature = "proxy")]
372            DatabaseConnectionType::ProxyDatabaseConnection(conn) => {
373                let transaction = DatabaseTransaction::new_proxy(conn.clone(), None)
374                    .map_err(TransactionError::Connection)?;
375                transaction.run(_callback)
376            }
377            DatabaseConnectionType::Disconnected => Err(conn_err("Disconnected").into()),
378        }
379    }
380
381    /// Execute the function inside a transaction.
382    /// If the function returns an error, the transaction will be rolled back. If it does not return an error, the transaction will be committed.
383    #[instrument(level = "trace", skip(_callback))]
384    fn transaction_with_config<F, T, E>(
385        &self,
386        _callback: F,
387        _isolation_level: Option<IsolationLevel>,
388        _access_mode: Option<AccessMode>,
389    ) -> Result<T, TransactionError<E>>
390    where
391        F: for<'c> FnOnce(&'c DatabaseTransaction) -> Result<T, E>,
392        E: std::fmt::Display + std::fmt::Debug,
393    {
394        match &self.inner {
395            #[cfg(feature = "sqlx-mysql")]
396            DatabaseConnectionType::SqlxMySqlPoolConnection(conn) => {
397                conn.transaction(_callback, _isolation_level, _access_mode)
398            }
399            #[cfg(feature = "sqlx-postgres")]
400            DatabaseConnectionType::SqlxPostgresPoolConnection(conn) => {
401                conn.transaction(_callback, _isolation_level, _access_mode)
402            }
403            #[cfg(feature = "sqlx-sqlite")]
404            DatabaseConnectionType::SqlxSqlitePoolConnection(conn) => {
405                conn.transaction(_callback, _isolation_level, _access_mode)
406            }
407            #[cfg(feature = "rusqlite")]
408            DatabaseConnectionType::RusqliteSharedConnection(conn) => {
409                conn.transaction(_callback, _isolation_level, _access_mode)
410            }
411            #[cfg(feature = "mock")]
412            DatabaseConnectionType::MockDatabaseConnection(conn) => {
413                let transaction = DatabaseTransaction::new_mock(Arc::clone(conn), None)
414                    .map_err(TransactionError::Connection)?;
415                transaction.run(_callback)
416            }
417            #[cfg(feature = "proxy")]
418            DatabaseConnectionType::ProxyDatabaseConnection(conn) => {
419                let transaction = DatabaseTransaction::new_proxy(conn.clone(), None)
420                    .map_err(TransactionError::Connection)?;
421                transaction.run(_callback)
422            }
423            DatabaseConnectionType::Disconnected => Err(conn_err("Disconnected").into()),
424        }
425    }
426}
427
428#[cfg(feature = "mock")]
429impl DatabaseConnection {
430    /// Generate a database connection for testing the Mock database
431    ///
432    /// # Panics
433    ///
434    /// Panics if [DbConn] is not a mock connection.
435    pub fn as_mock_connection(&self) -> &crate::MockDatabaseConnection {
436        match &self.inner {
437            DatabaseConnectionType::MockDatabaseConnection(mock_conn) => mock_conn,
438            _ => panic!("Not mock connection"),
439        }
440    }
441
442    /// Get the transaction log as a collection Vec<[crate::Transaction]>
443    ///
444    /// # Panics
445    ///
446    /// Panics if the mocker mutex is being held by another thread.
447    pub fn into_transaction_log(self) -> Vec<crate::Transaction> {
448        let mut mocker = self
449            .as_mock_connection()
450            .get_mocker_mutex()
451            .lock()
452            .expect("Fail to acquire mocker");
453        mocker.drain_transaction_log()
454    }
455}
456
457#[cfg(feature = "proxy")]
458impl DatabaseConnection {
459    /// Generate a database connection for testing the Proxy database
460    ///
461    /// # Panics
462    ///
463    /// Panics if [DbConn] is not a proxy connection.
464    pub fn as_proxy_connection(&self) -> &crate::ProxyDatabaseConnection {
465        match &self.inner {
466            DatabaseConnectionType::ProxyDatabaseConnection(proxy_conn) => proxy_conn,
467            _ => panic!("Not proxy connection"),
468        }
469    }
470}
471
472#[cfg(feature = "rbac")]
473impl DatabaseConnection {
474    /// Load RBAC data from the same database as this connection and setup RBAC engine.
475    /// If the RBAC engine already exists, it will be replaced.
476    pub fn load_rbac(&self) -> Result<(), DbErr> {
477        self.load_rbac_from(self)
478    }
479
480    /// Load RBAC data from the given database connection and setup RBAC engine.
481    /// This could be from another database.
482    pub fn load_rbac_from(&self, db: &DbConn) -> Result<(), DbErr> {
483        let engine = crate::rbac::RbacEngine::load_from(db)?;
484        self.rbac.replace(engine);
485        Ok(())
486    }
487
488    /// Replace the internal RBAC engine.
489    pub fn replace_rbac(&self, engine: crate::rbac::RbacEngine) {
490        self.rbac.replace(engine);
491    }
492
493    /// Create a restricted connection with access control specific for the user.
494    pub fn restricted_for(
495        &self,
496        user_id: crate::rbac::RbacUserId,
497    ) -> Result<crate::RestrictedConnection, DbErr> {
498        if self.rbac.is_some() {
499            Ok(crate::RestrictedConnection {
500                user_id,
501                conn: self.clone(),
502            })
503        } else {
504            Err(DbErr::RbacError("engine not set up".into()))
505        }
506    }
507}
508
509impl DatabaseConnection {
510    /// Get the database backend for this connection
511    ///
512    /// # Panics
513    ///
514    /// Panics if [DatabaseConnection] is `Disconnected`.
515    pub fn get_database_backend(&self) -> DbBackend {
516        match &self.inner {
517            #[cfg(feature = "sqlx-mysql")]
518            DatabaseConnectionType::SqlxMySqlPoolConnection(_) => DbBackend::MySql,
519            #[cfg(feature = "sqlx-postgres")]
520            DatabaseConnectionType::SqlxPostgresPoolConnection(_) => DbBackend::Postgres,
521            #[cfg(feature = "sqlx-sqlite")]
522            DatabaseConnectionType::SqlxSqlitePoolConnection(_) => DbBackend::Sqlite,
523            #[cfg(feature = "rusqlite")]
524            DatabaseConnectionType::RusqliteSharedConnection(_) => DbBackend::Sqlite,
525            #[cfg(feature = "mock")]
526            DatabaseConnectionType::MockDatabaseConnection(conn) => conn.get_database_backend(),
527            #[cfg(feature = "proxy")]
528            DatabaseConnectionType::ProxyDatabaseConnection(conn) => conn.get_database_backend(),
529            DatabaseConnectionType::Disconnected => panic!("Disconnected"),
530        }
531    }
532
533    /// Creates a [`SchemaBuilder`] for this backend
534    pub fn get_schema_builder(&self) -> SchemaBuilder {
535        Schema::new(self.get_database_backend()).builder()
536    }
537
538    #[cfg(feature = "entity-registry")]
539    #[cfg_attr(docsrs, doc(cfg(feature = "entity-registry")))]
540    /// Builds a schema for all the entites in the given module
541    pub fn get_schema_registry(&self, prefix: &str) -> SchemaBuilder {
542        let schema = Schema::new(self.get_database_backend());
543        crate::EntityRegistry::build_schema(schema, prefix)
544    }
545
546    /// Sets a callback to metric this connection
547    pub fn set_metric_callback<F>(&mut self, _callback: F)
548    where
549        F: Fn(&crate::metric::Info<'_>) + 'static,
550    {
551        match &mut self.inner {
552            #[cfg(feature = "sqlx-mysql")]
553            DatabaseConnectionType::SqlxMySqlPoolConnection(conn) => {
554                conn.set_metric_callback(_callback)
555            }
556            #[cfg(feature = "sqlx-postgres")]
557            DatabaseConnectionType::SqlxPostgresPoolConnection(conn) => {
558                conn.set_metric_callback(_callback)
559            }
560            #[cfg(feature = "sqlx-sqlite")]
561            DatabaseConnectionType::SqlxSqlitePoolConnection(conn) => {
562                conn.set_metric_callback(_callback)
563            }
564            #[cfg(feature = "rusqlite")]
565            DatabaseConnectionType::RusqliteSharedConnection(conn) => {
566                conn.set_metric_callback(_callback)
567            }
568            _ => {}
569        }
570    }
571
572    /// Checks if a connection to the database is still valid.
573    pub fn ping(&self) -> Result<(), DbErr> {
574        match &self.inner {
575            #[cfg(feature = "sqlx-mysql")]
576            DatabaseConnectionType::SqlxMySqlPoolConnection(conn) => conn.ping(),
577            #[cfg(feature = "sqlx-postgres")]
578            DatabaseConnectionType::SqlxPostgresPoolConnection(conn) => conn.ping(),
579            #[cfg(feature = "sqlx-sqlite")]
580            DatabaseConnectionType::SqlxSqlitePoolConnection(conn) => conn.ping(),
581            #[cfg(feature = "rusqlite")]
582            DatabaseConnectionType::RusqliteSharedConnection(conn) => conn.ping(),
583            #[cfg(feature = "mock")]
584            DatabaseConnectionType::MockDatabaseConnection(conn) => conn.ping(),
585            #[cfg(feature = "proxy")]
586            DatabaseConnectionType::ProxyDatabaseConnection(conn) => conn.ping(),
587            DatabaseConnectionType::Disconnected => Err(conn_err("Disconnected")),
588        }
589    }
590
591    /// Explicitly close the database connection.
592    /// See [`Self::close_by_ref`] for usage with references.
593    pub fn close(self) -> Result<(), DbErr> {
594        self.close_by_ref()
595    }
596
597    /// Explicitly close the database connection
598    pub fn close_by_ref(&self) -> Result<(), DbErr> {
599        match &self.inner {
600            #[cfg(feature = "sqlx-mysql")]
601            DatabaseConnectionType::SqlxMySqlPoolConnection(conn) => conn.close_by_ref(),
602            #[cfg(feature = "sqlx-postgres")]
603            DatabaseConnectionType::SqlxPostgresPoolConnection(conn) => conn.close_by_ref(),
604            #[cfg(feature = "sqlx-sqlite")]
605            DatabaseConnectionType::SqlxSqlitePoolConnection(conn) => conn.close_by_ref(),
606            #[cfg(feature = "rusqlite")]
607            DatabaseConnectionType::RusqliteSharedConnection(conn) => conn.close_by_ref(),
608            #[cfg(feature = "mock")]
609            DatabaseConnectionType::MockDatabaseConnection(_) => {
610                // Nothing to cleanup, we just consume the `DatabaseConnection`
611                Ok(())
612            }
613            #[cfg(feature = "proxy")]
614            DatabaseConnectionType::ProxyDatabaseConnection(_) => {
615                // Nothing to cleanup, we just consume the `DatabaseConnection`
616                Ok(())
617            }
618            DatabaseConnectionType::Disconnected => Err(conn_err("Disconnected")),
619        }
620    }
621}
622
623impl DatabaseConnection {
624    /// Get [sqlx::MySqlPool]
625    ///
626    /// # Panics
627    ///
628    /// Panics if [DbConn] is not a MySQL connection.
629    #[cfg(feature = "sqlx-mysql")]
630    pub fn get_mysql_connection_pool(&self) -> &sqlx::MySqlPool {
631        match &self.inner {
632            DatabaseConnectionType::SqlxMySqlPoolConnection(conn) => &conn.pool,
633            _ => panic!("Not MySQL Connection"),
634        }
635    }
636
637    /// Get [sqlx::PgPool]
638    ///
639    /// # Panics
640    ///
641    /// Panics if [DbConn] is not a Postgres connection.
642    #[cfg(feature = "sqlx-postgres")]
643    pub fn get_postgres_connection_pool(&self) -> &sqlx::PgPool {
644        match &self.inner {
645            DatabaseConnectionType::SqlxPostgresPoolConnection(conn) => &conn.pool,
646            _ => panic!("Not Postgres Connection"),
647        }
648    }
649
650    /// Get [sqlx::SqlitePool]
651    ///
652    /// # Panics
653    ///
654    /// Panics if [DbConn] is not a SQLite connection.
655    #[cfg(feature = "sqlx-sqlite")]
656    pub fn get_sqlite_connection_pool(&self) -> &sqlx::SqlitePool {
657        match &self.inner {
658            DatabaseConnectionType::SqlxSqlitePoolConnection(conn) => &conn.pool,
659            _ => panic!("Not SQLite Connection"),
660        }
661    }
662}
663
664impl DbBackend {
665    /// Check if the URI is the same as the specified database backend.
666    /// Returns true if they match.
667    ///
668    /// # Panics
669    ///
670    /// Panics if `base_url` cannot be parsed as `Url`.
671    pub fn is_prefix_of(self, base_url: &str) -> bool {
672        let base_url_parsed = Url::parse(base_url).expect("Fail to parse database URL");
673        match self {
674            Self::Postgres => {
675                base_url_parsed.scheme() == "postgres" || base_url_parsed.scheme() == "postgresql"
676            }
677            Self::MySql => base_url_parsed.scheme() == "mysql",
678            Self::Sqlite => base_url_parsed.scheme() == "sqlite",
679        }
680    }
681
682    /// Build an SQL [Statement]
683    pub fn build<S>(&self, statement: &S) -> Statement
684    where
685        S: StatementBuilder,
686    {
687        statement.build(self)
688    }
689
690    /// Check if the database supports `RETURNING` syntax on insert and update
691    pub fn support_returning(&self) -> bool {
692        match self {
693            Self::Postgres => true,
694            Self::Sqlite if cfg!(feature = "sqlite-use-returning-for-3_35") => true,
695            Self::MySql if cfg!(feature = "mariadb-use-returning") => true,
696            _ => false,
697        }
698    }
699
700    /// A getter for database dependent boolean value
701    pub fn boolean_value(&self, boolean: bool) -> sea_query::Value {
702        match self {
703            Self::MySql | Self::Postgres | Self::Sqlite => boolean.into(),
704        }
705    }
706
707    /// Get the display string for this enum
708    pub fn as_str(&self) -> &'static str {
709        match self {
710            DatabaseBackend::MySql => "MySql",
711            DatabaseBackend::Postgres => "Postgres",
712            DatabaseBackend::Sqlite => "Sqlite",
713        }
714    }
715}
716
717#[cfg(test)]
718mod tests {
719    use crate::DatabaseConnection;
720
721    #[cfg(not(feature = "sync"))]
722    #[test]
723    fn assert_database_connection_traits() {
724        fn assert_send_sync<T: Send>() {}
725
726        assert_send_sync::<DatabaseConnection>();
727    }
728}