From 79a5e5e1d52dbf2167cd2d0cbfc78a217c733cf9 Mon Sep 17 00:00:00 2001 From: Jon Pacheco Date: Tue, 31 Mar 2020 12:35:51 +0100 Subject: [PATCH] Clone TODOs example for SQLite Differences to Postgres version: - Minor changes to schema - Add TODO: "RETURNING" isn't supported, so retrieve ID separately --- Cargo.lock | 12 ++++ Cargo.toml | 1 + examples/sqlite/todos/Cargo.toml | 13 ++++ examples/sqlite/todos/README.md | 27 ++++++++ examples/sqlite/todos/schema.sql | 5 ++ examples/sqlite/todos/src/main.rs | 103 ++++++++++++++++++++++++++++++ 6 files changed, 161 insertions(+) create mode 100644 examples/sqlite/todos/Cargo.toml create mode 100644 examples/sqlite/todos/README.md create mode 100644 examples/sqlite/todos/schema.sql create mode 100644 examples/sqlite/todos/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index f6e0dbc1..5fd6d459 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1765,6 +1765,18 @@ dependencies = [ "structopt", ] +[[package]] +name = "sqlx-example-sqlite-todos" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-std", + "futures 0.3.4", + "paw", + "sqlx", + "structopt", +] + [[package]] name = "sqlx-macros" version = "0.3.2" diff --git a/Cargo.toml b/Cargo.toml index 2c7f74b9..aab4fd04 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,7 @@ members = [ "examples/postgres/listen", "examples/postgres/realworld", "examples/postgres/todos", + "examples/sqlite/todos", ] [package] diff --git a/examples/sqlite/todos/Cargo.toml b/examples/sqlite/todos/Cargo.toml new file mode 100644 index 00000000..4a429473 --- /dev/null +++ b/examples/sqlite/todos/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "sqlx-example-sqlite-todos" +version = "0.1.0" +edition = "2018" +workspace = "../../../" + +[dependencies] +anyhow = "1.0" +async-std = { version = "1.5.0", features = [ "attributes" ] } +futures = "0.3" +paw = "1.0" +sqlx = { path = "../../../", features = ["sqlite"] } +structopt = { version = "0.3", features = ["paw"] } diff --git a/examples/sqlite/todos/README.md b/examples/sqlite/todos/README.md new file mode 100644 index 00000000..ebee91b3 --- /dev/null +++ b/examples/sqlite/todos/README.md @@ -0,0 +1,27 @@ +# TODOs Example + +## Usage + +Declare the database URL: + +``` +export DATABASE_URL="sqlite:///path/to/this/directory/todos.db" +``` + +Create the database: + +``` +sqlite3 todos.db +``` + +Load the database schema (using the SQLite CLI interface opened from the previous command): + +``` +sqlite> .read schema.sql +``` + +Use `.exit` to leave the SQLite CLI. Then, to run this example: + +- Add a todo: `cargo run -- add "todo description"` +- Complete a todo: `cargo run -- done ` +- List all todos: `cargo run` diff --git a/examples/sqlite/todos/schema.sql b/examples/sqlite/todos/schema.sql new file mode 100644 index 00000000..c1dcbc39 --- /dev/null +++ b/examples/sqlite/todos/schema.sql @@ -0,0 +1,5 @@ +CREATE TABLE IF NOT EXISTS todos ( + id INTEGER PRIMARY KEY NOT NULL, + description TEXT NOT NULL, + done BOOLEAN NOT NULL DEFAULT 0 +); diff --git a/examples/sqlite/todos/src/main.rs b/examples/sqlite/todos/src/main.rs new file mode 100644 index 00000000..30738fde --- /dev/null +++ b/examples/sqlite/todos/src/main.rs @@ -0,0 +1,103 @@ +use sqlx::SqlitePool; +use sqlx::sqlite::SqliteQueryAs; +use std::env; +use structopt::StructOpt; + +#[derive(StructOpt)] +struct Args { + #[structopt(subcommand)] + cmd: Option, +} + +#[derive(StructOpt)] +enum Command { + Add { description: String }, + Done { id: i64 }, +} + +#[async_std::main] +#[paw::main] +async fn main(args: Args) -> anyhow::Result<()> { + let pool = SqlitePool::new(&env::var("DATABASE_URL")?).await?; + + match args.cmd { + Some(Command::Add { description }) => { + println!("Adding new todo with description '{}'", &description); + let todo_id = add_todo(&pool, description).await?; + println!("Added new todo with id {}", todo_id); + } + Some(Command::Done { id }) => { + println!("Marking todo {} as done", id); + if complete_todo(&pool, id).await? { + println!("Todo {} is marked as done", id); + } else { + println!("Invalid id {}", id); + } + } + None => { + println!("Printing list of all todos"); + list_todos(&pool).await?; + } + } + + Ok(()) +} + +async fn add_todo(pool: &SqlitePool, description: String) -> anyhow::Result { + // Insert the TODO, then obtain the ID of this row + sqlx::query!( + r#" +INSERT INTO todos ( description ) +VALUES ( $1 ) + "#, + description + ) + .execute(pool) + .await?; + + let rec: (i64,) = sqlx::query_as( + "SELECT last_insert_rowid()" + ) + .fetch_one(pool) + .await?; + + Ok(rec.0) +} + +async fn complete_todo(pool: &SqlitePool, id: i64) -> anyhow::Result { + let rows_affected = sqlx::query!( + r#" +UPDATE todos +SET done = TRUE +WHERE id = $1 + "#, + id + ) + .execute(pool) + .await?; + + Ok(rows_affected > 0) +} + +async fn list_todos(pool: &SqlitePool) -> anyhow::Result<()> { + let recs = sqlx::query!( + r#" +SELECT id, description, done +FROM todos +ORDER BY id + "# + ) + .fetch_all(pool) + .await?; + + for rec in recs { + println!( + "- [{}] {}: {}", + if rec.done { "x" } else { " " }, + rec.id, + &rec.description, + ); + } + + Ok(()) +}