From 98abcf8e842f482c57e7a326dfe4ee7b0a416682 Mon Sep 17 00:00:00 2001 From: "Dirk-Jan C. Binnema" Date: Tue, 16 Feb 2021 19:32:15 +0200 Subject: [PATCH] store: support in-memory database For testing, this is faster / and doesn't pollute the file system. --- lib/mu-store.cc | 28 +++++++++++++++++++++------- lib/mu-store.hh | 14 ++++++++++++-- lib/test-mu-store.cc | 36 ++++++++++++++++++++++++++++++++++-- 3 files changed, 67 insertions(+), 11 deletions(-) diff --git a/lib/mu-store.cc b/lib/mu-store.cc index c99bceaa..54fb4ffb 100644 --- a/lib/mu-store.cc +++ b/lib/mu-store.cc @@ -106,7 +106,7 @@ struct Store::Private { #define LOCKED std::lock_guard l(lock_); - enum struct XapianOpts {ReadOnly, Open, CreateOverwrite }; + enum struct XapianOpts {ReadOnly, Open, CreateOverwrite, InMemory }; Private (const std::string& path, bool readonly): read_only_{readonly}, @@ -128,6 +128,14 @@ struct Store::Private { writable_db().begin_transaction(); } + Private (const std::string& root_maildir, + const StringVec& personal_addresses, const Store::Config& conf): + read_only_{false}, + db_{make_xapian_db("", XapianOpts::InMemory)}, + mdata_{init_metadata(conf, "", root_maildir, personal_addresses)}, + contacts_{"", mdata_.personal_addresses} { + } + ~Private() try { g_debug("closing store @ %s", mdata_.database_path.c_str()); if (!read_only_) { @@ -145,6 +153,8 @@ struct Store::Private { return std::make_unique(db_path, Xapian::DB_OPEN); case XapianOpts::CreateOverwrite: return std::make_unique(db_path, Xapian::DB_CREATE_OR_OVERWRITE); + case XapianOpts::InMemory: + return std::make_unique(std::string{}, Xapian::DB_BACKEND_INMEMORY); default: throw std::logic_error ("invalid xapian options"); } @@ -174,6 +184,8 @@ struct Store::Private { void commit () try { g_debug("committing %zu modification(s)", dirtiness_); dirtiness_ = 0; + if (mdata_.in_memory) + return; // not supported in the in-memory backend. writable_db().commit_transaction(); writable_db().begin_transaction(); } MU_XAPIAN_CATCH_BLOCK; @@ -200,6 +212,7 @@ struct Store::Private { mdata.batch_size = ::atoll(db().get_metadata(BatchSizeKey).c_str()); mdata.max_message_size = ::atoll(db().get_metadata(MaxMessageSizeKey).c_str()); + mdata.in_memory = db_path.empty(); mdata.root_maildir = db().get_metadata(RootMaildirKey); mdata.personal_addresses = Mu::split(db().get_metadata(PersonalAddressesKey),","); @@ -288,6 +301,13 @@ Store::Store (const std::string& path, const std::string& maildir, priv_{std::make_unique(path, maildir, personal_addresses, conf)} {} + +Store::Store (const std::string& maildir, + const StringVec& personal_addresses, const Config& conf): + priv_{std::make_unique(maildir, personal_addresses, conf)} +{} + + Store::~Store() = default; const Store::Metadata& @@ -307,7 +327,6 @@ const Xapian::Database& Store::database() const { return priv_->db(); - } Xapian::WritableDatabase& @@ -370,7 +389,6 @@ maildir_from_path (const std::string& root, const std::string& path) return mdir; } - unsigned Store::add_message (const std::string& path) { @@ -395,7 +413,6 @@ Store::add_message (const std::string& path) return docid; } - bool Store::update_message (MuMsg *msg, unsigned docid) { @@ -573,9 +590,6 @@ Store::for_each_term (const std::string& field, Store::ForEachTermFunc func) con return n; } - - - void Store::commit () try { diff --git a/lib/mu-store.hh b/lib/mu-store.hh index 508ed41e..645d8c62 100644 --- a/lib/mu-store.hh +++ b/lib/mu-store.hh @@ -1,5 +1,5 @@ /* -** Copyright (C) 2020 Dirk-Jan C. Binnema +** Copyright (C) 2021 Dirk-Jan C. Binnema ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the @@ -69,6 +69,16 @@ public: Store (const std::string& path, const std::string& maildir, const StringVec& personal_addresses, const Config& conf); + /** + * Construct an in-memory, writeable store for testing + * + * @param maildir maildir to use for this store + * @param personal_addresses addresses that should be recognized as + * 'personal' for identifying personal messages. + */ + Store (const std::string& maildir, + const StringVec& personal_addresses, const Config& conf); + /** * DTOR */ @@ -81,6 +91,7 @@ public: bool read_only; /**< Is the database opened read-only? */ size_t batch_size; /**< Maximum database transaction batch size */ + bool in_memory; /**< Is this an in-memory database (for testing)?*/ std::string root_maildir; /**< Absolute path to the top-level maildir */ @@ -117,7 +128,6 @@ public: */ Xapian::WritableDatabase& writable_database(); - /** * Get the Indexer associated with this store. It is an error to call * this on a read-only store. diff --git a/lib/test-mu-store.cc b/lib/test-mu-store.cc index a96dc7c1..ca38123e 100644 --- a/lib/test-mu-store.cc +++ b/lib/test-mu-store.cc @@ -79,14 +79,46 @@ test_store_add_count_remove () } +static void +test_store_add_count_remove_in_memory () +{ + Mu::Store store{MuTestMaildir, {}, {}}; + + g_assert_true (store.metadata().in_memory); + + const auto id1 = store.add_message(MuTestMaildir + "/cur/1283599333.1840_11.cthulhu!2,"); + + g_assert_cmpuint(id1, !=, Mu::Store::InvalidId); + + g_assert_cmpuint(store.size(), ==, 1); + g_assert_true(store.contains_message(MuTestMaildir + "/cur/1283599333.1840_11.cthulhu!2,")); + + g_assert_cmpuint(store.add_message(MuTestMaildir2 + "/bar/cur/mail3"), + !=, Mu::Store::InvalidId); + + g_assert_cmpuint(store.size(), ==, 2); + g_assert_true(store.contains_message(MuTestMaildir2 + "/bar/cur/mail3")); + + store.remove_message(id1); + g_assert_cmpuint(store.size(), ==, 1); + g_assert_false(store.contains_message(MuTestMaildir + "/cur/1283599333.1840_11.cthulhu!2,")); + + store.remove_message (MuTestMaildir2 + "/bar/cur/mail3"); + g_assert_true(store.empty()); + g_assert_false(store.contains_message(MuTestMaildir2 + "/bar/cur/mail3")); +} + + + int main (int argc, char *argv[]) { g_test_init (&argc, &argv, NULL); /* mu_runtime_init/uninit */ - g_test_add_func ("/mu-store/ctor-dtor", test_store_ctor_dtor); - g_test_add_func ("/mu-store/add-count-remove", test_store_add_count_remove); + g_test_add_func ("/store/ctor-dtor", test_store_ctor_dtor); + g_test_add_func ("/store/add-count-remove", test_store_add_count_remove); + g_test_add_func ("/store/in-memory/add-count-remove", test_store_add_count_remove_in_memory); // if (!g_test_verbose()) // g_log_set_handler (NULL,