sea_orm/database/
db_connection.rs

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