mirror of https://github.com/djcb/mu.git
store: support reinit
Support reinitializing, based on some current store. This is useful for upgrading. Note that this is only the backend implementation + tests.
This commit is contained in:
parent
9e60ebb683
commit
5367122c08
|
@ -303,44 +303,31 @@ Store::Private::find_message_unlocked(Store::Id docid) const
|
||||||
Store::Store(const std::string& path, Store::Options opts)
|
Store::Store(const std::string& path, Store::Options opts)
|
||||||
: priv_{std::make_unique<Private>(path, none_of(opts & Store::Options::Writable))}
|
: priv_{std::make_unique<Private>(path, none_of(opts & Store::Options::Writable))}
|
||||||
{
|
{
|
||||||
if (properties().schema_version == ExpectedSchemaVersion)
|
if (none_of(opts & Store::Options::Writable) &&
|
||||||
return; // all is good.
|
any_of(opts & Store::Options::ReInit))
|
||||||
|
throw Mu::Error(Error::Code::InvalidArgument,
|
||||||
|
"Options::ReInit requires Options::Writable");
|
||||||
|
|
||||||
// Now, it seems the schema versions do not match; shall we automatically
|
if (any_of(opts & Store::Options::ReInit)) {
|
||||||
// update?
|
/* user wants to re-initialize an existing store */
|
||||||
|
Config conf{};
|
||||||
if (none_of(opts & Store::Options::AutoUpgrade)) {
|
conf.batch_size = properties().batch_size;
|
||||||
// not allowed to auto-upgrade, so we give up.
|
conf.max_message_size = properties().max_message_size;
|
||||||
throw Mu::Error(Error::Code::SchemaMismatch,
|
const auto root_maildir{properties().root_maildir};
|
||||||
"expected schema-version %s, but got %s; "
|
const auto addrs{properties().personal_addresses};
|
||||||
"cannot auto-upgrade; please use 'mu init'",
|
/* close the old one */
|
||||||
ExpectedSchemaVersion,
|
this->priv_.reset();
|
||||||
properties().schema_version.c_str());
|
/* and create a new one. */
|
||||||
|
Store new_store(path, root_maildir, addrs, conf);
|
||||||
|
this->priv_ = std::move(new_store.priv_);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Okay, let's attempt an auto-upgrade.
|
/* otherwise, the schema version should match. */
|
||||||
g_info("attempt reinit database from schema %s --> %s",
|
|
||||||
properties().schema_version.c_str(), ExpectedSchemaVersion);
|
|
||||||
|
|
||||||
Config conf;
|
|
||||||
conf.batch_size = properties().batch_size;
|
|
||||||
conf.max_message_size = properties().max_message_size;
|
|
||||||
|
|
||||||
priv_.reset();
|
|
||||||
priv_ = std::make_unique<Private>(path,
|
|
||||||
properties().root_maildir,
|
|
||||||
properties().personal_addresses,
|
|
||||||
conf);
|
|
||||||
// Now let's try again.
|
|
||||||
priv_.reset();
|
|
||||||
priv_ = std::make_unique<Private>(path, none_of(opts & Store::Options::Writable));
|
|
||||||
if (properties().schema_version != ExpectedSchemaVersion)
|
if (properties().schema_version != ExpectedSchemaVersion)
|
||||||
// Nope, we failed.
|
|
||||||
throw Mu::Error(Error::Code::SchemaMismatch,
|
throw Mu::Error(Error::Code::SchemaMismatch,
|
||||||
"failed to auto-upgrade from %s to %s; "
|
"expected schema-version %s, but got %s",
|
||||||
"please use 'mu init'",
|
ExpectedSchemaVersion,
|
||||||
properties().schema_version.c_str(),
|
properties().schema_version.c_str());
|
||||||
ExpectedSchemaVersion);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Store::Store(const std::string& path,
|
Store::Store(const std::string& path,
|
||||||
|
|
|
@ -45,15 +45,11 @@ public:
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configuration options.
|
* Configuration options.
|
||||||
*
|
|
||||||
* @param path
|
|
||||||
* @param readonly
|
|
||||||
*/
|
*/
|
||||||
enum struct Options {
|
enum struct Options {
|
||||||
None = 0, /**< No specific options */
|
None = 0, /**< No specific options */
|
||||||
Writable = 1 << 0, /**< Open in writable mode */
|
Writable = 1 << 0, /**< Open in writable mode */
|
||||||
AutoUpgrade = 1 << 1, /**< automatically re-initialize
|
ReInit = 1 << 1, /**< Re-initialize based on existing */
|
||||||
* versions do not match */
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -62,7 +58,7 @@ public:
|
||||||
* @param path path to the database
|
* @param path path to the database
|
||||||
* @param options startup options
|
* @param options startup options
|
||||||
*
|
*
|
||||||
* A store or an error.
|
* @return A store or an error.
|
||||||
*/
|
*/
|
||||||
static Result<Store> make(const std::string& path,
|
static Result<Store> make(const std::string& path,
|
||||||
Options opts=Options::None) noexcept try {
|
Options opts=Options::None) noexcept try {
|
||||||
|
@ -93,6 +89,8 @@ public:
|
||||||
* @param personal_addresses addresses that should be recognized as
|
* @param personal_addresses addresses that should be recognized as
|
||||||
* 'personal' for identifying personal messages.
|
* 'personal' for identifying personal messages.
|
||||||
* @param config a configuration object
|
* @param config a configuration object
|
||||||
|
*
|
||||||
|
* @return a store or an error
|
||||||
*/
|
*/
|
||||||
static Result<Store> make_new(const std::string& path,
|
static Result<Store> make_new(const std::string& path,
|
||||||
const std::string& maildir,
|
const std::string& maildir,
|
||||||
|
@ -448,6 +446,7 @@ private:
|
||||||
*/
|
*/
|
||||||
Store(const std::string& path, Options opts=Options::None);
|
Store(const std::string& path, Options opts=Options::None);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a store for a not-yet-existing document database
|
* Construct a store for a not-yet-existing document database
|
||||||
*
|
*
|
||||||
|
|
|
@ -53,6 +53,62 @@ test_store_ctor_dtor()
|
||||||
store->properties().schema_version.c_str());
|
store->properties().schema_version.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_store_reinit()
|
||||||
|
{
|
||||||
|
TempDir tempdir;
|
||||||
|
{
|
||||||
|
Store::Config conf{};
|
||||||
|
conf.max_message_size = 1234567;
|
||||||
|
conf.batch_size = 7654321;
|
||||||
|
|
||||||
|
StringVec my_addresses{ "foo@example.com", "bar@example.com" };
|
||||||
|
|
||||||
|
auto store{Store::make_new(tempdir.path(), MuTestMaildir, my_addresses, conf)};
|
||||||
|
assert_valid_result(store);
|
||||||
|
|
||||||
|
g_assert_true(store->empty());
|
||||||
|
g_assert_cmpuint(0, ==, store->size());
|
||||||
|
|
||||||
|
g_assert_cmpstr(MU_STORE_SCHEMA_VERSION, ==,
|
||||||
|
store->properties().schema_version.c_str());
|
||||||
|
|
||||||
|
const auto msgpath{MuTestMaildir + "/cur/1283599333.1840_11.cthulhu!2,"};
|
||||||
|
const auto id = store->add_message(msgpath);
|
||||||
|
assert_valid_result(id);
|
||||||
|
g_assert_true(store->contains_message(msgpath));
|
||||||
|
g_assert_cmpuint(store->size(), ==, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
//now let's reinitialize it.
|
||||||
|
{
|
||||||
|
auto store{Store::make(tempdir.path(),
|
||||||
|
Store::Options::Writable|Store::Options::ReInit)};
|
||||||
|
|
||||||
|
assert_valid_result(store);
|
||||||
|
g_assert_true(store->empty());
|
||||||
|
|
||||||
|
assert_equal(store->properties().database_path, tempdir.path());
|
||||||
|
g_assert_cmpuint(store->properties().batch_size,==,7654321);
|
||||||
|
g_assert_cmpuint(store->properties().max_message_size,==,1234567);
|
||||||
|
|
||||||
|
const auto addrs{store->properties().personal_addresses};
|
||||||
|
g_assert_cmpuint(addrs.size(),==,2);
|
||||||
|
g_assert_true(seq_some(addrs, [](auto&& a){return a=="foo@example.com";}));
|
||||||
|
g_assert_true(seq_some(addrs, [](auto&& a){return a=="bar@example.com";}));
|
||||||
|
|
||||||
|
const auto msgpath{MuTestMaildir + "/cur/1283599333.1840_11.cthulhu!2,"};
|
||||||
|
const auto id = store->add_message(msgpath);
|
||||||
|
assert_valid_result(id);
|
||||||
|
g_assert_true(store->contains_message(msgpath));
|
||||||
|
g_assert_cmpuint(store->size(), ==, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
test_store_add_count_remove()
|
test_store_add_count_remove()
|
||||||
{
|
{
|
||||||
|
@ -69,8 +125,6 @@ test_store_add_count_remove()
|
||||||
g_assert_cmpuint(store->size(), ==, 1);
|
g_assert_cmpuint(store->size(), ==, 1);
|
||||||
g_assert_true(store->contains_message(msgpath));
|
g_assert_true(store->contains_message(msgpath));
|
||||||
|
|
||||||
g_assert_true(store->contains_message(msgpath));
|
|
||||||
|
|
||||||
const auto id2 = store->add_message(MuTestMaildir2 + "/bar/cur/mail3");
|
const auto id2 = store->add_message(MuTestMaildir2 + "/bar/cur/mail3");
|
||||||
g_assert_false(!!id2); // wrong maildir.
|
g_assert_false(!!id2); // wrong maildir.
|
||||||
store->commit();
|
store->commit();
|
||||||
|
@ -346,13 +400,13 @@ test_store_fail()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int
|
int
|
||||||
main(int argc, char* argv[])
|
main(int argc, char* argv[])
|
||||||
{
|
{
|
||||||
mu_test_init(&argc, &argv);
|
mu_test_init(&argc, &argv);
|
||||||
|
|
||||||
g_test_add_func("/store/ctor-dtor", test_store_ctor_dtor);
|
g_test_add_func("/store/ctor-dtor", test_store_ctor_dtor);
|
||||||
|
g_test_add_func("/store/reinit", test_store_reinit);
|
||||||
g_test_add_func("/store/add-count-remove", test_store_add_count_remove);
|
g_test_add_func("/store/add-count-remove", test_store_add_count_remove);
|
||||||
g_test_add_func("/store/message/mailing-list",
|
g_test_add_func("/store/message/mailing-list",
|
||||||
test_message_mailing_list);
|
test_message_mailing_list);
|
||||||
|
|
Loading…
Reference in New Issue