1use std::{sync::Arc, time::Duration};
2
3#[cfg(not(feature = "sync"))]
4use futures_util::future::BoxFuture;
5#[cfg(feature = "sqlx-mysql")]
6use sqlx::mysql::MySqlConnectOptions;
7#[cfg(feature = "sqlx-postgres")]
8use sqlx::postgres::PgConnectOptions;
9#[cfg(feature = "sqlx-sqlite")]
10use sqlx::sqlite::SqliteConnectOptions;
11
12mod connection;
13mod db_connection;
14mod executor;
15#[cfg(feature = "mock")]
16#[cfg_attr(docsrs, doc(cfg(feature = "mock")))]
17mod mock;
18#[cfg(feature = "proxy")]
19#[cfg_attr(docsrs, doc(cfg(feature = "proxy")))]
20mod proxy;
21#[cfg(feature = "rbac")]
22mod restricted_connection;
23#[cfg(all(feature = "schema-sync", feature = "rusqlite"))]
24mod sea_schema_rusqlite;
25#[cfg(all(feature = "schema-sync", feature = "sqlx-dep"))]
26mod sea_schema_shim;
27mod statement;
28#[cfg(feature = "stream")]
29mod stream;
30mod tracing_spans;
31mod transaction;
32
33pub use connection::*;
34pub use db_connection::*;
35pub use executor::*;
36#[cfg(feature = "mock")]
37#[cfg_attr(docsrs, doc(cfg(feature = "mock")))]
38pub use mock::*;
39#[cfg(feature = "proxy")]
40#[cfg_attr(docsrs, doc(cfg(feature = "proxy")))]
41pub use proxy::*;
42#[cfg(feature = "rbac")]
43pub use restricted_connection::*;
44pub use statement::*;
45use std::borrow::Cow;
46#[cfg(feature = "stream")]
47pub use stream::*;
48use tracing::instrument;
49pub use transaction::*;
50
51use crate::error::*;
52
53#[derive(Debug, Default)]
55pub struct Database;
56
57#[cfg(feature = "sync")]
58type BoxFuture<'a, T> = T;
59
60#[cfg(feature = "sqlx-mysql")]
61type MapMySqlPoolOptsFn = Arc<
62 dyn Fn(sqlx::pool::PoolOptions<sqlx::MySql>) -> sqlx::pool::PoolOptions<sqlx::MySql>
63 + Send
64 + Sync,
65>;
66
67#[cfg(feature = "sqlx-postgres")]
68type MapPgPoolOptsFn = Arc<
69 dyn Fn(sqlx::pool::PoolOptions<sqlx::Postgres>) -> sqlx::pool::PoolOptions<sqlx::Postgres>
70 + Send
71 + Sync,
72>;
73
74#[cfg(feature = "sqlx-sqlite")]
75type MapSqlitePoolOptsFn = Option<
76 Arc<
77 dyn Fn(sqlx::pool::PoolOptions<sqlx::Sqlite>) -> sqlx::pool::PoolOptions<sqlx::Sqlite>
78 + Send
79 + Sync,
80 >,
81>;
82
83type AfterConnectCallback = Option<
84 Arc<
85 dyn Fn(DatabaseConnection) -> BoxFuture<'static, Result<(), DbErr>> + Send + Sync + 'static,
86 >,
87>;
88
89#[derive(derive_more::Debug, Clone)]
95pub struct ConnectOptions {
96 pub(crate) url: String,
98 pub(crate) max_connections: Option<u32>,
100 pub(crate) min_connections: Option<u32>,
102 pub(crate) connect_timeout: Option<Duration>,
104 pub(crate) idle_timeout: Option<Option<Duration>>,
107 pub(crate) acquire_timeout: Option<Duration>,
109 pub(crate) max_lifetime: Option<Option<Duration>>,
111 pub(crate) sqlx_logging: bool,
113 pub(crate) record_stmt_in_spans: bool,
115 pub(crate) sqlx_logging_level: log::LevelFilter,
117 pub(crate) sqlx_slow_statements_logging_level: log::LevelFilter,
119 pub(crate) sqlx_slow_statements_logging_threshold: Duration,
121 pub(crate) sqlcipher_key: Option<Cow<'static, str>>,
123 pub(crate) schema_search_path: Option<String>,
125 pub(crate) application_name: Option<String>,
127 pub(crate) statement_timeout: Option<Duration>,
129 pub(crate) test_before_acquire: bool,
130 pub(crate) test_before_acquire_if_idle_for: Option<Duration>,
133 pub(crate) connect_lazy: bool,
137
138 #[debug(skip)]
139 pub(crate) after_connect: AfterConnectCallback,
140
141 #[cfg(feature = "sqlx-mysql")]
142 #[debug(skip)]
143 pub(crate) mysql_pool_opts_fn: Option<MapMySqlPoolOptsFn>,
144 #[cfg(feature = "sqlx-postgres")]
145 #[debug(skip)]
146 pub(crate) pg_pool_opts_fn: Option<MapPgPoolOptsFn>,
147 #[cfg(feature = "sqlx-sqlite")]
148 #[debug(skip)]
149 pub(crate) sqlite_pool_opts_fn: MapSqlitePoolOptsFn,
150 #[cfg(feature = "sqlx-mysql")]
151 #[debug(skip)]
152 pub(crate) mysql_opts_fn:
153 Option<Arc<dyn Fn(MySqlConnectOptions) -> MySqlConnectOptions + Send + Sync>>,
154 #[cfg(feature = "sqlx-postgres")]
155 #[debug(skip)]
156 pub(crate) pg_opts_fn: Option<Arc<dyn Fn(PgConnectOptions) -> PgConnectOptions + Send + Sync>>,
157 #[cfg(feature = "sqlx-sqlite")]
158 #[debug(skip)]
159 pub(crate) sqlite_opts_fn:
160 Option<Arc<dyn Fn(SqliteConnectOptions) -> SqliteConnectOptions + Send + Sync>>,
161
162 #[cfg(feature = "sqlx-mysql")]
163 #[debug(skip)]
164 pub(crate) mysql_before_acquire_fn: Option<crate::driver::BeforeAcquireFn<sqlx::MySql>>,
165 #[cfg(feature = "sqlx-postgres")]
166 #[debug(skip)]
167 pub(crate) pg_before_acquire_fn: Option<crate::driver::BeforeAcquireFn<sqlx::Postgres>>,
168 #[cfg(feature = "sqlx-sqlite")]
169 #[debug(skip)]
170 pub(crate) sqlite_before_acquire_fn: Option<crate::driver::BeforeAcquireFn<sqlx::Sqlite>>,
171}
172
173impl Database {
174 #[instrument(level = "trace", skip(opt))]
177 pub async fn connect<C>(opt: C) -> Result<DatabaseConnection, DbErr>
178 where
179 C: Into<ConnectOptions>,
180 {
181 let opt: ConnectOptions = opt.into();
182
183 if url::Url::parse(&opt.url).is_err() {
184 return Err(conn_err(format!(
185 "The connection string '{}' cannot be parsed.",
186 opt.url
187 )));
188 }
189
190 #[cfg(feature = "sqlx-mysql")]
191 if DbBackend::MySql.is_prefix_of(&opt.url) {
192 return crate::SqlxMySqlConnector::connect(opt).await;
193 }
194 #[cfg(feature = "sqlx-postgres")]
195 if DbBackend::Postgres.is_prefix_of(&opt.url) {
196 return crate::SqlxPostgresConnector::connect(opt).await;
197 }
198 #[cfg(feature = "sqlx-sqlite")]
199 if DbBackend::Sqlite.is_prefix_of(&opt.url) {
200 return crate::SqlxSqliteConnector::connect(opt).await;
201 }
202 #[cfg(feature = "rusqlite")]
203 if DbBackend::Sqlite.is_prefix_of(&opt.url) {
204 return crate::driver::rusqlite::RusqliteConnector::connect(opt);
205 }
206 #[cfg(feature = "mock")]
207 if crate::MockDatabaseConnector::accepts(&opt.url) {
208 return crate::MockDatabaseConnector::connect(&opt.url).await;
209 }
210
211 Err(conn_err(format!(
212 "The connection string '{}' has no supporting driver.",
213 opt.url
214 )))
215 }
216
217 #[cfg(feature = "proxy")]
219 #[instrument(level = "trace", skip(proxy_func_arc))]
220 pub async fn connect_proxy(
221 db_type: DbBackend,
222 proxy_func_arc: std::sync::Arc<Box<dyn ProxyDatabaseTrait>>,
223 ) -> Result<DatabaseConnection, DbErr> {
224 match db_type {
225 DbBackend::MySql => {
226 return crate::ProxyDatabaseConnector::connect(
227 DbBackend::MySql,
228 proxy_func_arc.to_owned(),
229 );
230 }
231 DbBackend::Postgres => {
232 return crate::ProxyDatabaseConnector::connect(
233 DbBackend::Postgres,
234 proxy_func_arc.to_owned(),
235 );
236 }
237 DbBackend::Sqlite => {
238 return crate::ProxyDatabaseConnector::connect(
239 DbBackend::Sqlite,
240 proxy_func_arc.to_owned(),
241 );
242 }
243 }
244 }
245}
246
247impl<T> From<T> for ConnectOptions
248where
249 T: Into<String>,
250{
251 fn from(s: T) -> ConnectOptions {
252 ConnectOptions::new(s.into())
253 }
254}
255
256impl ConnectOptions {
257 pub fn new<T>(url: T) -> Self
259 where
260 T: Into<String>,
261 {
262 Self {
263 url: url.into(),
264 max_connections: None,
265 min_connections: None,
266 connect_timeout: None,
267 idle_timeout: None,
268 acquire_timeout: None,
269 max_lifetime: None,
270 sqlx_logging: true,
271 record_stmt_in_spans: true,
272 sqlx_logging_level: log::LevelFilter::Info,
273 sqlx_slow_statements_logging_level: log::LevelFilter::Off,
274 sqlx_slow_statements_logging_threshold: Duration::from_secs(1),
275 sqlcipher_key: None,
276 schema_search_path: None,
277 application_name: None,
278 statement_timeout: None,
279 test_before_acquire: true,
280 test_before_acquire_if_idle_for: None,
281 connect_lazy: false,
282 after_connect: None,
283 #[cfg(feature = "sqlx-mysql")]
284 mysql_pool_opts_fn: None,
285 #[cfg(feature = "sqlx-postgres")]
286 pg_pool_opts_fn: None,
287 #[cfg(feature = "sqlx-sqlite")]
288 sqlite_pool_opts_fn: None,
289 #[cfg(feature = "sqlx-mysql")]
290 mysql_opts_fn: None,
291 #[cfg(feature = "sqlx-postgres")]
292 pg_opts_fn: None,
293 #[cfg(feature = "sqlx-sqlite")]
294 sqlite_opts_fn: None,
295 #[cfg(feature = "sqlx-mysql")]
296 mysql_before_acquire_fn: None,
297 #[cfg(feature = "sqlx-postgres")]
298 pg_before_acquire_fn: None,
299 #[cfg(feature = "sqlx-sqlite")]
300 sqlite_before_acquire_fn: None,
301 }
302 }
303
304 pub fn get_url(&self) -> &str {
306 &self.url
307 }
308
309 pub fn max_connections(&mut self, value: u32) -> &mut Self {
311 self.max_connections = Some(value);
312 self
313 }
314
315 pub fn get_max_connections(&self) -> Option<u32> {
317 self.max_connections
318 }
319
320 pub fn min_connections(&mut self, value: u32) -> &mut Self {
322 self.min_connections = Some(value);
323 self
324 }
325
326 pub fn get_min_connections(&self) -> Option<u32> {
328 self.min_connections
329 }
330
331 pub fn connect_timeout(&mut self, value: Duration) -> &mut Self {
333 self.connect_timeout = Some(value);
334 self
335 }
336
337 pub fn get_connect_timeout(&self) -> Option<Duration> {
339 self.connect_timeout
340 }
341
342 pub fn idle_timeout<T>(&mut self, value: T) -> &mut Self
344 where
345 T: Into<Option<Duration>>,
346 {
347 self.idle_timeout = Some(value.into());
348 self
349 }
350
351 pub fn get_idle_timeout(&self) -> Option<Option<Duration>> {
353 self.idle_timeout
354 }
355
356 pub fn acquire_timeout(&mut self, value: Duration) -> &mut Self {
358 self.acquire_timeout = Some(value);
359 self
360 }
361
362 pub fn get_acquire_timeout(&self) -> Option<Duration> {
364 self.acquire_timeout
365 }
366
367 pub fn max_lifetime<T>(&mut self, lifetime: T) -> &mut Self
369 where
370 T: Into<Option<Duration>>,
371 {
372 self.max_lifetime = Some(lifetime.into());
373 self
374 }
375
376 pub fn get_max_lifetime(&self) -> Option<Option<Duration>> {
378 self.max_lifetime
379 }
380
381 pub fn sqlx_logging(&mut self, value: bool) -> &mut Self {
383 self.sqlx_logging = value;
384 self
385 }
386
387 pub fn get_sqlx_logging(&self) -> bool {
389 self.sqlx_logging
390 }
391
392 pub fn record_stmt_in_spans(&mut self, value: bool) -> &mut Self {
394 self.record_stmt_in_spans = value;
395 self
396 }
397
398 pub fn get_record_stmt_in_spans(&self) -> bool {
400 self.record_stmt_in_spans
401 }
402
403 pub fn sqlx_logging_level(&mut self, level: log::LevelFilter) -> &mut Self {
406 self.sqlx_logging_level = level;
407 self
408 }
409
410 pub fn sqlx_slow_statements_logging_settings(
413 &mut self,
414 level: log::LevelFilter,
415 duration: Duration,
416 ) -> &mut Self {
417 self.sqlx_slow_statements_logging_level = level;
418 self.sqlx_slow_statements_logging_threshold = duration;
419 self
420 }
421
422 pub fn get_sqlx_logging_level(&self) -> log::LevelFilter {
424 self.sqlx_logging_level
425 }
426
427 pub fn get_sqlx_slow_statements_logging_settings(&self) -> (log::LevelFilter, Duration) {
429 (
430 self.sqlx_slow_statements_logging_level,
431 self.sqlx_slow_statements_logging_threshold,
432 )
433 }
434
435 pub fn sqlcipher_key<T>(&mut self, value: T) -> &mut Self
437 where
438 T: Into<Cow<'static, str>>,
439 {
440 self.sqlcipher_key = Some(value.into());
441 self
442 }
443
444 pub fn set_schema_search_path<T>(&mut self, schema_search_path: T) -> &mut Self
446 where
447 T: Into<String>,
448 {
449 self.schema_search_path = Some(schema_search_path.into());
450 self
451 }
452
453 pub fn set_application_name<T>(&mut self, application_name: T) -> &mut Self
455 where
456 T: Into<String>,
457 {
458 self.application_name = Some(application_name.into());
459 self
460 }
461
462 pub fn statement_timeout(&mut self, value: Duration) -> &mut Self {
470 self.statement_timeout = Some(value);
471 self
472 }
473
474 pub fn get_statement_timeout(&self) -> Option<Duration> {
476 self.statement_timeout
477 }
478
479 pub fn test_before_acquire(&mut self, value: bool) -> &mut Self {
484 self.test_before_acquire = value;
485 self
486 }
487
488 pub fn get_test_before_acquire(&self) -> bool {
490 self.test_before_acquire
491 }
492
493 pub fn test_before_acquire_if_idle_for(&mut self, idle: Duration) -> &mut Self {
541 self.test_before_acquire = false;
542 self.test_before_acquire_if_idle_for = Some(idle);
543 self
544 }
545
546 pub fn get_test_before_acquire_if_idle_for(&self) -> Option<Duration> {
549 self.test_before_acquire_if_idle_for
550 }
551
552 pub fn connect_lazy(&mut self, value: bool) -> &mut Self {
555 self.connect_lazy = value;
556 self
557 }
558
559 pub fn get_connect_lazy(&self) -> bool {
561 self.connect_lazy
562 }
563
564 pub fn after_connect<F>(&mut self, f: F) -> &mut Self
566 where
567 F: Fn(DatabaseConnection) -> BoxFuture<'static, Result<(), DbErr>> + Send + Sync + 'static,
568 {
569 self.after_connect = Some(Arc::new(f));
570
571 self
572 }
573
574 #[cfg(feature = "sqlx-mysql")]
575 #[cfg_attr(docsrs, doc(cfg(feature = "sqlx-mysql")))]
576 pub fn map_sqlx_mysql_opts<F>(&mut self, f: F) -> &mut Self
579 where
580 F: Fn(MySqlConnectOptions) -> MySqlConnectOptions + Send + Sync + 'static,
581 {
582 self.mysql_opts_fn = Some(Arc::new(f));
583 self
584 }
585
586 #[cfg(feature = "sqlx-mysql")]
587 #[cfg_attr(docsrs, doc(cfg(feature = "sqlx-mysql")))]
588 pub fn map_sqlx_mysql_pool_opts<F>(&mut self, f: F) -> &mut Self
591 where
592 F: Fn(sqlx::pool::PoolOptions<sqlx::MySql>) -> sqlx::pool::PoolOptions<sqlx::MySql>
593 + Send
594 + Sync
595 + 'static,
596 {
597 self.mysql_pool_opts_fn = Some(Arc::new(f));
598 self
599 }
600
601 #[cfg(feature = "sqlx-postgres")]
602 #[cfg_attr(docsrs, doc(cfg(feature = "sqlx-postgres")))]
603 pub fn map_sqlx_postgres_opts<F>(&mut self, f: F) -> &mut Self
606 where
607 F: Fn(PgConnectOptions) -> PgConnectOptions + Send + Sync + 'static,
608 {
609 self.pg_opts_fn = Some(Arc::new(f));
610 self
611 }
612
613 #[cfg(feature = "sqlx-postgres")]
614 #[cfg_attr(docsrs, doc(cfg(feature = "sqlx-postgres")))]
615 pub fn map_sqlx_postgres_pool_opts<F>(&mut self, f: F) -> &mut Self
618 where
619 F: Fn(sqlx::pool::PoolOptions<sqlx::Postgres>) -> sqlx::pool::PoolOptions<sqlx::Postgres>
620 + Send
621 + Sync
622 + 'static,
623 {
624 self.pg_pool_opts_fn = Some(Arc::new(f));
625 self
626 }
627
628 #[cfg(feature = "sqlx-sqlite")]
629 #[cfg_attr(docsrs, doc(cfg(feature = "sqlx-sqlite")))]
630 pub fn map_sqlx_sqlite_opts<F>(&mut self, f: F) -> &mut Self
633 where
634 F: Fn(SqliteConnectOptions) -> SqliteConnectOptions + Send + Sync + 'static,
635 {
636 self.sqlite_opts_fn = Some(Arc::new(f));
637 self
638 }
639
640 #[cfg(feature = "sqlx-sqlite")]
641 #[cfg_attr(docsrs, doc(cfg(feature = "sqlx-sqlite")))]
642 pub fn map_sqlx_sqlite_pool_opts<F>(&mut self, f: F) -> &mut Self
645 where
646 F: Fn(sqlx::pool::PoolOptions<sqlx::Sqlite>) -> sqlx::pool::PoolOptions<sqlx::Sqlite>
647 + Send
648 + Sync
649 + 'static,
650 {
651 self.sqlite_pool_opts_fn = Some(Arc::new(f));
652 self
653 }
654
655 #[cfg(feature = "sqlx-mysql")]
656 #[cfg_attr(docsrs, doc(cfg(feature = "sqlx-mysql")))]
657 pub fn map_sqlx_mysql_before_acquire<F>(&mut self, f: F) -> &mut Self
669 where
670 F: for<'c> Fn(
671 &'c mut sqlx::mysql::MySqlConnection,
672 sqlx::pool::PoolConnectionMetadata,
673 )
674 -> futures_util::future::BoxFuture<'c, Result<bool, sqlx::Error>>
675 + Send
676 + Sync
677 + 'static,
678 {
679 self.mysql_before_acquire_fn = Some(Arc::new(f));
680 self
681 }
682
683 #[cfg(feature = "sqlx-postgres")]
684 #[cfg_attr(docsrs, doc(cfg(feature = "sqlx-postgres")))]
685 pub fn map_sqlx_postgres_before_acquire<F>(&mut self, f: F) -> &mut Self
711 where
712 F: for<'c> Fn(
713 &'c mut sqlx::postgres::PgConnection,
714 sqlx::pool::PoolConnectionMetadata,
715 )
716 -> futures_util::future::BoxFuture<'c, Result<bool, sqlx::Error>>
717 + Send
718 + Sync
719 + 'static,
720 {
721 self.pg_before_acquire_fn = Some(Arc::new(f));
722 self
723 }
724
725 #[cfg(feature = "sqlx-sqlite")]
726 #[cfg_attr(docsrs, doc(cfg(feature = "sqlx-sqlite")))]
727 pub fn map_sqlx_sqlite_before_acquire<F>(&mut self, f: F) -> &mut Self
739 where
740 F: for<'c> Fn(
741 &'c mut sqlx::sqlite::SqliteConnection,
742 sqlx::pool::PoolConnectionMetadata,
743 )
744 -> futures_util::future::BoxFuture<'c, Result<bool, sqlx::Error>>
745 + Send
746 + Sync
747 + 'static,
748 {
749 self.sqlite_before_acquire_fn = Some(Arc::new(f));
750 self
751 }
752}