* 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
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;
}

View File

@ -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);

View File

@ -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 : "<none>" );
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);
}

View File

@ -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__*/

View File

@ -23,16 +23,17 @@
#include <cstring>
#include <errno.h>
#include <xapian.h>
#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);

View File

@ -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,