Add support for _not_ creating a DB if one does not exist

This commit is contained in:
Dustin J. Mitchell 2022-10-10 15:59:45 +00:00 committed by Tomas Babej
parent 9e5c0001c4
commit 67fc422311
5 changed files with 46 additions and 25 deletions

View file

@ -14,7 +14,7 @@ static void test_replica_creation(void) {
// creating an on-disk replica does not crash // creating an on-disk replica does not crash
static void test_replica_creation_disk(void) { static void test_replica_creation_disk(void) {
TCReplica *rep = tc_replica_new_on_disk(tc_string_borrow("test-db"), NULL); TCReplica *rep = tc_replica_new_on_disk(tc_string_borrow("test-db"), true, NULL);
TEST_ASSERT_NOT_NULL(rep); TEST_ASSERT_NOT_NULL(rep);
TEST_ASSERT_NULL(tc_replica_error(rep).ptr); TEST_ASSERT_NULL(tc_replica_error(rep).ptr);
tc_replica_free(rep); tc_replica_free(rep);

View file

@ -140,6 +140,7 @@ pub unsafe extern "C" fn tc_replica_new_in_memory() -> *mut TCReplica {
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn tc_replica_new_on_disk( pub unsafe extern "C" fn tc_replica_new_on_disk(
path: TCString, path: TCString,
create_if_missing: bool,
error_out: *mut TCString, error_out: *mut TCString,
) -> *mut TCReplica { ) -> *mut TCReplica {
wrap_constructor( wrap_constructor(
@ -150,6 +151,7 @@ pub unsafe extern "C" fn tc_replica_new_on_disk(
let mut path = unsafe { TCString::val_from_arg(path) }; let mut path = unsafe { TCString::val_from_arg(path) };
let storage = StorageConfig::OnDisk { let storage = StorageConfig::OnDisk {
taskdb_dir: path.to_path_buf_mut()?, taskdb_dir: path.to_path_buf_mut()?,
create_if_missing,
} }
.into_storage()?; .into_storage()?;

View file

@ -492,7 +492,9 @@ struct TCReplica *tc_replica_new_in_memory(void);
* is written to the error_out parameter (if it is not NULL) and NULL is returned. The caller * is written to the error_out parameter (if it is not NULL) and NULL is returned. The caller
* must free this string. * must free this string.
*/ */
struct TCReplica *tc_replica_new_on_disk(struct TCString path, struct TCString *error_out); struct TCReplica *tc_replica_new_on_disk(struct TCString path,
bool create_if_missing,
struct TCString *error_out);
/** /**
* Get a list of all tasks in the replica. * Get a list of all tasks in the replica.

View file

@ -7,6 +7,9 @@ pub enum StorageConfig {
OnDisk { OnDisk {
/// Path containing the task DB. /// Path containing the task DB.
taskdb_dir: PathBuf, taskdb_dir: PathBuf,
/// Create the DB if it does not already exist
create_if_missing: bool,
}, },
/// Store the data in memory. This is only useful for testing. /// Store the data in memory. This is only useful for testing.
InMemory, InMemory,
@ -15,7 +18,10 @@ pub enum StorageConfig {
impl StorageConfig { impl StorageConfig {
pub fn into_storage(self) -> anyhow::Result<Box<dyn Storage>> { pub fn into_storage(self) -> anyhow::Result<Box<dyn Storage>> {
Ok(match self { Ok(match self {
StorageConfig::OnDisk { taskdb_dir } => Box::new(SqliteStorage::new(taskdb_dir)?), StorageConfig::OnDisk {
taskdb_dir,
create_if_missing,
} => Box::new(SqliteStorage::new(taskdb_dir, create_if_missing)?),
StorageConfig::InMemory => Box::new(InMemoryStorage::new()), StorageConfig::InMemory => Box::new(InMemoryStorage::new()),
}) })
} }

View file

@ -1,7 +1,7 @@
use crate::storage::{ReplicaOp, Storage, StorageTxn, TaskMap, VersionId, DEFAULT_BASE_VERSION}; use crate::storage::{ReplicaOp, Storage, StorageTxn, TaskMap, VersionId, DEFAULT_BASE_VERSION};
use anyhow::Context; use anyhow::Context;
use rusqlite::types::{FromSql, ToSql}; use rusqlite::types::{FromSql, ToSql};
use rusqlite::{params, Connection, OptionalExtension}; use rusqlite::{params, Connection, OpenFlags, OptionalExtension};
use std::path::Path; use std::path::Path;
use uuid::Uuid; use uuid::Uuid;
@ -76,13 +76,24 @@ pub struct SqliteStorage {
} }
impl SqliteStorage { impl SqliteStorage {
pub fn new<P: AsRef<Path>>(directory: P) -> anyhow::Result<SqliteStorage> { pub fn new<P: AsRef<Path>>(
directory: P,
create_if_missing: bool,
) -> anyhow::Result<SqliteStorage> {
if create_if_missing {
// Ensure parent folder exists // Ensure parent folder exists
std::fs::create_dir_all(&directory)?; std::fs::create_dir_all(&directory)?;
}
// Open (or create) database // Open (or create) database
let db_file = directory.as_ref().join("taskchampion.sqlite3"); let db_file = directory.as_ref().join("taskchampion.sqlite3");
let con = Connection::open(db_file)?; let mut flags = OpenFlags::SQLITE_OPEN_READ_WRITE
| OpenFlags::SQLITE_OPEN_NO_MUTEX
| OpenFlags::SQLITE_OPEN_URI;
if create_if_missing {
flags |= OpenFlags::SQLITE_OPEN_CREATE;
}
let con = Connection::open_with_flags(db_file, flags)?;
// Initialize database // Initialize database
let queries = vec![ let queries = vec![
@ -369,7 +380,7 @@ mod test {
fn test_empty_dir() -> anyhow::Result<()> { fn test_empty_dir() -> anyhow::Result<()> {
let tmp_dir = TempDir::new()?; let tmp_dir = TempDir::new()?;
let non_existant = tmp_dir.path().join("subdir"); let non_existant = tmp_dir.path().join("subdir");
let mut storage = SqliteStorage::new(&non_existant)?; let mut storage = SqliteStorage::new(&non_existant, true)?;
let uuid = Uuid::new_v4(); let uuid = Uuid::new_v4();
{ {
let mut txn = storage.txn()?; let mut txn = storage.txn()?;
@ -387,7 +398,7 @@ mod test {
#[test] #[test]
fn drop_transaction() -> anyhow::Result<()> { fn drop_transaction() -> anyhow::Result<()> {
let tmp_dir = TempDir::new()?; let tmp_dir = TempDir::new()?;
let mut storage = SqliteStorage::new(&tmp_dir.path())?; let mut storage = SqliteStorage::new(&tmp_dir.path(), true)?;
let uuid1 = Uuid::new_v4(); let uuid1 = Uuid::new_v4();
let uuid2 = Uuid::new_v4(); let uuid2 = Uuid::new_v4();
@ -416,7 +427,7 @@ mod test {
#[test] #[test]
fn test_create() -> anyhow::Result<()> { fn test_create() -> anyhow::Result<()> {
let tmp_dir = TempDir::new()?; let tmp_dir = TempDir::new()?;
let mut storage = SqliteStorage::new(&tmp_dir.path())?; let mut storage = SqliteStorage::new(&tmp_dir.path(), true)?;
let uuid = Uuid::new_v4(); let uuid = Uuid::new_v4();
{ {
let mut txn = storage.txn()?; let mut txn = storage.txn()?;
@ -434,7 +445,7 @@ mod test {
#[test] #[test]
fn test_create_exists() -> anyhow::Result<()> { fn test_create_exists() -> anyhow::Result<()> {
let tmp_dir = TempDir::new()?; let tmp_dir = TempDir::new()?;
let mut storage = SqliteStorage::new(&tmp_dir.path())?; let mut storage = SqliteStorage::new(&tmp_dir.path(), true)?;
let uuid = Uuid::new_v4(); let uuid = Uuid::new_v4();
{ {
let mut txn = storage.txn()?; let mut txn = storage.txn()?;
@ -452,7 +463,7 @@ mod test {
#[test] #[test]
fn test_get_missing() -> anyhow::Result<()> { fn test_get_missing() -> anyhow::Result<()> {
let tmp_dir = TempDir::new()?; let tmp_dir = TempDir::new()?;
let mut storage = SqliteStorage::new(&tmp_dir.path())?; let mut storage = SqliteStorage::new(&tmp_dir.path(), true)?;
let uuid = Uuid::new_v4(); let uuid = Uuid::new_v4();
{ {
let mut txn = storage.txn()?; let mut txn = storage.txn()?;
@ -465,7 +476,7 @@ mod test {
#[test] #[test]
fn test_set_task() -> anyhow::Result<()> { fn test_set_task() -> anyhow::Result<()> {
let tmp_dir = TempDir::new()?; let tmp_dir = TempDir::new()?;
let mut storage = SqliteStorage::new(&tmp_dir.path())?; let mut storage = SqliteStorage::new(&tmp_dir.path(), true)?;
let uuid = Uuid::new_v4(); let uuid = Uuid::new_v4();
{ {
let mut txn = storage.txn()?; let mut txn = storage.txn()?;
@ -486,7 +497,7 @@ mod test {
#[test] #[test]
fn test_delete_task_missing() -> anyhow::Result<()> { fn test_delete_task_missing() -> anyhow::Result<()> {
let tmp_dir = TempDir::new()?; let tmp_dir = TempDir::new()?;
let mut storage = SqliteStorage::new(&tmp_dir.path())?; let mut storage = SqliteStorage::new(&tmp_dir.path(), true)?;
let uuid = Uuid::new_v4(); let uuid = Uuid::new_v4();
{ {
let mut txn = storage.txn()?; let mut txn = storage.txn()?;
@ -498,7 +509,7 @@ mod test {
#[test] #[test]
fn test_delete_task_exists() -> anyhow::Result<()> { fn test_delete_task_exists() -> anyhow::Result<()> {
let tmp_dir = TempDir::new()?; let tmp_dir = TempDir::new()?;
let mut storage = SqliteStorage::new(&tmp_dir.path())?; let mut storage = SqliteStorage::new(&tmp_dir.path(), true)?;
let uuid = Uuid::new_v4(); let uuid = Uuid::new_v4();
{ {
let mut txn = storage.txn()?; let mut txn = storage.txn()?;
@ -515,7 +526,7 @@ mod test {
#[test] #[test]
fn test_all_tasks_empty() -> anyhow::Result<()> { fn test_all_tasks_empty() -> anyhow::Result<()> {
let tmp_dir = TempDir::new()?; let tmp_dir = TempDir::new()?;
let mut storage = SqliteStorage::new(&tmp_dir.path())?; let mut storage = SqliteStorage::new(&tmp_dir.path(), true)?;
{ {
let mut txn = storage.txn()?; let mut txn = storage.txn()?;
let tasks = txn.all_tasks()?; let tasks = txn.all_tasks()?;
@ -527,7 +538,7 @@ mod test {
#[test] #[test]
fn test_all_tasks_and_uuids() -> anyhow::Result<()> { fn test_all_tasks_and_uuids() -> anyhow::Result<()> {
let tmp_dir = TempDir::new()?; let tmp_dir = TempDir::new()?;
let mut storage = SqliteStorage::new(&tmp_dir.path())?; let mut storage = SqliteStorage::new(&tmp_dir.path(), true)?;
let uuid1 = Uuid::new_v4(); let uuid1 = Uuid::new_v4();
let uuid2 = Uuid::new_v4(); let uuid2 = Uuid::new_v4();
{ {
@ -581,7 +592,7 @@ mod test {
#[test] #[test]
fn test_base_version_default() -> anyhow::Result<()> { fn test_base_version_default() -> anyhow::Result<()> {
let tmp_dir = TempDir::new()?; let tmp_dir = TempDir::new()?;
let mut storage = SqliteStorage::new(&tmp_dir.path())?; let mut storage = SqliteStorage::new(&tmp_dir.path(), true)?;
{ {
let mut txn = storage.txn()?; let mut txn = storage.txn()?;
assert_eq!(txn.base_version()?, DEFAULT_BASE_VERSION); assert_eq!(txn.base_version()?, DEFAULT_BASE_VERSION);
@ -592,7 +603,7 @@ mod test {
#[test] #[test]
fn test_base_version_setting() -> anyhow::Result<()> { fn test_base_version_setting() -> anyhow::Result<()> {
let tmp_dir = TempDir::new()?; let tmp_dir = TempDir::new()?;
let mut storage = SqliteStorage::new(&tmp_dir.path())?; let mut storage = SqliteStorage::new(&tmp_dir.path(), true)?;
let u = Uuid::new_v4(); let u = Uuid::new_v4();
{ {
let mut txn = storage.txn()?; let mut txn = storage.txn()?;
@ -609,7 +620,7 @@ mod test {
#[test] #[test]
fn test_operations() -> anyhow::Result<()> { fn test_operations() -> anyhow::Result<()> {
let tmp_dir = TempDir::new()?; let tmp_dir = TempDir::new()?;
let mut storage = SqliteStorage::new(&tmp_dir.path())?; let mut storage = SqliteStorage::new(&tmp_dir.path(), true)?;
let uuid1 = Uuid::new_v4(); let uuid1 = Uuid::new_v4();
let uuid2 = Uuid::new_v4(); let uuid2 = Uuid::new_v4();
let uuid3 = Uuid::new_v4(); let uuid3 = Uuid::new_v4();
@ -694,7 +705,7 @@ mod test {
#[test] #[test]
fn get_working_set_empty() -> anyhow::Result<()> { fn get_working_set_empty() -> anyhow::Result<()> {
let tmp_dir = TempDir::new()?; let tmp_dir = TempDir::new()?;
let mut storage = SqliteStorage::new(&tmp_dir.path())?; let mut storage = SqliteStorage::new(&tmp_dir.path(), true)?;
{ {
let mut txn = storage.txn()?; let mut txn = storage.txn()?;
@ -708,7 +719,7 @@ mod test {
#[test] #[test]
fn add_to_working_set() -> anyhow::Result<()> { fn add_to_working_set() -> anyhow::Result<()> {
let tmp_dir = TempDir::new()?; let tmp_dir = TempDir::new()?;
let mut storage = SqliteStorage::new(&tmp_dir.path())?; let mut storage = SqliteStorage::new(&tmp_dir.path(), true)?;
let uuid1 = Uuid::new_v4(); let uuid1 = Uuid::new_v4();
let uuid2 = Uuid::new_v4(); let uuid2 = Uuid::new_v4();
@ -731,7 +742,7 @@ mod test {
#[test] #[test]
fn clear_working_set() -> anyhow::Result<()> { fn clear_working_set() -> anyhow::Result<()> {
let tmp_dir = TempDir::new()?; let tmp_dir = TempDir::new()?;
let mut storage = SqliteStorage::new(&tmp_dir.path())?; let mut storage = SqliteStorage::new(&tmp_dir.path(), true)?;
let uuid1 = Uuid::new_v4(); let uuid1 = Uuid::new_v4();
let uuid2 = Uuid::new_v4(); let uuid2 = Uuid::new_v4();
@ -762,7 +773,7 @@ mod test {
#[test] #[test]
fn set_working_set_item() -> anyhow::Result<()> { fn set_working_set_item() -> anyhow::Result<()> {
let tmp_dir = TempDir::new()?; let tmp_dir = TempDir::new()?;
let mut storage = SqliteStorage::new(&tmp_dir.path())?; let mut storage = SqliteStorage::new(&tmp_dir.path(), true)?;
let uuid1 = Uuid::new_v4(); let uuid1 = Uuid::new_v4();
let uuid2 = Uuid::new_v4(); let uuid2 = Uuid::new_v4();