diff --git a/src/mu-store-priv.hh b/src/mu-store-priv.hh index af386135..f60e4821 100644 --- a/src/mu-store-priv.hh +++ b/src/mu-store-priv.hh @@ -33,6 +33,19 @@ #include "mu-store.h" #include "mu-contacts.h" + +class MuStoreError { +public: + MuStoreError (MuError err, const std::string& what) : + _err (err), _what(what) {} + MuError mu_error () const { return _err; } + const std::string& what() const { return _what; } +private: + MuError _err; + const std::string _what; +}; + + struct _MuStore { /* by default, use transactions of 30000 messages */ @@ -40,34 +53,44 @@ struct _MuStore { /* http://article.gmane.org/gmane.comp.search.xapian.general/3656 */ #define MU_STORE_MAX_TERM_LENGTH 240 - _MuStore (const char *xpath, const char *contacts_cache, bool read_only): + _MuStore (const char *path, const char *contacts_path, bool read_only): _in_transaction (false), _processed (0), _batch_size(MU_STORE_DEFAULT_BATCH_SIZE), - _contacts(0), _version(0), _db(0), _read_only(read_only) { + _contacts(0), _path (0), _version(0), + _db(0), _read_only(read_only), _ref_count (1) { + + if (!check_path ()) + throw MuStoreError (MU_ERROR_FILE, "invalid_path"); + + _path = g_strdup (path); if (read_only) - _db = new Xapian::Database (xpath); + _db = new Xapian::Database (path); else - _db = new Xapian::WritableDatabase (xpath, - Xapian::DB_CREATE_OR_OPEN); + _db = new Xapian::WritableDatabase + (path, Xapian::DB_CREATE_OR_OPEN); + if (!check_version ()) - throw std::runtime_error - ("xapian db version check failed"); + throw MuStoreError (MU_ERROR_XAPIAN_NOT_UP_TO_DATE, + ("xapian db version check failed")); - if (contacts_cache) { - _contacts = mu_contacts_new (contacts_cache); + if (contacts_path) { + _contacts = mu_contacts_new (contacts_path); if (!_contacts) /* don't bail-out for this */ - throw std::runtime_error - ("failed to init contacts cache"); + throw MuStoreError (MU_ERROR_FILE, + ("failed to init contacts cache")); } - MU_WRITE_LOG ("%s: opened %s (batch size: %u)", - __FUNCTION__, xpath, batch_size()); + __FUNCTION__, this->path(), batch_size()); } ~_MuStore () { try { + if (_ref_count != 0) + g_warning ("ref count != 0"); + g_free (_version); + g_free (_path); mu_contacts_destroy (_contacts); @@ -81,24 +104,13 @@ struct _MuStore { } MU_XAPIAN_CATCH_BLOCK; } - /* get a unique id for this message; note, this function returns a - * static buffer -- not reentrant */ - const char* get_message_uid (const char* path) { - char pfx = 0; - static char buf[PATH_MAX + 10]; - if (G_UNLIKELY(!pfx)) { - pfx = mu_msg_field_xapian_prefix(MU_MSG_FIELD_ID_PATH); - buf[0]=pfx; - } - std::strcpy (buf + 1, path); - return buf; - } + /* close the old database, and write an empty one on top of it */ + void clear(); /* get a unique id for this message; note, this function returns a - * static buffer -- not reentrant */ - const char* get_message_uid (MuMsg *msg) { - return get_message_uid (mu_msg_get_path(msg)); - } + * static buffer -- not reentrant */ + const char* get_message_uid (const char* path); + const char* get_message_uid (MuMsg *msg); MuContacts* contacts() { return _contacts; } @@ -108,38 +120,20 @@ struct _MuStore { mu_store_get_metadata (this, MU_STORE_VERSION_KEY); } - void begin_transaction () { - try { - db_writable()->begin_transaction(); - set_in_transaction (true); - } MU_XAPIAN_CATCH_BLOCK; - } - - - void commit_transaction () { - try { - set_in_transaction (false); - db_writable()->commit_transaction(); - } MU_XAPIAN_CATCH_BLOCK; - } - - void rollback_transaction () { - try { - set_in_transaction (false); - db_writable()->cancel_transaction(); - } MU_XAPIAN_CATCH_BLOCK; - } + void begin_transaction (); + void commit_transaction (); + void rollback_transaction (); Xapian::WritableDatabase* db_writable() { - if (G_UNLIKELY(_read_only)) - throw std::runtime_error - ("database is read-only"); + if (G_UNLIKELY(is_read_only())) + throw std::runtime_error ("database is read-only"); return (Xapian::WritableDatabase*)_db; } - Xapian::Database* db_read_only() const { - return _db; - } + Xapian::Database* db_read_only() const { return _db; } + + const char* path () const { return _path; } + bool is_read_only () const { return _read_only; } size_t batch_size () const { return _batch_size;} size_t set_batch_size (size_t n) { @@ -153,31 +147,15 @@ struct _MuStore { int set_processed (int n) { return _processed = n;} int inc_processed () { return ++_processed; } + /* MuStore is ref-counted */ + guint ref () { return ++_ref_count; } + guint unref () { return --_ref_count; } + private: - bool check_version () { - const gchar *version; - version = mu_store_version (this); - - /* no version yet? it must be a new db then; we'll set the version */ - if (!version) { - if (!mu_store_set_metadata (this, MU_STORE_VERSION_KEY, - MU_XAPIAN_DB_VERSION)) { - g_warning ("failed to set database version"); - return FALSE; - } - return TRUE; /* ok, done. */ - } - - /* we have a version, but is it the right one? */ - if (std::strcmp (version, MU_XAPIAN_DB_VERSION) != 0) { - g_warning ("expected db version %s, but got %s", - MU_XAPIAN_DB_VERSION, - version ? version : "" ); - return FALSE; - } - - return TRUE; + bool check_version (); + bool check_path () { + return true; // FIXME } /* transaction handling */ @@ -187,10 +165,14 @@ private: /* contacts object to cache all the contact information */ MuContacts *_contacts; + + char *_path; mutable char *_version; Xapian::Database *_db; bool _read_only; + + guint _ref_count; }; diff --git a/src/mu-store-read.cc b/src/mu-store-read.cc index c384da30..8f3f213b 100644 --- a/src/mu-store-read.cc +++ b/src/mu-store-read.cc @@ -40,6 +40,51 @@ #include "mu-contacts.h" +bool +_MuStore::check_version () +{ + const gchar *version; + version = mu_store_version (this); + + /* no version yet? it must be a new db then; we'll set the version */ + if (!version) { + if (!mu_store_set_metadata (this, MU_STORE_VERSION_KEY, + MU_STORE_SCHEMA_VERSION)) { + g_warning ("failed to set database version"); + return FALSE; + } + return TRUE; /* ok, done. */ + } + + /* we have a version, but is it the right one? */ + return mu_store_needs_upgrade (this) ? FALSE : TRUE; +} + + + +/* get a unique id for this message; note, this function returns a + * static buffer -- not reentrant */ +const char* +_MuStore::get_message_uid (const char* path) { + char pfx = 0; + static char buf[PATH_MAX + 10]; + if (G_UNLIKELY(!pfx)) { + pfx = mu_msg_field_xapian_prefix(MU_MSG_FIELD_ID_PATH); + buf[0]=pfx; + } + std::strcpy (buf + 1, path); + return buf; +} + +/* get a unique id for this message; note, this function returns a + * static buffer -- not reentrant */ +const char* +_MuStore::get_message_uid (MuMsg *msg) { + return get_message_uid (mu_msg_get_path(msg)); +} + + + MuStore* mu_store_new_read_only (const char* xpath, GError **err) { @@ -48,12 +93,28 @@ mu_store_new_read_only (const char* xpath, GError **err) try { return new _MuStore (xpath, NULL, true); + } catch (const MuStoreError& merr) { + + g_set_error (err, 0, merr.mu_error(), "%s", + merr.what().c_str()); + } MU_XAPIAN_CATCH_BLOCK_G_ERROR(err,MU_ERROR_XAPIAN); return NULL; } +gboolean +mu_store_is_read_only (MuStore *store) +{ + g_return_val_if_fail (store, FALSE); + + try { + return store->is_read_only() ? TRUE : FALSE; + + } MU_XAPIAN_CATCH_BLOCK_RETURN(FALSE); +} + unsigned mu_store_count (MuStore *store) @@ -63,9 +124,7 @@ mu_store_count (MuStore *store) try { return store->db_read_only()->get_doccount(); - } MU_XAPIAN_CATCH_BLOCK; - - return 0; + } MU_XAPIAN_CATCH_BLOCK_RETURN(0); } @@ -77,6 +136,16 @@ mu_store_version (MuStore *store) } +gboolean +mu_store_needs_upgrade (MuStore *store) +{ + g_return_val_if_fail (store, TRUE); + + return (g_strcmp0 (mu_store_version (store), + MU_STORE_SCHEMA_VERSION) == 0) ? FALSE : TRUE; + +} + char* mu_store_get_metadata (MuStore *store, const char *key) @@ -88,9 +157,7 @@ mu_store_get_metadata (MuStore *store, const char *key) const std::string val (store->db_read_only()->get_metadata (key)); return val.empty() ? NULL : g_strdup (val.c_str()); - } MU_XAPIAN_CATCH_BLOCK; - - return NULL; + } MU_XAPIAN_CATCH_BLOCK_RETURN(NULL); } @@ -170,3 +237,5 @@ mu_store_foreach (MuStore *self, return MU_OK; } + + diff --git a/src/mu-store-write.cc b/src/mu-store-write.cc index 103ab17d..80cd6683 100644 --- a/src/mu-store-write.cc +++ b/src/mu-store-write.cc @@ -39,6 +39,51 @@ #include "mu-flags.h" #include "mu-contacts.h" +void +_MuStore::begin_transaction () +{ + try { + db_writable()->begin_transaction(); + set_in_transaction (true); + } MU_XAPIAN_CATCH_BLOCK; +} + + +void +_MuStore::commit_transaction () { + try { + set_in_transaction (false); + db_writable()->commit_transaction(); + } MU_XAPIAN_CATCH_BLOCK; +} + +void +_MuStore::rollback_transaction () { + try { + set_in_transaction (false); + db_writable()->cancel_transaction(); + } MU_XAPIAN_CATCH_BLOCK; +} + + + +/* close the old database, and write an empty one on top of it */ +void +_MuStore::clear () +{ + if (is_read_only()) + throw std::runtime_error ("database is read-only"); + + // clear the database + db_writable()->close (); + delete _db; + _db = new Xapian::WritableDatabase + (path(), Xapian::DB_CREATE_OR_OVERWRITE); + + // clear the contacts cache + if (_contacts) + mu_contacts_clear (_contacts); +} /* we cache these prefix strings, so we don't have to allocate the all @@ -110,6 +155,10 @@ mu_store_new_writable (const char* xpath, const char *contacts_cache, return store; + } catch (const MuStoreError& merr) { + g_set_error (err, 0, merr.mu_error(), + "%s", merr.what().c_str()); + } MU_XAPIAN_CATCH_BLOCK_G_ERROR(err,MU_ERROR_XAPIAN); return NULL; @@ -142,6 +191,19 @@ mu_store_set_metadata (MuStore *store, const char *key, const char *val) } +gboolean +mu_store_clear (MuStore *store) +{ + g_return_val_if_fail (store, FALSE); + + try { + store->clear(); + return TRUE; + + } MU_XAPIAN_CATCH_BLOCK_RETURN(FALSE); +} + + void mu_store_flush (MuStore *store) { diff --git a/src/mu-store.cc b/src/mu-store.cc index 9cbee9e5..4f109f3d 100644 --- a/src/mu-store.cc +++ b/src/mu-store.cc @@ -43,12 +43,29 @@ #include "mu-contacts.h" -void -mu_store_destroy (MuStore *store) + +MuStore* +mu_store_ref (MuStore *store) { - try { delete store; } MU_XAPIAN_CATCH_BLOCK; + g_return_val_if_fail (store, NULL); + store->ref(); + return store; } +MuStore* +mu_store_unref (MuStore *store) +{ + g_return_val_if_fail (store, NULL); + + if (store->unref() == 0) { + try { delete store; } MU_XAPIAN_CATCH_BLOCK; + } + + return NULL; +} + + + static char* xapian_get_metadata (const gchar *xpath, const gchar *key) @@ -79,76 +96,7 @@ mu_store_database_version (const gchar *xpath) return xapian_get_metadata (xpath, MU_STORE_VERSION_KEY); } -gboolean -mu_store_database_needs_upgrade (const gchar *xpath) -{ - char *version; - gboolean rv; - g_return_val_if_fail (xpath, TRUE); - - version = mu_store_database_version (xpath); - - if (g_strcmp0 (version, MU_XAPIAN_DB_VERSION) == 0) - rv = FALSE; - else - rv = TRUE; - - g_free (version); - - return rv; -} - - -gboolean -mu_store_database_is_empty (const gchar* xpath) -{ - g_return_val_if_fail (xpath, TRUE); - - /* it's 'empty' (non-existant) */ - if (access(xpath, F_OK) != 0 && errno == ENOENT) - return TRUE; - - try { - Xapian::Database db (xpath); - return db.get_doccount() == 0 ? TRUE : FALSE; - - } MU_XAPIAN_CATCH_BLOCK; - - return FALSE; -} - -gboolean -mu_store_database_clear (const gchar *xpath, const char *ccache) -{ - g_return_val_if_fail (xpath, FALSE); - g_return_val_if_fail (ccache, FALSE); - - try { - int rv; - - /* clear the database */ - Xapian::WritableDatabase db - (xpath, Xapian::DB_CREATE_OR_OVERWRITE); - db.flush (); - MU_WRITE_LOG ("emptied database %s", xpath); - - /* clear the contacts cache; this is not totally - * fail-safe, as some other process may still have it - * open... */ - rv = unlink (ccache); - if (rv != 0 && errno != ENOENT) { - g_warning ("failed to remove contacts-cache: %s", - strerror(errno)); - return FALSE; - } - - return TRUE; - - } MU_XAPIAN_CATCH_BLOCK; - - return FALSE; -} gboolean diff --git a/src/mu-store.h b/src/mu-store.h index f9b29cd7..b9fd5528 100644 --- a/src/mu-store.h +++ b/src/mu-store.h @@ -38,7 +38,8 @@ typedef struct _MuStore MuStore; * @param ccachepath path where to cache the contacts information, or NULL * @param err to receive error info or NULL. err->code is MuError value * - * @return a new MuStore object, or NULL in case of error + * @return a new MuStore object with ref count == 1, or NULL in case + * of error; free with mu_store_unref */ MuStore* mu_store_new_writable (const char *xpath, const char *ccachepath, GError **err) @@ -51,18 +52,32 @@ MuStore* mu_store_new_writable (const char *xpath, const char *ccachepath, * @param path the path to the database * @param err to receive error info or NULL. err->code is MuError value * - * @return a new MuStore object, or NULL in case of error + * @return a new MuStore object with ref count == 1, or NULL in case + * of error; free with mu_store_unref */ MuStore* mu_store_new_read_only (const char* xpath, GError **err) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; + /** - * destroy the MuStore object and free resources + * increase the reference count for this store with 1 * - * @param store a valid store, or NULL + * @param store a valid store object + * + * @return the same store with increased ref count, or NULL in case of + * error */ -void mu_store_destroy (MuStore *store); +MuStore* mu_store_ref (MuStore *store); + +/** + * decrease the reference count for this store with 1 + * + * @param store a valid store object + * + * @return NULL + */ +MuStore* mu_store_unref (MuStore *store); /** @@ -116,6 +131,9 @@ XapianDatabase* mu_store_get_read_only_database (MuStore *store); void mu_store_set_batch_size (MuStore *store, guint batchsize); + + + /** * get the numbers of documents in the database * @@ -221,7 +239,15 @@ time_t mu_store_get_timestamp (MuStore *store, const char* msgpath); - +/** + * check whether this store is read-only + * + * @param store a store + * + * @return TRUE if the store is read-only, FALSE otherwise (and in + * case of error) + */ +gboolean mu_store_is_read_only (MuStore *store); /** @@ -263,10 +289,9 @@ char* mu_store_get_metadata (MuStore *store, const char *key) G_GNUC_WARN_UNUSED_RESULT; - /** " * get the version of the xapian database (ie., the version of the - * 'schema' we are using). If this version != MU_XAPIAN_DB_VERSION, + * 'schema' we are using). If this version != MU_STORE_SCHEMA_VERSION, * it's means we need to a full reindex. * * @param xpath path to the xapian database @@ -281,35 +306,24 @@ gchar* mu_store_database_version (const gchar *xpath) G_GNUC_WARN_UNUSED_RESULT; * check whether the database needs to be upgraded, e.g., when it was * created with a different version of mu * - * @param xpath path to the database dir + * @param store a MuStore instance * * @return TRUE if the database needs upgrading, FALSE otherwise */ -gboolean mu_store_database_needs_upgrade (const gchar *xpath); +gboolean mu_store_needs_upgrade (MuStore *store); -/** - * check whether the database is empty (contains 0 documents); in - * addition, a non-existing database is considered 'empty' too - * - * @param xpath path to the xapian database - * - * @return TRUE if the database is empty, FALSE otherwise - */ -gboolean mu_store_database_is_empty (const gchar *xpath); /** * clear the database, ie., remove all of the contents. This is a * destructive operation, but the database can be restored be doing a * full scan of the maildirs. Also, clear the contacts cache file * - * @param xpath path to the database - * @param ccache path to the contacts cache file + * @param store a MuStore object * * @return TRUE if the clearing succeeded, FALSE otherwise. */ -gboolean mu_store_database_clear (const gchar *xpath, - const gchar *ccache); +gboolean mu_store_clear (MuStore *store); /**