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