diff --git a/examples/databases/.sqlx/query-3c289da9873097a11191dbedc5c78d86afd6a6d36771bfeb12f331abca6279cf.json b/examples/databases/.sqlx/query-3c289da9873097a11191dbedc5c78d86afd6a6d36771bfeb12f331abca6279cf.json deleted file mode 100644 index a78e822a..00000000 --- a/examples/databases/.sqlx/query-3c289da9873097a11191dbedc5c78d86afd6a6d36771bfeb12f331abca6279cf.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "db_name": "SQLite", - "query": "INSERT INTO posts (title, text) VALUES (?, ?)", - "describe": { - "columns": [], - "parameters": { - "Right": 2 - }, - "nullable": [] - }, - "hash": "3c289da9873097a11191dbedc5c78d86afd6a6d36771bfeb12f331abca6279cf" -} diff --git a/examples/databases/.sqlx/query-bea4ef6e25064f6b383e854f8bc2770d89cfaf9859d0bfca78b2ca24627675b7.json b/examples/databases/.sqlx/query-bea4ef6e25064f6b383e854f8bc2770d89cfaf9859d0bfca78b2ca24627675b7.json new file mode 100644 index 00000000..7aad158e --- /dev/null +++ b/examples/databases/.sqlx/query-bea4ef6e25064f6b383e854f8bc2770d89cfaf9859d0bfca78b2ca24627675b7.json @@ -0,0 +1,20 @@ +{ + "db_name": "SQLite", + "query": "INSERT INTO posts (title, text) VALUES (?, ?) RETURNING id", + "describe": { + "columns": [ + { + "name": "id", + "ordinal": 0, + "type_info": "Int64" + } + ], + "parameters": { + "Right": 2 + }, + "nullable": [ + false + ] + }, + "hash": "bea4ef6e25064f6b383e854f8bc2770d89cfaf9859d0bfca78b2ca24627675b7" +} diff --git a/examples/databases/Cargo.toml b/examples/databases/Cargo.toml index 63b50d17..2ed4fa5a 100644 --- a/examples/databases/Cargo.toml +++ b/examples/databases/Cargo.toml @@ -7,7 +7,7 @@ publish = false [dependencies] rocket = { path = "../../core/lib", features = ["json"] } -diesel = "2" +diesel = { version = "2", features = ["returning_clauses_for_sqlite_3_35"] } diesel_migrations = "2" [dependencies.sqlx] diff --git a/examples/databases/README.md b/examples/databases/README.md index 43dcdb8f..f9505bf6 100644 --- a/examples/databases/README.md +++ b/examples/databases/README.md @@ -1,8 +1,69 @@ # Databases Example -This example makes use of SQLite. You'll need `sqlite3` and its development -headers installed: +This example makes use of SQLite and MySQL. You'll need `sqlite3` and a MySQL +client installed: - * **macOS:** `brew install sqlite` - * **Debian**, **Ubuntu:** `apt-get install libsqlite3-dev` - * **Arch:** `pacman -S sqlite` + * **macOS:** `brew install sqlite mysql-client` + * **Debian**, **Ubuntu:** `apt-get install libsqlite3-dev libmysqlclient-dev` + * **Arch:** `pacman -S sqlite libmysqlclient` + +## API Implementation + +This example implements a JSON-based HTTP API for a "blog" using several database drivers: + + * `sqlx` (`/sqlx`, `sqlx.rs`) + * `rusqlite` (`/rusqlite`, `rusqlite.rs`) + * `diesel` (sqlite) (`/diesel`, `diesel_sqlite.rs`) + * `diesel-async` (mysql) (`/diesel-async`, `diesel_mysql.rs`) + +The exposed API is succinctly described as follows, with +[`httpie`](https://httpie.io/) CLI examples: + + * `POST /driver`: create post via JSON with `title` and `text`; returns new + post JSON with new `id` + + http http://127.0.0.1:8000/sqlx title="Title" text="Hello, world." + > { "id": 2128, "text": "Hello, world.", "title": "Title" } + + * `GET /driver`: returns JSON array of IDs for blog posts + + http http://127.0.0.1:8000/sqlx + > [ 2128, 2129, 2130, 2131 ] + + * `GET /driver/`: returns a JSON object for the post with id `` + + http http://127.0.0.1:8000/sqlx/2128 + > { "id": 2128, "text": "Hello, world.", "title": "Title" } + + * `DELETE /driver`: delete all posts + + http delete http://127.0.0.1:8000/sqlx + + * `DELETE /driver/`: delete post with id `` + + http delete http://127.0.0.1:8000/sqlx/4 + +## Migrations + +Database migrations are stored in the respective `db/${driver}` directory. + +### `diesel` + +Diesel migrations are found in `db/diesel/migrations`. They are run +automatically. They can be run manually as well: + +```sh +cargo install diesel_cli --no-default-features --features sqlite +DATABASE_URL="db/diesel/db.sqlite" diesel migration --migration-dir db/diesel/migrations redo +``` + +### `sqlx` + +sqlx migrations are found in `db/sqlx/migrations`. They are run automatically. + +Query metadata for offline checking was prepared with the following commands: + +```sh +cargo install sqlx-cli --no-default-features --features sqlite +DATABASE_URL="sqlite:$(pwd)/db/sqlx/db.sqlite" cargo sqlx prepare +``` diff --git a/examples/databases/src/diesel_mysql.rs b/examples/databases/src/diesel_mysql.rs index 6c6c7751..a09db8b1 100644 --- a/examples/databases/src/diesel_mysql.rs +++ b/examples/databases/src/diesel_mysql.rs @@ -92,6 +92,6 @@ async fn destroy(mut db: Connection) -> Result<()> { pub fn stage() -> AdHoc { AdHoc::on_ignite("Diesel SQLite Stage", |rocket| async { rocket.attach(Db::init()) - .mount("/diesel-async/", routes![list, read, create, delete, destroy]) + .mount("/diesel-async", routes![list, read, create, delete, destroy]) }) } diff --git a/examples/databases/src/diesel_sqlite.rs b/examples/databases/src/diesel_sqlite.rs index 1427729a..8e5a9da4 100644 --- a/examples/databases/src/diesel_sqlite.rs +++ b/examples/databases/src/diesel_sqlite.rs @@ -32,14 +32,16 @@ table! { } #[post("/", data = "")] -async fn create(db: Db, post: Json) -> Result>> { +async fn create(db: Db, mut post: Json) -> Result>> { let post_value = post.clone(); - db.run(move |conn| { + let id: Option = db.run(move |conn| { diesel::insert_into(posts::table) .values(&*post_value) - .execute(conn) + .returning(posts::id) + .get_result(conn) }).await?; + post.id = Some(id.expect("returning guarantees id present")); Ok(Created::new("/").body(post)) } diff --git a/examples/databases/src/rusqlite.rs b/examples/databases/src/rusqlite.rs index 09082467..92674e27 100644 --- a/examples/databases/src/rusqlite.rs +++ b/examples/databases/src/rusqlite.rs @@ -22,13 +22,15 @@ struct Post { type Result> = std::result::Result; #[post("/", data = "")] -async fn create(db: Db, post: Json) -> Result>> { +async fn create(db: Db, mut post: Json) -> Result>> { let item = post.clone(); - db.run(move |conn| { - conn.execute("INSERT INTO posts (title, text) VALUES (?1, ?2)", - params![item.title, item.text]) + let id = db.run(move |conn| { + conn.query_row("INSERT INTO posts (title, text) VALUES (?1, ?2) RETURNING id", + params![item.title, item.text], + |r| r.get(0)) }).await?; + post.id = Some(id); Ok(Created::new("/").body(post)) } diff --git a/examples/databases/src/sqlx.rs b/examples/databases/src/sqlx.rs index 0f0ca67b..0eb6a30f 100644 --- a/examples/databases/src/sqlx.rs +++ b/examples/databases/src/sqlx.rs @@ -23,12 +23,13 @@ struct Post { } #[post("/", data = "")] -async fn create(mut db: Connection, post: Json) -> Result>> { - // There is no support for `RETURNING`. - sqlx::query!("INSERT INTO posts (title, text) VALUES (?, ?)", post.title, post.text) - .execute(&mut **db) - .await?; +async fn create(mut db: Connection, mut post: Json) -> Result>> { + let query = sqlx::query! { + "INSERT INTO posts (title, text) VALUES (?, ?) RETURNING id", + post.title, post.text + }; + post.id = Some(query.fetch_one(&mut **db).await?.id); Ok(Created::new("/").body(post)) }