X-Git-Url: http://git.euphorik.ch/?p=recipes.git;a=blobdiff_plain;f=backend%2Fsrc%2Fdata%2Fdb.rs;fp=backend%2Fsrc%2Fdata%2Fdb.rs;h=5652ff38aff4bb68748146f4838ad5884a001cc6;hp=f16487d8707955f0d61cd07fb81f59b79cdf66f1;hb=cbe276fc0601041b13087a6ffd80c5b126dfbe59;hpb=642dd8a80ce2e1212b8e30c1edabb32bdb416cfc diff --git a/backend/src/data/db.rs b/backend/src/data/db.rs index f16487d..5652ff3 100644 --- a/backend/src/data/db.rs +++ b/backend/src/data/db.rs @@ -3,7 +3,7 @@ use std::{fmt, fs::{self, File}, path::Path, io::Read}; use itertools::Itertools; use chrono::{prelude::*, Duration}; use rusqlite::{named_params, OptionalExtension, params, Params}; -use r2d2::Pool; +use r2d2::{Pool, PooledConnection}; use r2d2_sqlite::SqliteConnectionManager; use rand::distributions::{Alphanumeric, DistString}; @@ -109,11 +109,19 @@ impl Connection { Ok(connection) } + fn get(&self) -> Result> { + let con = self.pool.get()?; + con.pragma_update(None, "synchronous", "NORMAL")?; + Ok(con) + } + /// Called after the connection has been established for creating or updating the database. /// The 'Version' table tracks the current state of the database. fn create_or_update_db(&self) -> Result<()> { // Check the Database version. - let mut con = self.pool.get()?; + let mut con = self.get()?; + con.pragma_update(None, "journal_mode", "WAL")?; + let tx = con.transaction()?; // Version 0 corresponds to an empty database. @@ -174,7 +182,7 @@ impl Connection { } pub fn get_all_recipe_titles(&self) -> Result> { - let con = self.pool.get()?; + let con = self.get()?; let mut stmt = con.prepare("SELECT [id], [title] FROM [Recipe] ORDER BY [title]")?; @@ -188,7 +196,7 @@ impl Connection { /* Not used for the moment. pub fn get_all_recipes(&self) -> Result> { - let con = self.pool.get()?; + let con = self.get()?; let mut stmt = con.prepare("SELECT [id], [title] FROM [Recipe] ORDER BY [title]")?; let recipes = stmt.query_map([], |row| { @@ -198,14 +206,14 @@ impl Connection { } */ pub fn get_recipe(&self, id: i64) -> Result { - let con = self.pool.get()?; + let con = self.get()?; con.query_row("SELECT [id], [title], [description] FROM [Recipe] WHERE [id] = ?1", [id], |row| { Ok(model::Recipe::new(row.get("id")?, row.get("title")?, row.get("description")?)) }).map_err(DBError::from) } pub fn get_user_login_info(&self, token: &str) -> Result { - let con = self.pool.get()?; + let con = self.get()?; con.query_row("SELECT [last_login_datetime], [ip], [user_agent] FROM [UserLoginToken] WHERE [token] = ?1", [token], |r| { Ok(UserLoginInfo { last_login_datetime: r.get("last_login_datetime")?, @@ -216,7 +224,7 @@ impl Connection { } pub fn load_user(&self, user_id: i64) -> Result { - let con = self.pool.get()?; + let con = self.get()?; con.query_row("SELECT [email] FROM [User] WHERE [id] = ?1", [user_id], |r| { Ok(User { email: r.get("email")?, @@ -229,7 +237,7 @@ impl Connection { } fn sign_up_with_given_time(&self, email: &str, password: &str, datetime: DateTime) -> Result { - let mut con = self.pool.get()?; + let mut con = self.get()?; let tx = con.transaction()?; let token = match tx.query_row("SELECT [id], [validation_token] FROM [User] WHERE [email] = ?1", [email], |r| { @@ -256,7 +264,7 @@ impl Connection { } pub fn validation(&self, token: &str, validation_time: Duration, ip: &str, user_agent: &str) -> Result { - let mut con = self.pool.get()?; + let mut con = self.get()?; let tx = con.transaction()?; let user_id = match tx.query_row("SELECT [id], [creation_datetime] FROM [User] WHERE [validation_token] = ?1", [token], |r| { @@ -279,7 +287,7 @@ impl Connection { } pub fn sign_in(&self, email: &str, password: &str, ip: &str, user_agent: &str) -> Result { - let mut con = self.pool.get()?; + let mut con = self.get()?; let tx = con.transaction()?; match tx.query_row("SELECT [id], [password], [validation_token] FROM [User] WHERE [email] = ?1", [email], |r| { Ok((r.get::<&str, i64>("id")?, r.get::<&str, String>("password")?, r.get::<&str, Option>("validation_token")?)) @@ -302,7 +310,7 @@ impl Connection { } pub fn authentication(&self, token: &str, ip: &str, user_agent: &str) -> Result { - let mut con = self.pool.get()?; + let mut con = self.get()?; let tx = con.transaction()?; match tx.query_row("SELECT [id], [user_id] FROM [UserLoginToken] WHERE [token] = ?1", [token], |r| { Ok((r.get::<&str, i64>("id")?, r.get::<&str, i64>("user_id")?)) @@ -318,7 +326,7 @@ impl Connection { } pub fn sign_out(&self, token: &str) -> Result<()> { - let mut con = self.pool.get()?; + let mut con = self.get()?; let tx = con.transaction()?; match tx.query_row("SELECT [id] FROM [UserLoginToken] WHERE [token] = ?1", [token], |r| { Ok(r.get::<&str, i64>("id")?) @@ -333,7 +341,7 @@ impl Connection { } pub fn create_recipe(&self, user_id: i64) -> Result { - let con = self.pool.get()?; + let con = self.get()?; // Verify if an empty recipe already exists. Returns its id if one exists. match con.query_row( @@ -355,13 +363,18 @@ impl Connection { } pub fn set_recipe_title(&self, recipe_id: i64, title: &str) -> Result<()> { - let con = self.pool.get()?; + let con = self.get()?; con.execute("UPDATE [Recipe] SET [title] = ?2 WHERE [id] = ?1", params![recipe_id, title]).map(|_n| ()).map_err(DBError::from) } + pub fn set_recipe_description(&self, recipe_id: i64, description: &str) -> Result<()> { + let con = self.get()?; + con.execute("UPDATE [Recipe] SET [description] = ?2 WHERE [id] = ?1", params![recipe_id, description]).map(|_n| ()).map_err(DBError::from) + } + /// Execute a given SQL file. pub fn execute_file + fmt::Display>(&self, file: P) -> Result<()> { - let con = self.pool.get()?; + let con = self.get()?; let sql = load_sql_file(file)?; con.execute_batch(&sql).map_err(DBError::from) } @@ -369,7 +382,7 @@ impl Connection { /// Execute any SQL statement. /// Mainly used for testing. pub fn execute_sql(&self, sql: &str, params: P) -> Result { - let con = self.pool.get()?; + let con = self.get()?; con.execute(sql, params).map_err(DBError::from) } @@ -400,7 +413,7 @@ mod tests { #[test] fn sign_up() -> Result<()> { let connection = Connection::new_in_memory()?; - match connection.sign_up("paul@test.org", "12345")? { + match connection.sign_up("paul@atreides.com", "12345")? { SignUpResult::UserCreatedWaitingForValidation(_) => (), // Nominal case. other => panic!("{:?}", other), } @@ -414,13 +427,13 @@ mod tests { INSERT INTO [User] ([id], [email], [name], [password], [creation_datetime], [validation_token]) VALUES ( 1, - 'paul@test.org', + 'paul@atreides.com', 'paul', '$argon2id$v=19$m=4096,t=3,p=1$1vtXcacYjUHZxMrN6b2Xng$wW8Z59MIoMcsIljnjHmxn3EBcc5ymEySZPUVXHlRxcY', 0, NULL );", [])?; - match connection.sign_up("paul@test.org", "12345")? { + match connection.sign_up("paul@atreides.com", "12345")? { SignUpResult::UserAlreadyExists => (), // Nominal case. other => panic!("{:?}", other), } @@ -431,7 +444,7 @@ mod tests { fn sign_up_and_sign_in_without_validation() -> Result<()> { let connection = Connection::new_in_memory()?; - let email = "paul@test.org"; + let email = "paul@atreides.com"; let password = "12345"; match connection.sign_up(email, password)? { @@ -455,13 +468,13 @@ mod tests { INSERT INTO [User] ([id], [email], [name], [password], [creation_datetime], [validation_token]) VALUES ( 1, - 'paul@test.org', + 'paul@atreides.com', 'paul', '$argon2id$v=19$m=4096,t=3,p=1$1vtXcacYjUHZxMrN6b2Xng$wW8Z59MIoMcsIljnjHmxn3EBcc5ymEySZPUVXHlRxcY', 0, :token );", named_params! { ":token": token })?; - match connection.sign_up("paul@test.org", "12345")? { + match connection.sign_up("paul@atreides.com", "12345")? { SignUpResult::UserCreatedWaitingForValidation(_) => (), // Nominal case. other => panic!("{:?}", other), } @@ -472,7 +485,7 @@ mod tests { fn sign_up_then_send_validation_at_time() -> Result<()> { let connection = Connection::new_in_memory()?; let validation_token = - match connection.sign_up("paul@test.org", "12345")? { + match connection.sign_up("paul@atreides.com", "12345")? { SignUpResult::UserCreatedWaitingForValidation(token) => token, // Nominal case. other => panic!("{:?}", other), }; @@ -487,7 +500,7 @@ mod tests { fn sign_up_then_send_validation_too_late() -> Result<()> { let connection = Connection::new_in_memory()?; let validation_token = - match connection.sign_up_with_given_time("paul@test.org", "12345", Utc::now() - Duration::days(1))? { + match connection.sign_up_with_given_time("paul@atreides.com", "12345", Utc::now() - Duration::days(1))? { SignUpResult::UserCreatedWaitingForValidation(token) => token, // Nominal case. other => panic!("{:?}", other), }; @@ -502,7 +515,7 @@ mod tests { fn sign_up_then_send_validation_with_bad_token() -> Result<()> { let connection = Connection::new_in_memory()?; let _validation_token = - match connection.sign_up("paul@test.org", "12345")? { + match connection.sign_up("paul@atreides.com", "12345")? { SignUpResult::UserCreatedWaitingForValidation(token) => token, // Nominal case. other => panic!("{:?}", other), }; @@ -518,7 +531,7 @@ mod tests { fn sign_up_then_send_validation_then_sign_in() -> Result<()> { let connection = Connection::new_in_memory()?; - let email = "paul@test.org"; + let email = "paul@atreides.com"; let password = "12345"; // Sign up. @@ -547,7 +560,7 @@ mod tests { fn sign_up_then_send_validation_then_authentication() -> Result<()> { let connection = Connection::new_in_memory()?; - let email = "paul@test.org"; + let email = "paul@atreides.com"; let password = "12345"; // Sign up. @@ -587,7 +600,7 @@ mod tests { fn sign_up_then_send_validation_then_sign_out_then_sign_in() -> Result<()> { let connection = Connection::new_in_memory()?; - let email = "paul@test.org"; + let email = "paul@atreides.com"; let password = "12345"; // Sign up.