rocket_sync_db_pools_community/
lib.rs

1//! Traits, utilities, and a macro for easy database connection pooling.
2//!
3//! # Overview
4//!
5//! This crate provides traits, utilities, and a procedural macro for
6//! configuring and accessing database connection pools in Rocket. A _database
7//! connection pool_ is a data structure that maintains active database
8//! connections for later use in the application. This implementation is backed
9//! by [`r2d2`] and exposes connections through request guards.
10//!
11//! Databases are individually configured through Rocket's regular configuration
12//! mechanisms. Connecting a Rocket application to a database using this library
13//! occurs in three simple steps:
14//!
15//!   1. Configure your databases in `Rocket.toml`.
16//!      (see [Configuration](#configuration))
17//!   2. Associate a request guard type and fairing with each database.
18//!      (see [Guard Types](#guard-types))
19//!   3. Use the request guard to retrieve a connection in a handler.
20//!      (see [Handlers](#handlers))
21//!
22//! For a list of supported databases, see [Provided Databases](#provided). This
23//! support can be easily extended by implementing the [`Poolable`] trait. See
24//! [Extending](#extending) for more.
25//!
26//! ## Example
27//!
28//! Before using this library, the feature corresponding to your database type
29//! in `rocket_sync_db_pools` must be enabled:
30//!
31//! ```toml
32//! [dependencies.rocket_sync_db_pools]
33//! package = "rocket_sync_db_pools-community"
34//! version = "0.3.2"
35//! features = ["diesel_sqlite_pool"]
36//! ```
37//!
38//! See [Provided](#provided) for a list of supported database and their
39//! associated feature name.
40//!
41//! In whichever configuration source you choose, configure a `databases`
42//! dictionary with an internal dictionary for each database, here `sqlite_logs`
43//! in a TOML source:
44//!
45//! ```toml
46//! [default.databases]
47//! sqlite_logs = { url = "/path/to/database.sqlite" }
48//! ```
49//!
50//! In your application's source code, one-time:
51//!
52//! ```rust
53//! # extern crate rocket_sync_db_pools_community as rocket_sync_db_pools;
54//! # #[macro_use] extern crate rocket;
55//! # #[cfg(feature = "diesel_sqlite_pool")]
56//! # mod test {
57//! use rocket_sync_db_pools::{database, diesel};
58//!
59//! #[database("sqlite_logs")]
60//! struct LogsDbConn(diesel::SqliteConnection);
61//!
62//! #[launch]
63//! fn rocket() -> _ {
64//!     rocket::build().attach(LogsDbConn::fairing())
65//! }
66//! # } fn main() {}
67//! ```
68//!
69//! Whenever a connection to the database is needed:
70//!
71//! ```rust
72//! # #[macro_use] extern crate rocket;
73//! # #[macro_use] extern crate rocket_sync_db_pools_community as rocket_sync_db_pools;
74//! #
75//! # #[cfg(feature = "diesel_sqlite_pool")]
76//! # mod test {
77//! # use rocket_sync_db_pools::diesel;
78//! #
79//! # #[database("sqlite_logs")]
80//! # struct LogsDbConn(diesel::SqliteConnection);
81//! #
82//! # type Logs = ();
83//! # type Result<T> = std::result::Result<T, ()>;
84//! #
85//! #[get("/logs/<id>")]
86//! async fn get_logs(conn: LogsDbConn, id: usize) -> Result<Logs> {
87//! # /*
88//!     conn.run(|c| Logs::by_id(c, id)).await
89//! # */
90//! # Ok(())
91//! }
92//! # } fn main() {}
93//! ```
94//!
95//! # Usage
96//!
97//! ## Configuration
98//!
99//! Databases can be configured as any other values. Using the default
100//! configuration provider, either via `Rocket.toml` or environment variables.
101//! You can also use a custom provider.
102//!
103//! ### `Rocket.toml`
104//!
105//! To configure a database via `Rocket.toml`, add a table for each database to
106//! the `databases` table where the key is a name of your choice. The table
107//! should have a `url` key and, optionally, `pool_size` and `timeout` keys.
108//! This looks as follows:
109//!
110//! ```toml
111//! # Option 1:
112//! [global.databases]
113//! sqlite_db = { url = "db.sqlite" }
114//!
115//! # Option 2:
116//! [global.databases.my_db]
117//! url = "postgres://root:root@localhost/my_db"
118//!
119//! # With `pool_size` and `timeout` keys:
120//! [global.databases.sqlite_db]
121//! url = "db.sqlite"
122//! pool_size = 20
123//! timeout = 5
124//! ```
125//!
126//! The table _requires_ one key:
127//!
128//!   * `url` - the URl to the database
129//!
130//! Additionally, all configurations accept the following _optional_ keys:
131//!
132//!   * `pool_size` - the size of the pool, i.e., the number of connections to
133//!     pool (defaults to the configured number of workers * 4)
134//!   * `timeout` - max number of seconds to wait for a connection to become
135//!     available (defaults to `5`)
136//!
137//! Additional options may be required or supported by other adapters.
138//!
139//! ### Procedurally
140//!
141//! Databases can also be configured procedurally via `rocket::custom()`.
142//! The example below does just this:
143//!
144//! ```rust
145//! # extern crate rocket_sync_db_pools_community as rocket_sync_db_pools;
146//! # #[cfg(feature = "diesel_sqlite_pool")] {
147//! # use rocket::launch;
148//! use rocket::figment::{value::{Map, Value}, util::map};
149//!
150//! #[launch]
151//! fn rocket() -> _ {
152//!     let db: Map<_, Value> = map! {
153//!         "url" => "db.sqlite".into(),
154//!         "pool_size" => 10.into(),
155//!         "timeout" => 5.into(),
156//!     };
157//!
158//!     let figment = rocket::Config::figment()
159//!         .merge(("databases", map!["my_db" => db]));
160//!
161//!     rocket::custom(figment)
162//! }
163//! # rocket();
164//! # }
165//! ```
166//!
167//! ### Environment Variables
168//!
169//! Lastly, databases can be configured via environment variables by specifying
170//! the `databases` table as detailed in the [Environment Variables
171//! configuration
172//! guide](https://rocket.rs/master/guide/configuration/#environment-variables):
173//!
174//! ```bash
175//! ROCKET_DATABASES='{my_db={url="db.sqlite"}}'
176//! ```
177//!
178//! Multiple databases can be specified in the `ROCKET_DATABASES` environment variable
179//! as well by comma separating them:
180//!
181//! ```bash
182//! ROCKET_DATABASES='{my_db={url="db.sqlite"},my_pg_db={url="postgres://root:root@localhost/my_pg_db"}}'
183//! ```
184//!
185//! ## Guard Types
186//!
187//! Once a database has been configured, the `#[database]` attribute can be used
188//! to tie a type in your application to a configured database. The database
189//! attribute accepts a single string parameter that indicates the name of the
190//! database. This corresponds to the database name set as the database's
191//! configuration key.
192//!
193//! See [`ExampleDb`](example::ExampleDb) for everything that the macro
194//! generates. Specifically, it generates:
195//!
196//!   * A [`FromRequest`] implementation for the decorated type.
197//!   * A [`Sentinel`](rocket::Sentinel) implementation for the decorated type.
198//!   * A [`fairing()`](example::ExampleDb::fairing()) method to initialize the
199//!     database.
200//!   * A [`run()`](example::ExampleDb::run()) method to execute blocking
201//!     database operations in an `async`-safe manner.
202//!   * A [`pool()`](example::ExampleDb::pool()) method to retrieve the
203//!     backing connection pool.
204//!
205//! The attribute can only be applied to tuple structs with one field. The
206//! internal type of the structure must implement [`Poolable`].
207//!
208//! ```rust
209//! # #[macro_use] extern crate rocket_sync_db_pools_community as rocket_sync_db_pools;
210//! # #[cfg(feature = "diesel_sqlite_pool")]
211//! # mod test {
212//! use rocket_sync_db_pools::diesel;
213//!
214//! #[database("my_db")]
215//! struct MyDatabase(diesel::SqliteConnection);
216//! # }
217//! ```
218//!
219//! Other databases can be used by specifying their respective [`Poolable`]
220//! type:
221//!
222//! ```rust
223//! # #[macro_use] extern crate rocket_sync_db_pools_community as rocket_sync_db_pools;
224//! # #[cfg(feature = "postgres_pool")]
225//! # mod test {
226//! use rocket_sync_db_pools::postgres;
227//!
228//! #[database("my_pg_db")]
229//! struct MyPgDatabase(postgres::Client);
230//! # }
231//! ```
232//!
233//! The fairing returned from the generated `fairing()` method _must_ be
234//! attached for the request guard implementation to succeed. Putting the pieces
235//! together, a use of the `#[database]` attribute looks as follows:
236//!
237//! ```rust
238//! # #[macro_use] extern crate rocket;
239//! # #[macro_use] extern crate rocket_sync_db_pools_community as rocket_sync_db_pools;
240//! #
241//! # #[cfg(feature = "diesel_sqlite_pool")] {
242//! # use rocket::figment::{value::{Map, Value}, util::map};
243//! use rocket_sync_db_pools::diesel;
244//!
245//! #[database("my_db")]
246//! struct MyDatabase(diesel::SqliteConnection);
247//!
248//! #[launch]
249//! fn rocket() -> _ {
250//! #   let db: Map<_, Value> = map![
251//! #        "url" => "db.sqlite".into(), "pool_size" => 10.into()
252//! #   ];
253//! #   let figment = rocket::Config::figment().merge(("databases", map!["my_db" => db]));
254//!     rocket::custom(figment).attach(MyDatabase::fairing())
255//! }
256//! # }
257//! ```
258//!
259//! ## Handlers
260//!
261//! Finally, use your type as a request guard in a handler to retrieve a
262//! connection wrapper for the database:
263//!
264//! ```rust
265//! # #[macro_use] extern crate rocket;
266//! # #[macro_use] extern crate rocket_sync_db_pools_community as rocket_sync_db_pools;
267//! #
268//! # #[cfg(feature = "diesel_sqlite_pool")]
269//! # mod test {
270//! # use rocket_sync_db_pools::diesel;
271//! #[database("my_db")]
272//! struct MyDatabase(diesel::SqliteConnection);
273//!
274//! #[get("/")]
275//! fn my_handler(conn: MyDatabase) {
276//!     // ...
277//! }
278//! # }
279//! ```
280//!
281//! A connection can be retrieved and used with the `run()` method:
282//!
283//! ```rust
284//! # #[macro_use] extern crate rocket;
285//! # #[macro_use] extern crate rocket_sync_db_pools_community as rocket_sync_db_pools;
286//! #
287//! # #[cfg(feature = "diesel_sqlite_pool")]
288//! # mod test {
289//! # use rocket_sync_db_pools::diesel;
290//! # type Data = ();
291//! #[database("my_db")]
292//! struct MyDatabase(diesel::SqliteConnection);
293//!
294//! fn load_from_db(conn: &diesel::SqliteConnection) -> Data {
295//!     // Do something with connection, return some data.
296//!     # ()
297//! }
298//!
299//! #[get("/")]
300//! async fn my_handler(mut conn: MyDatabase) -> Data {
301//!     conn.run(|c| load_from_db(c)).await
302//! }
303//! # }
304//! ```
305//!
306//! # Database Support
307//!
308//! Built-in support is provided for many popular databases and drivers. Support
309//! can be easily extended by [`Poolable`] implementations.
310//!
311//! ## Provided
312//!
313//! The list below includes all presently supported database adapters and their
314//! corresponding [`Poolable`] type.
315//!
316// Note: Keep this table in sync with site/guide/6-state.md
317//! | Kind     | Driver                | Version   | `Poolable` Type                | Feature                |
318//! |----------|-----------------------|-----------|--------------------------------|------------------------|
319//! | Sqlite   | [Diesel]              | `2`       | [`diesel::SqliteConnection`]   | `diesel_sqlite_pool`   |
320//! | Postgres | [Diesel]              | `2`       | [`diesel::PgConnection`]       | `diesel_postgres_pool` |
321//! | MySQL    | [Diesel]              | `2`       | [`diesel::MysqlConnection`]    | `diesel_mysql_pool`    |
322//! | Postgres | [Rust-Postgres]       | `0.19`    | [`postgres::Client`]           | `postgres_pool`        |
323//! | Sqlite   | [`Rusqlite`]          | `0.31`    | [`rusqlite::Connection`]       | `sqlite_pool`          |
324//! | Memcache | [`memcache`]          | `0.17`    | [`memcache::Client`]           | `memcache_pool`        |
325//!
326//! [Diesel]: https://diesel.rs
327//! [`diesel::SqliteConnection`]: https://docs.rs/diesel/2/diesel/sqlite/struct.SqliteConnection.html
328//! [`diesel::PgConnection`]: https://docs.rs/diesel/2/diesel/pg/struct.PgConnection.html
329//! [`diesel::MysqlConnection`]: https://docs.rs/diesel/2/diesel/mysql/struct.MysqlConnection.html
330//! [Rust-Postgres]: https://github.com/sfackler/rust-postgres
331//! [`postgres::Client`]: https://docs.rs/postgres/0.19/postgres/struct.Client.html
332//! [`Rusqlite`]: https://github.com/jgallagher/rusqlite
333//! [`rusqlite::Connection`]: https://docs.rs/rusqlite/0.31/rusqlite/struct.Connection.html
334//! [`diesel::PgConnection`]: http://docs.diesel.rs/diesel/pg/struct.PgConnection.html
335//! [`memcache`]: https://github.com/aisk/rust-memcache
336//! [`memcache::Client`]: https://docs.rs/memcache/0.17/memcache/struct.Client.html
337//!
338//! The above table lists all the supported database adapters in this library.
339//! In order to use particular `Poolable` type that's included in this library,
340//! you must first enable the feature listed in the "Feature" column. The
341//! interior type of your decorated database type should match the type in the
342//! "`Poolable` Type" column.
343//!
344//! ## Extending
345//!
346//! Extending Rocket's support to your own custom database adapter (or other
347//! database-like struct that can be pooled by `r2d2`) is as easy as
348//! implementing the [`Poolable`] trait. See the documentation for [`Poolable`]
349//! for more details on how to implement it.
350//!
351//! [`FromRequest`]: rocket::request::FromRequest
352//! [request guards]: rocket::request::FromRequest
353//! [`Poolable`]: crate::Poolable
354
355#![doc(html_root_url = "https://api.rocket.rs/master/rocket_sync_db_pools")]
356#![doc(html_favicon_url = "https://rocket.rs/images/favicon.ico")]
357#![doc(html_logo_url = "https://rocket.rs/images/logo-boxed.png")]
358#![cfg_attr(nightly, feature(doc_cfg))]
359
360#[doc(hidden)]
361#[macro_use]
362pub extern crate rocket;
363
364#[cfg(any(
365    feature = "diesel_sqlite_pool",
366    feature = "diesel_postgres_pool",
367    feature = "diesel_mysql_pool"
368))]
369pub use diesel;
370
371#[cfg(feature = "postgres_pool")]
372pub use postgres;
373#[cfg(feature = "postgres_pool")]
374pub use r2d2_postgres;
375
376#[cfg(feature = "sqlite_pool")]
377pub use r2d2_sqlite;
378#[cfg(feature = "sqlite_pool")]
379pub use rusqlite;
380
381#[cfg(feature = "memcache_pool")]
382pub use memcache;
383
384pub use r2d2;
385
386mod config;
387mod connection;
388mod error;
389mod poolable;
390
391pub use self::config::Config;
392pub use self::error::Error;
393pub use self::poolable::{PoolResult, Poolable};
394
395pub use self::connection::*;
396pub use rocket_sync_db_pools_codegen::*;
397
398/// Example of code generated by the `#[database]` attribute.
399#[cfg(all(nightly, doc, feature = "diesel_sqlite_pool"))]
400pub mod example {
401    use crate::diesel;
402
403    /// Example of code generated by the `#[database]` attribute.
404    ///
405    /// This implementation of `ExampleDb` was generated by:
406    ///
407    /// ```rust
408    /// # extern crate rocket_sync_db_pools_community as rocket_sync_db_pools;
409    /// use rocket_sync_db_pools::{database, diesel};
410    ///
411    /// #[database("example")]
412    /// pub struct ExampleDb(diesel::SqliteConnection);
413    /// ```
414    pub struct ExampleDb(crate::Connection<Self, diesel::SqliteConnection>);
415
416    impl ExampleDb {
417        /// Returns a fairing that initializes the database connection pool
418        /// associated with `Self`.
419        ///
420        /// The fairing _must_ be attached before `Self` can be used as a
421        /// request guard.
422        ///
423        /// # Example
424        ///
425        /// ```rust
426        /// # #[macro_use] extern crate rocket;
427        /// # #[macro_use] extern crate rocket_sync_db_pools_community as rocket_sync_db_pools;
428        /// #
429        /// # #[cfg(feature = "diesel_sqlite_pool")] {
430        /// use rocket_sync_db_pools::diesel;
431        ///
432        /// #[database("my_db")]
433        /// struct MyConn(diesel::SqliteConnection);
434        ///
435        /// #[launch]
436        /// fn rocket() -> _ {
437        ///     rocket::build().attach(MyConn::fairing())
438        /// }
439        /// # }
440        /// ```
441        pub fn fairing() -> impl crate::rocket::fairing::Fairing {
442            <crate::ConnectionPool<Self, diesel::SqliteConnection>>::fairing(
443                "'example' Database Pool",
444                "example",
445            )
446        }
447
448        /// Returns an opaque type that represents the connection pool backing
449        /// connections of type `Self` _as long as_ the fairing returned by
450        /// [`Self::fairing()`] is attached and has run on `__rocket`.
451        ///
452        /// The returned pool is `Clone`. Values of type `Self` can be retrieved
453        /// from the pool by calling `pool.get().await` which has the same
454        /// signature and semantics as [`Self::get_one()`].
455        ///
456        /// # Example
457        ///
458        /// ```rust
459        /// # #[macro_use] extern crate rocket;
460        /// # #[macro_use] extern crate rocket_sync_db_pools_community as rocket_sync_db_pools;
461        /// #
462        /// # #[cfg(feature = "diesel_sqlite_pool")] {
463        /// use rocket::tokio::{task, time};
464        /// use rocket::fairing::AdHoc;
465        /// use rocket_sync_db_pools::diesel;
466        ///
467        /// #[database("my_db")]
468        /// struct MyConn(diesel::SqliteConnection);
469        ///
470        /// #[launch]
471        /// fn rocket() -> _ {
472        ///     rocket::build()
473        ///         .attach(MyConn::fairing())
474        ///         .attach(AdHoc::try_on_ignite("Background DB", |rocket| async {
475        ///             let pool = match MyConn::pool(&rocket) {
476        ///                 Some(pool) => pool.clone(),
477        ///                 None => return Err(rocket)
478        ///             };
479        ///
480        ///             // Start a background task that runs some database
481        ///             // operation every 10 seconds. If a connection isn't
482        ///             // available, retries 10 + timeout seconds later.
483        ///             tokio::task::spawn(async move {
484        ///                 loop {
485        ///                     time::sleep(time::Duration::from_secs(10)).await;
486        ///                     if let Some(conn) = pool.get().await {
487        ///                         conn.run(|c| { /* perform db ops */ }).await;
488        ///                     }
489        ///                 }
490        ///             });
491        ///
492        ///             Ok(rocket)
493        ///         }))
494        /// }
495        /// # }
496        /// ```
497        pub fn pool<P: crate::rocket::Phase>(
498            __rocket: &crate::rocket::Rocket<P>,
499        ) -> Option<&crate::ConnectionPool<Self, diesel::SqliteConnection>> {
500            <crate::ConnectionPool<Self, diesel::SqliteConnection>>::pool(&__rocket)
501        }
502
503        /// Runs the provided function `__f` in an async-safe blocking thread.
504        /// The function is supplied with a mutable reference to the raw
505        /// connection (a value of type `&mut Self.0`). `.await`ing the return
506        /// value of this function yields the value returned by `__f`.
507        ///
508        /// # Example
509        ///
510        /// ```rust
511        /// # #[macro_use] extern crate rocket;
512        /// # #[macro_use] extern crate rocket_sync_db_pools_community as rocket_sync_db_pools;
513        /// #
514        /// # #[cfg(feature = "diesel_sqlite_pool")] {
515        /// use rocket_sync_db_pools::diesel;
516        ///
517        /// #[database("my_db")]
518        /// struct MyConn(diesel::SqliteConnection);
519        ///
520        /// #[get("/")]
521        /// async fn f(conn: MyConn) {
522        ///     // The type annotation is illustrative and isn't required.
523        ///     let result = conn.run(|c: &mut diesel::SqliteConnection| {
524        ///         // Use `c`.
525        ///     }).await;
526        /// }
527        /// # }
528        /// ```
529        pub async fn run<F, R>(&self, __f: F) -> R
530        where
531            F: FnOnce(&mut diesel::SqliteConnection) -> R + Send + 'static,
532            R: Send + 'static,
533        {
534            self.0.run(__f).await
535        }
536
537        /// Retrieves a connection of type `Self` from the `rocket` instance.
538        /// Returns `Some` as long as `Self::fairing()` has been attached and
539        /// there is a connection available within at most `timeout` seconds.
540        pub async fn get_one<P: crate::rocket::Phase>(
541            __rocket: &crate::rocket::Rocket<P>,
542        ) -> Option<Self> {
543            <crate::ConnectionPool<Self, diesel::SqliteConnection>>::get_one(&__rocket)
544                .await
545                .map(Self)
546        }
547    }
548
549    /// Retrieves a connection from the database pool or fails with a
550    /// `Status::ServiceUnavailable` if doing so times out.
551    impl<'r> crate::rocket::request::FromRequest<'r> for ExampleDb {
552        type Error = ();
553        #[allow(
554            clippy::let_unit_value,
555            clippy::no_effect_underscore_binding,
556            clippy::shadow_same,
557            clippy::type_complexity,
558            clippy::type_repetition_in_bounds,
559            clippy::used_underscore_binding
560        )]
561        fn from_request<'life0, 'async_trait>(
562            __r: &'r crate::rocket::request::Request<'life0>,
563        ) -> ::core::pin::Pin<
564            Box<
565                dyn ::core::future::Future<Output = crate::rocket::request::Outcome<Self, ()>>
566                    + ::core::marker::Send
567                    + 'async_trait,
568            >,
569        >
570        where
571            'r: 'async_trait,
572            'life0: 'async_trait,
573            Self: 'async_trait,
574        {
575            Box::pin(async move {
576                if let ::core::option::Option::Some(__ret) =
577                    ::core::option::Option::None::<crate::rocket::request::Outcome<Self, ()>>
578                {
579                    return __ret;
580                }
581                let __r = __r;
582                let __ret: crate::rocket::request::Outcome<Self, ()> = {
583                    <crate::Connection<Self, diesel::SqliteConnection>>::from_request(__r)
584                        .await
585                        .map(Self)
586                };
587                #[allow(unreachable_code)]
588                __ret
589            })
590        }
591    }
592    impl crate::rocket::Sentinel for ExampleDb {
593        fn abort(__r: &crate::rocket::Rocket<crate::rocket::Ignite>) -> bool {
594            <crate::Connection<Self, diesel::SqliteConnection>>::abort(__r)
595        }
596    }
597}