From d1bf8b3c736a3a9774f1f27edc8329bc0a8fa68f Mon Sep 17 00:00:00 2001 From: "Dirk-Jan C. Binnema" Date: Wed, 12 Jan 2011 23:13:03 +0200 Subject: [PATCH] * improve checks for database version --- src/mu-cmd-find.c | 8 ++-- src/mu-query.cc | 4 +- src/mu-store.cc | 98 ++++++++++++++++++++++++++--------------------- src/mu-store.h | 61 +++++++++++++++++++---------- src/mu-util-db.cc | 75 ++++++++++++++++++++++++++---------- src/mu-util.h | 80 +++++++++++++++++++------------------- 6 files changed, 195 insertions(+), 131 deletions(-) diff --git a/src/mu-cmd-find.c b/src/mu-cmd-find.c index 4d8bcc1a..1554e549 100644 --- a/src/mu-cmd-find.c +++ b/src/mu-cmd-find.c @@ -78,7 +78,7 @@ get_output_format (const char *formatstr) static void -update_warning (void) +upgrade_warning (void) { g_warning ("the database needs to be updated to version %s\n", MU_XAPIAN_DB_VERSION); @@ -300,14 +300,14 @@ get_query (MuConfig *opts) static gboolean db_is_ready (const char *xpath) { - if (mu_util_db_is_empty (xpath)) { + if (mu_util_xapian_is_empty (xpath)) { g_warning ("database is empty; use 'mu index' to " "add messages"); return FALSE; } - if (!mu_util_db_version_up_to_date (xpath)) { - update_warning (); + if (mu_util_xapian_needs_upgrade (xpath)) { + upgrade_warning (); return FALSE; } diff --git a/src/mu-query.cc b/src/mu-query.cc index a6b69a57..ecd2283f 100644 --- a/src/mu-query.cc +++ b/src/mu-query.cc @@ -313,14 +313,14 @@ mu_query_new (const char* xpath, GError **err) return NULL; } - if (!mu_util_db_version_up_to_date (xpath)) { + if (mu_util_xapian_needs_upgrade (xpath)) { g_set_error (err, 0, MU_ERROR_XAPIAN_NOT_UPTODATE, "%s is not up-to-date, needs a full update", xpath); return NULL; } - if (mu_util_db_is_empty (xpath)) + if (mu_util_xapian_is_empty (xpath)) g_warning ("database %s is empty; nothing to do", xpath); mqx = g_new (MuQuery, 1); diff --git a/src/mu-store.cc b/src/mu-store.cc index 6e470383..4cda56b1 100644 --- a/src/mu-store.cc +++ b/src/mu-store.cc @@ -32,6 +32,7 @@ #include "mu-str.h" #include "mu-msg-flags.h" + /* by default, use transactions of 30000 messages */ #define MU_STORE_DEFAULT_TRX_SIZE 30000 @@ -93,12 +94,13 @@ check_version (MuStore *store) { /* FIXME clear up versioning semantics */ const gchar *version; - + version = mu_store_version (store); /* no version yet? it must be a new db then; we'll set the version */ if (!version) { - if (!mu_store_set_version (store, MU_XAPIAN_DB_VERSION)) { + if (!mu_store_set_metadata (store, MU_STORE_VERSION_KEY, + MU_XAPIAN_DB_VERSION)) { g_warning ("failed to set database version"); return FALSE; } @@ -108,7 +110,8 @@ check_version (MuStore *store) /* 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); + MU_XAPIAN_DB_VERSION, + version ? version : "" ); return FALSE; } @@ -124,8 +127,10 @@ mu_store_new (const char* xpath, guint batchsize, GError **err) try { store = g_new0(MuStore,1); + store->_db = new Xapian::WritableDatabase (xpath,Xapian::DB_CREATE_OR_OPEN); + if (!check_version (store)) { mu_store_destroy (store); return NULL; @@ -191,34 +196,44 @@ const char* mu_store_version (MuStore *store) { g_return_val_if_fail (store, NULL); - - try { - std::string v; - v = store->_db->get_metadata (MU_XAPIAN_VERSION_KEY); - - g_free (store->_version); - return store->_version = - v.empty() ? NULL : g_strdup (v.c_str()); + g_free (store->_version); + return store->_version = + mu_store_get_metadata (store, MU_STORE_VERSION_KEY); +} + +gboolean +mu_store_set_metadata (MuStore *store, const char *key, const char *val) +{ + g_return_val_if_fail (store, FALSE); + g_return_val_if_fail (key, FALSE); + g_return_val_if_fail (val, FALSE); + + try { + store->_db->set_metadata (key, val); + return TRUE; + + } MU_XAPIAN_CATCH_BLOCK; + + return FALSE; +} + + +char* +mu_store_get_metadata (MuStore *store, const char *key) +{ + g_return_val_if_fail (store, NULL); + g_return_val_if_fail (key, NULL); + + try { + const std::string val (store->_db->get_metadata (key)); + return val.empty() ? NULL : g_strdup (val.c_str()); + } MU_XAPIAN_CATCH_BLOCK; return NULL; } -gboolean -mu_store_set_version (MuStore *store, const char* version) -{ - g_return_val_if_fail (store, FALSE); - g_return_val_if_fail (version, FALSE); - - try { - store->_db->set_metadata (MU_XAPIAN_VERSION_KEY, version); - return TRUE; - - } MU_XAPIAN_CATCH_BLOCK; - - return FALSE; -} static void @@ -550,35 +565,30 @@ mu_store_contains_message (MuStore *store, const char* path) time_t mu_store_get_timestamp (MuStore *store, const char* msgpath) { + char *stampstr; + g_return_val_if_fail (store, 0); g_return_val_if_fail (msgpath, 0); - - try { - const std::string stamp (store->_db->get_metadata (msgpath)); - if (stamp.empty()) - return 0; - - return (time_t) g_ascii_strtoull (stamp.c_str(), NULL, 10); - - } MU_XAPIAN_CATCH_BLOCK_RETURN (0); - return 0; + stampstr = mu_store_get_metadata (store, msgpath); + if (!stampstr) + return (time_t)0; + else + return (time_t) g_ascii_strtoull (stampstr, NULL, 10); } -void +gboolean mu_store_set_timestamp (MuStore *store, const char* msgpath, time_t stamp) { - g_return_if_fail (store); - g_return_if_fail (msgpath); + char buf[21]; + + g_return_val_if_fail (store, FALSE); + g_return_val_if_fail (msgpath, FALSE); - try { - char buf[21]; - sprintf (buf, "%" G_GUINT64_FORMAT, (guint64)stamp); - store->_db->set_metadata (msgpath, buf); - - } MU_XAPIAN_CATCH_BLOCK; + sprintf (buf, "%" G_GUINT64_FORMAT, (guint64)stamp); + return mu_store_set_metadata (store, msgpath, buf); } diff --git a/src/mu-store.h b/src/mu-store.h index d8d79d8e..78a8069b 100644 --- a/src/mu-store.h +++ b/src/mu-store.h @@ -30,6 +30,7 @@ G_BEGIN_DECLS struct _MuStore; typedef struct _MuStore MuStore; + /** * create a new Xapian store, a place to store documents * @@ -73,17 +74,6 @@ unsigned mu_store_count (MuStore *store); */ const char* mu_store_version (MuStore *store); -/** - * set the version string for the database - * - * @param store a valid MuStore - * @param version the version string (non-NULL) - * - * @return TRUE if setting the version succeeded, FALSE otherwise - */ -gboolean mu_store_set_version (MuStore *store, - const char* version); - /** * try to flush/commit all outstanding work @@ -100,7 +90,7 @@ void mu_store_flush (MuStore *store); * * @return TRUE if it succeeded, FALSE otherwise */ -MuResult mu_store_store (MuStore *store, MuMsg *msg); +MuResult mu_store_store (MuStore *store, MuMsg *msg); /** @@ -114,8 +104,8 @@ MuResult mu_store_store (MuStore *store, MuMsg *msg); * * @return TRUE if it succeeded, FALSE otherwise */ -MuResult mu_store_remove (MuStore *store, - const char* msgpath); +MuResult mu_store_remove (MuStore *store, + const char* msgpath); /** @@ -126,8 +116,8 @@ MuResult mu_store_remove (MuStore *store, * * @return */ -gboolean mu_store_contains_message (MuStore *store, - const char* path); +gboolean mu_store_contains_message (MuStore *store, + const char* path); /** * store a timestamp for a directory @@ -135,10 +125,11 @@ gboolean mu_store_contains_message (MuStore *store, * @param store a valid store * @param msgpath path to a maildir * @param stamp a timestamp + * + * @return TRUE if setting the timestamp succeeded, FALSE otherwise */ -void mu_store_set_timestamp (MuStore *store, - const char* msgpath, - time_t stamp); +gboolean mu_store_set_timestamp (MuStore *store, const char* msgpath, + time_t stamp); /** * get the timestamp for a directory @@ -148,8 +139,12 @@ void mu_store_set_timestamp (MuStore *store, * * @return the timestamp, or 0 in case of error */ -time_t mu_store_get_timestamp (MuStore *store, - const char* msgpath); +time_t mu_store_get_timestamp (MuStore *store, + const char* msgpath); + + + + /** * call a function for each document in the database @@ -167,6 +162,30 @@ MuResult mu_store_foreach (MuStore *self, MuStoreForeachFunc func, void *user_data); +/** + * set metadata for this MuStore + * + * @param store a store + * @param key metadata key + * @param val metadata value + * + * @return TRUE if succeeded, FALSE otherwise + */ +gboolean mu_store_set_metadata (MuStore *store, const char *key, const char *val); + +/** + * get metadata for this MuStore + * + * @param store a store + * @param key the metadata key + * + * @return the value of the metadata (gfree when done with it), or + * NULL in case of error + */ +char* mu_store_get_metadata (MuStore *store, const char *key) + G_GNUC_WARN_UNUSED_RESULT; + + G_END_DECLS #endif /*__MU_STORE_H__*/ diff --git a/src/mu-util-db.cc b/src/mu-util-db.cc index 2938b7cb..609770f5 100644 --- a/src/mu-util-db.cc +++ b/src/mu-util-db.cc @@ -23,16 +23,17 @@ #include #include - #include #include "mu-util.h" + char* -mu_util_db_version (const gchar *xpath) +mu_util_xapian_get_metadata (const gchar *xpath, const gchar *key) { g_return_val_if_fail (xpath, NULL); - + g_return_val_if_fail (key, NULL); + if (!access(xpath, F_OK) == 0) { g_warning ("cannot access %s: %s", xpath, strerror(errno)); return NULL; @@ -40,38 +41,70 @@ mu_util_db_version (const gchar *xpath) try { Xapian::Database db (xpath); - const std::string version - (db.get_metadata (MU_XAPIAN_VERSION_KEY)); - - return version.empty() ? NULL : g_strdup (version.c_str()); + const std::string val(db.get_metadata (key)); + return val.empty() ? NULL : g_strdup (val.c_str()); } MU_XAPIAN_CATCH_BLOCK; - + return NULL; } gboolean -mu_util_db_version_up_to_date (const gchar *xpath) +mu_util_xapian_set_metadata (const gchar *xpath, + const gchar *key, const gchar *val) { - gchar *version; - gboolean uptodate; - - g_return_val_if_fail (xpath, FALSE); - - version = mu_util_db_version (xpath); - if (!version) - return FALSE; + g_return_val_if_fail (xpath, NULL); + g_return_val_if_fail (key, NULL); + g_return_val_if_fail (val, NULL); - uptodate = (std::strcmp (version, MU_XAPIAN_DB_VERSION) == 0); + if (!access(xpath, F_OK) == 0) { + g_warning ("cannot access %s: %s", xpath, strerror(errno)); + return FALSE; + } + + try { + Xapian::WritableDatabase db (xpath, Xapian::DB_OPEN); + db.set_metadata (key, val); + return TRUE; + + } MU_XAPIAN_CATCH_BLOCK; + + return FALSE; +} + + +char* +mu_util_xapian_dbversion (const gchar *xpath) +{ + g_return_val_if_fail (xpath, NULL); + + return mu_util_xapian_get_metadata (xpath, MU_STORE_VERSION_KEY); +} + +gboolean +mu_util_xapian_needs_upgrade (const gchar *xpath) +{ + char *version; + gboolean rv; + + g_return_val_if_fail (xpath, TRUE); + + version = mu_util_xapian_dbversion (xpath); + + if (g_strcmp0 (version, MU_XAPIAN_DB_VERSION) == 0) + rv = FALSE; + else + rv = TRUE; + g_free (version); - return uptodate; + return rv; } gboolean -mu_util_db_is_empty (const gchar* xpath) +mu_util_xapian_is_empty (const gchar* xpath) { g_return_val_if_fail (xpath, TRUE); @@ -89,7 +122,7 @@ mu_util_db_is_empty (const gchar* xpath) } gboolean -mu_util_clear_database (const gchar *xpath) +mu_util_xapian_clear (const gchar *xpath) { g_return_val_if_fail (xpath, FALSE); diff --git a/src/mu-util.h b/src/mu-util.h index 195d978f..90e61092 100644 --- a/src/mu-util.h +++ b/src/mu-util.h @@ -120,7 +120,6 @@ int mu_util_create_writeable_fd (const char* path, mode_t mode, G_GNUC_WARN_UNUSED_RESULT; - /** * check if file is local, ie. on the local file system. this means * that it's either having a file URI, *or* that it's an existing file @@ -146,7 +145,7 @@ gboolean mu_util_play (const char *path, /** - * get the version of the xapian database (ie., the version of the +" * get the version of the xapian database (ie., the version of the * 'schema' we are using). If this version != MU_XAPIAN_DB_VERSION, * it's means we need to a full reindex. * @@ -155,7 +154,11 @@ gboolean mu_util_play (const char *path, * @return the version of the database as a newly allocated string * (free with g_free); if there is no version yet, it will return NULL */ -gchar* mu_util_db_version (const gchar *xpath) G_GNUC_WARN_UNUSED_RESULT; +gchar* mu_util_xapian_dbversion (const gchar *xpath) G_GNUC_WARN_UNUSED_RESULT; + + + +gboolean mu_util_xapian_needs_upgrade (const gchar *xpath); /** @@ -166,16 +169,8 @@ gchar* mu_util_db_version (const gchar *xpath) G_GNUC_WARN_UNUSED_RESULT; * * @return TRUE if the database is empty, FALSE otherwise */ -gboolean mu_util_db_is_empty (const gchar *xpath); +gboolean mu_util_xapian_is_empty (const gchar *xpath); -/** - * check if the 'schema' of the current database is up-to-date - * - * @param xpath path to the xapian database - * - * @return TRUE if it's up-to-date, FALSE otherwise - */ -gboolean mu_util_db_version_up_to_date (const gchar *xpath); /** * clear the database, ie., remove all of the contents. This is a @@ -186,7 +181,7 @@ gboolean mu_util_db_version_up_to_date (const gchar *xpath); * * @return TRUE if the clearing succeeded, FALSE otherwise. */ -gboolean mu_util_clear_database (const gchar *xpath); +gboolean mu_util_xapian_clear (const gchar *xpath); /** @@ -252,14 +247,21 @@ unsigned char mu_util_get_dtype_with_lstat (const char *path); g_critical ("%s: caught exception", __FUNCTION__); \ } -#define MU_XAPIAN_CATCH_BLOCK_G_ERROR(GE,E) \ - catch (const Xapian::Error &xerr) { \ - g_set_error ((GE),0,(E), \ - "%s: xapian error '%s'", \ - __FUNCTION__, xerr.get_msg().c_str()); \ - } catch (...) { \ - g_set_error ((GE),0,(MU_ERROR_INTERNAL), \ - "%s: caught exception", __FUNCTION__); \ +#define MU_XAPIAN_CATCH_BLOCK_G_ERROR(GE,E) \ + catch (const Xapian::DatabaseLockError &xerr) { \ + g_set_error ((GE),0,MU_ERROR_XAPIAN_CANNOT_GET_WRITELOCK, \ + "%s: xapian error '%s'", \ + __FUNCTION__, xerr.get_msg().c_str()); \ + } catch (const Xapian::DatabaseCorruptError &xerr ) { \ + g_set_error ((GE),0,MU_ERROR_XAPIAN_CORRUPTION, \ + "%s: xapian error '%s'", \ + __FUNCTION__, xerr.get_msg().c_str()); \ + } catch (const Xapian::Error &xerr) { \ + g_set_error ((GE),0,(E), "%s: xapian error '%s'", \ + __FUNCTION__, xerr.get_msg().c_str()); \ + } catch (...) { \ + g_set_error ((GE),0,(MU_ERROR_INTERNAL), \ + "%s: caught exception", __FUNCTION__); \ } @@ -286,14 +288,16 @@ unsigned char mu_util_get_dtype_with_lstat (const char *path); return (R); \ } - /* the name of the (leaf) dir which has the xapian database */ #define MU_XAPIAN_DIR_NAME "xapian" -#define MU_XAPIAN_VERSION_KEY "db_version" /* name of the bookmark file */ #define MU_BOOKMARK_FILENAME "bookmarks" +/* metdata key for the xapian 'schema' version */ +#define MU_STORE_VERSION_KEY "db_version" + + /** * log something in the log file; note, we use G_LOG_LEVEL_INFO * for such messages @@ -314,26 +318,24 @@ enum _MuResult { typedef enum _MuResult MuResult; enum _MuExitCode { - MU_EXITCODE_OK = 0, - MU_EXITCODE_ERROR = 1, - MU_EXITCODE_NO_MATCHES = 2 + MU_EXITCODE_OK = 0, + MU_EXITCODE_ERROR = 1, + MU_EXITCODE_NO_MATCHES = 2, + MU_EXITCODE_DB_LOCKED = 3, + MU_EXITCODE_DB_CORRUPTED = 4 }; typedef enum _MuExitCode MuExitCode; enum _MuError { - /* general xapian related error */ - MU_ERROR_XAPIAN, - /* xapian dir is not accessible */ - MU_ERROR_XAPIAN_DIR, - /* database version is not uptodate (ie. not compatible with - * the version that mu expects) */ - MU_ERROR_XAPIAN_NOT_UPTODATE, - /* missing data for a document */ - MU_ERROR_XAPIAN_MISSING_DATA, - /* (parsnng) error in the query */ - MU_ERROR_QUERY, - /* gmime parsing related error */ - MU_ERROR_GMIME, + MU_ERROR_XAPIAN, /* general xapian related error */ + MU_ERROR_XAPIAN_CANNOT_GET_WRITELOCK, /* can't get write lock */ + MU_ERROR_XAPIAN_CORRUPTION, /* database corruption */ + MU_ERROR_XAPIAN_DIR, /* xapian dir is not accessible */ + MU_ERROR_XAPIAN_NOT_UPTODATE, /* database version is not uptodate */ + MU_ERROR_XAPIAN_MISSING_DATA, /* missing data for a document */ + MU_ERROR_QUERY, /* (parsing) error in the query */ + + MU_ERROR_GMIME, /* gmime parsing related error */ /* File errors */ MU_ERROR_FILE_INVALID_SOURCE,