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;
27mod stream;
28mod tracing_spans;
29mod transaction;
30
31pub use connection::*;
32pub use db_connection::*;
33pub use executor::*;
34#[cfg(feature = "mock")]
35#[cfg_attr(docsrs, doc(cfg(feature = "mock")))]
36pub use mock::*;
37#[cfg(feature = "proxy")]
38#[cfg_attr(docsrs, doc(cfg(feature = "proxy")))]
39pub use proxy::*;
40#[cfg(feature = "rbac")]
41pub use restricted_connection::*;
42pub use statement::*;
43use std::borrow::Cow;
44pub use stream::*;
45use tracing::instrument;
46pub use transaction::*;
47
48use crate::error::*;
49
50#[derive(Debug, Default)]
52pub struct Database;
53
54#[cfg(feature = "sync")]
55type BoxFuture<'a, T> = T;
56
57#[cfg(feature = "sqlx-mysql")]
58type MapMySqlPoolOptsFn =
59 Arc<dyn Fn(sqlx::pool::PoolOptions<sqlx::MySql>) -> sqlx::pool::PoolOptions<sqlx::MySql>>;
60
61#[cfg(feature = "sqlx-postgres")]
62type MapPgPoolOptsFn =
63 Arc<dyn Fn(sqlx::pool::PoolOptions<sqlx::Postgres>) -> sqlx::pool::PoolOptions<sqlx::Postgres>>;
64
65#[cfg(feature = "sqlx-sqlite")]
66type MapSqlitePoolOptsFn = Option<
67 Arc<dyn Fn(sqlx::pool::PoolOptions<sqlx::Sqlite>) -> sqlx::pool::PoolOptions<sqlx::Sqlite>>,
68>;
69
70type AfterConnectCallback =
71 Option<Arc<dyn Fn(DatabaseConnection) -> BoxFuture<'static, Result<(), DbErr>> + 'static>>;
72
73#[derive(derive_more::Debug, Clone)]
75pub struct ConnectOptions {
76 pub(crate) url: String,
78 pub(crate) max_connections: Option<u32>,
80 pub(crate) min_connections: Option<u32>,
82 pub(crate) connect_timeout: Option<Duration>,
84 pub(crate) idle_timeout: Option<Option<Duration>>,
87 pub(crate) acquire_timeout: Option<Duration>,
89 pub(crate) max_lifetime: Option<Option<Duration>>,
91 pub(crate) sqlx_logging: bool,
93 pub(crate) sqlx_logging_level: log::LevelFilter,
95 pub(crate) sqlx_slow_statements_logging_level: log::LevelFilter,
97 pub(crate) sqlx_slow_statements_logging_threshold: Duration,
99 pub(crate) sqlcipher_key: Option<Cow<'static, str>>,
101 pub(crate) schema_search_path: Option<String>,
103 pub(crate) application_name: Option<String>,
105 pub(crate) statement_timeout: Option<Duration>,
107 pub(crate) test_before_acquire: bool,
108 pub(crate) connect_lazy: bool,
112
113 #[debug(skip)]
114 pub(crate) after_connect: AfterConnectCallback,
115
116 #[cfg(feature = "sqlx-mysql")]
117 #[debug(skip)]
118 pub(crate) mysql_pool_opts_fn: Option<MapMySqlPoolOptsFn>,
119 #[cfg(feature = "sqlx-postgres")]
120 #[debug(skip)]
121 pub(crate) pg_pool_opts_fn: Option<MapPgPoolOptsFn>,
122 #[cfg(feature = "sqlx-sqlite")]
123 #[debug(skip)]
124 pub(crate) sqlite_pool_opts_fn: MapSqlitePoolOptsFn,
125 #[cfg(feature = "sqlx-mysql")]
126 #[debug(skip)]
127 pub(crate) mysql_opts_fn: Option<Arc<dyn Fn(MySqlConnectOptions) -> MySqlConnectOptions>>,
128 #[cfg(feature = "sqlx-postgres")]
129 #[debug(skip)]
130 pub(crate) pg_opts_fn: Option<Arc<dyn Fn(PgConnectOptions) -> PgConnectOptions>>,
131 #[cfg(feature = "sqlx-sqlite")]
132 #[debug(skip)]
133 pub(crate) sqlite_opts_fn: Option<Arc<dyn Fn(SqliteConnectOptions) -> SqliteConnectOptions>>,
134}
135
136impl Database {
137 #[instrument(level = "trace", skip(opt))]
140 pub fn connect<C>(opt: C) -> Result<DatabaseConnection, DbErr>
141 where
142 C: Into<ConnectOptions>,
143 {
144 let opt: ConnectOptions = opt.into();
145
146 if url::Url::parse(&opt.url).is_err() {
147 return Err(conn_err(format!(
148 "The connection string '{}' cannot be parsed.",
149 opt.url
150 )));
151 }
152
153 #[cfg(feature = "sqlx-mysql")]
154 if DbBackend::MySql.is_prefix_of(&opt.url) {
155 return crate::SqlxMySqlConnector::connect(opt);
156 }
157 #[cfg(feature = "sqlx-postgres")]
158 if DbBackend::Postgres.is_prefix_of(&opt.url) {
159 return crate::SqlxPostgresConnector::connect(opt);
160 }
161 #[cfg(feature = "sqlx-sqlite")]
162 if DbBackend::Sqlite.is_prefix_of(&opt.url) {
163 return crate::SqlxSqliteConnector::connect(opt);
164 }
165 #[cfg(feature = "rusqlite")]
166 if DbBackend::Sqlite.is_prefix_of(&opt.url) {
167 return crate::driver::rusqlite::RusqliteConnector::connect(opt);
168 }
169 #[cfg(feature = "mock")]
170 if crate::MockDatabaseConnector::accepts(&opt.url) {
171 return crate::MockDatabaseConnector::connect(&opt.url);
172 }
173
174 Err(conn_err(format!(
175 "The connection string '{}' has no supporting driver.",
176 opt.url
177 )))
178 }
179
180 #[cfg(feature = "proxy")]
182 #[instrument(level = "trace", skip(proxy_func_arc))]
183 pub fn connect_proxy(
184 db_type: DbBackend,
185 proxy_func_arc: std::sync::Arc<Box<dyn ProxyDatabaseTrait>>,
186 ) -> Result<DatabaseConnection, DbErr> {
187 match db_type {
188 DbBackend::MySql => {
189 return crate::ProxyDatabaseConnector::connect(
190 DbBackend::MySql,
191 proxy_func_arc.to_owned(),
192 );
193 }
194 DbBackend::Postgres => {
195 return crate::ProxyDatabaseConnector::connect(
196 DbBackend::Postgres,
197 proxy_func_arc.to_owned(),
198 );
199 }
200 DbBackend::Sqlite => {
201 return crate::ProxyDatabaseConnector::connect(
202 DbBackend::Sqlite,
203 proxy_func_arc.to_owned(),
204 );
205 }
206 }
207 }
208}
209
210impl<T> From<T> for ConnectOptions
211where
212 T: Into<String>,
213{
214 fn from(s: T) -> ConnectOptions {
215 ConnectOptions::new(s.into())
216 }
217}
218
219impl ConnectOptions {
220 pub fn new<T>(url: T) -> Self
222 where
223 T: Into<String>,
224 {
225 Self {
226 url: url.into(),
227 max_connections: None,
228 min_connections: None,
229 connect_timeout: None,
230 idle_timeout: None,
231 acquire_timeout: None,
232 max_lifetime: None,
233 sqlx_logging: true,
234 sqlx_logging_level: log::LevelFilter::Info,
235 sqlx_slow_statements_logging_level: log::LevelFilter::Off,
236 sqlx_slow_statements_logging_threshold: Duration::from_secs(1),
237 sqlcipher_key: None,
238 schema_search_path: None,
239 application_name: None,
240 statement_timeout: None,
241 test_before_acquire: true,
242 connect_lazy: false,
243 after_connect: None,
244 #[cfg(feature = "sqlx-mysql")]
245 mysql_pool_opts_fn: None,
246 #[cfg(feature = "sqlx-postgres")]
247 pg_pool_opts_fn: None,
248 #[cfg(feature = "sqlx-sqlite")]
249 sqlite_pool_opts_fn: None,
250 #[cfg(feature = "sqlx-mysql")]
251 mysql_opts_fn: None,
252 #[cfg(feature = "sqlx-postgres")]
253 pg_opts_fn: None,
254 #[cfg(feature = "sqlx-sqlite")]
255 sqlite_opts_fn: None,
256 }
257 }
258
259 pub fn get_url(&self) -> &str {
261 &self.url
262 }
263
264 pub fn max_connections(&mut self, value: u32) -> &mut Self {
266 self.max_connections = Some(value);
267 self
268 }
269
270 pub fn get_max_connections(&self) -> Option<u32> {
272 self.max_connections
273 }
274
275 pub fn min_connections(&mut self, value: u32) -> &mut Self {
277 self.min_connections = Some(value);
278 self
279 }
280
281 pub fn get_min_connections(&self) -> Option<u32> {
283 self.min_connections
284 }
285
286 pub fn connect_timeout(&mut self, value: Duration) -> &mut Self {
288 self.connect_timeout = Some(value);
289 self
290 }
291
292 pub fn get_connect_timeout(&self) -> Option<Duration> {
294 self.connect_timeout
295 }
296
297 pub fn idle_timeout<T>(&mut self, value: T) -> &mut Self
299 where
300 T: Into<Option<Duration>>,
301 {
302 self.idle_timeout = Some(value.into());
303 self
304 }
305
306 pub fn get_idle_timeout(&self) -> Option<Option<Duration>> {
308 self.idle_timeout
309 }
310
311 pub fn acquire_timeout(&mut self, value: Duration) -> &mut Self {
313 self.acquire_timeout = Some(value);
314 self
315 }
316
317 pub fn get_acquire_timeout(&self) -> Option<Duration> {
319 self.acquire_timeout
320 }
321
322 pub fn max_lifetime<T>(&mut self, lifetime: T) -> &mut Self
324 where
325 T: Into<Option<Duration>>,
326 {
327 self.max_lifetime = Some(lifetime.into());
328 self
329 }
330
331 pub fn get_max_lifetime(&self) -> Option<Option<Duration>> {
333 self.max_lifetime
334 }
335
336 pub fn sqlx_logging(&mut self, value: bool) -> &mut Self {
338 self.sqlx_logging = value;
339 self
340 }
341
342 pub fn get_sqlx_logging(&self) -> bool {
344 self.sqlx_logging
345 }
346
347 pub fn sqlx_logging_level(&mut self, level: log::LevelFilter) -> &mut Self {
350 self.sqlx_logging_level = level;
351 self
352 }
353
354 pub fn sqlx_slow_statements_logging_settings(
357 &mut self,
358 level: log::LevelFilter,
359 duration: Duration,
360 ) -> &mut Self {
361 self.sqlx_slow_statements_logging_level = level;
362 self.sqlx_slow_statements_logging_threshold = duration;
363 self
364 }
365
366 pub fn get_sqlx_logging_level(&self) -> log::LevelFilter {
368 self.sqlx_logging_level
369 }
370
371 pub fn get_sqlx_slow_statements_logging_settings(&self) -> (log::LevelFilter, Duration) {
373 (
374 self.sqlx_slow_statements_logging_level,
375 self.sqlx_slow_statements_logging_threshold,
376 )
377 }
378
379 pub fn sqlcipher_key<T>(&mut self, value: T) -> &mut Self
381 where
382 T: Into<Cow<'static, str>>,
383 {
384 self.sqlcipher_key = Some(value.into());
385 self
386 }
387
388 pub fn set_schema_search_path<T>(&mut self, schema_search_path: T) -> &mut Self
390 where
391 T: Into<String>,
392 {
393 self.schema_search_path = Some(schema_search_path.into());
394 self
395 }
396
397 pub fn set_application_name<T>(&mut self, application_name: T) -> &mut Self
399 where
400 T: Into<String>,
401 {
402 self.application_name = Some(application_name.into());
403 self
404 }
405
406 pub fn statement_timeout(&mut self, value: Duration) -> &mut Self {
414 self.statement_timeout = Some(value);
415 self
416 }
417
418 pub fn get_statement_timeout(&self) -> Option<Duration> {
420 self.statement_timeout
421 }
422
423 pub fn test_before_acquire(&mut self, value: bool) -> &mut Self {
425 self.test_before_acquire = value;
426 self
427 }
428
429 pub fn connect_lazy(&mut self, value: bool) -> &mut Self {
432 self.connect_lazy = value;
433 self
434 }
435
436 pub fn get_connect_lazy(&self) -> bool {
438 self.connect_lazy
439 }
440
441 pub fn after_connect<F>(&mut self, f: F) -> &mut Self
443 where
444 F: Fn(DatabaseConnection) -> BoxFuture<'static, Result<(), DbErr>> + 'static,
445 {
446 self.after_connect = Some(Arc::new(f));
447
448 self
449 }
450
451 #[cfg(feature = "sqlx-mysql")]
452 #[cfg_attr(docsrs, doc(cfg(feature = "sqlx-mysql")))]
453 pub fn map_sqlx_mysql_opts<F>(&mut self, f: F) -> &mut Self
456 where
457 F: Fn(MySqlConnectOptions) -> MySqlConnectOptions + 'static,
458 {
459 self.mysql_opts_fn = Some(Arc::new(f));
460 self
461 }
462
463 #[cfg(feature = "sqlx-mysql")]
464 #[cfg_attr(docsrs, doc(cfg(feature = "sqlx-mysql")))]
465 pub fn map_sqlx_mysql_pool_opts<F>(&mut self, f: F) -> &mut Self
468 where
469 F: Fn(sqlx::pool::PoolOptions<sqlx::MySql>) -> sqlx::pool::PoolOptions<sqlx::MySql>
470 + 'static,
471 {
472 self.mysql_pool_opts_fn = Some(Arc::new(f));
473 self
474 }
475
476 #[cfg(feature = "sqlx-postgres")]
477 #[cfg_attr(docsrs, doc(cfg(feature = "sqlx-postgres")))]
478 pub fn map_sqlx_postgres_opts<F>(&mut self, f: F) -> &mut Self
481 where
482 F: Fn(PgConnectOptions) -> PgConnectOptions + 'static,
483 {
484 self.pg_opts_fn = Some(Arc::new(f));
485 self
486 }
487
488 #[cfg(feature = "sqlx-postgres")]
489 #[cfg_attr(docsrs, doc(cfg(feature = "sqlx-postgres")))]
490 pub fn map_sqlx_postgres_pool_opts<F>(&mut self, f: F) -> &mut Self
493 where
494 F: Fn(sqlx::pool::PoolOptions<sqlx::Postgres>) -> sqlx::pool::PoolOptions<sqlx::Postgres>
495 + 'static,
496 {
497 self.pg_pool_opts_fn = Some(Arc::new(f));
498 self
499 }
500
501 #[cfg(feature = "sqlx-sqlite")]
502 #[cfg_attr(docsrs, doc(cfg(feature = "sqlx-sqlite")))]
503 pub fn map_sqlx_sqlite_opts<F>(&mut self, f: F) -> &mut Self
506 where
507 F: Fn(SqliteConnectOptions) -> SqliteConnectOptions + 'static,
508 {
509 self.sqlite_opts_fn = Some(Arc::new(f));
510 self
511 }
512
513 #[cfg(feature = "sqlx-sqlite")]
514 #[cfg_attr(docsrs, doc(cfg(feature = "sqlx-sqlite")))]
515 pub fn map_sqlx_sqlite_pool_opts<F>(&mut self, f: F) -> &mut Self
518 where
519 F: Fn(sqlx::pool::PoolOptions<sqlx::Sqlite>) -> sqlx::pool::PoolOptions<sqlx::Sqlite>
520 + 'static,
521 {
522 self.sqlite_pool_opts_fn = Some(Arc::new(f));
523 self
524 }
525}