const char *prefix;
} prefix_t;
-#define NOTMUCH_DATABASE_VERSION 1
+#define NOTMUCH_DATABASE_VERSION 2
#define STRINGIFY(s) _SUB_STRINGIFY(s)
#define _SUB_STRINGIFY(s) #s
* In addition, terms from the content of the message are added with
* "from", "to", "attachment", and "subject" prefixes for use by the
* user in searching. Similarly, terms from the path of the mail
- * message are added with a "folder" prefix. But the database doesn't
- * really care itself about any of these.
+ * message are added with "folder" and "path" prefixes. But the
+ * database doesn't really care itself about any of these.
*
* The data portion of a mail document is empty.
*
{ "thread", "G" },
{ "tag", "K" },
{ "is", "K" },
- { "id", "Q" }
+ { "id", "Q" },
+ { "path", "P" },
+ /*
+ * Without the ":", since this is a multi-letter prefix, Xapian
+ * will add a colon itself if the first letter of the path is
+ * upper-case ASCII. Including the ":" forces there to always be a
+ * colon, which keeps our own logic simpler.
+ */
+ { "folder", "XFOLDER:" },
};
static prefix_t PROBABILISTIC_PREFIX[]= {
{ "to", "XTO" },
{ "attachment", "XATTACHMENT" },
{ "subject", "XSUBJECT"},
- { "folder", "XFOLDER"}
};
const char *
}
#if HAVE_XAPIAN_COMPACT
-static int unlink_cb (const char *path,
- unused (const struct stat *sb),
- unused (int type),
- unused (struct FTW *ftw))
+static int
+unlink_cb (const char *path,
+ unused (const struct stat *sb),
+ unused (int type),
+ unused (struct FTW *ftw))
{
- return remove(path);
+ return remove (path);
}
-static int rmtree (const char *path)
+static int
+rmtree (const char *path)
{
- return nftw(path, unlink_cb, 64, FTW_DEPTH | FTW_PHYS);
+ return nftw (path, unlink_cb, 64, FTW_DEPTH | FTW_PHYS);
}
class NotmuchCompactor : public Xapian::Compactor
{
notmuch_compact_status_cb_t status_cb;
+ void *status_closure;
public:
- NotmuchCompactor(notmuch_compact_status_cb_t cb) : status_cb(cb) { }
+ NotmuchCompactor(notmuch_compact_status_cb_t cb, void *closure) :
+ status_cb (cb), status_closure (closure) { }
virtual void
set_status (const std::string &table, const std::string &status)
{
- char* msg;
+ char *msg;
if (status_cb == NULL)
return;
- if (status.length() == 0)
+ if (status.length () == 0)
msg = talloc_asprintf (NULL, "compacting table %s", table.c_str());
else
msg = talloc_asprintf (NULL, " %s", status.c_str());
return;
}
- status_cb(msg);
- talloc_free(msg);
+ status_cb (msg, status_closure);
+ talloc_free (msg);
}
};
* compaction process to protect data integrity.
*/
notmuch_status_t
-notmuch_database_compact (const char* path,
- const char* backup_path,
- notmuch_compact_status_cb_t status_cb)
+notmuch_database_compact (const char *path,
+ const char *backup_path,
+ notmuch_compact_status_cb_t status_cb,
+ void *closure)
{
void *local;
char *notmuch_path, *xapian_path, *compact_xapian_path;
- char *old_xapian_path = NULL;
notmuch_status_t ret = NOTMUCH_STATUS_SUCCESS;
notmuch_database_t *notmuch = NULL;
struct stat statbuf;
+ notmuch_bool_t keep_backup;
local = talloc_new (NULL);
if (! local)
return NOTMUCH_STATUS_OUT_OF_MEMORY;
- ret = notmuch_database_open(path, NOTMUCH_DATABASE_MODE_READ_WRITE, ¬much);
+ ret = notmuch_database_open (path, NOTMUCH_DATABASE_MODE_READ_WRITE, ¬much);
if (ret) {
goto DONE;
}
goto DONE;
}
- if (backup_path != NULL) {
- if (! (old_xapian_path = talloc_asprintf (local, "%s/xapian.old", backup_path))) {
+ if (backup_path == NULL) {
+ if (! (backup_path = talloc_asprintf (local, "%s.old", xapian_path))) {
ret = NOTMUCH_STATUS_OUT_OF_MEMORY;
goto DONE;
}
+ keep_backup = FALSE;
+ }
+ else {
+ keep_backup = TRUE;
+ }
- if (stat(old_xapian_path, &statbuf) != -1) {
- fprintf (stderr, "Backup path already exists: %s\n", old_xapian_path);
- ret = NOTMUCH_STATUS_FILE_ERROR;
- goto DONE;
- }
- if (errno != ENOENT) {
- fprintf (stderr, "Unknown error while stat()ing backup path: %s\n",
- strerror(errno));
- goto DONE;
- }
+ if (stat (backup_path, &statbuf) != -1) {
+ fprintf (stderr, "Path already exists: %s\n", backup_path);
+ ret = NOTMUCH_STATUS_FILE_ERROR;
+ goto DONE;
+ }
+ if (errno != ENOENT) {
+ fprintf (stderr, "Unknown error while stat()ing path: %s\n",
+ strerror (errno));
+ ret = NOTMUCH_STATUS_FILE_ERROR;
+ goto DONE;
}
+ /* Unconditionally attempt to remove old work-in-progress database (if
+ * any). This is "protected" by database lock. If this fails due to write
+ * errors (etc), the following code will fail and provide error message.
+ */
+ (void) rmtree (compact_xapian_path);
+
try {
- NotmuchCompactor compactor(status_cb);
-
- compactor.set_renumber(false);
- compactor.add_source(xapian_path);
- compactor.set_destdir(compact_xapian_path);
- compactor.compact();
- } catch (Xapian::InvalidArgumentError e) {
- fprintf (stderr, "Error while compacting: %s\n", e.get_msg().c_str());
+ NotmuchCompactor compactor (status_cb, closure);
+
+ compactor.set_renumber (false);
+ compactor.add_source (xapian_path);
+ compactor.set_destdir (compact_xapian_path);
+ compactor.compact ();
+ } catch (const Xapian::Error &error) {
+ fprintf (stderr, "Error while compacting: %s\n", error.get_msg().c_str());
ret = NOTMUCH_STATUS_XAPIAN_EXCEPTION;
goto DONE;
}
- if (old_xapian_path != NULL) {
- if (rename(xapian_path, old_xapian_path)) {
- fprintf (stderr, "Error moving old database out of the way\n");
- ret = NOTMUCH_STATUS_FILE_ERROR;
- goto DONE;
- }
- } else {
- rmtree(xapian_path);
+ if (rename (xapian_path, backup_path)) {
+ fprintf (stderr, "Error moving %s to %s: %s\n",
+ xapian_path, backup_path, strerror (errno));
+ ret = NOTMUCH_STATUS_FILE_ERROR;
+ goto DONE;
}
- if (rename(compact_xapian_path, xapian_path)) {
- fprintf (stderr, "Error moving compacted database\n");
+ if (rename (compact_xapian_path, xapian_path)) {
+ fprintf (stderr, "Error moving %s to %s: %s\n",
+ compact_xapian_path, xapian_path, strerror (errno));
ret = NOTMUCH_STATUS_FILE_ERROR;
goto DONE;
}
-DONE:
+ if (! keep_backup) {
+ if (rmtree (backup_path)) {
+ fprintf (stderr, "Error removing old database %s: %s\n",
+ backup_path, strerror (errno));
+ ret = NOTMUCH_STATUS_FILE_ERROR;
+ goto DONE;
+ }
+ }
+
+ DONE:
if (notmuch)
notmuch_database_destroy (notmuch);
- talloc_free(local);
+ talloc_free (local);
return ret;
}
#else
notmuch_status_t
-notmuch_database_compact (unused (const char* path),
- unused (const char* backup_path),
- unused (notmuch_compact_status_cb_t status_cb))
+notmuch_database_compact (unused (const char *path),
+ unused (const char *backup_path),
+ unused (notmuch_compact_status_cb_t status_cb),
+ unused (void *closure))
{
fprintf (stderr, "notmuch was compiled against a xapian version lacking compaction support.\n");
return NOTMUCH_STATUS_UNSUPPORTED_OPERATION;
}
}
+ /*
+ * Prior to version 2, the "folder:" prefix was probabilistic and
+ * stemmed. Change it to the current boolean prefix. Add "path:"
+ * prefixes while at it.
+ */
+ if (version < 2) {
+ notmuch_query_t *query = notmuch_query_create (notmuch, "");
+ notmuch_messages_t *messages;
+ notmuch_message_t *message;
+
+ count = 0;
+ total = notmuch_query_count_messages (query);
+
+ for (messages = notmuch_query_search_messages (query);
+ notmuch_messages_valid (messages);
+ notmuch_messages_move_to_next (messages)) {
+ if (do_progress_notify) {
+ progress_notify (closure, (double) count / total);
+ do_progress_notify = 0;
+ }
+
+ message = notmuch_messages_get (messages);
+
+ _notmuch_message_upgrade_folder (message);
+ _notmuch_message_sync (message);
+
+ notmuch_message_destroy (message);
+
+ count++;
+ }
+
+ notmuch_query_destroy (query);
+ }
+
db->set_metadata ("version", STRINGIFY (NOTMUCH_DATABASE_VERSION));
db->flush ();
notmuch->last_doc_id++;
if (notmuch->last_doc_id == 0)
- INTERNAL_ERROR ("Xapian document IDs are exhausted.\n");
+ INTERNAL_ERROR ("Xapian document IDs are exhausted.\n");
return notmuch->last_doc_id;
}