1use std::{sync::Arc, time::Duration};
2
3#[cfg(not(feature = "sync"))]
4#[cfg(feature = "sqlx-mysql")]
5use sqlx::mysql::MySqlConnectOptions;
6#[cfg(feature = "sqlx-postgres")]
7use sqlx::postgres::PgConnectOptions;
8#[cfg(feature = "sqlx-sqlite")]
9use sqlx::sqlite::SqliteConnectOptions;
10
11mod connection;
12mod db_connection;
13mod executor;
14#[cfg(feature = "mock")]
15#[cfg_attr(docsrs, doc(cfg(feature = "mock")))]
16mod mock;
17#[cfg(feature = "proxy")]
18#[cfg_attr(docsrs, doc(cfg(feature = "proxy")))]
19mod proxy;
20#[cfg(feature = "rbac")]
21mod restricted_connection;
22#[cfg(all(feature = "schema-sync", feature = "rusqlite"))]
23mod sea_schema_rusqlite;
24#[cfg(all(feature = "schema-sync", feature = "sqlx-dep"))]
25mod sea_schema_shim;
26mod statement;
27#[cfg(feature = "stream")]
28mod stream;
29mod tracing_spans;
30mod transaction;
31
32pub use connection::*;
33pub use db_connection::*;
34pub use executor::*;
35#[cfg(feature = "mock")]
36#[cfg_attr(docsrs, doc(cfg(feature = "mock")))]
37pub use mock::*;
38#[cfg(feature = "proxy")]
39#[cfg_attr(docsrs, doc(cfg(feature = "proxy")))]
40pub use proxy::*;
41#[cfg(feature = "rbac")]
42pub use restricted_connection::*;
43pub use statement::*;
44use std::borrow::Cow;
45#[cfg(feature = "stream")]
46pub use stream::*;
47use tracing::instrument;
48pub use transaction::*;
49
50use crate::error::*;
51
52#[derive(Debug, Default)]
54pub struct Database;
55
56#[cfg(feature = "sync")]
57type BoxFuture<'a, T> = T;
58
59#[cfg(feature = "sqlx-mysql")]
60type MapMySqlPoolOptsFn =
61 Arc<dyn Fn(sqlx::pool::PoolOptions<sqlx::MySql>) -> sqlx::pool::PoolOptions<sqlx::MySql>>;
62
63#[cfg(feature = "sqlx-postgres")]
64type MapPgPoolOptsFn =
65 Arc<dyn Fn(sqlx::pool::PoolOptions<sqlx::Postgres>) -> sqlx::pool::PoolOptions<sqlx::Postgres>>;
66
67#[cfg(feature = "sqlx-sqlite")]
68type MapSqlitePoolOptsFn = Option<
69 Arc<dyn Fn(sqlx::pool::PoolOptions<sqlx::Sqlite>) -> sqlx::pool::PoolOptions<sqlx::Sqlite>>,
70>;
71
72type AfterConnectCallback =
73 Option<Arc<dyn Fn(DatabaseConnection) -> BoxFuture<'static, Result<(), DbErr>> + 'static>>;
74
75#[derive(derive_more::Debug, Clone)]
81pub struct ConnectOptions {
82 pub(crate) url: String,
84 pub(crate) max_connections: Option<u32>,
86 pub(crate) min_connections: Option<u32>,
88 pub(crate) connect_timeout: Option<Duration>,
90 pub(crate) idle_timeout: Option<Option<Duration>>,
93 pub(crate) acquire_timeout: Option<Duration>,
95 pub(crate) max_lifetime: Option<Option<Duration>>,
97 pub(crate) sqlx_logging: bool,
99 pub(crate) record_stmt_in_spans: bool,
101 pub(crate) sqlx_logging_level: log::LevelFilter,
103 pub(crate) sqlx_slow_statements_logging_level: log::LevelFilter,
105 pub(crate) sqlx_slow_statements_logging_threshold: Duration,
107 pub(crate) sqlcipher_key: Option<Cow<'static, str>>,
109 pub(crate) schema_search_path: Option<String>,
111 pub(crate) application_name: Option<String>,
113 pub(crate) statement_timeout: Option<Duration>,
115 pub(crate) test_before_acquire: bool,
116 pub(crate) test_before_acquire_if_idle_for: Option<Duration>,
119 pub(crate) connect_lazy: bool,
123
124 #[debug(skip)]
125 pub(crate) after_connect: AfterConnectCallback,
126
127 #[cfg(feature = "sqlx-mysql")]
128 #[debug(skip)]
129 pub(crate) mysql_pool_opts_fn: Option<MapMySqlPoolOptsFn>,
130 #[cfg(feature = "sqlx-postgres")]
131 #[debug(skip)]
132 pub(crate) pg_pool_opts_fn: Option<MapPgPoolOptsFn>,
133 #[cfg(feature = "sqlx-sqlite")]
134 #[debug(skip)]
135 pub(crate) sqlite_pool_opts_fn: MapSqlitePoolOptsFn,
136 #[cfg(feature = "sqlx-mysql")]
137 #[debug(skip)]
138 pub(crate) mysql_opts_fn: Option<Arc<dyn Fn(MySqlConnectOptions) -> MySqlConnectOptions>>,
139 #[cfg(feature = "sqlx-postgres")]
140 #[debug(skip)]
141 pub(crate) pg_opts_fn: Option<Arc<dyn Fn(PgConnectOptions) -> PgConnectOptions>>,
142 #[cfg(feature = "sqlx-sqlite")]
143 #[debug(skip)]
144 pub(crate) sqlite_opts_fn: Option<Arc<dyn Fn(SqliteConnectOptions) -> SqliteConnectOptions>>,
145
146 #[cfg(feature = "sqlx-mysql")]
147 #[debug(skip)]
148 pub(crate) mysql_before_acquire_fn: Option<crate::driver::BeforeAcquireFn<sqlx::MySql>>,
149 #[cfg(feature = "sqlx-postgres")]
150 #[debug(skip)]
151 pub(crate) pg_before_acquire_fn: Option<crate::driver::BeforeAcquireFn<sqlx::Postgres>>,
152 #[cfg(feature = "sqlx-sqlite")]
153 #[debug(skip)]
154 pub(crate) sqlite_before_acquire_fn: Option<crate::driver::BeforeAcquireFn<sqlx::Sqlite>>,
155}
156
157impl Database {
158 #[instrument(level = "trace", skip(opt))]
161 pub fn connect<C>(opt: C) -> Result<DatabaseConnection, DbErr>
162 where
163 C: Into<ConnectOptions>,
164 {
165 let opt: ConnectOptions = opt.into();
166
167 if url::Url::parse(&opt.url).is_err() {
168 return Err(conn_err(format!(
169 "The connection string '{}' cannot be parsed.",
170 opt.url
171 )));
172 }
173
174 #[cfg(feature = "sqlx-mysql")]
175 if DbBackend::MySql.is_prefix_of(&opt.url) {
176 return crate::SqlxMySqlConnector::connect(opt);
177 }
178 #[cfg(feature = "sqlx-postgres")]
179 if DbBackend::Postgres.is_prefix_of(&opt.url) {
180 return crate::SqlxPostgresConnector::connect(opt);
181 }
182 #[cfg(feature = "sqlx-sqlite")]
183 if DbBackend::Sqlite.is_prefix_of(&opt.url) {
184 return crate::SqlxSqliteConnector::connect(opt);
185 }
186 #[cfg(feature = "rusqlite")]
187 if DbBackend::Sqlite.is_prefix_of(&opt.url) {
188 return crate::driver::rusqlite::RusqliteConnector::connect(opt);
189 }
190 #[cfg(feature = "mock")]
191 if crate::MockDatabaseConnector::accepts(&opt.url) {
192 return crate::MockDatabaseConnector::connect(&opt.url);
193 }
194
195 Err(conn_err(format!(
196 "The connection string '{}' has no supporting driver.",
197 opt.url
198 )))
199 }
200
201 #[cfg(feature = "proxy")]
203 #[instrument(level = "trace", skip(proxy_func_arc))]
204 pub fn connect_proxy(
205 db_type: DbBackend,
206 proxy_func_arc: std::sync::Arc<Box<dyn ProxyDatabaseTrait>>,
207 ) -> Result<DatabaseConnection, DbErr> {
208 match db_type {
209 DbBackend::MySql => {
210 return crate::ProxyDatabaseConnector::connect(
211 DbBackend::MySql,
212 proxy_func_arc.to_owned(),
213 );
214 }
215 DbBackend::Postgres => {
216 return crate::ProxyDatabaseConnector::connect(
217 DbBackend::Postgres,
218 proxy_func_arc.to_owned(),
219 );
220 }
221 DbBackend::Sqlite => {
222 return crate::ProxyDatabaseConnector::connect(
223 DbBackend::Sqlite,
224 proxy_func_arc.to_owned(),
225 );
226 }
227 }
228 }
229}
230
231impl<T> From<T> for ConnectOptions
232where
233 T: Into<String>,
234{
235 fn from(s: T) -> ConnectOptions {
236 ConnectOptions::new(s.into())
237 }
238}
239
240impl ConnectOptions {
241 pub fn new<T>(url: T) -> Self
243 where
244 T: Into<String>,
245 {
246 Self {
247 url: url.into(),
248 max_connections: None,
249 min_connections: None,
250 connect_timeout: None,
251 idle_timeout: None,
252 acquire_timeout: None,
253 max_lifetime: None,
254 sqlx_logging: true,
255 record_stmt_in_spans: true,
256 sqlx_logging_level: log::LevelFilter::Info,
257 sqlx_slow_statements_logging_level: log::LevelFilter::Off,
258 sqlx_slow_statements_logging_threshold: Duration::from_secs(1),
259 sqlcipher_key: None,
260 schema_search_path: None,
261 application_name: None,
262 statement_timeout: None,
263 test_before_acquire: true,
264 test_before_acquire_if_idle_for: None,
265 connect_lazy: false,
266 after_connect: None,
267 #[cfg(feature = "sqlx-mysql")]
268 mysql_pool_opts_fn: None,
269 #[cfg(feature = "sqlx-postgres")]
270 pg_pool_opts_fn: None,
271 #[cfg(feature = "sqlx-sqlite")]
272 sqlite_pool_opts_fn: None,
273 #[cfg(feature = "sqlx-mysql")]
274 mysql_opts_fn: None,
275 #[cfg(feature = "sqlx-postgres")]
276 pg_opts_fn: None,
277 #[cfg(feature = "sqlx-sqlite")]
278 sqlite_opts_fn: None,
279 #[cfg(feature = "sqlx-mysql")]
280 mysql_before_acquire_fn: None,
281 #[cfg(feature = "sqlx-postgres")]
282 pg_before_acquire_fn: None,
283 #[cfg(feature = "sqlx-sqlite")]
284 sqlite_before_acquire_fn: None,
285 }
286 }
287
288 pub fn get_url(&self) -> &str {
290 &self.url
291 }
292
293 pub fn max_connections(&mut self, value: u32) -> &mut Self {
295 self.max_connections = Some(value);
296 self
297 }
298
299 pub fn get_max_connections(&self) -> Option<u32> {
301 self.max_connections
302 }
303
304 pub fn min_connections(&mut self, value: u32) -> &mut Self {
306 self.min_connections = Some(value);
307 self
308 }
309
310 pub fn get_min_connections(&self) -> Option<u32> {
312 self.min_connections
313 }
314
315 pub fn connect_timeout(&mut self, value: Duration) -> &mut Self {
317 self.connect_timeout = Some(value);
318 self
319 }
320
321 pub fn get_connect_timeout(&self) -> Option<Duration> {
323 self.connect_timeout
324 }
325
326 pub fn idle_timeout<T>(&mut self, value: T) -> &mut Self
328 where
329 T: Into<Option<Duration>>,
330 {
331 self.idle_timeout = Some(value.into());
332 self
333 }
334
335 pub fn get_idle_timeout(&self) -> Option<Option<Duration>> {
337 self.idle_timeout
338 }
339
340 pub fn acquire_timeout(&mut self, value: Duration) -> &mut Self {
342 self.acquire_timeout = Some(value);
343 self
344 }
345
346 pub fn get_acquire_timeout(&self) -> Option<Duration> {
348 self.acquire_timeout
349 }
350
351 pub fn max_lifetime<T>(&mut self, lifetime: T) -> &mut Self
353 where
354 T: Into<Option<Duration>>,
355 {
356 self.max_lifetime = Some(lifetime.into());
357 self
358 }
359
360 pub fn get_max_lifetime(&self) -> Option<Option<Duration>> {
362 self.max_lifetime
363 }
364
365 pub fn sqlx_logging(&mut self, value: bool) -> &mut Self {
367 self.sqlx_logging = value;
368 self
369 }
370
371 pub fn get_sqlx_logging(&self) -> bool {
373 self.sqlx_logging
374 }
375
376 pub fn record_stmt_in_spans(&mut self, value: bool) -> &mut Self {
378 self.record_stmt_in_spans = value;
379 self
380 }
381
382 pub fn get_record_stmt_in_spans(&self) -> bool {
384 self.record_stmt_in_spans
385 }
386
387 pub fn sqlx_logging_level(&mut self, level: log::LevelFilter) -> &mut Self {
390 self.sqlx_logging_level = level;
391 self
392 }
393
394 pub fn sqlx_slow_statements_logging_settings(
397 &mut self,
398 level: log::LevelFilter,
399 duration: Duration,
400 ) -> &mut Self {
401 self.sqlx_slow_statements_logging_level = level;
402 self.sqlx_slow_statements_logging_threshold = duration;
403 self
404 }
405
406 pub fn get_sqlx_logging_level(&self) -> log::LevelFilter {
408 self.sqlx_logging_level
409 }
410
411 pub fn get_sqlx_slow_statements_logging_settings(&self) -> (log::LevelFilter, Duration) {
413 (
414 self.sqlx_slow_statements_logging_level,
415 self.sqlx_slow_statements_logging_threshold,
416 )
417 }
418
419 pub fn sqlcipher_key<T>(&mut self, value: T) -> &mut Self
421 where
422 T: Into<Cow<'static, str>>,
423 {
424 self.sqlcipher_key = Some(value.into());
425 self
426 }
427
428 pub fn set_schema_search_path<T>(&mut self, schema_search_path: T) -> &mut Self
430 where
431 T: Into<String>,
432 {
433 self.schema_search_path = Some(schema_search_path.into());
434 self
435 }
436
437 pub fn set_application_name<T>(&mut self, application_name: T) -> &mut Self
439 where
440 T: Into<String>,
441 {
442 self.application_name = Some(application_name.into());
443 self
444 }
445
446 pub fn statement_timeout(&mut self, value: Duration) -> &mut Self {
454 self.statement_timeout = Some(value);
455 self
456 }
457
458 pub fn get_statement_timeout(&self) -> Option<Duration> {
460 self.statement_timeout
461 }
462
463 pub fn test_before_acquire(&mut self, value: bool) -> &mut Self {
468 self.test_before_acquire = value;
469 self
470 }
471
472 pub fn get_test_before_acquire(&self) -> bool {
474 self.test_before_acquire
475 }
476
477 pub fn test_before_acquire_if_idle_for(&mut self, idle: Duration) -> &mut Self {
525 self.test_before_acquire = false;
526 self.test_before_acquire_if_idle_for = Some(idle);
527 self
528 }
529
530 pub fn get_test_before_acquire_if_idle_for(&self) -> Option<Duration> {
533 self.test_before_acquire_if_idle_for
534 }
535
536 pub fn connect_lazy(&mut self, value: bool) -> &mut Self {
539 self.connect_lazy = value;
540 self
541 }
542
543 pub fn get_connect_lazy(&self) -> bool {
545 self.connect_lazy
546 }
547
548 pub fn after_connect<F>(&mut self, f: F) -> &mut Self
550 where
551 F: Fn(DatabaseConnection) -> BoxFuture<'static, Result<(), DbErr>> + 'static,
552 {
553 self.after_connect = Some(Arc::new(f));
554
555 self
556 }
557
558 #[cfg(feature = "sqlx-mysql")]
559 #[cfg_attr(docsrs, doc(cfg(feature = "sqlx-mysql")))]
560 pub fn map_sqlx_mysql_opts<F>(&mut self, f: F) -> &mut Self
563 where
564 F: Fn(MySqlConnectOptions) -> MySqlConnectOptions + 'static,
565 {
566 self.mysql_opts_fn = Some(Arc::new(f));
567 self
568 }
569
570 #[cfg(feature = "sqlx-mysql")]
571 #[cfg_attr(docsrs, doc(cfg(feature = "sqlx-mysql")))]
572 pub fn map_sqlx_mysql_pool_opts<F>(&mut self, f: F) -> &mut Self
575 where
576 F: Fn(sqlx::pool::PoolOptions<sqlx::MySql>) -> sqlx::pool::PoolOptions<sqlx::MySql>
577 + 'static,
578 {
579 self.mysql_pool_opts_fn = Some(Arc::new(f));
580 self
581 }
582
583 #[cfg(feature = "sqlx-postgres")]
584 #[cfg_attr(docsrs, doc(cfg(feature = "sqlx-postgres")))]
585 pub fn map_sqlx_postgres_opts<F>(&mut self, f: F) -> &mut Self
588 where
589 F: Fn(PgConnectOptions) -> PgConnectOptions + 'static,
590 {
591 self.pg_opts_fn = Some(Arc::new(f));
592 self
593 }
594
595 #[cfg(feature = "sqlx-postgres")]
596 #[cfg_attr(docsrs, doc(cfg(feature = "sqlx-postgres")))]
597 pub fn map_sqlx_postgres_pool_opts<F>(&mut self, f: F) -> &mut Self
600 where
601 F: Fn(sqlx::pool::PoolOptions<sqlx::Postgres>) -> sqlx::pool::PoolOptions<sqlx::Postgres>
602 + 'static,
603 {
604 self.pg_pool_opts_fn = Some(Arc::new(f));
605 self
606 }
607
608 #[cfg(feature = "sqlx-sqlite")]
609 #[cfg_attr(docsrs, doc(cfg(feature = "sqlx-sqlite")))]
610 pub fn map_sqlx_sqlite_opts<F>(&mut self, f: F) -> &mut Self
613 where
614 F: Fn(SqliteConnectOptions) -> SqliteConnectOptions + 'static,
615 {
616 self.sqlite_opts_fn = Some(Arc::new(f));
617 self
618 }
619
620 #[cfg(feature = "sqlx-sqlite")]
621 #[cfg_attr(docsrs, doc(cfg(feature = "sqlx-sqlite")))]
622 pub fn map_sqlx_sqlite_pool_opts<F>(&mut self, f: F) -> &mut Self
625 where
626 F: Fn(sqlx::pool::PoolOptions<sqlx::Sqlite>) -> sqlx::pool::PoolOptions<sqlx::Sqlite>
627 + 'static,
628 {
629 self.sqlite_pool_opts_fn = Some(Arc::new(f));
630 self
631 }
632
633 #[cfg(feature = "sqlx-mysql")]
634 #[cfg_attr(docsrs, doc(cfg(feature = "sqlx-mysql")))]
635 pub fn map_sqlx_mysql_before_acquire<F>(&mut self, f: F) -> &mut Self
647 where
648 F: for<'c> Fn(
649 &'c mut sqlx::mysql::MySqlConnection,
650 sqlx::pool::PoolConnectionMetadata,
651 )
652 -> futures_util::future::BoxFuture<'c, Result<bool, sqlx::Error>>
653 + 'static,
654 {
655 self.mysql_before_acquire_fn = Some(Arc::new(f));
656 self
657 }
658
659 #[cfg(feature = "sqlx-postgres")]
660 #[cfg_attr(docsrs, doc(cfg(feature = "sqlx-postgres")))]
661 pub fn map_sqlx_postgres_before_acquire<F>(&mut self, f: F) -> &mut Self
687 where
688 F: for<'c> Fn(
689 &'c mut sqlx::postgres::PgConnection,
690 sqlx::pool::PoolConnectionMetadata,
691 )
692 -> futures_util::future::BoxFuture<'c, Result<bool, sqlx::Error>>
693 + 'static,
694 {
695 self.pg_before_acquire_fn = Some(Arc::new(f));
696 self
697 }
698
699 #[cfg(feature = "sqlx-sqlite")]
700 #[cfg_attr(docsrs, doc(cfg(feature = "sqlx-sqlite")))]
701 pub fn map_sqlx_sqlite_before_acquire<F>(&mut self, f: F) -> &mut Self
713 where
714 F: for<'c> Fn(
715 &'c mut sqlx::sqlite::SqliteConnection,
716 sqlx::pool::PoolConnectionMetadata,
717 )
718 -> futures_util::future::BoxFuture<'c, Result<bool, sqlx::Error>>
719 + 'static,
720 {
721 self.sqlite_before_acquire_fn = Some(Arc::new(f));
722 self
723 }
724}