From b5c4df5d31a4624d24171ba70db567327f3aae67 Mon Sep 17 00:00:00 2001 From: Lars Wirzenius Date: Fri, 1 Jan 2021 16:21:36 +0200 Subject: refactor: move SQL use into sub-module This keeps all the SQL related functions closer together, making it easier to make changes to them. --- src/generation.rs | 150 +++++++++++++++++++++++++++++++++--------------------- 1 file changed, 92 insertions(+), 58 deletions(-) diff --git a/src/generation.rs b/src/generation.rs index 7a4b71b..5dc5f97 100644 --- a/src/generation.rs +++ b/src/generation.rs @@ -1,7 +1,6 @@ use crate::chunkid::ChunkId; -use crate::error::ObnamError; use crate::fsentry::FilesystemEntry; -use rusqlite::{params, Connection, OpenFlags, Row, Transaction}; +use rusqlite::Connection; use std::path::Path; /// A nascent backup generation. @@ -19,17 +18,7 @@ impl NascentGeneration { where P: AsRef, { - let flags = OpenFlags::SQLITE_OPEN_CREATE | OpenFlags::SQLITE_OPEN_READ_WRITE; - let conn = Connection::open_with_flags(filename, flags)?; - conn.execute( - "CREATE TABLE files (fileno INTEGER PRIMARY KEY, json TEXT)", - params![], - )?; - conn.execute( - "CREATE TABLE chunks (fileno INTEGER, chunkid TEXT)", - params![], - )?; - conn.pragma_update(None, "journal_mode", &"WAL")?; + let conn = sql::create_db(filename.as_ref())?; Ok(Self { conn, fileno: 0 }) } @@ -40,7 +29,7 @@ impl NascentGeneration { pub fn insert(&mut self, e: FilesystemEntry, ids: &[ChunkId]) -> anyhow::Result<()> { let t = self.conn.transaction()?; self.fileno += 1; - insert_one(&t, e, self.fileno, ids)?; + sql::insert_one(&t, e, self.fileno, ids)?; t.commit()?; Ok(()) } @@ -53,41 +42,13 @@ impl NascentGeneration { for r in entries { let (e, ids) = r?; self.fileno += 1; - insert_one(&t, e, self.fileno, &ids[..])?; + sql::insert_one(&t, e, self.fileno, &ids[..])?; } t.commit()?; Ok(()) } } -fn row_to_entry(row: &Row) -> rusqlite::Result<(u64, String)> { - let fileno: i64 = row.get(row.column_index("fileno")?)?; - let fileno = fileno as u64; - let json: String = row.get(row.column_index("json")?)?; - Ok((fileno, json)) -} - -fn insert_one( - t: &Transaction, - e: FilesystemEntry, - fileno: u64, - ids: &[ChunkId], -) -> anyhow::Result<()> { - let fileno = fileno as i64; - let json = serde_json::to_string(&e)?; - t.execute( - "INSERT INTO files (fileno, json) VALUES (?1, ?2)", - params![fileno, &json], - )?; - for id in ids { - t.execute( - "INSERT INTO chunks (fileno, chunkid) VALUES (?1, ?2)", - params![fileno, id], - )?; - } - Ok(()) -} - #[cfg(test)] mod test { use super::NascentGeneration; @@ -145,23 +106,98 @@ impl LocalGeneration { where P: AsRef, { + let conn = sql::open_db(filename.as_ref())?; + Ok(Self { conn }) + } + + pub fn file_count(&self) -> anyhow::Result { + Ok(sql::file_count(&self.conn)?) + } + + pub fn files(&self) -> anyhow::Result> { + Ok(sql::files(&self.conn)?) + } + + pub fn chunkids(&self, fileno: u64) -> anyhow::Result> { + Ok(sql::chunkids(&self.conn, fileno)?) + } + + pub fn get_file(&self, filename: &Path) -> anyhow::Result> { + Ok(sql::get_file(&self.conn, filename)?) + } + + pub fn get_fileno(&self, filename: &Path) -> anyhow::Result> { + Ok(sql::get_fileno(&self.conn, filename)?) + } +} + +mod sql { + use crate::chunkid::ChunkId; + use crate::error::ObnamError; + use crate::fsentry::FilesystemEntry; + use rusqlite::{params, Connection, OpenFlags, Row, Transaction}; + use std::path::Path; + + pub fn create_db(filename: &Path) -> anyhow::Result { + let flags = OpenFlags::SQLITE_OPEN_CREATE | OpenFlags::SQLITE_OPEN_READ_WRITE; + let conn = Connection::open_with_flags(filename, flags)?; + conn.execute( + "CREATE TABLE files (fileno INTEGER PRIMARY KEY, json TEXT)", + params![], + )?; + conn.execute( + "CREATE TABLE chunks (fileno INTEGER, chunkid TEXT)", + params![], + )?; + conn.pragma_update(None, "journal_mode", &"WAL")?; + Ok(conn) + } + + pub fn open_db(filename: &Path) -> anyhow::Result { let flags = OpenFlags::SQLITE_OPEN_READ_WRITE; let conn = Connection::open_with_flags(filename, flags)?; conn.pragma_update(None, "journal_mode", &"WAL")?; + Ok(conn) + } - Ok(Self { conn }) + pub fn insert_one( + t: &Transaction, + e: FilesystemEntry, + fileno: u64, + ids: &[ChunkId], + ) -> anyhow::Result<()> { + let fileno = fileno as i64; + let json = serde_json::to_string(&e)?; + t.execute( + "INSERT INTO files (fileno, json) VALUES (?1, ?2)", + params![fileno, &json], + )?; + for id in ids { + t.execute( + "INSERT INTO chunks (fileno, chunkid) VALUES (?1, ?2)", + params![fileno, id], + )?; + } + Ok(()) } - pub fn file_count(&self) -> anyhow::Result { - let mut stmt = self.conn.prepare("SELECT count(*) FROM files")?; + pub fn row_to_entry(row: &Row) -> rusqlite::Result<(u64, String)> { + let fileno: i64 = row.get(row.column_index("fileno")?)?; + let fileno = fileno as u64; + let json: String = row.get(row.column_index("json")?)?; + Ok((fileno, json)) + } + + pub fn file_count(conn: &Connection) -> anyhow::Result { + let mut stmt = conn.prepare("SELECT count(*) FROM files")?; let mut iter = stmt.query_map(params![], |row| row.get(0))?; let count = iter.next().expect("SQL count result"); let count = count?; Ok(count) } - pub fn files(&self) -> anyhow::Result> { - let mut stmt = self.conn.prepare("SELECT * FROM files")?; + pub fn files(conn: &Connection) -> anyhow::Result> { + let mut stmt = conn.prepare("SELECT * FROM files")?; let iter = stmt.query_map(params![], |row| row_to_entry(row))?; let mut files: Vec<(u64, FilesystemEntry)> = vec![]; for x in iter { @@ -172,11 +208,9 @@ impl LocalGeneration { Ok(files) } - pub fn chunkids(&self, fileno: u64) -> anyhow::Result> { + pub fn chunkids(conn: &Connection, fileno: u64) -> anyhow::Result> { let fileno = fileno as i64; - let mut stmt = self - .conn - .prepare("SELECT chunkid FROM chunks WHERE fileno = ?1")?; + let mut stmt = conn.prepare("SELECT chunkid FROM chunks WHERE fileno = ?1")?; let iter = stmt.query_map(params![fileno], |row| Ok(row.get(0)?))?; let mut ids: Vec = vec![]; for x in iter { @@ -186,25 +220,25 @@ impl LocalGeneration { Ok(ids) } - pub fn get_file(&self, filename: &Path) -> anyhow::Result> { - match self.get_file_and_fileno(filename)? { + pub fn get_file(conn: &Connection, filename: &Path) -> anyhow::Result> { + match get_file_and_fileno(conn, filename)? { None => Ok(None), Some((_, e)) => Ok(Some(e)), } } - pub fn get_fileno(&self, filename: &Path) -> anyhow::Result> { - match self.get_file_and_fileno(filename)? { + pub fn get_fileno(conn: &Connection, filename: &Path) -> anyhow::Result> { + match get_file_and_fileno(conn, filename)? { None => Ok(None), Some((id, _)) => Ok(Some(id)), } } fn get_file_and_fileno( - &self, + conn: &Connection, filename: &Path, ) -> anyhow::Result> { - let files = self.files()?; + let files = files(conn)?; let files: Vec<(u64, FilesystemEntry)> = files .iter() .filter(|(_, e)| e.pathbuf() == filename) -- cgit v1.2.1