This commit is contained in:
Ellie Huxtable 2023-05-31 22:44:36 +01:00
parent f499ae84ed
commit 5e79c02f78
7 changed files with 114 additions and 0 deletions

View File

@ -0,0 +1,10 @@
-- Add migration script here
create table if not exists records (
id text primary key,
host text not null,
timestamp integer not null,
tag text not null,
version text not null,
data blob not null,
);

View File

@ -14,4 +14,5 @@ pub mod database;
pub mod history; pub mod history;
pub mod import; pub mod import;
pub mod ordering; pub mod ordering;
pub mod record;
pub mod settings; pub mod settings;

View File

@ -0,0 +1,2 @@
pub mod sqlite_store;
pub mod store;

View File

@ -0,0 +1,70 @@
// Here we are using sqlite as a pretty dumb store, and will not be running any complex queries.
// Multiple stores of multiple types are all stored in one chonky table (for now), and we just index
// by tag/host
yo tomorrow morning me
drink that coffee
then wrap up this interface
you will need to
- make sure the records use string IDs
- add the version in
- write some tests with a memory sqlite
use std::path::Path;
use std::str::FromStr;
use async_trait::async_trait;
use fs_err as fs;
use sqlx::{
sqlite::{SqliteConnectOptions, SqliteJournalMode, SqlitePool, SqlitePoolOptions, SqliteRow},
Result, Row,
};
use atuin_common::record::Record;
use super::store::Store;
pub struct SqliteStore {
pool: SqlitePool,
}
impl SqliteStore {
pub async fn new(path: impl AsRef<Path>) -> Result<Self> {
let path = path.as_ref();
debug!("opening sqlite database at {:?}", path);
let create = !path.exists();
if create {
if let Some(dir) = path.parent() {
fs::create_dir_all(dir)?;
}
}
let opts = SqliteConnectOptions::from_str(path.as_os_str().to_str().unwrap())?
.journal_mode(SqliteJournalMode::Wal)
.create_if_missing(true);
let pool = SqlitePoolOptions::new().connect_with(opts).await?;
Self::setup_db(&pool).await?;
Ok(Self { pool })
}
async fn setup_db(pool: &SqlitePool) -> Result<()> {
debug!("running sqlite database setup");
sqlx::migrate!("./record-migrations").run(pool).await?;
Ok(())
}
}
#[async_trait]
impl Store for SqliteStore {
async fn push(record: Record) -> Result<Record> {
Ok(record)
}
}

View File

@ -0,0 +1,15 @@
use async_trait::async_trait;
use eyre::Result;
use atuin_common::record::Record;
/// A record store stores records
/// In more detail - we tend to need to process this into _another_ format to actually query it.
/// As is, the record store is intended as the source of truth for arbitratry data, which could
/// be shell history, kvs, etc.
#[async_trait]
pub trait Store {
async fn push(record: Record) -> Result<Record>;
async fn get(id: String) -> Result<Record>;
async fn len(host: String, tag: String) -> Result<usize>;
}

View File

@ -1,4 +1,5 @@
#![forbid(unsafe_code)] #![forbid(unsafe_code)]
pub mod api; pub mod api;
pub mod record;
pub mod utils; pub mod utils;

View File

@ -0,0 +1,15 @@
/// A single record stored inside of our local database
pub struct Record {
pub id: i64,
pub host: String,
pub timestamp: u64,
/// The type of data we are storing here. It is probably useful to also
/// include some sort of version. For example, history.v2
pub tag: String,
/// Some data. This can be anything you wish to store. Use the tag field to know how to handle it.
pub data: Vec<u8>,
}