Return-Path: X-Original-To: notmuch@notmuchmail.org Delivered-To: notmuch@notmuchmail.org Received: from localhost (localhost [127.0.0.1]) by olra.theworths.org (Postfix) with ESMTP id 28D41429E4A for ; Thu, 17 Feb 2011 23:59:38 -0800 (PST) X-Virus-Scanned: Debian amavisd-new at olra.theworths.org X-Spam-Flag: NO X-Spam-Score: 0 X-Spam-Level: X-Spam-Status: No, score=0 tagged_above=-999 required=5 tests=[RCVD_IN_DNSWL_NONE=-0.0001] autolearn=disabled Received: from olra.theworths.org ([127.0.0.1]) by localhost (olra.theworths.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id ZT6zN79RrO8j for ; Thu, 17 Feb 2011 23:59:36 -0800 (PST) Received: from dmz-mailsec-scanner-7.mit.edu (DMZ-MAILSEC-SCANNER-7.MIT.EDU [18.7.68.36]) by olra.theworths.org (Postfix) with ESMTP id 33F61429E38 for ; Thu, 17 Feb 2011 23:59:29 -0800 (PST) X-AuditID: 12074424-b7b0bae000000a05-6d-4d5e26e04bad Received: from mailhub-auth-3.mit.edu ( [18.9.21.43]) by dmz-mailsec-scanner-7.mit.edu (Symantec Brightmail Gateway) with SMTP id 8D.70.02565.0E62E5D4; Fri, 18 Feb 2011 02:59:28 -0500 (EST) Received: from outgoing.mit.edu (OUTGOING-AUTH.MIT.EDU [18.7.22.103]) by mailhub-auth-3.mit.edu (8.13.8/8.9.2) with ESMTP id p1I7xRwj029792; Fri, 18 Feb 2011 02:59:27 -0500 Received: from drake.mit.edu (209-6-116-242.c3-0.arl-ubr1.sbo-arl.ma.cable.rcn.com [209.6.116.242]) (authenticated bits=0) (User authenticated as amdragon@ATHENA.MIT.EDU) by outgoing.mit.edu (8.13.6/8.12.4) with ESMTP id p1I7xQp1001544 (version=TLSv1/SSLv3 cipher=AES256-SHA bits=256 verify=NOT); Fri, 18 Feb 2011 02:59:27 -0500 (EST) Received: from amthrax by drake.mit.edu with local (Exim 4.72) (envelope-from ) id 1PqLFO-0008LK-Jg; Fri, 18 Feb 2011 02:59:26 -0500 From: Austin Clements To: notmuch@notmuchmail.org Subject: [PATCH 06/10] lib: Add API's to find by filename and remove a filename from a message. Date: Fri, 18 Feb 2011 02:58:56 -0500 Message-Id: <1298015940-31986-7-git-send-email-amdragon@mit.edu> X-Mailer: git-send-email 1.7.2.3 In-Reply-To: <1298015940-31986-1-git-send-email-amdragon@mit.edu> References: <1298015940-31986-1-git-send-email-amdragon@mit.edu> X-Brightmail-Tracker: AAAAAA== Cc: amdragon@mit.edu X-BeenThere: notmuch@notmuchmail.org X-Mailman-Version: 2.1.13 Precedence: list List-Id: "Use and development of the notmuch mail system." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 18 Feb 2011 07:59:38 -0000 The two new API functions, notmuch_database_find_message_by_filename and notmuch_message_remove_filename give library users more control over the filename removal process. notmuch_database_remove_message has been reimplemented in terms of these new functions. notmuch_message_remove_filename acts much like notmuch_message_remove_tag in that it does not synchronize with the database if the message is frozen. Thus, callers can freeze a message to remove a filename and perform tag synchronization in one atomic operation. This new approach also naturally eliminates an atomicity violation in the old code. Previously, notmuch_database_remove_message would first update the database document to remove the filename and only then remove the document if it had no more filenames left. An interruption between these two steps resulted in a permanently un-removable zombie message that would produce errors and odd results in searches. Since this new approach delegates document deletion to _notmuch_message_sync, the document will be deleted without first being updated, eliminating this window. --- lib/database.cc | 58 +++++++++++++++++++----------------------------------- lib/message.cc | 21 +++++++++++++++++++ lib/notmuch.h | 43 ++++++++++++++++++++++++++++++++++++++- 3 files changed, 83 insertions(+), 39 deletions(-) diff --git a/lib/database.cc b/lib/database.cc index d88b168..bee1e96 100644 --- a/lib/database.cc +++ b/lib/database.cc @@ -1689,72 +1689,56 @@ notmuch_status_t notmuch_database_remove_message (notmuch_database_t *notmuch, const char *filename) { - Xapian::WritableDatabase *db; + notmuch_message_t *message; + notmuch_status_t status = NOTMUCH_STATUS_SUCCESS; + message = notmuch_database_find_message_by_filename (notmuch, filename); + if (message) { + status = notmuch_message_remove_filename (message, filename); + notmuch_message_destroy (message); + } + return status; +} + +notmuch_message_t * +notmuch_database_find_message_by_filename (notmuch_database_t *notmuch, + const char *filename) +{ void *local; const char *prefix = _find_prefix ("file-direntry"); char *direntry, *term; Xapian::PostingIterator i, end; - Xapian::Document document; + notmuch_message_t *message = NULL; notmuch_status_t status; - status = _notmuch_database_ensure_writable (notmuch); - if (status) - return status; - local = talloc_new (notmuch); - db = static_cast (notmuch->xapian_db); - try { status = _notmuch_database_filename_to_direntry (local, notmuch, filename, &direntry); if (status) - return status; + return NULL; term = talloc_asprintf (local, "%s%s", prefix, direntry); find_doc_ids_for_term (notmuch, term, &i, &end); - for ( ; i != end; i++) { - Xapian::TermIterator j; - notmuch_message_t *message; + if (i != end) { notmuch_private_status_t private_status; - message = _notmuch_message_create (local, notmuch, + message = _notmuch_message_create (notmuch, notmuch, *i, &private_status); - if (message == NULL) - return COERCE_STATUS (private_status, - "Inconsistent document ID in datbase."); - - _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); - - /* Was this the last file-direntry in the message? */ - if (j == document.termlist_end () || - strncmp ((*j).c_str (), prefix, strlen (prefix))) - { - db->delete_document (document.get_docid ()); - status = NOTMUCH_STATUS_SUCCESS; - } else { - status = NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID; - } } } catch (const Xapian::Error &error) { - fprintf (stderr, "Error: A Xapian exception occurred removing message: %s\n", + fprintf (stderr, "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; + message = NULL; } talloc_free (local); - return status; + return message; } notmuch_tags_t * diff --git a/lib/message.cc b/lib/message.cc index 635f5cf..b4adb5c 100644 --- a/lib/message.cc +++ b/lib/message.cc @@ -1300,6 +1300,27 @@ notmuch_message_remove_all_tags (notmuch_message_t *message) } notmuch_status_t +notmuch_message_remove_filename (notmuch_message_t *message, + const char *filename) +{ + notmuch_status_t status; + + status = _notmuch_database_ensure_writable (message->notmuch); + if (status) + return status; + + status = _notmuch_message_remove_filename (message, filename); + /* Was this the last file-direntry in the message? */ + if (status == NOTMUCH_STATUS_SUCCESS) + message->deleted = TRUE; + + if (! message->frozen) + _notmuch_message_sync (message); + + return status; +} + +notmuch_status_t notmuch_message_freeze (notmuch_message_t *message) { notmuch_status_t status; diff --git a/lib/notmuch.h b/lib/notmuch.h index e508309..61030cb 100644 --- a/lib/notmuch.h +++ b/lib/notmuch.h @@ -316,6 +316,22 @@ notmuch_message_t * notmuch_database_find_message (notmuch_database_t *database, const char *message_id); +/* Find a message with the given filename. + * + * If the database contains a message with the given filename, then a + * new notmuch_message_t object is returned. The caller should call + * notmuch_message_destroy when done with the message. + * + * This function returns NULL in the following situations: + * + * * No message is found with the given filename + * * An out-of-memory situation occurs + * * A Xapian exception occurs + */ +notmuch_message_t * +notmuch_database_find_message_by_filename (notmuch_database_t *notmuch, + const char *filename); + /* Return a list of all tags found in the database. * * This function creates a list of all tags found in the database. The @@ -979,11 +995,34 @@ notmuch_message_maildir_flags_to_tags (notmuch_message_t *message); notmuch_status_t notmuch_message_tags_to_maildir_flags (notmuch_message_t *message); +/* Remove a filename from a message. If this is the last copy of this + * message, also delete it from the database. + * + * Much like notmuch_message_remove_tag, if message is frozen, it will + * not be removed from or updated in the database until thawed. + * + * Return value: + * + * NOTMUCH_STATUS_SUCCESS: The last filename was removed and the + * message was removed from the database. + * + * NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID: This filename was removed but + * the message persists in the database with at least one other + * filename. + * + * NOTMUCH_STATUS_READ_ONLY_DATABASE: Database was opened in read-only + * mode so no message can be removed. + */ +notmuch_status_t +notmuch_message_remove_filename (notmuch_message_t *message, + const char *filename); + /* Freeze the current state of 'message' within the database. * * This means that changes to the message state, (via - * notmuch_message_add_tag, notmuch_message_remove_tag, and - * notmuch_message_remove_all_tags), will not be committed to the + * notmuch_message_add_tag, notmuch_message_remove_tag, + * notmuch_message_remove_all_tags, and + * notmuch_message_remove_filename), will not be committed to the * database until the message is thawed with notmuch_message_thaw. * * Multiple calls to freeze/thaw are valid and these calls will -- 1.7.2.3