Skip to content

Commit

Permalink
Add an 'operations.uuid' column, generated and virtual (GothenburgBit…
Browse files Browse the repository at this point in the history
…Factory#449)

This column is not stored separately, but allows indexing operations by
uuid without further modification or redundant storage.
  • Loading branch information
djmitche authored Sep 2, 2024
1 parent fb84873 commit 427ad78
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 3 deletions.
10 changes: 10 additions & 0 deletions taskchampion/src/operation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,16 @@ impl Operation {
pub fn is_undo_point(&self) -> bool {
self == &Self::UndoPoint
}

/// Get the UUID for this function, if it has one.
pub fn get_uuid(&self) -> Option<Uuid> {
match self {
Operation::Create { uuid: u } => Some(*u),
Operation::Delete { uuid: u, .. } => Some(*u),
Operation::Update { uuid: u, .. } => Some(*u),
Operation::UndoPoint => None,
}
}
}

/// Operations are a sequence of [`Operation`] values, which can be committed in a single
Expand Down
61 changes: 58 additions & 3 deletions taskchampion/src/storage/sqlite.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,15 +98,38 @@ impl SqliteStorage {
con.query_row("PRAGMA journal_mode=WAL", [], |_row| Ok(()))
.context("Setting journal_mode=WAL")?;

let queries = vec![
let create_tables = vec![
"CREATE TABLE IF NOT EXISTS operations (id INTEGER PRIMARY KEY AUTOINCREMENT, data STRING);",
"CREATE TABLE IF NOT EXISTS sync_meta (key STRING PRIMARY KEY, value STRING);",
"CREATE TABLE IF NOT EXISTS tasks (uuid STRING PRIMARY KEY, data STRING);",
"CREATE TABLE IF NOT EXISTS working_set (id INTEGER PRIMARY KEY, uuid STRING);",
];
for q in queries {
for q in create_tables {
con.execute(q, []).context("Creating table")?;
}
// At this point the DB schema is that of TaskChampion 0.7.0.

// Check for and add the `operations.uuid` column.
let res: u32 = con
.query_row(
"SELECT COUNT(*) AS c FROM pragma_table_xinfo('operations') WHERE name='uuid'",
[],
|r| r.get(0),
)
.context("Checking for operations.uuid")?;
if res == 0 {
con.execute(
r#"ALTER TABLE operations ADD COLUMN uuid GENERATED ALWAYS AS (
coalesce(json_extract(data, "$.Update.uuid"),
json_extract(data, "$.Create.uuid"),
json_extract(data, "$.Delete.uuid"))) VIRTUAL"#,
[],
)
.context("Adding operations.uuid")?;

con.execute("CREATE INDEX operations_by_uuid ON operations (uuid)", [])
.context("Creating operations_by_uuid")?;
}

Ok(SqliteStorage { con })
}
Expand Down Expand Up @@ -490,7 +513,7 @@ mod test {
fn test_empty_dir() -> Result<()> {
let tmp_dir = TempDir::new()?;
let non_existant = tmp_dir.path().join("subdir");
let mut storage = SqliteStorage::new(non_existant, true)?;
let mut storage = SqliteStorage::new(non_existant.clone(), true)?;
let uuid = Uuid::new_v4();
{
let mut txn = storage.txn()?;
Expand All @@ -502,6 +525,14 @@ mod test {
let task = txn.get_task(uuid)?;
assert_eq!(task, Some(taskmap_with(vec![])));
}

// Re-open the DB.
let mut storage = SqliteStorage::new(non_existant, true)?;
{
let mut txn = storage.txn()?;
let task = txn.get_task(uuid)?;
assert_eq!(task, Some(taskmap_with(vec![])));
}
Ok(())
}

Expand Down Expand Up @@ -536,6 +567,8 @@ mod test {
})?;
txn.commit()?;
}

// Read back the modification.
{
let mut txn = storage.txn()?;
let task_one = txn.get_task(one)?.unwrap();
Expand All @@ -544,6 +577,28 @@ mod test {
assert_eq!(ops.len(), 15);
}

// Check the UUID fields on the operations directly in the DB.
{
let t = storage
.con
.transaction_with_behavior(TransactionBehavior::Immediate)?;
let mut q = t.prepare("SELECT data, uuid FROM operations ORDER BY id ASC")?;
let mut num_ops = 0;
for row in q
.query_map([], |r| {
let uuid: Option<StoredUuid> = r.get("uuid")?;
let operation: Operation = r.get("data")?;
Ok((uuid.map(|su| su.0), operation))
})
.context("Get all operations")?
{
let (uuid, operation) = row?;
assert_eq!(uuid, operation.get_uuid());
num_ops += 1;
}
assert_eq!(num_ops, 15);
}

Ok(())
}

Expand Down

0 comments on commit 427ad78

Please sign in to comment.