]> git.notmuchmail.org Git - notmuch/blobdiff - lib/database.cc
lib/database: delete stemmer on destroy
[notmuch] / lib / database.cc
index 0f4e2ff96d4d6b9e7ed731d320219aecbd93d1ec..df83e2048673b235e6c1055ccb47d39923f480a7 100644 (file)
@@ -49,7 +49,8 @@ typedef struct {
 #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) {
+_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,
@@ -292,12 +293,26 @@ notmuch_status_to_string (notmuch_status_t status)
        return "Operation requires a database upgrade";
     case NOTMUCH_STATUS_PATH_ERROR:
        return "Path supplied is illegal for this function";
+    case NOTMUCH_STATUS_IGNORED:
+       return "Argument was ignored";
+    case NOTMUCH_STATUS_ILLEGAL_ARGUMENT:
+       return "Illegal argument for function";
     case NOTMUCH_STATUS_MALFORMED_CRYPTO_PROTOCOL:
        return "Crypto protocol missing, malformed, or unintelligible";
     case NOTMUCH_STATUS_FAILED_CRYPTO_CONTEXT_CREATION:
        return "Crypto engine initialization failure";
     case NOTMUCH_STATUS_UNKNOWN_CRYPTO_PROTOCOL:
        return "Unknown crypto protocol";
+    case NOTMUCH_STATUS_NO_CONFIG:
+       return "No configuration file found";
+    case NOTMUCH_STATUS_NO_DATABASE:
+       return "No database found";
+    case NOTMUCH_STATUS_DATABASE_EXISTS:
+       return "Database exists, not recreated";
+    case NOTMUCH_STATUS_BAD_QUERY_SYNTAX:
+       return "Syntax error in query";
+    case NOTMUCH_STATUS_NO_MAIL_ROOT:
+       return "No mail root found";
     default:
     case NOTMUCH_STATUS_LAST_STATUS:
        return "Unknown error status value";
@@ -453,109 +468,6 @@ notmuch_database_find_message (notmuch_database_t *notmuch,
     }
 }
 
-notmuch_status_t
-notmuch_database_create (const char *path, notmuch_database_t **database)
-{
-    char *status_string = NULL;
-    notmuch_status_t status;
-
-    status = notmuch_database_create_verbose (path, database,
-                                             &status_string);
-
-    if (status_string) {
-       fputs (status_string, stderr);
-       free (status_string);
-    }
-
-    return status;
-}
-
-notmuch_status_t
-notmuch_database_create_verbose (const char *path,
-                                notmuch_database_t **database,
-                                char **status_string)
-{
-    notmuch_status_t status = NOTMUCH_STATUS_SUCCESS;
-    notmuch_database_t *notmuch = NULL;
-    char *notmuch_path = NULL;
-    char *message = NULL;
-    struct stat st;
-    int err;
-
-    if (path == NULL) {
-       message = strdup ("Error: Cannot create a database for a NULL path.\n");
-       status = NOTMUCH_STATUS_NULL_POINTER;
-       goto DONE;
-    }
-
-    if (path[0] != '/') {
-       message = strdup ("Error: Database path must be absolute.\n");
-       status = NOTMUCH_STATUS_PATH_ERROR;
-       goto DONE;
-    }
-
-    err = stat (path, &st);
-    if (err) {
-       IGNORE_RESULT (asprintf (&message, "Error: Cannot create database at %s: %s.\n",
-                                path, strerror (errno)));
-       status = NOTMUCH_STATUS_FILE_ERROR;
-       goto DONE;
-    }
-
-    if (! S_ISDIR (st.st_mode)) {
-       IGNORE_RESULT (asprintf (&message, "Error: Cannot create database at %s: "
-                                "Not a directory.\n",
-                                path));
-       status = NOTMUCH_STATUS_FILE_ERROR;
-       goto DONE;
-    }
-
-    notmuch_path = talloc_asprintf (NULL, "%s/%s", path, ".notmuch");
-
-    err = mkdir (notmuch_path, 0755);
-
-    if (err) {
-       IGNORE_RESULT (asprintf (&message, "Error: Cannot create directory %s: %s.\n",
-                                notmuch_path, strerror (errno)));
-       status = NOTMUCH_STATUS_FILE_ERROR;
-       goto DONE;
-    }
-
-    status = notmuch_database_open_verbose (path,
-                                           NOTMUCH_DATABASE_MODE_READ_WRITE,
-                                           &notmuch, &message);
-    if (status)
-       goto DONE;
-
-    /* Upgrade doesn't add these feature to existing databases, but
-     * new databases have them. */
-    notmuch->features |= NOTMUCH_FEATURE_FROM_SUBJECT_ID_VALUES;
-    notmuch->features |= NOTMUCH_FEATURE_INDEXED_MIMETYPES;
-    notmuch->features |= NOTMUCH_FEATURE_UNPREFIX_BODY_ONLY;
-
-    status = notmuch_database_upgrade (notmuch, NULL, NULL);
-    if (status) {
-       notmuch_database_close (notmuch);
-       notmuch = NULL;
-    }
-
-  DONE:
-    if (notmuch_path)
-       talloc_free (notmuch_path);
-
-    if (message) {
-       if (status_string)
-           *status_string = message;
-       else
-           free (message);
-    }
-    if (database)
-       *database = notmuch;
-    else
-       talloc_free (notmuch);
-    return status;
-}
-
 notmuch_status_t
 _notmuch_database_ensure_writable (notmuch_database_t *notmuch)
 {
@@ -594,22 +506,15 @@ notmuch_database_close (notmuch_database_t *notmuch)
      * close it.  Thus, we explicitly close it here. */
     if (notmuch->open) {
        try {
-           /* If there's an outstanding transaction, it's unclear if
-            * closing the Xapian database commits everything up to
-            * that transaction, or may discard committed (but
-            * unflushed) transactions.  To be certain, explicitly
-            * cancel any outstanding transaction before closing. */
-           if (_notmuch_database_mode (notmuch) == NOTMUCH_DATABASE_MODE_READ_WRITE &&
-               notmuch->atomic_nesting)
-               notmuch->writable_xapian_db->cancel_transaction ();
-
            /* Close the database.  This implicitly flushes
-            * outstanding changes. */
+            * outstanding changes. If there is an open (non-flushed)
+            * transaction, ALL pending changes will be discarded */
            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",
+               _notmuch_database_log (notmuch,
+                                      "Error: A Xapian exception occurred closing database: %s\n",
                                       error.get_msg ().c_str ());
            }
        }
@@ -618,28 +523,6 @@ notmuch_database_close (notmuch_database_t *notmuch)
     return status;
 }
 
-notmuch_status_t
-_notmuch_database_reopen (notmuch_database_t *notmuch)
-{
-    if (_notmuch_database_mode (notmuch) != NOTMUCH_DATABASE_MODE_READ_ONLY)
-       return NOTMUCH_STATUS_UNSUPPORTED_OPERATION;
-
-    try {
-       notmuch->xapian_db->reopen ();
-    } catch (const Xapian::Error &error) {
-       if (! notmuch->exception_reported) {
-           _notmuch_database_log (notmuch, "Error: A Xapian exception reopening database: %s\n",
-                                  error.get_msg ().c_str ());
-           notmuch->exception_reported = true;
-       }
-       return NOTMUCH_STATUS_XAPIAN_EXCEPTION;
-    }
-
-    notmuch->view++;
-
-    return NOTMUCH_STATUS_SUCCESS;
-}
-
 static int
 unlink_cb (const char *path,
           unused (const struct stat *sb),
@@ -705,36 +588,58 @@ notmuch_database_compact (const char *path,
                          notmuch_compact_status_cb_t status_cb,
                          void *closure)
 {
-    void *local;
-    char *notmuch_path, *xapian_path, *compact_xapian_path;
     notmuch_status_t ret = NOTMUCH_STATUS_SUCCESS;
     notmuch_database_t *notmuch = NULL;
-    struct stat statbuf;
-    bool keep_backup;
     char *message = NULL;
 
-    local = talloc_new (NULL);
-    if (! local)
-       return NOTMUCH_STATUS_OUT_OF_MEMORY;
-
-    ret = notmuch_database_open_verbose (path,
-                                        NOTMUCH_DATABASE_MODE_READ_WRITE,
-                                        &notmuch,
-                                        &message);
+    ret = notmuch_database_open_with_config (path,
+                                            NOTMUCH_DATABASE_MODE_READ_WRITE,
+                                            "",
+                                            NULL,
+                                            &notmuch,
+                                            &message);
     if (ret) {
        if (status_cb) status_cb (message, closure);
-       goto DONE;
+       return ret;
     }
 
-    if (! (notmuch_path = talloc_asprintf (local, "%s/%s", path, ".notmuch"))) {
-       ret = NOTMUCH_STATUS_OUT_OF_MEMORY;
-       goto DONE;
-    }
+    _notmuch_config_cache (notmuch, NOTMUCH_CONFIG_DATABASE_PATH, path);
 
-    if (! (xapian_path = talloc_asprintf (local, "%s/%s", notmuch_path, "xapian"))) {
-       ret = NOTMUCH_STATUS_OUT_OF_MEMORY;
+    return notmuch_database_compact_db (notmuch,
+                                       backup_path,
+                                       status_cb,
+                                       closure);
+}
+
+notmuch_status_t
+notmuch_database_compact_db (notmuch_database_t *notmuch,
+                            const char *backup_path,
+                            notmuch_compact_status_cb_t status_cb,
+                            void *closure)
+{
+    void *local;
+    const char *xapian_path, *compact_xapian_path;
+    const char *path;
+    notmuch_status_t ret = NOTMUCH_STATUS_SUCCESS;
+    struct stat statbuf;
+    bool keep_backup;
+    char *message;
+
+    ret = _notmuch_database_ensure_writable (notmuch);
+    if (ret)
+       return ret;
+
+    path = notmuch_config_get (notmuch, NOTMUCH_CONFIG_DATABASE_PATH);
+    if (! path)
+       return NOTMUCH_STATUS_PATH_ERROR;
+
+    local = talloc_new (NULL);
+    if (! local)
+       return NOTMUCH_STATUS_OUT_OF_MEMORY;
+
+    ret = _notmuch_choose_xapian_path (local, path, &xapian_path, &message);
+    if (ret)
        goto DONE;
-    }
 
     if (! (compact_xapian_path = talloc_asprintf (local, "%s.compact", xapian_path))) {
        ret = NOTMUCH_STATUS_OUT_OF_MEMORY;
@@ -771,7 +676,8 @@ notmuch_database_compact (const char *path,
 
     try {
        NotmuchCompactor compactor (status_cb, closure);
-       notmuch->xapian_db->compact (compact_xapian_path, Xapian::DBCOMPACT_NO_RENUMBER, 0, compactor);
+       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 ());
        ret = NOTMUCH_STATUS_XAPIAN_EXCEPTION;
@@ -825,6 +731,15 @@ notmuch_status_t
 notmuch_database_destroy (notmuch_database_t *notmuch)
 {
     notmuch_status_t status;
+    const char *talloc_report;
+
+    talloc_report = getenv ("NOTMUCH_TALLOC_REPORT");
+    if (talloc_report && strcmp (talloc_report, "") != 0) {
+       FILE *report = fopen (talloc_report, "a");
+       if (report) {
+           talloc_report_full (notmuch, report);
+       }
+    }
 
     status = notmuch_database_close (notmuch);
 
@@ -840,6 +755,8 @@ notmuch_database_destroy (notmuch_database_t *notmuch)
     notmuch->date_range_processor = NULL;
     delete notmuch->last_mod_range_processor;
     notmuch->last_mod_range_processor = NULL;
+    delete notmuch->stemmer;
+    notmuch->stemmer = NULL;
 
     talloc_free (notmuch);
 
@@ -849,7 +766,7 @@ notmuch_database_destroy (notmuch_database_t *notmuch)
 const char *
 notmuch_database_get_path (notmuch_database_t *notmuch)
 {
-    return notmuch->path;
+    return notmuch_config_get (notmuch, NOTMUCH_CONFIG_DATABASE_PATH);
 }
 
 unsigned int
@@ -919,8 +836,8 @@ handle_sigalrm (unused (int signal))
  */
 notmuch_status_t
 notmuch_database_upgrade (notmuch_database_t *notmuch,
-                         void (*progress_notify) (void *closure,
-                                                  double progress),
+                         void (*progress_notify)(void *closure,
+                                                 double progress),
                          void *closure)
 {
     void *local = talloc_new (NULL);
@@ -1144,7 +1061,8 @@ notmuch_database_upgrade (notmuch_database_t *notmuch,
            if (private_status) {
                _notmuch_database_log (notmuch,
                                       "Upgrade failed while creating ghost messages.\n");
-               status = COERCE_STATUS (private_status, "Unexpected status from _notmuch_message_initialize_ghost");
+               status = COERCE_STATUS (private_status,
+                                       "Unexpected status from _notmuch_message_initialize_ghost");
                goto DONE;
            }
 
@@ -1224,13 +1142,21 @@ notmuch_database_end_atomic (notmuch_database_t *notmuch)
     db = notmuch->writable_xapian_db;
     try {
        db->commit_transaction ();
-
-       /* This is a hack for testing.  Xapian never flushes on a
-        * non-flushed commit, even if the flush threshold is 1.
-        * However, we rely on flushing to test atomicity. */
+       notmuch->transaction_count++;
+
+       /* Xapian never flushes on a non-flushed commit, even if the
+        * flush threshold is 1.  However, we rely on flushing to test
+        * atomicity. On the other hand, we can't straight replace
+        * XAPIAN_FLUSH_THRESHOLD with our autocommit counter, because
+        * the former also applies outside notmuch atomic
+        * commits. Hence the follow complicated  test */
        const char *thresh = getenv ("XAPIAN_FLUSH_THRESHOLD");
-       if (thresh && atoi (thresh) == 1)
+       if ((notmuch->transaction_threshold > 0 &&
+            notmuch->transaction_count >= notmuch->transaction_threshold) ||
+           (thresh && atoi (thresh) == 1)) {
            db->commit ();
+           notmuch->transaction_count = 0;
+       }
     } catch (const Xapian::Error &error) {
        _notmuch_database_log (notmuch, "A Xapian exception occurred committing transaction: %s.\n",
                               error.get_msg ().c_str ());
@@ -1446,7 +1372,7 @@ _notmuch_database_relative_path (notmuch_database_t *notmuch,
     const char *db_path, *relative;
     unsigned int db_path_len;
 
-    db_path = notmuch_database_get_path (notmuch);
+    db_path = notmuch_config_get (notmuch, NOTMUCH_CONFIG_MAIL_ROOT);
     db_path_len = strlen (db_path);
 
     relative = path;
@@ -1577,7 +1503,8 @@ notmuch_database_find_message_by_filename (notmuch_database_t *notmuch,
                status = NOTMUCH_STATUS_OUT_OF_MEMORY;
        }
     } catch (const Xapian::Error &error) {
-       _notmuch_database_log (notmuch, "Error: A Xapian exception occurred finding message by filename: %s\n",
+       _notmuch_database_log (notmuch,
+                              "Error: A Xapian exception occurred finding message by filename: %s\n",
                               error.get_msg ().c_str ());
        notmuch->exception_reported = true;
        status = NOTMUCH_STATUS_XAPIAN_EXCEPTION;