remove files database

This commit is contained in:
Nikita Galaiko 2023-09-13 13:53:59 +02:00 committed by GitButler
parent 3baa937129
commit 33cf7e7e63
9 changed files with 53 additions and 274 deletions

View File

@ -6,7 +6,7 @@ use tauri::{AppHandle, Manager};
use tokio::task;
use crate::{
bookmarks, deltas, files, gb_repository,
bookmarks, deltas, gb_repository,
git::{self, diff},
keys,
project_repository::{self, activity, conflicts},
@ -24,7 +24,6 @@ pub struct App {
searcher: search::Searcher,
watchers: watcher::Watchers,
sessions_database: sessions::Database,
files_database: files::Database,
deltas_database: deltas::Database,
bookmarks_database: bookmarks::Database,
}
@ -55,7 +54,6 @@ impl TryFrom<&AppHandle> for App {
watchers: value.state::<watcher::Watchers>().inner().clone(),
sessions_database: sessions::Database::try_from(value)?,
deltas_database: deltas::Database::try_from(value)?,
files_database: files::Database::try_from(value)?,
bookmarks_database: bookmarks::Database::try_from(value)?,
})
}
@ -242,8 +240,19 @@ impl App {
session_id: &str,
paths: Option<Vec<&str>>,
) -> Result<HashMap<String, String>> {
self.files_database
.list_by_project_id_session_id(project_id, session_id, paths)
let session = self
.sessions_database
.get_by_project_id_id(project_id, session_id)
.context("failed to get session")?
.context("session not found")?;
let gb_repo = self
.gb_repository(project_id)
.context("failed to open repository")?;
let session_reader =
sessions::Reader::open(&gb_repo, &session).context("failed to open session reader")?;
session_reader
.files(paths)
.context("failed to read session files")
}
pub fn mark_resolved(&self, project_id: &str, path: &str) -> Result<()> {

View File

@ -0,0 +1 @@
CREATE INDEX `sessions_project_id_id_index` ON `sessions` (`project_id`, `id`);

View File

@ -0,0 +1,2 @@
DROP TABLE files;
DROP TABLE contents;

View File

@ -1,228 +0,0 @@
use std::collections::HashMap;
use anyhow::{Context, Result};
use sha1::{Digest, Sha1};
use tauri::{AppHandle, Manager};
use crate::database;
#[derive(Clone)]
pub struct Database {
database: database::Database,
}
impl From<database::Database> for Database {
fn from(database: database::Database) -> Self {
Self { database }
}
}
impl From<&AppHandle> for Database {
fn from(value: &AppHandle) -> Self {
Self::from(value.state::<database::Database>().inner().clone())
}
}
impl Database {
pub fn insert(
&self,
project_id: &str,
session_id: &str,
file_path: &str,
content: &str,
) -> Result<()> {
let mut hasher = Sha1::new();
hasher.update(content);
let sha1 = hasher.finalize();
self.database.transaction(|tx| -> Result<()> {
let mut stmt = is_content_exist_by_sha1_stmt(tx)
.context("Failed to prepare is_content_exist_by_sha1 statement")?;
let mut rows = stmt
.query(rusqlite::named_params! {
":sha1": sha1.as_slice(),
})
.context("Failed to execute is_content_exist_by_sha1 statement")?;
let is_content_exist: bool = rows
.next()
.context("Failed to iterate over is_content_exist_by_sha1 results")?
.is_some();
if !is_content_exist {
let mut stmt =
insert_content_stmt(tx).context("Failed to prepare insert statement")?;
stmt.execute(rusqlite::named_params! {
":sha1": sha1.as_slice(),
":content": content,
})
.context("Failed to execute insert statement")?;
}
let mut stmt =
insert_file_stmt(tx).context("Failed to prepare insert file statement")?;
stmt.execute(rusqlite::named_params! {
":project_id": project_id,
":session_id": session_id,
":file_path": file_path,
":sha1": sha1.as_slice(),
})
.context("Failed to execute insert statement")?;
Ok(())
})?;
Ok(())
}
pub fn list_by_project_id_session_id(
&self,
project_id: &str,
session_id: &str,
file_path_filter: Option<Vec<&str>>,
) -> Result<HashMap<String, String>> {
self.database
.transaction(|tx| -> Result<HashMap<String, String>> {
let mut stmt = list_by_project_id_session_id_stmt(tx)
.context("Failed to prepare list_by_session_id statement")?;
let mut rows = stmt
.query(rusqlite::named_params! {
":project_id": project_id,
":session_id": session_id,
})
.context("Failed to execute list_by_session_id statement")?;
let mut files = HashMap::new();
while let Some(row) = rows
.next()
.context("Failed to iterate over list_by_session_id results")?
{
let file_path: String = row.get(0)?;
if let Some(file_path_filter) = &file_path_filter {
if !file_path_filter.contains(&file_path.as_str()) {
continue;
}
}
let content: String = row.get(1)?;
files.insert(file_path, content);
}
Ok(files)
})
}
}
fn list_by_project_id_session_id_stmt<'conn>(
tx: &'conn rusqlite::Transaction,
) -> Result<rusqlite::CachedStatement<'conn>> {
Ok(tx.prepare_cached(
"SELECT `file_path`, `content`
FROM `files`
JOIN `contents` ON `files`.`sha1` = `contents`.`sha1`
WHERE `project_id` = :project_id AND `session_id` = :session_id",
)?)
}
fn is_content_exist_by_sha1_stmt<'conn>(
tx: &'conn rusqlite::Transaction,
) -> Result<rusqlite::CachedStatement<'conn>> {
Ok(tx.prepare_cached(
"SELECT 1
FROM `contents`
WHERE `sha1` = :sha1",
)?)
}
fn insert_content_stmt<'conn>(
tx: &'conn rusqlite::Transaction,
) -> Result<rusqlite::CachedStatement<'conn>> {
Ok(tx.prepare_cached(
"INSERT INTO `contents` (
`sha1`, `content`
) VALUES (
:sha1, :content
)",
)?)
}
fn insert_file_stmt<'conn>(
tx: &'conn rusqlite::Transaction,
) -> Result<rusqlite::CachedStatement<'conn>> {
Ok(tx.prepare_cached(
"INSERT INTO `files` (
`project_id`, `session_id`, `file_path`, `sha1`
) VALUES (
:project_id, :session_id, :file_path, :sha1
) ON CONFLICT(`project_id`, `session_id`, `file_path`)
DO UPDATE SET `sha1` = :sha1",
)?)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_insert_query_with_filter() -> Result<()> {
let db = database::Database::memory()?;
let database = Database::from(db);
let project_id = "project_id";
let session_id = "session_id";
let file_path = "file_path";
let file = "file";
database
.insert(project_id, session_id, file_path, file)
.context("Failed to insert file")?;
assert_eq!(
database
.list_by_project_id_session_id(project_id, session_id, Some(vec!["file_path"]))
.context("filed to list by session id")?,
{
let mut files = HashMap::new();
files.insert(String::from(file_path), file.to_string());
files
}
);
assert_eq!(
database
.list_by_project_id_session_id(project_id, session_id, Some(vec!["file_path2"]))
.context("filed to list by session id")?,
HashMap::new()
);
Ok(())
}
#[test]
fn test_upsert() -> Result<()> {
let db = database::Database::memory()?;
let database = Database::from(db);
let project_id = "project_id";
let session_id = "session_id";
let file_path = "file_path";
let file = "file";
database
.insert(project_id, session_id, file_path, file)
.context("Failed to insert file1")?;
let file2 = "file2";
database
.insert(project_id, session_id, file_path, file2)
.context("Failed to insert file2")?;
assert_eq!(
database
.list_by_project_id_session_id(project_id, session_id, None)
.context("filed to list by session id")?,
{
let mut files = HashMap::new();
files.insert(String::from(file_path), file2.to_string());
files
}
);
Ok(())
}
}

View File

@ -1,3 +0,0 @@
mod database;
pub use database::Database;

View File

@ -7,7 +7,6 @@ pub mod dedup;
pub mod deltas;
pub mod error;
pub mod events;
pub mod files;
pub mod fs;
pub mod gb_repository;
pub mod git;

View File

@ -79,6 +79,31 @@ impl Database {
})
}
pub fn get_by_project_id_id(
&self,
project_id: &str,
id: &str,
) -> Result<Option<session::Session>> {
self.database.transaction(|tx| {
let mut stmt = get_by_project_id_id_stmt(tx)
.context("Failed to prepare get_by_project_id_id statement")?;
let mut rows = stmt
.query(rusqlite::named_params! {
":project_id": project_id,
":id": id,
})
.context("Failed to execute get_by_project_id_id statement")?;
if let Some(row) = rows
.next()
.context("Failed to iterate over get_by_project_id_id results")?
{
Ok(Some(parse_row(row)?))
} else {
Ok(None)
}
})
}
pub fn get_by_id(&self, id: &str) -> Result<Option<session::Session>> {
self.database.transaction(|tx| {
let mut stmt = get_by_id_stmt(tx).context("Failed to prepare get_by_id statement")?;
@ -128,6 +153,14 @@ fn list_by_project_id_stmt<'conn>(
)?)
}
fn get_by_project_id_id_stmt<'conn>(
tx: &'conn rusqlite::Transaction,
) -> Result<rusqlite::CachedStatement<'conn>> {
Ok(tx.prepare_cached(
"SELECT `id`, `project_id`, `hash`, `branch`, `commit`, `start_timestamp_ms`, `last_timestamp_ms` FROM `sessions` WHERE `project_id` = :project_id AND `id` = :id",
)?)
}
fn get_by_id_stmt<'conn>(
tx: &'conn rusqlite::Transaction,
) -> Result<rusqlite::CachedStatement<'conn>> {

View File

@ -4,8 +4,7 @@ use anyhow::{Context, Result};
use tauri::{AppHandle, Manager};
use crate::{
bookmarks, deltas, events as app_events, files, gb_repository, projects, search, sessions,
users,
bookmarks, deltas, events as app_events, gb_repository, projects, search, sessions, users,
};
use super::events;
@ -16,7 +15,6 @@ pub struct Handler {
project_store: projects::Storage,
user_store: users::Storage,
deltas_searcher: search::Searcher,
files_database: files::Database,
sessions_database: sessions::Database,
deltas_database: deltas::Database,
bookmarks_database: bookmarks::Database,
@ -35,7 +33,6 @@ impl TryFrom<&AppHandle> for Handler {
project_store: projects::Storage::try_from(value)?,
user_store: users::Storage::try_from(value)?,
deltas_searcher: value.state::<search::Searcher>().inner().clone(),
files_database: files::Database::try_from(value)?,
sessions_database: sessions::Database::try_from(value)?,
deltas_database: deltas::Database::try_from(value)?,
bookmarks_database: bookmarks::Database::try_from(value)?,
@ -57,19 +54,6 @@ impl Handler {
Ok(vec![])
}
pub fn index_file(
&self,
project_id: &str,
session_id: &str,
file_path: &str,
content: &str,
) -> Result<Vec<events::Event>> {
self.files_database
.insert(project_id, session_id, file_path, content)
.context("failed to insert file into database")?;
Ok(vec![])
}
pub fn index_bookmark(
&self,
project_id: &str,
@ -145,14 +129,6 @@ impl Handler {
project_id, session,
))];
for (file_path, content) in session_reader
.files(None)
.context("could not list files for session")?
{
let file_events = self.index_file(project_id, &session.id, &file_path, &content)?;
events.extend(file_events);
}
let deltas_reader = deltas::Reader::new(&session_reader);
for (file_path, deltas) in deltas_reader
.read(None)

View File

@ -92,22 +92,12 @@ impl Handler {
.context("failed to handle flush session event"),
events::Event::SessionFile((project_id, session_id, file_path, contents)) => {
let mut events = self
.index_handler
.index_file(
project_id,
session_id,
file_path.to_str().unwrap(),
contents,
)
.context("failed to index file")?;
events.push(events::Event::Emit(app_events::Event::file(
Ok(vec![events::Event::Emit(app_events::Event::file(
project_id,
session_id,
&file_path.display().to_string(),
contents,
)));
Ok(events)
))])
}
events::Event::Session(project_id, session) => self