From c3b9b68af8f4e233d48c4644c63fc4ba26118a08 Mon Sep 17 00:00:00 2001 From: "Christoph J. Scherr" Date: Tue, 25 Jun 2024 10:03:23 +0200 Subject: [PATCH] sqlite select and inserts with guards --- .gitignore | 1 + members/sqlite-demo/cats.db | Bin 16384 -> 0 bytes members/sqlite-demo/src/main.rs | 107 ++++++++++++++++++++++++++++---- 3 files changed, 96 insertions(+), 12 deletions(-) delete mode 100644 members/sqlite-demo/cats.db diff --git a/.gitignore b/.gitignore index 04ce869..47c0057 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ members/c-bindings/lib/cmake_install.cmake members/c-bindings/lib/libtest.so members/c-bindings/lib/libtest.so.1 members/c-bindings/lib/libtest.so.1.0.1 +members/sqlite-demo/data/cats.db diff --git a/members/sqlite-demo/cats.db b/members/sqlite-demo/cats.db deleted file mode 100644 index 9a80e9f2d8f866b6ae8d44d8642746959b1747cc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16384 zcmeI&Jx{_w7zgmXmH?7qHH371*dQ?)!{#I!;=-bmI5wp{Or zZG{~_yf&>bX6{R7omelrK|lZk5P$##AOHafKmY;|fWW^HSe=&+T^RfhG>tWRAbAOHafKmY;|fB*y_ z009U<00I!$1%aB`;`;n=)J=OYKmTXn|MgG*2nav`0uX=z1Rwwb2tWV=5P$##c39vW D3D1iP diff --git a/members/sqlite-demo/src/main.rs b/members/sqlite-demo/src/main.rs index 07cbd7c..b820c8f 100644 --- a/members/sqlite-demo/src/main.rs +++ b/members/sqlite-demo/src/main.rs @@ -1,34 +1,116 @@ +use std::path::PathBuf; +use std::{env, fs}; + use anyhow::Ok; /// This demo application uses a sqlite file to store some data. It does *not* use ORM (that would /// be done with the `diesel` crate.)! /// /// A very useful ressource is the /// [rust-cookbook](https://rust-lang-nursery.github.io/rust-cookbook/database/sqlite.html). -use rusqlite::{Connection, Result}; +use rusqlite::Connection; + +const DBNAME: &str = "cats.db"; +const TABLE_CAT_COLOR: &str = "cat_colors"; +const TABLE_CAT: &str = "cats"; + +fn connect() -> anyhow::Result { + let mut wd = env::current_dir()?; + let mut dbpath: Option = None; + + // does the current directory have a data/cats.db ? (the file need not exist, we can create it + // if data exists) + { + let mut wddata = wd.clone(); + wddata.push("data"); + if wddata.exists() { + println!("found {DBNAME} in {:?}", &wddata); + wddata.push(DBNAME); + dbpath = Some(wddata); + } + } + + // otherwise, does the current or any higher directories contain a cats.db ? + 'search_dir: while dbpath.is_none() { + for entry in fs::read_dir(&wd)? { + let entry = entry?; + if entry.file_name() == DBNAME { + println!("found {DBNAME} in {:?}", wd); + dbpath = Some(entry.path()); + break 'search_dir; + } + } + if !wd.pop() { + // we are at the root! + break 'search_dir; + } + } + + // if all fails, use $PWD/data/cats.db + if dbpath.is_none() { + println!( + "No {DBNAME} found, using {:?}/data/{DBNAME}", + env::current_dir()? + ); + fs::create_dir("data")?; + let mut path = env::current_dir()?; + path.push("data"); + path.push(DBNAME); + dbpath = Some(path); + } + + let conn = Connection::open(dbpath.unwrap())?; + + Ok(conn) +} fn setup(conn: &Connection) -> anyhow::Result<()> { conn.execute( - "create table if not exists cat_colors ( - id integer primary key, - name text not null unique - )", + &format!( + "CREATE TABLE IF NOT EXISTS {TABLE_CAT_COLOR} ( + id INTEGER PRIMARY KEY, + name TEXT NOT NULL UNIQUE COLLATE NOCASE + )" + ), (), )?; conn.execute( - "create table if not exists cats ( - id integer primary key, - name text not null, - color_id integer not null references cat_colors(id) - )", + &format!( + "CREATE TABLE IF NOT EXISTS {TABLE_CAT} ( + id INTEGER PRIMARY KEY, + name TEXT NOT NULL COLLATE NOCASE, + COLOR_ID INTEGER NOT NULL REFERENCES CAT_COLORS(ID) + )" + ), (), )?; Ok(()) } +/// Add a new color to the cat_colors table +/// +/// Will fail if the sql fails +/// +/// If the color already exists, will return Ok(()) +/// +/// The table stores the name in lowercase fn new_color(conn: &Connection, color: &str) -> anyhow::Result<()> { + // check if the color already exists, so we can just try adding a color and not worry if it + // already exists + + let mut stmt = conn.prepare(&format!( + "SELECT EXISTS( SELECT 1 FROM {TABLE_CAT_COLOR} WHERE name = (?1))" + ))?; + if let Some(row) = stmt.query([color.to_string()])?.next()? { + if row.get::(0)? == 1 { + // the color already exists + return Ok(()); + } + }; + + // do the inserting conn.execute( - "INSERT INTO cat_colors (name) values (?1)", + "INSERT INTO cat_colors (name) VALUES (?1)", [color.to_string()], )?; @@ -36,11 +118,12 @@ fn new_color(conn: &Connection, color: &str) -> anyhow::Result<()> { } fn main() -> anyhow::Result<()> { - let conn = Connection::open("cats.db")?; + let conn = connect()?; setup(&conn)?; new_color(&conn, "black")?; new_color(&conn, "red")?; + new_color(&conn, "white")?; Ok(()) }