* improve checks for database version

This commit is contained in:
Dirk-Jan C. Binnema
2011-01-12 23:13:03 +02:00
parent 87f9dc6cb6
commit d1bf8b3c73
6 changed files with 195 additions and 131 deletions

View File

@ -78,7 +78,7 @@ get_output_format (const char *formatstr)
static void static void
update_warning (void) upgrade_warning (void)
{ {
g_warning ("the database needs to be updated to version %s\n", g_warning ("the database needs to be updated to version %s\n",
MU_XAPIAN_DB_VERSION); MU_XAPIAN_DB_VERSION);
@ -300,14 +300,14 @@ get_query (MuConfig *opts)
static gboolean static gboolean
db_is_ready (const char *xpath) 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 " g_warning ("database is empty; use 'mu index' to "
"add messages"); "add messages");
return FALSE; return FALSE;
} }
if (!mu_util_db_version_up_to_date (xpath)) { if (mu_util_xapian_needs_upgrade (xpath)) {
update_warning (); upgrade_warning ();
return FALSE; return FALSE;
} }

View File

@ -313,14 +313,14 @@ mu_query_new (const char* xpath, GError **err)
return NULL; 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, g_set_error (err, 0, MU_ERROR_XAPIAN_NOT_UPTODATE,
"%s is not up-to-date, needs a full update", "%s is not up-to-date, needs a full update",
xpath); xpath);
return NULL; 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); g_warning ("database %s is empty; nothing to do", xpath);
mqx = g_new (MuQuery, 1); mqx = g_new (MuQuery, 1);

View File

@ -32,6 +32,7 @@
#include "mu-str.h" #include "mu-str.h"
#include "mu-msg-flags.h" #include "mu-msg-flags.h"
/* by default, use transactions of 30000 messages */ /* by default, use transactions of 30000 messages */
#define MU_STORE_DEFAULT_TRX_SIZE 30000 #define MU_STORE_DEFAULT_TRX_SIZE 30000
@ -98,7 +99,8 @@ check_version (MuStore *store)
/* no version yet? it must be a new db then; we'll set the version */ /* no version yet? it must be a new db then; we'll set the version */
if (!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"); g_warning ("failed to set database version");
return FALSE; return FALSE;
} }
@ -108,7 +110,8 @@ check_version (MuStore *store)
/* we have a version, but is it the right one? */ /* we have a version, but is it the right one? */
if (std::strcmp (version, MU_XAPIAN_DB_VERSION) != 0) { if (std::strcmp (version, MU_XAPIAN_DB_VERSION) != 0) {
g_warning ("expected db version %s, but got %s", g_warning ("expected db version %s, but got %s",
MU_XAPIAN_DB_VERSION, version); MU_XAPIAN_DB_VERSION,
version ? version : "<none>" );
return FALSE; return FALSE;
} }
@ -124,8 +127,10 @@ mu_store_new (const char* xpath, guint batchsize, GError **err)
try { try {
store = g_new0(MuStore,1); store = g_new0(MuStore,1);
store->_db = new Xapian::WritableDatabase store->_db = new Xapian::WritableDatabase
(xpath,Xapian::DB_CREATE_OR_OPEN); (xpath,Xapian::DB_CREATE_OR_OPEN);
if (!check_version (store)) { if (!check_version (store)) {
mu_store_destroy (store); mu_store_destroy (store);
return NULL; return NULL;
@ -192,27 +197,20 @@ mu_store_version (MuStore *store)
{ {
g_return_val_if_fail (store, NULL); g_return_val_if_fail (store, NULL);
try {
std::string v;
v = store->_db->get_metadata (MU_XAPIAN_VERSION_KEY);
g_free (store->_version); g_free (store->_version);
return store->_version = return store->_version =
v.empty() ? NULL : g_strdup (v.c_str()); mu_store_get_metadata (store, MU_STORE_VERSION_KEY);
} MU_XAPIAN_CATCH_BLOCK;
return NULL;
} }
gboolean gboolean
mu_store_set_version (MuStore *store, const char* version) mu_store_set_metadata (MuStore *store, const char *key, const char *val)
{ {
g_return_val_if_fail (store, FALSE); g_return_val_if_fail (store, FALSE);
g_return_val_if_fail (version, FALSE); g_return_val_if_fail (key, FALSE);
g_return_val_if_fail (val, FALSE);
try { try {
store->_db->set_metadata (MU_XAPIAN_VERSION_KEY, version); store->_db->set_metadata (key, val);
return TRUE; return TRUE;
} MU_XAPIAN_CATCH_BLOCK; } MU_XAPIAN_CATCH_BLOCK;
@ -221,6 +219,23 @@ mu_store_set_version (MuStore *store, const char* version)
} }
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;
}
static void static void
begin_trx_if (MuStore *store, gboolean cond) begin_trx_if (MuStore *store, gboolean cond)
{ {
@ -550,35 +565,30 @@ mu_store_contains_message (MuStore *store, const char* path)
time_t time_t
mu_store_get_timestamp (MuStore *store, const char* msgpath) mu_store_get_timestamp (MuStore *store, const char* msgpath)
{ {
char *stampstr;
g_return_val_if_fail (store, 0); g_return_val_if_fail (store, 0);
g_return_val_if_fail (msgpath, 0); g_return_val_if_fail (msgpath, 0);
try { stampstr = mu_store_get_metadata (store, msgpath);
const std::string stamp (store->_db->get_metadata (msgpath)); if (!stampstr)
if (stamp.empty()) return (time_t)0;
return 0; else
return (time_t) g_ascii_strtoull (stampstr, NULL, 10);
return (time_t) g_ascii_strtoull (stamp.c_str(), NULL, 10);
} MU_XAPIAN_CATCH_BLOCK_RETURN (0);
return 0;
} }
void gboolean
mu_store_set_timestamp (MuStore *store, const char* msgpath, mu_store_set_timestamp (MuStore *store, const char* msgpath,
time_t stamp) time_t stamp)
{ {
g_return_if_fail (store);
g_return_if_fail (msgpath);
try {
char buf[21]; char buf[21];
sprintf (buf, "%" G_GUINT64_FORMAT, (guint64)stamp);
store->_db->set_metadata (msgpath, buf);
} MU_XAPIAN_CATCH_BLOCK; g_return_val_if_fail (store, FALSE);
g_return_val_if_fail (msgpath, FALSE);
sprintf (buf, "%" G_GUINT64_FORMAT, (guint64)stamp);
return mu_store_set_metadata (store, msgpath, buf);
} }

View File

@ -30,6 +30,7 @@ G_BEGIN_DECLS
struct _MuStore; struct _MuStore;
typedef struct _MuStore MuStore; typedef struct _MuStore MuStore;
/** /**
* create a new Xapian store, a place to store documents * 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); 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 * try to flush/commit all outstanding work
@ -135,9 +125,10 @@ gboolean mu_store_contains_message (MuStore *store,
* @param store a valid store * @param store a valid store
* @param msgpath path to a maildir * @param msgpath path to a maildir
* @param stamp a timestamp * @param stamp a timestamp
*
* @return TRUE if setting the timestamp succeeded, FALSE otherwise
*/ */
void mu_store_set_timestamp (MuStore *store, gboolean mu_store_set_timestamp (MuStore *store, const char* msgpath,
const char* msgpath,
time_t stamp); time_t stamp);
/** /**
@ -151,6 +142,10 @@ void mu_store_set_timestamp (MuStore *store,
time_t mu_store_get_timestamp (MuStore *store, time_t mu_store_get_timestamp (MuStore *store,
const char* msgpath); const char* msgpath);
/** /**
* call a function for each document in the database * call a function for each document in the database
* *
@ -167,6 +162,30 @@ MuResult mu_store_foreach (MuStore *self,
MuStoreForeachFunc func, MuStoreForeachFunc func,
void *user_data); 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 G_END_DECLS
#endif /*__MU_STORE_H__*/ #endif /*__MU_STORE_H__*/

View File

@ -23,15 +23,16 @@
#include <cstring> #include <cstring>
#include <errno.h> #include <errno.h>
#include <xapian.h> #include <xapian.h>
#include "mu-util.h" #include "mu-util.h"
char* 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 (xpath, NULL);
g_return_val_if_fail (key, NULL);
if (!access(xpath, F_OK) == 0) { if (!access(xpath, F_OK) == 0) {
g_warning ("cannot access %s: %s", xpath, strerror(errno)); g_warning ("cannot access %s: %s", xpath, strerror(errno));
@ -40,10 +41,8 @@ mu_util_db_version (const gchar *xpath)
try { try {
Xapian::Database db (xpath); Xapian::Database db (xpath);
const std::string version const std::string val(db.get_metadata (key));
(db.get_metadata (MU_XAPIAN_VERSION_KEY)); return val.empty() ? NULL : g_strdup (val.c_str());
return version.empty() ? NULL : g_strdup (version.c_str());
} MU_XAPIAN_CATCH_BLOCK; } MU_XAPIAN_CATCH_BLOCK;
@ -52,26 +51,60 @@ mu_util_db_version (const gchar *xpath)
gboolean 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; g_return_val_if_fail (xpath, NULL);
gboolean uptodate; g_return_val_if_fail (key, NULL);
g_return_val_if_fail (val, NULL);
g_return_val_if_fail (xpath, FALSE); if (!access(xpath, F_OK) == 0) {
g_warning ("cannot access %s: %s", xpath, strerror(errno));
version = mu_util_db_version (xpath);
if (!version)
return FALSE; 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;
uptodate = (std::strcmp (version, MU_XAPIAN_DB_VERSION) == 0);
g_free (version); g_free (version);
return uptodate; return rv;
} }
gboolean gboolean
mu_util_db_is_empty (const gchar* xpath) mu_util_xapian_is_empty (const gchar* xpath)
{ {
g_return_val_if_fail (xpath, TRUE); g_return_val_if_fail (xpath, TRUE);
@ -89,7 +122,7 @@ mu_util_db_is_empty (const gchar* xpath)
} }
gboolean gboolean
mu_util_clear_database (const gchar *xpath) mu_util_xapian_clear (const gchar *xpath)
{ {
g_return_val_if_fail (xpath, FALSE); g_return_val_if_fail (xpath, FALSE);

View File

@ -120,7 +120,6 @@ int mu_util_create_writeable_fd (const char* path, mode_t mode,
G_GNUC_WARN_UNUSED_RESULT; G_GNUC_WARN_UNUSED_RESULT;
/** /**
* check if file is local, ie. on the local file system. this means * 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 * 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, * 'schema' we are using). If this version != MU_XAPIAN_DB_VERSION,
* it's means we need to a full reindex. * 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 * @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 * (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 * @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 * 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. * @return TRUE if the clearing succeeded, FALSE otherwise.
*/ */
gboolean mu_util_clear_database (const gchar *xpath); gboolean mu_util_xapian_clear (const gchar *xpath);
/** /**
@ -253,10 +248,17 @@ unsigned char mu_util_get_dtype_with_lstat (const char *path);
} }
#define MU_XAPIAN_CATCH_BLOCK_G_ERROR(GE,E) \ #define MU_XAPIAN_CATCH_BLOCK_G_ERROR(GE,E) \
catch (const Xapian::Error &xerr) { \ catch (const Xapian::DatabaseLockError &xerr) { \
g_set_error ((GE),0,(E), \ g_set_error ((GE),0,MU_ERROR_XAPIAN_CANNOT_GET_WRITELOCK, \
"%s: xapian error '%s'", \ "%s: xapian error '%s'", \
__FUNCTION__, xerr.get_msg().c_str()); \ __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 (...) { \ } catch (...) { \
g_set_error ((GE),0,(MU_ERROR_INTERNAL), \ g_set_error ((GE),0,(MU_ERROR_INTERNAL), \
"%s: caught exception", __FUNCTION__); \ "%s: caught exception", __FUNCTION__); \
@ -286,14 +288,16 @@ unsigned char mu_util_get_dtype_with_lstat (const char *path);
return (R); \ return (R); \
} }
/* the name of the (leaf) dir which has the xapian database */ /* the name of the (leaf) dir which has the xapian database */
#define MU_XAPIAN_DIR_NAME "xapian" #define MU_XAPIAN_DIR_NAME "xapian"
#define MU_XAPIAN_VERSION_KEY "db_version"
/* name of the bookmark file */ /* name of the bookmark file */
#define MU_BOOKMARK_FILENAME "bookmarks" #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 * log something in the log file; note, we use G_LOG_LEVEL_INFO
* for such messages * for such messages
@ -316,24 +320,22 @@ typedef enum _MuResult MuResult;
enum _MuExitCode { enum _MuExitCode {
MU_EXITCODE_OK = 0, MU_EXITCODE_OK = 0,
MU_EXITCODE_ERROR = 1, MU_EXITCODE_ERROR = 1,
MU_EXITCODE_NO_MATCHES = 2 MU_EXITCODE_NO_MATCHES = 2,
MU_EXITCODE_DB_LOCKED = 3,
MU_EXITCODE_DB_CORRUPTED = 4
}; };
typedef enum _MuExitCode MuExitCode; typedef enum _MuExitCode MuExitCode;
enum _MuError { enum _MuError {
/* general xapian related error */ MU_ERROR_XAPIAN, /* general xapian related error */
MU_ERROR_XAPIAN, MU_ERROR_XAPIAN_CANNOT_GET_WRITELOCK, /* can't get write lock */
/* xapian dir is not accessible */ MU_ERROR_XAPIAN_CORRUPTION, /* database corruption */
MU_ERROR_XAPIAN_DIR, MU_ERROR_XAPIAN_DIR, /* xapian dir is not accessible */
/* database version is not uptodate (ie. not compatible with MU_ERROR_XAPIAN_NOT_UPTODATE, /* database version is not uptodate */
* the version that mu expects) */ MU_ERROR_XAPIAN_MISSING_DATA, /* missing data for a document */
MU_ERROR_XAPIAN_NOT_UPTODATE, MU_ERROR_QUERY, /* (parsing) error in the query */
/* missing data for a document */
MU_ERROR_XAPIAN_MISSING_DATA, MU_ERROR_GMIME, /* gmime parsing related error */
/* (parsnng) error in the query */
MU_ERROR_QUERY,
/* gmime parsing related error */
MU_ERROR_GMIME,
/* File errors */ /* File errors */
MU_ERROR_FILE_INVALID_SOURCE, MU_ERROR_FILE_INVALID_SOURCE,