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) connect_lazy: bool,
120
121 #[debug(skip)]
122 pub(crate) after_connect: AfterConnectCallback,
123
124 #[cfg(feature = "sqlx-mysql")]
125 #[debug(skip)]
126 pub(crate) mysql_pool_opts_fn: Option<MapMySqlPoolOptsFn>,
127 #[cfg(feature = "sqlx-postgres")]
128 #[debug(skip)]
129 pub(crate) pg_pool_opts_fn: Option<MapPgPoolOptsFn>,
130 #[cfg(feature = "sqlx-sqlite")]
131 #[debug(skip)]
132 pub(crate) sqlite_pool_opts_fn: MapSqlitePoolOptsFn,
133 #[cfg(feature = "sqlx-mysql")]
134 #[debug(skip)]
135 pub(crate) mysql_opts_fn: Option<Arc<dyn Fn(MySqlConnectOptions) -> MySqlConnectOptions>>,
136 #[cfg(feature = "sqlx-postgres")]
137 #[debug(skip)]
138 pub(crate) pg_opts_fn: Option<Arc<dyn Fn(PgConnectOptions) -> PgConnectOptions>>,
139 #[cfg(feature = "sqlx-sqlite")]
140 #[debug(skip)]
141 pub(crate) sqlite_opts_fn: Option<Arc<dyn Fn(SqliteConnectOptions) -> SqliteConnectOptions>>,
142}
143
144impl Database {
145 #[instrument(level = "trace", skip(opt))]
148 pub fn connect<C>(opt: C) -> Result<DatabaseConnection, DbErr>
149 where
150 C: Into<ConnectOptions>,
151 {
152 let opt: ConnectOptions = opt.into();
153
154 if url::Url::parse(&opt.url).is_err() {
155 return Err(conn_err(format!(
156 "The connection string '{}' cannot be parsed.",
157 opt.url
158 )));
159 }
160
161 #[cfg(feature = "sqlx-mysql")]
162 if DbBackend::MySql.is_prefix_of(&opt.url) {
163 return crate::SqlxMySqlConnector::connect(opt);
164 }
165 #[cfg(feature = "sqlx-postgres")]
166 if DbBackend::Postgres.is_prefix_of(&opt.url) {
167 return crate::SqlxPostgresConnector::connect(opt);
168 }
169 #[cfg(feature = "sqlx-sqlite")]
170 if DbBackend::Sqlite.is_prefix_of(&opt.url) {
171 return crate::SqlxSqliteConnector::connect(opt);
172 }
173 #[cfg(feature = "rusqlite")]
174 if DbBackend::Sqlite.is_prefix_of(&opt.url) {
175 return crate::driver::rusqlite::RusqliteConnector::connect(opt);
176 }
177 #[cfg(feature = "mock")]
178 if crate::MockDatabaseConnector::accepts(&opt.url) {
179 return crate::MockDatabaseConnector::connect(&opt.url);
180 }
181
182 Err(conn_err(format!(
183 "The connection string '{}' has no supporting driver.",
184 opt.url
185 )))
186 }
187
188 #[cfg(feature = "proxy")]
190 #[instrument(level = "trace", skip(proxy_func_arc))]
191 pub fn connect_proxy(
192 db_type: DbBackend,
193 proxy_func_arc: std::sync::Arc<Box<dyn ProxyDatabaseTrait>>,
194 ) -> Result<DatabaseConnection, DbErr> {
195 match db_type {
196 DbBackend::MySql => {
197 return crate::ProxyDatabaseConnector::connect(
198 DbBackend::MySql,
199 proxy_func_arc.to_owned(),
200 );
201 }
202 DbBackend::Postgres => {
203 return crate::ProxyDatabaseConnector::connect(
204 DbBackend::Postgres,
205 proxy_func_arc.to_owned(),
206 );
207 }
208 DbBackend::Sqlite => {
209 return crate::ProxyDatabaseConnector::connect(
210 DbBackend::Sqlite,
211 proxy_func_arc.to_owned(),
212 );
213 }
214 }
215 }
216}
217
218impl<T> From<T> for ConnectOptions
219where
220 T: Into<String>,
221{
222 fn from(s: T) -> ConnectOptions {
223 ConnectOptions::new(s.into())
224 }
225}
226
227impl ConnectOptions {
228 pub fn new<T>(url: T) -> Self
230 where
231 T: Into<String>,
232 {
233 Self {
234 url: url.into(),
235 max_connections: None,
236 min_connections: None,
237 connect_timeout: None,
238 idle_timeout: None,
239 acquire_timeout: None,
240 max_lifetime: None,
241 sqlx_logging: true,
242 record_stmt_in_spans: true,
243 sqlx_logging_level: log::LevelFilter::Info,
244 sqlx_slow_statements_logging_level: log::LevelFilter::Off,
245 sqlx_slow_statements_logging_threshold: Duration::from_secs(1),
246 sqlcipher_key: None,
247 schema_search_path: None,
248 application_name: None,
249 statement_timeout: None,
250 test_before_acquire: true,
251 connect_lazy: false,
252 after_connect: None,
253 #[cfg(feature = "sqlx-mysql")]
254 mysql_pool_opts_fn: None,
255 #[cfg(feature = "sqlx-postgres")]
256 pg_pool_opts_fn: None,
257 #[cfg(feature = "sqlx-sqlite")]
258 sqlite_pool_opts_fn: None,
259 #[cfg(feature = "sqlx-mysql")]
260 mysql_opts_fn: None,
261 #[cfg(feature = "sqlx-postgres")]
262 pg_opts_fn: None,
263 #[cfg(feature = "sqlx-sqlite")]
264 sqlite_opts_fn: None,
265 }
266 }
267
268 pub fn get_url(&self) -> &str {
270 &self.url
271 }
272
273 pub fn max_connections(&mut self, value: u32) -> &mut Self {
275 self.max_connections = Some(value);
276 self
277 }
278
279 pub fn get_max_connections(&self) -> Option<u32> {
281 self.max_connections
282 }
283
284 pub fn min_connections(&mut self, value: u32) -> &mut Self {
286 self.min_connections = Some(value);
287 self
288 }
289
290 pub fn get_min_connections(&self) -> Option<u32> {
292 self.min_connections
293 }
294
295 pub fn connect_timeout(&mut self, value: Duration) -> &mut Self {
297 self.connect_timeout = Some(value);
298 self
299 }
300
301 pub fn get_connect_timeout(&self) -> Option<Duration> {
303 self.connect_timeout
304 }
305
306 pub fn idle_timeout<T>(&mut self, value: T) -> &mut Self
308 where
309 T: Into<Option<Duration>>,
310 {
311 self.idle_timeout = Some(value.into());
312 self
313 }
314
315 pub fn get_idle_timeout(&self) -> Option<Option<Duration>> {
317 self.idle_timeout
318 }
319
320 pub fn acquire_timeout(&mut self, value: Duration) -> &mut Self {
322 self.acquire_timeout = Some(value);
323 self
324 }
325
326 pub fn get_acquire_timeout(&self) -> Option<Duration> {
328 self.acquire_timeout
329 }
330
331 pub fn max_lifetime<T>(&mut self, lifetime: T) -> &mut Self
333 where
334 T: Into<Option<Duration>>,
335 {
336 self.max_lifetime = Some(lifetime.into());
337 self
338 }
339
340 pub fn get_max_lifetime(&self) -> Option<Option<Duration>> {
342 self.max_lifetime
343 }
344
345 pub fn sqlx_logging(&mut self, value: bool) -> &mut Self {
347 self.sqlx_logging = value;
348 self
349 }
350
351 pub fn get_sqlx_logging(&self) -> bool {
353 self.sqlx_logging
354 }
355
356 pub fn record_stmt_in_spans(&mut self, value: bool) -> &mut Self {
358 self.record_stmt_in_spans = value;
359 self
360 }
361
362 pub fn get_record_stmt_in_spans(&self) -> bool {
364 self.record_stmt_in_spans
365 }
366
367 pub fn sqlx_logging_level(&mut self, level: log::LevelFilter) -> &mut Self {
370 self.sqlx_logging_level = level;
371 self
372 }
373
374 pub fn sqlx_slow_statements_logging_settings(
377 &mut self,
378 level: log::LevelFilter,
379 duration: Duration,
380 ) -> &mut Self {
381 self.sqlx_slow_statements_logging_level = level;
382 self.sqlx_slow_statements_logging_threshold = duration;
383 self
384 }
385
386 pub fn get_sqlx_logging_level(&self) -> log::LevelFilter {
388 self.sqlx_logging_level
389 }
390
391 pub fn get_sqlx_slow_statements_logging_settings(&self) -> (log::LevelFilter, Duration) {
393 (
394 self.sqlx_slow_statements_logging_level,
395 self.sqlx_slow_statements_logging_threshold,
396 )
397 }
398
399 pub fn sqlcipher_key<T>(&mut self, value: T) -> &mut Self
401 where
402 T: Into<Cow<'static, str>>,
403 {
404 self.sqlcipher_key = Some(value.into());
405 self
406 }
407
408 pub fn set_schema_search_path<T>(&mut self, schema_search_path: T) -> &mut Self
410 where
411 T: Into<String>,
412 {
413 self.schema_search_path = Some(schema_search_path.into());
414 self
415 }
416
417 pub fn set_application_name<T>(&mut self, application_name: T) -> &mut Self
419 where
420 T: Into<String>,
421 {
422 self.application_name = Some(application_name.into());
423 self
424 }
425
426 pub fn statement_timeout(&mut self, value: Duration) -> &mut Self {
434 self.statement_timeout = Some(value);
435 self
436 }
437
438 pub fn get_statement_timeout(&self) -> Option<Duration> {
440 self.statement_timeout
441 }
442
443 pub fn test_before_acquire(&mut self, value: bool) -> &mut Self {
445 self.test_before_acquire = value;
446 self
447 }
448
449 pub fn connect_lazy(&mut self, value: bool) -> &mut Self {
452 self.connect_lazy = value;
453 self
454 }
455
456 pub fn get_connect_lazy(&self) -> bool {
458 self.connect_lazy
459 }
460
461 pub fn after_connect<F>(&mut self, f: F) -> &mut Self
463 where
464 F: Fn(DatabaseConnection) -> BoxFuture<'static, Result<(), DbErr>> + 'static,
465 {
466 self.after_connect = Some(Arc::new(f));
467
468 self
469 }
470
471 #[cfg(feature = "sqlx-mysql")]
472 #[cfg_attr(docsrs, doc(cfg(feature = "sqlx-mysql")))]
473 pub fn map_sqlx_mysql_opts<F>(&mut self, f: F) -> &mut Self
476 where
477 F: Fn(MySqlConnectOptions) -> MySqlConnectOptions + 'static,
478 {
479 self.mysql_opts_fn = Some(Arc::new(f));
480 self
481 }
482
483 #[cfg(feature = "sqlx-mysql")]
484 #[cfg_attr(docsrs, doc(cfg(feature = "sqlx-mysql")))]
485 pub fn map_sqlx_mysql_pool_opts<F>(&mut self, f: F) -> &mut Self
488 where
489 F: Fn(sqlx::pool::PoolOptions<sqlx::MySql>) -> sqlx::pool::PoolOptions<sqlx::MySql>
490 + 'static,
491 {
492 self.mysql_pool_opts_fn = Some(Arc::new(f));
493 self
494 }
495
496 #[cfg(feature = "sqlx-postgres")]
497 #[cfg_attr(docsrs, doc(cfg(feature = "sqlx-postgres")))]
498 pub fn map_sqlx_postgres_opts<F>(&mut self, f: F) -> &mut Self
501 where
502 F: Fn(PgConnectOptions) -> PgConnectOptions + 'static,
503 {
504 self.pg_opts_fn = Some(Arc::new(f));
505 self
506 }
507
508 #[cfg(feature = "sqlx-postgres")]
509 #[cfg_attr(docsrs, doc(cfg(feature = "sqlx-postgres")))]
510 pub fn map_sqlx_postgres_pool_opts<F>(&mut self, f: F) -> &mut Self
513 where
514 F: Fn(sqlx::pool::PoolOptions<sqlx::Postgres>) -> sqlx::pool::PoolOptions<sqlx::Postgres>
515 + 'static,
516 {
517 self.pg_pool_opts_fn = Some(Arc::new(f));
518 self
519 }
520
521 #[cfg(feature = "sqlx-sqlite")]
522 #[cfg_attr(docsrs, doc(cfg(feature = "sqlx-sqlite")))]
523 pub fn map_sqlx_sqlite_opts<F>(&mut self, f: F) -> &mut Self
526 where
527 F: Fn(SqliteConnectOptions) -> SqliteConnectOptions + 'static,
528 {
529 self.sqlite_opts_fn = Some(Arc::new(f));
530 self
531 }
532
533 #[cfg(feature = "sqlx-sqlite")]
534 #[cfg_attr(docsrs, doc(cfg(feature = "sqlx-sqlite")))]
535 pub fn map_sqlx_sqlite_pool_opts<F>(&mut self, f: F) -> &mut Self
538 where
539 F: Fn(sqlx::pool::PoolOptions<sqlx::Sqlite>) -> sqlx::pool::PoolOptions<sqlx::Sqlite>
540 + 'static,
541 {
542 self.sqlite_pool_opts_fn = Some(Arc::new(f));
543 self
544 }
545}