1use std::{sync::Arc, time::Duration};
2
3#[cfg(feature = "sqlx-mysql")]
4use sqlx::mysql::MySqlConnectOptions;
5#[cfg(feature = "sqlx-postgres")]
6use sqlx::postgres::PgConnectOptions;
7#[cfg(feature = "sqlx-sqlite")]
8use sqlx::sqlite::SqliteConnectOptions;
9
10mod connection;
11mod db_connection;
12mod executor;
13#[cfg(feature = "mock")]
14#[cfg_attr(docsrs, doc(cfg(feature = "mock")))]
15mod mock;
16#[cfg(feature = "proxy")]
17#[cfg_attr(docsrs, doc(cfg(feature = "proxy")))]
18mod proxy;
19#[cfg(feature = "rbac")]
20mod restricted_connection;
21#[cfg(all(feature = "schema-sync", feature = "rusqlite"))]
22mod sea_schema_rusqlite;
23#[cfg(all(feature = "schema-sync", feature = "sqlx-dep"))]
24mod sea_schema_shim;
25mod statement;
26mod stream;
27mod tracing_spans;
28mod transaction;
29
30pub use connection::*;
31pub use db_connection::*;
32pub use executor::*;
33#[cfg(feature = "mock")]
34#[cfg_attr(docsrs, doc(cfg(feature = "mock")))]
35pub use mock::*;
36#[cfg(feature = "proxy")]
37#[cfg_attr(docsrs, doc(cfg(feature = "proxy")))]
38pub use proxy::*;
39#[cfg(feature = "rbac")]
40pub use restricted_connection::*;
41pub use statement::*;
42use std::borrow::Cow;
43pub use stream::*;
44use tracing::instrument;
45pub use transaction::*;
46
47use crate::error::*;
48
49#[derive(Debug, Default)]
51pub struct Database;
52
53#[cfg(feature = "sync")]
54type BoxFuture<'a, T> = T;
55
56type AfterConnectCallback =
57 Option<Arc<dyn Fn(DatabaseConnection) -> BoxFuture<'static, Result<(), DbErr>> + 'static>>;
58
59#[derive(derive_more::Debug, Clone)]
61pub struct ConnectOptions {
62 pub(crate) url: String,
64 pub(crate) max_connections: Option<u32>,
66 pub(crate) min_connections: Option<u32>,
68 pub(crate) connect_timeout: Option<Duration>,
70 pub(crate) idle_timeout: Option<Option<Duration>>,
73 pub(crate) acquire_timeout: Option<Duration>,
75 pub(crate) max_lifetime: Option<Option<Duration>>,
77 pub(crate) sqlx_logging: bool,
79 pub(crate) sqlx_logging_level: log::LevelFilter,
81 pub(crate) sqlx_slow_statements_logging_level: log::LevelFilter,
83 pub(crate) sqlx_slow_statements_logging_threshold: Duration,
85 pub(crate) sqlcipher_key: Option<Cow<'static, str>>,
87 pub(crate) schema_search_path: Option<String>,
89 pub(crate) test_before_acquire: bool,
90 pub(crate) connect_lazy: bool,
94
95 #[debug(skip)]
96 pub(crate) after_connect: AfterConnectCallback,
97
98 #[cfg(feature = "sqlx-mysql")]
99 #[debug(skip)]
100 pub(crate) mysql_opts_fn: Option<Arc<dyn Fn(MySqlConnectOptions) -> MySqlConnectOptions>>,
101 #[cfg(feature = "sqlx-postgres")]
102 #[debug(skip)]
103 pub(crate) pg_opts_fn: Option<Arc<dyn Fn(PgConnectOptions) -> PgConnectOptions>>,
104 #[cfg(feature = "sqlx-sqlite")]
105 #[debug(skip)]
106 pub(crate) sqlite_opts_fn: Option<Arc<dyn Fn(SqliteConnectOptions) -> SqliteConnectOptions>>,
107}
108
109impl Database {
110 #[instrument(level = "trace", skip(opt))]
113 pub fn connect<C>(opt: C) -> Result<DatabaseConnection, DbErr>
114 where
115 C: Into<ConnectOptions>,
116 {
117 let opt: ConnectOptions = opt.into();
118
119 if url::Url::parse(&opt.url).is_err() {
120 return Err(conn_err(format!(
121 "The connection string '{}' cannot be parsed.",
122 opt.url
123 )));
124 }
125
126 #[cfg(feature = "sqlx-mysql")]
127 if DbBackend::MySql.is_prefix_of(&opt.url) {
128 return crate::SqlxMySqlConnector::connect(opt);
129 }
130 #[cfg(feature = "sqlx-postgres")]
131 if DbBackend::Postgres.is_prefix_of(&opt.url) {
132 return crate::SqlxPostgresConnector::connect(opt);
133 }
134 #[cfg(feature = "sqlx-sqlite")]
135 if DbBackend::Sqlite.is_prefix_of(&opt.url) {
136 return crate::SqlxSqliteConnector::connect(opt);
137 }
138 #[cfg(feature = "rusqlite")]
139 if DbBackend::Sqlite.is_prefix_of(&opt.url) {
140 return crate::driver::rusqlite::RusqliteConnector::connect(opt);
141 }
142 #[cfg(feature = "mock")]
143 if crate::MockDatabaseConnector::accepts(&opt.url) {
144 return crate::MockDatabaseConnector::connect(&opt.url);
145 }
146
147 Err(conn_err(format!(
148 "The connection string '{}' has no supporting driver.",
149 opt.url
150 )))
151 }
152
153 #[cfg(feature = "proxy")]
155 #[instrument(level = "trace", skip(proxy_func_arc))]
156 pub fn connect_proxy(
157 db_type: DbBackend,
158 proxy_func_arc: std::sync::Arc<Box<dyn ProxyDatabaseTrait>>,
159 ) -> Result<DatabaseConnection, DbErr> {
160 match db_type {
161 DbBackend::MySql => {
162 return crate::ProxyDatabaseConnector::connect(
163 DbBackend::MySql,
164 proxy_func_arc.to_owned(),
165 );
166 }
167 DbBackend::Postgres => {
168 return crate::ProxyDatabaseConnector::connect(
169 DbBackend::Postgres,
170 proxy_func_arc.to_owned(),
171 );
172 }
173 DbBackend::Sqlite => {
174 return crate::ProxyDatabaseConnector::connect(
175 DbBackend::Sqlite,
176 proxy_func_arc.to_owned(),
177 );
178 }
179 }
180 }
181}
182
183impl<T> From<T> for ConnectOptions
184where
185 T: Into<String>,
186{
187 fn from(s: T) -> ConnectOptions {
188 ConnectOptions::new(s.into())
189 }
190}
191
192impl ConnectOptions {
193 pub fn new<T>(url: T) -> Self
195 where
196 T: Into<String>,
197 {
198 Self {
199 url: url.into(),
200 max_connections: None,
201 min_connections: None,
202 connect_timeout: None,
203 idle_timeout: None,
204 acquire_timeout: None,
205 max_lifetime: None,
206 sqlx_logging: true,
207 sqlx_logging_level: log::LevelFilter::Info,
208 sqlx_slow_statements_logging_level: log::LevelFilter::Off,
209 sqlx_slow_statements_logging_threshold: Duration::from_secs(1),
210 sqlcipher_key: None,
211 schema_search_path: None,
212 test_before_acquire: true,
213 connect_lazy: false,
214 after_connect: None,
215 #[cfg(feature = "sqlx-mysql")]
216 mysql_opts_fn: None,
217 #[cfg(feature = "sqlx-postgres")]
218 pg_opts_fn: None,
219 #[cfg(feature = "sqlx-sqlite")]
220 sqlite_opts_fn: None,
221 }
222 }
223
224 pub fn get_url(&self) -> &str {
226 &self.url
227 }
228
229 pub fn max_connections(&mut self, value: u32) -> &mut Self {
231 self.max_connections = Some(value);
232 self
233 }
234
235 pub fn get_max_connections(&self) -> Option<u32> {
237 self.max_connections
238 }
239
240 pub fn min_connections(&mut self, value: u32) -> &mut Self {
242 self.min_connections = Some(value);
243 self
244 }
245
246 pub fn get_min_connections(&self) -> Option<u32> {
248 self.min_connections
249 }
250
251 pub fn connect_timeout(&mut self, value: Duration) -> &mut Self {
253 self.connect_timeout = Some(value);
254 self
255 }
256
257 pub fn get_connect_timeout(&self) -> Option<Duration> {
259 self.connect_timeout
260 }
261
262 pub fn idle_timeout<T>(&mut self, value: T) -> &mut Self
264 where
265 T: Into<Option<Duration>>,
266 {
267 self.idle_timeout = Some(value.into());
268 self
269 }
270
271 pub fn get_idle_timeout(&self) -> Option<Option<Duration>> {
273 self.idle_timeout
274 }
275
276 pub fn acquire_timeout(&mut self, value: Duration) -> &mut Self {
278 self.acquire_timeout = Some(value);
279 self
280 }
281
282 pub fn get_acquire_timeout(&self) -> Option<Duration> {
284 self.acquire_timeout
285 }
286
287 pub fn max_lifetime<T>(&mut self, lifetime: T) -> &mut Self
289 where
290 T: Into<Option<Duration>>,
291 {
292 self.max_lifetime = Some(lifetime.into());
293 self
294 }
295
296 pub fn get_max_lifetime(&self) -> Option<Option<Duration>> {
298 self.max_lifetime
299 }
300
301 pub fn sqlx_logging(&mut self, value: bool) -> &mut Self {
303 self.sqlx_logging = value;
304 self
305 }
306
307 pub fn get_sqlx_logging(&self) -> bool {
309 self.sqlx_logging
310 }
311
312 pub fn sqlx_logging_level(&mut self, level: log::LevelFilter) -> &mut Self {
315 self.sqlx_logging_level = level;
316 self
317 }
318
319 pub fn sqlx_slow_statements_logging_settings(
322 &mut self,
323 level: log::LevelFilter,
324 duration: Duration,
325 ) -> &mut Self {
326 self.sqlx_slow_statements_logging_level = level;
327 self.sqlx_slow_statements_logging_threshold = duration;
328 self
329 }
330
331 pub fn get_sqlx_logging_level(&self) -> log::LevelFilter {
333 self.sqlx_logging_level
334 }
335
336 pub fn get_sqlx_slow_statements_logging_settings(&self) -> (log::LevelFilter, Duration) {
338 (
339 self.sqlx_slow_statements_logging_level,
340 self.sqlx_slow_statements_logging_threshold,
341 )
342 }
343
344 pub fn sqlcipher_key<T>(&mut self, value: T) -> &mut Self
346 where
347 T: Into<Cow<'static, str>>,
348 {
349 self.sqlcipher_key = Some(value.into());
350 self
351 }
352
353 pub fn set_schema_search_path<T>(&mut self, schema_search_path: T) -> &mut Self
355 where
356 T: Into<String>,
357 {
358 self.schema_search_path = Some(schema_search_path.into());
359 self
360 }
361
362 pub fn test_before_acquire(&mut self, value: bool) -> &mut Self {
364 self.test_before_acquire = value;
365 self
366 }
367
368 pub fn connect_lazy(&mut self, value: bool) -> &mut Self {
371 self.connect_lazy = value;
372 self
373 }
374
375 pub fn get_connect_lazy(&self) -> bool {
377 self.connect_lazy
378 }
379
380 pub fn after_connect<F>(&mut self, f: F) -> &mut Self
382 where
383 F: Fn(DatabaseConnection) -> BoxFuture<'static, Result<(), DbErr>> + 'static,
384 {
385 self.after_connect = Some(Arc::new(f));
386
387 self
388 }
389
390 #[cfg(feature = "sqlx-mysql")]
391 #[cfg_attr(docsrs, doc(cfg(feature = "sqlx-mysql")))]
392 pub fn map_sqlx_mysql_opts<F>(&mut self, f: F) -> &mut Self
395 where
396 F: Fn(MySqlConnectOptions) -> MySqlConnectOptions + 'static,
397 {
398 self.mysql_opts_fn = Some(Arc::new(f));
399 self
400 }
401
402 #[cfg(feature = "sqlx-postgres")]
403 #[cfg_attr(docsrs, doc(cfg(feature = "sqlx-postgres")))]
404 pub fn map_sqlx_postgres_opts<F>(&mut self, f: F) -> &mut Self
407 where
408 F: Fn(PgConnectOptions) -> PgConnectOptions + 'static,
409 {
410 self.pg_opts_fn = Some(Arc::new(f));
411 self
412 }
413
414 #[cfg(feature = "sqlx-sqlite")]
415 #[cfg_attr(docsrs, doc(cfg(feature = "sqlx-sqlite")))]
416 pub fn map_sqlx_sqlite_opts<F>(&mut self, f: F) -> &mut Self
419 where
420 F: Fn(SqliteConnectOptions) -> SqliteConnectOptions + 'static,
421 {
422 self.sqlite_opts_fn = Some(Arc::new(f));
423 self
424 }
425}