X-Git-Url: https://git.notmuchmail.org/git?p=notmuch;a=blobdiff_plain;f=lib%2Fdatabase.cc;h=48abbe8e7c72bf01e0e46b00c36b89775b29b942;hp=289e41c45bb4d8607fa0c1b31e31fa355bd21d40;hb=957f1ba3fc1d737887029ff1787fc6bea94de00b;hpb=8bef9ba9220956beb8ff634c16344fb00689b973 diff --git a/lib/database.cc b/lib/database.cc index 289e41c4..48abbe8e 100644 --- a/lib/database.cc +++ b/lib/database.cc @@ -69,7 +69,7 @@ typedef struct { * * Multiple terms of given prefix: * - * reference: All message IDs from In-Reply-To and Re ferences + * reference: All message IDs from In-Reply-To and References * headers in the message. * * tag: Any tags associated with this message by the user. @@ -89,8 +89,9 @@ typedef struct { * * 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. But the database doesn't really care itself - * about any of these. + * 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. * * The data portion of a mail document is empty. * @@ -136,7 +137,7 @@ typedef struct { * ASCII integer. The initial database version * was 1, (though a schema existed before that * were no "version" database value existed at - * all). Succesive versions are allocated as + * all). Successive versions are allocated as * changes are made to the database (such as by * indexing new fields). * @@ -147,7 +148,7 @@ typedef struct { * incremented for each thread ID. * * thread_id_* A pre-allocated thread ID for a particular - * message. This is actually an arbitarily large + * message. This is actually an arbitrarily large * family of metadata name. Any particular name is * formed by concatenating "thread_id_" with a message * ID (or the SHA1 sum of a message ID if it is very @@ -204,7 +205,8 @@ static prefix_t PROBABILISTIC_PREFIX[]= { { "from", "XFROM" }, { "to", "XTO" }, { "attachment", "XATTACHMENT" }, - { "subject", "XSUBJECT"} + { "subject", "XSUBJECT"}, + { "folder", "XFOLDER"} }; int @@ -420,7 +422,7 @@ skip_space_and_comments (const char **str) } /* Parse an RFC 822 message-id, discarding whitespace, any RFC 822 - * comments, and the '<' and '>' delimeters. + * comments, and the '<' and '>' delimiters. * * If not NULL, then *next will be made to point to the first character * not parsed, (possibly pointing to the final '\0' terminator. @@ -972,6 +974,50 @@ notmuch_database_upgrade (notmuch_database_t *notmuch, return NOTMUCH_STATUS_SUCCESS; } +notmuch_status_t +notmuch_database_begin_atomic (notmuch_database_t *notmuch) +{ + if (notmuch->mode == NOTMUCH_DATABASE_MODE_READ_ONLY) + return NOTMUCH_STATUS_SUCCESS; + + try { + (static_cast (notmuch->xapian_db))->begin_transaction (false); + } catch (const Xapian::Error &error) { + fprintf (stderr, "A Xapian exception occurred beginning transaction: %s.\n", + error.get_msg().c_str()); + notmuch->exception_reported = TRUE; + return NOTMUCH_STATUS_XAPIAN_EXCEPTION; + } + return NOTMUCH_STATUS_SUCCESS; +} + +notmuch_status_t +notmuch_database_end_atomic (notmuch_database_t *notmuch) +{ + Xapian::WritableDatabase *db; + + if (notmuch->mode == NOTMUCH_DATABASE_MODE_READ_ONLY) + return NOTMUCH_STATUS_SUCCESS; + + db = static_cast (notmuch->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. */ + const char *thresh = getenv ("XAPIAN_FLUSH_THRESHOLD"); + if (thresh && atoi (thresh) == 1) + db->commit (); + } catch (const Xapian::Error &error) { + fprintf (stderr, "A Xapian exception occurred committing transaction: %s.\n", + error.get_msg().c_str()); + notmuch->exception_reported = TRUE; + return NOTMUCH_STATUS_XAPIAN_EXCEPTION; + } + return NOTMUCH_STATUS_SUCCESS; +} + /* We allow the user to use arbitrarily long paths for directories. But * we have a term-length limit. So if we exceed that, we'll use the * SHA-1 of the path for the database term. @@ -1147,7 +1193,7 @@ _notmuch_database_filename_to_direntry (void *ctx, /* Given a legal 'path' for the database, return the relative path. * - * The return value will be a pointer to the originl path contents, + * The return value will be a pointer to the original path contents, * and will be either the original string (if 'path' was relative) or * a portion of the string (if path was absolute and begins with the * database path). @@ -1474,7 +1520,7 @@ _notmuch_database_link_message_to_children (notmuch_database_t *notmuch, * In all cases, we assign to the current message the first thread_id * found (through either parent or child). We will also merge any * existing, distinct threads where this message belongs to both, - * (which is not uncommon when mesages are processed out of order). + * (which is not uncommon when messages are processed out of order). * * Finally, if no thread ID has been found through parent or child, we * call _notmuch_message_generate_thread_id to generate a new thread @@ -1716,11 +1762,20 @@ notmuch_database_remove_message (notmuch_database_t *notmuch, for ( ; i != end; i++) { Xapian::TermIterator j; + notmuch_message_t *message; + notmuch_private_status_t private_status; - document = find_document_for_doc_id (notmuch, *i); + message = _notmuch_message_create (local, notmuch, + *i, &private_status); + if (message == NULL) + return COERCE_STATUS (private_status, + "Inconsistent document ID in datbase."); - document.remove_term (term); + _notmuch_message_remove_filename (message, filename); + _notmuch_message_sync (message); + /* Take care to find document after sync'ing filename removal. */ + document = find_document_for_doc_id (notmuch, *i); j = document.termlist_begin (); j.skip_to (prefix); @@ -1731,7 +1786,6 @@ notmuch_database_remove_message (notmuch_database_t *notmuch, db->delete_document (document.get_docid ()); status = NOTMUCH_STATUS_SUCCESS; } else { - db->replace_document (document.get_docid (), document); status = NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID; } } @@ -1747,49 +1801,42 @@ notmuch_database_remove_message (notmuch_database_t *notmuch, return status; } -notmuch_tags_t * -_notmuch_convert_tags (void *ctx, Xapian::TermIterator &i, - Xapian::TermIterator &end) +notmuch_string_list_t * +_notmuch_database_get_terms_with_prefix (void *ctx, Xapian::TermIterator &i, + Xapian::TermIterator &end, + const char *prefix) { - const char *prefix = _find_prefix ("tag"); - notmuch_tags_t *tags; - std::string tag; - - /* Currently this iteration is written with the assumption that - * "tag" has a single-character prefix. */ - assert (strlen (prefix) == 1); + int prefix_len = strlen (prefix); + notmuch_string_list_t *list; - tags = _notmuch_tags_create (ctx); - if (unlikely (tags == NULL)) + list = _notmuch_string_list_create (ctx); + if (unlikely (list == NULL)) return NULL; - i.skip_to (prefix); - - while (i != end) { - tag = *i; - - if (tag.empty () || tag[0] != *prefix) + for (i.skip_to (prefix); i != end; i++) { + /* Terminate loop at first term without desired prefix. */ + if (strncmp ((*i).c_str (), prefix, prefix_len)) break; - _notmuch_tags_add_tag (tags, tag.c_str () + 1); - - i++; + _notmuch_string_list_append (list, (*i).c_str () + prefix_len); } - _notmuch_tags_prepare_iterator (tags); - - return tags; + return list; } notmuch_tags_t * notmuch_database_get_all_tags (notmuch_database_t *db) { Xapian::TermIterator i, end; + notmuch_string_list_t *tags; try { i = db->xapian_db->allterms_begin(); end = db->xapian_db->allterms_end(); - return _notmuch_convert_tags(db, i, 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) { fprintf (stderr, "A Xapian exception occurred getting tags: %s.\n", error.get_msg().c_str());