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;
12#[cfg(feature = "mock")]
13#[cfg_attr(docsrs, doc(cfg(feature = "mock")))]
14mod mock;
15#[cfg(feature = "proxy")]
16#[cfg_attr(docsrs, doc(cfg(feature = "proxy")))]
17mod proxy;
18#[cfg(feature = "rbac")]
19mod restricted_connection;
20#[cfg(all(feature = "schema-sync", feature = "rusqlite"))]
21mod sea_schema_rusqlite;
22#[cfg(all(feature = "schema-sync", feature = "sqlx-dep"))]
23mod sea_schema_shim;
24mod statement;
25mod stream;
26mod transaction;
27
28pub use connection::*;
29pub use db_connection::*;
30#[cfg(feature = "mock")]
31#[cfg_attr(docsrs, doc(cfg(feature = "mock")))]
32pub use mock::*;
33#[cfg(feature = "proxy")]
34#[cfg_attr(docsrs, doc(cfg(feature = "proxy")))]
35pub use proxy::*;
36#[cfg(feature = "rbac")]
37pub use restricted_connection::*;
38pub use statement::*;
39use std::borrow::Cow;
40pub use stream::*;
41use tracing::instrument;
42pub use transaction::*;
43
44use crate::error::*;
45
46#[derive(Debug, Default)]
48pub struct Database;
49
50#[cfg(feature = "sync")]
51type BoxFuture<'a, T> = T;
52
53type AfterConnectCallback =
54 Option<Arc<dyn Fn(DatabaseConnection) -> BoxFuture<'static, Result<(), DbErr>> + 'static>>;
55
56#[derive(derive_more::Debug, Clone)]
58pub struct ConnectOptions {
59 pub(crate) url: String,
61 pub(crate) max_connections: Option<u32>,
63 pub(crate) min_connections: Option<u32>,
65 pub(crate) connect_timeout: Option<Duration>,
67 pub(crate) idle_timeout: Option<Option<Duration>>,
70 pub(crate) acquire_timeout: Option<Duration>,
72 pub(crate) max_lifetime: Option<Option<Duration>>,
74 pub(crate) sqlx_logging: bool,
76 pub(crate) sqlx_logging_level: log::LevelFilter,
78 pub(crate) sqlx_slow_statements_logging_level: log::LevelFilter,
80 pub(crate) sqlx_slow_statements_logging_threshold: Duration,
82 pub(crate) sqlcipher_key: Option<Cow<'static, str>>,
84 pub(crate) schema_search_path: Option<String>,
86 pub(crate) test_before_acquire: bool,
87 pub(crate) connect_lazy: bool,
91
92 #[debug(skip)]
93 pub(crate) after_connect: AfterConnectCallback,
94
95 #[cfg(feature = "sqlx-mysql")]
96 #[debug(skip)]
97 pub(crate) mysql_opts_fn: Option<Arc<dyn Fn(MySqlConnectOptions) -> MySqlConnectOptions>>,
98 #[cfg(feature = "sqlx-postgres")]
99 #[debug(skip)]
100 pub(crate) pg_opts_fn: Option<Arc<dyn Fn(PgConnectOptions) -> PgConnectOptions>>,
101 #[cfg(feature = "sqlx-sqlite")]
102 #[debug(skip)]
103 pub(crate) sqlite_opts_fn: Option<Arc<dyn Fn(SqliteConnectOptions) -> SqliteConnectOptions>>,
104}
105
106impl Database {
107 #[instrument(level = "trace", skip(opt))]
110 pub fn connect<C>(opt: C) -> Result<DatabaseConnection, DbErr>
111 where
112 C: Into<ConnectOptions>,
113 {
114 let opt: ConnectOptions = opt.into();
115
116 if url::Url::parse(&opt.url).is_err() {
117 return Err(conn_err(format!(
118 "The connection string '{}' cannot be parsed.",
119 opt.url
120 )));
121 }
122
123 #[cfg(feature = "sqlx-mysql")]
124 if DbBackend::MySql.is_prefix_of(&opt.url) {
125 return crate::SqlxMySqlConnector::connect(opt);
126 }
127 #[cfg(feature = "sqlx-postgres")]
128 if DbBackend::Postgres.is_prefix_of(&opt.url) {
129 return crate::SqlxPostgresConnector::connect(opt);
130 }
131 #[cfg(feature = "sqlx-sqlite")]
132 if DbBackend::Sqlite.is_prefix_of(&opt.url) {
133 return crate::SqlxSqliteConnector::connect(opt);
134 }
135 #[cfg(feature = "rusqlite")]
136 if DbBackend::Sqlite.is_prefix_of(&opt.url) {
137 return crate::driver::rusqlite::RusqliteConnector::connect(opt);
138 }
139 #[cfg(feature = "mock")]
140 if crate::MockDatabaseConnector::accepts(&opt.url) {
141 return crate::MockDatabaseConnector::connect(&opt.url);
142 }
143
144 Err(conn_err(format!(
145 "The connection string '{}' has no supporting driver.",
146 opt.url
147 )))
148 }
149
150 #[cfg(feature = "proxy")]
152 #[instrument(level = "trace", skip(proxy_func_arc))]
153 pub fn connect_proxy(
154 db_type: DbBackend,
155 proxy_func_arc: std::sync::Arc<Box<dyn ProxyDatabaseTrait>>,
156 ) -> Result<DatabaseConnection, DbErr> {
157 match db_type {
158 DbBackend::MySql => {
159 return crate::ProxyDatabaseConnector::connect(
160 DbBackend::MySql,
161 proxy_func_arc.to_owned(),
162 );
163 }
164 DbBackend::Postgres => {
165 return crate::ProxyDatabaseConnector::connect(
166 DbBackend::Postgres,
167 proxy_func_arc.to_owned(),
168 );
169 }
170 DbBackend::Sqlite => {
171 return crate::ProxyDatabaseConnector::connect(
172 DbBackend::Sqlite,
173 proxy_func_arc.to_owned(),
174 );
175 }
176 }
177 }
178}
179
180impl<T> From<T> for ConnectOptions
181where
182 T: Into<String>,
183{
184 fn from(s: T) -> ConnectOptions {
185 ConnectOptions::new(s.into())
186 }
187}
188
189impl ConnectOptions {
190 pub fn new<T>(url: T) -> Self
192 where
193 T: Into<String>,
194 {
195 Self {
196 url: url.into(),
197 max_connections: None,
198 min_connections: None,
199 connect_timeout: None,
200 idle_timeout: None,
201 acquire_timeout: None,
202 max_lifetime: None,
203 sqlx_logging: true,
204 sqlx_logging_level: log::LevelFilter::Info,
205 sqlx_slow_statements_logging_level: log::LevelFilter::Off,
206 sqlx_slow_statements_logging_threshold: Duration::from_secs(1),
207 sqlcipher_key: None,
208 schema_search_path: None,
209 test_before_acquire: true,
210 connect_lazy: false,
211 after_connect: None,
212 #[cfg(feature = "sqlx-mysql")]
213 mysql_opts_fn: None,
214 #[cfg(feature = "sqlx-postgres")]
215 pg_opts_fn: None,
216 #[cfg(feature = "sqlx-sqlite")]
217 sqlite_opts_fn: None,
218 }
219 }
220
221 pub fn get_url(&self) -> &str {
223 &self.url
224 }
225
226 pub fn max_connections(&mut self, value: u32) -> &mut Self {
228 self.max_connections = Some(value);
229 self
230 }
231
232 pub fn get_max_connections(&self) -> Option<u32> {
234 self.max_connections
235 }
236
237 pub fn min_connections(&mut self, value: u32) -> &mut Self {
239 self.min_connections = Some(value);
240 self
241 }
242
243 pub fn get_min_connections(&self) -> Option<u32> {
245 self.min_connections
246 }
247
248 pub fn connect_timeout(&mut self, value: Duration) -> &mut Self {
250 self.connect_timeout = Some(value);
251 self
252 }
253
254 pub fn get_connect_timeout(&self) -> Option<Duration> {
256 self.connect_timeout
257 }
258
259 pub fn idle_timeout<T>(&mut self, value: T) -> &mut Self
261 where
262 T: Into<Option<Duration>>,
263 {
264 self.idle_timeout = Some(value.into());
265 self
266 }
267
268 pub fn get_idle_timeout(&self) -> Option<Option<Duration>> {
270 self.idle_timeout
271 }
272
273 pub fn acquire_timeout(&mut self, value: Duration) -> &mut Self {
275 self.acquire_timeout = Some(value);
276 self
277 }
278
279 pub fn get_acquire_timeout(&self) -> Option<Duration> {
281 self.acquire_timeout
282 }
283
284 pub fn max_lifetime<T>(&mut self, lifetime: T) -> &mut Self
286 where
287 T: Into<Option<Duration>>,
288 {
289 self.max_lifetime = Some(lifetime.into());
290 self
291 }
292
293 pub fn get_max_lifetime(&self) -> Option<Option<Duration>> {
295 self.max_lifetime
296 }
297
298 pub fn sqlx_logging(&mut self, value: bool) -> &mut Self {
300 self.sqlx_logging = value;
301 self
302 }
303
304 pub fn get_sqlx_logging(&self) -> bool {
306 self.sqlx_logging
307 }
308
309 pub fn sqlx_logging_level(&mut self, level: log::LevelFilter) -> &mut Self {
312 self.sqlx_logging_level = level;
313 self
314 }
315
316 pub fn sqlx_slow_statements_logging_settings(
319 &mut self,
320 level: log::LevelFilter,
321 duration: Duration,
322 ) -> &mut Self {
323 self.sqlx_slow_statements_logging_level = level;
324 self.sqlx_slow_statements_logging_threshold = duration;
325 self
326 }
327
328 pub fn get_sqlx_logging_level(&self) -> log::LevelFilter {
330 self.sqlx_logging_level
331 }
332
333 pub fn get_sqlx_slow_statements_logging_settings(&self) -> (log::LevelFilter, Duration) {
335 (
336 self.sqlx_slow_statements_logging_level,
337 self.sqlx_slow_statements_logging_threshold,
338 )
339 }
340
341 pub fn sqlcipher_key<T>(&mut self, value: T) -> &mut Self
343 where
344 T: Into<Cow<'static, str>>,
345 {
346 self.sqlcipher_key = Some(value.into());
347 self
348 }
349
350 pub fn set_schema_search_path<T>(&mut self, schema_search_path: T) -> &mut Self
352 where
353 T: Into<String>,
354 {
355 self.schema_search_path = Some(schema_search_path.into());
356 self
357 }
358
359 pub fn test_before_acquire(&mut self, value: bool) -> &mut Self {
361 self.test_before_acquire = value;
362 self
363 }
364
365 pub fn connect_lazy(&mut self, value: bool) -> &mut Self {
368 self.connect_lazy = value;
369 self
370 }
371
372 pub fn get_connect_lazy(&self) -> bool {
374 self.connect_lazy
375 }
376
377 pub fn after_connect<F>(&mut self, f: F) -> &mut Self
379 where
380 F: Fn(DatabaseConnection) -> BoxFuture<'static, Result<(), DbErr>> + 'static,
381 {
382 self.after_connect = Some(Arc::new(f));
383
384 self
385 }
386
387 #[cfg(feature = "sqlx-mysql")]
388 #[cfg_attr(docsrs, doc(cfg(feature = "sqlx-mysql")))]
389 pub fn map_sqlx_mysql_opts<F>(&mut self, f: F) -> &mut Self
392 where
393 F: Fn(MySqlConnectOptions) -> MySqlConnectOptions + 'static,
394 {
395 self.mysql_opts_fn = Some(Arc::new(f));
396 self
397 }
398
399 #[cfg(feature = "sqlx-postgres")]
400 #[cfg_attr(docsrs, doc(cfg(feature = "sqlx-postgres")))]
401 pub fn map_sqlx_postgres_opts<F>(&mut self, f: F) -> &mut Self
404 where
405 F: Fn(PgConnectOptions) -> PgConnectOptions + 'static,
406 {
407 self.pg_opts_fn = Some(Arc::new(f));
408 self
409 }
410
411 #[cfg(feature = "sqlx-sqlite")]
412 #[cfg_attr(docsrs, doc(cfg(feature = "sqlx-sqlite")))]
413 pub fn map_sqlx_sqlite_opts<F>(&mut self, f: F) -> &mut Self
416 where
417 F: Fn(SqliteConnectOptions) -> SqliteConnectOptions + 'static,
418 {
419 self.sqlite_opts_fn = Some(Arc::new(f));
420 self
421 }
422}