]> git.notmuchmail.org Git - notmuch/blobdiff - lib/database.cc
lib/n_d_needs_upgrade: handle error return from n_d_get_version
[notmuch] / lib / database.cc
index 1753117f99361b8f1fe25cacd1152dc25694e446..4ff748f6360c494775370c516c1c95a52c3266e6 100644 (file)
 #include <signal.h>
 #include <ftw.h>
 
-#include <glib.h> /* g_free, GPtrArray, GHashTable */
-#include <glib-object.h> /* g_type_init */
+#include <glib.h>               /* g_free, GPtrArray, GHashTable */
+#include <glib-object.h>        /* g_type_init */
 
-#include <gmime/gmime.h> /* g_mime_init */
+#include <gmime/gmime.h>        /* g_mime_init */
 
 using namespace std;
 
@@ -49,7 +49,7 @@ typedef struct {
 
 #define NOTMUCH_DATABASE_VERSION 3
 
-#define STRINGIFY(s) _SUB_STRINGIFY(s)
+#define STRINGIFY(s) _SUB_STRINGIFY (s)
 #define _SUB_STRINGIFY(s) #s
 
 #if HAVE_XAPIAN_DB_RETRY_LOCK
@@ -58,6 +58,17 @@ typedef struct {
 #define DB_ACTION Xapian::DB_CREATE_OR_OPEN
 #endif
 
+#define LOG_XAPIAN_EXCEPTION(message, error) _log_xapian_exception (__location__, message, error)
+
+static void
+_log_xapian_exception (const char *where, notmuch_database_t *notmuch,  const Xapian::Error error) {
+    _notmuch_database_log (notmuch,
+                          "A Xapian exception occurred at %s: %s\n",
+                          where,
+                          error.get_msg ().c_str ());
+    notmuch->exception_reported = true;
+}
+
 /* Here's the current schema for our database (for NOTMUCH_DATABASE_VERSION):
  *
  * We currently have three different types of documents (mail, ghost,
@@ -263,66 +274,121 @@ typedef struct {
 static const
 prefix_t prefix_table[] = {
     /* name                    term prefix     flags */
-    { "type",                  "T",            NOTMUCH_FIELD_NO_FLAGS },
-    { "reference",             "XREFERENCE",   NOTMUCH_FIELD_NO_FLAGS },
-    { "replyto",               "XREPLYTO",     NOTMUCH_FIELD_NO_FLAGS },
-    { "directory",             "XDIRECTORY",   NOTMUCH_FIELD_NO_FLAGS },
-    { "file-direntry",         "XFDIRENTRY",   NOTMUCH_FIELD_NO_FLAGS },
-    { "directory-direntry",    "XDDIRENTRY",   NOTMUCH_FIELD_NO_FLAGS },
-    { "body",                  "",             NOTMUCH_FIELD_EXTERNAL |
-                                               NOTMUCH_FIELD_PROBABILISTIC},
-    { "thread",                        "G",            NOTMUCH_FIELD_EXTERNAL |
-                                               NOTMUCH_FIELD_PROCESSOR },
-    { "tag",                   "K",            NOTMUCH_FIELD_EXTERNAL |
-                                               NOTMUCH_FIELD_PROCESSOR },
-    { "is",                    "K",            NOTMUCH_FIELD_EXTERNAL |
-                                               NOTMUCH_FIELD_PROCESSOR },
-    { "id",                    "Q",            NOTMUCH_FIELD_EXTERNAL },
-    { "mid",                   "Q",            NOTMUCH_FIELD_EXTERNAL |
-                                               NOTMUCH_FIELD_PROCESSOR },
-    { "path",                  "P",            NOTMUCH_FIELD_EXTERNAL|
-                                               NOTMUCH_FIELD_PROCESSOR },
-    { "property",              "XPROPERTY",    NOTMUCH_FIELD_EXTERNAL },
+    { "type",                   "T",            NOTMUCH_FIELD_NO_FLAGS },
+    { "reference",              "XREFERENCE",   NOTMUCH_FIELD_NO_FLAGS },
+    { "replyto",                "XREPLYTO",     NOTMUCH_FIELD_NO_FLAGS },
+    { "directory",              "XDIRECTORY",   NOTMUCH_FIELD_NO_FLAGS },
+    { "file-direntry",          "XFDIRENTRY",   NOTMUCH_FIELD_NO_FLAGS },
+    { "directory-direntry",     "XDDIRENTRY",   NOTMUCH_FIELD_NO_FLAGS },
+    { "body",                   "",             NOTMUCH_FIELD_EXTERNAL |
+      NOTMUCH_FIELD_PROBABILISTIC },
+    { "thread",                 "G",            NOTMUCH_FIELD_EXTERNAL |
+      NOTMUCH_FIELD_PROCESSOR },
+    { "tag",                    "K",            NOTMUCH_FIELD_EXTERNAL |
+      NOTMUCH_FIELD_PROCESSOR },
+    { "is",                     "K",            NOTMUCH_FIELD_EXTERNAL |
+      NOTMUCH_FIELD_PROCESSOR },
+    { "id",                     "Q",            NOTMUCH_FIELD_EXTERNAL },
+    { "mid",                    "Q",            NOTMUCH_FIELD_EXTERNAL |
+      NOTMUCH_FIELD_PROCESSOR },
+    { "path",                   "P",            NOTMUCH_FIELD_EXTERNAL |
+      NOTMUCH_FIELD_PROCESSOR },
+    { "property",               "XPROPERTY",    NOTMUCH_FIELD_EXTERNAL },
     /*
      * Unconditionally add ':' to reduce potential ambiguity with
      * overlapping prefixes and/or terms that start with capital
      * letters. See Xapian document termprefixes.html for related
      * discussion.
      */
-    { "folder",                        "XFOLDER:",     NOTMUCH_FIELD_EXTERNAL |
-                                               NOTMUCH_FIELD_PROCESSOR },
-#if HAVE_XAPIAN_FIELD_PROCESSOR
-    { "date",                  NULL,           NOTMUCH_FIELD_EXTERNAL |
-                                               NOTMUCH_FIELD_PROCESSOR },
-    { "query",                 NULL,           NOTMUCH_FIELD_EXTERNAL |
-                                               NOTMUCH_FIELD_PROCESSOR },
-#endif
-    { "from",                  "XFROM",        NOTMUCH_FIELD_EXTERNAL |
-                                               NOTMUCH_FIELD_PROBABILISTIC |
-                                               NOTMUCH_FIELD_PROCESSOR },
-    { "to",                    "XTO",          NOTMUCH_FIELD_EXTERNAL |
-                                               NOTMUCH_FIELD_PROBABILISTIC },
-    { "attachment",            "XATTACHMENT",  NOTMUCH_FIELD_EXTERNAL |
-                                               NOTMUCH_FIELD_PROBABILISTIC },
-    { "mimetype",              "XMIMETYPE",    NOTMUCH_FIELD_EXTERNAL |
-                                               NOTMUCH_FIELD_PROBABILISTIC },
-    { "subject",               "XSUBJECT",     NOTMUCH_FIELD_EXTERNAL |
-                                               NOTMUCH_FIELD_PROBABILISTIC |
-                                               NOTMUCH_FIELD_PROCESSOR},
+    { "folder",                 "XFOLDER:",     NOTMUCH_FIELD_EXTERNAL |
+      NOTMUCH_FIELD_PROCESSOR },
+    { "date",                   NULL,           NOTMUCH_FIELD_EXTERNAL |
+      NOTMUCH_FIELD_PROCESSOR },
+    { "query",                  NULL,           NOTMUCH_FIELD_EXTERNAL |
+      NOTMUCH_FIELD_PROCESSOR },
+    { "from",                   "XFROM",        NOTMUCH_FIELD_EXTERNAL |
+      NOTMUCH_FIELD_PROBABILISTIC |
+      NOTMUCH_FIELD_PROCESSOR },
+    { "to",                     "XTO",          NOTMUCH_FIELD_EXTERNAL |
+      NOTMUCH_FIELD_PROBABILISTIC },
+    { "attachment",             "XATTACHMENT",  NOTMUCH_FIELD_EXTERNAL |
+      NOTMUCH_FIELD_PROBABILISTIC },
+    { "mimetype",               "XMIMETYPE",    NOTMUCH_FIELD_EXTERNAL |
+      NOTMUCH_FIELD_PROBABILISTIC },
+    { "subject",                "XSUBJECT",     NOTMUCH_FIELD_EXTERNAL |
+      NOTMUCH_FIELD_PROBABILISTIC |
+      NOTMUCH_FIELD_PROCESSOR },
 };
 
 static void
 _setup_query_field_default (const prefix_t *prefix, notmuch_database_t *notmuch)
 {
     if (prefix->prefix)
-       notmuch->query_parser->add_prefix ("",prefix->prefix);
+       notmuch->query_parser->add_prefix ("", prefix->prefix);
     if (prefix->flags & NOTMUCH_FIELD_PROBABILISTIC)
        notmuch->query_parser->add_prefix (prefix->name, prefix->prefix);
     else
        notmuch->query_parser->add_boolean_prefix (prefix->name, prefix->prefix);
 }
 
-#if HAVE_XAPIAN_FIELD_PROCESSOR
+notmuch_string_map_iterator_t *
+_notmuch_database_user_headers (notmuch_database_t *notmuch)
+{
+    return _notmuch_string_map_iterator_create (notmuch->user_header, "", false);
+}
+
+const char *
+_user_prefix (void *ctx, const char *name)
+{
+    return talloc_asprintf (ctx, "XU%s:", name);
+}
+
+static notmuch_status_t
+_setup_user_query_fields (notmuch_database_t *notmuch)
+{
+    notmuch_config_list_t *list;
+    notmuch_status_t status;
+
+    notmuch->user_prefix = _notmuch_string_map_create (notmuch);
+    if (notmuch->user_prefix == NULL)
+       return NOTMUCH_STATUS_OUT_OF_MEMORY;
+
+    notmuch->user_header = _notmuch_string_map_create (notmuch);
+    if (notmuch->user_header == NULL)
+       return NOTMUCH_STATUS_OUT_OF_MEMORY;
+
+    status = notmuch_database_get_config_list (notmuch, CONFIG_HEADER_PREFIX, &list);
+    if (status)
+       return status;
+
+    for (; notmuch_config_list_valid (list); notmuch_config_list_move_to_next (list)) {
+
+       prefix_t query_field;
+
+       const char *key = notmuch_config_list_key (list)
+                         + sizeof (CONFIG_HEADER_PREFIX) - 1;
+
+       _notmuch_string_map_append (notmuch->user_prefix,
+                                   key,
+                                   _user_prefix (notmuch, key));
+
+       _notmuch_string_map_append (notmuch->user_header,
+                                   key,
+                                   notmuch_config_list_value (list));
+
+       query_field.name = talloc_strdup (notmuch, key);
+       query_field.prefix = _user_prefix (notmuch, key);
+       query_field.flags = NOTMUCH_FIELD_PROBABILISTIC
+                           | NOTMUCH_FIELD_EXTERNAL;
+
+       _setup_query_field_default (&query_field, notmuch);
+    }
+
+    notmuch_config_list_destroy (list);
+
+    return NOTMUCH_STATUS_SUCCESS;
+}
+
 static void
 _setup_query_field (const prefix_t *prefix, notmuch_database_t *notmuch)
 {
@@ -330,10 +396,10 @@ _setup_query_field (const prefix_t *prefix, notmuch_database_t *notmuch)
        Xapian::FieldProcessor *fp;
 
        if (STRNCMP_LITERAL (prefix->name, "date") == 0)
-           fp = (new DateFieldProcessor())->release ();
+           fp = (new DateFieldProcessor(NOTMUCH_VALUE_TIMESTAMP))->release ();
        else if (STRNCMP_LITERAL(prefix->name, "query") == 0)
            fp = (new QueryFieldProcessor (*notmuch->query_parser, notmuch))->release ();
-       else if (STRNCMP_LITERAL(prefix->name, "thread") == 0)
+       else if (STRNCMP_LITERAL (prefix->name, "thread") == 0)
            fp = (new ThreadFieldProcessor (*notmuch->query_parser, notmuch))->release ();
        else
            fp = (new RegexpFieldProcessor (prefix->name, prefix->flags,
@@ -341,19 +407,12 @@ _setup_query_field (const prefix_t *prefix, notmuch_database_t *notmuch)
 
        /* we treat all field-processor fields as boolean in order to get the raw input */
        if (prefix->prefix)
-           notmuch->query_parser->add_prefix ("",prefix->prefix);
+           notmuch->query_parser->add_prefix ("", prefix->prefix);
        notmuch->query_parser->add_boolean_prefix (prefix->name, fp);
     } else {
        _setup_query_field_default (prefix, notmuch);
     }
 }
-#else
-static inline void
-_setup_query_field (const prefix_t *prefix, notmuch_database_t *notmuch)
-{
-    _setup_query_field_default (prefix, notmuch);
-}
-#endif
 
 const char *
 _find_prefix (const char *name)
@@ -370,6 +429,26 @@ _find_prefix (const char *name)
     return "";
 }
 
+/* Like find prefix, but include the possibility of user defined
+ * prefixes specific to this database */
+
+const char *
+_notmuch_database_prefix (notmuch_database_t *notmuch, const char *name)
+{
+    unsigned int i;
+
+    /*XXX TODO: reduce code duplication */
+    for (i = 0; i < ARRAY_SIZE (prefix_table); i++) {
+       if (strcmp (name, prefix_table[i].name) == 0)
+           return prefix_table[i].prefix;
+    }
+
+    if (notmuch->user_prefix)
+       return _notmuch_string_map_get (notmuch->user_prefix, name);
+
+    return NULL;
+}
+
 static const struct {
     /* NOTMUCH_FEATURE_* value. */
     _notmuch_features value;
@@ -391,18 +470,18 @@ static const struct {
     { NOTMUCH_FEATURE_BOOL_FOLDER,
       "exact folder:/path: search", "rw" },
     { NOTMUCH_FEATURE_GHOSTS,
-      "mail documents for missing messages", "w"},
+      "mail documents for missing messages", "w" },
     /* Knowledge of the index mime-types are not required for reading
      * a database because a reader will just be unable to query
      * them. */
     { NOTMUCH_FEATURE_INDEXED_MIMETYPES,
-      "indexed MIME types", "w"},
+      "indexed MIME types", "w" },
     { NOTMUCH_FEATURE_LAST_MOD,
-      "modification tracking", "w"},
+      "modification tracking", "w" },
     /* Existing databases will work fine for all queries not involving
      * 'body:' */
     { NOTMUCH_FEATURE_UNPREFIX_BODY_ONLY,
-      "index body and headers separately", "w"},
+      "index body and headers separately", "w" },
 };
 
 const char *
@@ -451,8 +530,8 @@ notmuch_status_to_string (notmuch_status_t status)
 
 void
 _notmuch_database_log (notmuch_database_t *notmuch,
-                     const char *format,
-                     ...)
+                      const char *format,
+                      ...)
 {
     va_list va_args;
 
@@ -467,8 +546,8 @@ _notmuch_database_log (notmuch_database_t *notmuch,
 
 void
 _notmuch_database_log_append (notmuch_database_t *notmuch,
-                     const char *format,
-                     ...)
+                             const char *format,
+                             ...)
 {
     va_list va_args;
 
@@ -591,7 +670,7 @@ notmuch_database_find_message (notmuch_database_t *notmuch,
        return NOTMUCH_STATUS_SUCCESS;
     } catch (const Xapian::Error &error) {
        _notmuch_database_log (notmuch, "A Xapian exception occurred finding message: %s.\n",
-                error.get_msg().c_str());
+                              error.get_msg ().c_str ());
        notmuch->exception_reported = true;
        *message_ret = NULL;
        return NOTMUCH_STATUS_XAPIAN_EXCEPTION;
@@ -642,7 +721,7 @@ notmuch_database_create_verbose (const char *path,
     err = stat (path, &st);
     if (err) {
        IGNORE_RESULT (asprintf (&message, "Error: Cannot create database at %s: %s.\n",
-                               path, strerror (errno)));
+                                path, strerror (errno)));
        status = NOTMUCH_STATUS_FILE_ERROR;
        goto DONE;
     }
@@ -680,7 +759,7 @@ notmuch_database_create_verbose (const char *path,
 
     status = notmuch_database_upgrade (notmuch, NULL, NULL);
     if (status) {
-       notmuch_database_close(notmuch);
+       notmuch_database_close (notmuch);
        notmuch = NULL;
     }
 
@@ -815,7 +894,7 @@ notmuch_database_open (const char *path,
     notmuch_status_t status;
 
     status = notmuch_database_open_verbose (path, mode, database,
-                                          &status_string);
+                                           &status_string);
 
     if (status_string) {
        fputs (status_string, stderr);
@@ -874,7 +953,7 @@ notmuch_database_open_verbose (const char *path,
     }
 
     /* Initialize the GLib type system and threads */
-#if !GLIB_CHECK_VERSION(2, 35, 1)
+#if ! GLIB_CHECK_VERSION (2, 35, 1)
     g_type_init ();
 #endif
 
@@ -889,7 +968,7 @@ notmuch_database_open_verbose (const char *path,
     notmuch->status_string = NULL;
     notmuch->path = talloc_strdup (notmuch, path);
 
-    strip_trailing(notmuch->path, '/');
+    strip_trailing (notmuch->path, '/');
 
     notmuch->mode = mode;
     notmuch->atomic_nesting = 0;
@@ -911,9 +990,9 @@ notmuch_database_open_verbose (const char *path,
        version = notmuch_database_get_version (notmuch);
        if (version > NOTMUCH_DATABASE_VERSION) {
            IGNORE_RESULT (asprintf (&message,
-                     "Error: Notmuch database at %s\n"
-                     "       has a newer database format version (%u) than supported by this\n"
-                     "       version of notmuch (%u).\n",
+                                    "Error: Notmuch database at %s\n"
+                                    "       has a newer database format version (%u) than supported by this\n"
+                                    "       version of notmuch (%u).\n",
                                     notmuch_path, version, NOTMUCH_DATABASE_VERSION));
            notmuch->mode = NOTMUCH_DATABASE_MODE_READ_ONLY;
            notmuch_database_destroy (notmuch);
@@ -930,9 +1009,9 @@ notmuch_database_open_verbose (const char *path,
            &incompat_features);
        if (incompat_features) {
            IGNORE_RESULT (asprintf (&message,
-               "Error: Notmuch database at %s\n"
-               "       requires features (%s)\n"
-               "       not supported by this version of notmuch.\n",
+                                    "Error: Notmuch database at %s\n"
+                                    "       requires features (%s)\n"
+                                    "       not supported by this version of notmuch.\n",
                                     notmuch_path, incompat_features));
            notmuch->mode = NOTMUCH_DATABASE_MODE_READ_ONLY;
            notmuch_database_destroy (notmuch);
@@ -968,17 +1047,16 @@ notmuch_database_open_verbose (const char *path,
        notmuch->query_parser = new Xapian::QueryParser;
        notmuch->term_gen = new Xapian::TermGenerator;
        notmuch->term_gen->set_stemmer (Xapian::Stem ("english"));
-       notmuch->value_range_processor = new Xapian::NumberValueRangeProcessor (NOTMUCH_VALUE_TIMESTAMP);
-       notmuch->date_range_processor = new ParseTimeValueRangeProcessor (NOTMUCH_VALUE_TIMESTAMP);
-       notmuch->last_mod_range_processor = new Xapian::NumberValueRangeProcessor (NOTMUCH_VALUE_LAST_MOD, "lastmod:");
-
+       notmuch->value_range_processor = new Xapian::NumberRangeProcessor (NOTMUCH_VALUE_TIMESTAMP);
+       notmuch->date_range_processor = new ParseTimeRangeProcessor (NOTMUCH_VALUE_TIMESTAMP, "date:");
+       notmuch->last_mod_range_processor = new Xapian::NumberRangeProcessor (NOTMUCH_VALUE_LAST_MOD, "lastmod:");
        notmuch->query_parser->set_default_op (Xapian::Query::OP_AND);
        notmuch->query_parser->set_database (*notmuch->xapian_db);
        notmuch->query_parser->set_stemmer (Xapian::Stem ("english"));
        notmuch->query_parser->set_stemming_strategy (Xapian::QueryParser::STEM_SOME);
-       notmuch->query_parser->add_valuerangeprocessor (notmuch->value_range_processor);
-       notmuch->query_parser->add_valuerangeprocessor (notmuch->date_range_processor);
-       notmuch->query_parser->add_valuerangeprocessor (notmuch->last_mod_range_processor);
+       notmuch->query_parser->add_rangeprocessor (notmuch->value_range_processor);
+       notmuch->query_parser->add_rangeprocessor (notmuch->date_range_processor);
+       notmuch->query_parser->add_rangeprocessor (notmuch->last_mod_range_processor);
 
        for (i = 0; i < ARRAY_SIZE (prefix_table); i++) {
            const prefix_t *prefix = &prefix_table[i];
@@ -986,9 +1064,10 @@ notmuch_database_open_verbose (const char *path,
                _setup_query_field (prefix, notmuch);
            }
        }
+       status = _setup_user_query_fields (notmuch);
     } catch (const Xapian::Error &error) {
        IGNORE_RESULT (asprintf (&message, "A Xapian exception occurred opening database: %s\n",
-                                error.get_msg().c_str()));
+                                error.get_msg ().c_str ()));
        notmuch_database_destroy (notmuch);
        notmuch = NULL;
        status = NOTMUCH_STATUS_XAPIAN_EXCEPTION;
@@ -1008,6 +1087,10 @@ notmuch_database_open_verbose (const char *path,
        *database = notmuch;
     else
        talloc_free (notmuch);
+
+    if (notmuch)
+       notmuch->open = true;
+
     return status;
 }
 
@@ -1019,7 +1102,7 @@ notmuch_database_close (notmuch_database_t *notmuch)
     /* Many Xapian objects (and thus notmuch objects) hold references to
      * the database, so merely deleting the database may not suffice to
      * close it.  Thus, we explicitly close it here. */
-    if (notmuch->xapian_db != NULL) {
+    if (notmuch->open) {
        try {
            /* If there's an outstanding transaction, it's unclear if
             * closing the Xapian database commits everything up to
@@ -1029,33 +1112,20 @@ notmuch_database_close (notmuch_database_t *notmuch)
            if (notmuch->mode == NOTMUCH_DATABASE_MODE_READ_WRITE &&
                notmuch->atomic_nesting)
                (static_cast <Xapian::WritableDatabase *> (notmuch->xapian_db))
-                   ->cancel_transaction ();
+               ->cancel_transaction ();
 
            /* Close the database.  This implicitly flushes
             * outstanding changes. */
-           notmuch->xapian_db->close();
+           notmuch->xapian_db->close ();
        } catch (const Xapian::Error &error) {
            status = NOTMUCH_STATUS_XAPIAN_EXCEPTION;
            if (! notmuch->exception_reported) {
                _notmuch_database_log (notmuch, "Error: A Xapian exception occurred closing database: %s\n",
-                        error.get_msg().c_str());
+                                      error.get_msg ().c_str ());
            }
        }
     }
-
-    delete notmuch->term_gen;
-    notmuch->term_gen = NULL;
-    delete notmuch->query_parser;
-    notmuch->query_parser = NULL;
-    delete notmuch->xapian_db;
-    notmuch->xapian_db = NULL;
-    delete notmuch->value_range_processor;
-    notmuch->value_range_processor = NULL;
-    delete notmuch->date_range_processor;
-    notmuch->date_range_processor = NULL;
-    delete notmuch->last_mod_range_processor;
-    notmuch->last_mod_range_processor = NULL;
-
+    notmuch->open = false;
     return status;
 }
 
@@ -1103,7 +1173,9 @@ class NotmuchCompactor : public Xapian::Compactor
 
 public:
     NotmuchCompactor(notmuch_compact_status_cb_t cb, void *closure) :
-       status_cb (cb), status_closure (closure) { }
+       status_cb (cb), status_closure (closure)
+    {
+    }
 
     virtual void
     set_status (const std::string &table, const std::string &status)
@@ -1114,9 +1186,9 @@ public:
            return;
 
        if (status.length () == 0)
-           msg = talloc_asprintf (NULL, "compacting table %s", table.c_str());
+           msg = talloc_asprintf (NULL, "compacting table %s", table.c_str ());
        else
-           msg = talloc_asprintf (NULL, "     %s", status.c_str());
+           msg = talloc_asprintf (NULL, "     %s", status.c_str ());
 
        if (msg == NULL) {
            return;
@@ -1186,8 +1258,7 @@ notmuch_database_compact (const char *path,
            goto DONE;
        }
        keep_backup = false;
-    }
-    else {
+    } else {
        keep_backup = true;
     }
 
@@ -1198,7 +1269,7 @@ notmuch_database_compact (const char *path,
     }
     if (errno != ENOENT) {
        _notmuch_database_log (notmuch, "Unknown error while stat()ing path: %s\n",
-                strerror (errno));
+                              strerror (errno));
        ret = NOTMUCH_STATUS_FILE_ERROR;
        goto DONE;
     }
@@ -1211,27 +1282,23 @@ notmuch_database_compact (const char *path,
 
     try {
        NotmuchCompactor compactor (status_cb, closure);
-
-       compactor.set_renumber (false);
-       compactor.add_source (xapian_path);
-       compactor.set_destdir (compact_xapian_path);
-       compactor.compact ();
+       notmuch->xapian_db->compact (compact_xapian_path, Xapian::DBCOMPACT_NO_RENUMBER, 0, compactor);
     } catch (const Xapian::Error &error) {
-       _notmuch_database_log (notmuch, "Error while compacting: %s\n", error.get_msg().c_str());
+       _notmuch_database_log (notmuch, "Error while compacting: %s\n", error.get_msg ().c_str ());
        ret = NOTMUCH_STATUS_XAPIAN_EXCEPTION;
        goto DONE;
     }
 
     if (rename (xapian_path, backup_path)) {
        _notmuch_database_log (notmuch, "Error moving %s to %s: %s\n",
-                xapian_path, backup_path, strerror (errno));
+                              xapian_path, backup_path, strerror (errno));
        ret = NOTMUCH_STATUS_FILE_ERROR;
        goto DONE;
     }
 
     if (rename (compact_xapian_path, xapian_path)) {
        _notmuch_database_log (notmuch, "Error moving %s to %s: %s\n",
-                compact_xapian_path, xapian_path, strerror (errno));
+                              compact_xapian_path, xapian_path, strerror (errno));
        ret = NOTMUCH_STATUS_FILE_ERROR;
        goto DONE;
     }
@@ -1239,7 +1306,7 @@ notmuch_database_compact (const char *path,
     if (! keep_backup) {
        if (rmtree (backup_path)) {
            _notmuch_database_log (notmuch, "Error removing old database %s: %s\n",
-                    backup_path, strerror (errno));
+                                  backup_path, strerror (errno));
            ret = NOTMUCH_STATUS_FILE_ERROR;
            goto DONE;
        }
@@ -1271,6 +1338,20 @@ notmuch_database_destroy (notmuch_database_t *notmuch)
     notmuch_status_t status;
 
     status = notmuch_database_close (notmuch);
+
+    delete notmuch->term_gen;
+    notmuch->term_gen = NULL;
+    delete notmuch->query_parser;
+    notmuch->query_parser = NULL;
+    delete notmuch->xapian_db;
+    notmuch->xapian_db = NULL;
+    delete notmuch->value_range_processor;
+    notmuch->value_range_processor = NULL;
+    delete notmuch->date_range_processor;
+    notmuch->date_range_processor = NULL;
+    delete notmuch->last_mod_range_processor;
+    notmuch->last_mod_range_processor = NULL;
+
     talloc_free (notmuch);
 
     return status;
@@ -1290,7 +1371,13 @@ notmuch_database_get_version (notmuch_database_t *notmuch)
     const char *str;
     char *end;
 
-    version_string = notmuch->xapian_db->get_metadata ("version");
+    try {
+       version_string = notmuch->xapian_db->get_metadata ("version");
+    } catch (const Xapian::Error &error) {
+       LOG_XAPIAN_EXCEPTION (notmuch, error);
+       return 0;
+    }
+
     if (version_string.empty ())
        return 0;
 
@@ -1308,9 +1395,17 @@ notmuch_database_get_version (notmuch_database_t *notmuch)
 notmuch_bool_t
 notmuch_database_needs_upgrade (notmuch_database_t *notmuch)
 {
-    return notmuch->mode == NOTMUCH_DATABASE_MODE_READ_WRITE &&
-       ((NOTMUCH_FEATURES_CURRENT & ~notmuch->features) ||
-        (notmuch_database_get_version (notmuch) < NOTMUCH_DATABASE_VERSION));
+    unsigned int version;
+
+    if (notmuch->mode != NOTMUCH_DATABASE_MODE_READ_WRITE)
+       return FALSE;
+
+    if (NOTMUCH_FEATURES_CURRENT & ~notmuch->features)
+       return TRUE;
+
+    version = notmuch_database_get_version (notmuch);
+
+    return (version > 0 && version < NOTMUCH_DATABASE_VERSION);
 }
 
 static volatile sig_atomic_t do_progress_notify = 0;
@@ -1431,8 +1526,7 @@ notmuch_database_upgrade (notmuch_database_t *notmuch,
            goto DONE;
        for (;
             notmuch_messages_valid (messages);
-            notmuch_messages_move_to_next (messages))
-       {
+            notmuch_messages_move_to_next (messages)) {
            if (do_progress_notify) {
                progress_notify (closure, (double) count / total);
                do_progress_notify = 0;
@@ -1489,8 +1583,7 @@ notmuch_database_upgrade (notmuch_database_t *notmuch,
 
        for (t = notmuch->xapian_db->allterms_begin ("XTIMESTAMP");
             t != t_end;
-            t++)
-       {
+            t++) {
            Xapian::PostingIterator p, p_end;
            std::string term = *t;
 
@@ -1498,8 +1591,7 @@ notmuch_database_upgrade (notmuch_database_t *notmuch,
 
            for (p = notmuch->xapian_db->postlist_begin (term);
                 p != p_end;
-                p++)
-           {
+                p++) {
                Xapian::Document document;
                time_t mtime;
                notmuch_directory_t *directory;
@@ -1513,7 +1605,7 @@ notmuch_database_upgrade (notmuch_database_t *notmuch,
                mtime = Xapian::sortable_unserialise (
                    document.get_value (NOTMUCH_VALUE_TIMESTAMP));
 
-               directory = _notmuch_directory_create (notmuch, term.c_str() + 10,
+               directory = _notmuch_directory_create (notmuch, term.c_str () + 10,
                                                       NOTMUCH_FIND_CREATE, &status);
                notmuch_directory_set_mtime (directory, mtime);
                notmuch_directory_destroy (directory);
@@ -1562,7 +1654,7 @@ notmuch_database_upgrade (notmuch_database_t *notmuch,
 
            if (private_status) {
                _notmuch_database_log (notmuch,
-                        "Upgrade failed while creating ghost messages.\n");
+                                      "Upgrade failed while creating ghost messages.\n");
                status = COERCE_STATUS (private_status, "Unexpected status from _notmuch_message_initialize_ghost");
                goto DONE;
            }
@@ -1578,7 +1670,7 @@ notmuch_database_upgrade (notmuch_database_t *notmuch,
     db->set_metadata ("features", _print_features (local, notmuch->features));
     db->set_metadata ("version", STRINGIFY (NOTMUCH_DATABASE_VERSION));
 
- DONE:
 DONE:
     if (status == NOTMUCH_STATUS_SUCCESS)
        db->commit_transaction ();
     else
@@ -1618,12 +1710,12 @@ notmuch_database_begin_atomic (notmuch_database_t *notmuch)
        (static_cast <Xapian::WritableDatabase *> (notmuch->xapian_db))->begin_transaction (false);
     } catch (const Xapian::Error &error) {
        _notmuch_database_log (notmuch, "A Xapian exception occurred beginning transaction: %s.\n",
-                error.get_msg().c_str());
+                              error.get_msg ().c_str ());
        notmuch->exception_reported = true;
        return NOTMUCH_STATUS_XAPIAN_EXCEPTION;
     }
 
-DONE:
+  DONE:
     notmuch->atomic_nesting++;
     return NOTMUCH_STATUS_SUCCESS;
 }
@@ -1652,7 +1744,7 @@ notmuch_database_end_atomic (notmuch_database_t *notmuch)
            db->commit ();
     } catch (const Xapian::Error &error) {
        _notmuch_database_log (notmuch, "A Xapian exception occurred committing transaction: %s.\n",
-                error.get_msg().c_str());
+                              error.get_msg ().c_str ());
        notmuch->exception_reported = true;
        return NOTMUCH_STATUS_XAPIAN_EXCEPTION;
     }
@@ -1662,14 +1754,14 @@ notmuch_database_end_atomic (notmuch_database_t *notmuch)
        notmuch->atomic_dirty = false;
     }
 
-DONE:
+  DONE:
     notmuch->atomic_nesting--;
     return NOTMUCH_STATUS_SUCCESS;
 }
 
 unsigned long
 notmuch_database_get_revision (notmuch_database_t *notmuch,
-                               const char **uuid)
+                              const char **uuid)
 {
     if (uuid)
        *uuid = notmuch->uuid;
@@ -1787,7 +1879,7 @@ _notmuch_database_find_directory_id (notmuch_database_t *notmuch,
     }
 
     directory = _notmuch_directory_create (notmuch, path, flags, &status);
-    if (status || !directory) {
+    if (status || ! directory) {
        *directory_id = -1;
        return status;
     }
@@ -1841,7 +1933,7 @@ _notmuch_database_filename_to_direntry (void *ctx,
 
     status = _notmuch_database_find_directory_id (notmuch, directory, flags,
                                                  &directory_id);
-    if (status || directory_id == (unsigned int)-1) {
+    if (status || directory_id == (unsigned int) -1) {
        *direntry = NULL;
        return status;
     }
@@ -1871,11 +1963,10 @@ _notmuch_database_relative_path (notmuch_database_t *notmuch,
     relative = path;
 
     if (*relative == '/') {
-       while (*relative == '/' && *(relative+1) == '/')
+       while (*relative == '/' && *(relative + 1) == '/')
            relative++;
 
-       if (strncmp (relative, db_path, db_path_len) == 0)
-       {
+       if (strncmp (relative, db_path, db_path_len) == 0) {
            relative += db_path_len;
            while (*relative == '/')
                relative++;
@@ -1901,7 +1992,7 @@ notmuch_database_get_directory (notmuch_database_t *notmuch,
                                                NOTMUCH_FIND_LOOKUP, &status);
     } catch (const Xapian::Error &error) {
        _notmuch_database_log (notmuch, "A Xapian exception occurred getting directory: %s.\n",
-                error.get_msg().c_str());
+                              error.get_msg ().c_str ());
        notmuch->exception_reported = true;
        status = NOTMUCH_STATUS_XAPIAN_EXCEPTION;
     }
@@ -1944,13 +2035,13 @@ notmuch_database_remove_message (notmuch_database_t *notmuch,
                                                        &message);
 
     if (status == NOTMUCH_STATUS_SUCCESS && message) {
-           status = _notmuch_message_remove_filename (message, filename);
-           if (status == NOTMUCH_STATUS_SUCCESS)
-               _notmuch_message_delete (message);
-           else if (status == NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID)
-               _notmuch_message_sync (message);
+       status = _notmuch_message_remove_filename (message, filename);
+       if (status == NOTMUCH_STATUS_SUCCESS)
+           _notmuch_message_delete (message);
+       else if (status == NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID)
+           _notmuch_message_sync (message);
 
-           notmuch_message_destroy (message);
+       notmuch_message_destroy (message);
     }
 
     return status;
@@ -1981,7 +2072,7 @@ notmuch_database_find_message_by_filename (notmuch_database_t *notmuch,
     try {
        status = _notmuch_database_filename_to_direntry (
            local, notmuch, filename, NOTMUCH_FIND_LOOKUP, &direntry);
-       if (status || !direntry)
+       if (status || ! direntry)
            goto DONE;
 
        term = talloc_asprintf (local, "%s%s", prefix, direntry);
@@ -1998,7 +2089,7 @@ notmuch_database_find_message_by_filename (notmuch_database_t *notmuch,
        }
     } catch (const Xapian::Error &error) {
        _notmuch_database_log (notmuch, "Error: A Xapian exception occurred finding message by filename: %s\n",
-                error.get_msg().c_str());
+                              error.get_msg ().c_str ());
        notmuch->exception_reported = true;
        status = NOTMUCH_STATUS_XAPIAN_EXCEPTION;
     }
@@ -2043,15 +2134,15 @@ notmuch_database_get_all_tags (notmuch_database_t *db)
     notmuch_string_list_t *tags;
 
     try {
-       i = db->xapian_db->allterms_begin();
-       end = db->xapian_db->allterms_end();
+       i = db->xapian_db->allterms_begin ();
+       end = db->xapian_db->allterms_end ();
        tags = _notmuch_database_get_terms_with_prefix (db, i, end,
                                                        _find_prefix ("tag"));
        _notmuch_string_list_sort (tags);
        return _notmuch_tags_create (db, tags);
     } catch (const Xapian::Error &error) {
        _notmuch_database_log (db, "A Xapian exception occurred getting tags: %s.\n",
-                error.get_msg().c_str());
+                              error.get_msg ().c_str ());
        db->exception_reported = true;
        return NULL;
     }