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