diff options
| author | Ellie Huxtable <ellie@elliehuxtable.com> | 2023-03-20 09:26:54 +0000 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-03-20 09:26:54 +0000 |
| commit | dcd77749dd1fdf6b0c8183bfbdf4f97bf238ebe4 (patch) | |
| tree | 97c623911eeb52da65c2b3fd80092f2c86f3dd18 /atuin-client/src/database.rs | |
| parent | skim-demo (#695) (diff) | |
| download | atuin-dcd77749dd1fdf6b0c8183bfbdf4f97bf238ebe4.zip | |
Add history deletion (#791)
* Drop events. I'd still like to do them, but differently
* Start adding delete api stuff
* Set mailmap
* Delete delete delete
* Fix tests
* Make clippy happy
Diffstat (limited to 'atuin-client/src/database.rs')
| -rw-r--r-- | atuin-client/src/database.rs | 98 |
1 files changed, 28 insertions, 70 deletions
diff --git a/atuin-client/src/database.rs b/atuin-client/src/database.rs index b24020c0..a29dfa13 100644 --- a/atuin-client/src/database.rs +++ b/atuin-client/src/database.rs @@ -14,7 +14,6 @@ use sqlx::{ }; use super::{ - event::{Event, EventType}, history::History, ordering, settings::{FilterMode, SearchMode}, @@ -62,13 +61,14 @@ pub trait Database: Send + Sync { async fn update(&self, h: &History) -> Result<()>; async fn history_count(&self) -> Result<i64>; - async fn event_count(&self) -> Result<i64>; - async fn merge_events(&self) -> Result<i64>; async fn first(&self) -> Result<History>; async fn last(&self) -> Result<History>; async fn before(&self, timestamp: chrono::DateTime<Utc>, count: i64) -> Result<Vec<History>>; + async fn delete(&self, mut h: History) -> Result<()>; + async fn deleted(&self) -> Result<Vec<History>>; + // Yes I know, it's a lot. // Could maybe break it down to a searchparams struct or smth but that feels a little... pointless. // Been debating maybe a DSL for search? eg "before:time limit:1 the query" @@ -126,31 +126,10 @@ impl Sqlite { Ok(()) } - async fn save_event(tx: &mut sqlx::Transaction<'_, sqlx::Sqlite>, e: &Event) -> Result<()> { - let event_type = match e.event_type { - EventType::Create => "create", - EventType::Delete => "delete", - }; - - sqlx::query( - "insert or ignore into events(id, timestamp, hostname, event_type, history_id) - values(?1, ?2, ?3, ?4, ?5)", - ) - .bind(e.id.as_str()) - .bind(e.timestamp.timestamp_nanos()) - .bind(e.hostname.as_str()) - .bind(event_type) - .bind(e.history_id.as_str()) - .execute(tx) - .await?; - - Ok(()) - } - async fn save_raw(tx: &mut sqlx::Transaction<'_, sqlx::Sqlite>, h: &History) -> Result<()> { sqlx::query( - "insert or ignore into history(id, timestamp, duration, exit, command, cwd, session, hostname) - values(?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8)", + "insert or ignore into history(id, timestamp, duration, exit, command, cwd, session, hostname, deleted_at) + values(?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9)", ) .bind(h.id.as_str()) .bind(h.timestamp.timestamp_nanos()) @@ -160,6 +139,7 @@ impl Sqlite { .bind(h.cwd.as_str()) .bind(h.session.as_str()) .bind(h.hostname.as_str()) + .bind(h.deleted_at.map(|t|t.timestamp_nanos())) .execute(tx) .await?; @@ -167,6 +147,8 @@ impl Sqlite { } fn query_history(row: SqliteRow) -> History { + let deleted_at: Option<i64> = row.get("deleted_at"); + History { id: row.get("id"), timestamp: Utc.timestamp_nanos(row.get("timestamp")), @@ -176,6 +158,7 @@ impl Sqlite { cwd: row.get("cwd"), session: row.get("session"), hostname: row.get("hostname"), + deleted_at: deleted_at.map(|t| Utc.timestamp_nanos(t)), } } } @@ -184,11 +167,8 @@ impl Sqlite { impl Database for Sqlite { async fn save(&mut self, h: &History) -> Result<()> { debug!("saving history to sqlite"); - let event = Event::new_create(h); - let mut tx = self.pool.begin().await?; Self::save_raw(&mut tx, h).await?; - Self::save_event(&mut tx, &event).await?; tx.commit().await?; Ok(()) @@ -200,9 +180,7 @@ impl Database for Sqlite { let mut tx = self.pool.begin().await?; for i in h { - let event = Event::new_create(i); Self::save_raw(&mut tx, i).await?; - Self::save_event(&mut tx, &event).await?; } tx.commit().await?; @@ -227,7 +205,7 @@ impl Database for Sqlite { sqlx::query( "update history - set timestamp = ?2, duration = ?3, exit = ?4, command = ?5, cwd = ?6, session = ?7, hostname = ?8 + set timestamp = ?2, duration = ?3, exit = ?4, command = ?5, cwd = ?6, session = ?7, hostname = ?8, deleted_at = ?9 where id = ?1", ) .bind(h.id.as_str()) @@ -238,6 +216,7 @@ impl Database for Sqlite { .bind(h.cwd.as_str()) .bind(h.session.as_str()) .bind(h.hostname.as_str()) + .bind(h.deleted_at.map(|t|t.timestamp_nanos())) .execute(&self.pool) .await?; @@ -338,49 +317,15 @@ impl Database for Sqlite { Ok(res) } - async fn event_count(&self) -> Result<i64> { - let res: i64 = sqlx::query_scalar("select count(1) from events") - .fetch_one(&self.pool) + async fn deleted(&self) -> Result<Vec<History>> { + let res = sqlx::query("select * from history where deleted_at is not null") + .map(Self::query_history) + .fetch_all(&self.pool) .await?; Ok(res) } - // Ensure that we have correctly merged the event log - async fn merge_events(&self) -> Result<i64> { - // Ensure that we do not have more history locally than we do events. - // We can think of history as the merged log of events. There should never be more history than - // events, and the only time this could happen is if someone is upgrading from an old Atuin version - // from before we stored events. - let history_count = self.history_count().await?; - let event_count = self.event_count().await?; - - if history_count > event_count { - // pass an empty context, because with global listing we don't care - let no_context = Context { - cwd: String::from(""), - session: String::from(""), - hostname: String::from(""), - }; - - // We're just gonna load everything into memory here. That sucks, I know, sorry. - // But also even if you have a LOT of history that should be fine, and we're only going to be doing this once EVER. - let all_the_history = self - .list(FilterMode::Global, &no_context, None, false) - .await?; - - let mut tx = self.pool.begin().await?; - for i in all_the_history.iter() { - // A CREATE for every single history item is to be expected. - let event = Event::new_create(i); - Self::save_event(&mut tx, &event).await?; - } - tx.commit().await?; - } - - Ok(0) - } - async fn history_count(&self) -> Result<i64> { let res: (i64,) = sqlx::query_as("select count(1) from history") .fetch_one(&self.pool) @@ -528,6 +473,18 @@ impl Database for Sqlite { Ok(res) } + + // deleted_at doesn't mean the actual time that the user deleted it, + // but the time that the system marks it as deleted + async fn delete(&self, mut h: History) -> Result<()> { + let now = chrono::Utc::now(); + h.command = String::from(""); // blank it + h.deleted_at = Some(now); // delete it + + self.update(&h).await?; // save it + + Ok(()) + } } #[cfg(test)] @@ -585,6 +542,7 @@ mod test { 1, Some("beep boop".to_string()), Some("booop".to_string()), + None, ); db.save(&history).await } |
